Factory docs, home
Page navigation

Read this once to orient, then drill into the module references in this directory. It uses the Factory's one language and British spelling. Checked against the source at 574e70c on 28 September 2026, then updated for the dashboard brand increment merged from main at f5a8991, which changes no file under src/, for the Producers module (branch factory/producers), accepted on its independent implementation review (Astra round 9 PASS) with both live transports unavailable, and for step 1, increments 1–2 (Repository Assurance, Product gate and delivery records; branch factory/delivery, merged with main at d7e4001), accepted on their independent implementation review (Astra round 6 PASS) and composed into nothing, and for step 1, increment 3 (Repository collector, same branch), accepted on its independent implementation review (Astra round 2 PASS) and composed into nothing, and for step 1, increment 3b (the additive guard on Repository invokeOnce, same branch), accepted on its independent implementation review (Astra round 2 PASS) and composed into nothing, and for lifecycle increment 0 (branch factory/lifecycle, from e62abe6), which changes only src/intelligence.ts (planned declarations for the proposed lifecycle design) and passed independent review (Astra round 2 PASS), and for lifecycle increment 1 (branch factory/lifecycle-schedule, from 217887b), which adds the Lifecycle schedule (src/lifecycle-schedule.ts) and moves its declaration into MODULE_INTELLIGENCE, and passed independent review (Astra round 4 PASS), and for lifecycle increment 2 (branch factory/lifecycle-sources, from 217887b), which adds the Discovery sources collector (src/discovery-sources.ts, src/discovery-text.ts), moves its Intelligence declaration into the accepted list with its files, and passed independent review (Astra round 5 PASS), and for the merge of lifecycle increments 0–2 with main (branch factory/lifecycle-merge), which changes in src/intelligence.ts only where the step-planned declarations render (the Intelligence reference, not the README), and for increment 1 of the Service access module (branch factory/service-access, from main at 985b5f9), built on test fixtures and revised after an adversarial test pass and Astra round 1's FAIL, provisionally accepted — Fable 5.1 (max) round 1; Astra review pending (Codex usage limit until 2026-10-03 18:00) (Fable round 1 PASS, <local evidence archive>; from 3 October 2026 the highest-numbered <local evidence archive> report holds the verdict), and for its merge with main (branch factory/service-access-merge), which adds its declaration to src/intelligence.ts without moving a line the lifecycle cites (the Intelligence reference; independent review pending); the module references, the build review and the receipts under docs/ are the evidence behind every claim here. Where older prose and observed behaviour disagree, trust observed behaviour and accepted receipts.

Verification: independent Astra reviews of this model, AGENTS.md, CLAUDE.md and the docs site are archived at <local evidence archive>; the corrections from rounds 1–9 are applied here, and the highest-numbered report holds the current verdict.

1. Purpose and current truth

  • Purpose. Turn customer needs into small, independently verified improvements across several Products, and keep them healthy (design, values). Agents act on behalf of a domain; they are never the system of record.
  • What exists. Accepted local infrastructure only: the independently accepted modules in section 3 (fifteen references and sixteen accepted build review rows besides Producers: the review splits Repository into sealing / export, integration and the integration guard seam, and joins Project guidance with Required actions) in one Node.js application over local SQLite and Git, on one macOS host. Intelligence, accepted later, is not counted there, nor are the lifecycle's two modules or Service access below; each has its own build review row (Intelligence since lifecycle increment 0). Producers is accepted as code under independent review, but both of its live transports are unavailable and nothing composes it (section 3). Repository Assurance and the Product gate and delivery records are accepted as step 1, increments 1–2; only tests call them. The Repository collector is accepted as increment 3; only tests call it. The repository guard seam is accepted as increment 3b; nothing in src/ passes a guard. Delivery composition, the delivery CLI and the live Slice (increments 4–8) are not built. The Lifecycle schedule (lifecycle increment 1) is implemented on fixtures with a fake clock and passed independent review (Astra round 4 PASS); only tests call it, and nothing runs on a schedule. Discovery sources, lifecycle increment 2, is implemented, tested offline against a loopback corpus and accepted locally on independent review (Astra round 5 PASS); nothing composes it. Service access increment 1 (a names-only catalogue of Service kinds, Service map and readiness, on test-made fixtures) is built and provisionally accepted — Fable 5.1 (max) round 1; Astra review pending (Codex usage limit until 2026-10-03 18:00) (Astra round 1 FAIL, fixed; Fable round 1 PASS; from 3 October 2026 the highest-numbered <local evidence archive> report holds the verdict); it reads no live source and nothing composes it. The proposed production stack (Temporal, PostgreSQL) is a proposal; nothing of it runs here.
  • What does not exist. No active CI: .github/workflows/check.yml is configured on the private origin chrismitchelmore/software-factory but waits for a runner (hosted minutes are blocked by billing; a self-hosted Mac runner needs the owner). No production, pilot, release or customer (older prose calls the fixture path a "local pilot"; it is local synthetic-fixture infrastructure, not a pilot). No provider transport is qualified: the Producers module is accepted, but Claude's rules revision 5 needs re-qualification (the revision-4 live check decided unavailable) and OpenAI has no Platform credential. No nightly or weekly automation is active. No composed Product gate (the gate exists, but nothing calls it), Action gateway or planner, and no Assessment of any real Slice (delivery records can derive one; nothing has).
  • All 27 customer-value epics (E01–E27) remain open. Accepted modules, brand artwork and documentation close none of them (outcome map).
  • Last accepted full verification (build review): strict typecheck, 350/350 tests with none skipped, frontend build, and real desktop and 390 px browser checks. The later dashboard brand increment (0ab94ed–7b499ea) is recorded only in the handover: Astra high PASS on round 2, root 355/355 and dashboard package 10/10 tests; it has no receipt or build review row. The Producers acceptance (Astra round 9 PASS, <local evidence archive>) rests on 397/397 focused offline tests run locally and Astra's own 128/128 read-only tests and 32 in-memory probes; it is not a live transport qualification (344/344 at qualification time, Claude receipt). The step 1, increments 1–2 acceptance (Astra round 6 PASS, <local evidence archive>) rests on 73/73 increment tests and a 428/428 full check on the pre-merge base f5a8991; after merging main at d7e4001, the full check passes 851/851 with none skipped. At that merge one boundary test was adapted so that the Producers module may import its own contract; that change is not independently reviewed. No receipt or reference review exists for them yet. The step 1, increment 3 acceptance (Astra round 2 PASS, <local evidence archive>) rests on 50/50 focused tests run locally (collector, adversarial, delivery boundaries and Repository Assurance, none skipped) and Astra's own read-only typecheck, boundary tests and placement probes; the full check then passed 881/881 with none skipped. Test counts describe the tested boundary, not release or customer value.
  • Six Products are registered for metadata inspection only (JustSpeakToIt, Pasta, Family Watch, Tally, Focus Sentinel, Factory). Their state is <local Portfolio journal> (ignored, local data): preserve it and never re-run onboarding to recreate it.
  • Canonical changing status is the Notion Factory record linked from handover; the active task capsule is .intent/tasks/local-factory-completion.md.

