Skip to content

docs(tools): placement doctrine for Tools.md, plus the pointer rot the old model left behind - #1707

Open
anikinsasha wants to merge 5 commits into
danielmiessler:mainfrom
anikinsasha:docs/tools-placement-doctrine
Open

docs(tools): placement doctrine for Tools.md, plus the pointer rot the old model left behind#1707
anikinsasha wants to merge 5 commits into
danielmiessler:mainfrom
anikinsasha:docs/tools-placement-doctrine

Conversation

@anikinsasha

Copy link
Copy Markdown

Tools.md tells you how to add a tool, and the instructions no longer match the tree. It says "keep the directory flat - NO subdirectories" while five directory tools ship under LIFEOS/TOOLS/. It says "document here" for every new tool while documenting 21 of the 134 files in that root. And it says nothing about shape — how long an entry may run, or what should never have been in a tool index in the first place.

This replaces that section with a placement doctrine, and fixes the pointer rot the old model left behind.

Five commits, each independently reviewable. The last two are droppable — see below.


1. docs(tools): retire dead deprecated-skill rows and the pointers keeping them alive

The Deprecated Skills list retires six skill names. Five of them — Images, VideoTranscript, VoiceNarration, YouTube, Sensitive — no longer resolve to anything in the tree. What kept them looking live was a set of stale pointers still aimed at the retired skills:

Pointer Aimed at
LIFEOS/TOOLS/AddBg.ts:7,13 "Part of the Images skill" + @see ~/.claude/skills/Images/SKILL.md
LIFEOS/TOOLS/RemoveBg.ts:7 "Part of the Images skill"
LIFEOS/TOOLS/GetTranscript.ts:7,8,11,12 bun ~/.claude/skills/Videotranscript/Tools/GetTranscript.ts
LIFEOS/TOOLS/YouTubeApi.ts:13 bun ~/.claude/skills/YouTube/Tools/YouTubeApi.ts
skills/Art/Workflows/{Mermaid,Visualize×2,Essay}.md "use the Images skill for background removal"

Each now names the tool that actually ships. The four Art lines already carried the correct bun ~/.claude/LIFEOS/TOOLS/RemoveBg.ts command directly underneath — only the label was stale.

ExtractTranscript keeps its row: both targets ship (extract-transcript.py and ExtractTranscript.ts), so the name still resolves and the row does real work. The section now states that as the rule — a retirement row earns its place only while the retired name still resolves somewhere — so the next one is decidable rather than a judgment call.

Verified: the five retired names have zero remaining occurrences under LifeOS/install/.

2. docs: give executed-model verification a single home

The Inference-max Fable-carrier mechanism (subprocess spawn, modelUsage read-back, verifyExecutedModel, the downgrade log) was written out three times — RouterSystem.md § 3, Tools.md § Inference.ts, and the Architecture Router row — and two of the copies already disagreed on how the check works. RouterSystem.md keeps the mechanism, since it sits inside the select-model stage where the behavior belongs. The other two become pointers.

One thing worth flagging, because it looks wrong at a glance: RouterSystem.md opens with a RETIRED banner. That banner retires the classify and route-effort stages. Select-model survived, so the paragraph these pointers aim at is live text inside a doc labeled history. The Tools.md pointer says so explicitly — otherwise the banner reads as "ignore everything here". If you would rather move that paragraph out of the retired doc entirely, say where and I will re-roll this commit.

3. docs(tools): replace Adding New Tools with a placement doctrine

The doctrine covers four things the old section did not:

  • Three tiers with different registration rules. Flat tools are selective here and exhaustive in the generated registry. Directory tools are exhaustive over the adopted set — a directory existing on disk is not adoption, so an instance is never silently out of compliance for carrying a subsystem it has not committed to running. External tools are exhaustive here because nothing else indexes them.
  • What is not a tool, and where it goes instead — hooks, HTTP routes, harness primitives, changelogs, local-deviation inventories, skills. These are the categories that actually accrete into a tool index.
  • One entry template for all three tiers, with a per-tier row list.
  • A ~15-non-blank-line working ceiling. The line count is only the smell test; the structural bans (no archaeology, one entry per tool edited in place, at most one table) are the operative rule. A compliant entry cannot get long, and a long entry is already breaking one of them.

It also folds in the flat-tool header contract, since the generated registry reads those headers, and keeps stock's "don't create a separate skill for a CLI command" closer.

Every pointer in both tables resolves against this tree.

Not in scope: enforcement. This is doctrine a human follows. --audit reports against it; nothing blocks on it.

4. feat(tools): generated flat-tool registrydroppable

This is the half to cut if you want docs-only.

Tools.md documents 21 tools; 134 files sit in the LIFEOS/TOOLS root. The gap is not a documentation backlog — a curated index should be selective — but with only one surface there is nowhere to look up the other 113, and "not in Tools.md" silently reads as "does not exist".

