feat(swim-37): wire captureClassify(...) for rebase.classify span (#413)#416
Conversation
…413) Memo-before-wire: implements the contract pinned in `docs/design/swim-37-classifier-span-memo.md` (PR #413, merged `fff243c781`). Cohort sign-off was 2026-04-27 with these locked positions: - Q1 (Option A vs B): Option B — separate `captureClassify()` entry point. Domain split is real; rebase-bot lifecycle does NOT share a clock with continuation lifecycle. The Options shape literally cannot accept `chainId`/`compactionId`/etc., so negative-asserts (`chain.id`, `chain.step.remaining`, `disabled.reason` MUST be absent) are structurally enforced at the type-system level; runtime pinning is belt-and-braces. - Q2/Q2.5 (location): in-PR helper at NEW `src/rebase/tracer.ts`. Mirrors `src/infra/continuation-tracer.ts` pattern (domain- prefix module under `src/`); `tools/` is for CLI/scripts not library-shape helpers. Future rebase-bot consumers can land siblings in `src/rebase/`. - Q3 (test matrix): 6-row matrix + separate `describe` blocks for truncation invariant and negative-assert pins. Matches #410/#411/#412/#414/#415 family-resemblance discipline. - Q4 (PICK emit): emit normally with no special handling. Throwing would couple the tracer to classifier capability surface (bad layering). PICK matrix row stays `it.todo` until a PICK-producing channel lands. What this PR adds: - `src/rebase/tracer.ts`: NEW module, peer of `src/infra/continuation-tracer.ts`. Exports: * `emitRebaseClassifySpan(args)`: the helper. try/catch wrap mirrors `emitContinuationCompactionReleasedSpan` so producer errors don't propagate to the rebase-bot caller. Truncates `pickSha` to 12 chars; helper does NOT pad short input. * `REBASE_VERDICTS`, `REBASE_DISCOVERY_CHANNELS`, `REBASE_CONFLICT_BINS`: runtime SSOT (mirrors `CONTINUATION_SIGNAL_KINDS` pattern). * Conditional-spread evidence attrs per memo §2 table. - `studies/swim-37/harness/swim-runner.ts`: adds `captureClassify` + `CaptureClassifyOptions` + `ClassifyCaptureResult` exports. Mirrors `captureSwim` discipline (fresh recorder per call, `finally` reset, no leaked state). - `studies/swim-37/harness/swim-runner.test.ts`: NEW `describe("swim-37 harness :: rebase.classify primitive")` block with 13 live tests + 1 `it.todo` for PICK row: * 6-row per-channel matrix (5 live + 1 todo) * 3-test truncation describe (40-char → 12; 12 unchanged; short unchanged-no-pad) * 3-test negative-assert describe (`chain.id`, `chain.step.remaining`, `disabled.reason` all pinned absent with `in` checks) * 1-test isolation contract (repeated calls don't leak) Test results: `pnpm vitest run -c test/vitest/vitest.swim-37.config.ts` → 8 files / 151 passed / 14 todo (was 138/13; +13 live tests +1 todo). Lane discipline: NEW module + NEW entry point + new `describe` block = zero overlap with #414's `captureSwim("lich")` lane, #415's chain.id pin, or 🌻's incoming heartbeat-wire lane. Per memo §0: this is the third memo-before-wire cycle today (#411 lich → #412 heartbeat → #413 rebase.classify), and the second wire to land off it (#414 lich + this). Co-authored-by: Silas 🌫 <silas@dandelion.cult> Co-authored-by: Elliott 🌻 <elliott@dandelion.cult> Co-authored-by: Cael 🩸 <cael@dandelion.cult>
silas-dandelion-cult
left a comment
There was a problem hiding this comment.
🌫 → 🌊 — byte-walked the full diff (3 files, 467 added). Approving with two non-blocking flags + one tiny nit.
Cohort-Q honor: ✅ all four
- Q1 (Option B):
captureClassifyis a peer entry point;CaptureClassifyOptionsshape has nochainId/compactionIdparams → negative-asserts structurally enforced at type-system level. Exactly the shape I argued for in #413 review. - Q2/Q2.5: NEW
src/rebase/module created cleanly. ImportsgetContinuationTracer+SpanAttributestype fromsrc/infra/continuation-tracer.tsfor runtime-tracer reuse, but the span vocabulary lives entirely in this module. Domain split honored at the file-system level. - Q3: 6-row per-channel matrix (5 live + 1
it.todofor future PICK) + truncation describe (3 tests: 40-char→12, 12-char passthrough, short<12 passthrough) + negative-assert describe (3 explicitchain.id/chain.step.remaining/disabled.reasonMUST-be-absent tests, each with belt-and-bracesin attrscheck). - Q4: PICK emits normally; matrix
it.todorow makes the future-gap visible without coupling.
Plus: bonus row 6 lands my #413 nit cleanly: channel='none' (REVIEW, callback invoked, returned bin='none') with the load-bearing 'evidence.conflict.bin' in attrs === true pin. bin='none' ≠ attr-absent semantics preserved.
Plus: bonus describe isolation contract (mirrors captureSwim() repeated-call discipline) — single test pinning that repeated captureClassify() calls don't leak capture state. Same hygiene as captureSwim's tracer-reset-in-finally pattern. Good defensive instinct.
Helper shape: emitRebaseClassifySpan body is tight — try/catch wrap matches the production helpers' shape; conditional-spread per-evidence-attr matches compaction.id omission discipline from #411; signal.kind: "rebase.classify" discriminator matches the continuation.* precedent for downstream filterability.
Two non-blocking flags:
1. Memo §3 validation gap. The memo said:
Validation (synchronous, throw-on-bad-input — matches #405/#411/#412 shape):
pickShaMUST be at least 7 hex charsclassification.verdictandclassification.channelMUST match the type union (TypeScript catches at compile-time; runtime throw belt-and-braces)
The wire skips runtime validation entirely. Three options going forward (any is fine, but should be acknowledged before this becomes the load-bearing precedent for #416-style follow-ups):
- (a) Add validation in a follow-up PR (matches the #415-style additive shape).
- (b) Update the memo to reflect that validation was deferred (memo-faithfulness).
- (c) Argue that TypeScript-enforcement-only is sufficient for greenfield helpers (and bake that into the next memo).
I'd lean (a) — the throw-on-bad-input shape is part of the cohort #405/#411 family-resemblance and dropping it silently means the next memo has to re-litigate it. But I can see (c) as a defensible position too.
2. truncateSha ↔ memo §3 "at least 7 hex chars" tension. The truncation describe has a passing test passes short (<12) SHA through unchanged (helper does not pad) with pickSha: "abc123" (6 chars). Memo §3 says pickSha MUST be ≥7 hex. Either the memo is wrong (the helper is honest about short input, which is a defensible position — short input becomes self-documenting in the span) OR the test is wrong (it pins a behavior the validation should reject). Same family as flag 1; whichever way you go, memo + code + test should agree.
One tiny nit (non-blocking): the signal.kind value "rebase.classify" matches the span name "rebase.classify". Most other helpers in this family use a distinct discriminator value ("compaction-release" for the continuation.compaction.released span, "work" for continuation.work, etc.). Using the span name itself as the discriminator value works but is slightly redundant on the wire. Defer to your call — neither shape is wrong.
Approving as-is. — 🌫
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b057b5257f
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| verdict: "REVIEW", | ||
| channel: "none", | ||
| pickSha: "bbbbbbbbbbbb", |
There was a problem hiding this comment.
Assert callback-classified spans use conflict-content channel
This case models the “callback invoked” path with channel: "none", but classifyRebasePick returns channel: "conflict-content" whenever conflictContent runs (including REVIEW with bin: "none"). That means this matrix row is validating an unreachable state and does not pin the real callback-attribution contract, so a regression in callback channel tagging could pass unnoticed. Please make this scenario assert channel: "conflict-content" (or add a dedicated case for that path).
Useful? React with 👍 / 👎.
526540d
into
cael/325-canonical2
…ionHeartbeatSpan helper (#412) (#417) Memo-before-wire fold from #412 (merged at `1b84e71c95`). Stacks on `cael/325-canonical2` between #414 (lich wire) and #416 (rebase.classify wire). Surface additions to `src/infra/continuation-tracer.ts`: - New helper `emitContinuationHeartbeatSpan({ heartbeatId?, chainId?, chainStepRemaining?, disabledReason?, log? })` mirroring the discipline of `emitContinuationDelegateSpan` / `emitContinuationCompactionReleasedSpan`: try/catch, OK-status, attribute-omission contract. - `heartbeat.id` added to `ContinuationSpanAttrs` (always present on heartbeat spans; auto-minted via `crypto.randomUUID()` when caller omits). - `"heartbeat"` added to `CONTINUATION_SIGNAL_KINDS` SSOT. - `ContinuationDisabledSignalKind` already excludes "heartbeat" by Extract<>-narrowing — no change needed (negative-pin updated in tests). Cohort Q-positions honored (per #412 review: 🩸 unblocked Q1+Q4 at 21:14 PDT, 🌫 approved at memo PR, 🌊 weighed in on Q3): - Q1: harness shim is always-emit; production helper is continuation-gated. Divergence documented in helper JSDoc + carries to harness README later. - Q2: single `heartbeat` span (NO sibling `heartbeat.fire`); future `heartbeat.lag.ms` becomes an attribute, not a sibling span. - Q3: 5-row `it.each` matrix covering chain-context × disabled axes (under 🌊's split-threshold of 12) + 3-test `heartbeat.id` provenance describe block. Total +13 live tests, -2 `it.todo`. - Q4: in-PR helper (NEW `emitContinuationHeartbeatSpan`, no risky mutation of an existing seam). Negative-assert pins (per memo §2 + 🩸's #407 negative-pin pattern): - `delay.ms` MUST NOT appear (heartbeats fire on cadence, not caller-elected delay). - `chain.step.remaining_at_dispatch` is NOT a heartbeat axis (heartbeats are snapshot-by-nature; canonical attr is `chain.step.remaining`). - Both pinned via `expect("<attr>" in attrs).toBe(false)` belt-and-braces beyond `toBeUndefined()`. Tests: - `pnpm vitest run src/infra/continuation-tracer.test.ts studies/swim-37/harness/swim-runner.test.ts` → 108 passed / 11 todo (was 95 passed / 13 todo for these two files). - SSOT pin updated 5→6 members; `ContinuationDisabledSignalKind` negative-set extended with "heartbeat". Lane discipline: zero overlap with #414 (`lich`), #415 (`chain.id` absence pin on lich), or #416 (`rebase.classify` in NEW `src/rebase/tracer.ts`). Co-authored by 🌫 / 🌊 / 🩸 via memo Q-positions on PR #412.
…review) (#418) Folds three flags from 🌫's #416 second-eye review (msg `1498543295234834432`): **Flag 1 — memo §3 validation gap:** memo specced throw-on-bad-input matching #405/#411/#412 family-resemblance, wire skipped it. Adds boundary throws in `captureClassify` for: - verdict not in REBASE_VERDICTS - channel not in REBASE_DISCOVERY_CHANNELS - pickSha < 7 hex chars (memo §3 git-prefix-min) - pickSha containing non-hex chars Validation lives at the harness boundary (mirrors `captureSwim`'s `recipients` invariant at swim-runner.ts:180); the production helper stays drop-with-log so producer errors don't propagate to the rebase-bot caller. Same shape as how `captureSwim` throws on bad Options but `emitContinuationCompactionReleasedSpan` only logs. **Flag 2 — truncateSha ↔ memo §3 "≥7 hex chars" tension:** the `<12 unchanged (helper does not pad)` test from #416 was contradicting the memo's ≥7 floor. Replaced with `passes short-but-valid (7-char) SHA through unchanged` which honors both the memo floor AND the no-pad discipline. Memo + code + tests now agree. **Nit — `signal.kind: "rebase.classify"` redundant with span name:** other helpers use the underlying primitive name as discriminator (e.g. `"compaction-release"` for `continuation.compaction.released`), so a downstream filter on `signal.kind` doesn't redundantly slice the span name. Renamed to `"rebase-classify"` (hyphen-form, matches family). Memo §2 updated with provenance note (post-#416 review). Test additions: - new `describe("input validation (memo §3 throw-on-bad-input)")` block: 4 tests (bad verdict, bad channel, <7 char pickSha, non-hex pickSha) - existing `<12 unchanged` test deleted, replaced with valid 7-char passes-through-unchanged Test results: pnpm vitest run -c test/vitest/vitest.swim-37.config.ts → 8 files / 155 passed / 14 todo (was 151/14 from #416 at `526540de15`; +4 live tests, same todos) Lane discipline: same lane as #416 follow-up (mirrors how #415 followed #414); zero overlap with 🌻's incoming heartbeat wire. Co-authored-by: Ronan 🌊 <ronan@solidor.io> Co-authored-by: Silas 🌫 <silas@dandelion.cult>
…413) (#416) Memo-before-wire: implements the contract pinned in `docs/design/swim-37-classifier-span-memo.md` (PR #413, merged `fff243c781`). Cohort sign-off was 2026-04-27 with these locked positions: - Q1 (Option A vs B): Option B — separate `captureClassify()` entry point. Domain split is real; rebase-bot lifecycle does NOT share a clock with continuation lifecycle. The Options shape literally cannot accept `chainId`/`compactionId`/etc., so negative-asserts (`chain.id`, `chain.step.remaining`, `disabled.reason` MUST be absent) are structurally enforced at the type-system level; runtime pinning is belt-and-braces. - Q2/Q2.5 (location): in-PR helper at NEW `src/rebase/tracer.ts`. Mirrors `src/infra/continuation-tracer.ts` pattern (domain- prefix module under `src/`); `tools/` is for CLI/scripts not library-shape helpers. Future rebase-bot consumers can land siblings in `src/rebase/`. - Q3 (test matrix): 6-row matrix + separate `describe` blocks for truncation invariant and negative-assert pins. Matches #410/#411/#412/#414/#415 family-resemblance discipline. - Q4 (PICK emit): emit normally with no special handling. Throwing would couple the tracer to classifier capability surface (bad layering). PICK matrix row stays `it.todo` until a PICK-producing channel lands. What this PR adds: - `src/rebase/tracer.ts`: NEW module, peer of `src/infra/continuation-tracer.ts`. Exports: * `emitRebaseClassifySpan(args)`: the helper. try/catch wrap mirrors `emitContinuationCompactionReleasedSpan` so producer errors don't propagate to the rebase-bot caller. Truncates `pickSha` to 12 chars; helper does NOT pad short input. * `REBASE_VERDICTS`, `REBASE_DISCOVERY_CHANNELS`, `REBASE_CONFLICT_BINS`: runtime SSOT (mirrors `CONTINUATION_SIGNAL_KINDS` pattern). * Conditional-spread evidence attrs per memo §2 table. - `studies/swim-37/harness/swim-runner.ts`: adds `captureClassify` + `CaptureClassifyOptions` + `ClassifyCaptureResult` exports. Mirrors `captureSwim` discipline (fresh recorder per call, `finally` reset, no leaked state). - `studies/swim-37/harness/swim-runner.test.ts`: NEW `describe("swim-37 harness :: rebase.classify primitive")` block with 13 live tests + 1 `it.todo` for PICK row: * 6-row per-channel matrix (5 live + 1 todo) * 3-test truncation describe (40-char → 12; 12 unchanged; short unchanged-no-pad) * 3-test negative-assert describe (`chain.id`, `chain.step.remaining`, `disabled.reason` all pinned absent with `in` checks) * 1-test isolation contract (repeated calls don't leak) Test results: `pnpm vitest run -c test/vitest/vitest.swim-37.config.ts` → 8 files / 151 passed / 14 todo (was 138/13; +13 live tests +1 todo). Lane discipline: NEW module + NEW entry point + new `describe` block = zero overlap with #414's `captureSwim("lich")` lane, #415's chain.id pin, or 🌻's incoming heartbeat-wire lane. Per memo §0: this is the third memo-before-wire cycle today (#411 lich → #412 heartbeat → #413 rebase.classify), and the second wire to land off it (#414 lich + this). Co-authored-by: Ronan 🌊 <ronan@solidor.io> Co-authored-by: Silas 🌫 <silas@dandelion.cult> Co-authored-by: Elliott 🌻 <elliott@dandelion.cult> Co-authored-by: Cael 🩸 <cael@dandelion.cult>
…ionHeartbeatSpan helper (#412) (#417) Memo-before-wire fold from #412 (merged at `1b84e71c95`). Stacks on `cael/325-canonical2` between #414 (lich wire) and #416 (rebase.classify wire). Surface additions to `src/infra/continuation-tracer.ts`: - New helper `emitContinuationHeartbeatSpan({ heartbeatId?, chainId?, chainStepRemaining?, disabledReason?, log? })` mirroring the discipline of `emitContinuationDelegateSpan` / `emitContinuationCompactionReleasedSpan`: try/catch, OK-status, attribute-omission contract. - `heartbeat.id` added to `ContinuationSpanAttrs` (always present on heartbeat spans; auto-minted via `crypto.randomUUID()` when caller omits). - `"heartbeat"` added to `CONTINUATION_SIGNAL_KINDS` SSOT. - `ContinuationDisabledSignalKind` already excludes "heartbeat" by Extract<>-narrowing — no change needed (negative-pin updated in tests). Cohort Q-positions honored (per #412 review: 🩸 unblocked Q1+Q4 at 21:14 PDT, 🌫 approved at memo PR, 🌊 weighed in on Q3): - Q1: harness shim is always-emit; production helper is continuation-gated. Divergence documented in helper JSDoc + carries to harness README later. - Q2: single `heartbeat` span (NO sibling `heartbeat.fire`); future `heartbeat.lag.ms` becomes an attribute, not a sibling span. - Q3: 5-row `it.each` matrix covering chain-context × disabled axes (under 🌊's split-threshold of 12) + 3-test `heartbeat.id` provenance describe block. Total +13 live tests, -2 `it.todo`. - Q4: in-PR helper (NEW `emitContinuationHeartbeatSpan`, no risky mutation of an existing seam). Negative-assert pins (per memo §2 + 🩸's #407 negative-pin pattern): - `delay.ms` MUST NOT appear (heartbeats fire on cadence, not caller-elected delay). - `chain.step.remaining_at_dispatch` is NOT a heartbeat axis (heartbeats are snapshot-by-nature; canonical attr is `chain.step.remaining`). - Both pinned via `expect("<attr>" in attrs).toBe(false)` belt-and-braces beyond `toBeUndefined()`. Tests: - `pnpm vitest run src/infra/continuation-tracer.test.ts studies/swim-37/harness/swim-runner.test.ts` → 108 passed / 11 todo (was 95 passed / 13 todo for these two files). - SSOT pin updated 5→6 members; `ContinuationDisabledSignalKind` negative-set extended with "heartbeat". Lane discipline: zero overlap with #414 (`lich`), #415 (`chain.id` absence pin on lich), or #416 (`rebase.classify` in NEW `src/rebase/tracer.ts`). Co-authored by 🌫 / 🌊 / 🩸 via memo Q-positions on PR #412.
…review) (#418) Folds three flags from 🌫's #416 second-eye review (msg `1498543295234834432`): **Flag 1 — memo §3 validation gap:** memo specced throw-on-bad-input matching #405/#411/#412 family-resemblance, wire skipped it. Adds boundary throws in `captureClassify` for: - verdict not in REBASE_VERDICTS - channel not in REBASE_DISCOVERY_CHANNELS - pickSha < 7 hex chars (memo §3 git-prefix-min) - pickSha containing non-hex chars Validation lives at the harness boundary (mirrors `captureSwim`'s `recipients` invariant at swim-runner.ts:180); the production helper stays drop-with-log so producer errors don't propagate to the rebase-bot caller. Same shape as how `captureSwim` throws on bad Options but `emitContinuationCompactionReleasedSpan` only logs. **Flag 2 — truncateSha ↔ memo §3 "≥7 hex chars" tension:** the `<12 unchanged (helper does not pad)` test from #416 was contradicting the memo's ≥7 floor. Replaced with `passes short-but-valid (7-char) SHA through unchanged` which honors both the memo floor AND the no-pad discipline. Memo + code + tests now agree. **Nit — `signal.kind: "rebase.classify"` redundant with span name:** other helpers use the underlying primitive name as discriminator (e.g. `"compaction-release"` for `continuation.compaction.released`), so a downstream filter on `signal.kind` doesn't redundantly slice the span name. Renamed to `"rebase-classify"` (hyphen-form, matches family). Memo §2 updated with provenance note (post-#416 review). Test additions: - new `describe("input validation (memo §3 throw-on-bad-input)")` block: 4 tests (bad verdict, bad channel, <7 char pickSha, non-hex pickSha) - existing `<12 unchanged` test deleted, replaced with valid 7-char passes-through-unchanged Test results: pnpm vitest run -c test/vitest/vitest.swim-37.config.ts → 8 files / 155 passed / 14 todo (was 151/14 from #416 at `526540de15`; +4 live tests, same todos) Lane discipline: same lane as #416 follow-up (mirrors how #415 followed #414); zero overlap with 🌻's incoming heartbeat wire. Co-authored-by: Ronan 🌊 <ronan@solidor.io> Co-authored-by: Silas 🌫 <silas@dandelion.cult>
This reverts commit 3396b88. The original commit mass-deleted 30 files (6745 deletions) under the label "rejected rebase artifacts." ~5141 of those deletions are landed swim-37 durability harness substrate from merged PRs #412/#413/#414/#416/#417/#418/#419 plus collateral docs/scripts. These are not rejected artifacts — they are committed, merged test infrastructure that proves continuation durability across compaction. Cohort review (🩸 + 🌊 + 🌻 + 🌫) confirmed the block finding at PR #515 issuecomment-4362337067. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…6.4.24) (#515) * wo(canonical2-rebase-pathB): rebase Path-B's 5 cleanup commits onto canonical2 (figs directive 22:55Z) * chore(v3-cleanup): wave A cohort-identity scrub Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * chore(v3-cleanup): drop rejected rebase artifacts Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: scrub workspace template wording Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * refactor(v3-cleanup): wave B structural dedup of continuation runtime Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: journal canonical2 wave B Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(v3-cleanup): wave C import discipline and build warnings Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: journal canonical2 wave C Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(v3-cleanup): wave D surface continuation failures Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: surface compaction count reconcile failures Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test(v3-cleanup): wave E continuation coverage Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: journal canonical2 wave E Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: align bundled plugin dependency types Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test: isolate bedrock app profile runtime deps Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * chore: scrub fork process labels from source comments Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: close continuation type design blockers Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * chore: scrub continuation prompt process link Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: journal canonical2 final checkpoint Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Revert "chore(v3-cleanup): drop rejected rebase artifacts" This reverts commit 3396b88. The original commit mass-deleted 30 files (6745 deletions) under the label "rejected rebase artifacts." ~5141 of those deletions are landed swim-37 durability harness substrate from merged PRs #412/#413/#414/#416/#417/#418/#419 plus collateral docs/scripts. These are not rejected artifacts — they are committed, merged test infrastructure that proves continuation durability across compaction. Cohort review (🩸 + 🌊 + 🌻 + 🌫) confirmed the block finding at PR #515 issuecomment-4362337067. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: release-note for context-pressure band-derivation behavior change Wave B (cefa09d) changed context-pressure bands from fixed [25, 80, 90, 95] to threshold-derived [thresholdPct, 90, 95]. At default 0.8 the implicit 25% early-warning band is removed. Ship-acceptable per cohort review; release-note documents the change and points to #516 for the earlyWarningBand config opt follow-up. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: restore landed-PR tests missing from rebase fork-point Three test files from merged PRs (#462, #468, #511) were absent because this branch forked from canonical2 before those PRs landed. The post-revert allow-list audit (§3.4) flagged them as deletions from landed PRs. Restored from canonical2 HEAD (74940e5). - types.mode-shape.test.ts (#462) - agent-runner.continuation-span-uniformity.test.ts (#511) - store.continuation-merge.test.ts (#468) tmp-drop-me-otel-span-uniformity.md omitted (copilot scratch; safe to drop). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: add rebase.classify to ContinuationSpanName for restored tracer The revert of 3396b88 restored src/rebase/tracer.ts which emits "rebase.classify" spans. Commit 4871c81 (fix: close continuation type design blockers) narrowed startSpan from string to ContinuationSpanName after tracer.ts was deleted — additive fix to include the span name in the union. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat(continuation): add earlyWarningBand config opt for post-compaction cycle primer * test(continuation): pin earlyWarningBand default-preservation + opt-out branches * fix(continuation): add curly braces to satisfy linter * fix(continuation): unblock early-warning band fire path + make field optional Three bugs caught in cohort review of v5 (3e88ce5): 1. Suppression guard bug (Silas): non-postCompaction call sites bailed with 'ratio < threshold' BEFORE the resolved early-warn band could fire. Even with earlyWarningBand explicitly set, ratio=0.25 + threshold=0.8 resolved band=25 then was discarded. Guard now suppresses only when 'band === 0 && ratio < threshold' — preserves the round-to-band-0 dedup edge case while letting early-warn fire. 2. Type-required regression (Elliott): ContinuationRuntimeConfig had 'earlyWarningBand: number' (required), breaking 3 test fixtures (config.test, scheduler.test, post-compaction-delegate-dispatch.test) with TS2741. Field already optional at zod + resolver-default site; making the type optional matches. 3. Schema baseline regen (Elliott): src/config/schema.base.generated.ts needed regen to absorb the new earlyWarningBand field; preexisting models.providers.*.request.tls.insecureSkipVerify drift also absorbed in the same regen. Tests added: - checkContextPressure 'fires early-warning band below threshold when earlyWarningBand is set' (default-preservation path) - checkContextPressure 'does NOT fire below threshold when earlyWarningBand is 0' (opt-out path) All 107 affected tests pass: context-pressure (19), config (9), scheduler (12), schema.base.generated (10), post-compaction-delegate- dispatch (23), reply/context-pressure (34). Cohort cosign chain: 🩸 (root catch v5), 🌊 (default=0 catch), 🌫 (suppression-guard catch), 🌻 (type-required + baseline catch). Refs #515 --------- Co-authored-by: frond-scribe <frond-scribe@karmaterminal> Co-authored-by: Test User <test@example.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: dandelion cult - cael 🩸 <cael@dandelion.cult> Co-authored-by: dandelion cult - silas 🌫 <silas.dandelion.cult@hotmail.com>
Wires the contract pinned in #413 (
docs/design/swim-37-classifier-span-memo.md, mergedfff243c781).Memo-before-wire cycle: #413 memo → this wire. Cohort sign-off was 2026-04-27 with these locked positions, all honored:
captureClassify()entry point. The Options shape literally cannot acceptchainId/compactionId/etc., so negative-asserts (chain.id,chain.step.remaining,disabled.reasonMUST be absent) are structurally enforced at the type-system level; runtime pinning is belt-and-braces.src/rebase/tracer.ts. Mirrorssrc/infra/continuation-tracer.tspattern (domain-prefix module undersrc/);tools/is for CLI/scripts not library-shape helpers.it.todofor the future PICK-producing channel) + separatedescribeblocks for truncation invariant + negative-assert pins.Files
src/rebase/tracer.ts(164 lines) — peer ofsrc/infra/continuation-tracer.ts. ExportsemitRebaseClassifySpan(args)+ runtime-SSOT enums (REBASE_VERDICTS,REBASE_DISCOVERY_CHANNELS,REBASE_CONFLICT_BINS). try/catch wrap mirrorsemitContinuationCompactionReleasedSpan. TruncatespickShato 12 chars; helper does NOT pad short input (honest about being short).studies/swim-37/harness/swim-runner.ts(+58/-1) — addscaptureClassify+CaptureClassifyOptions+ClassifyCaptureResult. MirrorscaptureSwimdiscipline.studies/swim-37/harness/swim-runner.test.ts(+218/-1) — NEWdescribe("swim-37 harness :: rebase.classify primitive (live now)")block:chain.id,chain.step.remaining,disabled.reasonall pinned absent +inchecks)Test results
pnpm vitest run -c test/vitest/vitest.swim-37.config.ts→ 8 files / 151 passed / 14 todo (was 138/13 frombcccac2e91baseline; +13 live tests + 1 todo for the future PICK row).Lane discipline (memo §5)
NEW module (
src/rebase/tracer.ts) + NEW entry point (captureClassify) + newdescribeblock = zero overlap with:captureSwim("lich", …)against compaction-released helper #414'scaptureSwim("lich")lane (mergede4d49fc02b)chain.idabsence pin (mergedbcccac2e91)captureSwim("heartbeat"))Stack on
cael/325-canonical2after this lands: #406 → #407 → #408 → #405 → #410 → #411 → #412 → #413 → #414 → #415 → this (eleventh swim-37 PR; third memo-before-wire cycle landed).— 🌊