2. Domain language as used in code

One line per term. Status says how far the term exists in src/: implemented, narrow (real but limited), pinned only (an identity string, no behaviour) or designed, not implemented.

TermIn code todayStatus
ProductproductId registry entry in Portfolio (ProductRecord, one verified checkout, operationScope: "metadata-inspection", deliveryAuthority: "absent"); named in Repository identities and manifests and in Guidance aggregates. The Product gate (gate:product:<productId>) binds a Product once to a Factory-owned store outside every registered checkout and admits its delivery work; only tests call it.narrow
ObjectiveOnly the objective text of an imported Portfolio focus fact. No Planning.narrow
SignalNot present.designed, not implemented
CaptureCapture (sf-capture/1) in Discovery sources (lifecycle increment 2, accepted locally): one item of at most 4 096 bytes of normalised text from a commissioned Source, with its SHA-256, SimHash and novelty (new, changed, repeat, unchanged); larger text is refused, never cut short, and only its hashes are journalled, the text staying in a cache that is readable for 35 days and pruned at each collection (§8). A Capture handed back is trusted only as its occurrence's record holds it. Tested only on a loopback corpus; nothing composes it.narrow
CitationCitation (sf-citation/1) in Discovery sources: a verbatim quote of at most 15 words (counted strictly) that must occur in a recorded Capture's text and carry no address, handle or phone number; it re-verifies from those bytes and the occurrence's record, so a quote moved to another URL, time or occurrence is rejected, and is unverifiable (unknown, not failed) once they expire at 35 days. Not registered with the Evidence store; nothing makes one yet but tests.narrow
ClusterNot present. Proposed lifecycle §4: a Product's Signals under one problem key (signal-intake), scored for synthesis.designed, not implemented
OccurrenceLifecycleSchedule in lifecycle-schedule.ts: one scheduled discovery pass with a stable ID (discovery/<product>/basic/<local date>, …/full/<ISO week>, discovery/portfolio/<rhythm>/<ISO week>), admitted, deferred, reconciled, resumed and settled once, each transition its own journal command; missed ones coalesce into the next. Only tests call it.narrow
OpportunityNot present. Proposed lifecycle §4: a falsifiable need drafted from Clusters (opportunity-synthesis) that the opportunity gate admits, parks or rejects; admission never selects it into a Slice.designed, not implemented
OutcomeNot present. Dashboard measuredValue is always unknown ["no-outcome-assessment"]. (Outcome in factory-support.ts is the CLI result word, not the domain Outcome.)designed, not implemented
CapabilityNot present (disposition versus health). Dashboard capabilities: {inspect, guidance} are server feature flags, a different sense.designed, not implemented
SliceFrozenInputs.slice {id, revision} and IntegrationPayload.slice in Repository; sliceId on Portfolio observations. The Product gate freezes a SliceEntry (scope, operations, frozen-input digests, allowance, deadline) under a grant, and the delivery records keep the frozen SliceRecord (sf-slice/1: Need, scope, non-goals, acceptance, recipe, environment, brief with an optional guidance pin, assessment plan); only tests create them. No planning.narrow
Product PackNot present.designed, not implemented
ReciperecipeRevision pin. The one executable recipe is guarded fixture verification sf-local-fixture-verify/2 (/1 is read and settled, never dispatched). Repository scope defines a sf-repository-verify/1 recipe record (collector closure and executor bounds) that nothing runs yet.narrow
RunExecution aggregate run:<runId>: queued, running, completed, exhausted, cancelled; pins and Budget fixed at submit. completed means the recipe ran to its end, never a Verdict.implemented
Attempt<runId>/<epoch>, epochs 1–20, running, completed, failed or interrupted; one owner; ends only by the owner's report or a trusted cleanup confirmation for its epoch.implemented
CandidateTwo narrow forms: the fixture Candidate digest (sf-local-fixture/2, from fixture name and execution manifest digest) and Repository's SealedCandidate (sf-candidate/1: manifest plus exact bytes). The accepted Producers module can return an untrusted Proposal for seal, but nothing composes it and both live transports are unavailable, so no producer proposes anything yet.narrow
EvidenceEvidenceStore: content-addressed sf-evidence/1 records with eight EvidencePins and a ProcessObservation; deliberately no passed flag.implemented
Verdictjudge() → Verified, Failed or Inconclusive with scope: "local-fixture". judgeRepository() gives the same three results with scope: "local-repository" over store-written repository records, only in tests; the Repository collector (accepted, not composed) writes such records from real sandboxed runs, also only in tests. Portfolio verification facts carry a Verdict word plus a separate validity.narrow
ActionThe integration occurrence key action {id, occurrence} in Repository; the Product gate admits integrate-local dispatches under integrate-local:<productId>:<sliceRef> and records their outcomes; only tests call it. No Action gateway.narrow
Action grantNot present. Proposed lifecycle §4: the owner's single-use permission for one outward Action (owner-decisions), bound to an artefact digest and channel.designed, not implemented
ReceiptIntegrationReceipt {id, target}: the receipt commit after a confirmed target update; the delivery records' create-once ReceiptRecord keeps the sender's own IntegrationObservation of a dispatched move. Two other senses exist: the journal's command receipt (a stored row) and Portfolio's UsageReceipt (provider usage).narrow
AssessmentderiveAssessment in the delivery records derives supported, inconclusive or rejected from a frozen Slice record, a repository Verdict and the records it judged, Verdict-first; nothing stores a conclusion and only tests call it. No Supported, Rejected or Inconclusive record exists.narrow
AuthorityThe Product gate's grants (paths, operations, limits), revocations, stop intent and one fence-proved owner; only tests exercise them, and Portfolio still reports deliveryAuthority: "absent". confirmedBy and collector names are attribution, not authentication.narrow
PolicyNot present (only HTTP Permissions-Policy headers, unrelated).designed, not implemented
BudgetBudget {maxAttempts, attemptTimeoutMs} in Execution; never extended; no cost field. The Product gate's per-Slice allowance (GrantLimits: production runs, check runs, attempts per check, integrations, elapsed time) is counted separately and never decreases. The Lifecycle schedule's discovery-window ledger counts estimated tokens per ISO week against a ceiling set when the week opens and never raised; an unknown call stays debited at its bound; only tests call it. A shared Slice allowance across providers is planned.narrow
EscalationNot present. Nearest thing: an attention item with ownerRequired, derived from a source-reported blocker that names the owner and an exhaustion; the Factory records none itself.designed, not implemented
Command, EventCommandJournal.execute(commandId, payload, expectedVersion, aggregateId, decide): one atomic transaction records state, receipt, 1–100 Events and outbox rows. Nothing delivers the outbox.implemented
epochAn Attempt's position, 1–20, and the ownership version scope owners check; mirrored by MAX_FENCE_EPOCH and the ledger's MAX_EPOCH.implemented
pinsRunPins {candidateDigest, recipeRevision, scenarioRevision}; EvidencePins add runId, attemptId, epoch, environment, collector; RepositoryPins (Product, store, base, commit, Slice, snapshots, frozen records, Attempt) map to repository-scope EvidencePins through evidencePinsOf; GuidancePin {productId, revision, digest}. Any input change needs a fresh Verdict.implemented
ScenarioscenarioRevision = "local-fixture-scenario/" + digest(environment, timeoutMs, maxOutputBytes); the environment pin is node-<version> <platform>-<arch>. Repository scope uses sf-repository-scenario/1/<hex> over the Product, store, base, Candidate, Slice, snapshots and acceptance.narrow
Fence, WorkspaceFenceIdentity: a one-row SQLite file held by a kernel-released read lock, open → fenced one way. WorkspaceRef: one fresh 0700 directory bound to that fence. fenced proves protected work cannot resume, not process exit.implemented
CollectorAn attribution label on Evidence and Portfolio facts (sf-guarded-fixture-worker/1, local-git, claude-code-result); never authenticated. The repository collector (sf-repository-scenario-collector/1, pinned with the digest of its source closure) is accepted as step 1, increment 3, and nothing composes it.implemented
Observation, FactPortfolio Observation {collector, recordId, revision, productId, sliceId, observedAt, capturedAt, fact}; FactKind: focus, source-coverage, activity, verification, release, measurement, blocker, repository, usage-invocation, usage-session, retraction.implemented
Countexact {value}, partial {atLeast, reasons} or unknown {reasons}; only an exact Count ever shows 0.implemented
GuidanceProductGuidance: 0–6 values per Product, immutable digest-bound revisions, resolvePin. The Dashboard reads and edits it; no planner or Run briefing consumes a guidance pin yet.implemented
ServiceA third-party system a Product uses through a vendor account. Service access detects one from catalogue signatures (ServiceEntry in a Service map); nothing acts on a Service. Increment 1.narrow
Service kindA reviewed, versioned, frozen catalogue entry (vercel/1): signatures, credential slots, a 1Password item template, purposes and routes, desired settings, runbook templates, digested; eleven exist. Increment 1.narrow
Credential referenceWhere a credential lives and what it is for, never its value: CredentialReference (location, kind, instance, slot, class, presence, usage, consumers) with a reference id hashed from the Product and the location. Increment 1.narrow
Service mapsf-service-map/1: one Product's Services, Credential references, CI jobs, findings and Counts at one commit, derived inside the journal decision from names-only scans and listings; only test fixtures produce one. Increment 1.narrow
Product vaultA dedicated 1Password vault per Product, owner-created and bound by id, the source of truth for its secrets. Only its store binding (onepassword-vault) and location type exist; nothing reads a vault.designed, not implemented
BlockerPortfolio blocker fact (open or resolved, prerequisite, affected, remediesTried, nextAction, optional details) → an attention action. Resolution is the source's report, never verified.implemented

