Personal project · agentic tooling

The map of my skills checks itself

One changed line, caught, classified and fixed.

self-maintaining documentation drift detection ~123s

Filmed 2026-10-23

One line changed in one skill, and the checks behind my skill map caught it.

Exhibit 1What the check said

The map of my skills is one file, and every view of it is generated from that file. Each skill is a text file too. The check compares the two: it keeps a fingerprint of each skill's text from the last time its place on the map was written, and says so when the text has moved on.

The edit. One added line, from git diff.
diff --git a/draft-writer/SKILL.md b/draft-writer/SKILL.md
index acf5762..4204dfb 100644
--- a/draft-writer/SKILL.md
+++ b/draft-writer/SKILL.md
@@ -12,3 +12,4 @@ description: >-
 - Write the first draft from the outline.
 - Use only claims that /fact-check marked as held.
 - Save the draft next to the outline.
+- Run /style-pass on the finished draft before it is laid out.
The plain check. It calls the map coherent and adds one advisory line. Exit 0.
  ~ content-drift: node draft-writer's SKILL.md changed since last synthesized — re-synthesize its edges via /describe-workflow UPDATE (or `workflow.py re-stamp draft-writer` if the change was cosmetic)
VERDICT=COHERENT (1 content-drift advisory; run /describe-workflow UPDATE when convenient, or `check --strict` to enforce)
exit 0
The strict check. The same line is now a failure. Exit 1.
  - content-drift: node draft-writer's SKILL.md changed since last synthesized — re-synthesize its edges via /describe-workflow UPDATE (or `workflow.py re-stamp draft-writer` if the change was cosmetic)
VERDICT=INCOHERENT (1 issue(s))
exit 1
The classifier. It reads the added line and says what kind of change it is: a new thing this skill calls, so a link may be missing. 26 lines of empty sections are left out.
# DRIFT CLASSIFICATION (recall-biased surfacer — narrows the read-set, does NOT replace the read): 1
  - draft-writer [content] COMPOSITION — content unchanged since 3a63275 (2026-10-23)
      new-refs: /style-pass
  ADVICE=Read the COMPOSITION and UNRESOLVED deltas and re-synthesize those nodes; an edge exists where the procedure DISPATCHES at runtime, never where it merely CITES another skill as precedent. COSMETIC rows are re-stamp candidates — the surfacer is recall-biased, so verify before a blind re-stamp.
VERDICT=IN-SYNC
CONTENT-DRIFT=1 (advisory; not folded into VERDICT)
REFERENCE-DRIFT=0 (advisory; not folded into VERDICT)
CLASSIFY=composition:1 cosmetic:0 unresolved:0 (advisory; not folded into VERDICT)
exit 0
The catch, filmed 0:39

What to notice: the plain check and the strict check read the same line and disagree about whether it matters.

Silent. A replay of the recorded run, from the card's film.

The map, before and after 0:36

What to notice: the same skill, searched twice. Three connections before, four after, and the new one reaches into another group.

Silent. The two pages are the engine's own builds of the throwaway model.

Exhibit 2The one judgement step

Nothing in the engine decides that a skill now calls another one. The classifier points at the line and the new name. An agent reads it and edits the model: one link and one sentence. This is the whole edit to the model, as a diff.

The model's diff. 7 lines added, 1 removed, in two places.
synthesized: +1 edge draft-writer -> style-pass (invokes); role extended by one sentence
--- /tmp/sbx.ne7L/workflow.before-synthesis.json	2026-10-23 20:17:32.439936230 -0300
+++ workflow.json	2026-10-23 20:17:32.454363976 -0300
@@ -162,3 +162,3 @@
       "command": "/draft-writer",
-      "role": "Writes a first draft from an outline and the checked sources, in plain paragraphs.",
+      "role": "Writes a first draft from an outline and the checked sources, in plain paragraphs. The finished draft goes through /style-pass before layout.",
       "arguments": [
@@ -564,2 +564,8 @@
       "label": "kept current by"
+    },
+    {
+      "from": "draft-writer",
+      "to": "style-pass",
+      "type": "invokes",
+      "label": "voice pass on the finished draft before layout"
     }

Then the fix is stamped and every view is rebuilt: 4 stages, 6 files written, 41 lines of build notes. The strict check passes again (exit 0).

The generated map before the fix: the skill draft-writer with three connections in its list
Before. 3 connections.
The generated map after the fix: the same skill with four connections, the new one to style-pass
After. 4 connections; the new one reaches the communicate group.

Exhibit 3Who did each step

StepDone byCalls a language model
Notice the text moved (plain and strict check)scriptno
Say what kind of change it is (classifier)scriptno
Decide the skill now calls another, and write the linkagentyes
Stamp the skill, rebuild every view, check againscriptno

"No" is read off the engine's source: 3 files, no model client and no network call (8 patterns searched, 0 hits). The 3 libraries it imports from outside those files (a schema validator and two helpers for other commands) were not searched.

What this does not show.

  • The skills in the film are invented: 15 of them in a throwaway folder, with a copy of the engine's own model format. My real map has 314 nodes. The numbers on this page are the sandbox's.
  • The terminal is a replay. Every line is a line the engine printed in one recorded run, in order; 67 lines of empty sections and build notes are cut and the film says where.
  • The engine detects and rebuilds. It does not rewrite the map by itself: the link in the diff was written by a short script that holds the decision an agent made, standing in for that agent in the replay.
  • The check reads the text of a skill's file. A change of behaviour that is not in that text is invisible to it, and the classifier is a surfacer that leans toward flagging, not a judge.
  • In daily use the check runs from an end-of-session hook, in its quiet form (read from the hook's own script: no strict flag). On a second throwaway copy I added one more line to the same skill. The quiet form printed nothing and exited 0, as it had before the edit. The plain form printed the advisory, and the strict form exited 1. So the hook does not tell me about this kind of change; a plain or strict check run by hand does.
  • On 2026-10-23 the same plain check on my real map reported 3 structural issues, 60 content-drift advisories and 10 reference-drift advisories (exit 1). So it catches things on the real map too, and I have not caught up with them.

How the map grew over time is on the constellation card; this card is about how it stays true.

The filmThe self-check, start to end

A 2:03 film, narrated in my voice in English or Português: the edit, the catch, the judgement step, the rebuild and the map before and after.

Next clip: I dictated a review, and the page was rebuilt from it →