⚠️ Reconstructed for validation, not recovered. The original slice is not in this repository, but every task spec declares an archetype and half of them declare this one, so agents need it to exist. Tags: [derived] is supported by the standards and ADRs we have · [inferred] is deduced · [proposed] is a gap filled with judgement. The shape is well evidenced —Status: PROPOSED RECONSTRUCTION · Coexists withstandards/api.md, ADR-017 and thetrackEventslice all describe it from the outside. What is genuinely uncertain is the file layout and the naming, since no application code exists yet to point at.
track-event.md (asynchronous
pipeline archetype). Every task spec declares which one it follows (DEC-I5).
What this archetype is for
A synchronous command: a request arrives, the system validates it, changes state, and answers with the result. The caller waits, and the answer is authoritative — unlike the asynchronous pipeline, where the answer is “received” and the work happens behind a queue. Use it when the caller needs to know the outcome before proceeding: creating a record, publishing a rule, redeeming points at a counter. UsetrackEvent instead when the caller only needs to know the
system accepted the input.
Most of the platform is this archetype. It is the default, and the pipeline is the exception.
The six layers
1. Route — backend/api [derived]
- Authenticate — session, API key or member token.
- Authorize — exactly one declared permission,
{module}.{resource}.{action}(R16). - Validate — a Zod schema over the request body, producing a typed command.
- Resolve context — tenant, program or organization, whatever the module scopes by.
- Call the command handler, and map its result or typed error onto the response.
2. Command handler — the module’s application layer [derived]
The handler is where the transaction lives. It is the only place in the archetype that opens one, and it opens exactly one (R12).makeDeps(ctx). A handler that imports a vendor SDK is a defect.
3. Domain [derived]
Pure functions and domain services: invariants, state transitions, calculations. No I/O, no knowledge of Drizzle, no knowledge of HTTP. This is the layer worth being strict about. Everything above it is plumbing that can be regenerated; the rules here are the product. [inferred]4. Persistence — database/postgres through a repository [inferred]
Drizzle inside the transaction. Every write carries tenant_id and cell_id (R6), reaches the
database through getDb(tenantCtx) (R8), and obeys the data standard — parametric codes rather than
enums, amounts with their currency.
(The repository indirection is [inferred]. R8 mandates getDb(tenantCtx), which could equally
be called from the handler directly. I have proposed a repository because it keeps the handler
testable without a database, but this is a place the original may differ.)
5. Outbox and audit — same transaction [derived]
Non-negotiable and the reason the transaction exists:- One or more domain events written to the outbox (R13), never published directly.
- One
core.audit_logrow with a typed actor, the full old→new diff, and thecorrelation_idinherited from the request (R15).
6. Response [derived]
The created or updated resource, or a typed error as RFC 9457problem+json with a stable code.
The handler returns a result type; the route maps it. A handler that formats HTTP is a handler that
cannot be called from a worker.
What agents copy from here
- The five-step route and the discipline that it holds no business logic.
- One transaction per command, opened in the handler and nowhere else.
- Outbox and audit inside it, always, with the correlation id carried through.
- Ports through
makeDeps, never a vendor import. - Typed errors rather than thrown strings, so the route can map them without inspecting messages.
- The domain layer stays pure, which is what makes the tests fast enough that people write them.
Tests the archetype requires [inferred]
Where it differs from trackEvent
Copying this archetype into a pipeline produces a route that does heavy work inline and blows the
ingestion budget. Copying the pipeline into a command produces a caller who never learns whether
their action succeeded. Both are review-blocking errors, which is why every task spec declares its
archetype.
What needs validating
- The file layout and naming. Route → handler → domain → repository is the shape I inferred; the original may name or nest these differently. [inferred]
- Whether a repository indirection exists or handlers call
getDbdirectly. [inferred] - The name. “Initiative” suggests the Tracker module’s domain, which means the original slice probably lives in a module this repository does not contain yet. If so, this reconstruction should eventually be replaced by the real one rather than kept alongside it. [proposed]