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
  • 2 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…