@open-agent-toolkit/cli 0.2.66 → 0.2.68
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/bundle-metadata.json +1 -1
- package/assets/docs/cli-utilities/configuration.md +14 -1
- package/assets/docs/cli-utilities/project-log.md +63 -4
- package/assets/docs/contributing/code.md +8 -4
- package/assets/docs/contributing/skills.md +29 -17
- package/assets/docs/docs-tooling/commands.md +3 -1
- package/assets/docs/provider-sync/manifest-and-drift.md +2 -18
- package/assets/docs/provider-sync/scope-and-surface.md +3 -2
- package/assets/docs/reference/cli-reference.md +1 -1
- package/assets/docs/workflows/projects/evidence-layers.md +6 -3
- package/assets/docs/workflows/projects/implementation-execution.md +4 -3
- package/assets/docs/workflows/projects/lifecycle.md +17 -0
- package/assets/docs/workflows/projects/orchestration-model.md +6 -4
- package/assets/docs/workflows/projects/picking-up-projects.md +18 -0
- package/assets/docs/workflows/skills/repo-improve.md +7 -3
- package/assets/public-package-versions.json +4 -4
- package/assets/skills/create-agnostic-skill/SKILL.md +19 -37
- package/assets/skills/create-agnostic-skill/references/docs/skills-guide.md +58 -15
- package/assets/skills/create-agnostic-skill/references/skill-template.md +17 -15
- package/assets/skills/oat-agent-instructions-apply/SKILL.md +1 -1
- package/assets/skills/oat-agent-instructions-apply/references/instruction-file-templates/glob-scoped-rule.md +1 -4
- package/assets/skills/oat-dispatch-subagents/SKILL.md +4 -2
- package/assets/skills/oat-doctor/SKILL.md +1 -4
- package/assets/skills/oat-project-autonomous/references/gate-inventory.md +3 -2
- package/assets/skills/oat-project-complete/SKILL.md +158 -30
- package/assets/skills/oat-project-complete/scripts/finalize-synced-archive.mjs +26 -7
- package/assets/skills/oat-project-complete/scripts/validate-durable-archive-receipt.mjs +183 -0
- package/assets/skills/oat-project-dispatch-subagents/SKILL.md +9 -7
- package/assets/skills/oat-project-document/references/docs/autonomy-contract.md +3 -2
- package/assets/skills/oat-project-implement/SKILL.md +1 -1
- package/assets/skills/oat-project-implement/references/dispatch-and-dry-run.md +11 -10
- package/assets/skills/oat-project-implement/references/docs/autonomy-contract.md +3 -2
- package/assets/skills/oat-project-lite/references/docs/autonomy-contract.md +3 -2
- package/assets/skills/oat-project-plan-writing/SKILL.md +7 -6
- package/assets/skills/oat-project-pr-final/references/docs/autonomy-contract.md +3 -2
- package/assets/skills/oat-project-quick-start/references/docs/autonomy-contract.md +3 -2
- package/assets/skills/oat-project-retro/SKILL.md +1 -1
- package/assets/skills/oat-project-retro/references/apply-procedure.md +11 -0
- package/assets/skills/oat-project-review-provide/SKILL.md +14 -13
- package/assets/skills/oat-project-review-provide-remote/SKILL.md +7 -6
- package/assets/skills/oat-project-summary/SKILL.md +24 -2
- package/assets/skills/oat-repo-improve/SKILL.md +9 -2
- package/assets/skills/oat-repo-improve/references/plan-template.md +4 -6
- package/assets/skills/oat-repo-knowledge-index/SKILL.md +11 -9
- package/assets/skills/oat-wave-execute/SKILL.md +20 -6
- package/assets/skills/oat-wave-program/SKILL.md +9 -4
- package/dist/commands/config/index.d.ts +2 -1
- package/dist/commands/config/index.d.ts.map +1 -1
- package/dist/commands/config/index.js +133 -38
- package/dist/commands/docs/index-generate/index.d.ts.map +1 -1
- package/dist/commands/docs/index-generate/index.js +68 -12
- package/dist/commands/docs/init/index.d.ts +11 -0
- package/dist/commands/docs/init/index.d.ts.map +1 -1
- package/dist/commands/docs/init/index.js +19 -1
- package/dist/commands/gate/index.d.ts.map +1 -1
- package/dist/commands/gate/index.js +10 -2
- package/dist/commands/instructions/instructions.utils.d.ts.map +1 -1
- package/dist/commands/instructions/instructions.utils.js +42 -9
- package/dist/commands/internal/validate-skill-version-bumps.d.ts.map +1 -1
- package/dist/commands/internal/validate-skill-version-bumps.js +5 -5
- package/dist/commands/project/dispatch-ceiling/index.d.ts.map +1 -1
- package/dist/commands/project/dispatch-ceiling/index.js +32 -18
- package/dist/commands/project/log/append.d.ts +48 -0
- package/dist/commands/project/log/append.d.ts.map +1 -1
- package/dist/commands/project/log/append.js +207 -30
- package/dist/commands/project/log/check.d.ts +87 -1
- package/dist/commands/project/log/check.d.ts.map +1 -1
- package/dist/commands/project/log/check.js +139 -5
- package/dist/commands/project/log/grammar.d.ts +165 -0
- package/dist/commands/project/log/grammar.d.ts.map +1 -1
- package/dist/commands/project/log/grammar.js +219 -1
- package/dist/commands/project/log/rollup.d.ts +16 -1
- package/dist/commands/project/log/rollup.d.ts.map +1 -1
- package/dist/commands/project/log/rollup.js +22 -2
- package/dist/commands/project/log/synthesize.d.ts +16 -0
- package/dist/commands/project/log/synthesize.d.ts.map +1 -1
- package/dist/commands/project/log/synthesize.js +85 -9
- package/dist/commands/status/index.d.ts.map +1 -1
- package/dist/commands/status/index.js +24 -2
- package/dist/commands/sync/apply.d.ts.map +1 -1
- package/dist/commands/sync/apply.js +6 -4
- package/dist/config/dispatch-matrix.d.ts.map +1 -1
- package/dist/config/dispatch-matrix.js +13 -5
- package/dist/config/oat-config.d.ts +15 -0
- package/dist/config/oat-config.d.ts.map +1 -1
- package/dist/config/oat-config.js +61 -7
- package/dist/config/own-keys.d.ts +22 -0
- package/dist/config/own-keys.d.ts.map +1 -0
- package/dist/config/own-keys.js +32 -0
- package/dist/config/resolve.d.ts +11 -0
- package/dist/config/resolve.d.ts.map +1 -1
- package/dist/config/resolve.js +35 -11
- package/dist/config/sync-config.d.ts.map +1 -1
- package/dist/config/sync-config.js +16 -0
- package/dist/drift/detector.d.ts.map +1 -1
- package/dist/drift/detector.js +52 -1
- package/dist/drift/skill-view-diagnostic.d.ts.map +1 -1
- package/dist/drift/skill-view-diagnostic.js +2 -23
- package/dist/engine/compute-plan.d.ts.map +1 -1
- package/dist/engine/compute-plan.js +22 -72
- package/dist/engine/managed-copy-hash.d.ts +69 -0
- package/dist/engine/managed-copy-hash.d.ts.map +1 -0
- package/dist/engine/managed-copy-hash.js +175 -0
- package/dist/fs/assets.d.ts +11 -0
- package/dist/fs/assets.d.ts.map +1 -1
- package/dist/fs/assets.js +1 -1
- package/dist/manifest/hash.d.ts +110 -0
- package/dist/manifest/hash.d.ts.map +1 -1
- package/dist/manifest/hash.js +111 -13
- package/dist/providers/ceiling/registry.d.ts.map +1 -1
- package/dist/providers/ceiling/registry.js +6 -1
- package/dist/providers/identity/dispatch-report.d.ts.map +1 -1
- package/dist/providers/identity/dispatch-report.js +4 -1
- package/dist/release/public-package-contract.d.ts.map +1 -1
- package/dist/release/public-package-contract.js +21 -0
- package/dist/validation/skills.d.ts.map +1 -1
- package/dist/validation/skills.js +305 -40
- package/package.json +2 -2
|
@@ -5,7 +5,7 @@ disable-model-invocation: true
|
|
|
5
5
|
user-invocable: true
|
|
6
6
|
allowed-tools: Read, Write, Bash, AskUserQuestion
|
|
7
7
|
metadata:
|
|
8
|
-
version: 1.7.
|
|
8
|
+
version: 1.7.10
|
|
9
9
|
---
|
|
10
10
|
|
|
11
11
|
# Complete Project
|
|
@@ -54,6 +54,7 @@ SYNCED_ARCHIVE_ENTRY_SCRIPT="$SKILL_DIR/scripts/resolve-synced-archive-entry.mjs
|
|
|
54
54
|
SYNCED_ARCHIVE_EXECUTE_SCRIPT="$SKILL_DIR/scripts/execute-synced-archive-entry.mjs"
|
|
55
55
|
SYNCED_ARCHIVE_RESUME_FIELDS_SCRIPT="$SKILL_DIR/scripts/parse-synced-archive-resume-fields.mjs"
|
|
56
56
|
SYNCED_ARCHIVE_FINALIZE_SCRIPT="$SKILL_DIR/scripts/finalize-synced-archive.mjs"
|
|
57
|
+
DURABLE_ARCHIVE_RECEIPT_SCRIPT="$SKILL_DIR/scripts/validate-durable-archive-receipt.mjs"
|
|
57
58
|
test -f "$COMPLETION_RECEIPT_SCRIPT" || {
|
|
58
59
|
echo "oat: completion receipt recovery script is missing" >&2
|
|
59
60
|
exit 1
|
|
@@ -86,6 +87,10 @@ test -f "$SYNCED_ARCHIVE_FINALIZE_SCRIPT" || {
|
|
|
86
87
|
echo "Missing synced archive finalizer: $SYNCED_ARCHIVE_FINALIZE_SCRIPT" >&2
|
|
87
88
|
exit 1
|
|
88
89
|
}
|
|
90
|
+
test -f "$DURABLE_ARCHIVE_RECEIPT_SCRIPT" || {
|
|
91
|
+
echo "Missing durable archive receipt validator: $DURABLE_ARCHIVE_RECEIPT_SCRIPT" >&2
|
|
92
|
+
exit 1
|
|
93
|
+
}
|
|
89
94
|
|
|
90
95
|
PROJECT_SCOPE=$(oat project scope "$PROJECT_PATH" --format value) || { echo "oat: cannot resolve project scope for $PROJECT_PATH; refusing completion" >&2; exit 1; }
|
|
91
96
|
SYNCED_ARCHIVE_RESUME="false"
|
|
@@ -129,8 +134,45 @@ IS_DURABLE_PROJECT="false"
|
|
|
129
134
|
if [[ "$PROJECT_SCOPE" == "shared" || "$PROJECT_SCOPE" == "synced" ]]; then
|
|
130
135
|
IS_DURABLE_PROJECT="true"
|
|
131
136
|
fi
|
|
137
|
+
# shared-archive-resume:start
|
|
138
|
+
SHARED_ARCHIVE_RESUME="false"
|
|
139
|
+
if [[ "$PROJECT_SCOPE" == "shared" && ! -d "$PROJECT_PATH" ]]; then
|
|
140
|
+
ARCHIVED_PROJECTS_ROOT="$(dirname "$(dirname "$PROJECT_PATH")")/archived"
|
|
141
|
+
if ! DISCOVERED_ARCHIVE_PATH=$(node "$DURABLE_ARCHIVE_RECEIPT_SCRIPT" \
|
|
142
|
+
--mode directory \
|
|
143
|
+
--archived-root "$ARCHIVED_PROJECTS_ROOT" \
|
|
144
|
+
--project-name "$PROJECT_NAME"); then
|
|
145
|
+
echo "Shared archive completion cannot resume automatically: the retained active pointer names a source directory that no longer exists and no single validated archive matches $PROJECT_NAME." >&2
|
|
146
|
+
echo "Manual recovery: locate the archive directory under $ARCHIVED_PROJECTS_ROOT, confirm 'oat_lifecycle: complete' in its state.md, then run: oat config set activeProject \"\"" >&2
|
|
147
|
+
exit 1
|
|
148
|
+
fi
|
|
149
|
+
SHARED_ARCHIVE_RESUME="true"
|
|
150
|
+
PROJECT_PATH="$DISCOVERED_ARCHIVE_PATH"
|
|
151
|
+
ARCHIVE_PATH="$DISCOVERED_ARCHIVE_PATH"
|
|
152
|
+
echo "Verified discovered shared archive at $DISCOVERED_ARCHIVE_PATH; clearing the retained active pointer without a second archive."
|
|
153
|
+
fi
|
|
154
|
+
# shared-archive-resume:end
|
|
132
155
|
```
|
|
133
156
|
|
|
157
|
+
When `SHARED_ARCHIVE_RESUME="true"`, continue directly at **Step 12**. This is
|
|
158
|
+
the single recognized shared-scope checkpoint: the archive already succeeded
|
|
159
|
+
and the run died before the Step 12 clear. Do not execute Steps 2 through 11.5,
|
|
160
|
+
ask the upfront questions again, invoke `oat project archive` a second time,
|
|
161
|
+
regenerate any artifact, or replay the Step 3.7 seal append. The discovered
|
|
162
|
+
archive's `state.md` is the terminal evidence, re-derived through the same
|
|
163
|
+
`oat_lifecycle: complete` and archived-phase-marker checks Step 12 derives from
|
|
164
|
+
`ARCHIVE_OUTPUT`, so no second completion seal is written.
|
|
165
|
+
|
|
166
|
+
Every other interruption is not a resume checkpoint. After `complete-state`, or
|
|
167
|
+
after the Step 7 PR artifact, the retained pointer still names an existing
|
|
168
|
+
source directory, so this branch is not taken and the normal completion entry
|
|
169
|
+
runs: Step 3.7 routes on the status probe's `sealed` field as described there,
|
|
170
|
+
so a log that is already sealed is left alone (and a probe answering
|
|
171
|
+
`status: "ambiguous"` stops completion until the log is repaired), and
|
|
172
|
+
Step 7 regenerates the PR artifact only when it is missing. When the source
|
|
173
|
+
directory is gone and zero or several archived candidates match, the branch
|
|
174
|
+
stops with the manual-recovery message above and leaves the pointer untouched.
|
|
175
|
+
|
|
134
176
|
When `SYNCED_ARCHIVE_RESUME="true"`, continue directly at **Step 8.5**. Do not
|
|
135
177
|
execute Steps 2 through 8, ask the upfront questions again, read or mutate the
|
|
136
178
|
retired active checkout, regenerate active artifacts, or invoke archive a
|
|
@@ -637,15 +679,28 @@ Run the project-log status probe before any lifecycle mutation or archive work:
|
|
|
637
679
|
PROJECT_LOG_CHECK=$(oat project log check --project "$PROJECT_PATH" --json)
|
|
638
680
|
```
|
|
639
681
|
|
|
640
|
-
Route on the structured result:
|
|
682
|
+
Route on the structured result. Check `status: "ambiguous"` first: its counts
|
|
683
|
+
are all zero and `sealed` is `false`, so every other row below would otherwise
|
|
684
|
+
read it as an empty, unsealed log and fall through to the roll-up and the seal.
|
|
641
685
|
|
|
686
|
+
- `status: "ambiguous"`: the log's structure has two readings, so none of its
|
|
687
|
+
counts mean anything. Stop completion, report the result's `ambiguity` string
|
|
688
|
+
verbatim as the reason, and repair the log before re-running. Do not run the
|
|
689
|
+
summary hard gate, the roll-up, the seal append, or the retirement sweep. The
|
|
690
|
+
zeroed counts are not evidence that there is nothing to roll up.
|
|
642
691
|
- `status: "absent"`: the feature is inert; proceed without a roll-up or seal
|
|
643
692
|
append.
|
|
644
693
|
- `status: "synthesis_pending"` or `synthesisPending: true`: emit
|
|
645
694
|
`Warning: Project-log end-of-run synthesis is pending. Complete it with oat project log synthesize.`
|
|
646
695
|
Offer to invoke `oat project log synthesize`, but do not block completion if
|
|
647
696
|
the synthesis remains pending. Synthesis is warn-only.
|
|
648
|
-
-
|
|
697
|
+
- `sealed: true`: this completion already sealed the log on an earlier run.
|
|
698
|
+
Skip the summary hard gate below, the roll-up, and the seal append, and run
|
|
699
|
+
the retirement sweep in report-only mode as described below. A sealed log is
|
|
700
|
+
closed; re-entering any of those steps would either duplicate the seal or
|
|
701
|
+
attempt an append the CLI now refuses.
|
|
702
|
+
- When entry counts are nonzero **and the log is not sealed**, require a
|
|
703
|
+
current `summary.md`. This hard gate
|
|
649
704
|
overrides Step 3.5's tolerance for declined, skipped, missing, or failed
|
|
650
705
|
summary generation: load the current `oat-project-summary/SKILL.md` and follow
|
|
651
706
|
it; only when skill loading is unavailable in the current host/runtime, author
|
|
@@ -731,17 +786,30 @@ sweep runs:
|
|
|
731
786
|
- **No project log** (`status: "absent"` from the probe above). Record the
|
|
732
787
|
findings and their dispositions in the Step 12 completion summary
|
|
733
788
|
instead, and never create a project log for them.
|
|
734
|
-
- **Resumed completion whose log already carries a seal.**
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
789
|
+
- **Resumed completion whose log already carries a seal.** Route on the status
|
|
790
|
+
probe: `sealed: true` in `PROJECT_LOG_CHECK` means the log is already sealed,
|
|
791
|
+
and `seal` carries that entry's heading and date. `oat project log check`
|
|
792
|
+
owns this claim — it reports the seal from the log's own structural entries,
|
|
793
|
+
so a seal written before the seal append was keyed is recognized the same way
|
|
794
|
+
as a keyed one. On a sealed log the
|
|
739
795
|
sweep runs in report-only mode: surface the findings and dispositions in the
|
|
740
796
|
Step 12 completion summary, append nothing to the sealed log, and never
|
|
741
797
|
re-enter the roll-up or the seal. No project-log append may follow the seal,
|
|
742
|
-
on a resume as much as on a first run.
|
|
743
|
-
|
|
744
|
-
|
|
798
|
+
on a resume as much as on a first run. The CLI enforces this rather than
|
|
799
|
+
trusting the routing: any non-seal append carrying new content onto a sealed
|
|
800
|
+
log is refused with
|
|
801
|
+
`status: "sealed"` and a non-zero exit, and a replayed seal reports
|
|
802
|
+
`already-appended` instead of writing a second one. An append that
|
|
803
|
+
`--idempotency-key` recognizes as its own earlier entry also reports
|
|
804
|
+
`already-appended` and exits 0, because that entry predates the seal and
|
|
805
|
+
nothing is written; that carve-out never adds content after the seal.
|
|
806
|
+
|
|
807
|
+
Skip this roll-up entirely when the probe reported `sealed: true`; a sealed log
|
|
808
|
+
has already been rolled up and sealed, and the seal must remain its final
|
|
809
|
+
entry.
|
|
810
|
+
|
|
811
|
+
For an unsealed log with entries, reuse the summary flow's structured roll-up
|
|
812
|
+
result only
|
|
745
813
|
when this completion run has that exact result in memory and it reports
|
|
746
814
|
`status: "ok"`. Otherwise run the idempotent enforcement surface:
|
|
747
815
|
|
|
@@ -756,11 +824,15 @@ Do not set lifecycle complete, seal, or archive unless the structured
|
|
|
756
824
|
proceed.
|
|
757
825
|
- `ledgerOutcome: "skipped_permitted"` with `status: "ok"`: proceed and report
|
|
758
826
|
the permitted skip; the absent default reference layer is not a block.
|
|
827
|
+
- `status: "ambiguous"`: the log's structure has two readings, so nothing was
|
|
828
|
+
read and `summary.md` was not written. Stop, report the result's `ambiguity`
|
|
829
|
+
string verbatim, and repair the log. Never continue to seal or archive.
|
|
759
830
|
- `status: "failed"`, `ledgerOutcome: "failed"`, malformed JSON, or a command
|
|
760
831
|
error: stop and surface the roll-up failure. Never continue to seal or
|
|
761
832
|
archive.
|
|
762
833
|
|
|
763
|
-
When the status probe found an existing project log
|
|
834
|
+
When the status probe found an existing project log and did not report
|
|
835
|
+
`sealed: true`, append the completion seal
|
|
764
836
|
as the final project-log entry before any lifecycle-complete mutation:
|
|
765
837
|
|
|
766
838
|
```bash
|
|
@@ -769,9 +841,32 @@ oat project log append \
|
|
|
769
841
|
--structural \
|
|
770
842
|
--producer oat-project-complete \
|
|
771
843
|
--ref seal \
|
|
772
|
-
--
|
|
844
|
+
--idempotency-key "oat-seal:$PROJECT_NAME" \
|
|
845
|
+
--body "Completion sealed at $(date -u +%Y-%m-%dT%H:%M:%SZ); project-log roll-up status: ok. oat-seal:$PROJECT_NAME"
|
|
846
|
+
```
|
|
847
|
+
|
|
848
|
+
The key makes a replayed seal a no-op: `oat project log append` reports
|
|
849
|
+
`already-appended` and leaves exactly one seal entry. It must stand alone as
|
|
850
|
+
its own whitespace-delimited word in `--body` — the command records the whole
|
|
851
|
+
word carrying the key, so a key glued to the varying timestamp would never
|
|
852
|
+
match its own earlier append, and the command rejects a key the body omits.
|
|
853
|
+
|
|
854
|
+
Then verify the seal landed, rather than trusting the append's exit status:
|
|
855
|
+
|
|
856
|
+
```bash
|
|
857
|
+
SEAL_CHECK=$(oat project log check --project "$PROJECT_PATH" --json)
|
|
773
858
|
```
|
|
774
859
|
|
|
860
|
+
Require `sealed: true` in `SEAL_CHECK`, and require the `heading` the append
|
|
861
|
+
returned to be a seal heading — a `### <date> · structural · oat-project-complete · seal`
|
|
862
|
+
line. A zero exit with either condition unmet means the log is still open: stop
|
|
863
|
+
and report it; do not set lifecycle complete or archive. Re-reading the file is
|
|
864
|
+
the stronger of the two checks and the reason both are required: it reports what
|
|
865
|
+
the log now contains rather than what the append said about it, so it also
|
|
866
|
+
catches a seal that was written but is unreachable to the parser. This
|
|
867
|
+
verification exists because a seal that is silently not written reads exactly
|
|
868
|
+
like a successful one from the completion flow's side.
|
|
869
|
+
|
|
775
870
|
Only append this seal after Step 3.7 has either confirmed there are no entries
|
|
776
871
|
to roll up or obtained `status: "ok"`. If the append fails for an existing log,
|
|
777
872
|
stop before setting lifecycle complete or archiving. No project-log append may follow the seal.
|
|
@@ -831,17 +926,26 @@ It must set `oat_lifecycle: complete`, completion timestamps, `**Status:** Compl
|
|
|
831
926
|
### Step 6: Clear or Defer the Active Project Pointer
|
|
832
927
|
|
|
833
928
|
Clearing the active project pointer is implicit and requires no confirmation.
|
|
834
|
-
For
|
|
835
|
-
|
|
836
|
-
the
|
|
929
|
+
For every completion that will actually archive — that is, every durable
|
|
930
|
+
(`shared` or `synced`) project with archive selected — defer the clear until
|
|
931
|
+
the archive receipt has been validated in Step 12. Every archive or
|
|
932
|
+
configured-S3 failure therefore exits with the original pointer intact and the
|
|
933
|
+
completion remains directly resumable.
|
|
934
|
+
|
|
935
|
+
The guard mirrors the `complete-state` and Step 8 gate exactly. Keying it on
|
|
936
|
+
`SHOULD_ARCHIVE` alone would strand the pointer for `local` projects, which are
|
|
937
|
+
never durable and therefore never archive; they keep the immediate clear, as do
|
|
938
|
+
all non-archive completions.
|
|
837
939
|
|
|
838
940
|
```bash
|
|
839
|
-
|
|
840
|
-
|
|
941
|
+
# active-pointer-guard:start
|
|
942
|
+
if [[ "$SHOULD_ARCHIVE" == "true" && "$IS_DURABLE_PROJECT" == "true" ]]; then
|
|
943
|
+
echo "Active project pointer retained until durable archive receipt validation."
|
|
841
944
|
else
|
|
842
945
|
oat config set activeProject ""
|
|
843
946
|
echo "Active project pointer cleared."
|
|
844
947
|
fi
|
|
948
|
+
# active-pointer-guard:end
|
|
845
949
|
```
|
|
846
950
|
|
|
847
951
|
### Step 7: Generate PR Description
|
|
@@ -1140,8 +1244,12 @@ Use `ARCHIVE_S3_CONTEXT` in Step 12 if the command reports profile/region detail
|
|
|
1140
1244
|
|
|
1141
1245
|
Only after every applicable synced terminal and recap-export field above has
|
|
1142
1246
|
passed validation may the workflow continue. Keep the pointer through the
|
|
1143
|
-
required link, dashboard, bookkeeping, push, and PR-closeout work below
|
|
1144
|
-
|
|
1247
|
+
required link, dashboard, bookkeeping, push, and PR-closeout work below for
|
|
1248
|
+
every durable scope. For `synced`, a failure after record retirement retries
|
|
1249
|
+
through the recordless archive path. For `shared`, the archive has already
|
|
1250
|
+
removed the source directory, so a failure here retries through the
|
|
1251
|
+
shared-scope post-archive checkpoint in Step 1, which validates the discovered
|
|
1252
|
+
archive and clears the pointer without archiving again.
|
|
1145
1253
|
|
|
1146
1254
|
#### Step 8.5: Finalize Archive-Aware Recap Links
|
|
1147
1255
|
|
|
@@ -1602,24 +1710,44 @@ Failure handling:
|
|
|
1602
1710
|
before Step 12, retain the active pointer, and let the next invocation resume
|
|
1603
1711
|
recordlessly. Other completion shapes retain their existing warning-only
|
|
1604
1712
|
behavior and Step 12 summary guidance.
|
|
1713
|
+
- A shared archive completion also reaches this step with the pointer still
|
|
1714
|
+
retained, but its sync failure stays warning-only. Its shared-scope resume
|
|
1715
|
+
goes straight to the Step 12 clear and would not retry this PR update, so
|
|
1716
|
+
Step 12 clears the pointer after the receipt validates and the user updates
|
|
1717
|
+
the tracked PR by hand from the printed artifact path.
|
|
1605
1718
|
- Never re-archive or re-commit on failure here — the lifecycle bookkeeping
|
|
1606
1719
|
and any recap evidence update in Step 10.6 already shipped.
|
|
1607
1720
|
|
|
1608
1721
|
### Step 12: Confirm to User
|
|
1609
1722
|
|
|
1610
|
-
Immediately before confirmation, clear the deferred
|
|
1611
|
-
The
|
|
1612
|
-
|
|
1613
|
-
|
|
1723
|
+
Immediately before confirmation, clear the deferred durable archive pointer.
|
|
1724
|
+
The clear always runs after a validated archive receipt, never before: the
|
|
1725
|
+
synced finalizer re-validates the full terminal report, and every other durable
|
|
1726
|
+
scope re-validates `ARCHIVE_OUTPUT` through the separate durable receipt
|
|
1727
|
+
validator. Do not widen the synced finalizer to accept non-synced input. This
|
|
1728
|
+
ordering keeps every failure before final confirmation directly retryable,
|
|
1729
|
+
including a retry after record retirement:
|
|
1614
1730
|
|
|
1615
1731
|
```bash
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1732
|
+
# deferred-pointer-clear:start
|
|
1733
|
+
if [[ "$SHARED_ARCHIVE_RESUME" == "true" ]]; then
|
|
1734
|
+
oat config set activeProject ""
|
|
1735
|
+
echo "Discovered shared archive already validated; active project pointer cleared without a second archive."
|
|
1736
|
+
elif [[ "$SHOULD_ARCHIVE" == "true" && "$IS_DURABLE_PROJECT" == "true" ]]; then
|
|
1737
|
+
if [[ "$PROJECT_SCOPE" == "synced" ]]; then
|
|
1738
|
+
SYNCED_ARCHIVE_FINALIZATION=$(printf '%s\n' "$ARCHIVE_OUTPUT" | \
|
|
1739
|
+
node "$SYNCED_ARCHIVE_FINALIZE_SCRIPT" \
|
|
1740
|
+
--project-name "$PROJECT_NAME") || exit 1
|
|
1741
|
+
printf '%s\n' "$SYNCED_ARCHIVE_FINALIZATION"
|
|
1742
|
+
echo "Synced archive terminal receipt verified; active project pointer cleared."
|
|
1743
|
+
else
|
|
1744
|
+
VALIDATED_ARCHIVE_PATH=$(printf '%s\n' "$ARCHIVE_OUTPUT" | \
|
|
1745
|
+
node "$DURABLE_ARCHIVE_RECEIPT_SCRIPT" --mode receipt) || exit 1
|
|
1746
|
+
oat config set activeProject ""
|
|
1747
|
+
echo "Durable archive receipt verified for $VALIDATED_ARCHIVE_PATH; active project pointer cleared."
|
|
1748
|
+
fi
|
|
1622
1749
|
fi
|
|
1750
|
+
# deferred-pointer-clear:end
|
|
1623
1751
|
```
|
|
1624
1752
|
|
|
1625
1753
|
Show user:
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { execFile as execFileCallback } from 'node:child_process';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { readFileSync, realpathSync } from 'node:fs';
|
|
3
|
+
import { resolve } from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
4
5
|
import { promisify } from 'node:util';
|
|
5
6
|
|
|
6
7
|
const execFile = promisify(execFileCallback);
|
|
@@ -91,7 +92,7 @@ async function main(argv) {
|
|
|
91
92
|
projectName,
|
|
92
93
|
getArchiveReport: async () => {
|
|
93
94
|
try {
|
|
94
|
-
return JSON.parse(
|
|
95
|
+
return JSON.parse(readFileSync(0, 'utf8'));
|
|
95
96
|
} catch (error) {
|
|
96
97
|
throw finalizationError(
|
|
97
98
|
`Unable to parse synced archive terminal report: ${error.message}`,
|
|
@@ -110,10 +111,28 @@ async function main(argv) {
|
|
|
110
111
|
});
|
|
111
112
|
}
|
|
112
113
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
114
|
+
/**
|
|
115
|
+
* Direct invocation, compared as canonical paths on both sides. Comparing a raw
|
|
116
|
+
* `process.argv[1]` against `import.meta.url` makes this script a silent no-op
|
|
117
|
+
* that exits 0 whenever the skill is reached through a symlinked install root,
|
|
118
|
+
* and canonicalizing only one side has the same effect under
|
|
119
|
+
* `--preserve-symlinks-main`, which keeps the link in `import.meta.url`. Either
|
|
120
|
+
* way the caller reads "exited 0" as "verified" — the fail-open shape this
|
|
121
|
+
* finalizer exists to prevent.
|
|
122
|
+
*/
|
|
123
|
+
function isDirectInvocation(invokedPath) {
|
|
124
|
+
if (!invokedPath) return false;
|
|
125
|
+
try {
|
|
126
|
+
return (
|
|
127
|
+
realpathSync(fileURLToPath(import.meta.url)) ===
|
|
128
|
+
realpathSync(resolve(invokedPath))
|
|
129
|
+
);
|
|
130
|
+
} catch {
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (isDirectInvocation(process.argv[1])) {
|
|
117
136
|
main(process.argv.slice(2))
|
|
118
137
|
.then((result) => process.stdout.write(`${JSON.stringify(result)}\n`))
|
|
119
138
|
.catch((error) => {
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { readFileSync, realpathSync } from 'node:fs';
|
|
4
|
+
import { readFile, readdir } from 'node:fs/promises';
|
|
5
|
+
import { join, resolve } from 'node:path';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
|
|
8
|
+
const ARCHIVED_STATE_MARKER = 'Lifecycle complete; archived locally';
|
|
9
|
+
|
|
10
|
+
function receiptError(message) {
|
|
11
|
+
const error = new Error(message);
|
|
12
|
+
error.code = 'E_DURABLE_ARCHIVE_RECEIPT';
|
|
13
|
+
return error;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Validate a non-synced (`shared`) `oat project archive --json` report.
|
|
18
|
+
*
|
|
19
|
+
* Synced archives keep their own richer terminal contract in
|
|
20
|
+
* `finalize-synced-archive.mjs`; that finalizer must never be widened to
|
|
21
|
+
* accept non-synced input, so this validator stays separate and requires only
|
|
22
|
+
* the fields a shared archive receipt actually carries.
|
|
23
|
+
*/
|
|
24
|
+
export function validateDurableArchiveReceipt(report) {
|
|
25
|
+
if (!report || typeof report !== 'object' || Array.isArray(report)) {
|
|
26
|
+
throw receiptError(
|
|
27
|
+
'Durable archive validation requires a structured archive report.',
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
if (report.status !== 'ok' || report.mode !== 'apply') {
|
|
31
|
+
throw receiptError(
|
|
32
|
+
'Durable archive report is not a successful apply result.',
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
if (
|
|
36
|
+
typeof report.archivePath !== 'string' ||
|
|
37
|
+
report.archivePath.length === 0
|
|
38
|
+
) {
|
|
39
|
+
throw receiptError('Durable archive report has no archive path.');
|
|
40
|
+
}
|
|
41
|
+
return report.archivePath;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function isArchivedCandidateName(entryName, projectName) {
|
|
45
|
+
const escapedProjectName = projectName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
46
|
+
return (
|
|
47
|
+
entryName === projectName ||
|
|
48
|
+
new RegExp(`^\\d{8}-${escapedProjectName}$`).test(entryName)
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* A discovered archive counts as terminal evidence only when its `state.md`
|
|
54
|
+
* carries both the completed lifecycle field and the archived phase marker
|
|
55
|
+
* that `oat project complete-state --archived` writes.
|
|
56
|
+
*/
|
|
57
|
+
export function isTerminalArchivedState(stateContent) {
|
|
58
|
+
if (typeof stateContent !== 'string') return false;
|
|
59
|
+
return (
|
|
60
|
+
/^oat_lifecycle:[ \t]*complete[ \t]*$/m.test(stateContent) &&
|
|
61
|
+
stateContent.includes(ARCHIVED_STATE_MARKER)
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Re-derive the Step 2 archive checks from an archive that already exists on
|
|
67
|
+
* disk, for the single shared-scope resume checkpoint: the archive succeeded
|
|
68
|
+
* and the run died before the Step 12 pointer clear. Exactly one candidate
|
|
69
|
+
* must validate; zero or several are ambiguous and route to manual recovery.
|
|
70
|
+
*/
|
|
71
|
+
export async function discoverValidatedDurableArchive({
|
|
72
|
+
archivedRoot,
|
|
73
|
+
projectName,
|
|
74
|
+
listDirectory,
|
|
75
|
+
readStateFile,
|
|
76
|
+
}) {
|
|
77
|
+
if (typeof archivedRoot !== 'string' || archivedRoot.length === 0) {
|
|
78
|
+
throw receiptError('Durable archive discovery requires an archived root.');
|
|
79
|
+
}
|
|
80
|
+
if (typeof projectName !== 'string' || projectName.length === 0) {
|
|
81
|
+
throw receiptError('Durable archive discovery requires a project name.');
|
|
82
|
+
}
|
|
83
|
+
let entries;
|
|
84
|
+
try {
|
|
85
|
+
entries = await listDirectory(archivedRoot);
|
|
86
|
+
} catch {
|
|
87
|
+
entries = [];
|
|
88
|
+
}
|
|
89
|
+
const candidates = [];
|
|
90
|
+
for (const entryName of entries) {
|
|
91
|
+
if (!isArchivedCandidateName(entryName, projectName)) continue;
|
|
92
|
+
const archivePath = join(archivedRoot, entryName);
|
|
93
|
+
let stateContent;
|
|
94
|
+
try {
|
|
95
|
+
stateContent = await readStateFile(join(archivePath, 'state.md'));
|
|
96
|
+
} catch {
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
if (isTerminalArchivedState(stateContent)) candidates.push(archivePath);
|
|
100
|
+
}
|
|
101
|
+
if (candidates.length !== 1) {
|
|
102
|
+
throw receiptError(
|
|
103
|
+
candidates.length === 0
|
|
104
|
+
? `No archived project under ${archivedRoot} matches ${projectName} with a completed archived state.md.`
|
|
105
|
+
: `Multiple archived projects under ${archivedRoot} match ${projectName}; refusing an ambiguous resume.`,
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
return candidates[0];
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function parseArguments(argv) {
|
|
112
|
+
const result = {};
|
|
113
|
+
for (let index = 0; index < argv.length; index += 2) {
|
|
114
|
+
const flag = argv[index];
|
|
115
|
+
const value = argv[index + 1];
|
|
116
|
+
if (value === undefined) {
|
|
117
|
+
throw receiptError(`Missing value for ${flag ?? 'argument'}.`);
|
|
118
|
+
}
|
|
119
|
+
if (flag === '--mode') result.mode = value;
|
|
120
|
+
else if (flag === '--archived-root') result.archivedRoot = value;
|
|
121
|
+
else if (flag === '--project-name') result.projectName = value;
|
|
122
|
+
else throw receiptError(`Unsupported argument: ${flag}.`);
|
|
123
|
+
}
|
|
124
|
+
if (result.mode !== 'receipt' && result.mode !== 'directory') {
|
|
125
|
+
throw receiptError(
|
|
126
|
+
'Usage: validate-durable-archive-receipt.mjs --mode receipt | --mode directory --archived-root <dir> --project-name <name>',
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
return result;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async function main(argv) {
|
|
133
|
+
const options = parseArguments(argv);
|
|
134
|
+
if (options.mode === 'receipt') {
|
|
135
|
+
let report;
|
|
136
|
+
try {
|
|
137
|
+
report = JSON.parse(readFileSync(0, 'utf8'));
|
|
138
|
+
} catch (error) {
|
|
139
|
+
throw receiptError(
|
|
140
|
+
`Unable to parse durable archive report: ${error.message}`,
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
return validateDurableArchiveReceipt(report);
|
|
144
|
+
}
|
|
145
|
+
return discoverValidatedDurableArchive({
|
|
146
|
+
archivedRoot: options.archivedRoot,
|
|
147
|
+
projectName: options.projectName,
|
|
148
|
+
listDirectory: (directory) => readdir(directory),
|
|
149
|
+
readStateFile: (statePath) => readFile(statePath, 'utf8'),
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Direct invocation, compared as canonical paths on both sides. Comparing a raw
|
|
155
|
+
* `process.argv[1]` against `import.meta.url` makes this script a silent no-op
|
|
156
|
+
* that exits 0 whenever the skill is reached through a symlinked install root,
|
|
157
|
+
* and canonicalizing only one side has the same effect under
|
|
158
|
+
* `--preserve-symlinks-main`, which keeps the link in `import.meta.url`. Either
|
|
159
|
+
* way the caller reads "exited 0" as "verified" — the fail-open shape this
|
|
160
|
+
* validator exists to prevent.
|
|
161
|
+
*/
|
|
162
|
+
function isDirectInvocation(invokedPath) {
|
|
163
|
+
if (!invokedPath) return false;
|
|
164
|
+
try {
|
|
165
|
+
return (
|
|
166
|
+
realpathSync(fileURLToPath(import.meta.url)) ===
|
|
167
|
+
realpathSync(resolve(invokedPath))
|
|
168
|
+
);
|
|
169
|
+
} catch {
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (isDirectInvocation(process.argv[1])) {
|
|
175
|
+
main(process.argv.slice(2))
|
|
176
|
+
.then((archivePath) => process.stdout.write(`${archivePath}\n`))
|
|
177
|
+
.catch((error) => {
|
|
178
|
+
process.stderr.write(
|
|
179
|
+
`Invalid durable archive receipt: ${error.message}\n`,
|
|
180
|
+
);
|
|
181
|
+
process.exitCode = 1;
|
|
182
|
+
});
|
|
183
|
+
}
|
|
@@ -5,7 +5,7 @@ disable-model-invocation: true
|
|
|
5
5
|
user-invocable: false
|
|
6
6
|
allowed-tools: Read, Bash
|
|
7
7
|
metadata:
|
|
8
|
-
version: 1.1.
|
|
8
|
+
version: 1.1.6
|
|
9
9
|
---
|
|
10
10
|
|
|
11
11
|
# Dispatching OAT Project Subagents
|
|
@@ -152,12 +152,14 @@ For every lifecycle dispatch:
|
|
|
152
152
|
9. Add lifecycle outcome metadata and let the calling workflow perform state,
|
|
153
153
|
plan, implementation-log, commit, or review-table writes.
|
|
154
154
|
|
|
155
|
-
###
|
|
155
|
+
### Record the launch
|
|
156
156
|
|
|
157
|
-
For project-aware launches,
|
|
158
|
-
|
|
159
|
-
before the
|
|
160
|
-
`
|
|
157
|
+
For project-aware launches, construct and redact the complete generic record
|
|
158
|
+
plus OAT event before the native host call. When the call returns accepted or
|
|
159
|
+
`blocked-before-start`, the calling workflow writes the request ID, the
|
|
160
|
+
`Dispatch:` stamp, the launch status, and later the terminal outcome into its
|
|
161
|
+
run record in `implementation.md`. Writing a per-dispatch file with `oat project dispatch record` is optional and off by default: no lifecycle skill or command consumes those files, so do not write them unless the host has explicitly opted in. A host that has opted in persists the
|
|
162
|
+
validated record with:
|
|
161
163
|
|
|
162
164
|
```bash
|
|
163
165
|
oat project dispatch record \
|
|
@@ -166,7 +168,7 @@ oat project dispatch record \
|
|
|
166
168
|
--json
|
|
167
169
|
```
|
|
168
170
|
|
|
169
|
-
|
|
171
|
+
An accepted launch closes replacement. A rejected record must include
|
|
170
172
|
`provesNoChildStarted: true` before one target-preserving canonical-instruction
|
|
171
173
|
fallback may receive its own fresh request ID. Preserve exact provider, model,
|
|
172
174
|
effort, reasoning mode, service tier, route, authority, payload controls,
|