The folio board — requirements as agreed

  1. What this is, and what it is not
  2. The need, in the requester’s words
  3. Round one — R1 to R7
    1. R1 — the board renders content, not only notes
    2. R2 — semantic zoom, with a declared threshold
    3. R3 — targetLabel SHALL publish its parts
    4. R4 — the linear floor
    5. R5 to R7 — the badge
  4. Round two — R8 to R14
  5. R15 and R16 — reconstructed, then confirmed
  6. R17 — render safety
  7. Round three — R18 to R32, the folio visualisation
    1. Geometry — R18 to R23
    2. The folio is a convention, not a page — R24 to R29
    3. An asset has three states, not two — R30
    4. A sticky’s face — R31 and R32
    5. R19’s second clause — reported open, then settled the same day
    6. R27 and question 1 — answered by the corpus, then settled
      1. Where the bytes land — the part that DID need deciding
    7. Still open from this round
  8. What the set produced
    1. One framing worth keeping
  9. Where this diverged from what was agreed

What this is, and what it is not

A design record. It states what was agreed for the folio board, in the CRDM run on issue #602 between 2026-09-20 and 2026-09-21, and it exists so that a person asking why is the board shaped like this has one place to look.

It is history, not instruction, and that is the line the owner drew: a skill carries what a competent practitioner needs in order to do the task, and everything past that — the reasoning, the options not taken, the measurements behind a threshold — is documentation. Every sentence below describes what was agreed on a date, against a repository in a state. Where the implementation has since diverged, the divergence is stated rather than smoothed. Nothing here tells an agent what to do — the rules an agent follows live in the cat-harness skills this set produced, and the processes live in the three executable BPMN diagrams beside them.

Filed here on the owner’s ruling, 2026-09-21:

this is a memory asset as part of design, so it goes into docs/ […] this was an asset as part of building a feature […] but it is not the content we want displayed itself.

And why it is in cat-harness/docs/ specifically, sharpened the same day: “if it is realated to some harness/feature/tool that detailed infromation/ design/planning/etc go into that harness’ docs/”. The board’s schema and behaviour live in cat-harness by the requester’s own layering, so the record of why they are shaped that way lives in that instance’s docs/ — not in the repository root’s, and not in a folio’s.

The class, and why fsh-guts was the tempting wrong answer for it, is in kg-contribution-offer; where inside docs/ such a record files is in placement.


The need, in the requester’s words

The whole set derives from this, kept verbatim because the layering sentence is precise and a paraphrase loses it:

landing folio should really be resizable, switchiing over to content avatars if content no longer lgeible. liek miro board. content vieweing panes are resizzble. but ALWAYS collapsable to linearly rendablee/just the docs. the todos/miro board is a static content overlays, so new content type that sits under todos/ but scema and behavhoir of folio=miro board is in cat-harness. carefull separte tools and schema.

notes can mvoe aroundin board. be attached to nothing or show attachment to content nodes based on relationships

stickies are icon on content (or its avatars) with badge of # if > 1. can click to open sticky/sticky panel

Two things in it are constraints rather than preferences, and both shaped everything after:

  • the todos/ ↔ cat-harness split — content under todos/, schema and behaviour in cat-harness, tools kept distinct from schema;
  • “ALWAYS collapsable to linearly rendablee” — an accessibility floor. A board that cannot be read linearly cannot be read by a screen reader, printed or translated.

Round one — R1 to R7

Agreed in Phase 3. Of the seven, exactly one introduced a genuinely new mechanism; that finding is why the implementation was ten small units rather than one large one.

requirementnew mechanism?
R1the board renders content, not only notesno — a second rendering of an existing relation
R2semantic zoom, with the threshold declared rather than a literalyes — the only one
R3targetLabel publishes its partsno — a bug fix
R4linear collapse, reachable without XSS (RELAXED 2026-10-02)no, but it constrains R1
R5the badge counts only when > 1no — two lines
R6badge and panel stay one queryno — a regression test for something already true
R7badges on avatarsno — R2 plus R6

R1 — the board renders content, not only notes

