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. RequestTaskCreateFromWorkflowRequest(_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/:tokenandPOST /tasks/public/:token/submit,auth: false(merchant e-sign use case; not relevant to agent questions). - List/inbox:
GET /tasks,auth: true,VIEWERrole, filtersstatus/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.