cairn-ai 0.2.7 → 0.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -12,6 +12,6 @@ Procedure:
12
12
  2. Every agent reads project-root `MEMORY.md` when present and continues without repository memory when absent before assigned discovery or update work.
13
13
  3. Explore repository facts with focused tools.
14
14
  4. Use `explorer` for read-only domain discovery and contradiction checks when available; use `worker` for exact references, command checks, and bounded evidence capture. When subagent tools are available, each agent may recursively delegate bounded sub-tasks to subagents. Tell every delegated agent and child subagent to read the project-root `MEMORY.md` before work, keep scope, and preserve others' edits.
15
- 5. Write details to `docs/memory/<domain>.md`.
15
+ 5. Write details to `docs/memory/<domain>.md`. When a lesson or correction recurs and is machine-checkable, encode it as a test, lint rule, script, or hook instead of more prose; memory keeps judgment rules and a pointer to the enforcing check.
16
16
  6. Keep `MEMORY.md` as a short index.
17
17
  7. Use the OS locale for user-visible responses and generated or updated documentation, plans, and memory artifacts unless the user asks for another language.
@@ -14,7 +14,7 @@ Procedure:
14
14
 
15
15
  1. Every agent reads project-root `MEMORY.md` when present and continues without repository memory when absent before repository exploration, tool selection, work, or delegation.
16
16
  2. Keep routine known-target Git/GitHub operations plan/goal-free unless they require code edits, conflict resolution, destructive recovery, release/deploy, or design. For non-trivial implementation or continuation of planned work, write the initial repository plan and `PLAN.md` entry from the request and `MEMORY.md`. Include a planned `triage-plan` task and anticipated execution/verification stages, then start or attach the repository Cairn goal before exploration.
17
- 3. When the surface exposes native plan/goal UI tools, synchronize the same roadmap there before exploration; otherwise record that those tools are unavailable.
17
+ 3. When the surface exposes native plan/goal UI tools, synchronize the same roadmap there before exploration; otherwise record that those tools are unavailable. If an unfinished repository goal already exists when a new implementation request, steer, or drift arrives, do not cancel it to start another. Fold the new work into it with `goal replan` (completed tasks and their evidence are preserved; resume a paused or blocked goal first), synchronize the plan artifact and native UI roadmap, and keep advancing until goal final review. Start a separate goal only when the user explicitly switches work. When a hook reports that another session owns the goal and this session continues that same work, take ownership with `goal claim --session <id>` using the ID shown by the hook.
18
18
  4. Read relevant `docs/memory/*.md` and perform toolcheck, exploration, and Light/Heavy Path triage according to the initial task.
19
19
  5. Update the same plan to a decision-complete implementation revision before implementation.
20
20
  6. If the active or assigned model is Claude-family, read `claude.md` under the locator's `resources.modelGuidance`; if Codex-family, read `codex.md` there.
@@ -9,7 +9,7 @@ Goal: review completed work against plan, memory, policy, and evidence.
9
9
  Procedure:
10
10
 
11
11
  1. Every agent reads project-root `MEMORY.md` when present and continues without repository memory when absent before review work, then reads the plan task and available memory inputs.
12
- 2. Check changed files, tool readiness evidence, and verification evidence.
12
+ 2. Check changed files, tool readiness evidence, and verification evidence. Inspect test, fixture, and verification-command diffs and reject any weakening without a recorded plan reason. For defect fixes, confirm the same-rule sweep was performed and its result recorded.
13
13
  3. Delegate independent read-only review to `explorer` when it materially improves speed or quality; use `worker` only for scoped verification reruns or QA artifacts. When subagent tools are available, each agent may recursively delegate bounded sub-tasks to subagents. Tell every delegated agent and child subagent to read the project-root `MEMORY.md` before work, keep scope, and preserve others' edits.
14
14
  4. Report findings first, ordered by severity, with file and line references. If no issues are found, say so clearly and mention remaining test gaps or residual risk.
15
15
  5. Record review evidence in `docs/plan/<topic>.md`.
@@ -15,7 +15,7 @@ Procedure:
15
15
  1. Every agent reads project-root `MEMORY.md` when present and continues without repository memory when absent before selecting or executing a task.
16
16
  2. Read `PLAN.md`, the detailed plan, and relevant memory notes.
17
17
  3. Read the Codex or Claude model guidance recorded in the plan.
18
- 4. Select one small module task.
18
+ 4. Select one small module task. Read it with `goal task status` (full `goal status` only when receipts are needed). Fold a new request or steer that arrives during an unfinished goal into it with `goal replan` instead of cancelling it, and keep advancing to goal final review.
19
19
  5. Confirm required tool readiness by invoking Node directly with the locator's absolute `entrypoints.cli`, `toolcheck`, and `--root` for the target repository. Follow the installed toolcheck policy before any explicitly approved installation attempt.
20
20
  6. Confirm the complexity triage and selected Light/Heavy Path recorded in the plan. If missing, update the plan before mutating files.
21
21
  7. Spend reasoning on a focused executable test contract before implementation: requirements, invariants, boundaries, and failure modes. Confirm the intended failure, then require only the minimum implementation that passes.
@@ -23,7 +23,7 @@ Procedure:
23
23
  9. For Heavy Path, follow the plan's staged implementation and review gates without role-mapping shortcuts.
24
24
  10. Give implementation only the test contract, failing evidence, exact file ownership, and constraints. Delegate actual implementation edits to `worker` on both paths whenever subagent tools are available. When the subagent tool provides a progress-reporting channel, require subagents to report status to the orchestrator when starting work, when deciding or confirming direction, during periodic progress, and when finishing; immediately relay received status events to the user. If no mid-run reporting channel exists, relay observable events such as assignment, waiting, and final completion. Require delegated subagents to provide a final report before leaving; after capturing the final report and evidence, close or release the completed subagent, then review the final report and evidence before marking the work complete. When subagent tools are available, each agent may recursively delegate bounded sub-tasks to subagents. Tell every delegated agent and child subagent to read the project-root `MEMORY.md` before work, keep scope, and preserve others' edits. If subagent tools are unavailable, the main agent takes over implementation directly and records that takeover in evidence.
25
25
  11. Treat tool exit codes and bounded machine summaries as authoritative. Keep success concise; expand context only around failing tests.
26
- 12. Run focused tests, then one final full repository check through `goal verify -- ...`. Verification defaults to 600,000 ms (10 minutes), is capped at 3,600,000 ms (1 hour), and records evidence only when the starting goal/task identity and pre/post watched fingerprint still match. Select a semantic boundary that proves the claim. The default Git fingerprint excludes ignored files; explicitly `--watch` every ignored artifact needed by the claim. Any relevant later mutation makes affected evidence stale.
26
+ 12. Run focused tests, then one final full repository check through `goal verify -- ...`. Verification defaults to 600,000 ms (10 minutes), is capped at 3,600,000 ms (1 hour), and records evidence only when the starting goal/task identity and pre/post watched fingerprint still match. Select a semantic boundary that proves the claim. The default Git fingerprint excludes ignored files; explicitly `--watch` every ignored artifact needed by the claim. Any relevant later mutation makes affected evidence stale. Never edit, weaken, skip, or delete existing tests, fixtures, or verification commands to make a gate pass unless the plan records the contract change. Reproduce reported defects before fixing, test one hypothesis at a time with a measurement, and after a fix search for other implementations of the same rule or pattern.
27
27
  13. Inspect package lifecycle scripts before package verification and run normal `npm pack --dry-run` by default. Content-producing or unknown lifecycle scripts must never use `--ignore-scripts`; only absent or proven content-neutral scripts may use it while the full-check evidence remains fresh.
28
28
  14. Record tool readiness and verification evidence in `docs/plan/<topic>.md` and update `PLAN.md`.
29
29
  15. If the user asks a side question, status question, or narrow clarification while this task is still active, answer it briefly and then resume the previous active work unless the user explicitly asks to pause, stop, or switch tasks.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cairn",
3
- "version": "0.2.7",
3
+ "version": "0.2.8",
4
4
  "description": "Token-efficient agent harness with tool readiness for Codex, Claude Code, and Antigravity.",