3. Module map

Arrows are real import statements between src/ files, grouped by module. A dotted arrow is a type-only import or an HTTP call. factory-support.ts, local-factory.ts and cli.ts are composition files and belong to Guarded verification's reference.

Diagram source (Mermaid), shown as text
flowchart LR
  subgraph records["Records and runs (plate 1)"]
    CJ["Command journal<br/>journal.ts · canonical-json.ts · errors.ts"]
    EA["Evidence / Assurance<br/>evidence.ts · assurance.ts"]
    EX["Execution<br/>execution.ts"]
    LW["Local fixture worker<br/>local-worker.ts · fixture-child.ts · fixture-gate.ts · fixture-manifest.ts"]
    AF["Attempt fence / workspace<br/>attempt-fence.ts · attempt-workspace.ts"]
    GV["Guarded verification / recovery<br/>guarded-verification.ts · attempt-ledger.ts · factory-support.ts"]
    LF["Local factory CLI<br/>local-factory.ts · cli.ts"]
  end
  subgraph delivery["Isolation and delivery (plate 2)"]
    CE["Confined executor<br/>confined-executor.ts"]
    RP["Repository sealing / export and integration<br/>repository.ts"]
  end
  subgraph visibility["Visibility (plate 2 and unplated)"]
    PF["Portfolio<br/>portfolio.ts · portfolio-inventory.ts"]
    PG["Project guidance<br/>project-guidance.ts"]
    AT["Required actions and blockers<br/>attention.ts"]
    DC["Dashboard contract<br/>dashboard-contract.ts"]
    DS["Dashboard server and view<br/>dashboard-server.ts · dashboard-view.ts"]
    BA["Browser app<br/>apps/dashboard/src"]
  end
  subgraph providers["Providers (accepted; live transports unavailable)"]
    PR["Producers<br/>producer.ts · claude-producer.ts · openai-producer.ts"]
  end
  subgraph access["Service access (increment 1; not composed)"]
    SA["Service access<br/>service-access.ts · service-discovery.ts · service-kinds.ts"]
  end
  subgraph core["Delivery core (step 1, increments 1–3; not composed)"]
    RA["Repository Assurance<br/>repository-assurance.ts"]
    GD["Product gate and delivery records<br/>product-gate.ts · delivery-records.ts"]
    RC["Repository collector<br/>repository-collector.ts"]
  end
  subgraph lifecycle["Lifecycle (increments 1–2; not composed)"]
    LS["Lifecycle schedule<br/>lifecycle-schedule.ts"]
    DSC["Discovery sources<br/>discovery-sources.ts · discovery-text.ts"]
  end
  EX --> CJ
  LW --> EA
  LW --> AF
  GV --> EX
  GV --> AF
  GV --> LW
  GV --> EA
  GV --> CJ
  LF --> GV
  LF --> EX
  LF --> EA
  LF --> LW
  LF --> CJ
  RP -->|canonical JSON only| CJ
  RP -->|snapshotDigest| CE
  PF --> CJ
  PG --> CJ
  PG --> PF
  AT --> DC
  AT -.->|types| PF
  DS --> AT
  DS --> DC
  DS --> PF
  DS --> PG
  DS --> CJ
  BA --> DC
  BA -.->|HTTP, loopback| DS
  PR -->|canonical JSON only| CJ
  PR -->|types and limits only| RP
  RA -->|canonical JSON only| CJ
  RA -->|SNAPSHOT_PROTOCOL only| CE
  RA -->|PIN_FIELDS, isStoreRecord| EA
  RA -->|parseRepositoryIdentity| RP
  GD --> CJ
  GD -->|parseFenceIdentity only| AF
  GD --> RA
  GD -.->|types| RP
  GD -.->|types| EA
  RC -->|run, runtime and profile| CE
  RC -->|EvidenceStore| EA
  RC -->|pins and observation encoding| RA
  RC -->|validatedHoldIdentity| AF
  RC -->|canonical JSON only| CJ
  RC -.->|types| RP
  RC -.->|SnapshotManifest type| GD
  LS -->|journal and canonical JSON| CJ
  DSC -->|journal and canonical JSON only| CJ
  SA --> CJ
  SA -.->|Count and coverage types| PF

