ll-skills 2.0.2 → 3.0.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/CHANGELOG.md +21 -0
- package/README.md +42 -20
- package/agents/ll-executor.md +1 -0
- package/assets/preamble.md +29 -35
- package/bin/install.js +4 -1
- package/hooks/ll-precompact.js +29 -1
- package/hooks/ll-skills-check-update.js +6 -6
- package/hooks/ll-state.js +30 -2
- package/package.json +3 -2
- package/scripts/evals/README.md +57 -0
- package/scripts/evals/cases/auto-dry-run/assert.sh +35 -0
- package/scripts/evals/cases/auto-dry-run/case.json +8 -0
- package/scripts/evals/cases/auto-dry-run/prompt.txt +1 -0
- package/scripts/evals/cases/auto-empty-repo/assert.sh +25 -0
- package/scripts/evals/cases/auto-empty-repo/case.json +8 -0
- package/scripts/evals/cases/auto-empty-repo/fixture/.gitkeep +0 -0
- package/scripts/evals/cases/auto-empty-repo/prompt.txt +1 -0
- package/scripts/evals/cases/decide-final-round/assert.sh +32 -0
- package/scripts/evals/cases/decide-final-round/case.json +8 -0
- package/scripts/evals/cases/decide-final-round/fixture/README.md +3 -0
- package/scripts/evals/cases/decide-final-round/prompt.txt +1 -0
- package/scripts/evals/cases/executor-block/assert.sh +33 -0
- package/scripts/evals/cases/executor-block/case.json +8 -0
- package/scripts/evals/cases/executor-block/prompt.txt +14 -0
- package/scripts/evals/cases/goal-autonomous/assert.sh +35 -0
- package/scripts/evals/cases/goal-autonomous/case.json +8 -0
- package/scripts/evals/cases/goal-autonomous/fixture/PLAN.md +42 -0
- package/scripts/evals/cases/goal-autonomous/fixture/PROGRESS.md +20 -0
- package/scripts/evals/cases/goal-autonomous/fixture/ROADMAP.md +29 -0
- package/scripts/evals/cases/goal-autonomous/fixture/package.json +8 -0
- package/scripts/evals/cases/goal-autonomous/fixture/src/money.js +6 -0
- package/scripts/evals/cases/goal-autonomous/fixture/test/reconcile.test.js +8 -0
- package/scripts/evals/cases/goal-autonomous/prompt.txt +1 -0
- package/scripts/evals/cases/implement-review-gate/assert.sh +35 -0
- package/scripts/evals/cases/implement-review-gate/case.json +8 -0
- package/scripts/evals/cases/implement-review-gate/prompt.txt +1 -0
- package/scripts/evals/cases/implement-stops-at-next/assert.sh +39 -0
- package/scripts/evals/cases/implement-stops-at-next/case.json +9 -0
- package/scripts/evals/cases/implement-stops-at-next/prompt.txt +1 -0
- package/scripts/evals/cases/preamble-no-ritual/assert.sh +17 -0
- package/scripts/evals/cases/preamble-no-ritual/case.json +8 -0
- package/scripts/evals/cases/preamble-no-ritual/fixture/README.md +3 -0
- package/scripts/evals/cases/preamble-no-ritual/fixture/src/a.ts +3 -0
- package/scripts/evals/cases/preamble-no-ritual/prompt.txt +1 -0
- package/scripts/evals/cases/router-execute/assert.sh +12 -0
- package/scripts/evals/cases/router-execute/case.json +8 -0
- package/scripts/evals/cases/router-execute/prompt.txt +1 -0
- package/scripts/evals/cases/router-research/assert.sh +11 -0
- package/scripts/evals/cases/router-research/case.json +8 -0
- package/scripts/evals/cases/router-research/fixture/README.md +3 -0
- package/scripts/evals/cases/router-research/prompt.txt +1 -0
- package/scripts/evals/cases/router-small/assert.sh +21 -0
- package/scripts/evals/cases/router-small/case.json +8 -0
- package/scripts/evals/cases/router-small/fixture/README.md +17 -0
- package/scripts/evals/cases/router-small/prompt.txt +1 -0
- package/scripts/evals/cases/scout-no-plan/assert.sh +41 -0
- package/scripts/evals/cases/scout-no-plan/case.json +8 -0
- package/scripts/evals/cases/scout-no-plan/prompt.txt +8 -0
- package/scripts/evals/cases/verifier-weakened-test/assert.sh +19 -0
- package/scripts/evals/cases/verifier-weakened-test/case.json +8 -0
- package/scripts/evals/cases/verifier-weakened-test/prompt.txt +13 -0
- package/scripts/evals/cases/verifier-weakened-test/setup.sh +19 -0
- package/scripts/evals/fixtures/manual-contract/out.json +29 -0
- package/scripts/evals/fixtures/manual-contract/out.txt +5 -0
- package/scripts/evals/fixtures/manual-contract/with-skill.json +46 -0
- package/scripts/evals/lib/assert.sh +107 -0
- package/scripts/evals/lib/extract.js +73 -0
- package/scripts/evals/run.sh +369 -0
- package/scripts/fixtures/auto-closed/PLAN.md +5 -0
- package/scripts/fixtures/auto-closed/PROGRESS.md +20 -0
- package/scripts/fixtures/auto-closed/ROADMAP.md +6 -0
- package/scripts/fixtures/auto-closed/docs/DELIVERY.md +3 -0
- package/scripts/fixtures/auto-decisions/decisions/DEC-0001-taken-alone.md +13 -0
- package/scripts/fixtures/auto-decisions/decisions/DEC-0002-owner.md +13 -0
- package/scripts/fixtures/auto-noroadmap/PLAN.md +20 -0
- package/scripts/fixtures/auto-noroadmap/PROGRESS.md +11 -0
- package/scripts/fixtures/auto-verify-next/PLAN.md +5 -0
- package/scripts/fixtures/auto-verify-next/PROGRESS.md +18 -0
- package/scripts/fixtures/auto-verify-next/ROADMAP.md +5 -0
- package/scripts/fixtures/auto-verify-next/phases/01/PLAN.md +6 -0
- package/scripts/fixtures/evals-auto/auto-dry-run/pass.txt +18 -0
- package/scripts/fixtures/evals-auto/auto-empty-repo/pass.txt +2 -0
- package/scripts/fixtures/evals-auto/goal-autonomous/pass.txt +29 -0
- package/scripts/fixtures/lint-bad/folded-description/SKILL.md +13 -0
- package/scripts/fixtures/lint-bad/model-invocation-false/SKILL.md +10 -0
- package/scripts/fixtures/next-bad/skills/ll-bad/SKILL.md +30 -0
- package/scripts/fixtures/next-good/skills/ll-good/SKILL.md +26 -0
- package/scripts/fixtures/project/PROGRESS.md +4 -0
- package/scripts/lint-contract.cjs +495 -0
- package/scripts/lint-prompts.sh +396 -0
- package/scripts/ll-tools.js +465 -447
- package/scripts/smoke-test.sh +380 -1
- package/skills/ll-auto/SKILL.md +74 -0
- package/skills/ll-auto/references/run.md +75 -0
- package/skills/ll-auto/references/stages.md +66 -0
- package/skills/ll-auto/scripts/ll-auto.js +345 -0
- package/skills/ll-brainstorm/SKILL.md +5 -4
- package/skills/ll-brainstorm/references/decision-policy.md +3 -0
- package/skills/ll-close/SKILL.md +5 -5
- package/skills/ll-close/references/delivery.md +3 -1
- package/skills/ll-decide/SKILL.md +13 -11
- package/skills/ll-decide/references/decision-policy.md +3 -0
- package/skills/ll-decide/references/interview.md +10 -0
- package/skills/ll-decide/references/plan-skeleton.md +14 -14
- package/skills/ll-decide/references/premise-gate.md +7 -0
- package/skills/ll-goal/SKILL.md +22 -4
- package/skills/ll-goal/references/goal-template.md +57 -0
- package/skills/ll-implement/SKILL.md +4 -2
- package/skills/ll-implement/references/decision-policy.md +3 -0
- package/skills/ll-oncall/SKILL.md +3 -2
- package/skills/ll-refine/SKILL.md +3 -2
- package/skills/ll-research/SKILL.md +3 -2
- package/skills/ll-resume/SKILL.md +4 -3
- package/skills/ll-update/SKILL.md +6 -1
- package/skills/ll-verify/SKILL.md +2 -1
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# The run — `docs/AUTO.md`, decisions, end block
|
|
2
|
+
|
|
3
|
+
## The file
|
|
4
|
+
|
|
5
|
+
`ll-auto.js auto-md --objective "<objective>" --flags "<the arguments>"` prints the body; step 1
|
|
6
|
+
redirects it to `docs/AUTO.md`. Five sections, no frontmatter:
|
|
7
|
+
|
|
8
|
+
```
|
|
9
|
+
# AUTO — autonomous run
|
|
10
|
+
|
|
11
|
+
## Objective
|
|
12
|
+
|
|
13
|
+
<the objective, on its own line>
|
|
14
|
+
|
|
15
|
+
## Flags
|
|
16
|
+
|
|
17
|
+
<the flags string, on its own line>
|
|
18
|
+
|
|
19
|
+
## Roteiro
|
|
20
|
+
|
|
21
|
+
| # | stage | command | status | evidence |
|
|
22
|
+
|---|---|---|---|---|
|
|
23
|
+
| 1 | phase-08 | ll-implement 08 --no-talk | todo | ROADMAP row: PLANNED |
|
|
24
|
+
|
|
25
|
+
## Decisions taken alone
|
|
26
|
+
|
|
27
|
+
(filled at the end of the run)
|
|
28
|
+
|
|
29
|
+
## Log
|
|
30
|
+
|
|
31
|
+
(one dated line per stage transition)
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Status of a row: `todo` · `half` · `running` · `done` · `skipped` · `waiting`. Exactly one row is
|
|
35
|
+
`running` at a time. The `evidence` column is the one `detect` gave for that stage, refreshed when
|
|
36
|
+
the row is marked `done` or `half`.
|
|
37
|
+
|
|
38
|
+
A log line is `<YYYY-MM-DD HH:MM> <stage> <status> — <evidence or the stage's last line>`, appended,
|
|
39
|
+
never rewritten. The `## Flags` section is what `--resume` reads, so it is written once and left
|
|
40
|
+
alone; a run restarted with different flags rewrites the whole file.
|
|
41
|
+
|
|
42
|
+
## Decisions only the owner can take
|
|
43
|
+
|
|
44
|
+
After every stage, list the `decisions/*.md` whose status is `WAITING`.
|
|
45
|
+
|
|
46
|
+
Without `--auto-decision`:
|
|
47
|
+
- the run continues through the stages that do not depend on them; a phase whose ROADMAP section
|
|
48
|
+
carries `stop: owner` is skipped and its row is marked `skipped`;
|
|
49
|
+
- the rows that depend on a WAITING decision are marked `waiting`;
|
|
50
|
+
- when nothing else can run, print the WAITING list — one line per file, with the question and the
|
|
51
|
+
recommended option — and stop with `▶ Next — /clear, then ll-auto --resume`.
|
|
52
|
+
|
|
53
|
+
With `--auto-decision`:
|
|
54
|
+
- append to each WAITING decision file a dated line
|
|
55
|
+
`status: DECIDED — <the recommended option> [decided by absence — revisable]`;
|
|
56
|
+
- clear the `stop: owner` marks those decisions held in ROADMAP;
|
|
57
|
+
- continue the roteiro; the marker is what the end block lists.
|
|
58
|
+
|
|
59
|
+
Nothing is decided outside these two paths: a decision that has no recommended option stays
|
|
60
|
+
`WAITING` and stops the run even under `--auto-decision`, because there is nothing to pick.
|
|
61
|
+
|
|
62
|
+
## The end block
|
|
63
|
+
|
|
64
|
+
`ll-auto.js report --json` answers `{"decisions":[{file, title, line}]}` for every `decisions/*.md`
|
|
65
|
+
carrying `[decided by absence — revisable]`. Print it, and write the same lines into the
|
|
66
|
+
`## Decisions taken alone` section:
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
Decisions taken alone — review them:
|
|
70
|
+
- decisions/DEC-0007-retry-policy.md — retry policy for the provider adapter (line 12)
|
|
71
|
+
- decisions/DEC-0009-report-window.md — the window the report counts (line 9)
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
An empty list is printed as `Decisions taken alone: none`. Then the count line of the Completion
|
|
75
|
+
criterion, then the handover. The run ends there: the next command belongs to the owner.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Stages — detection, commands, flags
|
|
2
|
+
|
|
3
|
+
## Detection
|
|
4
|
+
|
|
5
|
+
`ll-auto.js detect` reads only the files below and is deterministic: the same tree gives the same
|
|
6
|
+
table twice. Every entry answers `{id, status, evidence}` with status `todo`, `half` or `done`.
|
|
7
|
+
|
|
8
|
+
| stage | done when | half when | evidence field |
|
|
9
|
+
|---|---|---|---|
|
|
10
|
+
| `research` | a `docs/research-*/SUMMARY.md` exists | never | the path, or what is missing |
|
|
11
|
+
| `brainstorm` | `docs/decide/OPENING.md` or any `phases/NN/DECISIONS.md` exists | never | the path, or what is missing |
|
|
12
|
+
| `decide` | `PLAN.md` at the root and `PROGRESS.md` with an `ll-state` block | never | `PLAN.md + PROGRESS.md ll-state block` |
|
|
13
|
+
| `phase-NN` | the ROADMAP row state starts with `DONE`, or `## Epilogue — phase NN` exists over a board that is for another phase or all `passes: true` | `phases/NN/PLAN.md` exists and the phase is not done | the ROADMAP row, or the epilogue and the milestones still red |
|
|
14
|
+
| `verify-NN` | `phases/NN/VERIFICATION.md` exists | never | the path, or what is missing |
|
|
15
|
+
| `close` | `docs/DELIVERY.md` exists and the last ROADMAP phase has an epilogue | never | the path, or what is missing |
|
|
16
|
+
|
|
17
|
+
One `phase-NN` per row of the ROADMAP table; with no ROADMAP.md, the phases come from the same
|
|
18
|
+
table inline at `PLAN.md` §8, same columns (`| phase | name | depends_on | requirements | state |`);
|
|
19
|
+
with no `PLAN.md`, none. One `verify-NN` per phase row. `close` is always last.
|
|
20
|
+
|
|
21
|
+
A phase with an epilogue over a board that still shows a milestone `passes: false` is `half`, not
|
|
22
|
+
`done`: the epilogue was written, the phase was not finished. `ll-implement NN` resumes it from the
|
|
23
|
+
board, which is why a `half` phase enters the roteiro with the same command as a `todo` one.
|
|
24
|
+
|
|
25
|
+
## The command per stage
|
|
26
|
+
|
|
27
|
+
| stage | command | note |
|
|
28
|
+
|---|---|---|
|
|
29
|
+
| `research` | `ll-research "<objective>"` | only with `--research`; the objective is the run's |
|
|
30
|
+
| `brainstorm` | `ll-brainstorm project --no-talk` | only with `--brainstorm`; `--interactive` drops `--no-talk` |
|
|
31
|
+
| `decide` | `ll-decide project --no-talk` | enters whenever it is `todo` |
|
|
32
|
+
| `phase-NN` | `ll-implement NN --no-talk` | `--interactive` drops `--no-talk`; a `half` phase resumes with the same command |
|
|
33
|
+
| `verify-NN` | `ll-verify NN` | never `--external`: that flag hands the audit to another session, which this run is not |
|
|
34
|
+
| `close` | `ll-close --no-talk` | `--milestone <name>` when the ROADMAP is complete |
|
|
35
|
+
|
|
36
|
+
The number is written as the ROADMAP writes it (`08`, not `8`). The roteiro entry carries the
|
|
37
|
+
command ready to follow; the stage's own skill decides everything inside it.
|
|
38
|
+
|
|
39
|
+
## Which stages enter the roteiro
|
|
40
|
+
|
|
41
|
+
`research` and `brainstorm` enter only when their flag is set and their status is not `done`.
|
|
42
|
+
`decide` enters when it is `todo`. Every phase row that is not `done` enters, filtered by
|
|
43
|
+
`--from/--to/--only`, the `half` ones first as a resume. `verify-NN` enters right after its phase
|
|
44
|
+
when `--verify all` is set or when the phase's epilogue names `ll-verify NN` in its handover line.
|
|
45
|
+
`close` enters last, unless `--only` or `--to` cut the roteiro before the last row.
|
|
46
|
+
|
|
47
|
+
## Flags
|
|
48
|
+
|
|
49
|
+
| flag | effect |
|
|
50
|
+
|---|---|
|
|
51
|
+
| `"<objective>"` | the run's objective: the argument of `ll-research`, and the `## Objective` of `docs/AUTO.md` |
|
|
52
|
+
| `--research` | put `research` in the roteiro when it is not done |
|
|
53
|
+
| `--brainstorm` | put `brainstorm` in the roteiro when it is not done |
|
|
54
|
+
| `--interactive` | drop `--no-talk` from `ll-brainstorm` and `ll-implement`: those stages talk to the owner |
|
|
55
|
+
| `--auto-decision` | resolve every owner-only decision to its recommended option and continue; without it the run stops when one blocks the way |
|
|
56
|
+
| `--pause-at <stage\|N>` | stop after that stage or phase, with `▶ Next — /clear, then ll-auto --resume` |
|
|
57
|
+
| `--from N` | skip the phase rows below N |
|
|
58
|
+
| `--to N` | skip the phase rows above N |
|
|
59
|
+
| `--only N` | phase N alone; implies `--from N --to N` and cuts `close` |
|
|
60
|
+
| `--verify all` | a `verify-NN` after every phase, whatever the epilogue says |
|
|
61
|
+
| `--redo <stage>` | force a `done` stage back to `todo` in the roteiro; repeatable |
|
|
62
|
+
| `--dry-run` | print the roteiro table and stop, before writing `docs/AUTO.md` |
|
|
63
|
+
| `--resume` | read the flags from the `## Flags` section of `docs/AUTO.md` and continue from the first row that is not `done` |
|
|
64
|
+
|
|
65
|
+
`--pause-at` and `--redo` are repeatable. A plain invocation in a repository that already carries
|
|
66
|
+
`docs/AUTO.md` behaves as `--resume`: the flags of the file are the flags of the run.
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// ll-auto helper — reads the cycle state from disk. Self-contained: `fs` and `path` only.
|
|
3
|
+
// Read commands never fail: on a broken input they print {"ok":false,"reason":…} and exit 0.
|
|
4
|
+
'use strict';
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const J = path.join;
|
|
8
|
+
function readText(f) { try { return fs.readFileSync(f, 'utf8'); } catch { return null; } }
|
|
9
|
+
function isDir(p) { try { return fs.statSync(p).isDirectory(); } catch { return false; } }
|
|
10
|
+
function exists(p) { try { fs.statSync(p); return true; } catch { return false; } }
|
|
11
|
+
function ls(d) { try { return fs.readdirSync(d).sort(); } catch { return []; } }
|
|
12
|
+
function nn(v) { return String(v).trim().padStart(2, '0'); }
|
|
13
|
+
|
|
14
|
+
// --- state block reader, copied from scripts/ll-tools.js (<ll-shared:state>) -----------------
|
|
15
|
+
function readAnchoredBlock(file, name) {
|
|
16
|
+
let txt;
|
|
17
|
+
try { txt = fs.readFileSync(file, 'utf8'); } catch { return null; }
|
|
18
|
+
const all = txt.split('\n');
|
|
19
|
+
const open = new RegExp('^<!--\\s*' + name + '\\s*-->\\s*$');
|
|
20
|
+
const close = new RegExp('^<!--\\s*/' + name + '\\s*-->\\s*$');
|
|
21
|
+
let start = -1, end = -1;
|
|
22
|
+
for (let i = 0; i < all.length; i++) {
|
|
23
|
+
if (start < 0) { if (open.test(all[i])) start = i; } else if (close.test(all[i])) { end = i; break; }
|
|
24
|
+
}
|
|
25
|
+
if (start < 0 || end < 0) return null;
|
|
26
|
+
return { file, start, end, all, lines: all.slice(start + 1, end) };
|
|
27
|
+
}
|
|
28
|
+
function llSplitFlow(str) { // top-level commas only
|
|
29
|
+
const out = [];
|
|
30
|
+
let cur = '', depth = 0, q = null;
|
|
31
|
+
for (const ch of str) {
|
|
32
|
+
if (q) { cur += ch; if (ch === q) q = null; continue; }
|
|
33
|
+
if (ch === '"' || ch === "'") { q = ch; cur += ch; continue; }
|
|
34
|
+
if (ch === '[' || ch === '{') depth++;
|
|
35
|
+
else if (ch === ']' || ch === '}') depth--;
|
|
36
|
+
else if (ch === ',' && depth === 0) { out.push(cur); cur = ''; continue; }
|
|
37
|
+
cur += ch;
|
|
38
|
+
}
|
|
39
|
+
if (cur.trim() !== '') out.push(cur);
|
|
40
|
+
return out;
|
|
41
|
+
}
|
|
42
|
+
function llScalar(raw) {
|
|
43
|
+
const s = String(raw).trim();
|
|
44
|
+
if (/^\[[\s\S]*\]$/.test(s)) return llSplitFlow(s.slice(1, -1)).map(llScalar);
|
|
45
|
+
if (/^\{[\s\S]*\}$/.test(s)) {
|
|
46
|
+
const o = {};
|
|
47
|
+
for (const part of llSplitFlow(s.slice(1, -1))) {
|
|
48
|
+
const m = /^\s*([A-Za-z_][\w-]*)\s*:\s*([\s\S]*)$/.exec(part);
|
|
49
|
+
if (m) o[m[1]] = llScalar(m[2]);
|
|
50
|
+
}
|
|
51
|
+
return o;
|
|
52
|
+
}
|
|
53
|
+
if (/^(true|yes)$/i.test(s)) return true;
|
|
54
|
+
if (/^(false|no)$/i.test(s)) return false;
|
|
55
|
+
if (/^".*"$/.test(s) || /^'.*'$/.test(s)) return s.slice(1, -1);
|
|
56
|
+
return s;
|
|
57
|
+
}
|
|
58
|
+
function parseStateBlockLite(bl) {
|
|
59
|
+
const st = { phase: null, milestones: {}, order: [] };
|
|
60
|
+
let inM = false;
|
|
61
|
+
for (let i = 0; i < bl.length; i++) {
|
|
62
|
+
const line = bl[i].replace(/\s+$/, '');
|
|
63
|
+
if (!line.trim() || /^\s*#/.test(line)) continue;
|
|
64
|
+
let m = /^phase:\s*(.*)$/.exec(line);
|
|
65
|
+
if (m) { st.phase = String(llScalar(m[1])); inM = false; continue; }
|
|
66
|
+
if (/^milestones:\s*(\{\s*\})?$/.test(line)) { inM = true; continue; }
|
|
67
|
+
m = /^(\s*)(M\d+|G-\d+):\s*\{(.*)\}\s*$/.exec(line);
|
|
68
|
+
if (m && inM) {
|
|
69
|
+
st.milestones[m[2]] = llScalar('{' + m[3] + '}');
|
|
70
|
+
st.order.push({ id: m[2], index: i, indent: m[1] });
|
|
71
|
+
} else if (/^\S/.test(line)) inM = false;
|
|
72
|
+
}
|
|
73
|
+
return st;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// --- phase rows ------------------------------------------------------------------------------
|
|
77
|
+
// Rows of the ROADMAP phase table: a header carrying `phase` and numeric first cells.
|
|
78
|
+
function phaseRows(md) {
|
|
79
|
+
const rows = [], seen = new Set();
|
|
80
|
+
let cols = null;
|
|
81
|
+
for (const line of String(md || '').split('\n')) {
|
|
82
|
+
if (!/^\s*\|/.test(line)) { cols = null; continue; }
|
|
83
|
+
const cells = line.trim().replace(/^\|/, '').replace(/\|$/, '').split('|').map((c) => c.trim());
|
|
84
|
+
if (/^:?-{2,}/.test(cells[0] || '')) continue; // separator row
|
|
85
|
+
if (!/^\d{1,3}$/.test(cells[0] || '')) { cols = cells.map((c) => c.toLowerCase()); continue; }
|
|
86
|
+
if (!cols || cols.indexOf('phase') < 0) continue;
|
|
87
|
+
const i = cols.indexOf('state');
|
|
88
|
+
const id = nn(cells[0]);
|
|
89
|
+
if (seen.has(id)) continue;
|
|
90
|
+
seen.add(id);
|
|
91
|
+
rows.push({ nn: id, state: (i >= 0 ? cells[i] : cells[cells.length - 1]) || '' });
|
|
92
|
+
}
|
|
93
|
+
return rows;
|
|
94
|
+
}
|
|
95
|
+
// Without ROADMAP.md the rows live in the `## §8 Phases` table of PLAN.md (`| NN | name | … |`,
|
|
96
|
+
// a header cell `phase` and, when there is one, a `state` cell); without PLAN.md there are no
|
|
97
|
+
// phases at all. The §8 slice runs to the next `## ` heading — `$` under /m ends at every line.
|
|
98
|
+
function rowsOf(root) {
|
|
99
|
+
const rm = readText(J(root, 'ROADMAP.md'));
|
|
100
|
+
if (rm !== null) return phaseRows(rm);
|
|
101
|
+
const plan = readText(J(root, 'PLAN.md'));
|
|
102
|
+
if (plan === null) return [];
|
|
103
|
+
const h = /^##\s*§?8\b[^\n]*\n/m.exec(plan);
|
|
104
|
+
if (!h) return [];
|
|
105
|
+
const rest = plan.slice(h.index + h[0].length);
|
|
106
|
+
const next = rest.search(/^##\s/m);
|
|
107
|
+
return phaseRows(next < 0 ? rest : rest.slice(0, next));
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// --- detect ----------------------------------------------------------------------------------
|
|
111
|
+
function phaseStage(root, row, ctx) {
|
|
112
|
+
if (/^DONE/.test(row.state)) return { status: 'done', evidence: 'ROADMAP row: ' + row.state };
|
|
113
|
+
const plan = 'phases/' + row.nn + '/PLAN.md';
|
|
114
|
+
if (ctx.epilogues.has(row.nn)) {
|
|
115
|
+
const own = ctx.state && nn(ctx.state.phase || '') === row.nn;
|
|
116
|
+
const red = own ? Object.keys(ctx.state.milestones).filter((k) => ctx.state.milestones[k].passes !== true) : [];
|
|
117
|
+
if (!own || red.length === 0) return { status: 'done', evidence: 'PROGRESS.md: ## Epilogue — phase ' + row.nn };
|
|
118
|
+
if (exists(J(root, 'phases', row.nn, 'PLAN.md'))) {
|
|
119
|
+
return { status: 'half', evidence: 'epilogue over a board with ' + red.join(', ') + ' not passing' };
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (exists(J(root, 'phases', row.nn, 'PLAN.md'))) return { status: 'half', evidence: plan };
|
|
123
|
+
return { status: 'todo', evidence: 'ROADMAP row: ' + (row.state || '-') };
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function detect(root) {
|
|
127
|
+
const stages = [];
|
|
128
|
+
const add = (id, status, evidence) => stages.push({ id, status, evidence });
|
|
129
|
+
const research = ls(J(root, 'docs')).filter((n) => /^research-/.test(n) && exists(J(root, 'docs', n, 'SUMMARY.md')));
|
|
130
|
+
add('research', research.length ? 'done' : 'todo',
|
|
131
|
+
research.length ? 'docs/' + research[0] + '/SUMMARY.md' : 'no docs/research-*/SUMMARY.md');
|
|
132
|
+
|
|
133
|
+
const opening = exists(J(root, 'docs', 'decide', 'OPENING.md'));
|
|
134
|
+
const decided = ls(J(root, 'phases')).filter((n) => exists(J(root, 'phases', n, 'DECISIONS.md')));
|
|
135
|
+
add('brainstorm', opening || decided.length ? 'done' : 'todo',
|
|
136
|
+
opening ? 'docs/decide/OPENING.md'
|
|
137
|
+
: decided.length ? 'phases/' + decided[0] + '/DECISIONS.md' : 'no docs/decide/OPENING.md');
|
|
138
|
+
|
|
139
|
+
const progress = J(root, 'PROGRESS.md');
|
|
140
|
+
const block = readAnchoredBlock(progress, 'll-state');
|
|
141
|
+
const state = block ? parseStateBlockLite(block.lines) : null;
|
|
142
|
+
const hasPlan = exists(J(root, 'PLAN.md'));
|
|
143
|
+
add('decide', hasPlan && state ? 'done' : 'todo',
|
|
144
|
+
hasPlan && state ? 'PLAN.md + PROGRESS.md ll-state block'
|
|
145
|
+
: hasPlan ? 'PROGRESS.md carries no ll-state block' : 'no PLAN.md at the root');
|
|
146
|
+
const epilogues = new Set();
|
|
147
|
+
for (const line of (readText(progress) || '').split('\n')) {
|
|
148
|
+
const h = /^## Epilogue — phase (\S+)/.exec(line);
|
|
149
|
+
if (h) epilogues.add(nn(h[1]));
|
|
150
|
+
}
|
|
151
|
+
const rows = rowsOf(root);
|
|
152
|
+
const ctx = { epilogues, state };
|
|
153
|
+
for (const row of rows) {
|
|
154
|
+
const s = phaseStage(root, row, ctx);
|
|
155
|
+
add('phase-' + row.nn, s.status, s.evidence);
|
|
156
|
+
}
|
|
157
|
+
for (const row of rows) {
|
|
158
|
+
const v = 'phases/' + row.nn + '/VERIFICATION.md';
|
|
159
|
+
const ok = exists(J(root, v));
|
|
160
|
+
add('verify-' + row.nn, ok ? 'done' : 'todo', ok ? v : 'no ' + v);
|
|
161
|
+
}
|
|
162
|
+
const last = rows.length ? rows[rows.length - 1].nn : null;
|
|
163
|
+
const delivery = exists(J(root, 'docs', 'DELIVERY.md'));
|
|
164
|
+
const closed = delivery && last !== null && epilogues.has(last);
|
|
165
|
+
add('close', closed ? 'done' : 'todo',
|
|
166
|
+
closed ? 'docs/DELIVERY.md + epilogue for phase ' + last
|
|
167
|
+
: delivery ? 'docs/DELIVERY.md without an epilogue for the last phase' : 'no docs/DELIVERY.md');
|
|
168
|
+
return { ok: true, root, stages };
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// --- next-cmd --------------------------------------------------------------------------------
|
|
172
|
+
// The handoff grammar of lint-contract rule 6: `▶ Next — /clear, then <cmd> (alternatives)`.
|
|
173
|
+
// Backticks are decoration; one trailing parenthetical is dropped. The last line in the file wins.
|
|
174
|
+
function nextCmdOf(txt) {
|
|
175
|
+
let out = '';
|
|
176
|
+
for (const line of String(txt || '').split('\n')) {
|
|
177
|
+
const m = /▶ Next\s*—(.*)$/.exec(line);
|
|
178
|
+
if (!m) continue;
|
|
179
|
+
const hand = m[1].replace(/`/g, '').trim();
|
|
180
|
+
const o = /^\/clear\s*,\s*then\s+(.*)$/.exec(hand);
|
|
181
|
+
if (o) out = o[1].replace(/\([^()]*\)\s*$/, '').trim();
|
|
182
|
+
}
|
|
183
|
+
return out;
|
|
184
|
+
}
|
|
185
|
+
// The slice of PROGRESS.md owned by phase NN's epilogue; the whole file when it has none.
|
|
186
|
+
function epilogueText(root, id) {
|
|
187
|
+
const txt = readText(J(root, 'PROGRESS.md')) || '';
|
|
188
|
+
const lines = txt.split('\n');
|
|
189
|
+
let start = -1;
|
|
190
|
+
for (let i = 0; i < lines.length; i++) {
|
|
191
|
+
const h = /^## Epilogue — phase (\S+)/.exec(lines[i]);
|
|
192
|
+
if (h && nn(h[1]) === id) start = i;
|
|
193
|
+
}
|
|
194
|
+
if (start < 0) return txt;
|
|
195
|
+
let end = lines.length;
|
|
196
|
+
for (let i = start + 1; i < lines.length; i++) if (/^## /.test(lines[i])) { end = i; break; }
|
|
197
|
+
return lines.slice(start, end).join('\n');
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// --- roteiro ---------------------------------------------------------------------------------
|
|
201
|
+
const FLAG_ONE = { '--research': 'research', '--brainstorm': 'brainstorm', '--interactive': 'interactive',
|
|
202
|
+
'--auto-decision': 'auto_decision', '--dry-run': 'dry_run', '--resume': 'resume' };
|
|
203
|
+
const FLAG_VAL = { '--pause-at': 'pause_at', '--redo': 'redo', '--from': 'from', '--to': 'to',
|
|
204
|
+
'--only': 'only', '--verify': 'verify' };
|
|
205
|
+
// A stage reference in --pause-at / --redo: an id, a bare phase number, or `phase 7`.
|
|
206
|
+
function stageId(x) {
|
|
207
|
+
const s = String(x || '').trim();
|
|
208
|
+
if (/^\d{1,3}$/.test(s)) return 'phase-' + nn(s);
|
|
209
|
+
const m = /^(phase|verify)[\s-]*(\d{1,3})$/.exec(s);
|
|
210
|
+
return m ? m[1] + '-' + nn(m[2]) : s;
|
|
211
|
+
}
|
|
212
|
+
function parseFlags(str) {
|
|
213
|
+
const f = { pause_at: [], redo: [], from: null, to: null, only: null, verify: null };
|
|
214
|
+
for (const k of Object.values(FLAG_ONE)) f[k] = false;
|
|
215
|
+
const t = String(str || '').trim().split(/\s+/).filter(Boolean);
|
|
216
|
+
for (let i = 0; i < t.length; i++) {
|
|
217
|
+
if (FLAG_ONE[t[i]]) { f[FLAG_ONE[t[i]]] = true; continue; }
|
|
218
|
+
const k = FLAG_VAL[t[i]];
|
|
219
|
+
if (!k) continue;
|
|
220
|
+
const v = i + 1 < t.length && !/^--/.test(t[i + 1]) ? t[++i] : null;
|
|
221
|
+
if (k === 'pause_at' || k === 'redo') { if (v !== null) f[k].push(v); } else f[k] = v;
|
|
222
|
+
}
|
|
223
|
+
if (f.only !== null) { f.from = f.only; f.to = f.only; } // --only N implies from = to = N
|
|
224
|
+
return f;
|
|
225
|
+
}
|
|
226
|
+
const num = (x) => Number(String(x).replace(/[^\d.]/g, '')) || 0;
|
|
227
|
+
const talk = (interactive) => (interactive ? '' : ' --no-talk'); // --interactive drops --no-talk
|
|
228
|
+
|
|
229
|
+
function roteiro(root, flagStr, objective) {
|
|
230
|
+
const d = detect(root);
|
|
231
|
+
const f = parseFlags(flagStr);
|
|
232
|
+
const st = {};
|
|
233
|
+
for (const s of d.stages) st[s.id] = s;
|
|
234
|
+
const redo = new Set(f.redo.map(stageId));
|
|
235
|
+
const pause = new Set(f.pause_at.map(stageId));
|
|
236
|
+
const obj = String(objective || '').trim();
|
|
237
|
+
// Empty repository per D-03-06: no research, no OPENING.md, no PLAN.md.
|
|
238
|
+
const bare = st.research.status === 'todo' && st.brainstorm.status === 'todo' && !exists(J(root, 'PLAN.md'));
|
|
239
|
+
if (bare && !obj) return { ok: true, root, needs_objective: true, roteiro: [] };
|
|
240
|
+
const out = [];
|
|
241
|
+
const want = (id) => redo.has(id) || !st[id] || st[id].status !== 'done';
|
|
242
|
+
const push = (id, command) => out.push({ stage: id, command,
|
|
243
|
+
status: st[id] ? st[id].status : 'todo', pause_after: pause.has(id) });
|
|
244
|
+
if (f.research && want('research')) push('research', 'll-research "' + (obj || '<objective>') + '"');
|
|
245
|
+
if (f.brainstorm && want('brainstorm')) push('brainstorm', 'll-brainstorm project' + talk(f.interactive));
|
|
246
|
+
if (want('decide')) push('decide', 'll-decide project --no-talk');
|
|
247
|
+
const rows = rowsOf(root);
|
|
248
|
+
const pend = rows.filter((r) => want('phase-' + r.nn));
|
|
249
|
+
const inRange = (r) => (f.from === null || num(r.nn) >= num(f.from))
|
|
250
|
+
&& (f.to === null || num(r.nn) <= num(f.to));
|
|
251
|
+
const kept = pend.filter(inRange);
|
|
252
|
+
const half = (r) => st['phase-' + r.nn].status === 'half';
|
|
253
|
+
for (const r of kept.filter(half).concat(kept.filter((r) => !half(r)))) { // a half phase resumes first
|
|
254
|
+
push('phase-' + r.nn, 'll-implement ' + r.nn + talk(f.interactive));
|
|
255
|
+
const asked = new RegExp('^ll-verify\\s+0*' + num(r.nn) + '$').test(nextCmdOf(epilogueText(root, r.nn)));
|
|
256
|
+
if ((f.verify === 'all' || asked) && want('verify-' + r.nn)) push('verify-' + r.nn, 'll-verify ' + r.nn);
|
|
257
|
+
}
|
|
258
|
+
// Close only when the roteiro covers every phase still open: --only, --from or --to cut it out.
|
|
259
|
+
if (f.only === null && kept.length === pend.length && want('close')) push('close', 'll-close --no-talk');
|
|
260
|
+
return { ok: true, root, needs_objective: false, roteiro: out };
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// --- report ----------------------------------------------------------------------------------
|
|
264
|
+
const MARK = '[decided by absence — revisable]';
|
|
265
|
+
function report(root) {
|
|
266
|
+
const dir = J(root, 'decisions'), out = [];
|
|
267
|
+
for (const name of ls(dir)) {
|
|
268
|
+
if (!/\.md$/.test(name)) continue;
|
|
269
|
+
const lines = (readText(J(dir, name)) || '').split('\n');
|
|
270
|
+
const i = lines.findIndex((l) => l.indexOf(MARK) >= 0);
|
|
271
|
+
if (i < 0) continue;
|
|
272
|
+
const h = lines.find((l) => /^#\s+/.test(l)) || '';
|
|
273
|
+
out.push({ file: 'decisions/' + name, title: h.replace(/^#\s+/, '').trim(), line: i + 1 });
|
|
274
|
+
}
|
|
275
|
+
return { ok: true, root, decisions: out };
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// --- auto-md ---------------------------------------------------------------------------------
|
|
279
|
+
// The docs/AUTO.md body of D-03-07: objective, flags, the roteiro table, then two empty sections.
|
|
280
|
+
function autoMd(root, objective, flagStr) {
|
|
281
|
+
const ev = {};
|
|
282
|
+
for (const s of detect(root).stages) ev[s.id] = s.evidence;
|
|
283
|
+
const r = roteiro(root, flagStr, objective);
|
|
284
|
+
const L = ['# AUTO — autonomous run', '',
|
|
285
|
+
'## Objective', '', String(objective || '').trim() || '<objective>', '',
|
|
286
|
+
'## Flags', '', String(flagStr || '').trim() || '(none)', '',
|
|
287
|
+
'## Roteiro', '', '| # | stage | command | status | evidence |', '|---|---|---|---|---|'];
|
|
288
|
+
r.roteiro.forEach((e, i) => L.push('| ' + (i + 1) + ' | ' + e.stage + ' | ' + e.command + ' | '
|
|
289
|
+
+ e.status + ' | ' + (ev[e.stage] || '—') + ' |'));
|
|
290
|
+
if (!r.roteiro.length) {
|
|
291
|
+
L.push('| — | — | — | — | ' + (r.needs_objective ? 'no objective given' : 'nothing left to run') + ' |');
|
|
292
|
+
}
|
|
293
|
+
L.push('', '## Decisions taken alone', '', '(filled at the end of the run)', '',
|
|
294
|
+
'## Log', '', '(one dated line per stage transition)');
|
|
295
|
+
return { ok: true, root, markdown: L.join('\n') };
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// --- cli -------------------------------------------------------------------------------------
|
|
299
|
+
function parseArgv(argv) {
|
|
300
|
+
const flags = {}, pos = [];
|
|
301
|
+
for (let i = 0; i < argv.length; i++) {
|
|
302
|
+
const a = argv[i];
|
|
303
|
+
const kv = /^--([^=]+)=([\s\S]*)$/.exec(a);
|
|
304
|
+
if (kv) { flags[kv[1]] = kv[2]; continue; }
|
|
305
|
+
if (a === '--cwd' || a === '--flags' || a === '--objective') { flags[a.slice(2)] = argv[++i]; continue; }
|
|
306
|
+
if (/^--/.test(a)) { flags[a.slice(2)] = true; continue; }
|
|
307
|
+
pos.push(a);
|
|
308
|
+
}
|
|
309
|
+
return { flags, pos };
|
|
310
|
+
}
|
|
311
|
+
function rootOf(a) {
|
|
312
|
+
const r = path.resolve(String(a.flags.cwd || process.cwd()));
|
|
313
|
+
if (!isDir(r)) throw new Error('not a directory: ' + r);
|
|
314
|
+
return r;
|
|
315
|
+
}
|
|
316
|
+
const C = {
|
|
317
|
+
detect: (a) => detect(rootOf(a)),
|
|
318
|
+
roteiro: (a) => roteiro(rootOf(a), a.flags.flags, a.flags.objective),
|
|
319
|
+
'next-cmd': (a) => ({ ok: true, command: nextCmdOf(readText(path.resolve(a.pos[0] || ''))) }),
|
|
320
|
+
report: (a) => report(rootOf(a)),
|
|
321
|
+
'auto-md': (a) => autoMd(rootOf(a), a.flags.objective, a.flags.flags),
|
|
322
|
+
};
|
|
323
|
+
const FMT = {
|
|
324
|
+
detect: (r) => r.stages.map((s) => s.id.padEnd(12) + ' ' + s.status.padEnd(5) + ' ' + s.evidence).join('\n'),
|
|
325
|
+
roteiro: (r) => (r.needs_objective ? 'needs_objective'
|
|
326
|
+
: r.roteiro.map((e, i) => (i + 1) + '. ' + e.stage.padEnd(12) + ' ' + e.command
|
|
327
|
+
+ (e.pause_after ? ' [pause]' : '')).join('\n')),
|
|
328
|
+
'next-cmd': (r) => r.command,
|
|
329
|
+
report: (r) => r.decisions.map((d) => d.file + ':' + d.line + ' ' + d.title).join('\n'),
|
|
330
|
+
'auto-md': (r) => r.markdown,
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
function main() {
|
|
334
|
+
const argv = process.argv.slice(2), cmd = argv[0];
|
|
335
|
+
try {
|
|
336
|
+
if (!C[cmd]) throw new Error('usage: ll-auto.js <' + Object.keys(C).join('|') + '> [args] [--cwd <dir>] [--json]');
|
|
337
|
+
const a = parseArgv(argv.slice(1));
|
|
338
|
+
const res = C[cmd](a);
|
|
339
|
+
process.stdout.write((a.flags.json || !FMT[cmd] ? JSON.stringify(res) : FMT[cmd](res)) + '\n');
|
|
340
|
+
} catch (e) {
|
|
341
|
+
process.stdout.write(JSON.stringify({ ok: false, reason: (e && e.message) || String(e) }) + '\n');
|
|
342
|
+
}
|
|
343
|
+
process.exit(0);
|
|
344
|
+
}
|
|
345
|
+
main();
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: ll-brainstorm
|
|
3
|
-
description:
|
|
3
|
+
description: Opens a phase, a project or a loose idea by deciding in front of the owner instead of asking, and writes the opening map to phases/NN/DECISIONS.md or docs/decide/OPENING.md with the next command.
|
|
4
4
|
argument-hint: "[phase-number | project | chat] [--no-talk]"
|
|
5
|
+
disable-model-invocation: true
|
|
5
6
|
---
|
|
6
7
|
|
|
7
8
|
# Brainstorm
|
|
@@ -123,7 +124,7 @@ Write the file, print the score in three lines and the next command. Nothing is
|
|
|
123
124
|
Closed. <k> locked by me (<r> revisable), <j> answered by you (<c> against the recommendation),
|
|
124
125
|
<m> deferred. Questions asked <j> / assumptions <a> / band-1 open 0. (printed even when <j> = 0)
|
|
125
126
|
→ phases/NN/DECISIONS.md
|
|
126
|
-
▶ Next —
|
|
127
|
+
▶ Next — /clear, then ll-implement NN (or "adjust X" if something is wrong)
|
|
127
128
|
```
|
|
128
129
|
|
|
129
130
|
File skeleton (8 sections, all present even when empty):
|
|
@@ -154,7 +155,7 @@ decision he did not make is a defect, not a rounding. Band-1 items and items tha
|
|
|
154
155
|
phases also get `decisions/DEC-NNNN-<slug>.md`. The helper `${CLAUDE_CONFIG_DIR:-$HOME/.claude}/skills/ll-implement/scripts/ll-tools.js` is used only if it exists — this skill does not ship it; when it is absent,
|
|
155
156
|
the DEC id is the next number after the highest in `decisions/`; with it, `dec-reserve <n>`.
|
|
156
157
|
|
|
157
|
-
##
|
|
158
|
+
## Completion criterion
|
|
158
159
|
|
|
159
160
|
The conversation closes when four list comparisons hold — no model score:
|
|
160
161
|
1. Zero Blocking items open (answered by the owner or explicitly deferred by him).
|
|
@@ -169,7 +170,7 @@ close A as it stands and handle the rest in execution, or take the premise that
|
|
|
169
170
|
to `ll-decide project` (its premortem) as the next command." Budget: ≤4 turns on the phase route, ≤8 on project.
|
|
170
171
|
|
|
171
172
|
Done when the file exists with its 8 sections and the Score line reads `0 band-1 items open`.
|
|
172
|
-
▶ Next —
|
|
173
|
+
▶ Next — /clear, then ll-implement NN (phase route; project route: ll-decide project)
|
|
173
174
|
nothing to run (chat route).
|
|
174
175
|
|
|
175
176
|
## References
|
|
@@ -61,6 +61,9 @@ Silence of 10 minutes ratifies the recommended list (A), never a blocking item (
|
|
|
61
61
|
## Silence, delegation, directives
|
|
62
62
|
- Silence for 10 minutes in the hot window (harness AFK timeout) ratifies option A of a band-2/3 item or
|
|
63
63
|
list A of a map — never option B, never a band-1 item. Record `ratified by silence`; in a run, `[decided by absence — revisable]`.
|
|
64
|
+
- Under `--no-talk` the ten minutes are not waited for: the recommended option is taken at once and
|
|
65
|
+
recorded `[decided by absence — revisable]`. A band-1 item is never taken this way — it is written
|
|
66
|
+
as a decision in state `WAITING` and the work that depends on it stops there.
|
|
64
67
|
- "Claude decide", "você decide", "pergunta pro time" are legitimate answers: record an assumption
|
|
65
68
|
`ASM-n` (permanent; announced if changed later) or the delegation, and never ask that item again.
|
|
66
69
|
- An order against the recommendation is a directive: one challenge with the cost named, then
|
package/skills/ll-close/SKILL.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: ll-close
|
|
3
|
-
description: Closes a
|
|
4
|
-
argument-hint: "[--milestone <name>]"
|
|
3
|
+
description: Closes a phase or a delivery, reconciling the backlog against executable conditions, writing docs/DELIVERY.md, stamping the epilogue, recording the retrospective and asking for one block of ratification.
|
|
4
|
+
argument-hint: "[--milestone <name>] [--no-talk]"
|
|
5
5
|
disable-model-invocation: true
|
|
6
6
|
allowed-tools: Bash(${CLAUDE_SKILL_DIR}/scripts/ll-tools.js *)
|
|
7
7
|
---
|
|
@@ -29,7 +29,7 @@ Reply to the owner in Portuguese; every file you write is in English.
|
|
|
29
29
|
|
|
30
30
|
Read the verification for the target: the newest by mtime between `phases/NN/VERIFICATION.md` (phase) and `VERIFICATION.md` at the repo root (delivery); say which one opened the gate. Close only on `APPROVED`, or on `APPROVED_WITH_RESERVATIONS` whose reservations are accepted in a `decisions/DEC-*.md` naming them. Anything else — `REJECTED`, `product: FAIL`, `process: FAIL` with no DEC, no verification file at all — stops here:
|
|
31
31
|
|
|
32
|
-
> Não fecho: `<motivo em uma linha>`. ▶ Next —
|
|
32
|
+
> Não fecho: `<motivo em uma linha>`. ▶ Next — /clear, then ll-verify NN (or record the reservations in a DEC and run ll-close again)
|
|
33
33
|
|
|
34
34
|
A verification whose ledger has `STALE` lines is verification of code that moved: it does not open the gate.
|
|
35
35
|
|
|
@@ -41,7 +41,7 @@ A verification whose ledger has `STALE` lines is verification of code that moved
|
|
|
41
41
|
4. **Write `docs/RETROSPECTIVE-<date>.md`** from `references/retrospective.md` — about 60 lines, driven by the numbers from step 3, one lesson per line with the evidence that produced it.
|
|
42
42
|
5. **Promote what is permanent.** A lesson that changes how the next phase runs goes to project memory; a lesson that is a standing rule of this repository goes as one line into the project's `CLAUDE.md`, in the owner's words when he gave them. Two lines at most per delivery: a CLAUDE.md that grows every close stops being read.
|
|
43
43
|
6. **Peer notice.** If a peer session (infra, ops, another repo) shipped, waits on something, or was asked for something in `docs/REQUESTS.md`, that goes in the summary as one line with the request id and its state — not as a new message.
|
|
44
|
-
7. **One block of ratification.** A single `AskUserQuestion`, at most 4 items, only after everything is written: the open pendings with their conditions, and the accepted risks the delivery carries. Options carry the cost of each path and the recommendation is marked. Silence keeps what is written; nothing here blocks the close.
|
|
44
|
+
7. **One block of ratification.** A single `AskUserQuestion`, at most 4 items, only after everything is written: the open pendings with their conditions, and the accepted risks the delivery carries. Options carry the cost of each path and the recommendation is marked. Silence keeps what is written; nothing here blocks the close. With `--no-talk`, this block is not asked: the same items — open pendings with their conditions, accepted risks, recommendation — are written instead under the epilogue in PROGRESS.md and in DELIVERY.md §5, each line marked `[decided by absence — revisable]` with the date; the gate above is unchanged.
|
|
45
45
|
8. **Report.** Verdict and seals, what changed in one line, numbers from `phase-stats`, open pendings by id, accepted risks, next command.
|
|
46
46
|
|
|
47
47
|
## Flow — `--milestone <name>`
|
|
@@ -53,7 +53,7 @@ The list always goes out; the audit ritual is optional and offered once, never i
|
|
|
53
53
|
3. Collapse the closed phases in `ROADMAP.md` into one `<details>` block per phase, keeping the numbering intact — phase 7 stays phase 7 forever, and a later reference to it still resolves.
|
|
54
54
|
4. Transport only what survives: an open pending with an executable condition goes to `BACKLOG.md`; a criterion never proven goes to the next milestone's ROADMAP as a criterion, not as a note. What survives neither test is dropped, and the report says which.
|
|
55
55
|
5. Update `## Current state` in the project's `CLAUDE.md`: what exists now, what the next milestone is, where the history went. Replace the section; do not append to it.
|
|
56
|
-
6. One block of ratification, as in the delivery flow, plus the open pendings the milestone inherits.
|
|
56
|
+
6. One block of ratification, as in the delivery flow, plus the open pendings the milestone inherits. `--no-talk` follows the same rule: the block is not asked, and the same items are written under the epilogue instead.
|
|
57
57
|
|
|
58
58
|
## Completion criterion
|
|
59
59
|
|
|
@@ -42,7 +42,9 @@ reason, never omitted.>
|
|
|
42
42
|
<What was decided on the owner's behalf while he was not there, one per line: the assumption, the
|
|
43
43
|
DEC that records it, what breaks if it is wrong, and how expensive it is to reverse. This section
|
|
44
44
|
feeds the ratification block; if it is empty, say "none — every decision in this delivery is in
|
|
45
|
-
decisions/"
|
|
45
|
+
decisions/". Under `--no-talk`, this section also carries the open pendings, accepted risks and
|
|
46
|
+
recommendation that would otherwise be asked in the ratification block, each line ending
|
|
47
|
+
`[decided by absence — revisable]` and the date.>
|
|
46
48
|
|
|
47
49
|
## 6. Out of this round, and why
|
|
48
50
|
<What was asked for or found and deliberately not done. One line each: what, why (cost, risk,
|