layer-sync
A research project's knowledge lives in ordered layers — typically
{{doc_layers}} = decision log → spec/docs → code → notebooks/reports. Any change that
lands in one layer and not the others is drift, and drift silently corrupts the paper the
project is building toward. This skill audits a scoped slice of the layers, reports drift
with file:line evidence, and proposes exactly one next action. It never fixes anything in
the same turn.
When to use
- After a decision-log entry lands (did it reach spec, code, notebooks?).
- After a commit range that touched a documented behavior.
- As a phase-gate step (the
phase-gate skill calls this before closing).
- When any two layers are suspected to disagree.
When NOT to use
- "Where are we / what's next" — that is a status question for the
session-historian agent.
- Verifying a single computed result — that is the
verifier agent's job.
- General code review, style review, or test-coverage review.
- Fixing the drift — fixes happen in a separate, user-approved turn.
Scoping the audit
Parse the user's scope; default to recent.
| Scope |
Meaning |
recent (default) |
last ~7 days of commits + last 2 decision-log entries |
decision scope (e.g. D15) |
that decision checked across all layers |
module scope (e.g. channel) |
one subpackage + the docs/notebooks that reference it |
commit scope (e.g. HEAD~3..HEAD) |
what that range changed and where else it must land |
links / links <id> |
check registered doc↔source anchor pairs only (see below) |
Runbook
Read the layers in authority order: {{decision_log}} (newest entries first), then
git log --oneline -20 (+ --name-only for the scope window), then the spec docs and
{{build_log_dir}} (last ~3 entries), then the implementation, then notebooks/reports.
Read only what the scope needs — for notebooks prefer targeted greps over full parses.
For each in-scope decision or change, build a layer-by-layer row: reflected / missing /
contradicting, with a file:line citation for every non-✅ cell.
Emit the report:
## Drift audit — <date>
**Scope**: <…> **Layers**: <{{doc_layers}}>
**Verdict**: ✅ in sync | ⚠ drift detected | ❌ contradictions
| Decision / change | <layer 1> | <layer 2> | <layer 3> | <layer 4> | Status |
| ... | ✅ | ✅ | ⚠ old term at src/x.py:42 | ✅ | ⚠ |
### Drift details (⚠/❌ only)
- What the authority layer says (cite), what the drifted layer says (cite),
one concrete suggested fix.
Append exactly one next action:
- ✅ → "no drift — proceed with ".
- ⚠ → the single highest-value fix, phrased as a proposal awaiting approval.
- ❌ → stop; ask the user which layer is canonical before proposing anything.
Anchor links (point-precise mode)
For pairs that must never drift (a doc section describing a specific function, a constant
table mirroring a config file), register an explicit link in a small manifest,
docs/drift_links.yml:
links:
- id: 1
doc: docs/<doc>.md # optionally with a doc_anchor section
src: src/<file> # optionally with a #symbol
note: "what this pair pins"
created: YYYY-MM-DD
Adding a link: verify the doc path exists, the source path exists, and the symbol (if given)
is actually declared in the source (grep for its definition; no match → refuse, show the
grep). Reject duplicates of the same (doc, src, symbol) triple. Removing: by id. The
manifest is the only file this mode ever writes. Audits with scope links compare each
pair's content substance directly — the manifest says where to look, never what is correct.
Rules
- The decision log is the source of truth. If code and the decision log disagree, the
code is the drift — unless the entry is itself marked superseded.
- Cite file:line for every drift claim. "The notebook still has the old form" is not a
finding; the cell/line reference is.
- Read-only audit. No test runs, no builds, no notebook execution, no edits.
- Skip layers that are out of scope for a given decision. A code-only decision with no
notebook impact must not be flagged for "missing notebook reference".
- Group related drifts into one row with the missing piece — not N rows for N sub-edits.
- Recency bias. Decisions older than ~30 days (or several numbers back) are assumed
propagated unless the caller explicitly widens the scope.
- Historical narrative is not drift. A prose cell explaining the old form next to code
using the new form is documentation; flag only executable code that uses a retired form.
- Beware short-token renames. Grep with word boundaries (
\bold_name\b) — short
identifiers collide.
- One action, not a menu. Three drifts found → propose the first fix; the rest surface
on the next audit.
- Never soften the verdict letter. ⚠ and ❌ are load-bearing for the next-action
branching; do not downgrade them to "minor inconsistency" prose.
Dispatching to the auditor agent
The pack ships a read-only drift-auditor agent implementing exactly this protocol. When
subagents are available, this skill is a thin coordinator: parse the scope, dispatch the
agent, present its report verbatim (do not re-summarize its table), append the single action
line. Do not pre-read layer files before dispatching — that duplicates the agent's work. Do
not run the audit inside another agent; it is a top-level, user-facing flow.
Configuration
{{doc_layers}} — ordered layer list (default: decisions, spec, code, notebooks).
{{decision_log}} — the authority layer file.
{{build_log_dir}} — build-log directory (part of the docs layer).
Provenance & maintenance
Generalized from a working four-layer sync discipline (coordinator skill + read-only audit
agent + anchor-link manifest) in a mature computational-research repository; see the pack's
examples/ directory for the worked exemplar mapping. The scope table, verdict semantics,
and rules 1–10 are verified practice; the YAML manifest schema is copied verbatim from the
exemplar (field names are the convention, not a requirement).
Re-verify in your project:
test -f {{decision_log}} && head -40 {{decision_log}} — the authority layer exists and
is newest-first.
git log --oneline -10 -- <spec doc> — the spec layer actually changes over time (a
never-touched spec is itself a drift signal).
test -f docs/drift_links.yml && cat docs/drift_links.yml — anchor manifest present (only
if you use the links mode).
1---2name: layer-sync3description: Use when checking whether the project's knowledge layers agree — the decision log, the spec/docs, the code, and the notebooks/reports — after a decision lands, after a refactor, before a phase closes, or when the user asks "is there drift", "did that decision propagate", "are docs and code in sync". Also use to register an explicit doc-to-source anchor link for point-precise checking. Trigger phrases: "sync check", "drift audit", "is the spec up to date with the code", "did D-numbers propagate", "link this doc to this source file".4---56# layer-sync78A research project's knowledge lives in ordered layers — typically9`{{doc_layers}}` = decision log → spec/docs → code → notebooks/reports. Any change that10lands in one layer and not the others is *drift*, and drift silently corrupts the paper the11project is building toward. This skill audits a scoped slice of the layers, reports drift12with file:line evidence, and proposes exactly one next action. It never fixes anything in13the same turn.1415## When to use1617- After a decision-log entry lands (did it reach spec, code, notebooks?).18- After a commit range that touched a documented behavior.19- As a phase-gate step (the `phase-gate` skill calls this before closing).20- When any two layers are suspected to disagree.2122## When NOT to use2324- "Where are we / what's next" — that is a status question for the `session-historian` agent.25- Verifying a single computed result — that is the `verifier` agent's job.26- General code review, style review, or test-coverage review.27- Fixing the drift — fixes happen in a separate, user-approved turn.2829## Scoping the audit3031Parse the user's scope; default to `recent`.3233| Scope | Meaning |34|---|---|35| `recent` (default) | last ~7 days of commits + last 2 decision-log entries |36| decision scope (e.g. `D15`) | that decision checked across all layers |37| module scope (e.g. `channel`) | one subpackage + the docs/notebooks that reference it |38| commit scope (e.g. `HEAD~3..HEAD`) | what that range changed and where else it must land |39| `links` / `links <id>` | check registered doc↔source anchor pairs only (see below) |4041## Runbook42431. Read the layers **in authority order**: `{{decision_log}}` (newest entries first), then44 `git log --oneline -20` (+ `--name-only` for the scope window), then the spec docs and45 `{{build_log_dir}}` (last ~3 entries), then the implementation, then notebooks/reports.46 Read only what the scope needs — for notebooks prefer targeted greps over full parses.472. For each in-scope decision or change, build a layer-by-layer row: reflected / missing /48 contradicting, with a `file:line` citation for every non-✅ cell.493. Emit the report:5051 ```52 ## Drift audit — <date>53 **Scope**: <…> **Layers**: <{{doc_layers}}>54 **Verdict**: ✅ in sync | ⚠ drift detected | ❌ contradictions5556 | Decision / change | <layer 1> | <layer 2> | <layer 3> | <layer 4> | Status |57 | ... | ✅ | ✅ | ⚠ old term at src/x.py:42 | ✅ | ⚠ |5859 ### Drift details (⚠/❌ only)60 - What the authority layer says (cite), what the drifted layer says (cite),61 one concrete suggested fix.62 ```63644. Append **exactly one** next action:65 - ✅ → "no drift — proceed with <next planned item>".66 - ⚠ → the single highest-value fix, phrased as a proposal awaiting approval.67 - ❌ → stop; ask the user which layer is canonical before proposing anything.6869## Anchor links (point-precise mode)7071For pairs that must never drift (a doc section describing a specific function, a constant72table mirroring a config file), register an explicit link in a small manifest,73`docs/drift_links.yml`:7475```yaml76links:77 - id: 178 doc: docs/<doc>.md # optionally with a doc_anchor section79 src: src/<file> # optionally with a #symbol80 note: "what this pair pins"81 created: YYYY-MM-DD82```8384Adding a link: verify the doc path exists, the source path exists, and the symbol (if given)85is actually declared in the source (grep for its definition; no match → refuse, show the86grep). Reject duplicates of the same (doc, src, symbol) triple. Removing: by id. The87manifest is the only file this mode ever writes. Audits with scope `links` compare each88pair's *content substance* directly — the manifest says where to look, never what is correct.8990## Rules91921. **The decision log is the source of truth.** If code and the decision log disagree, the93 code is the drift — unless the entry is itself marked superseded.942. **Cite file:line for every drift claim.** "The notebook still has the old form" is not a95 finding; the cell/line reference is.963. **Read-only audit.** No test runs, no builds, no notebook execution, no edits.974. **Skip layers that are out of scope for a given decision.** A code-only decision with no98 notebook impact must not be flagged for "missing notebook reference".995. **Group related drifts** into one row with the missing piece — not N rows for N sub-edits.1006. **Recency bias.** Decisions older than ~30 days (or several numbers back) are assumed101 propagated unless the caller explicitly widens the scope.1027. **Historical narrative is not drift.** A prose cell explaining the *old* form next to code103 using the new form is documentation; flag only executable code that uses a retired form.1048. **Beware short-token renames.** Grep with word boundaries (`\bold_name\b`) — short105 identifiers collide.1069. **One action, not a menu.** Three drifts found → propose the first fix; the rest surface107 on the next audit.10810. **Never soften the verdict letter.** ⚠ and ❌ are load-bearing for the next-action109 branching; do not downgrade them to "minor inconsistency" prose.110111## Dispatching to the auditor agent112113The pack ships a read-only `drift-auditor` agent implementing exactly this protocol. When114subagents are available, this skill is a thin coordinator: parse the scope, dispatch the115agent, present its report verbatim (do not re-summarize its table), append the single action116line. Do not pre-read layer files before dispatching — that duplicates the agent's work. Do117not run the audit inside another agent; it is a top-level, user-facing flow.118119## Configuration120121- `{{doc_layers}}` — ordered layer list (default: decisions, spec, code, notebooks).122- `{{decision_log}}` — the authority layer file.123- `{{build_log_dir}}` — build-log directory (part of the docs layer).124125## Provenance & maintenance126127Generalized from a working four-layer sync discipline (coordinator skill + read-only audit128agent + anchor-link manifest) in a mature computational-research repository; see the pack's129`examples/` directory for the worked exemplar mapping. The scope table, verdict semantics,130and rules 1–10 are verified practice; the YAML manifest schema is copied verbatim from the131exemplar (field names are the convention, not a requirement).132133Re-verify in your project:134135- `test -f {{decision_log}} && head -40 {{decision_log}}` — the authority layer exists and136 is newest-first.137- `git log --oneline -10 -- <spec doc>` — the spec layer actually changes over time (a138 never-touched spec is itself a drift signal).139- `test -f docs/drift_links.yml && cat docs/drift_links.yml` — anchor manifest present (only140 if you use the links mode).