Skip to content
GRPNR.

Atlas evidence: substrate investigation (r1)

Repo: /home/rob/groupon/refactor/repos/monorepo-development (Encore.ts two-plane monorepo). Second repo skimmed: /home/rob/groupon/refactor/repos/monorepo-infra. Date: 2026-07-18. reasons-and-decisions/ NOT read (confidential, per instruction).

1. Tasks service

Location: apps/backend-encore-core-ts/services/tasks/.

TaskType (interfaces/tasks.interfaces.ts:10-14): APPROVAL = "approval", SIGNATURE = "signature", INFORMATION_REQUEST = "information_request". Three values only, no QUESTION or AGENT_QUESTION type exists.

TaskStatus (interfaces/tasks.interfaces.ts:17-22): PENDING (only live state) -> COMPLETED / EXPIRED / CANCELLED (all terminal). Forward-only, enforced in utils/taskRules.utils.ts:48-59 (assertOutcomeWritable): re-applying the same terminal outcome is a no-op (idempotent for at-least-once activity retries), moving between different terminal states throws APIError.failedPrecondition.

Group queues: Task.assigneeGroupSlug: string | null (interfaces/tasks.interfaces.ts:55). No group-existence validation in the tasks service itself; a slug is just a string matched against actor.groups at completion time (utils/taskRules.utils.ts:24-38, canCompleteTask). Real groups (with membership) live in a separate user service: user_groups + user_group_memberships tables, userGroupsList endpoint GET /user/groups (apps/backend-encore-core-ts/services/user/README.md:30,43). Whether an “agent-questions” slug already exists there: NOT VERIFIED (did not query the groups list; likely does not exist yet given tasks service is fresh, ASSUMED absent).

Signal/resume pattern: TaskWorkflowStamp (interfaces/tasks.interfaces.ts:39-43): { workflowId, runId: string | null, signal: string }. Stored on the task at creation via _taskCreateFromWorkflow (controllers/_taskCreateFromWorkflow.controller.ts), which calls tasksService.createFromWorkflow (services/tasks.service.ts:43-84). That method is idempotent on the stamp: taskRepository.findTaskByStamp(workflowId, runId, signal) finds a replayed at-least-once activity call and returns the existing task instead of duplicating (rotating the public token if one was requested again).

Completion resumes the workflow: TasksService.#completeTask (services/tasks.service.ts:158-191) calls workflow_management._workflowSignalSend({ workflowId, runId, signal, data: [{ taskId, payload }] }) when task.workflow is set. That internal endpoint is _workflowSignalSend.controller.ts (apps/backend-encore-core-ts/services/workflow-management/controllers/executions/_workflowSignalSend.controller.ts), POST /workflow-management/internal/executions/signal, expose: false, auth: false (in-cluster only; tasks service is the auth boundary). It delegates to workflowsRunnerService.sendSignalToWorkflow. A signal-send failure fails the whole completion call by design (“a completed task whose workflow never learns of it is the worst state” — comment at tasks.service.ts:174-175).

Create/answer API shapes:

  • Create (internal, from workflow activity): POST /tasks/internal/create, expose: false, auth: false. Request TaskCreateFromWorkflowRequest (_taskCreateFromWorkflow.controller.ts:20-33): type, title, description?, assigneeUserId?, assigneeGroupSlug?, entityType?, entityId?, dueAt?, formSchema?, workflow?, withPublicLink?. Response: { task: Task, publicToken: string | null } (raw token returned exactly once).
  • Answer (human, authenticated): POST /tasks/:id/complete, auth: true. Request { id, formPayload? }, response { task } (taskComplete.controller.ts). ACL: assignee, group member, or ADMIN; unassigned+ungrouped = any signed-in employee (canCompleteTask).
  • Answer (public/unauthenticated token path): GET /tasks/public/:token and POST /tasks/public/:token/submit, auth: false (merchant e-sign use case; not relevant to agent questions).
  • List/inbox: GET /tasks, auth: true, VIEWER role, filters status/type/assigneeUserId/assigneeGroupSlug/entityType/entityId (tasksList.controller.ts). Doc comment gives the exact query for a group queue: assigneeGroupSlug=<queue>&status=pending (tasksList.controller.ts:14).

Full design reference cited repeatedly in the tasks README: _documentation/backend/deal-management-plan.md section 4 (not read in depth here; DESIGN-flagged doc per _documentation/README.md:19).

Deliberately NOT built yet (services/tasks/README.md:43-52): JSONForms/AJV payload validation, reminders/escalation/SLA auto-reassignment, and a task-events fan-out topic (“add when a consumer exists” — Atlas triage would be exactly such a consumer, currently absent).

2. Docs conventions, glossary, ADR practice

Docs live in two places kept in sync: source of truth _documentation/ at repo root, bundled runtime copy apps/backend-encore-core-ts/services/docs/content/ (synced by pnpm docs:sync, per scripts/docs-sync.mjs and docsContent.service.ts:7). Loaded once into memory at startup (23 files as of the in-code comment, docsContent.service.ts:7).

