@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.
- package/assets/NOTICES.md +48 -0
- package/assets/agents/oat-phase-implementer.md +26 -6
- package/assets/agents/oat-reviewer.md +51 -1
- package/assets/bundle-metadata.json +1 -1
- package/assets/docs/contributing/documentation.md +4 -0
- package/assets/docs/contributing/skills.md +3 -1
- package/assets/docs/getting-started/bootstrap.md +1 -1
- package/assets/docs/getting-started/index.md +0 -4
- package/assets/docs/getting-started/tool-packs.md +0 -7
- package/assets/docs/provider-sync/commands.md +21 -0
- package/assets/docs/reference/cli-reference.md +44 -2
- package/assets/docs/reference/config-and-local-state.md +11 -2
- package/assets/docs/reference/configuration.md +10 -2
- package/assets/docs/reference/project-artifacts.md +43 -8
- package/assets/docs/skills/index.md +1 -0
- package/assets/docs/skills/recon.md +8 -5
- package/assets/docs/skills/repo-improve.md +9 -7
- package/assets/docs/workflows/advanced/autonomy.md +50 -0
- package/assets/docs/workflows/advanced/workflow-gates.md +109 -10
- package/assets/docs/workflows/approvals-and-automation.md +3 -1
- package/assets/docs/workflows/backlog-and-planning/backlog-lifecycle.md +25 -24
- package/assets/docs/workflows/backlog-and-planning/remote-project-management.md +0 -7
- package/assets/docs/workflows/ideas/lifecycle.md +2 -7
- package/assets/docs/workflows/projects/closeout/closeout-skills.md +62 -1
- package/assets/docs/workflows/projects/execution/implementation-execution.md +23 -3
- package/assets/docs/workflows/projects/execution/project-log.md +6 -0
- package/assets/docs/workflows/projects/lifecycle.md +7 -0
- package/assets/docs/workflows/projects/reviews/index.md +63 -1
- package/assets/docs/workflows/projects/reviews/review-flavors.md +8 -0
- package/assets/docs/workflows/waves/wave-workflows.md +11 -3
- package/assets/public-package-versions.json +4 -4
- package/assets/skills/oat-agent-instructions-apply/SKILL.md +14 -4
- package/assets/skills/oat-brainstorm/SKILL.md +17 -13
- package/assets/skills/oat-docs-apply/SKILL.md +13 -3
- package/assets/skills/oat-doctor/SKILL.md +11 -1
- package/assets/skills/oat-explainer-kit/SKILL.md +1 -1
- package/assets/skills/oat-explainer-kit/references/lifecycle-contract.md +6 -3
- package/assets/skills/oat-pjm-review-backlog/SKILL.md +2 -2
- package/assets/skills/oat-pjm-update-repo-reference/SKILL.md +28 -3
- package/assets/skills/oat-project-autonomous/SKILL.md +60 -5
- package/assets/skills/oat-project-autonomous/references/gate-inventory.md +210 -165
- package/assets/skills/oat-project-capture/SKILL.md +7 -3
- package/assets/skills/oat-project-complete/SKILL.md +39 -34
- package/assets/skills/oat-project-complete/scripts/execute-synced-archive-entry.mjs +106 -28
- package/assets/skills/oat-project-complete/scripts/parse-synced-archive-resume-fields.mjs +30 -3
- package/assets/skills/oat-project-complete-auto/SKILL.md +388 -0
- package/assets/skills/oat-project-design/SKILL.md +12 -9
- package/assets/skills/oat-project-discover/SKILL.md +8 -4
- package/assets/skills/oat-project-document/SKILL.md +14 -5
- package/assets/skills/oat-project-document/references/docs/autonomy-contract.md +210 -165
- package/assets/skills/oat-project-implement/SKILL.md +6 -1
- package/assets/skills/oat-project-implement/references/completion-and-closeout.md +34 -7
- package/assets/skills/oat-project-implement/references/docs/autonomy-contract.md +210 -165
- package/assets/skills/oat-project-implement/references/docs/complexity-review-fallback.md +262 -0
- package/assets/skills/oat-project-implement/references/docs/gate-approval-record.md +80 -0
- package/assets/skills/oat-project-implement/references/phase-execution.md +38 -6
- package/assets/skills/oat-project-import-plan/SKILL.md +20 -6
- package/assets/skills/oat-project-lite/SKILL.md +7 -5
- package/assets/skills/oat-project-lite/references/docs/autonomy-contract.md +210 -165
- package/assets/skills/oat-project-new/SKILL.md +6 -2
- package/assets/skills/oat-project-next/SKILL.md +19 -2
- package/assets/skills/oat-project-next/references/docs/gate-approval-record.md +80 -0
- package/assets/skills/oat-project-plan/SKILL.md +14 -6
- package/assets/skills/oat-project-pr-final/SKILL.md +4 -3
- package/assets/skills/oat-project-pr-final/references/docs/autonomy-contract.md +210 -165
- package/assets/skills/oat-project-progress/SKILL.md +27 -1
- package/assets/skills/oat-project-progress/references/docs/gate-approval-record.md +80 -0
- package/assets/skills/oat-project-promote-spec-driven/SKILL.md +7 -3
- package/assets/skills/oat-project-quick-start/SKILL.md +47 -14
- package/assets/skills/oat-project-quick-start/references/docs/autonomy-contract.md +210 -165
- package/assets/skills/oat-project-quick-start/references/docs/complexity-review-fallback.md +262 -0
- package/assets/skills/oat-project-quick-start/references/docs/gate-approval-record.md +80 -0
- package/assets/skills/oat-project-reconcile/SKILL.md +8 -4
- package/assets/skills/oat-project-retro/SKILL.md +1 -1
- package/assets/skills/oat-project-retro/references/apply-procedure.md +6 -6
- package/assets/skills/oat-project-retro-file/SKILL.md +7 -3
- package/assets/skills/oat-project-review-provide/SKILL.md +23 -4
- package/assets/skills/oat-project-review-provide-remote/SKILL.md +19 -1
- package/assets/skills/oat-project-review-receive/SKILL.md +57 -14
- package/assets/skills/oat-project-review-receive/references/docs/complexity-review-fallback.md +262 -0
- package/assets/skills/oat-project-review-receive-remote/SKILL.md +7 -3
- package/assets/skills/oat-project-revise/SKILL.md +13 -5
- package/assets/skills/oat-project-spec/SKILL.md +8 -4
- package/assets/skills/oat-project-summary/SKILL.md +11 -7
- package/assets/skills/oat-repo-knowledge-index/SKILL.md +54 -20
- package/assets/skills/oat-repo-knowledge-index/scripts/refresh-owned.mjs +118 -0
- package/assets/skills/oat-review-provide/SKILL.md +7 -3
- package/assets/skills/oat-review-receive-remote/SKILL.md +14 -4
- package/assets/skills/oat-wave-execute/SKILL.md +72 -38
- package/assets/skills/oat-wave-execute/assets/wrapper-plan-template.md +16 -2
- package/assets/skills/oat-wave-program/SKILL.md +15 -6
- package/assets/skills/oat-worktree-bootstrap-auto/SKILL.md +45 -17
- package/assets/skills/oat-wrap-up/SKILL.md +9 -3
- package/assets/skills/oat-wrap-up/references/report-template.md +1 -1
- package/assets/skills/recon/SKILL.md +1 -1
- package/assets/skills/recon/references/packet-contract.md +18 -3
- package/assets/skills/recon/scripts/lib/contracts.mjs +16 -2
- package/assets/skills/recon/scripts/reconcile-ledger.mjs +20 -10
- package/assets/skills/recon/scripts/render-packet.mjs +31 -3
- package/assets/skills/recon/scripts/validate-packet.mjs +5 -5
- package/assets/templates/pjm-agents.md +38 -8
- package/assets/templates/repo-agents.md +5 -0
- package/assets/templates/repo-readme.md +5 -0
- package/assets/templates/state.md +7 -0
- package/dist/commands/backlog/archive.d.ts +3 -0
- package/dist/commands/backlog/archive.d.ts.map +1 -1
- package/dist/commands/backlog/archive.js +63 -26
- package/dist/commands/backlog/index.d.ts.map +1 -1
- package/dist/commands/backlog/index.js +3 -0
- package/dist/commands/backlog/rewrite-references.d.ts +5 -0
- package/dist/commands/backlog/rewrite-references.d.ts.map +1 -1
- package/dist/commands/backlog/rewrite-references.js +39 -1
- package/dist/commands/config/index.d.ts.map +1 -1
- package/dist/commands/config/index.js +13 -0
- package/dist/commands/gate/index.d.ts +5 -0
- package/dist/commands/gate/index.d.ts.map +1 -1
- package/dist/commands/gate/index.js +270 -9
- package/dist/commands/init/index.d.ts.map +1 -1
- package/dist/commands/init/index.js +1 -0
- package/dist/commands/internal/commit-paths.d.ts +3 -0
- package/dist/commands/internal/commit-paths.d.ts.map +1 -0
- package/dist/commands/internal/commit-paths.js +31 -0
- package/dist/commands/internal/index.d.ts.map +1 -1
- package/dist/commands/internal/index.js +2 -0
- package/dist/commands/pjm/init.d.ts.map +1 -1
- package/dist/commands/pjm/init.js +37 -8
- package/dist/commands/project/archive/archive-utils.d.ts +6 -3
- package/dist/commands/project/archive/archive-utils.d.ts.map +1 -1
- package/dist/commands/project/archive/archive-utils.js +510 -104
- package/dist/commands/project/archive/push-runner.d.ts.map +1 -1
- package/dist/commands/project/archive/push-runner.js +1 -0
- package/dist/commands/project/log/append.d.ts +2 -3
- package/dist/commands/project/log/append.d.ts.map +1 -1
- package/dist/commands/project/log/append.js +55 -101
- package/dist/commands/project/migrate/index.d.ts +1 -2
- package/dist/commands/project/migrate/index.d.ts.map +1 -1
- package/dist/commands/project/migrate/index.js +8 -2
- package/dist/commands/project/new/scaffold.d.ts.map +1 -1
- package/dist/commands/project/new/scaffold.js +4 -4
- package/dist/commands/project/promote/promote.d.ts +8 -0
- package/dist/commands/project/promote/promote.d.ts.map +1 -1
- package/dist/commands/project/promote/promote.js +37 -6
- package/dist/commands/project/status.d.ts.map +1 -1
- package/dist/commands/project/status.js +3 -0
- package/dist/commands/project/sync/ref-sync.d.ts +12 -1
- package/dist/commands/project/sync/ref-sync.d.ts.map +1 -1
- package/dist/commands/project/sync/ref-sync.js +347 -23
- package/dist/commands/shared/exact-path-commit.d.ts +32 -0
- package/dist/commands/shared/exact-path-commit.d.ts.map +1 -0
- package/dist/commands/shared/exact-path-commit.js +691 -0
- package/dist/commands/shared/frontmatter.d.ts +1 -1
- package/dist/commands/shared/frontmatter.d.ts.map +1 -1
- package/dist/commands/shared/frontmatter.js +1 -0
- package/dist/commands/state/generate.d.ts +1 -1
- package/dist/commands/state/generate.d.ts.map +1 -1
- package/dist/commands/state/generate.js +87 -8
- package/dist/commands/sync/apply.d.ts.map +1 -1
- package/dist/commands/sync/apply.js +31 -5
- package/dist/commands/sync/dry-run.d.ts.map +1 -1
- package/dist/commands/sync/dry-run.js +28 -11
- package/dist/commands/sync/sync.utils.d.ts +13 -0
- package/dist/commands/sync/sync.utils.d.ts.map +1 -1
- package/dist/commands/sync/sync.utils.js +21 -1
- package/dist/commands/tools/shared/pack-manifest.d.ts.map +1 -1
- package/dist/commands/tools/shared/pack-manifest.js +1 -0
- package/dist/config/oat-config.d.ts +1 -0
- package/dist/config/oat-config.d.ts.map +1 -1
- package/dist/config/oat-config.js +3 -0
- package/dist/config/resolve.d.ts.map +1 -1
- package/dist/config/resolve.js +1 -0
- package/dist/drift/detector.js +6 -6
- package/dist/engine/compute-plan.d.ts.map +1 -1
- package/dist/engine/compute-plan.js +87 -2
- package/dist/engine/engine.types.d.ts +10 -1
- package/dist/engine/engine.types.d.ts.map +1 -1
- package/dist/engine/engine.types.js +4 -0
- package/dist/engine/execute-plan.d.ts.map +1 -1
- package/dist/engine/execute-plan.js +42 -5
- package/dist/engine/managed-copy-hash.d.ts +4 -3
- package/dist/engine/managed-copy-hash.d.ts.map +1 -1
- package/dist/engine/managed-copy-hash.js +4 -3
- package/dist/fs/assets.d.ts.map +1 -1
- package/dist/fs/assets.js +7 -1
- package/dist/manifest/hash.d.ts +8 -10
- package/dist/manifest/hash.d.ts.map +1 -1
- package/dist/ui/output.js +1 -1
- package/dist/validation/skills.d.ts.map +1 -1
- package/dist/validation/skills.js +151 -11
- 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.
|
|
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
|
|
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
|
|
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
|
|
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
|
|
|
@@ -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
|
|
397
|
-
|
|
398
|
-
|
|
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
|
|
2285
|
-
//
|
|
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
|
|
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 = [...
|
|
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 = [...
|
|
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,
|
|
161
|
-
// without a disposition
|
|
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 } =
|
|
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
|
-
|
|
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
|
-
|
|
2087
|
-
|
|
2088
|
-
]
|
|
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
|
|
35
|
-
for
|
|
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`.
|
|
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.
|
|
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
|
|
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
|
-
(
|
|
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;
|
|
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
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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
|
-
|
|
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
|
|
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.
|
|
228
|
-
await
|
|
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,
|
|
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;
|
|
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"}
|