Skip to content

Commit e4dccc7

Browse files
V48 Gate 3 (specification-implementation): deposit review UX batch — dispatch-owned detail, telemetry error banner, repo/Obfuscations anchoring, clickable rows, /packs scoping, and fix background-dispatch survival on Vercel
Live QA on the deposit pipeline surfaced a batch of review-UX gaps and one correctness bug in the synthesis dispatch route: - Synthesize options hides once a run's detail owns the page (Back is the explicit path to a new dispatch), and the loaded run's failure surfaces through the telemetry log's own error banner (Retry/Dismiss) instead of a separate Obfuscations-pane alert — closing QA F19, which the spec already claimed was implemented but never was. - VCSFileTreePicker gained root-level Select all / Clear all (a directory selection already prefix-covers its subtree downstream, so no recursive fetch is needed). - Obfuscations configurations and repository selections can now be anchored into the activity ledger and reloaded on a later run, mirroring the existing repository-anchor primitive (V48-Gate3-F13/F17/F18). - Deposit pipelines table rows are fully clickable (not just the first cell); /packs is scoped to actual AssetPacks only (network-scope depository/settled types), leaving /deposits as the full activity stream. - V48-Gate3-F31: the dispatch route returned its response and continued the synthesis as a bare `void runSynthesis()` — Vercel can freeze a Function instance right after its response is sent, with no guarantee a non-awaited continuation survives. This is the same failure class the orphan sweep's own header already named as its reason for existing. Wrapped both background continuations in `waitUntil` (@vercel/functions) so a dispatched run always finishes and finalizes its row. - Adopted a standing practice: inline comments citing a QA finding must carry the fully-qualified `[VERSION]-Gate[N]-F[ID]` tag, matched by the same tag in the QA ledger, so code<->spec traceability is grep-able in both directions (AGENTS.md; retrofitted existing bare F-tags). - Cleared 8 dead-code diagnostics in DepositPageClient.tsx. Verified: tsc --noEmit 0 across uapi; full uapi jest suite green (158/159 suites, 637/638 tests, 1 pre-existing skip); eslint clean on touched files. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent b1f1e1e commit e4dccc7

24 files changed

Lines changed: 1095 additions & 189 deletions

AGENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
- Do not push work directly to `main`. Create a version base branch for each draft target, such as `version/v28`, then create scoped gate branches from that version branch. Gate branches must be prefixed with the gate number, such as `v28/gate-3-read-fit-workflow` or `v28/gate-8-promotion-proof`. Pull-request each closed gate back into the version branch. Pull-request the version branch into `main` only when all gates are closed and the version is formally promoted as canon. The default branch is protected by the `Bitcode Core Contributions` ruleset and requires pull requests plus verified signatures.
88
- Gate pull request titles must begin with the uppercase version and gate prefix, followed by a concise topical title, for example `V29 Gate 5: AssetPack Disclosure Rights And Preview Depth`. Version-promotion pull requests must begin with the uppercase version and name canonical promotion.
99
- Every commit and gate pull-request title must declare its specification/implementation category as a parenthetical immediately after the version and gate prefix — exactly one of `(specification-only)`, `(implementation-only)`, or `(specification-implementation)` — for example `V48 Gate 3 (implementation-only): Rename the deposit input to Obfuscations` or `V48 Gate 3 (specification-only): Record the SDIVF architecture in the V48 notes`. `specification-only` changes only specification artifacts (the `BITCODE_SPEC_*` family and version notes); `implementation-only` changes only implementation (code, tests, tooling, scripts); `specification-implementation` changes both in lockstep. This keeps the specification↔implementation relationship of every change auditable and enforces Complete Implementation Derivability (`BITCODE_SPECIFYING.md` §2) commit by commit. A change that touches both kinds must be a single `specification-implementation` commit or be split into a `specification-only` and an `implementation-only` commit — never an unlabeled mix.
10+
- Any inline code comment that cites an accepted QA finding shorthand must always carry the fully-qualified tag `[VERSION]-Gate[N]-F[ID]` (e.g. `V48-Gate3-F26-B`), never a bare `F26-B`-style tag. The same fully-qualified tag must always be discoverable in the specification/QA ledger file(s) (e.g. the finding's own `### V48-Gate3-F26` heading in `BITCODE_V48_QA.md`), so a reader can go from either direction — code comment to spec entry, or spec entry to every citing code comment — with a single grep. This keeps finding-to-fix traceability intact without requiring the surrounding file or commit to already establish which version/gate is active.
1011
- Once implementation starts on a gate branch, do not stop at partial progress unless blocked by missing external input or explicit user pause. A gate branch is ready to stop only when the gate's acceptance criteria are implemented, specified, tested, documented, committed, pushed, and pull-requested for closure into the version branch.
1112
- Treat gate and promotion workflow health as part of gate closure. Gate pull requests into version branches must be green through the maintained gate-quality checks, and repository-wide canon-quality checks must remain greenable during draft work. Version pull requests into `main` must pass the version promotion workflow, which performs promotion-grade validations and commits the standalone `BITCODE_SPEC.txt` pointer change only after those validations pass.
1213
- Keep CI greenable rather than ceremonial. Required application CI uses root pnpm workspace installation and maintained uapi lint/typecheck/build/Jest coverage. Heavy legacy scans such as full DB/browser E2E, Storybook build, super-linter, and advanced CodeQL are opt-in by repository variables until their backing catalogs and service assumptions are maintained for required branch protection.

BITCODE_V48_QA.md

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

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ describe('deposit-mode preprocess context assembly', () => {
107107

108108
await synthesizeAssetPacksPipeline(input, execution);
109109

110-
// F20: the mode lives on the SHARED outer execution, resolvable from any
110+
// V48-Gate3-F20: the mode lives on the SHARED outer execution, resolvable from any
111111
// phase sibling via the upward walk.
112112
expect(synthesizeAssetPacksModeFromExecution(execution)).toBe('deposit');
113113
expect(synthesizeAssetPacksModeFromExecution(execution.child('probe'))).toBe('deposit');

pnpm-lock.yaml

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

uapi/app/api/deposit/synthesize-options/route.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { randomUUID } from 'crypto';
22

33
import { NextResponse } from 'next/server';
4+
import { waitUntil } from '@vercel/functions';
45

56
import { supabaseAdmin } from '@bitcode/supabase';
67
import { createClient } from '@bitcode/supabase/ssr/server';
@@ -115,8 +116,10 @@ export async function POST(request: Request) {
115116

116117
// Fire-and-forget: finalize runs orphaned by server restarts/crashed hosts
117118
// (rows stuck `running` with no stream activity) so they read as
118-
// `interrupted` instead of running forever.
119-
void sweepOrphanedExecutions(supabaseAdmin).catch(() => {});
119+
// `interrupted` instead of running forever. waitUntil (V48-Gate3-F31) keeps
120+
// this Vercel Function instance alive for the sweep even though the
121+
// response below doesn't wait on it.
122+
waitUntil(sweepOrphanedExecutions(supabaseAdmin).catch(() => {}));
120123
const sourceBranch = readString(body.sourceBranch);
121124
const sourceCommit = readString(body.sourceCommit);
122125
const obfuscations = readString(body.obfuscations);
@@ -252,13 +255,22 @@ export async function POST(request: Request) {
252255
}
253256
};
254257

255-
// F26-B: the synthesis runs the full formal hierarchy (many LLM calls), so it
258+
// V48-Gate3-F26-B: the synthesis runs the full formal hierarchy (many LLM calls), so it
256259
// must NOT be bound to this request's maxDuration. Dispatch it as a background
257260
// run (the local in-process harness host) and return the runId immediately; the
258261
// client tails source-safe telemetry and, on the completion event, reads the
259262
// persisted synthesis from the execution row output. Prod durability is the
260263
// Vercel Sandbox host (pipeline-hosts). The F25 per-call LLM timeout remains the
261264
// safety bound within the run.
265+
//
266+
// V48-Gate3-F31: the JSON response below returns (and the request completes)
267+
// long before this finishes. On Vercel, a Function instance is free to be
268+
// frozen/recycled once its response is sent — a bare `void runSynthesis()`
269+
// has no guarantee of surviving past that point, which is exactly how the
270+
// "crashed serverless boxes" orphaned runs above got orphaned in the first
271+
// place (see execution-orphan-sweep.ts). waitUntil keeps this invocation
272+
// alive until the promise settles, so the run either completes or fails
273+
// (and finalizes its row) instead of being silently killed mid-flight.
262274
const runSynthesis = async () => {
263275
try {
264276
await emitPhaseTransition(execution as never, 'deposit-option-synthesis', 'start', {
@@ -487,6 +499,6 @@ export async function POST(request: Request) {
487499
}
488500
};
489501

490-
void runSynthesis();
502+
waitUntil(runSynthesis());
491503
return NextResponse.json({ ok: true, executionId: runId, runId, status: 'dispatched' });
492504
}

0 commit comments

Comments
 (0)