Facts the graph cannot show: only the Repository collector calls ConfinedExecutor.run, nothing in src/ calls the collector, and nothing in src/ calls repository.seal; the src/ importers of repository.ts are the Producers contract (src/producer.ts, for FileEdit, Proposal, SourceFile and REPOSITORY_LIMITS), Repository Assurance (parseRepositoryIdentity and types) and the Product gate, delivery records and Repository collector (types only); nothing in src/ or apps/ imports the Producers module, and nothing outside the delivery core (Repository Assurance, the Product gate and delivery records, and the Repository collector) imports any of its four files; nothing imports the Lifecycle schedule; nothing imports Discovery sources, and it imports only the Command journal, canonical JSON and Node built-ins; tests compose Repository with the executor (one in the Repository suite, and the collector's suite, which seals real Candidates and runs them). Nothing in src/ imports the three Service access files. npm run factory never starts the dashboard. The Dashboard's repository check is Portfolio.inspectProduct over an existing checkout, not Repository integration. Command IDs are journal-wide, so every module's prefixes must stay distinct.

ModuleResponsibilityDisposition (build review)Agent referenceHuman guide
Command journalAtomic command → state, receipt, Events, outbox; replay by command ID; read-only inspectionAccepted local reads and writescommand-journalguide
Evidence / AssuranceContent-addressed observations; pure three-valued Verdict foldAccepted for trusted local fixturesevidence-assuranceguide
ExecutionRun and Attempt ownership, Budget, durable cancellation; starts nothingAccepted for trusted local supervisionexecutionguide
Local fixture workerOne built-in fixture as a real bounded process, observed as EvidenceAccepted for fixed trusted fixtureslocal-fixture-workerguide
Attempt fence / workspacePer-Attempt ownership gate and private workspaceAccepted for trusted local POSIX fixturesattempt-fenceguide
Guarded verification / recoveryVerify a fixture under exclusive ownership; settle orphaned Attempts within Budget; the verify, inspect, cancel and recover CLIAccepted for trusted fixturesguarded-verificationguide
Confined executorOne bounded Node program against read-only snapshots in a macOS sandbox; observes, never judgesAccepted for the tested stateless macOS boundaryconfined-executorguide
Repository sealing / export and integrationSeal bounded text edits as an immutable Candidate; export exact bytes; move the target ref B → M at most once per prepared Action, optionally only after a caller's pre-move guard allows itSealing / export: fresh owned local repositories. Integration: a local effect primitive. The guard seam (step 1, increment 3b): accepted, not composedrepositoryguide
PortfolioRegistry of local Products bound to one checkout each; read-only views of sourced facts and usageAccepted for local metadata registration and sourced factsportfolioguide
DashboardNeutral contract, pure projection, loopback HTTP with two guarded commands, React presentationAccepted local browser interfacedashboardguide
Project guidanceUp to six versioned values per Product with immutable pinsAccepted local domain and browser interfaceproject-guidanceguide
Required actions and blockersPure projection of current sourced conditions into actions, notices and blocker detailAccepted local domain and browser interface (with Project guidance)attentionguide
Repository AssurancePure judgement of repository-scope Evidence for one sealed Candidate (Candidate-as-program checks, pinned stdout digests, authenticated manifest and base) into a local-repository Verdict; never relabels fixture proofAccepted as step 1, increment 1 (Astra round 6 PASS, 2026-09-29, with increment 2; rounds 1–5 FAIL). Report: <local evidence archive>. No collector feeds it; nothing composes it. No receiptrepository-assuranceguide
Repository collectorRun each frozen process check of one sealed Candidate once under the Confined executor (the base or Candidate tree as the program, the named fixture input, nothing else) and store exactly one repository-scope Evidence record per run; refuse, before anything runs, pins the sealed Candidate was not sealed under, and before each launch when the gate, the hold, the environment, placement of protected paths (also checked at setup; by name and by device and inode), the entry, size or deadline says so; a write-once snapshot store for fixture inputsAccepted as step 1, increment 3 (Astra round 2 PASS). Not composed. No receiptrepository-collectorguide
Product gate and delivery recordsOne journal aggregate per Product: store binding, grants and revocations, stop intent, fence-proved owner, frozen Slices with allowances, admissions and outcomes, with integration admitted only on a Verdict re-derived inside the decision; beside it, create-once delivery records and a pure Assessment derivationAccepted as step 1, increment 2 (Astra round 6 PASS, 2026-09-29, with increment 1; rounds 1–5 FAIL). Report: <local evidence archive>. Nothing composes it. No receiptproduct-gateguide
Producers (Claude / OpenAI)Turn one validated request into at most one provider exchange and return an untrusted Proposal for Repository sealing, or an explicit non-success; never applies, integrates or verdictsAccepted: provider-neutral producer contract, Claude CLI and OpenAI Responses adapters under independent review (Astra round 9 PASS, 2026-09-28). Live transports unavailable: Claude (rules revision 5 needs re-qualification; 5 built-in agents + 2 plugins lack established inert meaning), OpenAI (no Platform credential). Nothing composes it yet. Report: <local evidence archive> (rounds 1–8 FAIL). The Claude qualification receipt is for rules revision 4 (Claude receipt); the OpenAI receipt is for rules revision 4 and records no live request (OpenAI receipt)producerguide
Lifecycle scheduleOccurrence identity, the discovery-window ledger and deferral for the proposed discovery design: one journal aggregate over an injected clock; admits each provider call before contact and runs noneImplemented as lifecycle increment 1 (branch factory/lifecycle-schedule); an independent adversarial pass found 12 defects, all fixed, and the fixed revision passed independent review (Astra round 4 PASS; rounds 1-3 FAIL, every finding fixed). Tests under a fake clock, including real-process races; nothing composes it, no LaunchAgent is written or loaded, and every commission value is a proposal. No receipt reviewlifecycle-scheduleguide
Discovery sources (lifecycle increment 2)Fetch a Product's commissioned public Sources (allow-listed https URLs, robots.txt first, fixed headers, no cookies, credentials or redirects) into Captures with hashes and novelty, a coverage status per Source, and 15-word Citations that re-verify from a 35-day cache; limits refuse, never truncateAccepted locally on independent implementation review (branch factory/lifecycle-sources): Astra round 5 PASS after rounds 1–4 FAIL, every finding fixed. An independent adversarial pass found 17 defects, all fixed. Tested offline only (a loopback corpus, 76 tests, 17 of them the adversary's); its HTTPS transport has never fetched a real page, and nothing composes it. Logs and dispositions: <local evidence archive>discovery-sourcesguide
Service accessNames-only map of a Product's Services and where their credentials live (catalogue of Service kinds, deny-first tree scanning, listing and sign-in parsing, the Service map fold), exclusive store bindings, declarations, confirmations, route pins and readiness; built never to read or record a value (established for the tested positions and forms)Increment 1 (built 2026-09-29 and revised the same day on Fable 5.1's feedback and an adversarial test pass); provisionally accepted — Fable 5.1 (max) round 1; Astra review pending (Codex usage limit until 2026-10-03 18:00): Astra round 1 FAIL (eight findings, fixed), round 2 stopped at the usage limit; Fable 5.1 round 1 PASS (<local evidence archive>); from 3 October 2026 the highest-numbered <local evidence archive> report holds the verdict. Test fixtures only; nothing composes it. Receipt and build review row exist. Design: Astra round 5 PASS (<local evidence archive>)service-accessguide
IntelligenceOne registry of where a model takes part: each module's decision (none, optional, required) and why, one pinned model and effort per task, dated model facts per provider route; a pure selector that refuses rather than substitutes. Imports only canonical JSON; nothing imports itAccepted on its independent implementation review (Astra high, round 2 PASS, 2026-09-29) and merged to main at 298726b; not counted among the fifteen references in section 1, and it has no reference-review report; since lifecycle increment 0 it has its own build review row. Reports: <local evidence archive> (the highest-numbered report holds the current verdict). Nothing composes it. Lifecycle increment 0 changes it and passed its own independent review (Astra round 2 PASS): nine planned lifecycle declarations with five extension tasks and the three Astra tasks declared blocked until codex-cli's audit, since no effort is established on it, an efficient task refused until its own measured comparison, the weekly search pinned to a separate, unavailable research profile, the discovery-window allowance, and codex-cli as OpenAI's one route, declared unqualified, so the accepted OpenAI Responses adapter is kept but unpinned; the six earlier tasks' policy digests are unchanged (receipt). Increments 1 and 2 each move one landed declaration into MODULE_INTELLIGENCE (Astra rounds 4 and 5 PASS, with their modules), and the merge with main renders the step-planned declarations in the reference instead of the README, so the README section stays within its 25 lines (merge receipt). Merging service access increment 1 adds its none declaration without moving a line the lifecycle cites; that merge's independent review is pending (service access merge receipt). No model is qualified, so every task refusesintelligenceguide