The board SHALL render content nodes alongside notes, and a note attached to a content node SHALL render at that node rather than at an independent position.

Accepted when a board with one note attached to a block and one attached to nothing renders both — the first at its block, the second at its declared position. Removing the attachment moves the note to its position rather than hiding it: “attached to nothing” is an existing declared state, not a new one.

R2 — semantic zoom, with a declared threshold

As the board shrinks, a card SHALL stop rendering its words and render its avatar instead. The threshold SHALL be declared data, not a literal in the renderer.

Accepted when the card renders text at a declared size and its avatar below it, a kind with no avatar takes GENERIC rather than rendering blank, and the behaviour is falsified in both directions — a test that only checks the shrunk case passes for a board that is always avatars.

Consumer burden: the threshold is emitted, never documented. “We don’t need to emit it — the renderer knows” is the smell this phase names.

R3 — targetLabel SHALL publish its parts

The published todo index SHALL emit a note’s page and node as separate fields, not only the composite targetLabel.

This one was a defect in something already published. The value is sec:<page>-<node>, page-qualified for a good reason — the bare node id what-is-not-built-yet existed on two pages. But any consumer asking which page is this note on had to string-manipulate: know the sec: prefix, know the separator is -, and know node ids themselves contain -, so the split is not unambiguous without already holding the page list.

Our own client escaped it by accident, matching on the whole string and never parsing, which is exactly why it went unnoticed.

R4 — the linear floor

As agreed, R4 read reachable without JavaScript. The board SHALL always be collapsible to a linear rendering; movement SHALL be keyboard-operable, and drag MAY be an accelerator but SHALL NOT be the only way in — this instance’s declared interaction profile is low-dexterity.

RELAXED by the owner, 2026-10-02, verbatim:

relax…“reachable without JavaScript” to “reachable without XSS”

So the floor is no longer no JavaScript. It is no injected script: the board’s linear rendering MAY fetch and build its content client-side, and MUST NOT be reachable by anything that executes markup it did not author.

What the relaxation does NOT touch, because the same paragraph states them independently: movement stays keyboard-operable, and drag stays an accelerator rather than the only way in. No byte-count argument reaches the interaction profile.

The subject of that sentence is the board, and saying so is a correction rather than a clarification. The relaxation was first recorded here with the measurement that prompted it — the docs site’s <nav>, 87.7 KB of identical bytes in each of 2,023 published pages — presented as the reason a board requirement had to move. It was not. Three facts settled it on 2026-10-02:

  • this document is not instruction — §”What this is, and what it is not” says so in the owner’s own ruling, and the rules an agent follows live in the cat-harness skills this set produced;
  • the skill that does bind the docs site is ui-accessibility, which binds “the knowledge-graph viewer, the docs site, the action-icon tiles, anything future” to WCAG 2.2 A/AA, keyboard reachability, 24 px targets, computed contrast, visible focus and announced change — and carries no no-JavaScript clause, because WCAG imposes none;
  • the docs navbar already requires JavaScript. mountNavIconRow, mountDocumentIndex and mountInstanceGraphs build three of its regions in the DOM at load, shipped in #959, and no gate objects.

So the docs nav never stood on R4 and needed no relaxation to be loaded once. What the relaxation does reach is the artefact R4 names: the board’s linear floor is the todo listing, and the tools layer’s test/linear-floor.e2e.ts is five javaScriptEnabled: false assertions over it. Relaxing R4 permits converting that listing to a fetch; it does not decide whether to, and the test’s header records what the static floor cost to build (bean 0jtj: with JavaScript off, a reader previously got “no note, no count, no hint that notes exist”).

The two obligations a client-side rendering owes are rules, so they live in the skill — print and PDF wait for load and render before printing; a load that fails says so rather than rendering as empty. Both were stated by the owner the same day, and both were first written here, in a document that disclaims being instruction, where nothing enforces them and no agent looking for the rule would find them. They are in ui-accessibility §”A rendering built client-side owes two things the static one gave for free”, with the measurements and the console.warn trap. This entry is the history of how they arrived, which is all this file is for.

