Generated from cat-harness/skills/ui/ui-core/readme-sections.md — do not edit here.

✎ Edit this page’s source

A folio’s README — the folio owns the file, the platform owns the markers

Two tools divide the file between them, and between them no link in a folio README is unaccounted for: sync owns the marked regions, audit checks everything else. Neither ever rewrites the author’s prose.

what it doeswrites?
readme_sync · readme:syncrefresh each generated sectiononly between markers
readme_audit · readme:auditverify every Markdown link resolvesnever

readme:sync:check is the CI form; readme:sections lists what a README can opt into. Registered among the generic tools — a document folio has chapters, simulators and workflows for the same reason a paper folio does, and simply never carries the Lean markers.

Opt-in is per section, and absence means “not opted in”

A section is written only where the README already carries its <!-- marker:begin --> / <!-- marker:end --> pair. Nothing outside a marked region is ever touched, and a README with no markers is a README this tool leaves entirely alone. Adding a section is one registry entry: a marker, a one-line summary, and a renderer returning Markdown plus operator notes — the CLI, the MCP tool and the staleness check all read the registry.

“Could not determine” is a third state, everywhere

A section that cannot establish its answer returns skip, and the region is left exactly as it was. This is not decoration:

  • A folio configuring its simulators under a platform submodule has no such directory until that submodule is checked out. The first version rendered directory absent as this folio has no simulators — replacing a correct nine-row table with a sentence.
  • A shallow clone cannot read the publish ref. “Could not read it” must not silently blank a table that was right yesterday.

An empty directory is still a determined empty. Absent and empty are different answers, and only one of them is safe to render.

The contents table’s predecessor built every PDF cell by convention — <pages>/papers/<paper>/chapters/<dir>.pdf — and checked it against nothing. The folio’s publish branch had no chapters/ directory, so all twenty-three chapter links were 404 and always had been; three of six appendix links happened to resolve. Every cell is now looked up in a real listing of the publish ref, and a chapter with no published PDF renders —.

The audit half exists for the same reason and reports three states, never two: an external host, an in-page anchor, and a ref this checkout cannot read are all NOT CHECKED, so a shallow clone does not produce a wall of false findings.

A private folio whose README links to a Pages URL is unreachable for exactly the people who have repository access, and a raw-content host does not fix it: it 404s on a private repo without a token, and a browser session cookie does not authenticate it.

The default is blob — the forge’s own file view — which follows the viewer’s session, works whether the repo is public or private, and renders PDFs inline. The other styles remain configurable, and each prints a note under the table saying who can follow its links.

Why generating the rest was the wrong instinct

Two defects, both easy to write again.

The predecessor described one folio from inside the platform. It ended in a whole-file copy, carrying one folio’s title, badges, a registry of that folio’s own indices, a project-structure table naming its directories, and a licence block. Run it in any other folio and the author loses their README. Only five of its sections were derived from the tree at all; prose about a folio belongs to that folio. Three literals went with it, each worth recognising in new code: a namespace prefix applied regardless of the folio’s actual build library (now read from the build file, and left unprefixed when none names one — a wrong namespace is worse than none, because it is what a reader pastes into an import); descriptions from a hardcoded map of one folio’s filenames consulted before the artefact’s own declared name; and a hardcoded simulator directory.

And the authored half is worth keeping. One folio’s Published Artefacts table listed two directories that had never existed on its publish branch — both rows dead in both columns. But its labels were prose a generator would have had to invent. The defect was never a stale layout; it was targets that do not resolve. So that half is audited, not generated.

cat-harness:instances — the root README indexes every instance

