diff --git a/scripts/generate-guidance.test.mjs b/scripts/generate-guidance.test.mjs index 779d0af6..e8216c28 100644 --- a/scripts/generate-guidance.test.mjs +++ b/scripts/generate-guidance.test.mjs @@ -126,6 +126,7 @@ describe("canonical guidance generation", () => { } expect(GUIDANCE_CUES.rebaseCheck).toContain("Active summarized history is present"); expect(GUIDANCE_CUES.rebaseCheck).toContain("structural reset and cold start"); + expect(GUIDANCE_CUES.rebaseCheck).toContain("cold-start test passes"); expect(GUIDANCE_CUES.timelineActive).toContain("`active`"); expect(GUIDANCE_CUES.timelineCheckpoints).toContain("`checkpoints`"); expect(GUIDANCE_CUES.timelineSearch).toContain("`search`"); diff --git a/skills/context-management/CORE.md b/skills/context-management/CORE.md index 96bb2e48..3cabbf68 100644 --- a/skills/context-management/CORE.md +++ b/skills/context-management/CORE.md @@ -5,11 +5,15 @@ This file is the editable source for always-on ACM guidance and generated tool/r ## Agentic Context Management CORE v1 -A context window is a **working set**: the exact material needed for the next action. Keep that detail live. Compress finished process into a recoverable **handoff** only at a named **boundary**. +A context window is a **working set**: the exact material needed for the next action. Process detail accumulates naturally; keep live what NEXT needs. ACM keeps that growth recoverable by naming a **boundary** before folding finished process into a **handoff** that leaves **NEXT** executable. -### ACM preflight +### Working-set invariant + +Conserve the working set: live context holds only what NEXT will reason over. Everything else is still needed live, or already folded into an executable handoff whose **archive** remains recoverable. Tools enforce structure; you judge when a transition earns its place against this invariant. -A distinct user goal begins with an **ACM preflight** on the branch that will carry it. First complete any required `New request arrives over finished work` transition; then call `acm_checkpoint` with a semantic `-start` name before managed work. When no finished-chain transition is required, the checkpoint call is the first action. Managed work includes investigation, planning, delegation, any non-ACM tool call, file or external side effects, and multi-step reasoning. The preflight is complete only when the tool result confirms that the checkpoint was created or reused. If the call fails, follow the recovery guidance in its result before proceeding. A text-only direct reply requiring no managed work stays outside the managed-chain workflow. +### How judgment layers fit together + +**Decision smells** prompt a pause when something feels off. **Recognizable moments** map a situation to the lightest reasonable transition. **Fold criteria** and **rebase criteria** must pass before travel earns its place. **Failure shapes** describe what went wrong when a fold or target choice was too aggressive or too late. ### Vocabulary @@ -23,9 +27,24 @@ A distinct user goal begins with an **ACM preflight** on the branch that will ca - **rebase** — move all surviving state into one authoritative snapshot at the earliest safe base, retiring accumulated summary depth. - **cold start** — a fresh agent can execute `NEXT` from the snapshot and its pointers without archived summaries. -### Normal state transitions +### ACM preflight + +Recoverability has a cost curve: once managed work expands the trail, rewind without an anchor is expensive. A distinct user goal therefore begins with an **ACM preflight** on the branch that will carry it—opening a **managed chain**. After any needed finished-chain transition (see Recognizable moments), call `acm_checkpoint` with a semantic `-start` name before managed work: investigation, planning, delegation, any non-ACM tool call, file or external side effects, or multi-step reasoning. The preflight is complete only when the tool result confirms the checkpoint was created or reused. If the call fails, follow the recovery guidance in its result before proceeding. A **lightweight reply**—no evidence lookup, side effects, or multi-step reasoning—stays outside the managed-chain workflow. + +### Decision smells + +Quick checks against the working-set invariant—pause-worthy, not automatic commands: + +- **Nearest checkpoint feels obvious** — **anchor gravity**; name the boundary before choosing a target. +- **Summary upon summary** — rebase trigger; test structural reset and **cold start** from the earliest safe base. +- **User goal changed over finished work** — fold or rebase the prior chain before the new one starts. +- **Exploration done, implementation next** — raw burst can leave once distilled into the handoff. -| Event | Required transition | +### Recognizable moments + +When one of these situations arises, ask whether the working-set invariant still holds; restore it with the lightest transition that does: + +| What you can observe | What becomes reasonable | |---|---| | Phase, attempt, or batch item starts | checkpoint its `-start` boundary before acting | | Unbounded burst or risky step is next | checkpoint before output or side effects arrive | @@ -35,35 +54,42 @@ A distinct user goal begins with an **ACM preflight** on the branch that will ca | New request arrives over finished work | run a rebase check before starting the new chain; if it is not ready, fold the finished local chain | | Another fold would stack on an active summary, a stable subchain closes, or context pressure rises | run a rebase check before choosing a local fold or accepting native compaction | -Context pressure triggers a rebase check. It does not lower the cold start gate or authorize travel by itself. +Context pressure raises priority of a **rebase check**. Cold start remains hard; travel earns its place only when fold criteria—and any rebase criteria—still pass. -### Fold gate +### Fold criteria -Travel only when all three criteria hold: +Travel when all three criteria hold: 1. **Boundary named** — say exactly what raw process leaves the working set. 2. **NEXT executable** — write one immediate action that can run from the handoff. 3. **Raw recoverable** — preserve omitted detail through a checkpoint, node ID, file, command, URL, commit, or other pointer. -Call `acm_travel` alone in its assistant tool batch; do not combine this context mutation with sibling tool calls. +Before `acm_travel`, answer in one line: what leaves, what pointer recovers it, and what single action is NEXT. -Boundary decides whether folding is valid. Timeline reports evidence; it does not decide for you. A target must sit before the named boundary. Nearest and earliest anchors are candidates, never defaults. +Boundary decides whether folding is valid. Timeline reports evidence; it does not decide for you. A target must sit before the named boundary. Nearest and earliest anchors are candidates against boundary and cold start—not answers by proximity or anchor gravity. -### Rebase gate +### Rebase criteria -A **rebase** is a structural reset across accumulated summary depth. It uses the fold gate and adds two completion criteria: **structural reset** and **cold start**. +A **rebase** is a structural reset across accumulated summary depth. It uses the fold criteria and adds **structural reset** and **cold start**. Structural reset passes only when the target precedes an active `branch_summary` that leaves the spine and projected summary depth does not grow. Equal depth passes only when the new snapshot replaces an old summary; a target after all active summaries fails. Cold start passes only when `NEXT` is immediately executable, every surviving front and invariant is in the snapshot or a usable direct pointer, and ordinary execution needs no archived summary. -Build one authoritative snapshot, then evaluate candidate bases from earliest to latest. Choose the earliest base that passes both criteria. Root is ideal when it passes; it is never presumed safe. +Build one authoritative snapshot, then evaluate candidates from earliest to latest. Choose the earliest base that passes both. Root is ideal when it passes; treat it as a candidate until both criteria clear. + +If no candidate passes, rebase is not ready: keep required detail live, use a local fold, or accept native compaction. Ambiguous base selection, interleaved fronts, or raw-node fallback → load the context-management skill. + +### Failure shapes -If no candidate passes, rebase is not ready: keep required detail live, use a local fold, or accept native compaction. +- **Folded too early** — NEXT stalls; archived detail is needed again. +- **Folded too late** — working set bloats; context pressure rises. +- **Wrong target** — a parked front, invariant, or recovery pointer vanishes from the spine. +- **Rebase skipped** — summaries stack; cold start degrades across folds. -### Handoff contract +### Handoff shape -Every handoff uses these seven slots in this order. Write `none` when a category is empty. +Every handoff uses these seven slots in order. Write `none` when empty. Evidence is a pointer index, not a dump of the folded trail. ```text Goal: @@ -75,12 +101,14 @@ Recover: NEXT: ``` -The runtime can validate slot structure. Semantic completeness, target quality, recoverability sufficiency, and whether NEXT is genuinely executable remain agent completion criteria. +The runtime validates slot structure and rejects mixed tool batches. Semantic completeness, target quality, recoverability sufficiency, and whether NEXT is genuinely executable remain agent completion criteria. ### Representative transitions **Local fold example** +Why: findings are distilled; raw trail is unused by NEXT; Recover keeps the burst archive. + ```text Goal: Fix high CPU while preserving the sidebar. State: Profiling proved hidden tabs retain workers; implementation is next. @@ -93,6 +121,8 @@ NEXT: Checkpoint sidebar-lifecycle-fix-start, then inspect worker disposal. **Finished-chain rebase example** +Why: prior chain is stable; one cold-start snapshot carries the new request from the earliest safe base. + Rebase the finished chain to the earliest base that passes cold start: ```text @@ -106,26 +136,28 @@ NEXT: Checkpoint migration-dry-run-start, then inspect the migration command ent ``` After travel, confirm resolved target, summary leaf, backup outcome, raw usage/message/summary-depth deltas, and context-sync state. After a rebase, execute from the snapshot without rereading archived summaries. If NEXT begins a new phase, checkpoint that phase before acting. Travel changes conversation context only; inspect disk and external systems directly. + +For archive round trips, checkpoint-name collisions, travel failure, indeterminate mutation, exhausted context refresh, or ambiguous bases → load the context-management skill. -Preflight a distinct user goal on the branch that will carry it: after any required finished-chain transition and before managed work, call this tool with a semantic `-start` name. Also checkpoint later phase, attempt, burst, risky step, pause, milestone, or completion boundaries with a semantic `-start`, `-paused`, or `-done` name. Names are unique across the session tree and case-sensitive. Omitting target labels the nearest meaningful USER/AI turn; an explicit checkpoint name or node ID can label older history. This tool creates recoverability by labeling history without branching or folding the active context. +Preflight a distinct user goal on the branch that will carry it: after any finished-chain transition and before managed work makes rewind expensive, call this tool with a semantic `-start` name so the managed chain can begin. Also label later phase, attempt, burst, risky step, pause, milestone, or completion boundaries with a semantic `-start`, `-paused`, or `-done` name. Names are unique across the session tree and case-sensitive. Omitting target labels the nearest meaningful USER/AI turn; an explicit checkpoint name or node ID can label older history. This tool creates recoverability by labeling history without branching or folding the active context. -Inspect session structure and context evidence through one view: `active`, `checkpoints`, `search`, or `tree`. Omit view for `active`. Timeline reports active summary depth and projected depth for candidate bases. Use `checkpoints` or `search` to compare non-obvious targets; use `tree` only when topology matters. Choose a target by boundary or cold start, not proximity. +Inspect session structure and context evidence through one view: `active`, `checkpoints`, `search`, or `tree`. Omit view for `active`. Timeline reports active summary depth and projected depth for candidate bases. Use `checkpoints` or `search` to compare candidates against the named boundary or cold start; use `tree` only when topology matters. Choose by boundary or cold start, not by proximity or anchor gravity. -Apply one recoverable context transition: fold a named boundary or rebase accumulated summaries to the earliest safe base. Resolve a checkpoint, node ID, or root; complete validation before mutation; optionally bookmark the abandoned raw path. Use timeline evidence for target comparison. Travel reports structural and context deltas but cannot prove boundary quality or cold start completeness. It must run alone in its assistant tool batch; mixed tool batches are rejected before mutation. +Apply one recoverable context transition: fold a named boundary or rebase accumulated summaries to the earliest safe base. Resolve a checkpoint, node ID, or root; complete validation before mutation; optionally bookmark the abandoned raw path. Use timeline evidence for target comparison. Travel reports structural and context deltas but cannot prove boundary quality or cold start completeness. Mixed tool batches are rejected before mutation. -Recoverability created. Continue the current working set; choose any later fold target by boundary, not by nearest-anchor gravity. +Recoverability confirmed. Continue the current working set; any later fold target is chosen by boundary, not nearest-anchor gravity. -Milestone archived. Use it as a recovery pointer. If this closes a stable chain or another fold would stack, run a rebase check before continuing. +Milestone archived. Use it as a recovery pointer. If this closes a stable chain or another fold would stack, run a rebase check against the working-set invariant before continuing. @@ -133,7 +165,7 @@ View `active` selected. Continue from the visible spine; inspect another view on -Active summarized history is present. When the current event is a rebase trigger or the next fold would stack, test structural reset and cold start from the earliest safe base; root is a candidate, not a verdict. +Active summarized history is present. When the current situation is a rebase trigger or the next fold would stack, test structural reset and cold start from the earliest safe base; root passes only when the cold-start test passes. @@ -149,15 +181,15 @@ View `tree` selected. Use branch topology to verify front ownership and target p -Checkpoint the next phase before its first action, then execute the handoff NEXT. +Anchor the next phase with a checkpoint, then execute the handoff NEXT. -Answer from the handoff branch; do not reintroduce archived process into the final response. +Final answer sources the handoff working set; keep archived process detail off the spine. -Search existing checkpoints, preserve the semantic base, and add the smallest useful scope, ordinal, or date. Do not overwrite the existing recovery target. +Search existing checkpoints, preserve the semantic base, and add the smallest useful scope, ordinal, or date. Leave the existing recovery target unchanged. diff --git a/skills/context-management/SKILL.md b/skills/context-management/SKILL.md index 5bce33ad..a5ad3e7a 100644 --- a/skills/context-management/SKILL.md +++ b/skills/context-management/SKILL.md @@ -5,7 +5,7 @@ description: "Resolve advanced ACM cases: ambiguous earliest-safe-base selection # Advanced Context Management -The always-on **CORE owns the normal path**: ordinary checkpointing, clear phase folds, clear burst folds, pressure checks, task-end handling, and an obvious rebase all stay there. This Skill resolves one advanced condition at a time. +The always-on **CORE owns the normal path**: the working-set invariant, judgment layers, decision smells, recognizable moments, fold and rebase criteria, ordinary checkpointing, clear phase folds, clear burst folds, rebase checks under pressure, task-end handling, and an obvious rebase all stay there. This Skill resolves one advanced condition at a time when CORE's criteria pass but the decision remains ambiguous or a result reports an exceptional outcome. Select the reference whose observable condition is active: diff --git a/src/checkpoint-tool.ts b/src/checkpoint-tool.ts index 8fdf53c9..8d78500e 100644 --- a/src/checkpoint-tool.ts +++ b/src/checkpoint-tool.ts @@ -47,7 +47,7 @@ export function registerCheckpointTool(pi: ExtensionAPI): void { description: TOOL_DESCRIPTIONS.checkpoint, promptSnippet: "Label a recoverable session boundary without changing context", promptGuidelines: [ - "Use acm_checkpoint to preflight each distinct user goal before managed work and to label later phase, burst, pause, milestone, or completion boundaries.", + "Use acm_checkpoint to preflight each distinct user goal before managed work makes rewind expensive, and to label later phase, burst, pause, milestone, or completion boundaries.", ], parameters: schema, renderShell: "self", diff --git a/src/context-usage-nudge.ts b/src/context-usage-nudge.ts index fe303947..135ce6d6 100644 --- a/src/context-usage-nudge.ts +++ b/src/context-usage-nudge.ts @@ -154,17 +154,17 @@ export function buildContextUsageNudgeMessage(nudge: PendingContextUsageNudge): const guidance = nudge.level === 30 ? [ - "Continue the current work normally. At the next natural semantic boundary, consider whether current task requirements permit a safe fold or rebase travel.", - "Travel is optional, but keeping the active context small is preferred when it can be done without losing needed working state.", + "Continue the current work normally. At the next natural semantic boundary, check decision smells and whether fold criteria can pass against the working-set invariant.", + "Prefer a smaller active working set when a complete handoff can preserve needed state and make NEXT executable; otherwise keep required detail live.", ] : nudge.level === 50 ? [ - "Context pressure is becoming material. Based on the current task requirements, actively look for the next safe opportunity for a fold or rebase travel.", - "Travel is recommended when a complete handoff can preserve the required working state and make NEXT executable. Do not travel if important context is still needed, but prefer returning to a smaller active context when safely possible.", + "Context pressure is becoming material. Raise the priority of a rebase check, and look for the next recognizable moment where fold criteria and cold start can pass.", + "Travel earns its place only when the handoff preserves required working state and makes NEXT executable. Prefer restoring a smaller working set when those criteria hold.", ] : [ - "Context pressure is high. At the earliest safe semantic boundary, strongly consider a fold or rebase travel if current task requirements allow a complete and recoverable handoff.", - "Keeping the active context small is strongly preferred, but correctness, task continuity, and recoverability take priority. If no safe travel is available, continue normally; native compaction is acceptable for a genuinely long task.", + "Context pressure is high. At the earliest safe semantic boundary, prefer a fold or rebase when fold criteria and cold start pass.", + "Correctness, task continuity, recoverability, and cold start outrank pressure. If no safe transition is available, continue with required detail live; native compaction is acceptable for a genuinely long task.", ]; return { diff --git a/src/generated-guidance.ts b/src/generated-guidance.ts index cd1f39f3..f09f79ae 100644 --- a/src/generated-guidance.ts +++ b/src/generated-guidance.ts @@ -3,28 +3,28 @@ export const ACM_CORE_MARKER = ""; -export const ACM_CORE = "## Agentic Context Management CORE v1\n\nA context window is a **working set**: the exact material needed for the next action. Keep that detail live. Compress finished process into a recoverable **handoff** only at a named **boundary**.\n\n### ACM preflight\n\nA distinct user goal begins with an **ACM preflight** on the branch that will carry it. First complete any required `New request arrives over finished work` transition; then call `acm_checkpoint` with a semantic `-start` name before managed work. When no finished-chain transition is required, the checkpoint call is the first action. Managed work includes investigation, planning, delegation, any non-ACM tool call, file or external side effects, and multi-step reasoning. The preflight is complete only when the tool result confirms that the checkpoint was created or reused. If the call fails, follow the recovery guidance in its result before proceeding. A text-only direct reply requiring no managed work stays outside the managed-chain workflow.\n\n### Vocabulary\n\n- **working set** — live detail the next action will reason over.\n- **boundary** — the semantic edge being compressed: a burst, phase, failed direction, batch item, or task chain.\n- **handoff** — the executable state that survives a fold; an evidence index, not a dump.\n- **archive** — the raw off-path history and its recovery checkpoint or node ID.\n- **chain** — continuous work serving one user goal.\n- **burst** — temporary expansion from broad reads, logs, searches, diffs, or subagents.\n- **anchor gravity** — the misleading pull of the nearest checkpoint. Name the boundary before choosing a target.\n- **rebase** — move all surviving state into one authoritative snapshot at the earliest safe base, retiring accumulated summary depth.\n- **cold start** — a fresh agent can execute `NEXT` from the snapshot and its pointers without archived summaries.\n\n### Normal state transitions\n\n| Event | Required transition |\n|---|---|\n| Phase, attempt, or batch item starts | checkpoint its `-start` boundary before acting |\n| Unbounded burst or risky step is next | checkpoint before output or side effects arrive |\n| Findings are distilled and raw trail is no longer needed by NEXT | inspect timeline evidence, then fold the named boundary |\n| Direction is rejected or superseded | put the dead direction in Exclusions, preserve surviving facts, then fold to its start |\n| Final answer is next | run a rebase check; if it is not ready, fold the task chain when that removes material structure or create a unique `-done` checkpoint and answer directly |\n| New request arrives over finished work | run a rebase check before starting the new chain; if it is not ready, fold the finished local chain |\n| Another fold would stack on an active summary, a stable subchain closes, or context pressure rises | run a rebase check before choosing a local fold or accepting native compaction |\n\nContext pressure triggers a rebase check. It does not lower the cold start gate or authorize travel by itself.\n\n### Fold gate\n\nTravel only when all three criteria hold:\n\n1. **Boundary named** — say exactly what raw process leaves the working set.\n2. **NEXT executable** — write one immediate action that can run from the handoff.\n3. **Raw recoverable** — preserve omitted detail through a checkpoint, node ID, file, command, URL, commit, or other pointer.\n\nCall `acm_travel` alone in its assistant tool batch; do not combine this context mutation with sibling tool calls.\n\nBoundary decides whether folding is valid. Timeline reports evidence; it does not decide for you. A target must sit before the named boundary. Nearest and earliest anchors are candidates, never defaults.\n\n### Rebase gate\n\nA **rebase** is a structural reset across accumulated summary depth. It uses the fold gate and adds two completion criteria: **structural reset** and **cold start**.\n\nStructural reset passes only when the target precedes an active `branch_summary` that leaves the spine and projected summary depth does not grow. Equal depth passes only when the new snapshot replaces an old summary; a target after all active summaries fails.\n\nCold start passes only when `NEXT` is immediately executable, every surviving front and invariant is in the snapshot or a usable direct pointer, and ordinary execution needs no archived summary.\n\nBuild one authoritative snapshot, then evaluate candidate bases from earliest to latest. Choose the earliest base that passes both criteria. Root is ideal when it passes; it is never presumed safe.\n\nIf no candidate passes, rebase is not ready: keep required detail live, use a local fold, or accept native compaction.\n\n### Handoff contract\n\nEvery handoff uses these seven slots in this order. Write `none` when a category is empty.\n\n```text\nGoal: \nState: \nEvidence: \nExternal: \nExclusions: \nRecover: \nNEXT: \n```\n\nThe runtime can validate slot structure. Semantic completeness, target quality, recoverability sufficiency, and whether NEXT is genuinely executable remain agent completion criteria.\n\n### Representative transitions\n\n**Local fold example**\n\n```text\nGoal: Fix high CPU while preserving the sidebar.\nState: Profiling proved hidden tabs retain workers; implementation is next.\nEvidence: artifacts/sidebar-profile.json; src/sidebar/session-manager.ts.\nExternal: profiler stopped; no files changed.\nExclusions: disabling the sidebar violates the goal.\nRecover: sidebar-profile-start.\nNEXT: Checkpoint sidebar-lifecycle-fix-start, then inspect worker disposal.\n```\n\n**Finished-chain rebase example**\n\nRebase the finished chain to the earliest base that passes cold start:\n\n```text\nGoal: Release fix complete; new request is \"Add dry-run mode to migration.\"\nState: v2.4.1 is validated and pushed; migration work has not started.\nEvidence: commit 1a2b3c4; tag v2.4.1; full test output.\nExternal: commit and tag pushed to origin.\nExclusions: version-detection workaround remains rejected.\nRecover: release-fix-done.\nNEXT: Checkpoint migration-dry-run-start, then inspect the migration command entry point.\n```\n\nAfter travel, confirm resolved target, summary leaf, backup outcome, raw usage/message/summary-depth deltas, and context-sync state. After a rebase, execute from the snapshot without rereading archived summaries. If NEXT begins a new phase, checkpoint that phase before acting. Travel changes conversation context only; inspect disk and external systems directly."; +export const ACM_CORE = "## Agentic Context Management CORE v1\n\nA context window is a **working set**: the exact material needed for the next action. Process detail accumulates naturally; keep live what NEXT needs. ACM keeps that growth recoverable by naming a **boundary** before folding finished process into a **handoff** that leaves **NEXT** executable.\n\n### Working-set invariant\n\nConserve the working set: live context holds only what NEXT will reason over. Everything else is still needed live, or already folded into an executable handoff whose **archive** remains recoverable. Tools enforce structure; you judge when a transition earns its place against this invariant.\n\n### How judgment layers fit together\n\n**Decision smells** prompt a pause when something feels off. **Recognizable moments** map a situation to the lightest reasonable transition. **Fold criteria** and **rebase criteria** must pass before travel earns its place. **Failure shapes** describe what went wrong when a fold or target choice was too aggressive or too late.\n\n### Vocabulary\n\n- **working set** — live detail the next action will reason over.\n- **boundary** — the semantic edge being compressed: a burst, phase, failed direction, batch item, or task chain.\n- **handoff** — the executable state that survives a fold; an evidence index, not a dump.\n- **archive** — the raw off-path history and its recovery checkpoint or node ID.\n- **chain** — continuous work serving one user goal.\n- **burst** — temporary expansion from broad reads, logs, searches, diffs, or subagents.\n- **anchor gravity** — the misleading pull of the nearest checkpoint. Name the boundary before choosing a target.\n- **rebase** — move all surviving state into one authoritative snapshot at the earliest safe base, retiring accumulated summary depth.\n- **cold start** — a fresh agent can execute `NEXT` from the snapshot and its pointers without archived summaries.\n\n### ACM preflight\n\nRecoverability has a cost curve: once managed work expands the trail, rewind without an anchor is expensive. A distinct user goal therefore begins with an **ACM preflight** on the branch that will carry it—opening a **managed chain**. After any needed finished-chain transition (see Recognizable moments), call `acm_checkpoint` with a semantic `-start` name before managed work: investigation, planning, delegation, any non-ACM tool call, file or external side effects, or multi-step reasoning. The preflight is complete only when the tool result confirms the checkpoint was created or reused. If the call fails, follow the recovery guidance in its result before proceeding. A **lightweight reply**—no evidence lookup, side effects, or multi-step reasoning—stays outside the managed-chain workflow.\n\n### Decision smells\n\nQuick checks against the working-set invariant—pause-worthy, not automatic commands:\n\n- **Nearest checkpoint feels obvious** — **anchor gravity**; name the boundary before choosing a target.\n- **Summary upon summary** — rebase trigger; test structural reset and **cold start** from the earliest safe base.\n- **User goal changed over finished work** — fold or rebase the prior chain before the new one starts.\n- **Exploration done, implementation next** — raw burst can leave once distilled into the handoff.\n\n### Recognizable moments\n\nWhen one of these situations arises, ask whether the working-set invariant still holds; restore it with the lightest transition that does:\n\n| What you can observe | What becomes reasonable |\n|---|---|\n| Phase, attempt, or batch item starts | checkpoint its `-start` boundary before acting |\n| Unbounded burst or risky step is next | checkpoint before output or side effects arrive |\n| Findings are distilled and raw trail is no longer needed by NEXT | inspect timeline evidence, then fold the named boundary |\n| Direction is rejected or superseded | put the dead direction in Exclusions, preserve surviving facts, then fold to its start |\n| Final answer is next | run a rebase check; if it is not ready, fold the task chain when that removes material structure or create a unique `-done` checkpoint and answer directly |\n| New request arrives over finished work | run a rebase check before starting the new chain; if it is not ready, fold the finished local chain |\n| Another fold would stack on an active summary, a stable subchain closes, or context pressure rises | run a rebase check before choosing a local fold or accepting native compaction |\n\nContext pressure raises priority of a **rebase check**. Cold start remains hard; travel earns its place only when fold criteria—and any rebase criteria—still pass.\n\n### Fold criteria\n\nTravel when all three criteria hold:\n\n1. **Boundary named** — say exactly what raw process leaves the working set.\n2. **NEXT executable** — write one immediate action that can run from the handoff.\n3. **Raw recoverable** — preserve omitted detail through a checkpoint, node ID, file, command, URL, commit, or other pointer.\n\nBefore `acm_travel`, answer in one line: what leaves, what pointer recovers it, and what single action is NEXT.\n\nBoundary decides whether folding is valid. Timeline reports evidence; it does not decide for you. A target must sit before the named boundary. Nearest and earliest anchors are candidates against boundary and cold start—not answers by proximity or anchor gravity.\n\n### Rebase criteria\n\nA **rebase** is a structural reset across accumulated summary depth. It uses the fold criteria and adds **structural reset** and **cold start**.\n\nStructural reset passes only when the target precedes an active `branch_summary` that leaves the spine and projected summary depth does not grow. Equal depth passes only when the new snapshot replaces an old summary; a target after all active summaries fails.\n\nCold start passes only when `NEXT` is immediately executable, every surviving front and invariant is in the snapshot or a usable direct pointer, and ordinary execution needs no archived summary.\n\nBuild one authoritative snapshot, then evaluate candidates from earliest to latest. Choose the earliest base that passes both. Root is ideal when it passes; treat it as a candidate until both criteria clear.\n\nIf no candidate passes, rebase is not ready: keep required detail live, use a local fold, or accept native compaction. Ambiguous base selection, interleaved fronts, or raw-node fallback → load the context-management skill.\n\n### Failure shapes\n\n- **Folded too early** — NEXT stalls; archived detail is needed again.\n- **Folded too late** — working set bloats; context pressure rises.\n- **Wrong target** — a parked front, invariant, or recovery pointer vanishes from the spine.\n- **Rebase skipped** — summaries stack; cold start degrades across folds.\n\n### Handoff shape\n\nEvery handoff uses these seven slots in order. Write `none` when empty. Evidence is a pointer index, not a dump of the folded trail.\n\n```text\nGoal: \nState: \nEvidence: \nExternal: \nExclusions: \nRecover: \nNEXT: \n```\n\nThe runtime validates slot structure and rejects mixed tool batches. Semantic completeness, target quality, recoverability sufficiency, and whether NEXT is genuinely executable remain agent completion criteria.\n\n### Representative transitions\n\n**Local fold example**\n\nWhy: findings are distilled; raw trail is unused by NEXT; Recover keeps the burst archive.\n\n```text\nGoal: Fix high CPU while preserving the sidebar.\nState: Profiling proved hidden tabs retain workers; implementation is next.\nEvidence: artifacts/sidebar-profile.json; src/sidebar/session-manager.ts.\nExternal: profiler stopped; no files changed.\nExclusions: disabling the sidebar violates the goal.\nRecover: sidebar-profile-start.\nNEXT: Checkpoint sidebar-lifecycle-fix-start, then inspect worker disposal.\n```\n\n**Finished-chain rebase example**\n\nWhy: prior chain is stable; one cold-start snapshot carries the new request from the earliest safe base.\n\nRebase the finished chain to the earliest base that passes cold start:\n\n```text\nGoal: Release fix complete; new request is \"Add dry-run mode to migration.\"\nState: v2.4.1 is validated and pushed; migration work has not started.\nEvidence: commit 1a2b3c4; tag v2.4.1; full test output.\nExternal: commit and tag pushed to origin.\nExclusions: version-detection workaround remains rejected.\nRecover: release-fix-done.\nNEXT: Checkpoint migration-dry-run-start, then inspect the migration command entry point.\n```\n\nAfter travel, confirm resolved target, summary leaf, backup outcome, raw usage/message/summary-depth deltas, and context-sync state. After a rebase, execute from the snapshot without rereading archived summaries. If NEXT begins a new phase, checkpoint that phase before acting. Travel changes conversation context only; inspect disk and external systems directly.\n\nFor archive round trips, checkpoint-name collisions, travel failure, indeterminate mutation, exhausted context refresh, or ambiguous bases → load the context-management skill."; export const TOOL_DESCRIPTIONS = { - "checkpoint": "Preflight a distinct user goal on the branch that will carry it: after any required finished-chain transition and before managed work, call this tool with a semantic `-start` name. Also checkpoint later phase, attempt, burst, risky step, pause, milestone, or completion boundaries with a semantic `-start`, `-paused`, or `-done` name. Names are unique across the session tree and case-sensitive. Omitting target labels the nearest meaningful USER/AI turn; an explicit checkpoint name or node ID can label older history. This tool creates recoverability by labeling history without branching or folding the active context.", - "timeline": "Inspect session structure and context evidence through one view: `active`, `checkpoints`, `search`, or `tree`. Omit view for `active`. Timeline reports active summary depth and projected depth for candidate bases. Use `checkpoints` or `search` to compare non-obvious targets; use `tree` only when topology matters. Choose a target by boundary or cold start, not proximity.", - "travel": "Apply one recoverable context transition: fold a named boundary or rebase accumulated summaries to the earliest safe base. Resolve a checkpoint, node ID, or root; complete validation before mutation; optionally bookmark the abandoned raw path. Use timeline evidence for target comparison. Travel reports structural and context deltas but cannot prove boundary quality or cold start completeness. It must run alone in its assistant tool batch; mixed tool batches are rejected before mutation." + "checkpoint": "Preflight a distinct user goal on the branch that will carry it: after any finished-chain transition and before managed work makes rewind expensive, call this tool with a semantic `-start` name so the managed chain can begin. Also label later phase, attempt, burst, risky step, pause, milestone, or completion boundaries with a semantic `-start`, `-paused`, or `-done` name. Names are unique across the session tree and case-sensitive. Omitting target labels the nearest meaningful USER/AI turn; an explicit checkpoint name or node ID can label older history. This tool creates recoverability by labeling history without branching or folding the active context.", + "timeline": "Inspect session structure and context evidence through one view: `active`, `checkpoints`, `search`, or `tree`. Omit view for `active`. Timeline reports active summary depth and projected depth for candidate bases. Use `checkpoints` or `search` to compare candidates against the named boundary or cold start; use `tree` only when topology matters. Choose by boundary or cold start, not by proximity or anchor gravity.", + "travel": "Apply one recoverable context transition: fold a named boundary or rebase accumulated summaries to the earliest safe base. Resolve a checkpoint, node ID, or root; complete validation before mutation; optionally bookmark the abandoned raw path. Use timeline evidence for target comparison. Travel reports structural and context deltas but cannot prove boundary quality or cold start completeness. Mixed tool batches are rejected before mutation." } as const; export const GUIDANCE_CUES = { - "checkpointStart": "Recoverability created. Continue the current working set; choose any later fold target by boundary, not by nearest-anchor gravity.", - "checkpointDone": "Milestone archived. Use it as a recovery pointer. If this closes a stable chain or another fold would stack, run a rebase check before continuing.", - "rebaseCheck": "Active summarized history is present. When the current event is a rebase trigger or the next fold would stack, test structural reset and cold start from the earliest safe base; root is a candidate, not a verdict.", + "checkpointStart": "Recoverability confirmed. Continue the current working set; any later fold target is chosen by boundary, not nearest-anchor gravity.", + "checkpointDone": "Milestone archived. Use it as a recovery pointer. If this closes a stable chain or another fold would stack, run a rebase check against the working-set invariant before continuing.", + "rebaseCheck": "Active summarized history is present. When the current situation is a rebase trigger or the next fold would stack, test structural reset and cold start from the earliest safe base; root passes only when the cold-start test passes.", "timelineActive": "View `active` selected. Continue from the visible spine; inspect another view only when target identity or branch topology is not yet checkable.", "timelineCheckpoints": "View `checkpoints` selected. Compare named candidates against the boundary, then inspect the chosen target's branch only if its placement remains ambiguous.", "timelineSearch": "View `search` selected. Narrow by semantic label, node ID, or content until the pre-boundary target is identifiable; use the returned raw node ID when no checkpoint fits.", "timelineTree": "View `tree` selected. Use branch topology to verify front ownership and target placement; narrow with search or checkpoints if the tree is truncated.", - "travelPhase": "Checkpoint the next phase before its first action, then execute the handoff NEXT.", - "travelTask": "Answer from the handoff branch; do not reintroduce archived process into the final response." + "travelPhase": "Anchor the next phase with a checkpoint, then execute the handoff NEXT.", + "travelTask": "Final answer sources the handoff working set; keep archived process detail off the spine." } as const; export const RECOVERY_GUIDANCE = { - "nameCollision": "Search existing checkpoints, preserve the semantic base, and add the smallest useful scope, ordinal, or date. Do not overwrite the existing recovery target.", + "nameCollision": "Search existing checkpoints, preserve the semantic base, and add the smallest useful scope, ordinal, or date. Leave the existing recovery target unchanged.", "hostCapability": "The supported Host Bridge capability is unavailable or malformed. Stop mutation and report the named capability error; verify the exact supported Pi version before retrying.", "rollbackFailed": "The backup label remains in the tree. Record its label and entry ID as a recovery pointer before any retry.", "branchRolledBack": "Branch creation failed before mutation; the new backup label was rolled back. Correct the reported host failure before retrying.", diff --git a/src/travel-tool.ts b/src/travel-tool.ts index 7d1bbfae..99854182 100644 --- a/src/travel-tool.ts +++ b/src/travel-tool.ts @@ -86,8 +86,8 @@ export function registerTravelTool(pi: ExtensionAPI, runtime: AcmSessionRuntime) description: TOOL_DESCRIPTIONS.travel, promptSnippet: "Fold a named boundary or rebase summaries into a recoverable handoff", promptGuidelines: [ - "Use acm_travel only when the boundary is named, NEXT is executable from the handoff, and omitted raw history remains recoverable through a direct pointer.", - "Call acm_travel alone in its assistant tool batch; never combine this context mutation with sibling tool calls.", + "Use acm_travel when fold criteria hold: the boundary is named, NEXT is executable from the handoff, and omitted raw history remains recoverable through a direct pointer.", + "Call acm_travel alone in its assistant tool batch; mixed batches are rejected before mutation.", ], parameters: schema, executionMode: "sequential", diff --git a/test/context-nudge.test.ts b/test/context-nudge.test.ts index cd1bcff2..3d94aa52 100644 --- a/test/context-nudge.test.ts +++ b/test/context-nudge.test.ts @@ -98,7 +98,9 @@ describe("ACM context usage reminders", () => { }); expect(fixture.sentMessages[0]?.message.content).toContain("[ACM Context Reminder · 30% tier]"); expect(fixture.sentMessages[0]?.message.content).toContain("next natural semantic boundary"); - expect(fixture.sentMessages[0]?.message.content).toContain("Travel is optional"); + expect(fixture.sentMessages[0]?.message.content).toContain("decision smells"); + expect(fixture.sentMessages[0]?.message.content).toContain("fold criteria"); + expect(fixture.sentMessages[0]?.message.content).toContain("working-set invariant"); fixture.setUsagePercent(35); await fixture.emit("context", { messages: [] }); @@ -248,8 +250,9 @@ describe("ACM context usage reminders", () => { }, options: { deliverAs: "followUp" }, }); - expect(fixture.sentMessages[0]?.message.content).toContain("actively look for the next safe opportunity"); - expect(fixture.sentMessages[0]?.message.content).toContain("Travel is recommended"); + expect(fixture.sentMessages[0]?.message.content).toContain("Raise the priority of a rebase check"); + expect(fixture.sentMessages[0]?.message.content).toContain("Travel earns its place only when"); + expect(fixture.sentMessages[0]?.message.content).toContain("recognizable moment"); }); test("a successful travel starts a new baseline-only reminder cycle", async () => { diff --git a/test/guidance-quality.test.ts b/test/guidance-quality.test.ts index 2a4cc849..b0c6c317 100644 --- a/test/guidance-quality.test.ts +++ b/test/guidance-quality.test.ts @@ -4,79 +4,73 @@ import { ACM_CORE, GUIDANCE_CUES, TOOL_DESCRIPTIONS } from "../src/generated-gui const skillFile = (path: string) => Bun.file(new URL(`../skills/context-management/${path}`, import.meta.url)).text(); describe("ACM guidance quality", () => { - test("preserves the base workflow while bounding semantic rebase", () => { - for (const baseBehavior of [ + test("unifies judgment layers on the working-set invariant", () => { + for (const situation of [ "Phase, attempt, or batch item starts", "Unbounded burst or risky step is next", "Findings are distilled", "Direction is rejected or superseded", "Final answer is next", ]) { - expect(ACM_CORE).toContain(baseBehavior); + expect(ACM_CORE).toContain(situation); } - expect(ACM_CORE).toContain("Local fold example"); - expect(ACM_CORE).toContain("Finished-chain rebase example"); - expect(ACM_CORE).not.toContain("Failed-direction example"); - expect(ACM_CORE).toContain("Structural reset passes only when"); - expect(ACM_CORE).toContain("projected summary depth does not grow"); - expect(ACM_CORE).toContain("Cold start passes only when"); - expect(ACM_CORE.length).toBeLessThan(7500); - expect(GUIDANCE_CUES.rebaseCheck).toContain("Active summarized history is present"); - expect(ACM_CORE).toContain("Call `acm_travel` alone in its assistant tool batch"); - expect(TOOL_DESCRIPTIONS.travel).toContain("must run alone in its assistant tool batch"); + expect(ACM_CORE).toContain("Process detail accumulates naturally"); + expect(ACM_CORE).toContain("keep live what NEXT needs"); + expect(ACM_CORE).toContain("### Working-set invariant"); + expect(ACM_CORE).toContain("### How judgment layers fit together"); + expect(ACM_CORE).toContain("**Decision smells** prompt a pause"); + expect(ACM_CORE).toContain("**Recognizable moments** map a situation"); + expect(ACM_CORE).toContain("**Failure shapes** describe what went wrong"); + expect(ACM_CORE).toContain("### Decision smells"); + expect(ACM_CORE).toContain("### Recognizable moments"); + expect(ACM_CORE).toContain("lightest transition that does"); + expect(ACM_CORE).not.toContain("| Event | Required transition |"); + expect(ACM_CORE).not.toContain("| Situation | Judgment |"); + expect(ACM_CORE).toContain("### Fold criteria"); + expect(ACM_CORE).toContain("### Rebase criteria"); + expect(ACM_CORE).toContain("### Failure shapes"); + expect(ACM_CORE).toContain("Before `acm_travel`, answer in one line"); + expect(ACM_CORE).toContain("Cold start remains hard"); + expect(ACM_CORE).toContain("not answers by proximity or anchor gravity"); + expect(ACM_CORE).toContain("Why: findings are distilled"); + expect(ACM_CORE).not.toContain("**NEXT stalls**"); + expect(ACM_CORE).toContain("Ambiguous base selection, interleaved fronts, or raw-node fallback"); + expect(ACM_CORE).toContain("load the context-management skill"); + expect(ACM_CORE).toContain("managed-chain workflow"); + expect(ACM_CORE).toContain("without rereading archived summaries"); + expect(GUIDANCE_CUES.rebaseCheck).toContain("cold-start test passes"); + expect(TOOL_DESCRIPTIONS.travel).toContain("Mixed tool batches are rejected"); }); - test("front-loads a checkable checkpoint preflight", () => { + test("front-loads recoverability preflight as a cost judgment", () => { + const invariantIndex = ACM_CORE.indexOf("### Working-set invariant"); + const layersIndex = ACM_CORE.indexOf("### How judgment layers fit together"); const preflightIndex = ACM_CORE.indexOf("### ACM preflight"); const vocabularyIndex = ACM_CORE.indexOf("### Vocabulary"); - expect(preflightIndex).toBeGreaterThan(-1); - expect(preflightIndex).toBeLessThan(vocabularyIndex); - expect(ACM_CORE).toContain("A distinct user goal begins with an **ACM preflight** on the branch that will carry it"); - expect(ACM_CORE).toContain("First complete any required `New request arrives over finished work` transition"); - expect(ACM_CORE).toContain("then call `acm_checkpoint` with a semantic `-start` name before managed work"); - expect(ACM_CORE).toContain("When no finished-chain transition is required, the checkpoint call is the first action"); - expect(ACM_CORE).toContain("Managed work includes investigation, planning, delegation, any non-ACM tool call"); - expect(ACM_CORE).toContain("the checkpoint was created or reused"); - expect(ACM_CORE).toContain("follow the recovery guidance in its result before proceeding"); - expect(ACM_CORE).toContain("A text-only direct reply requiring no managed work stays outside"); - expect(ACM_CORE).toContain("live detail the next action will reason over"); - expect(ACM_CORE).toContain("Name the boundary before choosing a target"); - expect(ACM_CORE).toContain("without archived summaries"); - expect(ACM_CORE).toContain("checkpoint its `-start` boundary before acting"); - expect(ACM_CORE).toContain("checkpoint before output or side effects arrive"); - expect(ACM_CORE).not.toContain("| New chain starts |"); - expect(TOOL_DESCRIPTIONS.checkpoint.startsWith("Preflight a distinct user goal")).toBe(true); - expect(TOOL_DESCRIPTIONS.checkpoint.length).toBeLessThan(800); + expect(invariantIndex).toBeGreaterThan(-1); + expect(invariantIndex).toBeLessThan(layersIndex); + expect(layersIndex).toBeLessThan(vocabularyIndex); + expect(preflightIndex).toBeGreaterThan(vocabularyIndex); + expect(ACM_CORE).toContain("against this invariant"); + expect(ACM_CORE).toContain("Recoverability has a cost curve"); + expect(ACM_CORE).toContain("opening a **managed chain**"); + expect(ACM_CORE).not.toContain("the checkpoint call is the first action"); + expect(TOOL_DESCRIPTIONS.checkpoint).toContain("before managed work makes rewind expensive"); }); test("routes one advanced condition at a time and reroutes on state change", async () => { const skill = await skillFile("SKILL.md"); - expect(skill).toContain("CORE owns the normal path"); - expect(skill).toContain("ordinary checkpointing"); - expect(skill).toContain("clear phase folds"); - expect(skill).toContain("clear burst folds"); - expect(skill).toContain("task-end handling"); + expect(skill).toContain("working-set invariant"); + expect(skill).toContain("decision smells"); + expect(skill).toContain("judgment layers"); expect(skill).toContain("Load one reference at a time"); - expect(skill).toContain("observable condition changes"); - expect(skill).toContain("replace the active reference"); }); - test("keeps target and recovery criteria factual and checkable", async () => { - const target = await skillFile("references/target-selection.md"); - const archive = await skillFile("references/archive-recovery.md"); - const exceptional = await skillFile("references/exceptional-recovery.md"); - - expect(target).toContain("tree topology orders them"); - expect(target).toContain("must precede at least one active `branch_summary`"); - expect(target).toContain("projected summary depth must not grow"); - expect(target).toContain("every surviving item has one authoritative home"); - expect(archive).toContain("Pending is scheduled work, not success"); - expect(archive).toContain("return to the Skill router and replace this reference"); - expect(archive).not.toContain("structural effect"); - expect(exceptional).toContain("Backup rollback failure"); - expect(exceptional).toContain("branch creation was not applied"); - expect(exceptional).toContain("Indeterminate branch mutation"); - expect(exceptional).toContain("mutation may have landed"); + test("re-anchors tool cues with observables and positive targets", () => { + expect(GUIDANCE_CUES.checkpointStart).toContain("Recoverability confirmed"); + expect(GUIDANCE_CUES.travelTask).toContain("handoff working set"); + expect(GUIDANCE_CUES.travelTask).not.toContain("do not reintroduce"); + expect(GUIDANCE_CUES.checkpointDone).toContain("working-set invariant"); }); }); diff --git a/test/tool-descriptions.test.ts b/test/tool-descriptions.test.ts index 46274392..cec8e5ad 100644 --- a/test/tool-descriptions.test.ts +++ b/test/tool-descriptions.test.ts @@ -13,6 +13,7 @@ describe("ACM tool description contract", () => { expect(timelineTool).toContain("description: TOOL_DESCRIPTIONS.timeline"); expect(travelTool).toContain("description: TOOL_DESCRIPTIONS.travel"); expect(generatedGuidance).toContain("Preflight a distinct user goal on the branch that will carry it"); + expect(generatedGuidance).toContain("before managed work makes rewind expensive"); expect(generatedGuidance).toContain("Names are unique across the session tree and case-sensitive"); expect(generatedGuidance).toContain("Omitting target labels the nearest meaningful USER/AI turn"); expect(generatedGuidance).toContain("without branching or folding the active context");