Entry points: root CLAUDE.md (Claude-only, no AGENTS.md, stated explicitly), .claude/rules/*.md (encore-ts, frontend, biome-and-typescript, testing), .claude/skills/* (10 skills, e.g. drizzle, encore-client-gen, encore-service-registration; format: one skill = one figured-out-once procedure per root CLAUDE.md “Skills” section).

No project-wide glossary or ubiquitous-language file exists. Searched for glossary|ubiquitous|terminology (case-insensitive) across _documentation, docs content, and the whole repo: the only hits are apps/frontend-admin-ts/src/app/oncall-metrics/_components/MetricsGlossaryDrawer.tsx, a feature-local UI glossary for oncall metrics terms, not a domain-wide glossary. _documentation/backend/domain-model.md exists and is the closest thing to ubiquitous language (entity/aggregate definitions) but is domain-model documentation, not a glossary artifact.

ADR practice: no per-decision ADR files anywhere (find -iname "*adr*" empty). Decision recording is a single append-only running log, reasons-and-decisions/07-decision-log.md, format “date . decision . reason . rejected alternatives”, superseded entries marked not deleted (root CLAUDE.md “Decision log” section states this explicitly as the binding practice). monorepo-infra mirrors the same pattern at docs/decisions.md. This is a real, working precedent Atlas’s “decisions” surface could hang off, but it is a flat file, not a queryable/indexed store today.

3. Comms machinery

notifications service exists (apps/backend-encore-core-ts/services/notifications/): recipient inbox, typed rich content (plain_string or component_doc), two channels in_app and email (README.md:1-25). Endpoints: _notificationSend (internal producer), notificationsList (/notifications/my), notificationsCount, notificationMarkRead, notificationMarkAllRead. All recipient-scoped, no group broadcast or digest primitive found.

Explicitly stated in that same README (line 4-6): this was “migrated from the legacy notification service minus its Google-Chat/Sherlock triage gateway (a different domain, deliberately not ported) and minus every Salesforce coupling.” So legacy Groupon HAD a Chat-based triage gateway (name “Sherlock”) conceptually adjacent to Atlas’s job, and it was a deliberate deletion, not an oversight, when this monorepo was built. No Slack/Google Chat client code exists anywhere in this repo (grep -rn "chat\|slack" across notifications and mcp: zero hits). No digest job, cron, or rate-limiting code was found for notifications.

4. MCP skeleton

apps/backend-encore-core-ts/services/mcp/. Streamable HTTP JSON-RPC at POST /mcp/mcp, stateless (fresh server per request), OAuth 2.1 (dynamic client registration, PKCE, RFC 8414/9728 discovery) for client auth; access tokens are permanent API keys via api_tokens service (README.md).

Registered tools today (tools/_tool_registry.ts:19-31): ping (example), registerDocsTools (platform docs tree/read/search), registerReportsTools, registerFoldersTools. No task, question, or triage tool exists. Adding one is a documented one-file-plus-one-registry-line procedure (README “How to add a tool”).

5. Existing search/indexing over docs or code

Only one: docsContentService.search() (services/docs/services/docsContent.service.ts:111-122), an in-memory case-insensitive substring scan (doc.lowercase.indexOf(needle)) with a plain-text excerpt window, over the 23 bundled docs. Exposed as GET /docs/search (docsSearch.controller.ts) and as an MCP tool. No vector/embedding search, no code index, no ripgrep-backed service. This is the only “search” primitive Atlas could reuse as-is, and it does not cover code, only the bundled _documentation copy.

6. “Atlas” name check

Zero uses of “Atlas” as a name anywhere in monorepo-development (checked non-confidential paths only). All “atlas” string matches are substrings of “Atlassian” (Jira/Confluence integration: packages/encore-client/src/encore.ts, apps/frontend-admin-ts/.../bugs-and-ideas/, one Jira ticket URL comment in services/reports/utils/constants.utils.ts:9). Name is free to use; note the Atlassian collision could cause search/grep confusion if “Atlas” and “Atlassian” code coexist later.

monorepo-infra: notification/Grafana state (one paragraph)

Grafana is self-hosted via Helm on the (not-yet-billed) infra GKE cluster, Postgres-backed on a shared Cloud SQL instance, single replica, no PVC (grafana/README.md). Two datasources provisioned as code: Cloud Monitoring/Stackdriver (default) and a Temporal Prometheus stub pointing at a scrape endpoint that “does not exist yet.” provisioning/dashboards/ is empty (.gitkeep only, confirmed by directory listing) — “No dashboards exist yet” per the README. No alerting, no notification-channel config, no Slack/webhook integration found anywhere in monorepo-infra (grep -rn "notif|alert|Slack|chat" hit only _documentation/clusters.md, unrelated to notifications). This matches the ground truth that infra is 0% provisioned behind a billing gate: everything described is scripted-but-not-yet-run.