5
5
  "author": {
6
6
  "name": "WkNam"
package/README.ko.md CHANGED
@@ -58,6 +58,13 @@ cairn toolcheck --install --yes --root .
58
58
  - 검증 게이트가 실패하면 Cairn은 한 번 진단하고 작업을 줄이거나 sub-task로 나눈 뒤 두 게이트를 다시 실행합니다.
59
59
  - 같은 작업에서 두 번의 검증 패스가 실패하면 반복 루프를 계속하지 않고 `docs/plan/<topic>.md`에 blocker를 기록합니다.
60
60
 
61
+ ## Verification Integrity
62
+
63
+ - 구현자는 판정 기준을 바꿀 수 없습니다. 기존 test, fixture, snapshot, 기록된 검증 명령은 계획에 계약 변경 사유를 먼저 기록하지 않는 한 게이트 통과를 위해 수정·약화·skip·삭제하지 않습니다. 리뷰는 이 diff를 검사하고 사유 없는 약화를 거부합니다.
64
+ - 보고된 결함은 수정 전에 도구로 먼저 재현합니다. 원인이 불명확한 실패는 가설을 하나씩 세우고 측정으로 닫으며, 가능 여부 질문은 검증 없는 "불가능"이 아니라 제한된 실험으로 답합니다.
65
+ - 결함을 고친 뒤에는 같은 규칙이나 패턴의 다른 구현을 저장소 전체에서 찾아 범위 안에서 고치거나 후속 작업으로 기록합니다.
66
+ - 반복되고 기계적으로 확인 가능한 교훈은 메모리 문장을 늘리는 대신 test, lint rule, script, hook으로 옮깁니다.
67
+
61
68
  ## Model Guidance
62
69
 
63
70
  Cairn은 Claude-family와 Codex-family 모델에만 모델별 조정을 적용합니다.
@@ -105,10 +112,10 @@ cairn toolcheck
105
112
  - `cairn install`: custom marketplace source, versioned Codex runtime cache, Cairn-owned config section, Claude Code mirror, 현재 Antigravity IDE/CLI skill 표면을 transaction으로 설치합니다.
106
113
  - `cairn upgrade`: ownership manifest의 installed digest와 현재 값이 같은 항목만 교체하며, staged validation과 역순 rollback을 적용합니다.
107
114
  - `cairn doctor`: ownership manifest, managed digest, 유효 Codex feature, 실제 plugin installed/enabled/version, mirror/runtime locator를 수정 없이 진단합니다.
108
- - `cairn uninstall`: 수정되지 않은 ownership manifest 항목만 제거·복원한 뒤 빈 managed cache scaffold를 정리합니다. modified/unmanaged target과 그 상위 디렉터리는 보존하고 commit 전 실패는 rollback합니다.
115
+ - `cairn uninstall`: 먼저 config 외 모든 ownership manifest 항목을 설치 digest와 대조하고, 수정되거나 삭제된 managed artifact가 하나라도 있으면 아무것도 바꾸지 않고 uninstall 전체를 거부합니다. 그렇지 않으면 managed 항목을 제거·복원하고 빈 managed cache scaffold를 정리하며, unmanaged target과 그 상위 디렉터리는 보존하고 commit 전 실패는 rollback합니다.
109
116
  - `cairn toolcheck`: 저장소 스택과 필요한 LSP·검증 도구를 확인하며, 승인된 지원 installer만 실행합니다.
110
117
  - `cairn cleanup --root <repo>`: 기본값은 legacy 프로젝트 로컬 `.cairn` 잔여물을 읽기 전용으로 보고합니다. `--yes`는 유효한 활성 상태를 사용자 홈 저장소로 조정·이관하고 알려진 상태/lock/tool 잔여물만 제거하며, 알 수 없는 파일은 보존하고 비었을 때만 디렉터리를 정리합니다.
111
- - `cairn goal ...`: 저장소의 영속 goal을 시작·조회·일시정지·재개·차단·취소·완료합니다. 기본 tool-bound 정책은 `goal verify -- <argv>`가 명령을 직접 실행해 성공 증거를 기록합니다. `goal receipt`는 기존 선언 증거를 가져오는 호환 명령입니다.
118
+ - `cairn goal ...`: 저장소의 영속 goal을 시작·조회·일시정지·재개·차단·취소·완료합니다. `goal claim --session <id>`는 같은 작업을 이어받는 session(재개·압축·handoff·fork)에 미완료 goal의 소유권을 넘기며, 미완료 goal 중에 들어온 새 작업은 goal을 취소하지 않고 `goal replan`으로 합칩니다. 기본 tool-bound 정책은 `goal verify -- <argv>`가 명령을 직접 실행해 성공 증거를 기록합니다. `goal receipt`는 기존 선언 증거를 가져오는 호환 명령입니다.
112
119
  - `cairn-memory`: 도메인 지식을 탐색하고 `MEMORY.md`를 갱신합니다.
113
120
  - `cairn-plan`: `docs/plan/` 아래에 decision-complete plan을 만듭니다.
114
121
  - `cairn-work`: 현재 `PLAN.md`의 다음 모듈 작업을 실행하고 두 검증 게이트를 확보합니다.
package/README.md CHANGED
@@ -66,6 +66,13 @@ cairn toolcheck --install --yes --root .
66
66
  - If a verification gate fails, Cairn diagnoses once, shrinks the task or splits it into sub-tasks, and reruns both gates.
67
67
  - After two failed verification passes for the same task, Cairn records the blocker in `docs/plan/<topic>.md` instead of continuing a repeated loop.
68
68
 
69
+ ## Verification Integrity
70
+
71
+ - The implementer must not change the judge: existing tests, fixtures, snapshots, and recorded verification commands are never edited, weakened, skipped, or deleted to make a gate pass unless the plan records the contract change first. Review inspects those diffs and rejects unexplained weakening.
72
+ - Reported defects are reproduced with tools before fixing. Unclear failures are narrowed one hypothesis at a time, each closed by a measurement, and feasibility questions are answered with a bounded experiment rather than an unverified "not possible".
73
+ - After a defect fix, the repository is searched for other implementations of the same rule or pattern; they are fixed in scope or recorded as follow-up.
74
+ - Recurring, machine-checkable lessons are encoded as tests, lint rules, scripts, or hooks instead of more memory prose.
75
+
69
76
  ## Model Guidance
70
77
 
71
78
  Cairn only applies model-specific adjustment to Claude-family and Codex-family models.
@@ -113,10 +120,10 @@ cairn toolcheck
113
120
  - `cairn install`: transactionally installs the custom marketplace source, versioned Codex runtime cache, Cairn-owned config sections, Claude Code mirrors, and current Antigravity IDE/CLI skill surfaces.
114
121
  - `cairn upgrade`: replaces only ownership-manifest entries whose installed digests still match, with staged validation and reverse-order rollback on failure.
115
122
  - `cairn doctor`: validates the ownership manifest, managed digests, effective Codex features, actual installed/enabled/version plugin state, and mirror/runtime locators without repairing them.
116
- - `cairn uninstall`: removes or restores only ownership-manifest entries that remain unmodified, then prunes empty managed cache scaffolds; modified or unmanaged targets and their parent directories are preserved, and pre-commit failures roll back.
123
+ - `cairn uninstall`: verifies every non-config ownership-manifest entry against its installed digest first and refuses the whole uninstall, changing nothing, if any managed artifact was modified or removed. Otherwise it removes or restores the managed entries, prunes empty managed cache scaffolds, preserves unmanaged targets and their parent directories, and rolls back pre-commit failures.
117
124
  - `cairn toolcheck`: detects repository stacks and checks required LSP and verification tools; installation runs only for an approved supported installer.
118
125
  - `cairn cleanup --root <repo>`: reports legacy project-local `.cairn` residue without changing it; `--yes` migrates/reconciles valid active state into the user-home store, removes only known Cairn state/lock/tool residue, preserves unknown files, and prunes the directory only when empty.
119
- - `cairn goal ...`: starts, inspects, pauses, resumes, blocks, cancels, and completes a persisted repository goal. State lives under user-home `.cairn`, separated by repository and Git worktree; state without `worktreeId` and state copied from another worktree are stale and removed on the next hook/goal start. Successful completion or cancellation removes active state because completed evidence already lives in the plan document, while paused/blocked state remains resumable. The default tool-bound policy records successful evidence by executing `goal verify -- <argv>`. The legacy `goal receipt` command imports declared evidence only.
126
+ - `cairn goal ...`: starts, inspects, pauses, resumes, blocks, cancels, and completes a persisted repository goal. `goal claim --session <id>` hands an unfinished goal to the session that continues the same work (resume, compaction, handoff, fork); new work that arrives during an unfinished goal is folded in with `goal replan` instead of cancelling it. State lives under user-home `.cairn`, separated by repository and Git worktree; state without `worktreeId` and state copied from another worktree are stale and removed on the next hook/goal start. Successful completion or cancellation removes active state because completed evidence already lives in the plan document, while paused/blocked state remains resumable. The default tool-bound policy records successful evidence by executing `goal verify -- <argv>`. The legacy `goal receipt` command imports declared evidence only.
120
127
  - `cairn-memory`: explores domain knowledge and updates `MEMORY.md`.
121
128
  - `cairn-plan`: creates a decision-complete plan under `docs/plan/`.
122
129
  - `cairn-work`: executes the next module task in the current `PLAN.md` with two verification gates.
package/agents/worker.md CHANGED
@@ -25,6 +25,8 @@ Use for:
25
25
 
26
26
  Do not make architecture decisions. Do not ask the user. Do not delegate vague or unbounded work. Do not treat a missing tool as unavailable until install or bootstrap has been attempted. Report only important paths, commands, and outputs accurately.
27
27
 
28
+ Never edit, weaken, skip, or delete existing tests, fixtures, or verification commands to make a gate pass unless the plan records the contract change. Reproduce a reported defect before fixing it, and after the fix search for other implementations of the same rule or pattern; report them in the handoff.
29
+
28
30
  Before the first edit, inspect the assigned exact files, callers, and tests and report evidence for the plan's code checkpoint. If you discover a new Heavy Path signal before or during implementation, stop further edits and report it to the orchestrator so the plan artifact, repository goal task roadmap, native UI plan, review requirements, and stale evidence can be updated. Resume only after a new code checkpoint is recorded.
29
31
 
30
32
  Keep the model inherited. Read the task's `Requested reasoning effort` and `Effective reasoning effort`: Requested is Cairn's desired profile; Effective is what the dispatch response confirms. A newly delegated task or new worker may receive requested effort only through a host-exposed task/subagent reasoning-effort option or host-native equivalent; omit model overrides. For an unsupported host or value, report Effective `inherited`; this means no task override was confirmed, not proof that the host lacks capability. Describe capability only from the exposed interface or dispatch response, distinguish Cairn non-integration from host non-support, and do not alter model/global config.
@@ -12,6 +12,6 @@ Procedure:
12
12
  2. Every agent reads project-root `MEMORY.md` when present and continues without repository memory when absent before assigned discovery or update work.
13
13
  3. Explore repository facts with focused tools.
14
14
  4. Use `explorer` for read-only domain discovery and contradiction checks when available; use `worker` for exact references, command checks, and bounded evidence capture. When subagent tools are available, each agent may recursively delegate bounded sub-tasks to subagents. Tell every delegated agent and child subagent to read the project-root `MEMORY.md` before work, keep scope, and preserve others' edits.
15
- 5. Write details to `docs/memory/<domain>.md`.
15
+ 5. Write details to `docs/memory/<domain>.md`. When a lesson or correction recurs and is machine-checkable, encode it as a test, lint rule, script, or hook instead of more prose; memory keeps judgment rules and a pointer to the enforcing check.
16
16
  6. Keep `MEMORY.md` as a short index.
17
17
  7. Use the OS locale for user-visible responses and generated or updated documentation, plans, and memory artifacts unless the user asks for another language.
@@ -14,7 +14,7 @@ Procedure:
14
14
 
15
15
  1. Every agent reads project-root `MEMORY.md` when present and continues without repository memory when absent before repository exploration, tool selection, work, or delegation.
16
16
  2. Classify routine known-target Git/GitHub operations separately: status, fetch, checkout, branch, merge, push, and PR management stay plan/goal-free unless they require code edits, conflict resolution, destructive recovery, release/deploy, or design. For non-trivial implementation or continuation of planned work, write an initial `docs/plan/<topic>.md` and `PLAN.md` entry from the request and `MEMORY.md`. Make `triage-plan` the first active task and specify its investigation scope, path-decision criteria, plan-update condition, and anticipated implementation/verification stages. This initial plan must be decision-complete for triage and explicitly not implementation-ready.
17
- 3. Before repository exploration, toolcheck, complexity triage, delegation, or a non-blocking user question for planned work, call Codex `update_plan` and then `create_goal` when those UI tools are available. Mirror the same roadmap into the repository Cairn goal. Record unavailable UI tools instead of silently implying UI synchronization. Skip goal activation for routine known-target Git/GitHub operations, consultation, explanation, and plan-only requests.
17
+ 3. Before repository exploration, toolcheck, complexity triage, delegation, or a non-blocking user question for planned work, call Codex `update_plan` and then `create_goal` when those UI tools are available. Mirror the same roadmap into the repository Cairn goal. Record unavailable UI tools instead of silently implying UI synchronization. Skip goal activation for routine known-target Git/GitHub operations, consultation, explanation, and plan-only requests. If an unfinished repository goal already exists when a new implementation request, steer, or drift arrives, do not cancel it to start another. Fold the new work into it with `goal replan` (completed tasks and their evidence are preserved; resume a paused or blocked goal first), synchronize the plan artifact and native UI roadmap, and keep advancing until goal final review. Start a separate goal only when the user explicitly switches work. When a hook reports that another session owns the goal and this session continues that same work, take ownership with `goal claim --session <id>` using the ID shown by the hook.
18
18
  4. Read relevant `docs/memory/*.md` as directed by the initial triage task.
19
19
  5. Read the applicable installed model guidance.
20
20
  6. Run the installed runtime's `toolcheck --root <repoRoot>` and handle missing tools through the explicit approval policy.
@@ -9,7 +9,7 @@ Goal: review completed work against plan, memory, policy, and evidence.
9
9
  Procedure:
10
10
 
11
11
  1. Every agent reads project-root `MEMORY.md` when present and continues without repository memory when absent before review work, then reads the plan task and available memory inputs.
12
- 2. Check changed files, tool readiness evidence, and verification evidence.
12
+ 2. Check changed files, tool readiness evidence, and verification evidence. Inspect test, fixture, and verification-command diffs and reject any weakening without a recorded plan reason. For defect fixes, confirm the same-rule sweep was performed and its result recorded.
13
13
  3. Delegate independent read-only review to `explorer` when it materially improves speed or quality; use `worker` only for scoped verification reruns or QA artifacts. When subagent tools are available, each agent may recursively delegate bounded sub-tasks to subagents. Tell every delegated agent and child subagent to read the project-root `MEMORY.md` before work, keep scope, and preserve others' edits.
14
14
  4. Report findings first, ordered by severity, with file and line references. If no issues are found, say so clearly and mention remaining test gaps or residual risk.
15
15
  5. Record review evidence in `docs/plan/<topic>.md` and as the goal-level final review evidence record before completing the goal.
@@ -15,7 +15,7 @@ Procedure:
15
15
  1. Every agent reads project-root `MEMORY.md` when present and continues without repository memory when absent before selecting or executing a task.
16
16
  2. Read `PLAN.md`, the detailed plan, and relevant memory notes.
17
17
  3. Resolve and read the `cairn://` Codex or Claude model guidance recorded in the plan through the installed runtime.
18
- 4. Read Cairn goal status and select only its current task. If implementation was requested but no goal exists, create it from the decision-complete plan.
18
+ 4. Read the current task with `goal task status` (full `goal status` only when receipts are needed) and select only that task. Fold a new request or steer that arrives during an unfinished goal into it with `goal replan` instead of cancelling it, and keep advancing to goal final review. If implementation was requested but no goal exists, create it from the decision-complete plan.
19
19
  5. Confirm required tool readiness. If a required LSP/check tool is missing, report it and obtain explicit user approval before `toolcheck --install --yes` or a repository-native install command.
20
20
  6. Confirm the complexity triage and selected Light/Heavy Path recorded in the plan. If missing, update the plan before mutating files.
21
21
  7. Before implementation, spend reasoning on a focused executable test contract covering requirements, invariants, boundaries, and failure modes. Confirm the intended failure, then constrain implementation to the minimum change that passes it.
@@ -23,7 +23,7 @@ Procedure:
23
23
  9. Give implementation the test contract, failing evidence, exact file ownership, and constraints. Require minimal implementation and no repeated broad discovery. Preserve status, final-report, close-and-review, recursive bounded delegation, and main-agent fallback rules.
24
24
  10. Treat tool exit codes and bounded summaries as authoritative. Keep success output concise; expand only the failing test and related code context.
25
25
  11. For Heavy Path, run the recorded automated test command and record explicit `Tests:` evidence.
26
- 12. Run focused module acceptance, then one final surface integration check through `goal verify -- ...`. Verification defaults to 600,000 ms (10 minutes), may be shortened with `--timeout-ms`, and is capped at 3,600,000 ms (1 hour). The command runs without holding the state lock; evidence is recorded only if the starting goal/task identity and the pre/post watched fingerprint still match. The default Git fingerprint excludes ignored files, so explicitly `--watch` every ignored artifact needed by the claim.
26
+ 12. Run focused module acceptance, then one final surface integration check through `goal verify -- ...`. Verification defaults to 600,000 ms (10 minutes), may be shortened with `--timeout-ms`, and is capped at 3,600,000 ms (1 hour). The command runs without holding the state lock; evidence is recorded only if the starting goal/task identity and the pre/post watched fingerprint still match. The default Git fingerprint excludes ignored files, so explicitly `--watch` every ignored artifact needed by the claim. Never edit, weaken, skip, or delete existing tests, fixtures, or verification commands to make a gate pass unless the plan records the contract change. Reproduce reported defects before fixing, test one hypothesis at a time with a measurement, and after a fix search for other implementations of the same rule or pattern.
27
27
  13. Before package verification, inspect package lifecycle scripts. Run normal `npm pack --dry-run` by default. Content-producing or unknown lifecycle scripts must never use `--ignore-scripts`; only absent or proven content-neutral scripts may use it when the prior full check remains fresh.
28
28
  14. Record evidence at the semantic boundary that proves the claimed behavior, bound to goal/task/plan. Missing, failed, skipped, stale, or placeholder evidence never completes work; relevant mutation invalidates affected evidence.
29
29
  15. Complete the task and continue the goal. Use `--quiet` on successful state mutations; workers report only their assigned task and never select the next one.
@@ -14,7 +14,7 @@ The goal is not to make prompts long by model. The goal is to stabilize the same
14
14
 
15
15
  ## Common Rules
16
16
 
17
- - On every fresh entry, compaction recovery, restart, delegation, or handoff, restore context in this order: optional root `MEMORY.md` → current phase skill → active plan → current-task references → model guidance recorded by the plan. If `MEMORY.md` is absent, continue without repository memory and do not invoke another memory service. If persisted state or an assignment requires another reference that is missing, unreadable, or inconsistent, fail closed: do not edit, delegate, approve, or complete; report or record a blocker.
17
+ - On every fresh entry, compaction recovery, restart, delegation, or handoff, restore context in this order: optional root `MEMORY.md` → current phase skill → active plan → current-task references → model guidance recorded by the plan. If `MEMORY.md` is absent, continue without repository memory and do not invoke another memory service. If persisted state or an assignment requires another reference that is missing, unreadable, or inconsistent, fail closed: do not edit, delegate, approve, or complete; report or record a blocker. Delegation means the delegated agent restores on its own entry; the orchestrator does not re-read this chain after dispatching or collecting a subagent, and within one unbroken context it rereads only references that changed, which keeps the cached prompt prefix stable.
18
18
  - Keep root `MEMORY.md` and `PLAN.md` as indexes only.
19
19
  - Keep repository-specific judgment in `docs/memory/` and `docs/plan/`. Keep Cairn's model guidance in the installed plugin and reference it with `cairn://docs/model-guidance/...`.
20
20
  - Treat the user-home `.cairn/projects/<project-id>/worktrees/<worktree-id>/state.json` as a worktree-bound active-work slot. State without `worktreeId` and state copied into another worktree are stale and are removed on the next hook/goal start. Successful completion or cancellation removes the owning copy after plan evidence is recorded; only paused or blocked work remains for resumption.
@@ -17,7 +17,7 @@ Codex-family models are strong at small implementation tasks, explicit tool use,
17
17
 
18
18
  ## Adjustment Rules
19
19
 
20
- - On every fresh entry, compaction recovery, restart, delegation, or handoff, restore optional root `MEMORY.md` → current phase skill → active plan → current-task references → recorded model guidance. Missing `MEMORY.md` never blocks and must not trigger another memory service. If persisted state or the assignment requires another missing, unreadable, or inconsistent reference, fail closed and report a blocker before editing, delegating, approving, or completing.
20
+ - On every fresh entry, compaction recovery, restart, delegation, or handoff, restore optional root `MEMORY.md` → current phase skill → active plan → current-task references → recorded model guidance. Missing `MEMORY.md` never blocks and must not trigger another memory service. If persisted state or the assignment requires another missing, unreadable, or inconsistent reference, fail closed and report a blocker before editing, delegating, approving, or completing. Delegation means the delegated agent restores on its own entry; the orchestrator does not re-read this chain after dispatching or collecting a subagent, and within one unbroken context it rereads only references that changed, which keeps the cached prompt prefix stable.
21
21
  - Do not start implementation without a plan.
22
22
  - Keep the model inherited. `Requested reasoning effort` is Cairn's desired profile; `Effective reasoning effort` is what the dispatch response confirms. Only a newly dispatched task or worker may receive the requested effort through the host's task/subagent option, with no model override. For an unsupported host or value, record Effective `inherited`; this means no task override was confirmed, not proof of missing host capability. Infer capability only from the exposed interface or dispatch response, distinguish Cairn non-integration from host non-support, and leave model/global configuration unchanged.
23
23
  - Do not treat complexity as a one-time choice. Require the provisional request checkpoint, post-exploration planning checkpoint, and code checkpoint after exact file/caller/test inspection immediately before the first edit. Before editing, evidence may change either route. Every change must synchronize the plan artifact, repository goal task roadmap through `goal replan`, and native UI plan, including reviews and required evidence. After editing begins, a new Heavy signal promotes Light Path to Heavy Path: stop further edits, mark affected evidence stale, synchronize all three roadmaps, and repeat the code checkpoint.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cairn-ai",
3
- "version": "0.2.7",
3
+ "version": "0.2.8",
4
4
  "description": "Token-efficient agent harness with tool readiness for Codex, Claude Code, and Antigravity",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -58,6 +58,11 @@ export async function runGoalCli(args, {
58
58
  emit(JSON.stringify(state));
59
59
  return state;
60
60
  }
61
+ if (command === "claim") {
62
+ const state = await operations.claimGoal({ root, sessionId: options.session });
63
+ emit(JSON.stringify(state));
64
+ return state;
65
+ }
61
66
  if (command === "assign") {
62
67
  const state = await operations.assignTask({
63
68
  root,
@@ -113,7 +118,7 @@ export async function runGoalCli(args, {
113
118
  emit(JSON.stringify(state));
114
119
  return state;
115
120
  }
116
- throw new Error("Usage: cairn-goal start|status|task|replan|assign|receipt|verify|pause|resume|block|cancel|complete [--root PATH]");
121
+ throw new Error("Usage: cairn-goal start|status|task|replan|claim|assign|receipt|verify|pause|resume|block|cancel|complete [--root PATH]");
117
122
  }
118
123
 
119
124
  function parseArgs(args, command) {
@@ -173,6 +178,7 @@ function allowedOptions(command) {
173
178
  status: [],
174
179
  task: ["task", "status", "reason"],
175
180
  replan: ["tasks"],
181
+ claim: ["session"],
176
182
  assign: ["task", "agent"],
177
183
  receipt: ["task", "kind", "scope", "command", "exitCode", "exit-code", "exit", "timestamp", "goalId", "goal-id", "plan"],
178
184
  verify: ["task", "kind", "scope", "watch", "timeout-ms"],
@@ -82,7 +82,14 @@ function listGitWorkspacePaths(root) {
82
82
  maxBuffer: 16 * 1024 * 1024,
83
83
  });
84
84
  if (result.status !== 0 || result.error) return null;
85
- return result.stdout.split("\0").filter(Boolean).filter((path) => !isInternalStatePath(path));
85
+ return result.stdout.split("\0").filter(Boolean)
86
+ .filter((path) => !isInternalStatePath(path) && !isCairnRecordPath(path));
87
+ }
88
+
89
+ // Cairn records evidence in these artifacts after verification; including them in the default fingerprint would
90
+ // make every recorded receipt stale. Pass --watch explicitly when a claim is about these files.
91
+ function isCairnRecordPath(path) {
92
+ return path === "PLAN.md" || path === "MEMORY.md" || path.startsWith("docs/plan/") || path.startsWith("docs/memory/");
86
93
  }
87
94
 
88
95
  function listFilesystemPaths(root, watchPaths) {
@@ -41,7 +41,7 @@ export async function startGoal({ root = process.cwd(), goal, planId, tasks, com
41
41
  existing = null;
42
42
  }
43
43
  if (existing && !terminalGoalStatuses.has(existing.goal.status)) {
44
- throw new Error(`An active Cairn goal already exists (${existing.goal.id}); pause, block, cancel, or complete it first`);
44
+ throw new Error(`An unfinished Cairn goal already exists (${existing.goal.id}, ${existing.goal.status}); add the new work to it with goal replan (resume it first if paused or blocked), or cancel or complete it before starting another goal`);
45
45
  }
46
46
  const now = timestamp();
47
47
  const normalizedTasks = tasks.map((task, index) => normalizeTask(task, index, index === 0 ? "active" : "pending"));
@@ -163,6 +163,16 @@ export async function replanGoal({ root = process.cwd(), tasks } = {}) {
163
163
  });
164
164
  }
165
165
 
166
+ export async function claimGoal({ root = process.cwd(), sessionId } = {}) {
167
+ const normalizedSessionId = requiredText(sessionId, "sessionId");
168
+ return mutateGoal(root, (state) => {
169
+ assertGoalMutable(state);
170
+ state.goal.ownerSessionId = optionalText(normalizedSessionId, "ownerSessionId");
171
+ state.goal.updatedAt = timestamp();
172
+ return state;
173
+ });
174
+ }
175
+
166
176
  export async function assignTask({ root = process.cwd(), taskId, agentId = null } = {}) {
167
177
  const normalizedTaskId = requiredText(taskId, "taskId");
168
178
  const normalizedAgentId = agentId === null ? null : requiredText(agentId, "agentId");
@@ -7,7 +7,10 @@ export function currentTask(state) {
7
7
  }
8
8
 
9
9
  export function isGoalOwnedBySession(state, sessionId) {
10
- return !state?.goal?.ownerSessionId || state.goal.ownerSessionId === sessionId;
10
+ if (!state?.goal?.ownerSessionId) return true;
11
+ // A payload without a session id cannot prove another owner; treating it as foreign would silently halt the goal.
12
+ if (typeof sessionId !== "string" || sessionId.length === 0) return true;
13
+ return state.goal.ownerSessionId === sessionId;
11
14
  }
12
15
 
13
16
  export function evaluateStop(state, { subagent = false, agentId } = {}) {
@@ -12,9 +12,44 @@ export function goalStatePath(root = process.cwd()) {
12
12
  export async function readGoalState({ root = process.cwd() } = {}) {
13
13
  const state = await readCurrentGoalState(root);
14
14
  if (state) return state;
15
+ // Read-only callers (hooks) must not create user-home state directories when there is nothing to migrate.
16
+ if (!(await legacyGoalStateExists(root))) return null;
15
17
  return withGoalStateLock(root, () => readGoalStateUnlocked(root));
16
18
  }
17
19
 
20
+ export function stopGuardPath(root = process.cwd()) {
21
+ return join(goalStateDirectory(root), "stop-guard.json");
22
+ }
23
+
24
+ export async function readStopGuard(root = process.cwd()) {
25
+ try {
26
+ const path = stopGuardPath(root);
27
+ await assertNoSymlinkComponents(cairnHome(), path);
28
+ const value = JSON.parse(await readFile(path, "utf8"));
29
+ return value && typeof value === "object" ? value : null;
30
+ } catch {
31
+ return null;
32
+ }
33
+ }
34
+
35
+ export async function writeStopGuard(root = process.cwd(), value) {
36
+ try {
37
+ await safeWriteFile(cairnHome(), stopGuardPath(root), `${JSON.stringify(value)}\n`, "utf8");
38
+ } catch {
39
+ // The guard only prevents no-progress continuation loops; failing to persist it must not break the stop hook.
40
+ }
41
+ }
42
+
43
+ async function legacyGoalStateExists(root) {
44
+ try {
45
+ await lstat(join(resolve(root), ".cairn", "state.json"));
46
+ return true;
47
+ } catch (error) {
48
+ if (error?.code === "ENOENT" || error?.code === "ENOTDIR") return false;
49
+ throw error;
50
+ }
51
+ }
52
+
18
53
  export async function writeGoalState({ root = process.cwd(), state } = {}) {
19
54
  return withGoalStateLock(root, () => writeGoalStateUnlocked({ root, state }));
20
55
  }
@@ -2,7 +2,7 @@
2
2
  import { realpathSync } from "node:fs";
3
3
  import { fileURLToPath, pathToFileURL } from "node:url";
4
4
  import { runGoalCli } from "./cairn-goal-cli.mjs";
5
- import { assignTask, replanGoal, setTaskStatus, startGoal, transitionGoal } from "./cairn-goal-operations.mjs";
5
+ import { assignTask, claimGoal, replanGoal, setTaskStatus, startGoal, transitionGoal } from "./cairn-goal-operations.mjs";
6
6
  import { recordReceipt, verifyAndRecord } from "./cairn-goal-operations-evidence.mjs";
7
7
  import { readGoalState } from "./cairn-goal-state.mjs";
8
8
  import { defaultGoalEvidence } from "./cairn-goal-state-validation.mjs";
@@ -19,7 +19,7 @@ export {
19
19
  TASK_STATUSES,
20
20
  } from "./cairn-goal-state-validation.mjs";
21
21
  export { goalStatePath, readGoalState, writeGoalState } from "./cairn-goal-state.mjs";
22
- export { assignTask, reconcileWorktreeState, replanGoal, setTaskStatus, startGoal, transitionGoal } from "./cairn-goal-operations.mjs";
22
+ export { assignTask, claimGoal, reconcileWorktreeState, replanGoal, setTaskStatus, startGoal, transitionGoal } from "./cairn-goal-operations.mjs";
23
23
  export { recordReceipt, verifyAndRecord } from "./cairn-goal-operations-evidence.mjs";
24
24
  export { currentTask, evaluateStop, isGoalOwnedBySession } from "./cairn-goal-policy.mjs";
25
25
 
@@ -29,6 +29,7 @@ export async function handleGoalCli(args = process.argv.slice(2), { stdout = con
29
29
  defaultGoalEvidence,
30
30
  operations: {
31
31
  assignTask,
32
+ claimGoal,
32
33
  readGoalState,
33
34
  recordReceipt,
34
35
  replanGoal,
@@ -99,10 +99,14 @@ export function splitSections(config) {
99
99
  }
100
100
  const doubleCount = delimiterCount('"""');
101
101
  const singleCount = delimiterCount("'''");
102
- const match = line.trim().match(/^\[([^\]]+)\]$/);
103
- if (match) {
102
+ // Array-of-tables and headers with trailing comments start a new section too; otherwise a foreign section
103
+ // after Cairn's would be absorbed into it and deleted on upgrade/uninstall.
104
+ const trimmed = line.trim();
105
+ const arrayMatch = trimmed.match(/^\[\[([^\[\]]+)\]\]\s*(?:#.*)?$/);
106
+ const match = arrayMatch ? null : trimmed.match(/^\[([^\[\]]+)\]\s*(?:#.*)?$/);
107
+ if (arrayMatch || match) {
104
108
  if (current.text.length > 0) result.push(current);
105
- current = { header: match[1], text: line };
109
+ current = { header: arrayMatch ? `[${arrayMatch[1].trim()}]` : match[1].trim(), text: line };
106
110
  } else current.text += line;
107
111
  if (doubleCount % 2 === 1) multiline = '"""';
108
112
  else if (singleCount % 2 === 1) multiline = "'''";
@@ -43,8 +43,8 @@ export function contextResult({ ko, state, event }) {
43
43
  const planId = state.goal.planId;
44
44
  const phase = task ? "cairn-work" : "cairn-review";
45
45
  const activeHeader = ko
46
- ? `${base} ${phase}를 로드하고 정확한 plan ${planId}${task ? "과 아래 current task" : "의 완료 기준"}를 복원하세요. 압축·재시작·handoff·위임 뒤 다시 읽으세요.${failClosed}`
47
- : `${base} Load ${phase}; restore exact plan ${planId}${task ? " and the current task below" : " completion criteria"}. Re-read after compaction/restart/handoff/delegation.${failClosed}`;
46
+ ? `${base} ${phase}를 로드하고 정확한 plan ${planId}${task ? "과 아래 current task" : "의 완료 기준"}를 복원하세요. 압축·재시작·handoff 뒤 다시 읽으세요.${failClosed}`
47
+ : `${base} Load ${phase}; restore exact plan ${planId}${task ? " and the current task below" : " completion criteria"}. Re-read after compaction/restart/handoff.${failClosed}`;
48
48
  if (!task) {
49
49
  return contextHookResult({
50
50
  event,
@@ -63,11 +63,11 @@ export function contextResult({ ko, state, event }) {
63
63
  const resume = event === "user-prompt-submit"
64
64
  ? (taskId.length > 48
65
65
  ? (ko
66
- ? "곁가지 질문 뒤 일시정지·중단·전환 요청이 없으면 위의 정확한 task를 재개하세요."
67
- : "Resume that exact task after side questions unless paused, stopped, or switched.")
66
+ ? "곁가지 질문·조향 뒤 위의 정확한 task를 재개하고 최종 검토까지 진행하세요(일시정지·중단·전환 요청 제외)."
67
+ : "After side questions or steers, resume that exact task; advance to final review unless paused, stopped, or switched.")
68
68
  : (ko
69
- ? `곁가지 질문 뒤 일시정지·중단·전환 요청이 없으면 ${taskId}를 재개하세요.`
70
- : `After a side question, resume ${taskId} unless asked to pause, stop, or switch.`))
69
+ ? `곁가지 질문·조향 뒤 ${taskId}를 재개하고 남은 task를 최종 검토까지 진행하세요(일시정지·중단·전환 요청 제외).`
70
+ : `After a side question or steer, resume ${taskId}, then advance remaining tasks to final review unless asked to pause, stop, or switch.`))
71
71
  : "";
72
72
  return contextHookResult({ event, message: [activeHeader, taskRoadmap(ko, state), continuation, resume].filter(Boolean).join("\n") });
73
73
  }
@@ -87,18 +87,23 @@ function interruptedContextResult(event, ko, state) {
87
87
  : (ko ? "현재 task: 없음." : "Current task: none.");
88
88
  const blocker = state.goal.blocker ?? task?.blocker ?? (ko ? "없음" : "none");
89
89
  const recovery = ko
90
- ? "자동으로 재개하지 마세요. 정확한 context를 복원하고 원인을 처리한 뒤 명시적으로 resume하세요."
91
- : "Do not resume automatically. Restore this exact context, address the interruption, then resume explicitly.";
90
+ ? "goal 상태를 자동으로 바꾸지 마세요. 정확한 context를 복원하고 원인을 처리한 뒤 goal resume을 명시적으로 실행하고 현재 task를 이어가세요."
91
+ : "Do not change goal status automatically. Restore this exact context, address the interruption, then run goal resume explicitly and continue the current task.";
92
92
  const message = ko
93
93
  ? `${kernelBase(ko)} 정확한 plan: ${state.goal.planId}. ${taskContext} Goal 상태: ${state.goal.status}. Blocker: ${blocker}. ${recovery}`
94
94
  : `${kernelBase(ko)} Exact plan: ${state.goal.planId}. ${taskContext} Goal status: ${state.goal.status}. Blocker: ${blocker}. ${recovery}`;
95
95
  return contextHookResult({ event, message });
96
96
  }
97
97
 
98
- export function foreignSessionContextResult({ ko, event }) {
98
+ export function foreignSessionContextResult({ ko, event, sessionId }) {
99
+ const claim = typeof sessionId === "string" && sessionId.length > 0
100
+ ? (ko
101
+ ? ` 이 session이 같은 작업을 이어받는 경우(재개·압축·handoff·fork)에만 cairn goal claim --session ${JSON.stringify(sessionId)} --root <repoRoot>로 소유권을 넘겨받고 이어가세요.`
102
+ : ` Only if this session continues that same work (resume, compaction, handoff, fork), take ownership with cairn goal claim --session ${JSON.stringify(sessionId)} --root <repoRoot>, then continue.`)
103
+ : "";
99
104
  const message = ko
100
- ? "Cairn kernel: 루트 MEMORY.md는 선택 사항이며, 있으면 읽으세요. 저장소 goal을 다른 session이 소유합니다. cairn goal status로 상태를 확인하고 소유권이 해결될 때까지 Cairn 작업을 시작·수정·위임·완료하지 마세요. 이 capsule은 goal, plan, task 상세를 노출하지 않습니다."
101
- : "Cairn kernel: root MEMORY.md is optional; read it when present. A repository goal is owned by another session. Inspect cairn goal status; until ownership is resolved, do not start, edit, delegate, or complete Cairn work. This capsule does not expose goal, plan, or task details.";
105
+ ? `Cairn kernel: 루트 MEMORY.md는 선택 사항이며, 있으면 읽으세요. 저장소 goal을 다른 session이 소유합니다. cairn goal status로 상태를 확인하고 소유권이 해결될 때까지 Cairn 작업을 시작·수정·위임·완료하지 마세요.${claim} 이 capsule은 goal, plan, task 상세를 노출하지 않습니다.`
106
+ : `Cairn kernel: root MEMORY.md is optional; read it when present. A repository goal is owned by another session. Inspect cairn goal status; until ownership is resolved, do not start, edit, delegate, or complete Cairn work.${claim} This capsule does not expose goal, plan, or task details.`;
102
107
  return contextHookResult({ event, message });
103
108
  }
104
109
 
@@ -117,7 +122,10 @@ function taskRoadmap(ko, state) {
117
122
  : `Work steps (${state.tasks.length}; ${countSummary})`;
118
123
  const omission = ko ? `… ${omitted}개 단계 생략` : `… ${omitted} steps omitted`;
119
124
  const currentPosition = currentIndex + 1;
120
- return `${header}. ${ko ? `현재 위치 ${currentPosition}` : `Current position ${currentPosition}`}; ${omission}`;
125
+ const upcoming = state.tasks.slice(currentIndex + 1).filter((task) => task.status === "pending").slice(0, 2)
126
+ .map((task) => clipText(task.id, contextLimits.roadmapTaskId));
127
+ const next = upcoming.length > 0 ? `; ${ko ? "다음" : "next"} ${upcoming.join(", ")}` : "";
128
+ return `${header}. ${ko ? `현재 위치 ${currentPosition}` : `Current position ${currentPosition}`}; ${omission}${next}`;
121
129
  }
122
130
 
123
131
  function contextHookResult({ event, message }) {
@@ -1,12 +1,14 @@
1
- export function continuationReason(reason, payload) {
2
- const continuation = payload?.stop_hook_active ? " This turn was already continued; the active goal remains persisted for the next session." : "";
3
- return `${reason}${continuation}`;
1
+ export function continuationReason(reason, payload, { progressed = false } = {}) {
2
+ if (!payload?.stop_hook_active || progressed) return reason;
3
+ return `${reason} This turn was already continued without new Cairn state progress, so the loop guard allows this stop. The goal is still active and incomplete: resume the current task on the next prompt; do not treat it as finished or deferred.`;
4
4
  }
5
5
 
6
- export function stopHookOutput(gate, payload) {
6
+ // stop_hook_active alone would allow every second stop. Block again whenever the goal state advanced
7
+ // since the previous continuation, so a steered turn keeps driving the goal; allow only a no-progress repeat.
8
+ export function stopHookOutput(gate, payload, { progressed = false } = {}) {
7
9
  if (!gate.block) return { continue: true };
8
- const reason = continuationReason(gate.reason, payload);
9
- if (payload?.stop_hook_active) {
10
+ const reason = continuationReason(gate.reason, payload, { progressed });
11
+ if (payload?.stop_hook_active && !progressed) {
10
12
  return { continue: true, systemMessage: reason };
11
13
  }
12
14
  return { decision: "block", reason };
@@ -5,6 +5,7 @@ import { contextResult, foreignSessionContextResult, prependContext } from "./ca
5
5
  import { continuationReason, stopAllowedMessage, stopHookOutput } from "./cairn-state-hook.mjs";
6
6
  import { initializeProject } from "./cairn-state-init.mjs";
7
7
  import { evaluateStop, isGoalOwnedBySession, reconcileWorktreeState } from "./cairn-goal.mjs";
8
+ import { readStopGuard, writeStopGuard } from "./cairn-goal-state.mjs";
8
9
  import { resolveRepoRoot } from "./cairn-paths.mjs";
9
10
 
10
11
  const recoverableGoalStatuses = new Set(["active", "paused", "blocked"]);
@@ -12,7 +13,9 @@ const recoverableGoalStatuses = new Set(["active", "paused", "blocked"]);
12
13
  if (isCliEntry()) {
13
14
  const event = process.argv[2] ?? "manual";
14
15
  const payload = await readHookInput();
15
- const result = await runStateResult(event, { payload });
16
+ const result = isHookEvent(event)
17
+ ? await runStateResult(event, { payload }).catch((error) => hookFailureResult(event, error))
18
+ : await runStateResult(event, { payload });
16
19
  if (isHookEvent(event)) {
17
20
  console.log(JSON.stringify(result.hookOutput ?? {}));
18
21
  } else {
@@ -47,7 +50,7 @@ export async function runStateResult(event = "manual", {
47
50
  const reconciled = await reconcileWorktreeState({ root: resolvedRoot });
48
51
  const state = reconciled.state;
49
52
  if (recoverableGoalStatuses.has(state?.goal?.status) && !isGoalOwnedBySession(state, payload?.session_id)) {
50
- return foreignSessionContextResult({ ko, event });
53
+ return foreignSessionContextResult({ ko, event, sessionId: payload?.session_id, root: resolvedRoot });
51
54
  }
52
55
  const result = contextResult({ ko, state, event });
53
56
  if (!reconciled.removed) return result;
@@ -58,6 +61,12 @@ export async function runStateResult(event = "manual", {
58
61
  }
59
62
 
60
63
  if (event === "post-tool-use") {
64
+ // Only remind while an owned goal is active: an unconditional reminder after every edit accumulates in the
65
+ // conversation prefix, raising cached-read cost and bringing compaction (a full cache reset) sooner.
66
+ const { state } = await reconcileWorktreeState({ root: resolvedRoot });
67
+ if (state?.goal?.status !== "active" || !isGoalOwnedBySession(state, payload?.session_id)) {
68
+ return { status: 0, message: "", hookOutput: {} };
69
+ }
61
70
  const message = ko
62
71
  ? "Cairn 점검: 외부 상태 변경에는 dry-run/check 증거 기록이 필요합니다. 활성 목표가 있다면 현재 task에 성공 증거를 기록하세요."
63
72
  : "Cairn check: external-state changes need dry-run/check evidence. When a goal is active, record successful evidence for its current task.";
@@ -80,10 +89,11 @@ export async function runStateResult(event = "manual", {
80
89
  subagent: event === "subagent-stop",
81
90
  agentId: payload?.agent_id,
82
91
  });
83
- const hookOutput = stopHookOutput(gate, payload);
92
+ const progressed = gate.block ? await recordStopProgress(resolvedRoot, state, event, payload) : false;
93
+ const hookOutput = stopHookOutput(gate, payload, { progressed });
84
94
  return {
85
95
  status: 0,
86
- message: gate.block ? continuationReason(gate.reason, payload) : stopAllowedMessage({ ko, state, event }),
96
+ message: gate.block ? continuationReason(gate.reason, payload, { progressed }) : stopAllowedMessage({ ko, state, event }),
87
97
  hookOutput,
88
98
  };
89
99
  }
@@ -94,6 +104,22 @@ export async function runStateResult(event = "manual", {
94
104
  };
95
105
  }
96
106
 
107
+ async function recordStopProgress(root, state, event, payload) {
108
+ const scope = event === "subagent-stop" ? `subagent:${payload?.agent_id ?? ""}` : "turn";
109
+ const previous = await readStopGuard(root);
110
+ const progressed = previous?.goalId !== state.goal.id
111
+ || previous?.scope !== scope
112
+ || previous?.revision !== state.revision;
113
+ if (progressed) await writeStopGuard(root, { goalId: state.goal.id, scope, revision: state.revision });
114
+ return progressed;
115
+ }
116
+
117
+ function hookFailureResult(event, error) {
118
+ const detail = String(error?.message ?? error).split("\n")[0].slice(0, 300);
119
+ const message = `Cairn ${event} hook could not read goal state: ${detail}. Run cairn goal status to inspect it; do not treat an active goal as finished.`;
120
+ return { status: 0, message, hookOutput: { systemMessage: message } };
121
+ }
122
+
97
123
  function resolveRoot(root, payload) {
98
124
  return resolveRepoRoot({
99
125
  explicitRoot: root ?? null,
@@ -31,7 +31,8 @@ Keep the target repository root separate. Pass it explicitly with `--root <repoR
31
31
  - Recursive subagent delegation is allowed only for bounded sub-tasks when the current surface supports it.
32
32
  4. Write detailed facts to `docs/memory/<domain>.md`.
33
33
  5. Keep only one-line links and summaries in `MEMORY.md`.
34
- 6. Write user-visible responses and generated or updated documentation, plans, and memory artifacts in the OS locale unless the user asks for another language.
34
+ 6. When a lesson or correction recurs and is machine-checkable, encode it as a test, lint rule, script, or hook instead of more prose; memory keeps judgment rules and a pointer to the enforcing check.
35
+ 7. Write user-visible responses and generated or updated documentation, plans, and memory artifacts in the OS locale unless the user asks for another language.
35
36
 
36
37
  ## Delegation Prompt Format
37
38
 
@@ -33,7 +33,10 @@ For a non-trivial implementation request or continuation of already planned work
33
33
  node "<pluginRoot>/scripts/cairn.mjs" goal start --root "<repoRoot>" --goal "<objective>" --plan "docs/plan/<topic>.md" --tasks '[{"id":"triage-plan","title":"Run planned triage and finalize the plan","requiredEvidence":["planArtifact","triageDecision"]},{"id":"implement","title":"Implement the finalized plan"},{"id":"verify","title":"Verify and review the result"}]' --criteria "<completion criterion>" --session "<current session_id>"
34
34
  ```
35
35
 
36
- Use the current Codex hook `session_id` when it is available. If the surface does not expose a stable session ID, omit `--session` and record that the goal is repository-scoped. If Codex UI plan/goal tools are unavailable, record that limitation and continue with the repository plan and goal; never claim UI synchronization that did not occur.
36
+ Pass `--session` only with a session ID the surface actually exposes to the model; never guess or invent one, because a mismatched owner makes every later hook treat the goal as foreign. If no stable session ID is exposed, omit `--session` and record that the goal is repository-scoped.
37
+
38
+ If an unfinished repository goal already exists when a new implementation request, steer, or drift arrives, do not cancel it to start another. Fold the new work into it with `goal replan` (completed tasks and their evidence are preserved; resume a paused or blocked goal first), synchronize the plan artifact and native UI roadmap, and keep advancing until goal final review. Start a separate goal only when the user explicitly switches work. When a hook reports that another session owns the goal and this session continues that same work, take ownership with `goal claim --session <id>` using the ID shown by the hook.
39
+ If Codex UI plan/goal tools are unavailable, record that limitation and continue with the repository plan and goal; never claim UI synchronization that did not occur.
37
40
 
38
41
  ## Procedure
39
42
 
@@ -13,7 +13,7 @@ Review is not another implementation loop. Confirm that the completed task satis
13
13
 
14
14
  Every review run and delegated review agent reads project-root `MEMORY.md` when present. If it is absent, continue without repository memory and do not invoke another memory service.
15
15
 
16
- At the start and after compaction, restart, delegation, or handoff, restore context in this order: optional root `MEMORY.md` → `cairn-review` → the active plan → completed/current-task references → model guidance recorded by the plan. Missing `MEMORY.md` is not an inconsistency. Confirm the remaining references agree with persisted goal state before reviewing. If any required reference is missing, unreadable, or inconsistent, do not approve, delegate, record final review, or complete the goal; report a blocker. Do not use a read-receipt field as evidence that a model understood a reference.
16
+ At the start and after compaction, restart, delegation, or handoff, restore context in this order: optional root `MEMORY.md` → `cairn-review` → the active plan → completed/current-task references → model guidance recorded by the plan. Missing `MEMORY.md` is not an inconsistency. Confirm the remaining references agree with persisted goal state before reviewing. If any required reference is missing, unreadable, or inconsistent, do not approve, delegate, record final review, or complete the goal; report a blocker. Do not use a read-receipt field as evidence that a model understood a reference. Delegation means the delegated agent restores on its own entry; the orchestrator does not re-read this chain after dispatching or collecting a subagent, and within one unbroken context it rereads only references that changed, which keeps the cached prompt prefix stable.
17
17
 
18
18
  When subagent tools are available, each agent may recursively delegate bounded review sub-tasks to subagents. Every child subagent reads project-root `MEMORY.md` when present and continues when absent, keeps the assigned scope, and preserves others' edits.
19
19
 
@@ -23,7 +23,7 @@ Resolve any `cairn://...` resource through `references/cairn-runtime.json` next
23
23
 
24
24
  1. Read the completed task in `docs/plan/<topic>.md`.
25
25
  2. Read `MEMORY.md` when present and relevant `docs/memory/*.md`; continue when repository memory is absent.
26
- 3. Check changed files, tool readiness evidence, and evidence paths.
26
+ 3. Check changed files, tool readiness evidence, and evidence paths. Inspect test, fixture, and verification-command diffs and reject any weakening without a recorded plan reason. For defect fixes, confirm the same-rule sweep was performed and its result recorded.
27
27
  4. Delegate independent review when it materially improves speed or quality and the current tool policy allows it.
28
28
  - Use `explorer` for read-only impact analysis, pattern checks, and independent diff inspection.
29
29
  - Use `worker` for scoped verification command re-runs or QA artifact capture.
@@ -39,7 +39,7 @@ TASK: Review correctness, scope, and evidence for task <task-id>.
39
39
  EXPECTED OUTCOME: Findings ordered by severity with file and line references, or an explicit no-issue result with residual risk.
40
40
  REQUIRED TOOLS: Read-only diff inspection, tool readiness checks, verification command execution, plan/memory reading.
41
41
  MUST DO: Read the project-root `MEMORY.md` before doing assigned work. Check tool readiness and install-attempt evidence. Check dry-run or check evidence when external state could change. Check both module evidence and surface evidence. Preserve proper nouns exactly. Use the OS locale for user-visible responses and generated or updated documentation, plans, and memory artifacts.
42
- MUST NOT DO: Edit files. Ask the user. Approve skipped LSP/typecheck/lint without install evidence. Approve missing evidence or open-ended verification loops.
42
+ MUST NOT DO: Edit files. Ask the user. Approve skipped LSP/typecheck/lint without install evidence. Approve weakened, skipped, or deleted tests or verification commands without a recorded plan reason. Approve missing evidence or open-ended verification loops.
43
43
  CONTEXT: docs/plan/<topic>.md, MEMORY.md, relevant docs/memory notes, changed files.
44
44
  ```
45
45
 
@@ -23,7 +23,7 @@ If the user asks a side question, status question, or narrow clarification while
23
23
 
24
24
  ## Re-entry and Required References
25
25
 
26
- At the start and after compaction, restart, delegation, or handoff, restore context in this order: optional root `MEMORY.md` → `cairn-work` → the active plan → current-task references → model guidance recorded by the plan. Confirm that the plan task, persisted current task, and worker assignment agree before acting. Missing `MEMORY.md` is not an inconsistency. If state, skill, plan, assignment, or another required reference is missing, unreadable, or inconsistent, do not edit, delegate, record completion evidence, or complete work; report a blocker to the orchestrator. Do not introduce read receipts as proof of model attention: current state, readable references, and fresh tool-bound evidence are the authority.
26
+ At the start and after compaction, restart, delegation, or handoff, restore context in this order: optional root `MEMORY.md` → `cairn-work` → the active plan → current-task references → model guidance recorded by the plan. Confirm that the plan task, persisted current task, and worker assignment agree before acting. Missing `MEMORY.md` is not an inconsistency. If state, skill, plan, assignment, or another required reference is missing, unreadable, or inconsistent, do not edit, delegate, record completion evidence, or complete work; report a blocker to the orchestrator. Do not introduce read receipts as proof of model attention: current state, readable references, and fresh tool-bound evidence are the authority. Delegation means the delegated agent restores on its own entry; the orchestrator does not re-read this chain after dispatching or collecting a subagent, and within one unbroken context it rereads only references that changed, which keeps the cached prompt prefix stable.
27
27
 
28
28
  When subagent tools are available, each agent may recursively delegate bounded sub-tasks to subagents. Every child subagent reads project-root `MEMORY.md` when present and continues when absent, keeps the assigned scope, and preserves others' edits.
29
29
 
@@ -34,7 +34,9 @@ Resolve Cairn's read-only runtime from `references/cairn-runtime.json` next to t
34
34
  Use the installed CLI for durable state. Important forms are:
35
35
 
36
36
  ```sh
37
+ node "<pluginRoot>/scripts/cairn.mjs" goal task status --root "<repoRoot>"
37
38
  node "<pluginRoot>/scripts/cairn.mjs" goal status --root "<repoRoot>"
39
+ node "<pluginRoot>/scripts/cairn.mjs" goal claim --quiet --root "<repoRoot>" --session "<session-id-shown-by-hook>"
38
40
  node "<pluginRoot>/scripts/cairn.mjs" goal replan --quiet --root "<repoRoot>" --tasks '[{"id":"<task-id>","title":"<task-title>","requiredEvidence":["moduleAcceptance","surfaceIntegration"]}]'
39
41
  node "<pluginRoot>/scripts/cairn.mjs" goal assign --quiet --root "<repoRoot>" --task "<task-id>" --agent "<agent-id>"
40
42
  node "<pluginRoot>/scripts/cairn.mjs" goal verify --quiet --root "<repoRoot>" --task "<task-id>" --kind moduleAcceptance --watch "<path|path>" -- <tool> <arg> ...
@@ -65,6 +67,18 @@ Use this verification ladder:
65
67
  5. Run normal `npm pack --dry-run` by default. Content-producing or unknown lifecycle scripts must never be skipped with `--ignore-scripts`; their generated contents are part of the package contract.
66
68
  6. Only when lifecycle scripts are absent or proven content-neutral, the full check is still fresh, and the dry-run is only checking the file list may package verification use `npm pack --dry-run --ignore-scripts`.
67
69
 
70
+ ## Verification Integrity
71
+
72
+ - The implementer must not be able to change the judge. Never edit, weaken, skip, or delete existing tests, fixtures, snapshots, or recorded verification commands to make a gate pass. When a contract genuinely changes, record the reason in `docs/plan/<topic>.md` before that edit; review rejects any judge change without a recorded reason.
73
+ - Prefer executable checks (test, lint, script, hook) for machine-checkable rules; keep prose only for rules that need judgment.
74
+
75
+ ## Defect Investigation
76
+
77
+ - Reproduce a reported defect with repository tools before fixing it; do not wait for more detail when a local reproduction is possible.
78
+ - For unclear or non-reproducible failures, test one hypothesis at a time and close it with a measurement before changing code. Never fix on speculation.
79
+ - Answer feasibility questions with a bounded experiment or measurement instead of an unverified "not possible". Stop and ask only when the experiment would bypass a security policy or mutate external state without approval.
80
+ - After fixing a defect, search the repository for other implementations of the same rule or pattern. Fix them within the assigned scope or record them as follow-up in the plan.
81
+
68
82
  Any relevant mutation after a gate makes that result stale evidence. Rerun the affected focused gate and, when integration could change, the full gate. Use `--quiet` for successful Cairn state mutations so full state JSON does not consume conversation tokens. `goal status` is read-only and always returns state, including when `--quiet` is supplied.
69
83
 
70
84
  ## Reasoning Effort Routing
@@ -81,7 +95,7 @@ Any relevant mutation after a gate makes that result stale evidence. Rerun the a
81
95
  1. Read project-root `MEMORY.md` when present; continue without repository memory when absent.
82
96
  2. Read `PLAN.md`, the selected `docs/plan/<topic>.md`, and relevant memory notes.
83
97
  3. Read the `cairn://docs/model-guidance/*.md` inputs recorded in the plan through the installed runtime.
84
- 4. Read user-home, project/worktree-scoped state through `cairn goal status --root "<repoRoot>"`. Select only the active goal's current task. If implementation was requested but no goal exists, create one from the decision-complete plan before editing.
98
+ 4. Read the current task through `cairn goal task status --root "<repoRoot>"`; the hook capsule already carries the roadmap. Read full `cairn goal status` only when receipts or the complete roadmap are needed, because its output grows with every receipt. Select only the active goal's current task. If a new implementation request or steer arrives while a goal is unfinished, add the work through `goal replan` instead of cancelling the goal, then keep advancing to goal final review. If implementation was requested but no goal exists, create one from the decision-complete plan before editing.
85
99
  5. Confirm that required LSP, typecheck, lint, dry-run, and verification tools are available from the plan's tool readiness section.
86
100
  6. If a required tool is missing, do not install it implicitly. Obtain explicit user approval, then run the installed runtime's `toolcheck --install --yes --root "<repoRoot>"` only for pinned/supported installers or run the approved repository-native command. Otherwise record a blocker.
87
101
  7. Confirm the selected Light Path or Heavy Path in the plan. If the plan lacks complexity triage, stop and update the plan before mutating files.
@@ -110,7 +124,7 @@ Any relevant mutation after a gate makes that result stale evidence. Rerun the a
110
124
  TASK: Implement task <task-id> for <topic>.
111
125
  EXPECTED OUTCOME: Minimal code or documentation change satisfying the task contract.
112
126
  REQUIRED TOOLS: Scoped file editing, tool readiness checks, existing test/build commands, surface QA commands.
113
- MUST DO: Read the project-root `MEMORY.md` before doing assigned work and follow its policy. Stay within the listed file scope and ownership. Assume you are not alone in the codebase; do not revert others' edits. Treat the user-called/main agent as the orchestrator; delegate actual implementation edits to `worker` whenever subagent tools are available, regardless of Light Path or Heavy Path. When the subagent tool provides a progress-reporting channel, require subagents to report status to the orchestrator when starting work, when deciding or confirming direction, during periodic progress, and when finishing; the orchestrator must immediately relay received status events to the user. If no mid-run reporting channel exists, relay observable events such as assignment, waiting, and final completion. Require delegated subagents to provide a final report before leaving; after capturing final report and evidence, close or release the completed subagent, then review the final report and evidence before marking the work complete. If subagent tools are unavailable, the main agent takes over implementation directly and records that takeover in evidence. When subagent tools are available, recursively delegate only bounded sub-tasks and pass these same constraints to every child subagent. Obtain explicit user approval before installing missing tools. Run dry-run or check mode before external-state mutation when available. Provide module evidence, surface evidence, and a structured handoff scoped to the assigned goal/task. Use the OS locale for user-visible responses and generated or updated documentation, plans, and memory artifacts.
127
+ MUST DO: Read the project-root `MEMORY.md` before doing assigned work and follow its policy. Stay within the listed file scope and ownership. Assume you are not alone in the codebase; do not revert others' edits. Treat the user-called/main agent as the orchestrator; delegate actual implementation edits to `worker` whenever subagent tools are available, regardless of Light Path or Heavy Path. When the subagent tool provides a progress-reporting channel, require subagents to report status to the orchestrator when starting work, when deciding or confirming direction, during periodic progress, and when finishing; the orchestrator must immediately relay received status events to the user. If no mid-run reporting channel exists, relay observable events such as assignment, waiting, and final completion. Require delegated subagents to provide a final report before leaving; after capturing final report and evidence, close or release the completed subagent, then review the final report and evidence before marking the work complete. If subagent tools are unavailable, the main agent takes over implementation directly and records that takeover in evidence. When subagent tools are available, recursively delegate only bounded sub-tasks and pass these same constraints to every child subagent. Obtain explicit user approval before installing missing tools. Run dry-run or check mode before external-state mutation when available. Provide module evidence, surface evidence, and a structured handoff scoped to the assigned goal/task. Never edit, weaken, skip, or delete existing tests, fixtures, or verification commands to pass a gate without a reason recorded in the plan. After a defect fix, search for other implementations of the same rule or pattern and report them. Use the OS locale for user-visible responses and generated or updated documentation, plans, and memory artifacts.
114
128
  MUST NOT DO: Ask the user. Skip LSP/typecheck/lint because a tool is missing without attempting installation. Refactor unrelated code. Continue an open-ended verification loop. Treat a side question as permission to abandon the active task. Delegate vague or unbounded work. Claim completion without evidence. Modify files outside the assigned ownership.
115
129
  CONTEXT: docs/plan/<topic>.md task, relevant docs/memory notes, applied model guidance, exact files, verification commands.
116
130
  ```
@@ -156,5 +170,7 @@ Run the closest available dry-run or check mode before commands that mutate exte
156
170
  - Dry-run or check evidence exists when the task could mutate external state, or the plan records why no dry-run exists.
157
171
  - The required Light Path or Heavy Path flow was followed.
158
172
  - The model guidance recorded in the plan was applied.
173
+ - No existing test, fixture, or verification command was weakened without a recorded plan reason.
174
+ - Defect fixes record the same-rule sweep result.
159
175
  - Evidence was recorded in `docs/plan/<topic>.md`.
160
176
  - `PLAN.md` status was updated only after evidence existed.