This is the canonical Clockwork commerce backlog. main is the only active
branch. Historical lane names and tips are provenance only. This is not an RC or
launch declaration.
An independent audit re-derived every marker against main at 5cc5fdd on
2026-08-14 and added twenty-five findings, P0-47 onward. Ten work-stream pull
requests have merged since — #7 through #16, sixteen in total on main —
and this file has been rewritten against what they actually did. Read their
commit bodies alongside this file: they are the primary record and several of
them contradict the audit they were answering — but they are not uniformly
reliable in either direction. At least one body records work as undelivered that
its own diff delivered (deal-registration create, in 5897196), and later
bodies do not mention several things their trees shipped (server-side sortable
columns and the customer freshness disclosure, both in 8c53747). Where a body
and the tree disagree, the tree was re-checked and the tree is what this file
records.
Four facts govern how the rest of this file should be read.
First, a substantial minority of the audit's claims did not survive contact with the code — roughly one in five, counting outright refutations together with claims wrong in cause, scope or prescribed remedy. Four were refuted outright: three described the documented contract as a defect and one did not reproduce at all, and implementing two of the three was actively harmful — one opened a privilege escalation, proven against the live database, and one would have rolled back every subsequent order acceptance for a partner. Those four are pinned under "Refuted findings — pinned, do not reimplement", each with the test that fails if it is reimplemented. That section is not optional reading. Roughly the same number again were real but wrong about something material — P0-55's prescribed fix, P0-61's and P0-42's lead claims, P0-59's count, P0-67's money half, P0-68's population, two of P0-48's five diagnoses, and P0-71's prescribed remedy — and each of those entries says so where it says it is closed.
Second, all qualification evidence in this repository is local. GitHub
Actions is billing-blocked on the organization: the last run that executed was
on 2026-08-05, the 2026-08-10 run stopped all seven jobs within twelve seconds
on a billing condition, and each of the ten work-stream pull requests records
that Actions was still blocked and that no CI run was observed. Every figure any
of them reports — the last records typecheck 10/10, test:unit 86,
test:integration 10/10, pgTAP 38 files / 785 assertions, drift, lint,
boundaries, secrets, format, and the figures move with every merge — was
produced on a developer machine. That is a billing condition rather than a
repository defect, and it is nearly the whole of what remains in P0-48, now
[EXTERNAL-ONLY] under EXT-ACC-01: the five repository defects that failed
the last run that did execute are fixed; the billing, one observed green run,
and the branch-protection administration main still lacks are the gate's named
inputs; and the last repository-side caveat — the workflow installed no Poppler
while the semantic-PDF integration test spawns pdftotext unguarded — is now
closed, so nothing repository-side remains behind that gate.
Third, the CI configuration can now run the suite it describes. P0-47 was exact
and is closed: the workflow matrix defines all seven shards
RELEASE_SUITE_NAMES declares, including demo, and
scripts/release-artifacts.test.mjs parses the workflow and asserts the two
lists are equal in name and order. Any statement elsewhere in this file that the
gate cannot be satisfied is superseded by that. Satisfiability was proved
locally by running validate-release-join.mjs over synthetic per-shard
summaries — not by a green run.
Fourth, the "Accepted verification evidence" block below is a local capture
taken at 9464bab on 2026-08-01 and is now stale in more places than the audit
found. It has never been reproduced by CI. Treat it as provenance, not as a
gate; it is regenerated from pnpm generate:baseline in the final commit of
this pass, not edited by hand.
Status markers mean:
[COMPLETE]: repository implementation and direct acceptance evidence are complete;[OPEN]: the requirement is not met in this repository, and closing it is repository work;[EXTERNAL-ONLY]: repository controls, deterministic simulator, fail-closed enforcement, and activation test are complete; only a named external input or live evidence remains.
External inputs never excuse missing repository work. Neither marker fits a
finding that was wrong: [OPEN] means "closing it is repository work", which
for a refuted finding is an instruction to do the harmful thing. Refuted
findings therefore carry no marker and live in their own section, where the
parser cannot see them and a reader cannot mistake them for work.
A green run of a checker proves what that checker checks and nothing more. Where an entry below says something is closed only in part, the part that is open is named.
- P0-01 — Capture every outstanding source change
[COMPLETE]: repeated tracked, untracked, worktree, and ref inventories reconciled every accepted delta intomain; preservation commits and rejected disposable output are recorded in the consolidation report. - P0-02 — Canonical production specification and traceability
[COMPLETE]:commerce_platform_spec.mdand the 312-row checked-in ledger are reconciled against implementation, tests, handoffs, ADRs, launch controls, and external gates with zero unmapped or internally partial requirements. - P0-03 — Single-main history consolidation
[COMPLETE]: the commercial, runtime, and experience lanes were merged with explicit two-parent commits in the required order; their tips and accepted preservation commits are represented bymain. - P0-04 — Clean-main parity and requalification
[COMPLETE]: the pinned Node/pnpm clean-checkout design, generated-drift checks, reset, static, database, provider, document, UI, build, serial/debug, parallel, and stress suites are repository-controlled and accepted. The clean isolated UI regression at9464babpassed Storybook 5/5 and Playwright 79/79 with zero skip, flake, retry, or cache reuse; commit-addressed archival evidence is emitted after the documentation commit. - P0-05 — Recoverable legacy preservation and retirement
[COMPLETE]: the ancestry, clean-worktree, fsck, annotated-tag, all-refs-bundle, restore-drill, and safe-ref-retirement procedure is complete. Final commit/tag/bundle identifiers are necessarily written post-commit to the tag annotation and ignored archive manifest, not represented here as unfinished source work. - P0-06 — Partner order submission
[COMPLETE]: referral, resale, and distributor commands load governing parties and agreements from persisted truth, preserve confidential partner economics, and pass stale/concurrent and legal-party routing tests. - P0-07 — Lifecycle task execution
[COMPLETE]: all 24 lifecycle task IDs execute persisted planners, transitions, provider boundaries, recovery, audit, and outbox behavior. - P0-08 — Default-runtime activation proof
[COMPLETE]: production discovery composes persisted external-gate state, bounded provider probes, durable checkpoints, and crash recovery. - P0-09 — Exception ownership resolution
[COMPLETE]: affected-account queues resolve from the persisted qualified roster with requester exclusion, backup ownership, reassignment, and escalation evidence. - P0-10 — Scheduled core operations
[COMPLETE]: overage, dunning, partner-credit, usage, reconciliation, and report schedules have registered Trigger submitter/outbox paths and recovery tests. - P0-11 — Database gate failures
[COMPLETE]: RLS, canonical fixtures, buyer/MoR identities, migration reset, and full pgTAP pass fail closed. - P0-12 — Repository integration regressions
[COMPLETE]: lifecycle dead-letter and external-gate optimistic-version regressions retain focused integration coverage. - P0-13 — Collections hardening
[COMPLETE]: finance grants are narrow, acting-user audit visibility is scoped, and source currency/order/amount constraints are enforced. - P0-14 — Dependency advisories
[COMPLETE]: the authoring and telemetry dependency chains are updated; the accepted audit has no unexplained advisory or retry-masked provider/build failure. This marker had regressed and was re-earned in0f68e36: the audit found 10 live advisories, 6 high. Upgradinghonoto 4.12.34 and overridingjs-yamlandnanoidcloses 8. The 2image-sizeadvisories closed when the project (now on Codeberg) shipped the fixes in 2.0.3; GitHub's advisory data still reports no patched range, soimage-sizeis overridden to 2.0.4 and pinned by the test. The remaining build-path exception isextract-zipthrough the pinned Netlify CLI's local zipped-function emulator. The deployment workflow does not accept or extract untrusted function archives, and npm reports no patchedextract-ziprelease. It is accepted inpnpm.auditConfig.ignoreGhsaswith a written reason and bound by a test that fails if the manifest silences an advisory with no reason, if a new high advisory appears, or if the package publishes above the version that justified the acceptance. A dependency marker is a claim about a moving external set, so it is true as of the last run and not durably. - P0-15 — Release-gate closure
[COMPLETE]: all repository-controlled frozen-install, static, database, unit, integration, provider-replay, document, migration, telemetry-redaction, security, build, browser, visual, accessibility, and demo-safety gates are represented by accepted evidence. Failed isolated browser diagnostics remain visible, and their replacement pass at9464babexecuted all 79 Playwright tests without retry masking. - P0-16 — Canonical generated artifacts
[COMPLETE]: the current Drizzle authoring snapshot0009_early_vanishercovers 128 modeled tables and OpenAPI exposes 58 paths, including seven experience paths. Reviewed Supabase SQL remains canonical; schema/client generation and drift checks pass, and the disposable generator now fails on error output even when a generator exits zero. - P0-17 — Authoritative order transitions and offboarding
[COMPLETE]: provider-confirmed lifecycle transitions, recoverable cancellation/final billing, and two-person teardown replace direct caller-controlled status changes. - P0-18 — Pre-provisioning collection and partner-credit holds
[COMPLETE]: authoritative holds are locked in the acceptance transaction, denied orders create owned review without provider work, and concurrency cannot exceed credit. - P0-19 — Provider-authoritative credits and refunds
[COMPLETE]: provider acceptance and replay checkpoints precede local issued truth; persisted invoice/payment/currency/amount bindings and aggregate balance caps reject forged or duplicate adjustments. - P0-20 — Stripe adjustment projection correctness
[COMPLETE]: signed provider status and per-adjustment watermarks preserve pending/failure/success ordering and commission compensation without lost events. - P0-21 — Commission settlement integrity
[COMPLETE]: persisted partner and QBO vendor identity bind replay-safe bills/postings, included lines, accrual settlement, audit, and outbox state. - P0-22 — POC partner-relationship authorization
[COMPLETE]: partner identity derives from an approved persisted relationship and cross-tenant POC or evidence access fails closed; success-test targets are nonblank and database-enforced. - P0-23 — Gate-page credential containment
[COMPLETE]: internal gate access is server-bound and exact-origin checks prevent hostile, preview, or malformed origins from receiving a request, session cookie, or CSRF token. - P0-24 — Persistent assisted-session identity and exit
[COMPLETE]: the immutable staff actor, effective account, reason, expiry, exit path, audit, destructive controls, and global banner remain consistent across navigation. - P0-25 — Production identity, audience, and organization routing
[COMPLETE]: AuthKit sessions resolve persisted memberships and actual customer, partner, or internal homes; switching and forged-account denial are server-authoritative. - P0-26 — Authoritative portal projections and record-bound actions
[COMPLETE]: production collections/details/actions use scoped persisted projections, optimistic source versions, durable receipts, authoritative command execution, rematerialization, and replay-safe audit/outbox joins. - P0-27 — Authoritative e-sign completion
[COMPLETE]: persisted agreement, signer, document, opaque return correlation, provider/webhook reconciliation, terminal states, and verified signed-document readback replace query-derived success. - P0-28 — Evidence ingestion and quarantine
[COMPLETE]: durable create, complete, scan/quarantine, immutable promotion, retention, ownership, expiry, replay, and scoped download contracts cover all required journeys. - P0-29 — Complete immutable-document pipeline and delivery
[COMPLETE]: authoritative persisted sources, render-request CAS/redrive, immutable storage, safe public/private retrieval, generated clients, and corrupt/scope denial cover all fifteen artifact kinds. - P0-30 — Server-enforced activation and kill switches
[COMPLETE]: one persisted audited capability matrix is rechecked at command and last-mile provider boundaries; production simulators are forbidden and emergency disable creates no new effect. - P0-31 — Production-shaped browser release proof
[COMPLETE]: the production bundle drives a persisted issued-quote action through dispatcher, authoritative mutation, terminal receipt, audit/outboxes, rematerialized readback, exact replay, and stale-key denial without first-party interception. - P0-32 — Maximum safe test parallelization
[COMPLETE]: isolated shards, maximum-parallel orchestration, readable results, serial/debug parity, stress repetition, cache-disable controls, and 30-minute CI/45-minute local budgets are encoded and qualified. - P0-33 — Production telemetry and live alerting
[EXTERNAL-ONLY]: client, server, API, workflow, provider, webhook, queue, database, and web-vitals telemetry; correlation; redaction; dashboards; alerts; synthetics; runbooks; exporters; and tests are complete. Only selection of the hosted collector/backend, scoped credentials, verified signal/page delivery, and the staging-soak record remain underEXT-ACC-01. - P0-34 — Task-led, brand-distinct frontend redesign
[COMPLETE]: customer, partner, and operator information architecture, state gallery, 320px/reflow, Axe, visual, semantic task flows, and reject-list evidence have no remaining repository finding. Licensed assets remainEXT-BRAND-01; user-entered human design approval is future launch-only and is not claimed or required here. - P0-35 — Exact marketplace quantity boundary
[COMPLETE]: canonical nonnegativenumeric(38,18)strings and lossless last-mile conversion reject numeric, exponent, range, scale, and round-trip loss. - P0-36 — UUIDv7 database generation
[COMPLETE]: RFC 9562 timestamp and independent rand-b mapping, secret-token separation, grants/search paths, defaults, rollback/same-tick behavior, and persisted producers are verified. - P0-37 — Provider callback contract parity
[COMPLETE]: exact marketplace and support provider paths, strict raw verification, durable binding/dedupe, safe metadata, production composition, OpenAPI, clients, and replay tests agree. - P0-38 — Marketplace persistence exactness
[COMPLETE]: PostgreSQLbigintmoney bounds and canonical nonnegative quantity parsing reject overflow, negative, exponent, and over-precision input before persistence. - P0-39 — Populated portal-projection upgrade safety
[COMPLETE]: the exact-toolchain populated upgrade preserves a millisecond-matched source version, strips legacy mutation authority, terminalizes queued actions without inventing replay truth, fails closed on mismatch, restores canonical state, and was accepted in 93.813 seconds. - P0-40 — Authoritative events for six portal aggregates
[COMPLETE]: theagreement,poc,exception_case,approval,provider_operation, andterminationwrite paths appended audit rows without publishing an authoritative outbox event, so those channels stayed empty on every audience whatever the materializer did.lifecycleEventTopicsinpackages/workflows/src/experience/projection-definitions.tsnow registers the topics whose audit rows already bind to an aggregate the state loader can resolve, and a test pins the excluded set: topics bound to a satellite row (agreement drafts, signature envelopes, provisioning attempts, roster entries) would dead-letter rather than populate a channel, so they stay out deliberately. Where a rebinding would have rewritten an existing audit row's identity, an event was added alongside it instead:exception_case.openedbeside the provider-operation event,agreement.executedbeside the envelope events guarded by the insert result, andapproval.decidedoff the returned row.poc.activatedtook its version from the provisioning attempt rather than the POC and is corrected in place; the expiry branch published nothing at all, sopoc.expiredis new. - P0-41 — Commitment ledger write path
[COMPLETE]: the write path exists and is reached from production, and the ledger row that held this open has been re-derived.supabase/migrations/001320_commitment_ledger_write_path.sqladds the correction and allowance-adjustment streams;createCommitmentPeriodis called from the production create path and from renewal;usage_eventsare written under the provider dedup index; anddecideCommitmentOverageandreconcileCommitmentToSourceare reached from production inpackages/db/src/repositories/core/commitments.tsrather than only from a domain test. The verbs are admitted over the API, and P0-54's closure removed the failure that had made every one of those commands dead in production. The marker waited deliberately forSPEC-10-02: flipping a backlog entry toCOMPLETEwhile a requirement row still references it is exactly whatTRACEABILITY_BACKLOG_COMPLETE_STILL_MAPPEDrefuses, and that row was re-derived against this implementation in5c0f727. Spec §4 step 10 and §10. - P0-42 — Missing reporting-layer reports
[COMPLETE]: closed inbd42c1b, and the entry understated one half while overstating another. ARR/MRR already had a function (recurringRevenue); only its catalogue entry and API key were missing. Billing-and-collections and commission-and-settlement were genuinely absent and are now SQL views insupabase/migrations/001396_reporting_layer_completion.sqlwith pgTAP coverage insupabase/tests/1396_reporting_layer_completion.test.sql. The real defect was reach rather than absence: all three were in neither theinternalOnlylist nor the runtime grants, so a tenant holdingreport:read— anowneron a partner account is one — passed the account-scope check and hit a naked42501 permission denied for view core_commission_settlementrather than a typed refusal.001397_report_audience_grants.sqldecides each of the three, andsupabase/tests/1397_report_reach_and_partner_credit.test.sqlpins the grant half against the refusal half so the two cannot disagree again. - P0-43 — Missing exception queues and split queue vocabulary
[COMPLETE]: closed inbd42c1b, and worse than the entry said. There were not two competingExceptionQueuedeclarations but four, one of them a disjoint six-member union sharing exactly one value with the others, so a workflow-raised exception could not resolve an owner from the roster at all. One vocabulary now, bound behaviourally rather than by inspection, with a database check constraint insupabase/migrations/001395_exception_queue_vocabulary.sqland an integration test (packages/db/src/repositories/lifecycle/exception-queue-vocabulary.integration.test.ts) that reads an owner, backup and escalation contact back out ofexception_cases. One overstatement not to repeat: the binding is behavioural, not structural.packages/api/src/routes/lifecycle/types.tsandpackages/db/src/repositories/lifecycle/schemas.tsstill carry hand-written subset literals that import nothing, so a drift there surfaces at runtime through the behavioural tests and the check constraint, not at compile time. - P0-44 — CRM projection has no runtime caller
[COMPLETE]: closed inbd42c1b. The outbox consumer and theaccounts.crm_record_idwrite are composed inpackages/workflows/src/runtime/environment-production-adapters.ts, behindGuardedProviderJsonTransportso the upsert is denied from persistedEXT-ACC-01/EXT-PROVIDER-01state before any network work. Be exact about what that means: the path is present and inert until the CRM provider gate is active, and it is tested against the deterministic fake (packages/db/src/repositories/system/crm-projection.integration.test.ts,packages/integrations/src/crm/crm.contract.test.ts), not against a provider. Live projection remainsEXT-PROVIDER-01. Spec §15. - P0-45 — Tax identifier validation and reverse charge
[COMPLETE]: closed inca70c0ctogether with P0-61, and completed later:ca70c0cgavevalidateTaxIda real production caller inverifyRegistrationTaxIdsand a writer forcore_account_tax_identifiers, but the only production construction ofDatabaseLifecycleCommandRepositorynever passed thetaxport, so every registration carrying a tax identifier threwREGISTRATION_TAX_VERIFIER_UNAVAILABLEeven withTAX_PROVIDER_BASE_URLandTAX_PROVIDER_TOKENconfigured, and that table had no reachable production writer at all. A real caller behind a port nothing injects is the shape this repository has now produced several times, and a green suite did not show it — only reading the composition did. The port is now injected from the samecomposedTaxProvider()instance the finance service receives. Where no verifier can answer, a registration succeeds and the identifier is recorded honestly as unverified —validation_statuspending, nullvalidated_at, never reverse-charge eligible — because a tax identifier on a registration is reference data rather than an authorization decision, and refusing would block every legitimate registration in a jurisdiction where no verifier exists. Nothing downstream mistakes that row for evidence:identityIsVerifiedrefuses any unverified identifier and tax determination selects onlyvalidation_status = 'valid'. Two deliberate asymmetries, both typed refusals rather than bare throws: a provider that affirmatively rejects an identifier answers 422, since that is a wrong value rather than a missing verifier, and a transient provider failure answers a retryable 503, since one retry buys a verified row instead of permanently weaker evidence. Reverse-charge treatment is determined. No rate, country list or exemption rule is written into shipped code, and the mechanism that guarantees it has since changed in a way worth recording, because the earlier phrasing no longer describes the code. Determination was originally a provider port, refused fail-closed behindEXT-TAX-01so that an unconfigured provider could not issue a zero-tax invoice. It is now made by the engine in@clockwork/domainfromcore_tax_rule_booksandcore_tax_registrations. That is a stronger guarantee rather than a weaker one: an optional port is absent in production and silently zero everywhere else, whereas a missing rule book is a refusal that cannot be mistaken for a zero rate. Rates remain versioned data an authority supplies, never literals in shipped code, soEXT-TAX-01still names a real external input — the approved policy — and the port itself survives only as a vestigial optional field, documented as read by nothing, because three compositions outside that lane's scope still pass it. Spec §4, §10 and §19. - P0-46 — Notification delivery record
[COMPLETE]: closed across three commits, and the entry's lead claim was already stale when it was written.supabase/migrations/001330_notification_deliveries.sqlcreatesnotification_deliverieswith recipient array, template, alert kind, provider message identifier, failure code and an append-only trigger, written on every lifecycle notification effect;packages/workflows/src/quotes/tasks.ts:4registersquoteExpiryAlertsTaskon cron45 * * * *. The genuine residue was the §18 API catalogue and the account surface, both closed after it:bd42c1badded the notification operation and preferences with the generated OpenAPI regenerated rather than hand-edited, and5897196added the preferences page atapps/web/app/(experience)/(customer)/account/notifications/, without which this entry could not close. Spec §4, §11, §12 and §18.
These entries were new repository findings from an independent audit of main
at 5cc5fdd. Each named a file and line and survived an adversarial pass whose
default was to refute. Ten work-stream pull requests then implemented them, and
the implementations disagreed with the audit often enough that the disagreements
are recorded here rather than smoothed over: four findings were refuted outright
and are pinned in their own section below, and several of the entries that were
real were wrong about scope, cause or blast radius in ways the closure notes
name.
-
P0-47 — The release gate cannot pass and one browser suite never runs
[COMPLETE]: closed in0f68e36. The entry was exact:RELEASE_SUITE_NAMESdeclared seven suites, the workflow matrix defined six, andvalidate-release-join.mjsrequires the counts to agree before it setsreport.accepted, so the gate job could not pass however green the shards were. The missing shard wasdemo, whose spec is the only coverage of the public demo password gate..github/workflows/ci.ymlnow defines all seven, withdemoon port 32006 pinned tomacos-15becauseplaywright.config.tsrefuses that project off Darwin and its snapshots are darwin-only. The test that was actually missing is the binding: nothing held the workflow matrix andRELEASE_SUITE_NAMESin agreement, andscripts/release-artifacts.test.mjsnow parses the workflow and asserts they are equal in name and order, verified by mutation. The README describes seven shards. A second instance of the same defect class was found and closed ina1f89e6:pnpm test:unitand the releaseunitshard were two independent lists of the samenode --testfiles, so suites added locally would never have run in CI; the release suite now derives that list frompackage.jsonand the derivation is mutation-checked. What this does not establish is that the gate passes: satisfiability was proved by runningvalidate-release-join.mjsover synthetic per-shard summaries, because Actions has not run — see P0-48. -
P0-48 —
mainhas no observed green continuous-integration run[EXTERNAL-ONLY]: the five repository defects are fixed; the observation is not, and cannot be from inside this repository. Fixed in0f68e36, with two corrections to the entry. The heap ceiling was real —NODE_OPTIONS=--max-old-space-size=3072reproduces the SIGABRT locally with 3047 MB live, so the type-aware lint peak is above 3 GB and the CI default is under it; the workflow now sets 6144. The registry was not Docker Hub: "toomanyrequests: Rate exceeded" is ECR Public's anonymous-pull message and the Supabase CLI defaults topublic.ecr.aws, now pinned toghcr.io, whose manifests are byte-identical for the images used. The integration failure was not in the migration fixtures either:supabase/seed.sqlsetinvoices.amount_paid_minorunconditionally and the populated-upgrade drill replays that seed against001230, where the column does not yet exist, so the whole reset aborted on 42703; it is guarded on the column and the drill now runs and accepts. The two proof-shard authorization failures were test fixtures rather than product — hand-seeded partner payloads that did not satisfy the shapedashboardRecord()requires. Theuiclaim and the mobile drawer inside it did not reproduce at all; see the refuted section. Poppler exists on the qualification machine, which is where every recorded run of the semantic-PDF verification has happened, and the workflow itself installed none, whilepackages/documents/src/semantic-pdf.integration.test.tsspawnspdftotextunguarded — so the first observedintegrationshard would have died on a bare spawnENOENTnaming nothing. The shard now installspoppler-utilsand assertspdftotext -vanswers, in a step conditioned onintegrationalone, placed so the failure names itself rather than surfacing as an unreadable test error. That was the last repository-side caveat behindEXT-ACC-01.What remains is external: no CI run has been observed. Actions is billing-blocked on the organization — the 2026-08-10 run (id 31346389931) stopped all seven jobs within twelve seconds reporting failed account payments or a spending limit, and nothing has executed since, so all ten work-stream pull requests were merged without a CI result and every figure they record is local. The
EXT-ACC-01row now names this remainder exactly: Actions billing in good standing on the organization, one observed green run of the release workflow onmain, and branch-protection administration to require that run before merge — none of which this account can supply, since it holds no organization admin.SPEC-18-VAL-02sits behind that gate. The activation test is the run itself:scripts/release-artifacts.test.mjsholds the workflow matrix equal toRELEASE_SUITE_NAMESby mutation-checked parsing, andvalidate-release-join.mjsrefuses a partial shard set, so once billing is restored the observation either happens or fails loudly. No repository-side caveat remains: the Poppler step above was the last one. -
P0-49 — Accepted amendments persist no money
[COMPLETE]: closed inca70c0c, in four rounds, three of which shipped a control that then had to be repaired. All three tables now persist inside the header's transaction, the superseding identifier is set on each superseded order line, and both invoice writers add the net delta. Verified arithmetically against the database: +1 unit at 10000 full-period, 333 of 364 days remaining, 9148 billable, invoice 189148 = 180000 + 9148, run-rate 833; downgrades stay negative and unclamped. Two of the repairs were regressions this work introduced and are worth knowing about.001390's constraint compared the invoice amount against a live sum of amendment deltas whileinvoices_projection_truthfires on UPDATE, so once any amendment landed after an invoice every later update to that row was rejected — including the Stripe settlement projection, which meant a customer paid, the payment could never be recorded, and the invoice went to dunning;001393stores the delta applied at write time so the predicate is invariant over the row's lifetime, and backfills rows already trapped. And validating a new amendment against the current amended state was right, but the fold ran twice and the second fold saw only the order's own lines, discarding revenue from an amendment-added line, so a legitimate line swap bricked the order for every later amendment including ones moving no money; it now folds once. The entry's variance claim was also vacuous —deriveInvoicecompared a NET total against a GROSS amount, so every taxed invoice reported a permanent variance carrying no information — andcore_invoice_amendment_drift(001394) is now the exact tax-free signal at rest. Two modelling gaps this did not close are recorded under the open residue. Spec §12. -
P0-50 — Customer self-service forms demand identifiers no customer can obtain
[COMPLETE]: closed in26ac061. The entry was right and its first fix was not. Fixing the five routes it named left/partner/portfolio/[id]— also named in the entry — still rendering empty required identifiers with Submit posting nothing, plus/partner/brandand/internal/queues/[id]with the same hole, becausecontextwas optional and nothing forced a mount site to supply it. It is now required, so the compiler forces every mount current and future; that was verified by writing a mount without it and quoting TS2741, and by confirming no call site launders the type with a cast or a spread. All ten mounts were then enumerated by grep rather than from a list and driven in a real production build in headless Chromium: zero required, non-readOnly, empty record identifiers anywhere, and no fixture UUID survives in the client bundle.demoValue, theidsfixture map,demoFallbackAllowedand the "Development simulation accepted" branch are gone. Where a route genuinely cannot supply an identifier the surface refuses rather than posting — forcing the disabled control and callingrequestSubmit()directly both produce nothing on the wire, which matters becausecheckValidity()is true in that state and constraint validation alone would not have stopped it.WorkflowRecordContextalso declared fourteen identifier keys where routes supplied five;rowVersionis removed as a concurrency token rather than a record identity, and each remaining key is read by a live branch. -
P0-51 — E-sign binding writes the envelope's version as the agreement's aggregate version
[COMPLETE]: closed in26ac061, having been dropped by every work-stream before it.persistProviderBindingwrote the envelope's row version as the agreement's aggregate version, feeding two counters into one sequence that000001_foundation.sql:869declares unique. The entry named one site; there are two occurrences and only one is a defect — the other is a genuine organization aggregate and is left alone. It is also worse than the entry said: the envelope is always inserted at 1 and this is always its first update, so it always reaches 2 while the draft is already at 2, meaning every production signing session hit the 23505, not only the counter-signed flow. That was confirmed by reverting the fix and capturingduplicate key value violates unique constraint "audit_aggregate_version_unique"with params showingagreement,…,2. The class had no test at all; it now has five. Note the reason this guaranteed collision was never observed in practice:create_signature_envelopecannot execute at all on a tenant route, which is recorded under the open residue. -
P0-52 — The invoice payment surface is bound to one fixture invoice
[COMPLETE]: closed in26ac061.PaymentHandoffposted a literal account and invoice for every invoice a customer opened and rendered one amount and due date as markup; the session is now opened for the record on screen, confirmed off the wire in a production build. One caveat on the evidence: the demo store holds exactly one open invoice whose amount and date match the deleted literals, so screen text proves nothing here and only the request body discriminates. -
P0-53 — Nothing prevents several full-value invoices for one order
[COMPLETE]: closed inca70c0c.mutateInvoicetook a caller-supplied identifier and plain-inserted at the full quote total with no uniqueness guard, while the workflow writer used a deterministic identifier withonConflictDoNothing, so two posts with fresh idempotency keys produced two full-value invoices, each independently issuable and each unlocking a fresh full-invoice credit allowance. The two writers now agree. What was actually done is narrower than the entry's prescribed fix and is recorded as what it is: there is no database unique index oninvoices(order_id); the guarantee is the shared deterministic identifier both writers derive, enforced through the primary key. Adequate, and not the index the entry asked for. -
P0-54 — Commitment and price-book commands set the service role on the tenant connection
[COMPLETE]: closed inea4ab89. One call site now usespricingDatabase. It was deliberately not fixed by grantingclockwork_runtimemembership inclockwork_service, which the entry also warned against:app_is_internal()is literallycurrent_user = 'clockwork_service', so that grant would hand the tenant pool a standing exit from row-level security. Proved at the HTTP boundary against two real Postgres logins with genuinely separate role membership, because the existing price-book integration test passesdatabase: db, pricingDatabase: dbover one superuser connection and therefore could never have caught it. The guard that now watches for recurrence is weaker than it looks and is recorded under the open residue: it is a deny-list of one role name, and the static scan matches two literal spellings only. Spec §18. -
P0-55 — Every WorkOS webhook delivery fails verification
[COMPLETE]: closed inea4ab89, and the entry's prescribed fix was wrong and is not applied.@workos-inc/nodeis pinned to 10.9.0, which decodes aUint8ArraythroughTextDecoder, so passing raw bytes is correct. The real defect wastolerance: 300against an API that counts milliseconds, so every delivery was rejected unless it arrived within 300ms and no role-synchronisation event was ever ingested. Fixing that exposed two further holes, both found by attack rather than review: a blankt=was accepted forever, becauseNumber("")is 0, which a future-bound waves through, whileparseInt("")is NaN, so the SDK's past-bound never fires either; and the header timestamp was validated by key while both SDKs select it by position, never checking the field is namedt, so givenx=<far future>, v1=<signature over it>, t=<now>the two readers disagree and this validated the fresh field while the SDK authenticated and range-checked the far-future one. Both parses now select the same bytes. The class had no test at all; it now has 23. -
P0-56 — Operator webhook replay clears the processed marker and enqueues a task no worker implements
[COMPLETE]: the registeredwebhook-replay:v1task now executes the stored verified payload. The command serializes on the inbox row, retainsprocessedAt, leases failed/unprocessed rows against provider redelivery, deduplicates an active replay, and atomically writes the workflow run plus audit/outbox request. The queue receives only the workflow-run ID. The service-role worker binds that run back to provider, event ID, hash, and claim token before applying the same Stripe, WorkOS, e-sign, provisioning, marketplace, or support projection used by ingress. A live HTTP-ingress lease is neither stolen nor shown as stopped work. Retry, crash recovery, expiry replacement, and completion use one inbox-then-run lock order and remain fenced to the Trigger run and rotated claim token. A final failure releases a failed inbox with operator-safe text but preserves an original success timestamp/error state. The database integration test proves the payload stays out of the queue, expiry races do not deadlock, and a stale completion cannot close the run.A separate composition defect on the same surface —
webhook-replay/actions.tscomposingrequiredTaxProvider(), which throws at composition, so an unwiredEXT-TAX-01refused every replay before this earlier refusal could even run — was fixed in5c0f727: the action now composescomposedTaxProvider(), which refusescalculatewithout detonating the composition, verified at the emitted-JS level of a production build. -
P0-57 —
mfaVerifiedrecords an environment allow-list, not a second factor[COMPLETE]: closed inea4ab89. The boolean tested whether an organization is configured to require a second factor rather than whether this session completed one, and it was persisted toexperience_projection_action_requests.mfa_verifiedand re-trusted by the asynchronous executor without recheck. The assurance claim is now read directly from AuthKit and the organization allow-list is only a further restriction. -
P0-58 — Ten lifecycle tasks are never enqueued and execute inside the outbox cron
[COMPLETE]: closed inbd42c1b. A real submitter is passed, so the retry policy and per-task queue configuration are no longer inert and one slow provisioning call no longer starves every message behind it. Two things about the closure are worth keeping. The binding assertion is behavioural: the event-driven and scheduled partition is derived from which SDK factory each module actually called, so a fictional registry entry cannot satisfy it — which matters because three separate attempts at a binding test elsewhere in this pass were each evaded by a declaration standing in for an implementation. And carrying the invocation key across the queue hop turned out to be load-bearing rather than cosmetic:dead-letter-dispatchjoinsworkflow_runstooutbox_messagesonoutbox:<id>, so without it every operator redrive of these ten would have returned unmapped. Spec §18. -
P0-59 — The portal offers commands that are not implemented
[COMPLETE]: closed inca70c0c, and the count was wrong in the safe direction: six advertised verbs threwINVALID_STATE, not five —mutateInvoicenever handledissueeither.quotes:reviseis implemented; the rest are withdrawn from both the projection definition and the service catalogue, so the advertisement and the implementation now agree. Three attempts at a binding test were each evaded by declaring a verb rather than implementing one — the same defect class as P0-47 — so the binding that shipped invokes all 45 advertised verbs through the repository and requires something other than an unsupported-transition refusal, which a declaration cannot satisfy. -
P0-60 — Updating a procurement profile erases its certificates
[COMPLETE]: closed inca70c0c.mutateProcurementreadinput.actiononly to reject a duplicate create and then rebuilt the whole row from the payload, defaulting every absent key, so adding one exemption certificate deleted every previously validated certificate, resetpoRequiredand emptiedsupplierDocuments. It now branches on the action and patches present keys. Spec §4, §19. -
P0-61 — No tax is calculated or stored anywhere
[COMPLETE]: closed inca70c0cand then repaired in5897196, and the entry's lead claim was wrong. Tax columns already existed on threecore_tables, written as a hardcoded zero. What was true is thatinvoiceshad no tax column,TaxPort.calculatehad zero callers, and the provider composition had no tax slot; all three are closed. No rate, country list or exemption rule is written into shipped code — acceptance in a tax-bearing jurisdiction fails closed behindEXT-TAX-01rather than issuing a zero-tax invoice.The repair matters more than the closure. The first shape of this gate made the whole
DatabaseCoreFinanceServicecomposition conditional onTAX_PROVIDER_BASE_URLandTAX_PROVIDER_TOKEN, which existed nowhere in the repository, soPOST /v1/core/commands/*answered 500 "Core-finance route dependencies are not configured" on every surface, customer and partner alike, before permissions, payload or row policies were consulted. Six pull requests merged green over that, and the demo would have deployed with every core command dead. The entry asked to gate acceptance in tax-bearing jurisdictions; it did not ask for the service to be decomposed. The gate now sits where the risk is: an unconfigured provider refuses rather than vanishing, and the refused set is exactlyorders:createandinvoices:create— the only two verbs that reach a tax determination and the only placetax_minoris written.GET /v1/core/statuswent from{"service":"missing"}to{"service":"database"}.TAX_PROVIDER_*is in.env.exampleand the deploy runbook, with the documented-environment fingerprint recomputed from 95 to 97 variables. One consumer of that composition was left broken and is recorded under the open residue. Spec §10, §19. -
P0-62 — The partner quote builder carries a fabricated deal, a fixed partner identity, and an expiry default that lapses on 2026-08-31
[COMPLETE]: closed in26ac061, with the write path finished in5897196. Identity and options now resolve from the session, and a two-partner crossover check shows zero leakage in either direction. The expiry default is derived rather than hardcoded and is still correct with the clock pinned to 2031.The builder could not actually complete a write when
26ac061shipped, and that was recorded rather than hidden;5897196found the cause and it was structural.persistQuoteDraft's INSERT intoquotesusedRETURNING, and Postgres requires the new row to satisfy the SELECT policies.quotes_readadmits a partner only throughcore_quote_is_visible, a SECURITY DEFINER function that re-queriespublic.quotesby id — and inside the insert's own statement snapshot the row does not exist, so 42501. A direct customer quote succeeded because the inline arm evaluates against the new row itself. The fix dropsRETURNINGand re-reads in a following statement, the patternaudit-outbox.ts:65already documents for the same reason. The migration alternative was declined: it would have widened partner SELECT visibility as a side effect of fixing a write. This was the third instance of one class in this pass, after P0-54 andcreate_signature_envelope— a write path composing a database interaction the row policies refuse — and it survived because the only success-path partner-quote test ran againstMemoryCoreFinanceService, which has no row-level security and noRETURNING, while every database-backed partner test is a rejection case that dies before the insert. There is now an integration test that drives a partner quote under a real signed authorization context with policies live and reads the row back. One residue found later and not fixed here: the customer quote builder still ships fixture selector UUIDs in the client bundle — the partner builder's fix did not cover it, and P0-50's bundle claim was about the ten workflow mounts, not this surface. Recorded under the open residue. -
P0-63 — Internal surfaces render fixtures beneath explicit provenance claims
[COMPLETE]: closed in5897196, and the closure is split two ways because the honest answer differs per surface. Collections, provisioning and reports now read the channels the materializer was already producing. Renewals has since been wired to the internalorderschannel — renewal work rows are orders, and the page says so — superseding the honest-unwired state this entry first shipped for it. Migrations still has no backing channel at all and still says so, rather than printing a freshness claim over a constant — which is the entry's own remedy ("delete the freshness and source strings and render an unmissable unwired state"), not a wiring. The false provenance strings — "Collections ledger refreshed 3 minutes ago", "Operational snapshot refreshed 4 minutes ago", "operations · live provider state" — are gone from all eight pages, which was the aggravating factor the entry identified. Two loose ends were found while doing it: the collectionsWorkflowPanelbranch, reported as retired by an earlier round and still standing, was sending a payload shape a.strict()schema rejects, so it could never have written either, and is now gone; and addingservices/[id]activated dead code innextStep's services branch, which linked to/account/offboardingwith the aggregate id while offboarding selects onrecordKeyfrom a different channel — byte-identical pages with and without the parameter. That fix is bound by a test constructing the materialized payload shape, because under the demo adapter a record's id is its record key, so a lazy fix would have looked correct in a production build and still been wrong. The collections corrections surface named in P0-68 is a different thing and was delivered separately; see that entry. -
P0-64 — The downloadable CSV escapes fewer formula prefixes than the workflow export, and the test that proves otherwise runs against dead code
[COMPLETE]: closed inca70c0c. The API download escaped/^[=+@-]/while the workflow export also escaped whitespace-prefixed formulas, and the test establishing formula safety exercisedtoCsv, which nothing but that test references. Collapsed onto one writer with the assertion moved onto the HTTP response body, including the whitespace-prefixed case. Spec §17. -
P0-65 — Unauthenticated idempotency records share one namespace and replay whole responses
[COMPLETE]: closed inea4ab89. Every caller of the one route reachable without a session shared the scopeanonymous:/v1/lifecycle/registrations, and the store replays full bodies for twenty-four hours, so reusing another registrant's key returned their account, organization and user identifiers. Anonymous callers no longer receive a cached body and the scope is request-bound. Transient failures are no longer cached for a day either, which also closes the P1 entry on that. -
P0-66 — The telemetry ingest is unauthenticated, unmetered, and accepts caller-chosen trace parentage
[COMPLETE]: closed inea4ab89after three successful attacks, two on the first control and one on its repair. The ingest now requires a signed grant and is metered, caller-supplied trace parentage is refused, and the error bodies are RFC 9457. The attacks are the evidence that the current shape is not the obvious one. The grant was first minted from the proxy's own span — but that span adoptstraceparentfrom the request, so the server was signing the trace id the caller named, andcurl -H 'traceparent: …aaaa…' /demoreturned a valid grant for that trace with a forged span on it accepted. The meter keyed on a session the demo resolver fabricates from the caller-suppliedx-clockwork-personaheader, giving nine rotatable buckets. And the repair's grant-signing key fell back toCLOCKWORK_DEMO_ACCESS_PASSWORD, the password handed to every demo visitor, so a visitor who knew it could mint a grant naming any trace — proved end to end against a production build. That fallback is removed, the key floor is 32 bytes, andCLOCKWORK_TELEMETRY_INGEST_SECRETis documented. -
P0-67 — Currency and money sign are unconstrained on ten commerce tables
[COMPLETE]: closed ina1f89e6. The entry's currency half was exact; its money half was wrong in three places, and the corrected set was re-derived against the live schema before anything was written. Nineteen tables gain a vocabulary check reusingaccounts_currency_check's exact('USD','EUR','GBP')list rather than a second one — the ten the entry named, includingprice_books, plus ninecore_*tables it missed. Every writer was traced first: the Stripe, marketplace, commission and allocation paths all normalise or parse throughCurrencySchemabefore insert, so no live writer can produce a value the check rejects.The three money corrections are the reason a blanket fix would have been wrong.
commission_accrualsis not touched — its money columns already carry a deliberately directional check (>= 0for payment andcredit_note_voidsources,<= 0for adjustments) and a blanket>= 0would have broken clawbacks.quote_linesandrate_cardswere already half covered, leaving onlyline_total_minorandfloor_price_minoropen. Andamendment_lines.price_delta_minorstays signed: it is a delta, andpackages/domain/src/core/amendments/index.ts:156requires a downgrade's quantity delta to be negative, so a non-negativity check would reject correct data. Thirteen columns gain a check — an earlier count of eleven here omitted the constrained pair onorder_lines— and the pgTAP proves enforcement rather than existence — every assertion attempts a violating write and requires it to be rejected, and dropping the constraints on a live database turns 26 of them red. Spec §18, §19. -
P0-68 — Three customer and partner journeys have no working path
[COMPLETE]: two of the entry's named journeys were already delivered when it was written, one of its claims was stale that same day, and the journey that really was broken turned out to be worse than any phrasing this entry used. All of it now closes.recordRouteis closed. It returned/servicesfor the services channel with noservices/[id]route, so every row on the live-entitlements surface linked to itself; the route exists andrecordRoutenow returns a typedRoutealias rather thanstring, with theas Routecasts gone. Be precise about what that type buys, because the commit that shipped it was:RouteImpl's first three members are unconditional, so the alias does not bind the channel set to the route tree — the original defective body recompiles clean under the new type. A unit test holds that direction, and the code comment now says so instead of promising a guarantee the compiler does not give."Nine workflows mounted on no route" was wrong as one population. A design review established it is two: seven are superseded, because purpose-built record-bound surfaces already exist for quote, agreement, order, offboarding, payment, pricebook and assisted, so those panel branches are deleted rather than mounted. Each supersession was verified against its replacement first and none lost a live capability. Mounting them would have built seven duplicates alongside the real surfaces, and leaving them standing is what made one cause present as nine findings.
Deal-registration create is delivered, and the claim that it was missing was stale on the day it was written:
5897196— the same commit whose own body records it as undelivered — mountsDealRegistrationon/partner/registrationsbehindpartner:quote:write, with identity derived from the session, the end-client roster read under row-level security rather than a picker over fixtures, an empty roster rendered as a named state instead of a Submit over nothing, anddeal_registrations:createimplemented in the finance repository alongsideapprove,reject,extendandconvert. The collections corrections surface is likewise delivered in that commit:apps/web/src/features/internal-ops/collections-corrections/is wired into/internal/collections, and its three corrections map onto implemented verbs —credit_notes:issue,refunds:submit,disputes:create.The third journey was worse than the "two-pass refresh gap" this entry used to call it: no customer could complete an order acceptance at all, and the live database proved it — every order in the table carried its fixture's acceptance instant, so the journey had never once completed anywhere. The binding half was already sound: the prepared order form is hashed over one acceptance instant and the create pass refuses a different hash. But
mutateOrder's create branch also requiredDate.parse(command.acceptedAt) === Date.parse(input.occurredAt), and for any HTTP calleroccurredAtis the API's own receive instant, while the surface must resend the prepare-passacceptedAtprecisely because the hash covers it. Two correct controls, mutually unsatisfiable.The repair makes
orders:createbehave the way its siblings already did. Amendments — the two-pass resource that moves real money through deltas — andquotes:issueboth take a documentary timestamp with no clock check, relying on the hash binding alone;orders:createwas the only member of the family with a clock equality stacked on top of the binding it duplicated. A sweep of every timestamp comparison in the database, API and domain packages found no other instance, so this generalizes to nothing.acceptedAtis now documentary and carried from prepare to create; expiry is enforced againstoccurredAt, which is what the refusal message always described; and the four operational fields that had been quietly drinking from the merged value are keyed on server time. One of those matters on its own:boundAtresolves the selling entity by UTC date, so a documentary instant straddling midnight would have let the buyer influence which legal entity sells them the order.Two things worth recording because they nearly went wrong. Deleting the equality alone does not fix this — it trades the clock refusal for
COMMERCIAL_ARTIFACT_BINDING_INVALID, which would have been the fourth consecutive narrower instance of the same defect on this one bridge; the regression test is pinned against that reversion specifically, not only against the original. And an "acceptance cannot predate quote issuance" lower bound is wrong here however obvious it looks: the deployed fixtures accept before they issue, so adding it fails the suite wholesale.The test this needed did not exist in any form. Every prior integration test pinned
occurredAtequal to the fixtureacceptedAtand every surface test stubbed the send, which is exactly why a journey that had never worked looked covered. The new one drives both passes with a real clock gap between them and asserts the order carries the documentary instant from pass one alongside server-time facts. Spec §4, §8, §12, §14. -
P0-69 — An error on any partner or internal route destroys the application shell
[COMPLETE]: closed in26ac061. Per-group error boundaries mean an error on a partner or internal route no longer replaces the whole shell, andloadCommercialRecordnow distinguishes absent-or-foreign from malformed, which makes the "Record not found" branch that was already written reachable. One design point that reads like an oversight and is not: a foreign identifier and a nonexistent one are deliberately indistinguishable from outside. That is anti-enumeration. -
P0-70 — Migration start is reachable by a customer owner or admin
[COMPLETE]: closed inea4ab89. The route required onlydestructive:request, whichownerandadminhold, with no staff check at the route and none instartMigration, so a customer owner could drive an authenticated outbound request into the legacy source before any row policy intervened. Gated at both layers. Spec §21. -
P0-71 — The traceability ledger cites unreachable code as evidence
[COMPLETE]: delivered with a narrower, enforceable claim than the prescribed remedy. The prescribed rule, "every cited symbol resolves to a definition reachable from production", was tested before implementation against private helpers it would have called dead even though their declaring modules genuinely call them. Whole-program reachability also requires an agreed production-entrypoint set the repository does not define. The gate therefore claims syntax-resolved implementation use, not entrypoint reachability or semantic sufficiency.What shipped instead, in
5c0f727and wired into the gates in8c53747:scripts/validate-traceability.mjsnow enforces a citation grammar — apath#symbolcitation must name a file that exists and a symbol declared in it, a path citation must exist on disk, prose is grandfathered and the run's own report says so out loud — running insideverify:staticon every ledger change.scripts/check-citation-liveness.mjsreports cited symbols with no non-test reference outside their defining file. The rows the entry named were re-cited to the evidence that actually ships — the capacity and scorecard reports are the SQL viewscore_capacity_planningandcore_weekly_scorecard, and those rows now cite the views and name the dead citations they replaced. And the tooling's own tests, which on landing were run by no gate becausescripts/is not a workspace package, were wired into roottest:unitin8c53747.The liveness half now earns its required-gate position. TypeScript and JavaScript citations resolve through a syntax-aware import/export binding graph; comments, strings, unused import/re-export bindings, overload declarations, lexical shadows and unrelated same-name declarations do not make a dead symbol look live. SQL citations use a deliberately narrower comment-stripped lexical check. Regression tests hold each prior false-green mode, private in-module use, test-only use, exception expiry and the real ledger.
verify:static, the release static shard and the baseline qualification inventory all runpnpm check:citation-liveness; the release assertion inventory binds the command to the shard. The improved gate found one genuinely test-only workflow citation inSPEC-16-11, which now cites the production-usedexceptionTaskIdsinstead. The grandfathered prose corpus still converts row by row and whole-program reachability remains an explicit non-claim; neither makes this executable liveness gate report-only. No hosted CI has executed it, which remains P0-48's concern rather than this entry's.
The 2026-08-14 audit re-derived every marker and recorded ten corrections. Each has been re-checked against the current tree, because a correction can go stale the same way the marker it corrects did, and six of the ten now have. What follows is the surviving set; the corrections that are now spent say so rather than being carried forward.
-
P0-02
[COMPLETE]now has zero unmapped or internally partial requirements. The twelvepartialrows the audit found all pointed at P0-42 through P0-46 and were re-derived in5c0f727against what those closures actually shipped, withpath#symbolcitations the validator now checks; the two rationales the audit had already caught as stale (SPEC-10-02's andSPEC-18-API-08's) were rewritten with the rest.SPEC-08-DOC-04returned toimplementedwhen P0-68 closed;SPEC-18-INV-06returned when P0-56 delivered durable replay; andSPEC-18-VAL-01returned when P0-71's citation-liveness checker became a required static and release gate. The row count and specification hash check out. Hosted CI observation remains the external P0-48 gate rather than an internal traceability gap. -
P0-04 and P0-15
[COMPLETE]rest on evidence captured before the tree changed, and the drift is now much larger than the audit measured. The cited commit9464babis real and dated 2026-08-01; two experience-wide redesign merges landed on 2026-08-02 and rewrotepackages/ui/src/styles.css,packages/ui/src/components/shell.tsx, all four story files and fourteen visual baselines. Ten further work-streams have landed since. The audit measured the gap againstdocs/baseline/tests-baseline-artifacts.json, but that manifest is itself a capture —capturedAtis2026-08-02T20:06:37Z— and it is now stale too: it recordspgTapFiles: 22andpgTapPlannedAssertions: 488where the tree holds 38 files and the last work-stream reports 785 assertions, andtotalFiles: 269against a tree that has grown since. So the entries disagree with the manifest, the manifest disagrees with the tree, and neither figure should be quoted. Both markers need re-earning from a fresh capture, not re-argument. Regenerate withpnpm generate:baseline. -
P0-16
[COMPLETE]still names the wrong generated artifact. The entry cites Drizzle0004_nosy_valkyrieand 116 tables;packages/db/drizzle/meta/_journal.jsonhas nine entries with head0008_complete_captain_stacy, andmeta/0008_snapshot.jsonholds 118 tables. Verified again against the current tree, unchanged. The OpenAPI half was correct at the audit and has since moved —bd42c1bregenerated it to add the notification operation — so the recorded path count and hash are now stale as well. Both halves need regenerating rather than editing. A genuine improvement landed underneath this entry ina1f89e6:scripts/check-schema-drift.mjsnow compares the Drizzle model against the live applied schema — table presence, column presence in both directions, and nullability — and states in its own output what it does not cover, running inverify:databaseand in the release integration shard, the only shard with an applied schema to compare against. That checks the model against the database; it does not check this entry's recorded hashes against the tree, and nothing does. -
P0-39
[COMPLETE], "accepted in 93.813 seconds": half of this correction is spent and half stands. The audit said the drill was broken onmain. It is not any more —0f68e36found the real cause (supabase/seed.sqlsetinvoices.amount_paid_minorunconditionally while the drill replays that seed against001230, where the column does not exist, aborting the whole reset on 42703), guarded it on the column, and the drill now runs and accepts;a1f89e6andca70c0cboth re-ran it. That half is withdrawn.The fixture criticism stands, re-checked against
scripts/qualify-populated-upgrade.mjs:loadLegacyFixturestill inserts one account, one projection, one outbox message and two action requests, and the drill still measures elapsed wall-clock time and nothing about locks. So the 93.813-second figure is a timing of a nearly empty table, and the entry's claim to establish populated upgrade safety is not supported by what the script does. This is left as an honest note rather than papered over: fixing it means a fixture with enough rows to make lock behaviour observable and an assertion about locks rather than seconds, which is real work and is not claimed here. -
Spent corrections, recorded so they are not re-derived. Six of the ten no longer describe the tree and are withdrawn rather than carried: P0-34's mobile drawer, which did not reproduce and is pinned under refuted findings below; P0-46's lead claim, now folded into that entry's closure; P0-41's, spent in the other direction — its lead claim had already been overtaken by
001320and the production write path, the entry was held open only bySPEC-10-02's live ledger reference, and that row was re-derived in5c0f727, so the entry is[COMPLETE]above; and the P1 outbox and dead-letter recovery surface and the P1 derivation view, both of which the audit had already marked resolved and whose caveats — that P0-56 made the replay control report success without doing anything, and that P0-49 made the derivation view report every amended order as unamended — are now both fixed. The P1 runbook correction is spent too, in the good direction: all six named runbooks now have operator surfaces —6cf5f26added/internal/unhandled-errorsand/internal/billing-reconciliation, the residue this correction carried — while its narrower point survives and is restated where it belongs: the finance-lifecycle review action atapps/web/src/features/internal-ops/finance-lifecycle/review-action.tsxis still review-only and applies nothing. -
The "Accepted verification evidence" block is stale in every figure the audit named and several it did not. The dependency-lock hash, the canonical Drizzle SQL and snapshot hashes (still those of
0004_nosy_valkyrie, four migrations behind the journal head), the pgTAP figures (16 files / 431 assertions recorded, 22 / 488 in the last manifest, 32 / 687 in the tree), the OpenAPI hash (moved when the notification operation was generated) and the Playwright and Storybook figures are all wrong. Regenerate the block frompnpm generate:baselinerather than editing it by hand, and stop reading it as a release gate — it is a local capture. It is left unedited here so it is regenerated once, from the tree, in the final commit of this pass.
These four were reported as defects and are not defects. Three describe the documented contract, and implementing two of them caused real harm before being reverted: one put a privileged role on a customer organization's membership row against the live database, and one would have rolled back every subsequent order acceptance for a partner. The fourth did not reproduce at all.
They carry no status marker on purpose. [OPEN] is defined at the top of this
file as "the requirement is not met in this repository, and closing it is
repository work", which for these four is an instruction to do the harmful
thing; and deleting them loses the warning, which is the only thing of value
they now carry. This section is deliberately outside the format
scripts/validate-traceability.mjs parses, so nothing here can be mistaken for
a work item by a tool or by a reader.
Each entry names the claim, the authority that makes it the contract, what actually happened when it was implemented, and the test that fails if it is reimplemented. The last of those is the real artifact: the warning is only as durable as the assertion behind it.
-
WorkOS role synchronization — "role changes are acknowledged but never applied."
- Claim.
packages/db/src/repositories/webhooks.tsnever writesmemberships.role, so an identity-provider demotion leaves the old role in force. - Authority.
docs/adr/0004is Accepted and says it twice: "WorkOS role slugs never grant commerce roles: membership webhooks link or revoke identity records, while commerce approval remains authoritative", and "WorkOS role webhooks synchronize identifiers into commerce; they do not grant access without a matching commerce membership."packages/domain/src/identity/index.ts:138carries the same contract in code. Membership webhooks link or revoke identity records; commerce approval decides roles. - What happened when it was implemented. Confirmed against the live
database, not argued: writing the slug put
internal_operatoron a customer organization's membership row.packages/db/src/repositories/identity.tsselectsmemberships.role(and every role inmembership_roles) straight into the session identity throughresolveWorkosIdentity, soevaluateMembershipPolicy— which is what refuses onCOMMERCE_APPROVAL_REQUIRED,STAFF_BOUNDARYandMFA_POLICY_REQUIRED— never runs at all. An exploitable privilege escalation, granted from outside the commerce boundary. Reverted inbd42c1b. - Pinning test. Reimplementing this fails
packages/db/src/repositories/webhooks.test.ts:111, "WorkOS membership webhooks never writememberships.role" — four cases: a role-change event must updateworkosMembershipIdandupdatedAtand nothing else; every slug spelling includingInternal-Operatorandsuperusermust be ignored; no event may insert, change or delete a row inmembership_roles, where a person's additional roles live; and revocation must delete the membership rather than downgrade its role. It also failspackages/db/src/repositories/webhooks.integration.test.ts:113, which asserts the same four against a real database.
- Claim.
-
Outbox dead-lettering — "messages with no registered handler are never claimed, retried or dead-lettered."
- Claim.
packages/workflows/src/system/outbox-dispatcher.tsfilters claims to registered topics, so an event whose handler is missing accumulates silently rather than surfacing. - Authority. The premise is false. The outbox is the durable delivery and
evidence record for the whole audit log, not a work queue: per ADR 0005
audit-outbox.tsappends anoutbox_messagesrow inside the same transaction as everyaudit_eventsrow and defaults the topic to the event type. The dispatch map is the exception, andlifecycle-task-dispatch.tssays so outright — "Only domain events with an immediate task have outbox dispatch mappings." Measured on a reset database: 82 distinct topics and roughly 250 unprocessed rows against a production handler union under 40, withcore.accounts.create,core.quotes.create,core.orders.create,account.registeredandsecurity.assisted_action.*all legitimately unhandled. - What happened when it was implemented. Dead-lettering the unrouted rows on
a one-minute cron at
maxAttempts8 marches every audit-only row up the eight-attempt ladder, writes aworkflow_runsrow per message, stampsOUTBOX_DISPATCH_DEAD_LETTEREDon rows working exactly as designed, and mutatesattempt_countandavailable_aton the audit stream's own delivery record. It buries the dead-letter surface the runbooks depend on under hundreds of rows that are not incidents — the recovery surface added for the P1 operator entry becomes unusable in the incident it exists for. Reverted inbd42c1b. - Pinning test. Reimplementing this fails
packages/workflows/src/system/outbox-dispatcher.test.ts:233, "never claims outside its registered topics, leaving audit-only rows untouched". The mock returns a stranded audit-only row for any unfiltered claim, so a dispatcher that surveys the table to find things to fail on trips all three assertions: the run must report{ delivered: 0, idle: true }, every claim must carry the registered topic list, andfailmust never be called. The contract and its reasoning are written out at:209-232above the test.
- Claim.
-
accounts:set_partner_credit— the one-sided credit-limit write.- Claim. The audit's P1 entry that
accountscommands other thancreateare "accepted, audited as successful, and do nothing" was correct, and the four settings verbs now write. What is refuted is the obvious implementation of this one: setting the partner credit limit by writing the limit. - Authority.
core_validate_finance_chainrequires a partner account's approved credit limit oncore_account_commercial_profilesand theaggregate_credit_limit_minoronaccountsto be equal, and the constraint trigger fires on the profile. So a write that moves one side succeeds — nothing checks it at the time — and leaves the invariant broken behind it.supabase/seed.sqlseeds the profile from the account column for exactly this reason. - What happened when it was implemented. The one-sided write moved the
account aggregate to 6000000 against a profile approved at 5000000 and
returned success. The next statement to touch that profile is
core_reserve_order_acceptanceincrementingcurrent_exposure_minor, which runs on every accepted order — so it raised23514 partner credit limit must match the account aggregate limit, and one call of this verb rolled back every subsequent order acceptance for that partner. Every seeded partner has a commercial profile. It was invisible in testing because the test used a fresh account with no profile, which is the one state in which the invariant cannot break. The verb that shipped writes both sides — the account row first, the profile second, in one transaction — and is not the thing being warned against. - Pinning test. Reimplementing the one-sided write fails
supabase/tests/1397_report_reach_and_partner_credit.test.sql:225onward, which performs that exact write against the seeded partner, requires the following exposure update to throw23514, and separately asserts thatcore_reserve_order_acceptance's own definition contains the statement that fires the trigger, so the reproduction cannot be dismissed as hypothetical. It also failspackages/db/src/repositories/core/account-commands.integration.test.ts:382, which sends the real command and requires bothaccounts.aggregate_credit_limit_minorandcore_account_commercial_profiles.approved_credit_limit_minorto hold the new value. Note also what these tests deliberately allow: a limit below current exposure is accepted, because over-limit is a commercial state that should make the next order rejected, not make the profile unwritable.
- Claim. The audit's P1 entry that
-
Mobile drawer navigation — the finding recorded as a correction to P0-34.
- Claim.
packages/ui/src/components/shell.tsxcloses the drawer synchronously inside the navigation click handler, so the portal unmounts the link before the route commits and the drawer "navigates nowhere". The same report appeared in P0-48 as "theuishard fails three tests". - Authority. Observed non-reproduction, in four independent ways. The drawer
already calls
onNavigatebeforeonOpenChangein the drawer'sNavigationhandler inpackages/ui/src/components/shell.tsx. Both existing end-to-end tests that click a drawer link and assert the resulting route pass. A probe across three personas at 320px navigated every destination. And the shard claim did not hold either: Storybook was 4 files / 10 tests green and Playwright 82 passed before the release-gate change and 83 after. - What happened when it was implemented. Nothing was implemented, which is the point. The cost was diagnostic rather than operational: the report sent the release-gate work looking for a defect in the shell that was not there. The hazard now is the opposite one — re-fixing an ordering that is already correct risks inverting it, and the inverted order is the one that actually breaks navigation.
- Pinning test. Reimplementing the reported failure mode — closing the
drawer before handing the route to the router — fails
apps/web/src/features/shell/shell-dismissal.test.tsx, "routes from every drawer link before the drawer closes". It enumerates every link in the rendered drawer rather than sampling one, and for each opens the drawer, clicks, and requiresrouter.pushto have been called with that link's ownhrefbefore the dialog leaves the document. The comment above it records P0-34 as the origin so the next reader finds this section rather than the report.
- Claim.
- Canonical Drizzle SQL SHA-256:
ae5872e0deff09115d847268c3acb7f97cfd828e9773887cfb99d268330de70c; snapshot SHA-256:151ae01460e7def205349285df4b268f5c73ace79d17f275ef5a917d453bba35. - Generated OpenAPI SHA-256:
d1dcb164ab834ae12e065ce934ae665520cbefa1f1b2e7d2406e854477ecff18; generated schema SHA-256:a87b32a07841531447c0d34e24b0a137cd5288ff31470f1425edddcad0579784. - Dependency lock SHA-256:
6bc939e90cdba4cb8d055c4903bc49d0371d077780b8bfaffed97d952ea3e5fc; the clean-install helper edge reuses the already locked Next version and adds no package resolution or integrity record. - Populated upgrade: accepted on Node
24.18.1/ pnpm10.34.5in 93.813 seconds. Zero reset plus pgTAP: 16 files / 431 assertions. Full workspace typecheck: 10/10 packages in 45.593 seconds. Focused security, telemetry-redaction, migration, provider, controller, artifact, and workflow suites pass without skipped or retry-masked failures. - Clean isolated UI regression:
smoke-9464bab-ui4on exact Node24.18.1/ pnpm10.34.5, frozen store, Storybook 5/5, Playwright 79/79, zero skipped, flaky, unexpected, or retried tests, 140.241 seconds total. The retainedsmoke-836a894-ui2andsmoke-797a077-ui3diagnostics document the fixed Next/pnpm helper and copied-external defects. - Final commit SHA, non-RC annotated archival tag, verified all-refs bundle and
SHA-256, restore output, and complete clean-checkout/serial/parallel
wall-clock transcript are commit-addressed facts. They are populated after
this documentation commit in the tag annotation and ignored
.clockwork-archives/manifest; that required sequencing is not open repository work.
These are quality and operability gaps rather than absent capability. No launch requirement in the ledger depends on them, so they carry no P0 entry.
- Auth-anomaly alert
[RESOLVED]:runtime-auth-anomalyfiltered on codes nothing emitted, and the boundary ended every spanokbecause the framework converts a thrown denial into a returned response before the span closes.packages/contractsnow owns the canonical denial union and a resolver that maps the boundary-specific codes onto it, so those keep their wire meaning.RuntimeBoundaryInstrumentation.tracetakes anonResulthook, anddenialSpanAttributesreads the problem document on a 401 or 403 and setserror.code,clockwork.outcome, andhttp.response.status_code. The alert file already named the canonical three and is unchanged;operations-data.test.tsnow asserts its filter against the exported union so the two cannot drift apart again. - Telemetry receiver span
[RESOLVED]:/api/telemetryopens its own boundary span, parented on the incomingtraceparent, leaving the browser span's own parentage untouched. The sink swallows delivery failures, so a typed error carries the failed outcome onto the span and the route maps it back to the same 503 body. - Unbound
fetch[RESOLVED]: four sites heldfetchas a property and called it as a method, so an injected global receiver threw before the request was sent:provider-transport.ts,telemetry/otlp.ts, andapps/web/src/features/experience-server/evidence-gateway.ts, alongside the already-boundclient-telemetry.ts. Each test now drives a fake that refuses any receiver other thanglobalThis, so the case fails against unfixed code.esign/http-signing-client.tscalls a bare local, where the receiver is undefined andfetchaccepts it, and is deliberately unchanged. startAgreementEnvelope[RESOLVED]: removed. The live path throughexperience-server/controller.tsis unchanged.- Release benchmark budget
[RESOLVED]: re-measured on the reference machine at the end of this pass and written back toscripts/benchmark-release.mjsandscripts/release-suites.mjs. - Exemption certificate expiry
[RESOLVED]: expiry was evaluated once, at onboarding write time, and never again, so a lapsed certificate stayed valid at invoice time forever. A daily sweep re-reads the liveprocurement_profiles.exemptionsand records each certificate, opening a billing-operations exception and requesting re-collection on lapse. Expiry flags rather than blocks; the notice window and the block/flag policy are named constants carrying theirEXT-TAX-01reference, neither of them approved policy. The status vocabulary was never missing:000100has enforced it since the foundation migration and only the Drizzle model lacked it, so001350records the existing constraint rather than adding a second one. Two checks on a column AND together, so a second vocabulary would have forbidden the union of what each excluded.expiringis deliberately not among the persisted values: it is true only relative to the day you ask, so the row records whether a certificate has lapsed and nearness is recomputed on read. - Partial payment normalization
[RESOLVED]: invoice-category events now carryamountDue,amountPaid, andamountRemainingbeside the coalescedamount, which keeps its previous meaning, and inbox rows written before the totals existed still parse.invoicescarriesamount_paid_minorand the stored generatedamount_remaining_minor(supabase/migrations/001340_invoice_partial_payments.sql), and the Stripe projection advances the settled amount under the existing per-row watermark instead of rejecting any figure other than the full total. Evidence:packages/integrations/src/core/stripe/webhooks.test.ts,packages/db/src/repositories/system/partial-payments.integration.test.ts(underpayment, installments, overpayment, replay, late arrival, and a totals invariant that dead-letters), andsupabase/tests/1340_invoice_partial_payments.test.sql. - Part-paid collection cases
[RESOLVED]: the status vocabulary is unchanged; "partially paid" is derived from anopeninvoice with a settled amount above zero. Full settlement resolves anopen,promised, orescalatedcase inside the same projection transaction that records the money, appending onecollectionActionsrow attributed to the case owner and no more on replay. A partial payment leaves the case and its aging alone, and the dunning sweep chases the remainder rather than the original total, skipping invoices with nothing outstanding. Evidence:packages/db/src/repositories/system/partial-payments.integration.test.ts,packages/db/src/repositories/workflows/core-schedules.integration.test.ts, and the collections cases inpackages/workflows/src/core/engine.test.ts.
Recorded 2026-08-02. The entries above are defects; these four are improvement work, kept in P1 for the same reason — no ledger launch requirement depends on them. Each converts recurring engineering time into an operator action, which matters because steady-state engineering cost, not vendor spend, dominates the cost of running this platform. Each names the verified present state rather than a desired capability.
- Price-book activation
[RESOLVED]. Finance activates a price book from/internal/price-bookswithout an engineer. One finance approver proposes an activation with a reason, a second decides it, and the persistedapprovalsrow carries both identities under theapprovals_two_person_checkconstraint that forbids them being the same person. Activation runs the domainactivatePriceBook, so floors, currency, duplicate SKU and region, version uniqueness, and the effective date are revalidated against persisted truth, and the incumbent version of that currency is retired in the same transaction that activates its successor.recordPriceBookActivationis called on every transition and is no longer a definition without a caller.add_ratewas allowlisted by the route while the repository rejected it as unsupported; it now inserts a rate card into a draft only when the resulting book still passes the guardrails whole. Two facts were blocking the path beneath the TypeScript:price_booksrow policies admit the service connection alone, so writes run there like the commitment ledger already does, and finance now holds a narrow read on drafts (supabase/migrations/001360_price_book_activation_write.sql); and the book carried only its published version number, which never moves, so it now also carriesrow_versionfor the audit chain and forexpectedVersionconflict handling. Signed price content stays behind EXT-COMMERCIAL-01. Evidence:packages/db/src/repositories/core/price-books.integration.test.ts,supabase/tests/1360_price_book_activation.test.sql, andapps/web/src/features/internal-ops/price-books/server-price-book-loader.test.ts. - No operator surface for outbox and dead-letter recovery
[RESOLVED].apps/web/app/(experience)/(internal)/internal/recovery/page.tsxrenders dead-lettered work with its failure reason, andapps/web/src/features/internal-ops/recovery/actions.ts:48gates retry and abandon behind a role recheck with a reason and an audit row;.../internal/webhook-replay/page.tsxcovers replay. The entry's counts were stale when it was written and are staler now:mainhas seventeen internal pages, not fifteen, anddocs/operationsholds eighteen procedures, not thirteen. One caveat on the surface rather than the entry: the reason this surface stays usable in an incident is that unrouted outbox rows are not dead-lettered, which is a contract and not an omission — see the refuted section before changing it. - No derivation view for a computed figure
[RESOLVED].packages/domain/src/core/derivation/index.tsexportsderiveInvoice,packages/db/src/repositories/core/invoice-derivation.tsloads the chain from persisted rows, and it is mounted read-only on the internal account timeline. The audit's caveat that P0-49 made it report every amended order as unamended with zero variance no longer applies: the supersession set is now written, and the vacuous NET-against-GROSS variance was replaced bycore_invoice_amendment_driftas an exact tax-free signal at rest. - Recurring runbooks are prose rather than actions
[RESOLVED]. All six named runbooks now have operator surfaces:docs/operations/dead-letter-recovery.mdmaps/internal/recoveryonto the outbox, provisioning-attempt and workflow-run engines behindqueue-outbox-health.md,stuck-provisioning.mdandworkflow-recovery.md;/internal/webhook-replaycoverswebhook-replay.md; and6cf5f26added/internal/unhandled-errors— behindsystem:operate, reading the durableaudit_eventsfailure stream with persisted decisions — and/internal/billing-reconciliation, the two this entry had recorded as the residue. One caveat survives: the finance-lifecycle pages are not closure —apps/web/src/features/internal-ops/finance-lifecycle/review-action.tsxis review-only and applies nothing.
Rewritten against what the ten work-streams did. Two entries in the original list were refuted rather than fixed — WorkOS role synchronization and outbox dead-lettering — and are in "Refuted findings" above; they are not repeated here, and reintroducing either fails a named test.
- Retry jitter
[RESOLVED]— injected rather than hardcoded, so determinism stays a test property rather than a shipped behaviour (packages/workflows/src/policy.ts,packages/workflows/src/onboarding/durable.ts). - Schedules bound by array position
[RESOLVED]— bound by name, with a test asserting the pairing (packages/workflows/src/core/scheduled-tasks.ts). HttpEsignSigningClienttimeout, abort signal and response bound[RESOLVED]— it now has all three, matchingFetchJsonProviderTransport.GET /v1/core/records/{resource}over-advertised[RESOLVED]— it no longer advertises nine resources it does not implement. The sibling half of that entry —accountscommands other thancreateaccepted, audited as successful, and doing nothing — is also closed:add_role,add_contact,set_payment_termsandset_partner_creditwrite, each covered by an assertion that reads back the row the verb is supposed to have written (packages/db/src/repositories/core/account-commands.integration.test.ts). Read the refuted section before touchingset_partner_credit.- White-label partner notification branding and end-client redaction
[RESOLVED]— wired, with integration coverage. - Stripe ordering watermark ties on lexicographic event identifier
[RESOLVED], and worth reading rather than trusting: the first fix turned a coin flip into a deterministic regression that unconditionally reverted a WON dispute toneeds_response. Credit notes and refunds now dead-letter rather than vanish. The invoice category needed one further change — it decided whether a per-intent payment row should exist by comparing an invoice-level running total, so a second installment arriving late produced no payment row, left the invoiceopenand collectable, and traced only asubsumednote. Two tests encoded that loss as correct and are rewritten. - Idempotency caches failures for twenty-four hours
[RESOLVED]— closed with P0-65. - Open redirect in the demo password gate
[RESOLVED]—safeDemoReturnPathnow rejects/\as well as//. - No Content-Security-Policy and no Strict-Transport-Security
[RESOLVED]— both added, withframe-srcderived from the configured signing origins. LocalSessionResolverdefaults to the most privileged role[RESOLVED]— it no longer defaults tointernal_operator.- One shared secret signs every marketplace and every support webhook
[RESOLVED]— per-provider secrets replace the single shared key. - Hot foreign keys and the dead-letter lookup have no index
[RESOLVED]— nine indexes added, each with theEXPLAINplan showing it is used;paymentsis keyed oninvoice_idrather thanorder_id, which is what its query shape actually needs.dead-letter-dispatchnow bindsaggregate_typefrom the attempt's own scope, so the unique index's leading column is no longer unbound and the query no longer scans the audit log during an incident. - No migration is written to survive a populated table
[RESOLVED]—001380is written to survive one anddocs/adr/0009records the convention. Historical migrations are deliberately not rewritten: they are already applied, and rewriting an applied migration is its own hazard. - Drizzle drift from canonical SQL
[RESOLVED]in part —experience_esign_return_correlations.accountIdwas modelled nullable where the canonical SQL isnot nulland is corrected, andscripts/check-schema-drift.mjsnow compares the model against the live applied schema. The residue is named below. - The exception-queue vocabulary
[RESOLVED]— see P0-43; four declarations collapsed to one, with a database check constraint. The wider "vocabularies are duplicated without drift tests" entry is only partly closed: currency is now enforced (P0-67) and the exception queues are unified, but report keys, core resource names, document kinds, projection channels, internal roles, dead-letter sources and the localhost set are each still defined two or three times with only the external-gate unions carrying a drift test. - The authorization boundary is permissive by omission
[RESOLVED]in part —requirePermissionnow forces every call site to pass an account scope or declare itself internal-only, with the sentinel type derived from the permission table so it cannot drift; a route omitting the scope no longer compiles. One call site slipped through and is named below. - Two assisted-session subsystems exist
[RESOLVED]in part — the unreferenced repository is deleted.impersonation_sessionsis still created and indexed and still carries a policy admitting any internal role, withapps/web/src/features/internal-ops/assisted-session/repository.tsthe live path; the table is not retired. - Every production error discards its cause
[RESOLVED]in part — causes are propagated across the workflows work-stream's files, with an inventory of the rest. The remainder of the seventy-four barecatchblocks is open, anddocs/operations/unhandled-errors.mdstill asks an operator to diagnose provider failures from telemetry carrying only a code.
- The commission clawback ceiling is an unlocked read-then-write
[RESOLVED].001419_commission_clawback_ceiling.sqlserializes every reversal at its original payment accrual and enforces the cumulative ceiling in the database, net of signed credit-note voids. The two-connection race test proves one of two concurrent over-ceiling reversals is refused and that the refusal reaches callers as a readableDatabaseCoreError. - Contractual renewal price protection is captured and validated but never enforced by any pricing path. See the residue below: the enforcement was written, adopted, and then deliberately reverted.
- The design system is a parallel catalogue. Thirty of the fifty-three
components exported from
packages/uihave no use inapps/webwhile the product hand-rolls each of their jobs.internal-projection-page.tsxstill duplicatesprojection-detail-page.tsxand is still imported by no route, confirmed against the current tree. - Mutation feedback is generic and does not refresh.
apps/web/src/features/surfaces/workflow-panel.tsxreturns one of three sentences for the mutations it still owns after the seven supersessions, with no identifier, no version, no link and no revalidation; it still remounts the confirmation dialog by key, which defeats focus restoration, and still callssendCoreCommandwithout an idempotency key — both re-verified against the tree — while leaving the confirm control enabled during submission. The development-only guard that used to cover the pre-filled legally consequential free text is gone withdemoValue(P0-50), so that text needs re-checking rather than assuming. - Bulk actions and a collection export are still absent; sortable columns no
longer are.
8c53747delivered server-side sorting over the whole collection — not a slice of the loaded page — witharia-sorton the customer and partner collection surfaces and a test that discriminates the two. Bulk actions and an export of the collection surfaces remain absent; the formula-safe CSV export that exists is the §17 report download (P0-64), not a collection export. - Test tooling ships in the application.
apps/web/package.jsonstill listsmswand@clockwork/testingas dependencies. - Contract tests instantiate only the fake
[partly closed]. The CRM and notification contract tests now exercise more than the fake side;EsignProviderAdapter,DeniedPartyScreeningAdapter,ReadOnlySupportFeedAdapterandMarketplacePlatformAdapterremain unreferenced while eachFakesibling is exercised. - No end-to-end coverage of settlement. Invoice issuance through payment to receipt, dunning escalation, refunds, credit notes, disputes, commission settlement and any non-USD order still have no browser coverage, as do POC conversion, amendment and co-termination, renewal decline, termination and teardown, migration runs, marketplace orders and tax exemption. The money work-stream added database-level and integration coverage, not browser coverage.
- No
LICENSEfile[RESOLVED]. The repository now carries a proprietary evaluation licence: all rights reserved, with an express grant to view, run and evaluate the software for review, audit and diligence — the use this entry recorded as blocked. It grants no production, distribution or derivative rights. Choosing a different licence is a business decision this file does not preempt; what is resolved is that an external reviewer is no longer infringing by reading. - Volume, freshness disclosure and accessibility: mostly delivered, two
residues named. Delivered and verified against the tree: page-level
pagination and the sorting above on the collection surfaces (
8c53747); customer stale-data disclosure throughProjectionFreshnessNoticeon the customer and partner collection surfaces (8c53747); unsaved-changes protection on the six mutating surfaces, disarmed on the server's success signal so a successful submit does not warn; the internal queue search rebuilt so the input keeps local authority with a debounced URL commit and no longer drops typed characters (5c0f727); dashboard copy and timezone from the persona rather than a fixed string and America/New_York (5c0f727); and the accessibility gaps the Axe run cannot see — a focusable skip-link target, route announcements, the operator queue table keeping its semantics and keyboard scroll below 48rem, and the combobox no longer runningaria-activedescendantand real focus movement simultaneously (5c0f727). Still open or partial: the 100-page cliff is closed at the loader but only partly disclosed — the partner surface disclosestruncatedwhile the customer collection pages drop it and render the generic stale banner, so a cut-off ledger reads as merely "may be out of date"; that repair is in flight with the experience lane. Suspense boundaries exist on the acceptance page and are not claimed anywhere else. The two-pass acceptance refusal is P0-68's, not this entry's.
Everything below was found during the work and deliberately shipped open. It is collected here because a residue recorded only in a commit body is a residue nobody reads. None of it is speculative: each was observed by the work-stream that left it, and each has been re-checked against the tree by the pass that wrote this revision — entries a later work-stream closed say so instead of being silently deleted, because the closure is part of the record.
- The webhook-replay task is registered and worker-backed. P0-56 records the durable claim, stored-payload binding, retry, and completion evidence.
GuardedProviderJsonTransportis composed on exactly one path. The ws5 commit body records it as still uncomposed; the tree contradicts that —packages/workflows/src/runtime/environment-production-adapters.ts:530wraps the outbound CRM transport in it. Nothing else is wrapped. The marketplace finance adapters (packages/integrations/src/core/marketplaces/types.ts:69,adapters.ts) still have noMarketplaceTransportimplementation and are wired into nothing, so the declared last-mile gate covers one provider path out of the set it was written for.accepted-order-provisioning.tsemits anaggregateTypethe dead-letter join can never match — it appends withaggregateType: "provider_operation"against a join that expectsorderorpoc, so an operator redrive of these rows returns unmapped. Found by two separate work-streams and fixed by neither merged one; the join's own comment cites the wrong one of the two emitters, and both existing tests hand-seedorderrows by raw SQL — fixtures built to satisfy the join rather than exercise the emitter. In repair by another lane at the time of writing; treat this as the record of the defect, not a claim about the final tree.create_signature_envelopeexecutes now, and the sweep it forced found the class elsewhere. It was neither aproviderCommandnor astaffServiceCommand, so it ran asclockwork_runtimeand itsprovider_operationsinsert was refused with 42501 on a tenant route — which is why P0-51's guaranteed version collision never surfaced in practice.5c0f727fixed it with a narrow role-targeted INSERT policy admitting only an unstarted claim for the two enumerated provider calls, proven against the aggregate row's account — the split migration000200already gavelifecycle_provisioning_attempts— rather than reclassifying a customer-initiated write onto the service connection.decide_poc's approving half had the same defect and the same fix. Still open from that sweep:open_exceptionanddecide_exceptionare dead for a tenant fall-through their own comment declares intentional and are left deliberately —8c53747then removed a migration that would have opened tenant exception intake, because every spec'd tenant-triggered exception is machine-opened and the tenant write path has never worked at any point in this repository's history; the remaining finding is that the route should refuse non-staff callers explicitly, with the two-layer treatmentstart_migrationgot, rather than dying in the row policy.- The provider-before-local-write window in the e-sign path remains. Nothing retries, and there is no orphan sweeper for e-sign, so an envelope created at the provider before a failing local write leaves orphaned provider state.
- Renewal price protection is not enforced, and the control was deliberately
removed. The rule and its tests are correct and are kept; the adoption was
reverted because it refused a legitimate renewal governed by a signed paper
carrying no price protection. The blocker is a contradiction, not an
oversight:
governingAgreementForPricingasserts in its docstring that a quote and its order cannot be governed by two different papers, whichagreementOncontradicts. A control that refuses a signed valid renewal is worse than an unenforced rule, so the rule stays unenforced until that is settled. - An amendment-added line has no
order_linesrow, so it can never subsequently be reduced or removed. A modelling gap, not a bug in the amendment writer. - A standalone negative added-line delta is accepted while the same delta combined with a supersession throws an untyped error.
- A doc comment in
packages/domain/src/core/amendmentsoverstates whenapplyAmendmentruns. role_sync_events.erroris written durably and read by no operator-reachable path, andlistUnresolvedRoleSynchronizationshas no caller and no resolution semantics — a reason is never cleared, so an approved membership still returns as unresolved. The repository read and its tests exist; the surface does not.- The
withInternalTransactionruntime guard is a deny-list of one role name and should be an allow-list, and the static scan that enforces it matches two literal spellings only. That is the guard standing between the tree and a recurrence of P0-54. - The one unscoped
requirePermissioncall site is closed. The lifecycle route that took a tenant-reachable permission with no account scope now passes a scope sentinel like every other call site; no two-argumentrequirePermissioncall survives anywhere inpackages/api, re-checked by grep against the tree. - The Stripe financial verifier and the marketplace tolerance path were not repaired.
core_order_acceptance_reservationshas no Drizzle model at all, socheck:schema-driftcannot see it, and several pre-existing check-constraint names inpackages/db/src/schema/core/finance.tsdisagree with the applied SQL names.RELEASE_REQUIRED_RUNTIME_ENVIRONMENTomitted the two tax variables and no longer does — closed in5c0f727, which putTAX_PROVIDER_BASE_URLandTAX_PROVIDER_TOKENin the release isolation floor. The commit is exact about what that closed: an assertion gap, not a leak — the token was already scrubbed by the sensitive-name regex and the base URL by.env.examplemembership. The second site of the over-broad tax gate — the internal webhook-replay action — was fixed in the same commit (see P0-56); the third, in the production-workflow proof, was reported there and fixed in6cf5f26: the outbox drain now composescomposedTaxProvider(), so an unwiredEXT-TAX-01refuses the two writing commands instead of decomposing the whole drain, materialization included.migrationshas no backing projection channel. The surface says so honestly rather than printing a freshness claim over a constant (P0-63).renewals, which this entry used to name beside it, has since been wired to the internalorderschannel — renewal work rows are orders — so its honest-unwired state is superseded.recordRoute'sRoutealias does not bind the channel set to the route tree.RouteImpl's first three members are unconditional, so the original defective body recompiles clean under the new type; a unit test holds that direction and the compiler does not.- False claims in shipped file headers remain.
5c0f727records four and8c53747records three more that its own work wrote — the defect the citation tooling exists to catch, committed while building it — including a justification that argues from a premise about adapter coverage that is not true. They are code comments, not backlog rows, and they belong to the lanes that own those files. The prose inaccuracies that lived in this file — deal-registration create recorded as undelivered, the collections corrections surface recorded as missing, and "Poppler is installed" written as if it described the workflow — are corrected in this revision. commissions:accruecannot be executed by the role its own insert guard invites.commission_accruals_insert_role_guardnames roles no permissive lane admits — the only permissive lane oncommission_accrualsisapp_has_account(partner_account_id), which a finance approver does not hold — so the verb dies before its audit append. Recorded by8c53747, which fixed the finance audit guard beside it and deliberately did not widen an account-unscoped write lane to revive this; it is left with the commission lane in flight.core_deal_registration_disputesexists and no code reads or writes it. The deal-registration dispute path is unbuilt —disputeanddecide_disputeare deliberately absent from the admitted verbs, and the 3-business-day escalation the spec gives it has no owner in code. The service catalogue says this in place so the absence cannot read as an oversight.- Influenced attribution and registration-dispute writes remain unbuilt. The
partner portal derives its sourced-credit label from accepted registration
status and explains the agreement-derived route; it does not read or fabricate
a
core_deal_registration_attributionsrow. That table remains schema-only, and no portal control capturesinfluence_bps, opens a dispute, or decides one. Those writes need the dispute-owner workflow and its evidence boundary, rather than a manual tag added to the quote or registration page. - The customer quote catalogue boundary is now split by source. In
authoritative mode,
/quotes/newand/buyreceive the selected account's complete currency-compatible active rate-card set from the server; the live integration drivesquotes:createwith every returned book/SKU/region tuple. Fixture UUIDs remain only in the server-only explicit-demo catalogue consumed by the command echo simulator, whose UI says that no quote is priced, saved, or issued. Missing production database composition never falls back to it. maincarries no branch protection, so no status check gates a merge even once Actions can run. Requiring it needs organization administration this account does not hold; it is a named input ofEXT-ACC-01. See P0-48.
The staff tools the revenue team uses at commerce.fil.one: MNDAs, the contract
register and template signing, the sales library and indicative pricing, the
staff home page and roles, and the permission model underneath them. They run
without a commerce account, billing or provisioning (ADR 0011), so no launch
requirement in the ledger depends on them and they carry no P0 ID. Operator
detail lives in docs/operations/commerce-mnda.md,
docs/operations/contract-templates.md, docs/operations/self-approval.md and
docs/operations/revenue-team-guide.md.
- MNDA send, track and archive
[COMPLETE]: staff prepare an MNDA from the hash-pinned template (packages/documents/src/mnda/template.json), preview the PDF, and send it through SignWell for a partner signature followed by a Fil One countersignature. The provider id is bound to an unsent draft before sending, a lease serializes callbacks and staff actions, and a callback only triggers a re-read of the document. Completion requires the executed PDF in the same step (supabase/migrations/001439_commerce_mnda.sql,packages/workflows/src/mnda.ts,packages/integrations/src/esign/signwell.ts,apps/web/app/api/v1/webhooks/signwell/route.ts). Field-level errors, Edit details with automatic replacement of the superseded draft, the partner-completes-blanks default, Duplicate and Send again, embedded fonts and readable dates are in place. The "Partner completes details when signing" mode is retired for new drafts and still renders for MNDAs prepared in it (packages/contracts/src/mnda.ts). Evidence:packages/workflows/src/mnda.test.ts,packages/integrations/src/esign/signwell.test.ts,packages/documents/src/mnda/render.integration.test.ts,packages/db/src/repositories/mnda.integration.test.ts,apps/web/src/features/internal-ops/mnda/workspace.test.tsx. - Register, corrections and voids
[COMPLETE]: the register pages through every MNDA with status filters, Only mine, search, sent date and days outstanding, and a CSV export capped at 10,000 rows (supabase/migrations/001442_commerce_mnda_register.sql,apps/web/src/features/internal-ops/mnda/register.ts). Needs attention rows name the reason and the next step: bounced email, request stopped, document deleted in SignWell, a SignWell copy whose signers or binding no longer match, or a mismatched copy someone has signed (signwell_signed_mismatch), which a commerce administrator resolves in SignWell. Fix email, Someone else will sign and Void run in Commerce. Void re-reads SignWell first and is refused whenever SignWell's copy shows any signature. Fix email is also offered on a signer mismatch: when the address entered is the one SignWell already shows, it is recorded and the next read rechecks the rest. The duplicate warning checks earlier MNDAs and the contract register under one normalizer (commerce_mnda_normalize_company). Countersigners and the notice email are limited tosignatory:manage. Every PDF download and export is audited. - Missed callbacks and expired sessions
[COMPLETE]: the scheduled tasksystem.esign.reconcile.v1re-reads open MNDAs and template contracts every 15 minutes in rotation, at most 50 of each per run (packages/workflows/src/esign/tasks.ts,packages/db/src/repositories/esign-reconciliation.ts,supabase/migrations/001453_esign_reconciled_at.sql). A server action that arrives after the WorkOS access token expired returnsSESSION_EXPIRED; the page shows "Your session expired. Reload to continue." with a Reload control that refreshes the session in place and keeps typed input (apps/web/src/features/internal-ops/session-expiry.tsx). Evidence:packages/workflows/src/esign/reconciliation.test.ts,packages/db/src/repositories/esign-reconciliation.integration.test.ts. - Partner-entered details kept on the record
[COMPLETE]: on completion the signing engine reads the values the partner entered in the fields the MNDA asked of them (mndaSigning.capture) and stores them incommerce_mnda_requests.partner_details, in the same change as the state and the executed PDF. The protect trigger allows that write only with completion and never afterwards (001458). Register search matches the partner's legal name, jurisdiction and signer name; the duplicate check matches the partner's legal name; the register row shows "Signed as" when it differs from what staff entered; the CSV export carries the partner's values. When SignWell reports no values the record keeps an empty object and the history recordsmnda.fields_unreported. Evidence:packages/workflows/src/signing/scenarios.ts,packages/db/src/repositories/mnda.integration.test.ts,supabase/tests/1458_mnda_partner_details.test.sql. Whether live SignWell reports field values on a completed document (fields[].value) is[EXTERNAL-ONLY], below. - Partner signer delegation
[OPEN]: SignWell documents are created withallow_reassign: false, and a changed partner email fails the signer check (signwell_signers_mismatch). A partner whose authorized signatory is someone else needs Someone else will sign, which voids the MNDA and opens a new draft. In-provider reassignment needs the counterparty slot's email check relaxed and the reassigned recipient recorded; the Fil One slot stays strict. - Live SignWell behavior of corrections and partner fields
[EXTERNAL-ONLY]: as of 2026-10-10, Fix email (PATCH /documents/{id}/recipients), void (DELETE /documents/{id}), copied-contact completion notices, the partner-completed field values on the executed PDF and in the completed document'sfields[].value, and whether SignWell lets an administrator delete a document someone has signed are proven against fakes and the adapter contract, not against SignWell. The input is one test-mode run of each against the staging account (EXT-PROVIDER-01). - SignWell callback host
[EXTERNAL-ONLY]: the callback URLs arehttps://clockwork-staging.fil.one/api/v1/webhooks/signwellandhttps://clockwork.fil.one/api/v1/webhooks/signwell. As of 2026-10-10 the DNS for those hosts and the matching callback configuration in SignWell are pending (EXT-DOMAIN-01). Until they are live, the 15-minute reconciliation is what advances statuses. SignWell API pay-as-you-go billing must be active above the free allowance.
- Executed-contract register
[COMPLETE]: staff record any agreement without a commerce account: type, paper (ours or theirs), status includingin_negotiation, counterparty, owner, key terms, tags, and PDFs as main, attachment, counterparty draft or redline files (packages/contracts/src/contract-register.ts,supabase/migrations/001443_commerce_contracts.sql,packages/db/src/repositories/contracts.ts). Files are stored incommerce_stored_documents, PDF only, at most 25 MB, hash-checked on every read. The notices page lists renewal and notice deadlines; the register exports to CSV, and every download and export is audited. Evidence:packages/db/src/repositories/contracts.integration.test.ts,supabase/tests/1443_commerce_contracts.test.sql,apps/web/src/features/internal-ops/contracts/views.test.tsx. - Template signing engine
[COMPLETE]: a template is counsel's wording as data, bound to the SHA-256 of counsel's DOCX, with declared fields and fail-closed[[token]]resolution (packages/documents/src/contract-templates/definition.ts,registry.ts,render.tsx). Prepared contracts pass an approval by someone other than the preparer when the template requires it, enforced in the database, or a self-approval with a written reason by a preparer holdingapproval:self(supabase/migrations/001457_contract_self_approval.sql), then follow the MNDA's bind-then-send, lease and re-read rules (packages/workflows/src/contracts.ts,packages/integrations/src/esign/signwell-contracts.ts). Void runs in Commerce, survives a document deleted in SignWell, and is refused whenever SignWell's copy shows any signature. A copy whose signers or binding do not match is held in attention with nothing applied from it, keeps Check status, and clears on the next read that matches. History entries for voids, reminders and SignWell deletions read in words, with the void reason and the reminder's recipient. Reminders are spaced from the last reminder (supabase/migrations/001451_contract_signing_reminders.sql).scripts/render-contract-specimen.tsrenders a specimen for counsel. Evidence:packages/workflows/src/contracts.test.ts,packages/documents/src/contract-templates/registry.test.ts,packages/documents/src/contract-templates/render.integration.test.ts,apps/web/src/features/internal-ops/contracts/engine.integration.test.ts. - Signer correction on template contracts
[COMPLETE]: the signing panel offers Fix email while the counterparty has not started signing, through the engine'scorrectSigner(pending, then confirmed by SignWell, or settled by a re-read when SignWell's answer is lost), and Someone else will sign, which voids with thesigner_changecode and opens the template's prepare page with the earlier values and the signer blank (?from=<contractId>); approval runs again where the template requires it. The contract table keeps the corrections and a cancel code, frozen once terminal (supabase/migrations/001459_contract_signer_correction.sql), and history entries carry the replaced email as their before-image. Evidence:packages/workflows/src/signing/scenarios.test.ts(correction scenarios now run for both types),supabase/tests/1459_contract_signer_correction.test.sql,apps/web/src/features/internal-ops/contracts/engine.integration.test.ts. - Contract register "mine" filter
[COMPLETE]: Recorded by me lists what the reader recorded or prepared and the MNDAs they sent, and the home page links its own needs-attention and out-for-signature counts to it, while approvals awaiting the reader's decision link only the team count (apps/web/src/features/internal-ops/sales-home/contract-source.ts). - Counterparty paper for Fil One signature
[COMPLETE]: Send for Fil One signature on an unsigned contract on the counterparty's paper sends one of its uploaded PDFs, pinned by its SHA-256, with a Fil One signature page appended, either to Fil One alone (they signed already) or to the counterparty first. It is a second declaration on the signing engine (counterpartyPaperSigning) over the contract table (supabase/migrations/001464_contract_counterparty_paper.sql), with approval, reminders, correction, void and the executed PDF as for templates. The procedure it follows is adding a signed document type. - Any uploaded PDF, sending again and the signed copy
[COMPLETE]: an uploaded PDF on either party's paper can be sent for signature, and a contract whose request was declined, expired or voided is sent again from the same record, with the ended request kept incommerce_contract_signing_history(supabase/migrations/001465_contract_signing_any_pdf_resend.sql). The signature page matches the PDF's paper size and has standard execution, counterparts and electronic-signature wording (version2026-10-10; counsel may revise it as a new version). SignWell copies the preparer on the signed PDF. Contract signing is on in deployed environments: test mode on staging, live on production. - Uploaded PDF live run
[EXTERNAL-ONLY]: no uploaded-PDF request has yet been signed in SignWell, in test mode or live. - Renewal reminders outside the app
[OPEN]: notice deadlines appear in the notices list and on the contract record only. Email or chat reminders need a scheduled task and a sender, and the sender waits on a notification provider (EXT-PROVIDER-01). - Register search and storage
[OPEN]: search covers counterparty, title, owner and tags; PDF text is not indexed.COMMERCE_DOCUMENT_STOREaccepts onlypostgres; an S3-compatibleContractDocumentStoreis the planned second backend. - One e-signature engine
[COMPLETE]: MNDAs and template contracts run on oneSigningEngine(packages/workflows/src/signing/engine.ts, ADR 0012), each described by aSigningDocumentTypedeclaration, with one SignWell document schema and one scenario suite run against both (packages/workflows/src/signing/). Shared: bind before send, the bounded wait for SignWell's draft, post-send read tolerance, attention reasons, void and remind rules, and approval or sender copies where a type declares them. The lifecycle purchase-agreement path still uses its own provider-neutral port (packages/integrations/src/esign/index.ts). - One signing table
[OPEN]: each signing type keeps its own table and repository, so contracts have no first-sent time until their table does. Whether to move both onto one table is a decision for when a third document type or the lifecycleagreementsmodel needs it, not scheduled work. - Template line items
[COMPLETE]: a template field of kindline_itemscarries a priced table of up to 20 lines in one currency; its[[token]], a paragraph of its own, prints the lines, subtotal, discounts and total with the pricing summary's fonts and money formatting, and fails closed on a missing or malformed table (packages/contracts/src/contract-templates.ts,packages/documents/src/contract-templates/render.tsx). The prepare form edits the lines or imports them from a saved pricing scenario under the scenario page's scope rule. At preparation the server applies the in-force rate's minimum to each line that matches a rate by item, region and unit, and writes a pricing note that vouches for the scenario's list prices only when every line still matches the scenario, otherwise naming the edited lines (apps/web/src/features/internal-ops/contracts/line-items-editor.tsx,line-items.ts,line-items-server.ts,scenario-import.ts). Unit prices typed by a seller are not checked against a price book; approval is the control. Values live in the signing row'sinput; no migration. Proven against the test fixture only: the order form, SOW and DPA schedule wording that would use it is imported into the template store. Evidence:packages/documents/src/contract-templates/render.integration.test.ts,packages/contracts/src/contract-templates.test.ts,apps/web/src/features/internal-ops/contracts/actions.test.ts,apps/web/src/features/internal-ops/contracts/prepare-form.test.tsx. - Contract template store
[COMPLETE]: templates are versioned data incommerce_contract_templates(supabase/migrations/001475_contract_template_store.sql), managed by commerce administrators under Manage templates (contract_template:manage): import or draft, edit drafts, preview a specimen, activate, retire and duplicate, each audited. A version is frozen once activated or used, and a prepared contract pins its key, version and content hash. The engine supports optional sections (ticked or driven by a choice field),decimalandpercentfields, tables and worked-out figures. Agreement wording is imported in Commerce and never committed; the repository keeps only the demo's sample templates and the test fixtures. Sending stays in SignWell test mode untilCOMMERCE_CONTRACTS_TEST_MODE=false. Evidence:supabase/tests/1475_contract_template_store.test.sql,packages/db/src/repositories/contract-templates.integration.test.ts,apps/web/src/features/internal-ops/contracts/template-store.integration.test.ts,packages/documents/src/contract-templates/registry.test.ts.
- Sales library
[COMPLETE]: decks, one-pagers, pricing sheets and case studies as a stored PDF or an https link, each with audience, status and content date (packages/db/src/repositories/sales-library.ts,apps/web/src/features/internal-ops/sales-library/library-view.tsx). Staff withsales:readbrowse and download;collateral:manage(commerce administrators) adds and edits. Downloads are audited assales_collateral.downloaded. Evidence:apps/web/src/features/internal-ops/sales-library/library-view.test.tsx. - Indicative pricing
[COMPLETE]:/internal/pricingprices in the browser from active price books in force today, showing list prices only; floors, transfer prices and accounting codes stay on the server (apps/web/src/features/internal-ops/sales-pricing/books.ts). Signed prices remainEXT-COMMERCIAL-01. Evidence:apps/web/src/features/internal-ops/sales-pricing/pricing.test.tsx. - Pricing scenarios and indicative summary
[COMPLETE]: sellers withsales:readsave scenarios of up to 20 lines for a named prospect on/internal/pricing, then open, overwrite, delete or download them as an indicative pricing summary PDF. Lines keep the list price and book version in force on the day saved; totals are recomputed on every read, and floors, transfer prices, claims and accounting codes are never stored or read (packages/db/src/repositories/pricing-scenarios.ts,packages/documents/src/pricing-summary/render.tsx, migration001456).sales:readgates the saves and deletes because a seller's writes reach only their own rows;commerce_adminreads, overwrites and deletes every seller's scenarios. A line below its rate's minimum quantity, or a company the PDF cannot print, is refused at save. Saves, deletes and downloads are audited aspricing_scenario.*. Capacity can be entered in TB, PB, TiB or PiB and is stored exactly in the rate's unit beside the entry; lines and scenarios show per month, year 1 and term totals. Optional partner economics (referral with step-downs, resale, other) are saved inpartner_economics(migration001467), and the summary has a customer and a partner audience; a resale's customer summary is the partner's quote at its own price. The guided demo saves nothing and offers two fictional example scenarios. Prices stay indicative untilEXT-COMMERCIAL-01. Forward-only: once a scenario with an entered unit, a rate'segressTreatmentor partner inputs is saved, or a PiB or TiB scenario is imported into a contract (18-decimal quantity withentered), the schemas before001467reject those rows, so a rollback needs a forward fix rather than a revert. Evidence:packages/db/src/repositories/pricing-scenarios.integration.test.ts,packages/documents/src/pricing-summary/render.integration.test.ts,packages/domain/src/core/pricing/scenario-economics.test.ts,apps/web/src/features/internal-ops/sales-pricing/scenario-builder.test.tsx,apps/web/e2e/demo-pricing.spec.ts. - CRM provider
[EXTERNAL-ONLY]: prospects, contacts, activities and pipeline live in HubSpot, outside this repository. The outbound CRM projection exists and is inert behindCLOCKWORK_CRM_ENABLEDandEXT-PROVIDER-01; a HubSpot client follows provider selection. - CRM link for revenue records
[OPEN]: MNDA and contract records carry no CRM company reference, so joining them to HubSpot means matching on company name.
- Revenue role and sales workspace
[COMPLETE]:revenue(Fil One seller) holdsmnda:send,contract:read,contract:write,sales:read,audit:readandaudit:append, and no operations, billing or provisioning permission. The sales rail shows Home, MNDAs, Contracts, Sales library and Pricing, plus Owner console and Team for holders ofstaff:manage(apps/web/src/features/shell/navigation.ts,supabase/migrations/001441_revenue_roles.sql). Home ("My work") counts the reader's MNDAs needing attention, waiting on the partner (a send SignWell has not yet confirmed counts here), waiting on Fil One, completed in the last 30 days and unsent drafts, and the contracts they recorded or prepared that are out for signature or need attention. Approvers also see contracts prepared by others that await their decision; each team count opens the register filtered to the same rows (packages/db/src/repositories/sales-home.ts,apps/web/src/features/internal-ops/sales-home/). Evidence:packages/db/src/repositories/sales-home.integration.test.ts,apps/web/src/features/internal-ops/sales-home/sales-home.test.tsx. - Team page
[COMPLETE]: a commerce administrator adds staff, grants and removes roles, and removes access at/internal/team, with every change audited. The last commerce administrator cannot be removed or demoted (packages/db/src/repositories/system/staff-team.ts). One commerce administrator can grantcommerce_adminwithout a second approval. Evidence:packages/db/src/repositories/system/staff-team.integration.test.ts,packages/db/src/repositories/system/staff-team-administrators.integration.test.ts. - MFA receipt retention
[OPEN]:experience_mfa_receiptsgains a row on every staff sign-in and nothing prunes it; the team page's latest-verification lookup reads it throughexperience_mfa_receipt_user_idx(supabase/migrations/001455_mfa_receipts_user_index.sql). - Staff first sign-in
[EXTERNAL-ONLY]: each person verifies their email and enrolls their own authenticator; no test can complete another person's MFA. Production acceptance of a new seller is their own first sign-in.
- Handoff request and queue
[COMPLETE]: a seller withcontract:writehands an executed contract (or a template contract whose signing completed) to operations from the contract record, with the counterparty's legal name and signer, the requested side, notes, and optionally the completed MNDA and a saved pricing scenario./internal/handoffslists the queue foroperations:read; internal operators and commerce administrators (operations:write) take, complete or decline, a decline with a note for the seller. The seller sees status on the contract and on Home. The database refuses an unsigned contract, a contract already in a request that was not declined, an MNDA that did not complete and a missing scenario, and allows only open, in progress, then done or declined (supabase/migrations/001462_commerce_handoff_requests.sql,packages/db/src/repositories/system/handoff-requests.ts,apps/web/src/features/internal-ops/handoff/). Every change is audited ashandoff.*. Evidence:supabase/tests/1462_commerce_handoff_requests.test.sql,packages/db/src/repositories/system/handoff-requests.integration.test.ts,apps/web/src/features/internal-ops/handoff/handoff.test.tsx. - Organization set-up by staff
[COMPLETE]: an internal operator or commerce administrator (operations:write) creates a customer, channel partner or referral partner organization at/internal/organizations/new, from a handoff they are working or on its own, with legal name, country, currency, business domain, registered address and billing contact. Partners getrelationship_roles = {partner}and an agreement type (referral, orresale,msporembeddedfor a channel partner), so the side the account implies matches the organization. Self-registration and staff set-up share one account-and-organization writer (packages/db/src/repositories/lifecycle/organization-onboarding.ts). The account stays in screening review, the organization is recorded on its handoff (handoff.organization_recorded), andorganization.createdstarts the WorkOS organization as it does for a registration. Evidence:packages/db/src/repositories/lifecycle/organization-onboarding.integration.test.ts,apps/web/src/features/internal-ops/organizations/organizations.test.tsx. - Partner self-registration
[COMPLETE]:/registerhard-coded a customer organization and an owner. A registration whose relationship ispartner(and not alsodirect_client) now creates a channel partner organization run by apartner_admin. Evidence: the registration cases inpackages/db/src/repositories/lifecycle/command-repository.integration.test.ts. - Partner agreement type defaults disagree
[OPEN]: a partner account with no agreement type is a channel partner toorganization_side_from_account(001446) and a referral partner to the finance billing profile (packages/db/src/repositories/core/database-finance.ts). Self-registered partners have no agreement type; staff set-up always records one. - Handoff notifications
[OPEN]: nobody is told when a request is raised or decided; operations watches the queue and the seller watches Home. A notice waits on a notification provider (EXT-PROVIDER-01).
- Staff partner records and registered deals
[COMPLETE]:/internal/partnerskeeps each partner's models, status, owner, contacts, next step and terms (commission or revenue share, step-down rows, margin, territory, exclusivity, currency, NFR allowance, trial, free-form rows) before any organization exists, with an optional link to one later. Holders ofsales:readread and export;contract:writechanges. Registered deals take the channel policy's protection by default, warn on another partner's open registration for the same normalized end client without blocking, and expire on read once protection ends. Home counts partner next steps overdue and due this week. Every change is audited aspartner.*. The records do not feed billing, commissions or the switched-off partner-portal engine (docs/operations/partner-records.md,supabase/migrations/001466_commerce_partners.sql,packages/db/src/repositories/partners.ts,apps/web/src/features/internal-ops/partners/). Evidence:supabase/tests/1466_commerce_partners.test.sql,packages/db/src/repositories/partners.integration.test.ts,apps/web/src/features/internal-ops/partners/partners.test.tsx. - Partner next-step reminders
[OPEN]: nobody is emailed when a next step falls due; Home shows it. A reminder readsPartnerRepository.nextStepsDue.
- Permissions as the single check
[COMPLETE]: every application and database check tests a permission. The signed claim carries the permission set;app_has_permissionreads it, and stored-role checks join the generatedrole_permissionstable (supabase/migrations/001445_role_permissions.sql,supabase/migrations/001446_permission_model.sql). Organizations carry an explicit side, triggers refuse a role that does not fit it, andmembership_roleslets one person hold several roles. Restrictive policies test for a wider permission and never for holding a role (supabase/migrations/001447_permission_policies.sql). An assisted session withholds every approver permission (assistedSessionWithheldPermissionsinpackages/contracts/src/auth.ts).docs/security/access-matrix.mdis generated byscripts/generate-access-model.tsand checked bypnpm check:generated. Evidence:supabase/tests/1446_permission_model.test.sql,supabase/tests/1447_permission_policies.test.sql,supabase/tests/1447_restrictive_monotonicity.test.sql. - Owner console and self-approval
[COMPLETE]:/internal/ownergathers pending approvals, capability switches, staff, assisted sessions, notices, recent self-approvals and security events (packages/db/src/repositories/system/owner-console.ts,supabase/migrations/001452_audit_event_type_index.sql). A commerce administrator may approve their own two-person request with a written reason (approval:self); the database marks the row, writesapproval.self_approvedand notifies every other holder ofstaff:manage(supabase/migrations/001449_self_approval.sql). There is no countersign deadline. Evidence:supabase/tests/1449_self_approval.test.sql,packages/db/src/repositories/system/owner-console.integration.test.ts. - Invite acceptance
[COMPLETE]: an invite link opens/invite/<token>. After sign-in, the person whose verified WorkOS email matches the invite accepts it. Commerce checks everything first; then they join the organization's WorkOS organization, Commerce creates their identity if new and a membership with the invited role, marks the invite accepted (accepted_at,accepted_by) and writesinvite.accepted, and the session switches to the organization. A refusal after the WorkOS step (a lost race) removes the WorkOS membership again unless a Commerce membership holds it. An invite is used once and expires, is never created accepted, and the runtime pool may change only its expiry and acceptance (database-enforced insupabase/migrations/001463_invite_acceptance.sql); a different, unverified or staff address, an address bound to another WorkOS user, an impersonated session and a role the organization's side cannot hold are refused. Operations (operations:write) invites anyone into a customer or partner organization from/internal/organizations/<id>, which lists every invite, shows the link of each pending one to copy, and revokes pending ones (invite.revoked); no email is sent (EXT-PROVIDER-01). The token is derived from the invite id withAUTHORIZATION_CONTEXT_SECRET; only its hash is stored. After a secret rotation a pending invite shows as void, with no link, and no longer blocks a new invite to the same address. Evidence:supabase/tests/1463_invite_acceptance.test.sql,packages/db/src/repositories/lifecycle/invites.integration.test.ts,apps/web/src/features/invite/invite.test.tsx. - Sign-in for new customer and partner organizations
[OPEN]: owners, administrators and partner administrators are privileged roles, and a session counts as MFA-verified only for WorkOS organizations named inWORKOS_MFA_POLICY_ORGANIZATION_IDS. Each new organization's WorkOS id must be added there before its first administrator can use the portal. - Customer and partner user management
[OPEN]: an organization's own administrators invite through the lifecycle route, which applies their role ceiling, but the customer and partner portals show no invite link (operations copies it from the organization page), and there is no role change or removal for customer or partner administrators (packages/db/src/repositories/lifecycle/command-repository.ts). - Read-only "view as"
[OPEN]: staff can act for a customer only through an assisted session. A mode that renders a tenant portal with a chosen role's permissions and refuses every write does not exist.
Each input below already has a repository control, deterministic simulator,
fail-closed enforcement boundary, and exact live activation test in
docs/external-gates.md.
EXT-ACC-01— named hosted accounts/projects, scoped credentials, selected telemetry/paging backend and delivery proof, managed backup/PITR proof, staging soak, and — since P0-48 went external-only — Actions billing in good standing, one observed green run of the release workflow onmain, and branch-protection administration to require it.EXT-LEGAL-01— counsel-approved exact agreements, thresholds, retention, claims, screening, country variants, and re-execution policy.EXT-COMMERCIAL-01— signed prices, floors, commitment/overage terms, transfer/commission tiers, credit inputs, and claims wording.EXT-PROVIDER-01— selected provider contracts, endpoints, credentials, sender/webhook configuration, and posting model.EXT-PROVISION-01— live provisioning/usage/teardown contract, credentials, and SKU-entitlement mappings.EXT-TAX-01— approved tax, exemption, accounting, recognition, credit, dunning, country, and payment-term policies/mappings.EXT-DOMAIN-01— production/preview/custom domains, DNS, TLS, callbacks, webhook URLs, and sender authentication.EXT-BRAND-01— final licensed assets and claims/legal-approved brand copy.EXT-APPROVERS-01— named qualified queue owners, primary/backup approvers, escalation contacts, response targets, and on-call roster.EXT-TEARDOWN-01— written activation authority and scope for automated teardown plus two distinct approvers; automation remains off.EXT-MARKETPLACE-01— AWS/Azure/GCP enrollment, seller/payout/tax profiles, permissions, feeds, credentials, and commercial approval.EXT-MIGRATION-01— immutable production snapshot/access, approved handling and quiet window, two approvers, communications, and rollback boundary.
These are absent capabilities rather than defects, recorded because the portal is sold as a reason to buy and each gap is one a buyer or a buyer's agent meets before a human does.
- The trust surface exists now, and is honest about what it is not.
6cf5f26added/trust: a register of controls whose page states plainly that no SOC 2 report, ISO 27001 certificate, PCI attestation, HIPAA assurance or FedRAMP authorization is held or claimed, and whose integration list refuses the subprocessor-schedule reading by name. Two things about how it got there are the reason to trust it: the page's original guarantee — that the build fails if a control's evidence stops supporting it — was attacked with fabricated controls, three got through a strengthened guard, and the sentence was lowered to what the guard can actually detect rather than the guard being declared stronger than it is; and two false customer-visible security claims were deleted rather than defended. What is still missing is what needs external inputs: an attestation, a real subprocessor schedule, and a data-processing agreement, underEXT-LEGAL-01andEXT-ACC-01. - The API reference is published; credentials and a sandbox are not.
6cf5f26added/developers, rendering the generated contract publicly — both the page and/developers/openapi.jsonare in the unauthenticated path list, asserted by test, because an integrator evaluating the API has no account yet — with a plain statement of what is not yet available to an integrator. What remains absent is customer credential management and a sandbox, so the programmatic buying path is now documented but still not self-servable. - No machine-readable quote or price surface for a buyer's agent. Agent-led purchasing now has published protocols, and industry forecasts put roughly a fifth of business sellers in front of agent-led quote negotiation before the end of 2026. The pricing engine, guardrails and quote lifecycle needed to answer such a request already exist; what is missing is a contract that presents them to a caller that is not a browser.
- Accessibility is now a supervised obligation and the evidence is not
reproducible. The European Accessibility Act has applied since 28 June 2025,
2026 is the first full year of national supervision, and EN 301 549 v4.1.1
incorporating WCAG 2.2 is expected during 2026. The repository's Axe, reflow,
text-spacing, zoom and reduced-motion coverage is more thorough than most
commercial platforms ship, but the claim still cannot be demonstrated on
demand. The reason has changed: the accessibility-adjacent browser failures
P0-48 reported were fixtures and a non-reproducing drawer report, both now
disposed of, and the structural gaps this entry used to point at — the
skip-link target, route announcements, the queue table below 48rem, the
combobox — were closed in
5c0f727. What remains is that no run has been observed: Actions is billing-blocked, so the evidence exists only as local captures, and that isEXT-ACC-01's remainder, not a repository one.
- Optional shared-session/account linking with the existing Fil One product.
- Additional locales, currencies, payment rails, marketplaces, and providers after their legal/commercial/operational approval.
- Deeper distribution trees if a future channel program requires them.
- Bidirectional CRM editing or write-enabled support only after a new ownership/security ADR; the current support feed is deliberately read-only.
- Reporting dimensions beyond the ten §17 reports, and any BI export. P0-42 completed the ten, so this is now unblocked rather than waiting.
- The exception-queue vocabulary is settled and unified by P0-43 and enforced by a database check constraint, so the P2 item that waited on it is done; what remains under P2 is only the wider single-package consolidation of the other duplicated vocabularies named in the P1 list.
- Per-user and per-account notification preferences beyond what P0-46 shipped. Deliveries now have a record and the account surface has preferences; the remaining scope is per-user granularity.
- Usage ingestion from a second orchestrator source, after the first source
contract is live under
EXT-PROVISION-01.