So: Tools.md stays curated, FlatTools.md is generated from each tool's own header and exhaustive over the root.

File Role
hooks/lib/flat-tool-header.ts header parser + contract check + renderer
LIFEOS/TOOLS/ToolsRegistry.ts CLI: write / --check / --stdout / --audit
DOCUMENTATION/Tools/FlatTools.md generated output, committed

Deterministic — no timestamps, stable sort — so regeneration on an unchanged tree is a zero diff and --check works as a staleness gate. Paths resolve via getLifeosDir(), so plugin installs work.

Current output over this tree: 134 tools, 32 headers short of the element contract. Those 32 are pre-existing and untouched by this PR; the registry just makes them addressable by name (ToolsRegistry.ts --audit).

Parser notes, since headers in this tree vary:

  • The #1404 env-normalization preamble is skipped, not read as a header.
  • An ASCII hyphen separates an identity line only when followed by a space, so a usage line like algorithm -m loop -p <ISA> is not mistaken for one.
  • .sh and .py headers parse too (# comments, Python docstrings), so the registry covers the whole root rather than the .ts subset.
  • Class comes from what the header declares — an Install* prefix, a com.lifeos.*.plist schedule, a not a CLI / Consumed by: marker. No hand-maintained lists; a tool reclassifies itself by editing its header.

If you want docs-only, commit 3 references the registry in six places (the § Placement doctrine intro line, the Flat-tool table row, the selective-flat paragraph, the whole § The flat-tool header subsection, and steps 2 and 5 of § Adding a tool). Say the word and I will push a variant of commit 3 with those re-rolled to point at the tool's own header instead.

5. docs(tools): relocate the two sections that are not toolssecondary, separately reviewable

The doctrine says HTTP routes belong to the owning subsystem's doc and harness primitives belong to DelegationSystem.md. Two sections in Tools.md were on the wrong side of that line, so this applies the rule to the file that states it. Nothing is deleted — both move whole:

  • Voice Server API (~44 lines) → NotificationSystem.md § Long-form Narration. /notify is a Pulse route, and NotificationSystem.md already documented the same endpoint for notifications.
  • Monitor + 6 recipes (~90 lines) → DelegationSystem.md § Async Primitives. Monitor is a Claude Code harness primitive this repo does not ship. Async Primitives already carried the Monitor rules nearly verbatim, so only the recipe cookbook is new content there; the duplicated rule list is dropped rather than moved twice.

Tools.md keeps a one-line stub at each site pointing to the new home.


Two things the doctrine surfaces that this PR does not fix

  • Five directory tools carry no section in the indexConveyor, ISARender, TokenXray, healthsync, llcli. Under the adopted-set rule that is your call to make, not mine, and three of the five have no README or ISA to point a Depth row at, so I did not want to invent entries. Happy to add pointer sections for whichever you consider adopted.
  • skills/CreateSkill/SKILL.md:302 routes utility wrappers to LIFEOS/TOOLS.md, which does not exist (the file is LIFEOS/DOCUMENTATION/Tools/Tools.md). Same class of rot as commit 1, different owner — left alone to keep this PR's scope honest.

…ng them alive

The Deprecated Skills list in Tools.md retires six skill names. Five of them
(Images, VideoTranscript, VoiceNarration, YouTube, Sensitive) no longer resolve
to anything in the tree, so the rows are archaeology — and the stale pointers
still aimed at those skills made them look live:

  LIFEOS/TOOLS/AddBg.ts:7,13     -> 'Part of the Images skill' + @see skills/Images/SKILL.md
  LIFEOS/TOOLS/RemoveBg.ts:7     -> 'Part of the Images skill'
  LIFEOS/TOOLS/GetTranscript.ts  -> bun ~/.claude/skills/Videotranscript/Tools/GetTranscript.ts (x4)
  LIFEOS/TOOLS/YouTubeApi.ts:13  -> bun ~/.claude/skills/YouTube/Tools/YouTubeApi.ts
  skills/Art/Workflows/{Mermaid,Visualize,Visualize,Essay}.md -> 'use the Images skill'

Each pointer now names the tool that actually ships. The four Art lines already
carried the correct 'bun ~/.claude/LIFEOS/TOOLS/RemoveBg.ts' command directly
underneath; only the label was stale.

ExtractTranscript keeps its row: both targets ship (extract-transcript.py and
ExtractTranscript.ts), so the name still resolves and the row does real work.
The section now states that rule so the next retirement is decidable.

Verified: the five retired names have zero remaining occurrences under
LifeOS/install/.
The Inference-max Fable-carrier mechanism (subprocess spawn, modelUsage
read-back, verifyExecutedModel, the downgrade log) was written out three times:
RouterSystem.md § 3 Select the model, Tools.md § Inference.ts, and the
Architecture Router row. Three copies drift independently, and two of them
already disagreed on how the check works.

RouterSystem.md keeps the mechanism — it sits inside the select-model stage,
which is where the behavior belongs. The other two become pointers.

Worth naming: RouterSystem.md opens with a RETIRED banner, but that banner
retires the classify and route-effort stages. Select-model survived, so the
paragraph the pointers aim at is live text inside a doc labeled history. The
Tools.md pointer says so, otherwise the banner reads as 'ignore this'.
The old section said: put a .ts or .py file directly in LIFEOS/TOOLS, 'keep
the directory flat - NO subdirectories', document it here. Five directory
tools ship under LIFEOS/TOOLS/ today (Conveyor, ISARender, TokenXray,
healthsync, llcli), so the rule contradicts the tree it governs, and it gives
no guidance at all on shape: what a section should contain, how long it may
run, or what does not belong in this file in the first place.

The doctrine replacing it covers four things the old section did not:

- Three tiers with different registration rules. Flat tools are selective
  here and exhaustive in the generated registry. Directory tools are
  exhaustive over the ADOPTED set — a directory existing on disk is not
  adoption, so an instance is never silently out of compliance for carrying
  a subsystem it has not committed to. External tools are exhaustive here
  because nothing else indexes them.
- What is not a tool, and where it goes instead: hooks, HTTP routes, harness
  primitives, changelogs, local-deviation inventories, skills. These are the
  categories that actually accreted into tool indexes.
- One entry template for all tiers, with a per-tier row list.
- A ~15-non-blank-line working ceiling. The line count is the smell test;
  the structural bans (no archaeology, one entry per tool edited in place,
  at most one table) are the operative rule — a compliant entry cannot get
  long, and a long entry is already breaking one of them.

Also folds in the flat-tool header contract, since the generated registry
reads those headers, and keeps stock's 'don't create a separate skill for a
CLI command' closer.

Every pointer in the two tables resolves against this tree.

Not in scope: enforcement. This is doctrine a human follows; --audit reports
against it, nothing blocks on it.
Tools.md documents 21 tools. 134 files sit in the LIFEOS/TOOLS root. The gap
is not a documentation backlog — a curated index SHOULD be selective — but
without a second surface there is nowhere to look up the other 113, and
'not in Tools.md' silently reads as 'does not exist'.

This splits the two jobs. Tools.md stays curated: the tools whose invocation
needs more than one line. FlatTools.md is generated from each tool's own
leading header and is exhaustive over the root, so absence from the curated
index stops meaning absence from the system.

  hooks/lib/flat-tool-header.ts   header parser + contract check + renderer
  LIFEOS/TOOLS/ToolsRegistry.ts   CLI: write / --check / --stdout / --audit
  DOCUMENTATION/Tools/FlatTools.md  generated output, committed

Deterministic: no timestamps, LC-stable sort, so regeneration on an unchanged
tree is a zero diff and --check works as a staleness gate.

Current output over this tree: 134 tools, 32 headers short of the element
contract. Those 32 are pre-existing and unchanged by this PR — the registry
just makes them addressable (ToolsRegistry.ts --audit lists them by name).

Notes on the parser, since headers in this tree vary:
- The env-normalization preamble from danielmiessler#1404 is skipped, not read as a header.
- An ASCII hyphen only separates an identity line when followed by a space,
  so a usage line like 'algorithm -m loop -p <ISA>' is not mistaken for one.
- .sh and .py headers parse too (# comments, Python docstrings), so the
  registry covers the whole root rather than the .ts subset.
- Class comes from what the header declares: an Install* prefix, a
  com.lifeos.*.plist schedule, or a 'not a CLI' / 'Consumed by:' marker.
  No hand-maintained lists — a tool reclassifies itself by editing its header.

Paths resolve via getLifeosDir(), so plugin installs work.
SECONDARY — separately reviewable, and droppable without touching the rest
of this PR.

The placement doctrine says HTTP routes belong to the owning subsystem's doc
and Claude Code harness primitives belong to DelegationSystem.md. Two sections
in Tools.md were on the wrong side of that line, so this applies the rule to
the file that states it.

Nothing is deleted — both sections move whole:

  Voice Server API (~44 lines)  -> NotificationSystem.md § Long-form Narration.
    /notify is a Pulse route, and NotificationSystem.md already documented the
    same endpoint for notifications; the narration triggers, tuning values and
    450-char segment rule join it there.

  Monitor + 6 recipes (~90 lines) -> DelegationSystem.md § Async Primitives.
    Monitor is a harness primitive this repo does not ship. Async Primitives
    already carried the Monitor rules nearly verbatim, so only the recipe
    cookbook is new content there; the duplicated rule list is dropped rather
    than moved twice.

Tools.md keeps a one-line stub at each site pointing to the new home, so a
reader who goes looking where the section used to be still lands correctly.
The Research-skill cross-reference is repointed too.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant