@open-agent-toolkit/cli 0.3.15 → 0.3.17

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.
Files changed (189) hide show
  1. package/assets/NOTICES.md +48 -0
  2. package/assets/agents/oat-phase-implementer.md +26 -6
  3. package/assets/agents/oat-reviewer.md +51 -1
  4. package/assets/bundle-metadata.json +1 -1
  5. package/assets/docs/contributing/documentation.md +4 -0
  6. package/assets/docs/contributing/skills.md +3 -1
  7. package/assets/docs/getting-started/bootstrap.md +1 -1
  8. package/assets/docs/getting-started/index.md +0 -4
  9. package/assets/docs/getting-started/tool-packs.md +0 -7
  10. package/assets/docs/provider-sync/commands.md +21 -0
  11. package/assets/docs/reference/cli-reference.md +44 -2
  12. package/assets/docs/reference/config-and-local-state.md +11 -2
  13. package/assets/docs/reference/configuration.md +10 -2
  14. package/assets/docs/reference/project-artifacts.md +43 -8
  15. package/assets/docs/skills/index.md +1 -0
  16. package/assets/docs/skills/recon.md +8 -5
  17. package/assets/docs/skills/repo-improve.md +9 -7
  18. package/assets/docs/workflows/advanced/autonomy.md +50 -0
  19. package/assets/docs/workflows/advanced/workflow-gates.md +109 -10
  20. package/assets/docs/workflows/approvals-and-automation.md +3 -1
  21. package/assets/docs/workflows/backlog-and-planning/backlog-lifecycle.md +25 -24
  22. package/assets/docs/workflows/backlog-and-planning/remote-project-management.md +0 -7
  23. package/assets/docs/workflows/ideas/lifecycle.md +2 -7
  24. package/assets/docs/workflows/projects/closeout/closeout-skills.md +62 -1
  25. package/assets/docs/workflows/projects/execution/implementation-execution.md +23 -3
  26. package/assets/docs/workflows/projects/execution/project-log.md +6 -0
  27. package/assets/docs/workflows/projects/lifecycle.md +7 -0
  28. package/assets/docs/workflows/projects/reviews/index.md +63 -1
  29. package/assets/docs/workflows/projects/reviews/review-flavors.md +8 -0
  30. package/assets/docs/workflows/waves/wave-workflows.md +11 -3
  31. package/assets/public-package-versions.json +4 -4
  32. package/assets/skills/oat-agent-instructions-apply/SKILL.md +14 -4
  33. package/assets/skills/oat-brainstorm/SKILL.md +17 -13
  34. package/assets/skills/oat-docs-apply/SKILL.md +13 -3
  35. package/assets/skills/oat-doctor/SKILL.md +11 -1
  36. package/assets/skills/oat-explainer-kit/SKILL.md +1 -1
  37. package/assets/skills/oat-explainer-kit/references/lifecycle-contract.md +6 -3
  38. package/assets/skills/oat-pjm-review-backlog/SKILL.md +2 -2
  39. package/assets/skills/oat-pjm-update-repo-reference/SKILL.md +28 -3
  40. package/assets/skills/oat-project-autonomous/SKILL.md +60 -5
  41. package/assets/skills/oat-project-autonomous/references/gate-inventory.md +210 -165
  42. package/assets/skills/oat-project-capture/SKILL.md +7 -3
  43. package/assets/skills/oat-project-complete/SKILL.md +39 -34
  44. package/assets/skills/oat-project-complete/scripts/execute-synced-archive-entry.mjs +106 -28
  45. package/assets/skills/oat-project-complete/scripts/parse-synced-archive-resume-fields.mjs +30 -3
  46. package/assets/skills/oat-project-complete-auto/SKILL.md +388 -0
  47. package/assets/skills/oat-project-design/SKILL.md +12 -9
  48. package/assets/skills/oat-project-discover/SKILL.md +8 -4
  49. package/assets/skills/oat-project-document/SKILL.md +14 -5
  50. package/assets/skills/oat-project-document/references/docs/autonomy-contract.md +210 -165
  51. package/assets/skills/oat-project-implement/SKILL.md +6 -1
  52. package/assets/skills/oat-project-implement/references/completion-and-closeout.md +34 -7
  53. package/assets/skills/oat-project-implement/references/docs/autonomy-contract.md +210 -165
  54. package/assets/skills/oat-project-implement/references/docs/complexity-review-fallback.md +262 -0
  55. package/assets/skills/oat-project-implement/references/docs/gate-approval-record.md +80 -0
  56. package/assets/skills/oat-project-implement/references/phase-execution.md +38 -6
  57. package/assets/skills/oat-project-import-plan/SKILL.md +20 -6
  58. package/assets/skills/oat-project-lite/SKILL.md +7 -5
  59. package/assets/skills/oat-project-lite/references/docs/autonomy-contract.md +210 -165
  60. package/assets/skills/oat-project-new/SKILL.md +6 -2
  61. package/assets/skills/oat-project-next/SKILL.md +19 -2
  62. package/assets/skills/oat-project-next/references/docs/gate-approval-record.md +80 -0
  63. package/assets/skills/oat-project-plan/SKILL.md +14 -6
  64. package/assets/skills/oat-project-pr-final/SKILL.md +4 -3
  65. package/assets/skills/oat-project-pr-final/references/docs/autonomy-contract.md +210 -165
  66. package/assets/skills/oat-project-progress/SKILL.md +27 -1
  67. package/assets/skills/oat-project-progress/references/docs/gate-approval-record.md +80 -0
  68. package/assets/skills/oat-project-promote-spec-driven/SKILL.md +7 -3
  69. package/assets/skills/oat-project-quick-start/SKILL.md +47 -14
  70. package/assets/skills/oat-project-quick-start/references/docs/autonomy-contract.md +210 -165
  71. package/assets/skills/oat-project-quick-start/references/docs/complexity-review-fallback.md +262 -0
  72. package/assets/skills/oat-project-quick-start/references/docs/gate-approval-record.md +80 -0
  73. package/assets/skills/oat-project-reconcile/SKILL.md +8 -4
  74. package/assets/skills/oat-project-retro/SKILL.md +1 -1
  75. package/assets/skills/oat-project-retro/references/apply-procedure.md +6 -6
  76. package/assets/skills/oat-project-retro-file/SKILL.md +7 -3
  77. package/assets/skills/oat-project-review-provide/SKILL.md +23 -4
  78. package/assets/skills/oat-project-review-provide-remote/SKILL.md +19 -1
  79. package/assets/skills/oat-project-review-receive/SKILL.md +57 -14
  80. package/assets/skills/oat-project-review-receive/references/docs/complexity-review-fallback.md +262 -0
  81. package/assets/skills/oat-project-review-receive-remote/SKILL.md +7 -3
  82. package/assets/skills/oat-project-revise/SKILL.md +13 -5
  83. package/assets/skills/oat-project-spec/SKILL.md +8 -4
  84. package/assets/skills/oat-project-summary/SKILL.md +11 -7
  85. package/assets/skills/oat-repo-knowledge-index/SKILL.md +54 -20
  86. package/assets/skills/oat-repo-knowledge-index/scripts/refresh-owned.mjs +118 -0
  87. package/assets/skills/oat-review-provide/SKILL.md +7 -3
  88. package/assets/skills/oat-review-receive-remote/SKILL.md +14 -4
  89. package/assets/skills/oat-wave-execute/SKILL.md +72 -38
  90. package/assets/skills/oat-wave-execute/assets/wrapper-plan-template.md +16 -2
  91. package/assets/skills/oat-wave-program/SKILL.md +15 -6
  92. package/assets/skills/oat-worktree-bootstrap-auto/SKILL.md +45 -17
  93. package/assets/skills/oat-wrap-up/SKILL.md +9 -3
  94. package/assets/skills/oat-wrap-up/references/report-template.md +1 -1
  95. package/assets/skills/recon/SKILL.md +1 -1
  96. package/assets/skills/recon/references/packet-contract.md +18 -3
  97. package/assets/skills/recon/scripts/lib/contracts.mjs +16 -2
  98. package/assets/skills/recon/scripts/reconcile-ledger.mjs +20 -10
  99. package/assets/skills/recon/scripts/render-packet.mjs +31 -3
  100. package/assets/skills/recon/scripts/validate-packet.mjs +5 -5
  101. package/assets/templates/pjm-agents.md +38 -8
  102. package/assets/templates/repo-agents.md +5 -0
  103. package/assets/templates/repo-readme.md +5 -0
  104. package/assets/templates/state.md +7 -0
  105. package/dist/commands/backlog/archive.d.ts +3 -0
  106. package/dist/commands/backlog/archive.d.ts.map +1 -1
  107. package/dist/commands/backlog/archive.js +63 -26
  108. package/dist/commands/backlog/index.d.ts.map +1 -1
  109. package/dist/commands/backlog/index.js +3 -0
  110. package/dist/commands/backlog/rewrite-references.d.ts +5 -0
  111. package/dist/commands/backlog/rewrite-references.d.ts.map +1 -1
  112. package/dist/commands/backlog/rewrite-references.js +39 -1
  113. package/dist/commands/config/index.d.ts.map +1 -1
  114. package/dist/commands/config/index.js +13 -0
  115. package/dist/commands/gate/index.d.ts +5 -0
  116. package/dist/commands/gate/index.d.ts.map +1 -1
  117. package/dist/commands/gate/index.js +270 -9
  118. package/dist/commands/init/index.d.ts.map +1 -1
  119. package/dist/commands/init/index.js +1 -0
  120. package/dist/commands/internal/commit-paths.d.ts +3 -0
  121. package/dist/commands/internal/commit-paths.d.ts.map +1 -0
  122. package/dist/commands/internal/commit-paths.js +31 -0
  123. package/dist/commands/internal/index.d.ts.map +1 -1
  124. package/dist/commands/internal/index.js +2 -0
  125. package/dist/commands/pjm/init.d.ts.map +1 -1
  126. package/dist/commands/pjm/init.js +37 -8
  127. package/dist/commands/project/archive/archive-utils.d.ts +6 -3
  128. package/dist/commands/project/archive/archive-utils.d.ts.map +1 -1
  129. package/dist/commands/project/archive/archive-utils.js +510 -104
  130. package/dist/commands/project/archive/push-runner.d.ts.map +1 -1
  131. package/dist/commands/project/archive/push-runner.js +1 -0
  132. package/dist/commands/project/log/append.d.ts +2 -3
  133. package/dist/commands/project/log/append.d.ts.map +1 -1
  134. package/dist/commands/project/log/append.js +55 -101
  135. package/dist/commands/project/migrate/index.d.ts +1 -2
  136. package/dist/commands/project/migrate/index.d.ts.map +1 -1
  137. package/dist/commands/project/migrate/index.js +8 -2
  138. package/dist/commands/project/new/scaffold.d.ts.map +1 -1
  139. package/dist/commands/project/new/scaffold.js +4 -4
  140. package/dist/commands/project/promote/promote.d.ts +8 -0
  141. package/dist/commands/project/promote/promote.d.ts.map +1 -1
  142. package/dist/commands/project/promote/promote.js +37 -6
  143. package/dist/commands/project/status.d.ts.map +1 -1
  144. package/dist/commands/project/status.js +3 -0
  145. package/dist/commands/project/sync/ref-sync.d.ts +12 -1
  146. package/dist/commands/project/sync/ref-sync.d.ts.map +1 -1
  147. package/dist/commands/project/sync/ref-sync.js +347 -23
  148. package/dist/commands/shared/exact-path-commit.d.ts +32 -0
  149. package/dist/commands/shared/exact-path-commit.d.ts.map +1 -0
  150. package/dist/commands/shared/exact-path-commit.js +691 -0
  151. package/dist/commands/shared/frontmatter.d.ts +1 -1
  152. package/dist/commands/shared/frontmatter.d.ts.map +1 -1
  153. package/dist/commands/shared/frontmatter.js +1 -0
  154. package/dist/commands/state/generate.d.ts +1 -1
  155. package/dist/commands/state/generate.d.ts.map +1 -1
  156. package/dist/commands/state/generate.js +87 -8
  157. package/dist/commands/sync/apply.d.ts.map +1 -1
  158. package/dist/commands/sync/apply.js +31 -5
  159. package/dist/commands/sync/dry-run.d.ts.map +1 -1
  160. package/dist/commands/sync/dry-run.js +28 -11
  161. package/dist/commands/sync/sync.utils.d.ts +13 -0
  162. package/dist/commands/sync/sync.utils.d.ts.map +1 -1
  163. package/dist/commands/sync/sync.utils.js +21 -1
  164. package/dist/commands/tools/shared/pack-manifest.d.ts.map +1 -1
  165. package/dist/commands/tools/shared/pack-manifest.js +1 -0
  166. package/dist/config/oat-config.d.ts +1 -0
  167. package/dist/config/oat-config.d.ts.map +1 -1
  168. package/dist/config/oat-config.js +3 -0
  169. package/dist/config/resolve.d.ts.map +1 -1
  170. package/dist/config/resolve.js +1 -0
  171. package/dist/drift/detector.js +6 -6
  172. package/dist/engine/compute-plan.d.ts.map +1 -1
  173. package/dist/engine/compute-plan.js +87 -2
  174. package/dist/engine/engine.types.d.ts +10 -1
  175. package/dist/engine/engine.types.d.ts.map +1 -1
  176. package/dist/engine/engine.types.js +4 -0
  177. package/dist/engine/execute-plan.d.ts.map +1 -1
  178. package/dist/engine/execute-plan.js +42 -5
  179. package/dist/engine/managed-copy-hash.d.ts +4 -3
  180. package/dist/engine/managed-copy-hash.d.ts.map +1 -1
  181. package/dist/engine/managed-copy-hash.js +4 -3
  182. package/dist/fs/assets.d.ts.map +1 -1
  183. package/dist/fs/assets.js +7 -1
  184. package/dist/manifest/hash.d.ts +8 -10
  185. package/dist/manifest/hash.d.ts.map +1 -1
  186. package/dist/ui/output.js +1 -1
  187. package/dist/validation/skills.d.ts.map +1 -1
  188. package/dist/validation/skills.js +151 -11
  189. package/package.json +3 -2