Added 2026-09-20 (issue #592). The owner:

find beans related to readme, associated to harness instances, they must add to main one. should provide both Agent links (agents.md , memories) AND human docuemntaion (docs/) link for each harness.

One row per instance declaring a declaration, with two entries because two readers arrive: an agent entry (AGENTS.md, memories) and a human one (README, docs). Both halves are resolved from that instance’s own declaration — declared assets for the file links, declared graphs for the directory links.

Never composed from a convention. join(root, "AGENTS.md") would link a file that may not be declared, and the criterion in check:subgraph-coverage rests on an undeclared file being one no checker has a reason to look at. Scope is honoured too: a directory declared scope: "repository" is linked at the repository root, which is how memory/ was first rendered dead as ./cat-harness/memory/. A dead link in a generated table is worse than a missing row — the row asserts the entry exists.

A gap is rendered AS a gap: an em dash plus a counted note under the table, naming the check that lists them. Never a blank cell, never a guessed path. Eight of eleven instances had no agent entry when this was written, and a table that quietly omitted them would have read as though the work were done.

Which README, and the --dir flag

readme:sync with no --dir resolves to whichever instance carries a folio/ — which is cat-harness, not the repository root. Before #592 the two were one file (cat-harness declared the root’s README with scope: "repository"), so the difference could not show. Use the pair:

readme:sync · readme:auditthe cat-harness instance’s README
readme:sync:root · readme:audit:rootthe REPOSITORY’s README
readme:sync:allEVERY instance’s README, each against its own declaration

In an index checkout the root README is not authored at all. Its root declares no instance (owner, 2026-10-08), so nothing could own the file, and the owner ruled on 2026-10-09 that it and the root AGENTS.md are rendered: bun run cat index:render writes both — with the CLAUDE.md and GEMINI.md stubs — from index.config.json, each instance’s declared agent-instructions and instance-readme assets, bootstrap’s AGENTS.md, and the sections above, and index:render:check fails when any of the four is not its current output. readme:sync:root still agrees with it, because the renderer fills the same marker regions through the same registry.

Both are gated in code-quality-gates.yml, and that is not belt and braces: when the split landed, the bare readme:audit silently narrowed to cat-harness’s and took the root README’s 41 links out of the gate without failing anything. A gate that stops covering something still reports green.

readme:toc — every harness README’s own table of contents

Owner, 2026-09-30: “generated README.md … missing TOC, probably need on all harnesses”. A nested list of the README’s own h2/h3 headings, linked by the anchor GitHub assigns them. Every instance README carries it under a bold Contents line, above its first ##; the root README too.

Not folio:toc. The shared word hides two different sections: folio:toc is a folio’s contents — papers, chapters, verified PDFs — and says nothing about the file it sits in. readme:toc is the file’s outline and needs no folio at all. Both may sit in one README.

Anchors are GitHub’s, not a tidy approximation of them (githubSlug, github-slugger’s rule): lowercase, drop everything but letters, marks, digits, _, - and space, then each space becomes - — not collapsed, so Corpus → draft is #corpus--draft. Inline markup is stripped first, as the renderer does (`code` → code, [x](y) → x). Duplicates become -1, -2 counted over every heading level, h1 and h4+ included, because GitHub counts them all; a suffixed anchor colliding with a literal heading is bumped past it. A TOC link that is almost right is a dead link that reads as live.

What is not a heading is not in it: lines inside a code fence (closed only by a fence of the same character at least as long), inside an HTML comment, indented four or more spaces, and inside the TOC’s own region — so the section is never an input to itself and a second run is a no-op.

It is registered LAST, on purpose. It reads the README as the sections before it have left it, so a heading another section generates (folio:toc’s ### <paper>) is outlined on the same run. Registered earlier, every sync would leave the TOC one run behind and --check would fail on a README that had just been synced.

Zero h2/h3 is a determined empty (“This README has no sections yet.”), and a README with nothing to outline is better without the marker — which is why the immunizations IG carries none. A renderer called with no README text is undetermined and leaves the region alone.

Gated by readme:sync:all:check, over every declared instance. The three single-README checks above name one file each; a generated region in the other instances that nothing checked would be a hand-kept list, wrong the first time somebody added a heading.

kg:processes and kg:files — an instance’s diagrams and files, from its declaration

Two sections any instance’s README may carry, both read from the instance’s own declaration and the files it names (bootstrap-tools/scripts/readme-graph-sections.ts). Owner, 2026-09-29: “display bpmn(s) etc in README.md”, “part of readme.md generation is to do rendering”, and the file list at the end must be “a skill and tool in cat-harness”.

  • kg:processes — every .bpmn in the declared directories, the entry Process first (the one no other calls), each with its picture. The picture is the SVG render:bpmn writes beside the .bpmn for an instance exempt from workflow-visualiser — one with no site of its own, so its README is where its diagrams are seen. This section reads the SVG and never draws; rendering needs a browser, so render:bpmn must run first. bootstrap’s own README is generated and checked in bootstrap’s repository (owner, 2026-10-01: each repo owns its README; litlfred/bootstrap#1). A diagram with no picture beside it leaves the region untouched — never a broken image.
  • kg:files — every file, grouped by declared directory, with “what it is” read from the file itself (front-matter description, a Process’s name, a schema’s title, a JSON file’s own description or $comment) and “used by” only where a diagram records the relation: a task naming a Skill, a call activity naming a Process. Blank otherwise, never guessed. A directory whose files all sit in subdirectories collapses to one row.

Why kg: and not cat-harness:. The marker is written into the instance’s README, and the floor instance must not name the layer above it — its leak test fails on the word. kg is vocabulary every instance has.

A blank “used by” is a finding, not decoration. On bootstrap it showed skills/discussion.md used by nothing although a diagram names it: the file has no front matter, so by bootstrap’s own rule it is not a Skill. A hand-kept table had said “step 2” and hidden that.

subgraph-readmes — one README per declared directory

A large instance cannot list every file on one page, but it declares its directories, and each declared directory gets its own README (bun run cat readme:subgraphs, the subgraph-readmes Tool). The instance README’s kg:files then links each directory to it, and above 200 files lists directories instead of files. The README is part of the graph: the exported Directory node carries readmePath.

Everything shown is read from the Knowledge Graph, never composed: the heading is the directory’s declared title, the paragraph its description, the kinds its graphTypologies, each file row what the file says it is, and “used by” only a relation a diagram records.

The layout is Liquid, in bootstrap-tools/scripts/templates/readme/, beside the writer in the tools repository (bean xsqm). cat-harness’s readme:subgraphs resolves its own Extensions — a directory’s scope, an absent directory, the declared README — and calls that writer. Templates may {% include %} one another, Jekyll style, and read any declared field through kg. Change a template, run the command, commit the result. How to write one is liquid-templates.

It writes only between <!-- kg:subgraph:begin --> and :end. A README with no markers is left alone and reported: somebody wrote it. A generator that owns a whole README (bootstrap’s schema page) keeps that region intact.

A missing fact is a QA finding, not a blank. test/results/subgraph-readmes.qa-results.json records every directory with no title, no description or a description over 60 words, every declared directory absent from disk, and every unmarked README. Findings are reported, not failed: filling a declaration is its owner’s work. --check fails on a stale README — the READMEs are docs, committed — and judges the record rather than comparing it: it fails on a declared directory newly absent or a process newly unresolved against a baseline, and a missing baseline is UNKNOWN and not gated (bean 0dav). The record itself is a derived QA result whose home is the qa-reports branch (arc 3fva), so it is not something to regenerate and commit. A qa or health directory absent from the checkout is not an absent-directory finding: those kinds are leaving main (mayLeaveMain). It runs in CI and in the pre-commit hook (scripts/git-hooks/pre-commit).

Where the README render sits in the pipeline

Stage 1, after the current-state json/jsonld and before every other harness render, and fatal on failure — it is the file a reader opens first. The order and the reasoning are in render-order; do not restate them here.

“terms in readme like Skills, Process, Role, etc should be links in README.md s”. A capitalized term bootstrap defines links to its row in bootstrap/schemas/README.md wherever it appears in prose — not only at first use — so a reader who lands mid-page is one click from the definition.

  • Generated READMEs link them as they are written: subgraph-readmes runs linkTerms (bootstrap-tools/scripts/term-links.ts) over each declared description and each file’s description, with links made relative to the README by bootstrapTermTargets.
  • Hand-written READMEs are not rewritten by a generator — the folio owns the file. Link the terms as you write, and a guard catches a miss: graph.test.ts fails on any unlinked term in bootstrap/README.md’s prose (unlinkedTerms).
  • Left alone: code, headings, table header rows, existing links, and bold names — a Role’s name in a table (**Knowledge Graph Data Store**) is a name of its own, and a link inside it would split it.

Maintaining these files is memory work, not documentation work

README.md and AGENTS.md are declared assets with a declared purpose (ASSET_ROLES in schemas/cat-harness.ts — one place, per ROLE, not per asset). Keeping them true is part of agent-memory, and the owner put them there:

it is an asset and has a defined purpose (that is part of skills of mantiaing agent memroies)

The one distinction that governs how each is written: a file is read, memory is injected. Nothing truncates a file, so AGENTS.md carries what an agent must be able to look up; MEMORY.md carries what must arrive unasked and pays a 200-line budget for it.


folio-assistant — code under the Apache License 2.0, documentation under CC BY 3.0. Generated schema and API references are rebuilt from source on every push to main.

deployed b08c961 · 2026-10-23T23:11:09Z