agentjob.ioHandbook
Architecture Decision Records

ADR-0010: A daily-performance product with deterministic-first agents

The template's Saudi merchant vertical is replaced by one product module whose agents run on a local deterministic engine, with a model as an optional second implementation of the same output contract.

ADR-0010: A daily-performance product with deterministic-first agents

  • Date: 2026-09-20
  • Status: Accepted (owner-directed product decision)
  • Deciders: the owner
  • Tags: product domain ai

Context

The template shipped six product surfaces for a Saudi merchant vertical (storefront sites, Google Business presence, payment links with ZATCA invoices, Wathq CR lookup, sector packs, SMS OTP). A product that helps one person or team perform every day needs none of them: they add surfaces nobody maintains and they blur what the product is.

The AI question was the second one. An agent feature that only works with a provider key is a feature that cannot be demonstrated, tested in CI, or offered to a user whose key is missing, and a fallback bolted on afterwards usually produces different-shaped output that the UI has to special-case.

Decision

One product module. modules/registry loads exactly one module, performance (modules/registry/performance.ts): /today, /agents, /review. The merchant vertical is removed rather than hidden — its API modules, dashboard modules, routes, sector packs, Wathq lookup and ZATCA helpers are deleted. The ModuleManifest contract is narrowed to what a module actually needs (identity, requirements, entitlements, routes, settings schema, events); jobs, webhooks, Copilot tools, sector hooks and marketplace metadata are removed until something uses them.

Deterministic-first agents. A pattern declares its fields and returns one schema (patternResultSchema). The local engine (packages/core/src/domain/performance/engine.ts) implements every pattern deterministically and is the default; a model is an optional second implementation used only when ANTHROPIC_API_KEY is set, with the model's answer validated against the same schema. A model failure falls back to the engine inside the same request, and every run stores which engine answered (AgentRun.mode).

Measurement is domain code, not a dashboard query. Priority, day score, streaks and week aggregation live in packages/core/src/domain/performance/scoring.ts, are documented as a formula, and are unit-tested. Plan gating (patterns, history window, team board) lives in capabilities.ts and is enforced in the API, not only in the UI.

No containers. Local development uses a PostgreSQL server already running on the machine: pnpm db:setup creates the database from DATABASE_URL, pnpm setup chains install + generate + push. docker-compose.yml is deleted and every Docker instruction is removed from the docs.

Consequences

  • Positive — the product has one coherent story and one module to maintain; the agent feature works, and is tested, with no key and no cost; the model path cannot change the shape of what the UI renders or lie about having produced it; onboarding is a one-field form.
  • Negative — the removed vertical would have to be rebuilt from git history if a merchant product is ever wanted again; the engine's Arabic and English prose is maintained by hand rather than generated.
  • Rejected — keeping the vertical behind a feature flag (dead code nobody tests); making the model the only engine (untestable without a key, unpredictable cost); keeping Docker for local Postgres (a container runtime is not a prerequisite for a product like this).
  • Revenue impact (SAR / month, confidence) — the plan ladder is the product's only revenue channel: Starter (99), Growth (249, recommended) and Pro (499) per workspace. Pro is the only plan with the team board and with unlimited members, which is what makes it the natural upgrade for teams. Magnitude depends on acquisition, not on this decision; confidence low.
  • Supersedes — ADR-0003, ADR-0004, ADR-0006 and ADR-0007 describe surfaces of the removed vertical (PSP funds handling, custom domains for storefronts, ZATCA invoicing, tenant-scoped Copilot tools). ADR-0005's contract is narrowed as described above; its registry and entitlement mechanism is unchanged and still binds the single product module.

On this page