5-phase-workflow 3.0.1 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -104
- package/bin/install.js +163 -178
- package/bin/sync-agents.js +59 -54
- package/docs/workflow-guide.md +35 -79
- package/package.json +1 -1
- package/src/agents/step-executor-agent.md +3 -3
- package/src/agents/step-orchestrator-agent.md +12 -12
- package/src/agents/verification-agent.md +5 -8
- package/src/codex-commands/plan.md +65 -0
- package/src/commands/5/address-review-findings.md +43 -52
- package/src/commands/5/analyze-feature.md +1 -1
- package/src/commands/5/configure.md +75 -179
- package/src/commands/5/eject.md +0 -1
- package/src/commands/5/implement.md +68 -74
- package/src/commands/5/lean-check.md +7 -5
- package/src/commands/5/reconfigure.md +48 -129
- package/src/commands/5/review.md +42 -14
- package/src/commands/5/synchronize-agents.md +1 -1
- package/src/commands/5/update.md +2 -2
- package/src/hooks/statusline.js +5 -3
- package/src/references/configure-tables.md +0 -33
- package/src/settings.json +0 -12
- package/src/skills/baseline-conventions/SKILL.md +37 -0
- package/src/skills/cognitive-load/SKILL.md +52 -0
- package/src/skills/configure-agents-md/SKILL.md +72 -0
- package/src/skills/configure-rules/SKILL.md +146 -0
- package/src/skills/documentation-style/SKILL.md +32 -0
- package/src/skills/fowler-code-smells/SKILL.md +25 -0
- package/src/skills/review-style/SKILL.md +142 -0
- package/src/templates/AGENTS.md +19 -27
- package/src/templates/workflow/REVIEW-FINDINGS.md +2 -9
- package/src/workflows/5-implement.js +53 -91
- package/docs/findings.md +0 -361
- package/docs/progress.md +0 -102
- package/src/commands/5/apply-review-findings.md +0 -66
- package/src/commands/5/commit.md +0 -43
- package/src/commands/5/plan.md +0 -190
- package/src/commands/5/reply-pr-comments.md +0 -48
- package/src/commands/5/split.md +0 -86
- package/src/commands/5/triage-pr-comments.md +0 -70
- package/src/hooks/config-guard.js +0 -30
- package/src/skills/configure-docs-index/SKILL.md +0 -243
- package/src/skills/configure-skills/SKILL.md +0 -311
- package/src/skills/generate-readme/EXAMPLES.md +0 -168
- package/src/skills/generate-readme/SKILL.md +0 -122
- package/src/skills/generate-readme/TEMPLATE.md +0 -141
- package/src/skills/use-index/SKILL.md +0 -49
- package/src/templates/ARCHITECTURE.md +0 -33
- package/src/templates/CONCERNS.md +0 -33
- package/src/templates/CONVENTIONS.md +0 -74
- package/src/templates/TESTING.md +0 -24
- package/src/templates/workflow/FIX-PLAN.md +0 -55
- package/src/templates/workflow/PLAN-COMPACT.md +0 -42
- package/src/templates/workflow/PLAN.md +0 -76
- package/src/templates/workflow/REVIEW-SUMMARY.md +0 -53
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: review-style
|
|
3
|
+
description: Review changes against coding and documentation standards (repo standards plus a list of style skills), then optionally fix the findings. Use when the user wants a style, standards or conventions review of a branch, a commit range or uncommitted work.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Review style
|
|
7
|
+
|
|
8
|
+
Review a change against two kinds of standards:
|
|
9
|
+
|
|
10
|
+
- **Repo standards**: whatever the repository documents about how code is written.
|
|
11
|
+
- **Styles**: the style skills listed below. They apply in every repository.
|
|
12
|
+
|
|
13
|
+
Repo standards always win. Where a repo standard endorses something a style would flag, the style finding is
|
|
14
|
+
**suppressed**.
|
|
15
|
+
|
|
16
|
+
## Styles
|
|
17
|
+
|
|
18
|
+
Each path is relative to this skill's base directory. Add a style by adding one line.
|
|
19
|
+
|
|
20
|
+
- `../fowler-code-smells/SKILL.md`
|
|
21
|
+
- `../cognitive-load/SKILL.md`
|
|
22
|
+
- `../documentation-style/SKILL.md`. It applies only to code comments, doc comments and Markdown files.
|
|
23
|
+
- `../baseline-conventions/SKILL.md`
|
|
24
|
+
|
|
25
|
+
## Process
|
|
26
|
+
|
|
27
|
+
### 1. Pin the scope
|
|
28
|
+
|
|
29
|
+
The caller's words pick one of these modes:
|
|
30
|
+
|
|
31
|
+
| Mode | Example | Diff |
|
|
32
|
+
|------|---------|------|
|
|
33
|
+
| Since fixed point plus uncommitted (default) | `/review-style master` | `git diff $(git merge-base <fixed-point> HEAD)`, plus untracked files |
|
|
34
|
+
| Committed only | `/review-style master committed only` | `git diff <fixed-point>...HEAD` |
|
|
35
|
+
| Uncommitted only | `/review-style uncommitted` | `git diff HEAD`, plus untracked files |
|
|
36
|
+
| Staged only | `/review-style staged` | `git diff --cached` |
|
|
37
|
+
| Unstaged only | `/review-style unstaged` | `git diff`, plus untracked files |
|
|
38
|
+
|
|
39
|
+
When the words don't clearly name a mode, ask. When a mode needs a fixed point and none is given, ask for it.
|
|
40
|
+
Offer the merge-base with the default branch (`master` or `main`) as the suggested answer.
|
|
41
|
+
|
|
42
|
+
When the caller names files, limit the diff to them, for example `git diff <fixed-point> -- <files>`. Other
|
|
43
|
+
changed files stay out of the review and out of the fixes.
|
|
44
|
+
|
|
45
|
+
Untracked files are those from `git ls-files --others --exclude-standard`. In the modes whose diff says "plus
|
|
46
|
+
untracked files", add each one to the diff as a new file whose every line is added. The other modes leave them out.
|
|
47
|
+
|
|
48
|
+
Confirm the fixed point resolves with `git rev-parse`. Stop when the diff is empty. Write the diff to one temp
|
|
49
|
+
file, in the scratchpad when there is one. Also note `git log <fixed-point>..HEAD --oneline` when the mode
|
|
50
|
+
includes commits.
|
|
51
|
+
|
|
52
|
+
The step is done when one diff file exists and every reviewer will read that same file.
|
|
53
|
+
|
|
54
|
+
### 2. Collect the repo standards
|
|
55
|
+
|
|
56
|
+
Search the repository for every file that documents how code is written:
|
|
57
|
+
|
|
58
|
+
- `CODING_STANDARDS.md`, `CONTRIBUTING.md`, `CODESTYLE.md`, `STYLEGUIDE.md`, and docs about style or conventions.
|
|
59
|
+
Look at the repository root, `.github/` and `docs/`.
|
|
60
|
+
- `CLAUDE.md` and `AGENTS.md` at every level, plus every file they link to about code, tests or docs.
|
|
61
|
+
The block between `<!-- foifi:start` and `<!-- foifi:end -->` holds workflow rules, not repository
|
|
62
|
+
standards; count only the files it links under "Standards". In a legacy file that says a workflow generated it
|
|
63
|
+
from a template, count only the parts it names as repository standards.
|
|
64
|
+
|
|
65
|
+
Skip linter and formatter configs. Tooling enforces them already.
|
|
66
|
+
|
|
67
|
+
### 3. Resolve the styles
|
|
68
|
+
|
|
69
|
+
Turn each path in [Styles](#styles) into an absolute path from this skill's base directory. Reviewers don't
|
|
70
|
+
know that directory, so they only get absolute paths.
|
|
71
|
+
|
|
72
|
+
### 4. Review in parallel
|
|
73
|
+
|
|
74
|
+
Start both reviewers in one message, as foreground sub-agents:
|
|
75
|
+
|
|
76
|
+
- One **repo reviewer** for all repo standards together, when step 2 found any.
|
|
77
|
+
- One **style reviewer** for all styles together. Two reviewers keep the token cost flat as styles are added.
|
|
78
|
+
|
|
79
|
+
Each prompt carries:
|
|
80
|
+
|
|
81
|
+
- The diff file path and the commit list.
|
|
82
|
+
- Its standards: the repo standard files, or the absolute paths of every style's `SKILL.md`. The style
|
|
83
|
+
reviewer reads each `SKILL.md` and every file it links to, and applies each style only where it says it
|
|
84
|
+
applies.
|
|
85
|
+
- For the style reviewer, the repo standard files too, so they can suppress overridden findings.
|
|
86
|
+
- The **context rule**: before judging a hunk, read its whole enclosing method and type. Follow the calls,
|
|
87
|
+
types and parents it touches whenever a rule needs them. Search the repository when a rule asks about
|
|
88
|
+
duplication.
|
|
89
|
+
- The **scope rule**: anchor every finding on a changed line. Flag a problem in surrounding code only when the
|
|
90
|
+
change creates or worsens it, such as adding a fourth level of nesting. Leave problems that already existed
|
|
91
|
+
alone. Lift this rule only when the caller asked for it.
|
|
92
|
+
- The brief:
|
|
93
|
+
|
|
94
|
+
> Apply every rule of your standard to every changed hunk. Report each finding as:
|
|
95
|
+
> `path:line`, a title of a few words, the quoted code, the rule (standard file plus rule name), hard or judgement, and a suggested
|
|
96
|
+
> fix in a few words. Every finding gets a suggested fix. Use one sentence when a few words can't carry it. A breach of a repo standard may be hard. A style finding is always a judgement call. Skip anything
|
|
97
|
+
> tooling enforces.
|
|
98
|
+
>
|
|
99
|
+
> Style reviewer only: when a repo standard endorses what your style would flag, report it under
|
|
100
|
+
> **Suppressed**, citing the repo rule that overrides it.
|
|
101
|
+
|
|
102
|
+
### 5. Merge and report
|
|
103
|
+
|
|
104
|
+
- Merge findings that flag the same line for the same reason. Cite every rule involved.
|
|
105
|
+
- Number the findings `F1`, `F2`, … and group them by file.
|
|
106
|
+
- Write each finding in this shape. ⛔️ marks a hard breach of a repo standard. ⚠️ marks a judgement call.
|
|
107
|
+
The title names the problem in a few words.
|
|
108
|
+
```
|
|
109
|
+
- <icon> **<ID> <title>** _(<lines>)_
|
|
110
|
+
<what is wrong and why>
|
|
111
|
+
→ <fix> _(<standards>)_
|
|
112
|
+
```
|
|
113
|
+
For example:
|
|
114
|
+
```
|
|
115
|
+
- ⛔️ **F1 Javadoc repeats the accessor** _(:71)_
|
|
116
|
+
`@return the entity links.` adds nothing the method name doesn't say.
|
|
117
|
+
→ Drop the line. _(Repo comment rules, Doc)_
|
|
118
|
+
- ⚠️ **F7 Plural name on an invariant checker** _(:12)_
|
|
119
|
+
The plural name suggests a collection, but the class checks an invariant.
|
|
120
|
+
→ Rename to `DocumentEntityLinkInvariantHelper`. _(Cog)_
|
|
121
|
+
```
|
|
122
|
+
- Every finding shows its fix. When a reviewer sent none, write one.
|
|
123
|
+
- List suppressed findings in a separate **Suppressed** section, each with its overriding repo rule.
|
|
124
|
+
- End with one line: findings per standard, hard violations count, and the worst issue.
|
|
125
|
+
|
|
126
|
+
### 6. Fix
|
|
127
|
+
|
|
128
|
+
When the caller asked for a report only, stop after step 5. When the caller asked for fixes up front, fix every
|
|
129
|
+
finding. Otherwise, and only when there are findings, ask: fix all, fix hard violations only, fix the IDs I list,
|
|
130
|
+
or fix nothing.
|
|
131
|
+
|
|
132
|
+
Start one **fixer** sub-agent with:
|
|
133
|
+
|
|
134
|
+
- The selected findings, verbatim, with their rule references.
|
|
135
|
+
- The absolute paths of all standards, so it can look a rule up.
|
|
136
|
+
- The brief:
|
|
137
|
+
|
|
138
|
+
> Fix each finding in the working tree. Then run the repository's documented cheap check, such as compile
|
|
139
|
+
> and format, the way its `CLAUDE.md` or `CONTRIBUTING.md` says. Leave the changes uncommitted. Report
|
|
140
|
+
> each ID as fixed or skipped, with the reason for every skip.
|
|
141
|
+
|
|
142
|
+
Suppressed findings are never fixed. Relay the fixer's report.
|
package/src/templates/AGENTS.md
CHANGED
|
@@ -1,37 +1,35 @@
|
|
|
1
|
-
|
|
1
|
+
<!-- foifi:start — Managed by foifi. /5:reconfigure rewrites everything up to foifi:end. Everything outside this block belongs to the repository and ranks above it: it is part of the repository's own standards. -->
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
# Workflow Rules (foifi)
|
|
4
4
|
|
|
5
5
|
## Build & Run Commands
|
|
6
6
|
|
|
7
7
|
{BUILD_RUN_COMMANDS}
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## Commit Messages
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
{COMMIT_MESSAGE_RULE}
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
## Standards
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
These rules decide how code is written here, in this order:
|
|
16
|
+
|
|
17
|
+
1. **This repository's own standards always win.** They are everything in `AGENTS.md` and `CLAUDE.md` outside this block, plus these files:
|
|
18
|
+
|
|
19
|
+
{REPO_STANDARDS_LINKS}
|
|
16
20
|
|
|
17
|
-
|
|
21
|
+
2. **The workflow's style skills set the baseline** wherever the repository's standards say nothing: `fowler-code-smells`, `cognitive-load`, `documentation-style`, and `baseline-conventions`. Follow them when you write or review code. Where a repository standard endorses something a style would flag, the repository standard wins.
|
|
22
|
+
3. **The workflow rules below** (Simplicity First, Testing, Surgical Changes, Goal-Driven Execution) apply as well. When a repository standard asks for more, such as a changelog entry or a test, do what it asks.
|
|
18
23
|
|
|
19
|
-
|
|
24
|
+
Run the `review-style` skill to check a change against all of them.
|
|
20
25
|
|
|
21
|
-
|
|
22
|
-
Do not skip steps, combine phases, or proceed to actions not specified in the current command.
|
|
23
|
-
Each phase produces a specific artifact - do not create artifacts belonging to other phases.
|
|
26
|
+
## Planning
|
|
24
27
|
|
|
25
|
-
|
|
28
|
+
Plan non-trivial changes in plan mode (Codex: run `$5-plan`). Shape each plan as the change needs; add a mermaid diagram when a flow, state machine, or component relationships explain the change better than text, and plan what the repository standards require, such as tests or a changelog entry.
|
|
26
29
|
|
|
27
|
-
|
|
30
|
+
When the request names a ticket, fetch it (Jira MCP or `gh issue view`) and confirm its content with the user before planning.
|
|
28
31
|
|
|
29
|
-
|
|
30
|
-
2. Keep comments and docs concise; document constraints and non-obvious behavior, not the obvious.
|
|
31
|
-
3. Keep files focused and bounded; split broad controllers, services, hooks, and components early.
|
|
32
|
-
4. Extract helpers or classes when logic starts mixing orchestration, parsing, IO, and mapping in one place.
|
|
33
|
-
5. Favor SRP and DRY, but follow surrounding module conventions before introducing new abstractions.
|
|
34
|
-
6. Keep features modular: wire backend providers in feature modules, keep UI state in hooks, and avoid cross-layer shortcuts.
|
|
32
|
+
After the user approves a plan, run `/5:implement {plan-path}` (Codex: `$5-implement {plan-path}`). Do not implement the plan any other way.
|
|
35
33
|
|
|
36
34
|
## Simplicity First
|
|
37
35
|
|
|
@@ -86,7 +84,7 @@ When your changes create orphans:
|
|
|
86
84
|
|
|
87
85
|
The test: Every changed line should trace directly to the user's request.
|
|
88
86
|
|
|
89
|
-
Before you report a task as done, check your own diff (`git status --short` and `git diff --stat`): revert files and changes that do not trace to the request
|
|
87
|
+
Before you report a task as done, check your own diff (`git status --short` and `git diff --stat`): revert files and changes that do not trace to the request. For every dependency the diff adds: keep it and name it in your reply if the user asked for it; otherwise remove it with the package manager and use existing code, the standard library, or an installed dependency instead. For a deeper pass, run `/5:lean-check` (Codex: `$5-lean-check`).
|
|
90
88
|
|
|
91
89
|
## Goal-Driven Execution
|
|
92
90
|
|
|
@@ -110,10 +108,4 @@ Strong success criteria let you loop independently. Weak criteria ("make it work
|
|
|
110
108
|
|
|
111
109
|
{PROJECT_DOCUMENTATION_LINKS}
|
|
112
110
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
{CODEBASE_INDEX_LINKS}
|
|
116
|
-
|
|
117
|
-
Use the `use-index` skill before running broad Glob/Grep scans. It handles freshness checks, file selection by task type, and fallback strategies. If index files are more than one day old, regenerate them by running `.5/index/rebuild-index.sh` before relying on them.
|
|
118
|
-
|
|
119
|
-
{CUSTOM_DOCUMENTATION}
|
|
111
|
+
<!-- foifi:end -->
|
|
@@ -8,12 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
## How to Use This File
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
2. Run: `/5:address-review-findings`
|
|
13
|
-
3. The command will present each finding one by one with a recommendation
|
|
14
|
-
4. Choose `fix`, `wont_fix`, `wait`, or provide textual instructions
|
|
15
|
-
|
|
16
|
-
The command records your decisions separately and applies only findings marked `fix`.
|
|
11
|
+
`/5:review` hands this file to `/5:address-review-findings`, which fixes the clear findings on its own and asks you, one question at a time, about the rest. Run `/5:address-review-findings --interactive` to decide on every finding yourself.
|
|
17
12
|
|
|
18
13
|
---
|
|
19
14
|
|
|
@@ -44,6 +39,4 @@ The command records your decisions separately and applies only findings marked `
|
|
|
44
39
|
- Questions: {N}
|
|
45
40
|
- Manual Review: {N}
|
|
46
41
|
|
|
47
|
-
**Next
|
|
48
|
-
1. Run: `/5:address-review-findings`
|
|
49
|
-
2. Decide on each finding interactively
|
|
42
|
+
**Next Step:** `/5:address-review-findings` (runs automatically after `/5:review`)
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
export const meta = {
|
|
2
2
|
name: '5-implement',
|
|
3
|
-
description: 'Execute a
|
|
4
|
-
whenToUse: 'Invoked by the /5:implement command when the Workflow tool is available.
|
|
3
|
+
description: 'Execute a full plan: derive steps, run executors in parallel waves, verify. Claude Code only; Codex uses the prose loop.',
|
|
4
|
+
whenToUse: 'Invoked by the /5:implement command for full plans when the Workflow tool is available. Compact plans never reach it; the command implements them inline.',
|
|
5
5
|
phases: [
|
|
6
|
-
{ title: 'Orchestrate', detail: '
|
|
6
|
+
{ title: 'Orchestrate', detail: 'one orchestrator agent derives steps + components' },
|
|
7
7
|
{ title: 'Execute', detail: 'one executor agent per component; parallel components fire together per step' },
|
|
8
8
|
{ title: 'Verify', detail: 'inline when all passed + mechanical, else one verification agent' }
|
|
9
9
|
]
|
|
@@ -15,12 +15,11 @@ export const meta = {
|
|
|
15
15
|
// the canonical human-readable contract; keep these prompts in sync with them.
|
|
16
16
|
//
|
|
17
17
|
// This script has NO filesystem access. It orchestrates agents and RETURNS the
|
|
18
|
-
// final result. The /5:implement command
|
|
19
|
-
//
|
|
18
|
+
// final result. The /5:implement command runs the fast-path build/test check and
|
|
19
|
+
// auto-commit after this returns. Nothing is persisted: a rerun starts over.
|
|
20
20
|
//
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
// the plan's Component Checklist, so re-derivation is stable by name.
|
|
21
|
+
// Models: haiku for mechanical work, sonnet for ordinary logic, opus at effort
|
|
22
|
+
// medium for complex work and for every retry.
|
|
24
23
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
25
24
|
|
|
26
25
|
const RESULT_SCHEMA = {
|
|
@@ -41,13 +40,12 @@ const RESULT_SCHEMA = {
|
|
|
41
40
|
const VERIFICATION_SCHEMA = {
|
|
42
41
|
type: 'object',
|
|
43
42
|
additionalProperties: false,
|
|
44
|
-
required: ['status', 'completeness', 'infrastructure', '
|
|
43
|
+
required: ['status', 'completeness', 'infrastructure', 'scope'],
|
|
45
44
|
properties: {
|
|
46
45
|
status: { enum: ['passed', 'partial', 'failed'] },
|
|
47
46
|
completeness: { enum: ['passed', 'partial', 'failed'] },
|
|
48
47
|
infrastructure: { enum: ['passed', 'failed'] },
|
|
49
48
|
acceptanceCriteria: { type: 'string', description: 'satisfied/total, e.g. "3/3"' },
|
|
50
|
-
quality: { enum: ['passed', 'partial', 'failed'] },
|
|
51
49
|
scope: { enum: ['passed', 'drift'] },
|
|
52
50
|
commands: {
|
|
53
51
|
type: 'array',
|
|
@@ -81,7 +79,7 @@ const STATE_SCHEMA = {
|
|
|
81
79
|
number: { type: 'integer' },
|
|
82
80
|
name: { type: 'string' },
|
|
83
81
|
mode: { enum: ['parallel', 'sequential'] },
|
|
84
|
-
model: { enum: ['haiku', 'sonnet'] },
|
|
82
|
+
model: { enum: ['haiku', 'sonnet', 'opus'] },
|
|
85
83
|
components: { type: 'array', items: { type: 'string' } }
|
|
86
84
|
}
|
|
87
85
|
}
|
|
@@ -97,7 +95,7 @@ const STATE_SCHEMA = {
|
|
|
97
95
|
action: { enum: ['create', 'modify', 'delete', 'rename'] },
|
|
98
96
|
step: { type: 'integer' },
|
|
99
97
|
mode: { enum: ['parallel', 'sequential'] },
|
|
100
|
-
model: { enum: ['haiku', 'sonnet'] },
|
|
98
|
+
model: { enum: ['haiku', 'sonnet', 'opus'] },
|
|
101
99
|
file: { type: 'string' },
|
|
102
100
|
sourceFile: { type: ['string', 'null'] },
|
|
103
101
|
description: { type: 'string' },
|
|
@@ -130,17 +128,15 @@ function orchestratorPrompt(a) {
|
|
|
130
128
|
|
|
131
129
|
Feature: ${a.feature}
|
|
132
130
|
Plan: ${a.paths.plan}
|
|
133
|
-
Codebase scan: ${a.paths.scan} (read only if present)
|
|
134
131
|
Config: ${a.paths.config} (read only if present)
|
|
135
132
|
|
|
136
|
-
|
|
137
|
-
- COPY EVERY COMPONENT NAME VERBATIM from the plan's Component Checklist. Resume matches components by name across runs, so a renamed or reformatted name makes completed work run twice.
|
|
133
|
+
The plan has no fixed shape. Derive the components from whatever it describes: one component per target file (or tightly coupled file group), named in short kebab-case, then turn them into steps + components:
|
|
138
134
|
- Group independent components into one step with mode "parallel"; use "sequential" only when components touch the same file, one imports another, or there is an explicit dependency.
|
|
139
135
|
- Prefer fewer steps. Tests run after the components they validate.
|
|
140
|
-
- model "haiku"
|
|
141
|
-
- 1-2 high-signal patternRefs per component with line ranges or symbols so executors avoid whole-file reads.
|
|
136
|
+
- model "haiku" for mechanical work (UI tweaks, tests, docs, config, simple single-file edits); "sonnet" for ordinary logic; "opus" only for complex logic, cross-module behavior, security/auth, data migrations, or public API contracts.
|
|
137
|
+
- 1-2 high-signal patternRefs per component with line ranges or symbols so executors avoid whole-file reads. Take them from the plan where it names patterns, else find them with Glob/Grep.
|
|
142
138
|
- verifyCommands: narrowest relevant check first, then project build/test.
|
|
143
|
-
- Exclude
|
|
139
|
+
- Exclude work the plan defers or rules out. Represent every other component once. dependsOn must reference existing component names.
|
|
144
140
|
|
|
145
141
|
Return an object matching the schema (steps[] and pendingComponents[]).`
|
|
146
142
|
}
|
|
@@ -148,6 +144,9 @@ Return an object matching the schema (steps[] and pendingComponents[]).`
|
|
|
148
144
|
function executorPrompt(c, a, opts) {
|
|
149
145
|
const refs = (c.patternRefs || []).map(r => ` - ${r.file}${r.read ? ` (${r.read})` : ''}${r.reason ? ` — ${r.reason}` : ''}`).join('\n') || ' - none'
|
|
150
146
|
const verify = (c.verifyCommands || []).join(' && ') || 'infer the narrowest relevant test/build command'
|
|
147
|
+
const standards = a.standards || {}
|
|
148
|
+
const repoStandards = (standards.repo || []).map(f => ` - ${f}`).join('\n') || ' - none found'
|
|
149
|
+
const styles = (standards.styles || []).map(f => ` - ${f}`).join('\n') || ' - none found'
|
|
151
150
|
const retry = opts && opts.retry
|
|
152
151
|
? '\nThis is a RETRY of a failed attempt — read the error context carefully and correct the root cause.'
|
|
153
152
|
: ''
|
|
@@ -158,23 +157,27 @@ Action: ${c.action}
|
|
|
158
157
|
Target file: ${c.file}${c.sourceFile ? `\nSource file (rename from): ${c.sourceFile}` : ''}
|
|
159
158
|
Intent: ${c.description || ''}
|
|
160
159
|
|
|
161
|
-
Read only these pattern references (ranges/symbols), plus the target file for modify/rename:
|
|
160
|
+
Read only these pattern references (ranges/symbols), plus the target file for modify/rename — and for create when it already exists:
|
|
162
161
|
${refs}
|
|
163
162
|
|
|
164
|
-
|
|
163
|
+
Standards — read the parts that apply to your target and follow them. Repository standards win over the style skills.
|
|
164
|
+
Repository standards:
|
|
165
|
+
${repoStandards}
|
|
166
|
+
Style skills:
|
|
167
|
+
${styles}
|
|
168
|
+
|
|
169
|
+
Rules: Nothing is persisted between runs, so your work may already be done: for \`create\`, read the target first when it exists — if it already fulfils the intent, report success with no change; otherwise complete it, never overwrite it blindly. Smallest coherent change; reuse what already exists in this codebase before writing new code, then prefer stdlib, native platform features, and installed dependencies; touch only the target file and what it strictly needs (its test, an import site); mirror existing naming/exports/layout/tests; follow any local skill or rule; add no new dependency; no abstraction or error handling beyond what the intent requires. Fix mechanical issues you cause (imports, types, lint). STOP and report failed for missing dependencies, unplanned auth/schema/API/contract changes, or unclear product decisions. If verify fails only from pre-existing unrelated issues, report that as a deviation with the exact evidence — do not mark the component failed. Do not make more than three attempts on the same failing issue.
|
|
165
170
|
|
|
166
171
|
Then run: ${verify}
|
|
167
172
|
|
|
168
173
|
Return structured output: status, filesCreated, filesModified, verify, deviations (none or brief), skipped (none, or what you deliberately did not build — add when ...), error (none or description). Keep it concise — no logs or diffs unless failed.`
|
|
169
174
|
}
|
|
170
175
|
|
|
171
|
-
function verifyPrompt(a, results
|
|
176
|
+
function verifyPrompt(a, results) {
|
|
172
177
|
const ran = results.flatMap(s => s.items.map(i =>
|
|
173
|
-
` step ${s.step} ${i.component.name}: ${i.result ? i.result.status : 'no result'} (verify ${i.result ? i.result.verify : '?'})${i.escalated ? ' [escalated to
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
.map(n => ` ${n}: completed in a prior session`)
|
|
177
|
-
const summary = [...ran, ...prior].join('\n') || ' (no components ran this invocation)'
|
|
178
|
+
` step ${s.step} ${i.component.name}: ${i.result ? i.result.status : 'no result'} (verify ${i.result ? i.result.verify : '?'})${i.escalated ? ' [escalated to opus]' : ''}`))
|
|
179
|
+
const summary = ran.join('\n') || ' (no components ran)'
|
|
180
|
+
const repoStandards = ((a.standards && a.standards.repo) || []).join(', ') || 'none found'
|
|
178
181
|
const baseline = (a.baseline && a.baseline.length)
|
|
179
182
|
? a.baseline.map(b => ` ${b.command || b.details && b.details.command || '?'}: ${b.status}${b.summary ? ` — ${b.summary}` : ''}`).join('\n')
|
|
180
183
|
: ' (none recorded)'
|
|
@@ -183,6 +186,7 @@ function verifyPrompt(a, results, resumeDone) {
|
|
|
183
186
|
Feature: ${a.feature}
|
|
184
187
|
Plan: ${a.paths.plan}
|
|
185
188
|
Config: ${a.paths.config} (read only if present)
|
|
189
|
+
Repository standards: ${repoStandards}
|
|
186
190
|
|
|
187
191
|
Baseline (pre-change) command results — treat any failure listed here as PRE-EXISTING, not caused by this change:
|
|
188
192
|
${baseline}
|
|
@@ -190,40 +194,16 @@ ${baseline}
|
|
|
190
194
|
Component results:
|
|
191
195
|
${summary}
|
|
192
196
|
|
|
193
|
-
Checks: completeness (every planned component done, acceptance criteria addressed); files exist for create/modify, rename moved correctly, delete removed; build + test pass (reuse the baseline and component results above when they already prove status — rerun only the commands whose inputs changed, not identical passing ones);
|
|
197
|
+
Checks — only whether the plan was built and works; code quality and correctness belong to /5:review, which runs next: completeness (every planned component done, and any acceptance criteria the plan states addressed); files exist for create/modify, rename moved correctly, delete removed; build + test pass (reuse the baseline and component results above when they already prove status — rerun only the commands whose inputs changed, not identical passing ones); scope (run \`git status --short\` and \`git diff HEAD --stat\`, and read untracked files directly since \`git diff HEAD\` omits them: every changed file traces to a planned component — its target, its test, an import site, or a file a repository standard requires (such as a changelog entry); flag files outside the plan, work the plan rules out or defers, new dependencies no component requires, and unrequested abstractions — report drift only in scope; it does not change status). Prefer changed files over broad scanning.
|
|
194
198
|
|
|
195
|
-
Return structured output: status, completeness, infrastructure, acceptanceCriteria ("satisfied/total"),
|
|
199
|
+
Return structured output: status, completeness, infrastructure, acceptanceCriteria ("satisfied/total"), scope, commands[], failures[] (one "drift: {path} — {why}" entry per drift finding).`
|
|
196
200
|
}
|
|
197
201
|
|
|
198
202
|
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
199
203
|
|
|
200
|
-
//
|
|
201
|
-
function
|
|
202
|
-
|
|
203
|
-
name: c.name,
|
|
204
|
-
action: c.action || 'modify',
|
|
205
|
-
step: 1,
|
|
206
|
-
mode: 'parallel',
|
|
207
|
-
model: c.model || 'haiku',
|
|
208
|
-
file: c.file,
|
|
209
|
-
sourceFile: c.sourceFile || null,
|
|
210
|
-
description: c.description || '',
|
|
211
|
-
dependsOn: c.dependsOn || [],
|
|
212
|
-
patternRefs: c.patternRefs || [],
|
|
213
|
-
verifyCommands: c.verifyCommands || [],
|
|
214
|
-
notes: []
|
|
215
|
-
}))
|
|
216
|
-
// If two components write the same file or one depends on another, fall back to sequential.
|
|
217
|
-
const files = norm.map(c => c.file)
|
|
218
|
-
const fileConflict = new Set(files).size !== files.length
|
|
219
|
-
const hasDep = norm.some(c => c.dependsOn && c.dependsOn.length)
|
|
220
|
-
const mode = (fileConflict || hasDep) ? 'sequential' : 'parallel'
|
|
221
|
-
norm.forEach(c => { c.mode = mode })
|
|
222
|
-
const stepModel = norm.some(c => c.model === 'sonnet') ? 'sonnet' : 'haiku'
|
|
223
|
-
return {
|
|
224
|
-
steps: [{ number: 1, name: 'implement', mode, model: stepModel, components: norm.map(c => c.name) }],
|
|
225
|
-
pendingComponents: norm
|
|
226
|
-
}
|
|
204
|
+
// Model options per tier. Opus runs at medium effort: deep enough for complex work, cheaper than high.
|
|
205
|
+
function modelOpts(model) {
|
|
206
|
+
return model === 'opus' ? { model: 'opus', effort: 'medium' } : { model: model || 'haiku' }
|
|
227
207
|
}
|
|
228
208
|
|
|
229
209
|
async function runStep(step, components, a) {
|
|
@@ -234,28 +214,28 @@ async function runStep(step, components, a) {
|
|
|
234
214
|
if (step.mode === 'parallel' && stepComps.length > 1) {
|
|
235
215
|
// Fire every parallel component together — true concurrency, one wave.
|
|
236
216
|
waveResults = await parallel(stepComps.map(c => () =>
|
|
237
|
-
agent(executorPrompt(c, a), { label: `exec:${c.name}`, phase: 'Execute',
|
|
217
|
+
agent(executorPrompt(c, a), { label: `exec:${c.name}`, phase: 'Execute', ...modelOpts(c.model), schema: RESULT_SCHEMA })
|
|
238
218
|
.then(result => ({ component: c, result }))
|
|
239
219
|
.catch(() => ({ component: c, result: null }))))
|
|
240
220
|
} else {
|
|
241
221
|
waveResults = []
|
|
242
222
|
for (const c of stepComps) {
|
|
243
|
-
const result = await agent(executorPrompt(c, a), { label: `exec:${c.name}`, phase: 'Execute',
|
|
223
|
+
const result = await agent(executorPrompt(c, a), { label: `exec:${c.name}`, phase: 'Execute', ...modelOpts(c.model), schema: RESULT_SCHEMA })
|
|
244
224
|
.catch(() => null)
|
|
245
225
|
waveResults.push({ component: c, result })
|
|
246
226
|
}
|
|
247
227
|
}
|
|
248
228
|
|
|
249
|
-
// Retry failed OR crashed (null) components up to twice, escalating to
|
|
229
|
+
// Retry failed OR crashed (null) components up to twice, escalating to opus.
|
|
250
230
|
// A crashed agent (null result) is retried too — an infra error should not be
|
|
251
231
|
// treated more harshly than a clean structured failure.
|
|
252
232
|
for (const wr of waveResults) {
|
|
253
233
|
let attempt = 1
|
|
254
234
|
while ((wr.result == null || wr.result.status === 'failed') && attempt <= 2) {
|
|
255
|
-
log(`retry ${wr.component.name} (attempt ${attempt + 1}) on
|
|
235
|
+
log(`retry ${wr.component.name} (attempt ${attempt + 1}) on opus`)
|
|
256
236
|
wr.escalated = true
|
|
257
237
|
const retried = await agent(executorPrompt(wr.component, a, { retry: true }),
|
|
258
|
-
{ label: `retry:${wr.component.name}`, phase: 'Execute',
|
|
238
|
+
{ label: `retry:${wr.component.name}`, phase: 'Execute', ...modelOpts('opus'), schema: RESULT_SCHEMA }).catch(() => null)
|
|
259
239
|
if (retried) wr.result = retried
|
|
260
240
|
attempt++
|
|
261
241
|
}
|
|
@@ -267,35 +247,25 @@ async function runStep(step, components, a) {
|
|
|
267
247
|
// ── Main ─────────────────────────────────────────────────────────────────────
|
|
268
248
|
|
|
269
249
|
const a = args || {}
|
|
270
|
-
const resumeDone = new Set(a.completedComponents || [])
|
|
271
250
|
|
|
272
251
|
phase('Orchestrate')
|
|
273
|
-
|
|
274
|
-
let plan
|
|
275
|
-
if (a.isCompact && Array.isArray(a.components) && a.components.length) {
|
|
276
|
-
log(`compact plan: ${a.components.length} component(s), no orchestrator agent`)
|
|
277
|
-
plan = compactSteps(a.components)
|
|
278
|
-
} else {
|
|
279
|
-
plan = await agent(orchestratorPrompt(a), { label: 'orchestrate', phase: 'Orchestrate', schema: STATE_SCHEMA })
|
|
280
|
-
}
|
|
252
|
+
const plan = await agent(orchestratorPrompt(a), { label: 'orchestrate', phase: 'Orchestrate', schema: STATE_SCHEMA })
|
|
281
253
|
const steps = plan.steps
|
|
282
254
|
const components = plan.pendingComponents
|
|
283
255
|
|
|
284
256
|
phase('Execute')
|
|
285
257
|
const results = []
|
|
286
258
|
for (const step of steps) {
|
|
287
|
-
|
|
288
|
-
if (!remaining.length) { log(`step ${step.number} already complete (resume), skipping`); continue }
|
|
289
|
-
const stepResult = await runStep({ ...step, components: remaining.map(c => c.name) }, components, a)
|
|
290
|
-
results.push(stepResult)
|
|
259
|
+
results.push(await runStep(step, components, a))
|
|
291
260
|
}
|
|
292
261
|
|
|
293
262
|
phase('Verify')
|
|
294
263
|
const allItems = results.flatMap(s => s.items)
|
|
295
264
|
const allPassed = allItems.length > 0 && allItems.every(i => i.result && i.result.status === 'success' && i.result.verify !== 'failed')
|
|
296
|
-
//
|
|
297
|
-
//
|
|
298
|
-
const
|
|
265
|
+
// Mechanical when every component was planned as haiku and none needed a retry; anything
|
|
266
|
+
// else signals reasoning-level work that warrants the full verification agent.
|
|
267
|
+
const usedOpus = components.some(c => c.model === 'opus') || allItems.some(i => i.escalated)
|
|
268
|
+
const mechanical = !usedOpus && components.every(c => (c.model || 'haiku') === 'haiku')
|
|
299
269
|
// Deterministic scope signal: files an executor touched beyond its planned target(s).
|
|
300
270
|
// Tests and import sites are legitimate, so this is reported for the user, never a failure.
|
|
301
271
|
const extraFiles = allItems.flatMap(i => {
|
|
@@ -306,29 +276,21 @@ const extraFiles = allItems.flatMap(i => {
|
|
|
306
276
|
})
|
|
307
277
|
if (extraFiles.length) log(`scope: ${extraFiles.length} file(s) touched outside planned targets`)
|
|
308
278
|
let verification
|
|
309
|
-
if (allPassed &&
|
|
310
|
-
log('all components passed + mechanical
|
|
311
|
-
// No
|
|
312
|
-
verification = { status: 'passed', completeness: 'passed', infrastructure: 'passed', acceptanceCriteria: 'n/a',
|
|
279
|
+
if (allPassed && mechanical) {
|
|
280
|
+
log('all components passed + mechanical — no verification agent; /5:implement runs build/test and the scope check')
|
|
281
|
+
// No shell access here: /5:implement runs build/test and the scope check itself when it sees inline: true.
|
|
282
|
+
verification = { status: 'passed', completeness: 'passed', infrastructure: 'passed', acceptanceCriteria: 'n/a', commands: [], failures: [], inline: true }
|
|
313
283
|
} else {
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
label: 'verify', phase: 'Verify', model: usedSonnet ? 'sonnet' : 'haiku', schema: VERIFICATION_SCHEMA
|
|
284
|
+
verification = await agent(verifyPrompt(a, results), {
|
|
285
|
+
label: 'verify', phase: 'Verify', ...modelOpts(usedOpus ? 'opus' : 'sonnet'), schema: VERIFICATION_SCHEMA
|
|
317
286
|
})
|
|
318
287
|
}
|
|
319
288
|
|
|
320
|
-
//
|
|
321
|
-
const newlyCompleted = allItems.filter(i => i.result && i.result.status === 'success').map(i => i.component.name)
|
|
322
|
-
const completedComponents = [...new Set([...resumeDone, ...newlyCompleted])]
|
|
323
|
-
|
|
324
|
-
// Returned to the /5:implement command, which persists the slim state.json and auto-commits.
|
|
325
|
-
// steps + components are returned for reporting and per-step staging only — they are NOT persisted;
|
|
326
|
-
// the next run re-derives them, and resume matches on completedComponents by name.
|
|
289
|
+
// Returned to the /5:implement command for the fast-path check, per-step staging, and the report.
|
|
327
290
|
return {
|
|
328
291
|
feature: a.feature,
|
|
329
292
|
steps,
|
|
330
293
|
components,
|
|
331
|
-
completedComponents,
|
|
332
294
|
results: results.map(s => ({
|
|
333
295
|
step: s.step,
|
|
334
296
|
name: s.name,
|