Reference review. Each accepted module's agent reference, except Producers, Repository Assurance, the Product gate, the Repository collector, Intelligence, the Lifecycle schedule, Discovery sources and Service access, was checked independently against the source by Astra (gpt-6-astra, high effort); the reports are under <local evidence archive>, and the highest-numbered report for a slug holds its current verdict. Every one of those references has a PASS report: Execution, Local fixture worker and Required actions (round 3); Command journal, Project guidance and Repository (round 4); Attempt fence, Guarded verification and Portfolio (round 5); Confined executor, Dashboard and Evidence / Assurance (round 6). Producers was accepted on its implementation review instead: rounds 1–8 FAIL and round 9 PASS (<local evidence archive>; the current verdict is <local evidence archive>). Its agent reference has no separate reference-review report yet. Repository Assurance and the Product gate and delivery records were likewise accepted on their implementation review: rounds 1–5 FAIL and round 6 PASS (<local evidence archive>); their agent references have no reference-review report yet. The Repository collector was accepted on its implementation review, after an independent adversarial pass, rounds 1 FAIL and 2 PASS (<local evidence archive>); its agent reference has no reference-review report yet. Intelligence was accepted on its implementation review, rounds 1 FAIL and 2 PASS (<local evidence archive>); its agent reference has no reference-review report yet. The Lifecycle schedule passed its implementation review likewise, after an independent adversarial pass: rounds 1–3 FAIL and round 4 PASS (<local evidence archive>); its agent reference has no reference-review report yet. Discovery sources was likewise accepted on its implementation review: rounds 1–4 FAIL and round 5 PASS (<local evidence archive>); its agent reference has no reference-review report yet. Service access (increment 1) is provisionally accepted — Fable 5.1 (max) round 1; Astra review pending (Codex usage limit until 2026-10-03 18:00): Astra round 1 FAIL, Fable 5.1 interim round 1 PASS (<local evidence archive>), and from 3 October 2026 the highest-numbered <local evidence archive> report holds the verdict; its agent reference has no reference-review report. A reference review checks the documentation, not the module's behaviour or acceptance.

Each accepted module has one labelled position on one of two brand plates; Project guidance, Required actions, Producers, Repository Assurance, the Product gate, the Repository collector and Intelligence have no plate position yet, and neither have the Lifecycle schedule, Discovery sources and Service access (increment 1). Show a plate whole, name the position in text, and never crop it.

4. Lifecycle: what exists and what is planned

The designed loop is Need → Slice → Candidate → Verdict → Receipt → Assessment. The middle of it runs today only for four synthetic fixtures. Everything in the Planned column comes from the handover next steps 1–4; none of it is accepted, and nothing here claims otherwise.

