Skip to content

feat(F257): #2 L系列段观测粒度 — native-L0 L1-L7 trace from the actual L0 compiler manifest - #49

Merged
mindfn merged 4 commits into
develop_basefrom
feat/f257-lseries-trace
Jul 21, 2026
Merged

feat(F257): #2 L系列段观测粒度 — native-L0 L1-L7 trace from the actual L0 compiler manifest#49
mindfn merged 4 commits into
develop_basefrom
feat/f257-lseries-trace

Conversation

@mindfn

@mindfn mindfn commented Jul 21, 2026

Copy link
Copy Markdown
Owner

What

F257 修复清单 #2 — L 系列段观测粒度. Native-L0 cats (Claude/Codex/OpenCode) had a permanently blank segment lifeline: their L1-L7 governance identity is delivered via the compiled native prompt, so the session hook pipeline never ran and L1-L7 were never observed. This sources the injection trace for L1-L7 from the actual L0 compiler artifact and persists it per-segment.

Design (sourced from the real compile boundary)

  • compile-system-prompt-l0.mjs: compileL0WithManifest() captures each L1-L7 section at the one loop where per-segment content still exists (before flatten + label-strip) + a new --manifest-out CLI flag (orthogonal to --out).
  • l0-compiler.ts: l0ManifestCache populated in LOCKSTEP with l0Cache inside doCompileL0 (same subprocess / generation guard / cleared together); getL0ManifestViaSubprocess() is cache-first and, cold, shares the provider's own compile via in-flight dedup — no redundant work.
  • l0-manifest-trace.ts: atomic validation (exactly canonical L1-L7, non-blank, no foreign/duplicate/reordered) → adapt to a session PipelineResult → existing buildFromPipeline bridge with delivery channel native-l0 (pack-only stays for actual pack blocks). Any violation → visible producer-failure path, never a partial "healthy" trace.
  • native-l0-trace.ts: single shared, fire-and-forget producer seam called by both routes (never a pre-invoke tax).

Cross-family review (缅因猫 sol)

R1 (parallel-producer rejected → compiler-manifest) → R2 (atomic validation + real-CLI test + route-seam) → R3 (test rigor) → R4 APPROVE @ 724885886 (0 P1/P2/P3).

Evidence

  • Full api gate (pnpm --dir packages/api test): 19148 pass / 69 fail — all 69 pre-existing develop_base baseline (identical count to feat(#476): unify callback auth — headers replace body/query #3's gate; failures in signal-fetcher/capabilities/audit-cc/F188/CLAUDE.md/Redis-timeout suites, categorically unrelated). All F257 feat(#476): unify callback auth — headers replace body/query #2 suites pass in-gate. Zero new failures.
  • New/updated: f257-lseries-trace 14/0 (incl. 6 atomic-reject cases), f257-l0-manifest 5/0, f257-l0-manifest-cli 2/0 (real compiler byte-for-byte + isolated profile), f257-route-seam 4/0 (both routes + non-native control). Regression trace/l0/lifeline green. Biome clean.

🤖 Generated with Claude Code

mindfn and others added 4 commits July 21, 2026 14:58
…e path

## What
Make L1-L7 (governance-core session identity) observable per-segment for
native-L0 cats (Claude/Codex/OpenCode), so the Console segment lifeline is no
longer blank for them.

## Why (root cause — verified by reading, not the plan's guess)
The slice-2 plan located the fix at trace-collector.ts:116-129 (the
`session-init-pack-only` aggregate branch). Investigation of the actual call
graph proved that branch is DEAD CODE for native-L0:
- native-L0 builds session identity via buildStaticIdentityPackOnly() which
  does NOT run the hook pipeline → drainCapturedTraces().session is null.
- route → buildFromPipeline(null, turnResult, …): trace-bridge.ts:50 returns
  non-null (turnResult/D-hooks present) with an EMPTY session-segment array →
  the collectTrace fallback (incl. pack-only branch) is never reached.
- Net: native-L0 traces carry only D-series per-turn segments; L1-L7 never
  become ObservedSegments → segment-lifeline queries return nothing.

## Fix (bridge path, single-source)
collectNativeL0SessionTrace() runs the session-init pipeline in trace-only mode
(prompt discarded — the native --system-prompt-file is authoritative) and
returns the L-scoped PipelineResult. The routes feed it as buildFromPipeline's
sessionResult for native-L0 only (non-native path unchanged). eventsToSegments
already maps L1-L7 → ObservedSegment (id/version/fired/hash/tokens); zero bridge
or downstream-consumer changes. Respects the override snapshot already refreshed
by the routes (refreshOverrideSnapshot precedes the trace block).

## Tradeoff (disclosed for review)
Adds one session-init pipeline run per native-L0 turn, but only inside the
fire-and-forget trace block (after prompt assembly; never on the model-call
critical path) and only when a trace store is present. L-series is deterministic
per cat, so a per-cat cache (invalidate on OverrideChangeEvent) is the follow-up
optimization (KD-15 "L0 编译链 + cache invalidation" runway). Fail-open: returns
null on error → degrades to prior D-only behavior, never breaks invocation.

## Non-goal
Pack-block observation (buildStaticIdentityPackOnly output has no hookIds) stays
out of scope; #2 is L-series. Delivery channel left 'pack-only' (existing
contract, trace-bridge.test.js:155) — lifeline/judgment read segments, not channel.

## Evidence
- red→green: pre-fix native-L0 = 0 observable L segments; post-fix = 7 (L1-L7).
- new test f257-lseries-trace.test.js 4/4 (unit + bridge map + §16e store→lifeline
  reachability + registry metadata).
- tsc clean; zero new biome warnings (count parity vs develop_base baseline).
- affected suites green: trace-bridge/injection-trace-store/segment-lifeline/
  l0-pipeline-equivalence/hook-pipeline/segment-judgment-engine(+harness-eval)/
  segment-lifeline-chain/dual-path/transport-boundary/hook-pipeline-integration/
  hook-registry/system-prompt-builder.
- agent-router 89/11 and prompt-segments-eval-domain 11/4 failures proven
  pre-existing via git-stash baseline parity (identical names before/after).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…nifest

Reworks the rejected parallel producer per sol 2b R1 (all four findings).

## P1-2 (root) — source the trace from the ACTUAL compiled artifact, not a reconstruction
Deleted collectNativeL0SessionTrace (reran the API hook pipeline — a separate,
override-respecting code path that could report L content the override-blind native
compiler never delivered). Instead:
- scripts/compile-system-prompt-l0.mjs: compileL0WithManifest() captures each L1-L7
  section at the one loop where per-segment content still exists (before flattening +
  label-stripping) + new --manifest-out CLI flag (orthogonal to --out; stdout still
  carries the prompt for Codex/OpenCode).
- l0-compiler.ts: l0ManifestCache populated in LOCKSTEP with l0Cache inside doCompileL0
  (same subprocess, same generation guard, cleared together); getL0ManifestViaSubprocess()
  is cache-first and, on a cold cache, shares the provider's own compile via in-flight
  dedup — no redundant full-stage run. Manifest read is best-effort (never fails the
  fail-closed string compile).
- l0-manifest-trace.ts: adapts the manifest → session PipelineResult (hash/token via the
  existing conventions; version from the hook registry) → bridged through buildFromPipeline.
Verified end-to-end: real mjs emits all 7 L segments with byte-accurate content
(L4 = "1. **Runtime data safety**…").

## P1-1 — native-l0 delivery channel (not pack-only)
trace-bridge buildDelivery emits channel 'native-l0' when the session came from the
compiler manifest (new meta.sessionFromNativeCompiler); 'pack-only' stays correct for
actual pack blocks (no global relabel — trace-bridge.test.js:155 still green). Test asserts it.

## P2-1 — not a pre-invoke tax + visible failure
native-l0-trace.ts persistNativeL0SessionTrace() is called fire-and-forget (`void …`) from
both routes, so it never blocks the model critical path; the redundant full-stage run is
gone (cache-first manifest). An empty manifest emits a visible producer warning instead of
silently persisting D-only — tested (warn fired, zero fabricated L segments).

## P2-2 — one producer seam (de-duplicated), unit-tested
Wiring centralized in persistNativeL0SessionTrace (was duplicated in two large route
functions); both routes call the single shared helper. Seam unit-tested (success + failure).
NOTE: a full routeSerial/routeParallel driver integration test is not yet added — flagged.

## Evidence
- f257-lseries-trace 6/0; f257-l0-manifest 5/0; l0-compiler 17/0 (updated for --manifest-out).
- Regression green: trace-bridge / segment-lifeline / injection-trace-store / hook-pipeline /
  l0-pipeline-equivalence / system-prompt-builder / segment-judgment-engine /
  transport-boundary-l0-equivalence / codex+claude-bg+opencode L0.
- Real mjs manifest smoke: 7 segments byte-accurate. tsc clean; biome changed files clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…-seam tests

Addresses sol's 2b R2 REQUEST CHANGES (all three; R1's four remain CLOSED).

