liteagents 3.6.0 → 3.7.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 +68 -0
- package/package.json +1 -1
- package/packages/ampcode/skills/branch-review/SKILL.md +20 -3
- package/packages/ampcode/skills/docs-builder/SKILL.md +168 -70
- package/packages/ampcode/skills/docs-builder/docs-builder.cjs +325 -70
- package/packages/ampcode/skills/release/SKILL.md +38 -8
- package/packages/ampcode/skills/security/SKILL.md +6 -3
- package/packages/claude/plugins/live-canvas-marketplace/plugins/live-canvas-channel/package-lock.json +3 -3
- package/packages/claude/skills/branch-review/SKILL.md +20 -3
- package/packages/claude/skills/docs-builder/SKILL.md +168 -69
- package/packages/claude/skills/docs-builder/docs-builder.cjs +325 -70
- package/packages/claude/skills/release/SKILL.md +38 -8
- package/packages/claude/skills/security/SKILL.md +6 -3
- package/packages/droid/commands/branch-review.md +20 -3
- package/packages/droid/commands/docs-builder/docs-builder.cjs +325 -70
- package/packages/droid/commands/docs-builder.md +168 -70
- package/packages/droid/commands/release.md +38 -8
- package/packages/droid/commands/security.md +6 -3
- package/packages/opencode/command/branch-review.md +20 -3
- package/packages/opencode/command/docs-builder/docs-builder.cjs +325 -70
- package/packages/opencode/command/docs-builder.md +168 -70
- package/packages/opencode/command/release.md +38 -8
- package/packages/opencode/command/security.md +6 -3
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
description: Reorg a docs corpus, split an oversized doc, search it, keep pages current, index them
|
|
3
3
|
argument-hint: [reorg | cleanup <file.md> | search <query words...> — empty asks first run vs. drift]
|
|
4
4
|
---
|
|
5
|
-
|
|
6
5
|
# docs-builder
|
|
7
6
|
|
|
8
7
|
Keep project docs **current, complete and findable**, and split a file when it outgrows
|
|
@@ -48,8 +47,13 @@ Every command below is `node $DB …`; everything the script writes (`docs/.docs
|
|
|
48
47
|
JSON state, `docs/index.md`, the ledger, the log, the config pointer) lands under the target
|
|
49
48
|
repo. `REPO=` is optional and only needed when not running from the repo root.
|
|
50
49
|
|
|
51
|
-
**With an argument** (`reorg`, `cleanup <file>`, or `search <query words...>`) — run that
|
|
52
|
-
directly, no question asked.
|
|
50
|
+
**With an argument** (`reorg [dir]`, `cleanup <file>`, or `search <query words...>`) — run that
|
|
51
|
+
mode directly, no question asked. `reorg <dir>` re-checks every doc already inside `<dir>` —
|
|
52
|
+
useful to scope a check to one directory outside the default scan. A **bare** `reorg`/`discover`
|
|
53
|
+
(no argument) already re-checks `docs/product/`, `docs/wiki/` and `docs/logs/` on every run —
|
|
54
|
+
only `docs/archive/` stays frozen and out of scope. Scan scope with no argument is otherwise
|
|
55
|
+
narrow by design: root-level `.md` files (top level only, not recursive) plus everything under
|
|
56
|
+
`docs/` (recursive) — nothing else in the repo is ever listed or moved.
|
|
53
57
|
|
|
54
58
|
**Bare `/docs-builder`, no argument — ALWAYS ask, never auto-detect.** Run `due` first and
|
|
55
59
|
put its one-line verdict in the question text so the choice is informed. Then use
|
|
@@ -57,9 +61,9 @@ put its one-line verdict in the question text so the choice is informed. Then us
|
|
|
57
61
|
|
|
58
62
|
> **Question: What should docs-builder do?**
|
|
59
63
|
>
|
|
60
|
-
> - **First run** — sort
|
|
61
|
-
> too big into pages and index them. Use
|
|
62
|
-
> docs-builder has never run here.
|
|
64
|
+
> - **First run** — sort root-level `.md` files and everything under `docs/` into
|
|
65
|
+
> product/wiki/logs/archive, then split anything too big into pages and index them. Use
|
|
66
|
+
> when docs are a pile of loose files, or docs-builder has never run here.
|
|
63
67
|
> - **Docs drift** — docs moved on since the last run: report what changed, rebuild the
|
|
64
68
|
> index, re-run lint. Nothing is restructured and nothing is split.
|
|
65
69
|
|
|
@@ -86,8 +90,9 @@ read-only — no model cost, no interview, nothing moves.
|
|
|
86
90
|
bucket). `bucket` itself starts **empty** on every row.
|
|
87
91
|
2. **The classification interview.** Feed the model the WHOLE plan table (file, h1, snip,
|
|
88
92
|
lines, suggested+reason) in one call and have it fill `bucket` for every row —
|
|
89
|
-
`product`/`logs`/`archive` — with a one-line reason: honour that a SHOUTED
|
|
90
|
-
status is near-conclusive for `archive` and that `suggested` is a prior, not
|
|
93
|
+
`product`/`wiki`/`logs`/`archive` — with a one-line reason: honour that a SHOUTED
|
|
94
|
+
self-declared status is near-conclusive for `archive` and that `suggested` is a prior, not
|
|
95
|
+
an authority.
|
|
91
96
|
The model writes its answers straight into `reorg-plan.json`. Then show the user the full
|
|
92
97
|
resulting table via `AskUserQuestion` (approve all / correct specific rows / abort) — a
|
|
93
98
|
correction changes the plan file before anything moves.
|
|
@@ -99,12 +104,10 @@ read-only — no model cost, no interview, nothing moves.
|
|
|
99
104
|
to split** (any, all, none). Only then run `cleanup <file>` (Mode 1) on each chosen file —
|
|
100
105
|
`cleanup` itself prints the estimated split cost for that one file, then a mechanical
|
|
101
106
|
shape report, then stops for its own interview (Mode 1, step 1b) before anything else runs.
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
ledger, and without the stamp `due` stays NOT due, the picker's verdict stays uninformed,
|
|
107
|
-
and `/remember`'s docs nudge never fires.
|
|
107
|
+
Add `docs/.docs-builder/` to `.gitignore` if it is not already ignored: it is machine state,
|
|
108
|
+
regenerated every run, and the ledger stamp is per-clone by design — it must never ride into
|
|
109
|
+
history on a later `git add -A`. Then follow "Finishing a run" (below `apply-reorg`, Mode 0
|
|
110
|
+
step 3) to commit and stamp the ledger.
|
|
108
111
|
|
|
109
112
|
The two stops are deliberate and different. Step 2 guards *correctness* — the interview and
|
|
110
113
|
the user's approval, before a single file moves. Step 3's follow-up guards *cost* — splitting
|
|
@@ -115,7 +118,7 @@ when they pick "First run". Never split N files in one shot on an unseen list.
|
|
|
115
118
|
first, if a ledger stamp exists, then it runs `discover`. If any row's `bucket` is still
|
|
116
119
|
empty (true on a genuine first run, or when new files appeared since the last classification),
|
|
117
120
|
`reorg` **stops right there** and prints what to do next — it never silently proceeds past an
|
|
118
|
-
unclassified plan.
|
|
121
|
+
unclassified plan. Otherwise follow "Finishing a run" once it's done. Once
|
|
119
122
|
the plan is fully classified (an already-sorted corpus's re-run
|
|
120
123
|
carries its prior classifications forward automatically — see "Discover is idempotent"
|
|
121
124
|
below), `reorg` continues straight through `apply-reorg` → `lint`, no further stop, so
|
|
@@ -128,7 +131,7 @@ already sorted: nothing new to classify, so the interview gate never fires.
|
|
|
128
131
|
|
|
129
132
|
| Mode | Menu option | Does | Destructive |
|
|
130
133
|
|---|---|---|---|
|
|
131
|
-
| `/docs-builder reorg` (discover, classification interview, confirm, then apply-reorg) | *First run*, steps 1-3 | classify a WHOLE corpus into product/logs/archive | no (moves are `git mv`, plan classified and reviewed first) |
|
|
134
|
+
| `/docs-builder reorg` (discover, classification interview, confirm, then apply-reorg) | *First run*, steps 1-3 | classify a WHOLE corpus into product/wiki/logs/archive | no (moves are `git mv`, plan classified and reviewed first) |
|
|
132
135
|
| `/docs-builder cleanup <file>` | *First run*, step 3's split question | measure ONE named oversized doc (cost, scan, heading shape) → **stops for the interview** | no (measure-only; original preserved) |
|
|
133
136
|
| `/docs-builder reorg` (bare `docs-builder.cjs reorg`) | *Docs drift* | due's drift summary (if a ledger stamp exists) + discover → (stops here if anything is still unclassified) → apply-reorg → lint, whole corpus | no |
|
|
134
137
|
| `/docs-builder search <query words...>` | *(none — explicit-argument mode only, never offered in the bare picker)* | BM25-rank sections of `docs/.docs-builder/outline.json` against the query, read-only | no |
|
|
@@ -152,16 +155,31 @@ docs/
|
|
|
152
155
|
README.md entry point, referenced from AGENTS.md
|
|
153
156
|
index.md GENERATED by index-flat/apply-reorg/cleanup-apply. never hand-edited.
|
|
154
157
|
READER-FACING. The WHOLE-CORPUS map — the only file with a completeness
|
|
155
|
-
guarantee. ## Product, ## Logs, ## Archive.
|
|
158
|
+
guarantee. ## Product, ## Logs (grouped by subdir), ## Archive.
|
|
156
159
|
log.md append-only: ## [DATE] operation | description — written by
|
|
157
160
|
`archive`, `apply-reorg`, `validate`, and `reorg`; NOT written by
|
|
158
161
|
read-only commands (`due`, `search`, `discover`).
|
|
159
|
-
product/
|
|
160
|
-
(`git mv`);
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
162
|
+
product/ docs ABOUT THE PRODUCT ITSELF — specs, PRDs, designs, guides for this
|
|
163
|
+
product. FLAT, no subdirs. `apply-reorg` MOVES files here (`git mv`);
|
|
164
|
+
content is never rewritten. Re-checked on EVERY reorg (files sitting here
|
|
165
|
+
are re-classified along with everything else — agents litter buckets
|
|
166
|
+
over time).
|
|
167
|
+
wiki/ GENERIC knowledge for the whole repo, not product-specific — conventions,
|
|
168
|
+
how-tos, standards, reference. FLAT, no subdirs. Also where Mode 1
|
|
169
|
+
(`cleanup`)'s split page writers put synthesised pages. Re-checked every
|
|
170
|
+
reorg, same as product/.
|
|
171
|
+
logs/ ONE-TIME, specific, timely knowledge tied to one section/scenario/case —
|
|
172
|
+
POCs, experiments, investigations, incident/session write-ups, reports. The
|
|
173
|
+
ONLY bucket that may nest, ONE level: `docs/logs/<group>/*.md` — the group
|
|
174
|
+
is the FIRST path segment under `docs/` (a special subfolder is one
|
|
175
|
+
self-explanatory group, e.g. every POC under `docs/fwd/` stays together as
|
|
176
|
+
`fwd`, however deep a file actually sits inside it), UNLESS that segment is
|
|
177
|
+
itself a bucket name (`product`/`wiki`/`archive` → flat; `logs` → the group
|
|
178
|
+
is the SECOND segment instead). A file with no first segment — loose at the
|
|
179
|
+
repo root, or directly under `docs/` — stays flat. Re-checked every reorg,
|
|
180
|
+
same as product/wiki/.
|
|
181
|
+
archive/ what got cleaned up: self-declared dead. FROZEN — never re-checked, never
|
|
182
|
+
walked by a bare `discover`/`reorg` at all. Originals are BYTE-FROZEN:
|
|
165
183
|
nothing under here is ever a rewrite target, so a doc lands byte-identical
|
|
166
184
|
to what it carried in (a clean R100 rename) and stays that way. Links
|
|
167
185
|
elsewhere POINTING AT it are still repaired. History via `git mv`.
|
|
@@ -199,14 +217,19 @@ moved the problem (it then clobbered `outline.json` across concurrent splits ins
|
|
|
199
217
|
**the themed index was removed outright, 2026-08-24**. One index, rebuilt on every reorg and
|
|
200
218
|
after every split, is the whole design.
|
|
201
219
|
|
|
202
|
-
**Never moved — enforced in code, not just documented** (`PROTECTED_NAMES` / `
|
|
220
|
+
**Never moved — enforced in code, not just documented** (`PROTECTED_NAMES` / `isProtectedName` /
|
|
221
|
+
`walkMd`):
|
|
203
222
|
|
|
204
|
-
- **Files, at any depth:** `README.md`, `index.md`, `log.md`,
|
|
205
|
-
`
|
|
206
|
-
|
|
223
|
+
- **Files, at any depth, matched CASE-INSENSITIVELY:** `README.md`, `index.md`, `log.md`,
|
|
224
|
+
`CHANGELOG.md`, `LICENSE.md`, `CONTRIBUTING.md`, `CODE_OF_CONDUCT.md`, `SECURITY.md`,
|
|
225
|
+
`CLAUDE.md`, `AGENTS.md`, `AGENT.md` — so `readme.md`, `Claude.md`, `changelog.md`,
|
|
226
|
+
`agents.md`, etc. are protected too, not just their exact-case forms. Bare `LICENSE`/`NOTICE`
|
|
227
|
+
have no `.md` extension, so the walker never sees them.
|
|
207
228
|
- **Directories:** every dot-dir (`.git/`, `.github/`, `.claude/`, `.factory/`, `.opencode/`,
|
|
208
|
-
`.amp/`, `.docs-builder/`) plus `node_modules
|
|
209
|
-
|
|
229
|
+
`.amp/`, `.docs-builder/`) plus `node_modules/`. `archive/` is skipped unconditionally by a
|
|
230
|
+
bare `discover`/`reorg` (frozen, never re-checked); `product/`, `wiki/` and `logs/` are
|
|
231
|
+
entered and re-checked on that same bare run — only an explicit `discover <dir>` naming one
|
|
232
|
+
of the reserved names directly bypasses this at all four.
|
|
210
233
|
|
|
211
234
|
---
|
|
212
235
|
|
|
@@ -226,11 +249,31 @@ purpose, not silently dropped.
|
|
|
226
249
|
### 1. Discover (script) — enriches and PROPOSES, never classifies, never moves
|
|
227
250
|
|
|
228
251
|
```bash
|
|
229
|
-
node $DB discover #
|
|
252
|
+
node $DB discover # root-level .md files (top level only) + everything under docs/
|
|
253
|
+
node $DB discover <dir> # scopes to exactly <dir> instead — e.g. a directory outside docs/
|
|
230
254
|
```
|
|
231
255
|
|
|
232
|
-
|
|
233
|
-
|
|
256
|
+
**Scan scope with no argument is deliberately narrow:** (a) `.md` files sitting directly at
|
|
257
|
+
the repo root (top level, not recursive) and (b) everything under `docs/` (recursive, entering
|
|
258
|
+
`product/`, `wiki/` and `logs/` — only `docs/archive/` stays frozen and skipped). Every other
|
|
259
|
+
`.md` file anywhere else in the repo is out of scope entirely — never listed, never moved,
|
|
260
|
+
whether or not `docs/` exists. `PROTECTED_NAMES` still applies at the repo root (case-
|
|
261
|
+
insensitively — see Layout above), so `README.md`, `CLAUDE.md`, `CHANGELOG.md`, `AGENTS.md`,
|
|
262
|
+
`AGENT.md`, `LICENSE.md`, `CONTRIBUTING.md`, `CODE_OF_CONDUCT.md`, `SECURITY.md`, `index.md`
|
|
263
|
+
and `log.md` are never planned or moved. `ROOT=` is NOT read by this script — setting it
|
|
264
|
+
prints a `WARN`; pass the folder as the argument.
|
|
265
|
+
|
|
266
|
+
**Because `product/`, `wiki/` and `logs/` are re-checked every bare run,** a file already
|
|
267
|
+
sitting in one of them is a REAL row too, not folded away — its `suggested` bucket defaults to
|
|
268
|
+
wherever it already sits (a stronger signal, like a filename token or a SHOUTED status word,
|
|
269
|
+
can still override that). It still needs `bucket` filled by the interview like any other row
|
|
270
|
+
(even if the answer is "yes, stays put") — that settles after one classify+apply pass and
|
|
271
|
+
carries forward from then on, same as everything else. An explicit `discover <dir>` still
|
|
272
|
+
scopes to exactly that one directory, unaffected — useful for anything outside the default
|
|
273
|
+
root+docs/ coverage.
|
|
274
|
+
|
|
275
|
+
Recursively finds every in-scope `*.md` (skipping `.docs-builder/` and the protected files, at
|
|
276
|
+
any depth in scope), and for each one writes a row with:
|
|
234
277
|
|
|
235
278
|
- `h1` and a short `snip` (first ~200 chars of body, fence-masked) — reused straight from the
|
|
236
279
|
same `headings()`/`snippet()`/`fenceMask()` parsers `scan` uses, no second extraction path.
|
|
@@ -239,14 +282,22 @@ Recursively finds every `*.md` under the root (skipping `wiki/`, `logs/`, `archi
|
|
|
239
282
|
*sorted* — it is no longer a bucket.
|
|
240
283
|
- `suggested` + `reason` — a mechanical PRIOR, never a verdict:
|
|
241
284
|
|
|
285
|
+
Checked in this order — the first rule that matches wins:
|
|
286
|
+
|
|
242
287
|
| suggested | rule |
|
|
243
288
|
|---|---|
|
|
244
289
|
| `archive` | path already under `archive/old/reports/phases`, **or** the doc's own opening declares a SHOUTED status word (`CLOSED`, `DEPRECATED`, `SUPERSEDED`, `WITHDRAWN`, `RETRACTED`, `REFUTED`, `ARCHIVAL`, `ARCHIVED`), **or** the filename matches an archive-shaped prefix (`REPORT`, `STATUS`, `SUMMARY`, `FIX_`, `PHASE_`, `SPRINT_`, `DRAFT`, `WIP`, `OLD`, `TEMP` followed by `-` or `_`) |
|
|
245
290
|
| `logs` | filename carries an experiment-record token — `PREREG`, `LEARNINGS`, `REPORT`, `RESULTS`, `POSTMORTEM`, `RETRO` (case-sensitive, word-boundary, checked ONLY after the archive rules above, so a `REPORT-old.md` still reads as archive, not logs) |
|
|
246
|
-
|
|
|
291
|
+
| *(residency)* | already resident under `docs/product/`, `docs/wiki/` or `docs/logs/` — its own current bucket is its prior (a stronger signal above can still override, e.g. a product-resident doc now shouting `DEPRECATED`) |
|
|
292
|
+
| `archive`/`logs`/`wiki` | a WEAKER, case-insensitive secondary prior read from the doc's own **H1 + first 3 H2s** — e.g. "Postmortem"/"Retrospective"/"Investigation" → `logs`; "deprecated"/"retired"/"superseded" → `archive`; "Conventions"/"How-to"/"Style Guide"/"Glossary" → `wiki`. Deliberately narrow: bare "guide"/"reference" were tried and DROPPED after false-positiving on an ordinary doc plainly titled "Guide". |
|
|
293
|
+
| `product` | has an H1, no archive/logs/wiki signal — the default when nothing else applies |
|
|
247
294
|
| `product` | no H1, but an **include stub** — its whole non-blank content (≤3 lines) is nothing but include directives (mkdocs `--8<--`, `{% include %}`, `{{ .. }}`, `<!-- include -->`) and/or markdown links | a live pointer, not an unknown doc — real-world miss: uv's `docs/reference/contributing.md` |
|
|
248
295
|
| `product` | no H1 at all, and not an include stub — no strong signal either way; the interview decides, same as any other row |
|
|
249
296
|
|
|
297
|
+
`suggested` never proposes `wiki` from a residency check alone reaching further than its own
|
|
298
|
+
match — the heuristic is only ever a prior; the interview decides the real `bucket`, including
|
|
299
|
+
routing a doc INTO `wiki` from anywhere.
|
|
300
|
+
|
|
250
301
|
- `bucket` — **empty on any row discover has not classified before** (see carry-forward
|
|
251
302
|
below; a re-run keeps a bucket the interview already set). This is the field the
|
|
252
303
|
classification interview (step 2) fills, and the ONLY field `apply-reorg` reads to decide
|
|
@@ -305,7 +356,7 @@ was the silent move, not the judgement.
|
|
|
305
356
|
|
|
306
357
|
1. Read `docs/.docs-builder/reorg-plan.json`. Feed the model the WHOLE table — `file`, `h1`,
|
|
307
358
|
`snip`, `lines`, `suggested`+`reason` — **in one call**, and have it fill `bucket` for
|
|
308
|
-
every row (`product`/`logs`/`archive`) with a one-line reason. `suggested` is a PRIOR the
|
|
359
|
+
every row (`product`/`wiki`/`logs`/`archive`) with a one-line reason. `suggested` is a PRIOR the
|
|
309
360
|
model is shown, never an authority over it — but a SHOUTED self-declared status
|
|
310
361
|
(`**Status: CLOSED**`) is near-conclusive for `archive` regardless of what the mechanical
|
|
311
362
|
prior says.
|
|
@@ -345,9 +396,15 @@ message, not a crash — so nothing can move on an unreviewed plan. A plan from
|
|
|
345
396
|
version (`bucket: 'oversized'` or `'review'`, both gone from the schema) is refused too, with
|
|
346
397
|
a pointer to re-run `discover`.
|
|
347
398
|
|
|
348
|
-
- `product` → verified `git mv` to `docs/product/<basename>`
|
|
349
|
-
- `
|
|
350
|
-
- `
|
|
399
|
+
- `product` → verified `git mv` to `docs/product/<basename>` (always flat)
|
|
400
|
+
- `wiki` → verified `git mv` to `docs/wiki/<basename>` (always flat)
|
|
401
|
+
- `logs` → verified `git mv` to `docs/logs/<basename>` (loose) **or**
|
|
402
|
+
`docs/logs/<group>/<basename>` — the ONE bucket that may nest, ONE level, where the group is
|
|
403
|
+
the file's FIRST path segment under `docs/` (unless that segment is itself a bucket name —
|
|
404
|
+
see the Layout section above). A deeper path inside the same special subfolder joins the
|
|
405
|
+
SAME group (no ratchet on a re-check either).
|
|
406
|
+
- `archive` → verified `git mv` to `docs/archive/<basename>` (always flat, and frozen — never
|
|
407
|
+
re-checked again once there)
|
|
351
408
|
- **Oversized files move too — size decides splittable, not sorted.** No bucket is exempt.
|
|
352
409
|
After the move, every oversized row is printed as a follow-up list at its NEW path, one
|
|
353
410
|
`cleanup <path> (N lines)` line per file — run `cleanup` (Mode 1, below) on each, by hand,
|
|
@@ -366,20 +423,10 @@ a pointer to re-run `discover`.
|
|
|
366
423
|
static — it never varies with row count, so a re-run rewrites identical bytes. Idempotent:
|
|
367
424
|
an existing block is replaced in place, never duplicated; other content is left alone.
|
|
368
425
|
The target is `CONFIG=` (default `CLAUDE.md`); this package uses `CONFIG=AGENTS.md`.
|
|
369
|
-
- **The moves land STAGED in your git index —
|
|
370
|
-
rename immediately (that is what preserves history), so when `apply-reorg` returns the
|
|
371
|
-
is holding N staged renames
|
|
372
|
-
|
|
373
|
-
a closing advisory naming the counts and a copy-pasteable recipe. **Run that recipe
|
|
374
|
-
VERBATIM. Do NOT hand-edit it, and do NOT stage by hand instead.** If it looks incomplete
|
|
375
|
-
or names a path that errors, that is a BUG in the recipe — stop and report it to the user;
|
|
376
|
-
do not silently repair it and move on. OBSERVED, real (privcloud first field run): the
|
|
377
|
-
recipe omitted `docs/log.md`, the operator quietly added it by hand, and the bug only
|
|
378
|
-
surfaced because they were later asked for near-misses — a silent repair is a lost bug
|
|
379
|
-
report. Do NOT scope the commit to `docs` alone either: the renames are staged, but the
|
|
380
|
-
inbound-link rewrites are UNSTAGED and reach outside `docs/` (`src/`, `scripts/`, `tests/`,
|
|
381
|
-
`README.md`). Both belong in ONE commit, or you ship moved files whose links were never
|
|
382
|
-
repaired. The tool never auto-commits, by design.
|
|
426
|
+
- **The moves land STAGED in your git index — nothing is committed for you.** `git mv` stages
|
|
427
|
+
each rename immediately (that is what preserves history), so when `apply-reorg` returns the
|
|
428
|
+
repo is holding N staged renames plus unstaged link rewrites. `apply-reorg` prints a closing
|
|
429
|
+
commit advisory; see "Finishing a run" below for the actual commit flow.
|
|
383
430
|
- A basename collision (two files, same name, different original folders) is
|
|
384
431
|
disambiguated (`-2`, `-3`, …); a collision with a **file that already exists at the
|
|
385
432
|
destination** is skipped, logged, and does not stop the rest of the run.
|
|
@@ -390,7 +437,9 @@ a pointer to re-run `discover`.
|
|
|
390
437
|
touches — is never removed. Only directories THIS run emptied are candidates; a dir that
|
|
391
438
|
happened to already be empty before this run started is not this tool's to remove.
|
|
392
439
|
- **After every move, `apply-reorg` re-scans the whole corpus** — `docs/product/`,
|
|
393
|
-
`docs/logs/`, and `docs/archive/` all
|
|
440
|
+
`docs/logs/`, and `docs/archive/` all (`docs/wiki/` is excluded, same as any other
|
|
441
|
+
`PAGES` dir — it holds synthesised pages, not source docs to reorg) — straight into
|
|
442
|
+
`outline.json`, the database `search`
|
|
394
443
|
reads. Not a hint, not opt-in: it runs every time, even when nothing moved this run (e.g.
|
|
395
444
|
re-running on a corpus already sorted from a previous pass). Measured bug this closes: on a
|
|
396
445
|
real 37-doc corpus, `outline.json` used to hold records for only the 12 files a split had
|
|
@@ -399,6 +448,47 @@ a pointer to re-run `discover`.
|
|
|
399
448
|
content, so a pre-move scan would just be redone), and reuses the same `scan` used
|
|
400
449
|
everywhere else in this pipeline — no second scanner, no second outline format.
|
|
401
450
|
|
|
451
|
+
### Finishing a run — the commit flow, run by hand every time
|
|
452
|
+
|
|
453
|
+
`apply-reorg`/`archive`/`cleanup-apply` never auto-commit, by design. Each run's staged renames
|
|
454
|
+
and unstaged link rewrites (in `.md` files only, but anywhere — e.g. the root `README.md`)
|
|
455
|
+
belong in ONE commit, including the
|
|
456
|
+
`AGENTS.md` docs-pointer block `apply-reorg` writes — it is in `commit-files.txt` along with
|
|
457
|
+
everything else. Run these steps literally, in order:
|
|
458
|
+
|
|
459
|
+
1. `git branch --show-current`. If it prints `main` or `master`, do **NOT** commit — tell the
|
|
460
|
+
user the files are ready and to switch to a branch first.
|
|
461
|
+
2. `cat docs/.docs-builder/commit-files.txt` to see exactly what this run touched, and
|
|
462
|
+
`cat docs/.docs-builder/commit-dirty.txt` to see which of those files already carried the
|
|
463
|
+
operator's own uncommitted edits BEFORE this run — pathspec can't split hunks, so committing
|
|
464
|
+
the file commits that edit too. Then ask with `AskUserQuestion`, header `Commit`: if
|
|
465
|
+
`commit-dirty.txt` lists anything, **"Commit these N files now? Note: these files also
|
|
466
|
+
carry your own uncommitted edits, which will be committed too: …"** (name them); otherwise
|
|
467
|
+
**"Commit these N files now?"** — **Commit** (run the printed recipe) / **Leave uncommitted**
|
|
468
|
+
(say what is pending; nothing this run did gets undone).
|
|
469
|
+
3. On **Commit**, run the printed recipe line EXACTLY as printed:
|
|
470
|
+
```
|
|
471
|
+
git add --pathspec-from-file=docs/.docs-builder/commit-add.txt && git commit -m "docs: reorg" --pathspec-from-file=docs/.docs-builder/commit-files.txt
|
|
472
|
+
```
|
|
473
|
+
(when `REPO` isn't the shell's cwd, both commands are printed as `git -C '<REPO>' …` instead
|
|
474
|
+
— run that form, unmodified). Do not hand-edit it, do not stage by hand instead, and do not
|
|
475
|
+
scope it to `docs/` alone — a `.md` outside `docs/` (e.g. `README.md`) can carry a repaired
|
|
476
|
+
link. If it errors or names a path that doesn't exist, that is a BUG: stop and report it to
|
|
477
|
+
the user; do not silently hand-repair and move on.
|
|
478
|
+
OBSERVED, real (privcloud first field run, pre-dating the pathspec-file recipe): a
|
|
479
|
+
hand-rolled recipe once omitted `docs/log.md` and the operator quietly added it by hand —
|
|
480
|
+
the bug only surfaced later, when asked for near-misses. A silent repair is a lost bug
|
|
481
|
+
report.
|
|
482
|
+
4. After a successful commit, run `node $DB ledger` to stamp the consolidation — nothing in
|
|
483
|
+
`discover`/`apply-reorg`/`archive`/`cleanup-apply` stamps it for you, and without the stamp
|
|
484
|
+
`due` stays NOT due, the picker's verdict stays uninformed, and `/remember`'s docs nudge
|
|
485
|
+
never fires.
|
|
486
|
+
|
|
487
|
+
Add `docs/.docs-builder/` to `.gitignore` before the first commit if it is not already
|
|
488
|
+
ignored — it is machine state, regenerated every run, and the ledger stamp is per-clone by
|
|
489
|
+
design. Never use `git add -A` / `git add -u` / `git commit -a` for this: either would ALSO
|
|
490
|
+
absorb any unrelated in-flight work in the tree — OBSERVED TWICE, in two different repos.
|
|
491
|
+
|
|
402
492
|
**After each move it repairs the paths that move just broke** — the whole point of doing this
|
|
403
493
|
in a script. Both movers (`apply-reorg` and `archive`) go through ONE function, `moveDoc`, so
|
|
404
494
|
the follow-up list cannot be added to one and missed by the other; that exact miss shipped
|
|
@@ -410,8 +500,12 @@ reported as a file that needs re-moving. Two follow-ups:
|
|
|
410
500
|
(`records[].file`, and the `<file> :: ` prefix inside every key). This is the same
|
|
411
501
|
function `archive` calls; `apply-reorg` used to bypass it, which silently invalidated
|
|
412
502
|
every key of every file it moved. Both now reach it through `moveDoc`.
|
|
413
|
-
2. **Inbound links** — every git-tracked `.md
|
|
414
|
-
|
|
503
|
+
2. **Inbound links** — every git-tracked `.md` file, anywhere in the repo, that points at the
|
|
504
|
+
old path (repo-rooted, e.g. `docs/GUIDE.md`) is rewritten to the new one. Only `.md` files
|
|
505
|
+
are ever opened, read, or edited — this is a DOCS tool, not a repo-wide text rewriter.
|
|
506
|
+
FIELD BUG, real (bareloop, 2026-09-10): the previous version also scanned
|
|
507
|
+
`.js`/`.cjs`/`.mjs`/`.json`/`.yml`, and rewrote 6 signed JSON job specs (breaking their
|
|
508
|
+
hashes), a byte-signed `.mjs` close script, and a code comment that tripped a commit gate.
|
|
415
509
|
In `.md` files specifically, a RELATIVE link is also caught: inside actual markdown link
|
|
416
510
|
syntax only (`[text](../concepts/x.md)` or a reference-style `[label]: ./tools.md`), never
|
|
417
511
|
bare prose, the target is resolved against the SCANNING file's own directory, and — if it
|
|
@@ -446,13 +540,14 @@ predicate, `isRewriteExempt`, at one call site. `docs/.docs-builder/` is exclude
|
|
|
446
540
|
|
|
447
541
|
**A known, deliberate trade-off: this is a literal exact-path match over raw file bytes, not
|
|
448
542
|
fence-aware or context-aware.** It rewrites every exact, word-bounded occurrence of the old path
|
|
449
|
-
in every git-tracked `.md
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
543
|
+
in every git-tracked `.md` file (except the two exemptions above) — inside a code fence, inside
|
|
544
|
+
a sentence describing history ("this used to live at docs/OLD.md"), anywhere. A prose mention of
|
|
545
|
+
where a file *used to be* WILL be rewritten to say where it is now, changing what the sentence
|
|
546
|
+
says. This is intentional, not an oversight: a dead link is worse than a reworded sentence, the
|
|
547
|
+
match is exact rather than inferred (unlike the dangling-reference *lint*, which infers and was
|
|
548
|
+
cut outright at 1/27 precision), and every rewrite is printed per file so it is visible, never
|
|
549
|
+
silent. Only `.md` files are ever in scope, so this trade-off never touches non-doc files —
|
|
550
|
+
see "Inbound links" above.
|
|
456
551
|
|
|
457
552
|
---
|
|
458
553
|
|
|
@@ -743,14 +838,16 @@ node $DB index-flat
|
|
|
743
838
|
Writes **one** `docs/index.md` covering the whole corpus, in three sections: `## Product`
|
|
744
839
|
(one row per file under `docs/product/`, plus any pages under `PAGES` — default `docs/wiki/`
|
|
745
840
|
— if they exist, plus any doc still sitting in place elsewhere), `## Logs` (one row per file
|
|
746
|
-
under `docs/logs
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
841
|
+
under `docs/logs/`, **grouped by its one-level subdir** — a loose `docs/logs/x.md` row is
|
|
842
|
+
listed ungrouped first, then each subdir gets its own `### <subdir>/` header with its files
|
|
843
|
+
under it, subdirs sorted alphabetically), and `## Archive` (one row per file under
|
|
844
|
+
`docs/archive/`). Each row (product or logs, grouped or not) is an H1 title, a line count, and
|
|
845
|
+
a link, plus one indented line per H2 heading (in document order) so an agent can find and
|
|
846
|
+
slice-read a section without opening the doc — each H2 line carries its own `(Lstart–end)`
|
|
847
|
+
line range, reusing the SAME `headings()`+`fenceMask()` boundaries `scan` already writes to
|
|
848
|
+
`outline.json` (no second parser). Omitted when the doc has no H2s. `## Archive` rows are
|
|
849
|
+
H1-only, never H2 lines — an archived doc is frozen history, not a live section to route into.
|
|
850
|
+
No theme grouping, no `labels.json`, no model call.
|
|
754
851
|
Default destination `docs/index.md` — **the only writer of that default path** in this whole
|
|
755
852
|
pipeline (nothing else writes an index at all).
|
|
756
853
|
`search` reads `outline.json`, never `index.md`. Prints the row counts and records a `log.md`
|
|
@@ -771,7 +868,8 @@ v3 folds the old `reconcile` and `due` commands into one: "first run" (nothing s
|
|
|
771
868
|
state, and two separate commands only made users guess which one to run.
|
|
772
869
|
|
|
773
870
|
```bash
|
|
774
|
-
node $DB reorg
|
|
871
|
+
node $DB reorg # root-level .md files (top level only) + everything under docs/
|
|
872
|
+
node $DB reorg <dir> # re-checks every doc already inside <dir> — passes <dir> to discover
|
|
775
873
|
```
|
|
776
874
|
|
|
777
875
|
If a ledger stamp exists (see "Knowing when reorg is due" below), its `due`-style drift
|
|
@@ -123,19 +123,49 @@ confirmed in Phase 0.5.
|
|
|
123
123
|
summarize, escalate. Do not weigh it yourself.
|
|
124
124
|
- **All clean** → continue.
|
|
125
125
|
|
|
126
|
-
## Phase 2 — Docs sweep
|
|
127
|
-
Update what this
|
|
128
|
-
|
|
129
|
-
`docs/index.md` when the project has one to find what exists.
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
126
|
+
## Phase 2 — Docs sweep (required — no skipping, no sampling)
|
|
127
|
+
Update what this branch changed, wherever those docs live in this project —
|
|
128
|
+
match each file's existing format, touch nothing unrelated. Use
|
|
129
|
+
`docs/index.md` when the project has one to find what exists. **All three
|
|
130
|
+
passes run on every release**; the size of the branch or the change never
|
|
131
|
+
cuts one.
|
|
132
|
+
|
|
133
|
+
1. **List every change.** Read `git log --format='%h %s%n%b'
|
|
134
|
+
origin/main..HEAD` — the bodies, not just the subjects — and the diff.
|
|
135
|
+
Write one line per user-visible change: feature, command, flag, behaviour,
|
|
136
|
+
fix, dependency bump. A subject is a summary; the body is the list, and a
|
|
137
|
+
sweep built from subjects drops whatever only a body mentions.
|
|
138
|
+
2. **CHANGELOG.md** — a new entry holding every line from pass 1, each under
|
|
139
|
+
the heading the file already uses for its kind (Added / Changed / Fixed /
|
|
140
|
+
Security). A new capability is **Added** even when it shipped in a `fix:`
|
|
141
|
+
commit. Then check it back: every pass-1 line maps to an entry, and the
|
|
142
|
+
semver level in Phase 3 agrees with the headings (an Added entry means at
|
|
143
|
+
least minor).
|
|
144
|
+
3. **Grep for stale text.** For every string the diff removed or replaced
|
|
145
|
+
that a reader might have copied — a command line, flag, file name, recipe,
|
|
146
|
+
env var, printed message — search the docs for the old form:
|
|
147
|
+
`grep -rnF "<old string>" --include='*.md' .` (past CHANGELOG entries are
|
|
148
|
+
history; leave them). Every other hit is stale **because of this branch** —
|
|
149
|
+
update it. "It was already stale before this branch" is a claim: prove it
|
|
150
|
+
with `git show $(git merge-base origin/main HEAD):<source path>`. If the
|
|
151
|
+
base code already disagreed with the doc, report it as out of scope;
|
|
152
|
+
otherwise it is yours to fix.
|
|
153
|
+
|
|
154
|
+
Then judge each of these against the pass-1 list:
|
|
155
|
+
- **README.md** — if user-facing usage changed.
|
|
133
156
|
- **PRD** — the feature's entry / status.
|
|
134
157
|
- **Guide / context docs** — the project's standing context.
|
|
135
158
|
- **Findings / learnings** — where the project keeps them.
|
|
136
159
|
- **Any other frequently-updated doc** this change makes stale.
|
|
137
160
|
|
|
138
|
-
|
|
161
|
+
Report **one row per doc**: file · changed / no change · the evidence (the
|
|
162
|
+
grep or diff you ran). A "no change" with no evidence is a skip, and a skip
|
|
163
|
+
fails this phase — it is not a pass. If a doc truly needs no change, say so
|
|
164
|
+
with the evidence rather than editing it for its own sake.
|
|
165
|
+
|
|
166
|
+
**The sweep is the worker's job, start to finish.** The orchestrator checks
|
|
167
|
+
the report; it does not redo or patch the sweep. A gap it finds goes back to
|
|
168
|
+
the worker, and it counts as a failed sweep, not a small follow-up.
|
|
139
169
|
|
|
140
170
|
## Phase 3 — Cut (local only)
|
|
141
171
|
1. **Version bump** — pick the semver level from the change (patch / minor /
|
|
@@ -20,9 +20,12 @@ These show up in nearly every quickly-built app regardless of stack:
|
|
|
20
20
|
|
|
21
21
|
1. **Secrets in the repo.** Tokens / API keys / `.env` files committed to
|
|
22
22
|
tracked files or anywhere in git history. Verify `.env` is gitignored and
|
|
23
|
-
only a value-less `.env.example` is tracked
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
only a value-less `.env.example` is tracked. Scan ALL history, every
|
|
24
|
+
branch — a range or target never narrows this — with:
|
|
25
|
+
`git log --all -p | grep -nE '(sk-[A-Za-z0-9_-]{16,}|AKIA[0-9A-Z]{16}|gh[pousr]_[A-Za-z0-9]{30,}|xox[abprs]-[A-Za-z0-9-]{10,}|AIza[0-9A-Za-z_-]{35}|-----BEGIN [A-Z ]*PRIVATE KEY)'`
|
|
26
|
+
plus any key shape specific to this project's providers. Report each hit
|
|
27
|
+
as real or a known test fixture. Secrets must load from env / a secret
|
|
28
|
+
store at runtime — never hardcoded, never logged.
|
|
26
29
|
2. **Data-access authorization (tenant isolation).** Every record read or
|
|
27
30
|
written must be scoped to the requesting principal — via DB-level rules
|
|
28
31
|
(RLS / row policies) and/or application-layer ownership checks. Flag any
|
|
@@ -136,6 +136,13 @@ governs **stage 1 only**:
|
|
|
136
136
|
- **high / max** — broader coverage; uncertain findings are allowed, but each
|
|
137
137
|
must be labelled uncertain.
|
|
138
138
|
|
|
139
|
+
**No shortcuts.** The level decides how many findings you report, never which
|
|
140
|
+
checks you run. Every check this file calls required runs at every level —
|
|
141
|
+
never cut or sample one "given the effort level", the branch size, or time. If
|
|
142
|
+
a check truly cannot run, write `NOT RUN: <reason>` for it on the `checks:`
|
|
143
|
+
line of the report and the review record. That is a visible gap, not a
|
|
144
|
+
blocker and not a pass.
|
|
145
|
+
|
|
139
146
|
**Stage 2 (security) always runs full, at every level.** A shallow security
|
|
140
147
|
pass is worse than none — it reads as coverage while missing the class of bug
|
|
141
148
|
that costs the most.
|
|
@@ -185,7 +192,10 @@ carries the reproduction.
|
|
|
185
192
|
location outside the repo; the tree must still be clean at exit. A test that
|
|
186
193
|
passes against both the buggy and the fixed source is a tautology and proves
|
|
187
194
|
nothing. Flag every one you find, and say so explicitly when the tests are
|
|
188
|
-
the branch's only evidence for its claims.
|
|
195
|
+
the branch's only evidence for its claims. **Required, every test file the
|
|
196
|
+
diff adds or changes — one red run per file is enough; checking a sample of
|
|
197
|
+
the files is a skip.** Count them as `fail-first N/M files` on the
|
|
198
|
+
`checks:` line.
|
|
189
199
|
- **Maintainability.** Complexity, naming, duplication — only when material.
|
|
190
200
|
|
|
191
201
|
## Stage 2 — Security (always full)
|
|
@@ -200,7 +210,10 @@ If `security.md` cannot be found, run what you can from the list above and
|
|
|
200
210
|
|
|
201
211
|
This stage is repo- and history-scoped, not diff-scoped: a key committed forty
|
|
202
212
|
commits ago, an unbounded route the diff never touched, or a missing row
|
|
203
|
-
policy on a table the new code now reads are all in scope.
|
|
213
|
+
policy on a table the new code now reads are all in scope. **The review range
|
|
214
|
+
never narrows this stage** — even when you were handed `main..HEAD`, the
|
|
215
|
+
secrets scan covers every commit on every branch (`security.md` item 1 has
|
|
216
|
+
the command).
|
|
204
217
|
|
|
205
218
|
## Stage 3 — Verify (adversarial)
|
|
206
219
|
Findings are claims, not facts. **Try to break each one, not to confirm it** —
|
|
@@ -288,7 +301,10 @@ uncertain).
|
|
|
288
301
|
|
|
289
302
|
Then a coverage line: stage 1 at level `<level>`, stage 2 full, stage 3 —
|
|
290
303
|
each `ran ✓/✗` with its evidence. A stage you did not actually run is a **✗**, never an
|
|
291
|
-
assumed pass.
|
|
304
|
+
assumed pass. Then a `checks:` line for the two checks most often cut short:
|
|
305
|
+
`fail-first N/M files` and `secrets-history all-branches` (or `NOT RUN:
|
|
306
|
+
<reason>` for either). An N below M, or a NOT RUN, is reported as-is — it
|
|
307
|
+
does not block.
|
|
292
308
|
|
|
293
309
|
**Write the review record** to `.opencode/remember/last-review.md`, overwriting
|
|
294
310
|
it. `/release` reads this file; a SHA that lives only in a chat message is
|
|
@@ -307,6 +323,7 @@ level: <low | medium | high | max>
|
|
|
307
323
|
verdict: <ready | blocked>
|
|
308
324
|
date: <YYYY-MM-DD>
|
|
309
325
|
coverage: stage1 <ran|NOT RUN>, stage2 <ran|NOT RUN>, stage3 <ran|NOT RUN>
|
|
326
|
+
checks: fail-first <N/M files|NOT RUN: reason>, secrets-history <all-branches|NOT RUN: reason>
|
|
310
327
|
blockers:
|
|
311
328
|
- <file:line> · <one-sentence claim, no scenario, no suggested fix>
|
|
312
329
|
```
|