Skip to content

Commit f51a7b2

Browse files
V48 Gate 3 (specification-implementation): pluralize routes (/deposits, /reads) + pipelines master-detail on both
Routes: /deposit -> /deposits and /read -> /reads across nav, footer, public copy, docs, btd, route models, domain contracts (OrganizationPolicyRoute), tests, and e2e specs; old paths remain as permanentRedirect shims that preserve query strings (covers the localStorage next-path auth flow). Master-detail: both pages gain a 'Pipeline runs' master table (surface="pipelines") — selecting a running row attaches its live SSE telemetry stream, selecting a completed row resumes persisted results from the run's output. Deposits adopts synthesis runs via a selection-transition guard (no adopt/dispatch races); Reads derives the telemetry mode from the run's own lens. Correctness hardening from the adversarial review wave: - usePipelineExecution resets all tail state on every runId change (kills cross-run event poisoning) - terminal signals only honored when attributed to the attached run (execution-row id match), with persisted row status as the fallback for completed rows that never emit a completion event - dispatch route guards client-minted runIds: colliding ids 409 run_id_conflict before any write (no row hijack), then stamps the synthesis context at dispatch so running rows are lens-identifiable - 'interrupted' is now a terminal status for the SSE tail; activity mapper falls back to context repositoryFullName + lens override for deposit-option-synthesis rows Spec: BITCODE_SPEC_V48_NOTES.md routes-pluralize + master-detail sections, incl. run-id guard, terminal-state attribution, and the known read-gate gap (sandbox read runs don't persist lens-stamped rows yet). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 2e280a1 commit f51a7b2

64 files changed

Lines changed: 951 additions & 464 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

BITCODE_SPEC_V48_NOTES.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,47 @@ inventory/exclusions, setup obfuscation guidance — all invisible to their cons
502502
to the route's completion read); the Gate-3 correction re-homes cross-phase artifacts
503503
onto the shared execution and pins the producer/consumer store contract with tests.
504504

505+
### Product routes pluralize + master-detail pipelines (Garrett, 2026-07-03)
506+
507+
The lens routes RENAME to plural — `/deposit``/deposits`, `/read``/reads`
508+
(param-preserving redirect shims stay at the old paths for stale links and
509+
in-flight auth next-paths; `/api/*` route paths are unchanged). Both pages
510+
become MASTER-DETAIL:
511+
512+
- **Master: a pipelines table** (the shared transactions-table stack,
513+
lens-preset filters) over the account's executions rows; row selection is
514+
the URL `transactionId` (runId alias), so selection survives reload.
515+
- **Detail: selection connects to the run.** Selecting a RUNNING synthesis
516+
run reattaches its live stream (the history+stream tail attaches to any
517+
runId); selecting a COMPLETED run resumes its persisted results from the
518+
execution row output; failed/interrupted runs surface their terminal state
519+
with the historical log attached. On /deposits the detail is the Telemetry
520+
+ Options review; /reads gains the same Telemetry stack (log, pills, run
521+
clock, iteration marker, readiness verdict) for the selected pipeline run.
522+
- **Lens identity at dispatch:** the deposit dispatch route stamps the
523+
synthesis context (source/workbench/route/pipelineCore/synthesisMode +
524+
repository coordinates) onto the executions row AT DISPATCH (upsert), so
525+
running rows are lens-identifiable in the tables instead of context-null
526+
until completion. New rows stamp `route: '/deposits'`. Because the client
527+
may supply the runId, the route guards the id BEFORE any write: an id
528+
colliding with an existing row (any owner) is rejected 409
529+
`run_id_conflict` — no hijack, no double-dispatch into one row.
530+
- **Terminal-state attribution:** the run tail resets on every runId change
531+
and the detail only trusts terminal signals attributed to the attached run
532+
(execution-row match), with the persisted row status as the terminal
533+
fallback (completed rows without a completion event; interrupted rows via
534+
the orphan sweeper — 'interrupted' is now a terminal status for the SSE
535+
tail).
536+
- **Known read-gate gap:** read-lens pipeline runs dispatched through the
537+
sandbox harness do not yet persist their own lens-stamped executions rows
538+
(structured mode); the /reads table lists what exists and the telemetry
539+
detail attaches to any asset-pack pipeline row, labeled by the RUN's lens.
540+
Read dispatch persistence lands with the read gate.
541+
542+
Checker posture: all gate checkers literal-matching the old routes are
543+
era-pinned (V43–V47) and do not run at the current pointer; frozen `.bitcode/`
544+
era artifacts and promoted spec families keep the historical route names.
545+
505546
### PTRR step output schemas — steps validate against STEP schemas (Garrett, 2026-07-03)
506547

507548
Step outputs validate against STEP schemas, not the full agent schema — for all

BITCODE_V48_QA.md

Lines changed: 15 additions & 15 deletions
Large diffs are not rendered by default.

packages/pipelines/asset-pack/src/__tests__/deposit-agent-store-contract.test.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
// EXACT namespace:key pairs downstream consumers read (implementation reads
88
// discovery:*/setup:inputComprehension; validation reads implementation:options;
99
// the ReadyToFinish gate reads validation/implementation:issues; Finish reads
10-
// implementation:options/assetPack/summary; the /deposit route reads
10+
// implementation:options/assetPack/summary; the /deposits route reads
1111
// implementation:options). This is the CROSS-PHASE STORE-VISIBILITY LAW:
1212
// sequential() runs every phase on an ISOLATED seq-N sibling and findUp walks
1313
// ANCESTORS only, so a producer storing on its own sibling subtree is invisible
@@ -263,7 +263,7 @@ describe('deposit agent context/store contract', () => {
263263
expect(result.summary).toBe('Synthesized 2 measured deposit AssetPack patch(es).');
264264
expect(result.assetPack).toEqual({ repository: REPOSITORY });
265265

266-
// implementation:options — the store the /deposit route's completion read
266+
// implementation:options — the store the /deposits route's completion read
267267
// and the deposit validation agent consume; implementation:assetPacks — the
268268
// SAME array. Stored on the SHARED execution (cross-phase law): the route
269269
// holds the root, so its `execution.get('implementation','options')`
@@ -419,7 +419,7 @@ describe('deposit agent context/store contract', () => {
419419
});
420420

421421
describe('Finish — runUploadAssetPacksForReviewAgent', () => {
422-
it('deposit mode: uploads implementation:options read from the shared parent for /deposit admission review', async () => {
422+
it('deposit mode: uploads implementation:options read from the shared parent for /deposits admission review', async () => {
423423
const shared = new Execution('pipeline-root');
424424
storeSynthesizeAssetPacksMode(shared, 'deposit');
425425
const options = [measuredPatchOption()];
@@ -435,7 +435,7 @@ describe('deposit agent context/store contract', () => {
435435
expect(result.success).toBe(true);
436436
expect(result.deliveryMechanism).toBe('bitcode-review-upload');
437437
expect(result.review).toEqual({
438-
surface: '/deposit',
438+
surface: '/deposits',
439439
reviewFor: 'deposit-admission',
440440
decision: 'pending-user-review',
441441
});
@@ -447,13 +447,13 @@ describe('deposit agent context/store contract', () => {
447447
// where postprocess and the run-level surfaces resolve them.
448448
expect(shared.get('finish', 'uploadForReview')).toMatchObject({
449449
deliveryMechanism: 'bitcode-review-upload',
450-
review: { surface: '/deposit', reviewFor: 'deposit-admission' },
450+
review: { surface: '/deposits', reviewFor: 'deposit-admission' },
451451
options,
452452
});
453453
expect(shared.get('finish', 'deliveryMechanism')).toBe('bitcode-review-upload');
454454
});
455455

456-
it('read mode: uploads the implementation synthesis artifacts for /read purchase review', async () => {
456+
it('read mode: uploads the implementation synthesis artifacts for /reads purchase review', async () => {
457457
const shared = new Execution('pipeline-root');
458458
storeSynthesizeAssetPacksMode(shared, 'read');
459459
const artifacts = { summary: 'Read synthesis artifacts.', proofEvidence: ['evidence'] };
@@ -464,13 +464,13 @@ describe('deposit agent context/store contract', () => {
464464
const result = await runUploadAssetPacksForReviewAgent({}, finishChild);
465465

466466
expect(result.review).toEqual({
467-
surface: '/read',
467+
surface: '/reads',
468468
reviewFor: 'purchase',
469469
decision: 'pending-user-review',
470470
});
471471
expect(result.artifacts).toBe(artifacts);
472472
expect(shared.get('finish', 'uploadForReview')).toMatchObject({
473-
review: { surface: '/read', reviewFor: 'purchase' },
473+
review: { surface: '/reads', reviewFor: 'purchase' },
474474
artifacts,
475475
});
476476
});

packages/pipelines/asset-pack/src/__tests__/deposit-asset-pack-option-admission.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ describe('deposit-asset-pack-option-admission', () => {
1111
repositoryFullName: 'engineeredsoftware/ENGI',
1212
sourceBranch: 'main',
1313
sourceCommit: '31bbc0c5227b6b3aed5d107fd8507d35ec22970a',
14-
sourcePathHints: ['uapi/app/deposit/DepositPageClient.tsx'],
14+
sourcePathHints: ['uapi/app/deposits/DepositPageClient.tsx'],
1515
depositoryDemandSignals: [{ id: 'depository-demand', weight: 0.8 }],
1616
readingDemandSignals: [{ id: 'reading-demand', weight: 0.86 }],
1717
});

packages/pipelines/asset-pack/src/__tests__/deposit-asset-pack-option-policy.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ function reviewableSynthesis() {
1111
sourceCommit: '31bbc0c5227b6b3aed5d107fd8507d35ec22970a',
1212
obfuscations: 'Prepare source-safe deposit AssetPack options for non-critical reusable proof infrastructure.',
1313
sourcePathHints: [
14-
'uapi/app/deposit/DepositPageClient.tsx',
14+
'uapi/app/deposits/DepositPageClient.tsx',
1515
'packages/pipelines/asset-pack/src/deposit-asset-pack-options.ts',
1616
],
1717
depositoryDemandSignals: [{ id: 'depository-gap', label: 'Depository needs reviewable source supply', weight: 0.8 }],
@@ -39,7 +39,7 @@ describe('Deposit AssetPack option policy', () => {
3939

4040
expect(report.schema).toBe('bitcode.deposit.asset-pack-option-policy-report');
4141
expect(report.policy).toBe('DepositAssetPackOptionPolicy');
42-
expect(report.route).toBe('/deposit');
42+
expect(report.route).toBe('/deposits');
4343
expect(report.optionCount).toBe(3);
4444
expect(report.reviewablePositiveRoiCount).toBeGreaterThan(0);
4545
expect(report.aggregatePolicy).toMatchObject({

packages/pipelines/asset-pack/src/__tests__/deposit-asset-pack-options.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ describe('Deposit AssetPack option synthesis', () => {
4242
expect(synthesis.schema).toBe('bitcode.deposit.asset-pack-option-synthesis');
4343
expect(synthesis.pipeline).toBe('DepositAssetPackOptionSynthesis');
4444
expect(synthesis.reviewBoundary).toMatchObject({
45-
route: '/deposit',
45+
route: '/deposits',
4646
defaultDecisionState: 'pending-depositor-review',
4747
approvedOptionsAdmittedBy: 'future-gate7-deposit-option-review',
4848
sourceCriticalityDemandRoiPolicyOwnedBy: 'future-gate6-policy',

packages/pipelines/asset-pack/src/__tests__/depositor-earning-supply-intelligence.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ function reviewablePolicyReport() {
1212
sourceCommit: '31bbc0c5227b6b3aed5d107fd8507d35ec22970a',
1313
obfuscations: 'Prepare source-safe options likely to satisfy unfit Reading demand.',
1414
sourcePathHints: [
15-
'uapi/app/deposit/DepositPageClient.tsx',
15+
'uapi/app/deposits/DepositPageClient.tsx',
1616
'packages/pipelines/asset-pack/src/depository-supply-index.ts',
1717
],
1818
depositoryDemandSignals: [
@@ -57,7 +57,7 @@ describe('Depositor earning supply intelligence', () => {
5757

5858
expect(intelligence.schema).toBe('bitcode.deposit.earning-supply-intelligence');
5959
expect(intelligence.intelligence).toBe('DepositorEarningSupplyIntelligence');
60-
expect(intelligence.route).toBe('/deposit');
60+
expect(intelligence.route).toBe('/deposits');
6161
expect(intelligence.likelyDemand.state).toBe('strong-demand-opportunity');
6262
expect(intelligence.unfitNeedOpportunities.opportunityCount).toBe(1);
6363
expect(intelligence.earningStatements).toHaveLength(3);

packages/pipelines/asset-pack/src/__tests__/finish-upload-for-review.test.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const DEPOSIT_OPTIONS = [
2121
];
2222

2323
describe('upload-asset-packs-for-review Finish agent', () => {
24-
it('deposit: uploads the synthesized options for /deposit admission review (no PR fields)', async () => {
24+
it('deposit: uploads the synthesized options for /deposits admission review (no PR fields)', async () => {
2525
const root = new Execution('pipeline:finish-upload-deposit');
2626
storeSynthesizeAssetPacksMode(root, 'deposit');
2727
root.store('implementation', 'options', DEPOSIT_OPTIONS);
@@ -37,7 +37,7 @@ describe('upload-asset-packs-for-review Finish agent', () => {
3737
carried: 'from-validation', // input is spread through
3838
deliveryMechanism: 'bitcode-review-upload',
3939
review: {
40-
surface: '/deposit',
40+
surface: '/deposits',
4141
reviewFor: 'deposit-admission',
4242
decision: 'pending-user-review',
4343
},
@@ -61,7 +61,7 @@ describe('upload-asset-packs-for-review Finish agent', () => {
6161
expect(finishNode.findUp('finish', 'deliveryMechanism')).toBe('bitcode-review-upload');
6262
});
6363

64-
it('read: uploads the synthesis artifacts for /read purchase review', async () => {
64+
it('read: uploads the synthesis artifacts for /reads purchase review', async () => {
6565
const root = new Execution('pipeline:finish-upload-read');
6666
storeSynthesizeAssetPacksMode(root, 'read');
6767
const artifacts = {
@@ -75,7 +75,7 @@ describe('upload-asset-packs-for-review Finish agent', () => {
7575
expect(result).toMatchObject({
7676
deliveryMechanism: 'bitcode-review-upload',
7777
review: {
78-
surface: '/read',
78+
surface: '/reads',
7979
reviewFor: 'purchase',
8080
decision: 'pending-user-review',
8181
},
@@ -89,7 +89,7 @@ describe('upload-asset-packs-for-review Finish agent', () => {
8989

9090
const result = await runUploadAssetPacksForReviewAgent({}, root);
9191

92-
expect(result.review.surface).toBe('/read');
92+
expect(result.review.surface).toBe('/reads');
9393
expect(result.review.reviewFor).toBe('purchase');
9494
});
9595

packages/pipelines/asset-pack/src/__tests__/interface-disclosure-boundary.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ describe('Interface disclosure boundary', () => {
1515
expect(boundary).toMatchObject({
1616
schema: 'bitcode.interface-disclosure-boundary',
1717
routeVocabulary: {
18-
currentProductRoutes: ['/deposit', '/read', '/packs'],
18+
currentProductRoutes: ['/deposits', '/reads', '/packs'],
1919
compatibilityRedirects: {
2020
'/exchange': '/packs',
2121
},
@@ -122,7 +122,7 @@ describe('Interface disclosure boundary', () => {
122122
rowCount: boundary.rowCount,
123123
});
124124
expect(execution.get('interfaces/disclosure', 'routeVocabulary')).toMatchObject({
125-
currentProductRoutes: ['/deposit', '/read', '/packs'],
125+
currentProductRoutes: ['/deposits', '/reads', '/packs'],
126126
});
127127
expect(execution.get('interfaces/disclosure', 'proofRoots')).toMatchObject({
128128
boundaryRoot: expect.stringMatching(/^sha256:/u),

packages/pipelines/asset-pack/src/__tests__/organization-policy-wallet-authority.test.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
describe('OrganizationPolicyWalletAuthority', () => {
77
it('allows budgeted Reading BTC payment with role, grant, wallet authority, and approval', () => {
88
const statement = buildOrganizationPolicyWalletAuthority({
9-
route: '/read',
9+
route: '/reads',
1010
actorId: 'user-1',
1111
organizationId: 'org-1',
1212
teamId: 'team-core',
@@ -32,7 +32,7 @@ describe('OrganizationPolicyWalletAuthority', () => {
3232
});
3333

3434
expect(statement.schema).toBe('bitcode.organization.policy-wallet-authority');
35-
expect(statement.route).toBe('/read');
35+
expect(statement.route).toBe('/reads');
3636
expect(statement.budgetApproval.state).toBe('within-limit');
3737
expect(statement.walletAuthority.state).toBe('verified');
3838
expect(statement.actionStatements.find((entry) => entry.action === 'pay_btc_fee')).toMatchObject({
@@ -52,7 +52,7 @@ describe('OrganizationPolicyWalletAuthority', () => {
5252

5353
it('fails Reading payment closed when wallet authority is missing', () => {
5454
const statement = buildOrganizationPolicyWalletAuthority({
55-
route: '/read',
55+
route: '/reads',
5656
actorId: 'user-1',
5757
organizationId: 'org-1',
5858
organizationRole: 'admin',
@@ -76,7 +76,7 @@ describe('OrganizationPolicyWalletAuthority', () => {
7676

7777
it('allows deposit submission only after source criticality, deposit approval, wallet, and policy admit it', () => {
7878
const statement = buildOrganizationPolicyWalletAuthority({
79-
route: '/deposit',
79+
route: '/deposits',
8080
actorId: 'user-1',
8181
organizationId: 'org-1',
8282
teamId: 'team-supply',
@@ -106,7 +106,7 @@ describe('OrganizationPolicyWalletAuthority', () => {
106106

107107
it('blocks critical source deposit approval without exposing source-bearing payloads', () => {
108108
const statement = buildOrganizationPolicyWalletAuthority({
109-
route: '/deposit',
109+
route: '/deposits',
110110
actorId: 'user-1',
111111
organizationId: 'org-1',
112112
organizationRole: 'admin',

0 commit comments

Comments
 (0)