cairn-ai 0.2.9 → 0.3.0
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.
- package/.agents/workflows/cairn-plan.md +11 -18
- package/.agents/workflows/cairn-work.md +14 -20
- package/.claude/commands/cairn-plan.md +1 -1
- package/.claude/commands/cairn-work.md +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/README.ko.md +1 -1
- package/README.md +1 -1
- package/commands/cairn-plan.md +9 -15
- package/commands/cairn-work.md +13 -21
- package/docs/model-guidance/README.md +2 -2
- package/package.json +1 -1
- package/scripts/cairn-goal-cli.mjs +41 -10
- package/scripts/cairn-goal-operations-evidence.mjs +97 -22
- package/scripts/cairn-goal-operations-mutations.mjs +42 -8
- package/scripts/cairn-goal-operations.mjs +28 -15
- package/scripts/cairn-goal-policy.mjs +22 -2
- package/scripts/cairn-goal-state-validation.mjs +113 -12
- package/scripts/cairn-goal-state.mjs +17 -5
- package/scripts/cairn-goal.mjs +7 -2
- package/scripts/cairn-state-context.mjs +13 -4
- package/scripts/cairn-state.mjs +48 -6
- package/skills/cairn-plan/SKILL.md +34 -175
- package/skills/cairn-plan/delegation.md +43 -0
- package/skills/cairn-plan/heavy-path.md +26 -0
- package/skills/cairn-plan/planning-rules.md +68 -0
- package/skills/cairn-work/SKILL.md +37 -142
- package/skills/cairn-work/delegation.md +43 -0
- package/skills/cairn-work/heavy-path.md +16 -0
- package/skills/cairn-work/recovery.md +29 -0
- package/skills/cairn-work/verification.md +65 -0
- package/templates/work-plan.md +3 -1
|
@@ -2,28 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
Use the `cairn-plan` skill.
|
|
4
4
|
|
|
5
|
-
Read the installed Cairn runtime locator at `{{CAIRN_RUNTIME_LOCATOR_JSON}}`. Resolve Cairn scripts, model guidance, templates, commands, and agents through that locator; only repository state such as `MEMORY.md`, `PLAN.md`, and `docs/plan/` belongs to the target project. If the shared runtime or locator is missing, unreadable, or inconsistent, stop every dependent mirror and recover through published/global `cairn doctor` followed by `cairn upgrade`, never through another mirror.
|
|
5
|
+
Read the installed Cairn runtime locator at `{{CAIRN_RUNTIME_LOCATOR_JSON}}`. Resolve Cairn scripts, model guidance, templates, skills, commands, and agents through that locator (`pluginRoot`, `entrypoints.cli`, `resources.modelGuidance`); only repository state such as `MEMORY.md`, `PLAN.md`, and `docs/plan/` belongs to the target project. If the shared runtime or locator is missing, unreadable, or inconsistent, stop every dependent mirror and recover through published/global `cairn doctor` followed by `cairn upgrade`, never through another mirror.
|
|
6
6
|
|
|
7
|
-
Goal:
|
|
7
|
+
Goal: show an initial plan and goal before triage can be interrupted, then leave a decision-complete implementation plan before non-trivial implementation.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Paths: routine known-target Git/GitHub operations (status, fetch, checkout, branch, merge, push, PR management) stay plan/goal-free unless they require code edits, conflict resolution, destructive recovery, release/deploy, or design. Quick Path needs no `docs/plan` artifact and no goal when all hold: unambiguous requirements; about 3 files or fewer inside existing layers; no Heavy Path signal; no external-state mutation; one or two verification commands. Implement, run one focused check plus the project's normal check, and report; if any criterion fails, escalate to Light Path (plan + goal) before further edits. Otherwise use Light Path by default and Heavy Path only when a Heavy Path condition clearly applies.
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
Complexity is staged: request checkpoint, planning checkpoint after exploration, and code checkpoint immediately before the first edit. Before editing, either route may change; after editing begins, only Light → Heavy promotion is allowed. Every change synchronizes the plan artifact, repository goal task roadmap through `goal replan`, native UI plan, and reasoning effort profile. Models always inherit; request Light `medium`, Heavy `high`, final verification/review `xhigh`.
|
|
12
12
|
|
|
13
13
|
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
|
-
2.
|
|
17
|
-
3.
|
|
18
|
-
4. Read relevant `docs/memory/*.md` and
|
|
19
|
-
5.
|
|
20
|
-
6.
|
|
21
|
-
7.
|
|
22
|
-
8.
|
|
23
|
-
9. Use Light Path by default unless a Heavy Path condition clearly applies.
|
|
24
|
-
10. Use `explorer` for independent read-only discovery, impact analysis, pattern search, and read-only verification when available.
|
|
25
|
-
11. Treat the user-called/main agent as the orchestrator: it plans, assigns, verifies, relays received subagent status events or observable lifecycle events to the user, and records evidence. Use `worker` for actual implementation edits with clear file ownership whenever subagent tools are available, on both Light Path and Heavy Path. When the subagent tool provides a progress-reporting channel, require subagents to report status 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.
|
|
26
|
-
12. Record selected path, checked Heavy Path signals, tool readiness, module acceptance verification, and surface integration verification on every implementation task in the existing plan.
|
|
27
|
-
13. Advance native UI steps only after the matching repository task transition succeeds with its required bound evidence records.
|
|
28
|
-
14. Keep the existing `PLAN.md` entry and repository goal synchronized with the finalized plan status.
|
|
29
|
-
15. Use the OS locale for user-visible responses and generated or updated documentation, plans, and memory artifacts unless the user asks for another language.
|
|
16
|
+
2. For non-trivial implementation or continuation of planned work, write an initial `docs/plan/<topic>.md` and `PLAN.md` entry with `triage-plan` as the first active task. This initial plan is decision-complete for triage and explicitly not implementation-ready.
|
|
17
|
+
3. Before exploration, toolcheck, triage, or delegation, call Codex `update_plan` and then `create_goal` when available, and start the repository Cairn goal with the same roadmap. Copy the session-start hook's `Cairn CLI:` prefix literally into each command; never store it in a shell variable. `--root` may be omitted when the cwd is inside the repository. Fold new work into an unfinished goal with `goal replan` instead of cancelling it; when a hook reports another session owns the goal you continue, run `goal claim --session <id>`.
|
|
18
|
+
4. Read relevant `docs/memory/*.md` and, under the locator's `resources.modelGuidance`, `claude.md` for Claude-family or `codex.md` for Codex-family models; invoke Node with the locator's absolute `entrypoints.cli`, `toolcheck`, and `--root` for the target repository (installation only with explicit approval), and explore before asking the user.
|
|
19
|
+
5. Classify the whole work into executable tasks with `dependsOn`, `writeScope`, and handoff files, finalize triage, and update the same plan and `update_plan` roadmap to a decision-complete implementation plan: selected path, checked Heavy Path signals, tool readiness, file scope, dry-run/check, module acceptance, surface integration, stable IDs, and fail-closed evidence. Heavy Path adds an explicit automated test command and `Tests:` evidence line.
|
|
20
|
+
6. Resolve the skill's reference files through the locator's `pluginRoot` and read them only when needed: `cairn://skills/cairn-plan/planning-rules.md` when finalizing, `cairn://skills/cairn-plan/delegation.md` before dispatching, `cairn://skills/cairn-plan/heavy-path.md` only on a Heavy signal or route change.
|
|
21
|
+
7. The user-called/main agent orchestrates; implementation edits go to `worker` when subagent tools are available, otherwise the main agent takes over and records it. Advance a UI step only after the matching repository task transition succeeds with its required bound evidence.
|
|
22
|
+
8. Use the OS locale for user-visible responses and generated or updated documentation, plans, and memory artifacts unless the user asks for another language.
|
|
@@ -2,29 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
Use the `cairn-work` skill.
|
|
4
4
|
|
|
5
|
-
Read the installed Cairn runtime locator at `{{CAIRN_RUNTIME_LOCATOR_JSON}}`. Resolve Cairn scripts and policy resources through that locator; only work artifacts and state belong to the target project. If the shared runtime or locator is missing, unreadable, or inconsistent, stop every dependent mirror and recover through published/global `cairn doctor` followed by `cairn upgrade`, never through another mirror.
|
|
5
|
+
Read the installed Cairn runtime locator at `{{CAIRN_RUNTIME_LOCATOR_JSON}}`. Resolve Cairn scripts, skills, and policy resources through that locator (`pluginRoot`, `entrypoints.cli`, `resources.modelGuidance`); only work artifacts and state belong to the target project. If the shared runtime or locator is missing, unreadable, or inconsistent, stop every dependent mirror and recover through published/global `cairn doctor` followed by `cairn upgrade`, never through another mirror.
|
|
6
6
|
|
|
7
|
-
Goal: execute the
|
|
7
|
+
Goal: execute the active goal's current task without bypassing complexity triage, then keep advancing tasks until goal-level final review completes or the goal is explicitly paused, blocked, or cancelled. Quick Path work has no plan or goal: implement, run one focused check plus the project's normal check, and report; escalate to Light Path (plan + goal) before further edits if any Quick Path criterion fails.
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
Models always inherit. Route reasoning effort per task: Light planning/implementation/verification=`medium`; Heavy planning/review/implementation=`high`; final verification/review=`xhigh`. Requested is Cairn's desired profile; Effective records the dispatch result. Effective=`inherited` means no task override was confirmed, not host incapability. Claim capability only from an exposed interface or dispatch response and distinguish Cairn non-integration from host non-support. Only a new task/worker may receive requested effort through a supported host-native option; never change model/global config. Route changes synchronize the plan artifact, repository goal task roadmap, native UI plan, and reasoning effort profile; preserve completed profiles and recalculate incomplete profiles.
|
|
9
|
+
Record the code checkpoint after exact file/caller/test inspection immediately before the first edit. After editing begins, a new Heavy Path signal promotes Light Path to Heavy Path: stop further edits, mark affected evidence stale, synchronize the plan artifact, `goal replan` roadmap, and native UI plan, and repeat the code checkpoint. Models always inherit; request Light `medium`, Heavy `high`, final verification/review `xhigh`.
|
|
12
10
|
|
|
13
11
|
Procedure:
|
|
14
12
|
|
|
15
13
|
1. Every agent reads project-root `MEMORY.md` when present and continues without repository memory when absent before selecting or executing a task.
|
|
16
|
-
2. Read `PLAN.md`, the detailed plan,
|
|
17
|
-
3. Read the
|
|
18
|
-
4.
|
|
19
|
-
5.
|
|
20
|
-
6.
|
|
21
|
-
7.
|
|
22
|
-
8.
|
|
23
|
-
9.
|
|
24
|
-
10.
|
|
25
|
-
11.
|
|
26
|
-
12.
|
|
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
|
-
14. Record tool readiness and verification evidence in `docs/plan/<topic>.md` and update `PLAN.md`.
|
|
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.
|
|
30
|
-
16. 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
|
+
2. Read `PLAN.md`, the detailed plan, relevant memory notes, and the `cairn://` model guidance recorded in the plan through the installed runtime.
|
|
15
|
+
3. Read the current task with `goal task status`; run `goal ready` and delegate independent ready tasks to parallel workers (same worktree, disjoint `writeScope`). Fold a new request into an unfinished goal with `goal replan` instead of cancelling it. Copy the session-start hook's `Cairn CLI:` prefix literally; never store it in a shell variable. `--root` may be omitted when the cwd is inside the repository.
|
|
16
|
+
4. Confirm tool readiness by invoking Node with the locator's absolute `entrypoints.cli`, `toolcheck`, and `--root` (installation only with explicit approval) and the recorded route.
|
|
17
|
+
5. Before implementation, spend reasoning on a focused executable test contract covering requirements, invariants, boundaries, and failure modes. Confirm the intended failure, then give the `worker` the contract, failing evidence, exact file ownership, and constraints and require the minimum change.
|
|
18
|
+
6. Run module acceptance and surface integration through `goal verify -- ...`; `--kind "moduleAcceptance|surfaceIntegration"` records both from one run. Treat tool exit codes as authoritative and expand context only around failing tests. Heavy Path records explicit `Tests:` evidence.
|
|
19
|
+
7. 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, and after a fix search for other implementations of the same rule or pattern.
|
|
20
|
+
8. Inspect package lifecycle scripts 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 remains fresh.
|
|
21
|
+
9. Relevant mutation makes the current task's evidence stale; rerun it before completing the task. Do not rerun earlier tasks' checks; finish with one goal-level check through `goal finish -- ...`. Missing, failed, skipped, stale, or placeholder evidence never completes work. `goal stats` measures receipt reruns.
|
|
22
|
+
10. Resolve the skill's reference files through the locator's `pluginRoot` and read them only when needed: `cairn://skills/cairn-work/verification.md`, `cairn://skills/cairn-work/delegation.md`, `cairn://skills/cairn-work/recovery.md`, and `cairn://skills/cairn-work/heavy-path.md` (Heavy route only).
|
|
23
|
+
11. Update the plan, goal state, and `PLAN.md` only after evidence exists. 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.
|
|
24
|
+
12. Use the OS locale for user-visible responses and generated or updated documentation, plans, and memory artifacts unless the user asks for another language.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# cairn-plan
|
|
2
2
|
|
|
3
|
-
Start by reading the project-root `MEMORY.md`, write an initial `PLAN.md` and `docs/plan/<topic>.md` with a planned triage task, persist the goal before exploration, then update the same plan after Light/Heavy Path triage and tool readiness checks before implementation.
|
|
3
|
+
Start by reading the project-root `MEMORY.md`, write an initial `PLAN.md` and `docs/plan/<topic>.md` with a planned triage task, persist the goal before exploration, then update the same plan after Light/Heavy Path triage and tool readiness checks before implementation. Quick Path work (criteria in `cairn-plan.md`) needs no plan or goal and escalates to Light Path before further edits when any criterion fails.
|
|
4
4
|
|
|
5
5
|
Read the installed Cairn runtime locator at `{{CAIRN_RUNTIME_LOCATOR_JSON}}`. Resolve `resources.commands` from that JSON object and follow `cairn-plan.md` in that directory. Resolve model guidance and templates through the same locator. Do not resolve Cairn runtime resources from the target project. If the shared runtime or locator is missing, unreadable, or inconsistent, stop all dependent mirrors and recover with published/global `cairn doctor` followed by `cairn upgrade`; do not repair through another mirror.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# cairn-work
|
|
2
2
|
|
|
3
|
-
Start by reading the project-root `MEMORY.md`, then execute the next incomplete module task only after the plan records Light/Heavy Path complexity triage, tool readiness, module acceptance verification, and surface integration verification. Before implementation, spend reasoning on a focused executable test contract covering requirements, invariants, boundaries, and failure modes; confirm the intended failure and make only the minimum implementation that passes. Run verification through `goal verify -- ...`; the default timeout is 600,000 ms (10 minutes), the maximum is 3,600,000 ms (1 hour), and evidence is committed only when the starting goal/task identity and pre/post watched fingerprint still match. Choose a semantic boundary that proves the claim, treat tool exit codes and bounded machine summaries as authoritative, keep successes concise, and expand context only for failing tests. Inspect package lifecycle scripts 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 prior full check remains fresh.
|
|
3
|
+
Start by reading the project-root `MEMORY.md`, then execute the next incomplete module task only after the plan records Light/Heavy Path complexity triage, tool readiness, module acceptance verification, and surface integration verification. Before implementation, spend reasoning on a focused executable test contract covering requirements, invariants, boundaries, and failure modes; confirm the intended failure and make only the minimum implementation that passes. Run verification through `goal verify -- ...` and finish the goal with one goal-level check through `goal finish -- ...`; the default timeout is 600,000 ms (10 minutes), the maximum is 3,600,000 ms (1 hour), and evidence is committed only when the starting goal/task identity and pre/post watched fingerprint still match. Choose a semantic boundary that proves the claim, treat tool exit codes and bounded machine summaries as authoritative, keep successes concise, and expand context only for failing tests. Inspect package lifecycle scripts 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 prior full check remains fresh.
|
|
4
4
|
|
|
5
5
|
If the user asks a side question, status question, or narrow clarification while a 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.
|
|
6
6
|
|
package/README.ko.md
CHANGED
|
@@ -115,7 +115,7 @@ cairn toolcheck
|
|
|
115
115
|
- `cairn uninstall`: 먼저 config 외 모든 ownership manifest 항목을 설치 digest와 대조하고, 수정되거나 삭제된 managed artifact가 하나라도 있으면 아무것도 바꾸지 않고 uninstall 전체를 거부합니다. 그렇지 않으면 managed 항목을 제거·복원하고 빈 managed cache scaffold를 정리하며, unmanaged target과 그 상위 디렉터리는 보존하고 commit 전 실패는 rollback합니다.
|
|
116
116
|
- `cairn toolcheck`: 저장소 스택과 필요한 LSP·검증 도구를 확인하며, 승인된 지원 installer만 실행합니다.
|
|
117
117
|
- `cairn cleanup --root <repo>`: 기본값은 legacy 프로젝트 로컬 `.cairn` 잔여물을 읽기 전용으로 보고합니다. `--yes`는 유효한 활성 상태를 사용자 홈 저장소로 조정·이관하고 알려진 상태/lock/tool 잔여물만 제거하며, 알 수 없는 파일은 보존하고 비었을 때만 디렉터리를 정리합니다.
|
|
118
|
-
- `cairn goal ...`: 저장소의 영속 goal을 시작·조회·일시정지·재개·차단·취소·완료합니다. `goal claim --session <id>`는 같은 작업을 이어받는 session(재개·압축·handoff·fork)에 미완료 goal의 소유권을 넘기며, 미완료 goal 중에 들어온 새 작업은 goal을 취소하지 않고 `goal replan`으로 합칩니다. 기본 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`는 기존 선언 증거를 가져오는 호환 명령입니다. 모든 `goal` 하위 명령에서 `--root`는 선택이며, 생략하면 `HARNESS_REPO_ROOT`, 없으면 cwd에서 가장 가까운 `.git` 상위 디렉터리로 결정됩니다. `goal verify --kind "moduleAcceptance|surfaceIntegration" -- <argv>`는 한 번 실행으로 kind마다 receipt를 기록하며, evidence scope는 `task`와 `goal`뿐입니다. `goal finish [--watch "<path|path>"] [--timeout-ms N] -- <argv>`는 active task를 완료하고(필수 증거가 fresh여야 함) argv를 한 번 실행해 goal requiredEvidence의 모든 kind(기본 `finalReview`)를 goal scope로 기록한 뒤 goal을 완료합니다. 이 goal scope 증거가 모든 task receipt 이후에 기록되면 이전 task 검사를 다시 실행하지 않습니다. `goal stats`는 receipt 수, 재실행, stale receipt를 JSON으로 출력합니다. task는 `dependsOn`(생략 시 이전 task)과 `writeScope`를 선언하고, `goal ready`는 active/ready/blocked task id를 출력하며, write scope가 겹치지 않는 ready task는 같은 worktree에서 최대 4개까지 병렬로 active가 될 수 있습니다.
|
|
119
119
|
- `cairn-memory`: 도메인 지식을 탐색하고 `MEMORY.md`를 갱신합니다.
|
|
120
120
|
- `cairn-plan`: `docs/plan/` 아래에 decision-complete plan을 만듭니다.
|
|
121
121
|
- `cairn-work`: 현재 `PLAN.md`의 다음 모듈 작업을 실행하고 두 검증 게이트를 확보합니다.
|
package/README.md
CHANGED
|
@@ -123,7 +123,7 @@ cairn toolcheck
|
|
|
123
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.
|
|
124
124
|
- `cairn toolcheck`: detects repository stacks and checks required LSP and verification tools; installation runs only for an approved supported installer.
|
|
125
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.
|
|
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.
|
|
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. `--root` is optional for every `goal` subcommand (it resolves via `HARNESS_REPO_ROOT`, else the nearest ancestor containing `.git`). `goal verify --kind "moduleAcceptance|surfaceIntegration" -- <argv>` records one receipt per kind from one run; evidence scopes are `task` and `goal`. `goal finish [--watch "<path|path>"] [--timeout-ms N] -- <argv>` completes the active task (its evidence must be fresh), runs the argv once, records every goal requiredEvidence kind (default `finalReview`) at goal scope, and completes the goal; once that goal-scope evidence is recorded after every task receipt, earlier task checks are not rerun. `goal stats` prints JSON receipt counts, reruns, and stale receipts. Tasks declare `dependsOn` (omitted = previous task) and `writeScope`; `goal ready` prints active/ready/blocked task ids, and up to 4 ready tasks with disjoint write scopes can be active in parallel in the same worktree.
|
|
127
127
|
- `cairn-memory`: explores domain knowledge and updates `MEMORY.md`.
|
|
128
128
|
- `cairn-plan`: creates a decision-complete plan under `docs/plan/`.
|
|
129
129
|
- `cairn-work`: executes the next module task in the current `PLAN.md` with two verification gates.
|
package/commands/cairn-plan.md
CHANGED
|
@@ -6,23 +6,17 @@ Prerequisite: resolve the installed Cairn runtime from the plugin or skill locat
|
|
|
6
6
|
|
|
7
7
|
Goal: show an initial plan and goal before triage can be interrupted, then leave a decision-complete implementation plan before non-trivial implementation.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Paths: routine known-target Git/GitHub operations (status, fetch, checkout, branch, merge, push, PR management) stay plan/goal-free unless they require code edits, conflict resolution, destructive recovery, release/deploy, or design. Quick Path needs no `docs/plan` artifact and no goal when all hold: unambiguous requirements; about 3 files or fewer inside existing layers; no Heavy Path signal; no external-state mutation; one or two verification commands. Implement, run one focused check plus the project's normal check, and report; if any criterion fails, escalate to Light Path (plan + goal) before further edits. Otherwise use Light Path by default and Heavy Path only when a Heavy Path condition clearly applies.
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
Complexity is staged: request checkpoint, planning checkpoint after exploration, and code checkpoint immediately before the first edit. Before editing, either route may change; after editing begins, only Light → Heavy promotion is allowed. Every change synchronizes the plan artifact, repository goal task roadmap through `goal replan`, native UI plan, and reasoning effort profile. Models always inherit; request Light `medium`, Heavy `high`, final verification/review `xhigh`.
|
|
12
12
|
|
|
13
13
|
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
|
-
2.
|
|
17
|
-
3. Before
|
|
18
|
-
4. Read relevant `docs/memory/*.md`
|
|
19
|
-
5.
|
|
20
|
-
6.
|
|
21
|
-
7.
|
|
22
|
-
8.
|
|
23
|
-
9. Use Light Path by default unless a Heavy Path condition clearly applies.
|
|
24
|
-
10. Apply delegation and orchestration policy only after the route is recorded. If subagent tools are unavailable or disallowed by a higher-priority policy, the main agent takes over and records it.
|
|
25
|
-
11. Do not begin implementation until the updated plan records selected path, checked Heavy Path signals, tool readiness, file scope, dry-run/check, module acceptance, surface integration, stable IDs, and fail-closed evidence.
|
|
26
|
-
12. For Heavy Path, include an explicit automated test command and `Tests:` evidence line.
|
|
27
|
-
13. Keep the repository goal and Codex UI plan statuses synchronized as triage and later tasks advance. Advance a UI step only after the matching repository Cairn task transition succeeds with its required bound evidence records.
|
|
28
|
-
14. Use the OS locale for user-visible responses and generated or updated documentation, plans, and memory artifacts unless the user asks for another language.
|
|
16
|
+
2. For non-trivial implementation or continuation of planned work, write an initial `docs/plan/<topic>.md` and `PLAN.md` entry with `triage-plan` as the first active task. This initial plan is decision-complete for triage and explicitly not implementation-ready.
|
|
17
|
+
3. Before exploration, toolcheck, triage, or delegation, call Codex `update_plan` and then `create_goal` when available, and start the repository Cairn goal with the same roadmap. Copy the session-start hook's `Cairn CLI:` prefix literally into each command; never store it in a shell variable. `--root` may be omitted when the cwd is inside the repository. Fold new work into an unfinished goal with `goal replan` instead of cancelling it; when a hook reports another session owns the goal you continue, run `goal claim --session <id>`.
|
|
18
|
+
4. Read relevant `docs/memory/*.md` and the installed model guidance, run `toolcheck --root <repoRoot>` (installation only with explicit approval), and explore before asking the user.
|
|
19
|
+
5. Classify the whole work into executable tasks with `dependsOn`, `writeScope`, and handoff files, finalize triage, and update the same plan and `update_plan` roadmap to a decision-complete implementation plan: selected path, checked Heavy Path signals, tool readiness, file scope, dry-run/check, module acceptance, surface integration, stable IDs, and fail-closed evidence. Heavy Path adds an explicit automated test command and `Tests:` evidence line.
|
|
20
|
+
6. Read the skill's reference files only when needed: `cairn://skills/cairn-plan/planning-rules.md` when finalizing, `cairn://skills/cairn-plan/delegation.md` before dispatching, `cairn://skills/cairn-plan/heavy-path.md` only on a Heavy signal or route change.
|
|
21
|
+
7. The user-called/main agent orchestrates; implementation edits go to `worker` when subagent tools are available, otherwise the main agent takes over and records it. Advance a UI step only after the matching repository task transition succeeds with its required bound evidence.
|
|
22
|
+
8. Use the OS locale for user-visible responses and generated or updated documentation, plans, and memory artifacts unless the user asks for another language.
|
package/commands/cairn-work.md
CHANGED
|
@@ -4,29 +4,21 @@ Use the `cairn-work` skill.
|
|
|
4
4
|
|
|
5
5
|
Prerequisite: resolve the installed Cairn runtime from the plugin or skill locator. If the shared runtime or locator is missing, unreadable, or inconsistent, stop every dependent mirror; run published/global `cairn doctor`, then `cairn upgrade` to recover it. Never repair through another mirror or look for Cairn scripts or model guidance in the target repository.
|
|
6
6
|
|
|
7
|
-
Goal: execute the active goal's current task without bypassing complexity triage, then keep advancing tasks until goal-level final review completes or the goal is explicitly paused, blocked, or cancelled.
|
|
7
|
+
Goal: execute the active goal's current task without bypassing complexity triage, then keep advancing tasks until goal-level final review completes or the goal is explicitly paused, blocked, or cancelled. Quick Path work has no plan or goal: implement, run one focused check plus the project's normal check, and report; escalate to Light Path (plan + goal) before further edits if any Quick Path criterion fails.
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
Models always inherit. Route reasoning effort per task: Light planning/implementation/verification=`medium`; Heavy planning/review/implementation=`high`; final verification/review=`xhigh`. Requested is Cairn's desired profile; Effective records the dispatch result. Effective=`inherited` means no task override was confirmed, not that the host is incapable. Claim host capability only from its exposed interface or dispatch response, and distinguish Cairn non-integration from host non-support. Only a new task/worker may receive requested effort through a supported host-native option; never change model/global config. Route changes synchronize the plan artifact, repository goal task roadmap, native UI plan, and reasoning effort profile; preserve completed profiles and recalculate incomplete profiles.
|
|
9
|
+
Record the code checkpoint after exact file/caller/test inspection immediately before the first edit. After editing begins, a new Heavy Path signal promotes Light Path to Heavy Path: stop further edits, mark affected evidence stale, synchronize the plan artifact, `goal replan` roadmap, and native UI plan, and repeat the code checkpoint. Models always inherit; request Light `medium`, Heavy `high`, final verification/review `xhigh`.
|
|
12
10
|
|
|
13
11
|
Procedure:
|
|
14
12
|
|
|
15
13
|
1. Every agent reads project-root `MEMORY.md` when present and continues without repository memory when absent before selecting or executing a task.
|
|
16
|
-
2. Read `PLAN.md`, the detailed plan,
|
|
17
|
-
3.
|
|
18
|
-
4.
|
|
19
|
-
5.
|
|
20
|
-
6.
|
|
21
|
-
7.
|
|
22
|
-
8.
|
|
23
|
-
9.
|
|
24
|
-
10.
|
|
25
|
-
11.
|
|
26
|
-
12.
|
|
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
|
-
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
|
-
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.
|
|
30
|
-
16. Update the plan, goal state, and `PLAN.md` only after evidence exists.
|
|
31
|
-
17. Answer side questions briefly and resume unless explicitly asked to pause, stop, or switch.
|
|
32
|
-
18. 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
|
+
2. Read `PLAN.md`, the detailed plan, relevant memory notes, and the `cairn://` model guidance recorded in the plan through the installed runtime.
|
|
15
|
+
3. Read the current task with `goal task status`; run `goal ready` and delegate independent ready tasks to parallel workers (same worktree, disjoint `writeScope`). Fold a new request into an unfinished goal with `goal replan` instead of cancelling it. Copy the session-start hook's `Cairn CLI:` prefix literally; never store it in a shell variable. `--root` may be omitted when the cwd is inside the repository.
|
|
16
|
+
4. Confirm tool readiness (installation only with explicit approval) and the recorded route.
|
|
17
|
+
5. Before implementation, spend reasoning on a focused executable test contract covering requirements, invariants, boundaries, and failure modes. Confirm the intended failure, then give the `worker` the contract, failing evidence, exact file ownership, and constraints and require the minimum change.
|
|
18
|
+
6. Run module acceptance and surface integration through `goal verify -- ...`; `--kind "moduleAcceptance|surfaceIntegration"` records both from one run. Treat tool exit codes as authoritative and expand context only around failing tests. Heavy Path records explicit `Tests:` evidence.
|
|
19
|
+
7. 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, and after a fix search for other implementations of the same rule or pattern.
|
|
20
|
+
8. Inspect package lifecycle scripts 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 remains fresh.
|
|
21
|
+
9. Relevant mutation makes the current task's evidence stale; rerun it before completing the task. Do not rerun earlier tasks' checks; finish with one goal-level check through `goal finish -- ...`. Missing, failed, skipped, stale, or placeholder evidence never completes work. `goal stats` measures receipt reruns.
|
|
22
|
+
10. Read the skill's reference files only when needed: `cairn://skills/cairn-work/verification.md`, `cairn://skills/cairn-work/delegation.md`, `cairn://skills/cairn-work/recovery.md`, and `cairn://skills/cairn-work/heavy-path.md` (Heavy route only).
|
|
23
|
+
11. Update the plan, goal state, and `PLAN.md` only after evidence exists. Answer side questions briefly and resume unless explicitly asked to pause, stop, or switch.
|
|
24
|
+
12. Use the OS locale for user-visible responses and generated or updated documentation, plans, and memory artifacts unless the user asks for another language.
|
|
@@ -28,12 +28,12 @@ The goal is not to make prompts long by model. The goal is to stabilize the same
|
|
|
28
28
|
- If subagent tools are unavailable, the main agent takes over implementation directly and records that takeover in evidence.
|
|
29
29
|
- Detect required LSP, typecheck, lint, dry-run, and verification tools before implementation.
|
|
30
30
|
- Missing required tools must be reported with a proposed install. Installation requires explicit user approval and a pinned/supported installer.
|
|
31
|
-
- Treat non-trivial implementation or continuation of planned work as authorization to bind active work to a persisted Cairn goal and stable task IDs, even when the user does not mention a goal. Routine known-target Git/GitHub operations (status, fetch, checkout, branch, merge, push, PR management) stay plan/goal-free unless they require code edits, conflict resolution, destructive recovery, release/deploy, or design. For planned work, after MEMORY.md, first write an initial plan that makes triage the active task, synchronize it before exploration, and implement only from the decision-complete revision. Exclude consultation, explanation, and plan-only requests.
|
|
31
|
+
- Treat non-trivial implementation or continuation of planned work as authorization to bind active work to a persisted Cairn goal and stable task IDs, even when the user does not mention a goal. Routine known-target Git/GitHub operations (status, fetch, checkout, branch, merge, push, PR management) stay plan/goal-free unless they require code edits, conflict resolution, destructive recovery, release/deploy, or design. For planned work, after MEMORY.md, first write an initial plan that makes triage the active task, synchronize it before exploration, and implement only from the decision-complete revision. Exclude consultation, explanation, and plan-only requests. Quick Path changes (criteria in `cairn-plan`) need no plan or goal and escalate to Light Path before further edits when a criterion fails.
|
|
32
32
|
- Treat missing, failed, skipped, stale, or placeholder evidence as failure. Task evidence records must be bound to the current goal, task, and plan.
|
|
33
33
|
- Treat the repository Cairn state as the fail-closed transition authority. Advance the matching Codex UI plan step only after the evidence-gated repository task transition succeeds.
|
|
34
34
|
- Spend reasoning on a focused executable test contract before implementation: requirements, invariants, boundaries, and failure modes. Give implementation only that contract, failing evidence, file scope, and constraints; require the minimum passing change.
|
|
35
35
|
- Run verification through `goal verify -- ...`; its default timeout is 600,000 ms and its maximum is 3,600,000 ms. Treat tool exit codes and bounded machine summaries as authoritative. Record evidence only when the starting goal/task identity and pre/post watched fingerprint still match, and choose the watch set and command at the semantic boundary that proves the claim. The default Git fingerprint excludes ignored files; explicitly pass every ignored artifact needed by the claim with `--watch`. Compress success output and expand context only for failing tests and related code.
|
|
36
|
-
- Use a verification ladder: focused contract tests, one full check after the final change, then package dry-run while the full check remains fresh. Inspect package lifecycle scripts first 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.
|
|
36
|
+
- Use a verification ladder: focused contract tests, one full check after the final change, then package dry-run while the full check remains fresh. Inspect package lifecycle scripts first 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. Do not rerun earlier tasks' checks after later edits; finish with one goal-level check through `goal finish -- <argv>`.
|
|
37
37
|
- Every implementation task must pass module acceptance verification and surface integration verification.
|
|
38
38
|
- Before mutating external state, run the closest available dry-run, check, plan, diff, validate, or equivalent command.
|
|
39
39
|
- Limit each task to two verification passes by default. After two failed passes, record the blocker or split it into sub-tasks instead of continuing a repeated loop.
|
package/package.json
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { resolveRepoRoot } from "./cairn-paths.mjs";
|
|
2
|
+
|
|
1
3
|
export async function runGoalCli(args, {
|
|
2
4
|
stdout = console.log,
|
|
3
5
|
defaultGoalEvidence,
|
|
@@ -6,7 +8,8 @@ export async function runGoalCli(args, {
|
|
|
6
8
|
const [firstCommand = "help", ...remaining] = args;
|
|
7
9
|
const [command = "help", ...rest] = firstCommand === "goal" ? remaining : [firstCommand, ...remaining];
|
|
8
10
|
const { options, positional, passthrough } = parseArgs(rest, command);
|
|
9
|
-
|
|
11
|
+
// Without --root, resolve the repository (HARNESS_REPO_ROOT or the nearest .git ancestor), not a subdirectory cwd.
|
|
12
|
+
const root = resolveRepoRoot({ explicitRoot: options.root ?? null });
|
|
10
13
|
const emit = options.quiet ? () => {} : stdout;
|
|
11
14
|
if (command === "start") {
|
|
12
15
|
const tasks = parseTasks(options.tasks ?? positional.slice(0));
|
|
@@ -91,7 +94,7 @@ export async function runGoalCli(args, {
|
|
|
91
94
|
const result = await operations.verifyAndRecord({
|
|
92
95
|
root,
|
|
93
96
|
taskId: options.task ?? positional[0],
|
|
94
|
-
kind: options.kind,
|
|
97
|
+
kind: splitValues(options.kind),
|
|
95
98
|
scope: options.scope,
|
|
96
99
|
watchPaths: splitValues(options.watch),
|
|
97
100
|
argv: passthrough,
|
|
@@ -99,16 +102,41 @@ export async function runGoalCli(args, {
|
|
|
99
102
|
? 600_000
|
|
100
103
|
: positiveIntegerOption(options["timeout-ms"], "timeout-ms"),
|
|
101
104
|
});
|
|
102
|
-
|
|
103
|
-
id:
|
|
104
|
-
scope:
|
|
105
|
-
kind:
|
|
106
|
-
exitCode:
|
|
107
|
-
outputDigest:
|
|
108
|
-
workspaceFingerprint:
|
|
105
|
+
const summaries = result.receipts.map((receipt) => ({
|
|
106
|
+
id: receipt.id,
|
|
107
|
+
scope: receipt.scope,
|
|
108
|
+
kind: receipt.kind,
|
|
109
|
+
exitCode: receipt.exitCode,
|
|
110
|
+
outputDigest: receipt.outputDigest,
|
|
111
|
+
workspaceFingerprint: receipt.workspaceFingerprint,
|
|
109
112
|
}));
|
|
113
|
+
emit(JSON.stringify(summaries.length === 1 ? summaries[0] : summaries));
|
|
110
114
|
return result.state;
|
|
111
115
|
}
|
|
116
|
+
if (command === "finish") {
|
|
117
|
+
const state = await operations.finishGoal({
|
|
118
|
+
root,
|
|
119
|
+
watchPaths: splitValues(options.watch),
|
|
120
|
+
argv: passthrough,
|
|
121
|
+
timeoutMs: options["timeout-ms"] === undefined
|
|
122
|
+
? 600_000
|
|
123
|
+
: positiveIntegerOption(options["timeout-ms"], "timeout-ms"),
|
|
124
|
+
});
|
|
125
|
+
emit(JSON.stringify({ id: state.goal.id, status: state.goal.status }));
|
|
126
|
+
return state;
|
|
127
|
+
}
|
|
128
|
+
if (command === "ready") {
|
|
129
|
+
const state = await operations.readGoalState({ root });
|
|
130
|
+
const ids = (status) => (state ? state.tasks.filter((task) => task.status === status).map((task) => task.id) : []);
|
|
131
|
+
const summary = { active: ids("active"), ready: operations.readyTasks(state).map((task) => task.id), blocked: ids("blocked") };
|
|
132
|
+
stdout(JSON.stringify(summary));
|
|
133
|
+
return summary;
|
|
134
|
+
}
|
|
135
|
+
if (command === "stats") {
|
|
136
|
+
const stats = operations.goalStats(await operations.readGoalState({ root }), root);
|
|
137
|
+
stdout(JSON.stringify(stats));
|
|
138
|
+
return stats;
|
|
139
|
+
}
|
|
112
140
|
if (["pause", "resume", "block", "cancel", "complete"].includes(command)) {
|
|
113
141
|
const state = await operations.transitionGoal({
|
|
114
142
|
root,
|
|
@@ -118,7 +146,7 @@ export async function runGoalCli(args, {
|
|
|
118
146
|
emit(JSON.stringify(state));
|
|
119
147
|
return state;
|
|
120
148
|
}
|
|
121
|
-
throw new Error("Usage: cairn-goal start|status|task|replan|claim|assign|receipt|verify|pause|resume|block|cancel|complete [--root PATH]");
|
|
149
|
+
throw new Error("Usage: cairn-goal start|status|ready|stats|task|replan|claim|assign|receipt|verify|finish|pause|resume|block|cancel|complete [--root PATH]");
|
|
122
150
|
}
|
|
123
151
|
|
|
124
152
|
function parseArgs(args, command) {
|
|
@@ -182,6 +210,9 @@ function allowedOptions(command) {
|
|
|
182
210
|
assign: ["task", "agent"],
|
|
183
211
|
receipt: ["task", "kind", "scope", "command", "exitCode", "exit-code", "exit", "timestamp", "goalId", "goal-id", "plan"],
|
|
184
212
|
verify: ["task", "kind", "scope", "watch", "timeout-ms"],
|
|
213
|
+
finish: ["watch", "timeout-ms"],
|
|
214
|
+
stats: [],
|
|
215
|
+
ready: [],
|
|
185
216
|
pause: [],
|
|
186
217
|
resume: [],
|
|
187
218
|
block: ["reason"],
|
|
@@ -2,10 +2,11 @@ import { randomUUID } from "node:crypto";
|
|
|
2
2
|
import { normalizeVerificationArgv, normalizeWatchPaths, runVerificationEvidence, workspaceFingerprint } from "./cairn-goal-evidence.mjs";
|
|
3
3
|
import { readGoalState } from "./cairn-goal-state.mjs";
|
|
4
4
|
import {
|
|
5
|
-
normalizeToolEvidence, requiredText, timestamp, validReceiptCommand, validReceiptScope,
|
|
5
|
+
normalizeToolEvidence, pathContains, requiredText, timestamp, validReceiptCommand, validReceiptScope,
|
|
6
6
|
validReceiptSource, validTimestamp,
|
|
7
7
|
} from "./cairn-goal-state-validation.mjs";
|
|
8
|
-
import { assertGoalMutable, mutateGoal, taskById } from "./cairn-goal-operations-mutations.mjs";
|
|
8
|
+
import { assertGoalMutable, isReceiptFresh, mutateGoal, taskById } from "./cairn-goal-operations-mutations.mjs";
|
|
9
|
+
import { setTaskStatus, transitionGoal } from "./cairn-goal-operations.mjs";
|
|
9
10
|
|
|
10
11
|
export async function recordReceipt(options = {}) {
|
|
11
12
|
if (options.source !== undefined && options.source !== "declared") throw new Error("Only goal verify can create tool evidence");
|
|
@@ -69,7 +70,7 @@ export async function verifyAndRecord({
|
|
|
69
70
|
const normalizedArgv = normalizeVerificationArgv(argv);
|
|
70
71
|
const normalizedWatchPaths = normalizeWatchPaths(root, watchPaths);
|
|
71
72
|
const normalizedScope = validReceiptScope(scope);
|
|
72
|
-
const
|
|
73
|
+
const normalizedKinds = normalizeKinds(kind);
|
|
73
74
|
const initialState = await readGoalState({ root });
|
|
74
75
|
if (!initialState) throw new Error("No Cairn goal state exists; start a goal first");
|
|
75
76
|
assertGoalMutable(initialState);
|
|
@@ -78,6 +79,7 @@ export async function verifyAndRecord({
|
|
|
78
79
|
if (initialTask && !["active", "completed"].includes(initialTask.status)) {
|
|
79
80
|
throw new Error(`Verification task must be active or completed: ${initialTask.id}`);
|
|
80
81
|
}
|
|
82
|
+
assertParallelWatch(initialState, initialTask, normalizedWatchPaths);
|
|
81
83
|
const identity = {
|
|
82
84
|
goalId: initialState.goal.id,
|
|
83
85
|
planId: initialState.goal.planId,
|
|
@@ -102,23 +104,96 @@ export async function verifyAndRecord({
|
|
|
102
104
|
throw new Error("Verification task changed during verification; evidence was not recorded");
|
|
103
105
|
}
|
|
104
106
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
107
|
+
// One verification run may prove several evidence kinds; record one receipt per kind without rerunning it.
|
|
108
|
+
const receipts = [];
|
|
109
|
+
let state;
|
|
110
|
+
for (const normalizedKind of normalizedKinds) {
|
|
111
|
+
state = await recordEvidence({
|
|
112
|
+
root,
|
|
113
|
+
taskId: normalizedTaskId,
|
|
114
|
+
kind: normalizedKind,
|
|
115
|
+
scope: normalizedScope,
|
|
116
|
+
command: evidence.command,
|
|
117
|
+
exitCode: 0,
|
|
118
|
+
source: "tool",
|
|
119
|
+
argv: evidence.argv,
|
|
120
|
+
outputDigest: evidence.outputDigest,
|
|
121
|
+
summary: evidence.summary,
|
|
122
|
+
workspaceFingerprint: evidence.workspaceFingerprint,
|
|
123
|
+
watchPaths: evidence.watchPaths,
|
|
124
|
+
goalId: identity.goalId,
|
|
125
|
+
planId: identity.planId,
|
|
126
|
+
expectedIdentity: identity,
|
|
127
|
+
expectedFingerprint: evidence.workspaceFingerprint,
|
|
128
|
+
});
|
|
129
|
+
receipts.push(state.receipts.at(-1));
|
|
130
|
+
}
|
|
131
|
+
return { state, evidence: receipts.at(-1), receipts };
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Completes the active task, proves every goal-level evidence kind with one run, and completes the goal.
|
|
135
|
+
export async function finishGoal({
|
|
136
|
+
root = process.cwd(), argv = [], watchPaths = [], runner, timeoutMs = 600_000,
|
|
137
|
+
} = {}) {
|
|
138
|
+
const initialState = await readGoalState({ root });
|
|
139
|
+
if (!initialState) throw new Error("No Cairn goal state exists; start a goal first");
|
|
140
|
+
assertGoalMutable(initialState);
|
|
141
|
+
const active = initialState.tasks.find((task) => task.status === "active");
|
|
142
|
+
const unfinished = initialState.tasks.filter((task) => task.status !== "completed" && task !== active);
|
|
143
|
+
if (unfinished.length > 0) {
|
|
144
|
+
throw new Error(`Goal cannot be finished while other tasks remain: ${unfinished.map((task) => task.id).join(", ")}`);
|
|
145
|
+
}
|
|
146
|
+
const goalKinds = initialState.goal.requiredEvidence;
|
|
147
|
+
if (goalKinds.length > 0) normalizeVerificationArgv(argv);
|
|
148
|
+
if (active) await setTaskStatus({ root, taskId: active.id, status: "completed" });
|
|
149
|
+
if (goalKinds.length > 0) {
|
|
150
|
+
await verifyAndRecord({ root, scope: "goal", kind: goalKinds, argv, watchPaths, runner, timeoutMs });
|
|
151
|
+
}
|
|
152
|
+
return transitionGoal({ root, status: "completed" });
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Overhead measurement: how many verifications ran, how many were reruns, and which latest receipts are stale.
|
|
156
|
+
export function goalStats(state, root = process.cwd()) {
|
|
157
|
+
if (!state) return null;
|
|
158
|
+
const byKind = {};
|
|
159
|
+
const latest = new Map();
|
|
160
|
+
let reruns = 0;
|
|
161
|
+
for (const receipt of state.receipts) {
|
|
162
|
+
byKind[receipt.kind] = (byKind[receipt.kind] ?? 0) + 1;
|
|
163
|
+
const key = `${receipt.scope}\0${receipt.taskId ?? ""}\0${receipt.kind}`;
|
|
164
|
+
if (latest.has(key)) reruns += 1;
|
|
165
|
+
latest.set(key, receipt);
|
|
166
|
+
}
|
|
167
|
+
const stale = [...latest.values()]
|
|
168
|
+
.filter((receipt) => receipt.source === "tool" && !isReceiptFresh(root, receipt))
|
|
169
|
+
.map((receipt) => ({ scope: receipt.scope, ...(receipt.taskId ? { taskId: receipt.taskId } : {}), kind: receipt.kind }));
|
|
170
|
+
return {
|
|
171
|
+
goalId: state.goal.id,
|
|
172
|
+
status: state.goal.status,
|
|
173
|
+
receipts: state.receipts.length,
|
|
174
|
+
toolReceipts: state.receipts.filter((receipt) => receipt.source === "tool").length,
|
|
175
|
+
byKind,
|
|
176
|
+
reruns,
|
|
177
|
+
stale,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// While other tasks run in parallel, an unscoped fingerprint would include their edits. Require a watch set,
|
|
182
|
+
// and keep it inside the task's declared writeScope so sibling edits cannot stale or fail this evidence.
|
|
183
|
+
function assertParallelWatch(state, task, watchPaths) {
|
|
184
|
+
const othersActive = state.tasks.filter((item) => item.status === "active" && item.id !== task?.id);
|
|
185
|
+
if (task ? othersActive.length === 0 : othersActive.length < 2) return;
|
|
186
|
+
if (watchPaths.length === 0) {
|
|
187
|
+
throw new Error(`Other tasks are active (${othersActive.map((item) => item.id).join(", ")}); pass --watch with this task's write scope`);
|
|
188
|
+
}
|
|
189
|
+
if (task?.writeScope) {
|
|
190
|
+
const outside = watchPaths.filter((path) => !task.writeScope.some((scope) => pathContains(scope, path)));
|
|
191
|
+
if (outside.length > 0) throw new Error(`--watch must stay inside task ${task.id} writeScope while other tasks are active: ${outside.join(", ")}`);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function normalizeKinds(kind) {
|
|
196
|
+
const kinds = (Array.isArray(kind) ? kind : [kind]).map((item) => requiredText(item, "evidence kind"));
|
|
197
|
+
if (kinds.length === 0) throw new Error("evidence kind is required");
|
|
198
|
+
return [...new Set(kinds)];
|
|
124
199
|
}
|