Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
### Architecture

- **ADR 0013** — persist the `beta1m` fact, derive the session window (expert-panel design: Kleppmann/Hickey/Helland/Bailis).
- **ADR 0010 rewritten by #350** — per-conversation coreHash ownership replaces per-turn early-exit.
- **ADR 0010 rewritten by #350** — per-conversation coreHash ownership replaces the per-turn early-exit in the **batch** path (`wfInferLanes`) only; the live path and turn list keep the per-turn form.

## 2.0.0

Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ These constraints are enforced by structural encapsulation where possible, and g
- **Use `_wfIsMainLane(lane)` for main/orchestrator detection, never `!lane.spawnParent`** (`spawnParent` is always `null`, in every lane object, everywhere) — @docs/decisions/0007-wf-is-main-lane-not-spawn-parent.md
- **Temporal overlap overrides agentKey for lane placement** — the main lane is strictly serial (no temporally-overlapping turns); same-convId overlap turns (jitter/re-send/rewind) ride main's `overlapEntries` as event markers, never a separate lane (#364); different-convId overlaps still create `parallel-*`/`agent-*` lanes; null-convId non-main lanes retain strict no-overlap split. `_wfSeqRetroMove` must rebuild `mainConvIds` and migrate stale markers after every retro-move — @docs/decisions/0008-temporal-overlap-overrides-agent-key.md
- **Sequential-interleave classification goes through the shared seq tracker in both files** (`wfCreateSeqTracker` in `workflow-timeline.js`; instances in `wfInferLanes`, `wfAddEntry`, and `entry-rendering.js` `addEntry`), and the tracker never consults `isCompacted` (fan-out first-turns carry a false flag) — @docs/decisions/0009-sequential-interleave-conv-bracketing.md
- **coreHash lane ownership is per-CONVERSATION, never per-turn** — in `wfInferLanes` a main-agentKey turn is a subagent iff NONE of its conversation's *plurality-tied* coreHashes (`_wfComputeConvIdentity`, `convMaxHashes` = the hashes sharing the max count) is in `mainCoreHashSet` (the seed conversation's dominant only — routing is per-conversation, so a mixed `/model` conversation already rides main as a unit; adding a main conversation's *secondary* coreHashes over-merges a separate subagent sharing that hash — real c6e1ddaa). A lone seed/blip turn carrying main's coreHash is a minority so it cannot flip its conversation into main (add88512); a genuine plurality tie resolves toward main-membership so a `/model`-switching compaction successor is not ejected. Seed = the earliest-starting main-agentKey CONVERSATION that has a coreHash (not the earliest coreHash-bearing turn — else a subagent could seize the seed when main opens coreHash-less). This REWRITES ADR 0010's per-turn early-exit for the batch path; the live `wfAddEntry` + turn-list still use the per-turn form pending A2 (#350) — @docs/decisions/0010-corehash-identity-routing.md
- **coreHash lane ownership is per-CONVERSATION, never per-turn** — in `wfInferLanes` a main-agentKey turn is a subagent iff NONE of its conversation's *plurality-tied* coreHashes (`_wfComputeConvIdentity`, `convMaxHashes` = the hashes sharing the max count) is in `mainCoreHashSet` (the seed conversation's dominant only — routing is per-conversation, so a mixed `/model` conversation already rides main as a unit; adding a main conversation's *secondary* coreHashes over-merges a separate subagent sharing that hash — real c6e1ddaa). A lone seed/blip turn carrying main's coreHash is a minority so it cannot flip its conversation into main (add88512); a genuine plurality tie resolves toward main-membership so a `/model`-switching compaction successor is not ejected. Seed = the earliest-starting main-agentKey CONVERSATION that has a coreHash (not the earliest coreHash-bearing turn — else a subagent could seize the seed when main opens coreHash-less). This REWRITES ADR 0010's per-turn early-exit for the batch path; the live `wfAddEntry` + turn-list keep the per-turn form as an accepted bounded divergence (no open issue tracks a mirror) — @docs/decisions/0010-corehash-identity-routing.md
- **context% denominator: display folds a per-session window, classification never does** — display sites (session card, its window label, timeline minimap, swimlane) divide by `sessionCtxWindow(sid)` (or the `_wfWinByTurn` lane fold): 1M if any main turn has `beta1m===true` (authoritative, `SUPPORTS_1M`-gated at write time), else the largest `maxContext` fossil (heals legacy), else 200K. Classification (`isCompacted`, per-turn `severity`, lane placement) keeps dividing by raw per-turn `maxContext` — never the fold (feeding a future-dependent latch into a prefix-local classifier reintroduces the reverted live≠batch divergence). The fold is stateless-at-render (never a stored `sess.maxWindow` → can't go stale); `beta1m` is persisted add-only so it's rebuildable. Never cross the two — @docs/decisions/0013-beta1m-persist-session-window-derive.md
- **persisted weather carries a derivation revision; all three writers stamp it through `_assignWeather`** — the load-time probe tests field EXISTENCE and `reconcile` compares COUNTS, so neither notices a change in HOW weather is derived; a semantics change without a `WEATHER_REV` bump leaves every cold session card rendering the deleted rule with all checks green. An unstamped writer (restore's step-6 `setWeather` overwrites the rebuild's output) turns the probe into a permanent rebuild loop — @docs/decisions/0013-beta1m-persist-session-window-derive.md
- **cost-worker lifecycle: imported mode is side-effect free, executed mode exits by drain** — R1 no handle may ref the loop past the final stdout write; R2 no `process.exit()` on success; R3 one result protocol (stdout JSON); R4 adding an env-derived root obliges updating `isolatedEnv()` in every test that forks the worker — @docs/decisions/0015-cost-worker-lifecycle-drain-exit.md
Expand Down
12 changes: 7 additions & 5 deletions docs/decisions/0010-corehash-identity-routing.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

- Status: Accepted — **batch routing rule REWRITTEN by #350 (2026-07-24), see
"## Rewritten by #350" below**; the live path (`wfAddEntry`) + turn-list
(`entry-rendering.js`) still use the original per-turn early-exit pending A2.
(`entry-rendering.js`) keep the original per-turn early-exit as an ACCEPTED
bounded divergence — see "A1/A2 boundary" below; no open issue tracks a mirror.
- Date: 2026-07-16
- Related: #258 / #257 / #350 / ADR 0005 / ADR 0008

Expand Down Expand Up @@ -227,8 +228,9 @@ fix any *completed* session on cold-load+rebuild, which is how the dashboard
renders `4b15c248`. `wfBuildState` re-derives `mainCoreHash`/`mainConvIds` from
the now-correctly-composed final main lane, so the live `wfAddEntry` and
turn-list `entry-rendering.js` paths inherit a correct main identity for new
turns; their per-turn early-exit is left in place. A2 (#350) mirrors the
turns; their per-turn early-exit is left in place. Mirroring the
per-conversation rule + dominant-coreHash flip/trunk rebuild into those two
sites. Until A2, the only residual divergence is a *live* seed turn arriving on
an actively-viewed session — bounded and self-correcting on the next full
rebuild. See `docs/solutions/same-convid-lane-classification.md`.
sites — once tracked as "A2" — was never implemented, and #350 is closed, so
nothing tracks it. The residual divergence is therefore ACCEPTED, not pending:
a *live* seed turn arriving on an actively-viewed session — bounded and
self-correcting on the next full rebuild. See `docs/solutions/same-convid-lane-classification.md`.
3 changes: 2 additions & 1 deletion public/workflow-timeline.js
Original file line number Diff line number Diff line change
Expand Up @@ -1097,7 +1097,8 @@ function wfAddEntry(entry) {
// derived from the (now correctly composed) final main lane — so a fresh
// cold-load/rebuild of a completed session is already correct, and the only
// residual divergence is a *live* seed turn on an actively-viewed session.
// A2 mirrors the per-conversation rule + dominant-coreHash flip/rebuild here.
// Mirroring the per-conversation rule here (once "A2") was never implemented
// and #350 is closed — this per-turn form is the accepted bounded divergence.
if (entry.agentKey && WF_MAIN_AGENT_KEYS[entry.agentKey] &&
wfState.mainCoreHash && entry.coreHash && entry.coreHash !== wfState.mainCoreHash &&
entry.convId && wfState.mainConvIds && !wfState.mainConvIds.has(entry.convId)) {
Expand Down
Loading