@@ -6,7 +6,7 @@ disable-model-invocation: false
6
6
  allowed-tools: Read, Write, Bash, Glob, Grep, AskUserQuestion
7
7
  user-invocable: true
8
8
  metadata:
9
- version: 1.0.3
9
+ version: 1.0.4
10
10
  ---
11
11
 
12
12
  # Wrap-Up
@@ -365,7 +365,13 @@ so I can review before committing it.
365
365
  - Automation patterns (Claude Code `CronCreate`, Codex host scheduling, plain cron): `references/automation-recipes.md`
366
366
  - Prerequisite command: `oat repo archive sync` at `packages/cli/src/commands/repo/archive/index.ts`
367
367
  - Merged-PR query pattern: `packages/cli/src/commands/repo/pr-comments/collect/collect-comments.ts:198-205`
368
- - Summary frontmatter schema: `.oat/templates/summary.md`
368
+ - Summary frontmatter schema: the `summary` template, resolved through the repository, user, then bundled tier. Run `oat template resolve summary --json` and read the reported `path`. A user-scope-only install has no repository `.oat/templates/`, and the bundle tier reports `"path": null`; in that case copy it to a scratch file and read the copy:
369
+
370
+ ```bash
371
+ SUMMARY_TEMPLATE_DIR="$(mktemp -d)"
372
+ oat template resolve summary --output "$SUMMARY_TEMPLATE_DIR/summary.md"
373
+ ```
374
+
369
375
  - Config key: `archive.wrapUpExportPath` (managed via `oat config set archive.wrapUpExportPath <path>`)
