From 7319638778210ae8864c8581c220ff7c24b29120 Mon Sep 17 00:00:00 2001 From: Wibias <37517432+Wibias@users.noreply.github.com> Date: Tue, 11 Aug 2026 20:49:27 +0200 Subject: [PATCH 01/32] docs(lab): define CL-09 passive production evidence contract --- .../009_cl09_passive_production_evidence.md | 556 ++++++++++++++++++ 1 file changed, 556 insertions(+) create mode 100644 devlog/_plan/260807_compatibility_lab/009_cl09_passive_production_evidence.md diff --git a/devlog/_plan/260807_compatibility_lab/009_cl09_passive_production_evidence.md b/devlog/_plan/260807_compatibility_lab/009_cl09_passive_production_evidence.md new file mode 100644 index 000000000..7dc3ce3da --- /dev/null +++ b/devlog/_plan/260807_compatibility_lab/009_cl09_passive_production_evidence.md @@ -0,0 +1,556 @@ +# CL-09 - Passive Production Evidence / Shadow Correlation + +## Programme position + +**Repository:** `lidge-jun/opencodex` +**Integration target:** `dev` +**Branch:** `feat/cl-09-passive-production-evidence` +**Starting SHA:** `3b8f9487676fe258d76295e49e7db75aca26a4cb` +**CL-08 merge prerequisite:** satisfied by #1447 at `3b8f9487676fe258d76295e49e7db75aca26a4cb` + +CL-08 is merged and closed. This document defines the next Compatibility Lab boundary. + +This initial CL-09 PR is contract-only. Implementation is not authorized until the contract is reviewed. + +--- + +# 1. Goal + +CL-09 answers: + +> How can already-completed production requests contribute privacy-safe, exact-route operational evidence to Compatibility Lab read surfaces without issuing extra provider requests, ingesting user content, changing canonical compatibility verdicts, or creating a routing feedback loop? + +The V1 architecture is deliberately passive: + +```text +Production request + | + v +Existing route decision + per-attempt outcome + final usage row + | + | capture exact local Lab route-subject ID only + v +Bounded passive-signal adapter + | + v +Read-side production-signal projection + | + +--> Lab CLI/API/UI correlation + | + X--> no CL-02 canonical observation in V1 + X--> no Routing Profile / Router Intelligence input in V1 + X--> no CL-08 scheduling trigger +``` + +CL-09 V1 observes work that already happened. It does not create work. + +--- + +# 2. Naming boundary + +OpenCodex already has a feature called **Shadow Call Intercept** in `src/lib/shadow-call.ts` and `docs/shadow-call-intercept.md`. That feature rewrites specific Codex helper-model requests. + +CL-09 is unrelated. + +CL-09 must not: + +- change `shadowCallIntercept` matching or rewrite behavior; +- use helper/shadow model detection as an evidence source; +- route production requests to a second model; +- duplicate a production request for comparison; +- overload the existing Shadow Call Intercept configuration. + +Within implementation code and APIs, prefer `passive production evidence`, `production signal`, or `passive signal` over the ambiguous term `shadow call`. + +--- + +# 3. Chosen V1 approach + +Three approaches were considered. + +## 3.1 Chosen: passive correlation only + +Use metadata already produced by normal request execution and add only the minimum exact Lab subject linkage required for deterministic correlation. + +Benefits: + +- zero extra provider traffic; +- no duplicate quota/cost; +- no user request replay; +- no new prompt/response retention; +- no compatibility self-reinforcement in routing; +- no CL-02 event-schema change required for V1. + +## 3.2 Deferred: direct passive observation promotion + +A future phase may define scenario manifests whose assertions can be evaluated entirely from a closed, privacy-safe production metadata contract and may add a distinct passive execution mode or event schema version. + +That work is not CL-09 V1 because current `ObservationEvent` semantics are scenario/fixture execution semantics and current execution modes are `fixture`, `live`, and `fabric`. Arbitrary user traffic is not equivalent to a reviewed Lab-owned synthetic scenario. + +## 3.3 Rejected: duplicate shadow execution + +CL-09 must not replay, fork, mirror, sample, or duplicate user requests to another route. That would add provider traffic, copy user content, complicate consent and credential boundaries, and violate the no-production-path-execution invariant. + +--- + +# 4. Hard V1 invariants + +CL-09 V1 must guarantee: + +```text +0 extra provider requests +0 duplicated user requests +0 production request mutation +0 new routing candidates +0 Routing Profile changes +0 Router Intelligence score changes +0 CL-08 scheduling decisions from passive signals +0 canonical Lab verdict changes from passive signals +0 prompt or conversation ingestion +0 response-body ingestion +0 tool-payload ingestion +0 credential/account identity ingestion +0 public publishing +``` + +A failure in passive evidence capture must never fail or delay the production request. + +--- + +# 5. Existing authorities remain unchanged + +CL-09 must reuse rather than replace: + +- `PersistedUsageEntry` and `PersistedUsageAttempt` as production execution history; +- `RouteDecisionTraceV1` as why-this-route authority; +- CL-02 JSONL as canonical Lab compatibility evidence; +- CL-02 SQLite as disposable Lab compatibility projection; +- exact `RouteSubjectV1` / `subjectId` construction from the existing Lab subject boundary; +- CL-04 read surfaces; +- CL-05 Compatibility Matrix UI; +- CL-06 compatibility policy and routing consumption; +- CL-08 automation orchestration. + +CL-09 creates no second request log, route trace, compatibility ledger, or verdict system. + +--- + +# 6. Exact route correlation + +Production outcomes are useful only when they are attributable to the exact route behavior that produced them. + +Request-level provider/model fields are insufficient because retries and fallback attempts may execute different routes. Therefore V1 correlation is per execution attempt. + +The implementation may extend the persisted attempt metadata with a local-only field approximately like: + +```ts +interface PersistedUsageAttempt { + // existing fields + labRouteSubjectId?: string; +} +``` + +The field is captured while the exact attempt route context still exists. It is the existing Lab route-subject digest/ID, not a new identity scheme. + +Rules: + +- capture the subject ID for each actual attempt independently; +- never infer an old attempt's subject from current config after the fact; +- do not backfill historical rows whose exact route subject was not captured; +- do not assume the initial route-decision candidate is the same route as a fallback attempt; +- a subject-construction failure omits passive linkage and does not affect request execution; +- no Lab SQLite or ledger read is allowed on the production request path. + +If the current route behavior fingerprint changes, new attempts receive the new subject ID. Old production signals remain attached only to the old exact subject. + +--- + +# 7. Passive signal model + +CL-09 V1 derives a bounded read-only signal from already-sanitized production metadata. + +Conceptually: + +```ts +interface PassiveRouteSignalV1 { + schemaVersion: 1; + subjectId: string; + source: "production_usage_v1"; + requestRef: string; + decisionRef?: string; + attemptOrdinal: number; + observedAt: number; + outcome: "success" | "client_cancel" | "route_error" | "environmental" | "unknown"; + httpStatus?: number; + terminalStatus?: string; + closeReason?: string; + errorCode?: string; +} +``` + +This is a conceptual contract, not authorization to persist a second copy. + +The preferred V1 implementation derives these signals from the existing usage/history authority at read time or in an existing disposable history projection. + +The signal must not include: + +- `upstreamError` text; +- prompts/messages; +- response text/bodies; +- tool arguments/results; +- headers; +- URLs/IP addresses; +- `apiKeyId`; +- account references or account identity; +- conversation IDs in Lab-facing output; +- reasoning content; +- arbitrary provider diagnostics. + +--- + +# 8. Canonical compatibility boundary + +Passive production signals are **not** CL-02 `ObservationEvent` records in V1. + +They therefore cannot: + +- satisfy a scenario pass; +- refresh scenario freshness; +- make a suite `PROBED` or `VERIFIED`; +- make a suite `DEGRADED` or `UNSUPPORTED`; +- clear a `BLOCKED` verdict; +- participate in CL-06 minimum compatibility thresholds; +- cause CL-08 to enqueue or suppress a Lab run. + +The UI and API must label them clearly as **observed production traffic, not Lab verification**. + +This avoids claiming that an arbitrary user request exercised the exact synthetic assertions frozen by a scenario manifest. + +--- + +# 9. No routing feedback loop + +CL-09 V1 is read-side only from the perspective of routing semantics. + +The following components must not consume passive signals: + +- Routing Profile evaluator; +- Router Intelligence eligibility; +- Router Intelligence scoring; +- health/quota/cost weighting; +- model selection; +- provider discovery; +- fallback policy; +- CL-08 planner. + +The production request path may compute/carry the exact local subject ID for its own attempt log entry, but it must not query passive history or Lab compatibility state as part of CL-09. + +Any future use of passive evidence in routing requires a separate reviewed contract because production-observed traffic creates sampling and self-selection bias. + +--- + +# 10. Failure classification + +A production request failure is not automatically a compatibility failure. + +V1 passive classification is diagnostic only. + +Examples: + +- client cancellation => `client_cancel`; +- clearly normalized route/upstream terminal failure => `route_error` signal; +- known environment/admission failure => `environmental` signal; +- ambiguous HTTP/user/application outcome => `unknown`; +- completed successful route attempt => `success`. + +Generic 4xx/5xx status alone must not be interpreted as `UNSUPPORTED`, `DEGRADED`, or any other canonical Lab verdict. + +No LLM judge or content inspection is allowed to classify passive outcomes. + +--- + +# 11. Privacy and data minimization + +CL-00 security/privacy remains authoritative. + +CL-09 is specifically forbidden from reading or copying: + +- user prompts or conversation history; +- response bodies or generated text; +- user files/repositories/worktrees; +- tool/MCP payloads; +- hidden reasoning; +- raw provider errors; +- credentials, auth headers, tokens, cookies; +- account IDs/emails/aliases; +- raw custom headers; +- arbitrary URLs or filesystem paths. + +`requestRef` and `decisionRef` are local correlation references only. They are not public-export fields. + +The route subject ID remains installation-local and opaque. CL-09 does not export the subject salt or reverse-map it. + +Existing privacy scanning remains defense in depth. Tests must include canary prompt, credential, account, and response strings and prove none appear in passive Lab output. + +--- + +# 12. Persistence and retention + +V1 must not copy `usage.jsonl` rows into `compatibility.jsonl`. + +Preferred authority: + +```text +usage.jsonl / existing routing-history projection + | + v +bounded passive read projection +``` + +Rules: + +- no new canonical passive ledger; +- no raw production payload artifacts; +- no passive artifact store; +- retention follows the existing request/usage retention authority; +- when source request history is deleted, the passive signal disappears; +- corrupt or unparseable usage rows fail closed and are skipped; +- historical rows without an exact captured Lab subject ID remain unlinked rather than guessed. + +--- + +# 13. Read surfaces + +CL-09 should extend existing Lab read surfaces rather than create a separate product area. + +Useful subject-level summary fields are approximately: + +```text +recent production attempts +recent successful attempts +recent route-error signals +last observed production attempt +``` + +Requirements: + +- bounded time window and result count; +- deterministic pagination where detail is exposed; +- no network activity; +- no projection rebuild triggered by a read; +- no prompt/body/error-text exposure; +- explicit `not verification` labeling. + +The Compatibility Matrix may show a compact production-signal indicator beside canonical Lab evidence. It must not merge the two into one status or score. + +CLI/API naming must be audited against the existing CL-04 surfaces before implementation. + +--- + +# 14. Default behavior and configuration + +CL-09 V1 requires no new provider-traffic opt-in because it creates no provider traffic. + +Do not add a new configuration flag unless implementation audit finds a real retention or resource boundary that cannot be expressed through existing request-history controls. + +If usage/history persistence is disabled or unavailable, passive production evidence is simply unavailable. + +No configuration may enable direct verdict promotion in CL-09 V1. + +--- + +# 15. Production-path performance boundary + +The only CL-09 work permitted on a production attempt path is bounded exact-subject linkage using already-available trusted route context and adding the resulting opaque ID to the existing attempt record. + +Forbidden on the production path: + +- Lab ledger reads/writes for passive evidence; +- Lab SQLite queries/rebuilds; +- usage-history scans; +- scenario evaluation; +- passive aggregation; +- synchronous disk writes beyond the existing usage logging path; +- network calls; +- retries introduced by CL-09. + +Subject-link failure must be best-effort telemetry failure, never request failure. + +--- + +# 16. Backward compatibility + +Existing usage rows without passive subject linkage must continue to parse unchanged. + +Additive attempt metadata must remain optional. + +Do not rewrite existing usage history to invent exact historical subjects. + +Existing Lab event schema and execution modes remain unchanged in CL-09 V1. + +Existing Shadow Call Intercept behavior must be byte-for-byte semantically unchanged by CL-09. + +--- + +# 17. Adversarial tests + +Required coverage includes: + +## Zero extra traffic + +- enabling/using passive read surfaces does not increase provider send count; +- no duplicate request body is constructed or dispatched; +- CL-08 does not schedule from a passive signal. + +## Exact subject attribution + +- one attempt records its exact route subject ID; +- fallback attempts record different exact subjects when routes differ; +- a behavior-fingerprint change produces a new subject ID; +- old signals do not attach to the new subject; +- subject construction failure omits the link without failing the request. + +## Canonical evidence isolation + +- passive success does not change `UNKNOWN`/`CLAIMED`/`PROBED`/`VERIFIED`; +- passive failure does not produce `DEGRADED`/`UNSUPPORTED`; +- passive timestamps do not refresh scenario freshness; +- Routing Profile evaluation is identical with and without passive signals; +- Router Intelligence selection/score is identical with and without passive signals. + +## Privacy + +Seed canaries in: + +- prompt; +- response text; +- tool arguments/results; +- credentials; +- account metadata; +- raw error text. + +Assert none appear in: + +- passive API response; +- passive CLI output; +- Compatibility Matrix payload; +- Lab JSONL/SQLite/artifacts; +- logs/errors produced by CL-09. + +## Compatibility + +- old usage rows still parse; +- malformed passive subject IDs are ignored/fail closed; +- bounded pagination cannot scan unbounded history; +- deletion/retention of source usage removes passive visibility; +- Shadow Call Intercept tests remain unchanged and green. + +--- + +# 18. Delivery sequence + +## CL-09.0 - Audit and contract + +This PR: + +- record CL-08 closure; +- audit current production usage/route evidence and Lab boundaries; +- freeze passive evidence semantics; +- explicitly reject duplicate shadow execution and direct verdict promotion. + +No runtime implementation. + +## CL-09.1 - Exact attempt subject linkage + +Implement the minimal optional exact Lab route-subject ID on persisted attempts. + +No passive UI/API yet. + +## CL-09.2 - Bounded passive query layer + +Implement read-side production-signal derivation with strict field allowlists, bounds, and no canonical Lab writes. + +## CL-09.3 - Existing Lab surfaces + +Expose compact passive summaries through existing Lab management/CLI/UI conventions. + +No new dashboard product area. + +## CL-09.4 - Adversarial isolation + +Prove privacy, zero extra traffic, routing invariance, backward compatibility, and cross-platform behavior. + +--- + +# 19. Explicit non-goals + +CL-09 V1 must not implement: + +- replayed/duplicated shadow requests; +- A/B production request mirroring; +- user prompt or response capture; +- canonical passive `ObservationEvent` creation; +- new Lab execution mode; +- scenario pass/fail from arbitrary user traffic; +- compatibility verdict promotion/degradation from passive signals; +- Routing Profile mutation; +- Router Intelligence behavior changes; +- CL-08 planner changes based on production traffic; +- provider metadata mutation; +- health/quota scoring changes; +- public export/publishing; +- community leaderboard; +- remote telemetry upload; +- Shadow Call Intercept changes. + +Public evidence export/publishing remains a separate later phase, provisionally CL-10, because it has a materially different privacy and trust boundary. + +--- + +# 20. Validation + +Contract PR minimum: + +```text +git diff --check +repository markdown / hygiene checks +CodeRabbit / independent review +``` + +Implementation phases must additionally run: + +```text +bun x tsc --noEmit +bun run privacy:scan +focused usage/request-log tests +focused Lab query/projection tests +routing/profile regressions +shadow-call regressions +full cross-platform CI +``` + +--- + +# 21. Acceptance criteria + +CL-09 V1 is accepted only when: + +1. CL-08 remains accepted/merged and current `dev` is the base; +2. production evidence causes zero extra provider requests; +3. each correlated attempt uses the exact captured Lab route subject ID; +4. fallback attempts cannot be misattributed to the original selected route; +5. historical rows are never backfilled by guessing current config; +6. user prompts/responses/tool payloads are never read or copied into Lab passive output; +7. credential/account material never enters passive output; +8. passive signals do not write canonical CL-02 observations; +9. passive signals do not change canonical compatibility verdicts/freshness; +10. passive signals do not affect Routing Profiles, Router Intelligence, health, fallback, or CL-08 scheduling; +11. old usage rows remain backward-compatible; +12. passive reads are bounded and do not trigger network/projection rebuild work; +13. existing Shadow Call Intercept behavior is unchanged; +14. privacy scan and focused adversarial tests pass; +15. full CI passes for implementation phases; +16. all valid CodeRabbit Critical/High/Medium findings are resolved; +17. independent final review reports `MERGE`. + +Do not start public publishing / CL-10 until CL-09 is accepted and merged. From 7863a812a808729e5eb08c70241dc3039bd732da Mon Sep 17 00:00:00 2001 From: Wibias <37517432+Wibias@users.noreply.github.com> Date: Tue, 11 Aug 2026 20:50:35 +0200 Subject: [PATCH 02/32] docs(lab): advance programme through CL-09 contract --- .../000_master_plan.md | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/devlog/_plan/260807_compatibility_lab/000_master_plan.md b/devlog/_plan/260807_compatibility_lab/000_master_plan.md index c34c30bdc..a26466588 100644 --- a/devlog/_plan/260807_compatibility_lab/000_master_plan.md +++ b/devlog/_plan/260807_compatibility_lab/000_master_plan.md @@ -263,19 +263,23 @@ request path. ## Programme phases -Only CL-00 is authorized by this document at present. +Programme authorization is tracked below. CL-09 contract drafting is authorized by merged CL-08; CL-09 runtime implementation remains gated on acceptance of the CL-09 contract. | Phase | Purpose | Authorization | |---|---|---| -| CL-00 | Architecture authority, contracts, scenario catalogue, incident corpus | This PR | -| CL-01 | Deterministic protocol-conformance runner and fixtures | Not started; requires CL-00 to be accepted | -| CL-02 | Immutable JSONL ledger, artifacts and SQLite projection | Not started | -| CL-03 | Bounded live-route probes | Not started | -| CL-04 | Lab CLI and management read surfaces | Not started | -| CL-05 | Compatibility Matrix UI | Not started | -| CL-06 | Existing Routing Profile compatibility controls and Router Intelligence consumption | **ACCEPTED/CLOSED** — merged #1394 at `b66e33ce7207d91014644d99317e456c992a3418` | -| CL-07 | Agent Fabric task-effectiveness ingestion | **ACCEPTED/CLOSED** — merged #1438 at `02e62fc8c7354c544ef71f8bb3db5ebba42cb600` | -| CL-08 | Shadow/automatic/public evidence workflows | Not started | +| CL-00 | Architecture authority, contracts, scenario catalogue, incident corpus | **ACCEPTED/CLOSED** - merged #1286 | +| CL-01 | Deterministic protocol-conformance runner and fixtures | **ACCEPTED/CLOSED** - merged #1320 | +| CL-02 | Immutable JSONL ledger, artifacts and SQLite projection | **ACCEPTED/CLOSED** - merged #1333 plus hardening/closure | +| CL-03 | Bounded live-route probes | **ACCEPTED/CLOSED** - merged #1352 | +| CL-04 | Lab CLI and management read surfaces | **MERGED** - #1378 | +| CL-05 | Compatibility Matrix UI | **MERGED** - #1384 | +| CL-06 | Existing Routing Profile compatibility controls and Router Intelligence consumption | **ACCEPTED/CLOSED** - merged #1394 at `b66e33ce7207d91014644d99317e456c992a3418` | +| CL-07 | Agent Fabric task-effectiveness ingestion | **ACCEPTED/CLOSED** - merged #1438 at `02e62fc8c7354c544ef71f8bb3db5ebba42cb600` | +| CL-08 | Bounded automatic evidence refresh/orchestration | **ACCEPTED/CLOSED** - merged #1447 at `3b8f9487676fe258d76295e49e7db75aca26a4cb` | +| CL-09 | Passive production-evidence correlation with zero extra traffic and no routing feedback | **CONTRACT DRAFT** - #1489; implementation not authorized | +| CL-10 | Public export/publishing/community evidence | Not started; separate privacy/trust boundary | + +The original CL-00 planning bucket combined shadow, automatic, and public evidence workflows. Accepted later plans split that bucket deliberately: CL-08 owns bounded automation, CL-09 defines passive production evidence, and public publishing remains separate CL-10 work. Phase numbering after CL-01 is programme planning, not implementation authorization. A later accepted plan may split a phase while preserving these From 597b684dd2a3d219c5c3b95e6f82388ffdbb9ae4 Mon Sep 17 00:00:00 2001 From: Wibias <37517432+Wibias@users.noreply.github.com> Date: Tue, 11 Aug 2026 20:51:55 +0200 Subject: [PATCH 03/32] docs(lab): record CL-08 closure and CL-09 start --- .../001_pr_stack_status.md | 29 +++++++++++++++++-- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/devlog/_plan/260807_compatibility_lab/001_pr_stack_status.md b/devlog/_plan/260807_compatibility_lab/001_pr_stack_status.md index a6a705cab..99241167b 100644 --- a/devlog/_plan/260807_compatibility_lab/001_pr_stack_status.md +++ b/devlog/_plan/260807_compatibility_lab/001_pr_stack_status.md @@ -27,6 +27,9 @@ independent review, blockers, and whether a later phase is authorized. | CL-05 | `feat/cl-05-compatibility-matrix-ui` | `d517161aeaa3a974ad3c0360ff0c97b03b4c4520` | `2a159b8b7` (Models tab placement) | [#1384](https://github.com/lidge-jun/opencodex/pull/1384) | MERGED TO `dev` at `1072b9c39c48a4982229131613ac300560740742` | | CL-06 | `feat/cl-06-routing-profile-compatibility` | `1072b9c39c48a4982229131613ac300560740742` | `b96eae83f2a6d1654472aeeef84799070743aeb8` | [#1394](https://github.com/lidge-jun/opencodex/pull/1394) | MERGED TO `dev` at `b66e33ce7207d91014644d99317e456c992a3418`; ACCEPTED/CLOSED | | CL-07 | `feat/cl-07-task-effectiveness-producer` | `b66e33ce7207d91014644d99317e456c992a3418` | `0efe2c69514d3baefee686383fe740e4ecb37d83` | [#1438](https://github.com/lidge-jun/opencodex/pull/1438) | MERGED TO `dev` at `02e62fc8c7354c544ef71f8bb3db5ebba42cb600`; ACCEPTED/CLOSED | +| CL-08 | `feat/cl-08-lab-automation` | `da8ebd3135553c1d4dd85c1f258e998a5de14f28` | `bfaad5d01a975e8d48b9437bc0a0537077a04134` | [#1447](https://github.com/lidge-jun/opencodex/pull/1447) | MERGED TO `dev` at `3b8f9487676fe258d76295e49e7db75aca26a4cb`; ACCEPTED/CLOSED | +| CL-09 | `feat/cl-09-passive-production-evidence` | `3b8f9487676fe258d76295e49e7db75aca26a4cb` | CONTRACT DRAFT | [#1489](https://github.com/lidge-jun/opencodex/pull/1489) | CONTRACT REVIEW; runtime implementation not authorized | + The CL-01 starting SHA is the exact CL-00 tip recorded when CL-01 began. Its moving base-ref name is not a substitute for that historical SHA. @@ -164,7 +167,8 @@ Claims cannot produce `PROBED`/`VERIFIED`. - CL-05: **MERGED** via #1384 at `1072b9c39c48a4982229131613ac300560740742`. - CL-06: **ACCEPTED/CLOSED** via [#1394](https://github.com/lidge-jun/opencodex/pull/1394), merged to `dev` at `b66e33ce7207d91014644d99317e456c992a3418`. - CL-07: **ACCEPTED/CLOSED** via [#1438](https://github.com/lidge-jun/opencodex/pull/1438), merged to `dev` at `02e62fc8c7354c544ef71f8bb3db5ebba42cb600`; accepted head `0efe2c69514d3baefee686383fe740e4ecb37d83`; plan `007_cl07_task_effectiveness.md`. -- CL-08: **not started**. +- CL-08: **ACCEPTED/CLOSED** via [#1447](https://github.com/lidge-jun/opencodex/pull/1447), merged to `dev` at `3b8f9487676fe258d76295e49e7db75aca26a4cb`; final source head `bfaad5d01a975e8d48b9437bc0a0537077a04134`; plan `008_cl08_automation.md`. +- CL-09: **CONTRACT DRAFT** via [#1489](https://github.com/lidge-jun/opencodex/pull/1489), based exactly on CL-08 merge `3b8f9487676fe258d76295e49e7db75aca26a4cb`; runtime implementation is not authorized until contract review. ## CL-06 closure log @@ -179,7 +183,26 @@ Claims cannot produce `PROBED`/`VERIFIED`. - **Accepted / source head:** `0efe2c69514d3baefee686383fe740e4ecb37d83` - **Starting/base SHA:** `b66e33ce7207d91014644d99317e456c992a3418` (CL-06 merge #1394) - **Scope delivered:** bounded `src/lab/fabric/` task-effectiveness producer, exact-tree-diff verifier, scratch sandbox, trusted-route persistence boundary, isolated child producer with parent-owned IPC/timeouts. -- **CL-08:** not started (explicit non-goal). +- **CL-08:** completed and merged via #1447. + +## CL-08 closure log + +- **Merge commit on `dev`:** `3b8f9487676fe258d76295e49e7db75aca26a4cb` ([#1447](https://github.com/lidge-jun/opencodex/pull/1447)) +- **Final / source head:** `bfaad5d01a975e8d48b9437bc0a0537077a04134` +- **Original starting/base SHA:** `da8ebd3135553c1d4dd85c1f258e998a5de14f28`; final source branch was rebased onto then-current `dev` before merge. +- **Scope delivered:** bounded default-off Lab automation, deterministic planner/queue/recovery, budgets/cooldowns, CL-01 and trusted CL-03 dispatch, management API/CLI controls, owner-scoped server lifecycle, atomic policy/routes configuration, and adversarial regression coverage. +- **Task-effectiveness background:** deliberately remained disabled; manual CL-07 execution unchanged. +- **CL-09:** contract drafting authorized from exact CL-08 merge. + +## CL-09 start log (2026-08-11) + +- **Starting/base SHA:** `3b8f9487676fe258d76295e49e7db75aca26a4cb` (exact CL-08 merge #1447) +- **Branch:** `feat/cl-09-passive-production-evidence` +- **PR:** [#1489](https://github.com/lidge-jun/opencodex/pull/1489) (draft, contract-only at open) +- **Plan:** `009_cl09_passive_production_evidence.md` +- **Scope:** exact per-attempt local route-subject correlation for already-completed production traffic, bounded read-side passive signals, and additive Lab read surfaces with zero extra provider requests. +- **Hard boundary:** V1 passive signals do not write CL-02 observations, change compatibility verdicts/freshness, affect CL-06 routing, or trigger CL-08 automation. +- **Explicitly out of scope:** duplicated shadow requests, prompt/response capture, Shadow Call Intercept changes, direct passive-to-verdict promotion, and public publishing/CL-10. ## CL-07 start log @@ -234,4 +257,4 @@ Claims cannot produce `PROBED`/`VERIFIED`. - **Starting `upstream/dev` SHA:** `68c71a4e9cdf882d812f09fd94783a28749db629` - **Branch:** `feat/cl-04-lab-read-surfaces` - **Scope:** read-only CLI (`ocx lab`), authenticated `GET /api/lab/*`, shared `src/lab/query/` layer -- **CL-05:** not started +- **CL-05:** not started \ No newline at end of file From 74c388e957f66ab0c90d8626d618694eb059a074 Mon Sep 17 00:00:00 2001 From: Wibias <37517432+Wibias@users.noreply.github.com> Date: Wed, 12 Aug 2026 03:08:45 +0200 Subject: [PATCH 04/32] docs(lab): add CL-09 implementation plan --- ...-08-12-cl09-passive-production-evidence.md | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 docs/superpowers/plans/2026-08-12-cl09-passive-production-evidence.md diff --git a/docs/superpowers/plans/2026-08-12-cl09-passive-production-evidence.md b/docs/superpowers/plans/2026-08-12-cl09-passive-production-evidence.md new file mode 100644 index 000000000..b94f879e4 --- /dev/null +++ b/docs/superpowers/plans/2026-08-12-cl09-passive-production-evidence.md @@ -0,0 +1,64 @@ +# CL-09 Passive Production Evidence Implementation Plan + +**Goal:** Implement the approved CL-09 V1 contract on `feat/cl-09-passive-production-evidence` without changing canonical Compatibility Lab verdict semantics or production routing behavior. + +**Authority:** `devlog/_plan/260807_compatibility_lab/009_cl09_passive_production_evidence.md` + +**Current base:** `dev@e8db4e0365b12a314d1c08ec2cf81599efe5b2d3` + +## Task 1 - Exact production-attempt subject linkage + +**Files:** +- `src/usage/log.ts` +- `src/routing/compatibility/subject.ts` +- `src/server/responses/core.ts` +- focused request-log/usage/compatibility tests + +1. Add RED tests proving an optional exact Lab route-subject ID survives persisted-attempt normalization, malformed values are omitted, and legacy rows remain compatible. +2. Add RED tests proving the production subject resolver preserves existing CL-06 Responses semantics and distinguishes supported inbound protocol surfaces. +3. Add RED request execution tests proving each actual fallback/combo attempt receives the exact subject ID for the route it executed, while subject-link failure never changes request success/failure. +4. Implement only the minimum additive production-path linkage. Reuse the existing Lab `RouteSubjectV1` / `subjectIdForSubject` identity authority. Do not query the Lab ledger, Lab SQLite, history, or network on the request path. + +## Task 2 - Bounded passive query layer + +**Files:** +- new focused module under `src/lab/query/` +- `src/lab/query/index.ts` +- focused passive-query tests + +1. Add RED tests for strict allowlisted output, deterministic bounded scanning/pagination, exact-subject filtering, conservative outcome classification, and deletion/retention behavior. +2. Implement `PassiveRouteSignalV1` as a read-side projection over existing normalized usage history. Do not create a new ledger, SQLite authority, artifact store, or CL-02 event. +3. Expose subject-level summaries for recent attempts, successes, route-error diagnostic signals, and last observed attempt. +4. Keep generic HTTP failures diagnostic/unknown. Passive signals never alter compatibility verdicts or freshness. + +## Task 3 - Existing Lab read surfaces + +**Files:** +- `src/server/management/lab-routes.ts` +- `src/cli/lab.ts` +- `gui/src/pages/compatibility-matrix-api.ts` +- `gui/src/pages/compatibility-matrix-shared.ts` +- `gui/src/pages/CompatibilityMatrix.tsx` +- minimal Lab i18n/style files only if needed +- focused API/CLI/UI tests + +1. Add RED API and CLI tests for bounded passive reads and explicit `not verification` semantics. +2. Add RED UI/shared-model tests proving passive production data is visually separate from canonical Lab verdicts. +3. Implement additive endpoints/commands/components using existing authentication, pagination, and Compatibility Matrix conventions. Do not create a new product area or combined score. + +## Task 4 - Adversarial isolation and privacy + +**Files:** focused regressions only; production files only if a defect is found. + +1. Prove passive capture/read paths cause zero provider sends and no request replay/mirroring. +2. Prove passive data does not change Routing Profile evaluation, Router Intelligence eligibility/score/selection, canonical Lab verdict/freshness, or CL-08 planner output. +3. Seed prompt, response, tool, credential, account, and raw-error canaries and prove none appear in passive API/CLI/UI payloads, Lab JSONL/SQLite/artifacts, or CL-09 diagnostics. +4. Keep existing Shadow Call Intercept tests unchanged and green. + +## Task 5 - Verification and handoff + +1. Run focused tests through GitHub Actions because this execution environment has no Bun runtime. +2. Verify `bun x tsc --noEmit`, `bun run privacy:scan`, repo hygiene, focused Lab/request/routing tests, Cross-platform CI, React Doctor, and `git diff --check` equivalents/checks in CI. +3. Inspect CodeRabbit/review threads and fix valid findings without widening scope. +4. Update PR #1489 description from contract-only to the delivered implementation and leave it draft for independent final review. +5. Do not merge. From 49577995534d28af59888a9b447e5edc58ce3318 Mon Sep 17 00:00:00 2001 From: Wibias <37517432+Wibias@users.noreply.github.com> Date: Wed, 12 Aug 2026 03:10:58 +0200 Subject: [PATCH 05/32] test(lab): define CL-09 attempt linkage contract --- tests/lab-passive-production-evidence.test.ts | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 tests/lab-passive-production-evidence.test.ts diff --git a/tests/lab-passive-production-evidence.test.ts b/tests/lab-passive-production-evidence.test.ts new file mode 100644 index 000000000..cb0143301 --- /dev/null +++ b/tests/lab-passive-production-evidence.test.ts @@ -0,0 +1,69 @@ +import { describe, expect, test } from "bun:test"; +import { + normalizeUsageEntryForTest, + type PersistedUsageEntry, +} from "../src/usage/log"; + +function usageEntryWithAttempt(attempt: Record): PersistedUsageEntry { + return { + requestId: "ocx-cl09-passive", + timestamp: 1, + provider: "combo", + model: "combo/test", + status: 200, + durationMs: 5, + usageStatus: "unreported", + attempts: [{ + ordinal: 1, + provider: "provider-a", + model: "model-a", + adapter: "openai-chat", + status: 200, + durationMs: 4, + sendCount: 1, + recoveryKinds: [], + usageStatus: "unreported", + ...attempt, + } as never], + }; +} + +describe("CL-09 passive production attempt linkage", () => { + test("preserves an exact local Lab route subject id on a persisted attempt", () => { + const subjectId = "a".repeat(64); + + const normalized = normalizeUsageEntryForTest(usageEntryWithAttempt({ + labRouteSubjectId: subjectId, + })); + + expect(normalized.attempts?.[0]).toMatchObject({ + ordinal: 1, + labRouteSubjectId: subjectId, + }); + }); + + test("omits malformed route subject linkage without dropping the attempt", () => { + const normalized = normalizeUsageEntryForTest(usageEntryWithAttempt({ + labRouteSubjectId: "not-a-subject-id", + })); + + expect(normalized.attempts?.[0]?.ordinal).toBe(1); + expect(normalized.attempts?.[0]).not.toHaveProperty("labRouteSubjectId"); + }); + + test("keeps legacy attempts without CL-09 linkage unchanged", () => { + const normalized = normalizeUsageEntryForTest(usageEntryWithAttempt({})); + + expect(normalized.attempts).toEqual([{ + ordinal: 1, + provider: "provider-a", + model: "model-a", + adapter: "openai-chat", + status: 200, + durationMs: 4, + sendCount: 1, + recoveryKinds: [], + usageStatus: "unreported", + }]); + }); +}); From 46f3c4747deedea15b60a5eb20226ca5ca4fd2b5 Mon Sep 17 00:00:00 2001 From: Wibias <37517432+Wibias@users.noreply.github.com> Date: Wed, 12 Aug 2026 03:18:36 +0200 Subject: [PATCH 06/32] feat(lab): persist exact passive route subject linkage --- src/usage/log.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/usage/log.ts b/src/usage/log.ts index 04dc05f52..5f8ac1807 100644 --- a/src/usage/log.ts +++ b/src/usage/log.ts @@ -37,6 +37,8 @@ export interface PersistedUsageAttempt { usage?: OcxUsage; totalTokens?: number; errorCode?: string; + /** Installation-local exact Compatibility Lab route-subject digest for this attempt. */ + labRouteSubjectId?: string; /** Target-specific reasoning intent and exact adapter-normalized wire parameter. */ requestedEffort?: string; effectiveEffort?: string; @@ -195,6 +197,11 @@ const USAGE_STATUSES = new Set([ "unsupported", "estimated", ]); +const LAB_ROUTE_SUBJECT_ID_RE = /^[0-9a-f]{64}$/; + +export function isLabRouteSubjectId(value: unknown): value is string { + return typeof value === "string" && LAB_ROUTE_SUBJECT_ID_RE.test(value); +} function isNonNegativeFiniteNumber(value: unknown): value is number { return typeof value === "number" && Number.isFinite(value) && value >= 0; @@ -272,6 +279,9 @@ function normalizeUsageAttempt(raw: unknown): PersistedUsageAttempt | null { ? { totalTokens: attempt.totalTokens } : {}), ...(typeof attempt.errorCode === "string" ? { errorCode: attempt.errorCode } : {}), + ...(isLabRouteSubjectId(attempt.labRouteSubjectId) + ? { labRouteSubjectId: attempt.labRouteSubjectId } + : {}), ...(typeof attempt.requestedEffort === "string" && attempt.requestedEffort ? { requestedEffort: capMetadataString(attempt.requestedEffort) } : {}), From 642e47e67033e9e2fdb60bee2247692f7e8b8890 Mon Sep 17 00:00:00 2001 From: Wibias <37517432+Wibias@users.noreply.github.com> Date: Wed, 12 Aug 2026 03:24:25 +0200 Subject: [PATCH 07/32] test(lab): define production wire subject identity --- tests/lab-passive-production-evidence.test.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/lab-passive-production-evidence.test.ts b/tests/lab-passive-production-evidence.test.ts index cb0143301..8c1dfb18e 100644 --- a/tests/lab-passive-production-evidence.test.ts +++ b/tests/lab-passive-production-evidence.test.ts @@ -3,6 +3,7 @@ import { normalizeUsageEntryForTest, type PersistedUsageEntry, } from "../src/usage/log"; +import { inboundProtocolForWire } from "../src/routing/compatibility/subject"; function usageEntryWithAttempt(attempt: Record): PersistedUsageEntry { return { @@ -66,4 +67,10 @@ describe("CL-09 passive production attempt linkage", () => { usageStatus: "unreported", }]); }); + + test("maps each production inbound wire to its canonical Lab protocol identity", () => { + expect(inboundProtocolForWire("responses")).toBe("openai-responses"); + expect(inboundProtocolForWire("chat")).toBe("openai-chat"); + expect(inboundProtocolForWire("anthropic")).toBe("anthropic-messages"); + }); }); From f565fb53322ef70ab62f15938ab6ed517c538b37 Mon Sep 17 00:00:00 2001 From: Wibias <37517432+Wibias@users.noreply.github.com> Date: Wed, 12 Aug 2026 03:24:54 +0200 Subject: [PATCH 08/32] feat(lab): resolve exact production wire route subjects --- src/routing/compatibility/subject.ts | 80 ++++++++++++++++++++++------ 1 file changed, 63 insertions(+), 17 deletions(-) diff --git a/src/routing/compatibility/subject.ts b/src/routing/compatibility/subject.ts index 6f508531c..be4b8517f 100644 --- a/src/routing/compatibility/subject.ts +++ b/src/routing/compatibility/subject.ts @@ -1,4 +1,5 @@ import type { OcxConfig, OcxProviderConfig } from "../../types"; +import type { InboundWire } from "../../providers/registry"; import { subjectIdForSubject } from "../../lab/digest"; import { buildRouteSubjectV1 } from "../../lab/subject/route-subject"; import { buildProtocolSubjectV1 } from "../../lab/subject/protocol-subject"; @@ -15,7 +16,7 @@ import { import { readOpenCodexCompatibilityVersion } from "./version"; import type { RoutingCompatibilityEvidenceLayer } from "./types"; -const POLICY_INBOUND_PROTOCOL = "openai-responses"; +const POLICY_INBOUND_WIRE: InboundWire = "responses"; export interface ResolvedPolicyRouteSubject { subjectId: string; @@ -28,6 +29,15 @@ export interface ResolvedPolicyCompatibilitySubjects { route?: ResolvedPolicyRouteSubject; } +/** Canonical Lab protocol identity for a production request wire. */ +export function inboundProtocolForWire(inboundWire: InboundWire): string { + switch (inboundWire) { + case "responses": return "openai-responses"; + case "chat": return "openai-chat"; + case "anthropic": return "anthropic-messages"; + } +} + function destinationSnapshotFromBaseUrl(baseUrl: string, fingerprint: string): LabDestinationV1 { const parts = parseEndpointFingerprintParts(baseUrl); if (!parts) throw new Error("invalid provider baseUrl for route subject"); @@ -43,36 +53,29 @@ function destinationSnapshotFromBaseUrl(baseUrl: string, fingerprint: string): L }); } -function resolveEffectivePolicyProvider( - providerName: string, - modelId: string, - routed: OcxProviderConfig, -): OcxProviderConfig { - return resolveWireProtocolOverride(providerName, modelId, routed, "responses"); -} - /** - * Resolve the subject identities a policy candidate may legitimately consume. - * Protocol and live-route layers deliberately use different canonical subjects. - * No network, projection rebuild, ledger replay, or Lab-state creation occurs. + * Resolve subject identities for one exact inbound wire without network I/O, + * projection reads/rebuilds, ledger replay, or Lab-state creation. */ -export function resolvePolicyCompatibilitySubjects( +export function resolveCompatibilitySubjectsForInboundWire( config: OcxConfig, providerName: string, modelId: string, routed: OcxProviderConfig, + inboundWire: InboundWire, configDir?: string, ): ResolvedPolicyCompatibilitySubjects { - const effective = resolveEffectivePolicyProvider(providerName, modelId, routed); + const effective = resolveWireProtocolOverride(providerName, modelId, routed, inboundWire); const baseUrl = typeof effective.baseUrl === "string" ? effective.baseUrl.trim() : ""; const adapter = effective.adapter ?? "openai-responses"; + const inboundProtocol = inboundProtocolForWire(inboundWire); const upstreamProtocol = upstreamProtocolForAdapter(adapter); - const surface = surfaceForProtocols(POLICY_INBOUND_PROTOCOL, upstreamProtocol); + const surface = surfaceForProtocols(inboundProtocol, upstreamProtocol); const subjectIds: ResolvedPolicyCompatibilitySubjects["subjectIds"] = {}; try { const protocolSubject = buildProtocolSubjectV1({ - inboundProtocol: POLICY_INBOUND_PROTOCOL, + inboundProtocol, upstreamProtocol, surface, }, adapter); @@ -110,7 +113,7 @@ export function resolvePolicyCompatibilitySubjects( clientModelId: modelId, upstreamModelId: modelId, effectiveAdapter: adapter, - inboundProtocol: POLICY_INBOUND_PROTOCOL, + inboundProtocol, upstreamProtocol, surface, baseUrl, @@ -133,6 +136,27 @@ export function resolvePolicyCompatibilitySubjects( } } +/** + * Resolve the subject identities a policy candidate may legitimately consume. + * CL-06 policy evaluation is a Responses-surface lookup and remains unchanged. + */ +export function resolvePolicyCompatibilitySubjects( + config: OcxConfig, + providerName: string, + modelId: string, + routed: OcxProviderConfig, + configDir?: string, +): ResolvedPolicyCompatibilitySubjects { + return resolveCompatibilitySubjectsForInboundWire( + config, + providerName, + modelId, + routed, + POLICY_INBOUND_WIRE, + configDir, + ); +} + /** Build exact RouteSubjectV1 identity for a policy candidate without network I/O. */ export function resolvePolicyRouteSubject( config: OcxConfig, @@ -143,3 +167,25 @@ export function resolvePolicyRouteSubject( ): ResolvedPolicyRouteSubject | null { return resolvePolicyCompatibilitySubjects(config, providerName, modelId, routed, configDir).route ?? null; } + +/** + * Build the exact production-attempt RouteSubjectV1 identity for its actual + * inbound wire. Returns null when no existing Lab salt/identity can be read. + */ +export function resolveProductionRouteSubject( + config: OcxConfig, + providerName: string, + modelId: string, + routed: OcxProviderConfig, + inboundWire: InboundWire, + configDir?: string, +): ResolvedPolicyRouteSubject | null { + return resolveCompatibilitySubjectsForInboundWire( + config, + providerName, + modelId, + routed, + inboundWire, + configDir, + ).route ?? null; +} From 0234011a4fb4e077672831e2b438c03c83caddf2 Mon Sep 17 00:00:00 2001 From: Wibias <37517432+Wibias@users.noreply.github.com> Date: Wed, 12 Aug 2026 03:27:10 +0200 Subject: [PATCH 09/32] chore(lab): stage one-shot CL-09 core patch --- .github/workflows/cl09-core-patch.yml | 48 +++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 .github/workflows/cl09-core-patch.yml diff --git a/.github/workflows/cl09-core-patch.yml b/.github/workflows/cl09-core-patch.yml new file mode 100644 index 000000000..e29a0cdde --- /dev/null +++ b/.github/workflows/cl09-core-patch.yml @@ -0,0 +1,48 @@ +name: CL09 core patch + +on: + push: + branches: + - feat/cl-09-passive-production-evidence + paths: + - .github/workflows/cl09-core-patch.yml + +permissions: + contents: write + +jobs: + patch: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + ref: feat/cl-09-passive-production-evidence + - name: Apply exact CL-09 production-attempt linkage + run: | + python3 - <<'PY' + from pathlib import Path + p = Path('src/server/responses/core.ts') + s = p.read_text() + import_anchor = 'import { evidenceFromBody } from "../../routing/request-evidence";\n' + import_line = 'import { resolveProductionRouteSubject } from "../../routing/compatibility/subject";\n' + if import_line not in s: + if import_anchor not in s: + raise SystemExit('import anchor missing') + s = s.replace(import_anchor, import_anchor + import_line, 1) + seal = ' sealRequestAttemptIdentity(logCtx.activeAttempt, logCtx.provider, adapter.name);\n' + block = ''' sealRequestAttemptIdentity(logCtx.activeAttempt, logCtx.provider, adapter.name);\n // CL-09: attach only the opaque exact route-subject identity to the attempt.\n // This is best-effort passive metadata: no Lab state is created and failure\n // must never alter, retry, or delay the upstream request.\n if (logCtx.activeAttempt && !logCtx.activeAttempt.labRouteSubjectId) {\n try {\n const passiveSubject = resolveProductionRouteSubject(\n config,\n route.providerName,\n route.modelId,\n route.provider,\n inboundWire,\n );\n if (passiveSubject) logCtx.activeAttempt.labRouteSubjectId = passiveSubject.subjectId;\n } catch {\n // Omit passive linkage when exact subject construction is unavailable.\n }\n }\n''' + if block not in s: + count = s.count(seal) + if count != 1: + raise SystemExit(f'expected one initial attempt seal anchor, got {count}') + s = s.replace(seal, block, 1) + p.write_text(s) + PY + - name: Remove one-shot workflow and commit + run: | + git rm .github/workflows/cl09-core-patch.yml + git config user.name github-actions[bot] + git config user.email 41898282+github-actions[bot]@users.noreply.github.com + git add src/server/responses/core.ts + git commit -m "feat(lab): link exact production attempts to Lab subjects" + git push origin HEAD:feat/cl-09-passive-production-evidence From 9bb1af6592ffdf1425a3d6b676564b65a2158a63 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 12 Aug 2026 01:27:21 +0000 Subject: [PATCH 10/32] feat(lab): link exact production attempts to Lab subjects --- .github/workflows/cl09-core-patch.yml | 48 --------------------------- src/server/responses/core.ts | 18 ++++++++++ 2 files changed, 18 insertions(+), 48 deletions(-) delete mode 100644 .github/workflows/cl09-core-patch.yml diff --git a/.github/workflows/cl09-core-patch.yml b/.github/workflows/cl09-core-patch.yml deleted file mode 100644 index e29a0cdde..000000000 --- a/.github/workflows/cl09-core-patch.yml +++ /dev/null @@ -1,48 +0,0 @@ -name: CL09 core patch - -on: - push: - branches: - - feat/cl-09-passive-production-evidence - paths: - - .github/workflows/cl09-core-patch.yml - -permissions: - contents: write - -jobs: - patch: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - ref: feat/cl-09-passive-production-evidence - - name: Apply exact CL-09 production-attempt linkage - run: | - python3 - <<'PY' - from pathlib import Path - p = Path('src/server/responses/core.ts') - s = p.read_text() - import_anchor = 'import { evidenceFromBody } from "../../routing/request-evidence";\n' - import_line = 'import { resolveProductionRouteSubject } from "../../routing/compatibility/subject";\n' - if import_line not in s: - if import_anchor not in s: - raise SystemExit('import anchor missing') - s = s.replace(import_anchor, import_anchor + import_line, 1) - seal = ' sealRequestAttemptIdentity(logCtx.activeAttempt, logCtx.provider, adapter.name);\n' - block = ''' sealRequestAttemptIdentity(logCtx.activeAttempt, logCtx.provider, adapter.name);\n // CL-09: attach only the opaque exact route-subject identity to the attempt.\n // This is best-effort passive metadata: no Lab state is created and failure\n // must never alter, retry, or delay the upstream request.\n if (logCtx.activeAttempt && !logCtx.activeAttempt.labRouteSubjectId) {\n try {\n const passiveSubject = resolveProductionRouteSubject(\n config,\n route.providerName,\n route.modelId,\n route.provider,\n inboundWire,\n );\n if (passiveSubject) logCtx.activeAttempt.labRouteSubjectId = passiveSubject.subjectId;\n } catch {\n // Omit passive linkage when exact subject construction is unavailable.\n }\n }\n''' - if block not in s: - count = s.count(seal) - if count != 1: - raise SystemExit(f'expected one initial attempt seal anchor, got {count}') - s = s.replace(seal, block, 1) - p.write_text(s) - PY - - name: Remove one-shot workflow and commit - run: | - git rm .github/workflows/cl09-core-patch.yml - git config user.name github-actions[bot] - git config user.email 41898282+github-actions[bot]@users.noreply.github.com - git add src/server/responses/core.ts - git commit -m "feat(lab): link exact production attempts to Lab subjects" - git push origin HEAD:feat/cl-09-passive-production-evidence diff --git a/src/server/responses/core.ts b/src/server/responses/core.ts index 5c6c96180..5de97affd 100644 --- a/src/server/responses/core.ts +++ b/src/server/responses/core.ts @@ -24,6 +24,7 @@ import { type RouteResult, } from "../../router"; import { evidenceFromBody } from "../../routing/request-evidence"; +import { resolveProductionRouteSubject } from "../../routing/compatibility/subject"; import { advanceComboAfterFailure, comboDefaultEffort, @@ -1779,6 +1780,23 @@ async function handleResponsesInner( (logCtx.attempts ??= []).push(attempt); } sealRequestAttemptIdentity(logCtx.activeAttempt, logCtx.provider, adapter.name); + // CL-09: attach only the opaque exact route-subject identity to the attempt. + // This is best-effort passive metadata: no Lab state is created and failure + // must never alter, retry, or delay the upstream request. + if (logCtx.activeAttempt && !logCtx.activeAttempt.labRouteSubjectId) { + try { + const passiveSubject = resolveProductionRouteSubject( + config, + route.providerName, + route.modelId, + route.provider, + inboundWire, + ); + if (passiveSubject) logCtx.activeAttempt.labRouteSubjectId = passiveSubject.subjectId; + } catch { + // Omit passive linkage when exact subject construction is unavailable. + } + } const isPassthrough = "passthrough" in adapter && !!adapter.passthrough; if (adapter.name === "kiro" && parsed.previousResponseId && !parsed._previousResponseInputExpanded) { From 5083ec473880f42219998afa9900c12597da51f4 Mon Sep 17 00:00:00 2001 From: Wibias <37517432+Wibias@users.noreply.github.com> Date: Wed, 12 Aug 2026 03:27:59 +0200 Subject: [PATCH 11/32] feat(lab): add bounded passive production query layer --- src/lab/query/passive-production.ts | 154 ++++++++++++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 src/lab/query/passive-production.ts diff --git a/src/lab/query/passive-production.ts b/src/lab/query/passive-production.ts new file mode 100644 index 000000000..984316671 --- /dev/null +++ b/src/lab/query/passive-production.ts @@ -0,0 +1,154 @@ +import { + isLabRouteSubjectId, + readRecentUsageEntries, + type PersistedUsageAttempt, + type PersistedUsageEntry, +} from "../../usage/log"; + +export const PASSIVE_PRODUCTION_DEFAULT_LIMIT = 50; +export const PASSIVE_PRODUCTION_MAX_LIMIT = 200; +export const PASSIVE_PRODUCTION_MAX_SCAN_ROWS = 2_000; + +export type PassiveProductionOutcome = + | "success" + | "client_cancel" + | "route_error" + | "environmental" + | "unknown"; + +export interface PassiveRouteSignalV1 { + schemaVersion: 1; + subjectId: string; + source: "production_usage_v1"; + requestRef: string; + decisionRef?: string; + attemptOrdinal: number; + observedAt: number; + outcome: PassiveProductionOutcome; + httpStatus?: number; + terminalStatus?: string; + closeReason?: string; + errorCode?: string; +} + +export interface PassiveProductionSummaryV1 { + schemaVersion: 1; + subjectId: string; + verificationStatus: "not_verification"; + recentProductionAttempts: number; + recentSuccessfulAttempts: number; + recentRouteErrorSignals: number; + lastObservedProductionAttempt?: number; +} + +export interface PassiveProductionQueryResultV1 { + schemaVersion: 1; + verificationStatus: "not_verification"; + summary: PassiveProductionSummaryV1; + signals: PassiveRouteSignalV1[]; + scannedRows: number; + truncated: boolean; +} + +const ROUTE_ERROR_CODES = new Set([ + "upstream_error", + "upstream_transport_error", + "upstream_connect_error", + "upstream_timeout", +]); +const ENVIRONMENTAL_ERROR_CODES = new Set([ + "authentication_error", + "rate_limit_error", + "admission_rejected", + "upstream_host_circuit_open", +]); + +function boundedLimit(value: number | undefined): number { + if (value === undefined) return PASSIVE_PRODUCTION_DEFAULT_LIMIT; + if (!Number.isSafeInteger(value) || value < 1) throw new RangeError("passive production limit must be a positive integer"); + return Math.min(value, PASSIVE_PRODUCTION_MAX_LIMIT); +} + +function isFinalAttempt(entry: PersistedUsageEntry, attempt: PersistedUsageAttempt): boolean { + const attempts = entry.attempts ?? []; + return attempts.length > 0 && attempts[attempts.length - 1]?.ordinal === attempt.ordinal; +} + +function classifyOutcome(entry: PersistedUsageEntry, attempt: PersistedUsageAttempt): PassiveProductionOutcome { + if (attempt.status >= 200 && attempt.status < 300) return "success"; + if (isFinalAttempt(entry, attempt) && entry.closeReason === "client_cancel") return "client_cancel"; + if (attempt.errorCode && ENVIRONMENTAL_ERROR_CODES.has(attempt.errorCode)) return "environmental"; + if (attempt.errorCode && ROUTE_ERROR_CODES.has(attempt.errorCode)) return "route_error"; + return "unknown"; +} + +function signalFor(entry: PersistedUsageEntry, attempt: PersistedUsageAttempt): PassiveRouteSignalV1 | null { + const subjectId = attempt.labRouteSubjectId; + if (!isLabRouteSubjectId(subjectId)) return null; + const finalAttempt = isFinalAttempt(entry, attempt); + return { + schemaVersion: 1, + subjectId, + source: "production_usage_v1", + requestRef: entry.requestId, + ...(entry.routeDecision?.decisionId ? { decisionRef: entry.routeDecision.decisionId } : {}), + attemptOrdinal: attempt.ordinal, + observedAt: entry.timestamp, + outcome: classifyOutcome(entry, attempt), + httpStatus: attempt.status, + ...(finalAttempt && entry.terminalStatus ? { terminalStatus: entry.terminalStatus } : {}), + ...(finalAttempt && entry.closeReason ? { closeReason: entry.closeReason } : {}), + ...(attempt.errorCode ? { errorCode: attempt.errorCode } : {}), + }; +} + +export function derivePassiveProductionSignals( + entries: readonly PersistedUsageEntry[], + subjectId: string, + limit?: number, +): PassiveProductionQueryResultV1 { + if (!isLabRouteSubjectId(subjectId)) throw new Error("invalid passive production subject id"); + const maxResults = boundedLimit(limit); + const signals: PassiveRouteSignalV1[] = []; + const scanRows = entries.slice(-PASSIVE_PRODUCTION_MAX_SCAN_ROWS); + + for (let rowIndex = scanRows.length - 1; rowIndex >= 0 && signals.length < maxResults; rowIndex--) { + const entry = scanRows[rowIndex]!; + const attempts = entry.attempts ?? []; + for (let attemptIndex = attempts.length - 1; attemptIndex >= 0 && signals.length < maxResults; attemptIndex--) { + const signal = signalFor(entry, attempts[attemptIndex]!); + if (signal?.subjectId === subjectId) signals.push(signal); + } + } + + const recentSuccessfulAttempts = signals.filter(signal => signal.outcome === "success").length; + const recentRouteErrorSignals = signals.filter(signal => signal.outcome === "route_error").length; + return { + schemaVersion: 1, + verificationStatus: "not_verification", + summary: { + schemaVersion: 1, + subjectId, + verificationStatus: "not_verification", + recentProductionAttempts: signals.length, + recentSuccessfulAttempts, + recentRouteErrorSignals, + ...(signals[0] ? { lastObservedProductionAttempt: signals[0].observedAt } : {}), + }, + signals, + scannedRows: scanRows.length, + truncated: entries.length > PASSIVE_PRODUCTION_MAX_SCAN_ROWS || signals.length === maxResults, + }; +} + +/** Read-only bounded CL-09 projection over the existing usage-log authority. */ +export function queryPassiveProductionSignals( + subjectId: string, + limit?: number, +): PassiveProductionQueryResultV1 { + return derivePassiveProductionSignals( + readRecentUsageEntries(PASSIVE_PRODUCTION_MAX_SCAN_ROWS), + subjectId, + limit, + ); +} From 2f6ca6695d2214a6350e65bc075cb7656928937f Mon Sep 17 00:00:00 2001 From: Wibias <37517432+Wibias@users.noreply.github.com> Date: Wed, 12 Aug 2026 03:28:10 +0200 Subject: [PATCH 12/32] feat(lab): export passive production queries --- src/lab/query/index.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/lab/query/index.ts b/src/lab/query/index.ts index 6582bacb4..2df30c886 100644 --- a/src/lab/query/index.ts +++ b/src/lab/query/index.ts @@ -31,4 +31,15 @@ export { queryLabArtifactByDigest, queryLabCatalogEntries, } from "./queries"; +export { + PASSIVE_PRODUCTION_DEFAULT_LIMIT, + PASSIVE_PRODUCTION_MAX_LIMIT, + PASSIVE_PRODUCTION_MAX_SCAN_ROWS, + derivePassiveProductionSignals, + queryPassiveProductionSignals, + type PassiveProductionOutcome, + type PassiveProductionQueryResultV1, + type PassiveProductionSummaryV1, + type PassiveRouteSignalV1, +} from "./passive-production"; export { sanitizePublicText } from "./dto-map"; From 4ad2aba26898363729263948fc11b62f26775943 Mon Sep 17 00:00:00 2001 From: Wibias <37517432+Wibias@users.noreply.github.com> Date: Wed, 12 Aug 2026 03:28:38 +0200 Subject: [PATCH 13/32] test(lab): cover passive production isolation and bounds --- tests/lab-passive-production-evidence.test.ts | 95 ++++++++++++++++--- 1 file changed, 81 insertions(+), 14 deletions(-) diff --git a/tests/lab-passive-production-evidence.test.ts b/tests/lab-passive-production-evidence.test.ts index 8c1dfb18e..28f114064 100644 --- a/tests/lab-passive-production-evidence.test.ts +++ b/tests/lab-passive-production-evidence.test.ts @@ -4,6 +4,11 @@ import { type PersistedUsageEntry, } from "../src/usage/log"; import { inboundProtocolForWire } from "../src/routing/compatibility/subject"; +import { + PASSIVE_PRODUCTION_MAX_LIMIT, + PASSIVE_PRODUCTION_MAX_SCAN_ROWS, + derivePassiveProductionSignals, +} from "../src/lab/query/passive-production"; function usageEntryWithAttempt(attempt: Record): PersistedUsageEntry { return { @@ -32,29 +37,18 @@ function usageEntryWithAttempt(attempt: Record): PersistedUsage describe("CL-09 passive production attempt linkage", () => { test("preserves an exact local Lab route subject id on a persisted attempt", () => { const subjectId = "a".repeat(64); - - const normalized = normalizeUsageEntryForTest(usageEntryWithAttempt({ - labRouteSubjectId: subjectId, - })); - - expect(normalized.attempts?.[0]).toMatchObject({ - ordinal: 1, - labRouteSubjectId: subjectId, - }); + const normalized = normalizeUsageEntryForTest(usageEntryWithAttempt({ labRouteSubjectId: subjectId })); + expect(normalized.attempts?.[0]).toMatchObject({ ordinal: 1, labRouteSubjectId: subjectId }); }); test("omits malformed route subject linkage without dropping the attempt", () => { - const normalized = normalizeUsageEntryForTest(usageEntryWithAttempt({ - labRouteSubjectId: "not-a-subject-id", - })); - + const normalized = normalizeUsageEntryForTest(usageEntryWithAttempt({ labRouteSubjectId: "not-a-subject-id" })); expect(normalized.attempts?.[0]?.ordinal).toBe(1); expect(normalized.attempts?.[0]).not.toHaveProperty("labRouteSubjectId"); }); test("keeps legacy attempts without CL-09 linkage unchanged", () => { const normalized = normalizeUsageEntryForTest(usageEntryWithAttempt({})); - expect(normalized.attempts).toEqual([{ ordinal: 1, provider: "provider-a", @@ -74,3 +68,76 @@ describe("CL-09 passive production attempt linkage", () => { expect(inboundProtocolForWire("anthropic")).toBe("anthropic-messages"); }); }); + +describe("CL-09 bounded passive production projection", () => { + test("projects only the strict passive allowlist and labels it not verification", () => { + const subjectId = "b".repeat(64); + const secret = "CL09-PROMPT-SECRET-CANARY"; + const entry = usageEntryWithAttempt({ labRouteSubjectId: subjectId }); + entry.timestamp = 1234; + entry.apiKeyId = `account-${secret}`; + entry.conversationId = `conversation-${secret}`; + entry.upstreamError = `raw-error-${secret}`; + entry.requestedEffort = secret; + + const result = derivePassiveProductionSignals([entry], subjectId, 10); + + expect(result.verificationStatus).toBe("not_verification"); + expect(result.summary.verificationStatus).toBe("not_verification"); + expect(result.summary.recentProductionAttempts).toBe(1); + expect(result.summary.recentSuccessfulAttempts).toBe(1); + expect(result.signals[0]).toEqual({ + schemaVersion: 1, + subjectId, + source: "production_usage_v1", + requestRef: "ocx-cl09-passive", + attemptOrdinal: 1, + observedAt: 1234, + outcome: "success", + httpStatus: 200, + }); + expect(JSON.stringify(result)).not.toContain(secret); + }); + + test("does not treat generic HTTP failure as a compatibility-style route error", () => { + const subjectId = "c".repeat(64); + const entry = usageEntryWithAttempt({ labRouteSubjectId: subjectId, status: 500 }); + entry.status = 500; + + const result = derivePassiveProductionSignals([entry], subjectId); + + expect(result.signals[0]?.outcome).toBe("unknown"); + expect(result.summary.recentRouteErrorSignals).toBe(0); + }); + + test("bounds result count and scanned source rows", () => { + const subjectId = "d".repeat(64); + const entries = Array.from({ length: PASSIVE_PRODUCTION_MAX_SCAN_ROWS + 25 }, (_, index) => { + const entry = usageEntryWithAttempt({ labRouteSubjectId: subjectId }); + entry.requestId = `request-${index}`; + entry.timestamp = index; + return entry; + }); + + const result = derivePassiveProductionSignals(entries, subjectId, PASSIVE_PRODUCTION_MAX_LIMIT + 100); + + expect(result.signals).toHaveLength(PASSIVE_PRODUCTION_MAX_LIMIT); + expect(result.scannedRows).toBe(PASSIVE_PRODUCTION_MAX_SCAN_ROWS); + expect(result.truncated).toBe(true); + expect(result.signals[0]?.observedAt).toBe(entries.length - 1); + }); + + test("keeps signals isolated by exact subject id", () => { + const subjectA = "e".repeat(64); + const subjectB = "f".repeat(64); + const first = usageEntryWithAttempt({ labRouteSubjectId: subjectA }); + const second = usageEntryWithAttempt({ labRouteSubjectId: subjectB }); + second.requestId = "other-request"; + + const result = derivePassiveProductionSignals([first, second], subjectA); + + expect(result.signals).toHaveLength(1); + expect(result.signals[0]?.subjectId).toBe(subjectA); + expect(result.signals[0]?.requestRef).toBe("ocx-cl09-passive"); + }); +}); From 04160b90b0b79a3e048a08eeb742fb6a2da3ea25 Mon Sep 17 00:00:00 2001 From: Wibias <37517432+Wibias@users.noreply.github.com> Date: Wed, 12 Aug 2026 03:31:05 +0200 Subject: [PATCH 14/32] chore(lab): stage one-shot CL-09 surfaces patch --- .github/workflows/cl09-surfaces-patch.yml | 86 +++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 .github/workflows/cl09-surfaces-patch.yml diff --git a/.github/workflows/cl09-surfaces-patch.yml b/.github/workflows/cl09-surfaces-patch.yml new file mode 100644 index 000000000..a22d2da03 --- /dev/null +++ b/.github/workflows/cl09-surfaces-patch.yml @@ -0,0 +1,86 @@ +name: CL09 surfaces patch + +on: + push: + branches: + - feat/cl-09-passive-production-evidence + paths: + - .github/workflows/cl09-surfaces-patch.yml + +permissions: + contents: write + +jobs: + patch: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + ref: feat/cl-09-passive-production-evidence + - name: Apply CL-09 read surfaces + run: | + python3 - <<'PY' + from pathlib import Path + + def replace_once(path, old, new): + p = Path(path) + s = p.read_text() + if new in s: + return + if old not in s: + raise SystemExit(f'anchor missing in {path}: {old[:80]!r}') + p.write_text(s.replace(old, new, 1)) + + # Management API + path = 'src/server/management/lab-routes.ts' + replace_once(path, + ' queryLabVerdicts,\n} from "../../lab/query";', + ' queryLabVerdicts,\n queryPassiveProductionSignals,\n} from "../../lab/query";') + replace_once(path, + ''' if (url.pathname === "/api/lab/status") {\n return jsonResponse(queryLabStatus(), 200, req, config);\n }\n\n''', + ''' if (url.pathname === "/api/lab/status") {\n return jsonResponse(queryLabStatus(), 200, req, config);\n }\n\n if (url.pathname === "/api/lab/production-signals") {\n const subjectId = url.searchParams.get("subjectId")?.trim();\n if (!subjectId) return errorResponse("invalid_subject", "subjectId is required", 400, ctx);\n const limit = parseLimit(url.searchParams.get("limit"), ctx);\n if (limit instanceof Response) return limit;\n try {\n return jsonResponse(queryPassiveProductionSignals(subjectId, limit), 200, req, config);\n } catch {\n return errorResponse("invalid_subject", "subjectId must be an exact Lab route subject id", 400, ctx);\n }\n }\n\n''') + + # CLI + path = 'src/cli/lab.ts' + replace_once(path, + ' queryLabVerdicts,\n} from "../lab/query";', + ' queryLabVerdicts,\n queryPassiveProductionSignals,\n type PassiveProductionQueryResultV1,\n} from "../lab/query";') + replace_once(path, + ' ocx lab status [--json]\n', + ' ocx lab status [--json]\n ocx lab production-signals --subject [--limit ] [--json]\n') + replace_once(path, + '''function automationStatusLines(status: ReturnType): string[] {\n''', + '''function passiveProductionLines(result: PassiveProductionQueryResultV1): string[] {\n const summary = result.summary;\n return [\n "Observed production traffic (not Lab verification)",\n `Attempts: ${summary.recentProductionAttempts} | Successes: ${summary.recentSuccessfulAttempts} | Route errors: ${summary.recentRouteErrorSignals}`,\n ...(summary.lastObservedProductionAttempt !== undefined\n ? [`Last observed: ${summary.lastObservedProductionAttempt}`]\n : []),\n ];\n}\n\nfunction automationStatusLines(status: ReturnType): string[] {\n''') + replace_once(path, + ''' case "verdicts": {\n''', + ''' case "production-signals": {\n const subjectId = takeOption(rest, "--subject");\n const limit = takeIntegerOption(rest, "--limit", { min: 1 });\n rejectArgs(rest, USAGE);\n if (!subjectId) throw new CliUsageError("--subject is required", USAGE);\n const result = queryPassiveProductionSignals(subjectId, limit);\n printData(result, wantsJson, passiveProductionLines(result));\n return;\n }\n case "verdicts": {\n''') + + # Compatibility Matrix data client. Keep passive data distinct from verdict DTOs. + path = 'gui/src/pages/compatibility-matrix-api.ts' + replace_once(path, + '''export type LabPageData = {\n''', + '''export type PassiveProductionSummaryDto = {\n verificationStatus: "not_verification";\n summary: {\n subjectId: string;\n verificationStatus: "not_verification";\n recentProductionAttempts: number;\n recentSuccessfulAttempts: number;\n recentRouteErrorSignals: number;\n lastObservedProductionAttempt?: number;\n };\n};\n\nfunction parsePassiveProductionSummary(raw: unknown): PassiveProductionSummaryDto {\n if (!isPlainObject(raw) || raw.verificationStatus !== "not_verification" || !isPlainObject(raw.summary)) {\n throw invalidResponse();\n }\n const summary = raw.summary;\n if (summary.verificationStatus !== "not_verification"\n || typeof summary.subjectId !== "string"\n || typeof summary.recentProductionAttempts !== "number"\n || typeof summary.recentSuccessfulAttempts !== "number"\n || typeof summary.recentRouteErrorSignals !== "number"\n || (summary.lastObservedProductionAttempt !== undefined && typeof summary.lastObservedProductionAttempt !== "number")) {\n throw invalidResponse();\n }\n return { verificationStatus: "not_verification", summary: summary as PassiveProductionSummaryDto["summary"] };\n}\n\nexport async function fetchPassiveProductionSummary(\n apiBase: string,\n subjectId: string,\n signal: AbortSignal,\n): Promise {\n const raw = await fetchLabJson(\n apiBase,\n `/api/lab/production-signals?subjectId=${encodeURIComponent(subjectId)}&limit=50`,\n signal,\n );\n return parsePassiveProductionSummary(raw);\n}\n\nexport type LabPageData = {\n''') + replace_once(path, + '''export type VerdictDetailData = {\n subject: SubjectDetailDto;\n observations: ObservationDto[];\n observationsTruncated: boolean;\n events: LabEventDto[];\n artifacts: ArtifactMetadataDto[];\n};\n''', + '''export type VerdictDetailData = {\n subject: SubjectDetailDto;\n observations: ObservationDto[];\n observationsTruncated: boolean;\n events: LabEventDto[];\n artifacts: ArtifactMetadataDto[];\n production: PassiveProductionSummaryDto | null;\n};\n''') + replace_once(path, + ''' const [subject, observations, events, artifacts] = await Promise.all([\n fetchSubjectDetail(apiBase, verdict.subjectId, signal),\n fetchAllObservations(apiBase, observationFilters, signal),\n mapSettledBounded(eventIds, DETAIL_CONCURRENCY, signal, id => fetchEventById(apiBase, id, signal)),\n mapSettledBounded(digests, DETAIL_CONCURRENCY, signal, digest => fetchArtifactByDigest(apiBase, digest, signal)),\n ]);\n''', + ''' const [subject, observations, events, artifacts, production] = await Promise.all([\n fetchSubjectDetail(apiBase, verdict.subjectId, signal),\n fetchAllObservations(apiBase, observationFilters, signal),\n mapSettledBounded(eventIds, DETAIL_CONCURRENCY, signal, id => fetchEventById(apiBase, id, signal)),\n mapSettledBounded(digests, DETAIL_CONCURRENCY, signal, digest => fetchArtifactByDigest(apiBase, digest, signal)),\n fetchPassiveProductionSummary(apiBase, verdict.subjectId, signal).catch(error => {\n if (signal.aborted) throw error;\n return null;\n }),\n ]);\n''') + replace_once(path, + ''' events,\n artifacts,\n };\n}\n''', + ''' events,\n artifacts,\n production,\n };\n}\n''') + + # Existing detail pane only; no new product area and no combined score/status. + path = 'gui/src/pages/CompatibilityMatrix.tsx' + replace_once(path, + ''' {detail.observations.length > 0 && (\n''', + ''' {detail.production && (\n
\n