TransitionIn code todayPlanned (not accepted)
Need → SlicePortfolio can hold imported focus facts (an objective and a sliceId) as sourced statements. The accepted Product gate binds a Product to a Factory-owned store, grants and revokes, and freezes a Slice with its scope, frozen-input digests, allowance and deadline; the delivery records keep the frozen SliceRecord (Need, brief with an optional guidance pin, assessment plan). Only tests call them.Step 1 (the gate and records exist; the composition that binds from the Portfolio, plans a Slice and runs it does not): a Product authority and admission boundary around the accepted Journal, Execution, Assurance and Repository interfaces, binding Product, source snapshot, integration destination and ref, Slice, exact baseline and candidate commits, acceptance and environment identities, independent evidence and a guidance pin. Portfolio checkout identity and owned bare-repository identity stay distinct. Step 3: briefs that pin {productId, revision, digest} from guidance, present the applicable rules and explain trade-offs; old briefs keep their pins; guidance grants no authority and weakens nothing.
Slice → CandidateRepository seal(FrozenInputs, Proposal) turns 1–64 bounded text edits into an immutable SealedCandidate in a fresh Factory-owned bare repository. The proposal comes from the caller; only tests call it. The fixture path derives a fixture Candidate digest instead. Accepted, with both live transports unavailable: the Producers contract turns one validated request into at most one provider exchange and an untrusted Proposal for seal; nothing composes it. The accepted Product gate admits one production per proposal digest and records the Candidate it sealed; nothing composes it with seal.Step 2: proposal-only providers, qualified by exact model, tool boundary, strict bounded text-edit proposals, refusals and malformed or truncated output, timeout and cancellation, and unchanged host canaries. Tool-enabled construction sessions are not adapter qualification. The accepted Producers module is this step's first part, not its completion; still open: a person's completeness audit of the CLI's 5 built-in agents and 2 plugins followed by re-qualification under rules revision 5, and, for OpenAI, a Platform credential plus one bounded live qualification, or instead the Codex CLI route on the owner's ChatGPT sign-in, never a Platform key, which the proposed lifecycle takes as decision 3's default (open) and audits in its increment 7. The DispatchGate a producer needs is not built: the accepted Product gate has no producer dispatch kind (step-1 spec §7 D1 defers it to step 2's composition), and a durable Slice allowance shared across providers is still to come; unknown remote spend stays unknown.
Candidate → VerdictFor the built-in fixtures only: create and hold a fence → provision the workspace → durably record preparation → Execution claims an Attempt → recheck admission → LocalFixtureWorker.runGuarded → Evidence → judge → Verdict local-fixture, with controller-death recovery within Budget. The Confined executor can run an exported repository Candidate (one permanent test), but no Run composes it. Accepted and not composed: Repository Assurance's pure judgeRepository gives local-repository Verdicts over store-written repository records (tests only), and the Product gate admits a check and each of its collector dispatches within the Slice's allowance. Accepted and not composed: the Repository collector runs each check of a sealed test Candidate through the Confined executor and stores one record per run, which its tests judge.Step 1, increment 4: the collector's composition under the Product gate (gate reads, re-exports around a collection, Verdict retention), after the collector's independent review. Proven so far at module level: wrong-Product and wrong-proof rejection, concurrent ownership and revocation. Still to prove in the composition: moved targets and lost acknowledgements.
Verdict → ReceiptRepository prepareIntegration → invokeOnce → IntegrationObservation. After submission, Git exit 0 plus an exact readback of M confirms the invocation; receipt retention is separate, and its failure returns confirmed with receipt: null and a problem; any other submitted outcome stays unknown. Before submission the observation can be not-dispatched, or, from a call given a guard that did not allow the move after the claim, withheld (the claim is kept and reads as unknown afterwards; the guard seam is accepted as increment 3b and not composed). The payload's proof is bound and retained, not checked as a Verdict. Nothing in src/ calls it. Accepted and not composed: the Product gate admits an integrate-local dispatch only after re-deriving the proof's Verdict inside the decision and checking that the Candidate changes nothing outside the Slice's scope, blocks every later integration of the Product while one is unknown, and the delivery records keep the prepared intent and the sender's receipt.Step 1, increments 4–8: delivery composition under the Product gate, passing the guard seam (3b, accepted) a repeat of the final read. Prove it first in a disposable repository, then deliver one evidenced useful local Slice without touching a checked-out branch or unrelated work.
Receipt → AssessmentPortfolio imports release and measurement facts as sourced statements and never sums measurements; the Dashboard's measured value stays unknown. The delivery records' pure deriveAssessment derives supported, inconclusive or rejected from a frozen Slice record, a repository Verdict and the records its judgement reproduces, Verdict-first and never stored; only tests call it.Step 4: collectors only for a real fact the useful Slice needs, keeping correction, deduplication, capture time and coverage; notification state stays derived. Capability requirement and retirement separate from observed health; immutable Outcomes; independently verified Supported, Rejected or Inconclusive Assessments; research and cleanup proposals that enter ordinary verified Slices. Regression restoration, duplicate and crash handling and bounded budgets are proven before any schedule is activated.

Proposed, two parts built: discovery and post-development. The lifecycle design (Astra round 3 PASS on the design only) would add nine modules: scheduled discovery of public Sources, triage into Signals and Clusters, at most five Opportunities a week that a rule-first gate admits, parks or rejects, a weekly owner bundle with single-use Action grants, release drafts with a claim trace, outcome validation and Capability retirement. Increment 0 adds only their planned Intelligence declarations; increment 1 builds the Lifecycle schedule on fixtures (Astra round 4 PASS; nothing composes it); increment 2 builds the Discovery sources collector (Astra round 5 PASS), which runs only against a loopback corpus, and nothing schedules or reads it; increments 3–11 build the rest on fixtures before any live run, and every owner decision in its §11 is open. It depends on step 1's delivery composition (its A12).

5. Cross-cutting invariants every agent preserves

Each line names where the rule already lives, so a change can be checked against it.

  1. Unknown is not zero, and not failed. Portfolio Counts are exact only under declared complete coverage; the Dashboard renders unknown as —; Assurance folds a timeout, signal, collector error or missing reference to Inconclusive; the executor's unresolved and Repository's unknown are neither failed nor done.
  2. A historical Verdict is not current validity. Guarded verification shows historicalVerdict as issued and recomputes currentValidity from the Evidence and the source digests now; any source drift invalidates without rewriting the Run.
  3. SQLite and Git (or any provider) are never one transaction. Journal commands wrap no external effect; Repository's claim, target move and receipt are separate single-ref updates with no crash atomicity claimed; the journal's replay avoids re-running the decision only, so a caller's other effects can repeat.
  4. An unknown effect never permits a blind retry. invokeOnce throws only before submission and never downgrades unknown to "not applied"; after a lost acknowledgement, repeat the same command ID with identical inputs (journal, Execution, guidance, Product gate) and let the replay say whether it committed. The Product gate blocks every later integration of a Product while one is null or unknown, and its REPLAYED answer is history, never a permit.
  5. The implementer is never the verifier, and output has no authority. The worker and the executor observe and never judge; a producer's passed flag, printed control text or exit claim never changes admission or a Verdict; hand-built records never qualify; Opus builds, Astra verifies.
  6. Products stay isolated. One repository binds to one Product; a Candidate manifest includes its repository identity; guidance is never copied between Products; the executor denies reads of another Product's files; usage is attributed once and never moves; the Product gate keys every record, ID and pin by Product, so one Product cannot integrate with another's Candidate, proof or store; Service access (increment 1) binds each repository and vault to one Product and hashes the Product into every reference id.
  7. Everything is bounded; input limits refuse rather than truncate. JOURNAL_LIMITS, EXECUTION_LIMITS, worker and executor bounds, REPOSITORY_LIMITS, PORTFOLIO_LIMITS, GUIDANCE_LIMITS, DASHBOARD_LIMITS, REPOSITORY_ASSURANCE_LIMITS, GATE_LIMITS, RECORD_LIMITS, SCHEDULE_LIMITS and (Service access, increment 1) ACCESS_LIMITS bound what modules accept, and oversized input is refused, not cut short; so does DISCOVERY_LIMITS (lifecycle increment 2), for fetched bodies, items and Captures. Presentation is capped instead: the worker and the executor keep a bounded prefix of process output, report the overflow and request termination (local-worker.ts:601-612, confined-executor.ts:1022-1031); diagnostic and error messages are shortened to fixed lengths (for example DASHBOARD_LIMITS.maxMessageLength); an attention item lists at most MAX_WINDOWS (5) source windows. A Budget is never extended; recovery loops have round counts; there are no leases, heartbeats or takeover by time.
  8. No human gate for self-solvable work. Attention infers nothing: empty guidance, unresolved verification, dirty files and zero counts make no item; ownerRequired needs a source that names the owner and an exhaustion; a missing fact is never an owner approval request.
  9. Never weaken tests, acceptance or pins. Drift blocks dispatch by design; never re-pin silently; never relax a corrupt check; a stored-shape change is a protocol revision, not an edit in place; a reused command ID with other content is a conflict, not an update.
  10. Validate before writing, fail closed, and never adopt, recreate or repair silently. Validation order is module-specific: modules check input before writing, but journal open configures WAL before it detects missing tables (journal.ts:161-162, :354-357; see command journal). Missing or replaced fences and repository roots are refused, never recreated, and the executor quarantines a changed namespace. For journals, openExisting and openReadOnly refuse a missing file while open may create one, and storage identity is not fenced: a valid replacement journal is accepted. Cleanup is non-recursive and anything uncertain is quarantined and reported.
  11. Local proof is not release, and neither is customer value. Fixture recovery is not customer-release recovery; a merge, an upload, a screenshot or a green suite establishes nothing about availability or benefit; all 27 epics stay open until their own evidence closes them.
  12. Plumb is editorial. The character belongs in docs, onboarding and calm empty states; in the dashboard the mark sits beside the wordmark and Plumb appears only on an empty Portfolio that a successful current read confirms is calm (calmEmptyPortfolio in apps/dashboard/src/brand.ts); failures, security, destructive actions, blockers, privacy, spend, uncertain effects, Verdicts and Assessments use plain words, no character, no colour-only meaning (brand guide).

