Gate 3 (synthesis pipeline correctness / deposit full-stack) is closed for
product law + implementation on v48/gate-3-synthesis-pipeline-correctness
with rebuild-alone canon in BITCODE_SPEC_V48.md §G3-1…G3-15. Optional live
smoke remains in the Gate 3 runbook §6; it does not block version-branch merge
when CI is green.
- Version:
V48 - Active canon during QA:
V47 - Posture: interactive local experiential QA of the first live commercial (testnet) experience; app runs locally (
pnpm -C apps/uapi dev:remote) against the staging Supabase project; wallet network testnet4 - Source-safety posture: source-safe evidence only; no secrets, protected source, provider payloads, wallet material, service-role keys, database credentials, or raw private prompts are serialized here.
ONE GitHub App for every environment and every account:
bitcode-github-auxiliary
(https://github.com/apps/bitcode-github-auxiliary).
- Install / uninstall this same app on any GitHub user or org for local, preview, staging, or production — do not maintain a separate staging app.
- All deploys and
.env.localmust use the same app credentials:GITHUB_APP_ID,GITHUB_PRIVATE_KEY,GITHUB_APP_CLIENT_ID,GITHUB_APP_CLIENT_SECRET, andNEXT_PUBLIC_GITHUB_APP_PUBLIC_URL=https://github.com/apps/bitcode-github-auxiliary(code fallback inapps/uapi/lib/github-app-url.tsalso names this slug). - Historical notes below that mention
bitcode-github-auxillary-stag-test, dual apps, or “production vs staging app” are superseded by this law. Stale dual-app installs on accounts should be uninstalled and reinstalled againstbitcode-github-auxiliaryonly.
THREE Supabase projects (Garrett, 2026-07-06 — supersedes the earlier
two-project model that mislabeled mwugicjpxmrtctvjghjg as staging-testnet):
-
staging-testnet — Supabase
tkpyosihuouusyaxtbau; the LIVEcustom:bitcode-bitcoinwallet auth provider is configured here (authorize → 302). Its provider OAuth endpoints point atbitcode.exchange(Authorization/Token/Userinfo/JWKS). Schema is BEHIND the current branch: it has the full execution + BTD-ledger schema (59 tables —executions,user_connections,vcs_repositories,btd_*) but still carriesdeliverable_pipeline_otf_instructions(Gate 3 drops it), has noprofilestable, and has noasset-pack-artifactsbucket — the current migrations must be applied to it (needs its DB password +supabase link). -
local-testnet — Supabase
mwugicjpxmrtctvjghjg(created 2026-07-05). This is where the 2026-07-05 bring-up actually landed: all 10 current migrations + theasset-pack-artifactsbucket. (The bring-up notes below that say "staging-testnet = mwugicjpxmrtctvjghjg" are the OLD mislabel — read them as local-testnet.) -
production-mainnet — Supabase
rinalyjfecxnmyczrpzo+ thebitcode.exchange/www.bitcode.exchangedomains; GitHub App is the sharedbitcode-github-auxiliary(see law above); mainnet posture. NOTE: the 2026-07-05 "production == staging" Vercel push pointed theproductionenv'sSUPABASE_URLatmwugicjpxmrtctvjghjg(local-testnet) — that must be corrected torinalyjfecxnmyczrpzowhen mainnet returns. -
local — for Gate-3 deposit QA,
localhost:3000develops AGAINST staging-testnet (tkpyosihuouusyaxtbau) so the wallet auth provider is available; browser-facing hops stay onlocalhost:3000, and the wallet OAuth server endpoints resolve throughwww.bitcode.exchange(the provider'sbitcode.exchangeURLs 307→www, method-preserving).apps/uapi/.env.localwas repointed totkpyosihuouusyaxtbauon 2026-07-06 (Supabase URL + anon / service / publishable / secret keys synced fromapps/uapi/.env.local.remote;SUPABASE_JWT_SECRETleft as its placeholder — vestigial, no app code reads it). GitHub App credentials must be the singlebitcode-github-auxiliaryapp (not the retired stag-test registration), withNEXT_PUBLIC_GITHUB_APP_PUBLIC_URL=https://github.com/apps/bitcode-github-auxiliaryandNEXT_PUBLIC_BITCODE_BITCOIN_NETWORK=testnet4..env.local.bak.*backups retain the prior state.
Bring-up state (2026-07-05): all 10 migrations applied to
mwugicjpxmrtctvjghjg (schema + asset-pack-artifacts bucket verified live);
Vercel env matrix pushed to staginglocal-testnet (Supabase URL/keys, shared
wallet-OAuth secret, GitHub App id/client/secret/private key,
testnet4 network, GoTrue callback URL); local .env.local flipped to the
staging-testnet project (old staging values retained as comments);
SUPABASE_JWT_SECRET confirmed vestigial (no app code reads it). The
2026-07-05 vercel-env run pushed the full matrix — including GitHub App
vars — to ALL THREE Vercel targets. 2026-07-14 law: every target must
use the same app
bitcode-github-auxiliary
credentials and
NEXT_PUBLIC_GITHUB_APP_PUBLIC_URL=https://github.com/apps/bitcode-github-auxiliary
(the retired stag-test app is no longer used). Preview is the target every
git push to a non-main branch builds against — see F30.
Pushing to the preview target requires Vercel CLI ≥ 54.20 (54.1.0 returned
git_branch_required for preview even with --yes; the local CLI was
upgraded 54.1.0 → 54.20.1). GITHUB_WEBHOOK_SECRET stays unfilled pending the
F11 webhook handler. Pending manual: dashboard URL config + custom provider +
GoTrue GitHub provider, GitHub App callback URLs, and the stale-var sweep
(production retains non-matrix vars from its mainnet configuration). A fresh
production/staging deploy picks up the new matrix; CLI vercel deploy from
this repo is impractical (2.3 GB local upload → EPIPE), so deploys go through
git push (Vercel clones) or vercel redeploy.
Bring-up automation: scripts/bringup-staging-testnet.sh
(migrate | vercel-env | verify) with secrets in
scripts/.env.staging-testnet (gitignored; template committed as
.example). The asset-pack-artifacts bucket + RLS ship as migration
20260705190000_asset_pack_artifacts_bucket.sql, so migrate covers
storage setup too.
Topology law: localhost:3000 for as much as possible;
testnet.bitcode.exchange only for functionality Supabase's cloud must
reach server-to-server.
- Auth URL configuration (new project): Site URL
http://localhost:3000; Redirect URLs (exact, query-free per the allow-list law):http://localhost:3000/tps/supabase/callback(+:3001variant if used). - Custom provider
custom:bitcode-bitcoin(manual configuration):- Authorization URL:
http://localhost:3000/tps/wallet/authorize— BROWSER hop, so localhost works and the wallet-proof page runs local code with local wallets. - Token URL:
https://testnet.bitcode.exchange/api/wallet/oauth/token— called SERVER-side by Supabase cloud; must be publicly reachable. - Userinfo URL:
https://testnet.bitcode.exchange/api/wallet/oauth/userinfo— same server-side constraint. - JWKS URI:
https://testnet.bitcode.exchange/.well-known/jwks.json. - Client ID
bitcode-bitcoin-wallet; Allow users without email: on.
- Authorization URL:
- Shared-secret law: the authorization code is a stateless HMAC token, so
ONE
BITCODE_BITCOIN_OAUTH_CLIENT_SECRETvalue must be shared by (a) the provider Client Secret in the new Supabase project, (b) thetestnet.bitcode.exchangedeployment env, and (c) local.env.local— a locally minted code must verify at the testnet token endpoint. - testnet host requirement:
testnet.bitcode.exchange's env must accept the new project's callback as a redirect target (NEXT_PUBLIC_SUPABASE_URL/SUPABASE_URLpointing at the new project, or the project origin listed inBITCODE_BITCOIN_OAUTH_ALLOWED_REDIRECT_ORIGINS) —isAllowedBitcoinWalletOAuthRedirectUrireads those. - Local
.env.localswitch:NEXT_PUBLIC_SUPABASE_URL+ anon key → the new project;BITCODE_BITCOIN_OAUTH_CLIENT_SECRET→ the shared value. - Browser prerequisite (root-caused 2026-07-05): Chrome's "Pop-ups and
redirects" must be allowed for the app origin — "Don't allow" silently
cancels the programmatic
signInWithOAuthnavigation (URL built, request never leaves the page; the panel's 8s watchdog + manual-continuation link surface exactly this).
Project URL https://mwugicjpxmrtctvjghjg.supabase.co; GoTrue callback
https://mwugicjpxmrtctvjghjg.supabase.co/auth/v1/callback. Data API enabled.
Dashboard checklist (new project):
- Apply the repo schema: run
supabase/migrations/*against the new project (9 files,001_v26_production.sqlonward), then thev48_qa_*scripts work as on staging. - Auth → URL Configuration: Site URL
http://localhost:3000; Redirect URLs (exact, query-free):http://localhost:3000/tps/supabase/callback,http://localhost:3001/tps/supabase/callback,https://testnet.bitcode.exchange/tps/supabase/callback. - Auth → Custom provider (
custom:bitcode-bitcoin, manual configuration):- Display name: Bitcode Bitcoin Wallet
- Authorization URL:
http://localhost:3000/tps/wallet/authorize(browser hop — flip tohttps://testnet.bitcode.exchange/tps/wallet/authorizewhen QAing the hosted surface; the setting is project-global) - Token URL:
https://testnet.bitcode.exchange/api/wallet/oauth/token - Userinfo URL:
https://testnet.bitcode.exchange/api/wallet/oauth/userinfo - JWKS URI: leave empty — the app serves no
/.well-known/jwks.json(oauth2 type; no id_token is issued, GoTrue uses the userinfo endpoint) - Client ID
bitcode-bitcoin-wallet; Client Secret = the shared HMAC secret; Scopesprofile, wallet:bitcoin; Allow users without email ON.
- Storage → Files: create bucket
asset-pack-artifacts(PRIVATE) — the raw AssetPack artifact store; see the artifact-storage contract below. - Auth → GitHub provider (GoTrue): client id/secret from the
bitcode-github-auxillary-stag-testapp's OAuth credentials; the app's callback/setup URLs point athttps://testnet.bitcode.exchange(webhook likewise if used). - Database SSL enforcement: fine off for dev; enable before any production promotion of this project's pattern.
Vercel env (testnet.bitcode.exchange environment):
NEXT_PUBLIC_SUPABASE_URL+SUPABASE_URL=https://mwugicjpxmrtctvjghjg.supabase.coNEXT_PUBLIC_SUPABASE_ANON_KEY+SUPABASE_ANON_KEY/SUPABASE_PUBLISHABLE_KEY= new project anon/publishable keySUPABASE_SERVICE_ROLE_KEY(+SUPABASE_SECRET_KEYwhere read) = new project service-role/secret keySUPABASE_JWT_SECRET= new project JWT secretBITCODE_BITCOIN_OAUTH_CLIENT_SECRET= the shared HMAC secret (same value as the dashboard provider Client Secret and local.env.local)BITCODE_BITCOIN_OAUTH_SUPABASE_CALLBACK_URL=https://mwugicjpxmrtctvjghjg.supabase.co/auth/v1/callbackNEXT_PUBLIC_BITCODE_BITCOIN_NETWORK=testnet4- carry over the non-Supabase vars (GitHub app, provider keys) from staging.
No
BITCODE_BITCOIN_OAUTH_ALLOWED_REDIRECT_ORIGINSneeded — the redirect allow-check derives fromSUPABASE_URL/NEXT_PUBLIC_SUPABASE_URL.
Local .env.local: prepared commented block sits under the Supabase
section (six values flip to the new project + callback URL); uncomment and
paste keys, restart dev.
Code edits: none required for the auth flow — the wallet authorize page, authorization-code mint, token, and userinfo endpoints are host-agnostic (env-driven). JWKS is intentionally unset (no such route exists).
Raw AssetPack artifacts are ALWAYS .patch files (an AssetPack is a measured
patch) and are stored in Supabase Storage FILE buckets, not in database rows:
- Bucket
asset-pack-artifacts, PRIVATE. Object path law:<user_id>/<run_id>/<option_id>.patch— owner id first segment so RLS stays trivial. - RLS: owner-only insert/select via
(storage.foldername(name))[1] = auth.uid()::textANDstorage.extension(name) = 'patch'; buyers get time-limited signed URLs minted server-side (service role) only after settlement — the source-safety law (withheld until BTC finality + BTD rights transfer) is enforced by the bucket being private, never by obscurity. - Bucket-level upload restrictions: allowed content type
text/x-patch/text/plain; conservative max object size. - Implementation lands with the upload/delivery work in Gates 4-5 (the read Finish→upload step and /packs deliveries write to and read from this bucket).
Subsequent-gate note: depository VECTORS (search embeddings) move to Supabase Vector buckets (S3-backed similarity indexes) in a later V48 gate — the current pgvector/document-root posture stays until that gate opens.
- Faucet (testnet4): https://coinfaucet.eu/en/btc-testnet4/
- Faucet/exchange (testnet coins): https://altquick.com/exchange/
- Explorer (testnet4): https://mempool.space/testnet4 — tx view at
…/testnet4/tx/<txid>, address view at…/testnet4/address/<address> - The in-app "BTC in wallet" card reads the bound address via
/api/wallet/btc-balance(mempool.space, testnet4-first with testnet3 fallback for the ambiguoustestnetbinding label).
Run in the Supabase SQL editor; all auto-target the most recent custom:bitcode-bitcoin user (purge-proof, no UUID editing). Run-after map:
| Script | Run after |
|---|---|
v48_qa_01_auth_user_identity |
wallet sign-up / sign-in |
v48_qa_02_profile_wallet_binding |
first sessioned page load (bridge mount) or any wallet persist |
v48_qa_03_user_connections_token_safe |
wallet binding write or GitHub install callback |
v48_qa_04_repository_inventory |
Externals pane load post-GitHub-connect (repo sync) |
v48_qa_05_track1_readiness_rollup |
anytime — one-row Track 1 summary |
v48_qa_06_deposit_activity |
each /deposits action (connect, synthesize, approve, deposit) |
v48_qa_07_depository_admission_evidence |
deposit approval (Depository admission roots/index state) |
v48_qa_08_recent_errors |
whenever a flow misbehaves or qa06 shows has_error |
Track 3-4 scripts (BTD ledger, settlement, pack journaling) get added when those tracks open; the v28_qa_terminal_* set remains the historical reference.
- Identity, authentication, sign up/in, Auxillaries (GitHub, wallet connections)
- Depositing (connect knowledge, request AssetPack syntheses, review, deposit)
- Reading (connect knowledge, request Read, review Need, review Fits, buy Fits)
- Ledgerized journaling (replayability, auditability,
/packsUX/UI, Auxillaries history)
- Severity: low (no runtime consumers; only the
uapi devscript's prebuild chain invokes it) - Observed:
pnpm -w --filter @bitcode/generic-doc-comments-doc-code buildfails with TS6059/TS6307 rootDir violations — the package extends the root tsconfig whosepathsmap@bitcode/*to package source trees, sotsc -bpullsagent-generics/srcinto the program. - Disposition: candidate for repair or retirement in a V48 gate;
dev:remoteunaffected.
- Severity: medium (Supabase: "may produce undefined behavior when used concurrently under the same storage key" — credible source of auth/session flakiness)
- Observed on first page load (
/): threeGoTrueClientconstructions —packages/supabase/src/index.ts:87module-level client, reaching the browser bundle viapackages/btd→packages/api/src/routes/auxillaries-contract.ts→auxillary-pane-meta.ts→hooks/useUserData.ts→WalletSessionPersistenceBridge.packages/artifacts/src/artifacts.ts:29module-level client, viapackages/logger→packages/ormmodels →useUserData(note: ORM in the client bundle at all is a packaging smell).uapiAuthProvider→createBrowserClient(the legitimate one).
- Disposition: V48 fix candidate — single browser client (or lazy/server-only module-level clients) and pruning server-only packages from the client graph.
- Observed:
contentscript.js/inpage.jsMaxListenersExceededWarning, ObjectMultiplex orphaned-stream warnings,Unable to set StacksProvider— emitted by browser wallet extensions (provider injection collisions), not by the app. - Disposition: no action; QA hygiene note — run with a single wallet extension enabled to keep sats-connect provider selection deterministic.
- Severity: medium (user-facing identity confusion; canonical law is "a Bitcoin wallet is the minimum identity/authentication path for staging; email remains optional notification and recovery contact after wallet identity exists")
- Canonical path (calibrated): nav "Connect Wallet" guest CTA (gated by
DISABLE_CREATE_ACCOUNT, default-open in dev/QA) → Auxillaries SignUpWindow → Wallet pane → sats-connect signature (testnet4) →signInWithOAuth({ provider: 'custom:bitcode-bitcoin' })→ Supabase session →/tps/supabase/callback. - Residue inventory (eradication candidates for a V48 gate):
apps/uapi/app/login/route family (page, head, magic-linkactions.ts,callback/LoginCallbackClient.tsx) — live legacy email-first sign-in surface at/login.AuxillariesLoginPane.tsx:76mounts legacy email-OTPLoginForminside the canonical wallet-first pane.components/base/bitcode/auth/PhoneSSO.tsx— phone-OTP sign-in, routes to/login.AuxillariesProfilePane.tsx:283-289—signInWithOtpused for the optional-email flow; needs disambiguation from authentication semantics.SocialAccountLinker.tsx— genericsignInWithOAuth; keep only if scoped to account linking, not session minting.- Root cause:
/loginwas never classified by the V47 Gate 2 feature-excess audit (absent from.proofs/v47/feature-excess-alignment-audit.json), so it escaped the launch freeze.
- Production note: deployed launch requires
NEXT_PUBLIC_BITCODE_ENV=testnet(or explicit flag) orDISABLE_CREATE_ACCOUNTdefaults ON and the Connect Wallet CTA is disabled.
F5 — Query string on redirect_to defeats GoTrue allow-list matching; auth code strands on the Site URL origin; no session is ever minted (FIXED in code)
- Severity: high (track-1 blocker; also breaks production sign-in from
www.bitcode.exchange, not just localhost) - Observed (two attempts): Connect Wallet → Leather signature OK → Supabase created the auth user (
custom:bitcode-bitcoin, subbitcoin:testnet:tb1p6x7…) → both callbacks landed onbitcode.exchangeinstead of localhost;auth.users.last_sign_in_at: nullboth times;user_profilestrigger-created with all-null binding fields; localhost showed sessionless "connected" chrome. - Root cause (proven by curl probes against staging GoTrue, no wallet needed —
/auth/v1/authorizeto capture flowstate, then/auth/v1/callback?code=garbage&state=…to reveal the stored redirect):http://localhost:3000/tps/supabase/callback(exact allow-list entry) → honored.- The same URL with
?next=%2Fauxillaries%2Fwallet(what the app actually sent) → rejected → Site URL fallback (https://bitcode.exchange). https://www.bitcode.exchange/tps/supabase/callback?next=…→ also rejected → apex fallback (production sign-in equally broken).- The dashboard glob
http://localhost:3000/**matches nothing (bare root rejected too); the Site-URL hostname (apex) is exempt from matching entirely, which is why only apex "worked". - GoTrue allow-list matching is exact-string against entries; any query string defeats the match. The first-attempt diagnosis (missing allow-list entry) was wrong — the entries were present and correct.
- Failure chain after fallback: auth code lands on apex root →
apps/uapi/app/page.tsx:40forwards stray?codeto/tps/supabase/callback→exchangeCodeForSessionfails because the PKCEcode_verifierlives in the initiating origin's storage →LoginCallbackClient.tsxemits/?loginError=server_error&loginErrorDescription=…code verifier should be non-empty. All observed symptoms reproduce from this one defect. - Fix (code, implemented):
apps/uapi/lib/supabase-auth-redirect.ts—redirect_tois now always the query-free${origin}/tps/supabase/callback; the post-auth destination travels via origin-local storage and is consumed once byLoginCallbackClient(explicit?next=still wins for back-compat). Call sites converted:AuxillariesWalletConnectionPanel,SocialAccountLinker,SocialLoginButton. Regression-pinned byapps/uapi/tests/supabaseAuthRedirect.test.ts. - Dashboard: no change required — exact entries for localhost and www callbacks already exist and exact matching now applies; glob entries can stay but are ineffective.
- Code-hardening candidates (still open): (a) the app renders "connected" wallet chrome from client-side wallet-session persistence even when
hasUser: false— sessionless state should not look signed-in; (b)loginError/loginErrorDescriptionquery params surfaced only as raw URL params — no visible error UI; (c) callback flow should fail loudly when the PKCE verifier is absent. - Retest (local, 2026-06-12): VERIFIED — session minted (
auth.users.last_sign_in_atset ~3s after creation, user267ccb92…), callback returned tolocalhost:3000, signed-in nav chrome with Sign out renders. Post-auth landing initially hit/packsbecausebuildAuxillariesRoutePathtargets the legacy overlay root (see F8); destination now pinned to/packs. - Note on the production
/tps/wallet/authorizehop: expected and correct even for localhost sessions. The custom provider's authorize/token endpoints are registered once per Supabase project (project-level config, one URL), so every client transits the registered production origin. The hop is PKCE-safe: the verifier never leaves the initiating origin; the provider page only ferriescode+stateback to GoTrue, which then redirects to the initiating origin's callback. A localhost authorize URL would require a separate Supabase project (or local GoTrue) — unnecessary. - Residual (FIXED in code — F5a):
user_profilesbinding fields remained all-null after sign-in; Wallet Auxillary showed "No Bitcoin wallet connected"/"Binding repairable" and Profile readiness Blocked.- Trace: the canonical binding write is
POST /api/wallet/authenticate→settings.walletBinding(hydrated into the virtualwallet_*fields byhydrateBitcodeProfile; the raw columns staying null is expected). That route was never called in the canonical sign-up: it 401s without a session, the wallet signature happens on the prod/tps/wallet/authorizepage (not the initiating origin), soWalletSessionPersistenceBridgefinds no locally staged proof to replay after the session lands. GoTrue also drops the provider's custombitcoin_*userinfo claims fromidentity_data(onlysub/name/preferred_usernamesurvive), so the rich proof material is not recoverable client-side. - Fix:
/api/wallet/authenticategains ansource: 'oauth-identity'mode that derives the binding server-side from the session's GoTrue-verifiedcustom:bitcode-bitcoinidentity (address+network parsed from the identitysub, provider from the userinfo label, no client-trusted wallet fields), writessettings.walletBinding(proofKind: 'provider_session', statuspending) +user_connections(auth_source: 'bitcoin_wallet_oauth_identity'), setsusername(clears the blockingprofile.identity_missing), and no-ops idempotently when the binding already matches.WalletSessionPersistenceBridgetriggers it whenever a wallet-backed session exists with nothing replayable staged locally, then mirrors the result into the local wallet identity so the Wallet Auxillary shows the connection. Tests:tests/api/walletAuthenticateRoute.test.ts,tests/walletSessionPersistenceBridge.test.tsx. - Retest: refresh localhost while signed in → bridge fires on mount → QA script 03 should show
username+settings.bitcodeProfile.walletBindingpopulated (rawwallet_*columns stay null by design), Wallet Auxillary shows the bound address, Profile readiness no longer lists "Display identity is missing" / "Wallet binding is missing".
- Trace: the canonical binding write is
- Severity: medium (misleading; says "Profile row is missing" / "Wallet binding is missing" when the actual condition is "not signed in")
- Cause:
packages/api/src/routes/auxillaries-contract.ts:765emitsprofile.missingwhenever the contract receives no profile; an unauthenticated request gets no profile via RLS, so all blockers fire. The readiness contract should distinguish "no session" from "session with incomplete profile" and lead with the wallet sign-in action.
F7 — React hydration errors (#418/#423) on production landing (www.bitcode.exchange)
- Severity: low/medium (prod console errors on first paint; minified — needs dev reproduction to attribute; suspects: client-only state rendered during SSR in nav/landing chrome)
- Severity: medium (legacy surface reachable post-launch; post-auth callback landed there until pinned to
/packs) - Observed: first successful wallet sign-in landed on
/packs—buildAuxillariesRoutePathbuilds/terminal?auxillary-open-to=…viaAUXILLARY_OVERLAY_ROUTE_ROOT = '/terminal'(apps/uapi/app/auxillaries/components/auxillary-pane-meta.ts:18), and the legacy cockpit page still renders. - Canon:
/packsfunctionality was split into/packs,/reads, and/deposits. A V48 gate should (a) verify each terminal capability was properly ported to the three routes, (b) retarget or removeAUXILLARY_OVERLAY_ROUTE_ROOTand remaining/packslinks, and (c) remove the unused terminal page/code (relates to F4 — another surface the V47 feature-excess audit never classified). - Interim fix: wallet sign-in post-auth destination pinned to
/packsinAuxillariesWalletConnectionPanel; GitHub connect redirect retargeted to/packs?auxillary-open-to=externalsintps/github/_callback-handler.ts; all sevenauxillaries-contract.tsrepair/recovery routes retargeted from/terminal?auxillary-open-to=…to/packs?auxillary-open-to=…after "Add Email" landed on the legacy cockpit page with no email experience (theAuxillariesProviderreads the open-to param on any route, so panes open over/packs; the v40 browser-proof checker pins onlybitcode-browser-proof.tsroute states, untouched). Remaining/packsconsumers (orbitals links, browser-proof paths,AUXILLARY_OVERLAY_ROUTE_ROOT/buildAuxillariesRoutePathitself) stay for the F8 gate.
- Severity: medium now (misleading pane UX), high later (blocker the moment enforcement lands)
- Observed: fresh wallet-identity account shows Organization Authority Denied for
pay_btc_fee via terminal(organizationn/a, explicit grantsnone) despite wallet bindingbound. - Law trace (
packages/btd/src/authority.tsbuildBtdOrganizationPolicyAuthority):pay_btc_feerequires organizationId + normalized role + explicitsettlement:pay_btc_feegrant + policy id/hash. Denial reasons fired:organization_missing,role_missing(profile roleusernormalizes to null — only viewer/member/admin/owner/lead/dev count),explicit_permission_grant_required,policy_missing. The decision is correct fail-closed law. - Gap: no writer exists anywhere in the product for
organization_id,organization_permission_grants, organization-grade roles, ororganization_policy_confirmed— the only organizationId sources are GitHub-org repos or Profile companyName, and neither supplies role/grants. Every solo operator therefore reads Denied permanently. - Enforcement status: display-only today —
buildBtdOrganizationPolicyAuthorityfeeds only the Auxillaries pane, andpostBtdOrganizationInterfaceAuthority(/api/btd/organization-interface-authority) has no callers; the V47 ip-exchange e2e proof ran deposit→read→settle without org config. Tracks 2–3 QA are not blocked. - V48 gate decision needed (spec law, not a hotfix): either (a) personal-organization bootstrap at wallet sign-up (organization = operator, role
owner, default grants includingsettlement:pay_btc_fee, policy confirmed) so solo commerce is allowed-by-law, or (b) the pane renders "no organization configured" as a neutral state instead of Denied until organization features ship — paired with deciding where enforcement actually attaches before mainnet.
- Severity: medium (all GitHub webhook deliveries lost;
POST /api/webhookdrives asset-pack pipeline automation from GitHub events) - Observed: the App registration's Webhook URL is
https://bitcode.exchange/github, but the only routes there are GET-only 308 shims (/github/callback,/github/setup); the live receiver isPOST /api/webhook(signature-verified viaGITHUB_WEBHOOK_SECRET). GitHub does not follow redirects on webhook POSTs, so a shim cannot fix this. - Fix (dashboard): Webhook URL →
https://bitcode.exchange/api/webhook; verify via App Advanced → Recent Deliveries (history should show the 404s; redeliver one to confirm 2xx).
- Severity: medium (drops real installations whenever the post-install redirect lands on an origin without a session — e.g. the registered Setup URL origin differs from where the user signed in)
- Trace:
apps/uapi/app/tps/github/_callback-handler.ts— with a session, the installation persists touser_connectionscorrectly; without one, it writes abitcode_github_installation_pendingcookie (15 min) and redirects withvcsConnection=installation_staged, but no code anywhere reads that cookie. Staged installations are silently dropped. - Structural note: GitHub Apps have a single registered Setup URL (no per-request redirect override), so the post-install origin is fixed at registration — same origin-hop class as F5. The grace path was clearly designed for this and needs completing: on next authenticated load, claim the pending cookie (verify the installation's account/visibility) and persist the connection; or replace the cookie with a server-side staged row keyed to the installation id.
- Local QA workaround: the App has "Request user authorization (OAuth) during installation" enabled, which disables the Setup URL — GitHub instead redirects post-install to the FIRST registered Callback URL with
code+installation_id. The handler dispatches oninstallation_idbefore the OAuth-code branch, so the install leg needs onlyGITHUB_APP_ID+GITHUB_PRIVATE_KEY(present locally). For QA: addhttp://localhost:3000/tps/github/callbackas the first Callback URL and enable "Redirect on update"; reorder back after. - Adjacent residue (F4-class): a legacy OAuth App registration still points at
https://engi.software/api/auth/github/callback(pre-Bitcode domain). Not part of the App install flow; check whether its client id backsSUPABASE_AUTH_GITHUB_CLIENT_IDorGITHUB_CLIENT_ID, then retire or rotate. - Env preparation (2026-06-12): local
GITHUB_APP_ID/GITHUB_PRIVATE_KEYwere template placeholders (YOUR_GITHUB_APP_ID; JWT signing failed withDECODER routines::unsupported); the install callback otherwise reached localhost and dispatched correctly. Fix: real App ID + base64-encoded PEM (the JWT helper accepts base64/\n-escaped/quoted PEMs). Also verify the prod deployment env carries real values — placeholders there break the prod install path identically. Re-running the flow after an env fix does not need a fresh install: the installation branch ignores the single-usecode, so re-visiting the callback URL withinstallation_idsuffices. - Related: post-connect redirect builds
/terminal?auxillary-open-to=externals— another F8 legacy-route consumer.
F12 — Deposit AssetPack option "synthesis" is deterministic blueprints; the measurement pipeline never runs
- Severity: high (Track 2 core; the commercial deposit experience is not real)
- Observed: options appeared instantly after connect+notes; every execution row has
total_tokens: null,duration_ms: null,created_at == completed_at; the three option titles are fixed archetypes. - Trace:
/depositsoption synthesis runsdeposit-route-model.ts→buildDepositAssetPackOptions(packages/asset-packs-pipelines/domain/src/deposit-asset-pack-options.ts) — three hardcodedOPTION_BLUEPRINTSwith constantmeasurementBias(0.72/0.66/0.61) and FNV-hash pseudo-roots; the file still carriesapprovedOptionsAdmittedBy: 'future-gate7-deposit-option-review'scaffolding markers from an earlier version.BITCODE_ASSET_PACK_REAL_INFERENCEreaches only the pipeline-host QA routes (/api/pipeline-host/asset-pack), never the/depositsflow — the env flags change nothing for depositing. Admission/rejection rows are journal writes over the blueprints; the qa7 sha256 roots are the composer execution's deterministic evidence. - Gate 2 core work: wire
/depositsoption synthesis to the real asset-pack measurement pipeline under the bounded real-inference profile, producing measured options (real measurement vectors, token/cost/duration accounting, depositor-notes-conditioned synthesis) with the blueprint path retained only as an explicit mock/bring-up mode. - IMPLEMENTED (first increment, 2026-06-12): the AssetPacksSynthesis pipeline core (
packages/asset-packs-pipelines/domain/src/asset-packs-synthesis.ts) — Bitcode's single synthesis/measurement pipeline, lens-parameterized (deposit | read) per the accepted V48 architecture law (steering prompts + measurement catalogs carry the variance; one run creates multiple packs). The deposit lens adapter (deposit-option-real-synthesis.ts) translates candidates into the promoted V43/V47 option law (same schema/roots/review boundaries, so policy + admission consume them unchanged).POST /api/deposit/synthesize-optionsbuilds an exclusion-filtered source inventory from the connected GitHub source (session + repo-ownership checked, server-derived token), runs bounded structured inference fail-closed (real_inference_requiredwithout the flags), persists the execution with REALtotal_tokens/duration_ms, and the/depositsUI now requests synthesis from it (async with status; blueprint path unreachable from the surface). Gate 2 charter elevation: consolidate remaining pipelines onto AssetPacksSynthesis, clean all legacy cockpit code, correct pipeline-execution actualities (data, Vercel sandbox actually running pipelines) — reading lens migration lands with Track 3.
F13 — Deposit option decision semantics: approve is irreversibly final with no confirmation; reject should be archive
- Severity: medium (spec intent for the V48 family + UX gate work)
- Law intent (Garrett, 2026-06-12): approve = permanent Depository admission — correct that it is final, wrong that one click does it with no explicit confirmation boundary; toggling an admission back is NOT correct. Reject is semantically "archive": re-depositable at any time, with the caveat that measurements go stale over time — re-deposit triggers resynthesis/remeasurement.
- Work: explicit confirm step (or staged "ready to admit" state) before admission; rename/restyle reject to archive; archived options carry measurement-staleness posture and a resynthesize action.
- IMPLEMENTED (2026-06-12): approval is a one-time armed-confirmation flow ("Approve for Depository" → "Confirm permanent deposit"); an admitted option locks permanently ("Admitted to Depository — permanent", no further decisions accepted by the handler). Reject renders and records as Archive: re-depositable anytime, visible in the depositor's packs (personal scope), with the staleness/resynthesis note; the
'rejected-by-depositor'contract value is unchanged so admission law and the V47 checker pins hold.
- Severity: medium-high (source-safety law surface)
- Gap: the deposit composer accepts depositor notes but offers no way to declare which IP in the connected source must be protected and excluded from AssetPack knowledge synthesis. The synthesis pipeline (once real, F12) must accept and honor exclusion instructions as a fail-closed boundary (excluded paths/concepts never enter measurement, prompts, or option summaries).
- IMPLEMENTED (2026-06-12): "Protected IP exclusions" field on
/deposits; exclusions are honored fail-closed at both ends of AssetPacksSynthesis — excluded paths removed from the source inventory before any prompt is built, and candidates whose covered paths violate exclusions (or cite paths outside the real inventory) dropped after inference. Exclusion roots + withheld-path counts surface in the synthesis exclusion posture and the executions row.
- Severity: high (Gate 2 charter: pipeline-execution actualities — data)
- Observed: the live deposit QA session wrote ~1,200 lines of runtime depository state (assets, options, roots for the "Some Python" deposit) into
scripts/specifying/data/state.json, a tracked file that historically changes only at canonical promotions. Runtime commerce state does not belong in git: it cannot serve concurrent users, deployments reset it, and QA sessions dirty the working tree (one such mutation was accidentally committed in569c6e19and reverted immediately after). - Gate 2 work: move depository/ledger runtime state to the database (executions/ledger tables + object storage roots already exist for this), keeping
state.jsonas promotion-managed demonstration canon only.
-
Severity: medium (Track 4 UX, surfaced during Track 2)
-
Observed: pack activity rows render but cannot be selected to open their detail view (the master-detail contract's most critical interaction). The TYPE column shows generic "Executions" for everything; the conceptual taxonomy should read as: Deposit Request ("Some Python" — parity with Read Request) → synthesized AssetPack options → per-option decisions (admitted/archived).
-
Work: row selection opens the per-activity detail page (clean full readback); type column gains the deposit-request/option/decision taxonomy.
-
PARTIAL (2026-06-12) — scope taxonomy implemented: execution activity now derives scope by specification (admitted Depository AssetPacks and settled/read APs =
network, globally visible; deposit requests, syntheses, review decisions including archived options, connected sources =personal) instead of hardcoding everythingnetwork;/api/packs/activitymerges a global Depository feed (admitted APs across all accounts, source-safe projections, dedupe against own rows);/packsgains a Visibility scope filter (All / Network — deposited and read AssetPacks / Mine — archived options, sources, requests). Remaining: live-verify row→detail selection (the detailId mechanism exists and is test-covered — widen the click target if the live miss reproduces) and the clearer type-column taxonomy. -
Environment note (by design, not a finding):
www.bitcode.exchangeand localhost both point at the staging-testnet Supabase project — the testnet launch IS the production deployment. QA users/data therefore land in live data; keep QA to dedicated testnet wallets.
V48-Gate3-F17 — Synthesized AssetPack option cards overflow into the right panel; per-option action buttons overlap
- Severity: low (Gate 3 deposit review UX)
- Observed (2026-06-26): on
/deposits, synthesized AssetPack option cards overflow their column rightward into the 380px telemetry/activity panel, visually x-overlapping the per-option action buttons (Select for deposit / Archive / Resynthesize) with the right column. - Cause: the option
<article>(a grid item in thexl:grid-cols-3options grid) lackedmin-w-0, and thefont-monocovered-source-paths list lackedbreak-all, so a card with long unbreakable mono content refused to shrink below its content's min-content width and overflowed theminmax(0,1.45fr)left column. - Repair (2026-06-26,
DepositPageClient.tsx):min-w-0on the option card +break-allon the covered-paths list (the Option-roots<dd>already wrapped). The card now shrinks to its grid column and long paths/roots wrap rather than forcing overflow.
V48-Gate3-F18 — Synthesis run telemetry leaks the raw model response and x-overflows the page (one accordion row per content line)
- Severity: high (source-safety law) + low (layout). Telemetry must never expose raw prompts/responses (
rawProviderResponseVisible=false). - Observed (2026-06-26): during
/deposits"Synthesize options", the Synthesis run telemetry accordion rendered the raw model output line-by-line —```json,{,"analysis": "…","steps": [,"1. ANCHOR IDENTITY: …"each became their own row — and the long unwrapped"analysis"/step lines x-overflowed the entire page (page shifted right, panel toggle clipped). The raw content was the setup-plan agent's plan prose (a provider response). - Cause (root, source-safety): the formal Thricified substeps store LLM content under
llm/input|prompt|output|parsedOutput, butAgentLLMsRegistry/PipelineLLMRegistry(directgetLLMcalls, used by the setup-plan agent) store the raw prompt underllm/messagesand the raw response underllm/response(output.content). The universal streaming filtersourceSafeStreamEventwithheld only the substep key names, sollm/response(a raw string) passed through as astatus-eventmessageand reachedexecution_eventsunredacted. - Cause (display):
buildTerminalRunActivityFromEventsjoins event messages with\nandPipelineExecutionLogsplitsoutputon\n(one row per line), so the multi-line leak fragmented into many rows; the compact/desktop title spans lackedmin-w-0, so a long line's min-content width escaped theoverflow-autolog container and widened the page. - Repair (2026-06-26):
pipeline-stream-integration.ts—sourceSafeStreamEventnow withholds by metadata allowlist: everyllmstore is content-withheld except a fixed source-safe set (startTime/endTime/duration/usage/status/provider/model/configKey/stopReason/error). Robust to content-key drift between the two LLM-call paths;llm/response+llm/messagesare now withheld (message →[content withheld — source-safe],data→ structural summary). Regression test added.terminal-run-activity.ts— every event line is collapsed to a single bounded line (toSafeSingleLine, 280 chars) so one event = exactly one row and theoutputDetailskey lookup stays intact (defense-in-depth).pipeline-execution-log.tsx+DepositPageClient.tsx—min-w-0on the compact/desktop title spans and on the telemetry panel section/wrapper (+overflow-hiddenon the panel) so a long line truncates within its row instead of x-overflowing the page.
- Related observation (not yet filed): the deposit run executes
ReadFitsFindingSynthesisSetupPlanAgentplanning a "Read-Need" — a read-lens setup agent running under the deposit lens. Telemetry is now source-safe regardless, but the deposit setup-plan agent identity should be confirmed/queued as a separate correctness finding.
V48-Gate3-F19 — Rich telemetry fragments the hierarchy across rows; lock it to LLM calls + Tool uses only
- Severity: medium (telemetry correctness + pipeline↔UI contract stability).
- Observed (2026-06-26): after F18 the raw-content rows were gone, but the Synthesis run telemetry still rendered intermediate store values as standalone rows —
try,setup-plan,thricified-generation, a cwd path, a bare agent name — and the hierarchy was split across rows (Phase on one, Step+Generation on another, Agent on a third) instead of consolidated onto one rich log line. - Contract (decided 2026-06-26): the rich telemetry renders EXACTLY two formal log-line kinds, nothing else — (a) LLM calls, which carry the full hierarchy Phase→Agent→Step→Failsafe→Thricified + source-safe content + provider/model/usage; (b) Tool uses, which carry Phase→Agent→Step + tool name/arguments (no Failsafe/Thricified). Every other store event (step/agent/phase name stores, prompt-side llm keys,
llm/responseregistry copies, paths, generation markers, tool sub-keys) is intermediate context that advances the rolling hierarchy but never becomes a row. - Cause:
ExecutionStreamAdapter.onStoreemits one event perexecution.store(...), andbuildTerminalRunActivityFromEventsturned any event with a non-empty message into a row. The canonical LLM-call event (llm/output, typegeneration) already carries the full hierarchy in its value, but tool stores use the singulartoolnamespace with no hierarchy (andinferEventTypeonly recognized pluraltools), so tool calls were mis-typed and hierarchy-less. - Repair (2026-06-26,
terminal-run-activity.ts+pipeline-execution-log.tsx):- The activity builder maintains a rolling
{phase, agent, step, failsafe, generation}context updated from every event, and emits a row ONLY for LLM calls (generation/llm:output), Tool uses (tool|tools:result|error), and terminal/high-level signals (completion/error/no-namespace status). LLM rows stamp their own 5-field hierarchy; tool rows stamp Phase/Agent/Step from the rolling context + the tool name/args accumulated per tool-execution node. - Distinct calls that share withheld text are kept distinct via a unique null-separated row key (
TELEMETRY_ROW_KEY_SEP); the renderer displays only the text before the separator, looks up details by the full key, and bypasses message de-dup for uniquely-keyed rows. usePipelineExecution.ts(the second half — fragments persisted live) — the live SSE tail re-parsed each frame throughparseStreamChunk, which flattened the structured onStore event into a namespace-less{type:'status', status:{message}}shape; the activity-builder classifier keys offnamespaceto suppress fragments, so namespace-stripped live events leaked every store as a row (the history path, which relays rawevent_data, was already correct). The tail now relays rawevent_dataverbatim — identical in shape to history — so the contract holds during streaming, not just on reload.
- The activity builder maintains a rolling
- Verified: uapi tsc 0; F19 activity-builder contract test + a
usePipelineExecutiontest asserting the live tail preservesnamespace/key/executionState+ 10-suite telemetry/terminal/deposit regression batch — all green.
- Severity: high (the deposit pipeline isn't actually depositing). Surfaced as "Read" verbiage in a deposit run, but the cause is that the read agents execute.
- Observed (2026-06-26): a
/depositsrun ("SynthesizeAssetPacks (deposit mode)") rendered read-lens agents in telemetry —ReadFitsFindingSynthesisReadComprehensionAgentwith aread-comprehensionstep,ReadFitsFindingSynthesisAssetPackSynthesisAgentwith asynthesisstep — and ended with "Pipeline yielded no admissible options; falling back to bounded deposit synthesis." The agent pills are each agent's realconfig.name, so the read agents were genuinely running. - Cause:
factorySDIVFExecutorPipelinecomposes the phases withsequential, which runs preprocess and every phase on ISOLATED sibling child executions (execution.child('seq-N')).factoryPreprocessstored the resolved mode on its own child (seq-0); the phases run onseq-1/seq-2and resolve the mode withsynthesizeAssetPacksModeFromExecution, which only walks ANCESTORS — never sideways toseq-0. So every phase resolvednull → 'read', took the read branch, and resolved the init-registered read agents. The agents registry is already shared (the read implementation agent is registered by the phase and resolves), so the only gap was the mode. - Repair (2026-06-26,
index.ts): thefactorySynthesizeAssetPacksPipelinewrapper now resolves the mode and stores it on the shared outer execution (the parent of allseq-Nphase children) before running the SDIVF executor, so every phase resolves it via the upward walk. Once the phases seedeposit, their mode-conditional registrations (setup comprehension override, deposit Discovery agents, deposit Implementation/Validation) take effect on the shared registry and the deposit-lens agents run — which also makes the telemetry verbiage deposit-correct at the root. Read mode is unchanged (default was already read). Regression test added (sibling isolation reproduced; shared-parent storage resolves). Needs a live deposit run to confirm end-to-end.
- Severity: low (telemetry consistency).
- Observed (2026-06-26): the Agent pill had no icon; Step/Failsafe/Thricified pills frequently had no icon (the icon maps were keyed by labels that don't match the real values —
Try,read-comprehension,synthesis,structured_output). Informational rows ("AssetPacksSynthesis started", "Building source inventory", "Inventory ready…", "Running SynthesizeAssetPacks…", "Pipeline yielded no admissible options…") still rendered alongside the LLM-call rows. - Contract: pills map by fixed role — Phase (grey, phase icon) top-left, Agent (blue, agent icon) top-2nd; Step / Failsafe / Thricified bottom 1-2-3 (each with its icon). The rich log renders ONLY the ultimate LLM-call layer + Tool uses; nothing else is a row.
- Repair (2026-06-26):
PathPill.tsx— every pill type renders a guaranteed icon: a per-type default (Agent = person, Tool = sliders) refined by a substring match on the normalized label, so PTRR steps / Thricified generations / failsafe stages / custom step names always resolve an icon. Colors and the compact top/bottom pill order already matched the role contract.terminal-run-activity.ts— the formal-log-line classifier now returns onlyllmandtool; informational status / completion / error rows are dropped from the accordion (run completion is shown by the processing indicator, errors by the log's error banner). The deadnormalizeEventMessagehelper is removed.
- Verified: uapi tsc 0; terminalTransactionActivity (tightened) + 7-suite telemetry batch green.
V48-Gate3-F22 — Setup-plan agent is read fits-finding work; punt it under the deposit lens + give the log rows breathing room
- Severity: low (deposit correctness + telemetry polish).
- Observed (2026-06-26, after F20): with the deposit agents now running, the only remaining read-named row was
ReadFitsFindingSynthesisSetupPlanAgent— a name that merges phase (Setup) + step (Plan) and, worse, plans "Finding Fits from an accepted Read-Need," which is read work that is irrelevant to a deposit. Separately, the overhanging pills crowded the adjacent log rows. - Decision: the Setup-plan agent is read-lens work for the subsequent (read) gate — its fits-finding planning belongs in the read-lens Discovery phase, not Setup. Under the deposit lens it is irrelevant and is punted.
- Repair (2026-06-26):
phases/setup.ts— the deposit branch now re-registerssetup:ReadFitsFindingSynthesisSetupPlanAgentwith a passthrough (no LLM call, no telemetry row), so the deposit Setup runs clone → comprehend Obfuscations → init with no read fits-finding plan. Read keeps the agent unchanged. (F28 later punts the read danger-wall too, so the deposit Setup is clone → comprehend Obfuscations → init.)pipeline-execution-log.tsx— compact log rows gomb-3 → mb-6so the top/bottom overhanging pills clear the adjacent rows.
- Gate-4 (read lens) directive: move the read setup-plan (fits-finding planning) into the read-lens Discovery phase, with a deposit-free agent name; do not reintroduce a deposit setup-plan.
- Verified: asset-pack tsc 0 + setup-agents suite green; uapi tsc 0 + telemetry render tests green.
- Severity: low (telemetry UX).
- Observed (2026-06-26): the synthesis log tracked
userHasScrolledbut never scrolled, so new streamed rows didn't pin to the bottom — the user couldn't watch passively. - Behavior: the log rests at the bottom as new rows stream in (passive watch); when the user scrolls away from the bottom (e.g. to read an earlier line or an open accordion) the follow pauses and never yanks them back; returning to the bottom resumes it.
- Repair (2026-06-26,
pipeline-execution-log.tsx): a follow effect scrolls the internal scroll container to the latest line on new rows when!userHasScrolled(rAF-deferred).handleScrollnow uses a modest near-bottom band (48px) — momentum/rounding still counts as following; a deliberate scroll up setsuserHasScrolledand pauses the follow until the user returns to the bottom. - Verified: uapi tsc 0 + telemetry render tests green.
V48-Gate3-F24 — Neediness v0: deposit-side preview of read Need-fit (depository search during depositing)
- Motivation (Garrett, 2026-06-26): depository search during depositing estimates the potential fit value of the packs being synthesized — which packs are worth making correspond to what is likely to be read. Surface it to the depositor as a per-pack "neediness" preview (and a new AP measurement).
- Model:
neediness∈ [0,1] = a deposit-lens PREVIEW measurement (separate from the absolute composite) of the pack's future read Need-fit / earning potential.neediness = clamp01(demand × (0.5 + 0.5·(1 − saturation)))— demand gates, scarcity boosts. - v0 signals (no new infra; LLM-grounded): the deposit
discovery:depository-searchagent emitsunderservedTopics(supply-scarcity hint) alongside its demand guidance; the deposit Implementation synthesis agent emits a per-packneedinessSignal {demand, saturation, rationale}grounded in that guidance;computeNeedinessderives the scalar deterministically invalidateDepositSynthesisOptions. - Flow: synthesis agent →
implementation:options(carryneedinessSignal) → routevalidateDepositSynthesisOptions(attachesneediness) →buildRealDepositAssetPackOptionSynthesis(projectsneediness+needinessRootonto the deposit option) →/depositsoption card previews it (amber tile: volume %, demand/saturation, rationale). - Source-safety: derived scalars + topic-level rationale only (no raw source). v1 seam (spec): replace
saturationwith a real embedding-vector probe of the pack against the Depository supply index, anddemandwith a search against an accrued Read-Need / demand corpus; realized readneed-fit+ BTD later calibrate it. - Implemented over two commits (spec + lib core; then agents + projection + UI). Verified: asset-pack tsc 0 + full suite (214) green incl. a new neediness test; uapi tsc 0 + deposit UI/model tests green; spec checker green.
- Neediness v1 is deferred to Gate 7 (Garrett, 2026-06-26): the real embedding-vector supply-index probe + Read-Need/demand-corpus search + realized-need-fit/BTD calibration.
V48-Gate3-F25 — Deposit synthesis stalls indefinitely; bound the pipeline LLM call + (throughput) deposit runs full PTRR
- Severity: high (interactive deposit usability).
- Observed (2026-06-26, run
0f9d2421-de2b-48c3-aa08-5ad6c27e000c): a deposit run (deposit agents correctly running, post-F20) stuck for many minutes on a single generation — theDepositDepositorySearchAgentPlan → PrepareContext → StructuredOutput row — with no progress. - Cause (acute): the pipeline LLM wrappers (
AgentLLMsRegistry/PipelineLLMRegistry)await llm(input)with NO timeout; the provider SDK sets no short bound, so a hung/very-slow generation stalls the entire INLINE synthesis. The/depositssynthesize-options route runs the pipeline inline undermaxDuration = 300s, so a stall just freezes the UI on the last telemetry row. - Cause (throughput, related): the deposit agents (
deposit-codebase-comprehension,deposit-depository-search,deposit-inherent-regurgitation,deposit-input-comprehension,deposit-asset-pack-synthesis) callfactoryPTRRAgentdirectly and ALWAYS run the full PTRR × Failsafe × Thricified fan-out — they do NOT gate on the bounded profile (unlike the read agents'shouldUseDiscoveryPtrrdeterministic/bounded fast-path). So even withBITCODE_ASSET_PACK_REAL_INFERENCE_PROFILE=boundedthe deposit pipeline issues dozens of LLM calls and can exceed the 300s inline budget. - Repair (2026-06-26, acute):
AgentLLMsRegistry+PipelineLLMRegistrynow wrap the base LLM call in a timeout (BITCODE_LLM_CALL_TIMEOUT_MS, default 90s; 0 disables). On timeout the call rejects and the existing failsafe/PTRR retry handles it — a clean failure surfaced to the UI, never an indefinite hang. - Resolution (Garrett, 2026-06-26): both — remove inference profiles entirely (always-full hierarchy + real generation; F26-A) AND run the synthesis decoupled via the pipeline host (F26-B). Spec: F26 (
BITCODE_SPEC_V48_NOTES.md).
- Design (committed spec, F26): inference is non-configurable (no profiles/
shouldUsePtrr/bounded/deterministic in-agent branches — always the full Pipeline→Phase→Agent→Step→Failsafe→Thricified→Generation hierarchy, always real inference; test determinism via boundary LLM mocks). And SynthesizeAssetPacks runs via the pipeline HOST, decoupled from the dispatching request: the route validates + dispatches, the host runs to completion while telemetry streams, the client reads the persisted synthesis on the completion event. Hosts: local in-process (dev) + Vercel Sandbox (prod). - F26-B (done — local decoupling):
synthesize-options/route.tsnow dispatches the run as a background task (void runSynthesis()) and returns{ runId, status: 'dispatched' }immediately; the synthesis (+ reviewProjections + inference) is persisted to the execution rowoutputBEFORE the completion event.DepositPageClientno longer reads the synthesis from the response — a completion effect, onisStreamingComplete, fetches/api/executions/history/{runId}and renders the options (a streamed error fails the run). Verified: uapi tsc 0; depositPageClient test reworked to drive the dispatched→completion→history flow (+ deposit/telemetry batch green). The Vercel Sandbox host (prod durability) is the existingpipeline-hostshost — not wired in this commit (needs a cloud run to verify). Pre-existing:tests/api/depositSynthesizeOptionsRoute.test.tsfails to LOAD (its@bitcode/pipelines-genericsmock omitscreatePhaseRunner, which the route now pulls in via the full pipeline) AND its assertions assume the inline contract; both need addressing when the load is fixed. - F26-A (done):
runtime-inference-policygutted to onlyisAssetPackRealInferenceEnabled(the master switch — kept; not a profile);bounded-structured-inferencedeleted; the 8 read-lens consumers (setup-plan, read-comprehension, read synthesis, danger-wall, deliver, validation ×3, discovery ×5, read-need) de-gated to ALWAYS run their formal PTRR core (deterministic + bounded branches removed). Boundary LLM-mock test infra built (src/__tests__/support/generic-llms-mock.ts—jest.mock('@bitcode/generic-llms')seam) and the read-agent tests reworked to assert via the mock. Latent bug fixed along the way: the PTRR wrappers readresult.summary/.options/.finalApproval/.issuesdirectly, butfactoryPTRRAgentreturns an envelope{context, output, finalOutput}— so real inference output was silently dropped (masked by the deterministic defaults). The read/validation wrappers now unwrapfinalOutput ?? output ?? raw. Verified: asset-pack tsc 0 + full suite 210/210; uapi tsc 0. (DeadBITCODE_ASSET_PACK_REAL_INFERENCE_PROFILE/*_USE_PTRRenv STRING references remain inuapihost/route files — no-ops, no imports of the removed fns; flagged for a follow-up scrub.)
V48-Gate3-F27 — Deposit agents drop their real PTRR output to the fallback defaults (envelope not unwrapped)
- Severity: high (deposit-critical — the likely reason deposit synthesis "yielded no admissible options" even after F20 made the deposit agents run).
- Cause:
factoryPTRRAgentreturns an envelope{ context, output, finalOutput }, but the six deposit agents read the typed fields straight off it —deposit-input-comprehension(result.comprehension),deposit-codebase-comprehension(.comprehension),deposit-depository-search(.guidance),deposit-inherent-regurgitation(.regurgitation),deposit-asset-pack-synthesis(.options),deposit-validation(agentOutput.issues). Each was thereforeundefined, so every agent silently fell back to its?? {default}/ empty-options branch — the real inference output never reached the synthesis (and Implementation's emptyoptionstriggered the route's bounded fallback). Same class as the F26-A read-agent bug, in the deposit lens. - Repair (2026-06-26): unwrap
(raw)?.finalOutput ?? (raw)?.output ?? rawin all six deposit agents before reading the typed output. asset-pack tsc 0 + full suite 210/210 (deposit agents aren't test-exercised — the pipeline short-circuits in tests — so this is verified by compile + the read-agent pattern; needs a live deposit run to confirm options now materialize).
V48-Gate3-F28 — Deposit Setup runs (and is blocked/hung by) the read danger-wall; punt it under the deposit lens
- Severity: high (deposit-blocking — the deposit cannot produce options).
- Observed (2026-06-30, run
3e82ddd1-bf24-41cb-9a9a-58e9f6a35335): a deposit run got through clone →DepositInputComprehensionAgent(obfuscation comprehension) and then stalled in Setup onbitcode-read-risk-admission— its Plan step alone ran 132s, its Try step dispatched a generation that never returned, and the run sat instatus:runningwith no further telemetry. - Cause:
phases/setup.tsrunssetup:asset-pack-danger-wall-agent(=bitcodeReadRiskAdmissionAgent) unconditionally. That agent is the Bitcode read risk-admission — its whole contract is read-framed (readSafeToMeasure/assetPackSafeToSynthesize/deliveryMechanismSafeToAttempt, proof- and delivery-boundary gates) and it expects a Read to admit. On a deposit it hasread: '', flails, and everystructured_outputreturnssafe:false / maxSeverity:high. Two failures stack: (1) would block anyway —dangerWallWithShortCircuittreatsmaxSeverity==='high'asisBlocked→SHORT_CIRCUIT(refundType:'full'), so the deposit is aborted with zero options even if the LLM returned; (2) the locus of the hang — the full PTRR × two failsafes (prepare_concise_context+chunk_then_sum) × reason/judge/structured_output fan-out, withstructured_outputechoing the whole accumulated state until it hitsmax_tokens(37s/call). It is the sibling of the F22 Setup-plan leak, one agent over. - Repair (2026-06-30,
phases/setup.ts): the deposit branch now re-registerssetup:asset-pack-danger-wall-agentwith the same no-LLM passthrough as the Setup-plan punt. The deposit Setup is now clone → comprehend Obfuscations → init — no read danger-wall row, no short-circuit. Read keeps the danger-wall unchanged. The depositor's source-safety is already enforced by the streaming source-safe filter + the obfuscation comprehension; a deposit-lens admission is a later-gate…ForDepositor/…ForReadersplit. Spec:BITCODE_SPEC_V48_NOTES.mdSetup-phase note. Verified: asset-pack tsc 0; setup-agents + deposit-asset-pack-options + deposit-asset-pack-option-admission suites green (10/10). - Liveness aside: F25's per-call timeout (
BITCODE_LLM_CALL_TIMEOUT_MS, default 90000ms) should have bounded the hung Try generation but did not — the run stayed frozen ~8 min past the last event. The punt removes this locus for deposits, but the operator should confirmBITCODE_LLM_CALL_TIMEOUT_MSis not0in the run env; if it is non-zero and a generation still hangs unbounded, the in-box host LLM path bypassing the F25 wrapper is a follow-up (read lens + remaining deposit agents still depend on that bound).
V48-Gate3-F29 — First live run of the redesigned Failsafes (3703f48f, 2026-07-03): machinery works; PCC selection shorthand was silently dropped (FIXED)
- Observed working end-to-end on the first post-wave run: the keys-only execution-state
tree streamed (values never present); the PCC selection Thinkings ran (reason/judge/
structured_output, ~14s/14s/2s, small selection output); CS measured the composed
request (6,582 chars vs the 600k budget → correctly non-triggering, single task
generation); cross-phase
deposit:*/pipeline:*stores landed on the ROOT node; the per-run sidecar dir~/.proofs/logs/executions/<runId>/materialized; and the Implementation plan step converged in ONE stitch cycle whose repair request carried the literal schema-validation error (vs the pre-fix five-cycles-to-death). - Defect: the selection model chose exactly the right context (deposit obfuscations/
inventory/impermissibleSources/demandContext + mode) but emitted
deposit#obfuscationsshorthand (<namespace>#<key>), while the resolver demanded the canonical<execution-path>#<namespace>:<key>— all five selected keys missed,selectedContextcame back empty (fail-soft), and every downstream generation ran without its selected context for the whole run. - Repair (2026-07-03): strict resolution stays first; on a miss the resolver reinterprets
the string as
<namespace>:<key>and resolves it depth-first from the root (packages/execution-generics/src/state-keys.ts; regression-pinned against the five live-run misses). Follow-up candidates: canonical-path examples in the PCC selection prompt part;context:selectedKeysnon-empty as a QA acceptance row (added to §2).
V48-Gate3-F30 — Vercel Preview builds fail: Supabase env vars … are missing at static prerender (env, not routing; FIXED)
- Severity: high (every git-push build to the gate branch errored, blocking deploys).
- Observed (2026-07-05, commit
4fbc67e, Preview builds):Generating static pagesthrewError: Supabase env vars (NEXT_PUBLIC_SUPABASE_URL or SUPABASE_URL / …ANON_KEY / …PUBLISHABLE_KEY) are missingwhile prerendering ~30 pages —/deposits,/reads,/packs,/auxillaries, all/docs/[slug], and a prior product route,/orbitals/*,/executions,/edgetimes,/demo-video— thenCommand "pnpm run build" exited with 1. - Cause: the throw is in
packages/supabase/src/ssr/client.ts(createClient), which raises ONLY when both the URL and the anon/publishable key are absent from the environment. Next statically prerenders these client-component pages at build time, inliningNEXT_PUBLIC_*from the BUILD environment. The two provisioned environments were production-mainnet and thestaginglocal-testnetcustom environment; the Preview target (which every push to a non-mainbranch builds against) was never given the Supabase keys, so every prerender that constructs a client threw. NOT a routing defect (the listed legacy routes physically exist asapps/uapi/app/*dirs — F4/F8 eradication residue — and merely surfaced in the error list because they too callcreateClientat prerender) and NOT related to the Engi→Bitcode repo/dir/dashboard rename (the git integration clonesadvancedengineeredsoftware/Bitcodecorrectly). - Repair (2026-07-05): pushed the full env matrix to the
previewtarget (verified all Supabase + GitHub vars present). Confirmed byvercel redeployof the previously-errored commit4fbc67e→ Ready (green), same source, only the env vars changed. - Hardening candidate (deferred, not yet law): builds should not DEPEND on env presence to
prerender.
packages/supabase/src/index.tsalready uses a build-safe dummy-value fallback ("prevents build-time crashes when env vars are missing");ssr/client.ts/ssr/server.tsthrow instead. Mirroring the dummy-value fallback there (or marking the authenticated pagesexport const dynamic = 'force-dynamic'so they are not prerendered) makes builds env-independent. Weigh against the value of a loud fail when a real deployment lacks config.
V48-Gate3-F31 — Deposit synthesis dispatch relied on undocumented post-response continuation on Vercel (FIXED)
- Severity: high (reported live as "errors running deposit pipelines" — intermittent by nature, so hard to pin from symptoms alone).
- Cause:
POST /api/deposit/synthesize-options(V48-Gate3-F26-B) dispatches the run then returns{status:'dispatched'}immediately, with the actual multi-agent synthesis (runSynthesis()) continuing as a barevoid runSynthesis()promise after the response completes. Vercel Functions (including Fluid Compute) do not guarantee an instance stays alive to finish work started before the response but not awaited by it — the platform is free to freeze/recycle the instance once the response is flushed, unless the continuation is registered viawaitUntil(). This is the same failure classexecution-orphan-sweep.ts's own header comment already names as the reason its sweep exists ("dev-server restarts (and crashed serverless boxes) kill in-process pipelines … leaving them running forever" — QA runs7f023f23and the 2026-07-03 pre-restart orphans): that sweep detects and relabels the resulting stuck-runningrows asinterrupted, but nothing previously stopped the box from being killed mid-run in the first place. The same bare-voidpattern was also on the orphan sweep's own fire-and-forget call at dispatch time. - Repair: added
@vercel/functionsand wrapped both fire-and-forget continuations inwaitUntil()— therunSynthesis()background run and thesweepOrphanedExecutions()call inapps/uapi/app/api/deposit/synthesize-options/route.ts. This keeps the Function instance alive until the promise settles, so a dispatched run either completes or fails and finalizes its row through the normalfinalizeExecutionRow/error-banner path, instead of being silently killed mid-flight. Checked the only sibling background-dispatch route (/api/pipeline-host/asset-pack) and confirmed it does NOT have this defect: its continuation runs inside an openReadableStream(SSE), whose lifetime keeps the Function instance alive by construction — nowaitUntilneeded there. Verified:tsc --noEmit0;depositSynthesizeOptionsRoute.test.ts6/6 green.
V48-Gate3-F32 — Branch/Commit selects on /deposits silently stuck on a stale GitHub connection, no explanation (FIXED)
- Severity: high (reported live, 2026-07-08: "branch and commit selects seem broken … not loading lists+defaults nor is clicking opening dropdowns" — with a repository already selected).
- Cause:
DepositSourceSelection.tsx(the deposit-native replacement for the legacyProductRepositoryContextPanel/ProductSupplySelectionPanel) fetches the repository list gated only onconnectionStatus.connected, but gates the Branch and Commit fetch effects on BOTHconnectionStatus.connectedANDconnectionStatus.valid(same asymmetry as the legacy panel).validcomes fromvalidateStoredConnection, which re-checks the live GitHub session/token — a GitHub App installation token expires ~hourly and is regenerated lazily, soconnected:true, valid:falseis a normal, recurring state, not an edge case. In that state a repository is still selectable (stored inventory doesn't needvalid), but Branch/Commit silently stay at[]forever:disabled={... || branches.length === 0}(and the same for commits) keeps the selects inert with no loading spinner and no error text — the legacy panel this replaced had dedicated "reconnect required" messaging + a Reconnect CTA for exactly this state (connectionStatus?.connected && !connectionStatus.valid,ProductRepositoryContextPanel.tsx), but that messaging was dropped whenDepositSourceSelectionreplaced it — the refactor's docblock even says it "carries no terminal-UI dependency," which is what left this state unhandled. - Repair: added a
connectionNeedsReconnectderived flag toDepositSourceSelection.tsx; when true, an amber banner (mirroring the legacy panel's messaging) explains that Branch/Commit stay empty until the live session reconnects, with a "Reconnect GitHub" button (ProductOpenAuxillariesButton, opens Auxillaries → Externals — reused directly since its own logic has no terminal-workspace coupling, only its file location is underproduct experience components/). The Branch/Commit helper hints below each select also swap to "Reconnect required to load branches/commits" instead of the normal "default is selected when available" copy in this state.ProductOpenAuxillariesButton.tsxwas missing an explicitReactimport (never directly rendered by a test before — ts-jest's classic JSX transform needs it in scope); added it. Verified:tsc --noEmit0; newdepositSourceSelection.test.tsxcoverage (valid connection → no banner; invalid → banner + Reconnect button + both hint texts) 5/5 green; full uapi jest suite 639/640 green (1 pre-existing skip).
V48-Gate3-F33 — GitHub "Refresh" (re-validate) never healed an Invalid connection: installation-token regeneration checked one expiry field but persisted to another (FIXED)
- Severity: high (asked live, 2026-07-08, re: the Externals GitHub card showing "Invalid" with a past Token-expires date: "can Refresh work to re-connect/re-validate instead of disconnecting" — disconnect forces a full GitHub App uninstall+reinstall).
- Confirmed by design, NOT by env config: Refresh (
VCSConnectionCard.tsx'shandleRefresh→checkConnection→GET /api/vcs/[provider]/connection) already re-validates AND is supposed to silently regenerate an expired GitHub App installation token with no reinstall needed —VCSConnections.getAuthFromConnection(packages/vcs-generics/src/connections.ts) mints a fresh installation token viaGitHubAppAuth.generateInstallationTokenwhenever the stored one is missing/expired. ConfirmedGITHUB_APP_ID/GITHUB_PRIVATE_KEY/GITHUB_APP_CLIENT_ID/GITHUB_APP_CLIENT_SECRETare present on all three Vercel targets (Preview, Production, staginglocal-testnet) viavercel env ls— so this isn't an env-configuration gap. - Cause (a real bug, found by inspection): the expiry check that decides whether to regenerate reads
connectionData.installation_token_expires_at(line ~193) — the field_callback-handler.tswrites at initial install. But the regeneration's own persistence call,this.updateTokens(...), only ever wrotetoken_expires_at— a DIFFERENT field, never read by that check. So every regeneration attempt "succeeded" (fresh token minted,validateTokenpasses for that one request) yet the field the NEXT check reads never moved off its original, already-past value — forcing a full re-regeneration on every single subsequent call (every Refresh click, every repo/branch/commit fetch that reaches this path) forever. Separately,GET /api/vcs/[provider]/connectionbuilt the returned status from theconnectionrow fetched BEFOREvalidateStoredConnectionran, so even a successful in-request regeneration wouldn't show its freshexpiresAt/metadata until a subsequent call. - Repair:
VCSConnections.updateTokensnow writesinstallation_token_expires_atalongsidetoken_expires_at(its only caller is this one GitHub-App regeneration path, so this is safe with no other call sites to consider).GET /api/vcs/[provider]/connectionnow re-fetches the connection after a successfulvalidateStoredConnectionbefore building the response, so a same-request regeneration is reflected immediately instead of one Refresh click behind. Note: if the GitHub App installation itself was actually revoked/uninstalled on GitHub's side (not just an expired token), regeneration will keep failing and Disconnect+reinstall genuinely is required then — check https://github.com/settings/installations (or the org's) to tell the two apart. Verified:tsc --noEmit0 (both packages); newpackages/vcs-generics/src/__tests__/connections.test.ts(2/2, pins both fields moving together and that a fresh token skips re-regeneration); full uapi jest suite 639/640 green (1 pre-existing skip).
V48-Gate3-F34 — Refreshing GitHub still showed bare "Invalid" after the F33 fix, no way to tell why (FIXED)
- Severity: high (live-confirmed, 2026-07-08: clicked Refresh after the F33 deploy went live — same "Invalid" badge in Auxillaries, same reconnect-required banner on
/deposits. Confirmed deploy timing: commitdbef719e(F33) pushed 11:24:50, its Preview build wasReadyby ~11:27, the retest screenshot is 11:46 — the fix WAS live when retested, so F33's fix, while a real and necessary bug, was not the reason THIS specific connection stayed Invalid). - Investigation: with F33 confirmed deployed and env vars confirmed present (F33), the only two remaining explanations are (a) the GitHub App installation was genuinely revoked/uninstalled on GitHub's side (only a real reinstall fixes that), or (b) a different bug in the regeneration call chain. Neither was distinguishable from the UI or from
vercel logs(historical logs aren't retrievable after the fact for this project's plan/log-drain setup — confirmed by trying). - Repair (turns the black box into a source-safe diagnostic instead of resolving blind):
VCSConnectionsnow persists alast_regeneration_error(andlast_regeneration_at) onto the connection row every time installation-token regeneration is attempted — the actual reason on failure (GitHub's own API error text; orgithub_app_credentials_not_configuredif the env gate itself was skipped), or cleared tonullon success. This flows through the existingsanitizeConnectionMetadatapass-through intoconnectionStatus.metadatawith no other route changes needed (the new keys aren't in the token/secret blocklist).VCSConnectionCard.tsxnow renders this reason under the badge when!valid. This makes the NEXT occurrence of "Refresh doesn't fix Invalid" immediately diagnosable from the card itself — for this user or anyone — without needing server log access. Verified:tsc --noEmit0 (both packages); newapps/uapi/tests/vcsConnectionCard.test.tsx(4/4); full uapi jest suite 643/644 green (1 pre-existing skip);packages/vcssuite 7/7 green. See F35 immediately below: the initial "a 404 means the installation was removed" copy this repair shipped with turned out to be WRONG for the actual case it then revealed. - Noted but out of scope here:
apps/uapi/components/base/bitcode/vcs/__tests__/VCSConnectionCard.test.tsxis a pre-existing test file for an entirely superseded implementation of this component (mocks@supabase/auth-helpers-nextjsdirectly, expects copy/icons that no longer exist) — it isn't injest.config.cjs'stestMatchallowlist, so it silently never runs and was not caught by this drift. Left as-is; a follow-up should either delete it or rewrite it against the current component.
V48-Gate3-F35 — The 404 is a stale installation id from a DIFFERENT Bitcode GitHub App, not a removed installation — GitHub still shows the app installed (FIXED copy; DIAGNOSIS refined)
- Severity: high (live-confirmed, 2026-07-08: the F34 diagnostic surfaced
Failed to generate installation token: 404 {"message":"Not Found",...}; the org's Installed GitHub Apps page shows BOTHBitcode GitHub Auxillary(production) ANDBitcode GitHub Auxillary (Stag-Test)still installed onadvancedengineeredsoftware— disproving F34's own "the installation was removed" hint for this case). - Refined cause: per the Environments section above (
## Environments, corrected 2026-07-06), there are genuinely TWO separate Bitcode GitHub App registrations —bitcode-github-auxillary(production-mainnet) andbitcode-github-auxillary-stag-test(GITHUB_APP_ID=4224019) — and, per the 2026-07-05 "production == staging" decision, the stag-test app's credentials were pushed to ALL THREE Vercel targets (staginglocal-testnet,production,preview). A connection whose stored installation id was minted underbitcode-github-auxillary(e.g. from before that unification, or from a flow that used the production app's install link) is invisible to a JWT signed forbitcode-github-auxillary-stag-test— GitHub returns 404 for "this app doesn't own that installation id," which reads identically to "the installation was removed" even though the app is very much still installed (just the OTHER one). Confirmed can't rule this out by comparingGITHUB_APP_IDvalues directly:vercel env pullreturns encrypted vars as empty strings under this session's CLI permissions (confirmed non-sensitive vars pull fine, so this is a permission gate, not empty config). - Repair: corrected
VCSConnectionCard.tsx's 40x hint — it no longer claims "the installation was removed, reinstalling will fix it" (unverifiable and, in this case, wrong). It now says a 40x means this connection's stored installation isn't one the CURRENT deployment's app can use, even if GitHub still shows an app installed, and suggests Disconnect (in Bitcode) + reconnect — which re-runs the install/authorize flow against whichever app this deployment is actually configured with (already shown as installed in GitHub's list) and writes a fresh installation id, with no GitHub-side action needed. Updatedapps/uapi/tests/vcsConnectionCard.test.tsxto match. Verified:tsc --noEmit0;vcsConnectionCard.test.tsx4/4 green. - Not yet done: confirming from the Supabase side which specific
connectionId/app the existing stuck row actually points to (would need direct DB access, out of reach here) — Disconnect + reconnect is the actionable next step for the live account; if it does NOT resolve to Valid, the next lead is confirming this Preview deployment's actualGITHUB_APP_ID/SUPABASE_URLpairing against the documented map above. - CONFIRMED (2026-07-08, live): disconnected via a direct
fetch(..., {method:'DELETE'})from the browser console (the UI Disconnect button itself was blocked — see F36), then reconnected through Bitcode's normal Connect GitHub flow. Result: Connected / Valid. This confirms the diagnosis — the stuck row's installation id belonged to the OTHER Bitcode GitHub App; reconnecting against the currently-configured app resolved it, no GitHub-side action needed./deposits' Branch/Commit reconnect-required banner (F32) should now be gone for this account too, since it shares the sameconnectionStatus.validgate.
V48-Gate3-F36 — Refresh and Disconnect buttons on the GitHub connection card don't respond to clicks; the underlying API works fine (OPEN)
-
Severity: medium (workaround exists — the same actions can be driven directly via
fetch()from the console — but the UI affordances for two of the four card actions are dead). -
Observed (2026-07-08, live): clicking Disconnect never opened its confirmation
AlertDialogat all (confirmed explicitly — no dialog appeared). Clicking Refresh produced no visible change either. Both: no console errors, no Network tab entries for the click (not even a pending request), a brief whole-tab "hang" that self-resolves, then the page is normally responsive again. A hard reload did not change the behavior. Incognito (rules out an interfering browser extension) was not tested at the user's judgment call. -
Ruled out during investigation: NOT a server/API problem — calling the exact same endpoint directly from the console (
fetch('/api/vcs/github/connection', {method:'DELETE'})) succeeded instantly with a clean response. This isolates the bug to the click → handler path in the browser for these two specific buttons (both plain<Button onClick=...>and theAlertDialogTrigger-wrapped one), not tohandleRefresh/handleDisconnect's own logic or the route they call. -
Investigated and NOT confirmed as the cause (documented so this isn't re-litigated): (a) Radix
AlertDialognested inside Auxillaries' own customcreatePortal-based overlay — plausible stacking/portal interaction, but Auxillaries can also render as a dedicated non-modal route, unconfirmed which applies here; (b) neither the "Refresh" nor "Disconnect"<Button>sets an explicittype="button"(shadcn'sButtondoes not default it, so a bare<button>inside a<form>defaults totype="submit") — but no<form>ancestor was found in this card's actual render tree (VCSIntegrationPanel→Tabs→ this card; the sibling "Personal Access Token" tab does have its own<form>, but as a siblingTabsContent, not an ancestor, so it shouldn't matter — worth settingtype="button"explicitly anyway as a zero-risk hardening, defensive habit regardless of whether it's the cause here); (c)AnalyticsEventsClient.tsx's global capture-phasedocument.addEventListener('click', handler, true)GA4 click tracker — a plausible collision point for a delegated capture-phase listener, but its own logic is a small bounded DOM walk + a defensively-wrapped no-op-when-unloadedgtag()call, nothing that should itself hang. -
Next steps (not done — needs live repro access this session didn't have): reproduce with React DevTools Profiler recording to see if a render is actually happening during the "hang" window; check whether the same card, freshly mounted after a full reconnect-triggered page reload, still exhibits the same behavior (untested — the user's remaining verification was done via the direct-fetch workaround, not by retrying the buttons post-reconnect); check Chrome's Task Manager (Shift+Esc) for CPU during the hang to confirm/deny a compute-bound cause vs. something else entirely. — Identity / Authentication / Auxillaries — COMPLETE 2026-06-12 (email deferred by decision; F2/F9 and legacy eradication queued for gates)
-
Sign up / sign in via Connect Wallet (nav CTA → SignUpWindow → wallet signature on testnet4 →
custom:bitcode-bitcoinsession →/tps/supabase/callback) — verified 2026-06-12 after F5 fix; lands on/packs. Re-verified from fully nuked state (purged user + cleared site data): created 19:29:21 → session 19:29:25 → binding auto-written 19:29:29 by the bridge on/packsmount with no Auxillaries visit; UI consistent across nav, Wallet, and Profile panes. -
Wallet binding persisted after sign-in — verified 2026-06-12: identity-derived bind populated
username(wallet_tb1p6x70u8ag) and the fullwalletBinding(address, providerleather, networktestnet, statuspending,proofKind: provider_session,boundAtstamped on bridge mount).payment_address/addressTypestay null in identity-derived binds (GoTrue drops the custom claims); they backfill via the in-panel signature flow, which settlement-adjacent actions require anyway. -
Session persists across refresh — verified 2026-06-12 across many refreshes, dev-server restarts, and pane navigation in one session with no re-auth; F2 (triplicated GoTrueClient) produced no observable flakiness but stays open as a packaging fix.
-
[~] Profile pane readback (optional email binding) — deferred by decision 2026-06-12: wallet (plus GitHub) is the root of identity; email is optional contact only and not required for Track 1 closure. The "Add Email" repair CTA routing was fixed regardless (contract repair routes →
/packs). ThesignInWithOtpcontact-binding semantics check moves to the F4 legacy-auth eradication gate, where the email path gets disambiguated from authentication wholesale. -
Externals: GitHub App connect + repository inventory — verified 2026-06-12: installation
139922918(engineeredsoftware, repository_selectionall), provider readiness "succeeded" with source-safe token posture, 46 repositories synced intovcs_repositorieswith full metadata. Post-connect redirect retargeted from a prior product route overlay to/packs?auxillary-open-to=externals. Connected Scope pill list was hard-capped at 8 (repositories.slice(0, 8)) — now renders all in a scrollable wrap. -
QA evidence hygiene note: ad-hoc
user_connectionsdumps that selectconnection_datawholesale expose the GitHub installationaccess_token(short-lived, 1h expiry — the one pasted on 2026-06-12 expired 21:08Z; no rotation needed). Usev48_qa_03_user_connections_token_safe.sqlinstead; never paste rawconnection_data. -
Wallet pane: fauceted testnet4 balance visible — verified 2026-06-12: "BTC in wallet" shows 0.00763373 BTC live from mempool.space testnet4 (faucet tx
fff98a94…ec6dpaid the payment addresstb1q8whq…s7d8t2, 263,373 sats + follow-up). The identity-derived bind initially showed 0 because it carried only the taproot auth address; Reconnect Leather (signed-in panel flow) backfilledpaymentAddress/addressTypeand upgraded proofKind tobitcoin_message_signature— the F5a payment-address residual repair path is verified. Binding status stayspendingby design until signature verification ships. Nav chip wiring to the live source is a follow-up. -
Organization/team + treasury panes render — verified 2026-06-12: Organization Authority and treasury/wallet-posture surfaces render without errors; the Denied content posture is the parked F9 decision, not a render defect.
-
Environment: add to
apps/uapi/.env.localand restartdev:remote—BITCODE_ASSET_PACK_REAL_INFERENCE=trueandBITCODE_ASSET_PACK_REAL_INFERENCE_PROFILE=bounded(provider keys already present; without the flags, synthesis runs deterministic bring-up branches, and the pipeline host preflight rejects Read/Fit QA).BITCODE_ENABLE_PIPELINE_HOST_APIis unnecessary locally (the host route is open on non-production deployments). -
Connect repository on /deposits
-
Synthesize AssetPack options (real inference if enabled)
-
Source-safe measurement review renders
-
Approve → Depository admission readback
-
/packs?type=depository-assetpack shows the admission
Manual end-to-end verification of the Gate-3 depositing parity matrix
(BITCODE_SPEC_V48_NOTES.md → "Gate-3 depositing PARITY MATRIX"). Copy the named log
lines / UI values into §6 for the QA record.
0. Preconditions
- Branch
v48/gate-3-synthesis-pipeline-correctness;apps/uapi/.env.localthen restartdev:remote:BITCODE_ASSET_PACK_REAL_INFERENCE=true— the ONLY required flag. Do NOT setBITCODE_ASSET_PACK_REAL_INFERENCE_PROFILE(profiles removed, F26-A; it is a no-op).- Anthropic key present (default
claude-haiku-4-5since 2026-07-03;BITCODE_LLM_MODELoverrides). BITCODE_PIPELINE_HOSTunset (defaults tolocal) — the dev Node server IS the inline host, so it must havegiton PATH + a writable temp dir.
- Supabase LOCAL (decided 2026-07-03):
apps/uapi/.env.localpoints at the local stack (127.0.0.1:54321,supabase start/Docker running);dev:remoteserves against it directly. A connected GitHub repo (vcs_repositoriesrow + an activeuser_connectionsgithub row). Pick a small/medium real TS or Python repo (meaningful sizes). - Restart
dev:remoteAFTER pulling the 2026-07-02/03 correctness wave (stitch self-repair, store topology, PCC keys-selection, Thinkings rename, haiku default) — a server predating it runs stale code.
1. Run (manual) — /deposits → select repo + revision → optionally add obfuscations
- protected-IP exclusions (e.g.
secret/) + demand context → Synthesize options → watch the streaming accordion log.
2. Acceptance criteria + log copy points (line = accordion / execution_events; UI = the option card)
| # | What | COPY | PASS |
|---|---|---|---|
| Host | Provisioning {repo}@{ref} on the local host… then Checkout ready: N files (M withheld by K protected-IP exclusions; full source measured, S prompt excerpts). |
the "Checkout ready" line | N = the repo's REAL tracked-file count (full repo, not ≤400/≤10); "full source measured"; M = exclusion-withheld files. FAIL if N≤10 or "samples"/API language → stopgap still active |
| Pipeline | Running SynthesizeAssetPacks (deposit mode): Setup → Discovery → Implementation → Validation → Finish… |
the Phase pills from telemetry | all five phases present; Validation never skipped |
| Absolutes | Validation telemetry shows AssetPackMeasureAbsolutesAgent:deposit; card measurements tile: Functions: <N> functions · <v>% / weight 0.18, Types, File span, Correctness · <v>% / weight 0.28, Semantic volume |
the tile values + output.depositOptionSynthesis.options[i].measurements |
size measures carry magnitude(int)+unit+category:'absolute'; magnitudes are plausible real counts; weights sum to 1. FAIL if source-coverage/demand-alignment/reuse-likelihood appear → placeholders still in use |
| Card payload | option card emerald panel "If deposited, Bitcode receives": patchSummary + "Synthesized contents · N file(s)" (op-colored create/modify/delete) + "Provenant source · N files available to Bitcode" | …options[i].contents (patchSummary, fileChanges, provenantSourcePaths, provenantSourceCount) + screenshot |
BOTH the synthesized contents AND the provenant source files shown prominently; fileChanges are path+op only (no raw code) |
| Neediness | amber tile Neediness · est. read demand <v>% · demand <d>% · saturation <s>% + rationale |
…options[i].neediness |
present on deposit options; volume = clamp01(demand×(0.5+0.5(1−saturation))); rationale source-safe |
| Completion | Validated candidates fail-closed: A admissible, D dropped. then Synthesized X measured AssetPack options (T tokens, Ds s). |
the executions row (id=runId): context (pipelineCore=AssetPacksSynthesis, synthesisMode, optionCount, excludedPathCount, inventoryPathCount) + output |
status=completed; optionCount ≥ 1; options carry absolutes + contents + neediness. The options MUST materialize from the route's completion read (GET /api/executions/history/<runId> → .output.depositOptionSynthesis.options) — zero options with a completed pipeline = the store-topology regression (fixed 2026-07-03, test-pinned) |
| Sidecar | ~/.proofs/logs/executions/<runId>/ exists (the REAL run id, not run), files named NNNNN-phase-agent-step-failsafe-generation.{request|response|error}.json |
ls the dir |
per-run dir present; any failure writes .error.json with the literal request on disk |
| PCC selection | telemetry shows the prepare_concise_context selection generations then context:selectedKeys non-empty; context:missingKeys absent or small |
the context namespace events |
selected keys resolve (lenient resolver, 2026-07-03); an all-missing selection = the shorthand-resolution regression |
| Failsafe badges | failsafe pills read handle large inputs / handle large outputs; real failsafe work badges as · chunk N / · sum / · stitch ×N |
screenshot of a badged row (if any triggered) | stitch ×N converges in ≤2 (self-repair carries the schema error); no exceeded maximum stitch attempts failure |
3. Source-safety (the telemetry contract — all must hold)
- Accordion log +
execution_events+ theexecutionsrowoutput: NO raw source lines, NO raw prompts, NO raw provider responses, NO secrets. - Telemetry rows are ONLY LLM-call (
generation) + Tool-use, each with the full Phase→Agent→Step→Failsafe→Thricified pills; no```json/{-fragment rows. - In the persisted synthesis: every
options[i].visibility.*flag isfalse(rawSourceTextVisible, rawPromptVisible, rawProviderResponseVisible, …). - Grep the
outputJSON forPRIVATE_SOURCE_DO_NOT_SERIALIZE,BEGIN_PRIVATE_KEY, and any verbatim ≥40-char source line from the repo → must be ABSENT. (assertDepositAssetPackOptionSynthesisSourceSaferuns server-side; this is the manual cross-check.)
4. Automated baseline (run before + after the manual pass; expect all green)
pnpm -C packages/agent-generics exec jest
pnpm -C packages/pipelines/asset-pack exec jest --config jest.config.cjs # 45 suites / 227
pnpm -C packages/pipeline-hosts exec jest --config jest.config.cjs # 6 suites / 34
cd apps/uapi && pnpm exec jest depositSourceProvisioning depositSynthesizeOptionsRoute depositPageClient # 3 / 14
5. #25 SandboxHost in-box dispatch (deployment-conditional)
- Local default is local host. To exercise the sandbox path:
BITCODE_PIPELINE_HOST=sandbox- sandbox auth (
VERCEL_OIDC_TOKENviavercel env pull, orVERCEL_TOKEN/VERCEL_TEAM_ID/VERCEL_PROJECT_ID) +@vercel/sandbox+ git in the box image. Missing auth must fail closed with a clear message (not a silent hang).
- sandbox auth (
- Deposit boxes are non-persistent (
persistent: false— no snapshot billing). - Log:
Dispatching deposit synthesis to the sandbox host (in-box) for {repo}@{ref}…thensandbox: sandbox-create-started/sandbox-created/command-started/ … /sandbox-stopped. On create,executions.context.sandboxIdmust be set while status is stillrunning. - PASS: the box clones + runs the deposit SDIVF in-box;
evidence.depositOptionsreturns with formal absolutes; the SAME deposit option synthesis persists as an inline run of the same repo/revision (modulo run-to-run LLM variance). If the sandbox infra isn't deployed, mark N/A.
5b. #26 Cooperative cancel
- Start a synthesis (inline or sandbox). While Telemetry shows Cancel run, click it.
- PASS:
POST /api/executions/<runId>/cancel→ 200; rowstatus=cancelled; UI Cancelled badge; background does not flip the row tofailed; sandbox path stops the box (Observability → Sandboxes, orsandbox: sandbox-cancelled/sandbox-stoppedstatus lines). Completing a run that finished before cancel must remaincompleted(409 not_running on cancel).
6. QA record (fill per run)
| Criterion | Log/UI copied | Pass/Fail | Run id / notes |
|---|---|---|---|
| Host — full checkout | |||
| Pipeline — 5 phases, Validation present | |||
| Absolutes — real sizes (magnitude+unit+category) | |||
| Card — contents + provenant source | |||
| Neediness | |||
| Completion — persisted synthesis | |||
| Source-safety — no leak | |||
| Automated baseline green | |||
| #25 sandbox in-box (or N/A) | |||
| #26 cancel run |
Pulling a run's persisted output (for the COPY targets) — Supabase SQL editor:
select status, context, jsonb_pretty(output->'depositOptionSynthesis') as synthesis
from executions where id = '<runId>';
-- per-option absolutes / contents / neediness only:
select jsonb_pretty(output->'depositOptionSynthesis'->'options') from executions where id = '<runId>';or the history API the client uses (authenticated session):
GET /api/executions/history/<runId> → .output.depositOptionSynthesis; e.g.
… | jq '.output.depositOptionSynthesis.options[] | {title, measurements, contents, neediness}'
(source-safe fields only). The runId is in the Synthesized X measured AssetPack options…
completion event and the /deposits URL after dispatch.
- Read request on /reads
- Need synthesis + review
- Finding Fits + fit measurement review + BTC-testnet quote
- Settle (testnet) → observation → finality → BTD rights readback
- Repository PR delivery readback
- /packs master-detail: states, proof roots, repair surface
- Replay/audit: journaled rows match actions taken
- Auxillaries personal history readback