370
376
 
371
377
  ## Troubleshooting
@@ -394,7 +400,7 @@ so I can review before committing it.
394
400
 
395
401
  **"Shipped via OAT projects" is empty or "Other merged PRs" contains PRs that clearly correspond to included summaries (false negative):**
396
402
 
397
- - Step 7's cross-reference logic is strict: it only claims a PR for a summary when the summary body literally contains the PR number (`#<n>`, `github.com/.../pull/<n>`, or a bare PR number adjacent to keywords like "PR", "merged", "ships in"), or when a sibling `pr/` directory inside the project references the PR. OAT summaries as authored today (see `.oat/templates/summary.md`) do not inline PR numbers, so in repos that have not adopted the convention of citing PRs in summaries, the cross-reference will often find zero matches and all merged PRs will land in "Other merged PRs".
403
+ - Step 7's cross-reference logic is strict: it only claims a PR for a summary when the summary body literally contains the PR number (`#<n>`, `github.com/.../pull/<n>`, or a bare PR number adjacent to keywords like "PR", "merged", "ships in"), or when a sibling `pr/` directory inside the project references the PR. OAT summaries as authored today (see the `summary` template, resolved as in Reference) do not inline PR numbers, so in repos that have not adopted the convention of citing PRs in summaries, the cross-reference will often find zero matches and all merged PRs will land in "Other merged PRs".
398
404
  - **Workaround options (author-time)**: add a short "Associated PRs" section to project summaries and list PR numbers inline, OR populate `pr/` under each project directory with PR metadata so the sibling scan has data to match against.