6. Trust scope summary

Condensed from the module references. Every established item holds for trusted local code on one macOS arm64 host with Node 26.8.1, a local POSIX filesystem and process-crash tests.

Established locally

  • Atomic, idempotent journal commands under real process races and SIGKILL on either side of a commit; openExisting and openReadOnly refuse foreign, corrupt, empty or missing stores (open refuses foreign or unknown schemas but creates or initialises a missing or empty one); read-only inspection that initialises nothing.
  • Content-addressed Evidence that survives a killed writer; Verdicts that producer flags and hand-built records cannot forge.
  • One owner per Attempt under racing claims; stale reports refused; durable cancellation decided by commit order alone.
  • Real bounded fixture processes with an explicit environment; fence-held admission; drift refusal; quarantine instead of claimed cleanup.
  • Recovery from controller death at every protected boundary within the pinned Budget, including actual CLI crash recovery, with one fresh payload per epoch and no resurrection of terminal Runs.
  • A stateless sandbox boundary with tested denials of writes, foreign reads, network, subprocesses and signals, judged from host-side evidence with negative controls.
  • Exact sealing and export with fail-closed handling of corruption and forged manifests; one integration invocation per intent across repeats, concurrent processes and kills, with truthful unknowns and read-only reconciliation; a native HEAD.lock tolerated, never taken.
  • Metadata-only registration of six local Products with checkouts left unchanged; globally deduplicated usage; exact, partial and unknown Counts that never collapse to 0.
  • A loopback-only dashboard whose reads never write or run Git, with Host, Origin and token guards, two guarded commands, and checks at desktop and 390 px widths; decorative brand placement tested by the root suite and the dashboard package's rendered read states (no receipt, and no browser check of it is recorded).
  • Revisioned guidance with exact replay, stale refusal and immutable pins; source-derived attention with no inferred gates and no resolve route.
  • Service access increment 1 (test-made fixtures only; provisionally accepted — Fable 5.1 (max) round 1; Astra review pending (Codex usage limit until 2026-10-03 18:00), as above): names-only discovery that opens no deny-listed path, reads no line inside a workflow or env-template value and no text inside a manifest's string, an expression's literal or a comment as a name, records no path, name or identifier shaped like a credential, and, for every position and form its canary, adversarial and regression suites plant, records no value in any journal byte, event, view, answer or error (evidence for those forms, not proof for every construct); absence only from a complete, authenticated, non-empty listing and a use that shows where the name resolves (never from an unknown environment, a reusable workflow or a file not scanned whole), and an environment job's name resolved only through a conclusive listing of its environment; exclusive bindings under racing processes, one repository per Product; readiness that is presence, never validity, and says when only the owner can act.
  • At module level, with no composition: Product gate admissions linearised against revocation in real processes, ownership passed only by fence proof, integration refused unless its Verdict re-derives inside the decision and its Candidate stays inside the Slice's scope, separate allowance counters and refusal of corrupt state; repository-scope Verdicts over store-written records that refuse fixture proof and omitted or forged records; create-once delivery records.

Not established

  • Power-loss durability, other platforms or hosts, network filesystems, multiple hosts, CI, remote or production reliability.
  • Sandboxing of arbitrary code: the fence and worker are not sandboxes; the executor has no hard RAM, disk or CPU-time stop and does not reclaim an orphan after controller loss; Node's permission model is defence in depth only.
  • Authentication or authority: collectors, confirmedBy and provenance are attribution; anyone with write access to the journal, Evidence store, fence directory or repository root can forge consistent records; the Product gate's grants, revocation and owner exist but nothing composes them, its proofs are re-derivation over trusted local files, and it relies on the caller's fence check.
  • Outbox delivery, schema migrations, retention or compaction; background recovery or any automation; blocker rechecks; automatic collection of any source.
  • Any provider adapter qualification (both Producers transports are unavailable), remote spend accounting, planner, or consumer of guidance pins beyond the Dashboard's own reads and edits.
  • Any live credential source: no reader lists a repository's secrets or reads a vault, no registration runbook or broker exists, and Service access's bindings, declarations and pins are attribution, not authentication.
  • A Verdict on a real Slice's repository Candidate (the local-repository judge and the collector exist and are accepted, but nothing composes them), an Assessment of a real Slice, Outcomes, releases, customer availability or customer value.
  • Any lifecycle module other than the schedule (implemented on fixtures, independently reviewed, Astra round 4 PASS, and composed by nothing) and Discovery sources (accepted locally on independent review, Astra round 5 PASS, but tested offline only: its HTTPS transport has never fetched a real page; composed by nothing); the codex-cli transport or its audit, a qualified subscription route for either provider, a LaunchAgent, sign-in from launchd, the owner's signed schedule commission, and any scheduled discovery or post-development run.

