@open-agent-toolkit/cli 0.1.66 → 0.1.72
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/agents/oat-reviewer.md +0 -1
- package/assets/docs/cli-utilities/configuration.md +27 -0
- package/assets/docs/cli-utilities/index.md +1 -1
- package/assets/docs/cli-utilities/workflow-gates.md +147 -5
- package/assets/docs/reference/cli-reference.md +5 -1
- package/assets/docs/workflows/projects/dispatch-ceiling.md +25 -8
- package/assets/docs/workflows/projects/orchestration-model.md +21 -0
- package/assets/docs/workflows/projects/pr-flow.md +12 -1
- package/assets/docs/workflows/projects/review-flavors.md +9 -0
- package/assets/docs/workflows/projects/reviews.md +21 -1
- package/assets/public-package-versions.json +4 -4
- package/assets/skills/oat-dispatch-subagents/SKILL.md +21 -1
- package/assets/skills/oat-dispatch-subagents/references/provider-claude.md +21 -0
- package/assets/skills/oat-dispatch-subagents/references/provider-codex.md +16 -0
- package/assets/skills/oat-dispatch-subagents/references/provider-cursor.md +21 -0
- package/assets/skills/oat-project-autonomous/SKILL.md +1 -1
- package/assets/skills/oat-project-autonomous/references/gate-inventory.md +5 -2
- package/assets/skills/oat-project-complete/SKILL.md +20 -2
- package/assets/skills/oat-project-document/references/docs/autonomy-contract.md +5 -2
- package/assets/skills/oat-project-implement/SKILL.md +1 -1
- package/assets/skills/oat-project-implement/references/completion-and-closeout.md +12 -1
- package/assets/skills/oat-project-implement/references/dispatch-and-dry-run.md +24 -9
- package/assets/skills/oat-project-implement/references/docs/autonomy-contract.md +5 -2
- package/assets/skills/oat-project-implement/references/phase-execution.md +5 -2
- package/assets/skills/oat-project-next/SKILL.md +21 -6
- package/assets/skills/oat-project-plan/SKILL.md +6 -1
- package/assets/skills/oat-project-plan-writing/SKILL.md +41 -34
- package/assets/skills/oat-project-pr-final/SKILL.md +20 -5
- package/assets/skills/oat-project-pr-final/references/docs/autonomy-contract.md +5 -2
- package/assets/skills/oat-project-pr-progress/SKILL.md +18 -3
- package/assets/skills/oat-project-progress/SKILL.md +4 -1
- package/assets/skills/oat-project-quick-start/SKILL.md +6 -1
- package/assets/skills/oat-project-quick-start/references/docs/autonomy-contract.md +5 -2
- package/assets/skills/oat-project-review-provide/SKILL.md +68 -2
- package/assets/skills/oat-project-review-receive/SKILL.md +46 -18
- package/assets/skills/oat-project-review-receive-remote/SKILL.md +41 -8
- package/dist/commands/cleanup/project/project.utils.d.ts.map +1 -1
- package/dist/commands/cleanup/project/project.utils.js +19 -1
- package/dist/commands/config/index.d.ts.map +1 -1
- package/dist/commands/config/index.js +65 -1
- package/dist/commands/gate/__fixtures__/fake-runtime.d.mts +3 -0
- package/dist/commands/gate/__fixtures__/fake-runtime.d.mts.map +1 -0
- package/dist/commands/gate/__fixtures__/fake-runtime.mjs +167 -0
- package/dist/commands/gate/activity-probes.d.ts +35 -0
- package/dist/commands/gate/activity-probes.d.ts.map +1 -0
- package/dist/commands/gate/activity-probes.js +135 -0
- package/dist/commands/gate/branch-local-cli.d.ts +31 -0
- package/dist/commands/gate/branch-local-cli.d.ts.map +1 -0
- package/dist/commands/gate/branch-local-cli.js +116 -0
- package/dist/commands/gate/index.d.ts +31 -0
- package/dist/commands/gate/index.d.ts.map +1 -1
- package/dist/commands/gate/index.js +465 -31
- package/dist/commands/gate/route.d.ts +22 -0
- package/dist/commands/gate/route.d.ts.map +1 -0
- package/dist/commands/gate/route.js +109 -0
- package/dist/commands/project/dispatch-ceiling/index.d.ts.map +1 -1
- package/dist/commands/project/dispatch-ceiling/index.js +100 -16
- package/dist/commands/review/latest.d.ts +1 -0
- package/dist/commands/review/latest.d.ts.map +1 -1
- package/dist/commands/review/latest.js +96 -22
- package/dist/config/oat-config.d.ts +9 -0
- package/dist/config/oat-config.d.ts.map +1 -1
- package/dist/config/oat-config.js +23 -0
- package/dist/config/resolve.js +7 -0
- package/package.json +2 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: oat-project-review-provide
|
|
3
|
-
version: 1.3.
|
|
3
|
+
version: 1.3.20
|
|
4
4
|
description: Use when the user explicitly asks to review an OAT project — e.g. "review project", "review the project", "run project review", or confirms a previously offered review. Do NOT auto-invoke on completed work alone. Resolves a project review scope and offers before running.
|
|
5
5
|
disable-model-invocation: false
|
|
6
6
|
user-invocable: true
|
|
@@ -584,6 +584,17 @@ candidate ladder, fail closed before review. Route interactive repair through
|
|
|
584
584
|
the planning workflow's `Complete Dispatch Ladder Adoption Contract`; do not
|
|
585
585
|
invent a reviewer target.
|
|
586
586
|
|
|
587
|
+
**Pre-plan policy inheritance:** When the resolver returns
|
|
588
|
+
`unresolvedReason: 'policy'`, and only then, an `artifact` review whose scope is
|
|
589
|
+
`discovery`, `design`, or `spec` does not block or prompt. Review by deliberate
|
|
590
|
+
inheritance in the current context and record
|
|
591
|
+
`selection_reason: inherit (pre-plan; no project policy)`. An explicitly set
|
|
592
|
+
project policy is always honored, including for pre-plan artifacts. Code
|
|
593
|
+
reviews and `artifact plan` reviews still hard-require a resolved policy.
|
|
594
|
+
Missing or incomplete ladder results (`unresolvedReason: 'ladder' | 'both'`)
|
|
595
|
+
still fail closed. Gate exec-target selection is unaffected by this
|
|
596
|
+
inheritance rule because gates resolve their target independently.
|
|
597
|
+
|
|
587
598
|
After constructing the complete provider payload, record the launcher-owned
|
|
588
599
|
`target`, `model_axis`, and `effort_axis` with
|
|
589
600
|
`launcher-selected/config-declared` provenance. These fields are immutable:
|
|
@@ -609,6 +620,56 @@ managed base role is forbidden except for
|
|
|
609
620
|
explicit inherit/default behavior or the documented managed-uncapped reviewer
|
|
610
621
|
fallback.
|
|
611
622
|
|
|
623
|
+
**Step 6.1: Headless gate route (overrides normal tier selection)**
|
|
624
|
+
|
|
625
|
+
When either prompt frontmatter has `oat_gate_headless: true` or the environment
|
|
626
|
+
has `OAT_GATE_HEADLESS=1`, copy the expected runtime and model from
|
|
627
|
+
`oat_gate_runtime` and `oat_invocation_model`, determine whether this host has
|
|
628
|
+
an awaited-child capability, and use the gate-provided branch-local CLI path.
|
|
629
|
+
Fail closed with `OAT_GATE_REFUSAL` if `OAT_GATE_CLI_PATH` is absent or not
|
|
630
|
+
executable. Run the route command and validate its JSON before using it:
|
|
631
|
+
|
|
632
|
+
```bash
|
|
633
|
+
ROUTE_JSON="$("$OAT_GATE_CLI_PATH" gate route --json \
|
|
634
|
+
--expect-runtime "$OAT_GATE_RUNTIME" \
|
|
635
|
+
--expect-model "$OAT_INVOCATION_MODEL" \
|
|
636
|
+
--can-await true)"
|
|
637
|
+
|
|
638
|
+
OAT_GATE_ROUTE_JSON="$ROUTE_JSON" node -e '
|
|
639
|
+
const value = JSON.parse(process.env.OAT_GATE_ROUTE_JSON);
|
|
640
|
+
if (!["inline", "delegate-sync", "refuse"].includes(value.route) ||
|
|
641
|
+
typeof value.reason !== "string") process.exit(1);
|
|
642
|
+
if (value.cliRoot !== process.env.OAT_GATE_CLI_ROOT) process.exit(1);
|
|
643
|
+
process.stdout.write(JSON.stringify(value));
|
|
644
|
+
'
|
|
645
|
+
```
|
|
646
|
+
|
|
647
|
+
Pass `--can-await false` when this host cannot synchronously await delegated
|
|
648
|
+
review completion. An absent command, command failure, help/non-JSON output,
|
|
649
|
+
invalid envelope, or `cliRoot` different from `OAT_GATE_CLI_ROOT` is a
|
|
650
|
+
structured refusal; never retry with bare `oat` or another installed CLI. Do
|
|
651
|
+
not make a separate runtime/model identity judgment in skill prose; follow the
|
|
652
|
+
validated helper route:
|
|
653
|
+
|
|
654
|
+
In the terminal headless response, report
|
|
655
|
+
`Gate route: <route> (runtime=<expected-runtime>, cliRoot=<validated-cliRoot>)`
|
|
656
|
+
so the parent can retain branch-local route evidence.
|
|
657
|
+
|
|
658
|
+
- `inline`: execute the complete `oat-reviewer` role contract in the current
|
|
659
|
+
context, write the artifact, and finish bookkeeping before returning.
|
|
660
|
+
- `delegate-sync`: dispatch through an awaited handle, then verify the expected
|
|
661
|
+
artifact exists and carries the matching `oat_gate_run_id` before returning.
|
|
662
|
+
A terminal `BLOCKED`, interruption, timeout, or missing/mismatched artifact
|
|
663
|
+
remains terminal and must not trigger a second launch.
|
|
664
|
+
- `refuse`: print `OAT_GATE_REFUSAL: <reason from route output>` on its own
|
|
665
|
+
line. Exit nonzero where the host permits; the gate classifies this line
|
|
666
|
+
independently of exit code.
|
|
667
|
+
|
|
668
|
+
Headless gate mode overrides the Tier 1 background-dispatch preference:
|
|
669
|
+
NEVER use fire-and-forget background dispatch in this mode. The review and its
|
|
670
|
+
artifact/bookkeeping completion must be inline or synchronously awaited before
|
|
671
|
+
the gate child exits.
|
|
672
|
+
|
|
612
673
|
**Step 6a: Probe Subagent Availability**
|
|
613
674
|
|
|
614
675
|
Before selecting a tier, announce the probe and its result so the user can see what's happening:
|
|
@@ -842,7 +903,7 @@ Run the `oat-project-review-receive` skill to convert findings into plan tasks.
|
|
|
842
903
|
|
|
843
904
|
After review artifact is written, update `plan.md` `## Reviews` table _if plan.md exists_.
|
|
844
905
|
|
|
845
|
-
|
|
906
|
+
Record this artifact as one append-ordered review event:
|
|
846
907
|
|
|
847
908
|
- `Scope`: `{scope}` (examples: `p02`, `final`, `spec`, `design`)
|
|
848
909
|
- Phase-range examples such as `p02-p03` are valid code-review scopes and should be preserved exactly.
|
|
@@ -851,6 +912,11 @@ Update or add a row matching `{scope}`:
|
|
|
851
912
|
- `Date`: `{today}`
|
|
852
913
|
- `Artifact`: `reviews/{filename}.md`
|
|
853
914
|
|
|
915
|
+
For the first event with this Scope + Type, claim an unbound `pending`
|
|
916
|
+
placeholder only when its Artifact is `-`. Otherwise append a new row for the
|
|
917
|
+
new artifact. Never replace or regress a bound event merely because Scope +
|
|
918
|
+
Type matches; distinct artifact filenames are distinct review events.
|
|
919
|
+
|
|
854
920
|
If plan.md is missing (e.g., spec/design review before planning), skip this update and rely on the review artifact + next-step routing.
|
|
855
921
|
|
|
856
922
|
### Step 9.5: Commit Review Bookkeeping Atomically (Required)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: oat-project-review-receive
|
|
3
|
-
version: 1.5.
|
|
3
|
+
version: 1.5.9
|
|
4
4
|
description: Use when the user explicitly asks to receive review findings for an OAT project — e.g. "receive review", "process review", "process the project review", or confirms a previously offered review-receive step. Do NOT auto-invoke merely because a review file exists. Resolves the latest review and offers before acting.
|
|
5
5
|
disable-model-invocation: false
|
|
6
6
|
user-invocable: true
|
|
@@ -102,7 +102,7 @@ PROJECTS_ROOT="${PROJECTS_ROOT%/}"
|
|
|
102
102
|
|
|
103
103
|
```bash
|
|
104
104
|
if [ -n "${PROJECT_PATH:-}" ] && [ -d "$PROJECT_PATH" ]; then
|
|
105
|
-
oat review latest --project "$PROJECT_PATH" --json
|
|
105
|
+
oat review latest --project "$PROJECT_PATH" --actionable-project --json
|
|
106
106
|
else
|
|
107
107
|
oat review latest --json
|
|
108
108
|
fi
|
|
@@ -110,11 +110,13 @@ fi
|
|
|
110
110
|
|
|
111
111
|
Selection rules:
|
|
112
112
|
|
|
113
|
-
-
|
|
113
|
+
- With a valid project, use the `--actionable-project` path first. It resolves only an active/actionable project review from the top-level `reviews/` directory, so newer historical artifacts in `reviews/archived/` cannot strand an older active event.
|
|
114
|
+
- If the actionable-project result has `path: null`, run `oat review latest --json` without `--actionable-project` to discover the latest ad-hoc target or report project history. The default resolver intentionally preserves all-history ordering across project (`reviews/` and `reviews/archived/`) and ad-hoc locations.
|
|
115
|
+
- Both resolver paths order candidates by `oat_generated_at` frontmatter rather than filesystem mtime. Review artifacts carry a seconds-precision `oat_generated_at` and a matching timestamped filename, so same-scope same-day re-gates order deterministically and the newest round wins — never assume the plain `<scope>-review-<date>.md` name is current.
|
|
114
116
|
- Read the JSON result:
|
|
115
117
|
- `path: null` means no review target was found.
|
|
116
118
|
- `kind: "project"` and `actionable: true` means this skill can process the target when the path is an active top-level project review.
|
|
117
|
-
- `archived: true` or `actionable: false` means the
|
|
119
|
+
- An `archived: true` or `actionable: false` result from the all-history fallback means no active project review was resolved and the selected result is historical/non-actionable for project review-receive.
|
|
118
120
|
- `kind: "adhoc"` means route to `oat-review-receive` after offering that handoff to the user.
|
|
119
121
|
- Only process active project review artifacts in the top level of `"$PROJECT_PATH/reviews/"`.
|
|
120
122
|
- Treat archived project artifacts as history only; do not receive them automatically. If `oat review latest` returns an archived project review, tell the user no active project review is waiting and offer to run `oat-project-review-provide`.
|
|
@@ -140,11 +142,32 @@ Use this fallback only for active project reviews. It cannot discover ad-hoc rev
|
|
|
140
142
|
Derive archive bookkeeping before making lifecycle edits:
|
|
141
143
|
|
|
142
144
|
```bash
|
|
143
|
-
|
|
145
|
+
SOURCE_REVIEW_FILENAME=$(basename "$REVIEW_PATH")
|
|
146
|
+
REVIEW_FILENAME="$SOURCE_REVIEW_FILENAME"
|
|
144
147
|
ARCHIVED_REVIEW_DIR="$PROJECT_PATH/reviews/archived"
|
|
145
148
|
ARCHIVED_REVIEW_PATH="$ARCHIVED_REVIEW_DIR/$REVIEW_FILENAME"
|
|
149
|
+
|
|
150
|
+
if [[ -e "$ARCHIVED_REVIEW_PATH" ]]; then
|
|
151
|
+
REVIEW_STEM="${SOURCE_REVIEW_FILENAME%.md}"
|
|
152
|
+
ARCHIVE_TIMESTAMP=$(date -u +%Y-%m-%dT%H%M%SZ)
|
|
153
|
+
REVIEW_FILENAME="${REVIEW_STEM}-${ARCHIVE_TIMESTAMP}.md"
|
|
154
|
+
ARCHIVED_REVIEW_PATH="$ARCHIVED_REVIEW_DIR/$REVIEW_FILENAME"
|
|
155
|
+
ARCHIVE_COLLISION_INDEX=2
|
|
156
|
+
|
|
157
|
+
while [[ -e "$ARCHIVED_REVIEW_PATH" ]]; do
|
|
158
|
+
REVIEW_FILENAME="${REVIEW_STEM}-${ARCHIVE_TIMESTAMP}-${ARCHIVE_COLLISION_INDEX}.md"
|
|
159
|
+
ARCHIVED_REVIEW_PATH="$ARCHIVED_REVIEW_DIR/$REVIEW_FILENAME"
|
|
160
|
+
ARCHIVE_COLLISION_INDEX=$((ARCHIVE_COLLISION_INDEX + 1))
|
|
161
|
+
done
|
|
162
|
+
fi
|
|
146
163
|
```
|
|
147
164
|
|
|
165
|
+
`SOURCE_REVIEW_FILENAME` identifies the selected active event before archival.
|
|
166
|
+
`REVIEW_FILENAME` and `ARCHIVED_REVIEW_PATH` are the collision-free final
|
|
167
|
+
identity and destination. Resolve them here, before writing plan,
|
|
168
|
+
implementation, or artifact-review references. Never choose a different
|
|
169
|
+
basename later in either receive path.
|
|
170
|
+
|
|
148
171
|
### Step 2: Parse Findings into Buckets
|
|
149
172
|
|
|
150
173
|
Extract findings from the review artifact and categorize:
|
|
@@ -382,7 +405,9 @@ Add new tasks to plan.md in the target phase. When adding or editing tasks, pres
|
|
|
382
405
|
|
|
383
406
|
**Review-fix bookkeeping (required):**
|
|
384
407
|
- When you add review-generated fix tasks:
|
|
385
|
-
-
|
|
408
|
+
- Locate the Reviews event matching the selected review's Scope, Type, and `SOURCE_REVIEW_FILENAME`, then update it to `fixes_added` (work queued), set the Date, and replace its Artifact with `reviews/archived/$REVIEW_FILENAME`.
|
|
409
|
+
- The written `REVIEW_FILENAME` becomes the event's artifact filename and identity for every later mutation; use the already-resolved final basename in every plan and implementation reference.
|
|
410
|
+
- Never select a row by scope alone or move an event status backward. If the exact bound event is missing, stop and reconcile the ledger instead of mutating another event.
|
|
386
411
|
- Update `## Implementation Complete` totals (phase counts + total task count) so downstream PR/review summaries don’t go stale.
|
|
387
412
|
- If the plan includes any phase rollups that reference task counts, update those too.
|
|
388
413
|
|
|
@@ -399,10 +424,11 @@ Add new tasks to plan.md in the target phase. When adding or editing tasks, pres
|
|
|
399
424
|
**Update Reviews section:**
|
|
400
425
|
```markdown
|
|
401
426
|
## Reviews
|
|
402
|
-
-
|
|
427
|
+
- Find the existing event by `{scope}`, review Type, and
|
|
428
|
+
`$SOURCE_REVIEW_FILENAME`, then update only that row:
|
|
403
429
|
- Status: `fixes_added` (if tasks were added) or `passed` (if no Critical/Important/Medium and no unresolved final-scope gates)
|
|
404
430
|
- Date: `{today}`
|
|
405
|
-
- Artifact: `reviews/archived
|
|
431
|
+
- Artifact: `reviews/archived/$REVIEW_FILENAME`
|
|
406
432
|
````
|
|
407
433
|
|
|
408
434
|
**Status semantics (v1):**
|
|
@@ -410,6 +436,7 @@ Add new tasks to plan.md in the target phase. When adding or editing tasks, pres
|
|
|
410
436
|
- `fixes_added`: fix tasks were created and added to the plan
|
|
411
437
|
- `fixes_completed`: fix tasks implemented, awaiting re-review
|
|
412
438
|
- `passed`: re-review completed and recorded as passing (no unresolved Critical/Important/Medium, and all final-scope gates satisfied: deferred-medium + minor disposition)
|
|
439
|
+
- Status changes are monotonic. Never move an event status backward, replace an earlier event, or update a different event that happens to share the same scope/type.
|
|
413
440
|
|
|
414
441
|
### Step 7: Update Implementation.md
|
|
415
442
|
|
|
@@ -419,7 +446,7 @@ Add a note to implementation.md:
|
|
|
419
446
|
### Review Received: {scope}
|
|
420
447
|
|
|
421
448
|
**Date:** {today}
|
|
422
|
-
**Review artifact:** reviews/archived
|
|
449
|
+
**Review artifact:** reviews/archived/$REVIEW_FILENAME
|
|
423
450
|
|
|
424
451
|
**Findings:**
|
|
425
452
|
|
|
@@ -438,7 +465,7 @@ Add a note to implementation.md:
|
|
|
438
465
|
|
|
439
466
|
After the fix tasks are complete:
|
|
440
467
|
|
|
441
|
-
- Update
|
|
468
|
+
- Update this same artifact-identified review event to `fixes_completed`
|
|
442
469
|
- Re-run `oat-project-review-provide {type} {scope}` then `oat-project-review-receive` to reach `passed`
|
|
443
470
|
```
|
|
444
471
|
|
|
@@ -468,8 +495,8 @@ mv "$REVIEW_PATH" "$ARCHIVED_REVIEW_PATH"
|
|
|
468
495
|
|
|
469
496
|
Rules:
|
|
470
497
|
|
|
471
|
-
- Perform the move only after all written references have been updated to `reviews/archived
|
|
472
|
-
-
|
|
498
|
+
- Perform the move only after all written references and event identity have been updated to `reviews/archived/$REVIEW_FILENAME`.
|
|
499
|
+
- Use the `REVIEW_FILENAME` and `ARCHIVED_REVIEW_PATH` resolved in Step 1. Do not rename or re-resolve the destination here.
|
|
473
500
|
- Report the archived location in the final summary.
|
|
474
501
|
|
|
475
502
|
### Step 7.6: Commit Review Bookkeeping (Required)
|
|
@@ -647,8 +674,9 @@ mkdir -p "$ARCHIVED_REVIEW_DIR"
|
|
|
647
674
|
mv "$REVIEW_PATH" "$ARCHIVED_REVIEW_PATH"
|
|
648
675
|
```
|
|
649
676
|
|
|
650
|
-
-
|
|
651
|
-
-
|
|
677
|
+
- Before the move, use the Step 1 `REVIEW_FILENAME` for every artifact-review reference or event identity written by the approved edits.
|
|
678
|
+
- Perform the move only after all artifact edits are applied and every written reference points to `reviews/archived/$REVIEW_FILENAME`.
|
|
679
|
+
- Use the Step 1 `ARCHIVED_REVIEW_PATH` unchanged. Do not rename or re-resolve the destination here.
|
|
652
680
|
|
|
653
681
|
**Then route:**
|
|
654
682
|
|
|
@@ -662,14 +690,14 @@ mv "$REVIEW_PATH" "$ARCHIVED_REVIEW_PATH"
|
|
|
662
690
|
```
|
|
663
691
|
Review received for {project-name}.
|
|
664
692
|
|
|
665
|
-
Review:
|
|
693
|
+
Review: $REVIEW_FILENAME
|
|
666
694
|
Scope: {scope}
|
|
667
695
|
Findings: {N} critical, {N} important, {N} medium, {N} minor
|
|
668
696
|
|
|
669
697
|
Actions taken:
|
|
670
698
|
- Added {N} fix tasks to plan.md ({task_ids})
|
|
671
699
|
- Updated implementation.md with review notes
|
|
672
|
-
- Archived review artifact to `reviews/archived
|
|
700
|
+
- Archived review artifact to `reviews/archived/$REVIEW_FILENAME`
|
|
673
701
|
- Deferred/accepted Medium findings: {N}
|
|
674
702
|
- Minor findings dispositioned: {N} converted (default), {N} deferred-with-rationale (explicit user decision required for final scope)
|
|
675
703
|
- Finding disposition map: {ID -> converted|deferred|accepted + rationale summary}
|
|
@@ -684,13 +712,13 @@ For `artifact` reviews, summarize instead:
|
|
|
684
712
|
```
|
|
685
713
|
Review received for {project-name}.
|
|
686
714
|
|
|
687
|
-
Review:
|
|
715
|
+
Review: $REVIEW_FILENAME
|
|
688
716
|
Scope: {scope}
|
|
689
717
|
Findings: {N} critical, {N} important, {N} medium, {N} minor
|
|
690
718
|
|
|
691
719
|
Actions taken:
|
|
692
720
|
- Applied {N} artifact edits
|
|
693
|
-
- Archived review artifact to `reviews/archived
|
|
721
|
+
- Archived review artifact to `reviews/archived/$REVIEW_FILENAME`
|
|
694
722
|
- No plan tasks created
|
|
695
723
|
- Finding disposition map: {ID -> resolve_in_artifact|rejected_with_rationale|needs_user_direction}
|
|
696
724
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: oat-project-review-receive-remote
|
|
3
|
-
version: 1.4.
|
|
3
|
+
version: 1.4.2
|
|
4
4
|
description: Use when processing GitHub PR review comments within project context. Fetches PR comments, creates plan tasks, and updates project artifacts.
|
|
5
5
|
disable-model-invocation: true
|
|
6
6
|
user-invocable: true
|
|
@@ -117,9 +117,22 @@ npx agent-reviews --json --unresolved --pr <N>
|
|
|
117
117
|
|
|
118
118
|
If no unresolved comments:
|
|
119
119
|
|
|
120
|
-
|
|
121
|
-
-
|
|
122
|
-
|
|
120
|
+
1. Create a UTC timestamp and event-distinct filename:
|
|
121
|
+
`reviews/archived/remote-pr-<N>-review-YYYY-MM-DDTHHMMSSZ.md`.
|
|
122
|
+
2. Ensure `reviews/archived/` exists, then write that artifact with the PR
|
|
123
|
+
number, fetch timestamp, remote scope/type, and a zero-unresolved-findings
|
|
124
|
+
result. A clean remote review is consumed as it is recorded, so it must not
|
|
125
|
+
remain in top-level `reviews/`.
|
|
126
|
+
3. Record the clean result as a `passed` Reviews event whose event identity
|
|
127
|
+
combines `Scope`, `Type`, and artifact filename:
|
|
128
|
+
- Claim only an unbound `pending` placeholder with matching Scope + Type and
|
|
129
|
+
Artifact `-`; otherwise append a distinct row.
|
|
130
|
+
- Set Date and Artifact to this clean event. Advance only this event and
|
|
131
|
+
never mutate another row by scope alone.
|
|
132
|
+
4. Commit `plan.md` and the clean review artifact atomically with
|
|
133
|
+
`chore(oat): record clean remote review (pr-#<N>)`. Do not stop with
|
|
134
|
+
uncommitted bookkeeping.
|
|
135
|
+
5. Report clean status and stop.
|
|
123
136
|
|
|
124
137
|
### Step 3: Classify and Normalize Findings
|
|
125
138
|
|
|
@@ -162,14 +175,34 @@ For each converted finding:
|
|
|
162
175
|
|
|
163
176
|
### Step 6: Update Project Artifacts
|
|
164
177
|
|
|
178
|
+
Before changing the ledger, write an event-distinct review artifact containing
|
|
179
|
+
the PR number, fetch timestamp, normalized findings, and dispositions:
|
|
180
|
+
`reviews/archived/remote-pr-<N>-review-YYYY-MM-DDTHHMMSSZ.md`. Remote receive
|
|
181
|
+
fully dispositions the event as `passed` or `fixes_added`, so the artifact is
|
|
182
|
+
consumed immediately and belongs in `reviews/archived/`, not top-level
|
|
183
|
+
`reviews/`. Each fetch/triage cycle gets a new timestamped artifact filename,
|
|
184
|
+
even when the PR and lifecycle scope are unchanged.
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
REMOTE_REVIEW_TIMESTAMP=$(date -u +%Y-%m-%dT%H%M%SZ)
|
|
188
|
+
REMOTE_REVIEW_FILENAME="remote-pr-${PR_NUMBER}-review-${REMOTE_REVIEW_TIMESTAMP}.md"
|
|
189
|
+
mkdir -p "$PROJECT_PATH/reviews/archived"
|
|
190
|
+
REMOTE_REVIEW_PATH="$PROJECT_PATH/reviews/archived/$REMOTE_REVIEW_FILENAME"
|
|
191
|
+
```
|
|
192
|
+
|
|
165
193
|
Update `plan.md`:
|
|
166
194
|
|
|
167
195
|
- Append inserted review-fix task sections in correct phase order.
|
|
168
|
-
-
|
|
196
|
+
- Record an append-ordered `## Reviews` event for the remote scope:
|
|
169
197
|
- status `fixes_added` when tasks were added
|
|
170
198
|
- status `passed` when no actionable findings remain
|
|
171
199
|
- date set to today
|
|
172
|
-
- artifact `
|
|
200
|
+
- artifact `reviews/archived/remote-pr-<N>-review-YYYY-MM-DDTHHMMSSZ.md`
|
|
201
|
+
- Claim an unbound `pending` placeholder only when its Scope + Type matches and
|
|
202
|
+
its Artifact is `-`; otherwise append the event. Later mutations select it by
|
|
203
|
+
Scope + Type + artifact filename, never by scope or `github-pr #<N>` alone.
|
|
204
|
+
- Never move an event status backward or overwrite an earlier event from the
|
|
205
|
+
same PR.
|
|
173
206
|
- Update `## Implementation Complete` totals.
|
|
174
207
|
|
|
175
208
|
Update `implementation.md`:
|
|
@@ -192,12 +225,12 @@ Update `state.md`:
|
|
|
192
225
|
|
|
193
226
|
### Step 6.5: Commit Review Bookkeeping (Required)
|
|
194
227
|
|
|
195
|
-
**CRITICAL — DO NOT SKIP.** This skill modifies `plan.md`, `implementation.md`, and `state.md` when processing GitHub PR comments. When it runs in a separate agent session (subagent, fresh session, or different conversation), uncommitted bookkeeping updates cause state drift for the original agent. The commit below is the safety net.
|
|
228
|
+
**CRITICAL — DO NOT SKIP.** This skill modifies the event-distinct review artifact, `plan.md`, `implementation.md`, and `state.md` when processing GitHub PR comments. When it runs in a separate agent session (subagent, fresh session, or different conversation), uncommitted bookkeeping updates cause state drift for the original agent. The commit below is the safety net.
|
|
196
229
|
|
|
197
230
|
Commit all modified OAT tracking files atomically:
|
|
198
231
|
|
|
199
232
|
```bash
|
|
200
|
-
git add "$PROJECT_PATH/plan.md" "$PROJECT_PATH/implementation.md" "$PROJECT_PATH/state.md"
|
|
233
|
+
git add "$PROJECT_PATH/plan.md" "$PROJECT_PATH/implementation.md" "$PROJECT_PATH/state.md" "$REMOTE_REVIEW_PATH"
|
|
201
234
|
git diff --cached --quiet || git commit -m "chore(oat): record remote review findings and add fix tasks (pr-#$PR_NUMBER)"
|
|
202
235
|
```
|
|
203
236
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project.utils.d.ts","sourceRoot":"","sources":["../../../../src/commands/cleanup/project/project.utils.ts"],"names":[],"mappings":"AAQA,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,OAAO,CAG1E;AAED,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,OAAO,CAG1E;AAED,wBAAgB,wBAAwB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,
|
|
1
|
+
{"version":3,"file":"project.utils.d.ts","sourceRoot":"","sources":["../../../../src/commands/cleanup/project/project.utils.ts"],"names":[],"mappings":"AAQA,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,OAAO,CAG1E;AAED,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,OAAO,CAG1E;AAED,wBAAgB,wBAAwB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CA8BrE;AAED,wBAAgB,wBAAwB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAEtE;AAED,wBAAgB,6BAA6B,CAC3C,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,GACnB,OAAO,CAKT;AAED,wBAAgB,0BAA0B,CACxC,eAAe,EAAE,MAAM,EACvB,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,MAAM,GACZ,MAAM,CAMR;AAED,wBAAgB,kCAAkC,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAkB1E"}
|
|
@@ -14,7 +14,25 @@ export function projectNeedsStateFile(fileNames) {
|
|
|
14
14
|
return !files.has('state.md') && hasLifecycleArtifacts(files);
|
|
15
15
|
}
|
|
16
16
|
export function projectPlanMarksComplete(planContent) {
|
|
17
|
-
|
|
17
|
+
const reviewsHeading = /^## Reviews[ \t]*\r?$/m.exec(planContent);
|
|
18
|
+
if (!reviewsHeading) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
const afterHeading = planContent.slice(reviewsHeading.index + reviewsHeading[0].length);
|
|
22
|
+
const nextSectionIndex = afterHeading.search(/^##(?!#)[ \t]+\S.*\r?$/m);
|
|
23
|
+
const reviewsSection = nextSectionIndex === -1
|
|
24
|
+
? afterHeading
|
|
25
|
+
: afterHeading.slice(0, nextSectionIndex);
|
|
26
|
+
const finalCodeEvents = reviewsSection
|
|
27
|
+
.split('\n')
|
|
28
|
+
.filter((line) => line.trim().startsWith('|'))
|
|
29
|
+
.map((line) => line
|
|
30
|
+
.split('|')
|
|
31
|
+
.slice(1, -1)
|
|
32
|
+
.map((cell) => cell.trim()))
|
|
33
|
+
.filter(([scope, type]) => scope?.toLowerCase() === 'final' && type?.toLowerCase() === 'code');
|
|
34
|
+
const latestFinalCodeEvent = finalCodeEvents[finalCodeEvents.length - 1];
|
|
35
|
+
return latestFinalCodeEvent?.[2]?.toLowerCase() === 'passed';
|
|
18
36
|
}
|
|
19
37
|
export function stateLifecycleIsComplete(stateContent) {
|
|
20
38
|
return /^oat_lifecycle:\s*complete$/m.test(stateContent);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/config/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,mBAAmB,EAAE,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEhF,OAAO,EAEL,KAAK,aAAa,EACnB,MAAM,iCAAiC,CAAC;AAezC,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/config/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,mBAAmB,EAAE,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEhF,OAAO,EAEL,KAAK,aAAa,EACnB,MAAM,iCAAiC,CAAC;AAezC,OAAO,EAML,KAAK,SAAS,EACd,KAAK,cAAc,EAGnB,KAAK,UAAU,EAehB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAEL,KAAK,cAAc,EAEpB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAGL,KAAK,8BAA8B,EACnC,KAAK,yBAAyB,EAC/B,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EACL,mCAAmC,EACnC,0BAA0B,EAC3B,MAAM,yCAAyC,CAAC;AACjD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAuFpC,UAAU,yBAAyB;IACjC,mBAAmB,EAAE,CACnB,OAAO,EAAE,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC,CAAC,CAAC,KAC/C,cAAc,CAAC;IACpB,kBAAkB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACrD,aAAa,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC;IACxD,cAAc,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvE,kBAAkB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;IAClE,mBAAmB,EAAE,CACnB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,cAAc,KACnB,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,cAAc,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;IAC/D,eAAe,EAAE,CAAC,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9E,mBAAmB,EAAE,CACnB,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,CAAC,UAAU,KACnB,OAAO,CAAC,MAAM,CAAC,CAAC;IACrB,sBAAsB,EAAE,CACtB,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,EACrB,GAAG,EAAE,MAAM,CAAC,UAAU,KACnB,OAAO,CAAC,cAAc,CAAC,CAAC;IAC7B,iBAAiB,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IACzC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5C,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,aAAa,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACzE,kBAAkB,EAAE,CAClB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,yBAAyB,KAC/B,OAAO,CAAC,8BAA8B,CAAC,CAAC;IAC7C,mCAAmC,EAAE,OAAO,mCAAmC,CAAC;IAChF,0BAA0B,EAAE,OAAO,0BAA0B,CAAC;IAC9D,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC;CAC/B;AAg+DD,wBAAgB,mBAAmB,CACjC,SAAS,GAAE,OAAO,CAAC,yBAAyB,CAAM,GACjD,OAAO,CAsKT"}
|
|
@@ -7,7 +7,7 @@ import { readGlobalOptions } from '../shared/shared.utils.js';
|
|
|
7
7
|
import { compileDispatchCeilingPreset } from '../../config/dispatch-ceiling-preset.js';
|
|
8
8
|
import { normalizeDispatchMatrix, validateDispatchRouteTarget, walkDispatchMatrix, } from '../../config/dispatch-matrix.js';
|
|
9
9
|
import { dispatchPolicyModeDescription, dispatchPolicyPolicyDescription, managedDispatchPolicyValueList, } from '../../config/dispatch-policy-options.js';
|
|
10
|
-
import { VALID_DISPATCH_POLICY_MODES, VALID_MANAGED_DISPATCH_POLICIES, normalizeWorkflowPostImplementSequence, readOatConfig, readOatLocalConfig, readUserConfig, writeOatConfig, writeOatLocalConfig, writeUserConfig, } from '../../config/oat-config.js';
|
|
10
|
+
import { VALID_DISPATCH_POLICY_MODES, VALID_MANAGED_DISPATCH_POLICIES, MAX_GATE_TIMEOUT_MS, MIN_GATE_TIMEOUT_MS, isValidGateTimeoutMs, normalizeWorkflowPostImplementSequence, readOatConfig, readOatLocalConfig, readUserConfig, writeOatConfig, writeOatLocalConfig, writeUserConfig, } from '../../config/oat-config.js';
|
|
11
11
|
import { resolveEffectiveConfig, } from '../../config/resolve.js';
|
|
12
12
|
import { resolveAssetsRoot } from '../../fs/assets.js';
|
|
13
13
|
import { resolveProjectRoot } from '../../fs/paths.js';
|
|
@@ -63,6 +63,8 @@ const KEY_ORDER = [
|
|
|
63
63
|
'workflow.dispatchCeiling.preset',
|
|
64
64
|
'workflow.dispatchCeiling.providers.codex',
|
|
65
65
|
'workflow.dispatchCeiling.providers.claude',
|
|
66
|
+
'workflow.gateTimeouts.code',
|
|
67
|
+
'workflow.gateTimeouts.artifact',
|
|
66
68
|
'worktrees.root',
|
|
67
69
|
];
|
|
68
70
|
const CONFIG_CATALOG = [
|
|
@@ -583,6 +585,8 @@ const DEFAULT_DEPENDENCIES = {
|
|
|
583
585
|
};
|
|
584
586
|
function isConfigKey(value) {
|
|
585
587
|
return (KEY_ORDER.includes(value) ||
|
|
588
|
+
value === 'workflow.dispatchCeiling' ||
|
|
589
|
+
value === 'workflow.dispatchCeiling.providers' ||
|
|
586
590
|
isDispatchCeilingProviderKey(value));
|
|
587
591
|
}
|
|
588
592
|
function isDispatchCeilingProviderKey(value) {
|
|
@@ -758,6 +762,14 @@ function parseWorkflowValue(key, rawValue) {
|
|
|
758
762
|
return sequence;
|
|
759
763
|
}
|
|
760
764
|
}
|
|
765
|
+
if (key === 'workflow.gateTimeouts.code' ||
|
|
766
|
+
key === 'workflow.gateTimeouts.artifact') {
|
|
767
|
+
const value = Number(rawValue);
|
|
768
|
+
if (!isValidGateTimeoutMs(value)) {
|
|
769
|
+
throw new Error(`Invalid value for ${key}: expected an integer between ${MIN_GATE_TIMEOUT_MS} and ${MAX_GATE_TIMEOUT_MS}`);
|
|
770
|
+
}
|
|
771
|
+
return value;
|
|
772
|
+
}
|
|
761
773
|
const allowed = WORKFLOW_ENUM_VALUES[key];
|
|
762
774
|
if (allowed) {
|
|
763
775
|
const normalized = rawValue.trim();
|
|
@@ -875,6 +887,16 @@ function applyWorkflowValue(workflow, key, value) {
|
|
|
875
887
|
postImplementSequence: value,
|
|
876
888
|
};
|
|
877
889
|
}
|
|
890
|
+
if (subKey.startsWith('gateTimeouts.')) {
|
|
891
|
+
const timeoutKey = subKey.slice('gateTimeouts.'.length);
|
|
892
|
+
return {
|
|
893
|
+
...workflow,
|
|
894
|
+
gateTimeouts: {
|
|
895
|
+
...workflow.gateTimeouts,
|
|
896
|
+
[timeoutKey]: value,
|
|
897
|
+
},
|
|
898
|
+
};
|
|
899
|
+
}
|
|
878
900
|
if (subKey === 'dispatchPolicy.mode') {
|
|
879
901
|
const mode = value;
|
|
880
902
|
if (mode === 'inherit') {
|
|
@@ -991,6 +1013,10 @@ async function getConfigValue(repoRoot, userConfigDir, key, dependencies, preser
|
|
|
991
1013
|
const resolved = await dependencies.resolveEffectiveConfig(repoRoot, userConfigDir, dependencies.processEnv);
|
|
992
1014
|
const entry = resolved.resolved[key];
|
|
993
1015
|
if (!entry) {
|
|
1016
|
+
const aggregate = buildResolvedConfigAggregate(resolved, key);
|
|
1017
|
+
if (aggregate) {
|
|
1018
|
+
return { key, ...aggregate };
|
|
1019
|
+
}
|
|
994
1020
|
return { key, value: null, source: 'default' };
|
|
995
1021
|
}
|
|
996
1022
|
return {
|
|
@@ -1001,6 +1027,44 @@ async function getConfigValue(repoRoot, userConfigDir, key, dependencies, preser
|
|
|
1001
1027
|
source: entry.source,
|
|
1002
1028
|
};
|
|
1003
1029
|
}
|
|
1030
|
+
function buildResolvedConfigAggregate(resolved, key) {
|
|
1031
|
+
const prefix = `${key}.`;
|
|
1032
|
+
const entries = Object.entries(resolved.resolved).filter(([entryKey]) => entryKey.startsWith(prefix));
|
|
1033
|
+
if (entries.length === 0) {
|
|
1034
|
+
return null;
|
|
1035
|
+
}
|
|
1036
|
+
const value = {};
|
|
1037
|
+
let source = 'default';
|
|
1038
|
+
const sourceRank = {
|
|
1039
|
+
default: 0,
|
|
1040
|
+
user: 1,
|
|
1041
|
+
shared: 2,
|
|
1042
|
+
local: 3,
|
|
1043
|
+
env: 4,
|
|
1044
|
+
};
|
|
1045
|
+
for (const [entryKey, entry] of entries) {
|
|
1046
|
+
if (sourceRank[entry.source] > sourceRank[source]) {
|
|
1047
|
+
source = entry.source;
|
|
1048
|
+
}
|
|
1049
|
+
const parts = entryKey.slice(prefix.length).split('.');
|
|
1050
|
+
let cursor = value;
|
|
1051
|
+
for (const [index, part] of parts.entries()) {
|
|
1052
|
+
if (index === parts.length - 1) {
|
|
1053
|
+
cursor[part] = entry.value;
|
|
1054
|
+
}
|
|
1055
|
+
else {
|
|
1056
|
+
const nested = cursor[part];
|
|
1057
|
+
if (typeof nested !== 'object' ||
|
|
1058
|
+
nested === null ||
|
|
1059
|
+
Array.isArray(nested)) {
|
|
1060
|
+
cursor[part] = {};
|
|
1061
|
+
}
|
|
1062
|
+
cursor = cursor[part];
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
return { value, source };
|
|
1067
|
+
}
|
|
1004
1068
|
async function listConfigKeys(repoRoot, userConfigDir, dependencies) {
|
|
1005
1069
|
const resolved = await dependencies.resolveEffectiveConfig(repoRoot, userConfigDir, dependencies.processEnv);
|
|
1006
1070
|
const staticKeys = new Set(KEY_ORDER);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fake-runtime.d.mts","sourceRoot":"","sources":["../../../../src/commands/gate/__fixtures__/fake-runtime.mjs"],"names":[],"mappings":""}
|