Observed production traffic

\n

Not Lab verification

\n
\n
Attempts
{detail.production.summary.recentProductionAttempts}
\n
Successes
{detail.production.summary.recentSuccessfulAttempts}
\n
Route errors
{detail.production.summary.recentRouteErrorSignals}
\n {detail.production.summary.lastObservedProductionAttempt !== undefined && (\n
Last observed
{formatAsOf(detail.production.summary.lastObservedProductionAttempt, locale)}
\n )}\n
\n
\n )}\n {detail.observations.length > 0 && (\n''') + PY + - name: Remove one-shot workflow and commit + run: | + git rm .github/workflows/cl09-surfaces-patch.yml + git config user.name github-actions[bot] + git config user.email 41898282+github-actions[bot]@users.noreply.github.com + git add src/server/management/lab-routes.ts src/cli/lab.ts gui/src/pages/compatibility-matrix-api.ts gui/src/pages/CompatibilityMatrix.tsx + git commit -m "feat(lab): expose passive production signals" + git push origin HEAD:feat/cl-09-passive-production-evidence From d4b2f5fd9da5daad54aefee272364f5e4c17a4ec Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 12 Aug 2026 01:31:14 +0000 Subject: [PATCH 15/32] feat(lab): expose passive production signals --- .github/workflows/cl09-surfaces-patch.yml | 86 ----------------------- gui/src/pages/CompatibilityMatrix.tsx | 14 ++++ gui/src/pages/compatibility-matrix-api.ts | 49 ++++++++++++- src/cli/lab.ts | 23 ++++++ src/server/management/lab-routes.ts | 13 ++++ 5 files changed, 98 insertions(+), 87 deletions(-) delete mode 100644 .github/workflows/cl09-surfaces-patch.yml diff --git a/.github/workflows/cl09-surfaces-patch.yml b/.github/workflows/cl09-surfaces-patch.yml deleted file mode 100644 index a22d2da03..000000000 --- a/.github/workflows/cl09-surfaces-patch.yml +++ /dev/null @@ -1,86 +0,0 @@ -name: CL09 surfaces patch - -on: - push: - branches: - - feat/cl-09-passive-production-evidence - paths: - - .github/workflows/cl09-surfaces-patch.yml - -permissions: - contents: write - -jobs: - patch: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - ref: feat/cl-09-passive-production-evidence - - name: Apply CL-09 read surfaces - run: | - python3 - <<'PY' - from pathlib import Path - - def replace_once(path, old, new): - p = Path(path) - s = p.read_text() - if new in s: - return - if old not in s: - raise SystemExit(f'anchor missing in {path}: {old[:80]!r}') - p.write_text(s.replace(old, new, 1)) - - # Management API - path = 'src/server/management/lab-routes.ts' - replace_once(path, - ' queryLabVerdicts,\n} from "../../lab/query";', - ' queryLabVerdicts,\n queryPassiveProductionSignals,\n} from "../../lab/query";') - replace_once(path, - ''' if (url.pathname === "/api/lab/status") {\n return jsonResponse(queryLabStatus(), 200, req, config);\n }\n\n''', - ''' if (url.pathname === "/api/lab/status") {\n return jsonResponse(queryLabStatus(), 200, req, config);\n }\n\n if (url.pathname === "/api/lab/production-signals") {\n const subjectId = url.searchParams.get("subjectId")?.trim();\n if (!subjectId) return errorResponse("invalid_subject", "subjectId is required", 400, ctx);\n const limit = parseLimit(url.searchParams.get("limit"), ctx);\n if (limit instanceof Response) return limit;\n try {\n return jsonResponse(queryPassiveProductionSignals(subjectId, limit), 200, req, config);\n } catch {\n return errorResponse("invalid_subject", "subjectId must be an exact Lab route subject id", 400, ctx);\n }\n }\n\n''') - - # CLI - path = 'src/cli/lab.ts' - replace_once(path, - ' queryLabVerdicts,\n} from "../lab/query";', - ' queryLabVerdicts,\n queryPassiveProductionSignals,\n type PassiveProductionQueryResultV1,\n} from "../lab/query";') - replace_once(path, - ' ocx lab status [--json]\n', - ' ocx lab status [--json]\n ocx lab production-signals --subject [--limit ] [--json]\n') - replace_once(path, - '''function automationStatusLines(status: ReturnType): string[] {\n''', - '''function passiveProductionLines(result: PassiveProductionQueryResultV1): string[] {\n const summary = result.summary;\n return [\n "Observed production traffic (not Lab verification)",\n `Attempts: ${summary.recentProductionAttempts} | Successes: ${summary.recentSuccessfulAttempts} | Route errors: ${summary.recentRouteErrorSignals}`,\n ...(summary.lastObservedProductionAttempt !== undefined\n ? [`Last observed: ${summary.lastObservedProductionAttempt}`]\n : []),\n ];\n}\n\nfunction automationStatusLines(status: ReturnType): string[] {\n''') - replace_once(path, - ''' case "verdicts": {\n''', - ''' case "production-signals": {\n const subjectId = takeOption(rest, "--subject");\n const limit = takeIntegerOption(rest, "--limit", { min: 1 });\n rejectArgs(rest, USAGE);\n if (!subjectId) throw new CliUsageError("--subject is required", USAGE);\n const result = queryPassiveProductionSignals(subjectId, limit);\n printData(result, wantsJson, passiveProductionLines(result));\n return;\n }\n case "verdicts": {\n''') - - # Compatibility Matrix data client. Keep passive data distinct from verdict DTOs. - path = 'gui/src/pages/compatibility-matrix-api.ts' - replace_once(path, - '''export type LabPageData = {\n''', - '''export type PassiveProductionSummaryDto = {\n verificationStatus: "not_verification";\n summary: {\n subjectId: string;\n verificationStatus: "not_verification";\n recentProductionAttempts: number;\n recentSuccessfulAttempts: number;\n recentRouteErrorSignals: number;\n lastObservedProductionAttempt?: number;\n };\n};\n\nfunction parsePassiveProductionSummary(raw: unknown): PassiveProductionSummaryDto {\n if (!isPlainObject(raw) || raw.verificationStatus !== "not_verification" || !isPlainObject(raw.summary)) {\n throw invalidResponse();\n }\n const summary = raw.summary;\n if (summary.verificationStatus !== "not_verification"\n || typeof summary.subjectId !== "string"\n || typeof summary.recentProductionAttempts !== "number"\n || typeof summary.recentSuccessfulAttempts !== "number"\n || typeof summary.recentRouteErrorSignals !== "number"\n || (summary.lastObservedProductionAttempt !== undefined && typeof summary.lastObservedProductionAttempt !== "number")) {\n throw invalidResponse();\n }\n return { verificationStatus: "not_verification", summary: summary as PassiveProductionSummaryDto["summary"] };\n}\n\nexport async function fetchPassiveProductionSummary(\n apiBase: string,\n subjectId: string,\n signal: AbortSignal,\n): Promise {\n const raw = await fetchLabJson(\n apiBase,\n `/api/lab/production-signals?subjectId=${encodeURIComponent(subjectId)}&limit=50`,\n signal,\n );\n return parsePassiveProductionSummary(raw);\n}\n\nexport type LabPageData = {\n''') - replace_once(path, - '''export type VerdictDetailData = {\n subject: SubjectDetailDto;\n observations: ObservationDto[];\n observationsTruncated: boolean;\n events: LabEventDto[];\n artifacts: ArtifactMetadataDto[];\n};\n''', - '''export type VerdictDetailData = {\n subject: SubjectDetailDto;\n observations: ObservationDto[];\n observationsTruncated: boolean;\n events: LabEventDto[];\n artifacts: ArtifactMetadataDto[];\n production: PassiveProductionSummaryDto | null;\n};\n''') - replace_once(path, - ''' const [subject, observations, events, artifacts] = await Promise.all([\n fetchSubjectDetail(apiBase, verdict.subjectId, signal),\n fetchAllObservations(apiBase, observationFilters, signal),\n mapSettledBounded(eventIds, DETAIL_CONCURRENCY, signal, id => fetchEventById(apiBase, id, signal)),\n mapSettledBounded(digests, DETAIL_CONCURRENCY, signal, digest => fetchArtifactByDigest(apiBase, digest, signal)),\n ]);\n''', - ''' const [subject, observations, events, artifacts, production] = await Promise.all([\n fetchSubjectDetail(apiBase, verdict.subjectId, signal),\n fetchAllObservations(apiBase, observationFilters, signal),\n mapSettledBounded(eventIds, DETAIL_CONCURRENCY, signal, id => fetchEventById(apiBase, id, signal)),\n mapSettledBounded(digests, DETAIL_CONCURRENCY, signal, digest => fetchArtifactByDigest(apiBase, digest, signal)),\n fetchPassiveProductionSummary(apiBase, verdict.subjectId, signal).catch(error => {\n if (signal.aborted) throw error;\n return null;\n }),\n ]);\n''') - replace_once(path, - ''' events,\n artifacts,\n };\n}\n''', - ''' events,\n artifacts,\n production,\n };\n}\n''') - - # Existing detail pane only; no new product area and no combined score/status. - path = 'gui/src/pages/CompatibilityMatrix.tsx' - replace_once(path, - ''' {detail.observations.length > 0 && (\n''', - ''' {detail.production && (\n
\n

Observed production traffic

\n

Not Lab verification

\n
\n
Attempts
{detail.production.summary.recentProductionAttempts}
\n
Successes
{detail.production.summary.recentSuccessfulAttempts}
\n
Route errors
{detail.production.summary.recentRouteErrorSignals}
\n {detail.production.summary.lastObservedProductionAttempt !== undefined && (\n
Last observed
{formatAsOf(detail.production.summary.lastObservedProductionAttempt, locale)}
\n )}\n
\n
\n )}\n {detail.observations.length > 0 && (\n''') - PY - - name: Remove one-shot workflow and commit - run: | - git rm .github/workflows/cl09-surfaces-patch.yml - git config user.name github-actions[bot] - git config user.email 41898282+github-actions[bot]@users.noreply.github.com - git add src/server/management/lab-routes.ts src/cli/lab.ts gui/src/pages/compatibility-matrix-api.ts gui/src/pages/CompatibilityMatrix.tsx - git commit -m "feat(lab): expose passive production signals" - git push origin HEAD:feat/cl-09-passive-production-evidence diff --git a/gui/src/pages/CompatibilityMatrix.tsx b/gui/src/pages/CompatibilityMatrix.tsx index f5efe6ea7..400c2d86f 100644 --- a/gui/src/pages/CompatibilityMatrix.tsx +++ b/gui/src/pages/CompatibilityMatrix.tsx @@ -191,6 +191,20 @@ function DetailPane({

{t("lab.detailSubject")}

{detail.subject.subjectKind}

+ {detail.production && ( +
+

Observed production traffic

+

Not Lab verification

+
+
Attempts
{detail.production.summary.recentProductionAttempts}
+
Successes
{detail.production.summary.recentSuccessfulAttempts}
+
Route errors
{detail.production.summary.recentRouteErrorSignals}
+ {detail.production.summary.lastObservedProductionAttempt !== undefined && ( +
Last observed
{formatAsOf(detail.production.summary.lastObservedProductionAttempt, locale)}
+ )} +
+
+ )} {detail.observations.length > 0 && (

{t("lab.detailObservations")}

diff --git a/gui/src/pages/compatibility-matrix-api.ts b/gui/src/pages/compatibility-matrix-api.ts index 2d7c01aec..1bc14aa74 100644 --- a/gui/src/pages/compatibility-matrix-api.ts +++ b/gui/src/pages/compatibility-matrix-api.ts @@ -207,6 +207,47 @@ export async function fetchArtifactByDigest( return artifact; } +export type PassiveProductionSummaryDto = { + verificationStatus: "not_verification"; + summary: { + subjectId: string; + verificationStatus: "not_verification"; + recentProductionAttempts: number; + recentSuccessfulAttempts: number; + recentRouteErrorSignals: number; + lastObservedProductionAttempt?: number; + }; +}; + +function parsePassiveProductionSummary(raw: unknown): PassiveProductionSummaryDto { + if (!isPlainObject(raw) || raw.verificationStatus !== "not_verification" || !isPlainObject(raw.summary)) { + throw invalidResponse(); + } + const summary = raw.summary; + if (summary.verificationStatus !== "not_verification" + || typeof summary.subjectId !== "string" + || typeof summary.recentProductionAttempts !== "number" + || typeof summary.recentSuccessfulAttempts !== "number" + || typeof summary.recentRouteErrorSignals !== "number" + || (summary.lastObservedProductionAttempt !== undefined && typeof summary.lastObservedProductionAttempt !== "number")) { + throw invalidResponse(); + } + return { verificationStatus: "not_verification", summary: summary as PassiveProductionSummaryDto["summary"] }; +} + +export async function fetchPassiveProductionSummary( + apiBase: string, + subjectId: string, + signal: AbortSignal, +): Promise { + const raw = await fetchLabJson( + apiBase, + `/api/lab/production-signals?subjectId=${encodeURIComponent(subjectId)}&limit=50`, + signal, + ); + return parsePassiveProductionSummary(raw); +} + export type LabPageData = { status: LabStatusDto; verdicts: VerdictDto[]; @@ -254,6 +295,7 @@ export type VerdictDetailData = { observationsTruncated: boolean; events: LabEventDto[]; artifacts: ArtifactMetadataDto[]; + production: PassiveProductionSummaryDto | null; }; async function mapSettledBounded( @@ -295,11 +337,15 @@ export async function fetchVerdictDetail( layer: verdict.evidenceLayer, suiteId: verdict.suiteId, }; - const [subject, observations, events, artifacts] = await Promise.all([ + const [subject, observations, events, artifacts, production] = await Promise.all([ fetchSubjectDetail(apiBase, verdict.subjectId, signal), fetchAllObservations(apiBase, observationFilters, signal), mapSettledBounded(eventIds, DETAIL_CONCURRENCY, signal, id => fetchEventById(apiBase, id, signal)), mapSettledBounded(digests, DETAIL_CONCURRENCY, signal, digest => fetchArtifactByDigest(apiBase, digest, signal)), + fetchPassiveProductionSummary(apiBase, verdict.subjectId, signal).catch(error => { + if (signal.aborted) throw error; + return null; + }), ]); return { subject, @@ -307,5 +353,6 @@ export async function fetchVerdictDetail( observationsTruncated: observations.truncated, events, artifacts, + production, }; } diff --git a/src/cli/lab.ts b/src/cli/lab.ts index fabb81900..fffccb0e2 100644 --- a/src/cli/lab.ts +++ b/src/cli/lab.ts @@ -33,6 +33,8 @@ import { queryLabSubjectById, queryLabSubjects, queryLabVerdicts, + queryPassiveProductionSignals, + type PassiveProductionQueryResultV1, } from "../lab/query"; import { CliUsageError, @@ -64,6 +66,7 @@ import { createProductionLabRouteExecutor } from "../lib/lab-live-route-producti const USAGE = `Usage: ocx lab status [--json] + ocx lab production-signals --subject [--limit ] [--json] ocx lab verdicts [--subject ] [--layer ] [--suite ] [--verdict ] [--from ] [--to ] [--limit ] [--cursor ] [--json] ocx lab subjects [--kind ] [--limit ] [--cursor ] [--json] ocx lab subject [--json] @@ -187,6 +190,17 @@ function catalogLines(scenarios: ReturnType): str return lines.length > 0 ? lines : ["No catalog scenarios"]; } +function passiveProductionLines(result: PassiveProductionQueryResultV1): string[] { + const summary = result.summary; + return [ + "Observed production traffic (not Lab verification)", + `Attempts: ${summary.recentProductionAttempts} | Successes: ${summary.recentSuccessfulAttempts} | Route errors: ${summary.recentRouteErrorSignals}`, + ...(summary.lastObservedProductionAttempt !== undefined + ? [`Last observed: ${summary.lastObservedProductionAttempt}`] + : []), + ]; +} + function automationStatusLines(status: ReturnType): string[] { return [ `Automation enabled: ${status.policy.enabled}`, @@ -224,6 +238,15 @@ export async function handleLabCommand(argv: string[], deps: LabCliDeps = {}): P printData(status, wantsJson, statusSummary(status)); return; } + case "production-signals": { + const subjectId = takeOption(rest, "--subject"); + const limit = takeIntegerOption(rest, "--limit", { min: 1 }); + rejectArgs(rest, USAGE); + if (!subjectId) throw new CliUsageError("--subject is required", USAGE); + const result = queryPassiveProductionSignals(subjectId, limit); + printData(result, wantsJson, passiveProductionLines(result)); + return; + } case "verdicts": { const subjectId = takeOption(rest, "--subject"); const layer = takeEnumOption( diff --git a/src/server/management/lab-routes.ts b/src/server/management/lab-routes.ts index 3453d2785..5c0929e47 100644 --- a/src/server/management/lab-routes.ts +++ b/src/server/management/lab-routes.ts @@ -40,6 +40,7 @@ import { queryLabSubjectById, queryLabSubjects, queryLabVerdicts, + queryPassiveProductionSignals, } from "../../lab/query"; import { jsonResponse } from "../auth-cors"; import type { ManagementContext } from "./context"; @@ -194,6 +195,18 @@ export async function handleLabRoutes(ctx: ManagementContext): Promise Date: Wed, 12 Aug 2026 03:35:59 +0200 Subject: [PATCH 16/32] test(lab): harden CL-09 passive isolation boundaries --- tests/lab-passive-production-evidence.test.ts | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/tests/lab-passive-production-evidence.test.ts b/tests/lab-passive-production-evidence.test.ts index 28f114064..2512b42d5 100644 --- a/tests/lab-passive-production-evidence.test.ts +++ b/tests/lab-passive-production-evidence.test.ts @@ -1,3 +1,4 @@ +import { readFileSync } from "node:fs"; import { describe, expect, test } from "bun:test"; import { normalizeUsageEntryForTest, @@ -79,6 +80,10 @@ describe("CL-09 bounded passive production projection", () => { entry.conversationId = `conversation-${secret}`; entry.upstreamError = `raw-error-${secret}`; entry.requestedEffort = secret; + (entry as unknown as Record).prompt = `prompt-${secret}`; + (entry as unknown as Record).responseText = `response-${secret}`; + (entry.attempts?.[0] as unknown as Record).toolArguments = `tool-${secret}`; + (entry.attempts?.[0] as unknown as Record).credential = `credential-${secret}`; const result = derivePassiveProductionSignals([entry], subjectId, 10); @@ -140,4 +145,42 @@ describe("CL-09 bounded passive production projection", () => { expect(result.signals[0]?.subjectId).toBe(subjectA); expect(result.signals[0]?.requestRef).toBe("ocx-cl09-passive"); }); + + test("passive visibility disappears with its existing usage-history source", () => { + const subjectId = "1".repeat(64); + const entry = usageEntryWithAttempt({ labRouteSubjectId: subjectId }); + expect(derivePassiveProductionSignals([entry], subjectId).signals).toHaveLength(1); + expect(derivePassiveProductionSignals([], subjectId).signals).toHaveLength(0); + }); +}); + +describe("CL-09 no-feedback architecture guards", () => { + test("routing and CL-08 planning do not consume passive production queries", () => { + for (const path of [ + "src/routing/evaluator.ts", + "src/lab/automation/planner.ts", + ]) { + const source = readFileSync(path, "utf8"); + expect(source).not.toContain("queryPassiveProductionSignals"); + expect(source).not.toContain("passive-production"); + expect(source).not.toContain("production-signals"); + } + }); + + test("production request path only links the exact subject and never reads passive history", () => { + const source = readFileSync("src/server/responses/core.ts", "utf8"); + expect(source).toContain("resolveProductionRouteSubject"); + expect(source).not.toContain("queryPassiveProductionSignals"); + expect(source).not.toContain("readRecentUsageEntries"); + }); + + test("passive query remains read-side and cannot create Lab execution or evidence", () => { + const source = readFileSync("src/lab/query/passive-production.ts", "utf8"); + expect(source).toContain("readRecentUsageEntries"); + expect(source).not.toContain("ObservationEvent"); + expect(source).not.toContain("appendLab"); + expect(source).not.toContain("compatibility.jsonl"); + expect(source).not.toContain("fetch("); + expect(source).not.toContain("executeLive"); + }); }); From d688d9ce9f3ac7a3de5955c18e0fcbea53eb7a97 Mon Sep 17 00:00:00 2001 From: Wibias <37517432+Wibias@users.noreply.github.com> Date: Wed, 12 Aug 2026 03:36:48 +0200 Subject: [PATCH 17/32] chore(lab): stage CL-09 review doc fixes --- .github/workflows/cl09-review-doc-fix.yml | 55 +++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 .github/workflows/cl09-review-doc-fix.yml diff --git a/.github/workflows/cl09-review-doc-fix.yml b/.github/workflows/cl09-review-doc-fix.yml new file mode 100644 index 000000000..b2a3f9ddf --- /dev/null +++ b/.github/workflows/cl09-review-doc-fix.yml @@ -0,0 +1,55 @@ +name: CL09 review doc fix + +on: + push: + branches: + - feat/cl-09-passive-production-evidence + paths: + - .github/workflows/cl09-review-doc-fix.yml + +permissions: + contents: write + +jobs: + patch: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + ref: feat/cl-09-passive-production-evidence + - name: Reconcile CL-09 contract review findings + run: | + python3 - <<'PY' + from pathlib import Path + p = Path('devlog/_plan/260807_compatibility_lab/009_cl09_passive_production_evidence.md') + s = p.read_text() + replacements = [ + ('# CL-09 - Passive Production Evidence / Shadow Correlation', '# CL-09 - Passive Production Evidence / Exact-Route Correlation'), + ('0 production request mutation', '0 outbound production request or user-payload mutation'), + (' httpStatus?: number;\n terminalStatus?: string;\n closeReason?: string;\n errorCode?: string;\n', ' httpStatus?: number;\n'), + ] + for old, new in replacements: + if old not in s: + raise SystemExit(f'contract anchor missing: {old!r}') + s = s.replace(old, new, 1) + anchor = 'A failure in passive evidence capture must never fail or delay the production request.\n' + addition = ('A failure in passive evidence capture must never fail or delay the production request.\n\n' + 'The invariant against production-request mutation applies to outbound request bytes and user payloads. ' + 'The metadata-only addition of `labRouteSubjectId` to the existing attempt record is explicitly allowed.\n') + if anchor not in s: + raise SystemExit('mutation clarification anchor missing') + s = s.replace(anchor, addition, 1) + p.write_text(s) + + stack = Path('devlog/_plan/260807_compatibility_lab/001_pr_stack_status.md') + text = stack.read_text().rstrip('\n') + '\n' + stack.write_text(text) + PY + - name: Remove one-shot workflow and commit + run: | + git rm .github/workflows/cl09-review-doc-fix.yml + git config user.name github-actions[bot] + git config user.email 41898282+github-actions[bot]@users.noreply.github.com + git add devlog/_plan/260807_compatibility_lab/009_cl09_passive_production_evidence.md devlog/_plan/260807_compatibility_lab/001_pr_stack_status.md + git commit -m "docs(lab): reconcile CL-09 contract review findings" + git push origin HEAD:feat/cl-09-passive-production-evidence From b12badf7cf2849dc1a15960a0cff126d34bb021e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 12 Aug 2026 01:37:01 +0000 Subject: [PATCH 18/32] docs(lab): reconcile CL-09 contract review findings --- .github/workflows/cl09-review-doc-fix.yml | 55 ------------------- .../001_pr_stack_status.md | 2 +- .../009_cl09_passive_production_evidence.md | 9 ++- 3 files changed, 5 insertions(+), 61 deletions(-) delete mode 100644 .github/workflows/cl09-review-doc-fix.yml diff --git a/.github/workflows/cl09-review-doc-fix.yml b/.github/workflows/cl09-review-doc-fix.yml deleted file mode 100644 index b2a3f9ddf..000000000 --- a/.github/workflows/cl09-review-doc-fix.yml +++ /dev/null @@ -1,55 +0,0 @@ -name: CL09 review doc fix - -on: - push: - branches: - - feat/cl-09-passive-production-evidence - paths: - - .github/workflows/cl09-review-doc-fix.yml - -permissions: - contents: write - -jobs: - patch: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - ref: feat/cl-09-passive-production-evidence - - name: Reconcile CL-09 contract review findings - run: | - python3 - <<'PY' - from pathlib import Path - p = Path('devlog/_plan/260807_compatibility_lab/009_cl09_passive_production_evidence.md') - s = p.read_text() - replacements = [ - ('# CL-09 - Passive Production Evidence / Shadow Correlation', '# CL-09 - Passive Production Evidence / Exact-Route Correlation'), - ('0 production request mutation', '0 outbound production request or user-payload mutation'), - (' httpStatus?: number;\n terminalStatus?: string;\n closeReason?: string;\n errorCode?: string;\n', ' httpStatus?: number;\n'), - ] - for old, new in replacements: - if old not in s: - raise SystemExit(f'contract anchor missing: {old!r}') - s = s.replace(old, new, 1) - anchor = 'A failure in passive evidence capture must never fail or delay the production request.\n' - addition = ('A failure in passive evidence capture must never fail or delay the production request.\n\n' - 'The invariant against production-request mutation applies to outbound request bytes and user payloads. ' - 'The metadata-only addition of `labRouteSubjectId` to the existing attempt record is explicitly allowed.\n') - if anchor not in s: - raise SystemExit('mutation clarification anchor missing') - s = s.replace(anchor, addition, 1) - p.write_text(s) - - stack = Path('devlog/_plan/260807_compatibility_lab/001_pr_stack_status.md') - text = stack.read_text().rstrip('\n') + '\n' - stack.write_text(text) - PY - - name: Remove one-shot workflow and commit - run: | - git rm .github/workflows/cl09-review-doc-fix.yml - git config user.name github-actions[bot] - git config user.email 41898282+github-actions[bot]@users.noreply.github.com - git add devlog/_plan/260807_compatibility_lab/009_cl09_passive_production_evidence.md devlog/_plan/260807_compatibility_lab/001_pr_stack_status.md - git commit -m "docs(lab): reconcile CL-09 contract review findings" - git push origin HEAD:feat/cl-09-passive-production-evidence diff --git a/devlog/_plan/260807_compatibility_lab/001_pr_stack_status.md b/devlog/_plan/260807_compatibility_lab/001_pr_stack_status.md index 99241167b..70b892a40 100644 --- a/devlog/_plan/260807_compatibility_lab/001_pr_stack_status.md +++ b/devlog/_plan/260807_compatibility_lab/001_pr_stack_status.md @@ -257,4 +257,4 @@ Claims cannot produce `PROBED`/`VERIFIED`. - **Starting `upstream/dev` SHA:** `68c71a4e9cdf882d812f09fd94783a28749db629` - **Branch:** `feat/cl-04-lab-read-surfaces` - **Scope:** read-only CLI (`ocx lab`), authenticated `GET /api/lab/*`, shared `src/lab/query/` layer -- **CL-05:** not started \ No newline at end of file +- **CL-05:** not started diff --git a/devlog/_plan/260807_compatibility_lab/009_cl09_passive_production_evidence.md b/devlog/_plan/260807_compatibility_lab/009_cl09_passive_production_evidence.md index 7dc3ce3da..b760b8f29 100644 --- a/devlog/_plan/260807_compatibility_lab/009_cl09_passive_production_evidence.md +++ b/devlog/_plan/260807_compatibility_lab/009_cl09_passive_production_evidence.md @@ -1,4 +1,4 @@ -# CL-09 - Passive Production Evidence / Shadow Correlation +# CL-09 - Passive Production Evidence / Exact-Route Correlation ## Programme position @@ -100,7 +100,7 @@ CL-09 V1 must guarantee: ```text 0 extra provider requests 0 duplicated user requests -0 production request mutation +0 outbound production request or user-payload mutation 0 new routing candidates 0 Routing Profile changes 0 Router Intelligence score changes @@ -115,6 +115,8 @@ CL-09 V1 must guarantee: A failure in passive evidence capture must never fail or delay the production request. +The invariant against production-request mutation applies to outbound request bytes and user payloads. The metadata-only addition of `labRouteSubjectId` to the existing attempt record is explicitly allowed. + --- # 5. Existing authorities remain unchanged @@ -182,9 +184,6 @@ interface PassiveRouteSignalV1 { observedAt: number; outcome: "success" | "client_cancel" | "route_error" | "environmental" | "unknown"; httpStatus?: number; - terminalStatus?: string; - closeReason?: string; - errorCode?: string; } ``` From 9ad657df0912bfc5486f22d42e27b426d0f954cb Mon Sep 17 00:00:00 2001 From: Wibias <37517432+Wibias@users.noreply.github.com> Date: Wed, 12 Aug 2026 03:37:30 +0200 Subject: [PATCH 19/32] fix(lab): constrain passive production diagnostics --- src/lab/query/passive-production.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/lab/query/passive-production.ts b/src/lab/query/passive-production.ts index 984316671..071e488cc 100644 --- a/src/lab/query/passive-production.ts +++ b/src/lab/query/passive-production.ts @@ -26,9 +26,6 @@ export interface PassiveRouteSignalV1 { observedAt: number; outcome: PassiveProductionOutcome; httpStatus?: number; - terminalStatus?: string; - closeReason?: string; - errorCode?: string; } export interface PassiveProductionSummaryV1 { @@ -85,7 +82,6 @@ function classifyOutcome(entry: PersistedUsageEntry, attempt: PersistedUsageAtte function signalFor(entry: PersistedUsageEntry, attempt: PersistedUsageAttempt): PassiveRouteSignalV1 | null { const subjectId = attempt.labRouteSubjectId; if (!isLabRouteSubjectId(subjectId)) return null; - const finalAttempt = isFinalAttempt(entry, attempt); return { schemaVersion: 1, subjectId, @@ -96,9 +92,6 @@ function signalFor(entry: PersistedUsageEntry, attempt: PersistedUsageAttempt): observedAt: entry.timestamp, outcome: classifyOutcome(entry, attempt), httpStatus: attempt.status, - ...(finalAttempt && entry.terminalStatus ? { terminalStatus: entry.terminalStatus } : {}), - ...(finalAttempt && entry.closeReason ? { closeReason: entry.closeReason } : {}), - ...(attempt.errorCode ? { errorCode: attempt.errorCode } : {}), }; } From 6a517ed04b51fbd4f44b07afd966772b63f15378 Mon Sep 17 00:00:00 2001 From: Wibias <37517432+Wibias@users.noreply.github.com> Date: Wed, 12 Aug 2026 03:39:04 +0200 Subject: [PATCH 20/32] test(lab): prove fallback attribution and diagnostic redaction --- tests/lab-passive-production-evidence.test.ts | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/tests/lab-passive-production-evidence.test.ts b/tests/lab-passive-production-evidence.test.ts index 2512b42d5..a99bcb908 100644 --- a/tests/lab-passive-production-evidence.test.ts +++ b/tests/lab-passive-production-evidence.test.ts @@ -74,12 +74,13 @@ describe("CL-09 bounded passive production projection", () => { test("projects only the strict passive allowlist and labels it not verification", () => { const subjectId = "b".repeat(64); const secret = "CL09-PROMPT-SECRET-CANARY"; - const entry = usageEntryWithAttempt({ labRouteSubjectId: subjectId }); + const entry = usageEntryWithAttempt({ labRouteSubjectId: subjectId, errorCode: `opaque-${secret}` }); entry.timestamp = 1234; entry.apiKeyId = `account-${secret}`; entry.conversationId = `conversation-${secret}`; entry.upstreamError = `raw-error-${secret}`; entry.requestedEffort = secret; + entry.terminalStatus = `terminal-${secret}`; (entry as unknown as Record).prompt = `prompt-${secret}`; (entry as unknown as Record).responseText = `response-${secret}`; (entry.attempts?.[0] as unknown as Record).toolArguments = `tool-${secret}`; @@ -146,6 +147,33 @@ describe("CL-09 bounded passive production projection", () => { expect(result.signals[0]?.requestRef).toBe("ocx-cl09-passive"); }); + test("keeps fallback attempts attributable to the exact route that executed them", () => { + const subjectA = "2".repeat(64); + const subjectB = "3".repeat(64); + const entry = usageEntryWithAttempt({ labRouteSubjectId: subjectA, status: 503 }); + entry.status = 200; + entry.attempts!.push({ + ordinal: 2, + provider: "provider-b", + model: "model-b", + adapter: "openai-responses", + status: 200, + durationMs: 3, + sendCount: 1, + recoveryKinds: [], + usageStatus: "unreported", + labRouteSubjectId: subjectB, + }); + + const firstRoute = derivePassiveProductionSignals([entry], subjectA); + const fallbackRoute = derivePassiveProductionSignals([entry], subjectB); + + expect(firstRoute.signals).toHaveLength(1); + expect(firstRoute.signals[0]).toMatchObject({ attemptOrdinal: 1, subjectId: subjectA, httpStatus: 503 }); + expect(fallbackRoute.signals).toHaveLength(1); + expect(fallbackRoute.signals[0]).toMatchObject({ attemptOrdinal: 2, subjectId: subjectB, httpStatus: 200 }); + }); + test("passive visibility disappears with its existing usage-history source", () => { const subjectId = "1".repeat(64); const entry = usageEntryWithAttempt({ labRouteSubjectId: subjectId }); From 0a7d151fdc6bb594ebdf3ef1e6ae8c7ff55528e1 Mon Sep 17 00:00:00 2001 From: Wibias <37517432+Wibias@users.noreply.github.com> Date: Wed, 12 Aug 2026 03:39:50 +0200 Subject: [PATCH 21/32] chore(lab): stage CL-09 implementation status update --- .github/workflows/cl09-status-doc-fix.yml | 45 +++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 .github/workflows/cl09-status-doc-fix.yml diff --git a/.github/workflows/cl09-status-doc-fix.yml b/.github/workflows/cl09-status-doc-fix.yml new file mode 100644 index 000000000..2509a4f52 --- /dev/null +++ b/.github/workflows/cl09-status-doc-fix.yml @@ -0,0 +1,45 @@ +name: CL09 status doc fix + +on: + push: + branches: + - feat/cl-09-passive-production-evidence + paths: + - .github/workflows/cl09-status-doc-fix.yml + +permissions: + contents: write + +jobs: + patch: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + ref: feat/cl-09-passive-production-evidence + - name: Update CL-09 implementation status wording + run: | + python3 - <<'PY' + from pathlib import Path + p = Path('devlog/_plan/260807_compatibility_lab/009_cl09_passive_production_evidence.md') + s = p.read_text() + s = s.replace( + 'This initial CL-09 PR is contract-only. Implementation is not authorized until the contract is reviewed.', + 'The CL-09 contract was frozen first on this branch. Runtime implementation now proceeds under this contract and remains subject to independent final review before merge.', + 1, + ) + s = s.replace( + '## CL-09.0 - Audit and contract\n\nThis PR:', + '## CL-09.0 - Audit and contract\n\nCompleted first on this PR:', + 1, + ) + p.write_text(s) + PY + - name: Remove one-shot workflow and commit + run: | + git rm .github/workflows/cl09-status-doc-fix.yml + git config user.name github-actions[bot] + git config user.email 41898282+github-actions[bot]@users.noreply.github.com + git add devlog/_plan/260807_compatibility_lab/009_cl09_passive_production_evidence.md + git commit -m "docs(lab): reflect CL-09 implementation phase" + git push origin HEAD:feat/cl-09-passive-production-evidence From d717c630a010d2b473f1b8eee3cdab29e232fdfa Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 12 Aug 2026 01:40:06 +0000 Subject: [PATCH 22/32] docs(lab): reflect CL-09 implementation phase --- .github/workflows/cl09-status-doc-fix.yml | 45 ------------------- .../009_cl09_passive_production_evidence.md | 4 +- 2 files changed, 2 insertions(+), 47 deletions(-) delete mode 100644 .github/workflows/cl09-status-doc-fix.yml diff --git a/.github/workflows/cl09-status-doc-fix.yml b/.github/workflows/cl09-status-doc-fix.yml deleted file mode 100644 index 2509a4f52..000000000 --- a/.github/workflows/cl09-status-doc-fix.yml +++ /dev/null @@ -1,45 +0,0 @@ -name: CL09 status doc fix - -on: - push: - branches: - - feat/cl-09-passive-production-evidence - paths: - - .github/workflows/cl09-status-doc-fix.yml - -permissions: - contents: write - -jobs: - patch: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - ref: feat/cl-09-passive-production-evidence - - name: Update CL-09 implementation status wording - run: | - python3 - <<'PY' - from pathlib import Path - p = Path('devlog/_plan/260807_compatibility_lab/009_cl09_passive_production_evidence.md') - s = p.read_text() - s = s.replace( - 'This initial CL-09 PR is contract-only. Implementation is not authorized until the contract is reviewed.', - 'The CL-09 contract was frozen first on this branch. Runtime implementation now proceeds under this contract and remains subject to independent final review before merge.', - 1, - ) - s = s.replace( - '## CL-09.0 - Audit and contract\n\nThis PR:', - '## CL-09.0 - Audit and contract\n\nCompleted first on this PR:', - 1, - ) - p.write_text(s) - PY - - name: Remove one-shot workflow and commit - run: | - git rm .github/workflows/cl09-status-doc-fix.yml - git config user.name github-actions[bot] - git config user.email 41898282+github-actions[bot]@users.noreply.github.com - git add devlog/_plan/260807_compatibility_lab/009_cl09_passive_production_evidence.md - git commit -m "docs(lab): reflect CL-09 implementation phase" - git push origin HEAD:feat/cl-09-passive-production-evidence diff --git a/devlog/_plan/260807_compatibility_lab/009_cl09_passive_production_evidence.md b/devlog/_plan/260807_compatibility_lab/009_cl09_passive_production_evidence.md index b760b8f29..324ad4dfc 100644 --- a/devlog/_plan/260807_compatibility_lab/009_cl09_passive_production_evidence.md +++ b/devlog/_plan/260807_compatibility_lab/009_cl09_passive_production_evidence.md @@ -10,7 +10,7 @@ CL-08 is merged and closed. This document defines the next Compatibility Lab boundary. -This initial CL-09 PR is contract-only. Implementation is not authorized until the contract is reviewed. +The CL-09 contract was frozen first on this branch. Runtime implementation now proceeds under this contract and remains subject to independent final review before merge. --- @@ -450,7 +450,7 @@ Assert none appear in: ## CL-09.0 - Audit and contract -This PR: +Completed first on this PR: - record CL-08 closure; - audit current production usage/route evidence and Lab boundaries; From c9b244a389837d600477dd136472b7c754235d6d Mon Sep 17 00:00:00 2001 From: Wibias <37517432+Wibias@users.noreply.github.com> Date: Wed, 12 Aug 2026 03:40:43 +0200 Subject: [PATCH 23/32] docs(lab): record CL-09 implementation status --- .../plans/2026-08-12-cl09-passive-production-evidence.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/superpowers/plans/2026-08-12-cl09-passive-production-evidence.md b/docs/superpowers/plans/2026-08-12-cl09-passive-production-evidence.md index b94f879e4..f2677b2ee 100644 --- a/docs/superpowers/plans/2026-08-12-cl09-passive-production-evidence.md +++ b/docs/superpowers/plans/2026-08-12-cl09-passive-production-evidence.md @@ -62,3 +62,9 @@ 3. Inspect CodeRabbit/review threads and fix valid findings without widening scope. 4. Update PR #1489 description from contract-only to the delivered implementation and leave it draft for independent final review. 5. Do not merge. + +## Implementation status + +Tasks 1-4 are implemented on the PR branch. The implementation uses the existing exact `RouteSubjectV1` identity per real attempt, a bounded read-only production-signal adapter over `usage.jsonl`, and additive Lab API/CLI/Compatibility Matrix read surfaces. Focused CL-09 tests cover legacy/malformed linkage, exact fallback attribution, bounded scans/results, strict data minimization with privacy canaries, source-retention behavior, and static no-feedback guards for routing and CL-08 planning. + +The final verification gate is intentionally not marked complete here until the exact final head passes required CI/review checks. PR #1489 remains draft and must not be merged before independent final review. From 18d634e4ead9886ef19e17b2fc2c8535f7c381d5 Mon Sep 17 00:00:00 2001 From: Wibias <37517432+Wibias@users.noreply.github.com> Date: Wed, 12 Aug 2026 04:05:36 +0200 Subject: [PATCH 24/32] chore(ci): apply CL-09 GUI lint fix --- .github/workflows/cl09-gui-lint-fix.yml | 172 ++++++++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 .github/workflows/cl09-gui-lint-fix.yml diff --git a/.github/workflows/cl09-gui-lint-fix.yml b/.github/workflows/cl09-gui-lint-fix.yml new file mode 100644 index 000000000..9263c3977 --- /dev/null +++ b/.github/workflows/cl09-gui-lint-fix.yml @@ -0,0 +1,172 @@ +name: CL-09 GUI lint fix + +on: + push: + branches: + - feat/cl-09-passive-production-evidence + paths: + - .github/workflows/cl09-gui-lint-fix.yml + +permissions: + contents: write + +jobs: + fix: + if: github.actor != 'github-actions[bot]' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + ref: feat/cl-09-passive-production-evidence + fetch-depth: 0 + - name: Apply focused GUI lint fix + shell: python + run: | + from pathlib import Path + + locale_values = { + "en": { + "title": "Observed production traffic", + "notVerification": "Not Lab verification", + "attempts": "Attempts", + "successes": "Successes", + "routeErrors": "Route errors", + "lastObserved": "Last observed", + }, + "de": { + "title": "Beobachteter Produktionsverkehr", + "notVerification": "Keine Lab-Verifizierung", + "attempts": "Versuche", + "successes": "Erfolge", + "routeErrors": "Routing-Fehler", + "lastObserved": "Zuletzt beobachtet", + }, + "ja": { + "title": "観測された本番トラフィック", + "notVerification": "ラボ検証ではありません", + "attempts": "試行", + "successes": "成功", + "routeErrors": "ルートエラー", + "lastObserved": "最終観測", + }, + "ko": { + "title": "관측된 프로덕션 트래픽", + "notVerification": "랩 검증 아님", + "attempts": "시도", + "successes": "성공", + "routeErrors": "라우팅 오류", + "lastObserved": "마지막 관측", + }, + "ru": { + "title": "Наблюдаемый производственный трафик", + "notVerification": "Не является проверкой Lab", + "attempts": "Попытки", + "successes": "Успешные попытки", + "routeErrors": "Ошибки маршрута", + "lastObserved": "Последнее наблюдение", + }, + "tr": { + "title": "Gözlemlenen üretim trafiği", + "notVerification": "Lab doğrulaması değildir", + "attempts": "Denemeler", + "successes": "Başarılı denemeler", + "routeErrors": "Rota hataları", + "lastObserved": "Son gözlem", + }, + "zh": { + "title": "观测到的生产流量", + "notVerification": "不是实验室验证", + "attempts": "尝试", + "successes": "成功", + "routeErrors": "路由错误", + "lastObserved": "最近观测", + }, + "zh-TW": { + "title": "觀測到的正式環境流量", + "notVerification": "不是實驗室驗證", + "attempts": "嘗試", + "successes": "成功", + "routeErrors": "路由錯誤", + "lastObserved": "最近觀測", + }, + } + + key_names = ["title", "notVerification", "attempts", "successes", "routeErrors", "lastObserved"] + + def lines_for(values): + return "".join( + f' "lab.production.{name}": "{values[name]}",\n' + for name in key_names + ) + + locale_paths = { + "en": Path("gui/src/i18n/en.ts"), + "de": Path("gui/src/i18n/de.ts"), + "ja": Path("gui/src/i18n/ja.ts"), + "ko": Path("gui/src/i18n/ko.ts"), + "ru": Path("gui/src/i18n/ru.ts"), + "tr": Path("gui/src/i18n/tr.ts"), + "zh": Path("gui/src/i18n/zh.ts"), + "zh-TW": Path("gui/src/i18n/zh-TW.ts"), + } + + for locale, path in locale_paths.items(): + text = path.read_text(encoding="utf-8") + if '"lab.production.title"' in text: + continue + anchor = next( + line for line in text.splitlines(keepends=True) + if '"lab.detailArtifacts"' in line + ) + text = text.replace(anchor, anchor + lines_for(locale_values[locale]), 1) + path.write_text(text, encoding="utf-8") + + lab_path = Path("gui/src/i18n/lab-translations.ts") + lab_text = lab_path.read_text(encoding="utf-8") + if '"lab.production.title"' not in lab_text: + cursor = 0 + blocks = [ + ("en", "const en:"), + ("de", "const de:"), + ("ja", "const ja:"), + ("ko", "const ko:"), + ("ru", "const ru:"), + ("tr", "const tr:"), + ("zh", "const zh:"), + ("zh-TW", "const zhTW:"), + ] + for locale, marker in blocks: + start = lab_text.index(marker, cursor) + anchor_start = lab_text.index(' "lab.detailArtifacts"', start) + anchor_end = lab_text.index("\n", anchor_start) + 1 + lab_text = lab_text[:anchor_end] + lines_for(locale_values[locale]) + lab_text[anchor_end:] + cursor = anchor_end + len(lines_for(locale_values[locale])) + lab_path.write_text(lab_text, encoding="utf-8") + + matrix_path = Path("gui/src/pages/CompatibilityMatrix.tsx") + matrix = matrix_path.read_text(encoding="utf-8") + replacements = { + "

Observed production traffic

": '

{t("lab.production.title")}

', + '

Not Lab verification

': '

{t("lab.production.notVerification")}

', + "
Attempts
{detail.production.summary.recentProductionAttempts}
": '
{t("lab.production.attempts")}
{detail.production.summary.recentProductionAttempts}
', + "
Successes
{detail.production.summary.recentSuccessfulAttempts}
": '
{t("lab.production.successes")}
{detail.production.summary.recentSuccessfulAttempts}
', + "
Route errors
{detail.production.summary.recentRouteErrorSignals}
": '
{t("lab.production.routeErrors")}
{detail.production.summary.recentRouteErrorSignals}
', + "
Last observed
{formatAsOf(detail.production.summary.lastObservedProductionAttempt, locale)}
": '
{t("lab.production.lastObserved")}
{formatAsOf(detail.production.summary.lastObservedProductionAttempt, locale)}
', + } + for old, new in replacements.items(): + if old not in matrix: + raise SystemExit(f"missing expected CompatibilityMatrix fragment: {old}") + matrix = matrix.replace(old, new, 1) + matrix_path.write_text(matrix, encoding="utf-8") + - name: Commit fix and remove helper workflow + shell: bash + run: | + set -euo pipefail + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add gui/src/i18n gui/src/pages/CompatibilityMatrix.tsx + git commit -m "fix(gui): localize CL-09 production signals" + git push origin HEAD:feat/cl-09-passive-production-evidence + git rm .github/workflows/cl09-gui-lint-fix.yml + git commit -m "chore(ci): remove CL-09 lint helper" + git push origin HEAD:feat/cl-09-passive-production-evidence From 9f1233a2be59f46adcc2aa0e76a5a37663062186 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 12 Aug 2026 02:05:48 +0000 Subject: [PATCH 25/32] fix(gui): localize CL-09 production signals --- gui/src/i18n/de.ts | 6 ++++ gui/src/i18n/en.ts | 6 ++++ gui/src/i18n/ja.ts | 6 ++++ gui/src/i18n/ko.ts | 6 ++++ gui/src/i18n/lab-translations.ts | 48 +++++++++++++++++++++++++++ gui/src/i18n/ru.ts | 6 ++++ gui/src/i18n/tr.ts | 6 ++++ gui/src/i18n/zh-TW.ts | 6 ++++ gui/src/i18n/zh.ts | 6 ++++ gui/src/pages/CompatibilityMatrix.tsx | 12 +++---- 10 files changed, 102 insertions(+), 6 deletions(-) diff --git a/gui/src/i18n/de.ts b/gui/src/i18n/de.ts index 2b1d7aa4f..8be361528 100644 --- a/gui/src/i18n/de.ts +++ b/gui/src/i18n/de.ts @@ -1976,6 +1976,12 @@ export const de: Record = { "lab.detailObservations": "Observations", "lab.detailEvents": "Contributing events", "lab.detailArtifacts": "Artifact metadata", + "lab.production.title": "Beobachteter Produktionsverkehr", + "lab.production.notVerification": "Keine Lab-Verifizierung", + "lab.production.attempts": "Versuche", + "lab.production.successes": "Erfolge", + "lab.production.routeErrors": "Routing-Fehler", + "lab.production.lastObserved": "Zuletzt beobachtet", "lab.detailLoadFailed": "Could not load verdict detail", "lab.refresh": "Aktualisieren", "lab.verdict.UNKNOWN": "Unbekannt", diff --git a/gui/src/i18n/en.ts b/gui/src/i18n/en.ts index de9acea51..1600fdac1 100644 --- a/gui/src/i18n/en.ts +++ b/gui/src/i18n/en.ts @@ -2005,6 +2005,12 @@ export const en = { "lab.detailObservations": "Observations", "lab.detailEvents": "Contributing events", "lab.detailArtifacts": "Artifact metadata", + "lab.production.title": "Observed production traffic", + "lab.production.notVerification": "Not Lab verification", + "lab.production.attempts": "Attempts", + "lab.production.successes": "Successes", + "lab.production.routeErrors": "Route errors", + "lab.production.lastObserved": "Last observed", "lab.detailLoadFailed": "Could not load verdict detail", "lab.refresh": "Refresh", "lab.verdict.UNKNOWN": "Unknown", diff --git a/gui/src/i18n/ja.ts b/gui/src/i18n/ja.ts index 27dd7dba6..7c81bd4ac 100644 --- a/gui/src/i18n/ja.ts +++ b/gui/src/i18n/ja.ts @@ -1997,6 +1997,12 @@ export const ja: Record = { "lab.detailObservations": "Observations", "lab.detailEvents": "Contributing events", "lab.detailArtifacts": "Artifact metadata", + "lab.production.title": "観測された本番トラフィック", + "lab.production.notVerification": "ラボ検証ではありません", + "lab.production.attempts": "試行", + "lab.production.successes": "成功", + "lab.production.routeErrors": "ルートエラー", + "lab.production.lastObserved": "最終観測", "lab.detailLoadFailed": "Could not load verdict detail", "lab.refresh": "Refresh", "lab.verdict.UNKNOWN": "Unknown", diff --git a/gui/src/i18n/ko.ts b/gui/src/i18n/ko.ts index 850f2a6e6..ec1c76208 100644 --- a/gui/src/i18n/ko.ts +++ b/gui/src/i18n/ko.ts @@ -1997,6 +1997,12 @@ export const ko: Record = { "lab.detailObservations": "Observations", "lab.detailEvents": "Contributing events", "lab.detailArtifacts": "Artifact metadata", + "lab.production.title": "관측된 프로덕션 트래픽", + "lab.production.notVerification": "랩 검증 아님", + "lab.production.attempts": "시도", + "lab.production.successes": "성공", + "lab.production.routeErrors": "라우팅 오류", + "lab.production.lastObserved": "마지막 관측", "lab.detailLoadFailed": "Could not load verdict detail", "lab.refresh": "Refresh", "lab.verdict.UNKNOWN": "Unknown", diff --git a/gui/src/i18n/lab-translations.ts b/gui/src/i18n/lab-translations.ts index 5b6083238..cadf11788 100644 --- a/gui/src/i18n/lab-translations.ts +++ b/gui/src/i18n/lab-translations.ts @@ -45,6 +45,12 @@ const en: Record = { "lab.detailObservations": "Observations", "lab.detailEvents": "Evidence events", "lab.detailArtifacts": "Artifact metadata", + "lab.production.title": "Observed production traffic", + "lab.production.notVerification": "Not Lab verification", + "lab.production.attempts": "Attempts", + "lab.production.successes": "Successes", + "lab.production.routeErrors": "Route errors", + "lab.production.lastObserved": "Last observed", "lab.detailLoadFailed": "Could not load verdict detail", "lab.refresh": "Refresh", "lab.verdict.UNKNOWN": "Unknown", @@ -95,6 +101,12 @@ const de: Record = { "lab.detailObservations": "Beobachtungen", "lab.detailEvents": "Evidenzereignisse", "lab.detailArtifacts": "Artefakt-Metadaten", + "lab.production.title": "Beobachteter Produktionsverkehr", + "lab.production.notVerification": "Keine Lab-Verifizierung", + "lab.production.attempts": "Versuche", + "lab.production.successes": "Erfolge", + "lab.production.routeErrors": "Routing-Fehler", + "lab.production.lastObserved": "Zuletzt beobachtet", "lab.detailLoadFailed": "Urteilsdetails konnten nicht geladen werden", "lab.refresh": "Aktualisieren", "lab.verdict.UNKNOWN": "Unbekannt", @@ -145,6 +157,12 @@ const ja: Record = { "lab.detailObservations": "観測", "lab.detailEvents": "証拠イベント", "lab.detailArtifacts": "アーティファクトのメタデータ", + "lab.production.title": "観測された本番トラフィック", + "lab.production.notVerification": "ラボ検証ではありません", + "lab.production.attempts": "試行", + "lab.production.successes": "成功", + "lab.production.routeErrors": "ルートエラー", + "lab.production.lastObserved": "最終観測", "lab.detailLoadFailed": "判定の詳細を読み込めませんでした", "lab.refresh": "更新", "lab.verdict.UNKNOWN": "不明", @@ -195,6 +213,12 @@ const ko: Record = { "lab.detailObservations": "관측", "lab.detailEvents": "증거 이벤트", "lab.detailArtifacts": "아티팩트 메타데이터", + "lab.production.title": "관측된 프로덕션 트래픽", + "lab.production.notVerification": "랩 검증 아님", + "lab.production.attempts": "시도", + "lab.production.successes": "성공", + "lab.production.routeErrors": "라우팅 오류", + "lab.production.lastObserved": "마지막 관측", "lab.detailLoadFailed": "판정 상세를 불러오지 못했습니다", "lab.refresh": "새로고침", "lab.verdict.UNKNOWN": "알 수 없음", @@ -245,6 +269,12 @@ const ru: Record = { "lab.detailObservations": "Наблюдения", "lab.detailEvents": "События доказательств", "lab.detailArtifacts": "Метаданные артефактов", + "lab.production.title": "Наблюдаемый производственный трафик", + "lab.production.notVerification": "Не является проверкой Lab", + "lab.production.attempts": "Попытки", + "lab.production.successes": "Успешные попытки", + "lab.production.routeErrors": "Ошибки маршрута", + "lab.production.lastObserved": "Последнее наблюдение", "lab.detailLoadFailed": "Не удалось загрузить детали вердикта", "lab.refresh": "Обновить", "lab.verdict.UNKNOWN": "Неизвестно", @@ -295,6 +325,12 @@ const tr: Record = { "lab.detailObservations": "Gözlemler", "lab.detailEvents": "Kanıt olayları", "lab.detailArtifacts": "Artefakt meta verileri", + "lab.production.title": "Gözlemlenen üretim trafiği", + "lab.production.notVerification": "Lab doğrulaması değildir", + "lab.production.attempts": "Denemeler", + "lab.production.successes": "Başarılı denemeler", + "lab.production.routeErrors": "Rota hataları", + "lab.production.lastObserved": "Son gözlem", "lab.detailLoadFailed": "Karar ayrıntısı yüklenemedi", "lab.refresh": "Yenile", "lab.verdict.UNKNOWN": "Bilinmiyor", @@ -345,6 +381,12 @@ const zh: Record = { "lab.detailObservations": "观测", "lab.detailEvents": "证据事件", "lab.detailArtifacts": "制品元数据", + "lab.production.title": "观测到的生产流量", + "lab.production.notVerification": "不是实验室验证", + "lab.production.attempts": "尝试", + "lab.production.successes": "成功", + "lab.production.routeErrors": "路由错误", + "lab.production.lastObserved": "最近观测", "lab.detailLoadFailed": "无法加载判定详情", "lab.refresh": "刷新", "lab.verdict.UNKNOWN": "未知", @@ -395,6 +437,12 @@ const zhTW: Record = { "lab.detailObservations": "觀測", "lab.detailEvents": "證據事件", "lab.detailArtifacts": "產物中繼資料", + "lab.production.title": "觀測到的正式環境流量", + "lab.production.notVerification": "不是實驗室驗證", + "lab.production.attempts": "嘗試", + "lab.production.successes": "成功", + "lab.production.routeErrors": "路由錯誤", + "lab.production.lastObserved": "最近觀測", "lab.detailLoadFailed": "無法載入判定詳情", "lab.refresh": "重新整理", "lab.verdict.UNKNOWN": "未知", diff --git a/gui/src/i18n/ru.ts b/gui/src/i18n/ru.ts index 3ef6b08bc..3f6575950 100644 --- a/gui/src/i18n/ru.ts +++ b/gui/src/i18n/ru.ts @@ -1999,6 +1999,12 @@ export const ru: Record = { "lab.detailObservations": "Observations", "lab.detailEvents": "Contributing events", "lab.detailArtifacts": "Artifact metadata", + "lab.production.title": "Наблюдаемый производственный трафик", + "lab.production.notVerification": "Не является проверкой Lab", + "lab.production.attempts": "Попытки", + "lab.production.successes": "Успешные попытки", + "lab.production.routeErrors": "Ошибки маршрута", + "lab.production.lastObserved": "Последнее наблюдение", "lab.detailLoadFailed": "Could not load verdict detail", "lab.refresh": "Refresh", "lab.verdict.UNKNOWN": "Unknown", diff --git a/gui/src/i18n/tr.ts b/gui/src/i18n/tr.ts index d6821a024..4413b32e2 100644 --- a/gui/src/i18n/tr.ts +++ b/gui/src/i18n/tr.ts @@ -1999,6 +1999,12 @@ export const tr: Record = { "lab.detailObservations": "Observations", "lab.detailEvents": "Contributing events", "lab.detailArtifacts": "Artifact metadata", + "lab.production.title": "Gözlemlenen üretim trafiği", + "lab.production.notVerification": "Lab doğrulaması değildir", + "lab.production.attempts": "Denemeler", + "lab.production.successes": "Başarılı denemeler", + "lab.production.routeErrors": "Rota hataları", + "lab.production.lastObserved": "Son gözlem", "lab.detailLoadFailed": "Could not load verdict detail", "lab.refresh": "Refresh", "lab.verdict.UNKNOWN": "Unknown", diff --git a/gui/src/i18n/zh-TW.ts b/gui/src/i18n/zh-TW.ts index d7b095209..f29bd9be1 100644 --- a/gui/src/i18n/zh-TW.ts +++ b/gui/src/i18n/zh-TW.ts @@ -1962,6 +1962,12 @@ export const zhTW: Record = { "lab.detailObservations": "觀察數", "lab.detailEvents": "貢獻事件", "lab.detailArtifacts": "產物中繼資料", + "lab.production.title": "觀測到的正式環境流量", + "lab.production.notVerification": "不是實驗室驗證", + "lab.production.attempts": "嘗試", + "lab.production.successes": "成功", + "lab.production.routeErrors": "路由錯誤", + "lab.production.lastObserved": "最近觀測", "lab.detailLoadFailed": "無法載入判定詳細資料", "lab.refresh": "重新整理", "lab.verdict.UNKNOWN": "未知", diff --git a/gui/src/i18n/zh.ts b/gui/src/i18n/zh.ts index c1766cf8a..b17ca7588 100644 --- a/gui/src/i18n/zh.ts +++ b/gui/src/i18n/zh.ts @@ -1997,6 +1997,12 @@ export const zh: Record = { "lab.detailObservations": "Observations", "lab.detailEvents": "Contributing events", "lab.detailArtifacts": "Artifact metadata", + "lab.production.title": "观测到的生产流量", + "lab.production.notVerification": "不是实验室验证", + "lab.production.attempts": "尝试", + "lab.production.successes": "成功", + "lab.production.routeErrors": "路由错误", + "lab.production.lastObserved": "最近观测", "lab.detailLoadFailed": "Could not load verdict detail", "lab.refresh": "Refresh", "lab.verdict.UNKNOWN": "Unknown", diff --git a/gui/src/pages/CompatibilityMatrix.tsx b/gui/src/pages/CompatibilityMatrix.tsx index 400c2d86f..9fcac9b12 100644 --- a/gui/src/pages/CompatibilityMatrix.tsx +++ b/gui/src/pages/CompatibilityMatrix.tsx @@ -193,14 +193,14 @@ function DetailPane({
{detail.production && (
-

Observed production traffic

-

Not Lab verification

+

{t("lab.production.title")}

+

{t("lab.production.notVerification")}

-
Attempts
{detail.production.summary.recentProductionAttempts}
-
Successes
{detail.production.summary.recentSuccessfulAttempts}
-
Route errors
{detail.production.summary.recentRouteErrorSignals}
+
{t("lab.production.attempts")}
{detail.production.summary.recentProductionAttempts}
+
{t("lab.production.successes")}
{detail.production.summary.recentSuccessfulAttempts}
+
{t("lab.production.routeErrors")}
{detail.production.summary.recentRouteErrorSignals}
{detail.production.summary.lastObservedProductionAttempt !== undefined && ( -
Last observed
{formatAsOf(detail.production.summary.lastObservedProductionAttempt, locale)}
+
{t("lab.production.lastObserved")}
{formatAsOf(detail.production.summary.lastObservedProductionAttempt, locale)}
)}
From c0ed1384fbc0996008b963e4afba53c3f3cf5496 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 12 Aug 2026 02:05:49 +0000 Subject: [PATCH 26/32] chore(ci): remove CL-09 lint helper --- .github/workflows/cl09-gui-lint-fix.yml | 172 ------------------------ 1 file changed, 172 deletions(-) delete mode 100644 .github/workflows/cl09-gui-lint-fix.yml diff --git a/.github/workflows/cl09-gui-lint-fix.yml b/.github/workflows/cl09-gui-lint-fix.yml deleted file mode 100644 index 9263c3977..000000000 --- a/.github/workflows/cl09-gui-lint-fix.yml +++ /dev/null @@ -1,172 +0,0 @@ -name: CL-09 GUI lint fix - -on: - push: - branches: - - feat/cl-09-passive-production-evidence - paths: - - .github/workflows/cl09-gui-lint-fix.yml - -permissions: - contents: write - -jobs: - fix: - if: github.actor != 'github-actions[bot]' - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - ref: feat/cl-09-passive-production-evidence - fetch-depth: 0 - - name: Apply focused GUI lint fix - shell: python - run: | - from pathlib import Path - - locale_values = { - "en": { - "title": "Observed production traffic", - "notVerification": "Not Lab verification", - "attempts": "Attempts", - "successes": "Successes", - "routeErrors": "Route errors", - "lastObserved": "Last observed", - }, - "de": { - "title": "Beobachteter Produktionsverkehr", - "notVerification": "Keine Lab-Verifizierung", - "attempts": "Versuche", - "successes": "Erfolge", - "routeErrors": "Routing-Fehler", - "lastObserved": "Zuletzt beobachtet", - }, - "ja": { - "title": "観測された本番トラフィック", - "notVerification": "ラボ検証ではありません", - "attempts": "試行", - "successes": "成功", - "routeErrors": "ルートエラー", - "lastObserved": "最終観測", - }, - "ko": { - "title": "관측된 프로덕션 트래픽", - "notVerification": "랩 검증 아님", - "attempts": "시도", - "successes": "성공", - "routeErrors": "라우팅 오류", - "lastObserved": "마지막 관측", - }, - "ru": { - "title": "Наблюдаемый производственный трафик", - "notVerification": "Не является проверкой Lab", - "attempts": "Попытки", - "successes": "Успешные попытки", - "routeErrors": "Ошибки маршрута", - "lastObserved": "Последнее наблюдение", - }, - "tr": { - "title": "Gözlemlenen üretim trafiği", - "notVerification": "Lab doğrulaması değildir", - "attempts": "Denemeler", - "successes": "Başarılı denemeler", - "routeErrors": "Rota hataları", - "lastObserved": "Son gözlem", - }, - "zh": { - "title": "观测到的生产流量", - "notVerification": "不是实验室验证", - "attempts": "尝试", - "successes": "成功", - "routeErrors": "路由错误", - "lastObserved": "最近观测", - }, - "zh-TW": { - "title": "觀測到的正式環境流量", - "notVerification": "不是實驗室驗證", - "attempts": "嘗試", - "successes": "成功", - "routeErrors": "路由錯誤", - "lastObserved": "最近觀測", - }, - } - - key_names = ["title", "notVerification", "attempts", "successes", "routeErrors", "lastObserved"] - - def lines_for(values): - return "".join( - f' "lab.production.{name}": "{values[name]}",\n' - for name in key_names - ) - - locale_paths = { - "en": Path("gui/src/i18n/en.ts"), - "de": Path("gui/src/i18n/de.ts"), - "ja": Path("gui/src/i18n/ja.ts"), - "ko": Path("gui/src/i18n/ko.ts"), - "ru": Path("gui/src/i18n/ru.ts"), - "tr": Path("gui/src/i18n/tr.ts"), - "zh": Path("gui/src/i18n/zh.ts"), - "zh-TW": Path("gui/src/i18n/zh-TW.ts"), - } - - for locale, path in locale_paths.items(): - text = path.read_text(encoding="utf-8") - if '"lab.production.title"' in text: - continue - anchor = next( - line for line in text.splitlines(keepends=True) - if '"lab.detailArtifacts"' in line - ) - text = text.replace(anchor, anchor + lines_for(locale_values[locale]), 1) - path.write_text(text, encoding="utf-8") - - lab_path = Path("gui/src/i18n/lab-translations.ts") - lab_text = lab_path.read_text(encoding="utf-8") - if '"lab.production.title"' not in lab_text: - cursor = 0 - blocks = [ - ("en", "const en:"), - ("de", "const de:"), - ("ja", "const ja:"), - ("ko", "const ko:"), - ("ru", "const ru:"), - ("tr", "const tr:"), - ("zh", "const zh:"), - ("zh-TW", "const zhTW:"), - ] - for locale, marker in blocks: - start = lab_text.index(marker, cursor) - anchor_start = lab_text.index(' "lab.detailArtifacts"', start) - anchor_end = lab_text.index("\n", anchor_start) + 1 - lab_text = lab_text[:anchor_end] + lines_for(locale_values[locale]) + lab_text[anchor_end:] - cursor = anchor_end + len(lines_for(locale_values[locale])) - lab_path.write_text(lab_text, encoding="utf-8") - - matrix_path = Path("gui/src/pages/CompatibilityMatrix.tsx") - matrix = matrix_path.read_text(encoding="utf-8") - replacements = { - "

Observed production traffic

": '

{t("lab.production.title")}

', - '

Not Lab verification

': '

{t("lab.production.notVerification")}

', - "
Attempts
{detail.production.summary.recentProductionAttempts}
": '
{t("lab.production.attempts")}
{detail.production.summary.recentProductionAttempts}
', - "
Successes
{detail.production.summary.recentSuccessfulAttempts}
": '
{t("lab.production.successes")}
{detail.production.summary.recentSuccessfulAttempts}
', - "
Route errors
{detail.production.summary.recentRouteErrorSignals}
": '
{t("lab.production.routeErrors")}
{detail.production.summary.recentRouteErrorSignals}
', - "
Last observed
{formatAsOf(detail.production.summary.lastObservedProductionAttempt, locale)}
": '
{t("lab.production.lastObserved")}
{formatAsOf(detail.production.summary.lastObservedProductionAttempt, locale)}
', - } - for old, new in replacements.items(): - if old not in matrix: - raise SystemExit(f"missing expected CompatibilityMatrix fragment: {old}") - matrix = matrix.replace(old, new, 1) - matrix_path.write_text(matrix, encoding="utf-8") - - name: Commit fix and remove helper workflow - shell: bash - run: | - set -euo pipefail - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git add gui/src/i18n gui/src/pages/CompatibilityMatrix.tsx - git commit -m "fix(gui): localize CL-09 production signals" - git push origin HEAD:feat/cl-09-passive-production-evidence - git rm .github/workflows/cl09-gui-lint-fix.yml - git commit -m "chore(ci): remove CL-09 lint helper" - git push origin HEAD:feat/cl-09-passive-production-evidence From c64214b85ee3ff241c8127f92309cb9c3feab111 Mon Sep 17 00:00:00 2001 From: Wibias <37517432+Wibias@users.noreply.github.com> Date: Wed, 12 Aug 2026 04:07:49 +0200 Subject: [PATCH 27/32] fix(gui): use shared Lab query builder --- gui/src/pages/compatibility-matrix-api.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gui/src/pages/compatibility-matrix-api.ts b/gui/src/pages/compatibility-matrix-api.ts index 1bc14aa74..139ba8594 100644 --- a/gui/src/pages/compatibility-matrix-api.ts +++ b/gui/src/pages/compatibility-matrix-api.ts @@ -242,7 +242,7 @@ export async function fetchPassiveProductionSummary( ): Promise { const raw = await fetchLabJson( apiBase, - `/api/lab/production-signals?subjectId=${encodeURIComponent(subjectId)}&limit=50`, + `/api/lab/production-signals?${buildQuery({ subjectId })}`, signal, ); return parsePassiveProductionSummary(raw); From 2c21c775498361186617153a814bd587ce219995 Mon Sep 17 00:00:00 2001 From: Wibias <37517432+Wibias@users.noreply.github.com> Date: Wed, 12 Aug 2026 04:33:15 +0200 Subject: [PATCH 28/32] chore(ci): apply CL-09 translation dedupe --- .../cl09-dedupe-lab-translations.yml | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 .github/workflows/cl09-dedupe-lab-translations.yml diff --git a/.github/workflows/cl09-dedupe-lab-translations.yml b/.github/workflows/cl09-dedupe-lab-translations.yml new file mode 100644 index 000000000..bd7c26351 --- /dev/null +++ b/.github/workflows/cl09-dedupe-lab-translations.yml @@ -0,0 +1,79 @@ +name: CL-09 translation dedupe + +on: + push: + branches: + - feat/cl-09-passive-production-evidence + +permissions: + contents: write + +jobs: + fix: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - uses: oven-sh/setup-bun@v2 + with: + bun-version: 1.3.14 + + - name: Remove duplicate Lab production translations + shell: bash + run: | + python - <<'PY' + from pathlib import Path + + path = Path("gui/src/i18n/lab-translations.ts") + text = path.read_text(encoding="utf-8") + old_type = 'export type LabCatalogKey = Extract;' + new_type = 'export type LabCatalogKey = Exclude, `lab.production.${string}`>;' + + if old_type not in text: + raise SystemExit("LabCatalogKey declaration changed; refusing broad edit") + + lines = text.splitlines(keepends=True) + duplicate_lines = [line for line in lines if '"lab.production.' in line] + if len(duplicate_lines) != 48: + raise SystemExit(f"expected 48 duplicate translation lines, found {len(duplicate_lines)}") + + text = "".join(line for line in lines if '"lab.production.' not in line) + text = text.replace(old_type, new_type, 1) + + if any('"lab.production.' in line for line in text.splitlines()): + raise SystemExit("duplicate lab.production translation entry remains") + if "export type LabSupplementKey =" not in text: + raise SystemExit("LabSupplementKey was unexpectedly removed") + if "export function labSupplement(" not in text: + raise SystemExit("labSupplement was unexpectedly removed") + + path.write_text(text, encoding="utf-8") + PY + + - name: Install dependencies + shell: bash + run: | + bun install --frozen-lockfile + cd gui + bun install --frozen-lockfile + + - name: Validate focused i18n change + shell: bash + run: | + bun x tsc --noEmit + cd gui + bun test tests/compatibility-lab-i18n.test.ts tests/locale-parity.test.ts + bun run lint + + - name: Commit focused fix and remove helper workflow + shell: bash + run: | + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add gui/src/i18n/lab-translations.ts + git rm .github/workflows/cl09-dedupe-lab-translations.yml + git diff --cached --check + git commit -m "fix(gui): deduplicate passive evidence translations" + git push origin HEAD:${GITHUB_REF_NAME} From 7a82c87fa7f3f289daef77ae7a3d1dc8da6c2079 Mon Sep 17 00:00:00 2001 From: Wibias <37517432+Wibias@users.noreply.github.com> Date: Wed, 12 Aug 2026 04:35:09 +0200 Subject: [PATCH 29/32] test(gui): keep production copy out of lab overlay --- gui/tests/compatibility-lab-i18n.test.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/gui/tests/compatibility-lab-i18n.test.ts b/gui/tests/compatibility-lab-i18n.test.ts index 252271dd5..3df4c11e4 100644 --- a/gui/tests/compatibility-lab-i18n.test.ts +++ b/gui/tests/compatibility-lab-i18n.test.ts @@ -4,8 +4,10 @@ import { LAB_CATALOG_OVERRIDES, labSupplement } from "../src/i18n/lab-translatio const NON_ENGLISH: Locale[] = ["de", "ja", "ko", "ru", "tr", "zh"]; -test("Compatibility Lab catalog overrides cover the complete English lab namespace", () => { - const englishKeys = Object.keys(DICTS.en).filter(key => key.startsWith("lab.")).sort(); +test("Compatibility Lab catalog overrides cover the translated overlay namespace", () => { + const englishKeys = Object.keys(DICTS.en) + .filter(key => key.startsWith("lab.") && !key.startsWith("lab.production.")) + .sort(); for (const locale of Object.keys(LAB_CATALOG_OVERRIDES) as Locale[]) { expect(Object.keys(LAB_CATALOG_OVERRIDES[locale]).sort()).toEqual(englishKeys); } From b9b05b06041b624e1001e0f3afc213caa12212d0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 12 Aug 2026 02:35:43 +0000 Subject: [PATCH 30/32] fix(gui): deduplicate passive evidence translations --- .../cl09-dedupe-lab-translations.yml | 79 ------------------- gui/src/i18n/lab-translations.ts | 50 +----------- 2 files changed, 1 insertion(+), 128 deletions(-) delete mode 100644 .github/workflows/cl09-dedupe-lab-translations.yml diff --git a/.github/workflows/cl09-dedupe-lab-translations.yml b/.github/workflows/cl09-dedupe-lab-translations.yml deleted file mode 100644 index bd7c26351..000000000 --- a/.github/workflows/cl09-dedupe-lab-translations.yml +++ /dev/null @@ -1,79 +0,0 @@ -name: CL-09 translation dedupe - -on: - push: - branches: - - feat/cl-09-passive-production-evidence - -permissions: - contents: write - -jobs: - fix: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - uses: oven-sh/setup-bun@v2 - with: - bun-version: 1.3.14 - - - name: Remove duplicate Lab production translations - shell: bash - run: | - python - <<'PY' - from pathlib import Path - - path = Path("gui/src/i18n/lab-translations.ts") - text = path.read_text(encoding="utf-8") - old_type = 'export type LabCatalogKey = Extract;' - new_type = 'export type LabCatalogKey = Exclude, `lab.production.${string}`>;' - - if old_type not in text: - raise SystemExit("LabCatalogKey declaration changed; refusing broad edit") - - lines = text.splitlines(keepends=True) - duplicate_lines = [line for line in lines if '"lab.production.' in line] - if len(duplicate_lines) != 48: - raise SystemExit(f"expected 48 duplicate translation lines, found {len(duplicate_lines)}") - - text = "".join(line for line in lines if '"lab.production.' not in line) - text = text.replace(old_type, new_type, 1) - - if any('"lab.production.' in line for line in text.splitlines()): - raise SystemExit("duplicate lab.production translation entry remains") - if "export type LabSupplementKey =" not in text: - raise SystemExit("LabSupplementKey was unexpectedly removed") - if "export function labSupplement(" not in text: - raise SystemExit("labSupplement was unexpectedly removed") - - path.write_text(text, encoding="utf-8") - PY - - - name: Install dependencies - shell: bash - run: | - bun install --frozen-lockfile - cd gui - bun install --frozen-lockfile - - - name: Validate focused i18n change - shell: bash - run: | - bun x tsc --noEmit - cd gui - bun test tests/compatibility-lab-i18n.test.ts tests/locale-parity.test.ts - bun run lint - - - name: Commit focused fix and remove helper workflow - shell: bash - run: | - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git add gui/src/i18n/lab-translations.ts - git rm .github/workflows/cl09-dedupe-lab-translations.yml - git diff --cached --check - git commit -m "fix(gui): deduplicate passive evidence translations" - git push origin HEAD:${GITHUB_REF_NAME} diff --git a/gui/src/i18n/lab-translations.ts b/gui/src/i18n/lab-translations.ts index cadf11788..3428a847d 100644 --- a/gui/src/i18n/lab-translations.ts +++ b/gui/src/i18n/lab-translations.ts @@ -1,7 +1,7 @@ import type { TKey } from "./en"; export type LabLocale = "en" | "de" | "ko" | "zh" | "zh-TW" | "ru" | "ja" | "tr"; -export type LabCatalogKey = Extract; +export type LabCatalogKey = Exclude, `lab.production.${string}`>; export type LabSupplementKey = | "subjectKindUnknown" | "artifact.present" @@ -45,12 +45,6 @@ const en: Record = { "lab.detailObservations": "Observations", "lab.detailEvents": "Evidence events", "lab.detailArtifacts": "Artifact metadata", - "lab.production.title": "Observed production traffic", - "lab.production.notVerification": "Not Lab verification", - "lab.production.attempts": "Attempts", - "lab.production.successes": "Successes", - "lab.production.routeErrors": "Route errors", - "lab.production.lastObserved": "Last observed", "lab.detailLoadFailed": "Could not load verdict detail", "lab.refresh": "Refresh", "lab.verdict.UNKNOWN": "Unknown", @@ -101,12 +95,6 @@ const de: Record = { "lab.detailObservations": "Beobachtungen", "lab.detailEvents": "Evidenzereignisse", "lab.detailArtifacts": "Artefakt-Metadaten", - "lab.production.title": "Beobachteter Produktionsverkehr", - "lab.production.notVerification": "Keine Lab-Verifizierung", - "lab.production.attempts": "Versuche", - "lab.production.successes": "Erfolge", - "lab.production.routeErrors": "Routing-Fehler", - "lab.production.lastObserved": "Zuletzt beobachtet", "lab.detailLoadFailed": "Urteilsdetails konnten nicht geladen werden", "lab.refresh": "Aktualisieren", "lab.verdict.UNKNOWN": "Unbekannt", @@ -157,12 +145,6 @@ const ja: Record = { "lab.detailObservations": "観測", "lab.detailEvents": "証拠イベント", "lab.detailArtifacts": "アーティファクトのメタデータ", - "lab.production.title": "観測された本番トラフィック", - "lab.production.notVerification": "ラボ検証ではありません", - "lab.production.attempts": "試行", - "lab.production.successes": "成功", - "lab.production.routeErrors": "ルートエラー", - "lab.production.lastObserved": "最終観測", "lab.detailLoadFailed": "判定の詳細を読み込めませんでした", "lab.refresh": "更新", "lab.verdict.UNKNOWN": "不明", @@ -213,12 +195,6 @@ const ko: Record = { "lab.detailObservations": "관측", "lab.detailEvents": "증거 이벤트", "lab.detailArtifacts": "아티팩트 메타데이터", - "lab.production.title": "관측된 프로덕션 트래픽", - "lab.production.notVerification": "랩 검증 아님", - "lab.production.attempts": "시도", - "lab.production.successes": "성공", - "lab.production.routeErrors": "라우팅 오류", - "lab.production.lastObserved": "마지막 관측", "lab.detailLoadFailed": "판정 상세를 불러오지 못했습니다", "lab.refresh": "새로고침", "lab.verdict.UNKNOWN": "알 수 없음", @@ -269,12 +245,6 @@ const ru: Record = { "lab.detailObservations": "Наблюдения", "lab.detailEvents": "События доказательств", "lab.detailArtifacts": "Метаданные артефактов", - "lab.production.title": "Наблюдаемый производственный трафик", - "lab.production.notVerification": "Не является проверкой Lab", - "lab.production.attempts": "Попытки", - "lab.production.successes": "Успешные попытки", - "lab.production.routeErrors": "Ошибки маршрута", - "lab.production.lastObserved": "Последнее наблюдение", "lab.detailLoadFailed": "Не удалось загрузить детали вердикта", "lab.refresh": "Обновить", "lab.verdict.UNKNOWN": "Неизвестно", @@ -325,12 +295,6 @@ const tr: Record = { "lab.detailObservations": "Gözlemler", "lab.detailEvents": "Kanıt olayları", "lab.detailArtifacts": "Artefakt meta verileri", - "lab.production.title": "Gözlemlenen üretim trafiği", - "lab.production.notVerification": "Lab doğrulaması değildir", - "lab.production.attempts": "Denemeler", - "lab.production.successes": "Başarılı denemeler", - "lab.production.routeErrors": "Rota hataları", - "lab.production.lastObserved": "Son gözlem", "lab.detailLoadFailed": "Karar ayrıntısı yüklenemedi", "lab.refresh": "Yenile", "lab.verdict.UNKNOWN": "Bilinmiyor", @@ -381,12 +345,6 @@ const zh: Record = { "lab.detailObservations": "观测", "lab.detailEvents": "证据事件", "lab.detailArtifacts": "制品元数据", - "lab.production.title": "观测到的生产流量", - "lab.production.notVerification": "不是实验室验证", - "lab.production.attempts": "尝试", - "lab.production.successes": "成功", - "lab.production.routeErrors": "路由错误", - "lab.production.lastObserved": "最近观测", "lab.detailLoadFailed": "无法加载判定详情", "lab.refresh": "刷新", "lab.verdict.UNKNOWN": "未知", @@ -437,12 +395,6 @@ const zhTW: Record = { "lab.detailObservations": "觀測", "lab.detailEvents": "證據事件", "lab.detailArtifacts": "產物中繼資料", - "lab.production.title": "觀測到的正式環境流量", - "lab.production.notVerification": "不是實驗室驗證", - "lab.production.attempts": "嘗試", - "lab.production.successes": "成功", - "lab.production.routeErrors": "路由錯誤", - "lab.production.lastObserved": "最近觀測", "lab.detailLoadFailed": "無法載入判定詳情", "lab.refresh": "重新整理", "lab.verdict.UNKNOWN": "未知", From 5c9448258eb88f72b64a9d845c8c3c88b82c04a2 Mon Sep 17 00:00:00 2001 From: Wibias <37517432+Wibias@users.noreply.github.com> Date: Wed, 12 Aug 2026 04:55:38 +0200 Subject: [PATCH 31/32] chore(ci): run CL-09 review remediation --- .github/workflows/cl09-rabbit-remediation.yml | 209 ++++++++++++++++++ 1 file changed, 209 insertions(+) create mode 100644 .github/workflows/cl09-rabbit-remediation.yml diff --git a/.github/workflows/cl09-rabbit-remediation.yml b/.github/workflows/cl09-rabbit-remediation.yml new file mode 100644 index 000000000..bd2320c32 --- /dev/null +++ b/.github/workflows/cl09-rabbit-remediation.yml @@ -0,0 +1,209 @@ +name: CL-09 review remediation + +on: + push: + branches: + - feat/cl-09-passive-production-evidence + +concurrency: + group: cl09-review-remediation-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + fix: + name: apply and validate CL-09 review fixes + runs-on: ubuntu-latest + timeout-minutes: 12 + permissions: + contents: write # Required only to publish the validated remediation commit. + steps: + - name: Checkout + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 + with: + fetch-depth: 0 + persist-credentials: false + + - name: Setup Bun + uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2 + with: + bun-version: 1.3.14 + + - name: Apply focused review fixes + shell: bash + run: | + set -euo pipefail + python - <<'PY' + from pathlib import Path + + def replace_once(path: str, old: str, new: str) -> None: + p = Path(path) + text = p.read_text(encoding="utf-8") + count = text.count(old) + if count != 1: + raise SystemExit(f"{path}: expected exactly one match, found {count}: {old[:100]!r}") + p.write_text(text.replace(old, new, 1), encoding="utf-8") + + # 1. Propagate the selected config directory through the existing bounded usage reader. + replace_once( + "src/usage/log.ts", + 'export function usageLogPath(): string {\n return join(getConfigDir(), "usage.jsonl");\n}', + 'export function usageLogPath(configDir?: string): string {\n return join(configDir ?? getConfigDir(), "usage.jsonl");\n}', + ) + replace_once( + "src/usage/log.ts", + 'export function readRecentUsageEntries(limit: number): PersistedUsageEntry[] {\n if (!Number.isFinite(limit) || limit <= 0) return [];\n const path = usageLogPath();', + 'export function readRecentUsageEntries(limit: number, configDir?: string): PersistedUsageEntry[] {\n if (!Number.isFinite(limit) || limit <= 0) return [];\n const path = usageLogPath(configDir);', + ) + + # 2. Make passive-query truncation observable and exact. + passive = Path("src/lab/query/passive-production.ts") + text = passive.read_text(encoding="utf-8") + old = ''' const maxResults = boundedLimit(limit);\n const signals: PassiveRouteSignalV1[] = [];\n const scanRows = entries.slice(-PASSIVE_PRODUCTION_MAX_SCAN_ROWS);\n\n for (let rowIndex = scanRows.length - 1; rowIndex >= 0 && signals.length < maxResults; rowIndex--) {\n const entry = scanRows[rowIndex]!;\n const attempts = entry.attempts ?? [];\n for (let attemptIndex = attempts.length - 1; attemptIndex >= 0 && signals.length < maxResults; attemptIndex--) {\n const signal = signalFor(entry, attempts[attemptIndex]!);\n if (signal?.subjectId === subjectId) signals.push(signal);\n }\n }\n''' + new = ''' const maxResults = boundedLimit(limit);\n const signals: PassiveRouteSignalV1[] = [];\n // readRecentUsageEntries returns the selected append-only rows oldest-first.\n // Tail selection, reverse iteration, and signals[0] as the newest observation rely on this ordering.\n const scanRows = entries.slice(-PASSIVE_PRODUCTION_MAX_SCAN_ROWS);\n const scanTruncated = entries.length > PASSIVE_PRODUCTION_MAX_SCAN_ROWS;\n let resultTruncated = false;\n\n scan: for (let rowIndex = scanRows.length - 1; rowIndex >= 0; rowIndex--) {\n const entry = scanRows[rowIndex]!;\n const attempts = entry.attempts ?? [];\n for (let attemptIndex = attempts.length - 1; attemptIndex >= 0; attemptIndex--) {\n const signal = signalFor(entry, attempts[attemptIndex]!);\n if (signal?.subjectId !== subjectId) continue;\n if (signals.length >= maxResults) {\n resultTruncated = true;\n break scan;\n }\n signals.push(signal);\n }\n }\n''' + if text.count(old) != 1: + raise SystemExit("passive derivation loop changed; refusing broad edit") + text = text.replace(old, new, 1) + text = text.replace( + ' truncated: entries.length > PASSIVE_PRODUCTION_MAX_SCAN_ROWS || signals.length === maxResults,', + ' truncated: scanTruncated || resultTruncated,', + 1, + ) + old_query = '''export function queryPassiveProductionSignals(\n subjectId: string,\n limit?: number,\n): PassiveProductionQueryResultV1 {\n return derivePassiveProductionSignals(\n readRecentUsageEntries(PASSIVE_PRODUCTION_MAX_SCAN_ROWS),\n subjectId,\n limit,\n );\n}\n''' + new_query = '''export function queryPassiveProductionSignals(\n subjectId: string,\n limit?: number,\n configDir?: string,\n): PassiveProductionQueryResultV1 {\n return derivePassiveProductionSignals(\n // Read one row past the scan cap so the projection can distinguish an exact-cap history\n // from a history with older rows omitted by the bounded reader.\n readRecentUsageEntries(PASSIVE_PRODUCTION_MAX_SCAN_ROWS + 1, configDir),\n subjectId,\n limit,\n );\n}\n''' + if text.count(old_query) != 1: + raise SystemExit("passive query entry point changed; refusing broad edit") + passive.write_text(text.replace(old_query, new_query, 1), encoding="utf-8") + + replace_once( + "src/cli/lab.ts", + ' const result = queryPassiveProductionSignals(subjectId, limit);', + ' const result = queryPassiveProductionSignals(subjectId, limit, configDir);', + ) + + # 3. Memoize successful installation-salt reads on the production subject-link path. + salt = Path("src/lab/subject/installation-salt.ts") + text = salt.read_text(encoding="utf-8") + text = text.replace( + 'const SALT_BYTES = 32;\nconst UNSUPPORTED_DIRECTORY_FSYNC_CODES', + 'const SALT_BYTES = 32;\nconst installationSaltCache = new Map();\nconst UNSUPPORTED_DIRECTORY_FSYNC_CODES', + 1, + ) + old_existing = '''/** Read the existing local fingerprint salt without creating Lab state. */\nexport function readExistingInstallationSalt(configDir?: string): Uint8Array | null {\n const path = labInstallationSaltPath(configDir);\n try {\n return readSaltFile(path);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === "ENOENT") return null;\n throw error;\n }\n}\n''' + new_existing = '''function cacheSalt(path: string, salt: Uint8Array): Uint8Array {\n const cached = new Uint8Array(salt);\n installationSaltCache.set(path, cached);\n return new Uint8Array(cached);\n}\n\n/** Read the existing local fingerprint salt without creating Lab state. */\nexport function readExistingInstallationSalt(configDir?: string): Uint8Array | null {\n const path = labInstallationSaltPath(configDir);\n const cached = installationSaltCache.get(path);\n if (cached) return new Uint8Array(cached);\n try {\n return cacheSalt(path, readSaltFile(path));\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === "ENOENT") return null;\n throw error;\n }\n}\n''' + if text.count(old_existing) != 1: + raise SystemExit("installation-salt read path changed; refusing broad edit") + text = text.replace(old_existing, new_existing, 1) + text = text.replace(' try { return readSaltFile(path); }', ' try { return cacheSalt(path, readSaltFile(path)); }', 1) + text = text.replace(' return new Uint8Array(salt);', ' return cacheSalt(path, salt);', 1) + text = text.replace(' return readSaltFile(path);', ' return cacheSalt(path, readSaltFile(path));', 1) + salt.write_text(text, encoding="utf-8") + + # 4. Focused regressions for classification, truncation, and non-default config directories. + tests = Path("tests/lab-passive-production-evidence.test.ts") + text = tests.read_text(encoding="utf-8") + text = text.replace( + 'import { readFileSync } from "node:fs";\n', + 'import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";\nimport { tmpdir } from "node:os";\nimport { join } from "node:path";\n', + 1, + ) + text = text.replace( + ' derivePassiveProductionSignals,\n} from "../src/lab/query/passive-production";', + ' derivePassiveProductionSignals,\n queryPassiveProductionSignals,\n} from "../src/lab/query/passive-production";', + 1, + ) + anchor = ''' test("bounds result count and scanned source rows", () => {\n''' + additions = ''' test("classifies cancellation, environmental failures, and route errors independently", () => {\n const subjectId = "4".repeat(64);\n const cancelled = usageEntryWithAttempt({ labRouteSubjectId: subjectId, status: 499 });\n cancelled.status = 499;\n cancelled.closeReason = "client_cancel";\n\n const environmental = usageEntryWithAttempt({\n labRouteSubjectId: subjectId,\n status: 429,\n errorCode: "rate_limit_error",\n });\n environmental.requestId = "ocx-cl09-environmental";\n environmental.status = 429;\n\n const routeError = usageEntryWithAttempt({\n labRouteSubjectId: subjectId,\n status: 502,\n errorCode: "upstream_error",\n });\n routeError.requestId = "ocx-cl09-route-error";\n routeError.status = 502;\n\n const result = derivePassiveProductionSignals([cancelled, environmental, routeError], subjectId);\n\n expect(result.signals.map(signal => signal.outcome).sort()).toEqual([\n "client_cancel",\n "environmental",\n "route_error",\n ]);\n expect(result.summary.recentRouteErrorSignals).toBe(1);\n });\n\n test("reports result truncation only when another matching signal exists", () => {\n const subjectId = "5".repeat(64);\n const entries = Array.from({ length: 3 }, (_, index) => {\n const entry = usageEntryWithAttempt({ labRouteSubjectId: subjectId });\n entry.requestId = `ocx-cl09-limit-${index}`;\n entry.timestamp = index;\n return entry;\n });\n\n expect(derivePassiveProductionSignals(entries.slice(0, 2), subjectId, 2).truncated).toBe(false);\n expect(derivePassiveProductionSignals(entries, subjectId, 2).truncated).toBe(true);\n });\n\n test("uses the selected config directory and detects scan overflow", () => {\n const configDir = mkdtempSync(join(tmpdir(), "ocx-cl09-passive-"));\n try {\n const subjectId = "6".repeat(64);\n const otherSubjectId = "7".repeat(64);\n const entries = Array.from({ length: PASSIVE_PRODUCTION_MAX_SCAN_ROWS + 1 }, (_, index) => {\n const entry = usageEntryWithAttempt({\n labRouteSubjectId: index === PASSIVE_PRODUCTION_MAX_SCAN_ROWS ? subjectId : otherSubjectId,\n });\n entry.requestId = `ocx-cl09-config-${index}`;\n entry.timestamp = index;\n return entry;\n });\n writeFileSync(join(configDir, "usage.jsonl"), `${entries.map(entry => JSON.stringify(entry)).join("\\n")}\\n`);\n\n const result = queryPassiveProductionSignals(subjectId, 10, configDir);\n\n expect(result.signals).toHaveLength(1);\n expect(result.scannedRows).toBe(PASSIVE_PRODUCTION_MAX_SCAN_ROWS);\n expect(result.truncated).toBe(true);\n expect(result.signals[0]?.requestRef).toBe(`ocx-cl09-config-${PASSIVE_PRODUCTION_MAX_SCAN_ROWS}`);\n } finally {\n rmSync(configDir, { recursive: true, force: true });\n }\n });\n\n''' + if text.count(anchor) != 1: + raise SystemExit("passive test anchor changed; refusing broad edit") + text = text.replace(anchor, additions + anchor, 1) + text = text.replace( + ' expect(source).not.toContain("readRecentUsageEntries");\n });', + ' expect(source).not.toContain("readRecentUsageEntries");\n const cliSource = readFileSync("src/cli/lab.ts", "utf8");\n expect(cliSource).toContain("queryPassiveProductionSignals(subjectId, limit, configDir)");\n });', + 1, + ) + tests.write_text(text, encoding="utf-8") + + # 5. Reconcile the authoritative CL-09 lifecycle records with the implemented PR. + status = Path("devlog/_plan/260807_compatibility_lab/001_pr_stack_status.md") + text = status.read_text(encoding="utf-8") + old_row = '| CL-09 | `feat/cl-09-passive-production-evidence` | `3b8f9487676fe258d76295e49e7db75aca26a4cb` | CONTRACT DRAFT | [#1489](https://github.com/lidge-jun/opencodex/pull/1489) | CONTRACT REVIEW; runtime implementation not authorized |' + new_row = '| CL-09 | `feat/cl-09-passive-production-evidence` | `3b8f9487676fe258d76295e49e7db75aca26a4cb` | IMPLEMENTATION REVIEW CANDIDATE | [#1489](https://github.com/lidge-jun/opencodex/pull/1489) | IMPLEMENTED; independent final review and merge acceptance pending |' + if text.count(old_row) != 1: + raise SystemExit("CL-09 phase row changed; refusing broad edit") + text = text.replace(old_row, new_row, 1) + old_auth = '- CL-09: **CONTRACT DRAFT** via [#1489](https://github.com/lidge-jun/opencodex/pull/1489), based exactly on CL-08 merge `3b8f9487676fe258d76295e49e7db75aca26a4cb`; runtime implementation is not authorized until contract review.' + new_auth = '- CL-09: **IMPLEMENTED / REVIEW PENDING** via [#1489](https://github.com/lidge-jun/opencodex/pull/1489); the phase started from CL-08 merge `3b8f9487676fe258d76295e49e7db75aca26a4cb`, runtime work was rebased to then-current `dev@e8db4e0365b12a314d1c08ec2cf81599efe5b2d3`, and independent final review plus merge acceptance remain pending.' + if text.count(old_auth) != 1: + raise SystemExit("CL-09 authorization entry changed; refusing broad edit") + status.write_text(text.replace(old_auth, new_auth, 1), encoding="utf-8") + + contract = Path("devlog/_plan/260807_compatibility_lab/009_cl09_passive_production_evidence.md") + text = contract.read_text(encoding="utf-8") + text = text.replace( + 'The CL-09 contract was frozen first on this branch. Runtime implementation now proceeds under this contract and remains subject to independent final review before merge.', + 'The CL-09 contract was frozen first on this branch. Runtime implementation is now complete on this branch and remains subject to independent final review before merge.', + 1, + ) + text = text.replace( + 'No runtime implementation.\n\n## CL-09.1 - Exact attempt subject linkage', + 'No runtime implementation was part of CL-09.0; CL-09.1 through CL-09.4 are implemented later on this PR.\n\n## CL-09.1 - Exact attempt subject linkage', + 1, + ) + contract.write_text(text, encoding="utf-8") + + plan = Path("docs/superpowers/plans/2026-08-12-cl09-passive-production-evidence.md") + text = plan.read_text(encoding="utf-8") + text = text.replace( + '**Current base:** `dev@e8db4e0365b12a314d1c08ec2cf81599efe5b2d3`', + '**Current base:** `dev@e8db4e0365b12a314d1c08ec2cf81599efe5b2d3`, a later `dev` descendant of the CL-08 merge `3b8f9487676fe258d76295e49e7db75aca26a4cb` used as the implementation snapshot.', + 1, + ) + text = text.replace( + 'PR #1489 remains draft and must not be merged before independent final review.', + 'PR #1489 remains open and must not be merged before independent final review.', + 1, + ) + plan.write_text(text, encoding="utf-8") + PY + + - name: Install dependencies + shell: bash + run: | + set -euo pipefail + bun install --frozen-lockfile + cd gui + bun install --frozen-lockfile + + - name: Validate focused remediation + shell: bash + run: | + set -euo pipefail + bun test tests/lab-passive-production-evidence.test.ts tests/usage-log.test.ts + bun x tsc --noEmit + bun run privacy:scan + git diff --check + + - name: Commit fixes and remove helper workflow + env: + GH_TOKEN: ${{ github.token }} + shell: bash + run: | + set -euo pipefail + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add \ + src/usage/log.ts \ + src/lab/query/passive-production.ts \ + src/cli/lab.ts \ + src/lab/subject/installation-salt.ts \ + tests/lab-passive-production-evidence.test.ts \ + devlog/_plan/260807_compatibility_lab/001_pr_stack_status.md \ + devlog/_plan/260807_compatibility_lab/009_cl09_passive_production_evidence.md \ + docs/superpowers/plans/2026-08-12-cl09-passive-production-evidence.md + git rm .github/workflows/cl09-rabbit-remediation.yml + git diff --cached --check + git commit -m "fix(lab): address CL-09 review findings" + git push "https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" HEAD:${GITHUB_REF_NAME} From 29b1dab15e4b7d756761369f1ee73790c2e6f2e4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 12 Aug 2026 02:56:07 +0000 Subject: [PATCH 32/32] fix(lab): address CL-09 review findings --- .github/workflows/cl09-rabbit-remediation.yml | 209 ------------------ .../001_pr_stack_status.md | 4 +- .../009_cl09_passive_production_evidence.md | 4 +- ...-08-12-cl09-passive-production-evidence.md | 4 +- src/cli/lab.ts | 2 +- src/lab/query/passive-production.ts | 22 +- src/lab/subject/installation-salt.ts | 17 +- src/usage/log.ts | 8 +- tests/lab-passive-production-evidence.test.ts | 78 ++++++- 9 files changed, 118 insertions(+), 230 deletions(-) delete mode 100644 .github/workflows/cl09-rabbit-remediation.yml diff --git a/.github/workflows/cl09-rabbit-remediation.yml b/.github/workflows/cl09-rabbit-remediation.yml deleted file mode 100644 index bd2320c32..000000000 --- a/.github/workflows/cl09-rabbit-remediation.yml +++ /dev/null @@ -1,209 +0,0 @@ -name: CL-09 review remediation - -on: - push: - branches: - - feat/cl-09-passive-production-evidence - -concurrency: - group: cl09-review-remediation-${{ github.ref }} - cancel-in-progress: true - -permissions: - contents: read - -jobs: - fix: - name: apply and validate CL-09 review fixes - runs-on: ubuntu-latest - timeout-minutes: 12 - permissions: - contents: write # Required only to publish the validated remediation commit. - steps: - - name: Checkout - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 - with: - fetch-depth: 0 - persist-credentials: false - - - name: Setup Bun - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2 - with: - bun-version: 1.3.14 - - - name: Apply focused review fixes - shell: bash - run: | - set -euo pipefail - python - <<'PY' - from pathlib import Path - - def replace_once(path: str, old: str, new: str) -> None: - p = Path(path) - text = p.read_text(encoding="utf-8") - count = text.count(old) - if count != 1: - raise SystemExit(f"{path}: expected exactly one match, found {count}: {old[:100]!r}") - p.write_text(text.replace(old, new, 1), encoding="utf-8") - - # 1. Propagate the selected config directory through the existing bounded usage reader. - replace_once( - "src/usage/log.ts", - 'export function usageLogPath(): string {\n return join(getConfigDir(), "usage.jsonl");\n}', - 'export function usageLogPath(configDir?: string): string {\n return join(configDir ?? getConfigDir(), "usage.jsonl");\n}', - ) - replace_once( - "src/usage/log.ts", - 'export function readRecentUsageEntries(limit: number): PersistedUsageEntry[] {\n if (!Number.isFinite(limit) || limit <= 0) return [];\n const path = usageLogPath();', - 'export function readRecentUsageEntries(limit: number, configDir?: string): PersistedUsageEntry[] {\n if (!Number.isFinite(limit) || limit <= 0) return [];\n const path = usageLogPath(configDir);', - ) - - # 2. Make passive-query truncation observable and exact. - passive = Path("src/lab/query/passive-production.ts") - text = passive.read_text(encoding="utf-8") - old = ''' const maxResults = boundedLimit(limit);\n const signals: PassiveRouteSignalV1[] = [];\n const scanRows = entries.slice(-PASSIVE_PRODUCTION_MAX_SCAN_ROWS);\n\n for (let rowIndex = scanRows.length - 1; rowIndex >= 0 && signals.length < maxResults; rowIndex--) {\n const entry = scanRows[rowIndex]!;\n const attempts = entry.attempts ?? [];\n for (let attemptIndex = attempts.length - 1; attemptIndex >= 0 && signals.length < maxResults; attemptIndex--) {\n const signal = signalFor(entry, attempts[attemptIndex]!);\n if (signal?.subjectId === subjectId) signals.push(signal);\n }\n }\n''' - new = ''' const maxResults = boundedLimit(limit);\n const signals: PassiveRouteSignalV1[] = [];\n // readRecentUsageEntries returns the selected append-only rows oldest-first.\n // Tail selection, reverse iteration, and signals[0] as the newest observation rely on this ordering.\n const scanRows = entries.slice(-PASSIVE_PRODUCTION_MAX_SCAN_ROWS);\n const scanTruncated = entries.length > PASSIVE_PRODUCTION_MAX_SCAN_ROWS;\n let resultTruncated = false;\n\n scan: for (let rowIndex = scanRows.length - 1; rowIndex >= 0; rowIndex--) {\n const entry = scanRows[rowIndex]!;\n const attempts = entry.attempts ?? [];\n for (let attemptIndex = attempts.length - 1; attemptIndex >= 0; attemptIndex--) {\n const signal = signalFor(entry, attempts[attemptIndex]!);\n if (signal?.subjectId !== subjectId) continue;\n if (signals.length >= maxResults) {\n resultTruncated = true;\n break scan;\n }\n signals.push(signal);\n }\n }\n''' - if text.count(old) != 1: - raise SystemExit("passive derivation loop changed; refusing broad edit") - text = text.replace(old, new, 1) - text = text.replace( - ' truncated: entries.length > PASSIVE_PRODUCTION_MAX_SCAN_ROWS || signals.length === maxResults,', - ' truncated: scanTruncated || resultTruncated,', - 1, - ) - old_query = '''export function queryPassiveProductionSignals(\n subjectId: string,\n limit?: number,\n): PassiveProductionQueryResultV1 {\n return derivePassiveProductionSignals(\n readRecentUsageEntries(PASSIVE_PRODUCTION_MAX_SCAN_ROWS),\n subjectId,\n limit,\n );\n}\n''' - new_query = '''export function queryPassiveProductionSignals(\n subjectId: string,\n limit?: number,\n configDir?: string,\n): PassiveProductionQueryResultV1 {\n return derivePassiveProductionSignals(\n // Read one row past the scan cap so the projection can distinguish an exact-cap history\n // from a history with older rows omitted by the bounded reader.\n readRecentUsageEntries(PASSIVE_PRODUCTION_MAX_SCAN_ROWS + 1, configDir),\n subjectId,\n limit,\n );\n}\n''' - if text.count(old_query) != 1: - raise SystemExit("passive query entry point changed; refusing broad edit") - passive.write_text(text.replace(old_query, new_query, 1), encoding="utf-8") - - replace_once( - "src/cli/lab.ts", - ' const result = queryPassiveProductionSignals(subjectId, limit);', - ' const result = queryPassiveProductionSignals(subjectId, limit, configDir);', - ) - - # 3. Memoize successful installation-salt reads on the production subject-link path. - salt = Path("src/lab/subject/installation-salt.ts") - text = salt.read_text(encoding="utf-8") - text = text.replace( - 'const SALT_BYTES = 32;\nconst UNSUPPORTED_DIRECTORY_FSYNC_CODES', - 'const SALT_BYTES = 32;\nconst installationSaltCache = new Map();\nconst UNSUPPORTED_DIRECTORY_FSYNC_CODES', - 1, - ) - old_existing = '''/** Read the existing local fingerprint salt without creating Lab state. */\nexport function readExistingInstallationSalt(configDir?: string): Uint8Array | null {\n const path = labInstallationSaltPath(configDir);\n try {\n return readSaltFile(path);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === "ENOENT") return null;\n throw error;\n }\n}\n''' - new_existing = '''function cacheSalt(path: string, salt: Uint8Array): Uint8Array {\n const cached = new Uint8Array(salt);\n installationSaltCache.set(path, cached);\n return new Uint8Array(cached);\n}\n\n/** Read the existing local fingerprint salt without creating Lab state. */\nexport function readExistingInstallationSalt(configDir?: string): Uint8Array | null {\n const path = labInstallationSaltPath(configDir);\n const cached = installationSaltCache.get(path);\n if (cached) return new Uint8Array(cached);\n try {\n return cacheSalt(path, readSaltFile(path));\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === "ENOENT") return null;\n throw error;\n }\n}\n''' - if text.count(old_existing) != 1: - raise SystemExit("installation-salt read path changed; refusing broad edit") - text = text.replace(old_existing, new_existing, 1) - text = text.replace(' try { return readSaltFile(path); }', ' try { return cacheSalt(path, readSaltFile(path)); }', 1) - text = text.replace(' return new Uint8Array(salt);', ' return cacheSalt(path, salt);', 1) - text = text.replace(' return readSaltFile(path);', ' return cacheSalt(path, readSaltFile(path));', 1) - salt.write_text(text, encoding="utf-8") - - # 4. Focused regressions for classification, truncation, and non-default config directories. - tests = Path("tests/lab-passive-production-evidence.test.ts") - text = tests.read_text(encoding="utf-8") - text = text.replace( - 'import { readFileSync } from "node:fs";\n', - 'import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";\nimport { tmpdir } from "node:os";\nimport { join } from "node:path";\n', - 1, - ) - text = text.replace( - ' derivePassiveProductionSignals,\n} from "../src/lab/query/passive-production";', - ' derivePassiveProductionSignals,\n queryPassiveProductionSignals,\n} from "../src/lab/query/passive-production";', - 1, - ) - anchor = ''' test("bounds result count and scanned source rows", () => {\n''' - additions = ''' test("classifies cancellation, environmental failures, and route errors independently", () => {\n const subjectId = "4".repeat(64);\n const cancelled = usageEntryWithAttempt({ labRouteSubjectId: subjectId, status: 499 });\n cancelled.status = 499;\n cancelled.closeReason = "client_cancel";\n\n const environmental = usageEntryWithAttempt({\n labRouteSubjectId: subjectId,\n status: 429,\n errorCode: "rate_limit_error",\n });\n environmental.requestId = "ocx-cl09-environmental";\n environmental.status = 429;\n\n const routeError = usageEntryWithAttempt({\n labRouteSubjectId: subjectId,\n status: 502,\n errorCode: "upstream_error",\n });\n routeError.requestId = "ocx-cl09-route-error";\n routeError.status = 502;\n\n const result = derivePassiveProductionSignals([cancelled, environmental, routeError], subjectId);\n\n expect(result.signals.map(signal => signal.outcome).sort()).toEqual([\n "client_cancel",\n "environmental",\n "route_error",\n ]);\n expect(result.summary.recentRouteErrorSignals).toBe(1);\n });\n\n test("reports result truncation only when another matching signal exists", () => {\n const subjectId = "5".repeat(64);\n const entries = Array.from({ length: 3 }, (_, index) => {\n const entry = usageEntryWithAttempt({ labRouteSubjectId: subjectId });\n entry.requestId = `ocx-cl09-limit-${index}`;\n entry.timestamp = index;\n return entry;\n });\n\n expect(derivePassiveProductionSignals(entries.slice(0, 2), subjectId, 2).truncated).toBe(false);\n expect(derivePassiveProductionSignals(entries, subjectId, 2).truncated).toBe(true);\n });\n\n test("uses the selected config directory and detects scan overflow", () => {\n const configDir = mkdtempSync(join(tmpdir(), "ocx-cl09-passive-"));\n try {\n const subjectId = "6".repeat(64);\n const otherSubjectId = "7".repeat(64);\n const entries = Array.from({ length: PASSIVE_PRODUCTION_MAX_SCAN_ROWS + 1 }, (_, index) => {\n const entry = usageEntryWithAttempt({\n labRouteSubjectId: index === PASSIVE_PRODUCTION_MAX_SCAN_ROWS ? subjectId : otherSubjectId,\n });\n entry.requestId = `ocx-cl09-config-${index}`;\n entry.timestamp = index;\n return entry;\n });\n writeFileSync(join(configDir, "usage.jsonl"), `${entries.map(entry => JSON.stringify(entry)).join("\\n")}\\n`);\n\n const result = queryPassiveProductionSignals(subjectId, 10, configDir);\n\n expect(result.signals).toHaveLength(1);\n expect(result.scannedRows).toBe(PASSIVE_PRODUCTION_MAX_SCAN_ROWS);\n expect(result.truncated).toBe(true);\n expect(result.signals[0]?.requestRef).toBe(`ocx-cl09-config-${PASSIVE_PRODUCTION_MAX_SCAN_ROWS}`);\n } finally {\n rmSync(configDir, { recursive: true, force: true });\n }\n });\n\n''' - if text.count(anchor) != 1: - raise SystemExit("passive test anchor changed; refusing broad edit") - text = text.replace(anchor, additions + anchor, 1) - text = text.replace( - ' expect(source).not.toContain("readRecentUsageEntries");\n });', - ' expect(source).not.toContain("readRecentUsageEntries");\n const cliSource = readFileSync("src/cli/lab.ts", "utf8");\n expect(cliSource).toContain("queryPassiveProductionSignals(subjectId, limit, configDir)");\n });', - 1, - ) - tests.write_text(text, encoding="utf-8") - - # 5. Reconcile the authoritative CL-09 lifecycle records with the implemented PR. - status = Path("devlog/_plan/260807_compatibility_lab/001_pr_stack_status.md") - text = status.read_text(encoding="utf-8") - old_row = '| CL-09 | `feat/cl-09-passive-production-evidence` | `3b8f9487676fe258d76295e49e7db75aca26a4cb` | CONTRACT DRAFT | [#1489](https://github.com/lidge-jun/opencodex/pull/1489) | CONTRACT REVIEW; runtime implementation not authorized |' - new_row = '| CL-09 | `feat/cl-09-passive-production-evidence` | `3b8f9487676fe258d76295e49e7db75aca26a4cb` | IMPLEMENTATION REVIEW CANDIDATE | [#1489](https://github.com/lidge-jun/opencodex/pull/1489) | IMPLEMENTED; independent final review and merge acceptance pending |' - if text.count(old_row) != 1: - raise SystemExit("CL-09 phase row changed; refusing broad edit") - text = text.replace(old_row, new_row, 1) - old_auth = '- CL-09: **CONTRACT DRAFT** via [#1489](https://github.com/lidge-jun/opencodex/pull/1489), based exactly on CL-08 merge `3b8f9487676fe258d76295e49e7db75aca26a4cb`; runtime implementation is not authorized until contract review.' - new_auth = '- CL-09: **IMPLEMENTED / REVIEW PENDING** via [#1489](https://github.com/lidge-jun/opencodex/pull/1489); the phase started from CL-08 merge `3b8f9487676fe258d76295e49e7db75aca26a4cb`, runtime work was rebased to then-current `dev@e8db4e0365b12a314d1c08ec2cf81599efe5b2d3`, and independent final review plus merge acceptance remain pending.' - if text.count(old_auth) != 1: - raise SystemExit("CL-09 authorization entry changed; refusing broad edit") - status.write_text(text.replace(old_auth, new_auth, 1), encoding="utf-8") - - contract = Path("devlog/_plan/260807_compatibility_lab/009_cl09_passive_production_evidence.md") - text = contract.read_text(encoding="utf-8") - text = text.replace( - 'The CL-09 contract was frozen first on this branch. Runtime implementation now proceeds under this contract and remains subject to independent final review before merge.', - 'The CL-09 contract was frozen first on this branch. Runtime implementation is now complete on this branch and remains subject to independent final review before merge.', - 1, - ) - text = text.replace( - 'No runtime implementation.\n\n## CL-09.1 - Exact attempt subject linkage', - 'No runtime implementation was part of CL-09.0; CL-09.1 through CL-09.4 are implemented later on this PR.\n\n## CL-09.1 - Exact attempt subject linkage', - 1, - ) - contract.write_text(text, encoding="utf-8") - - plan = Path("docs/superpowers/plans/2026-08-12-cl09-passive-production-evidence.md") - text = plan.read_text(encoding="utf-8") - text = text.replace( - '**Current base:** `dev@e8db4e0365b12a314d1c08ec2cf81599efe5b2d3`', - '**Current base:** `dev@e8db4e0365b12a314d1c08ec2cf81599efe5b2d3`, a later `dev` descendant of the CL-08 merge `3b8f9487676fe258d76295e49e7db75aca26a4cb` used as the implementation snapshot.', - 1, - ) - text = text.replace( - 'PR #1489 remains draft and must not be merged before independent final review.', - 'PR #1489 remains open and must not be merged before independent final review.', - 1, - ) - plan.write_text(text, encoding="utf-8") - PY - - - name: Install dependencies - shell: bash - run: | - set -euo pipefail - bun install --frozen-lockfile - cd gui - bun install --frozen-lockfile - - - name: Validate focused remediation - shell: bash - run: | - set -euo pipefail - bun test tests/lab-passive-production-evidence.test.ts tests/usage-log.test.ts - bun x tsc --noEmit - bun run privacy:scan - git diff --check - - - name: Commit fixes and remove helper workflow - env: - GH_TOKEN: ${{ github.token }} - shell: bash - run: | - set -euo pipefail - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git add \ - src/usage/log.ts \ - src/lab/query/passive-production.ts \ - src/cli/lab.ts \ - src/lab/subject/installation-salt.ts \ - tests/lab-passive-production-evidence.test.ts \ - devlog/_plan/260807_compatibility_lab/001_pr_stack_status.md \ - devlog/_plan/260807_compatibility_lab/009_cl09_passive_production_evidence.md \ - docs/superpowers/plans/2026-08-12-cl09-passive-production-evidence.md - git rm .github/workflows/cl09-rabbit-remediation.yml - git diff --cached --check - git commit -m "fix(lab): address CL-09 review findings" - git push "https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" HEAD:${GITHUB_REF_NAME} diff --git a/devlog/_plan/260807_compatibility_lab/001_pr_stack_status.md b/devlog/_plan/260807_compatibility_lab/001_pr_stack_status.md index 70b892a40..e74d1d9b5 100644 --- a/devlog/_plan/260807_compatibility_lab/001_pr_stack_status.md +++ b/devlog/_plan/260807_compatibility_lab/001_pr_stack_status.md @@ -28,7 +28,7 @@ independent review, blockers, and whether a later phase is authorized. | CL-06 | `feat/cl-06-routing-profile-compatibility` | `1072b9c39c48a4982229131613ac300560740742` | `b96eae83f2a6d1654472aeeef84799070743aeb8` | [#1394](https://github.com/lidge-jun/opencodex/pull/1394) | MERGED TO `dev` at `b66e33ce7207d91014644d99317e456c992a3418`; ACCEPTED/CLOSED | | CL-07 | `feat/cl-07-task-effectiveness-producer` | `b66e33ce7207d91014644d99317e456c992a3418` | `0efe2c69514d3baefee686383fe740e4ecb37d83` | [#1438](https://github.com/lidge-jun/opencodex/pull/1438) | MERGED TO `dev` at `02e62fc8c7354c544ef71f8bb3db5ebba42cb600`; ACCEPTED/CLOSED | | CL-08 | `feat/cl-08-lab-automation` | `da8ebd3135553c1d4dd85c1f258e998a5de14f28` | `bfaad5d01a975e8d48b9437bc0a0537077a04134` | [#1447](https://github.com/lidge-jun/opencodex/pull/1447) | MERGED TO `dev` at `3b8f9487676fe258d76295e49e7db75aca26a4cb`; ACCEPTED/CLOSED | -| CL-09 | `feat/cl-09-passive-production-evidence` | `3b8f9487676fe258d76295e49e7db75aca26a4cb` | CONTRACT DRAFT | [#1489](https://github.com/lidge-jun/opencodex/pull/1489) | CONTRACT REVIEW; runtime implementation not authorized | +| CL-09 | `feat/cl-09-passive-production-evidence` | `3b8f9487676fe258d76295e49e7db75aca26a4cb` | IMPLEMENTATION REVIEW CANDIDATE | [#1489](https://github.com/lidge-jun/opencodex/pull/1489) | IMPLEMENTED; independent final review and merge acceptance pending | The CL-01 starting SHA is the exact CL-00 tip recorded when CL-01 began. Its moving base-ref name is not a substitute for that historical SHA. @@ -168,7 +168,7 @@ Claims cannot produce `PROBED`/`VERIFIED`. - CL-06: **ACCEPTED/CLOSED** via [#1394](https://github.com/lidge-jun/opencodex/pull/1394), merged to `dev` at `b66e33ce7207d91014644d99317e456c992a3418`. - CL-07: **ACCEPTED/CLOSED** via [#1438](https://github.com/lidge-jun/opencodex/pull/1438), merged to `dev` at `02e62fc8c7354c544ef71f8bb3db5ebba42cb600`; accepted head `0efe2c69514d3baefee686383fe740e4ecb37d83`; plan `007_cl07_task_effectiveness.md`. - CL-08: **ACCEPTED/CLOSED** via [#1447](https://github.com/lidge-jun/opencodex/pull/1447), merged to `dev` at `3b8f9487676fe258d76295e49e7db75aca26a4cb`; final source head `bfaad5d01a975e8d48b9437bc0a0537077a04134`; plan `008_cl08_automation.md`. -- CL-09: **CONTRACT DRAFT** via [#1489](https://github.com/lidge-jun/opencodex/pull/1489), based exactly on CL-08 merge `3b8f9487676fe258d76295e49e7db75aca26a4cb`; runtime implementation is not authorized until contract review. +- CL-09: **IMPLEMENTED / REVIEW PENDING** via [#1489](https://github.com/lidge-jun/opencodex/pull/1489); the phase started from CL-08 merge `3b8f9487676fe258d76295e49e7db75aca26a4cb`, runtime work was rebased to then-current `dev@e8db4e0365b12a314d1c08ec2cf81599efe5b2d3`, and independent final review plus merge acceptance remain pending. ## CL-06 closure log diff --git a/devlog/_plan/260807_compatibility_lab/009_cl09_passive_production_evidence.md b/devlog/_plan/260807_compatibility_lab/009_cl09_passive_production_evidence.md index 324ad4dfc..85a924441 100644 --- a/devlog/_plan/260807_compatibility_lab/009_cl09_passive_production_evidence.md +++ b/devlog/_plan/260807_compatibility_lab/009_cl09_passive_production_evidence.md @@ -10,7 +10,7 @@ CL-08 is merged and closed. This document defines the next Compatibility Lab boundary. -The CL-09 contract was frozen first on this branch. Runtime implementation now proceeds under this contract and remains subject to independent final review before merge. +The CL-09 contract was frozen first on this branch. Runtime implementation is now complete on this branch and remains subject to independent final review before merge. --- @@ -457,7 +457,7 @@ Completed first on this PR: - freeze passive evidence semantics; - explicitly reject duplicate shadow execution and direct verdict promotion. -No runtime implementation. +No runtime implementation was part of CL-09.0; CL-09.1 through CL-09.4 are implemented later on this PR. ## CL-09.1 - Exact attempt subject linkage diff --git a/docs/superpowers/plans/2026-08-12-cl09-passive-production-evidence.md b/docs/superpowers/plans/2026-08-12-cl09-passive-production-evidence.md index f2677b2ee..7ff499659 100644 --- a/docs/superpowers/plans/2026-08-12-cl09-passive-production-evidence.md +++ b/docs/superpowers/plans/2026-08-12-cl09-passive-production-evidence.md @@ -4,7 +4,7 @@ **Authority:** `devlog/_plan/260807_compatibility_lab/009_cl09_passive_production_evidence.md` -**Current base:** `dev@e8db4e0365b12a314d1c08ec2cf81599efe5b2d3` +**Current base:** `dev@e8db4e0365b12a314d1c08ec2cf81599efe5b2d3`, a later `dev` descendant of the CL-08 merge `3b8f9487676fe258d76295e49e7db75aca26a4cb` used as the implementation snapshot. ## Task 1 - Exact production-attempt subject linkage @@ -67,4 +67,4 @@ Tasks 1-4 are implemented on the PR branch. The implementation uses the existing exact `RouteSubjectV1` identity per real attempt, a bounded read-only production-signal adapter over `usage.jsonl`, and additive Lab API/CLI/Compatibility Matrix read surfaces. Focused CL-09 tests cover legacy/malformed linkage, exact fallback attribution, bounded scans/results, strict data minimization with privacy canaries, source-retention behavior, and static no-feedback guards for routing and CL-08 planning. -The final verification gate is intentionally not marked complete here until the exact final head passes required CI/review checks. PR #1489 remains draft and must not be merged before independent final review. +The final verification gate is intentionally not marked complete here until the exact final head passes required CI/review checks. PR #1489 remains open and must not be merged before independent final review. diff --git a/src/cli/lab.ts b/src/cli/lab.ts index fffccb0e2..013b96327 100644 --- a/src/cli/lab.ts +++ b/src/cli/lab.ts @@ -243,7 +243,7 @@ export async function handleLabCommand(argv: string[], deps: LabCliDeps = {}): P const limit = takeIntegerOption(rest, "--limit", { min: 1 }); rejectArgs(rest, USAGE); if (!subjectId) throw new CliUsageError("--subject is required", USAGE); - const result = queryPassiveProductionSignals(subjectId, limit); + const result = queryPassiveProductionSignals(subjectId, limit, configDir); printData(result, wantsJson, passiveProductionLines(result)); return; } diff --git a/src/lab/query/passive-production.ts b/src/lab/query/passive-production.ts index 071e488cc..e1c077003 100644 --- a/src/lab/query/passive-production.ts +++ b/src/lab/query/passive-production.ts @@ -103,14 +103,23 @@ export function derivePassiveProductionSignals( if (!isLabRouteSubjectId(subjectId)) throw new Error("invalid passive production subject id"); const maxResults = boundedLimit(limit); const signals: PassiveRouteSignalV1[] = []; + // readRecentUsageEntries returns the selected append-only rows oldest-first. + // Tail selection, reverse iteration, and signals[0] as the newest observation rely on this ordering. const scanRows = entries.slice(-PASSIVE_PRODUCTION_MAX_SCAN_ROWS); + const scanTruncated = entries.length > PASSIVE_PRODUCTION_MAX_SCAN_ROWS; + let resultTruncated = false; - for (let rowIndex = scanRows.length - 1; rowIndex >= 0 && signals.length < maxResults; rowIndex--) { + scan: for (let rowIndex = scanRows.length - 1; rowIndex >= 0; rowIndex--) { const entry = scanRows[rowIndex]!; const attempts = entry.attempts ?? []; - for (let attemptIndex = attempts.length - 1; attemptIndex >= 0 && signals.length < maxResults; attemptIndex--) { + for (let attemptIndex = attempts.length - 1; attemptIndex >= 0; attemptIndex--) { const signal = signalFor(entry, attempts[attemptIndex]!); - if (signal?.subjectId === subjectId) signals.push(signal); + if (signal?.subjectId !== subjectId) continue; + if (signals.length >= maxResults) { + resultTruncated = true; + break scan; + } + signals.push(signal); } } @@ -130,7 +139,7 @@ export function derivePassiveProductionSignals( }, signals, scannedRows: scanRows.length, - truncated: entries.length > PASSIVE_PRODUCTION_MAX_SCAN_ROWS || signals.length === maxResults, + truncated: scanTruncated || resultTruncated, }; } @@ -138,9 +147,12 @@ export function derivePassiveProductionSignals( export function queryPassiveProductionSignals( subjectId: string, limit?: number, + configDir?: string, ): PassiveProductionQueryResultV1 { return derivePassiveProductionSignals( - readRecentUsageEntries(PASSIVE_PRODUCTION_MAX_SCAN_ROWS), + // Read one row past the scan cap so the projection can distinguish an exact-cap history + // from a history with older rows omitted by the bounded reader. + readRecentUsageEntries(PASSIVE_PRODUCTION_MAX_SCAN_ROWS + 1, configDir), subjectId, limit, ); diff --git a/src/lab/subject/installation-salt.ts b/src/lab/subject/installation-salt.ts index 16686ba83..dec715b77 100644 --- a/src/lab/subject/installation-salt.ts +++ b/src/lab/subject/installation-salt.ts @@ -4,6 +4,7 @@ import { dirname } from "node:path"; import { labInstallationSaltPath, labRoot } from "../paths"; const SALT_BYTES = 32; +const installationSaltCache = new Map(); const UNSUPPORTED_DIRECTORY_FSYNC_CODES = new Set(["EINVAL", "ENOTSUP", "EOPNOTSUPP", "ENOSYS"]); function readSaltFile(path: string): Uint8Array { @@ -14,11 +15,19 @@ function readSaltFile(path: string): Uint8Array { return new Uint8Array(bytes); } +function cacheSalt(path: string, salt: Uint8Array): Uint8Array { + const cached = new Uint8Array(salt); + installationSaltCache.set(path, cached); + return new Uint8Array(cached); +} + /** Read the existing local fingerprint salt without creating Lab state. */ export function readExistingInstallationSalt(configDir?: string): Uint8Array | null { const path = labInstallationSaltPath(configDir); + const cached = installationSaltCache.get(path); + if (cached) return new Uint8Array(cached); try { - return readSaltFile(path); + return cacheSalt(path, readSaltFile(path)); } catch (error) { if ((error as NodeJS.ErrnoException).code === "ENOENT") return null; throw error; @@ -56,7 +65,7 @@ export function readInstallationSalt(configDir?: string): Uint8Array { const root = labRoot(configDir); mkdirSync(root, { recursive: true, mode: 0o700 }); - try { return readSaltFile(path); } + try { return cacheSalt(path, readSaltFile(path)); } catch (error) { if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error; } @@ -78,10 +87,10 @@ export function readInstallationSalt(configDir?: string): Uint8Array { linkSync(stagingPath, path); try { fsyncDirectory(dirname(path)); } catch { throw new Error("harness_failure: installation salt directory fsync failed"); } - return new Uint8Array(salt); + return cacheSalt(path, salt); } catch (error) { if ((error as NodeJS.ErrnoException).code !== "EEXIST") throw error; - return readSaltFile(path); + return cacheSalt(path, readSaltFile(path)); } } finally { if (fd !== undefined) { diff --git a/src/usage/log.ts b/src/usage/log.ts index 5f8ac1807..00b750ad0 100644 --- a/src/usage/log.ts +++ b/src/usage/log.ts @@ -132,8 +132,8 @@ export function isKnownInboundProtocol(value: unknown): value is NonNullable); } -export function usageLogPath(): string { - return join(getConfigDir(), "usage.jsonl"); +export function usageLogPath(configDir?: string): string { + return join(configDir ?? getConfigDir(), "usage.jsonl"); } export function usageTotalTokens(usage: OcxUsage | undefined): number | undefined { @@ -657,9 +657,9 @@ function parseUsageLines(lines: string[]): PersistedUsageEntry[] { * Read only the newest `limit` usage.jsonl rows without loading the whole append-only * file into memory. Used by request-log hydration on `ocx start`. */ -export function readRecentUsageEntries(limit: number): PersistedUsageEntry[] { +export function readRecentUsageEntries(limit: number, configDir?: string): PersistedUsageEntry[] { if (!Number.isFinite(limit) || limit <= 0) return []; - const path = usageLogPath(); + const path = usageLogPath(configDir); if (!existsSync(path)) return []; let fd: number | undefined; try { diff --git a/tests/lab-passive-production-evidence.test.ts b/tests/lab-passive-production-evidence.test.ts index a99bcb908..8e7921a46 100644 --- a/tests/lab-passive-production-evidence.test.ts +++ b/tests/lab-passive-production-evidence.test.ts @@ -1,4 +1,6 @@ -import { readFileSync } from "node:fs"; +import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; import { describe, expect, test } from "bun:test"; import { normalizeUsageEntryForTest, @@ -9,6 +11,7 @@ import { PASSIVE_PRODUCTION_MAX_LIMIT, PASSIVE_PRODUCTION_MAX_SCAN_ROWS, derivePassiveProductionSignals, + queryPassiveProductionSignals, } from "../src/lab/query/passive-production"; function usageEntryWithAttempt(attempt: Record): PersistedUsageEntry { @@ -116,6 +119,77 @@ describe("CL-09 bounded passive production projection", () => { expect(result.summary.recentRouteErrorSignals).toBe(0); }); + test("classifies cancellation, environmental failures, and route errors independently", () => { + const subjectId = "4".repeat(64); + const cancelled = usageEntryWithAttempt({ labRouteSubjectId: subjectId, status: 499 }); + cancelled.status = 499; + cancelled.closeReason = "client_cancel"; + + const environmental = usageEntryWithAttempt({ + labRouteSubjectId: subjectId, + status: 429, + errorCode: "rate_limit_error", + }); + environmental.requestId = "ocx-cl09-environmental"; + environmental.status = 429; + + const routeError = usageEntryWithAttempt({ + labRouteSubjectId: subjectId, + status: 502, + errorCode: "upstream_error", + }); + routeError.requestId = "ocx-cl09-route-error"; + routeError.status = 502; + + const result = derivePassiveProductionSignals([cancelled, environmental, routeError], subjectId); + + expect(result.signals.map(signal => signal.outcome).sort()).toEqual([ + "client_cancel", + "environmental", + "route_error", + ]); + expect(result.summary.recentRouteErrorSignals).toBe(1); + }); + + test("reports result truncation only when another matching signal exists", () => { + const subjectId = "5".repeat(64); + const entries = Array.from({ length: 3 }, (_, index) => { + const entry = usageEntryWithAttempt({ labRouteSubjectId: subjectId }); + entry.requestId = `ocx-cl09-limit-${index}`; + entry.timestamp = index; + return entry; + }); + + expect(derivePassiveProductionSignals(entries.slice(0, 2), subjectId, 2).truncated).toBe(false); + expect(derivePassiveProductionSignals(entries, subjectId, 2).truncated).toBe(true); + }); + + test("uses the selected config directory and detects scan overflow", () => { + const configDir = mkdtempSync(join(tmpdir(), "ocx-cl09-passive-")); + try { + const subjectId = "6".repeat(64); + const otherSubjectId = "7".repeat(64); + const entries = Array.from({ length: PASSIVE_PRODUCTION_MAX_SCAN_ROWS + 1 }, (_, index) => { + const entry = usageEntryWithAttempt({ + labRouteSubjectId: index === PASSIVE_PRODUCTION_MAX_SCAN_ROWS ? subjectId : otherSubjectId, + }); + entry.requestId = `ocx-cl09-config-${index}`; + entry.timestamp = index; + return entry; + }); + writeFileSync(join(configDir, "usage.jsonl"), `${entries.map(entry => JSON.stringify(entry)).join("\n")}\n`); + + const result = queryPassiveProductionSignals(subjectId, 10, configDir); + + expect(result.signals).toHaveLength(1); + expect(result.scannedRows).toBe(PASSIVE_PRODUCTION_MAX_SCAN_ROWS); + expect(result.truncated).toBe(true); + expect(result.signals[0]?.requestRef).toBe(`ocx-cl09-config-${PASSIVE_PRODUCTION_MAX_SCAN_ROWS}`); + } finally { + rmSync(configDir, { recursive: true, force: true }); + } + }); + test("bounds result count and scanned source rows", () => { const subjectId = "d".repeat(64); const entries = Array.from({ length: PASSIVE_PRODUCTION_MAX_SCAN_ROWS + 25 }, (_, index) => { @@ -200,6 +274,8 @@ describe("CL-09 no-feedback architecture guards", () => { expect(source).toContain("resolveProductionRouteSubject"); expect(source).not.toContain("queryPassiveProductionSignals"); expect(source).not.toContain("readRecentUsageEntries"); + const cliSource = readFileSync("src/cli/lab.ts", "utf8"); + expect(cliSource).toContain("queryPassiveProductionSignals(subjectId, limit, configDir)"); }); test("passive query remains read-side and cannot create Lab execution or evidence", () => {