399
405
  - **Workaround options (report-time)**: manually re-read the generated report; move PRs from "Other merged PRs" into "Shipped via OAT projects" by hand if the correspondence is obvious (matching keywords in the PR title and the summary's What Was Implemented section). The skill is designed to produce an editable draft, not a final release note.
400
406
  - This is a v1 limitation. Future versions may add a looser heuristic (e.g., title-keyword matching, commit-to-project mapping via `git log`) to close the gap.
@@ -63,7 +63,7 @@ generated_at: YYYY-MM-DDTHH:MM:SSZ
63
63
 
64
64
  ## Section omission rule
65
65
 
66
- Apply the same rule as `.oat/templates/summary.md`: **omit any section with no content**. Do not leave empty sections or "None" placeholders. The minimum viable wrap-up is `# Wrap-up` + `## TL;DR` + one of (`Features introduced` | `Bug fixes` | `Shipped via OAT projects` | `Other merged PRs`) + `Included summaries (provenance)`.
66
+ Apply the same rule as the `summary` template (resolve it with `oat template resolve summary`, as in the skill's Reference section): **omit any section with no content**. Do not leave empty sections or "None" placeholders. The minimum viable wrap-up is `# Wrap-up` + `## TL;DR` + one of (`Features introduced` | `Bug fixes` | `Shipped via OAT projects` | `Other merged PRs`) + `Included summaries (provenance)`.
67
67
 
68
68
  If the window is completely empty (no summaries, no merged PRs), still write the file (or stdout) with:
69
69
 
@@ -6,7 +6,7 @@ disable-model-invocation: true
6
6
  user-invocable: true
7
7
  allowed-tools: Read, Write, Glob, Grep, Bash, AskUserQuestion, Agent, mcp__*
8
8
  metadata:
9
- version: 1.1.7
9
+ version: 1.1.8
10
10
  ---
11
11
 
12
12
  # Recon
@@ -393,9 +393,24 @@ canonical JSON. Any edited, injected, duplicated, or unprojected field differs
393
393
  (`REVIEW_BRIEF_MISMATCH`), so a brief cannot carry an injected claim, note, or
394
394
  source. Each review's dispositions must be unique and name claims its brief
395
395
  lists. The brief `id` is a lowercase slug of at most 64 characters and
396
- `createdAt` a UTC ISO-8601 instant. A reviewer may leave a briefed claim without
397
- a disposition; like an `uncertain` one, the claim stays `unresolved`, and
398
- `packet.md` lists it under Review Downgrades as not reviewed.
396
+ `createdAt` a UTC ISO-8601 instant. A `verified` claim needs the affirming
397
+ disposition of every review kind the achieved profile requires, and
398
+ reconciliation and publication read the one rule
399
+ (`requiredReviewKindsForProfile` in `scripts/lib/contracts.mjs`):
400
+
401
+ - `semantic` (`affirmed`), `adversarial` (`unchallenged`), and `coverage`
402
+ (`covered`) are required at `standard` and `thorough`.
403
+ - `redundant-verification` (`affirmed`) is also required at `thorough`. The
404
+ reconciler requires it whenever a complete same-run result is incorporated,
405
+ because only a complete redundant-verification pass achieves `thorough`.
406
+ - `contradiction-resolution` is conditional and never required; a `rejected`
407
+ disposition still makes a claim `unsupported`.
408
+
409
+ A claim any required review leaves without a disposition stays `unresolved`,
410
+ like an `uncertain` one, so the packet publishes as an honest `complete` or
411
+ `partial` instead of failing with `MISSING_INDEPENDENT_REVIEW`. `packet.md`
412
+ lists it under Review Downgrades: as not reviewed when the review's brief
413
+ listed the claim, and as outside its brief when it did not.
399
414
  All reject dossier paths, compiler reasoning, synthesis prose, provenance
400
415
  references, and prior review IDs.
401
416
 
@@ -2281,8 +2281,9 @@ function validateReviewBriefArtifact(value, errors) {
2281
2281
  }
2282
2282
 
2283
2283
  // The affirming disposition each review kind gives a claim it supports. The
2284
- // first three are the required reviews for `verified`. The reconciler, the
2285
- // validator, and the renderer all read this one table.
2284
+ // first three are required for `verified` at every reviewed profile
2285
+ // (`requiredReviewKindsForProfile` adds the thorough kind). The reconciler,
2286
+ // the validator, and the renderer all read this one table.
2286
2287
  export const affirmingDispositionByReviewKind = Object.freeze({
2287
2288
  semantic: 'affirmed',
2288
2289
  adversarial: 'unchallenged',
@@ -2297,6 +2298,19 @@ export const requiredReviewKinds = Object.freeze([
2297
2298
  'coverage',
2298
2299
  ]);
2299
2300
 
2301
+ // The review kinds whose affirming disposition a `verified` claim needs at the
2302
+ // given achieved profile: the core reviews, plus redundant verification at
2303
+ // `thorough`. Contradiction resolution is conditional and never required.
2304
+ // Reconciliation, publication, and the renderer all read this one rule, so a
2305
+ // claim any required kind leaves without a disposition stays below `verified`
2306
+ // everywhere.
2307
+ export function requiredReviewKindsForProfile(profile) {
2308
+ return Object.freeze([
2309
+ ...requiredReviewKinds,
2310
+ ...(profile === 'thorough' ? ['redundant-verification'] : []),
2311
+ ]);
2312
+ }
2313
+
2300
2314
  // `unresolvedIssues` entries are a closed union:
2301
2315
  // - a string (legacy), read as a global issue;
2302
2316
  // - `{ text, claimIds }`, scoped to a non-empty list of unique claims the
@@ -15,6 +15,7 @@ import { isDirectExecution } from './lib/cli-entry.mjs';
15
15
  import {
16
16
  affirmingDispositionByReviewKind,
17
17
  requiredReviewKinds,
18
+ requiredReviewKindsForProfile,
18
19
  unresolvedIssuesBlockClaim,
19
20
  validateArtifactShape,
20
21
  } from './lib/contracts.mjs';
@@ -25,13 +26,6 @@ import {
25
26
  assertUnchangedRoot,
26
27
  } from './lib/safe-path.mjs';
27
28
 
28
- const requiredDispositions = new Map(
29
- requiredReviewKinds.map((kind) => [
30
- kind,
31
- affirmingDispositionByReviewKind[kind],
32
- ]),
33
- );
34
-
35
29
  const permittedDispositions = new Map([
36
30
  ['semantic', new Set(['affirmed', 'rejected', 'uncertain'])],
37
31
  ['adversarial', new Set(['unchallenged', 'challenged'])],
@@ -99,7 +93,7 @@ export function reconcileLedger({
99
93
  const reviews = new Map(
100
94
  reviewResults.map((review) => [review.reviewKind, review]),
101
95
  );
102
- for (const [kind] of requiredDispositions) {
96
+ for (const kind of requiredReviewKinds) {
103
97
  const review = reviews.get(kind);
104
98
  if (!review || review.runId !== runId || review.status !== 'complete') {
105
99
  throw new Error(`Reconciliation requires a complete ${kind} result`);
@@ -109,6 +103,22 @@ export function reconcileLedger({
109
103
  throw new Error(`Reconciliation received an invalid ${kind} disposition`);
110
104
  }
111
105
  }
106
+ // A claim needs a disposition from every review kind publication requires
107
+ // for the achieved profile. The thorough profile is achieved only with a
108
+ // complete redundant-verification pass, so an incorporated complete result
109
+ // of that kind makes it required here too. Missing it keeps the claim
110
+ // `unresolved`, exactly like an `uncertain` disposition.
111
+ const achievesThorough = reviewResults.some(
112
+ (review) =>
113
+ review.reviewKind === 'redundant-verification' &&
114
+ review.runId === runId &&
115
+ review.status === 'complete',
116
+ );
117
+ const verifyingDispositions = new Map(
118
+ requiredReviewKindsForProfile(
119
+ achievesThorough ? 'thorough' : 'standard',
120
+ ).map((kind) => [kind, affirmingDispositionByReviewKind[kind]]),
121
+ );
112
122
  const ledger = exactClone(priorLedger);
113
123
  ledger.revision = priorLedger.revision + 1;
114
124
  ledger.inputArtifacts = [
@@ -244,7 +254,7 @@ export function reconcileLedger({
244
254
  const issueBlocked = supporting.some((review) =>
245
255
  unresolvedIssuesBlockClaim(review, claim.id),
246
256
  );
247
- const incomplete = [...requiredDispositions.keys()].some(
257
+ const incomplete = [...verifyingDispositions.keys()].some(
248
258
  (kind) =>
249
259
  !reviewResults.some(
250
260
  (review) =>
@@ -267,7 +277,7 @@ export function reconcileLedger({
267
277
  finding.material === true && finding.claimIds.includes(claim.id),
268
278
  ),
269
279
  );
270
- const coreComplete = [...requiredDispositions].every(
280
+ const coreComplete = [...verifyingDispositions].every(
271
281
  ([kind, disposition]) =>
272
282
  supporting.some(
273
283
  (review) =>
@@ -9,6 +9,7 @@ import { isDirectExecution } from './lib/cli-entry.mjs';
9
9
  import {
10
10
  affirmingDispositionByReviewKind,
11
11
  classifyUnresolvedIssue,
12
+ requiredReviewKindsForProfile,
12
13
  unresolvedIssuesBlockClaim,
13
14
  } from './lib/contracts.mjs';
14
15
  import { reviewBriefEntries } from './lib/review-binding.mjs';
@@ -151,17 +152,35 @@ function omittedReviews(claimId, reviews) {
151
152
  .map(({ value }) => value);
152
153
  }
153
154
 
155
+ // Every incorporated review of a kind the achieved profile requires whose
156
+ // brief did not list `claimId`, so it gave the claim no disposition. Such a
157
+ // claim cannot be verified, and the reason is the missing review, not an
158
+ // omission by the reviewer.
159
+ function unbriefedRequiredReviews(claimId, reviews, requiredKinds) {
160
+ return reviews
161
+ .filter(
162
+ ({ value, briefed }) =>
163
+ requiredKinds.includes(value.reviewKind) &&
164
+ !briefed.has(claimId) &&
165
+ !(value.dispositions ?? []).some((item) => item.claimId === claimId),
166
+ )
167
+ .map(({ value }) => value);
168
+ }
169
+
154
170
  function issueText(entry) {
155
171
  return typeof entry === 'string' ? entry : (entry?.text ?? '');
156
172
  }
157
173
 
158
174
  // Every claim an incorporated review kept below `verified` (an unresolved
159
175
  // issue that applies to it, a coverage finding that names it, a
160
- // non-affirming disposition, or an assurance review that left a briefed claim
161
- // without a disposition), with the review's own words. Key-claim status
176
+ // non-affirming disposition, an assurance review that left a briefed claim
177
+ // without a disposition, or a review the achieved profile requires whose
178
+ // brief left the claim out), with the review's own words. Key-claim status
162
179
  // alone would let a `complete` packet hide a downgraded non-key claim.
163
180
  function reviewDowngradeLines(validatedRun) {
164
- const { ledger, artifacts, assuranceReviewIds } = validatedRun;
181
+ const { ledger, artifacts, assuranceReviewIds, achievedProfile } =
182
+ validatedRun;
183
+ const requiredKinds = requiredReviewKindsForProfile(achievedProfile);
165
184
  const assurance = new Set(assuranceReviewIds);
166
185
  const reviews = artifacts
167
186
  .map(({ value }) => value)
@@ -214,6 +233,15 @@ function reviewDowngradeLines(validatedRun) {
214
233
  `${review.reviewKind} review: not reviewed (no disposition)`,
215
234
  );
216
235
  }
236
+ for (const review of unbriefedRequiredReviews(
237
+ claim.id,
238
+ briefedReviews,
239
+ requiredKinds,
240
+ )) {
241
+ reasons.push(
242
+ `${review.reviewKind} review: outside its brief (no disposition)`,
243
+ );
244
+ }
217
245
  for (const review of reviews) {
218
246
  for (const finding of review.coverageFindings ?? []) {
219
247
  if (!finding.claimIds.includes(claim.id)) continue;
@@ -12,7 +12,7 @@ import {
12
12
  isObject,
13
13
  issue,
14
14
  profiles,
15
- requiredReviewKinds,
15
+ requiredReviewKindsForProfile,
16
16
  unresolvedIssuesBlockClaim,
17
17
  validateArtifactShape,
18
18
  } from './lib/contracts.mjs';
@@ -2082,10 +2082,10 @@ function validateAssurance(validatedRun, errors, checkBrief) {
2082
2082
  );
2083
2083
  }
2084
2084
  const required = new Map(
2085
- [
2086
- ...requiredReviewKinds,
2087
- ...(achievedProfile === 'thorough' ? ['redundant-verification'] : []),
2088
- ].map((kind) => [kind, affirmingDispositionByReviewKind[kind]]),
2085
+ requiredReviewKindsForProfile(achievedProfile).map((kind) => [
2086
+ kind,
2087
+ affirmingDispositionByReviewKind[kind],
2088
+ ]),
2089
2089
  );
2090
2090
  const satisfied = new Set();
2091
2091
  const reviewerLanes = new Set();
@@ -31,15 +31,41 @@ Invariant: `backlog/items/` holds only active work (`status: open` or
31
31
  The agent or person shipping the work owns the close-out, in the same
32
32
  commit/PR as the work whenever practical.
33
33
 
34
- **Close-out (primary path).** Run `oat backlog archive <id>` — add `--wont-do`
35
- for abandoned work and `--summary "<text>"` to record the outcome. The command
34
+ **Close-out (primary path).** Run `oat backlog archive <id> --summary "<outcome>" --json`
35
+ for completed work; a nonblank summary is required. Add `--wont-do` for
36
+ abandoned work; that path may omit its summary and ledger entry. The command
36
37
  performs the whole close-out atomically: it flips `status` to the terminal
37
38
  value and bumps `updated`, appends the canonical `backlog/completed.md` entry
38
39
  (always for `closed`; for `wont_do` only when `--summary` is given), moves the
39
40
  item file from `backlog/items/` to `backlog/archived/`, rewrites inbound
40
41
  `.oat/repo` links and path references to the moved file (reporting each
41
- rewritten file), and regenerates `backlog/index.md`. Stage the resulting
42
- changes with the shipping commit/PR.
42
+ rewritten file), and regenerates `backlog/index.md`. Commit the resulting
43
+ changes through the exact-path close-out below with the shipping commit/PR.
44
+
45
+ Archive never stages files. Capture each successful `--json` result and retain
46
+ its complete `affectedPaths` array, including the old tracked item deletion,
47
+ the archived destination, completed ledger, index and rewritten references.
48
+ On retry use the returned operation paths; do not reconstruct ownership from
49
+ all files that mention the item. If this close-out owns a kickoff handoff,
50
+ remove it with a filesystem operation and append its tracked deletion path.
51
+ Convert returned absolute paths to literal repository-relative files using
52
+ the same repository root (avoiding checkout/scratch-path aliases).
53
+ Deduplicate the exact file list, format only existing supported text files
54
+ with the repository's documented formatter, and commit that list through
55
+ `oat internal commit-paths` with enabled hooks. Verify helper availability
56
+ first; if unavailable, stop and update the CLI. Set a unique `COMMIT_IDENTITY`
57
+ before the first attempt and retain the identity, message and literal paths
58
+ through retries; a blocked/failed helper result stops close-out. Unrelated
59
+ staged and unstaged work must remain intact. An empty settled-noop path list
60
+ needs no commit; omit a missing source path only if Git proves it was never
61
+ tracked, never omit a tracked old path or the new destination.
62
+
63
+ ```bash
64
+ oat internal commit-paths --help || exit 1
65
+ # ARCHIVE_OWNED_FILES is the literal affectedPaths union plus owned handoff deletion.
66
+ # Format the existing text members with the repository's documented write command.
67
+ oat internal commit-paths --identity "${COMMIT_IDENTITY:?set once and retain for retries}:backlog-closeout" --message "chore(pjm): archive completed backlog work" -- "${ARCHIVE_OWNED_FILES[@]}" || exit 1
68
+ ```
43
69
 
44
70
  **Manual fallback.** These are the steps the command automates — follow them, in
45
71
  order, only when closing out by hand:
@@ -48,13 +74,16 @@ order, only when closing out by hand:
48
74
  2. Append a summary entry to `backlog/completed.md` (newest first; entry format
49
75
  is documented at the top of that file). Add the entry for a `wont_do` item
50
76
  only when the abandonment itself is worth recording (an explicit summary).
51
- 3. `git mv` the item file from `backlog/items/` to `backlog/archived/`, then
77
+ 3. Move the item file with a filesystem rename (`mv`, without staging) from `backlog/items/` to `backlog/archived/`, then
52
78
  rewrite inbound `.oat/repo` links to `archived/` (plans, decision records,
53
79
  other items, and repository-root paths such as `oat_external_plan_sources`).
54
- 4. Run `oat backlog regenerate-index` and stage the regenerated
55
- `backlog/index.md`.
80
+ 4. Run `oat backlog regenerate-index`; include the regenerated
81
+ `backlog/index.md` in the exact owned path list.
56
82
  5. If the completion changes the operating picture, refresh
57
83
  `current-state.md` and the curated overview section of `backlog/index.md`.
84
+ 6. Collect both old/new item names, every written ledger/index/reference and
85
+ any owned handoff deletion, format existing text, then commit the complete
86
+ operation through `oat internal commit-paths` as above.
58
87
 
59
88
  A partial close-out (status flipped or `completed.md` updated, but the file
60
89
  left in `items/`) is how drift starts — finish all steps or none. `oat pjm
@@ -97,7 +126,8 @@ convention.
97
126
  - repo conventions and verification gates the item file does not restate;
98
127
  - a close-out section requiring (a) the **Backlog Lifecycle** above executed
99
128
  in the same PR that ships the item and (b) deletion of the handoff file
100
- (`git rm`) in that same PR.
129
+ (filesystem removal, included as an owned tracked deletion in
130
+ the same exact-path commit) in that same PR.
101
131
  - **Staleness:** if a later alignment pass drops an item from the kickoff
102
132
  stack, delete its handoff in that pass rather than letting it drift.
103
133
  - Every backlog item reference — in review output, alignment docs, and
@@ -15,3 +15,8 @@ Use this directory as the canonical OAT repo-reference root.
15
15
  `oat backlog archive <id>` in the same PR that ships the work.
16
16
  - `README.md` is the human-facing orientation for this directory; agent-facing
17
17
  rules live in the `AGENTS.md` files alongside each subdirectory.
18
+
19
+ Backlog archive is staging-neutral. Follow `pjm/AGENTS.md` Backlog Lifecycle:
20
+ consume the complete JSON `affectedPaths`, add any owned handoff deletion,
21
+ format existing text, and commit the exact operation through
22
+ `oat internal commit-paths`, preserving unrelated staged/unstaged work.
@@ -33,3 +33,8 @@ Agent-facing rules live in the `AGENTS.md` files alongside each directory.
33
33
  - **Close-out.** When work ships that satisfies a backlog item's acceptance
34
34
  criteria, the item is closed and archived in the same commit/PR — run
35
35
  `oat backlog archive <id>`. See the Backlog Lifecycle in `pjm/AGENTS.md`.
36
+
37
+ Backlog archive is staging-neutral. Follow `pjm/AGENTS.md` Backlog Lifecycle:
38
+ consume the complete JSON `affectedPaths`, add any owned handoff deletion,
39
+ format existing text, and commit the exact operation through
40
+ `oat internal commit-paths`, preserving unrelated staged/unstaged work.
@@ -75,6 +75,13 @@ oat_workflow_origin: native # native | imported
75
75
  # receive_completed: false
76
76
  # failure: null
77
77
  # updated_at: '2026-07-18T00:00:00Z'
78
+ # decided_at: null # ISO 8601 UTC; set with every allowed or blocked outcome (core record: gate-approval-record.md)
79
+ # oat_quick_start_gate: # optional; persisted quick-start plan gate outcome, absent when no gate is configured (core record: gate-approval-record.md)
80
+ # status: allowed # allowed | blocked
81
+ # disposition: passed # passed | warned | prompt_approved | project_disabled; null when blocked
82
+ # config_fingerprint: '<stable hash of resolved gate declaration>'
83
+ # reviewed_head: null # full SHA of the commit the gate reviewed; provenance only
84
+ # decided_at: null # ISO 8601 UTC
78
85
  oat_docs_updated: null # null | skipped | complete — documentation sync status
79
86
  oat_pr_status: null # null | ready | open | closed | merged — actual PR state for the current project
80
87
  oat_pr_url: null # null | string — tracked PR URL when a PR exists
@@ -13,6 +13,9 @@ export interface ArchiveBacklogItemResult {
13
13
  * repository root.
14
14
  */
15
15
  rewrittenReferences: string[];
16
+ /** Normalized absolute paths owned by this operation, including the old
17
+ * item deletion and retry outputs. Archive never stages any of these paths. */
18
+ affectedPaths: string[];
16
19
  warnings: string[];
17
20
  }
18
21
  export interface ArchiveBacklogItemOptions {
@@ -1 +1 @@
1
- {"version":3,"file":"archive.d.ts","sourceRoot":"","sources":["../../../src/commands/backlog/archive.ts"],"names":[],"mappings":"AAUA,OAAO,EAEL,KAAK,iBAAiB,EAGvB,MAAM,sBAAsB,CAAC;AAmB9B,mDAAmD;AACnD,MAAM,WAAW,wBAAwB;IACvC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,UAAU,GAAG,MAAM,CAAC;IAC5B,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAAC;IACjC,cAAc,EAAE,SAAS,GAAG,YAAY,GAAG,SAAS,CAAC;IACrD,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,gBAAgB,EAAE,OAAO,CAAC;IAC1B;;;;OAIG;IACH,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,yBAAyB;IACxC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,8EAA8E;IAC9E,GAAG,CAAC,EAAE,IAAI,CAAC;CACZ;AAED;;;GAGG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,QAAQ,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,CAAC;IAEzB,YAAY,OAAO,EAAE,MAAM,EAAE,QAAQ,GAAE,CAAC,GAAG,CAAK,EAI/C;CACF;AAkID;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CACtC,WAAW,EAAE,MAAM,EACnB,EAAE,EAAE,MAAM,EACV,OAAO,GAAE,yBAA8B,GACtC,OAAO,CAAC,wBAAwB,CAAC,CA4InC"}
1
+ {"version":3,"file":"archive.d.ts","sourceRoot":"","sources":["../../../src/commands/backlog/archive.ts"],"names":[],"mappings":"AAUA,OAAO,EAEL,KAAK,iBAAiB,EAGvB,MAAM,sBAAsB,CAAC;AAmB9B,mDAAmD;AACnD,MAAM,WAAW,wBAAwB;IACvC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,UAAU,GAAG,MAAM,CAAC;IAC5B,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAAC;IACjC,cAAc,EAAE,SAAS,GAAG,YAAY,GAAG,SAAS,CAAC;IACrD,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,gBAAgB,EAAE,OAAO,CAAC;IAC1B;;;;OAIG;IACH,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B;mFAC+E;IAC/E,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,yBAAyB;IACxC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,8EAA8E;IAC9E,GAAG,CAAC,EAAE,IAAI,CAAC;CACZ;AAED;;;GAGG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,QAAQ,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,CAAC;IAEzB,YAAY,OAAO,EAAE,MAAM,EAAE,QAAQ,GAAE,CAAC,GAAG,CAAK,EAI/C;CACF;AAwJD;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CACtC,WAAW,EAAE,MAAM,EACnB,EAAE,EAAE,MAAM,EACV,OAAO,GAAE,yBAA8B,GACtC,OAAO,CAAC,wBAAwB,CAAC,CA0LnC"}
@@ -1,6 +1,6 @@
1
1
  import { execFile } from 'node:child_process';
2
2
  import { access, readFile, rename, writeFile } from 'node:fs/promises';
3
- import { join } from 'node:path';
3
+ import { join, relative, resolve } from 'node:path';
4
4
  import { promisify } from 'node:util';
5
5
  import { getFrontmatterBlock } from '../shared/frontmatter.js';
6
6
  import YAML from 'yaml';
@@ -48,15 +48,6 @@ async function pathExists(path) {
48
48
  return false;
49
49
  }
50
50
  }
51
- async function isInsideGitWorkTree(cwd) {
52
- try {
53
- await execFileAsync('git', ['rev-parse', '--is-inside-work-tree'], { cwd });
54
- return true;
55
- }
56
- catch {
57
- return false;
58
- }
59
- }
60
51
  /**
61
52
  * Read the item `title` from its frontmatter. Parses the YAML block (rather
62
53
  * than a line regex) so a legitimate `#` inside a quoted title — e.g.
@@ -117,19 +108,37 @@ function insertCompletedEntry(completed, entryLine) {
117
108
  }
118
109
  return { content, scaffolded: false, warning: null };
119
110
  }
120
- async function moveItemFile(backlogRoot, fromPath, toPath, warnings) {
121
- if (await isInsideGitWorkTree(backlogRoot)) {
122
- try {
123
- await execFileAsync('git', ['mv', fromPath, toPath], {
124
- cwd: backlogRoot,
125
- });
126
- return;
127
- }
128
- catch {
129
- warnings.push(`\`git mv\` failed for ${fromPath}; falling back to a plain filesystem rename (the move is no longer staged).`);
130
- }
111
+ async function readHeadFile(backlogRoot, path) {
112
+ try {
113
+ const { stdout } = await execFileAsync('git', ['show', `HEAD:./${relative(resolve(backlogRoot), resolve(path))}`], { cwd: backlogRoot });
114
+ return stdout;
115
+ }
116
+ catch {
117
+ // No Git/HEAD or absent path gives no evidence of a prior operation.
118
+ return null;
131
119
  }
132
- await rename(fromPath, toPath);
120
+ }
121
+ function hasCompletedEntry(content, id) {
122
+ return (content !== null &&
123
+ content
124
+ .split('\n')
125
+ .some((line) => line.startsWith('- ') && line.includes(` — ${id} — `)));
126
+ }
127
+ function hasIndexEntry(content, id) {
128
+ return (content !== null &&
129
+ content
130
+ .split('\n')
131
+ .some((line) => line.startsWith('|') && line.split('|')[1]?.trim() === id));
132
+ }
133
+ function collectAffectedPaths(backlogRoot, id, references, changes) {
134
+ const paths = [...references];
135
+ if (changes.item)
136
+ paths.push(resolve(backlogRoot, 'items', `${id}.md`), resolve(backlogRoot, 'archived', `${id}.md`));
137
+ if (changes.completed)
138
+ paths.push(resolve(backlogRoot, 'completed.md'));
139
+ if (changes.index)
140
+ paths.push(resolve(backlogRoot, 'index.md'));
141
+ return [...new Set(paths)].sort();
133
142
  }
134
143
  /**
135
144
  * Atomic backlog close-out. Validates the current status, sets the terminal
@@ -143,6 +152,8 @@ export async function archiveBacklogItem(backlogRoot, id, options = {}) {
143
152
  const itemsPath = join(backlogRoot, 'items', `${id}.md`);
144
153
  const archivedPath = join(backlogRoot, 'archived', `${id}.md`);
145
154
  const warnings = [];
155
+ const completedPath = join(backlogRoot, 'completed.md');
156
+ const indexPath = join(backlogRoot, 'index.md');
146
157
  // Already archived: no status, completed-log, or move writes; only the
147
158
  // idempotent reference rewrite and index regeneration are retried.
148
159
  if (await pathExists(archivedPath)) {
@@ -165,7 +176,19 @@ export async function archiveBacklogItem(backlogRoot, id, options = {}) {
165
176
  // Retry the idempotent tail of a close-out: a run that failed during the
166
177
  // reference rewrite (or an item archived before the rewrite existed)
167
178
  // still gets its inbound links repointed and the index regenerated.
168
- const references = await rewriteInboundReferences(backlogRoot, itemsPath, archivedPath, { rebaseMovedItem: false });
179
+ const [headItem, headArchived, headCompleted, headIndex] = await Promise.all([
180
+ readHeadFile(backlogRoot, itemsPath),
181
+ readHeadFile(backlogRoot, archivedPath),
182
+ readHeadFile(backlogRoot, completedPath),
183
+ readHeadFile(backlogRoot, indexPath),
184
+ ]);
185
+ const pendingMove = headItem !== null && headArchived === null;
186
+ const indexBefore = await readFile(indexPath, 'utf8').catch((error) => {
187
+ if (error.code === 'ENOENT')
188
+ return null;
189
+ throw error;
190
+ });
191
+ const references = await rewriteInboundReferences(backlogRoot, itemsPath, archivedPath, { rebaseMovedItem: false, recoverPendingArchive: pendingMove });
169
192
  warnings.push(...references.warnings);
170
193
  const regeneration = await regenerateBacklogIndex(backlogRoot);
171
194
  warnings.push(...regeneration.warnings);
@@ -177,6 +200,20 @@ export async function archiveBacklogItem(backlogRoot, id, options = {}) {
177
200
  movedTo: archivedPath,
178
201
  indexRegenerated: true,
179
202
  rewrittenReferences: references.rewritten,
203
+ affectedPaths: collectAffectedPaths(backlogRoot, id, references.affectedPaths, {
204
+ item: pendingMove,
205
+ completed: pendingMove &&
206
+ hasCompletedEntry(await readFile(completedPath, 'utf8').catch((error) => {
207
+ if (error.code === 'ENOENT')
208
+ return '';
209
+ throw error;
210
+ }), id) &&
211
+ !hasCompletedEntry(headCompleted, id),
212
+ index: indexBefore !== (await readFile(indexPath, 'utf8')) ||
213
+ (pendingMove &&
214
+ hasIndexEntry(headIndex, id) &&
215
+ !hasIndexEntry(await readFile(indexPath, 'utf8'), id)),
216
+ }),
180
217
  warnings,
181
218
  };
182
219
  }
@@ -206,7 +243,6 @@ export async function archiveBacklogItem(backlogRoot, id, options = {}) {
206
243
  const shouldWriteEntry = targetStatus === 'closed' || summary.length > 0;
207
244
  if (shouldWriteEntry) {
208
245
  const entryLine = `- ${entryDate} — ${id} — ${title} — ${summary}`;
209
- const completedPath = join(backlogRoot, 'completed.md');
210
246
  let scaffoldedFile = false;
211
247
  let existing;
212
248
  if (await pathExists(completedPath)) {
@@ -224,8 +260,8 @@ export async function archiveBacklogItem(backlogRoot, id, options = {}) {
224
260
  completedEntry =
225
261
  scaffoldedFile || inserted.scaffolded ? 'scaffolded' : 'written';
226
262
  }
227
- // 6. Move items/<id>.md -> archived/<id>.md (git mv with rename fallback).
228
- await moveItemFile(backlogRoot, itemsPath, archivedPath, warnings);
263
+ // 6. Filesystem-only move: the caller owns staging the complete operation.
264
+ await rename(itemsPath, archivedPath);
229
265
  // 7. Rewrite inbound references so no `.oat/repo` link dangles at items/.
230
266
  const references = await rewriteInboundReferences(backlogRoot, itemsPath, archivedPath);
231
267
  warnings.push(...references.warnings);
@@ -240,6 +276,7 @@ export async function archiveBacklogItem(backlogRoot, id, options = {}) {
240
276
  movedTo: archivedPath,
241
277
  indexRegenerated: true,
242
278
  rewrittenReferences: references.rewritten,
279
+ affectedPaths: collectAffectedPaths(backlogRoot, id, references.affectedPaths, { item: true, completed: shouldWriteEntry, index: true }),
243
280
  warnings,
244
281
  };
245
282
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/backlog/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,mBAAmB,EAAuB,MAAM,sBAAsB,CAAC;AAChF,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAG5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,kBAAkB,EAAuB,MAAM,WAAW,CAAC;AACpE,OAAO,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAC3C,OAAO,EAAE,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAC1C,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AA8B5D,UAAU,0BAA0B;IAClC,mBAAmB,EAAE,OAAO,mBAAmB,CAAC;IAChD,kBAAkB,EAAE,OAAO,kBAAkB,CAAC;IAC9C,iBAAiB,EAAE,OAAO,iBAAiB,CAAC;IAC5C,kBAAkB,EAAE,OAAO,kBAAkB,CAAC;IAC9C,iBAAiB,EAAE,OAAO,iBAAiB,CAAC;IAC5C,sBAAsB,EAAE,OAAO,sBAAsB,CAAC;IACtD,iBAAiB,EAAE,OAAO,iBAAiB,CAAC;IAC5C,kBAAkB,EAAE,OAAO,kBAAkB,CAAC;IAC9C,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CAChD;AA4ED,wBAAgB,oBAAoB,CAClC,SAAS,GAAE,OAAO,CAAC,0BAA0B,CAAM,GAClD,OAAO,CA6RT"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/backlog/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,mBAAmB,EAAuB,MAAM,sBAAsB,CAAC;AAChF,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAG5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,kBAAkB,EAAuB,MAAM,WAAW,CAAC;AACpE,OAAO,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAC3C,OAAO,EAAE,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAC1C,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AA8B5D,UAAU,0BAA0B;IAClC,mBAAmB,EAAE,OAAO,mBAAmB,CAAC;IAChD,kBAAkB,EAAE,OAAO,kBAAkB,CAAC;IAC9C,iBAAiB,EAAE,OAAO,iBAAiB,CAAC;IAC5C,kBAAkB,EAAE,OAAO,kBAAkB,CAAC;IAC9C,iBAAiB,EAAE,OAAO,iBAAiB,CAAC;IAC5C,sBAAsB,EAAE,OAAO,sBAAsB,CAAC;IACtD,iBAAiB,EAAE,OAAO,iBAAiB,CAAC;IAC5C,kBAAkB,EAAE,OAAO,kBAAkB,CAAC;IAC9C,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CAChD;AA4ED,wBAAgB,oBAAoB,CAClC,SAAS,GAAE,OAAO,CAAC,0BAA0B,CAAM,GAClD,OAAO,CAkST"}
@@ -210,6 +210,9 @@ export function createBacklogCommand(overrides = {}) {
210
210
  for (const path of result.rewrittenReferences) {
211
211
  context.logger.info(`Rewrote references in ${path}`);
212
212
  }
213
+ for (const path of result.affectedPaths) {
214
+ context.logger.info(`Affected path (caller-owned staging): ${path}`);
215
+ }
213
216
  }
214
217
  process.exitCode = 0;
215
218
  }
@@ -2,6 +2,9 @@
2
2
  export interface RewriteInboundReferencesResult {
3
3
  /** Files whose content was rewritten, relative to the repository root. */
4
4
  rewritten: string[];
5
+ /** Normalized absolute reference paths associated with this item, including
6
+ * links already repaired by an interrupted prior archive pass. */
7
+ affectedPaths: string[];
5
8
  /** One warning per reference that names the item but cannot be resolved. */
6
9
  warnings: string[];
7
10
  }
@@ -13,6 +16,8 @@ export interface RewriteInboundReferencesOptions {
13
16
  * because its links were rebased (or written) against `archived/` already.
14
17
  */
15
18
  rebaseMovedItem?: boolean;
19
+ /** True only when the archive caller proved an uncommitted old/new move in HEAD. */
20
+ recoverPendingArchive?: boolean;
16
21
  }
17
22
  /** A Markdown segment: prose, an inline code span, or a fenced code block. */
18
23
  export interface MarkdownSegment {
@@ -1 +1 @@
1
- {"version":3,"file":"rewrite-references.d.ts","sourceRoot":"","sources":["../../../src/commands/backlog/rewrite-references.ts"],"names":[],"mappings":"AAsCA,kDAAkD;AAClD,MAAM,WAAW,8BAA8B;IAC7C,0EAA0E;IAC1E,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,4EAA4E;IAC5E,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,oDAAoD;AACpD,MAAM,WAAW,+BAA+B;IAC9C;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AA0RD,8EAA8E;AAC9E,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC;IACjC,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe,EAAE,CA0EpE;AAmND;;;;;;;;;GASG;AACH,wBAAsB,wBAAwB,CAC5C,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,EACpB,OAAO,GAAE,+BAAoC,GAC5C,OAAO,CAAC,8BAA8B,CAAC,CAsCzC"}
1
+ {"version":3,"file":"rewrite-references.d.ts","sourceRoot":"","sources":["../../../src/commands/backlog/rewrite-references.ts"],"names":[],"mappings":"AAsCA,kDAAkD;AAClD,MAAM,WAAW,8BAA8B;IAC7C,0EAA0E;IAC1E,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB;sEACkE;IAClE,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,4EAA4E;IAC5E,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,oDAAoD;AACpD,MAAM,WAAW,+BAA+B;IAC9C;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,oFAAoF;IACpF,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACjC;AAqSD,8EAA8E;AAC9E,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC;IACjC,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe,EAAE,CA0EpE;AAmND;;;;;;;;;GASG;AACH,wBAAsB,wBAAwB,CAC5C,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,EACpB,OAAO,GAAE,+BAAoC,GAC5C,OAAO,CAAC,8BAA8B,CAAC,CAsEzC"}