## P1-1 (blocking) — partial/foreign/blank manifest was recorded as healthy fired data
The row-filter (`readL0Manifest`) + "non-empty ⇒ success" adapter marked every row `fired`,
so `[{L1:'only-one'}]` persisted 1 native-l0 segment with NO warning (L2-L7 silently gone),
and `[{L4:''}]` counted an empty iron-law segment as injected — the original incident in
partial form. Now `validateL0Manifest()` validates the manifest as ONE atomic artifact:
exactly L1-L7 in canonical order, non-blank content, no foreign/duplicate/reordered rows.
ANY violation → `l0ManifestToSessionResult` returns null → the visible producer-failure path
(warning with the specific reason), never a partial success. red→green: missing / foreign /
duplicate / blank-content / reordered all reject.

## P2-1 — no automated real-compiler regression
Added `f257-l0-manifest-cli.test.js` (no fake spawn): invokes the real `compileL0WithManifest`
+ the actual CLI, proving exactly L1-L7 and each manifest content byte-for-byte in the
compiled prompt, and `--manifest-out` orthogonal to `--out` (file + stdout modes). If the
producer stops writing or diverges, this goes red instead of the fake-spawn tests staying green.

## P2-2 — route-seam evidence (R1 gap now filled)
Added `f257-route-seam.test.js`: drives the REAL route-serial + route-parallel with a
bootstrapped fake trace store + prewarmed manifest cache (sol's recipe, no full-suite driver).
Native-L0 service → persisted L1-L7 with channel `native-l0` (both routes); non-native control
→ no compiler L segments, not native-l0 (existing path unchanged).

## Evidence
- f257-lseries-trace 14/0 (incl. 6 atomic-reject cases + empty/partial seam-failure),
  f257-l0-manifest 5/0, f257-l0-manifest-cli 2/0, f257-route-seam 4/0.
- Regression green: trace-bridge, l0-compiler, l0-pipeline-equivalence, segment-lifeline,
  injection-trace-store, hook-pipeline, segment-judgment-engine. tsc + biome clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…file isolation

Addresses sol's 2b R3 REQUEST CHANGES (both test-proof/isolation gaps; production code
CLOSED, no correctness finding).

## P2-1 — non-native route control passed vacuously on empty results
The non-native cases only asserted "no summary uses native-l0" + "no summary has L
segments" — both true when summaries=[] (e.g. if the non-native persistence path were
deleted/broken), so it did not prove "existing non-native path unchanged". Now REQUIRE a
persisted summary first, then assert the expected non-native session channel
(message-prepend) + an existing pipeline S/D segment with a real observed/absent status.
Kept for both serial and parallel.

## P2-2 — real-CLI test read potential real profile data + left temp files
compileL0WithManifest({catId}) and the CLI ran without --profile-dir, so the compiler
resolved options.profileDir > CAT_CAFE_PROFILE_DIR > <repo>/private/profile and could read
real user capsule/primer, then left mkdtemp compiled prompts behind. Now both the direct
call and the CLI are pointed at a dedicated EMPTY temp profile dir (explicit profileDir /
--profile-dir), and every temp dir (profile + compile outputs) is tracked and removed in
after(). Test-data isolation boundary, not cosmetic.

## Evidence
- f257-route-seam 4/0 (non-native now asserts message-prepend + S/D segment), f257-l0-manifest-cli
  2/0 (isolated empty profile + cleanup). biome clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@mindfn
mindfn merged commit 21f57df into develop_base Jul 21, 2026
@mindfn
mindfn deleted the feat/f257-lseries-trace branch July 21, 2026 11:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant