Skip to content
Back to skills

mdhumanviewer

ASecurity

mdHumanViewer: assemble one self-contained, strongly human-readable HTML overview from a group of related Markdown files (skills, specs, RCA notes, docs) so a human grasps a whole system in minutes instead of reading dozens of files. Use whenever the user wants to understand, review, audit, map, or onboard onto a body of .md files AS A SYSTEM — phrases like \"what's actually in this project\", \"give me an overview of these skills\", \"contradictions between these docs\", \"render my markdown...

  • 3 stars
  • 0 votes
  • 0 copies
  • 3 views
  • Added June 5, 2026
ai-agentspythonrustgoshellbashnodegitapifrontenddocumentation

Works with

  • claude code
  • api

Security analysis

A100/100

Pro scans all 19 files and shows the line behind each finding

Scanned June 5, 2026

npx -y skills add werkodev/mdhumanviewer --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of mdhumanviewer?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for mdhumanviewer
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/werkodev-mdhumanviewer/badge)](https://www.skillsdirectory.com/skills/werkodev-mdhumanviewer)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: mdhumanviewer
description: "mdHumanViewer: assemble one self-contained, strongly human-readable HTML overview from a group of related Markdown files (skills, specs, RCA notes, docs) so a human grasps a whole system in minutes instead of reading dozens of files. Use whenever the user wants to understand, review, audit, map, or onboard onto a body of .md files AS A SYSTEM — phrases like \"what's actually in this project\", \"give me an overview of these skills\", \"contradictions between these docs\", \"render my markdown as a readable HTML map\", \"onboarding view of this folder\", \"big picture of ./references\", or points at a markdown directory and asks for the big picture. View, not storage: reads existing .md without modifying them; output is regenerated on demand. Read-once and fully parallel: each source enters an LLM context exactly twice (render + verify), both as N parallel agents in one turn; discovery, structure, graph, and final page assembly are pure deterministic Python off the LLM hot path."
---

# mdHumanViewer

Read a group of Markdown files and assemble from them a single self-contained
HTML representation, optimized for human perception of the system **as a whole**.
The design follows the project manifesto's render-lane / grep-lane split over a
disk-as-contract backbone, with two hard rules: each source `.md` is read into an
LLM context **exactly twice** (render + verify), **both fully parallel**, and there
are **zero serial whole-corpus generation passes** — discovery, structure, the
dependency graph, and final page assembly all run in stdlib Python off the LLM hot
path.

**Mission (from the origin manifesto):** the HTML is an *intelligent representation* of
the md — it loses no meaning, no section, no contract; its structure closely
mirrors the original; raw prose lives in the `.md` (linked). A human grasps
"what's there and how" in minutes and opens the md only for raw detail.

## Requirements (verified at S0, stated up front)

- The **`frontend-design`** skill must be installed/enabled — the
  `mdhv-design-author` agent uses it **once** to author (or `--reskin`) the committed
  `references/design-system.html`. The per-file render agents do **not** call it;
  they emit semantic HTML against the already-committed class vocabulary. S0 matches
  it from the system-reminder so the design-author can reach it.
- **Python 3.9+** on `PATH` — the orchestrator shells out to stdlib-only
  deterministic helpers and **never** hand-rolls glue: `scripts/parse_structure.py`,
  `scripts/init_session.py` (with `--list` / `--select`), `scripts/check_session.py`,
  `scripts/mark_step.py`, and `scripts/assemble.py`. 3.9 is the minimum because they
  use PEP 585 builtin generics.
- **Claude Code with root-level single-skill `SKILL.md` support** — this plugin
  uses the root-level single-skill layout.

## Operating principles (read before running)

- **View, not storage.** Never edit the source `.md`. Every run is a fresh full
  regeneration into a new dated session directory under `<ROOT>/.mdHumanViewer/`.
  The intermediate artifacts are disposable; only the current `overview.html`
  matters.
- **Disk is the contract between stages.** `structure.json` → `fragments/<slug>.html`
  + `analysis/<slug>.json` → `findings.json` → `overview.html`. Each stage reads
  from disk and writes to disk. This makes the pipeline restartable and keeps your
  context small.
- **The orchestrator stays light.** You (the main agent) hold only paths, the
  session directory, the matched `frontend-design` name, and step statuses. All
  heavy reading and reasoning happens inside subagents and Python scripts whose
  results land on disk. **Never read the full content of all source files into
  your own context** — that defeats the entire purpose. In particular you **must
  not `Read` `overview.html`** (S4's output): it embeds the whole corpus and
  re-reading it re-ingests everything you spent the pipeline avoiding.

- **HARD RULE — the scripts do all the glue; you never hand-roll it.** Every
  deterministic step has exactly one owning plugin script, and you only ever
  *invoke* that script and read its stdout + exit code. You **must NEVER**:
  - run `python3 -c …` or `python3 <<EOF` heredocs (or any ad-hoc inline Python)
    to read, filter, format, or check artifacts — `parse_structure.py` /
    `init_session.py --list` / `check_session.py` already produce that output;
  - **build `selection.json` by hand** or extract a renderer's slice by hand —
    `init_session.py --select` writes `selection.json`, the scoped
    `structure.json`, and one `slices/<slug>.json` per file for you;
  - **`Edit` `manifest.json` by hand** — `mark_step.py` is the only writer of step
    statuses.
  If you find yourself about to write inline Python, build JSON, or edit the
  manifest, stop: the correct move is to call the owning script.

The artifact schemas are the source of truth. Read them before assembly:
`${CLAUDE_PLUGIN_ROOT}/references/schemas.md`.

---

## How subagent invocations are written below

Every stage's invocation block looks like this:

> Use the `mdhv-X` subagent.
> KEY = `value`
> KEY = `value`
> Follow your instructions and ...

This is shorthand for **one real `Agent` tool call**, not a Bash command:
- `subagent_type` = the `mdhv-X` name on the first line.
- `prompt` = the rest of the block (the `KEY = value` lines plus the final
  imperative sentence), pasted verbatim into the prompt string.

The four pipeline subagents are `mdhv-design-author`, `mdhv-renderer`,
`mdhv-verifier`, and `mdhv-crossfile`.

**Subagent naming under plugin namespacing.** When this plugin is installed,
Claude Code namespaces each subagent as `mdhumanviewer:mdhv-X` (e.g.
`mdhumanviewer:mdhv-renderer`). The bare `mdhv-X` form below is the short
alias; if a particular Claude Code version or environment does not resolve the
bare name, pass the fully qualified `mdhumanviewer:mdhv-X` to `subagent_type`
instead. Both refer to the same subagent — pick whichever your runtime accepts.

Subagents only receive a single prompt string — there are no structured
parameters — so the `KEY = value` lines are how parameters reach them. Keep the
formatting (Markdown blockquote, backticks around values) so the subagent parses
them reliably.

**The parallelism convention (S2 and S2b).** When a stage fans out per file, **all
N `Agent` calls must go out in the same assistant turn** — as multiple tool-use
blocks in one message, not back-to-back across turns. That is what actually makes
them run concurrently (true parallelism). Claude Code runs all N subagents in
parallel and delivers all responses before you proceed, so you see all results in
one batch. The cost of S2 / S2b is therefore **max-of-files**, never
sum-of-files, and there is **no serial whole-corpus pass**.

---

## Pipeline overview (S0–S5)

| Stage | What | How | Reads src | Output |
|---|---|---|---|---|
| S0 Preflight | match `frontend-design`; ensure design system; create session | skill + python | 0 | session dir + `manifest.json` |
| S1 Parse | discover + structure + dependency graph | **Python, 0 LLM** | 0 | `structure.json` |
| S2 Render | intelligent per-file HTML fragment + analysis sidecar | **N parallel LLM, 1 read each** | 1 | `fragments/<slug>.html`, `analysis/<slug>.json` |
| S2b Verify | re-read source, check fragment fidelity, fix in place | **N parallel LLM, 1 read each** | 1 | revised `fragments/<slug>.html` + verdict |
| S2.5 Reconcile | losslessly close coverage + contract gates so assemble passes first try | **Python, 0 LLM** | 0 | fixed `fragments/` + `analysis/` in place |
| S3 Cross-file | contradictions / coverage / signal-noise | **1 LLM, reads only `analysis/`** | 0 | `findings.json` |
| S4 Assemble | shell + zones + stitch fragments + 4 hard-fail gates | **Python, 0 LLM** | 0 | `overview.html` + JSON report |
| S5 Report | signpost in chat | skill | 0 | chat message |

**Net:** each source enters an LLM context **exactly twice** (S2 + S2b), **both
parallel**; **zero** serial whole-corpus passes; S1 and S4 are pure Python.

`${SESSION}` below is the absolute session directory path returned by S1's
`init_session.py` (resolvable from the current working directory). Thread it into
every subagent call and every script invocation.

### Manifest discipline (honest resume)

The manifest's `steps` object is seeded **all `pending`** by `init_session.py`
with **exactly** these seven keys: `preflight`, `parse`, `render`, `verify`,
`crossfile`, `assemble`, `report`. After **each** stage's main action succeeds,
set `steps.<key>.status = "done"` — **including `preflight` and `parse`**, which
complete during S0/S1 (the script cannot know they ran, so if you skip marking
them the resume record stays misleadingly `pending`).

**Mechanics:** set every step status with `mark_step.py` — **never** `Edit`
`manifest.json` by hand:
```bash
python3 ${CLAUDE_PLUGIN_ROOT}/scripts/mark_step.py ${SESSION} <step> done
```
`<step>` is one of the seven keys above; `mark_step.py` validates the step key
against the known set, does a single read-modify-write of the `steps.<step>`
entry, and exits non-zero on an unknown key. Step marking is **serial** in the
orchestrator — mark one step at a time, never concurrently — so the
read-modify-write cannot clobber a sibling status. The seven step entries it can
write are exactly `steps.preflight`, `steps.parse`, `steps.render`,
`steps.verify`, `steps.crossfile`, `steps.assemble`, and `steps.report`.

**Honest resume.** On a restart with an existing session, read the manifest and
**skip a stage if and only if** its `steps.<key>.status == "done"` **AND** its
output artifact exists and validates:

| Stage | status key | artifact must exist + validate |
|---|---|---|
| S0 Preflight | `preflight` | `${SESSION}/manifest.json` parses; `references/design-system.html` exists |
| S1 Parse | `parse` | `${SESSION}/structure.json` parses |
| S2 Render | `render` | every `fragments/<slug>.html` AND `analysis/<slug>.json` exists and `analysis` parses |
| S2b Verify | `verify` | every `fragments/<slug>.html` still exists |
| S3 Cross-file | `crossfile` | `${SESSION}/findings.json` parses (has `cross_file_findings`) |
| S4 Assemble | `assemble` | `${SESSION}/overview.html` exists (do **not** `Read` it; an existence check is enough) |
| S5 Report | `report` | n/a (chat) |

If `status == "done"` but the artifact is missing or fails to validate, **re-run
that stage** — never trust a `done` flag without its artifact. Resume is honest: a
stage is skipped only when its status is `done` AND its output artifact exists and
validates.

---

## S0 — Preflight (match dependency, ensure design system, create session)

1. **Match the `frontend-design` skill from the system-reminder.** Look at the
   list of available skills in your system context (the `<system-reminder>` block
   listing skills). Match by **suffix** `frontend-design`. **Accept
   plugin-namespaced variants** (`document-skills:frontend-design`,
   `example-skills:frontend-design`, `claude-api:frontend-design`, or a bare
   `frontend-design`). Remember the matched name as `FRONTEND_DESIGN_SKILL` — the
   `mdhv-design-author` agent needs it (the per-file render agents do not). Do
   **not** verify via Bash or by invoking the skill.

   If no match appears and you cannot see the skills list, ask the user **once**
   for the exact name; hard-abort only if they confirm it is not installed:

   > mdHumanViewer uses the `frontend-design` skill to author its design system,
   > but I cannot confirm it from my current context. Is it installed, and if so
   > what is its exact name (e.g.
   > `document-skills:frontend-design` or a bare `frontend-design`)?

2. **Ensure the committed design system exists.** Check that
   `${CLAUDE_PLUGIN_ROOT}/references/design-system.html` exists (a Bash `test -f`
   or `ls` is fine — this is metadata, not corpus content). If it is **missing**,
   author it **once** via the design-author subagent, then continue:

   > Use the `mdhv-design-author` subagent.
   > DESIGN_OUT = `${CLAUDE_PLUGIN_ROOT}/references/design-system.html`
   > CONSTANTS_PATH = `${CLAUDE_PLUGIN_ROOT}/scripts/constants.py`
   > FRONTEND_DESIGN_SKILL = `<matched name from step 1>`
   > Author the single self-contained design system per your instructions.

   This is a **one-time / `--reskin`** path only; on a normal run the design
   system is already committed and you skip straight past this.

3. **Create the session.** This happens at the end of S1 (after selection), via
   `init_session.py` — see S1 step 3. The created `manifest.json` seeds all seven
   step statuses `pending`.

After S0 + S1 run `python3 ${CLAUDE_PLUGIN_ROOT}/scripts/mark_step.py ${SESSION}
preflight done`.

---

## S1 — Parse (deterministic, no LLM)

S1 is **three script calls and one question** — `parse_structure.py` →
`init_session.py --list` (show the menu) → ask the user → `init_session.py
--select "<spec>"`. **You write NO Python, build NO `selection.json`, and extract
NO slices by hand** (the HARD RULE above): the scripts produce every artifact.

1. **Parse** the target root (current directory unless the user named one). Use
   the **`${CLAUDE_PLUGIN_ROOT}` dollar-brace Bash form** so the script resolves
   regardless of install location, and **save the output to a file**:
   ```bash
   python3 ${CLAUDE_PLUGIN_ROOT}/scripts/parse_structure.py <ROOT> \
     > <ROOT>/.mdHumanViewer/structure.full.json
   ```
   It prints the **whole-ROOT** `structure.json`: `meta`, `files[]` (slug,
   file_path, language, title, token_estimate, headings[], links[]),
   `groups[]` (directory grouping), and `graph {nodes[], edges[]}`. This is the
   `<full>` structure you hand to `init_session.py` in steps 2 and 3 so the
   session gets a copy **scoped to the selection**. It is whole-corpus and almost
   always lists MORE files than the user will select. The script already skips the
   usual noise (`.git`, `.claude`, `node_modules`, `.mdHumanViewer`, `dist`,
   `build`, `.venv`, `venv`, `__pycache__`). If the tree
   has additional large dirs to skip, pass `--exclude DIR1 DIR2 ...` (this
   **overrides** the defaults entirely, so re-list the standard noise names if you
   still want them skipped).

2. **Show the grouped menu** — do **not** format it yourself with Python. Run
   `init_session.py --list` against the saved `<full>` structure; it prints a
   ready-to-show **markdown** menu grouped by directory (per group: dir · file
   count · summed token estimate; per file: slug · title · ~token estimate):
   ```bash
   python3 ${CLAUDE_PLUGIN_ROOT}/scripts/init_session.py \
     --structure <ROOT>/.mdHumanViewer/structure.full.json --list
   ```
   **Display its stdout verbatim** to the user (this is the menu — no session is
   created in `--list` mode). Then **ask what to include** in one clear prompt —
   accept a group/directory, individual files (by slug or ROOT-relative path), a
   glob (e.g. `skills/*`), or `all`. Do **not** skip selection even for an
   "obvious" all-files run: the selection defines the system boundary the overview
   describes.

3. **Create the scoped session** from the user's selection with
   `init_session.py --select "<spec>"` — this single call resolves the spec,
   writes `selection.json`, the **scoped** `${SESSION}/structure.json`, the
   per-file `${SESSION}/slices/<slug>.json`, and the manifest. **You never build
   `selection.json` or the slices by hand:**
   ```bash
   python3 ${CLAUDE_PLUGIN_ROOT}/scripts/init_session.py \
     --structure <ROOT>/.mdHumanViewer/structure.full.json --root <ROOT> \
     --select "<spec>" --language <source|tag>
   ```
   `<spec>` is the user's selection as a comma-separated mix of slugs,
   ROOT-relative file paths, directory names, globs (`skills/*`), and the reserved
   token `all` (resolution: `all` first; otherwise each token tries slug → exact
   path → directory → glob and the first category with ≥1 match wins; a token
   matching nothing exits non-zero — re-ask the user). `--language` is `source`
   unless the user explicitly asked for the overview in another language (normalize
   to a BCP-47 tag: `ru`, `en`, `de`, …). The script prints the session dir path on
   its **last stdout line** — **capture it as `${SESSION}`**; it is an absolute path
   usable directly from your current directory.

   **`--select` writes `${SESSION}/structure.json` scoped to your selection**
   (selected slugs only; graph pruned; whole-corpus `groups[]` dropped; cross-file
   `resolved_slug` dropped for unselected targets) **plus one
   `${SESSION}/slices/<slug>.json` per selected file** (the renderer's
   `STRUCTURE_SLICE`, built from the scoped data so it never points at an
   un-rendered file). Always use that scoped `${SESSION}/structure.json` for
   S2/S3/S4 — never the whole-ROOT `structure.full.json`. This is what keeps
   `assemble.py`'s coverage and anchor gates from failing on unselected files that
   were never rendered. **Never hand-edit `${SESSION}/structure.json`, the
   `slices/`, or `selection.json`** — they are produced only by `init_session.py`;
   if one looks wrong, the selection spec or the `--structure` path is wrong, not
   the artifact.

   (The legacy `--selection <SELECTION.json>` mode still works for a
   pre-built selection file, but it carries no headings/links so it writes **no**
   `slices/` — prefer `--select` so the renderer gets `STRUCTURE_SLICE_PATH`.)

After this stage you hold: `${SESSION}`, the list of `{path, slug}`, the output
language, and `FRONTEND_DESIGN_SKILL`. That is all you keep in context. Mark the
step done with the script (never by editing `manifest.json`):
```bash
python3 ${CLAUDE_PLUGIN_ROOT}/scripts/mark_step.py ${SESSION} parse done
```

---

## S2 — Render (N parallel subagents)

Invoke the **`mdhv-renderer`** subagent once per selected file, **all in the
same assistant turn** (the parallelism convention above), so they run in parallel
and finish together. Each sees exactly one file and knows nothing of the others.

For each selected `{path, slug}`, emit one `Agent` call with this prompt:

> Use the `mdhv-renderer` subagent.
> SOURCE_PATH = `<path>`
> SLUG = `<slug>`
> STRUCTURE_SLICE_PATH = `${SESSION}/slices/<slug>.json`
> FRAGMENT_OUT = `${SESSION}/fragments/<slug>.html`
> ANALYSIS_OUT = `${SESSION}/analysis/<slug>.json`
> OUTPUT_LANGUAGE = `<value of manifest.output_language, e.g. source or ru>`
> CLASS_VOCAB = `${CLAUDE_PLUGIN_ROOT}/references/schemas.md`
> Read the source once and write the layered fragment + analysis sidecar.

`STRUCTURE_SLICE_PATH` **points at the slice file** `init_session.py` wrote in S1
(`${SESSION}/slices/<slug>.json`) — the renderer reads it itself. Do **not**
inline-extract this file's headings/links from `structure.json` by hand; pass the
path.

When all complete, **verify the batch deterministically** — do **not** write an
ad-hoc existence/parse loop. Run `check_session.py`, which checks every selected
slug has a non-empty `fragments/<slug>.html` and a parseable `analysis/<slug>.json`:
```bash
python3 ${CLAUDE_PLUGIN_ROOT}/scripts/check_session.py ${SESSION}
```
It prints `{ok, missing_fragments:[], unparsable_analysis:[], bad_findings}` and
exits non-zero when not ok. On `!ok`, **re-invoke only the named-failing slug(s)**
(a fresh `mdhv-renderer` call for just those slugs, from `missing_fragments` /
`unparsable_analysis`) — never redo the whole batch — then re-run
`check_session.py`. If a slug still fails after a second attempt, report which file
is broken and ask the user whether to skip it or abort.

Mark the step done with the script:
```bash
python3 ${CLAUDE_PLUGIN_ROOT}/scripts/mark_step.py ${SESSION} render done
```

---

## S2b — Verify (N parallel subagents)

Invoke the **`mdhv-verifier`** subagent once per selected file, again **all in
the same assistant turn**. Each re-reads exactly one source once, checks the
fragment for completeness and distortion, and fixes defects **in place** via
`Edit` (preserving the class vocabulary and `data-src-heading`).

For each selected `{path, slug}`, emit one `Agent` call:

> Use the `mdhv-verifier` subagent.
> SOURCE_PATH = `<path>`
> FRAGMENT_PATH = `${SESSION}/fragments/<slug>.html`
> ANALYSIS_PATH = `${SESSION}/analysis/<slug>.json`
> Re-read the source once, verify completeness/fidelity, fix in place, return your verdict.

Each returns a verdict `{needs_revision, issues[]}`. When all complete, **validate
the batch again with `check_session.py`** instead of an ad-hoc loop:
```bash
python3 ${CLAUDE_PLUGIN_ROOT}/scripts/check_session.py ${SESSION}
```
On `!ok`, **re-invoke only the named-failing slug(s)** (a fresh `mdhv-verifier`
call for just those slugs), then re-run `check_session.py`. Carry any unresolved
`issues[]` into the S5 report so the user knows. Mark the step done with the
script:
```bash
python3 ${CLAUDE_PLUGIN_ROOT}/scripts/mark_step.py ${SESSION} verify done
```

---

## S2.5 — Reconcile (deterministic, no LLM)

Run **after verify (S2b), before crossfile/assemble** — `reconcile.py` is a
sanctioned plugin script, **not** improvisation (it is *not* `python3 -c` and it
is *not* hand-editing artifacts; the HARD RULE about ad-hoc python and manual
artifact edits below still stands in full). It deterministically closes the two
*mechanical* gate properties — gate 2 (per-file coverage) and gate 3 (contract
fidelity) — **losslessly**, so `assemble.py` (S4) passes on the **FIRST attempt**
in the common case, with **no** verifier re-invoke round. Run it once:
```bash
python3 ${CLAUDE_PLUGIN_ROOT}/scripts/reconcile.py --session ${SESSION}
```

It reads each selected file's `fragments/<slug>.html` + `analysis/<slug>.json`,
attaches missing `<slug>--<anchor>` coverage ids via the language-agnostic
`analysis.sections[].source_headings[]` join, surfaces source-faithful contracts
into the fragment, and removes only exact-duplicate contract entries — all
losslessly (it never snaps a contract across a `not`/`never`/`unless`, never
drops a real contract, never weakens a gate). It writes the lossless fixes in
place and prints the JSON report:
`{auto_closed:{coverage:[],contracts:[],anchors:[]}, genuine_gaps:{coverage:[],contracts:[],anchors:[]}}`.

- **Exit 0** (`genuine_gaps` empty) → coverage + lossless contract contracts are
  closed deterministically. Go **straight to S4 assemble** — no re-invoke round.
- **Non-zero exit** (`genuine_gaps` non-empty) → there is genuine content loss
  that cannot be closed losslessly. **Re-invoke ONLY the named slug's agent**, by
  gap kind (each entry names its slug):
  - a `genuine_gaps.coverage` entry (a source heading no analysis section claims)
    → re-invoke that slug's **`mdhv-renderer`**;
  - a `genuine_gaps.contracts` entry (a stored contract not present in the source
    bytes — a semantic judgment, never snapped or dropped automatically) →
    re-invoke that slug's **`mdhv-verifier`**;
  - a `genuine_gaps.anchors` entry (a **real** in-page `<a href="#x">` whose `#x`
    resolves to no heading id and is not a chrome anchor) → re-invoke that slug's
    **`mdhv-renderer`** / **`mdhv-verifier`** to fix the offending `href`, mirroring
    the `dangling_anchors` guidance in S4. `reconcile.py` detects this in S2.5 (gate
    1 is HTML-aware, in parity with `assemble.py`), so S4's anchor gate passes on the
    first try in the common case; documentation that merely **quotes** an anchor like
    `#<slug>--<anchor>` inside `<code>` is **not** a dangling anchor and is left
    untouched.

  Then **re-run `reconcile.py`**. This is **bounded — at most two rounds** for a
  given failing slug. If `genuine_gaps` is still non-empty after that, **stop and
  report** the reconcile JSON verbatim (the genuine gap plus the offending
  ids/contracts) and ask how to proceed — do **not** improvise a workaround, do
  **not** shrink the selection, and do **not** weaken a contract to force the gate
  green (fidelity is the mission).

`reconcile.py` is the deterministic form of the join the verifier already
performs in prose; the verifier's prose coverage/contract sections stay as
belt-and-suspenders. There is no manifest step for reconcile — it sits between
`verify` and `crossfile`/`assemble` and is gated by their step statuses.

---

## S3 — Cross-file (1 subagent, reads only analysis/)

Invoke the **`mdhv-crossfile`** subagent **once**. It reads **only**
`analysis/*.json` plus `structure.json` — it **never** re-reads the source `.md`
(that read-once guarantee is the whole point) — and writes the cross-file
findings.

> Use the `mdhv-crossfile` subagent.
> ANALYSIS_DIR = `${SESSION}/analysis`
> STRUCTURE_PATH = `${SESSION}/structure.json`
> FINDINGS_OUT = `${SESSION}/findings.json`
> Apply the three optics and write the root-object findings.json.

It writes the **root object** `findings.json` = `{"cross_file_findings": [...]}`
(an empty run emits `{"cross_file_findings": []}`, never a bare `[]` or `{}`).
**Validate it with `check_session.py`** (its `bad_findings` flag is true unless
`findings.json` is a well-formed root object carrying a `cross_file_findings`
list) — do **not** write an ad-hoc parse loop:
```bash
python3 ${CLAUDE_PLUGIN_ROOT}/scripts/check_session.py ${SESSION}
```
On `bad_findings`, re-invoke `mdhv-crossfile` once, then re-run
`check_session.py`. Mark the step done with the script:
```bash
python3 ${CLAUDE_PLUGIN_ROOT}/scripts/mark_step.py ${SESSION} crossfile done
```

---

## S4 — Assemble (deterministic, no LLM)

Run the assembler. Use the `${CLAUDE_PLUGIN_ROOT}` dollar-brace Bash form and
pass the committed design system as `--design`:
```bash
python3 ${CLAUDE_PLUGIN_ROOT}/scripts/assemble.py \
  --structure   ${SESSION}/structure.json \
  --analysis-dir ${SESSION}/analysis \
  --fragments-dir ${SESSION}/fragments \
  --findings    ${SESSION}/findings.json \
  --design      ${CLAUDE_PLUGIN_ROOT}/references/design-system.html \
  --out         ${SESSION}/overview.html \
  --manifest    ${SESSION}/manifest.json
```

Run it with the **current working directory at the analyzed ROOT** (where you
parsed the corpus): gate 3 reads each file's source bytes via its `file_path`,
resolved relative to CWD. (`assemble.py` carries a `sys.path` shim so a direct
`python3 …/scripts/assemble.py` still imports `scripts.constants` from any CWD —
so only the *working directory* needs to be the ROOT, not the script location.)

Consume **only the stdout JSON report and the exit code**. The report shape is:
`{files, sections, findings, graph_mode, dangling_anchors:[], coverage_gaps:[],
contract_violations:[], external_fetches:[]}`.

- **Exit 0** → the page was written to `${SESSION}/overview.html` and all four
  hard-fail gates passed (anchor resolution, per-file coverage, contract check,
  no-runtime-fetch). **Do NOT `Read` `overview.html`** — it embeds the entire
  corpus and reading it re-ingests everything the read-once design avoided. The
  stdout report is all you need.
- **Non-zero exit** → one or more gate arrays
  (`dangling_anchors` / `coverage_gaps` / `contract_violations` /
  `external_fetches`) are non-empty and **no page was written**. **Surface the
  report** to the user: name the failing gate(s) and the specific offending
  ids/anchors/contracts from the arrays, so they can see exactly what is missing
  or wrong. Do not paper over a dropped contract to make the stage "pass".

**Targeted repair, not a broad redo.** A gate failure is a *per-file* fact:
every entry in the gate arrays names a specific slug, so route the fix to that
slug alone — never re-render or re-verify the whole corpus, and **never shrink
the selection to dodge the gate**.

- When `assemble.py` reports a **non-empty `coverage_gaps` array**, the
  orchestrator must **re-invoke** ONLY the `mdhv-verifier` subagent for the
  specific slug(s) named in `coverage_gaps`, passing the exact missing
  `SLUG--anchor` ids so the verifier knows precisely which source headings lack a
  `data-src-heading` id, then re-run `assemble.py`. The missing ids are a
  mechanical fix the verifier closes **in place** (a merged section carries the
  extra ids space-separated in `data-src-heading`, or a hidden anchor span is
  added for each non-lead heading — both already allowed by the fragment
  contract). Do NOT re-render or re-verify the whole corpus to close a coverage
  gap.
- A non-empty **`contract_violations`** array routes to the named slug's
  **`mdhv-verifier`**, passing the exact offending contract text(s). The verifier
  re-derives that `analysis.contracts[].text` so its words are faithful to the
  source (gate 3 compares content words, so a contract lifted from a table row or
  list need only carry the same words, in order — not the source's `|`/`-`/`[]()`
  punctuation), and/or fixes the `.mdhv-contract` callout. Then re-run `assemble.py`.
- A non-empty **`dangling_anchors`** array routes to the named slug's
  `mdhv-renderer` / `mdhv-verifier` to fix the offending `href`/id, then re-run.

**Never debug or patch the pipeline by hand — this is a hard rule, not a
preference.** Every artifact has exactly one producer (a script or a subagent);
the orchestrator only *invokes* producers and reads their stdout + exit code. On
a gate failure you MUST NOT:
- read, `grep`, `cat`, `sed`, or otherwise **inspect the plugin's own scripts**
  (`parse_structure.py`, `init_session.py`, `assemble.py`, `constants.py`) to
  reason about the failure — the stdout report already names the exact offending
  ids / anchors / contracts;
- write **ad-hoc Python** (`python3 -c …`, `python3 <<EOF` heredocs) to inspect
  artifacts, re-implement `normalize` or any gate, or compute/apply a fix;
- **hand-edit** `analysis/*.json`, `fragments/*.html`, `structure.json`, or
  `findings.json` — these are produced ONLY by their owning subagent/script.

The ONLY allowed response to a gate failure is the **targeted re-invoke** above,
then re-running `assemble.py`.

**Bounded, then stop.** Attempt at most **two** targeted re-invoke rounds for a
given failing slug. If the gate still fails, **stop and report** the assembler's
JSON report verbatim to the user (the failing gate plus the offending items) and
ask how to proceed. Do NOT improvise a workaround, do NOT shrink the selection to
dodge the gate, and do NOT write a partial or lossy page.

On success mark the step done with the script (never by editing `manifest.json`):
```bash
python3 ${CLAUDE_PLUGIN_ROOT}/scripts/mark_step.py ${SESSION} assemble done
```

---

## S5 — Report (you, in chat)

Finish in the main conversation with a short, scannable signpost (the HTML is the
deliverable; the report is the signpost):
- Where the output is: `${SESSION}/overview.html` (and how to open it).
- What was processed: file count, total token estimate (labelled an estimate),
  output language, and the graph mode (`chips` / `diagram`) from the report.
- The headline of the system map: how many files, the main groups.
- **Cross-file findings up front** — especially `high`-severity contradictions and
  coverage gaps from S3, with the files involved. This is the part the user most
  needs and the hardest to get any other way.
- Any unresolved S2b verifier `issues[]`.
- A pointer that detail is collapsible in the HTML and links back to the source
  `.md` for raw digging.

After sending the chat report, mark the step done with the script (never by
editing `manifest.json`):
```bash
python3 ${CLAUDE_PLUGIN_ROOT}/scripts/mark_step.py ${SESSION} report done
```

---

## Failure handling

- A failed render/verify subagent: re-invoke just that slug's `mdhv-renderer` /
  `mdhv-verifier`; never redo the batch.
- Malformed `analysis/<slug>.json`: re-run that file's `mdhv-renderer` once; if it
  still fails, report which file is broken rather than silently proceeding.
- An S4 gate failure: surface the report's failure arrays; never write or present a
  partial `overview.html`. Surfacing a gap is more valuable than a clean-looking but
  lossy overview. Then repair it **targeted, per slug** (see S4's "Targeted repair"
  note): a non-empty `coverage_gaps` re-invokes ONLY the named slug's
  `mdhv-verifier` with the exact missing `SLUG--anchor` ids; `dangling_anchors` /
  `contract_violations` route back to the named slug's renderer/verifier. Never
  re-render/re-verify the whole corpus and never shrink the selection to dodge the
  gate.

Files in this skill

  • .claude-plugin/marketplace.json987 B
  • .claude-plugin/plugin.json1.2 KB
  • .markdownlint-cli2.yaml1.4 KB
  • CHANGELOG.md14.9 KB
  • CONTRIBUTING.md3.2 KB
  • SKILL.md31.8 KB
  • agents/mdhv-crossfile.md9.9 KB
  • agents/mdhv-design-author.md8.2 KB
  • agents/mdhv-renderer.md17.1 KB
  • agents/mdhv-verifier.md13.5 KB
  • references/design-system.html51.8 KB
  • references/schemas.md22.7 KB
  • ruff.toml1.2 KB
  • scripts/assemble.py50.3 KB
  • scripts/check_session.py3.8 KB
  • scripts/constants.py5.6 KB
  • scripts/gates.py10.9 KB
  • scripts/init_session.py16.4 KB
  • scripts/mark_step.py3.9 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.