R5 to R7 — the badge

The badge SHALL show a count only when more than one note is attached (R5); the badge and its panel SHALL be two readings of one query rather than a number somebody maintains (R6); and an avatar carries the same badge from the same query as the card it replaced (R7).

A badge that can disagree with its own panel is the defect designed out, and it is designed out by derivation rather than by discipline.


Round two — R8 to R14

These arrived after the requirements gateway had closed, and were checked against the declared model rather than assumed compatible. They are additive: nothing already agreed was invalidated.

requirementrelation to round one
R8the dynamic board is an overlay; the standard rendering is a simple tile listingsharpens R4 — the floor is a tile listing, and movement is additive over it
R9everything starts as an avatar; opening is an explicit act, [x] closes backsplits R2 into two mechanisms — an explicit open/close a person performs, and automatic zoom by size. Conflating them is the defect to design out
R10an open card is a panel with fixed controls, plus others depending on content; each content type controls its own renderingthe kind owned the zoom threshold; it now owns its whole rendering, under a chrome contract it cannot reach
R11fsh-guts is a tile, and [fishbones] puts content into it as a confirmed actionnew; reuses the declared non-rendered trashcan, whose founding rule is do not delete unless explicit confirm
R12create a new sticky note — itself a tilenew
R13filter the board by kind properties, interactivelya reader’s filter at view time, distinct from the board document’s declared filter. Two things that must not become one field
R14resize open content, move it, drag and droprides R4’s floor: drag is an accelerator, keyboard operation is not optional

Recorded alongside them as scope rather than as a requirement: “do all the skills and bpmn workflows” — each of these gets its skill and its diagram, not only its schema.


R15 and R16 — reconstructed, then confirmed

They were referenced but never written down. Bean 51wf is assigned “R2, R9, R15” and bean zsah “R12, R16”, and no comment on #602 states either requirement. When this record was first written they were therefore reported as undefined, with the recoverable meaning marked as inference — because a design record that fills its own gaps with plausible sentences is worse than one that names them, since invented text reads exactly like agreed text.

The owner confirmed both readings on 2026-09-21 (“agreed on r15/16”), so they are now agreed requirements rather than reconstructions:

requirementhow it was recovered
R15selecting any part of an open window raises it — z-order is a reader’s act51wf’s unit description, “z-order with raise-on-select”
R16a tile opens the existing visualisation; nothing builds a second vieweran aside on the issue, “R12/R16 said a tile opens the existing visualisation”

Both were implemented to that reading before it was confirmed, which is why the gap mattered: the code was already committed to an interpretation nothing had stated. The provenance is kept rather than tidied away — a requirement recovered from a bean’s unit description and later ratified is not the same artefact as one written down at the time, and a reader re-checking this subsystem should know which it is.


R17 — render safety

The last instruction became its own requirement: “skill tool hints for XSS… lazy load… assume assets in KG accessible, dynamic render where can”.

Only part of it has a subject. The Url half shipped as schemas/safe-url.ts, default-deny, with a source-level check that a renderer ignoring it fails a test rather than shipping. The Tool-output render vocabulary did not, and the reason is recorded rather than deferred silently: nothing renders a tool’s output even now, so choosing the terms would be choosing them with no consumer to constrain them — a declared safety property whose check does not answer its claim is worse than prose, because prose is honestly unchecked.


Round three — R18 to R32, the folio visualisation

These arrived on 2026-09-21, after R1–R17 had shipped and while v0jv’s tile dock was still unmerged. They are the first round that is about the folio as a surface rather than about the board’s mechanics, and two of them corrected work that had not yet landed — which is why they are recorded as a round rather than folded into the requirements they revise. Reading them as amendments would lose the fact that a shipped-to-branch design was turned round before a reader ever saw it.

Geometry — R18 to R23