7. How agents work here

Commands (Node 26.8.1, npm 11; dependencies are already installed; the root has no runtime dependency, and apps/dashboard has its own manifest and lockfile that the root suite never needs):

npm run check                                   # strict typecheck + node:test suite (the final stage runs it; do not run mid-task unless told)
npm --prefix apps/dashboard run build           # browser typecheck + Vite bundle; restart the server afterwards (assets load at startup)
npm --prefix apps/dashboard run check           # build, then the dashboard package's own tests (apps/dashboard/test)
node src/dashboard-server.ts --journal <local-portfolio-journal> --port 4317   # loopback only; check whether a server already owns the port
npm run factory -- verify pass --run-id demo-pass --state-dir .factory         # fixtures: pass, fail, hang, noisy; then inspect / cancel / recover
rm -rf site && npm run docs                     # regenerates site/ from docs/ (the build only creates files; it refuses a differing site/); commit site/ with any docs change (test/docs-site.test.ts fails on drift)

Focused suites per module are listed under each reference's Changing it safely. Test fixtures use disposable journals and repositories, never <local Portfolio journal> or a registered checkout. Bulky logs and regenerable output go under <local evidence archive>, never /tmp.

Worktrees. Parallel producers work in separate worktrees under <home directory path>, each on its own branch from main. Never edit the main checkout or a worktree another coordinator owns; touch only the files assigned to you; node_modules is symlinked, so install nothing. Preserve dirty work, <local Portfolio journal> and the ignored evidence under <local evidence directory>. Do not commit unless told.

Review protocol. Opus 5.5 implements (exact claude-opus-5-5; report the effort actually used and never substitute a model silently). Fable 5.1 collaborates on documentation, review and composition. Astra (gpt-6-astra) verifies independently, for example codex exec -m gpt-6-astra -c model_reasoning_effort=high ...; the accepted reviews in the build review record Astra at xhigh. Prove each component before composing dependants, and refine an interface when an observed failure reveals a weakness. A module is complete only when independent verification, its docs/agents/<slug>.md reference and its docs/guide/<slug>.md page all exist and the docs site is rebuilt.

Receipts. Each acceptance has a receipt under docs/*-receipt.json, but they are not uniform. Some pin source and test hashes directly (files or final_sources); some hash only review and browser evidence (evidenceSha256, often files under the ignored <local evidence directory>); guidance relies on a source-hash list that is not in Git. Before saying a module's current files are covered, compare its receipt's actual coverage with the files now; each module reference says which receipt still matches. Any edit to a covered file makes that coverage stale: get a fresh independent review, add a new receipt (never rewrite hashes in an old one), and update the build review row, the module's narrative section and its references. Keep old receipts as history.

Local-only authority. Only the coordinator pushes verified main to the private origin; CI runs deterministic checks only, never a model, sign-in or secret. No production change, public release, customer account, advertising or broader credential. Registration permits metadata inspection, not delivery. Do not extract OAuth credentials, infer Platform API access from a sign-in, or substitute a transport or model. Resolve self-solvable blockers within existing authority and keep independent work moving; escalate only a genuinely unavailable decision, access or resource, with what was tried.

Writing. British spelling, the one language above, and the brand voice: warm and brief where calm, plain where it matters. Never overclaim: say accepted local scope, say what is not established, keep unknown distinct from zero, and never call an epic complete, a release safe or a customer helped without the evidence.

8. Design language

Read this before building or changing any UI or docs page. The design system is Factory · Plumb (version 1790613544-ec35), iterated in Claude Design; its link is in the Notion status, not in this repository. docs/brand/tokens.json is the canonical copy of its tokens, byte for byte as exported; design language says where each token goes and how to re-sync.

  • Tokens only. Use the token names and values (--ink, --blue, --space-4, --radius-md, --rail-width, --focus and the rest). Add no colour, family, type size, spacing, radius, shadow or layout size that is not a token, and never edit tokens.json by hand: change the design system, then copy its export over. The design system has no token for a reading measure or a minimum width yet; the docs site names its few such measures once (SITE_MEASURES in the builder), and its only other lengths are 1 to 3 px lines and offsets.
  • Light only. One theme. Add no dark scheme and invent no dark palette until the design system has one.
  • Colour. Flat surfaces: no gradients, glow, texture or raised shadows. blue is for links and the one primary action per view. ok, warn and problem mean status only, on their status grounds, always beside a word and never in artwork; ok is for icons and borders, not text. rail, rule and select are decorative, never the only boundary or state.
  • Type. The sans and mono system stacks, no font files; weights 400, 500, 650 and 750 only; the named type styles (page-title once per view, body, supporting, code and the rest). The wordmark is live text, never an image.
  • Space and shape. Spacing space-1 to space-12 (4, 8, 12, 16, 24, 32, 40, 48 px) with nothing in between; radii radius-sm, radius-md and radius-lg (4, 6, 8 px), and nothing is a pill. Controls are control-height; targets are at least touch-min. At 1280 px and below the rail is 224 px and the inset 32 px; at 960 px and below the rail becomes a top bar and the inset 20 px.
  • Focus. Every control shows the focus ring, the only shadow, on keyboard focus; links and linked rows show a 3 px blue outline.
  • Status. Always a word, never colour or an icon alone. Unknown is an em dash with its reason, never zero.
  • Plumb. Docs, onboarding and calm empty states only, at illustration size, below the words, never replacing them, never clickable, with empty alt text when the words carry the meaning. Never in tables, metrics, navigation (the mark excepted), decisions, blockers, failures, spend or unknowns. The mark is never below mark-min, and the favicon stays favicon.svg. Module plates are shown whole, never cropped; nicknames are captions only.
  • Voice. Warm, brief and curious, in British spelling and the Factory's domain words; plain words for limits, status and anything that matters; no emoji, exclamation marks or hype. The say-and-not table is in the brand guide.

How parity is tested. test/design-tokens.test.ts fails when tokens.json stops being one light theme with system fonts, and reads both stylesheets and the docs' drawings with allow-lists, so a value it does not know fails. The allow-lists cover colour, type, radius, spacing, shadow and outline; custom properties and var(); resets; at-rules; the responsive values at 1280 and 960 px; and the drawings' elements and fills. The design language lists the rules; scripts/render_docs.py draws the diagrams from tokens.json. Its mutation cases show each check failing on altered copies. test/docs-site.test.ts checks the site's status words, notices, Plumb placement and whole plates on every page. After any UI or docs change, run node --test test/design-tokens.test.ts test/docs-site.test.ts and rebuild the site.

Where it is iterated. Visual changes start in Claude Design (link in the Notion status) and reach this repository only through a re-sync of tokens.json, followed by the stylesheets that use it.

Source: docs/agents/factory-model.md