@cleocode/skills 2026.5.111 → 2026.5.113
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/package.json +1 -1
- package/skills/ct-documentor/SKILL.md +221 -1
package/package.json
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: ct-documentor
|
|
3
3
|
description: Documentation coordinator with CLEO style guide compliance. Routes every canonical-doc write (spec, adr, research, handoff, note, llm-readme) through the docs SSoT via `cleo docs add` / `cleo docs publish` / `cleo docs fetch` — never raw filesystem writes. Coordinates ct-docs-lookup, ct-docs-write, ct-docs-review, ct-spec-writer, and ct-adr-recorder. Use when creating or updating documentation files, consolidating scattered documentation, or validating documentation against style standards. Triggers on documentation tasks, doc update requests, or style guide compliance checks.
|
|
4
|
-
version: 3.
|
|
4
|
+
version: 3.11.0
|
|
5
5
|
tier: 3
|
|
6
6
|
core: false
|
|
7
7
|
category: specialist
|
|
8
8
|
protocol: null
|
|
9
|
+
metadata:
|
|
10
|
+
version: 3.11.0
|
|
11
|
+
lastReviewed: 2026-05-24
|
|
12
|
+
stability: stable
|
|
9
13
|
dependencies:
|
|
10
14
|
- ct-docs-lookup
|
|
11
15
|
- ct-docs-write
|
|
@@ -264,6 +268,222 @@ Contract for the docs-add path:
|
|
|
264
268
|
`data.slug`, `data.attachmentId`, and `data.sha256` — round-trip
|
|
265
269
|
identical to what `cleo changeset add` emits.
|
|
266
270
|
|
|
271
|
+
#### CI gate — DocKind Writer Uniqueness (T10369)
|
|
272
|
+
|
|
273
|
+
`scripts/lint-dockind-writer-uniqueness.mjs` (CI job:
|
|
274
|
+
`DocKind Writer Uniqueness (T10369)`) enforces the
|
|
275
|
+
WriterRegistry invariants at PR-time. It refuses to merge a PR that:
|
|
276
|
+
|
|
277
|
+
1. Adds a new entry to `BUILTIN_DOC_KINDS` (in
|
|
278
|
+
`packages/contracts/src/docs-taxonomy.ts`) without a matching
|
|
279
|
+
descriptor in `writer-registry.ts` (`dockind-coverage-missing`).
|
|
280
|
+
2. Declares more than one descriptor for the same DocKind
|
|
281
|
+
(`dockind-coverage-collision` — the registry itself throws at module
|
|
282
|
+
load too, this gate surfaces it earlier in CI).
|
|
283
|
+
3. Has a `mode: 'ssot-first'` descriptor that does NOT match
|
|
284
|
+
`.cleo/canon.yml`'s `canonicalHome` for the same kind, or vice versa
|
|
285
|
+
(`canon-yml-ssot-first-drift`).
|
|
286
|
+
4. Adds a NEW raw `writeFileSync(path.md, …)` / `writeFile(path.md, …)`
|
|
287
|
+
call inside `packages/core/src/**` that is not in
|
|
288
|
+
`.lint-dockind-writer-baseline.json` (`unregistered-md-write`).
|
|
289
|
+
|
|
290
|
+
Schema-parity rules (#1-#3) are ALWAYS strict — there is no baseline.
|
|
291
|
+
The unregistered-md-write rule runs in baseline mode by default; count
|
|
292
|
+
decreases always pass, count increases fail. The two legitimate
|
|
293
|
+
non-DocKind `.md` writers (`packages/core/src/sessions/handoff-markdown.ts`
|
|
294
|
+
for session snapshots and `packages/core/src/changesets/writer.ts` for
|
|
295
|
+
the canonical `changeset` DocKind) are allowlisted in the script.
|
|
296
|
+
|
|
297
|
+
Per-line opt-out (use sparingly): append
|
|
298
|
+
`// dockind-writer-allowed: <reason>` on the writeFile line.
|
|
299
|
+
|
|
300
|
+
#### Audit complement — manual-write sweep (T10372)
|
|
301
|
+
|
|
302
|
+
`scripts/sweep-manual-doc-writes.mjs` (CI job:
|
|
303
|
+
`Manual Write Sweep (T10372)`) is the read-only audit counterpart to
|
|
304
|
+
the writer-uniqueness lint. Where T10369 prevents *new* raw `.md`
|
|
305
|
+
writers from landing in `packages/core/src/**`, this sweep walks every
|
|
306
|
+
`*.md` file *already* added under `.cleo/canon.yml`'s `rawMdPaths`
|
|
307
|
+
directories since the T9791 docs-import cutoff (commit `251814e86`)
|
|
308
|
+
and classifies each one against the docs SSoT:
|
|
309
|
+
|
|
310
|
+
| Remediation | Meaning | Fix |
|
|
311
|
+
|---|---|---|
|
|
312
|
+
| `in-sync` | File SHA matches a blob already in the SSoT — bytes are tracked. | None. |
|
|
313
|
+
| `drift` | Slug exists in SSoT but the on-disk content has changed. | Re-publish via `cleo docs publish` or re-add as a new version. |
|
|
314
|
+
| `orphan` | Neither SHA nor slug resolves — the file is a raw fs write that bypassed `cleo docs add`. | Migrate via `cleo docs add <ownerId> <file> --type <kind> --slug <slug>`. |
|
|
315
|
+
| `deleted` | File was added since the cutoff but no longer exists on disk. | Informational only — does not count toward `unresolved`. |
|
|
316
|
+
|
|
317
|
+
Each run writes a timestamped report to
|
|
318
|
+
`audit/manual-write-sweep-<date>.json` and prints the summary block to
|
|
319
|
+
stdout. The CI job uploads the report as a workflow artefact on every
|
|
320
|
+
run and is wired with `continue-on-error: true` initially so the
|
|
321
|
+
existing orphan corpus does not break PRs. Saga T10288 / Epic T10293
|
|
322
|
+
E5.3 closes the orphan migration; the gate flips strict after that
|
|
323
|
+
lands.
|
|
324
|
+
|
|
325
|
+
```bash
|
|
326
|
+
# Local invocation — uses the globally-installed `cleo` on PATH.
|
|
327
|
+
node scripts/sweep-manual-doc-writes.mjs
|
|
328
|
+
|
|
329
|
+
# CI / monorepo build — point at the just-built local CLI bundle.
|
|
330
|
+
node scripts/sweep-manual-doc-writes.mjs \
|
|
331
|
+
--cleo-bin "node packages/cleo/dist/cli/index.js" \
|
|
332
|
+
--allow-unresolved
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
Exit codes: `0` (clean OR `--allow-unresolved`), `1` (at least one
|
|
336
|
+
`orphan` or `drift` entry), `2` (canon.yml parse failure, git not
|
|
337
|
+
available, SSoT query failed).
|
|
338
|
+
|
|
339
|
+
### T10179 + T10203 manual-write migration (T10371)
|
|
340
|
+
|
|
341
|
+
Saga T10176's two known raw-write workarounds are normalised:
|
|
342
|
+
|
|
343
|
+
| Original file | SSoT slug | Type | Notes |
|
|
344
|
+
|---|---|---|---|
|
|
345
|
+
| `docs/research/t10179-executor-probe-result.md` | `t10179-executor-probe` | research | in-sync via earlier T9791 import — verified by SHA. |
|
|
346
|
+
| `.changeset/t10179-executor-probe.md` (consumed v5.108) | `t10179-changeset-archive` | note | bytes preserved verbatim from git `cc48ca10e`; archived because the pnpm/changesets `"@cleocode/cleo": patch` frontmatter does not satisfy the `changeset` DocKind schema. |
|
|
347
|
+
| `.changeset/t10203-napi-step-exports.md` (consumed v5.108) | `t10203-napi-step-exports` | changeset | in-sync via the `cleo changeset add` dual-write at PR-time. |
|
|
348
|
+
|
|
349
|
+
Round-trip parity is regression-locked by
|
|
350
|
+
`packages/core/src/docs/__tests__/manual-write-migration.test.ts`. The
|
|
351
|
+
test embeds the canonical bytes inline and asserts that
|
|
352
|
+
`createAttachmentStore().put(...) → findBySlug(...)` returns the same
|
|
353
|
+
SHA-256 it started with. Any future migration that silently rewrites or
|
|
354
|
+
recompresses these blobs fails the test.
|
|
355
|
+
|
|
356
|
+
### Sweep-driven remediation loop (T10373)
|
|
357
|
+
|
|
358
|
+
T10371 only covers the *known* manual-write set declared in the original
|
|
359
|
+
Saga T10176 disposition. The T10372 sweep surfaces *every* orphan
|
|
360
|
+
remaining under `rawMdPaths` at the moment it runs. T10373 closes the
|
|
361
|
+
loop by consuming the sweep report and migrating each orphan into the
|
|
362
|
+
SSoT using the same `cleo docs add --slug` pattern T10371 established.
|
|
363
|
+
|
|
364
|
+
The recurring pattern (use this any time the sweep flags fresh
|
|
365
|
+
orphans):
|
|
366
|
+
|
|
367
|
+
1. Run the sweep: `node scripts/sweep-manual-doc-writes.mjs`. The
|
|
368
|
+
report lands at `audit/manual-write-sweep-<date>.json`.
|
|
369
|
+
2. For each `orphan` entry, derive the migration tuple:
|
|
370
|
+
- `--type` from the file's parent directory (`.cleo/adrs/` → `adr`,
|
|
371
|
+
`.cleo/research/` → `research`, `.cleo/agent-outputs/` →
|
|
372
|
+
`handoff` or `note` based on content, `.cleo/rcasd/` → `rcasd`).
|
|
373
|
+
- `--slug` from the filename — lowercase, kebab-case, no
|
|
374
|
+
extension (e.g. `ADR-085-cross-db-invariants.md` →
|
|
375
|
+
`adr-085-cross-db-invariants`).
|
|
376
|
+
- `<owner-id>` from the file's frontmatter `task:` field if
|
|
377
|
+
present, otherwise from `cleo find "<filename-keyword>"`.
|
|
378
|
+
3. Run `cleo docs add <ownerId> <file> --type <kind> --slug <slug>
|
|
379
|
+
--desc "<sweep-remediation context>"`. The `--desc` should
|
|
380
|
+
reference the originating task ID so future operators can trace
|
|
381
|
+
the migration.
|
|
382
|
+
4. Verify via `cleo docs fetch <slug>` and re-run the sweep — the
|
|
383
|
+
`orphan` count MUST drop by the number of files migrated.
|
|
384
|
+
5. Add the new (slug, sha256, type, ownerId) row to a
|
|
385
|
+
round-trip parity test alongside the T10371 set. The canonical
|
|
386
|
+
example lives at
|
|
387
|
+
`packages/core/src/docs/__tests__/sweep-remediation.test.ts`.
|
|
388
|
+
|
|
389
|
+
T10373 migrated five orphans this way: `ADR-083`, `ADR-085`,
|
|
390
|
+
`T10268-saga-closeout`, `t10292-e4-cli-verb-matrix`, and
|
|
391
|
+
`t10292-e4-sdk-import-edges`. The last two were direct fallout from
|
|
392
|
+
the pre-T10389 worktree-unreachable bug — T10353 and T10354 workers
|
|
393
|
+
fell back to raw filesystem writes because `cleo docs add` rejected
|
|
394
|
+
inside their spawned worktrees. Re-publishing the bytes via the SSoT
|
|
395
|
+
proves the round-trip and closes the loop the bug opened.
|
|
396
|
+
|
|
397
|
+
If a sweep run surfaces a file that should genuinely stay as raw
|
|
398
|
+
markdown (e.g. an audit log not meant for SSoT propagation), add an
|
|
399
|
+
entry to `audit/sweep-exemptions.yml` rather than migrating it. The
|
|
400
|
+
sweep script honours exemptions and does not flag them as orphans.
|
|
401
|
+
|
|
402
|
+
### Stuck-saga closure via `cleo saga reconcile` (T10374 · Saga T10288 / Epic T10293)
|
|
403
|
+
|
|
404
|
+
When a Saga's docs-related closeout was completed under the saga's
|
|
405
|
+
member Epics — every Epic flipped to `status='done'` — but the parent
|
|
406
|
+
Saga row itself is still `pending`, the recovery verb is
|
|
407
|
+
`cleo saga reconcile <sagaId>`. This is the cron-safe T10121 path
|
|
408
|
+
that the ADR-076 / T10113 auto-close path delivers; sagas that pre-date
|
|
409
|
+
the auto-close path (T9625 is the canonical example) need an explicit
|
|
410
|
+
nudge.
|
|
411
|
+
|
|
412
|
+
The recipe — use this any time a docs-canon Saga is observably stuck
|
|
413
|
+
even though its members have all shipped via `cleo docs add` /
|
|
414
|
+
`cleo docs publish`:
|
|
415
|
+
|
|
416
|
+
1. Verify member-Epic terminality:
|
|
417
|
+
`for E in <memberIds>; do cleo show $E | jq '.data.task.status'; done`.
|
|
418
|
+
Every member must be `done`, `cancelled`, or `archived` before
|
|
419
|
+
reconcile will close the parent. If any member is genuinely stuck,
|
|
420
|
+
close THAT one first (evidence-based per ADR-051) — do NOT cancel
|
|
421
|
+
a member just to satisfy the gate.
|
|
422
|
+
2. Verify the SSoT fetch-gate the Saga's acceptance gates on (typically
|
|
423
|
+
a research plan or closure note):
|
|
424
|
+
`cleo docs fetch <slug>` — must return `success: true` with the
|
|
425
|
+
expected bytes.
|
|
426
|
+
3. Reconcile: `cleo saga reconcile <sagaId>`. The verb is idempotent
|
|
427
|
+
(re-runs return `action: 'no-op'`) and never modifies member rows.
|
|
428
|
+
4. Confirm: `cleo show <sagaId>` — `status` must be `done` and
|
|
429
|
+
`completedAt` populated. The action is appended to
|
|
430
|
+
`.cleo/audit/saga-reconcile.jsonl` for audit.
|
|
431
|
+
5. Write a closure-evidence handoff via
|
|
432
|
+
`cleo docs add <taskId> <file> --type handoff --slug <saga>-closure-evidence`
|
|
433
|
+
capturing: member statuses (table), reconcile envelope output,
|
|
434
|
+
sibling-saga sanity check (no cross-saga side effects), and
|
|
435
|
+
ADR-076 + T10113 path validation. The slug `t9625-closure-evidence`
|
|
436
|
+
is the canonical reference.
|
|
437
|
+
|
|
438
|
+
Regression coverage for this path lives at
|
|
439
|
+
`packages/core/src/sagas/__tests__/t9625-closure.test.ts` and locks
|
|
440
|
+
three invariants: stuck-saga closure (AC1), sibling-saga isolation
|
|
441
|
+
(AC2), and idempotency (AC3). Add a new case there whenever you close
|
|
442
|
+
another stuck docs-canon Saga so the recovery pattern stays under
|
|
443
|
+
test.
|
|
444
|
+
|
|
445
|
+
### Docs->memory auto-emit (T9976 · regression-tested by T10375)
|
|
446
|
+
|
|
447
|
+
Every successful `cleo docs add` fires a fire-and-forget memory observation
|
|
448
|
+
into `brain_observations`. The CLI never blocks on this write — a BRAIN
|
|
449
|
+
failure cannot fail `docs add` — but the observation is the bridge that
|
|
450
|
+
makes `cleo memory find '<slug>'` surface attached docs.
|
|
451
|
+
|
|
452
|
+
**Title shape**: `"Doc attached: <slug>"` (or `"Doc attached: <attachmentId>"`
|
|
453
|
+
when no slug is provided). This is what the FTS index matches on, so the
|
|
454
|
+
slug is also a memory-discovery key — not just a docs-lookup key.
|
|
455
|
+
|
|
456
|
+
**Narrative payload** (the {@link DocAttachmentObservationPayload} contract):
|
|
457
|
+
|
|
458
|
+
```jsonc
|
|
459
|
+
{
|
|
460
|
+
"kind": "doc-attachment", // discriminator
|
|
461
|
+
"attachmentId": "<id>", // assigned by the docs store
|
|
462
|
+
"ownerId": "<T#### | SG-#### | …>",
|
|
463
|
+
"slug": "<kebab-slug>", // omitted only when --slug not passed
|
|
464
|
+
"type": "<docKind>", // omitted only when --type not passed
|
|
465
|
+
"addedAt": "<ISO 8601 timestamp>"
|
|
466
|
+
}
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
The payload is consumed by `cleo memory verify <observationId>` for
|
|
470
|
+
round-trip checks against the docs store — see AC3 of the original T9976
|
|
471
|
+
suite at `packages/cleo/src/dispatch/domains/__tests__/docs-memory-observation.test.ts`.
|
|
472
|
+
|
|
473
|
+
**Retroactive sweeps**: when migrating manual `Write`-based docs back into
|
|
474
|
+
the SSoT (the T10371 + T10373 pattern), the auto-emit fires uniformly for
|
|
475
|
+
the kebab-case slugs the sweep uses (`t<num>-<kebab>`, `adr-<num>-<kebab>`).
|
|
476
|
+
Regression coverage lives at
|
|
477
|
+
`packages/cleo/src/dispatch/domains/__tests__/docs-memory-observation-retroactive.test.ts`
|
|
478
|
+
(T10375). Add a new case to that table whenever you discover a slug shape
|
|
479
|
+
not yet under test.
|
|
480
|
+
|
|
481
|
+
**Anti-pattern**: do NOT write a `cleo memory observe` manually after a
|
|
482
|
+
`cleo docs add` — the auto-emit already happened, and the duplicate
|
|
483
|
+
observation pollutes the FTS index. Use `cleo memory backfill-docs` (AC4
|
|
484
|
+
of T9976) only to repair attachments that pre-date the auto-emit feature
|
|
485
|
+
or were written outside the SSoT.
|
|
486
|
+
|
|
267
487
|
### Slug similarity warn (T10361 · closes T10167)
|
|
268
488
|
|
|
269
489
|
`cleo docs add` runs a fuzzy-match check against existing slugs for the
|