requirementin the requester’s words
R18the board’s spacing SHALL be condensed“to much padding between panels, condense”
R19the board’s tiles SHALL be the same square tiles as the LHS navbar’s expanding menu, or a theme/avatar where that is the better fit — which is which is R19b, settled below“i meant to use same SQUARE TILES taht are in the expanding menu of LHS navbar OR use theme/avatars as appraopriate”
R20no sub-panels. One open, Miro-like board; everything lives on fa-sticky-board / fa-landing-board“i dont want sub-panels of the folio, just one open (miro-like) board”
R21stickies SHALL be closed avatars, and small“THE STICKIES MUST BE CLOSED AVATRS AND SMALL”
R22the folio visualisation SHALL have no rounded corners“drop all the rounded corners… too much dead space”
R23the square tiles SHALL be lined up along the top of the board, and the whole strip SHALL slide up when the reader does not want it“lets have the square tiles lined up on the top of the folio-sicky-board-landingpanel whole slides up if user doesnt want”

R23 reverses v0jv, and that bean’s own text says why the reversal was available. v0jv quoted the owner as “stacked around (bottom?) of folio” — the parenthesis and the question mark are the requester’s, and were carried into the bean verbatim precisely because they marked an open question rather than a decision. The dock was built to the bottom, closed, and R23 settles both halves the other way: top, and open by default. The default is the half that would have shipped silently — tiles a reader must open before they can see what a folio offers read as absent, which is the complaint v0jv itself opened with about the in-flow row.

R21 and R22 share one reason, and it is not taste. “Too much dead space” is the stated cost in both. A corner radius on a card that is 320px wide spends its corners on nothing; a sticky that opens by default spends the board on one note. Recording the reason matters because the two requirements have different limits: dead space is recoverable from a panel and is not recoverable from a pill, which is why the count badges keep their 999px and the record says so rather than leaving a reader to discover the exception in the stylesheet.

The folio is a convention, not a page — R24 to R29

This is the layering statement of the round, and it is the one a paraphrase would lose:

Paraphrased: these requirements are really for how the “folio” visualisation from cat-harness should work, and by convention it should be available on any harness for a consistent feel. The content layers (the IRIS replica, the SMART IGs and so on) are libraries a user browses, and the cat-harness “folio” is consistent across them; todos and the like can reference the cross-KG library. A content layer may implement its own landing page or have its own harness.

requirementnote
R24the folio visualisation is cat-harness’s, and SHALL be available by convention on any harness, for a consistent feelthe harness supplies the folio; the instance supplies the library
R25a reader SHALL be able to pull their folio down over whatever they are browsing“the user in visualization should be able to pull down their folio”
R26cat-harness SHALL declare folio/ as the directory of the reader’s own contenta declared ContentDirectory, like every other graph
R27materialised assets from the static KG SHALL live in the folio — settled 2026-09-21, see below“clarifying if KG is static w/ materizlied…, materialized should live in folio”
R28new documents a reader creates or links to go in folio/linking is a creation act here, not a reference
R29todos and sticky notes MAY reference things across KG libraries“todo, etc can refernces cross KG library”

A content library is not a folio, and the distinction is what R24 buys. the IRIS replica and the smart-* instances are things a reader browses; each may implement its own landing page and its own harness. What travels with the reader between them is the folio — their notes, their materialised assets, their links — and it looks and behaves the same in each because it belongs to cat-harness rather than to any one library. Consistency is the requirement, not similarity: two harnesses that each build a good folio have failed R24.

An asset has three states, not two — R30

Clarified in a follow-up the same day, and it is the requirement most likely to be implemented as two states by somebody reading quickly:

pulling down folio panel = glass/window on which stikcy notes/avatrs of materialized assets (including materialzied KG like bootrstap, cat-harness are visualized. they can also be closed and returned to their homes (e.g. “back in library”, matieral asset still in folio/ but not displayed in folio, need to go back to library and pull it out to folio display window)

requirement
R30an asset SHALL have three distinguishable states: in the library (not the reader’s), in folio/ but not displayed (materialised, theirs, off the glass), and on the glass (displayed in the pulled-down folio). Closing it from the glass returns it to the second state, never the first; returning it to the first is a separate act performed from the library

Why three rather than two. A two-state model — in the folio or not — makes closing a sticky and un-materialising an asset the same gesture, so a reader tidying their glass silently discards work. The middle state is what makes closing safe: the asset stays in folio/, and the way back onto the glass is “go back to the library and pull it out”. That is also l4zi applied one level up — the inverse of close must be reachable, and here it is reachable from a different surface than the one that closed it, which is a thing a design can get wrong without any single screen looking wrong.

A sticky’s face — R31 and R32

requirementin the requester’s words
R31a closed sticky SHALL show its condensed own text — whitespace, newlines, bullets and list markers stripped. No second string“no, not hidden label, that’s new data to maintain… just the condensend text”, “(strip whitesaplnce, newlines, bullets….)”
R32a sticky SHALL still be about 2.5 physical inches on a large MacBook screen“sticky should stilll be ~2.5" in large macbook screen”

R31 rejected the recommendation that was put to the owner, which had been a hidden accessible label alongside the visible summary. The reason given is the better one and is worth keeping: “that’s new data to maintain” — a second string is a second thing that can drift from the first, and a hidden label that disagrees with the visible text is a defect no reader can see. Condensing the text the sticky already carries has no such failure mode.

R32 is a PHYSICAL measurement, and CSS cannot express it. A CSS in is defined as exactly 96 CSS px and is not an inch of glass. The 16” MacBook Pro named in the requirement is 3456x2234 over a 16.2” diagonal — about 13.6” of width — and presents 1728 CSS px across it, so one physical inch is roughly 127 CSS px and 2.5” is about 318px. 20rem is 320px. Writing 2.5in would have rendered about 1.9” and looked like the requirement had been ignored. The device is named in the code comment because the conversion is device-dependent: the same 20rem is about 3.7 physical inches on a 27” 1440p display, and that is correct rather than a defect — the size was chosen for the screen the requester uses.

R21 and R32 are a pair, not a contradiction. “Small” without R32 shrinks the card until its condensed text is unreadable; R32 without “small” leaves the dead space R18 was about. Either alone satisfies half the round and looks like it satisfied all of it, which is why both are asserted.

R19’s second clause — reported open, then settled the same day

It was recorded here as undefined, and deliberately: “OR use theme/avatars as appropriate” named a choice that nothing in the corpus said how to make, and a rule invented to close the gap reads exactly like a rule that was agreed.

The owner settled it on 2026-09-21, choosing declaration ownership from four options put to them:

the rule
R19ba thing renders as a tile iff the harness declares it; as an avatar iff the folio holds it (a note, a document, a materialised asset in library/ or uploads/). A theme is how either one looks, never a third kind
R19ca tile is one of two kinds. A functional tile (a directory or sub-graph) is the visual interface to a skill, implemented by a tool chosen from potentially several — so it names the skill, never the tool. A content tile stands for a set of schema instances

R19c is the owner’s correction to R19b, the same day, and it is recorded as its own requirement rather than folded in because R19b is still true and still insufficient:

content tiles = todos are schema instances. different from dir/sub-graph tiles = functional (they infact visual interfaces to skills implemented by some (potential choice of) tools)

Who declared it answers tile or avatar and stops. It does not say what a tile is, and the two kinds bind to different things, resolve different things when opened, and mean opposite things when empty:

functionalcontent
bound toa skilla schema
resolved on opena tool implementing itthe instances
empty meansa gap to report — a skill nothing implements is unreachablea real state — “nothing outstanding” is not “broken”

The operative half is “names the skill, never the tool.” schemas/tool-types.ts exists to make tools interchangeable — “Two tools satisfying one skill — beans-cli and beans-manual — must declare the same input type, or ‘these are interchangeable’ is an assertion nothing can verify” — so a tile bound to a tool breaks the moment the other tool is chosen. The choice is the point.

This is a shape the corpus already had, which is the argument for it: VisualiserDeclarationSchema takes “one path, or several visualisations”, so a sub-graph may be rendered more than one way exactly as a skill may be implemented more than one way. R19c names the two latitudes as the same latitude.

The empty asymmetry was already half-written too. head_custom.html records the content side — “a board that opens empty is indistinguishable from a person with nothing outstanding, and those are opposite facts” — while pb04 covers the functional side: an affordance that promises and delivers nothing.

The provenance is kept rather than tidied away, the same as R15/R16: this is a rule recovered by argument and then ratified, not one written down at the time, and a reader re-checking the board should know which it is.

Two candidate rules were falsified by R30 before the choice was put. R30 places “avatars of materialized assets (including materialized KG like bootstrap, cat-harness)” on the glass — so a whole knowledge graph renders as an avatar. That kills “many behind it → tile” outright, and it kills “opens a viewer over a set → tile” too, since opening that avatar does open a viewer over a set. Declaration ownership survives because a materialised asset is in the reader’s folio/ however big it is.

It is recognition rather than invention, which is the reason to prefer it: harness-tiles already carried “a tile is the harness’s, not the node’s”, and the owner’s own 2026-09-20 line — “if harness declares visaluzers, those should have tile. defaults to theme, but new can be changed” — already said both halves. The gap was that nobody had read them as an answer to this question.

The rule lives in harness-tiles, with a pointer from board-windows. There is no check, and that is stated in the skill rather than left as an omission: nothing classifies a folio item yet, so a gate would be a declared property whose check cannot answer its own claim — R17’s lesson, one round later.

R27 and question 1 — answered by the corpus, then settled

The requester’s own parenthetical marked this unsettled: “clarifying if KG is static w// materizlied…”. Measured 2026-09-21, the answer was already in the corpus in three places, none of which had been read as answering it:

the content core’s schemas/materialization.tsthree states — referenced (we know where, we hold no bytes), materialized (the bytes are here), unknown (we have not established which) — with no default, plus five gates, and localPath present iff materialized
cat-harness/processes/library/materialize-remote.bpmnthe act itself, as an executable STRICT process running five gates in a fixed order; unknown on any one keeps the node referenced
skills/kg/kg-core/directory-conventions.mdthe catalogue graph typology — “a remote catalogue modelled BY REFERENCE … Distinct from library: that is content which IS here, this is the shape of a collection of which almost none is”

So: yes. The knowledge graph is static and modelled by reference; materialisation is a separate, gated, checked act that produces bytes. Question 1 needed no decision — it needed reading.

Where the bytes land — the part that DID need deciding

R27 says “materialized should live in folio”, and the IRIS replica materialises into library/ (items and covers) and uploads/ (source PDFs). Neither is folio/. Three readings were put to the owner; they chose the folio is the reader’s REPOSITORY, of which library/ and uploads/ are already declared parts:

{ id: "uploads", path: "uploads/", dependents: "reproduce", graphTypologies: ["uploads"] },
{ id: "library", path: "library/", dependents: "reproduce", graphTypologies: ["library"] },

dependents: "reproduce" (retired 2026-09-30; now the perInstance flag on the kind) was the schema saying this is the reader’s own copy — the exact fact R27 reaches for, and it predates R27.

The literal reading would have broken a consumer, and that was found AFTER the options were tabled. It is recorded here for that reason rather than folded into the argument as though it had been known. schemas/materialization.ts states it outright — corpus-grep searches library/ only, so a node materialised into folio/ would read as ABSENT to every consumer. That is the same defect the schema forbids one state up, where collapsing referenced into materialized hides a node nobody holds. Had the owner chosen the literal reading, it would have been a migration into a known breakage, and the recommendation put alongside the options did not say so because this had not been measured yet.

folio/ is also the renderable graph typology, so the literal reading would additionally have put a 40 MB PDF into the site build.

R27 is therefore already satisfied, and already gated. the content core’s scripts/check-catalogue.ts checks that a materialized claim names bytes that exist, with local-path.ts’s three states (ok / missing / unknown) and bean yl5w’s scar behind it: three claims resolved to nothing and the gate said clean.

Still open from this round

The library view already renders all three states — this was recorded here as open twice, and both were wrong (bean mm36, measured 2026-09-22).

gen-iris-pages.ts emits materialized, referenced and unknown as distinct badges, with stateBadge(k.materialization?.state ?? "unknown") — defaulting to unknown, never to referenced, which is the schema’s no-default rule honoured at the render layer rather than only in the type. On origin/gh-pages a real collection page carries state materialized ×2 and state referenced ×1.

The first look was misleading and the trap is worth recording: the source tree holds three committed the IRIS replica’s docs/ pages and the badges appear on only one of them — kg-to-portal.html, the legend. The catalogue pages are generated at build time, so a source-tree grep says “almost nowhere” about something that is in fact everywhere. This had to be measured on the published site.

The owner’s own words are in that generator’s header and are about exactly this — “collectiosn w/ nothing greyed out” — with the reasoning beside them: “a row that says referenced reads as ‘upstream, not here’, which is the actual state and the distinction the whole catalogue-by-reference model is for.”

What this does to the ordering question: it dissolves it. The open decision was R25’s glass versus R30’s library half, on R30’s warning that building the glass alone ships the two-state model. The library half is not missing — what is missing is one action, the way from a library row onto the glass (“go back to the library and pull it out to folio display window”, the 1 → 3 transition). The view that action hangs off exists and already says which items are held. So the glass and its pull-out action are one unit of work, and there is nothing to sequence.

F8/F9 is blocked on R25, structurally rather than by scheduling — bean jpjt, settled 2026-09-21.

The gap is measured: on the deployed preview the IRIS replica’s index.html loads docs-ui.js 0 times and carries 0 boards and 0 tiles, against 9 / 1 / 28 on the folio-assistant landing page. A reader browsing that library has no folio at all, not a degraded one.

The delivery mechanism is decided — three ways were tabled and the owner chose an exported mount fragment plus a gate, the shape staging-banner.ts already proves, reusing bodyInsertionPoint() so that ur84 cannot recur.

What blocks it is that nothing would mount. the IRIS replica pages are complete standalone HTML from their own generator — their own <style> built from the captured IRIS theme, no Jekyll, no head_custom.html — because the page is a faithful replica and the “ingested copy — not WHO, not live” banner is its requirement 1. Loading docs-ui.js there is safe (all seven mounts guard themselves and return without their markers), and that is the whole problem:

surfacerequireson a replica page
launchera sidebar headerabsent
board#main-content / .main-content / <main>, inserted at the topabsent, and the top is the IRIS content
language bar.main-contentabsent

Every visible folio surface today is bound to just-the-docs page furniture, and giving a library page that furniture means making it look like folio-assistant — the one thing the IRIS replica exists not to do.

R25 is therefore the unblock rather than the next item. A pull-down glass comes down OVER what is being browsed, so it is the only folio surface that does not require the host page to look like the harness. Shipping the mount fragment before it would create a gate for a thing with no consumer — R17’s lesson, a third time.

Both tracked on issue #796.


What the set produced

Each requirement’s artefacts, so the record points at the code rather than describing it:

graphwhat landed
cat-harness (BPMN)board-open-close, board-relocate, board-place-note — executable diagrams with hand-authored Diagram Interchange
cat-harness (skills)board-windows, board-diagram-interchange, harness-tiles, plus the board-renderer role and actor
schemasboard-positions, window-stack, panel-chrome, reader-filter, semantic-zoom, safe-url

One framing worth keeping

A board is a diagram OF a folio, in the OMG sense that a BPMNDiagram is a diagram of a bpmn:process. The folio carries what is true; the layout layer carries where it was drawn; the arrow runs folio → board → position → note and never back.

Two consequences follow, and both are load-bearing: board-positions may not put x/y on a note, for the same reason BPMN does not put coordinates on a task — and a folio is complete with no board. Deleting every board loses layout and no content.


Where this diverged from what was agreed

One place, and it is recorded on the bean as well.

51wf’s checklist said “every card starts as its avatar, at any size”, which cannot be literal alongside R2 — if cards are avatars at every width, R2 has nothing to do. It was read as “starts closed”: a closed card shows its words above the declared threshold and its avatar below it, with the avatar present at every width as the control that opens the window. Flagged rather than silently chosen, and confirmed by the owner on 2026-09-21.


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 dev