{
  "chapters": [
    {
      "group": "format",
      "id": "anchored-inline-objects",
      "order": 13,
      "title": "Anchored & inline objects"
    },
    {
      "group": "format",
      "id": "color-swatches",
      "order": 10,
      "title": "Color & swatches"
    },
    {
      "group": "format",
      "id": "companion-formats",
      "order": 21,
      "title": "Companion formats"
    },
    {
      "group": "format",
      "id": "conditional-text",
      "order": 18,
      "title": "Conditional text"
    },
    {
      "group": "format",
      "id": "cross-references-hyperlinks",
      "order": 14,
      "title": "Cross-references & hyperlinks"
    },
    {
      "group": "plugin",
      "id": "data",
      "order": 36,
      "title": "Plugin · data"
    },
    {
      "group": "format",
      "id": "edge-cases",
      "order": 23,
      "title": "Edge cases & degradation"
    },
    {
      "group": "editor",
      "id": "editor-shell",
      "order": 27,
      "title": "Editor shell"
    },
    {
      "group": "editor",
      "id": "editor-tools",
      "order": 28,
      "title": "Editor tools & gestures"
    },
    {
      "group": "format",
      "id": "effects-transparency",
      "order": 12,
      "title": "Effects & transparency"
    },
    {
      "group": "format",
      "id": "foundations",
      "order": 1,
      "title": "Foundations"
    },
    {
      "group": "format",
      "id": "frames-paths",
      "order": 7,
      "title": "Frames & paths"
    },
    {
      "group": "format",
      "id": "geometry-coordinates",
      "order": 3,
      "title": "Geometry & coordinates"
    },
    {
      "group": "plugin",
      "id": "image",
      "order": 35,
      "title": "Plugin · image"
    },
    {
      "group": "format",
      "id": "images-graphics",
      "order": 11,
      "title": "Images & graphics"
    },
    {
      "group": "format",
      "id": "interactive-dynamic",
      "order": 19,
      "title": "Interactive & dynamic"
    },
    {
      "group": "format",
      "id": "layers",
      "order": 16,
      "title": "Layers"
    },
    {
      "group": "format",
      "id": "layout-model",
      "order": 4,
      "title": "Layout model"
    },
    {
      "group": "format",
      "id": "master-spreads-overrides",
      "order": 15,
      "title": "Master spreads & overrides"
    },
    {
      "group": "format",
      "id": "package-anatomy",
      "order": 2,
      "title": "Package anatomy"
    },
    {
      "group": "format",
      "id": "parser-internals",
      "order": 25,
      "title": "Parser internals"
    },
    {
      "group": "plugin",
      "id": "plugin-doc",
      "order": 34,
      "title": "Plugin · doc"
    },
    {
      "group": "plugin",
      "id": "plugin-draw",
      "order": 32,
      "title": "Plugin · draw"
    },
    {
      "group": "plugin",
      "id": "plugin-pdf",
      "order": 39,
      "title": "Plugin · pdf"
    },
    {
      "group": "plugin",
      "id": "plugin-platform",
      "order": 31,
      "title": "Plugin platform"
    },
    {
      "group": "plugin",
      "id": "plugin-publish",
      "order": 38,
      "title": "Plugin · publish (IDML/PDF interchange)"
    },
    {
      "group": "plugin",
      "id": "plugin-slide",
      "order": 35,
      "title": "Plugin · slide"
    },
    {
      "group": "plugin",
      "id": "plugin-web",
      "order": 33,
      "title": "Plugin · web"
    },
    {
      "group": "format",
      "id": "round-tripping",
      "order": 22,
      "title": "Round-tripping"
    },
    {
      "group": "editor",
      "id": "scripting",
      "order": 29,
      "title": "Scripting"
    },
    {
      "group": "format",
      "id": "sections-numbering-variables",
      "order": 17,
      "title": "Sections, numbering & variables"
    },
    {
      "group": "server",
      "id": "server",
      "order": 30,
      "title": "Server"
    },
    {
      "group": "plugin",
      "id": "sheet",
      "order": 37,
      "title": "Plugin · sheet"
    },
    {
      "group": "format",
      "id": "stories-text",
      "order": 5,
      "title": "Stories & text"
    },
    {
      "group": "format",
      "id": "styles",
      "order": 6,
      "title": "Styles"
    },
    {
      "group": "format",
      "id": "tables",
      "order": 9,
      "title": "Tables"
    },
    {
      "group": "format",
      "id": "tagged-xml",
      "order": 20,
      "title": "Tagged XML"
    },
    {
      "group": "corpus",
      "id": "test-corpus",
      "order": 26,
      "title": "Test corpus"
    },
    {
      "group": "format",
      "id": "the-renderer",
      "order": 24,
      "title": "The renderer"
    },
    {
      "group": "format",
      "id": "typography",
      "order": 8,
      "title": "Typography"
    }
  ],
  "components": [
    {
      "group": "core",
      "id": "core.parser",
      "order": 1,
      "title": "Parser"
    },
    {
      "group": "core",
      "id": "core.renderer",
      "order": 2,
      "title": "Renderer"
    },
    {
      "group": "core",
      "id": "core.mutation",
      "order": 3,
      "title": "Mutation"
    },
    {
      "group": "core",
      "id": "core.canvas-wasm",
      "order": 4,
      "title": "Canvas/WASM"
    },
    {
      "group": "core",
      "id": "core.sdk",
      "order": 5,
      "title": "SDK"
    },
    {
      "group": "editor",
      "id": "editor.canvas",
      "order": 6,
      "title": "WASM Canvas"
    },
    {
      "group": "editor",
      "id": "editor.shell",
      "order": 7,
      "title": "Shell"
    },
    {
      "group": "editor",
      "id": "editor.panel",
      "order": 8,
      "title": "Panel"
    },
    {
      "group": "editor",
      "id": "editor.gesture",
      "order": 9,
      "title": "Gesture"
    },
    {
      "group": "editor",
      "id": "editor.script",
      "order": 10,
      "title": "Script"
    },
    {
      "group": "editor",
      "id": "editor.styleguide",
      "order": 11,
      "title": "Styleguide"
    },
    {
      "group": "server",
      "id": "server.api",
      "order": 12,
      "title": "API"
    },
    {
      "group": "plugin",
      "id": "plugin.api",
      "order": 13,
      "title": "API"
    },
    {
      "group": "plugin",
      "id": "plugin.draw",
      "order": 14,
      "title": "Draw"
    },
    {
      "group": "plugin",
      "id": "plugin.web",
      "order": 15,
      "title": "Web"
    },
    {
      "group": "plugin",
      "id": "plugin.image",
      "order": 16,
      "title": "Image"
    },
    {
      "group": "plugin",
      "id": "plugin.data",
      "order": 17,
      "title": "Data"
    },
    {
      "group": "plugin",
      "id": "plugin.sheet",
      "order": 18,
      "title": "Sheet"
    },
    {
      "group": "plugin",
      "id": "plugin.publish",
      "order": 19,
      "title": "Publish"
    },
    {
      "group": "plugin",
      "id": "plugin.pdf",
      "order": 20,
      "title": "PDF"
    },
    {
      "group": "plugin",
      "id": "plugin.doc",
      "order": 21,
      "title": "Doc"
    },
    {
      "group": "plugin",
      "id": "plugin.slide",
      "order": 22,
      "title": "Slide"
    }
  ],
  "evidence": [
    {
      "component": "core.parser",
      "id": "core/nextest-unit",
      "kind": "unit",
      "last_seen": "2026-10-18T07:59:59.319Z",
      "runs": 1
    },
    {
      "component": "core.canvas-wasm",
      "id": "core-script/nextest-canvas-wasm",
      "kind": "unit",
      "last_seen": "2026-10-18T07:59:59.319Z",
      "runs": 1
    },
    {
      "component": "core.sdk",
      "id": "core/idml-viewer-vitest",
      "kind": "unit",
      "last_seen": "2026-10-18T07:16:44.226Z",
      "runs": 1
    },
    {
      "component": "editor.canvas",
      "id": "editor/playwright-e2e",
      "kind": "e2e",
      "last_seen": "2026-10-18T05:12:58.574Z",
      "runs": 1
    },
    {
      "component": "editor.panel",
      "id": "editor/playwright-panels",
      "kind": "e2e",
      "last_seen": null,
      "runs": 0
    },
    {
      "component": "editor.canvas",
      "id": "editor/playwright-journey",
      "kind": "e2e",
      "last_seen": null,
      "runs": 0
    },
    {
      "component": "plugin.api",
      "id": "plugin-sdk/vitest",
      "kind": "unit",
      "last_seen": "2026-10-18T06:37:31.551Z",
      "runs": 1
    },
    {
      "component": "core.renderer",
      "id": "state/conformance",
      "kind": "conformance",
      "last_seen": null,
      "runs": 0
    },
    {
      "component": "plugin.draw",
      "id": "plugin-draw/vitest",
      "kind": "unit",
      "last_seen": "2026-10-16T07:57:44.992Z",
      "runs": 1
    },
    {
      "component": "plugin.web",
      "id": "plugin-web/vitest",
      "kind": "unit",
      "last_seen": "2026-10-17T07:20:17.587Z",
      "runs": 1
    },
    {
      "component": "plugin.web",
      "id": "plugin-web/nextest",
      "kind": "unit",
      "last_seen": "2026-10-17T07:15:37.781Z",
      "runs": 1
    },
    {
      "component": "plugin.image",
      "id": "plugin-image/vitest",
      "kind": "unit",
      "last_seen": "2026-10-13T11:19:55.08Z",
      "runs": 1
    },
    {
      "component": "plugin.image",
      "id": "plugin-image/nextest",
      "kind": "unit",
      "last_seen": "2026-10-13T11:20:42.125Z",
      "runs": 1
    },
    {
      "component": "plugin.data",
      "id": "plugin-data/vitest",
      "kind": "unit",
      "last_seen": "2026-10-17T07:28:03.285Z",
      "runs": 1
    },
    {
      "component": "plugin.data",
      "id": "plugin-data/nextest",
      "kind": "unit",
      "last_seen": "2026-10-17T07:26:48.181Z",
      "runs": 1
    },
    {
      "component": "plugin.doc",
      "id": "plugin-doc/vitest",
      "kind": "unit",
      "last_seen": "2026-10-17T17:14:10.682Z",
      "runs": 1
    },
    {
      "component": "plugin.slide",
      "id": "plugin-slide/nextest",
      "kind": "unit",
      "last_seen": "2026-10-17T17:16:33.627Z",
      "runs": 1
    },
    {
      "component": "plugin.slide",
      "id": "plugin-slide/vitest",
      "kind": "unit",
      "last_seen": "2026-10-17T17:18:23.366Z",
      "runs": 1
    },
    {
      "component": "plugin.doc",
      "id": "plugin-doc/nextest",
      "kind": "unit",
      "last_seen": "2026-10-17T17:16:18.632Z",
      "runs": 1
    },
    {
      "component": "plugin.sheet",
      "id": "plugin-sheets/vitest",
      "kind": "unit",
      "last_seen": "2026-10-18T04:47:34.939Z",
      "runs": 1
    },
    {
      "component": "plugin.sheet",
      "id": "plugin-sheets/nextest",
      "kind": "unit",
      "last_seen": "2026-10-18T04:46:31.651Z",
      "runs": 1
    },
    {
      "component": "plugin.publish",
      "id": "plugin-publish/vitest",
      "kind": "unit",
      "last_seen": "2026-10-18T07:01:26.364Z",
      "runs": 1
    },
    {
      "component": "plugin.publish",
      "id": "plugin-publish/nextest",
      "kind": "unit",
      "last_seen": "2026-10-18T07:01:17.337Z",
      "runs": 1
    },
    {
      "component": "plugin.pdf",
      "id": "plugin-pdf/vitest",
      "kind": "unit",
      "last_seen": "2026-10-18T07:01:24.546Z",
      "runs": 1
    },
    {
      "component": "plugin.pdf",
      "id": "plugin-pdf/nextest",
      "kind": "unit",
      "last_seen": "2026-10-18T07:01:17.337Z",
      "runs": 1
    },
    {
      "component": "core.renderer",
      "id": "core/fidelity-local",
      "kind": "visual",
      "last_seen": null,
      "runs": 0
    },
    {
      "component": "core.renderer",
      "id": "core/fidelity-ubuntu",
      "kind": "visual",
      "last_seen": "2026-10-18T07:21:09.397Z",
      "runs": 1
    },
    {
      "component": "core.renderer",
      "id": "core/fidelity-macos",
      "kind": "visual",
      "last_seen": "2026-10-18T07:26:12.575Z",
      "runs": 1
    },
    {
      "component": null,
      "id": "core/fidelity-measurements-ubuntu",
      "kind": "benchmark",
      "last_seen": "2026-10-18T07:21:09.397Z",
      "runs": 1
    },
    {
      "component": null,
      "id": "core/fidelity-measurements-macos",
      "kind": "benchmark",
      "last_seen": "2026-10-18T07:26:12.575Z",
      "runs": 1
    },
    {
      "component": "core.renderer",
      "id": "core/vello-lavapipe",
      "kind": "unit",
      "last_seen": "2026-10-18T07:02:50.654Z",
      "runs": 1
    },
    {
      "component": null,
      "id": "editor/wasm-budget",
      "kind": "benchmark",
      "last_seen": "2026-10-18T05:59:46.951Z",
      "runs": 1
    }
  ],
  "features": [
    {
      "chapter": "anchored-inline-objects",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "SpaceOrAlign on both axes (W1.6); line-ref metrics real since W1.16 (LineRefMetrics)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:57:42.667Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "anchored-inline-objects.anchor-alignment",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Anchored alignment (space-or-align relative options)"
    },
    {
      "chapter": "anchored-inline-objects",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "above/below + custom-position reference points; LineXHeight/CapHeight/TopOfLeading real metrics + PageMargins resolution (W1.16 LineRefMetrics, pipeline/anchored.rs). A custom-anchored object is placed against the line holding its anchor, and takes the anchored-object options it leaves out from its object style, attribute by attribute, with the category on (core ADR 143; object-style-options matches InDesign to 0.05 pt).",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "Anchored panel (W2.12) surfaces the anchored* PropertyEntries for a frame anchored into a story (see editor-shell.panels.anchored; anchored-panel e2e green 2026-06-11)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:57:42.667Z",
        "headline": "All linked evidence is passing (5 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 5,
          "passed": 5,
          "skipped": 0
        }
      },
      "id": "anchored-inline-objects.anchored-frames",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Anchored frames (above/below line placement)"
    },
    {
      "chapter": "anchored-inline-objects",
      "claims": [
        {
          "component": "core.mutation",
          "note": "W1.16 AnchoredObjectSetting SetProperty paths: AnchoredPosition / AnchoredXOffset / AnchoredYOffset / AnchoredSpineRelative / AnchoredLockPosition (apply locates the setting in the story runs; snapshot inverse; unknown-frame rejected). CREATE ADDED 2026-08-09 by audit: this note covered only the SetProperty paths, so the row read as though an anchored object could be adjusted but not made. `Mutation::InsertAnchoredFrame { story_id, offset, width, height, image_uri }` has shipped at protocol v52 (paged-canvas channel.rs, paged-mutate operation.rs) — it is how plugin-doc lands a Word inline image as a native anchored frame rather than a placeholder.",
          "status": "shipped"
        },
        {
          "component": "editor.canvas",
          "note": "anchored-object offset mutation exercised end-to-end on the editor canvas (e2e/anchored-ops.spec.ts green)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:06.543Z",
        "headline": "All linked evidence is passing (6 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 6,
          "passed": 6,
          "skipped": 0
        }
      },
      "id": "anchored-inline-objects.anchored-ops",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Anchored-object mutations"
    },
    {
      "chapter": "color-swatches",
      "claims": [
        {
          "component": "core.parser",
          "note": "FillColor on <CharacterStyleRange> read into the run model; corpus-verified, 31 envato packs (W4.17)",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "per-run glyph fill resolved + painted (color_paint.rs per-run FillColor/FillTint, :710) (W4.17)",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "characterFillColor",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "Character panel",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:05.126Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "color-swatches.character-fill",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Character fill color"
    },
    {
      "chapter": "color-swatches",
      "claims": [
        {
          "component": "core.parser",
          "note": "<ColorGroup> + ColorGroupSwatches read into Graphic.color_groups (graphic.rs:56); corpus-verified across 57 envato packs + generated swatches.idml (W4.17). No independent renderer path — member swatches render when applied (fill-stroke-apply)",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "Color groups panel + Swatches grid assignment",
          "status": "shipped"
        },
        {
          "component": "editor.script",
          "note": "paged.colorGroups()",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:05.262Z",
        "headline": "All linked evidence is passing (21 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 21,
          "passed": 21,
          "skipped": 0
        }
      },
      "id": "color-swatches.color-groups",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Color groups (CRUD + membership)"
    },
    {
      "chapter": "color-swatches",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "re-verified 2026-08-17: no ColorOverride handling anywhere in paged-renderer (the only core mentions are paged-gen fixture builders); the lone green conformance row on this cell is name-routed parse-level evidence — the doc parses, no override logic renders. Stays planned",
          "status": "planned"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "color-swatches.color-overrides",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "ColorOverride specials (paper/black/registration)"
    },
    {
      "chapter": "color-swatches",
      "claims": [
        {
          "component": "core.mutation",
          "note": "setDocumentDefaults (app-level, non-undoable)",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:47:09.22Z",
        "headline": "All linked evidence is passing (11 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 11,
          "passed": 11,
          "skipped": 0
        }
      },
      "id": "color-swatches.document-defaults",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Document defaults for new objects (fill/stroke/weight)"
    },
    {
      "chapter": "color-swatches",
      "claims": [
        {
          "component": "core.parser",
          "note": "Fill/StrokeColor + tint on page items read (spread/styles); corpus-verified, 61 envato packs (W4.17)",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "frame fill/stroke paint resolved + emitted (color_paint.rs) (W4.17)",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "frameFillColor/frameStrokeColor/frameFillTint",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "Swatches/Color/Gradients apply arms",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:05.955Z",
        "headline": "All linked evidence is passing (11 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 11,
          "passed": 11,
          "skipped": 0
        }
      },
      "id": "color-swatches.fill-stroke-apply",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Apply fill/stroke color + tint to frames"
    },
    {
      "chapter": "color-swatches",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "Angle counter-clockwise on the page, default start at the inner box's bottom-left (R-02a); a rotated frame's default start and length asked of InDesign every 15 degrees (core ADR 147).",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "createGradient/editGradient/deleteGradient + fill angle/length paths",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "Gradients panel ramp editor + stops",
          "status": "shipped"
        },
        {
          "component": "editor.gesture",
          "note": "gradient swatch tool drag (angle/length)",
          "status": "shipped"
        },
        {
          "component": "editor.script",
          "note": "paged.gradients()",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:57:29.729Z",
        "headline": "All linked evidence is passing (25 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 25,
          "passed": 25,
          "skipped": 0
        }
      },
      "id": "color-swatches.gradients",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Gradients (linear/radial, stops + midpoints)"
    },
    {
      "chapter": "color-swatches",
      "claims": [
        {
          "component": "core.renderer",
          "note": "intent + black-point compensation per DisplaySetup",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "note": "registerColorProfile + colorCompute",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "Colour settings: profile upload, intent, BPC, soft-proof",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:51:34.225Z",
        "headline": "All linked evidence is passing (12 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 12,
          "passed": 12,
          "skipped": 0
        }
      },
      "id": "color-swatches.icc-cmm",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "ICC color management (lcms2 native / qcms wasm)"
    },
    {
      "chapter": "color-swatches",
      "claims": [
        {
          "component": "core.mutation",
          "note": "setInkSetting/setUseStandardLabForSpots (non-undoable)",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "Ink manager panel",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-06-16T14:08:38Z",
        "evidence_at": "2026-10-18T07:49:40.272Z",
        "headline": "All linked evidence is passing (4 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 4,
          "passed": 4,
          "skipped": 0
        }
      },
      "id": "color-swatches.ink-manager",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Ink management (convert-to-process, aliases, standard Lab)"
    },
    {
      "chapter": "color-swatches",
      "claims": [
        {
          "component": "core.parser",
          "note": "ColorModel::MixedInk recognised but resolved as Unknown (graphic.rs:200)",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "Lab analytic shipped (Bradford, cmm.rs:255); mixed-ink + spot-no-CMYK fall back to RGB/CMYK — spectral simulation deep-deferred (deep-defer-records.md §mixed-ink)",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:57:06.727Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "color-swatches.lab-mixed-ink",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Lab / mixed-ink / spot-without-CMYK"
    },
    {
      "chapter": "color-swatches",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "plane-aware on CPU; RGB approximates",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:58:20.193Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "color-swatches.overprint",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "CMYK overprint"
    },
    {
      "chapter": "color-swatches",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "Color panel fill/tint; kind badges in Swatches",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:58:35.62Z",
        "headline": "All linked evidence is passing (8 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 8,
          "passed": 8,
          "skipped": 0
        }
      },
      "id": "color-swatches.process-spot-tint",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Process CMYK/RGB + spot inks + tints"
    },
    {
      "chapter": "color-swatches",
      "claims": [
        {
          "component": "core.parser",
          "note": "<Color>/<Swatch>/<Tint> read from Resources/Graphic.xml into the swatch palette (graphic.rs); corpus-verified (generated swatches.idml, W4.17). Swatches render only when APPLIED — see fill-stroke-apply/character-fill",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "note": "create/edit/deleteSwatch, importSwatchLibrary, swatch export",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "Swatches manager: mixer popover, rename, groups, libraries",
          "status": "shipped"
        },
        {
          "component": "editor.script",
          "note": "paged.swatches()",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-06-18T11:50:06Z",
        "evidence_at": "2026-10-18T07:52:05.288Z",
        "headline": "All linked evidence is passing (20 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 20,
          "passed": 20,
          "skipped": 0
        }
      },
      "id": "color-swatches.swatch.crud",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Swatch CRUD (create/edit/delete + .ase import/export)"
    },
    {
      "chapter": "companion-formats",
      "claims": [
        {
          "component": "core.parser",
          "note": ".indb is a binary format; the plan defines a .pagedbook manifest plus an InDesign-side export script (core parity plan D-10)",
          "status": "planned"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "companion-formats.book",
      "importance": "normal",
      "since": null,
      "status": "planned",
      "title": "Books (a set of documents paginated, synchronised and exported as one)"
    },
    {
      "chapter": "companion-formats",
      "claims": [
        {
          "component": "core.parser",
          "note": "no .idms/.icml fragment entry point (deep-defer-records.md §companion-formats)",
          "status": "planned"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "companion-formats.idms-icml",
      "importance": "normal",
      "since": null,
      "status": "planned",
      "title": "Snippets & InCopy (.idms / .icml / libraries)"
    },
    {
      "chapter": "conditional-text",
      "claims": [
        {
          "component": "core.parser",
          "note": "AppliedConditions on runs parsed (styles.rs); the run-level visibility IS enforced — see conditional-text.visibility-filtering. Corpus-verified (generated conditions.idml, W4.17)",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "conditioned runs dropped pre-layout (pipeline.rs:2765,3726) — same render path as visibility-filtering (W4.17)",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "appliedConditions property",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "status": "shipped"
        },
        {
          "component": "editor.script",
          "note": "paged.conditions()/conditionSets()",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "Conditions rows; visibility toggles pending (gap 25)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:05.011Z",
        "headline": "All linked evidence is passing (13 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 13,
          "passed": 13,
          "skipped": 0
        }
      },
      "id": "conditional-text.applied-conditions",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Apply conditions to text ranges"
    },
    {
      "chapter": "conditional-text",
      "claims": [
        {
          "component": "core.mutation",
          "note": "SetConditionVisible / ActivateConditionSet (+ RestoreConditionVisibility inverse) (W0.5)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:06.425Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "conditional-text.condition-ops",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Condition visibility ops (SetConditionVisible, ActivateConditionSet)"
    },
    {
      "chapter": "conditional-text",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:57:17.385Z",
        "headline": "All linked evidence is passing (3 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 3,
          "passed": 3,
          "skipped": 0
        }
      },
      "id": "conditional-text.visibility-filtering",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Condition visibility filtering (hidden runs pre-layout)"
    },
    {
      "chapter": "cross-references-hyperlinks",
      "claims": [
        {
          "component": "core.parser",
          "note": "cross-references read as InDesign 20.0.1 writes them (core ADR 169): formats with their building blocks, each source's AppliedFormat, ParagraphDestination / HyperlinkTextDestination where they stand in the text",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "the page number is live as InDesign prints it (a stale stored 9 prints the destination paragraph's laid-out page 2; core ADR 169, xref fixture text-gated against InDesign's PDF); the text blocks print as stored until updated",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "updateCrossReferences (core ADR 169) rewrites a source's (or every source's) text from its format as InDesign 20.0.1's Update Cross-References writes it — full paragraph with its list number, paragraph text, paragraph number, anchor name — matched against xref-update-probe; undoable, saved. OPEN: lists continued across stories, chapter numbers, insertCrossReference, format editing",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "ListRows",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:58:45.442Z",
        "headline": "All linked evidence is passing (9 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 9,
          "passed": 9,
          "skipped": 0
        }
      },
      "id": "cross-references-hyperlinks.cross-references",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Cross-references"
    },
    {
      "chapter": "cross-references-hyperlinks",
      "claims": [
        {
          "component": "core.parser",
          "note": "designmap hyperlinks; in-story regions resolved to LinkRegionTable side-channel",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "hyperlink spans → LinkRegionTable + PDF /Annots (URI + GoTo) (W1.4)",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "create (v53 InsertHyperlink, v70 to a page), and since v0.71.13 delete and edit by the Hyperlink id for a link of any origin (core ADR 164): deleteHyperlink unlinks its text and drops its URL or page destination unless shared or a text anchor; editHyperlink renames it or sends it to a new URL or page (in place when the destination is its own). Both undo exactly. An InDesign link edited in place keeps its appearance (Visible, Highlight, Width, BorderStyle, BorderColor) through a save (plugin-publish). OPEN: the destination Hidden flag is not modelled; page-item sources are not addressable",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "Hyperlinks ListRows; the panel's edit and delete verbs wait on the editor's protocol-73 pin (core ships create, edit and delete)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:58:19.553Z",
        "headline": "All linked evidence is passing (10 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 10,
          "passed": 10,
          "skipped": 0
        }
      },
      "id": "cross-references-hyperlinks.hyperlinks",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Hyperlinks (targets + sources)"
    },
    {
      "chapter": "cross-references-hyperlinks",
      "claims": [
        {
          "component": "core.parser",
          "note": "a TOC style read as InDesign 20.0.1 writes it (core ADR 165): the entry's Name is the collected paragraph style, FormatStyle / PageNumberStyle / SeparatorStyle are Properties children, PageNumberPosition AfterEntry / BeforeEntry / None, the separator behind $ID/; the engine's older attributes still read",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "a TOC story prints the text it holds and an empty one prints nothing, as InDesign does (no render-time rebuild, core ADR 165; toc fixture gated against InDesign's PDF of a stale TOC). A TOC entry and an index page take the page a heading is laid out on (L-06)",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "updateTableOfContents (v0.71.13, core ADR 165) rewrites a TOC story over the current layout as InDesign writes it — the title, then each heading in its entry's format style with the page number after or before it in its own character style — matched against InDesign's own update (toc-update-probe); undoable, batched after an edit it lists the edit, and saved as IDML. OPEN: the hidden hyperlinks and bookmarks InDesign adds to the entries, RunIn, SortAlphabet, NumberedParagraphs, insertTableOfContents",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "Outline panel heading anchors + page numbers",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:58:51.977Z",
        "headline": "All linked evidence is passing (12 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 12,
          "passed": 12,
          "skipped": 0
        }
      },
      "id": "cross-references-hyperlinks.toc",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "TOC entry resolution"
    },
    {
      "chapter": "data",
      "claims": [
        {
          "component": "plugin.data",
          "note": "batch-plan engine (per-record / per-group / one-catalog) + batch RUN executor + headless native CLI (data-cli) + sandboxed Boa scripting surface (data-script) all landed; data_automation_* conformance tests green 2026-06-11. In the editor the Dataset panel's Plan batch (a Run batch button from canary.2, 2026-08-03; a plan in canary.16) paginates the record flow over a nominal frame chain and reports per-unit counts; it makes no pages, and documents still materialize via the automation lane",
          "status": "shipped"
        },
        {
          "component": "editor.shell",
          "note": "No batch run that makes documents from the editor. The Dataset panel's Plan batch (per record, per group or one catalog) paginates a record flow over a nominal frame chain and makes no pages; output documents come only from the headless tools (paged-data-batch, built from source). The editor has no service to send a batch to and collect its documents from.",
          "status": "planned"
        }
      ],
      "health": {
        "changed_at": "2026-06-10T09:21:22Z",
        "evidence_at": "2026-10-17T07:27:59.009Z",
        "headline": "All linked evidence is passing (17 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 17,
          "passed": 17,
          "skipped": 0
        }
      },
      "id": "data.automation.batch",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Print automation / batch generation (per-record / per-group / one catalog)"
    },
    {
      "chapter": "data",
      "claims": [
        {
          "component": "plugin.data",
          "note": "5 registry rows (data.barcode.{ean13,code128,qr,bind,lower}). PURE CLEAN-ROOM symbology encoders: EAN-13/UPC-A, Code-128 (auto A/B/C), QR (the module matrix). The frozen Binding::Barcode kind (target frame + query + symbology + expr + options) resolves the expression to a value string + applies the missing policy (Skip/Flag) WITHOUT pulling the encoders into bind (encode happens in lowering). LOWERING is the VECTOR lane: the engine encodes then scales the unit-box module grid to the bound frame's content box as content-space filled rects (§9.6 — frame transforms honored), the host-model turns each module into a closed 4-anchor insertPath filled rect (no asset-store door; raster BLOCKED since placeImage needs a resolvable uri); an empty/absent value lowers to zero modules, never a fake symbol. Conformance + registry + bundle vitest. JOURNEY (editor surface, data-barcode.journey, 2026-06-18, both lanes): the symbology is driven THROUGH the bindings panel — select a rectangle → choose Code-128 → Bind barcode → Lower; the engine encodes the VECTOR modules end to end. RENDER-VERIFY FINDING — RESOLVED (2026-06-18): the lower's atomic batch (insertPath ×N + a setPluginMetadata stamping the binding envelope) was rejected ('insertPath batch rejected'). The cause was NOT the `$created` target (it resolves fine to the last minted module) — it was the ENVELOPE SHAPE: makeEnvelope emitted `{ v, payload }`, but the host's setPluginMetadata door VALIDATES `{ v, data, engine? }`, so the metadata op failed validation and sank the whole atomic batch. The fix (data-host-model binding.ts) emits `{ v, data }`; the batch now applies and the VECTOR modules paint. Pure bundle-side (works on the published engine — no core change); root-caused via a paged-canvas batch probe. The data-barcode.journey now asserts the page render change HARD (verified on the GPU lane). + a lower.test regression pinning the `{ v, data }` envelope shape",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-05T14:26:31Z",
        "evidence_at": "2026-10-18T05:01:51.063Z",
        "headline": "All linked evidence is passing (60 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 60,
          "passed": 60,
          "skipped": 0
        }
      },
      "id": "data.barcode.symbology",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Barcode / QR symbologies — clean-room EAN-13/UPC-A, Code-128, QR encoders + binding + VECTOR lowering (§9.7)"
    },
    {
      "chapter": "data",
      "claims": [
        {
          "component": "plugin.data",
          "note": "the demo-grade buttons gain a real authoring lane: a kind/field/seq/message row drives addVariableBinding / addImageBinding / addBarcodeBinding against the live engine (the demo buttons remain beneath as worked examples). Ships in data canary.3. Wave 4 (2026-10-05; published in @paged-media/data 0.1.0-canary.10, 2026-10-06): the row defines every binding kind — table (comma-separated columns), show/hide (selected element, invert), style rule (condition + document style on the selected cell column or the caret story) and record flow (defineRecordFlow + previewRecordFlow list)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-05T15:34:16Z",
        "evidence_at": "2026-10-18T05:07:40.619Z",
        "headline": "All linked evidence is passing (25 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 25,
          "passed": 25,
          "skipped": 0
        }
      },
      "id": "data.bind.authoring",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Bindings authoring — first-class add-binding flow in the bindings panel"
    },
    {
      "chapter": "data",
      "claims": [
        {
          "component": "plugin.data",
          "note": "resolved_fingerprint(&Resolved) is a pure, locale-free, type-tagged identity of a binding's resolved content; diff_resolved(before, after) compares two id->fingerprint snapshots into a ChangeReport (per-binding changed/unchanged/added/removed + rolled-up counts); resolve_content is the read-only resolve core (does NOT mutate sync state). data-js DataSession.refresh_change_report() holds the last snapshot, diffs current vs prior, and reports the delta WITHOUT driving the sync-state machine (never clobbers); bundle session.refreshDiff() returns the report the panel renders as a change list. Rust + vitest. JOURNEY (editor surface, data-preview.journey, 2026-06-18, both lanes): the change report drives THROUGH the bindings panel — 'What changed?' refreshes the data then diffs every binding vs the prior snapshot; the journey asserts the panel renders the 'changed since last sync' headline + the baseline 'added' entries (data-change-kind tags)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-05T14:29:52Z",
        "evidence_at": "2026-10-18T05:02:54.311Z",
        "headline": "All linked evidence is passing (7 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 7,
          "passed": 7,
          "skipped": 0
        }
      },
      "id": "data.bind.change-report",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Change-highlighting on refresh — per-binding \"what changed since last sync\" report (§8)"
    },
    {
      "chapter": "data",
      "claims": [
        {
          "component": "plugin.data",
          "note": "salsa-shaped ResolutionEngine: resolve variable + single-region table; ResolveStamp invalidation (only the affected cut goes stale); sync states Linked/Pinned/Overridden/Stale/Error with the non-destructive conflict policy (refresh never clobbers pinned/overridden); record-identity minimal diff. 5 tier tests + property tests green. JOURNEY (editor surface, data-binding.journey, 2026-06-18, both lanes): the resolution engine drives THROUGH the bindings panel — 'Wire demo binding' defines a query + variable + single-region table binding on the engine, 'Lower to document' refreshes the data + resolves + commits the content (render-verified change on the page)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:03:00.715Z",
        "headline": "All linked evidence is passing (27 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 27,
          "passed": 27,
          "skipped": 0
        }
      },
      "id": "data.bind.engine",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Binding + synchronization engine — resolution graph, sync states, identity diff"
    },
    {
      "chapter": "data",
      "claims": [
        {
          "component": "plugin.data",
          "note": "suggest_mappings(schema) is a pure schema -> ColumnMapping[] kernel: one row per result column with a humanised header (unit_price -> Unit Price), the bound expr = the bare field reference, a logical type hint, and a mappable flag = data-expr::is_field_ident (the DSL grammar mirror — the DSL has NO quoted/bracketed field syntax, so a column whose name is not a bare identifier is flagged non-mappable with an empty expr, the wizard asking for a manual expression rather than inventing a quoting grammar). Bundle: session.queryMappings renders the header->binding UI and generates each variable binding from the engine-computed expr (the data semantics stay in Rust). Rust + vitest. JOURNEY (editor surface, data-fieldmap.journey, 2026-06-18, both lanes): the wizard drives THROUGH the bindings panel — 'Map fields…' surfaces one row per CSV column (humanised headers Name/Role, each with the engine-computed expr in a <code>), toggling a checkbox moves the confirm label ('Create 2 bindings' ↔ 'Create 1 binding'), and confirm generates the variable bindings (v_name, v_role) into the bindings list",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-06-13T08:39:53Z",
        "evidence_at": "2026-10-18T05:03:54.89Z",
        "headline": "All linked evidence is passing (8 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 8,
          "passed": 8,
          "skipped": 0
        }
      },
      "id": "data.bind.field-mapping",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Field-mapping wizard — map source columns → variable bindings one-click (§9)"
    },
    {
      "chapter": "data",
      "claims": [
        {
          "component": "plugin.data",
          "note": "resolve_at(binding, record) evaluates per-record kinds (variable/image/barcode) over records[record] instead of always row 0 (resolve()=resolve_at(0)); record_count(query) is the stepper's of-N bound; an out-of-range index degrades to the missing policy (no panic); whole-result kinds (table/flow) ignore the index. Record N is the Nth row as the query delivers it when the query defines that order (an ORDER BY, or a scan of one table, which keeps the imported file's row order, as Data Merge numbers it) and the Nth by content when it does not; an unindexed binding shows record 1. Uses the SAME resolve+lower lanes the batch uses, so preview == output; a barcode step replaces the previous symbol. Bundle: the session keeps each query's record count and the stepped-to record, so the stepper in the Bindings panel shows \"1 / N\" as soon as the query has run, keeps its place when the panel is shown again (another tab, then back), and \"next ›\" runs a query that has not run yet. Until @paged-media/data 0.1.0-canary.16 (plugin-data #4) it opened disabled (\"— / —\") until \"jump to\" was used, and a tab switch disabled it again. Rust + vitest. JOURNEY (editor surface, data-preview.journey, both lanes): the stepper drives THROUGH the bindings panel — jump to record 2, assert '2 / 3' over the 3-record fixture, then ‹ prev → '1 / 3' and next › → '2 / 3'",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-06-13T08:33:10Z",
        "evidence_at": "2026-10-18T05:02:54.311Z",
        "headline": "All linked evidence is passing (18 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 18,
          "passed": 18,
          "skipped": 0
        }
      },
      "id": "data.bind.preview-step",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Record-preview stepper — resolve a binding against a chosen record index N (§9)"
    },
    {
      "chapter": "data",
      "claims": [
        {
          "component": "plugin.data",
          "note": "Binding::Property { target (selector, never a raw Self), path, query, expr, schema, coerce, missing } on plugin-data main, published in @paged-media/data since 0.1.0-canary.11 (2026-10-11). Coercion against the ADR 132 schema row is Rust (bool, number/percent/range, length with units, colour → swatch, enum members, text, ref, asset, bounds); units MM/CM/IN/PT/PX/AS and colours RGB/CMYK/HEX/SWATCH/ENUM in the expression language. One apply = one host.objects.batch (50 bindings: 1 batch, 1 undo step). Visibility re-expressed as elementVisible; rule and barcode paint as property triples. Data sets over any property. On engine protocol 71 (contract 0.2.45; 0.2.44 was never published): a new literal colour's swatch is a create op in the same batch (one undo step) and a table rule writes appliedCellStyle on cell: addresses through host.objects, its style created in the same batch (one undo step). Core v0.71 (2026-10-11) and contract 0.2.45 are published; on a 0.70 engine the swatch is still a second step and rules keep their old write path.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-12T07:33:33Z",
        "evidence_at": "2026-10-17T07:27:59.021Z",
        "headline": "All linked evidence is passing (38 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 38,
          "passed": 38,
          "skipped": 0
        }
      },
      "id": "data.bind.property",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Universal property binding — any host.objects property bound to data, one undo step per apply (ADR 558)"
    },
    {
      "chapter": "data",
      "claims": [
        {
          "component": "plugin.data",
          "note": "wave 7 (2026-10-05; on main, published in @paged-media/data 0.1.0-canary.10, 2026-10-06): DataSession::row_diff (data-js review.rs, on data-bind diff()) diffs each query's current result against the one recorded by mark_rows_applied (called after lowerAll and refreshFields): added/removed rows with their values, each changed cell before/after, the key (declared, else the first column unique in both results, else the whole row), and which bindings read what changed. \"What changed?\" renders it per query with a key picker. A reopened document starts with every row new (the \"before\" is not saved).",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-05T16:06:13Z",
        "evidence_at": "2026-10-17T07:27:59.044Z",
        "headline": "All linked evidence is passing (4 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 4,
          "passed": 4,
          "skipped": 0
        }
      },
      "id": "data.bind.row-diff",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Row diff since the document was last written — added, removed and changed rows, and the bindings they reach"
    },
    {
      "chapter": "data",
      "claims": [
        {
          "component": "plugin.data",
          "note": "wave 7 (2026-10-05; on main, published in @paged-media/data 0.1.0-canary.10, 2026-10-06): the Bindings panel shows each binding's sync state (synced/stale/pinned/overridden/error) and offers Pin, Unpin and Accept source (relink, then write the source value). The engine assigns Error to a binding that cannot resolve unless it is pinned or overridden. Pinned/overridden decisions persist with the session part. Accepting a table only re-links it (a table is re-inserted as a whole on lower, ADR 551); nothing marks a hand-edited field Overridden.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-05T16:06:13Z",
        "evidence_at": "2026-10-17T07:27:59.044Z",
        "headline": "All linked evidence is passing (8 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 8,
          "passed": 8,
          "skipped": 0
        }
      },
      "id": "data.bind.sync-review",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Sync states per binding, with pin, unpin and accept-source"
    },
    {
      "chapter": "data",
      "claims": [
        {
          "component": "plugin.data",
          "note": "From engine protocol 71 (feature-detected by the textVariable object kind) a variable binding k is the custom text variable paged:k, created in the batch that inserts its instance; a refresh, preview step or data set switches it with one Set on textVariableContents (one undo step, every instance re-baked). Exported as InDesign's own TextVariable + TextVariableInstance; InDesign 2025 kept it with its value through a save (conformance/indesign-binding/recorded/bound71.json) and re-spelled its Self (dTextVariablenpaged-v_name), so variables are found by name; paged restores the binding and drives the variable again. Placeholder-field documents keep being refreshed through their fields. The Data Merge template turns each instance into a <<field>> placeholder (InDesign merged it). Published in @paged-media/data 0.1.0-canary.11 (2026-10-11) with core v0.71 and contract 0.2.45 (0.2.44 was never published); on a 0.70 engine variables stay placeholder fields.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-12T07:33:33Z",
        "evidence_at": "2026-10-17T07:27:58.999Z",
        "headline": "All linked evidence is passing (7 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 7,
          "passed": 7,
          "skipped": 0
        }
      },
      "id": "data.bind.text-variables",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Variable bindings are custom InDesign text variables; one Set re-bakes every instance (ADR 559, protocol 71)"
    },
    {
      "chapter": "data",
      "claims": [
        {
          "component": "plugin.data",
          "note": "clean-room: the shape derives from Adobe's PUBLISHED Variables- namespace documentation and from the structure of files the application emits — never from Illustrator source (§3). TWO DECLARED DEVIATIONS, stated up front rather than discovered later. (1) We do NOT emit Adobe's DTD entity subset: Illustrator writes its namespace declarations as entity references (xmlns:v=\"&ns_vars;\") backed by an internal DTD, which no conforming XML parser but its own accepts. We WRITE the resolved namespace URIs (well-formed XML, same namespaces) and READ either form, because the reader matches LOCAL NAMES and never resolves an entity — so a real Illustrator export parses. Whether the Illustrator IMPORTER accepts a URI where it wrote an entity is UNVERIFIED: no Illustrator was run (clean-room). (2) graphdata bodies are opaque — round-tripped verbatim, never interpreted. The reader refuses what it cannot import honestly: an unknown trait, a value for an undeclared variable, and a visibility value that is neither true nor false are typed errors rather than coercions, because importing them as the wrong kind would apply a text string to a visibility target. Deterministic: the same model always serializes to the same bytes. Data sets persist in the document payload as an ADDITIVE amendment (a pre-amendment payload still loads; the recipe round-trip stays exact) and the size is MEASURED against core's 64 KiB setPluginMetadata cap (D-08), with the panel warning at 80% BEFORE a bulk capture makes the document unsaveable. quick-xml is pinned >= 0.41 because 0.37 carries an unbounded-allocation advisory in its namespace resolver and this codec parses USER-SUPPLIED files (§11)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-17T07:26:41.247Z",
        "headline": "All linked evidence is passing (7 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 7,
          "passed": 7,
          "skipped": 0
        }
      },
      "id": "data.dataset.library-xml",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Illustrator-compatible variable-library XML — import + export (§16.4)"
    },
    {
      "chapter": "data",
      "claims": [
        {
          "component": "plugin.data",
          "note": "capture snapshots every bindable variable's resolved value at a chosen record; capture_every_record turns a whole query result into ONE DATA SET PER RECORD — the join that justifies siting Variables in the data plugin rather than the drawing one, since an Illustrator author builds every set by hand while a data source already has the rows. MEASURED UNDO COST: applying a set writes every variable in a single `batch` mutation, so switching a data set that moves N variables costs the user ONE undo press, not N — asserted as a mutate-CALL count through a capturing fake host, not merely as op shape, and re-proven through the real wasm (e2e Part E). Each trait routes to the door that already ships for it: setFieldValue (D-01), placeImage (D-14), setElementProperty elementVisible (§9.8). Placeholder offsets are read FRESH from placeholders() at apply time (a cached place-time offset would write into the wrong run after any edit above the field). An applied set marks its bindings Overridden — the shipped sync state — so the next refresh cannot clobber it and relink returns it to live data. Rows that cannot be written are reported with a REASON; a data set never half-applies in silence. Exposed as two payload-carrying COMMANDS (captureDataSet / applyDataSet) so a host-side loop — e.g. the editor's Actions recorder replaying CommandRegistry.invoke steps — can drive batch output over N data sets without this bundle depending on the recorder. Rust + vitest + real-wasm e2e",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-17T07:27:59.045Z",
        "headline": "All linked evidence is passing (25 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 25,
          "passed": 25,
          "skipped": 0
        }
      },
      "id": "data.dataset.palette",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Data sets — capture / switch / delete, applied as ONE undo step (§16.4)"
    },
    {
      "chapter": "data",
      "claims": [
        {
          "component": "plugin.data",
          "note": "The typed command media.paged.data.exportDataMergeTemplate writes the document as IDML with every merge field as <<field>> in a HyperlinkTextSource, DBF_<field> destinations + hyperlinks, DataMergeImagePlaceholder, <DataMerge> in Preferences, and the query rows as UTF-16 CSV with a BOM. Rewriting InDesign's dm-base gives the verified dm-minimal form; InDesign 2025 opened our template with fields sku, name and merged 3 records (conformance/indesign-binding).",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-12T07:33:33Z",
        "evidence_at": "2026-10-17T07:27:58.981Z",
        "headline": "All linked evidence is passing (5 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 5,
          "passed": 5,
          "skipped": 0
        }
      },
      "id": "data.export.datamerge",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Export as InDesign Data Merge template (native placeholders + UTF-16 data source)"
    },
    {
      "chapter": "data",
      "claims": [
        {
          "component": "plugin.data",
          "note": "own publishing DSL (not Excel-grammar); 42 fns across format/logic/text/math/temporal (M3 additions incl. TEXTJOIN/IFERROR/SWITCH); FnId-parity codegen (no row -> no dispatch -> uncallable); CPU/f64 bit-stable; the §9.5 rule-evaluation engine evaluates (application blocked on the style-read door, D-13); per-function + engine tier tests green. JOURNEY (editor surface, data-binding.journey, 2026-06-18, both lanes): the DSL is entered THROUGH the bindings panel — the field-mapping wizard ('Map fields…') asks the engine for column→binding suggestions, each carrying an engine-COMPUTED DSL expression (a bare field reference, rendered in a <code>); confirming wires those expression bindings, and the next Lower resolves each DSL expression against the live records → the computed value renders on the page (render-verified)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-12T08:39:26Z",
        "evidence_at": "2026-10-18T05:02:00.285Z",
        "headline": "All linked evidence is passing (61 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 61,
          "passed": 61,
          "skipped": 0
        }
      },
      "id": "data.expr.engine",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Binding-expression DSL — lexer, parser, evaluator and function library, registry-driven dispatch"
    },
    {
      "chapter": "data",
      "claims": [
        {
          "component": "plugin.data",
          "note": "the governed-catalog KERNEL is built — the engine enriches a query's live schema with a column-metadata sidecar and reports drift (data-sources governed.rs, data_governed_catalog_* tests; the engine-neutral outputs-only boundary held, never an embedded engine, §3). PARTIAL (re-checked 2026-10-05): nothing reads a sidecar from a source location — the dataset panel calls governedCatalog with an EMPTY sidecar (packages/data-bundle/src/panels/dataset-panel.tsx:100), so every column shows as undocumented, and reading a governed table + its sidecar from a location is not built (plugin-data docs/status.md, Not built). The earlier shipped claim covered the kernel only",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": "2026-10-05T14:29:52Z",
        "evidence_at": "2026-10-17T07:27:59.011Z",
        "headline": "All linked evidence is passing (5 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 5,
          "passed": 5,
          "skipped": 0
        }
      },
      "id": "data.governed.extract",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Governed-extract source (governed tables + optional metadata sidecar -> catalog)"
    },
    {
      "chapter": "data",
      "claims": [
        {
          "component": "plugin.data",
          "note": "en/de locale number/date display formatting landed at M3; per-function format tests green. JOURNEY (editor surface, data-dataset.journey, 2026-06-18, both lanes): the display locale drives THROUGH the dataset panel — the locale <select> (en — $1,234.50 / de — 1.234,56 €) sets the engine's §9.1 formatting locale (NUMBER/CURRENCY/PERCENT/DATEFMT); the journey switches en→de and asserts the panel reflects the chosen locale",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:02:08.466Z",
        "headline": "All linked evidence is passing (4 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 4,
          "passed": 4,
          "skipped": 0
        }
      },
      "id": "data.i18n.display",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Locale-aware display formatting (en/de v1)"
    },
    {
      "chapter": "data",
      "claims": [
        {
          "component": "plugin.data",
          "note": "wave 7 (2026-10-05; on main, published in @paged-media/data 0.1.0-canary.10, 2026-10-06): data-core LOCALES table (decimal, group, minimumGroupingDigits, currency symbol + placement pattern, date pattern) with en, de, en-GB, de-AT, de-CH, fr, it, es, nl, CLDR values cited per row; en/de keep their pre-table output. A binding can override the session locale (saved in the payload `locales`); a variable field takes a number/currency/percent/date pattern (its expression wrapped in a format function; apply_format/split_format). Canonical values and resolve stamps stay locale-free.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-05T16:06:13Z",
        "evidence_at": "2026-10-17T07:27:59.044Z",
        "headline": "All linked evidence is passing (11 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 11,
          "passed": 11,
          "skipped": 0
        }
      },
      "id": "data.i18n.locale-table",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Locales as a data table (CLDR values), per-field locale and display patterns"
    },
    {
      "chapter": "data",
      "claims": [
        {
          "component": "plugin.data",
          "note": "variable + single-region table lower to a content-space IR (frame ops honored, §9.6); the host-model translates to mutations; NATIVE insertTable lowering landed (D-02 mostly resolved, tab-text retained as old-host fallback); record flow + pagination engine landed (multi-level nested grouping, per-group SUM/AVG/MIN/MAX footers, parent path on frame spill) against a caller-supplied chain. PARTIAL: the D-12 SDK gate (frameChain for data's owned frames) is open; image-placeholder binding lowers but cannot place (D-14); data-driven rules application blocked on style read (D-13). JOURNEY (editor surface, data-binding.journey + data-render.journey, 2026-06-18, both lanes): the lowering reaches the page — Lower to document commits the resolved variable/table content and the page visibly changes (render-verified before/after pixel diff)",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:02:36.05Z",
        "headline": "All linked evidence is passing (56 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 56,
          "passed": 56,
          "skipped": 0
        }
      },
      "id": "data.lower.content",
      "importance": "normal",
      "since": "2026-06",
      "status": "experimental",
      "title": "Placeholder lowering — variable replacement + single-region dynamic table"
    },
    {
      "chapter": "data",
      "claims": [
        {
          "component": "plugin.data",
          "note": "Published in @paged-media/data since 0.1.0-canary.10 (2026-10-06); the current canary, 0.1.0-canary.16 (npm, 2026-10-13; the version the editor pins), exports plan_merge from its data-js wasm. data_lower::merge (plan_merge, merge_grid, merge_text, merge_words, merge_overset) plans a Data Merge from the template's <<field>> frames and image rectangles over the records in delivered order: records per column/columns from margin box + spacing, rows-first or columns-first, one page per record in Single Record mode, verbatim field text, Remove Blank Lines only for a field-only line whose fields are all empty, image fields and missing columns. Exposed as DataSession::plan_merge. The InDesign oracle's merge lane (data-conformance oracle.rs) agrees with all 8 recordings in pages, placements, texts and images; the one difference (overset flag of `overset`, 1 of 2) is by design: the planner does not measure text, the host writer does (data.lower.merge-writer).",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-06T03:04:22Z",
        "evidence_at": "2026-10-17T07:26:41.377Z",
        "headline": "All linked evidence is passing (8 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 8,
          "passed": 8,
          "skipped": 0
        }
      },
      "id": "data.lower.merge-plan",
      "importance": "normal",
      "since": "2026-10",
      "status": "stable",
      "title": "Data Merge planner — Single and Multiple Records laid out by InDesign's rule"
    },
    {
      "chapter": "data",
      "claims": [
        {
          "component": "plugin.data",
          "note": "wave 5 (2026-10-05; on main, published in @paged-media/data 0.1.0-canary.10, 2026-10-06; commits 7f6d40d, da5a172, 46ed7ce, d1efb60): src/merge.ts reads the <<field>> template off the page, merges the query's records (or the first source when no query exists) in delivered order, keeps or consumes the template page, measures overset through the host, and replaces its previous run (frames, labels, added pages) in the first batch. Command media.paged.data.command.mergeRecords + Data menu + a Data Merge row in the Bindings panel. test/merge-real-core.spec.ts: all 8 InDesign recordings agree in full on real core (pages, frames per page, bounds ±0.5 pt, text, overset, images); undo restores the template. W8 budget: 57 records, 1 mutate and 1 undo step on protocol 69 and later (2 and 2 before). PARTIAL: a merge to a new document copies the document through the host documents door (contract 0.2.41, implemented by the editor), but runRecordFlowBatch returns paginated units, not documents; a merge that adds pages is one batch on protocol 69 and later (core v0.69.0 names a page minted in the batch) and two undo steps on an older engine; added pages have zero margins; frames are minted fresh (duplicateElements refuses stories with hyperlinks, duplicatePage shares stories). Before protocol 70 the IDML import moved a paragraph mark between adjacent placeholders (pinned as fixed from 70).",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": "2026-10-06T03:04:22Z",
        "evidence_at": "2026-10-17T07:27:59.051Z",
        "headline": "All linked evidence is passing (32 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 32,
          "passed": 32,
          "skipped": 0
        }
      },
      "id": "data.lower.merge-writer",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Data Merge writer — merge the records into the document, matching InDesign"
    },
    {
      "chapter": "data",
      "claims": [
        {
          "component": "plugin.data",
          "note": "wave 5 (2026-10-05; on main, published in @paged-media/data 0.1.0-canary.10, 2026-10-06; commit da5a172): src/flow-writer.ts places the paginator's frames as labelled text frames, one per page in the active page's margin box, adding pages after the start page; the frames are not threaded, so the paginator's breaks hold. A re-lower replaces the frames and the pages it added (data.lower.relower-in-place). Real-core proof: test/relower-real-core.spec.ts \"a record flow becomes frames on added pages, and a re-lower replaces them\", which is tagged [data.lower.relower-in-place] (an explicit tag wins over test-map, so that test does not link here until it also carries this id). PARTIAL: a record's height is its field count × a line height, not measured text (ADR 554); no threaded-frame growth.",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": "2026-10-06T03:08:46Z",
        "evidence_at": "2026-10-17T07:27:59.003Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "data.lower.recordflow-writer",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Record flow lowers into text frames on pages added as the flow needs them"
    },
    {
      "chapter": "data",
      "claims": [
        {
          "component": "plugin.data",
          "note": "wave 5 (2026-10-05; on main, published in @paged-media/data 0.1.0-canary.10, 2026-10-06; commits da5a172, 46ed7ce): src/relower.ts plans the removal from what the session saw minted and from the labels on the content. A table re-lower swaps the table inside its own frame and story (deleteTable + insertTable + cells + label, one batch); a barcode re-lower and every preview step remove the previous symbol's modules in the batch that draws the new one (the Wave 0 DEFECT pin now passes); a record flow or merge removes its frames and the pages it added once they are empty; a page the user put something on stays. Pages added by a lowering are known from labels after a reopen. PARTIAL: after a reopen only labels are known, so a table frame is replaced rather than reused and only a barcode's labelled last module is found; the core side of a one-batch lower was released in core v0.69.0 (2026-10-06; the data branch was renumbered from protocol 68 to 69): a table lower and a table re-lower are each one batch and one undo step.",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": "2026-10-06T03:04:22Z",
        "evidence_at": "2026-10-17T07:27:59.04Z",
        "headline": "All linked evidence is passing (10 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 10,
          "passed": 10,
          "skipped": 0
        }
      },
      "id": "data.lower.relower-in-place",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Re-lowering a table, barcode, record flow or merge updates its content in place"
    },
    {
      "chapter": "data",
      "claims": [
        {
          "component": "plugin.data",
          "note": "the four v43 SDK consumers landed. JOURNEY (editor surface, data-binding.journey, 2026-06-18, both lanes): the variable-field consumer (D-01) drives THROUGH the bindings panel — Wire demo binding + the field-mapping wizard place tagged FIELDS and the refresh loop re-resolves them, with the lowered content render-verified on the page (image-place D-14 + rules D-13 + live record-flow D-12 are the lower-side residuals below, exercised by the same lower lanes; barcode is data.barcode.symbology). data.lower.variable-field (D-01): a placeholder field model — insertFieldMutation places a tagged run keyed by the binding id, the session refresh loop re-enumerates host.document.placeholders() and setFieldValues only CHANGED values; CARET GAP — HALF-CLOSED 2026-08: the C-9 read door (host.text.caret) EXISTS in plugin-sdk main but is in NO published plugin-api canary (the newest, 0.2.27-canary.1 at the time of writing; 11 contract commits sat unpublished. CORRECTED 2026-08-09: `npm view @paged-media/plugin-sdk dist-tags` returns canary 0.2.30-canary.0, published 08-08, and plugin-data itself pins it — so the blocker this row names is closed), so the bundle consumes it through a capability probe + a structural declaration and lights up with ZERO code change the day a canary carries it — until then a NEW field still lands at story start. The action item is a plugin-sdk publish, not engineering (RFI D-01 update). data.lower.image-place (D-14): placeImage onto the bound RECTANGLE through the CORE asset mechanism (never plugin-image, §2.1), idmlFit maps ImgFit -> the IDML FittingOnEmptyFrame vocabulary; a non-URI/absent reference is an honest skip. data.lower.recordflow-live (D-12): paginate_flow now runs over the LIVE host frame chain (readLiveChain reads frameChain(storyId) + content-box heights), re-paginating on DocumentChangeEvent.reflow (resize-only, the C-2/S-05 contract). data.lower.rule (D-13): evaluate_rule decides which records fire which DOCUMENT STYLE (always a style reference, never a literal, §9.5), ruleMutations applies via applyStyle / per-cell appliedCellStyle (composes from create*Style + setElementProperty, no new wire). PARTIAL: the D-01 caret residual remains, now blocked on a plugin-sdk canary publish rather than on a missing door",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:02:00.285Z",
        "headline": "All linked evidence is passing (45 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 45,
          "passed": 45,
          "skipped": 0
        }
      },
      "id": "data.lower.v43-consumers",
      "importance": "normal",
      "since": "2026-06",
      "status": "experimental",
      "title": "v43 lowering consumers — in-text variables (D-01), image place (D-14), live record-flow (D-12), rules (D-13)"
    },
    {
      "chapter": "data",
      "claims": [
        {
          "component": "plugin.data",
          "note": "Published: @paged-media/data has declared contributes.objectModel since 0.1.0-canary.11; the current canary, 0.1.0-canary.16 (npm, 2026-10-13; the version the editor pins), has five kinds at plugin:media.paged.data/<kind>/<id> (source, query, binding, dataSet, variable, schema rows in the manifest) and ten typed commands: bindProperty (the editor's \"Bind to data…\", completed in the Bindings panel), defineProperty, propertyBindings (the badge read), refresh, apply, defineSource, captureDataSet, applyDataSet, exportDataMergeTemplate and merge (Data Merge with flat options, the path the Data menu takes, so a script can start a merge; new in canary.16). A set defines or redefines through the session; a binding write (create, set, delete) is one undo step that the session follows. The Data objects panel shows every schema row as a host PropertyField. Needs plugin-api/plugin-sdk 0.2.45-canary.0 or later (published). Limits: a plugin object binds as a target only through its selector (host.objects has no hostOf door); a variable whose text instance was deleted keeps its definition; every text-variable read costs one objects.get.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-12T07:33:33Z",
        "evidence_at": "2026-10-17T07:27:58.995Z",
        "headline": "All linked evidence is passing (13 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 13,
          "passed": 13,
          "skipped": 0
        }
      },
      "id": "data.object-model",
      "importance": "normal",
      "since": "2026-10",
      "status": "stable",
      "title": "paged.data's own objects in host.objects — sources, queries, bindings, data sets, variables and typed commands"
    },
    {
      "chapter": "data",
      "claims": [
        {
          "component": "plugin.data",
          "note": "MEASURED 2026-10-05 (plugin-data docs/design/perf-baseline-2026-10-05.md). Engine: data-conformance/tests/perf.rs keeps its two wall-clock ceilings (200-page catalog lower, 100-record-delta diff); tests/perf_counts.rs pins COUNT budgets (resolves, stabilize sorts, sort-key allocations, fingerprints, diff rows) through the perf-counters feature, which the shipped wasm exports as perfCounters(); criterion benches (benches/engine.rs) trend the wall clock. Bundle: packages/data-bundle/test/perf/ pins host calls, mutates, undo steps, wasm calls and cells across the boundary for seven workloads against real core, the real data-js wasm and real DuckDB. DuckDB half: the cited 1M-row CSV to grouped RecordSet now has a lane (test/perf/duckdb-1m.trend.spec.ts, opt-in PERF_DUCKDB_1M=1, TRENDED not gated): 0.37 s measured. Still PARTIAL: no editor-session measurement, the 1M lane is not in CI, and the budgets are baselines (nothing optimised yet)",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": "2026-10-05T15:22:56Z",
        "evidence_at": "2026-10-17T07:27:59.051Z",
        "headline": "All linked evidence is passing (29 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 30,
          "passed": 29,
          "skipped": 1
        }
      },
      "id": "data.perf.gates",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Performance gates (§12.5) — engine-tier perf assertions"
    },
    {
      "chapter": "data",
      "claims": [
        {
          "component": "plugin.data",
          "note": "Every binding on one page item is written into that item's label x-paged:media.paged.data (data.oid/bind/queries/sources/extra, ASCII JSON; <?AID?> decoded on read); the document label carries the whole recipe. A .paged with its parts stripped, and InDesign 2025's own re-save (conformance/indesign-binding, recorded 2026-10-06), reopen with every binding; only the data is re-linked. On core protocol 71 with the plugin-publish object-model adapter: the document label is in designmap.xml and InDesign 2025 kept it (recorded/bound71.json); hidden items export as Visible=\"false\". Released in core v0.71.0 (2026-10-11; the editor runs 0.71.11) and published with @paged-media/data 0.1.0-canary.11; core 0.69/0.70 do neither.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-12T07:33:33Z",
        "evidence_at": "2026-10-17T07:27:58.999Z",
        "headline": "All linked evidence is passing (14 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 15,
          "passed": 14,
          "skipped": 1
        }
      },
      "id": "data.persist.labels",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Bindings live in the document's labels; the session part is a cache; restore from an InDesign re-save (ADR 559)"
    },
    {
      "chapter": "data",
      "claims": [
        {
          "component": "plugin.data",
          "note": "manifest validates via plugin-cli (incl. dataProviders.publish); activate registers the sources + bindings + dataset panels + 5 commands; bundle vitest green. The data-js wasm ships (541 KiB, under the 8 MiB budget) via the RATIFIED bundle-realm glue path (D-07a closed; residual D-07b = a large-artifact budget class for DuckDB). PARTIAL: the in-text variable placement awaits the tagged-placeholder model (D-01). RENDER-VERIFY (editor render journey data-render.journey, 2026-06-18): render verification of the bound→lowered content is BLOCKED on DuckDB-WASM headless boot — (3) Vite serves the vendored *.worker.js via the SPA fallback ('Unexpected token <' in the COI worker's nested fetch), (4) the COI/pthread+SAB DuckDB boot in headless Chrome is unproven. The render journey is a TRIPWIRE: it drives the real import gateway and skips-with-blocker today, auto-firing the render assertion the day DuckDB boots. data carries no editor render claim until then (host-integration stays @smoke)",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": "2026-09-09T12:59:36Z",
        "evidence_at": "2026-10-18T05:04:40.337Z",
        "headline": "All linked evidence is passing (68 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 68,
          "passed": 68,
          "skipped": 0
        }
      },
      "id": "data.plugin.bundle",
      "importance": "normal",
      "since": "2026-06",
      "status": "experimental",
      "title": "paged.data bundle (media.paged.data) — manifest + activate + 3 panels + DuckDB query glue"
    },
    {
      "chapter": "data",
      "claims": [
        {
          "component": "plugin.data",
          "note": "wave 4 (2026-10-05; on main, published in @paged-media/data 0.1.0-canary.10, 2026-10-06): sources, queries, bindings, data sets, locale, pins and imported CSV text persist as the `session` container part (paged/media.paged.data/session.json; CSV over 64 KiB as data/<hash>.csv) and restore on activate and on every documentLoaded (load_payload; data into DuckDB on its first boot; remote sources inert). Restore reconciles placed fields and the labels of lowered tables. On engine protocol 69 and later (core v0.69.0, 2026-10-06; the editor runs 0.71.11) each session version is written once as sessions/<hash>.json and the document label (setDocumentMetadata, undoable) names the live one, riding the batch of the document write that made the change, so undo brings the session back with it (test/protocol69-real-core.spec.ts). The editor pins data 0.1.0-canary.16. Limits: on an older engine the session part takes no part in undo; a change no document write carried takes no undo step.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-06T04:21:20Z",
        "evidence_at": "2026-10-17T07:27:59.028Z",
        "headline": "All linked evidence is passing (34 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 34,
          "passed": 34,
          "skipped": 0
        }
      },
      "id": "data.plugin.persistence",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "The data session is saved with the document and restored on open"
    },
    {
      "chapter": "data",
      "claims": [
        {
          "component": "plugin.data",
          "note": "D-09 chain complete across 4 repos: engine publish_provider (stabilized order + etag revision) + bundle registration (ed32771) + manifest dataProviders.publish (abcd7b6) + editor registry injection + the sheets consumer (S-15, 2026-06-11); SDK-mediated, zero inter-plugin contact. Residuals tracked: live cross-plugin e2e in the running editor; the per-cell encoding (plain vs tagged) contract amendment. JOURNEY (editor surface, data-dataset.journey, 2026-06-18, both lanes): the producer side drives THROUGH the dataset panel — 'Publish provider' produces the engine publication (provider id + revision etag), the panel renders it ('provider \"…-dataset\" ready · rev …'; in a standalone editor with no host.dataProviders door it honestly says 'registration deferred (D-09)'). The CONSUMER side (sheetFromDataset) is journey-covered cross-plugin by sheet-dataset.journey (sheet.data.consumer / S-15)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:02:08.466Z",
        "headline": "All linked evidence is passing (9 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 9,
          "passed": 9,
          "skipped": 0
        }
      },
      "id": "data.provider.contract",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Register as an SDK data provider (publish RecordSet to other consumers, e.g. sheets)"
    },
    {
      "chapter": "data",
      "claims": [
        {
          "component": "plugin.data",
          "note": "wave 6 (2026-10-05/06; on main, published in @paged-media/data 0.1.0-canary.10, 2026-10-06; commits fddd8c4, a0469cd, 3fdae1c): the Data query panel has a SQL field with DuckDB's diagnostics (error class, line and column in the query as written), filter/sort/group builders that write SQL into it, a preview grid of the first 50 rows, and saving under a query id. Every query, typed or restored, passes the guard (data.security.gates). A failing query is a diagnostic for that query; the refresh carries on. Tests (tagged [data.query.seam], which wins over test-map): src/__tests__/query-panel.test.ts, src/__tests__/query-lane.test.ts, test/sources-real.spec.ts. PARTIAL: the Bindings panel still binds over SELECT * FROM the first source; saved queries are used by bindings defined through the session or by id.",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": "2026-10-06T03:08:46Z",
        "evidence_at": "2026-10-17T07:27:59.035Z",
        "headline": "All linked evidence is passing (3 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 3,
          "passed": 3,
          "skipped": 0
        }
      },
      "id": "data.query.panel",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Data query panel — SQL with DuckDB diagnostics, filter/sort/group builders, preview grid"
    },
    {
      "chapter": "data",
      "claims": [
        {
          "component": "plugin.data",
          "note": "DuckDB-WASM 1.29.0 (MIT) VENDORED + booted; e2e harness proves CSV->Arrow->RecordSet->engine parity end-to-end. RecordSet shaping (record-stream/single/scalar/grouped), permutation-invariant deterministic ordering, content hashing — all tier-tested. PARTIAL: raw Arrow-IPC decode in Rust is the M1 seam (M0 converts Arrow->RecordSet in the TS layer); Decimal scale handling rides with M1 DB-attach. JOURNEY (editor surface, data-binding/data-dataset/data-preview/data-fieldmap.journeys, 2026-06-18, both lanes): the CSV->DuckDB->Arrow->RecordSet seam runs live in the headless harness — the import gateway reaches 'ready' (the seam ran) and the catalog/stepper read the materialized records (DuckDB-WASM boots on both lanes via the Vite duckdbDistRoute + COOP/COEP isolation) WAVES 0-6 (2026-10-05/06; on main, published in @paged-media/data 0.1.0-canary.10, 2026-10-06, the first package that ships DuckDB): DuckDB ships in the package (one eh variant as bin/duckdb-engine.wasm plus worker, refused by pubcheck when missing) and DuckDB's json + parquet extensions ship signed and SHA-256 pinned in bin/duckdb-ext/v1.1.1/wasm_eh (json 696,809 B, parquet 2,803,379 B), loaded same-origin; REQUIRE_REAL_DUCKDB=1 lanes in CI (test/duckdb-real.spec.ts, the native-DuckDB SQL oracle test/duckdb-sql-oracle.spec.ts) and the headless-Chromium browser lane (test/duckdb-browser.spec.ts). Arrow is read by type id from raw buffers (DQ-1..4), DECIMAL keeps its scale, the column door (arrowToColumns + ingestColumnBatch) exists. Queries are user-authored in the Data query panel (data.query.panel).",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": "2026-10-06T03:04:22Z",
        "evidence_at": "2026-10-18T05:02:08.466Z",
        "headline": "All linked evidence is passing (100 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 100,
          "passed": 100,
          "skipped": 0
        }
      },
      "id": "data.query.seam",
      "importance": "normal",
      "since": "2026-06",
      "status": "experimental",
      "title": "Query engine (DuckDB-WASM) + RecordSet shape/order/hash — the Arrow seam"
    },
    {
      "chapter": "data",
      "claims": [
        {
          "component": "plugin.data",
          "note": "wave 7 (2026-10-05; on main, published in @paged-media/data 0.1.0-canary.10, 2026-10-06): the rule editor checks the condition (parse error, fields the data lacks) and refuses an unusable one, previews which records fire through the same evaluation that applies the rule (preview_condition / evaluate_condition), picks the style from the document's paragraph/character/cell style collections (selfId applied), and styles the whole story, one paragraph per fired record (storyParagraphs target), or the fired rows' cells via appliedCellStyle. Fixed: a story-range rule applied its style even when no record fired.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-05T16:06:13Z",
        "evidence_at": "2026-10-17T07:27:59.044Z",
        "headline": "All linked evidence is passing (10 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 10,
          "passed": 10,
          "skipped": 0
        }
      },
      "id": "data.rule.authoring",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Rule editor — checked condition, firing preview, document style, per-record paragraphs"
    },
    {
      "chapter": "data",
      "claims": [
        {
          "component": "plugin.data",
          "note": "HARD gates green: no file/network access without the granting capability; no resolution of remote sources pre-consent; credentials NEVER serialized into the document payload (round-trip asserts absent). The consent CONTRACT + host.network door landed (structured {origins,purpose}, default-deny). D-03 ENFORCEMENT landed 2026-06-12 (plugin-platform.network-consent editor.canvas): the editor wires the ConsentBackend + consent-prompt UI AND the CSP connect-src hard wall (e2e 3/3). PARTIAL: the M1 remote adapter (first network:true caller, with server-mediated dynamic-CSP loosening) and legal review of rfc-network-consent remain WAVE 6 (2026-10-05/06; on main, published in @paged-media/data 0.1.0-canary.10, 2026-10-06; commits a0469cd, 3fdae1c, 792f017): every query, typed or restored from a document, passes the SQL guard (src/query/sql.ts): a lexer that reads strings, quoted names and comments as DuckDB does, then an allow-list (one statement, SELECT/WITH/FROM/VALUES/(, no statement keyword, every table position a source table, subquery or range/generate_series/unnest; no file or URL table function or quoted file/URL table name); bootDuckDB sets lock_configuration = true so no statement changes a setting, and turns extension autoinstall off with custom_extension_repository = the package's bin/duckdb-ext. The first guard (json_serialize_sql) made DuckDB autoload the json extension from extensions.duckdb.org, the editor CSP refused it and the worker trapped on every refresh: the BROWSER LANE test/duckdb-browser.spec.ts now runs the shipped worker in headless Chromium under the editor's headers (CI REQUIRE_REAL_BROWSER=1) and asserts no off-origin request; the Node lanes (test/query-guard.spec.ts, test/sources-real.spec.ts) prove the same matrix (test/guard-matrix.ts). Remote refresh polls only consented origins; a reopened remote source is inert. D-03 residual: a page CSP cannot follow runtime grants, so remote sources reach only origins a deployment lists at build time (editor ADR 218, on editor main); following grants needs host.network.fetch.",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": "2026-10-06T03:04:22Z",
        "evidence_at": "2026-10-18T05:02:27.236Z",
        "headline": "All linked evidence is passing (30 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 31,
          "passed": 30,
          "skipped": 1
        }
      },
      "id": "data.security.gates",
      "importance": "normal",
      "since": "2026-06",
      "status": "experimental",
      "title": "Capability-gated data access + threat model (the suite's largest surface)"
    },
    {
      "chapter": "data",
      "claims": [
        {
          "component": "plugin.data",
          "note": "inline + file source models + SourceAdapter registry; authorize() gate + credential-redacting data-source manifest tier-tested. PARTIAL: remote/httpfs adapter pending (the D-03 consent contract + host.network door now exist, bundle dormant at network:false); host.shell.pickFile (K-5) exists platform-side but is unconsumed by data's panels; DB-attach is an M1+ base-idea decision. JOURNEY (editor surface, data-dataset.journey + data-binding.journey, 2026-06-18, both lanes): the file/inline source adapter drives THROUGH the sources panel (CSV file input → registerCsvSource → the source row lists data_people) and the dataset panel materializes it (Refresh + catalog shows the resolved 2-column schema). The remote adapter has no driveable surface (network:false) — that lane is covered by data.source.db-attach's coverage_scope:none WAVE 6 (2026-10-05/06; on main, published in @paged-media/data 0.1.0-canary.10, 2026-10-06): local JSON/NDJSON/Parquet/XLSX import (data.source.local-formats), refresh policies (data.source.refresh-policy), and the remote adapter: a remote CSV/TSV/JSON/Parquet URL is a descriptor whose Load fetches only an origin the host reports consented. PARTIAL: credentialRef is stored but never resolved; SQLite unreadable in DuckDB-WASM 1.29.0, attach_plan not executed.",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": "2026-10-06T03:04:22Z",
        "evidence_at": "2026-10-18T05:02:27.236Z",
        "headline": "All linked evidence is passing (53 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 54,
          "passed": 53,
          "skipped": 1
        }
      },
      "id": "data.source.adapters",
      "importance": "normal",
      "since": "2026-06",
      "status": "experimental",
      "title": "Source adapters (inline + file at M0) + capability/consent gate + visible manifest"
    },
    {
      "chapter": "data",
      "claims": [
        {
          "component": "plugin.data",
          "note": "D-11 landed across four source rows. data.source.db-attach: a structured DbAttach { db, target, credentialRef } — NO connection string with secrets; the host resolves the ref + injects the live connection (pairs with the D-03 consent door). HONEST transport scoping: SQLite attach (file/OPFS) is pure-web-reachable; Postgres/MySQL over raw TCP is the headless/napi + proxy lane (a named deferral — attach_plan().in_browser reports it). data.source.duckdb-engine: the vendored MIT DuckDB-WASM (~36 MiB) loads first-class via purpose:engine (the governed 64 MiB per-artifact ceiling, D-07b — the plugin-cli size-gate would REJECT it as compute). data.source.remote: consent-gated HTTP(S) CSV/JSON/Parquet, transport-agnostic descriptor, content-hash invalidation, credentialRef strings only. data.source.refresh-policy: Manual(default)|OnOpen|Interval|Never round-trips; POLICY SHAPE ONLY — honored_interactively() acts on Manual/OnOpen(behind the §11 consent gate)/Never, Interval DEFERRED to the batch/automation lane (no background timer in the document realm, §11). PARTIAL: the Postgres/MySQL TCP lane + the live Interval scheduler are the named deferrals",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": "2026-06-13T09:20:46Z",
        "evidence_at": "2026-10-17T07:26:41.541Z",
        "headline": "All linked evidence is passing (4 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 4,
          "passed": 4,
          "skipped": 0
        }
      },
      "id": "data.source.db-attach",
      "importance": "normal",
      "since": "2026-06",
      "status": "experimental",
      "title": "D-11 DB attach (SQLite/Postgres/MySQL) + DuckDB-WASM first-class engine + refresh-policy + remote"
    },
    {
      "chapter": "data",
      "claims": [
        {
          "component": "plugin.data",
          "note": "wave 6 (2026-10-05/06; on main, published in @paged-media/data 0.1.0-canary.10, 2026-10-06; commits fddd8c4, ae29610, 792f017): the Data sources panel imports CSV, TSV, JSON (array or newline-delimited), Parquet and .xlsx (one worksheet per source, switchable) as one DuckDB table per source. JSON and Parquet use DuckDB's readers with the json/parquet extensions shipped in bin/duckdb-ext and loaded same-origin; XLSX is read by the data-xlsx crate (calamine 0.36) with one type per column and handed to read_json. Imported files persist with the document (inline up to 64 KiB, else data/<hash>.<ext>). File ▸ Import and drop route .json/.ndjson/.jsonl/.parquet to the plugin (contribute.importer). Tests: data-xlsx unit tests (test-map), and tagged [data.source.adapters] lanes test/sources-real.spec.ts, test/duckdb-browser.spec.ts, test/sources-persist-real-core.spec.ts. PARTIAL: SQLite unreadable in DuckDB-WASM 1.29.0 (sqlite_scanner attaches but SQLite's VFS cannot see registered files); CSV/TSV/XLSX not routed from File ▸ Import (contested extensions go to the first registrant, paged.sheet loses them).",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": "2026-10-06T03:04:22Z",
        "evidence_at": "2026-10-17T07:26:41.576Z",
        "headline": "All linked evidence is passing (5 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 5,
          "passed": 5,
          "skipped": 0
        }
      },
      "id": "data.source.local-formats",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Local import of JSON, NDJSON, Parquet and XLSX into DuckDB (data-xlsx for workbooks)"
    },
    {
      "chapter": "data",
      "claims": [
        {
          "component": "plugin.data",
          "note": "wave 6 (2026-10-05; on main, published in @paged-media/data 0.1.0-canary.10, 2026-10-06; commit fddd8c4): src/refresh.ts acts on each source's RefreshPolicy: manual; on open (queries re-run when the document opens; a remote source is fetched again only with a remembered grant); every N seconds (≥ 15) for a remote source only while its origin is consented, re-running queries only on a content change; never. A local file refuses the interval policy (a page keeps no handle to watch the file). The policy is saved with the session. Tests: data-conformance sources.rs data_source_refresh_policy_roundtrip (test-map); src/__tests__/refresh-policy.test.ts is tagged [data.source.adapters]/[data.security.gates]. PARTIAL: no file watching in a browser page; remote reach is limited to origins a deployment lists (D-03 residual).",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": "2026-10-06T03:04:22Z",
        "evidence_at": "2026-10-17T07:26:41.024Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "data.source.refresh-policy",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Refresh policies per source — manual, on open, interval (remote, consented), never"
    },
    {
      "chapter": "data",
      "claims": [
        {
          "component": "plugin.data",
          "note": "the Illustrator catalog's Variables row, landed HERE (not in paged.draw) because the donor engine is here and §2.1 forbids a drawing plugin from reaching into it — a Variables surface there would have had to grow a SECOND data model. A variable is a PROJECTION of a binding, not a new model: the binding id is the variable name, the binding kind is the trait. Trait-by-trait, verified rather than assumed. textcontent — ALREADY MET by the shipped D-01 tagged placeholder (Binding::Variable); no new mechanism, only the palette projection, pinned by a test so 'already met' is verified. filereference — ALREADY MET by the shipped D-14 image binding (placeImage through the CORE asset mechanism, never plugin-image); honest residual inherited from D-14: only URI-addressable references place, inline bytes/asset ids capture as no value rather than a fake path. visibility — BUILT (Binding::Visibility): a field value drives the element's OWN elementVisible property via setElementProperty (the same visibility the Layers panel toggles and the IDML Visible attribute carries) — never a parallel visibility system, never a delete-and-reinsert that would destroy the element identity other bindings anchor to. NO new core door was needed: elementVisible was already in the PropertyPath vocabulary. Three missing policies — hide (default), show, and LEAVE which writes nothing at all, so an unresolvable binding can never blank artwork; a present-but-not- coercible value takes the same policy (the engine never guesses a visibility). The ElementId KIND that setElementProperty needs is read from host.document.tree(), not guessed. graphdata — NOT resolvable here and deliberately so: paged.data has no chart surface and §2.1 forbids depending on paged.sheet (which has one — see sheet.chart.graphs) or paged.draw. The honest half IS shipped: an imported library's graph variables round-trip losslessly and applying one reports exactly why it cannot. Cross-plugin contract filed as RFI D-15. PARTIAL is the graphdata trait",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-17T07:27:59.045Z",
        "headline": "All linked evidence is passing (11 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 11,
          "passed": 11,
          "skipped": 0
        }
      },
      "id": "data.variable.traits",
      "importance": "normal",
      "since": "2026-08",
      "status": "experimental",
      "title": "Illustrator \"Variables\" — the four traits, mapped onto the shipped binding model (§16.4)"
    },
    {
      "chapter": "edge-cases",
      "claims": [
        {
          "component": "core.renderer",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "note": "diagnostics on load + export replies",
          "status": "shipped"
        },
        {
          "component": "core.sdk",
          "note": "structured Diagnostics on load/render",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:58:59.951Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "edge-cases.diagnostics-channel",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Render diagnostics channel (overset, missing assets, fonts)"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "note": "No control loads a font file. The editor hands its bundled Inter to every load as the default face, and plugins register faces through host.assets.registerFont; the Fonts panel lists the families in use and the missing ones but cannot supply a face, so text set in a font the editor was not given stays substituted.",
          "status": "planned"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "editor-shell.add-font",
      "importance": "normal",
      "since": null,
      "status": "planned",
      "title": "Add a font from a file"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "note": "Properties panel card (replaces the inert seam): diagnosis from the overset / links / fonts collections with no model call; suggestions and typed requests run the agent in assist mode on the selection; Undo reverts exactly Claude's steps (undoneSeq) and refuses after a later edit. Command palette assistant row hands requests to the Script panel. Canned-turn e2e in ai-assistant.spec.ts.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-11T17:52:49Z",
        "evidence_at": "2026-10-18T04:37:08.93Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "editor-shell.ai-assistant",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "AI Assistant card — local diagnosis, Claude makes the change, exact undo"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.shell",
          "note": "Dialog mints a blank page of the chosen size, then the agent runs in document mode (styles and swatches, then page by page, overset checks, page renders). CLI twin: `paged ai --new <size> <brief> -o out.paged`. Canned-turn e2e in ai-new-from-brief.spec.ts; quality with the live model not yet measured.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-11T17:52:49Z",
        "evidence_at": "2026-10-18T04:37:13.988Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "editor-shell.ai-new-from-brief",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "File ▸ New from a brief — Claude builds a whole document"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.shell",
          "status": "shipped"
        },
        {
          "component": "editor.styleguide",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:59:26.399Z",
        "headline": "All linked evidence is passing (20 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 20,
          "passed": 20,
          "skipped": 0
        }
      },
      "id": "editor-shell.cockpit-modes",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Cockpit workflow modes (Design/Content/Prepress/Data/Review/Export)"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.shell",
          "note": "seven insert verbs shipped + spec'd (command-authoring.spec.ts 9/9, @feat-tagged). PARTIAL residue = an ENGINE limitation found during the build: the batch $created sentinel binds only ElementId-typed fields (a minted STORY handle is not substitutable) and the batch executor does not dispatch PlaceImage/ ReplaceImageBytes children — so the compound verbs (table-with- frame, place image) run as two sequential mutations with compensating rollback instead of one atomic undo step; RFI candidate filed in the module header (story-handle door + image ops in the batch executor). CLICK-TO-PLACE (2026-08-23): after the picker returns the pointer arms (copy cursor) and the next click inside the canvas positions the image top-left; Escape cancels and places NOTHING, because a place that landed anyway after a cancel is worse than one that always centred. A click OUTSIDE the canvas cancels AND IS LET THROUGH — no preventDefault — because the obvious implementation (capture every pointerdown, consume it) is the bug already catalogued in the threading controller, where a loaded cursor ate clicks meant for panels and menus. Pinned ORIGIN-INDEPENDENTLY: two placements must recover the same page origin, which is only true if each frame's top-left sits on its own click, and which needs no knowledge of where layoutPages puts the page",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:55:03.266Z",
        "headline": "All linked evidence is passing (17 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 17,
          "passed": 17,
          "skipped": 0
        }
      },
      "id": "editor-shell.command-authoring",
      "importance": "normal",
      "since": "2026-08",
      "status": "experimental",
      "title": "Authoring verbs in palette/menus (insert/create commands)"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.shell",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-08-09T12:06:02Z",
        "evidence_at": "2026-10-18T05:03:50.196Z",
        "headline": "All linked evidence is passing (5 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 5,
          "passed": 5,
          "skipped": 0
        }
      },
      "id": "editor-shell.command-palette",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Command palette (Cmd+K)"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.shell",
          "note": "ADR 024 ACCEPTED 2026-08-07. THE ROOT CAUSE IS FIXED: `PagedEditor` gained `editContext`, without which no `when` predicate could ask what it was inside — that absence explained a whole class of defect and everything after it was cheap. SHIPPED: `CommandRegistry.invoke` honours `when` (declared on five contribution types, previously read by ONE registry, with the palette's own comment falsely claiming it applied); the menu bar evaluates it instead of dropping the field in `groupByTopLevel`; `Object > Group/Ungroup/Send to back` no longer silently reorders or DESTROYS the host frame from inside a plugin context; leaving a context by tool is derived in the controller rather than wired into four entry points of which one was right; a declared-EMPTY `toolIds` now means `no tool applies` instead of collapsing into `unrestricted`; the Window menu stops offering other content types' panels. ALL FIVE content plugins now declare `toolIds` + `panelIds` (sheets [] , web [], doc the host text tools — three different answers to one rule). NOT DONE and named honestly: the context TOOLBAR is still driven by workflow MODE, a different axis, which ADR 024 deliberately does not decide; per-panel applicability beyond the Window menu; plugin-data is content-bearing with no context at all. The `17 core-only panels` finding was RE-SCOPED after reading them — most are document-level surfaces behaving correctly, and a blanket migration would have been a regression",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:41:30.27Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "editor-shell.context-sensitivity",
      "importance": "normal",
      "since": "2026-08",
      "status": "experimental",
      "title": "Context-sensitivity — the surface follows what you are editing"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.shell",
          "note": "Design real tool pills; other modes honest stubs",
          "status": "partial"
        },
        {
          "component": "editor.styleguide",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-08-09T12:06:02Z",
        "evidence_at": "2026-10-18T05:11:40.5Z",
        "headline": "All linked evidence is passing (34 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 34,
          "passed": 34,
          "skipped": 0
        }
      },
      "id": "editor-shell.context-toolbars",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Per-mode context toolbars"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.shell",
          "note": "The title bar shows DocumentMeta's title, and falls back to the loaded file's name when the meta carries none (cockpit-panels.spec.ts U14). Registered 2026-08-19 — the behaviour and its test both shipped, tagged @feat:editor-shell.cockpit, an id that never existed; the nearest row (cockpit-modes) is about workflow MODES, so retagging would have filed the title bar under the wrong feature.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-09-09T09:42:19Z",
        "evidence_at": "2026-10-18T04:41:30.621Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "editor-shell.document-title",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Cockpit document title bar (falls back to the loaded file's name)"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.shell",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:02:45.484Z",
        "headline": "All linked evidence is passing (33 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 33,
          "passed": 33,
          "skipped": 0
        }
      },
      "id": "editor-shell.file-intake",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Document intake (file input + drop)"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.shell",
          "note": "Every registered binding is now asserted to resolve to a registered command (AC-CHROME-1) — a key pointing at a deleted command dispatches, finds nothing, and swallows the keypress, which from the DOM is indistinguishable from a key that was never bound. ONE COLLISION in the whole live surface: `j` is claimed by the shell's paged.fillStroke.toggleAffects AND by media.paged.image.tool.heal. Both carry the identical contentSelectionInactive guard, dispatch returns on the first enabled match, and chrome mounts before the bundle-load effect — so the shell wins and the healing brush's key is DEAD. The INV-REG-1 uniqueness claim covers tool-vs-tool, not tool-vs-shell-command. NOT SURFACED ANYWHERE: no UI lists shortcuts; KeybindingRegistry.list() existed for 'the future Show keybindings panel' with zero call sites until these tests, and Help > Keyboard shortcuts is a disabled soon seam",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-06-16T14:08:38Z",
        "evidence_at": "2026-10-18T05:10:20.563Z",
        "headline": "All linked evidence is passing (25 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 25,
          "passed": 25,
          "skipped": 0
        }
      },
      "id": "editor-shell.keyboard-shortcuts",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Global keyboard shortcuts (undo/redo, zoom, page nav)"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.shell",
          "note": "46 static items, 27 live and 19 `soon` seams; THREE MENUS ARE 100% DEAD — Type (4), Data (3), Help (2). The Data menu is the sharpest label/capability mismatch in the app: its three disabled items (Connect source… / Field mapping… / Generate pages…) duplicate three LIVE, enabled pills in the Data-layout toolbar that raise real paged.data panels, so a designer who reaches for the menu concludes the feature does not exist. NO ACCELERATOR COLUMN: MenuBar renders a label and, for seams, a `soon` pill — so Cmd+Z, Cmd+D, Cmd+G, Cmd+] and the rest are undiscoverable from the menus while tools DO advertise their keys in rail tooltips. A `when:false` item renders DISABLED, not removed (MenuBar.tsx:168-174 separates the two greying reasons deliberately), so a context-claimed panel entry stays visible and greyed rather than vanishing. Object's arrange and group items carry no `when` although their COMMANDS do, so inside an edit context they render enabled and invoke returns silently — the Insert items got this right",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": "2026-09-09T12:59:36Z",
        "evidence_at": "2026-10-18T05:10:25.556Z",
        "headline": "All linked evidence is passing (64 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 64,
          "passed": 64,
          "skipped": 0
        }
      },
      "id": "editor-shell.menus",
      "importance": "normal",
      "since": null,
      "status": "experimental",
      "title": "Menu bar (File/Edit/View/Window)"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.shell",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "docking + per-mode panel sets",
          "status": "shipped"
        },
        {
          "component": "editor.styleguide",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:06:33.53Z",
        "headline": "All linked evidence is passing (18 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 18,
          "passed": 18,
          "skipped": 0
        }
      },
      "id": "editor-shell.panel-rail",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Panel rail + inspector context routing"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "note": "The Illustrator catalog's Actions row, and the missing third of its Phase-3 \"scripting/actions/plugins\" (scripting ships as paged-script's Boa paged.* global; plugins ship as the BundleHost platform). Taps CommandRegistry.invoke via a new observe() seam — provably the complete command surface, since a repo-wide grep for `.handler(` returns exactly one hit. Records steps with per-step verdicts, replays against the REPLAY-TIME selection (Illustrator's semantics), and exports/imports JSON. Persisted app-level in localStorage, not the .paged container: an action's value is applying it to documents it was NOT recorded in, matching Illustrator .aia and Photoshop .atn. HONEST LIMIT, measured rather than disclaimed — direct manipulation never reaches the command registry (canvas gestures, typing, ~75 direct client.mutate call sites in panels), so a second tap counts gestureCommitted and mutationApplied arriving with no command in flight and each recording reports its own blind spot; the direct-edit figure is a FLOOR, not a ledger. Replay cannot collapse to one undo step: every command issues its own mutate, so there is no seam to compose at — that needs commands to DECLARE their mutations, a command-contract change. Editor bd60375; e2e actions.spec.ts (14).",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-08-05T13:27:53Z",
        "evidence_at": "2026-10-18T04:36:48.564Z",
        "headline": "All linked evidence is passing (14 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 14,
          "passed": 14,
          "skipped": 0
        }
      },
      "id": "editor-shell.panels.actions",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Actions (record / replay command sequences)"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "note": "selection scope live (one undo); page/margins/spread pending gap 7",
          "status": "shipped"
        },
        {
          "component": "editor.styleguide",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:07:53.299Z",
        "headline": "All linked evidence is passing (12 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 12,
          "passed": 12,
          "skipped": 0
        }
      },
      "id": "editor-shell.panels.align",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Align"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "note": "W2.12 — LIVE on the W1.16 AnchoredObjectSetting surface (protocol v35): selecting a frame anchored into a story surfaces ten anchored* PropertyEntries read back into the position controls; non-anchored selection states it honestly (anchored-panel.spec green; op round-trip in e2e/anchored-ops)",
          "status": "shipped"
        },
        {
          "component": "editor.styleguide",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:59:01.556Z",
        "headline": "All linked evidence is passing (5 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 5,
          "passed": 5,
          "skipped": 0
        }
      },
      "id": "editor-shell.panels.anchored",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Anchored object"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "note": "W2.3 — nonprinting + overprint pair (fill/stroke) live; per-frame visible/locked still seamed",
          "status": "shipped"
        },
        {
          "component": "editor.styleguide",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:05:05.142Z",
        "headline": "All linked evidence is passing (13 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 13,
          "passed": 13,
          "skipped": 0
        }
      },
      "id": "editor-shell.panels.attributes",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Attributes"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "note": "W2.4 listType / bulletChar / numberingFormat live (bullets-ops + bullets-panel e2e green); list-DEFINITION CRUD now wired — createNumberingList/editNumberingList/deleteNumberingList (W1.22, protocol v35, 2026-06-07); engine gap 22 closed",
          "status": "shipped"
        },
        {
          "component": "editor.styleguide",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:08:20.327Z",
        "headline": "All linked evidence is passing (30 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 30,
          "passed": 30,
          "skipped": 0
        }
      },
      "id": "editor-shell.panels.bullets-numbering",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Bullets & numbering (concept)"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "note": "W3.A2 — appliedCellStyle apply path live on the v29 table surface (table-ops e2e exercises cell ops); full table-selection UX still pending (gap 8)",
          "status": "partial"
        },
        {
          "component": "editor.styleguide",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:59:01.556Z",
        "headline": "All linked evidence is passing (8 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 8,
          "passed": 8,
          "skipped": 0
        }
      },
      "id": "editor-shell.panels.cell-styles",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Cell styles"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "note": "W2.1 — family/style/kerning/case/position/language/baseline/h+v-scale/skew/underline/strikethru/ligatures now live (character-ops e2e green); residual seams: OTF-feature chips (no per-chip mapping) + baseline-grid align (no wire path). RETARGETS since 2026-08-05 (commit 6e2640a, ADR 023 phase C/D — the VALUE axis: scalar properties over a RANGE, where the value can be MIXED). It retargets INDIRECTLY and that is the point: the panel still calls useBindings and never learns who answered, because binding-hook.ts was rewired to resolve every selectionProperty through the provider seam; the panel only emits data-binding-source for diagnostics. paged.sheet is the provider (3 valued paths, 28 declared-but-absent, writablePaths: [] — strictly read-only, because all spreadsheet semantics live in Rust). E2E: text-retarget.spec.ts over a real .xlsx fixture, asserting MIXED for core AND provider content. A LIVE PRE-EXISTING DEFECT the proof exposed: FillStrokeCluster rendered a multi-colour text range as 'None' — core's signal for mixed is entry-present-value-null, a different fact from no-entry-at-all, and the hook collapsed both into entry?.value ?? null with no mixed flag for content scope, so the cluster stated a falsehood about the user's own selection",
          "status": "shipped"
        },
        {
          "component": "editor.styleguide",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:08:09.605Z",
        "headline": "All linked evidence is passing (28 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 31,
          "passed": 28,
          "skipped": 3
        }
      },
      "id": "editor-shell.panels.character",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Character"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "note": "ApplyList; redefine/groups/overrides seamed (gap 15)",
          "status": "shipped"
        },
        {
          "component": "editor.styleguide",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:05:25.628Z",
        "headline": "All linked evidence is passing (11 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 11,
          "passed": 11,
          "skipped": 0
        }
      },
      "id": "editor-shell.panels.character-styles",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Character styles"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "note": "fill/tint + mixer; eyedropper pending",
          "status": "shipped"
        },
        {
          "component": "editor.styleguide",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:04:49.1Z",
        "headline": "All linked evidence is passing (12 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 12,
          "passed": 12,
          "skipped": 0
        }
      },
      "id": "editor-shell.panels.color",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Color"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "status": "shipped"
        },
        {
          "component": "editor.styleguide",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:59:01.556Z",
        "headline": "All linked evidence is passing (9 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 9,
          "passed": 9,
          "skipped": 0
        }
      },
      "id": "editor-shell.panels.color-groups",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Color groups"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "status": "shipped"
        },
        {
          "component": "editor.styleguide",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:05:45.938Z",
        "headline": "All linked evidence is passing (16 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 16,
          "passed": 16,
          "skipped": 0
        }
      },
      "id": "editor-shell.panels.color-settings",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Colour settings"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "note": "fully live; harmonies → batched swatches",
          "status": "shipped"
        },
        {
          "component": "editor.styleguide",
          "note": "brand kit wheel",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:59:01.556Z",
        "headline": "All linked evidence is passing (12 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 12,
          "passed": 12,
          "skipped": 0
        }
      },
      "id": "editor-shell.panels.color-wheel",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Color wheel"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "note": "collaboration backend",
          "status": "planned"
        },
        {
          "component": "editor.styleguide",
          "note": "honest ComingSoon",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:59:01.556Z",
        "headline": "All linked evidence is passing (8 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 8,
          "passed": 8,
          "skipped": 0
        }
      },
      "id": "editor-shell.panels.comments",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Comments + review inspector"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "status": "planned"
        },
        {
          "component": "editor.styleguide",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:59:01.556Z",
        "headline": "All linked evidence is passing (8 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 8,
          "passed": 8,
          "skipped": 0
        }
      },
      "id": "editor-shell.panels.component-library",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Component library"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "note": "rows; toggles/apply pending gap 25",
          "status": "shipped"
        },
        {
          "component": "editor.styleguide",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:04:16.776Z",
        "headline": "All linked evidence is passing (10 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 10,
          "passed": 10,
          "skipped": 0
        }
      },
      "id": "editor-shell.panels.conditions",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Conditions + condition sets"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "note": "may retire for the context toolbar",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:59:01.556Z",
        "headline": "All linked evidence is passing (3 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 3,
          "passed": 3,
          "skipped": 0
        }
      },
      "id": "editor-shell.panels.control",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Control strip"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "note": "data-publishing engine",
          "status": "planned"
        },
        {
          "component": "editor.styleguide",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:59:01.556Z",
        "headline": "All linked evidence is passing (9 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 9,
          "passed": 9,
          "skipped": 0
        }
      },
      "id": "editor-shell.panels.data-suite",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Data source / mapping / generated pages"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "note": "W2.12 — spread tree + thumbnails + health footer + sections list with insertSection now live (document-map-panel e2e green); per-page status chips still seamed (gaps 2–4)",
          "status": "partial"
        },
        {
          "component": "editor.styleguide",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:59:01.556Z",
        "headline": "All linked evidence is passing (13 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 13,
          "passed": 13,
          "skipped": 0
        }
      },
      "id": "editor-shell.panels.document-map",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Document map"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "note": "W2.2 — all 8 effect families live per-field (inner/outer glow, shadows, bevel, satin, feather, directional feather); effects-ops e2e green (glow render-honored in W4); residual seam: drop-shadow spread (no path)",
          "status": "shipped"
        },
        {
          "component": "editor.styleguide",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:04:54.399Z",
        "headline": "All linked evidence is passing (35 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 35,
          "passed": 35,
          "skipped": 0
        }
      },
      "id": "editor-shell.panels.effects",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Effects"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "note": "PDF/X-4 row real; web/social/package seamed",
          "status": "partial"
        },
        {
          "component": "editor.styleguide",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:59:01.556Z",
        "headline": "All linked evidence is passing (8 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 8,
          "passed": 8,
          "skipped": 0
        }
      },
      "id": "editor-shell.panels.export-center",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Export center"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "note": "concept panel LIVE in the real dock (main.tsx paged.export-tagging, panels/concept/export-tagging-panel.tsx): paragraph/character scope toggle is live local UI state, style→tag/class/PDF-tag mapping fields are inert seams awaiting style-level export-metadata writes (concept-panels.spec AC-CONCEPT + AC-CONCEPT-SCOPE); engine side remains gap 23",
          "status": "partial"
        },
        {
          "component": "editor.styleguide",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:59:01.556Z",
        "headline": "All linked evidence is passing (13 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 13,
          "passed": 13,
          "skipped": 0
        }
      },
      "id": "editor-shell.panels.export-tagging",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Export tagging (concept)"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "note": "W2.12 — families-in-use + Missing tab now real (FontSummary.is_missing, W0.6); fonts-panel e2e green",
          "status": "shipped"
        },
        {
          "component": "editor.styleguide",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:01:33.189Z",
        "headline": "All linked evidence is passing (12 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 12,
          "passed": 12,
          "skipped": 0
        }
      },
      "id": "editor-shell.panels.fonts",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Fonts"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "note": "fit type + crops live (rectangle-only)",
          "status": "shipped"
        },
        {
          "component": "editor.styleguide",
          "status": "shipped"
        },
        {
          "component": "editor.shell",
          "note": "Fitting is a setting, not a command. The fit type is reached from the rail (the Image button opens the Image inspector, whose Fitting section shows for a placed image), from the Window menu and from the command palette (\"Show: Frame Fitting\" opens the panel). There are no fitting commands (fit content proportionally, fill frame proportionally, fit content to frame, fit frame to content, center content): the command palette, the Object menu and the keyboard cannot fit a selection in one step.",
          "status": "planned"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:05:10.117Z",
        "headline": "All linked evidence is passing (12 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 12,
          "passed": 12,
          "skipped": 0
        }
      },
      "id": "editor-shell.panels.frame-fitting",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Frame fitting"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "note": "W2.12 — grid inserts via insertText + family select live; scope select still awaits the font registry",
          "status": "partial"
        },
        {
          "component": "editor.styleguide",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:59:31.292Z",
        "headline": "All linked evidence is passing (9 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 9,
          "passed": 9,
          "skipped": 0
        }
      },
      "id": "editor-shell.panels.glyphs",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Glyphs"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "note": "ramp editor + stops",
          "status": "shipped"
        },
        {
          "component": "editor.styleguide",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:08:31.173Z",
        "headline": "All linked evidence is passing (11 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 11,
          "passed": 11,
          "skipped": 0
        }
      },
      "id": "editor-shell.panels.gradients",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Gradients"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "status": "shipped"
        },
        {
          "component": "editor.styleguide",
          "note": "mono readout rows",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:00:27.436Z",
        "headline": "All linked evidence is passing (9 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 9,
          "passed": 9,
          "skipped": 0
        }
      },
      "id": "editor-shell.panels.info",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Info"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "status": "shipped"
        },
        {
          "component": "editor.styleguide",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:00:32.369Z",
        "headline": "All linked evidence is passing (8 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 8,
          "passed": 8,
          "skipped": 0
        }
      },
      "id": "editor-shell.panels.ink-manager",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Ink manager"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "status": "shipped"
        },
        {
          "component": "editor.styleguide",
          "note": "gallery filter + mono footer",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:07:47.191Z",
        "headline": "All linked evidence is passing (13 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 13,
          "passed": 13,
          "skipped": 0
        }
      },
      "id": "editor-shell.panels.inspector",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Inspector (raw, dev)"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "note": "ADR 025's local flight recorder; row added 2026-09-09 with the keyboard-shortcuts one, for the same reason",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:05:23.285Z",
        "headline": "All linked evidence is passing (5 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 5,
          "passed": 5,
          "skipped": 0
        }
      },
      "id": "editor-shell.panels.journal",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Journal"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "note": "Rendered from the LIVE KeybindingRegistry, so it cannot drift: a binding that exists appears, one removed disappears, plugin bindings show the moment their bundle loads. Closes the `Help` menu, which had two items and BOTH were disabled `soon(...)` seams — the app's only offer to explain itself explained nothing, while `KeybindingRegistry.list()` sat documented as existing 'for diagnostics + the future Show keybindings panel' with zero call sites. HONEST LIMIT, stated in the panel itself rather than only in the code: ~2 dozen bindings never reach the registry (undo/redo, zoom, page nav, caret motion, the spring-loaded holds are bound with ad-hoc window listeners), so they are listed from a maintained table and marked as such. A panel that silently omitted them would LOOK complete and be wrong, which is a worse lie than the seam it replaces.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:59:01.556Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "editor-shell.panels.keyboard-shortcuts",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Keyboard shortcuts panel (Help ▸ Keyboard shortcuts)"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "note": "full CRUD. RETARGETS since 2026-08-05 (commit 14eb7d4, ADR 023 phase C/D — the COLLECTION axis): rows come from useProvidedCollection('layers'), writes from useProviderFirstMutate, and per-control enablement from useCollectionPathOffered — so the ONE panel serves core layers or, inside a plugin's edit context, that plugin's own object stack. paged.draw is the first provider and serves the ENTERED CONTEXT'S object stack rather than the document's layers, which is what makes the test unpassable by serving the same rows twice. E2E: layers-retarget.spec.ts. The seam's retargeting read was put in the PLATFORM, not the panel, so every schema list declaring a documentCollection inherits it",
          "status": "shipped"
        },
        {
          "component": "editor.styleguide",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:08:04.987Z",
        "headline": "All linked evidence is passing (14 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 14,
          "passed": 14,
          "skipped": 0
        }
      },
      "id": "editor-shell.panels.layers",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Layers"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "note": "W2.12 — rows + status / PPI badges live (LinkSummary.{status,effective_ppi}, W0.6); links-panel e2e green; broken/lo-res fixtures still missing (fixmes)",
          "status": "shipped"
        },
        {
          "component": "editor.styleguide",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:01:43.186Z",
        "headline": "All linked evidence is passing (12 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 12,
          "passed": 12,
          "skipped": 0
        }
      },
      "id": "editor-shell.panels.links",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Links"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "note": "gallery ListRows; CRUD + jump-to pending",
          "status": "shipped"
        },
        {
          "component": "editor.styleguide",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:59:01.556Z",
        "headline": "All linked evidence is passing (12 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 12,
          "passed": 12,
          "skipped": 0
        }
      },
      "id": "editor-shell.panels.list-collections",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Articles / Hyperlinks / Bookmarks / Cross refs / Index"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "note": "apply-to-page pending gap 25",
          "status": "shipped"
        },
        {
          "component": "editor.styleguide",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:04:21.457Z",
        "headline": "All linked evidence is passing (16 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 16,
          "passed": 16,
          "skipped": 0
        }
      },
      "id": "editor-shell.panels.master-pages",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Master pages"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "note": "concept panel LIVE in the real dock (main.tsx paged.object-export, panels/concept/object-export-panel.tsx): tab switcher is live local UI state, all alt-text/tagged-PDF/EPUB fields are inert seams awaiting export-metadata writes against the element model (concept-panels.spec AC-CONCEPT + AC-CONCEPT-TABS); engine side remains gap 23",
          "status": "partial"
        },
        {
          "component": "editor.styleguide",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:59:01.556Z",
        "headline": "All linked evidence is passing (13 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 13,
          "passed": 13,
          "skipped": 0
        }
      },
      "id": "editor-shell.panels.object-export",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Object export options (concept)"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "note": "ApplyList",
          "status": "shipped"
        },
        {
          "component": "editor.styleguide",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:59:01.556Z",
        "headline": "All linked evidence is passing (9 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 9,
          "passed": 9,
          "skipped": 0
        }
      },
      "id": "editor-shell.panels.object-styles",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Object styles"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "note": "W2.3 — X/Y/W/H + opacity + rotation/scaleX/scaleY/flipH decompose dials live; flipV is WRITE-only (decompose V-lossiness — reads as flip_h+180°, seam note)",
          "status": "shipped"
        },
        {
          "component": "editor.styleguide",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:05:00.144Z",
        "headline": "All linked evidence is passing (28 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 28,
          "passed": 28,
          "skipped": 0
        }
      },
      "id": "editor-shell.panels.object-transform",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Object transform"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "status": "shipped"
        },
        {
          "component": "editor.styleguide",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:59:01.556Z",
        "headline": "All linked evidence is passing (8 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 8,
          "passed": 8,
          "skipped": 0
        }
      },
      "id": "editor-shell.panels.outline",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Outline"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "note": "CMYK check real; other rows seamed",
          "status": "partial"
        },
        {
          "component": "editor.styleguide",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:59:01.556Z",
        "headline": "All linked evidence is passing (9 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 9,
          "passed": 9,
          "skipped": 0
        }
      },
      "id": "editor-shell.panels.output-readiness",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Output readiness"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "note": "target nav + live export button; inline settings pending",
          "status": "partial"
        },
        {
          "component": "editor.canvas",
          "note": "Export Center / outputs panel driven in the full editor export flow (export-family.spec.ts green)",
          "status": "partial"
        },
        {
          "component": "editor.styleguide",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:59:01.556Z",
        "headline": "All linked evidence is passing (18 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 20,
          "passed": 18,
          "skipped": 2
        }
      },
      "id": "editor-shell.panels.outputs",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Outputs + export inspector"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "note": "insert/delete live; duplicate seamed (gap 25)",
          "status": "shipped"
        },
        {
          "component": "editor.styleguide",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:04:05.858Z",
        "headline": "All linked evidence is passing (15 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 15,
          "passed": 15,
          "skipped": 0
        }
      },
      "id": "editor-shell.panels.pages-list",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Pages (list)"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "status": "shipped"
        },
        {
          "component": "editor.styleguide",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:03:45.009Z",
        "headline": "All linked evidence is passing (12 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 12,
          "passed": 12,
          "skipped": 0
        }
      },
      "id": "editor-shell.panels.pages-navigator",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Pages (navigator filmstrip)"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "note": "W2.1 — L/R indents, drop-caps, hyphenation, keep-options, rule above/below now live (paragraph-ops e2e green; render-honor verified for indents + rule-above in W4); gap-12 set closed. RETARGETS through the same binding-hook rewiring as editor-shell.panels.character (commit 6e2640a, ADR 023 phase C/D); paged.sheet declares 13 paragraph paths as ABSENT rather than leaving them unclaimed, which is the difference between 'this content has no such property' and 'nobody answered'",
          "status": "shipped"
        },
        {
          "component": "editor.styleguide",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:08:43.436Z",
        "headline": "All linked evidence is passing (32 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 33,
          "passed": 32,
          "skipped": 1
        }
      },
      "id": "editor-shell.panels.paragraph",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Paragraph"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "note": "ApplyList; seams as character styles",
          "status": "shipped"
        },
        {
          "component": "editor.styleguide",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:08:26.116Z",
        "headline": "All linked evidence is passing (18 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 18,
          "passed": 18,
          "skipped": 0
        }
      },
      "id": "editor-shell.panels.paragraph-styles",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Paragraph styles"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "note": "boolean four live; minus-back/divide/convert seamed",
          "status": "shipped"
        },
        {
          "component": "editor.styleguide",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:04:00.71Z",
        "headline": "All linked evidence is passing (15 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 15,
          "passed": 15,
          "skipped": 0
        }
      },
      "id": "editor-shell.panels.pathfinder",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Pathfinder"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "note": "W2.12 — real dry PDF-export findings + page-jump (PreflightFinding{code,severity,page_index}, W0.6; preflight-panel e2e green); client.exportPdf still drops the typed findings (W3.A2 follow-up #6); PPI/bleed seamed (gap 20)",
          "status": "partial"
        },
        {
          "component": "editor.styleguide",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:59:01.556Z",
        "headline": "All linked evidence is passing (17 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 17,
          "passed": 17,
          "skipped": 0
        }
      },
      "id": "editor-shell.panels.preflight",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Preflight"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "note": "paged.web W-05 — host problems panel consuming host.diagnostics from EVERY loaded bundle via the injected sink (problems-store); a published diagnostic lists + click focuses the source panel (web-plugin.spec AC-WEB-5)",
          "status": "shipped"
        },
        {
          "component": "editor.styleguide",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:10:49.715Z",
        "headline": "All linked evidence is passing (13 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 13,
          "passed": 13,
          "skipped": 0
        }
      },
      "id": "editor-shell.panels.problems",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Problems"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "note": "routes by selection kind; overset chip + AI card seamed",
          "status": "shipped"
        },
        {
          "component": "editor.shell",
          "note": "the Properties inspector is surfaced by the shell context per intent (DTP journey tier green)",
          "status": "shipped"
        },
        {
          "component": "editor.styleguide",
          "note": "kit inspectors + gallery sub-header",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:11:40.5Z",
        "headline": "All linked evidence is passing (67 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 67,
          "passed": 67,
          "skipped": 0
        }
      },
      "id": "editor-shell.panels.properties",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Properties (context inspector)"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "note": "W2.12 — real publication-health metric tiles + counts + X-4 pill (publication-health-panel e2e green); broken-link / missing-font / overset risk counts still need their fixtures (gaps 1–4)",
          "status": "partial"
        },
        {
          "component": "editor.styleguide",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:59:01.556Z",
        "headline": "All linked evidence is passing (10 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 10,
          "passed": 10,
          "skipped": 0
        }
      },
      "id": "editor-shell.panels.publication-health",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Publication health"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "note": "renders entirely from a PanelSchema over the real swatches collection: live rows (incl. a mid-session createSwatch), selection publish-back gating a readout row, an applyEntity action writing frameFillColor, and a command action receiving the row id. Kept deliberately small — it is the executable proof that a bundle could author the same schema",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:43:33.788Z",
        "headline": "All linked evidence is passing (5 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 5,
          "passed": 5,
          "skipped": 0
        }
      },
      "id": "editor-shell.panels.schema-list-demo",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Schema list demo (consumer proof for the v1.1 list tier)"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "note": "declares the v1.2 members against a live collection so the tier has a real consumer: tree rows over a parentField, drag-reorder emitting reorderElement in its absolute {index} form, and inline rename through a command action. Exists for the same reason the v1.1 demo does — the tier is only proven if something outside the panel that motivated it can express it",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "editor-shell.panels.schema-tree-demo",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Schema tree demo (consumer proof for the v1.2 tree / reorder / rename tier)"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "note": "§21 advanced prepress, ink-coverage third. TWO readings, both real: swatch ink limit from SwatchSummary.totalAreaCoveragePct (exact palette arithmetic — no render, no profile, resolution-free) and the plate lane from core's protocol-61 inkCoverage collection (job plate list, per-page max/mean TAC, stored histogram so a press-preset change re-thresholds with no re-render). partial for ONE reason: plate-isolated CANVAS preview is not built — Vello keeps no page-level ink-plane state, only the CPU rasterizer does, so the panel points at paged-inspect --separations rather than render a second image beside the real canvas. separations-panel.spec.ts 5/5 green",
          "status": "partial"
        },
        {
          "component": "editor.styleguide",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:50:54.638Z",
        "headline": "All linked evidence is passing (4 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 5,
          "passed": 4,
          "skipped": 1
        }
      },
      "id": "editor-shell.panels.separations",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Separations & ink limit"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "note": "page membership pending gap 7",
          "status": "shipped"
        },
        {
          "component": "editor.styleguide",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:59:01.556Z",
        "headline": "All linked evidence is passing (15 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 15,
          "passed": 15,
          "skipped": 0
        }
      },
      "id": "editor-shell.panels.spreads",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Spreads"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "note": "W2.12 — story list + counts live via paged.stories() (stories-panel e2e green); W3.A2 follow-up switches to collection(\"stories\") (landed W3.A0); per-story field inspector still seamed (gaps 9, 10). Panel→canvas selection sync is ABSENT: clicking a story in the panel does not select/reveal its frames on the canvas — the current wiring is canvas→panel context only (hands-on audit 2026-08-17, U6)",
          "status": "partial"
        },
        {
          "component": "editor.styleguide",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:59:01.556Z",
        "headline": "All linked evidence is passing (15 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 15,
          "passed": 15,
          "skipped": 0
        }
      },
      "id": "editor-shell.panels.stories",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Stories + story inspector"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "note": "W2.2 — type/join/miter/alignment/gap-colour+tint now live (join/miter/align Rectangle-only); stroke-ops e2e green (gap-colour under-paint render-honored in W4); residual seam: dashes & arrows (no dash-array path)",
          "status": "shipped"
        },
        {
          "component": "editor.styleguide",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-11T17:03:11Z",
        "evidence_at": "2026-10-18T05:07:23.39Z",
        "headline": "All linked evidence is passing (28 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 28,
          "passed": 28,
          "skipped": 0
        }
      },
      "id": "editor-shell.panels.stroke",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Stroke"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "note": "full manager: mixer popover, .ase, groups, gamut badge. RETARGETS since 2026-08-05 (commit b3115ff, ADR 023 phase C/D — the SCOPE axis, a DOCUMENT RESOURCE the panel itself edits rather than a property of the selection): useProvidedCollection over 'swatches' and 'colorGroups', with useCollectionOpOffered disabling every control the active owner cannot serve. Read AND write retarget — a rename flows through as editSwatch to paged.sheet, which mints via createSwatch, and the engine's collection gains the row. This axis needed a NEW HOST HOOK (useCollectionOpOffered) because core models a swatch's mutable surface as structural OPS carrying a full SwatchSpec — the PropertyPath union has no swatchName and no swatch colour — so useCollectionPathOffered could not ask the question; the CONTRACT needed nothing (provides.ops already existed), the host did. E2E: swatches-retarget.spec.ts",
          "status": "shipped"
        },
        {
          "component": "editor.styleguide",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:59:01.556Z",
        "headline": "All linked evidence is passing (9 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 9,
          "passed": 9,
          "skipped": 0
        }
      },
      "id": "editor-shell.panels.swatches",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Swatches"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "note": "W3.A2 — Table panel op sandwiches live on the v29 NodeId surface (cell fill/tint/insets/vjust, row-height/col-width, insert/delete row+column — table-ops e2e green); spans + header/footer inserts now wired (setCellSpan/insertHeaderRow/removeHeaderRow/insertFooterRow/removeFooterRow, W1.11-13, protocol v35, 2026-06-07); cell TEXT editing still deferred (no op — W3.A1)",
          "status": "partial"
        },
        {
          "component": "editor.styleguide",
          "note": "kit field layout, Concept badge",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:07:58.283Z",
        "headline": "All linked evidence is passing (24 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 24,
          "passed": 24,
          "skipped": 0
        }
      },
      "id": "editor-shell.panels.table",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Table (concept)"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "note": "W3.A2 — appliedTableStyle apply path live on the v29 table surface; full table-selection UX still pending (gap 8)",
          "status": "partial"
        },
        {
          "component": "editor.styleguide",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:59:01.556Z",
        "headline": "All linked evidence is passing (8 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 8,
          "passed": 8,
          "skipped": 0
        }
      },
      "id": "editor-shell.panels.table-styles",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Table styles"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "note": "W2.4 Tabs panel fully live (whole-list TabStopSpec editor) — tabs-ops + tabs-panel e2e green",
          "status": "shipped"
        },
        {
          "component": "editor.styleguide",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:59:01.556Z",
        "headline": "All linked evidence is passing (17 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 17,
          "passed": 17,
          "skipped": 0
        }
      },
      "id": "editor-shell.panels.tabs",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Tabs (concept)"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "note": "W2.3 — inset + columns (count/gutter/balance) + vertical-justify + auto-sizing + first-baseline now live; residual seam: none core-blocking",
          "status": "shipped"
        },
        {
          "component": "editor.styleguide",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:04:26.077Z",
        "headline": "All linked evidence is passing (11 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 12,
          "passed": 11,
          "skipped": 1
        }
      },
      "id": "editor-shell.panels.text-frame-options",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Text frame options"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "note": "W2.3 — mode + offsets + invert (textWrapInvert) live; residual seams: wrap-to / contour-type (core DEFERRED W0.3)",
          "status": "shipped"
        },
        {
          "component": "editor.styleguide",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:59:01.556Z",
        "headline": "All linked evidence is passing (11 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 11,
          "passed": 11,
          "skipped": 0
        }
      },
      "id": "editor-shell.panels.text-wrap",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Text wrap"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.panel",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:59:01.556Z",
        "headline": "All linked evidence is passing (10 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 10,
          "passed": 10,
          "skipped": 0
        }
      },
      "id": "editor-shell.panels.tree",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Tree (scene graph, dev)"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.shell",
          "note": "main.tsx loads + composes the EIGHT first-party bundles — draw, web, image, sheet, data, doc, pdf, publish — and injects the shared dataProviders registry (5df546b, d66bfeb — D-09); capability enforcement at the host chokepoints (W3.10, capabilityMode=enforce). Corrected 2026-08-09: this row said FIVE until an audit counted the imports in main.tsx. NOTE for anyone re-running CI: @paged-media/doc is the one bundle NOT on npm (E404) — a deliberate deferral — so the editor reaches it through a pnpm link: override in package.json, and that override is why a CI install cannot resolve it.",
          "status": "shipped"
        },
        {
          "component": "plugin.draw",
          "note": "the paged.draw bundle loads + activates through the shell loader — tools/anchors/undo proven end-to-end (e2e/draw-plugin.spec.ts AC-DRAW-1)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-06-18T11:50:06Z",
        "evidence_at": "2026-10-18T05:12:44.489Z",
        "headline": "All linked evidence is passing (43 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 48,
          "passed": 43,
          "skipped": 5
        }
      },
      "id": "editor-shell.plugin-bundles",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "First-party bundle loading + composition (draw/web/image/sheet/data/doc/pdf/publish)"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.shell",
          "note": "check-protocol-version.sh already guarded the ENGINE boundary; the PLUGIN boundary had nothing, and had drifted on 6 of 8 bundles (data canary.1→3, draw canary.1→5, image canary.0→3, pdf 0.2.3→0.2.5, sheet canary.1→3, web canary.1→3). Two refinements keep it honest rather than merely loud: a local link:/file: pnpm override is reported SEPARATELY and excluded from the failure count (it is not shipping drift — real shipping drift was 3, not 6), and an absent sibling repo reports SKIPPED with exit 0 rather than a manufactured green. Probes both the ~/paged/plugins/ and flat-sibling layouts and both bundle locations (packages/<name>-bundle and glue/ for paged.image). DELIBERATELY NOT CI AND NOT A pnpm SCRIPT — run as `node scripts/check-plugin-pins.mjs` (or --warn-only) at release time, because a plugin repo running ahead of the pin is the NORMAL intermediate state during a campaign and a CI gate on it would be red by design for weeks at a time",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "editor-shell.plugin-pin-drift-guard",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Plugin-canary pin drift check (the PLUGIN boundary, as protocol-version guards the ENGINE one)"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.shell",
          "note": "lifts the scalar WidgetValueBinding ceiling for LISTS: rows from a documentCollection or a plugin-published binding, label/secondary fields, selectionBinding publish-back, per-row command or applyEntity actions (element or content scope). THE v1.2 TIER LANDED 2026-08-05 (commit 755a753), closing what this note used to list as absent — TREE ROWS (SchemaListSpec.tree {parentField, defaultExpanded}: rows stay FLAT with a parent pointer, mirroring LayerSummary.parentId from protocol 60; orphans render as roots, cycles surface at depth 0, no row is ever dropped), DRAG-REORDER (SchemaListSpec.reorder, emitting the engine's own reorderElement from protocol 59 in its ABSOLUTE {index} form — cross-parent drops are refused and an out-of-range index stays the engine's loud rejection, never a clamp) and INLINE RENAME (SchemaListSpec.rename, deliberately COMMAND-ONLY because every rename the engine models is a dedicated op: layerSetName needs NodeId::Layer which no wire ElementId can express, and editSwatch takes a whole spec). It is the hard dependency ADR 023 phase B named. HONEST LIMITS: there is NO version STRING in the code — 'v1.2' is a documentary tier name in comments, enforced structurally instead (every member is additive + optional, so v1.1 schemas stay assignable, and the injection-seam assert in main.tsx is what has teeth). Still absent: virtualization (the 500-row cap + visible overflow row stands), an expansionBinding (expansion is panel-local and resets when a dock tab reopens), and a state COLUMN — SchemaListSpec carries only static-label actions, so the retired draw Layers panel's eye/lock/print glyphs toggle but do not SHOW state",
          "status": "partial"
        },
        {
          "component": "plugin.api",
          "note": "mirrored additively (schema v1.1) so bundles can author list rows; assignability against the editor's mirror verified rather than assumed",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:43:33.788Z",
        "headline": "All linked evidence is passing (5 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 5,
          "passed": 5,
          "skipped": 0
        }
      },
      "id": "editor-shell.schema-panel-list-tier",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Schema panels v1.1 — list widget + applyEntity write (B-01 / G3)"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.shell",
          "note": "`?solo=paged.draw` drops the DTP furniture (Layout/Type menus, mode switcher, page verbs), boots a document so the bundle's menu entries are ENABLED rather than uniformly greyed, and composes the host's geometry tools with the bundle's modifiers. `scripts/solo-profiles-guard.mjs` gates the profiles; three `solo-mode.journey.spec.ts` tests drive them. Row added 2026-09-09: the capability and its tests both shipped tagged @feat:editor-shell.solo-mode, an id that had no row — the same shape as editor-shell.document-title below, and the reason the editor now carries a vocabulary guard.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:59:45.05Z",
        "headline": "All linked evidence is passing (7 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 7,
          "passed": 7,
          "skipped": 0
        }
      },
      "id": "editor-shell.solo-mode",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Solo mode — the shell boots as a single plugin's own application (?solo=<bundle>)"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.shell",
          "note": "Counts the CONCRETE surface a user can touch — 362 ids: host panels, live tools, the keybinding and menu tables, and every contribution the loaded plugin bundles inject — and fails when one is named by no spec. It answers what the @feat gate cannot: a registry row is satisfied by any ONE of the surfaces exposing it, so a rail tool can be untested while its capability reads covered. At 362/362 with every ceiling 0, so new surface arrives with a spec or it does not arrive. Fails BOTH ways — over a ceiling, and UNDER it, because a ceiling left above reality readmits everything between the real count and the number. Two fail-open traps closed while writing it: extractors assert a floor on what they match (a regex that quietly stops matching turns it into a gate that always passes), and bundle manifests resolve through the canvas app's node_modules rather than a scan of the pnpm store, which holds four draw versions and had it measuring one the app does not load. Runs in the `checks` job — no browser, no fixtures. Honest limit: covered means the id appears in a spec, which is a weak proxy chosen deliberately — only 7 call sites invoke by command id, so a stricter rule would report false gaps everywhere and be ignored. Depth is the @feat gate's coverage_level axis.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "editor-shell.surface-coverage",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Surface-coverage gate — every registered id is named by a spec"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.shell",
          "status": "shipped"
        },
        {
          "component": "editor.styleguide",
          "note": "--paged-* channels + resolved layers",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:59:26.399Z",
        "headline": "All linked evidence is passing (11 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 11,
          "passed": 11,
          "skipped": 0
        }
      },
      "id": "editor-shell.theme",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Dark/light theming (dark default, token system)"
    },
    {
      "chapter": "editor-shell",
      "claims": [
        {
          "component": "editor.shell",
          "note": "NO MORE DEAD ENTRIES (commit 2e6f835). Of 31 built-in tools, 15 rendered in the rail, accepted a click and then silently did nothing — no gesture, no id-routing anywhere in app code, which is worse than an empty slot because the user reads the dead affordance as a fault in their own input. All 15 resolved: SIX WIRED over engine arms that already existed (rectangleFrame / ellipseFrame / polygonFrame onto the same insertFrame / insertOval / insertPath handlers their Shape-slot twins use — paged has no separate placeholder node kind; rotate / scale onto the gesture arms the selection chrome already drove; smooth onto simplifyPath with tolerance as a tool option). FIVE STUBBED behind a new ToolStatus 'planned' — the rail's equivalent of the cockpit's ComingSoon: dimmed, data-tool-status='planned', aria-disabled, refuses activation, and contributes NO command and NO keybinding (a shortcut on a planned tool is an INV-REG-1 reservation, never bound) — gap, freeTransform, contentCollector, contentPlacer (no element-duplicate op exists on the wire at all) and note (no Note in the engine model). FOUR RETIRED as redundant with working paged.draw tools, freeing keys the plugin must now claim in its own repo: typePath (shift+t, which had been SHADOWING media.paged.draw.tool.typeOnPath and forcing it onto shift+h), eyedropper (i — it was even its slot's group DEFAULT), measure (k) and erase. 27 built-in tool ids remain, verified in the tree",
          "status": "shipped"
        },
        {
          "component": "editor.styleguide",
          "note": "clean-room tool glyphs",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:59:26.399Z",
        "headline": "All linked evidence is passing (37 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 37,
          "passed": 37,
          "skipped": 0
        }
      },
      "id": "editor-shell.tool-rail",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Tool rail (built-in tool catalog, flyout groups, tear-off, ToolStatus)"
    },
    {
      "chapter": "editor-tools",
      "claims": [
        {
          "component": "core.canvas-wasm",
          "note": "Phase G LANDED the two gestures the old note called reserved. All three content gestures resolve in paged-canvas gesture.rs — TranslateContent (Phase F, world delta inverse-rotated through the frame's item_transform so it no longer drifts on rotated frames), RotateContent and ScaleContent (both about the bounds centroid in frame-inner space) — and each emits NodeMutation::ImageTransform, which build_op_from_mutation maps to Operation::SetProperty{ImageContentTransform} (apply arm in paged-mutate apply/set_property.rs). Targets the PLACED IMAGE's content transform, so a TextFrame is a documented no-op (it carries no content transform). Unit-tested in gesture.rs (pivot invariance, 90° basis, both op-emission arms)",
          "status": "shipped"
        },
        {
          "component": "editor.gesture",
          "note": "Cmd-drag content grabber + W2.10 content rotate (Cmd+Shift) / scale (Cmd+Alt) — gesture-content e2e green (image-paint delta is fixture-conditioned on a resolvable image)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:00:44.272Z",
        "headline": "All linked evidence is passing (8 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 8,
          "passed": 8,
          "skipped": 0
        }
      },
      "id": "editor-tools.content-transform",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Content transforms inside frame (translate/rotate/scale content)"
    },
    {
      "chapter": "editor-tools",
      "claims": [
        {
          "component": "editor.gesture",
          "note": "W2.7 gridify on rectangle drags (arrows mid-drag, 12pt gutter, batch commit); N×M unblocked by core batch-id minting (27f7d0a) — gesture-gridify e2e green",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:03:50.041Z",
        "headline": "All linked evidence is passing (8 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 8,
          "passed": 8,
          "skipped": 0
        }
      },
      "id": "editor-tools.draw.gridify",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Gridify (arrow keys mid-drag → N×M frames)"
    },
    {
      "chapter": "editor-tools",
      "claims": [
        {
          "component": "editor.gesture",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-06-17T04:51:32Z",
        "evidence_at": "2026-10-18T04:58:43.753Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "editor-tools.draw.line",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Line draw tool (Shift 45° constraint)"
    },
    {
      "chapter": "editor-tools",
      "claims": [
        {
          "component": "editor.gesture",
          "note": "W2.5 built-in Pen (PenMachine-identical math, insertPath single undo) — gesture-pen e2e green; coexists with draw-bundle pen in one flyout slot; handle-line preview (VR-06) deferred Updated 2026-10-05: the handler is now the host half over paged.draw's PenMachine v2 (editor a4865d5), not a copy of its math; it also continues, closes and joins existing open paths and adds / deletes anchors on the selected path, each one undo step (plugin-draw.pen-existing-paths; e2e/path-editing.spec.ts AC-PENX-1…4).",
          "status": "shipped"
        },
        {
          "component": "editor.canvas",
          "note": "pen path preview renders on the editor canvas overlay (e2e/overlay-path-preview.spec.ts green)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:11:52.018Z",
        "headline": "All linked evidence is passing (17 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 17,
          "passed": 17,
          "skipped": 0
        }
      },
      "id": "editor-tools.draw.pen",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Pen tool path drawing (+ add/delete/convert anchor)"
    },
    {
      "chapter": "editor-tools",
      "claims": [
        {
          "component": "editor.gesture",
          "note": "pressure ignored",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:00:37.433Z",
        "headline": "All linked evidence is passing (3 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 3,
          "passed": 3,
          "skipped": 0
        }
      },
      "id": "editor-tools.draw.pencil",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Pencil freehand (RDP simplification)"
    },
    {
      "chapter": "editor-tools",
      "claims": [
        {
          "component": "editor.gesture",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:02:05.371Z",
        "headline": "All linked evidence is passing (25 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 25,
          "passed": 25,
          "skipped": 0
        }
      },
      "id": "editor-tools.draw.rectangle",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Rectangle draw tool"
    },
    {
      "chapter": "editor-tools",
      "claims": [
        {
          "component": "core.canvas-wasm",
          "status": "shipped"
        },
        {
          "component": "editor.canvas",
          "note": "document-level Escape under pointer capture",
          "status": "shipped"
        },
        {
          "component": "editor.gesture",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:01:32.969Z",
        "headline": "All linked evidence is passing (21 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 22,
          "passed": 21,
          "skipped": 1
        }
      },
      "id": "editor-tools.gesture-lifecycle",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Gesture lifecycle guarantees (Esc cancel, atomic commit, snap lines)"
    },
    {
      "chapter": "editor-tools",
      "claims": [
        {
          "component": "editor.gesture",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:00:37.433Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "editor-tools.gradient-tools",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Gradient swatch + gradient feather drag tools"
    },
    {
      "chapter": "editor-tools",
      "claims": [
        {
          "component": "editor.gesture",
          "note": "W2.8 guide drag from ruler hit zones (any tool; move/delete/Esc) — gesture-guides e2e green; W3.A0 SpreadSummary.guides now backs the overlay mirror; guide-undo mirror + reload round-trip are W3.A2 fixmes",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:00:22.946Z",
        "headline": "All linked evidence is passing (10 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 10,
          "passed": 10,
          "skipped": 0
        }
      },
      "id": "editor-tools.guides.drag",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Ruler-guide drag (create/move/delete)"
    },
    {
      "chapter": "editor-tools",
      "claims": [
        {
          "component": "core.canvas-wasm",
          "note": "CloneTranslate node spec",
          "status": "shipped"
        },
        {
          "component": "editor.gesture",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:00:44.272Z",
        "headline": "All linked evidence is passing (4 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 4,
          "passed": 4,
          "skipped": 0
        }
      },
      "id": "editor-tools.move.duplicate-drag",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Duplicate-drag (Alt)"
    },
    {
      "chapter": "editor-tools",
      "claims": [
        {
          "component": "core.canvas-wasm",
          "note": "GestureType::Translate + SAB hot path",
          "status": "shipped"
        },
        {
          "component": "editor.canvas",
          "status": "shipped"
        },
        {
          "component": "editor.gesture",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-06-18T11:50:06Z",
        "evidence_at": "2026-10-18T05:11:17.046Z",
        "headline": "All linked evidence is passing (52 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 52,
          "passed": 52,
          "skipped": 0
        }
      },
      "id": "editor-tools.move.translate",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Move / drag frames (Shift dominant-axis, snap, multi-select rigid)"
    },
    {
      "chapter": "editor-tools",
      "claims": [
        {
          "component": "editor.gesture",
          "note": "hand tool + Space spring-load via tool stack",
          "status": "shipped"
        },
        {
          "component": "editor.shell",
          "note": "Space spring-loads the Hand tool from the tool rail (tool-rail.spec.ts AC-RAIL-4 green)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-06-16T14:08:38Z",
        "evidence_at": "2026-10-18T05:00:44.272Z",
        "headline": "All linked evidence is passing (3 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 3,
          "passed": 3,
          "skipped": 0
        }
      },
      "id": "editor-tools.nav.pan",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Pan (hand tool, Space spring-load)"
    },
    {
      "chapter": "editor-tools",
      "claims": [
        {
          "component": "editor.gesture",
          "status": "shipped"
        },
        {
          "component": "editor.shell",
          "note": "zoom commands + keyboard shortcuts",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:02:32.378Z",
        "headline": "All linked evidence is passing (6 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 6,
          "passed": 6,
          "skipped": 0
        }
      },
      "id": "editor-tools.nav.zoom",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Zoom (tool click/Alt-click, commands, fit)"
    },
    {
      "chapter": "editor-tools",
      "claims": [
        {
          "component": "editor.shell",
          "note": "SEVEN verbs, and the reason they live in the HOST is the placement argument ADR 023 was raised out of — basic object operations are what plugins BUILD ON. paged.object.bringToFront / bringForward / sendBackward / sendToBack / group / ungroup / selectParentGroup, category 'Object', registered as commands + menu items + keybindings. Group / Ungroup / Select-parent existed ONLY inside paged.draw, so a user without the vector plugin could not group although CreateGroup / DissolveGroup have been wire ops the whole time; Arrange did not exist ANYWHERE (the editor's entire command set was Undo, Redo, Open PDF, Save As IDML and four zoom verbs). paged.draw's three structural commands retired in the same wave so there is exactly ONE implementation of each. Four measured facts shape it: (1) Arrange rides reorderElement with the four RELATIVE verbs, never {index}, because the verbs are evaluated against the order the engine holds AT APPLY TIME — a concurrent insert cannot make them restack the wrong item; the absolute form is the honest shape for a layers-panel DRAG and is what the schema-panel renderer uses. (2) reorderElement CANNOT REPARENT — the engine derives the sibling list from where the node already is, so bring- to-front on a grouped item brings it to the front OF ITS GROUP. (3) ARRANGE IS WITHIN A LAYER: the renderer sorts frames_in_order by ItemLayer before painting, so the op applies and the z table changes but nothing moves on canvas — said out loud through report rather than left for the user to discover. (4) client.mutate NEVER REJECTS (a refusal arrives as a resolved mutationFailed), so every call site inspects the reply instead of writing a .catch that would swallow the loud refusal. Multi-selection preserves RELATIVE ORDER",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "note": "rides reorderElement (protocol 59) — see layers.z-ordering",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:08:01.487Z",
        "headline": "All linked evidence is passing (3 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 3,
          "passed": 3,
          "skipped": 0
        }
      },
      "id": "editor-tools.object-commands",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "paged.object.* — the structural object command layer (Arrange x4 + Group / Ungroup / Select parent)"
    },
    {
      "chapter": "editor-tools",
      "claims": [
        {
          "component": "editor.canvas",
          "note": "12 overlay contributions, SVG tokens. The title enumerates them on purpose: `thread-lines.journey.spec.ts` was tagged @feat:editor-shell.overlays.text-threads, an id that never existed, because the reader could not tell from a title what was in scope here. Adding an overlay means naming it in this title — or, when it is substantial enough to describe on its own, giving it a row, which is what `editor-tools.text.thread-lines` above is.",
          "status": "shipped"
        },
        {
          "component": "editor.styleguide",
          "note": "magenta selection / violet guides per brand",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-09-09T12:03:15Z",
        "evidence_at": "2026-10-18T05:03:18.821Z",
        "headline": "All linked evidence is passing (12 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 12,
          "passed": 12,
          "skipped": 0
        }
      },
      "id": "editor-tools.overlays",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Canvas overlays (selection chrome, handles, marquee, tool preview, snap lines, caret, page decorations, rulers, hit marker, threading ports)"
    },
    {
      "chapter": "editor-tools",
      "claims": [
        {
          "component": "editor.gesture",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:10:20.563Z",
        "headline": "All linked evidence is passing (3 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 3,
          "passed": 3,
          "skipped": 0
        }
      },
      "id": "editor-tools.page-tool",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Page tool (select, Alt-insert, drag-resize, delete)"
    },
    {
      "chapter": "editor-tools",
      "claims": [
        {
          "component": "core.canvas-wasm",
          "note": "pathEdit gesture (ONE address per gesture — a single anchor or handle; exact for un-rotated polygons) + path topology ops",
          "status": "shipped"
        },
        {
          "component": "editor.gesture",
          "note": "Updated 2026-10-05 (was partial). SHIPPED on editor main (701f6ad): dragging anchors, handles and segments; multi-anchor selection (selectedAnchors) and the anchor marquee; arrow-key nudge (1 pt, Shift ×10); Delete on the selected anchors; Escape cancels mid-drag; path-edit mode (Enter / Esc), segment-click insert, double-click convert, Scissors at anchors as before. The behaviour is paged.draw's DirectSelectMachine (plugin-draw.direct-selection); the editor's DirectSelectSession is the shim, each committed edit one batch and one undo step. Direct Selection enters path-edit mode for a single path-bearing selection with no Enter. Frame handles are hidden in the mode. e2e/path-editing.spec.ts (AC-PATHEDIT-1…13, AC-DIRECT-1/2) green on editor main 2026-10-03.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-05T09:37:15Z",
        "evidence_at": "2026-10-18T05:10:15.705Z",
        "headline": "All linked evidence is passing (28 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 28,
          "passed": 28,
          "skipped": 0
        }
      },
      "id": "editor-tools.path.direct-edit",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Direct path editing (anchors, handles, scissors)"
    },
    {
      "chapter": "editor-tools",
      "claims": [
        {
          "component": "core.canvas-wasm",
          "status": "shipped"
        },
        {
          "component": "editor.gesture",
          "note": "overlay handles + worker gesture",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:04:23.582Z",
        "headline": "All linked evidence is passing (17 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 17,
          "passed": 17,
          "skipped": 0
        }
      },
      "id": "editor-tools.resize.handles",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Resize via 8 handles (Shift aspect, Alt from-centre)"
    },
    {
      "chapter": "editor-tools",
      "claims": [
        {
          "component": "core.canvas-wasm",
          "status": "shipped"
        },
        {
          "component": "editor.gesture",
          "note": "Since 2026-10-05 (editor 8667db0, on main at 445788c; core C-67 in v0.66.0) a click sets the pivot the gesture turns about.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:08:47.516Z",
        "headline": "All linked evidence is passing (10 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 11,
          "passed": 10,
          "skipped": 1
        }
      },
      "id": "editor-tools.rotate",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Rotate (Shift snaps to 15°)"
    },
    {
      "chapter": "editor-tools",
      "claims": [
        {
          "component": "core.canvas-wasm",
          "status": "shipped"
        },
        {
          "component": "editor.gesture",
          "note": "Since 2026-10-05 (editor 8667db0, on main at 445788c; core C-67 in v0.66.0) a click sets the pivot the gesture turns about.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:04:29.539Z",
        "headline": "All linked evidence is passing (19 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 19,
          "passed": 19,
          "skipped": 0
        }
      },
      "id": "editor-tools.scale",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Matrix scale (Cmd-handle, multi-select)"
    },
    {
      "chapter": "editor-tools",
      "claims": [
        {
          "component": "core.canvas-wasm",
          "note": "hitTest + marqueeHits",
          "status": "shipped"
        },
        {
          "component": "editor.gesture",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:01:08.886Z",
        "headline": "All linked evidence is passing (14 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 14,
          "passed": 14,
          "skipped": 0
        }
      },
      "id": "editor-tools.select.click-marquee",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Click select + marquee (intersect, Shift add, Cmd toggle)"
    },
    {
      "chapter": "editor-tools",
      "claims": [
        {
          "component": "editor.gesture",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:07:25.373Z",
        "headline": "All linked evidence is passing (10 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 10,
          "passed": 10,
          "skipped": 0
        }
      },
      "id": "editor-tools.select.group-descent",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Group descent (double-click enter, Escape exit)"
    },
    {
      "chapter": "editor-tools",
      "claims": [
        {
          "component": "core.canvas-wasm",
          "status": "shipped"
        },
        {
          "component": "editor.gesture",
          "note": "shear tool; Since 2026-10-05 (editor 8667db0, on main at 445788c; core C-67 in v0.66.0) a click sets the pivot the gesture turns about.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:00:54.957Z",
        "headline": "All linked evidence is passing (9 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 9,
          "passed": 9,
          "skipped": 0
        }
      },
      "id": "editor-tools.shear",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Shear (Shift snaps shear angle to 15°)"
    },
    {
      "chapter": "editor-tools",
      "claims": [
        {
          "component": "editor.gesture",
          "note": "the five that remain are HONESTLY stubbed rather than dead: ToolStatus 'planned' renders them dimmed with data-tool-status='planned' and aria-disabled, refuses activation, and contributes no command and no keybinding. Two are editor-side work (gap, freeTransform); three name real engine absences — content collector/placer have no element-duplicate op on the wire at all, and note has no Note in the engine model",
          "status": "planned"
        }
      ],
      "health": {
        "changed_at": "2026-09-09T14:13:29Z",
        "evidence_at": null,
        "headline": "No evidence linked yet; implementation last changed 6 w ago",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "editor-tools.stub-tools",
      "importance": "normal",
      "since": null,
      "status": "planned",
      "title": "Registered stub tools (gap, free-transform, content collector/placer, note)"
    },
    {
      "chapter": "editor-tools",
      "claims": [
        {
          "component": "editor.gesture",
          "note": "W2.11 — click caret + typing + caret Up/Down/Home/End + Shift-extend (caret-nav e2e green); double/triple-click escalates to LINE granularity (word-bounds wire still missing — CARET-NAV-6/7)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:09:58.745Z",
        "headline": "All linked evidence is passing (18 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 18,
          "passed": 18,
          "skipped": 0
        }
      },
      "id": "editor-tools.text.caret-typing",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Text tool caret + typing"
    },
    {
      "chapter": "editor-tools",
      "claims": [
        {
          "component": "editor.gesture",
          "note": "gated behind caret/selection model (deep-defer-records.md §editorial-text)",
          "status": "planned"
        }
      ],
      "health": {
        "changed_at": "2026-10-12T18:25:54Z",
        "evidence_at": null,
        "headline": "No evidence linked yet; implementation last changed 6 d ago",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "editor-tools.text.drag-drop",
      "importance": "normal",
      "since": null,
      "status": "planned",
      "title": "Drag-and-drop text"
    },
    {
      "chapter": "editor-tools",
      "claims": [
        {
          "component": "editor.canvas",
          "note": "SHIPPED 2026-08-23. Draws a story's ROUTE across the spread: a dashed hop from each frame's out corner to the next frame's in corner — the same two corners the ports occupy — and a red stub where an overset story continues into a frame the document does not have, because drawing nothing there would read as `the story ends here`, the opposite of the truth. THE GAP IT CLOSES: ports render only on the SELECTED, page-owned frame, so a three-frame story across two spreads had no representation anywhere in the product — the Stories panel counts frames but cannot show WHERE they are. WHY IT WAS DEFERRED AND WHAT UNBLOCKED IT, because the reason was wrong in an instructive way: the deferral said the overlay layer had no geometry for non-selected frames, true of the door it was looking at (`frameChain` answers {frameId, next, overflow} — topology, no bounds). The bounds were behind a DIFFERENT door the whole time: `elementGeometry(ids)` takes an arbitrary id list and answers bounds + itemTransform + pageId. Nothing had to ship engine-side. `The data isn't there` and `I asked the wrong door` look identical from the call site. Off by default and issues ZERO wire traffic while off. Reuses the existing violet/red overlay tokens rather than minting a fifth — the brand repo owns that palette.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-09-09T14:20:18Z",
        "evidence_at": "2026-10-18T05:00:18.876Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "editor-tools.text.thread-lines",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Text thread overlay (View ▸ Show text threads)"
    },
    {
      "chapter": "editor-tools",
      "claims": [
        {
          "component": "editor.gesture",
          "note": "W2.9 ports live (TH-01..03: out-port loaded cursor, link/unlink, insertTextFrame+linkFrames on empty canvas) — gesture-threading e2e green; chain-state mirrored in session (W3.A2: switch to next/previousTextFrame truth). TH-04 IS DONE and this row said otherwise until 2026-08-23. The note read `TH-04 overset badge fixme (needs overset fixture)` while the badge was live and painting: the port renders a red overset marker from `threading.isOverset`, fed by StorySummary.overset off the `paged.stories()` surface — engine truth, not an optimistic mirror. A registry row claiming a shipped feature is missing is the same lie as one claiming an unbuilt feature works, pointing the other way, and harder to notice because it looks conservative.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:03:04.206Z",
        "headline": "All linked evidence is passing (9 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 10,
          "passed": 9,
          "skipped": 1
        }
      },
      "id": "editor-tools.text.threading-ports",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Text threading via ports"
    },
    {
      "chapter": "effects-transparency",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "SHIPPED — all four named fields are now honoured. paged-renderer module/effects.rs:320 describes \"the style/direction/technique knobs the rasterizer now honours\"; soften is read at :355 and budgeted by paged-compose extent.rs:81. The row enumerated four specific sub-gaps and every one closed, but the status never moved. Was: Lambertian height-field rasterisation; ignores style/direction/technique/soften (W1.8)",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "13 per-field paths (W0.4)",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "status": "planned"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:06.24Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "effects-transparency.bevel-emboss",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Bevel & emboss"
    },
    {
      "chapter": "effects-transparency",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "FrameBlendMode path (W0.4)",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "blend select seamed (gap 18)",
          "status": "planned"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:06.262Z",
        "headline": "All linked evidence is passing (4 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 4,
          "passed": 4,
          "skipped": 0
        }
      },
      "id": "effects-transparency.blend-modes",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Blend modes (26 modes incl. multiply/screen/overlay)"
    },
    {
      "chapter": "effects-transparency",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "frameDropShadow* property family",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "Effects panel pill + expanded fields",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:05.399Z",
        "headline": "All linked evidence is passing (11 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 11,
          "passed": 11,
          "skipped": 0
        }
      },
      "id": "effects-transparency.drop-shadow",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Drop shadow (soft blur, offset, color, opacity)"
    },
    {
      "chapter": "effects-transparency",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "Feather (5) + DirectionalFeather (8) per-field paths (W0.4)",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "SHIPPED — corrected by the 2026-08-09 audit. The Effects panel binds it live: apps/canvas/src/panels/effects-panel.tsx:96, gated on `frameFeatherEnabled`, with the panel's own header comment (:27-28) already reading \"LIVE — W2.2\". The row simply never moved.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:06.255Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "effects-transparency.feather",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Feather (basic + directional)"
    },
    {
      "chapter": "effects-transparency",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "OuterGlow (7) + InnerGlow (8) per-field paths (W0.4)",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "SHIPPED — corrected by the 2026-08-09 audit. The Effects panel binds it live: apps/canvas/src/panels/effects-panel.tsx:92-93, gated on `frameOuterGlowEnabled` / `frameInnerGlowEnabled`, with the panel's own header comment (:27-28) already reading \"LIVE — W2.2\". The row simply never moved.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:06.29Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "effects-transparency.glows",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Outer / inner glow"
    },
    {
      "chapter": "effects-transparency",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "frameGradientFeather whole-struct",
          "status": "shipped"
        },
        {
          "component": "editor.gesture",
          "note": "gradient-feather tool drag",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-08-19T07:39:14Z",
        "evidence_at": "2026-10-18T07:52:05.477Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "effects-transparency.gradient-feather",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Gradient feather"
    },
    {
      "chapter": "effects-transparency",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "9 per-field paths; enabled = struct presence, defaults materialize on first write (W0.4)",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "SHIPPED — corrected by the 2026-08-09 audit. The Effects panel binds it live: apps/canvas/src/panels/effects-panel.tsx:91, gated on `frameInnerShadowEnabled`, with the panel's own header comment (:27-28) already reading \"LIVE — W2.2\". The row simply never moved. Was: seam pill",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:06.283Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "effects-transparency.inner-shadow",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Inner shadow"
    },
    {
      "chapter": "effects-transparency",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "frameOpacity",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "Effects + Object panels",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:11.61Z",
        "headline": "All linked evidence is passing (9 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 9,
          "passed": 9,
          "skipped": 0
        }
      },
      "id": "effects-transparency.opacity",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Object opacity"
    },
    {
      "chapter": "effects-transparency",
      "claims": [
        {
          "component": "core.renderer",
          "note": "a real per-pixel soft mask, not another hard clip. THREE display-list markers (BeginSoftMask{mask_type,invert,transform} / BeginMaskedContent / EndSoftMask) rather than a soft_masks pool referenced by a 2-marker pair, so the mask ARTWORK stays inline in the same flat command stream and every pass that walks the list by command RANGE keeps working untouched (vertical justification, frame_cmd_ranges, the master-text delta cache + rebase_path_ids, the PDF walker, the scene-layer splicer). It also maps 1:1 onto PDF. CPU rasterizer (the correctness gate): the artwork is captured into a target-sized offscreen buffer, resolved to a tiny_skia::Mask (Luminance or Alpha, optionally inverted) and multiplied into the parent clip entry, so coverage composes with clips and group scoping for free. PDF export: lowered onto the existing PendingFormGroup::LuminosityGray path plus a new AlphaGroup for /S /Alpha, with invert riding /TR as a Type-2 1-x. VELLO — SHIPPED, AND THE RECORDED BLOCKER WAS FALSE (core d7a8611): the note said push_layer takes a shape, not a coverage buffer, so the artwork range was SKIPPED and a designer who applied a mask saw NOTHING CHANGE and got a correct PDF, which reads as broken rather than limited. The pinned vello v0.9.0 already had Scene::push_luminance_mask_layer, and alpha masks need no mask layer at all (Compose::DestIn IS dst · src.a); both inverted forms fall out (DestOut for alpha; for luminosity the artwork over opaque white under Mix::Difference resolves to 1 - a·lum(c), exactly Mask::invert). No dependency change. Vello masks content drawn BEFORE the layer — the reverse of the display list's artwork-then-content order — so the artwork is captured into a sub-scene and replayed after the content. PARITY AGAINST THE CPU RASTERIZER, THE PATH OF RECORD — and the measurement and the assertion are different numbers, which the code says out loud: MEASURED byte-identical (worst channel delta 0) at 72 dpi over the whole image for all four (mask type x invert) combinations on Metal/macOS, while the CODIFIED GATE is worst <= 2, declared as 'headroom for rounding on other adapters, not observed error'. The 300 dpi companion is deliberately NOT whole-image: it excludes a +/-3 px band around every step edge, because the two back ends already differ there with no mask present. A second-order divergence was closed with it — Vello's mask reads the enclosing layer, so the masked content must be isolated, and a blend group inside that isolation composited against an EMPTY BACKDROP instead of the page (a Multiply frame with a mask came out green where the CPU gives black); hoistable_blend_group folds that blend onto the content layer for the shape the emitter produces. paged-gpu --features vello-backend went 60 -> 68 tests",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "ApplyOpacityMask / ReleaseOpacityMask, modelled on the B-18 PasteInto/ReleaseFrom pair — the artwork LEAVES frames_in_order and joins Spread::opacity_masks; exact inverses, one-step undo. RemoveNode refuses to delete a live mask item or a masked target. Because mask artwork is painted from no sibling list, the v59 reorder op rejects it BY NAME rather than silently no-op'ing",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "note": "applyOpacityMask / releaseOpacityMask on the wire at PROTOCOL_VERSION 58 (C-28, landed with C-29 type-on-a-path in one bump because both are new operations arriving together; the pair shipped as C-23/C-24 and was RENUMBERED to C-28/C-29 a commit later on an id collision — the ids are load-bearing because they prefix operator-facing refusal messages such as 'C-28: an item cannot mask itself', and note that plugin-publish's own 'C-23' refers to a different gap). 29 tests across the two doors: 6 CPU-rasterizer pixel probes, 4 renderer end-to-end, 4 PDF re-parse, 7 mutate apply/inverse/round-trip, 5 wire end-to-end, 2 wire-shape. PUBLISH STATE CORRECTED 2026-08-09 by audit: `npm view @paged-media/canvas-wasm dist-tags` returns latest 0.61.1, so every protocol up to 61 IS published and reachable without sync-wasm.sh. Was: PUBLISH STATE: PUBLISHED SINCE (canvas-wasm latest 0.61.1, verified 2026-08-09). Was: UNPUBLISHED - npm canvas-wasm was 0.51.0 and the editor reached protocol 60 only through a local sync-wasm.sh file: override, so the editor's live-probed capability table (94 ops, the registry's stated source of truth for this stage) does NOT yet carry these ops. The claim here rests on core's own crate reality and tests",
          "status": "shipped"
        },
        {
          "component": "plugin.publish",
          "note": "IDML HAS NO OPACITY-MASK ELEMENT, so a mask is a paged-NATIVE construct. .paged keeps it verbatim (the N2 model part serialises the side map); .idml cannot, AND THE LOSS IS LOUD — CanvasModel::idml_export_losses NAMES every masked item and the IdmlExported reply carries it as `lost`. Smuggling the relation through Properties/Label was considered and REJECTED: it round-trips on paper but makes InDesign draw the mask artwork as an opaque object",
          "status": "partial"
        },
        {
          "component": "plugin.draw",
          "note": "the consumer — see plugin-draw.opacity-mask-commands",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:47:24.255Z",
        "headline": "All linked evidence is passing (6 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 6,
          "passed": 6,
          "skipped": 0
        }
      },
      "id": "effects-transparency.opacity-mask",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Opacity masks — a real per-pixel soft mask (luminosity or alpha, invertible)"
    },
    {
      "chapter": "effects-transparency",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "invert honoured on all three lanes by the shared satin_stamps (paged-compose mask.rs:784; CPU, Vello, PDF since 72cd7b37)",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "8 per-field paths incl. invert bool (W0.4)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:06.297Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "effects-transparency.satin",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Satin"
    },
    {
      "chapter": "effects-transparency",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "nested blend groups buffer and composite on CPU and Vello; clip parity fixed (7751cda: the clip is the group rect on both back ends) and a group's bounds now hold its own drop shadow (paged-renderer tests/blend_group_bounds.rs). OPEN: IsolateBlending and KnockoutGroup are not modelled (byte-preserved by the IDML adapter, rewritten false on mutation): plan R-10/A-18",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:57:47.448Z",
        "headline": "All linked evidence is passing (7 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 7,
          "passed": 7,
          "skipped": 0
        }
      },
      "id": "effects-transparency.transparency-groups",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Transparency groups (nested blend group buffering)"
    },
    {
      "chapter": "foundations",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "note": "loadDocument",
          "status": "shipped"
        },
        {
          "component": "core.sdk",
          "note": "ViewerSession.load",
          "status": "shipped"
        },
        {
          "component": "editor.canvas",
          "note": "file input + worker load path",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:58:59.916Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "foundations.container.open",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "IDML container open (ZIP + mimetype validation)"
    },
    {
      "chapter": "foundations",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:01:17.313Z",
        "headline": "All linked evidence is passing (3 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 3,
          "passed": 3,
          "skipped": 0
        }
      },
      "id": "foundations.designmap.structure",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "designmap parse (spreads, stories, resources wiring)"
    },
    {
      "chapter": "foundations",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:01:16.785Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "foundations.domversion-capture",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "DOMVersion capture (version-agnostic parser)"
    },
    {
      "chapter": "frames-paths",
      "claims": [
        {
          "component": "plugin.draw",
          "note": "NOT a multi-paint frame model: core is an IDML engine and IDML carries one FillColor/StrokeColor per page item, so multi-paint could not round-trip. The stack lowers to a GROUP — carrier frame keeps identity + the editable metadata, N derived paths carry one paint each, back-to-front. Two batches (a batch cannot address an id minted in the same batch); groups cannot hold plugin metadata (setPluginMetadata on a group id answers notImplemented), hence the carrier",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "C-20 gave Polygon+Oval their FrameFillTint/FrameBlendMode arms so per-layer modifiers carry; GraphicLine still refuses with evidence (no such field in the model)",
          "status": "shipped"
        },
        {
          "component": "plugin.publish",
          "note": "C-19: the IDML writer emits scene-created groups (both emitters previously dropped FrameRef::Group on the floor), members re-base like B-18's nested content, unmutated documents stay byte-identical; inserted items emit in z-table order (was reversed) and carry FillTint/opacity/blend/Label. C-22 CLOSED 2026-08-05 — an inserted item used to emit at the SPREAD'S CLOSE, so a baked group reopened ABOVE the source page items, and a draw test pinned the exact wrong order. The z-order save-back work (plugin-publish.save-back-defects, crates/idml-export/src/reorder.rs across all three sibling lists) put items back at their real z-slot and the draw pin was FLIPPED to assert the right one",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:17.637Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "frames-paths.appearance-bake",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Appearance stack bakes to a group of stacked page items (B-24)"
    },
    {
      "chapter": "frames-paths",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "calibration pending",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-11T17:03:11Z",
        "evidence_at": "2026-10-18T07:57:19.767Z",
        "headline": "All linked evidence is passing (4 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 4,
          "passed": 4,
          "skipped": 0
        }
      },
      "id": "frames-paths.arrowheads",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "GraphicLine arrowheads (triangle/circle/bar)"
    },
    {
      "chapter": "frames-paths",
      "claims": [
        {
          "component": "core.parser",
          "note": "Read as InDesign reads them (core ADR 149, asked of InDesign 20.0.1 through its DOM): corners come from the four per-corner attributes only; the item-level CornerOption / CornerRadius pair is not read, an unset per-corner radius is 12 pt, and a bare Rounded / Inset / Beveled / Fancy or BeveledCorner is no option. An object style's corners are read the same way (ADR 149 amendment, object-style-corners-probe): its four per-corner attributes, each cascading through BasedOn, fill a rectangle that states no corners; a style with only the pair draws square. plugin-publish reads them and writes them back, an edited source object style included.",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "All five corner options emit geometry; Fancy = 3-arc scallop, Inset = fold-in notch; per-corner radii + join/miter/cap honoured. corners p4 (inset) 0.827 -> 0.300 once the sample spelt corners as InDesign does (ADR 149); the worst page is now Fancy (0.665), an approximated shape.",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "8 per-corner option + radius paths (W0.3); objectStyleCornerOption / objectStyleCornerRadius set a style's pair and all four corners (ADR 149 amendment).",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-14T06:31:27Z",
        "evidence_at": "2026-10-18T07:58:41.798Z",
        "headline": "All linked evidence is passing (5 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 5,
          "passed": 5,
          "skipped": 0
        }
      },
      "id": "frames-paths.corner-options",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Decorative corner options (Rounded/Inverse/Bevel/Inset/Fancy)"
    },
    {
      "chapter": "frames-paths",
      "claims": [
        {
          "component": "core.mutation",
          "note": "frameCornerOption*/frameCornerRadius* apply arms extended to NodeId::Polygon via find_corners_mut (the FrameStrokeJoin idiom); prior-value capture and bytewise inverse unchanged",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "generalized, not special-cased: push_corner_segments is now the single definition of a corner's shape, and corner_polygon_path frames each corner by true inscribed-circle construction (d = r/tan(θ/2)), which at θ=90° reduces exactly to the rect math — pinned by a quad≡rect equivalence test so no rectangle fidelity moved. All five options render. PARTIAL: only the TopLeft slot (or the global pair) drives polygon geometry — the four names are rectangle-shaped ADDRESSING with no meaning for N≠4, and a 61-document corpus census found nothing relying on one. Open contours, <3-anchor contours and smooth spline junctions keep their raw outline",
          "status": "partial"
        },
        {
          "component": "core.parser",
          "note": "corpus census (61 real Adobe IDMLs) settled the semantics: the corner vocabulary appears on EVERY page-item kind (CornerRadius — Rectangle 301, Polygon 228, Group 28, GraphicLine 21, Oval 16, TextFrame 16), and Rectangle's PathGeometry stays a plain 4-point path under RoundedCorner, proving the effect is parametric. BUG FOUND: CornerOption::from_idml did not know IDML's actual BevelCorner spelling — 14 corpus rectangles were silently squaring. Fixed + tested",
          "status": "shipped"
        },
        {
          "component": "plugin.publish",
          "note": "idml-import reads polygon corner attrs; idml-export gained a corner write path — it had NONE before, so corner mutations were previously unsaveable for rectangles too. Byte-preserving patching keeps an unmutated CornerRadius='44.51279527491718' from coming back '44.5128' and BevelCorner from becoming BeveledCorner",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:58:20.87Z",
        "headline": "All linked evidence is passing (5 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 5,
          "passed": 5,
          "skipped": 0
        }
      },
      "id": "frames-paths.corner-options-polygon",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Corner options on polygons (B-23) — apply arms, renderer, IDML write path"
    },
    {
      "chapter": "frames-paths",
      "claims": [
        {
          "component": "core.mutation",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "note": "deleteFrame",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:05.873Z",
        "headline": "All linked evidence is passing (34 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 34,
          "passed": 34,
          "skipped": 0
        }
      },
      "id": "frames-paths.frame.delete",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Delete frame"
    },
    {
      "chapter": "frames-paths",
      "claims": [
        {
          "component": "core.mutation",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "note": "insertFrame (capability-probed supported)",
          "status": "shipped"
        },
        {
          "component": "editor.gesture",
          "note": "rectangle tool drag",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-09-09T14:20:18Z",
        "evidence_at": "2026-10-18T07:47:09.242Z",
        "headline": "All linked evidence is passing (25 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 25,
          "passed": 25,
          "skipped": 0
        }
      },
      "id": "frames-paths.frame.insert",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Insert frame (rectangle) by bounds"
    },
    {
      "chapter": "frames-paths",
      "claims": [
        {
          "component": "core.canvas-wasm",
          "note": "wire {op:'moveFrame', args:{frameId, transform:[a,b,c,d,tx,ty]}} bridges to Operation::SetProperty{FrameTransform} (model.rs build_op_from_mutation, W1.2) — the same matrix the translate gesture lands, so the Phase D apply arm's prior-transform capture gives it an inverse with no new machinery. Display geometry only: asserted NOT to re-paginate (paged-canvas model.rs tests)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:46:19.395Z",
        "headline": "All linked evidence is passing (13 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 13,
          "passed": 13,
          "skipped": 0
        }
      },
      "id": "frames-paths.frame.move-op",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "moveFrame wire op"
    },
    {
      "chapter": "frames-paths",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "createGroup/dissolveGroup (v32) plus W1.20 groups v2, which landed the two things the v1 note listed as pending: NESTED create (member_frame_ref extends leaf_frame_ref with NodeId::Group, so a group can be a member — group-of-groups) and GROUP TRANSFORMS (dissolve walks every descendant FrameRef through nested groups and rebases each). Atomic: fully validated before any mutation. Z-order-neutral for contiguous members (INV-tested identical display-list streams); scattered members collect InDesign-style at the earliest slot, and the inverse carries the original slots so undo restores z-order EXACTLY either way. Members must share a spread — a group IS a spread page item, so that is structural, not a gap. crates/paged-mutate/tests/group_ops.rs",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "note": "CreateGroup{memberIds}/DissolveGroup{groupId} wire ops, minted id as createdId (v32)",
          "status": "shipped"
        },
        {
          "component": "editor.gesture",
          "note": "double-click descent; Escape exits; group transforms",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:12.686Z",
        "headline": "All linked evidence is passing (44 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 44,
          "passed": 44,
          "skipped": 0
        }
      },
      "id": "frames-paths.groups",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Groups (nesting + group transparency/blending + create/dissolve ops)"
    },
    {
      "chapter": "frames-paths",
      "claims": [
        {
          "component": "core.mutation",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "note": "insertLine",
          "status": "shipped"
        },
        {
          "component": "editor.gesture",
          "note": "line tool; Shift 45° constraint",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:05.505Z",
        "headline": "All linked evidence is passing (19 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 19,
          "passed": 19,
          "skipped": 0
        }
      },
      "id": "frames-paths.line.insert",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Insert line (two-anchor open GraphicLine)"
    },
    {
      "chapter": "frames-paths",
      "claims": [
        {
          "component": "core.parser",
          "note": "FINDING: skipped_nested_frames was vestigial — nested page items were being FLATTENED to top-level siblings (single-slot current_frame clobbered container state, no transform composition), which is worse than the recorded 'counted and skipped'. Children now lift into a Spread::nested_children side map keyed by host id, with transforms composed into spread space (the existing group-member convention); the counter is retained for the formats still flattened (Group children, TextFrame/GraphicLine hosts)",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "children render bracketed by PushClip(container outline)/PopClip per host page, recursive for nested containers; the clip path is built in container-inner coords WITH corner effects and rides frame_outer_transform, so rotated containers clip correctly. Nested text-frame glyphs get the same clip via StoryEmitter::apply_container_clip. Both raster backends and the PDF exporter inherit Push/PopClip",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "pasteInto{containerId,childId} / releaseFrom{childId} at protocol 56 with exact-z-slot faithful inverses; geometry is preserved in document space so nothing moves on canvas. RemoveNode of a nested child is rejected ('release first') to protect undo identity",
          "status": "shipped"
        },
        {
          "component": "plugin.publish",
          "note": "idml-import lifts nested children with composed transforms; idml-export re-bases on emit (on_disk = inverse(host composed) ∘ child composed) and keeps unmutated documents byte-identical",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:58:20.352Z",
        "headline": "All linked evidence is passing (25 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 25,
          "passed": 25,
          "skipped": 0
        }
      },
      "id": "frames-paths.nested-content",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Nested page items (paste-into) — parse, render clip, pasteInto/releaseFrom"
    },
    {
      "chapter": "frames-paths",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "non-rect PathGeometry frames lift to real Polygon outlines (W1.1)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:01:17Z",
        "headline": "All linked evidence is passing (3 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 3,
          "passed": 3,
          "skipped": 0
        }
      },
      "id": "frames-paths.page-item-kinds",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Page-item kinds (TextFrame, Rectangle, Oval, Polygon, GraphicLine, Group)"
    },
    {
      "chapter": "frames-paths",
      "claims": [
        {
          "component": "core.renderer",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:58:42.847Z",
        "headline": "All linked evidence is passing (3 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 3,
          "passed": 3,
          "skipped": 0
        }
      },
      "id": "frames-paths.path-clipping",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Path clipping/masking (non-zero winding)"
    },
    {
      "chapter": "frames-paths",
      "claims": [
        {
          "component": "core.mutation",
          "note": "coincident endpoints (a scissors-cut twin pair) merge back into ONE anchor, the head taking the tail's incoming handle — the exact inverse of pathOpenAt's split; endpoints apart simply flip subpath_open (implicit straight closing edge). Snapshot-triple inverse, so undo reopens at the same point bytewise. Rejects cleanly (no-op) on closed/degenerate/out-of-range input",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "note": "wire {op:'closePath', args:{elementId, subpath?}} at protocol 56 (PUBLISHED; canvas-wasm latest is 0.61.1 as of the 2026-08-09 audit)",
          "status": "shipped"
        },
        {
          "component": "plugin.draw",
          "note": "consumed by media.paged.draw.command.closePath",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:16.089Z",
        "headline": "All linked evidence is passing (5 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 5,
          "passed": 5,
          "skipped": 0
        }
      },
      "id": "frames-paths.path.close",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "closePath — close an open subpath (the faithful inverse of pathOpenAt)"
    },
    {
      "chapter": "frames-paths",
      "claims": [
        {
          "component": "core.mutation",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "note": "insertPath{smooth}",
          "status": "shipped"
        },
        {
          "component": "editor.gesture",
          "note": "pencil tool (RDP simplification) AND the Pen tool (paged.tool.pen, packages/tools/src/handlers/pen-tool.ts — click/drag anchors, Alt-break, Shift-constrain, close, one insertPath per path). Corrected 2026-10-02: this row said \"pen tool unbuilt\". The Pen authors NEW paths only: continuing or extending an existing path is not built",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:05.547Z",
        "headline": "All linked evidence is passing (20 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 20,
          "passed": 20,
          "skipped": 0
        }
      },
      "id": "frames-paths.path.insert",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Insert freehand path (optional smoothing)"
    },
    {
      "chapter": "frames-paths",
      "claims": [
        {
          "component": "core.mutation",
          "note": "welds the nearest endpoint pair of 4 combinations, reversing either contour with handle-swap for orientation; merges coincident weld anchors; closes into a ring when both endpoint pairs coincide; deletes the other element. Mirrors pathfinderBoolean's machinery — internal Batch[SetProperty(kept), RemoveNode(other)] so ONE undo restores both. Rejects (no-op) closed/multi-contour/primitive/degenerate/self-join. Inverse fidelity equals the existing pathfinder undo (NodeSpec capture — fields outside that spec, e.g. stroke_dash, are not restored)",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "note": "wire {op:'joinPaths', args:{elementId, otherId}} at protocol 56 (PUBLISHED; canvas-wasm latest is 0.61.1 as of the 2026-08-09 audit)",
          "status": "shipped"
        },
        {
          "component": "plugin.draw",
          "note": "the '(coincide)' fallback is retired for v56 engines",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:16.413Z",
        "headline": "All linked evidence is passing (5 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 5,
          "passed": 5,
          "skipped": 0
        }
      },
      "id": "frames-paths.path.join",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "joinPaths — weld two open paths into one element"
    },
    {
      "chapter": "frames-paths",
      "claims": [
        {
          "component": "core.mutation",
          "note": "curve-preserving CSG via flo_curves",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "the four booleans plus all six region verbs (divide, trim, merge, crop, outline, minus-back) are LIVE in the Pathfinder panel since B-22 closed (protocol 57). Corrected 2026-10-02: this row still listed minus-back and divide as seamed. Convert-shape is the one remaining seam (data-convert-shape-seam in apps/canvas/src/panels/pathfinder-panel.tsx — no convert-shape operation)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:05.843Z",
        "headline": "All linked evidence is passing (11 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 11,
          "passed": 11,
          "skipped": 0
        }
      },
      "id": "frames-paths.pathfinder-boolean",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Pathfinder boolean (union/intersect/subtract/exclude)"
    },
    {
      "chapter": "frames-paths",
      "claims": [
        {
          "component": "core.mutation",
          "note": "Illustrator semantics with the ambiguous choices pinned by tests: Trim/Merge/Crop drop strokes; Crop treats the topmost object as a cookie cutter; Merge groups by fill; Outline emits one open GraphicLine per arrangement edge; Minus Back keeps what only the backmost object covers. A face is owned by the topmost input covering it and is written ONTO that element, so fill/stroke/transform/effects/metadata ride along — only surplus faces become fresh Polygons, which carry only what NodeSpec::Polygon carries",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "the Pathfinder panel's Divide + Minus-back seams are retired; z-order derived from the scene tree (a spec selects BOTTOM-UP and asserts the back quad survives Minus back, which is what proves the derivation); refusals render the engine's own sentence",
          "status": "shipped"
        },
        {
          "component": "plugin.draw",
          "note": "six bundle commands + region-level Shape Builder (hover resolves the face under the cursor, drag collects in first-cross order, release commits keep / Alt commits remove). Arrangement read ONCE per gesture, later hovers are local point-in-path tests",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:18.215Z",
        "headline": "All linked evidence is passing (13 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 13,
          "passed": 13,
          "skipped": 0
        }
      },
      "id": "frames-paths.pathfinder-verbs",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Divide / Trim / Merge / Crop / Outline / Minus Back"
    },
    {
      "chapter": "frames-paths",
      "claims": [
        {
          "component": "core.mutation",
          "note": "flo_curves 0.8's GraphPath exposes no cyclic-by-angle edge order at a vertex (GraphEdgeRef fields are pub(crate), no twin/next accessor), so a DCEL face-walk is NOT buildable on it; signature enumeration is exact but 2^N boolean chains. Shipped design rides set_edge_kinds_by_ray_casting, whose predicate receives PER-INPUT crossing counts: build the arrangement once, extract each face in one reset/ray-cast/heal/exterior_paths pass. Two hardening measures came from the property sweep, not from reasoning — a boolean-chain fallback on unhealed gaps, and per-component signature validation for when gap-healing bridges into a neighbour. Caps REFUSE (12 inputs / 256 faces) and the arrangement reports whether its faces tile the union. Since v0.71.8 an oval enters the arrangement (and pathfinderBoolean) as its four-arc ellipse, not its bounding box (paged-canvas tests/oval_path_geometry.rs)",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "note": "requestPlanarRegions read door (optional point = the hover query) at protocol 57 (PUBLISHED; canvas-wasm latest is 0.61.1 as of the 2026-08-09 audit)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:18.79Z",
        "headline": "All linked evidence is passing (28 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 28,
          "passed": 28,
          "skipped": 0
        }
      },
      "id": "frames-paths.planar-arrangement",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Planar arrangement — faces of overlapping paths (B-22)"
    },
    {
      "chapter": "frames-paths",
      "claims": [
        {
          "component": "core.mutation",
          "note": "InsertOval (NodeSpec::Oval, fresh u<hex> ids) (W0.5); polygons via insertPath",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "note": "insertOval on the wire (W0.5)",
          "status": "shipped"
        },
        {
          "component": "editor.gesture",
          "note": "W2.6 Ellipse (insertOval) + Polygon (insertPath N-gon/star via toolSettings) draw gestures — gesture-ellipse / gesture-polygon e2e green; DR-02/03 modifiers in shared drawBoundsFor",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:06.36Z",
        "headline": "All linked evidence is passing (13 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 13,
          "passed": 13,
          "skipped": 0
        }
      },
      "id": "frames-paths.shape-tools",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Ellipse / polygon shape tools"
    },
    {
      "chapter": "frames-paths",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "inside/outside alignment on rectangles, and since C-24 on polygons, ovals and text frames: closed contours are offset by half the weight (shapes.rs aligned_outline_path); open paths stay centred, as InDesign draws them. OPEN: styled strokes (striped, wavy, gap) keep the centred outline and curves are offset as flattened polylines: plan R-14",
          "status": "partial"
        },
        {
          "component": "core.mutation",
          "note": "alignment path, paint-only (W0.3)",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "stroke panel Align segments Center/Inside/Outside LIVE (W2.2, stroke.composition.ts) but Rectangle-only — the wire's apply arm + read-side expose alignment on Rectangle only, other kinds em-dash; journey stroke-depth",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:58:37.921Z",
        "headline": "All linked evidence is passing (8 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 8,
          "passed": 8,
          "skipped": 0
        }
      },
      "id": "frames-paths.stroke-alignment",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Stroke alignment (center/inside/outside)"
    },
    {
      "chapter": "frames-paths",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "Laid out side by side as InDesign lays them out (core ADR 148): whole periods per side, half-down rounding, corner pieces, open paths ending on whole dashes; InDesign's built-in patterns (Dashed 12/12 pt, Canned Dashed 3x2 / 4x4, Canned Dotted 0/3w, Japanese Dots 0/2w). Measured on stroke-styles and strokes-fills (p11-p14 ink 46-52 -> 0.6-2.0). Hashes and white diamonds are marks along the whole contour (core ADR 150): stroke-styles ink 29.93 -> 7.17.",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "frameStrokeDashArray (Value lengths[]) on the published wire (protocol 35); verified applied:true against real wasm by plugin-draw dash-commands.spec",
          "status": "shipped"
        },
        {
          "component": "plugin.draw",
          "note": "B-12 — dash editing as command presets strokeDash{Solid,Dashed,Dotted,DashDot} committing frameStrokeDashArray to the selection (draw-bundle src/commands/dash.ts); inline per-segment array editing awaits a schema array binding (B-01 ceiling)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-11T17:03:11Z",
        "evidence_at": "2026-10-18T07:51:36.746Z",
        "headline": "All linked evidence is passing (6 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 6,
          "passed": 6,
          "skipped": 0
        }
      },
      "id": "frames-paths.stroke-dashed",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Dashed + dotted strokes (dash arrays, 12 dotted variants)"
    },
    {
      "chapter": "frames-paths",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "gap-color under-stroke beneath dashed/dotted/striped (W1.2)",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "gap color + gap tint paths, paint-only (W0.3)",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "stroke panel Gap swatch + tint metric LIVE on the frameStrokeGap* paths (W2.2, stroke.composition.ts); journey stroke-depth",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:58:37.741Z",
        "headline": "All linked evidence is passing (3 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 3,
          "passed": 3,
          "skipped": 0
        }
      },
      "id": "frames-paths.stroke-gap-color",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Stroke gap color/tint"
    },
    {
      "chapter": "frames-paths",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "plane-aware on CPU",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "overprint fill + stroke paths (W0.3)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-06-19T05:18:46Z",
        "evidence_at": "2026-10-18T07:52:06.183Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "frames-paths.stroke-overprint",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Stroke overprint"
    },
    {
      "chapter": "frames-paths",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "Striped = N parallel offset rules; canned Wavy measured on InDesign 20.0.1 (core PR #85): pen weight/4, amplitude (weight - pen)/2, period 4 x weight, on the outline's (dy, -dx) side. Hash and diamond styles are not modelled (draw solid).",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "stroke type path, paint-only (W0.3)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:58:37.927Z",
        "headline": "All linked evidence is passing (5 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 5,
          "passed": 5,
          "skipped": 0
        }
      },
      "id": "frames-paths.stroke-striped-wavy",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Striped / wavy strokes"
    },
    {
      "chapter": "frames-paths",
      "claims": [
        {
          "component": "core.mutation",
          "note": "B-08 — OutlineStrokeVariable op + kurbo_kernel::variable_width_outline_stroke (arc-length-interpolated half-width → closed filled contour), snapshot inverse, kernel+apply round-trip tests; protocol v36, published 0.36.0; reachable over the wire via SetElementProperty",
          "status": "shipped"
        },
        {
          "component": "editor.gesture",
          "note": "B-08 — the Pencil (packages/tools/src/handlers/pencil-tool.ts) captures per-sample pressure; a PEN stroke commits insertPath + OutlineStrokeVariable in one undo step (batch + $created), mouse keeps the plain stroke. Wired + typecheck/build green at protocol 36; Playwright e2e for the variable-width path not yet added",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": "2026-08-19T07:39:14Z",
        "evidence_at": "2026-10-18T07:52:17.415Z",
        "headline": "All linked evidence is passing (4 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 4,
          "passed": 4,
          "skipped": 0
        }
      },
      "id": "frames-paths.stroke-variable-width",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Variable-width (tapered / pressure) strokes — per-anchor width outline"
    },
    {
      "chapter": "frames-paths",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "frameStrokeWeight/frameStrokeEndCap + join/miter paths (W0.3). KIND COVERAGE, verified 2026-10-02 against paged-mutate apply/set_property.rs + helpers.rs: END CAP is Rectangle-only (the only kind with an end_cap field; the renderer passes it from rect alone); ALIGNMENT writes Rectangle-only (RFI C-24); JOIN and MITER cover Rectangle, Polygon and GraphicLine; line ends (arrowheads) are GraphicLine-only. A pen or pencil path is a Polygon, so it has no cap and no arrowheads",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "stroke panel; weight/color/cap LIVE since W2.1 and the W2.2 stroke-detail pass flipped Type/Join/Miter/Align seam→LIVE too. Corrected 2026-10-02 — the previous '(join/miter/align Rectangle-only per the wire)' was stale for join and miter (three kinds since v35) and right for align; cap is Rectangle-only too. The dashes-and-arrows disclosure remains the only seam (stroke.composition.ts), but its stated reason is stale as well - frameStrokeDashArray and the arrowhead paths ARE on the wire now, the panel has simply not consumed them",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-06-18T11:50:06Z",
        "evidence_at": "2026-10-18T07:52:06.198Z",
        "headline": "All linked evidence is passing (4 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 4,
          "passed": 4,
          "skipped": 0
        }
      },
      "id": "frames-paths.stroke-weight-caps-joins",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Stroke weight + line caps + joins"
    },
    {
      "chapter": "frames-paths",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "emit_text_path_into (pipeline/text_path.rs) shapes the host story's runs, walks the tessellated outline by cumulative arc length and emits each glyph with a tangent-rotated transform. Honours PathTypeAlignment (Baseline / Center / Ascender / Descender), FlipPathEffect=Flipped (the polyline is reversed and cumulative distances recomputed), and the StartBracket / EndBracket arc-length window — brackets are CLAMPED to the tessellated length, the run is centred in the window when it fits, and glyphs carried past EndBracket are dropped with one OversetTextDropped diagnostic (the body-text overset contract). LIMIT: only RainbowPathEffect (the IDML default) actually renders — SkewPathEffect / Path3DRibbonEffect / StairStepPathEffect / GravityPathEffect parse but draw AS Rainbow; horizontal distribution beyond centred and side/flip options beyond Flipped are future work. 4 tests, crates/paged-renderer/tests/text_on_path.rs",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "C-29 (protocol 58) — Operation::AttachTextToPath{host, story_id, TextPathSpec} / DetachTextFromPath. The renderer had drawn <TextPath> for a long time; nothing could CREATE one, and these two ops close exactly that gap (no renderer change). TextPathSpec carries only the knobs the renderer HONOURS (path_type_alignment, flip_path_effect, start_bracket, end_bracket) — PathEffect is deliberately NOT exposed because only Rainbow renders. Attach rejects, with no mutation, an unknown story, a story already flowing into a frame or onto another path, and a host kind the renderer's text-path pass never walks (an Oval); detach LEAVES THE STORY ALONE and its inverse re-attaches with every knob intact. 3 tests, crates/paged-mutate/tests/kernel_ops.rs",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "note": "wire {op:'attachTextToPath', args:{elementId, storyId, pathTypeAlignment?, flipPathEffect?, startBracket?, endBracket?}} and {op:'detachTextFromPath', args:{elementId}} at protocol 58. 2 tests, crates/paged-canvas/tests/opacity_mask_and_text_path_wire.rs — one a wire round-trip, one asserting the attached story actually renders along the path. PUBLISH STATE CORRECTED 2026-08-09 by audit: `npm view @paged-media/canvas-wasm dist-tags` returns latest 0.61.1, so every protocol up to 61 IS published and reachable without sync-wasm.sh. Was: PUBLISH STATE: UNPUBLISHED - npm canvas-wasm is 0.51.0 and the editor reaches protocol 58/59/60 only through a local sync-wasm.sh file: override, so the editor's live-probed capability table does not yet carry these two ops",
          "status": "shipped"
        },
        {
          "component": "plugin.draw",
          "note": "the consumer landed 2026-08-05: 2 commands (media.paged.draw.command.attachTextToPath / detachTextFromPath) + the media.paged.draw.tool.typeOnPath tool, over the bundle's shared v58-wire seam, with 28 conformance specs. The editor's own paged.tool.typePath stub was RETIRED in the same wave — it had been shadowing this tool's shift+t and forcing it onto shift+h (editor-shell.tool-rail). A TextFrame host is refused with a reason, because its glyphs come from the story pass",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:42:11.607Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "frames-paths.text-on-path",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Text on a path"
    },
    {
      "chapter": "geometry-coordinates",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "non-rect PathGeometry lifts to real Polygon outlines (W1.1); text LAYOUT conforms to shaped frames — oval/triangle/donut band intersection (W1.10 frame_shape.rs, tests/text_in_shape.rs)",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "framePathPoint / framePath wholesale",
          "status": "shipped"
        },
        {
          "component": "editor.gesture",
          "note": "pathEdit gesture drags anchors/handles",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-08-19T07:39:14Z",
        "evidence_at": "2026-10-18T07:58:20.762Z",
        "headline": "All linked evidence is passing (8 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 8,
          "passed": 8,
          "skipped": 0
        }
      },
      "id": "geometry-coordinates.bezier-path-geometry",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Bezier path geometry (PathPointType anchors + handles)"
    },
    {
      "chapter": "geometry-coordinates",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "multi-contour boundaries rasterised on real outlines, nonzero winding (W1.1)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:58:20.762Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "geometry-coordinates.compound-subpaths",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Compound paths (multi-contour subpath boundaries)"
    },
    {
      "chapter": "geometry-coordinates",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "frameBounds + pageBounds properties",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "Object transform X/Y/W/H projections",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:05.947Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "geometry-coordinates.geometric-bounds",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "GeometricBounds (y1 x1 y2 x2, pt)"
    },
    {
      "chapter": "geometry-coordinates",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "imageContentTransform property",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "note": "read surface exposes imageContentTransform on image-bearing rectangles (paged-canvas w3a0_read_surface.rs image_content_transform_entry_present_for_image_rect)",
          "status": "shipped"
        },
        {
          "component": "editor.gesture",
          "note": "content grabber (Cmd-drag) translateContent",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:05.984Z",
        "headline": "All linked evidence is passing (7 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 7,
          "passed": 7,
          "skipped": 0
        }
      },
      "id": "geometry-coordinates.image-content-transform",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Image content transform (inner image within frame)"
    },
    {
      "chapter": "geometry-coordinates",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "frameTransform + decompose paths (rotation/scaleX/scaleY/flipH/flipV via polar decomposition; flip folds into X axis, shear captured not exposed) (W0.3)",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "note": "frameFlipV read entry (decompose V-lossiness: reads as flip_h+180°) (W3.A0)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:06.226Z",
        "headline": "All linked evidence is passing (4 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 4,
          "passed": 4,
          "skipped": 0
        }
      },
      "id": "geometry-coordinates.item-transform",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "ItemTransform (2×3 affine per frame/page)"
    },
    {
      "chapter": "geometry-coordinates",
      "claims": [
        {
          "component": "core.mutation",
          "note": "pathPointInsert/Remove/CurveType/Set, pathOpenAt; kurbo kernel ops outlineStroke/offsetPath/simplifyPath (v30, snapshot-inverse undo; offset = single closed contour v1)",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "note": "incl. requestNearestPathPoint read (v30 — retires bundle-side closest-t copies)",
          "status": "shipped"
        },
        {
          "component": "editor.gesture",
          "note": "scissors tool + path-edit overlay",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:05.311Z",
        "headline": "All linked evidence is passing (18 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 18,
          "passed": 18,
          "skipped": 0
        }
      },
      "id": "geometry-coordinates.path-topology-ops",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Path topology ops (insert/remove anchor, open-at, curve type)"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "a TipCoverage trait implemented by both the parametric tip and a sampled tip, with the stroke accumulator generic over it — so an imported brush uses the SAME accumulator, spacing walk and composite rule. No second paint path, and image-gpu gained no dependency on image-psd. PARTIAL: ROTATION is deliberately not applied — the angle sign convention is unresolved in the spec and choosing a handedness would be silently wrong on half the world's brushes. The `abr_presets` bundle door now EXISTS (2026-08-06), so the reader is reachable in the shipped wasm and the Brush-presets panel section drives it — see image.editor.brush-presets",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "image.abr.engine-bridge",
      "importance": "normal",
      "since": "2026-08",
      "status": "experimental",
      "title": "A sampled .abr tip paints through the existing stroke accumulator"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "built under the §3.1 two-role clean room from a behaviour spec verified against 3,215 real presets. The descriptor reader is the bulk and lives in image-psd because adjustment layers, effects and the text engine need the same tree. The virtual-memory-array-list is parsed STRUCTURALLY — the widely-repeated 'fixed 264-byte skip' is arithmetic coincidence, not layout. A 24-variant warning channel means unknown constructs are reported, never silently wrong. DECLINED with reasons: legacy v1/v2, 16-bit RLE, object-array types (a descriptor item is not length-delimited, so an unknown layout cannot be stepped over), and the airbrush percents whose scale is undetermined",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "image.abr.reader",
      "importance": "normal",
      "since": "2026-08",
      "status": "experimental",
      "title": ".abr brush-preset reader over a new PSD descriptor reader"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "the §3.1 two-role clean room needed a gate each role can actually RUN, so it is two lanes. LANE A (abr_lane_a.rs, 23 tests, IMPLEMENTER- owned, always on, needs no corpus byte): drives an INDEPENDENT abr_builder emitter from the published corpus-profile.json / corpus-record-ledger.tsv, so expectations were measured on 3,215 real presets while every byte the reader sees is synthetic — sub-gates A1..A7. LANE B (abr_corpus.rs, 4 tests, ANALYST-owned, #[ignore]d behind PAGED_ABR_CORPUS=1 + the clean-room mount): B1 per-file profile match, B2 all 3,202 ledger rows including a SHA-256 of the decoded coverage, B3 the 238-way published-PNG alpha-polarity oracle, B4 the corpus aggregates (81,926 descriptor items, 102 distinct keys, 16,611 class ids). ALL FOUR NOW RUN — B3 was the last never-run gate and self-skipped on a hard-coded 'png-oracle' directory name that is not what the pack unpacks to; pointing it at the real mercator-png-oracle directory (with PAGED_ABR_PNG_DIR as the override) was the whole fix, and a run now reports 0 filtered out, 0 ignored. The skip path stays STAGED and loud rather than silent: wrong switch, absent mount and PARTIAL mount each print their own reason, because 'a partial corpus would gate on a subset and report green for it'. PARTIAL: Lane B runs only with the switch AND the mount — in the default cargo nextest --workspace lane those four are 4 of the 6 skips, by design",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "image.abr.regression-gate",
      "importance": "normal",
      "since": "2026-08",
      "status": "experimental",
      "title": "The .abr regression gate, split into the two lanes its roles can run"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "Min/max/mean for R/G/B/A plus the derived Rec.709 luma, reduced from the straight-RGBA8 buffer `image_histogram` reads, so the two readouts agree by construction rather than by coincidence. An empty buffer reports nulls, not zeros — 'no pixels' and 'all pixels are zero' are different measurements. PARTIAL: viewing one channel in ISOLATION is not offered and the panel says so — that is a display state, and the host scene channel takes a composited image rather than a view mode, so faking it would mean compositing greyscale into the document (a destructive edit wearing a view's clothes).",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:07:24.724Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "image.channels.readout",
      "importance": "normal",
      "since": "2026-08",
      "status": "experimental",
      "title": "Per-channel statistics, from the same buffer as the histogram"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "The operation a channels list exists to enable. A channel's bytes ARE the coverage representation, so this is a COPY and not a threshold: a 50%-grey channel yields a 50%-selected region, which is what a luminosity mask means and what every masked kernel already honours at the ABI's r16float @group(2) binding. An unknown channel name is an ERROR rather than a fallback, because masking on the wrong channel is a silent wrong answer.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:07:24.724Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "image.channels.to-selection",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Load a channel as the selection (luminosity masking)"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "M1: the display-consistency lane — RGBA8 RGB↔RGB through qcms 0.3 (the EXACT build core uses, so the page render and the image editor agree by construction). Differential oracle lcms2 (CI-only dev-dep, never shipped); tolerance 3/255 per channel over matrix-shaper profiles. PARTIAL: deliberate limits inherited from the Firefox-driven build (a0-audit §A) — no BPC (bpc:true recorded on the handle but inert), 8-bit endpoints, Perceptual/RelCol-centric intents (Saturation/Absolute degrade to nearest); no CMYK ingest (that is the moxcms print lane, image.cms.print)",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-13T11:20:32.656Z",
        "headline": "All linked evidence is passing (3 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 3,
          "passed": 3,
          "skipped": 0
        }
      },
      "id": "image.cms.display",
      "importance": "normal",
      "since": "2026-06",
      "status": "experimental",
      "title": "CMS display lane — qcms RGB↔RGB (the exact build core ships, page/editor agree)"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "D-11 ruled HYBRID (a0-audit §F); CmsEngine seam + qcms display backend (core's exact build) live; lcms2 dev-only differential oracle GREEN (measured ≤2/255); identity LUT bake pinned. PARTIAL: moxcms print lane (intents/BPC/float) + the cms.apply GPU kernel are M1",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-13T11:20:34.412Z",
        "headline": "All linked evidence is passing (15 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 15,
          "passed": 15,
          "skipped": 0
        }
      },
      "id": "image.cms.engine",
      "importance": "normal",
      "since": "2026-06",
      "status": "experimental",
      "title": "CMS behind the swappable CmsEngine seam (D-11: hybrid)"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "decode_rgba8 handed RGB pixels through VERBATIM until now, so an image tagged AdobeRGB or ProPhoto decoded to its raw encoded numbers while every kernel's math is specified over the post-CMS working space — plausible and wrong, with nothing reporting it. Now transformed at decode via the qcms display lane, in the same shape as the CMYK cast one branch away. Three honest states surfaced in the panel's Colour row: ICC managed / sRGB assumed / sRGB assumed (profile rejected); a broken profile falls back rather than failing the decode. The K-3 worker path carries its treatment out so its already-transformed pixels are not relabelled 'assumed' on re-registration. PARTIAL: rung 2 (moxcms per-intent + soft-proof UI) needs the host's proof-setup state piped to the plugin; PSD's image-resource profile is not surfaced by the merged-composite decode, so PSD reads as assumed",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "image.cms.ingest-transform",
      "importance": "normal",
      "since": "2026-08",
      "status": "experimental",
      "title": "CMS rung 1 — the RGB display transform at decode"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "M1: the print lane through moxcms 0.8.1 (pure-Rust, BSD-3 OR Apache-2.0), the capability qcms's build lacks (audit §A). RGB↔RGB half: genuine per-intent A2B LUT selection (all four map 1:1 to moxcms RenderingIntent, none silently degraded; intent-dependence proven on a synthetic distinct-A2B profile); tolerance 3/255 vs lcms2. CMYK8→RGBA8 ingest half (spec §5.2): a CMYK *device* source → RGB dest applied to the packed 4-ink cmyk8 the JPEG adapter delivers (post Adobe-APP14 re-inversion); intent honored, the lane REFUSES a non-CMYK source profile (guarded, never a silent mis-colour); bulk 8/255 vs the lcms2 CMYK→RGB oracle, 20/255 admits the paper-white/relcol media-white corner. PARTIAL: moxcms 0.8.1 exposes no runtime BPC knob (bpc:true recorded but byte-identical to false; the BPC-fidelity row stays pinned to a future moxcms release); CMYK ingest is conformance-gated here but NOT YET WIRED into the M4 image-js ingest slice (needs an image-cms dep under the wasm budget + threading the embedded source ICC) — that path still rejects CMYK as \"the M2 cast/CMS lane\"",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-13T11:20:35.395Z",
        "headline": "All linked evidence is passing (14 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 14,
          "passed": 14,
          "skipped": 0
        }
      },
      "id": "image.cms.print",
      "importance": "normal",
      "since": "2026-06",
      "status": "experimental",
      "title": "CMS print lane — moxcms per-intent RGB↔RGB + CMYK8→RGBA8 ingest"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "DEFERRED / NON-GOAL per the 2026-06-13 codec re-audit (no code landed). AVIF: DECODE blocked — no permissive pure-Rust AV1 *library* decoder (rav1d is C-API-only, avif-decode/dav1d-rs are C, rav1d-safe is AGPL); ENCODE is now license-clean (ravif BSD-3 over rav1e BSD-2) but DEFERRED on the 8 MiB wasm budget (I-07, ~257k-SLoC rav1e) + no round-trip oracle. JXL: a v2 NON-GOAL — DECODE license-clean via jxl-oxide (MIT OR Apache-2.0) but deferred on v2 scope + the wasm budget (~10 jxl-* crates + brotli); ENCODE blocked-on-ecosystem (jxl-encoder is AGPL/commercial; libjxl is C++). Camera-RAW + HEIC/HEIF: EXPLICIT NON-GOALS — out of the editable-RGBA/PSD lane, need heavy/non-permissive decoders (libraw/rawler LGPL; libheif C/LGPL-3); at most a future host-side document-link rasterize into RGBA. (Re-open AVIF/JXL encode/decode on wasm-budget headroom + a permissive decoder; this row tracks all four v2-format slots until a split.)",
          "status": "planned"
        }
      ],
      "health": {
        "changed_at": "2026-06-13T09:36:44Z",
        "evidence_at": null,
        "headline": "No evidence linked yet; implementation last changed Jun 13",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "image.codec.avif",
      "importance": "normal",
      "since": "2026-06",
      "status": "planned",
      "title": "AVIF / JXL / camera-RAW / HEIC — codec verdicts (deferred + explicit non-goals)"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "a tiny dependency-free EXIF/TIFF reader over the raw payload codecs already capture (SourceInfo.exif). Reads three facts off IFD0 (+ Exif sub-IFD): Orientation (0x0112), XResolution/YResolution/ResolutionUnit → DPI, ColorSpace (0xA001: sRGB/Uncalibrated/Other); both byte orders (II/MM). Lenient by design: any malformed/truncated/unknown structure yields None for that field, never an error (EXIF is advisory). Hand-rolled (not kamadak-exif) to keep the codec crate dependency-flat under the 8 MiB wasm budget (I-07) + #![forbid(unsafe_code)]. APPLY SITE: the decode-to-RGBA bridge in image-js auto-orients on the straight-RGBA8 buffer BEFORE the GPU adjustment pipeline (a CPU transpose/flip inherent to ingest, the eight CIPA transforms unit- + e2e-tested). EXPORT N/A by construction: ingest auto-orients to canonical upright, so re-export is already identity orientation (no tag to strip). 10 unit (tags/endianness/garbage) + 6 ingest geometry + an APP1-orientation e2e test",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-06-13T04:31:34Z",
        "evidence_at": "2026-10-13T11:20:33.024Z",
        "headline": "All linked evidence is passing (10 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 10,
          "passed": 10,
          "skipped": 0
        }
      },
      "id": "image.codec.exif",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "EXIF/TIFF read — orientation + DPI + color-space tag, auto-orient at ingest"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "traits frozen + exercised; raw source + PNG adapter landed (D-4 RESOLVED → zune-png, wins every bench cell 2.3×–13.6×, provisional until the Links corpus audit; 8-bit/no-iCCP M0 limits documented); JPEG/TIFF/WebP/AVIF are M1",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-13T11:20:33.043Z",
        "headline": "All linked evidence is passing (17 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 17,
          "passed": 17,
          "skipped": 0
        }
      },
      "id": "image.codec.traits",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Codec adapter contract (ImageSource/ImageTarget over sans-IO ByteSource) + PNG + raw"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "REQUIRE_GPU=1 turns every skipped device test into a failure (224 of 1,034 used to pass by skipping); CI lanes gpu-sw (lavapipe, every PR) and gpu-metal (main); wasm freshness stamp so specs never test an engine older than its sources. PARTIAL: the CI lanes have not run yet (not pushed).",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "image.conformance.gpu-lanes",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "GPU tests that cannot pass silently"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "f16-ULP parity harness (quantize-last per §6.3) + independent PSD fixture builder + proptest layers live; 329 tests green at M3 incl. GPU parity on Metal (verified 2026-06-11); cargo-tree guards wired in CI. PARTIAL: libvips/GEGL oracle containers + Playwright/WebGPU browser lane + pinned-swrast CI are follow-ups (§9.3)",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-13T11:20:39.818Z",
        "headline": "All linked evidence is passing (76 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 76,
          "passed": 76,
          "skipped": 0
        }
      },
      "id": "image.conformance.harness",
      "importance": "normal",
      "since": "2026-06",
      "status": "experimental",
      "title": "Conformance harness — scalar references, gpu↔ref parity, fixture corpus"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "18-stage documented chain order (input remaps → tone → colour grade → chroma → quantizers → spatial → curves), all identity-short-circuited and mask-aware. New door adjust_image_ext carries a 47-float ext block; adjust_image_full delegates with an empty ext so nothing broke. is_identity became semantic (a gated-off filter is a no-op regardless of its other fields). Panel: Levels-per-channel, Color, Effects, Generate sections",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-13T11:20:40.817Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "image.editor.adjust-breadth",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Panel + chain integration of the new adjustments and generators"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "Phase 6: a single \"auto\" adjustment composing the EXISTING levels + white-balance kernels from the panel RGB+luma histogram (histogram_rgba8). Derives a percentile-clipped auto-levels black/white range (0.5%/99.5% of luma) and a gray-world white-balance estimate fitted to the adjust.white_balance temp/tint axes (gr=1+temp,gg=1+tint,gb=1-temp), and emits the params the LEVELS/WB panel commits through adjust_image_full. Pure readout/orchestration (spec §6): image_gpu::auto_enhance over the histogram counts, deterministic fixed-order scalar arithmetic, bit-stable, its own golden — NOT a dispatch kernel (owns no registry/kernels.yaml row, like the histogram readout it consumes). wasm surface: image_auto_enhance_params(handle) -> [in_black,in_white,temp,tint] f32 (no GPU, no kernel dispatch). Degenerate/flat/neutral inputs collapse to the identity no-op [0,1,0,0] — never a wrong auto-correction. 6 conformance tests (image_adjust_auto_enhance_*). EDITOR WIRING (2026-06-18, was the HONEST GAP): now surfaced end to end — engine.autoEnhanceParams facade + session.autoEnhance (merges the 4 params into the panel levels/WB state, preview-only) + an 'Auto-enhance' panel button + the media.paged.image.command.autoEnhance command. JOURNEY (editor surface, image-curves.journey, GPU lane): click Auto-enhance → the In white slider populates below 1 from the histogram → Apply composites and the page render changes (HARD)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:06:41.601Z",
        "headline": "All linked evidence is passing (6 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 7,
          "passed": 6,
          "skipped": 1
        }
      },
      "id": "image.editor.auto-enhance",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Auto-enhance — gray-world WB + percentile auto-levels params from the panel histogram"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "Filter strokes over the conv.gaussian_h/v -> conv.unsharp chain (blur = amount -1, sigma follows the tip; sharpen = unsharp mask sigma 1 amount 1); the brush's flow is the strength.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:08:14.663Z",
        "headline": "All linked evidence is passing (7 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 8,
          "passed": 7,
          "skipped": 1
        }
      },
      "id": "image.editor.blur-sharpen-brush",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Blur and sharpen brushes"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "The `.abr` reader had NO caller, so a wasm32 release build eliminated it wholesale — the capability existed in the repository and not in the artifact. `abr_presets` is its caller. The projection surfaces exactly what the paint engine's tip has (size, hardness, spacing) under three rules the corpus forced: hardness is not universal (only a computedBrush carries `Hrdn`, so `null` means 'the file does not say' and never a substituted default), diameter is not always pixels (the unit rides along so the panel refuses rather than applying a wrong number), and an unsupported tip is still a preset (listed, with its kind explaining the missing parameters). PARTIAL: roundness and angle are REPORTED, not applied — the tip is a parametric circle — and a sampled (bitmap) tip contributes its size only, because stamping a bitmap is the separate engine image.abr.engine-bridge tracks.",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:07:36.564Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "image.editor.brush-presets",
      "importance": "normal",
      "since": "2026-08",
      "status": "experimental",
      "title": "Photoshop .abr brush libraries load and set the brush"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "Rotate 90/180, flip H/V and Canvas Size with a 3×3 anchor move every layer, mask and smart source exactly; one undo step; Canvas Size refused over a smart object.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:06:03.995Z",
        "headline": "All linked evidence is passing (5 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 5,
          "passed": 5,
          "skipped": 0
        }
      },
      "id": "image.editor.canvas-ops",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Rotate, flip and Canvas Size over all layers"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "NO NEW KERNEL. A dab has never cared where its paint layer came from, so the clone stamp is `gen.solid` replaced by an uploaded window of the image — the same tip, spacing, pressure mapping, blend and selection masking as the brush. The anchor lives on the SESSION, not the stroke, so it survives between strokes (repeated retouching is the normal case), and the source OFFSET is fixed at the first dab because one recomputed per dab would follow the cursor and smear the copy instead of translating it. Out-of-bounds source reads are TRANSPARENT, not clamped: clamping would smear the border across the canvas and read as a rendering bug. A clone with no anchor deposits NOTHING and says so — falling back to a colour would look like a broken clone.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:08:46.834Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "image.editor.clone-stamp",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Clone stamp — the brush with a sampled paint layer"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "Two session colours (swap, black/white), the browser colour input as picker, Alt-click with a paint tool samples a 3×3 average of the composite; the brush paints the foreground, the gradient command and tool run fg → bg. The host colour-picker widget replaces the browser input where the host offers one (widgets.colorPicker@1). PARTIAL: that path waits for the v0.66.0 contract publish.",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": "2026-10-04T13:05:39Z",
        "evidence_at": "2026-10-18T05:05:52.184Z",
        "headline": "All linked evidence is passing (5 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 6,
          "passed": 5,
          "skipped": 1
        }
      },
      "id": "image.editor.color-fg-bg",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Foreground/background colour, picker and eyedropper"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "Exemplar-based inpainting (Criminisi/Perez/Toyama 2004) run COARSE TO FINE. The hole is filled patch by patch from its boundary, ordered by confidence x isophote term, each patch copied from the best match elsewhere in the image; a half-resolution pass runs first and records WHERE each pixel came from, and the full-resolution pass centres its search on twice that offset — better (the coarse decision about large-scale structure is honoured, since a 9x9 patch sees twice as much at half res) and cheaper (a 12px window around a known-good offset instead of a 96px blind one). Below 64px the coarse pass has nothing useful to say and the single-scale path runs directly. Every written pixel is COPIED, never averaged — asserted on both paths, and the pyramid path is the one where it could have gone wrong, because downsampling averages. CPU by nature: no kernel can express 'find the patch that best continues this one'; the GPU-only rule governs the kernel path and this adds none. The one remaining limit, stated in the panel: the search is WINDOWED, so a match on the far side of a large image is not found. Honest test-scope note in the file: the structure test passes with the isophote term removed — the exemplar search alone carries an edge on simple geometry — so the ordering term is exercised but not isolated.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "image.editor.content-aware-fill",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Content-aware fill — coarse-to-fine exemplar inpainting"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "Phase 6: a host-agnostic crop interaction machine (rect + straighten angle + aspect lock + in-flight drag) thin over the leaf-pure Rust crop geometry (image-core::crop — 8 Handle grips + Move, hit_handle, apply_drag with AspectLock presets free/1:1/3:2/4:3/16:9, ratio-preserving fit, to_region commit, frame_corners, normalize_angle/rotate_point). The crop TOOL gesture (glue/crop-tool.ts) maps page-local pt ↔ image px against the composited frame box and renders the crop frame through the LIVE host.overlay door (setToolPreview); manifest contributes media.paged.image.tool.crop (transform rail, shortcut \"c\") + the commitCrop command. Commit windows the buffer via geom.crop (image.kernel.family-t2). Crop geometry Rust-tested, TS thin (crop machine drag/aspect/commit vitest over real wasm)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:06:34.407Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 3,
          "passed": 2,
          "skipped": 1
        }
      },
      "id": "image.editor.crop",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Editor crop + straighten tool — host-agnostic crop machine over the geom.crop window"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "closes the 'straighten previews but cannot commit' gap. Backward-map rotation with 2×2 bilinear + clamp-to-edge; 0° short-circuits to the exact axis-aligned cut (no GPU, no interpolation blur); non-zero is GPU-only and rejects honestly. Residual: clamp-to-edge smears corners — an alpha-aware variant would be a second kernel",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "image.editor.crop-straighten",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Crop straighten commits (arbitrary-angle rotation resample)"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "Phase 6: the levels/curves/white-balance panel over the existing T2 adjust lane + the Phase-6 engine helpers. Histogram: SVG R/G/B/luma view from the engine histogram_rgba8 readout (image.reduce.statistics). Levels: black/white/gamma + output sliders → adjust.levels. White balance: temp/tint sliders → adjust.white_balance. Curves: a draggable SVG control-point editor → image-core::curve_lut(control_points) -> [u8;256] (monotone cubic Fritsch-Carlson) → the curve LUT the curves stage consumes. adjust_image_full composites WB + levels GPU stages + the optional curve LUT. 10 vitest (real wasm: histogram, curve LUT) + curve→LUT Rust tests. PARTIAL: curves ran a CPU LUT pass until 2026-08-06; the GPU class now exists (adjust.lut1d, linearly interpolated — nearest indexing was measured at 61-135 f16 ULP against a declared 4 because a step function cannot hold that tolerance). The panel still drives the CPU pass, now with per-channel curves (red, green, blue under the composite; 2026-10-04) — adjust.lut1d applies one table to all channels, so the per-channel route stays the CPU table pass; straight-/255 working values until the M1 CMS lane",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": "2026-10-05T12:57:04Z",
        "evidence_at": "2026-10-18T05:06:37.957Z",
        "headline": "All linked evidence is passing (3 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 5,
          "passed": 3,
          "skipped": 2
        }
      },
      "id": "image.editor.curves",
      "importance": "normal",
      "since": "2026-06",
      "status": "experimental",
      "title": "Editor levels/curves/white-balance panel — histogram view + adjust kernels"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "K-3 (I-02) closes: the decode lane runs OFF the main thread. createDecodePool spawns N workers (N = host.workers.concurrency() = min(declared 4, hardwareConcurrency, 8)); each worker boots its own image-js, runs the codec/PSD CPU decode, and posts straight RGBA8 back (transferred); the main realm registers it via the new image-js ingest_rgba8 export → a handle for the GPU adjust + tile paths. decodeBatch fans a multi-image ingest across the pool. manifest declares capabilities.workers { max:4, sharedMemory:true }; the editor injects the WorkerBackend; supports(\"workers@1\") true. Degrades HONESTLY: no backend / 0 grant → createDecodePool null → main-thread engine.decode (same pixels). dispose terminates every worker (host also auto-terminates on bundle dispose). 5 vitest (decode-pool.spec, real wasm in Node) + 2 native ingest_rgba8 tests + 11 plugin-sdk host.workers tests. PARTIAL: SAB zero-copy hand-off is granted + budgeted but the pool still messages bytes (transfer, not SAB); full-pool Engine-B tile parallelism (the 100+ MP C-6 lane) is the M2 follow-on",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:06:34.407Z",
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 0,
          "skipped": 1
        }
      },
      "id": "image.editor.decode-pool",
      "importance": "normal",
      "since": "2026-06",
      "status": "experimental",
      "title": "K-3 decode worker pool — off-main-thread ingest decode (host.workers consumer)"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "Filter strokes through the adjust.dodge_burn kernel masked by the dabs' coverage: dodge/burn by tonal range (shadows, midtones, highlights) with an exposure, the sponge saturates or desaturates at the brush's flow. Three tools in the clone stamp's rail slot; options in the panel's Dodge / burn / sponge section.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:08:03.431Z",
        "headline": "All linked evidence is passing (17 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 18,
          "passed": 17,
          "skipped": 1
        }
      },
      "id": "image.editor.dodge-burn",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Dodge, burn and sponge"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "the registered T1/T2 kernels' FIRST wasm reach: the adjust chain grows optional ADJUST_HUE_ROTATE / ADJUST_INVERT_RGB / separable CONV_GAUSSIAN_H+V (radius ceil(3sigma) clamped) / CONV_UNSHARP (two-input apply2 blend) stages — same registry-driven dispatch, no new kernels; adjust_image_full +4 params; wasm 0.80 MiB of the 8 MiB budget. Panel: a Filters section (blur sigma, sharpen, hue degrees, invert) on the same setParams->Apply lane. Remaining T1/T3 reach (resample, box, morphology, generators, compose modes) still engine-only — see the editor-ui-coverage spec",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:06:10.821Z",
        "headline": "All linked evidence is passing (31 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 33,
          "passed": 31,
          "skipped": 2
        }
      },
      "id": "image.editor.filters",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Filters — blur / unsharp / hue-rotate / invert in the adjustments panel"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "Drag sets where a foreground → background gradient starts and ends (linear, radial, angular, reflected, diamond); Shift snaps to 45°. FillSpec::GradientLine.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:07:51.847Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 1,
          "skipped": 1
        }
      },
      "id": "image.editor.gradient-tool",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Gradient tool (drag the line)"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "UPGRADED from a mean offset to a gradient-domain solve on 2026-08-06, which removed the limitation the first version documented in the panel. The correction is the harmonic (membrane) field matching the source-destination mismatch on the dab's BOUNDARY — the Perez/Gangnet/Blake seamless-cloning formulation, Laplace rather than full Poisson because the guide field is the source's own gradient. Applied as one `math.add` over an uploaded rgba16float window, so it is still a registered dispatch and no new kernel. The mean offset is the CONSTANT solution of the same equation, so nothing that used to work changed — asserted directly. The solve runs on an EXPANDED window: a dab's dirty region IS the dab, so solving on it alone finds no boundary and silently produces no correction at all (the first version did exactly that and healed identically to a clone). Where the sampled window runs off the image there is genuinely no boundary, and the tool falls back to a plain clone rather than inventing a correction — stated in the panel. CPU because Jacobi is a 5-point stencil and an honest GPU version is hundreds of round-trips per dab; this is input preparation like the CMS transform at decode, not a CPU kernel path.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:08:40.753Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "image.editor.healing-brush",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Healing brush — a clone tone-matched in the gradient domain"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "The catalog called this a PANEL gap and not an engine gap and was right — the bounded COW journal has carried labels, scopes and an honest dropped count since it shipped. What was actually missing was smaller than a panel: the journal exposed only the TOP label per side, which answers 'what does ctrl-Z do' rather than 'where can I go back to'. Steps list oldest-first with the position marked; clicking one walks the journal as N undos, because it is a stack and not a random-access log — making a jump O(1) would mean holding every intermediate state, which is what the byte budget exists to bound. The list is the RETAINED history and says so; a budget-evicted entry leaves it while `dropped` still counts it.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "image.editor.history-panel",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "History panel over the tile journal"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "The rasterImage context declares onUndo/onRedo/onCanUndo/onCanRedo: the host's undo steps the image's list and falls through to the document when there is nothing to undo.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-13T11:19:54.569Z",
        "headline": "All linked evidence is passing (3 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 3,
          "passed": 3,
          "skipped": 0
        }
      },
      "id": "image.editor.host-undo",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Host Cmd+Z inside an image"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "New kernel adjust.hue_saturation (HSL; ranges fully on within ±15° of their centre, fading to 0 at ±45°; greys in no range) with WGSL, scalar reference and GPU↔reference parity of 1 f16 ULP; panel section; rides the extended adjust block (47 → 72, the old form still reads). PARTIAL: not yet compared with Photoshop's own dialog (the probe lane will).",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-13T11:19:54.512Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "image.editor.hue-saturation",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Hue/Saturation — master, six colour ranges, colorize"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "M4: the loop is LIVE — host.assets.getPlacedImage (C-5) → image-js wasm decode (PSD merged-composite RAW/RLE RGB/Gray 8-bit, PNG, JPEG; 16-bit/CMYK/ZIP answer honest Unsupported) → Engine A adjustments (exposure/brightness-contrast/saturation, GPU-only via the async sink) → v41 image scene item sized to the frame content box; committed-Apply re-submit (Stage A), clear-on-deselect/reset; K-2 raster importer (.psd/.png/.jpg) routes File/Open here; wasm artifact 0.45 MiB vs the 8 MiB budget; 9 vitest over the real wasm + 5 native ingest tests + GPU exposure parity. PARTIAL: preview-only (no adjusted-pixel save-back), no resample stage (huge images submit at natural size), straight-/255 working values until the M1 CMS lane, interactive per-drag preview is Stage B (M2). RENDER-VERIFY (editor render journey image-adjust.journey, 2026-06-18): the browser-realm WebGPU smoke is now LIVE — on the real-Chrome WebGPU lane (journeys-gpu) a REAL PNG imports through the K-2 importer, the engine boots headless (4-worker decode pool), the GPU adjust runs, and the Stage-A C-1 composite RENDERS the adjusted image onto the page (verified pixel change in requestSnapshot); skip-with-note on the CPU lane (GPU-only kernels). This closes the 'browser-realm WebGPU smoke awaits the editor e2e lane' caveat for the committed Apply path",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": "2026-06-18T11:50:06Z",
        "evidence_at": "2026-10-18T05:08:52.797Z",
        "headline": "All linked evidence is passing (6 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 10,
          "passed": 6,
          "skipped": 4
        }
      },
      "id": "image.editor.ingest",
      "importance": "normal",
      "since": "2026-06",
      "status": "experimental",
      "title": "M4 editor enablement — C-5 placed bytes → decode → adjust → C-1 Stage-A in-frame composite"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "NO new kernels — a stack folds through the existing compose.* set, one dispatch per layer, with the layer's opacity riding the compose alpha. The premultiplied invariant is stated once: layer pixels straight, accumulator premultiplied, cast in and out ONCE each rather than per pair, and skipped where the window is provably opaque. Two EXACT short-circuits carry the design: a single visible plain layer folds to ITSELF (same Arc, no GPU), so every pre-layer lane keeps its old behaviour; and a fully transparent layer is the identity for all 26 modes, so Add-layer is free. PER-LAYER MASKS and ADJUSTMENT LAYERS ship (2026-08-06). A mask is the same SelectionCoverage the selection tools author, lowered to the ABI @group(2) mask the compose fold always took and every caller passed None for; disabled and deleted are distinct states so a toggle never loses painted coverage. An adjustment layer carries AdjustParams instead of pixels and transforms the accumulator beneath it, so removing it restores the original BYTE-FOR-BYTE — asserted on a real device, and the assertion was mutation-checked. A masked adjustment applies only inside its mask. SMART OBJECTS ship too (2026-08-06): a layer whose pixels are a cached RENDER of preserved source bytes, so a rescale re-renders from source and the round trip is lossless; conversion is one-way because going back would discard the source. PARTIAL: no groups, no clipping layers, no smart FILTERS (they need clipping), no merge-down; crop/resize/straighten flatten the stack; export and save-back still write the flattened composite",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:07:45.857Z",
        "headline": "All linked evidence is passing (16 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 19,
          "passed": 16,
          "skipped": 3
        }
      },
      "id": "image.editor.layers",
      "importance": "normal",
      "since": "2026-08",
      "status": "experimental",
      "title": "Pixel-layer stack with a compose.* fold"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "Slider moves re-run the chain on a Lanczos proxy at about the frame's on-screen size (latest-wins), full resolution 400 ms after the sliders rest; layout always at source size; with a selection the preview runs at full size. Panel 'Live' toggle.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-13T11:19:54.622Z",
        "headline": "All linked evidence is passing (3 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 3,
          "passed": 3,
          "skipped": 0
        }
      },
      "id": "image.editor.live-preview",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Live adjustment preview on a resampled proxy"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "Add layer mask (reveal all / hide all) and a Pixels/Mask edit target on the active layer: brush and pencil paint the foreground's grey into the mask, the eraser paints white (reveals). The stroke paints the mask as a grey plate through the existing stroke compositor, previews by folding the stack with that plate standing in for the mask, and commits as one step on the one undo list (journal scope layer id | MASK_SCOPE_BIT).",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:06:28.869Z",
        "headline": "All linked evidence is passing (14 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 15,
          "passed": 14,
          "skipped": 1
        }
      },
      "id": "image.editor.mask-painting",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Paint on layer masks (Pixels/Mask edit target)"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "Drag moves the selected pixels (Alt copies) or the active layer; arrows nudge 1 px, Shift 10; whole-pixel moves are exact. Panel nudges and Offset (wrap).",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:07:51.847Z",
        "headline": "All linked evidence is passing (5 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 6,
          "passed": 5,
          "skipped": 1
        }
      },
      "id": "image.editor.move-tool",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Move tool for pixels and layers"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "TWO more tools joined this lane on 2026-08-06 — clone and heal — and it STILL needs no new kernel, because a dab never cared where its paint layer came from (see image.editor.clone-stamp). needed ZERO new kernels — painting falls out of the mask ABI: paint is gen.solid composed through the requested blend with stroke coverage as the mask, erase is one band.set_alpha (mix(a,(a.rgb,0),m) IS destination-out in straight space, so RGB survives under a partly-erased pixel). The identity mix(a,over(a,b,α),m) ≡ over(a,b,α·m) is proven on-device for separable AND non-separable blends, which licenses opacity riding the mask across all 26 modes. Spacing carries leftover arc length across pointer samples, so a drag outrunning the event rate still paints a stroke, not dots. PARTIAL: strokes commit PER GESTURE, not per drag — Stage-B GPU texture sharing is deferred (ADR-018, pending a Vello external-texture path in core). The destructive-commit ceiling this note used to carry is CLOSED: painting targets the active layer of the layer graph (image-js/src/layers.rs) and the bounded COW tile journal (image-graph/src/journal.rs) restores it, so re-ingest is no longer the only restore",
          "status": "partial"
        },
        {
          "component": "plugin.api",
          "note": "three tools (q / shift+f / shift+e), verified against all 47 shortcut claims across built-ins + draw + image; titled '(raster)' to disambiguate from paged.draw's vector paintbrush/blob/eraser on the same rail",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:08:58.648Z",
        "headline": "All linked evidence is passing (4 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 13,
          "passed": 4,
          "skipped": 9
        }
      },
      "id": "image.editor.paint",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Brush / Pencil / Eraser — the raster paint lane"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "Floods from the clicked pixel within the wand tolerance (contiguous or global), sampling the composite, inside the selection, with the foreground colour (bucket_fill).",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:07:51.847Z",
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 0,
          "skipped": 1
        }
      },
      "id": "image.editor.paint-bucket",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Paint bucket"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "Drag the selection onto the area to copy from; the selection is replaced by the region at that offset, healed to its surroundings (membrane correction over the selection, composited through its coverage on the GPU). One undo step.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:08:31.445Z",
        "headline": "All linked evidence is passing (7 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 8,
          "passed": 7,
          "skipped": 1
        }
      },
      "id": "image.editor.patch",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Patch tool"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "Define pattern copies the selection's bounds into an image of its own; Fill with pattern tiles it into the selection; Shape blur uses it as the blur's shape. Built engine-side earlier with no way in; now panel + commands.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:06:10.821Z",
        "headline": "All linked evidence is passing (4 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 5,
          "passed": 4,
          "skipped": 1
        }
      },
      "id": "image.editor.pattern-fill",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Define pattern, pattern fill and shape blur"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "Median/Maximum/Minimum through rank.median3 and morph.dilate/erode (3×3); Color Lookup parses .cube (TITLE, LUT_3D_SIZE, DOMAIN) and resamples any size to the kernel's 9³. PARTIAL: 3×3 only; a 9³ cube is coarse for fine grades.",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:05:58.061Z",
        "headline": "All linked evidence is passing (6 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 7,
          "passed": 6,
          "skipped": 1
        }
      },
      "id": "image.editor.rank-filters",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Median, Maximum, Minimum and Color Lookup"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "BUILT 2026-08-07, having been listed the same morning as not-built after the catalog was found scoring typography 'Partly' on the premise that it existed. It needed no new kernel — a rasterized glyph run IS a coverage field, and every masked dispatch already takes coverage at @group(2), so type composites as an ordinary masked solid fill. That is the fifth feature in this wave to fall out of the mask ABI (clone, heal, clipping, groups, type). SHAPING runs through harfrust (the maintained HarfBuzz port) rather than advance-width layout, deliberately: laying out by advance alone looks plausible in Latin and comes out unjoined and backwards in Arabic, which is exactly the plausible-looking-wrong failure this crate is arranged against. FONTS COME FROM THE HOST (assets.getFontFace, capability `assets: fonts`) — the document's own embedded faces, never a network fetch, so a bundle stays offline by construction; an unknown family is a stated dead end. A glyph the face lacks is COUNTED and left undrawn, never replaced with a tofu box. PARTIAL and stated in the panel: one run on one line — no wrapping, no paragraph layout, no styles, and no live text OBJECT. The host's text frame is the real text engine and a second one here would drift from it, which is the same reasoning that kept the Paths panel unbuilt. DEPENDENCY NOTE: the first attempt used rustybuzz + ab_glyph and tripped TWO unmaintained advisories (RUSTSEC-2026-0206, -0192). deny.toml has ignore = [] and always has, so adding the first suppression to land a feature would have been a policy change made to avoid a red gate — the swap to the maintained Linebender stack was the correct answer instead, and ab_glyph_rasterizer survives because it is standalone and carries no flagged parser.",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:09:16.827Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "image.editor.raster-type",
      "importance": "normal",
      "since": "2026-08",
      "status": "experimental",
      "title": "Raster type — shaped glyphs painted into a layer"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "Drag a box around the eye: inside the fitting ellipse only RED pixels (relative redness) are remapped by the channel mixer to the mean of green and blue, darkened.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:07:51.847Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 1,
          "skipped": 1
        }
      },
      "id": "image.editor.red-eye",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Red eye"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "resize_image(handle, out_w, out_h, filter) rides the T1 resample kernels (nearest / mitchell / lanczos3) through a new windowed-once async execute lane: RGBA8 -> f16 window -> resample at target dims -> new handle. The session swaps handles on the crop-commit pattern; the panel gains a Resize row (dims + filter picker, data-image-resize-*). Ships in image canary.3",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-13T11:20:34.037Z",
        "headline": "All linked evidence is passing (3 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 3,
          "passed": 3,
          "skipped": 0
        }
      },
      "id": "image.editor.resample",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Resample — resize the working image through the registered kernels"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "Paint over a blemish; on release an exemplar search around the stroke (inpaint::spot_source_offset) picks a source offset that clears the hole, and the heal composite (shifted source + membrane tone correction) lands under the stroke. No Alt-click.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:08:24.182Z",
        "headline": "All linked evidence is passing (7 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 8,
          "passed": 7,
          "skipped": 1
        }
      },
      "id": "image.editor.spot-heal",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Spot healing brush"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "C-6 (I-06): claim a selected placed image's pyramid through host.images.claimImageResource and serve tiles to the renderer (the v44 wire). The session's claimTiles() (the media.paged.image.command.claimTiles command) creates the provider for the ingested frame; the SDK adapter pulls source(level,x,y) and submits. ENGINE-B VERDICT — honest subset, gap named (not faked): Engine B (image-graph::BufferGraph::request) DOES support full (node,region,level) mip-aware window evaluation in Rust, but it is NOT exposed across the image-js wasm boundary (and emits rgba16float, not rgba8). So the provider serves the LEVEL-0 lane only — image-js image_tile_rgba8 → DecodedImage::tile_window_rgba8 (pure windowing of the decoded RGBA8 buffer, no kernel/GPU; orchestration per §6); levels:1, level>0 returns null. manifest: rendering ∋ \"resourceProvider\". Native test image_editor_resource_tile_window_cuts_and_clamps + tile-provider.spec (5 cases, real wasm + SDK routing): claim, level-0 serve, the level>0 gap, release on dispose, release on re-ingest. PARTIAL: wiring the tiled mip eval (+ rgba16f→rgba8 downconvert) to wasm is the M2/Stage-B follow-on",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:09:22.985Z",
        "headline": "All linked evidence is passing (3 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 3,
          "passed": 3,
          "skipped": 0
        }
      },
      "id": "image.editor.tile-provider",
      "importance": "normal",
      "since": "2026-06",
      "status": "experimental",
      "title": "C-6 tile provider for placed images (host.images.claimImageResource, v44; honest level-0 subset)"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "Brush, pencil, eraser (size, hardness, opacity, flow), magic wand and paint bucket (tolerance, contiguous) and gradient (shape) declare their options where the host reports values back (tools.settings@1, protocol 66), and the values drive the session. PARTIAL: no write-back from the panel to the bar; waits for the v0.66.0 contract publish.",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-13T11:19:54.648Z",
        "headline": "All linked evidence is passing (4 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 4,
          "passed": 4,
          "skipped": 0
        }
      },
      "id": "image.editor.tool-options",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Raster tool options in the host's tool-options bar"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "closes image-graph's documented COW deferral: snapshots only the tiles the damage region covers, as Arc clones. Entries are SCOPED by layer, which fixed a real bug found on the way — paint on one layer, select another, undo, and the restore wrote the first layer's tiles into the second. Bounded at 32 entries or 256 MiB, oldest-first, and what fell off is COUNTED and surfaced in the panel; a single edit larger than the whole budget is not recorded and clears the journal, because recording it would evict everything and then itself. Since 2026-10-04 one undo list covers pixel edits AND every change to the stack (snapshots; removing a layer and the canvas ops keep the history before them; drags merge) — ADR 463. PARTIAL: a crop/resize/straighten still flattens and clears it; the history is not persisted",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": "2026-10-05T12:57:04Z",
        "evidence_at": "2026-10-18T05:06:51.235Z",
        "headline": "All linked evidence is passing (5 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 6,
          "passed": 5,
          "skipped": 1
        }
      },
      "id": "image.editor.undo-journal",
      "importance": "normal",
      "since": "2026-08",
      "status": "experimental",
      "title": "Bounded COW tile journal — undo/redo for pixel edits"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "M2: generation-keyed invalidation + per-node sparse caches + mip-aware eval; the incremental-correctness gate (random graph × random damage → request==from-scratch byte+generation identical) GREEN on Metal. PARTIAL: COW undo journaling + batched per-node dispatch + the I-01 viewport presentation are the remaining M2/M3 work",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-13T11:20:33.53Z",
        "headline": "All linked evidence is passing (5 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 5,
          "passed": 5,
          "skipped": 0
        }
      },
      "id": "image.graph.engine-b",
      "importance": "normal",
      "since": "2026-06",
      "status": "experimental",
      "title": "Engine B — persistent buffer graph (invalidation, mip-aware eval, Gestures/Operations)"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "A 16-bit PSD used to be REFUSED outright, so it could not be opened at all. It now opens: the composite decodes 16-bit RAW planes by taking the high byte — the correct nearest-8-bit reading, and PSD's big-endian sample order is spec-documented and already established in this crate's `to_eight_lossy` — and the reduction is REPORTED all the way to the panel, which shows a Depth row saying '16-bit source, reduced to 8'. The layer stack, the edits and the save-back are 8-bit either way, so the precision could not have survived; opening while saying what was lost is the better of the two honest answers. EXTENDED 2026-08-07 to the LAYERED import: a 16-bit RAW-compressed PSD now imports its LAYERS instead of the whole file falling back to a flattened composite, using the same verified big-endian high-byte reduction. PARTIAL, with every remaining boundary drawn on EVIDENCE rather than effort: 16-bit RLE stays refused (per-channel AND composite) because no corpus fixture is 16-bit and the row-table semantics for 16-bit samples are unverified, and the CODEC lane (PNG/JPEG) stays 8-bit because no adapter there documents its 16-bit byte order and there is no fixture to check a guess against. NOTE that a synthesized fixture cannot close these: it would encode the very assumption under test, which is why the original refusals stand and why ONE REAL 16-bit RLE PSD in the corpus is the specific thing that would unblock them — a wrong guess decodes to a plausible-looking WRONG image, which is worse than a refusal. A true 16-bit PIPELINE (a 16-bit layer stack and save-back) is a separate, larger change to a core type. DEFERRED BY DECISION 2026-08-09 (maintainer): 16-bit is not needed now, it is a step for the future. That status differs from 'blocked on a fixture' in what it asks of the next person — do not spend on it, and do not read the missing PSD as a gap to close. The evidence note above stays because it is what that future work would need, not because anyone is waiting on it.",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "image.io.depth-16bit-ingest",
      "importance": "normal",
      "since": "2026-08",
      "status": "experimental",
      "title": "16-bit PSD sources open, reduced to 8 and reported"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "psd_write_stack writes pixel layers, masks, nested groups (lsct, pass-through), opacity, blend, visibility, clipping and Unicode names; psd-tools reads the same tree. PARTIAL: adjustment layers fall back to the flattened save (stated).",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-13T11:19:54.512Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "image.io.layered-psd",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Layered PSD export"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "Layers stored as content-addressed container parts plus a revision record; the frame's placed image becomes the composite PNG and its marker v2, in one batch mutation (one document undo step); reopening restores the layers when the placed bytes hash to the marker. ADR 462. On a protocol-66 host the PNG crosses as bytes (no 8 MB cap), revisions past the last 8 and the layer data only they named are deleted, and saving the document commits pending edits first. PARTIAL: the protocol-66 paths wait for the v0.66.0 contract publish (8 MB cap and no deletion on older hosts).",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": "2026-10-04T13:05:39Z",
        "evidence_at": "2026-10-18T05:07:45.857Z",
        "headline": "All linked evidence is passing (15 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 16,
          "passed": 15,
          "skipped": 1
        }
      },
      "id": "image.io.persistence",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Commit image edits to the document and reopen them"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "PSD: replace_channel_pixels on the single canvas-sized content layer, else FLATTEN into a new single-layer PSD — the UI states that verbatim rather than silently discarding structure. PNG/JPEG re-encode for raster sources (JPEG only when the source was JPEG). A plain export stays byte-identical only while there are no edits: since 2026-10-04 (plugin-image 74c9d40) an export at identity params after a stroke, layer edit, fill, filter, crop or resize runs the save-back, and when the save-back declines (size change, non-RGB, non-8-bit) the export is refused with the reason instead of returning the unedited original — before that fix those edits were silently missing from the PSD. PARTIAL: 8-bit RGB only — and note the asymmetry that opened on 2026-08-07, a 16-bit PSD now INGESTS (reduced and reported, see image.io.depth-16bit-ingest) but saves back at 8, so a round-trip through this lane loses depth and the panel says so at ingest rather than at save. No size change (crop+save-back is a follow-up), spot channels dropped by the flatten",
          "status": "partial"
        },
        {
          "component": "plugin.api",
          "note": "ADOPTED 2026-08-06. RFI K-10 shipped the door on 2026-08-04 with paged.image named as its consumer, and this bundle went on staging bytes for the Export Center regardless — the gap had migrated out of the contract and into the plugin, which is what a comment asserting a fact about someone else's code eventually does. `session.saveToFile` now hands the staged bytes to host.shell.saveFile, PROBED via supports('shell.saveFile@1') because without the probe the door's `false` and a user cancel are the same answer. The exporters remain the delivery lane for a host with no saver. Honest ceiling carried through to the panel text: a browser anchor-download backing cannot observe a cancel, so `true` means delivered to the download path, not that a file exists on disk",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-05T12:57:04Z",
        "evidence_at": "2026-10-18T05:07:19.132Z",
        "headline": "All linked evidence is passing (10 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 10,
          "passed": 10,
          "skipped": 0
        }
      },
      "id": "image.io.save-back",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Save-back — adjusted composite to PSD / PNG / JPEG"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "adjust +8 (vibrance, color_balance, black_white, posterize, threshold, photo_filter, channel_mixer, levels_rgb); compose +10 (linear_burn, linear_dodge, darker_color, lighter_color, vivid_light, linear_light, pin_light, hard_mix, subtract, divide — PSD fourcc joins, 26 of ~27 PS modes); gen +5 (radial/angular/reflected/diamond gradient + PCG hash noise, bit-identical WGSL↔Rust); geom +1 (rotate_bilinear, resample class). Determinism engineering over tolerance inflation: pow2-alpha stimulus for the quantizing kernels, own polynomial atan for the angular seam",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "image.kernel.breadth-2026-08",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Kernel breadth — 24 new point/compose/gen kernels (73 → 97)"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "32 ops landed, all at parity(gpu↔ref) on Metal within declared f16-ULP tolerance (most Exact); registry-driven dispatch live (unregistered ops unreachable by construction). PARTIAL: parity(ref↔oracle) awaits the libvips/GEGL CI containers; pinned-swrast CI lane pending (§9.3)",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-13T11:20:34.395Z",
        "headline": "All linked evidence is passing (158 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 158,
          "passed": 158,
          "skipped": 0
        }
      },
      "id": "image.kernel.family-t0",
      "importance": "normal",
      "since": "2026-06",
      "status": "experimental",
      "title": "T0 generated kernel families (arith/relational/boolean/cast/band/minmax/linear)"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "T1 landed — conv.gaussian_h/_v (separable, separability-proven) + unsharp, resample nearest/mitchell/lanczos3, the 16-op W3C compose set incl. the non-separable four — all at parity(gpu↔ref) on Metal within declared f16-ULP tolerances (the live kernel total lives in plugin-image registry/kernels.yaml, not here). PARTIAL: parity(ref↔oracle) awaits libvips/GEGL CI containers; the cms.apply LUT kernel needs a texture_3d ABI slot (M2 amendment)",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-13T11:20:34.324Z",
        "headline": "All linked evidence is passing (50 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 50,
          "passed": 50,
          "skipped": 0
        }
      },
      "id": "image.kernel.family-t1",
      "importance": "normal",
      "since": "2026-06",
      "status": "experimental",
      "title": "T1 crown-jewel kernels (conv/resample/compose — handwritten WGSL modules, ABI v1.1)"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "Phase 6: 15 T2 kernels landed — adjust.{exposure,brightness_contrast,levels,saturation,hue_rotate,invert_rgb,white_balance}, geom.{flip_h,flip_v,rotate90_cw,rotate90_ccw,crop}, gen.{solid,checker,linear_gradient} — all at parity(gpu↔ref) on Metal (levels at ChannelEpsF16(16): GPU pow + unpremul amplification). adjust.white_balance = temp/tint per-channel von-Kries gains (1+temp,1+tint,1-temp); geom.crop windows the buffer. These power the editor crop tool + levels/curves/WB panel (image.editor.crop/curves). PARTIAL: noise generator + native-CMYK editing deferred; ref↔oracle CI containers pending",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-13T11:20:33.971Z",
        "headline": "All linked evidence is passing (70 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 70,
          "passed": 70,
          "skipped": 0
        }
      },
      "id": "image.kernel.family-t2",
      "importance": "normal",
      "since": "2026-06",
      "status": "experimental",
      "title": "T2 editor-bearing kernels (adjust/geom/generators)"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "M3: morph.dilate/erode + rank.median3 (3×3 windowed modules, exact parity on Metal; median via a 19-comparator network verified vs brute sort over 200k inputs) + CPU two-pass chamfer distance transform. PARTIAL: FFT, draw ops, GPU jump-flood distance, params-driven morphology radius are the T3+ backlog",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-13T11:20:34.017Z",
        "headline": "All linked evidence is passing (14 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 14,
          "passed": 14,
          "skipped": 0
        }
      },
      "id": "image.kernel.family-t3",
      "importance": "normal",
      "since": "2026-06",
      "status": "experimental",
      "title": "T3 breadth kernels (morphology, rank/median, distance transform)"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "The two classes catalog §36.4 identified as accounting for three of the four adjustment gaps. Both interpolate rather than sample nearest, decided by MEASUREMENT: nearest makes the kernel a step function and cannot hold an f16-ULP tolerance (61 ULP on an identity table, 135 on an inverted one, against a declared 4) because f16 storage flips the chosen entry at index boundaries. Both carry their table in PARAMS because the ABI is frozen and every kernel input uploads at tile size — which caps the 3D cube at edge 9 (11.4 KiB) rather than the .CUBE convention's 17 (78 KiB, over the uniform floor). PARTIAL: the kernels exist and are parity-gated, but no panel surface drives them yet — Gradient Map and Color Lookup are reachable, not shipped",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "image.kernel.lut-samplers",
      "importance": "normal",
      "since": "2026-08",
      "status": "experimental",
      "title": "1D and 3D LUT sampler classes"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "The class catalog §36.4 named as gating the whole Distort family. Pinch/Spherize/Twirl/Wave are not independent effects but source-coordinate functions over one backward map; sharing a sampler keeps the reconstruction filter, edge rule and parity tolerance identical across them, which is what makes them comparable. Backward rather than forward because a forward map scatters and leaves holes. amount == 0 is the IDENTITY for every kind, so one slider per distortion needs no special cases — mutation-checked (breaking the pinch branch fails the identity test at 922 ULP). PARTIAL: four kinds ship; Ripple/ZigZag/Polar/Shear/Displace are further functions in the same switch plus a displacement-map input, and no panel drives any of them yet.",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "image.kernel.warp-backward",
      "importance": "normal",
      "since": "2026-08",
      "status": "experimental",
      "title": "General backward-map warp"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "frozen + golden-expansion-pinned; group0 inputs / group1 params / group2 selection mask / group3 rgba16float storage out; textureLoad-only; naga-validated for every registered kernel — plugin-image registry/kernels.yaml is the count's source of truth (128 rows as of the 2026-08-09 wave, which added 3 blur/sharpen/denoise, 2 rank-noise, geom.offset, 12 Filter Gallery primitives on top of the six from 08-08). NOTE (RFI E-4): the ABI documents a Windowed kernel's in0 as expanded by the radius, which image-graph honours and image-gpu's one-shot dispatcher does not; kernels now DERIVE the halo from the two texture dimensions so they are correct under either. The group2 mask is no longer constant-1 by default — the 2026-08 selection wave binds a real per-tile coverage window, so every pointwise kernel honors the user's selection",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-13T11:20:41.389Z",
        "headline": "All linked evidence is passing (5 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 5,
          "passed": 5,
          "skipped": 0
        }
      },
      "id": "image.kernel.wgsl-abi",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Frozen WGSL kernel ABI v1 (4 bind groups, 16×16, mask-mixed pointwise)"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "What 'smart filters' actually wanted: an adjustment layer clipped to a smart object IS a smart filter. Expressed as an extra MASK FACTOR rather than a second compositing path — a clip base IS its alpha, and 'show this only where the one below is opaque' is exactly what a coverage field says, so clipping needed no new kernel and no second fold. Two coverages MULTIPLY, so a layer that is both masked and clipped is confined by both rather than one winning. A clipped layer with NO base contributes nothing: compositing it unclipped would be the one behaviour a designer cannot recover from, since confining it was the point. GROUPS are not built — they are a tree where this is a stack, and clipping closes the smart-filter row without them.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-13T11:20:38.935Z",
        "headline": "All linked evidence is passing (3 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 3,
          "passed": 3,
          "skipped": 0
        }
      },
      "id": "image.layers.clipping",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Clipping layers — an adjustment confined to the layer beneath"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "Selecting an adjustment layer this session created binds the panel sliders to it (layers_set_adjustment, coalesced recomposite); selecting another layer gives the sliders back. PARTIAL: layers restored from the document are editable (their chain travels in the manifest), but layers opened from a PSD carry no chain.",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-13T11:19:54.516Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "image.layers.editable-adjustments",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Editable adjustment layers"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "An adjustment layer used to composite as f16 -> 8-bit -> adjust -> 8-bit -> f16, quantizing the backdrop TWICE per layer. In a non-destructive stack — the thing the spine exists to enable — three stacked adjustments meant three round trips through 256 levels, and that is where banding in a gradient actually came from; the kernels were always f16 and only the bridge was not. `adjust_f16` runs the SAME chain (extracted into `build_adjust_chain` and shared, because two transcriptions of a thirteen-stage ordered chain drift) with an f16 source and sink. The pipeline gained matching F16 read/write paths: both are the WORKING depth, so they are a widen and a verbatim write-out rather than casts, and they deliberately keep the U8 path's exact semantics minus the *255 round — a transfer or premultiply difference there would not fail, it would produce a plausible-looking wrong image. ONE stage still quantizes and is stated: a CURVE is a 256-entry table, so applying it quantizes by construction whatever the buffer holds. The first version of the banding test used four gentle nudges and PASSED with the 8-bit round trip reinstated; a compress/expand pair is what actually measures the mechanism.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "image.layers.f16-fold",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "The layer fold keeps adjustment layers in f16"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "A group owns a CONTIGUOUS run. PASS THROUGH is the default, as in Photoshop: members composite straight into the stack below, so an adjustment inside reaches everything beneath. ISOLATED composites members into their own buffer first, which is what makes group opacity fade the RESULT rather than each member and what confines an adjustment to its group-mates. An opacity below 1 FORCES isolation — Photoshop's rule and the honest one, since fading a group with no composite of its own is undefined; the JSON reports `passThrough` (declared) and `isolates` (effective) separately and the panel shows the effective one, because showing the declared mode would tell the designer the opposite of what the composite is about to do. NESTING works up to 8 levels and needed no tree of layers, only a STACK of parked accumulators keyed on the ancestor chain; the bound is a MEMORY guard (each open level parks a full-canvas buffer) and refusing explicitly beats an allocation failure mid-fold. A layer inside ANY hidden ancestor contributes nothing — checking only the immediate group would leak nested content out of a closed folder. Ungrouping RE-PARENTS children rather than orphaning them: dissolving one level of wrapping must not take a level of content with it.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "image.layers.groups",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Layer groups — pass-through and isolated, nesting up to eight"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "Convert a layer to a smart object and re-render it at a scale from its preserved source (lossless round trip); panel section + command.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:06:17.109Z",
        "headline": "All linked evidence is passing (3 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 3,
          "passed": 3,
          "skipped": 0
        }
      },
      "id": "image.layers.smart-objects",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Smart objects"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "Layers, masks, adjustments and smart objects are typed objects with schema rows generated from the kernel parameter layouts, written through one plugin-level batch (one undo step) and shown as host PropertyFields; typed commands twin the untyped ones (ADR 323). The committed schema is gated against the kernels (object_schema.rs).",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-13T11:20:40.8Z",
        "headline": "All linked evidence is passing (6 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 6,
          "passed": 6,
          "skipped": 0
        }
      },
      "id": "image.object-model",
      "importance": "normal",
      "since": "2026-10",
      "status": "stable",
      "title": "Object model — layers, masks, adjustments and smart objects addressable through host.objects"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "The other half, and it needed no engine work: the host answers `document.pathAnchors` and the selection lane already took a polygon. Cubic segments are flattened at a fixed subdivision because the target is a per-pixel mask, so more precision buys nothing measurable and the cost stays bounded. An element with no path geometry — which a plain rectangle legitimately is — is REPORTED in the contract's own terms rather than raised as an error the designer cannot act on.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:07:30.632Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "image.paths.path-to-selection",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "A host path becomes the selection"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "Half of the catalog's 'Paths / shapes' gap. A boundary walk over pixel EDGES rather than marching squares over centres, so a marquee rect traces to four corners instead of a staircase approximating one; holes are their own contours (a donut is two paths — collapsing them would silently fill the hole). Classifying outer-vs-hole by the START condition reported a donut as two outer rings, because the pixel below a hole passes the same test as the top of a region; the WINDING decides it instead. Traced contours are inserted as host polygons through `InsertNode` — this bundle's first document mutation — so they are undoable through the document's history and the whole vector toolchain applies. PARTIAL: anchors carry no bezier handles (fitting curves to a mask's staircase would invent smoothness it does not have), and each contour is a separate polygon rather than one even-odd compound path.",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:07:30.632Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "image.paths.selection-to-path",
      "importance": "normal",
      "since": "2026-08",
      "status": "experimental",
      "title": "A raster selection becomes a real vector path"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "Thread-local GPU and engine counters (wasm perf_counters), Rust and TS count budgets at measured values (equality asserted), criterion benches; per-device pipeline cache (steady-state pipelines 7/140/276 → 0); latest-wins recomposites (20 → 2 per slider drag); 16-bit tile cut narrowings 64 → 0.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-04T13:05:39Z",
        "evidence_at": "2026-10-18T05:07:42.191Z",
        "headline": "All linked evidence is passing (5 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 6,
          "passed": 5,
          "skipped": 1
        }
      },
      "id": "image.perf.budgets",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Work counters and count budgets"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "M4: one recorded dispatch, two readback maps — the async lane awaits the map callback (the only correct readback on wasm32/WebGPU, where a blocking poll cannot pump it) and is byte-for-byte the sync lane (parity + shared-op-cache tests green on Metal, image-conformance/tests/pipeline_async.rs); natively testable under pollster",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-06-12T18:23:53Z",
        "evidence_at": "2026-10-13T11:20:34.173Z",
        "headline": "All linked evidence is passing (8 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 8,
          "passed": 8,
          "skipped": 0
        }
      },
      "id": "image.pipeline.async-sink",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Engine A async sinks — to_buffer_async/to_encoder_async (the wasm32/WebGPU readback lane)"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "lazy DAG + ROI propagation + operation cache (hit/miss hooks) + to_buffer sink; e2e chain green (RawSource → linear → invert, cache-hit assert). Single-threaded decode bridge by design until the worker/SAB grant (I-02); shrink-on-load + to_pyramid/to_encoder are M1",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-13T11:20:41.413Z",
        "headline": "All linked evidence is passing (8 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 8,
          "passed": 8,
          "skipped": 0
        }
      },
      "id": "image.pipeline.engine-a-skeleton",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Engine A — demand-driven streaming pipeline skeleton"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "M4: manifest validates (single compute wasm artifact, built by scripts/build-wasm.sh into manifest/wasm/, size-gated); activate registers the adjustments panel + openImage/adjustSelected commands + the K-2 raster importer; capabilities now declare assets:[images] + rendering:[sceneLayer,hitTest]; loadBundle honesty tests green (9 vitest over the REAL wasm). PARTIAL: C-1 Stage-B GPU-texture/interactive stages (I-01) remain the platform gap; see image.editor.ingest for the live loop",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-13T11:19:54.654Z",
        "headline": "All linked evidence is passing (12 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 12,
          "passed": 12,
          "skipped": 0
        }
      },
      "id": "image.plugin.bundle-manifest",
      "importance": "normal",
      "since": "2026-06",
      "status": "experimental",
      "title": "paged.image bundle (media.paged.image) — manifest + activate + panel"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "the mutatable tier had NO wasm reach ('Paged never destroys a PSD' was a Rust-test-only property). Seven doors retain the parsed PsdFile behind a handle: open / layer_list (luni-first names, opacity, hidden flag, bounds) / set_layer_opacity / set_layer_name / remove_layer / save (carry-through preservation, zero-edit byte-identical) / close. Glue: the session opens the structural lane on 8BPS import, the panel gains a PSD-layers section (opacity %, rename, remove, hidden badge; honest note — the canvas shows the import-time flatten), and Export Center gains 'PSD (edited layers)' — the bundle's first exporter. Channel-pixel replacement stays engine-side (payload contract follow-up). Ships in glue canary.2",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "image.psd.editor-doors",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "PSD structural session — layer list/edit/save reach the editor"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "a layered open replaces Photoshop's own composite with ours, so it DECLINES rather than approximating, with a typed reason; PsdFile::layer_import_blockers lists every reason and the corpus ledger records them per file. IMPORTED (2026-10-05): layers and masks at their own bounds (ADR 464); groups and group masks; clipping; user and VECTOR masks (Photoshop's rasterization rules, 25 fixtures, 314 cached corpus renders); mask density/feather; fill opacity; RGB and CMYK (CMYK blended in RGB, Normal/Multiply, judged per file against the converted composite); smart objects as their stored render where the composite vouches for it; text layers in a gamma space (compose.normal_gamma); Curves/Levels/Exposure/Invert/Hue-Saturation adjustment layers; effects that draw nothing; Color Overlay (RGB or CMYK colour); artboards (members cut to the artboard, background beneath). Photoshop-written fixtures: layer-stacks, adjustment-layers, layer-effects, group-masks, vector-masks, mask-parameters, text-layers, cmyk-stacks; bounded and canvas-sized layers fold identically on all of them. DECLINED: drawn effects other than Color Overlay, Photo Filter/Brightness-Contrast/Gradient Map layers, group vector masks, feathered shape masks, CMYK files whose RGB blend shifts the tint, fill opacity on special modes, other CMYK blend modes, Lr16, over the 384 MiB budget. Corpus (157 PSDs): 76 compared (was 3 on 2026-10-04), all but one within 1.5 % of pixels more than 2 levels off; first refusals: smart objects 26 (mostly Multiply in CMYK), effects 16, CMYK blending 12, fill opacity 8.",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": "2026-10-12T07:33:33Z",
        "evidence_at": "2026-10-13T11:20:41.782Z",
        "headline": "All linked evidence is passing (29 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 29,
          "passed": 29,
          "skipped": 0
        }
      },
      "id": "image.psd.layer-import",
      "importance": "normal",
      "since": "2026-08",
      "status": "experimental",
      "title": "PSD import populates the layer stack (declines rather than approximates)"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "M2: set opacity/name, replace channel pixels, remove layer — dirty the touched subtree (Raw→None), re-encode canonically, leave everything else verbatim; reparse-stability proven (8 tests). PARTIAL: full editing-semantics (the companion spec) + op-log/undo journaling ride the SDK mutation surface",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:41:04.609Z",
        "headline": "All linked evidence is passing (10 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 10,
          "passed": 10,
          "skipped": 0
        }
      },
      "id": "image.psd.mutatable",
      "importance": "normal",
      "since": "2026-06",
      "status": "experimental",
      "title": "PSD mutatable — edits re-encode through the preservation writer"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "channel decode-on-demand (RAW/RLE/ZIP via miniz_oxide; verbatim bytes stay canonical) + Engine A flatten through compose.* kernels vs the scalar reference GREEN (worst f16 ULP 0 on the layered fixtures); psd-tools ecosystem lane opens 20 of our files. PARTIAL: clipping/masks/groups in flatten + a real-Adobe embedded-composite corpus are the M1.5/M2 confidence steps",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-13T11:20:41.848Z",
        "headline": "All linked evidence is passing (40 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 40,
          "passed": 40,
          "skipped": 0
        }
      },
      "id": "image.psd.rendered",
      "importance": "normal",
      "since": "2026-06",
      "status": "experimental",
      "title": "PSD rendered tier — channel decode + flatten render oracle"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "byte-identical zero-edit round-trip GREEN over the synthesized 11-fixture corpus + 64-case proptest (lazy-verbatim guard; re-encode lane proven); unknown blocks preserved opaquely; PSB green; per-block ladder at preserved in plugin-image/registry/psd-blocks.yaml. PARTIAL: no real-Adobe-PSD corpus yet (donated files + the psd-tools ecosystem lane are the M1 confidence step); ZIP parse-opaque until M1",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-13T11:20:41.919Z",
        "headline": "All linked evidence is passing (177 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 177,
          "passed": 177,
          "skipped": 0
        }
      },
      "id": "image.psd.roundtrip",
      "importance": "normal",
      "since": "2026-06",
      "status": "experimental",
      "title": "PSD/PSB structural parse + preservation-invariant writer"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "M2: CPU histogram (256-bin/channel) + min/max/mean over rgba16float tiles, 7 tests. Phase 6 added histogram_rgba8 -> RgbaLumaHistogram (panel-facing RGB + Rec.601-luma 256-bin readout, to_flat() [r,g,b,luma] 1024-u32 wasm row) feeding the levels/curves/WB panel and the auto-enhance readout (image.editor.curves/auto-enhance). PARTIAL: the compute-shader atomic reduction is the M3 perf path",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": "2026-06-13T09:02:34Z",
        "evidence_at": "2026-10-18T05:06:41.601Z",
        "headline": "No evidence linked yet; implementation last changed Jun 13",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 0,
          "skipped": 2
        }
      },
      "id": "image.reduce.statistics",
      "importance": "normal",
      "since": "2026-06",
      "status": "experimental",
      "title": "Reductions — histogram + min/max/mean statistics"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "M3: acquire_or_spill over-subscription path (evict LRU → Tier-1 heap → re-acquire; never panics/leaks) with upload→evict→download byte-identity proven on Metal. Tier-2 (OPFS swap) is now buildable against host.blob (Wave 3b, K-4 landed); the typed I-03 stub upgrade is scheduled M4",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-13T11:20:39.466Z",
        "headline": "All linked evidence is passing (3 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 3,
          "passed": 3,
          "skipped": 0
        }
      },
      "id": "image.residency.tier01",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Residency Tier 0/1 hardening under working-set pressure"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "M3: SelectionMask builder (constant/rect/from_fn/from_bytes → the r16float bytes execute_tile_once consumes) + end-to-end proof that a non-trivial mask limits a point op to the selected region (out=mix(backdrop,result,mask)), kernel-agnostic across math.linear+math.invert. The ABI carried it from day one; M3 surfaced it. (The editor tools-layer selection UX is the companion spec, out of scope.)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-13T11:20:39.496Z",
        "headline": "All linked evidence is passing (9 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 9,
          "passed": 9,
          "skipped": 0
        }
      },
      "id": "image.selection.mask",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Selection-mask plumbing — the §6.1 mask surfaced end-to-end"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "SelectionCoverage (u8 field + CombineMode) + SessionSelection (bound image, revision); analytic-AA rect, supersampled ellipse, scanline polygon, Chebyshev-distance wand (contiguous BFS / global), add/subtract/intersect via max/min/mul, gaussian feather on the mask (CPU — mask prep, not image processing). 12 selection_* wasm exports; 4 tools (y, shift+y, shift+l, shift+w) + selectAll/deselect/invert/feather commands + a panel Selection section",
          "status": "shipped"
        },
        {
          "component": "plugin.api",
          "note": "marching ants ride the existing overlay tool-preview channel; modifiers come from CanvasPointerEvent.modifiers — no new door needed",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:08:52.797Z",
        "headline": "All linked evidence is passing (4 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 5,
          "passed": 4,
          "skipped": 1
        }
      },
      "id": "image.selection.mask-tools",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Selection system — marquee/ellipse/lasso/wand over the mask ABI"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "Pipeline::set_selection binds the per-tile r16float coverage window on every apply dispatch (was the constant-one mask); the coverage hash XOR-folds into the op-cache key so a changed selection never serves stale tiles; the CPU curves stage honors the same coverage. 4 GPU tests incl. sync↔async lockstep",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:08:58.648Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 1,
          "skipped": 1
        }
      },
      "id": "image.selection.masked-pipeline",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Selection coverage threaded through the GPU adjust chain"
    },
    {
      "chapter": "image",
      "claims": [
        {
          "component": "plugin.image",
          "note": "Expand, contract, border and smooth (chamfer distance transform); wand and bucket tolerance and contiguity in the panel.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-13T11:19:54.566Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "image.selection.modify",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Select ▸ Modify and wand options"
    },
    {
      "chapter": "images-graphics",
      "claims": [
        {
          "component": "core.parser",
          "note": "ClippingPathSettings + clip anchors/subpaths (spread.rs, W1.21)",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "frame ∩ clip-path as nested PushClip; InvertPath + IncludeInsideEdges holes (images.rs build_image_clip_path, W1.21)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:57:41.903Z",
        "headline": "All linked evidence is passing (8 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 8,
          "passed": 8,
          "skipped": 0
        }
      },
      "id": "images-graphics.clipping-paths",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Image clipping paths (from image metadata)"
    },
    {
      "chapter": "images-graphics",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "images-graphics.cmyk-jpeg-icc",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "CMYK JPEG with embedded ICC profile"
    },
    {
      "chapter": "images-graphics",
      "claims": [
        {
          "component": "core.parser",
          "note": "the image-bearing arm is b\"Image\" | b\"EPS\" | b\"PDF\" | b\"ImportedPage\" | b\"Link\"; %!PS magic still sniffed. Inline EPS PostScript is deliberately NOT stashed as image bytes. No decode — there is no PostScript interpreter and that is not planned",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "core 134fb97: the missing-image placeholder is NO LONGER stamped over an inline EPS. Model side gains has_inline_eps on Rectangle/Oval/Polygon, mirroring has_inline_pdf and kept as its own flag rather than folded into a shared 'unrasterizable' boolean so a diagnostic can still name the format the document actually used; renderer side, report_image_resolution's parameter becomes has_inline_unrasterizable (has_inline_pdf || has_inline_eps) and the placeholder guards widen the same way. The reasoning is the Q-06 one applied unchanged: the artwork is PRESENT but unrasterizable, so InDesign's grey-X would assert 'the link is missing' over content that is in fact embedded — falling back to the frame's intrinsic fill is the honest render. This is what the impl-without-claim drift rule was pointing at on this row",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:01:16.871Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "images-graphics.eps-decode",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "EPS / PostScript placements (recognised + honestly rendered; no decode)"
    },
    {
      "chapter": "images-graphics",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "frameFittingType/frameFittingCrops + fitting reference point + auto-fit (W0.3; rectangle-only)",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "Frame fitting panel; auto-fit seamed",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:06.176Z",
        "headline": "All linked evidence is passing (6 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 6,
          "passed": 6,
          "skipped": 0
        }
      },
      "id": "images-graphics.frame-fitting",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Frame fitting (fill/fit/content + crops)"
    },
    {
      "chapter": "images-graphics",
      "claims": [
        {
          "component": "core.renderer",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:57:30.341Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "images-graphics.image-clipping",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Image clipped to frame path"
    },
    {
      "chapter": "images-graphics",
      "claims": [
        {
          "component": "core.renderer",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:57:31.255Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "images-graphics.missing-image-placeholder",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Missing-image placeholder (grey + diagonal X) + diagnostic"
    },
    {
      "chapter": "images-graphics",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "JPEG/PNG/GIF/TIFF/WebP decode. A placed PDF (or Illustrator .ai) is drawn from its own drawing operations (core ADR 154, hayro-interpret): paths, clips, groups, images and glyph outlines, at its ItemTransform; fixture placed-pdf mean 0.111. Limits: colours via the interpreter's RGB (not the document CMYK profile), even-odd filled non-zero, patterns/shadings mid-grey, soft masks ignored, first page only",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "Links panel rows; status badges/PPI pending (gaps 2–3)",
          "status": "shipped"
        },
        {
          "component": "editor.script",
          "note": "paged.links()",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-15T07:45:51Z",
        "evidence_at": "2026-10-18T07:57:07.379Z",
        "headline": "All linked evidence is passing (18 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 18,
          "passed": 18,
          "skipped": 0
        }
      },
      "id": "images-graphics.placed-images",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Placed images (URI links via AssetResolver + inline base64)"
    },
    {
      "chapter": "interactive-dynamic",
      "claims": [
        {
          "component": "core.parser",
          "note": "no <AnimationSetting> consumer; deep-deferred (deep-defer-records.md §interactive-dynamic)",
          "status": "planned"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "interactive-dynamic.animation",
      "importance": "normal",
      "since": null,
      "status": "planned",
      "title": "Animation + timing settings"
    },
    {
      "chapter": "interactive-dynamic",
      "claims": [
        {
          "component": "core.parser",
          "note": "deep-deferred (deep-defer-records.md §interactive-dynamic)",
          "status": "planned"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "interactive-dynamic.behaviors",
      "importance": "normal",
      "since": null,
      "status": "planned",
      "title": "Button behaviors (goto-page/state, show-hide, forms, zoom)"
    },
    {
      "chapter": "interactive-dynamic",
      "claims": [
        {
          "component": "core.parser",
          "note": "a <Button> or <MultiStateObject> is read as a group of its ACTIVE state, placed by its ItemTransform, as InDesign lays it out (asked of InDesign 20.0.1; inactive states are skipped). InDesign's print PDF leaves interactive elements out. A save keeps every interactive subtree byte-identical (plugin-publish #54, core v0.73.4; asked of InDesign: interactive-probe). OPEN: states, behaviors and PDF interactivity are not modelled: plan D-05..D-06",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:01:16.838Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "interactive-dynamic.buttons",
      "importance": "normal",
      "since": null,
      "status": "planned",
      "title": "Buttons (states Up/RollOver/Down)"
    },
    {
      "chapter": "interactive-dynamic",
      "claims": [
        {
          "component": "core.parser",
          "note": "no <Sound>/<Movie> consumer; deep-deferred (deep-defer-records.md §interactive-dynamic)",
          "status": "planned"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "interactive-dynamic.media",
      "importance": "normal",
      "since": null,
      "status": "planned",
      "title": "Sound & movie elements"
    },
    {
      "chapter": "layers",
      "claims": [
        {
          "component": "core.mutation",
          "note": "PropertyPath::ItemLayer (protocol 62), on TextFrame|Rectangle|Oval|Polygon|GraphicLine; the empty string clears back to the default layer. Before 62 layer membership was a BIRTH-only property (gap C-35) — expressible in a paged-gen fixture's extra_attrs and nowhere on the wire, because apply_move_node accepts only NodeId::Spread as a parent.",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "note": "setProperty path \"itemLayer\"; structural invalidation (the renderer sorts by layer first)",
          "status": "shipped"
        },
        {
          "component": "editor.script",
          "note": "SHIPPED as of canvas-wasm 0.63.0 (2026-09-10), and the history is worth keeping because the row was wrong in both directions before it. CLAIMED shipped on 2026-08-22 when it was not: protocol 62 added the path to the introspect catalog (so the script host PARSED it) and to the mutate layer (so the wire APPLIED it), and never taught `js_value_to_wire` that it is string-valued — the conversion between parse and apply had no arm for it, so `paged.set(id, \"itemLayer\", ...)` returned false and wrote nothing. Found because state's coverage gate refused the claim for having no linked test: the claim was written first and the evidence disproved it. Then the row sat at `planned` while the FIX existed in core (a806321, 2026-08-22 16:35) but not in any published package — the tag had gone out that morning at 10:07. v0.63.0 carries it. Evidence: apps/canvas/tests/e2e/item-layer.spec.ts AC-ITEMLAYER-3, whose assertions are flipped and whose `@feat:` tag is back.",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "SHIPPED. The affordance is `paged.layers.assignSelection`, a per-layer row action — \"move the selection here\", which is the verb a designer reaches for; item ROWS under each layer would mean synthesising a row set in a panel that is a schema list bound to the `layers` collection. AC-ITEMLAYER-4 in `e2e/item-layer.spec.ts` clicks that action and asserts the item's layer changes. Flipped 2026-09-09: the note said \"the op exists, the gesture does not\" and the gesture had since been built, so the first editor ingest after 2026-08-22 landed green evidence on a `planned` stage and the join reported it as drift. The test was right and the row was stale.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-09-09T14:12:50Z",
        "evidence_at": "2026-10-18T07:47:17.166Z",
        "headline": "All linked evidence is passing (6 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 6,
          "passed": 6,
          "skipped": 0
        }
      },
      "id": "layers.item-assignment",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Assign an existing page item to a layer"
    },
    {
      "chapter": "layers",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "visibility + z respected; printable honoured in exports",
          "status": "shipped"
        },
        {
          "component": "editor.canvas",
          "note": "layer flags drive canvas rendering + hit gating",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-06-18T11:50:06Z",
        "evidence_at": "2026-10-18T06:37:31.119Z",
        "headline": "All linked evidence is passing (40 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 40,
          "passed": 40,
          "skipped": 0
        }
      },
      "id": "layers.model",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Layer model (visibility, lock, printable, z-order)"
    },
    {
      "chapter": "layers",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "note": "LayerSummary.parentId, PROTOCOL_VERSION 60 — AN EXPOSURE GAP, NOT A MODEL GAP, and the distinction is the record. ADR 023 phase A reported a nested Layers panel as blocked and classified it as core work: a parentage field or a new read. It was neither. paged_model::Layer has carried parent_id ALL ALONG, and the renderer already resolves visibility and lock THROUGH ancestors with it ('an item on a visible child layer inside a hidden parent group is still hidden'). The wire summary simply listed six fields and not that one, so every consumer saw a FLAT list and no panel could render a tree. One field. Worth stating because the two diagnoses have completely different costs and calling the second the first invents machinery that already exists. The test asserts the summaries against the MODEL rather than against literals, so the invariant under guard is 'the mapping does not LOSE what the model holds' — a future refactor re-dropping the field fails there instead of silently flattening a panel. Stated on the field itself: visible/locked on the summary remain the layer's OWN flags, not the resolved ones, so a panel showing effective state must walk the chain. PUBLISH STATE CORRECTED 2026-08-09 by audit: `npm view @paged-media/canvas-wasm dist-tags` returns latest 0.61.1, so every protocol up to 61 IS published and reachable without sync-wasm.sh. Was: UNPUBLISHED - npm canvas-wasm was 0.51.0; protocol 60 reaches the editor only through a local sync-wasm.sh override",
          "status": "shipped"
        },
        {
          "component": "editor.shell",
          "note": "consumed by the schema v1.2 tree tier, whose rows stay FLAT with a parent pointer precisely because that is the shape the wire now hands over (editor-shell.schema-panel-list-tier)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-12T17:16:47Z",
        "evidence_at": "2026-10-18T07:58:51.788Z",
        "headline": "All linked evidence is passing (4 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 4,
          "passed": 4,
          "skipped": 0
        }
      },
      "id": "layers.nested-groups",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Nested layer groups (folders, inherited hide/lock)"
    },
    {
      "chapter": "layers",
      "claims": [
        {
          "component": "core.mutation",
          "note": "layerInsert/Remove/Move/SetName/SetVisible/SetLocked/SetPrintable",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "Layers panel full CRUD incl. drag reorder",
          "status": "shipped"
        },
        {
          "component": "editor.script",
          "note": "paged.layers()",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-09-09T09:42:19Z",
        "evidence_at": "2026-10-18T07:52:05.526Z",
        "headline": "All linked evidence is passing (21 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 21,
          "passed": 21,
          "skipped": 0
        }
      },
      "id": "layers.ops",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Layer ops (insert/remove/move/rename + flags)"
    },
    {
      "chapter": "layers",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "ARRANGE WAS A REAL ENGINE GAP, not merely an unexposed one, and that is the finding worth keeping. Operation::MoveNode LOOKS like the missing primitive and is not one — verified in code, not inferred: its `position` is a bare usize with no z semantics (contrast InsertNode, which carries a separate z_slot), it indexes the KIND vec (Spread::rectangles, ...) while the renderer, hit-tester and scene tree all walk Spread::frames_in_order whenever it is non-empty — and MoveNode's forward path re-registers the node with z_slot: None, i.e. ON TOP, whatever `position` said. On a PARSED document it cannot express a z change at all; on a synthesised one it can only permute within a single kind. Its existing test asserted only the kind vec (never frames_in_order), which is why this went unnoticed. NEW: Operation::ReorderNode{node, target} with FIVE targets — ZOrderTarget = front | back | forward | backward | {index} (0 = backmost); forward on an already-frontmost item is a successful NO-OP, which is Illustrator's behaviour. RELATIVE AND ABSOLUTE DELIBERATELY BOTH — the four verbs are what a UI or a plugin can express (read-modify-write against the order the engine holds at apply time, so no stale index races a concurrent insert), while Index makes the INVERSE EXACT (the apply layer echoes back the slot the node came from, so one undo restores the previous order bytewise) and is the honest shape for a layers-panel drag. An out-of-range Index is REJECTED, never clamped: it means the caller's model is stale and smoothing that over would hide it. NO REPARENTING — MoveNode's new_parent stays off the wire and the op takes no parent at all: it DERIVES the sibling list from where the node already is (the spread's frames_in_order, materialised first via the render-neutral ensure_frames_in_order so Arrange works on a File-New document; or that Group::members; or that B-18 nested_children entry). So containment is STRUCTURAL rather than a rule kept in sync with PasteInto's guards. Nodes with no stacking position (Story / Table / Spread / Layer) and C-28 mask artwork are rejected with an error naming the fix. HONEST LIMIT, pinned by a test: Arrange is WITHIN a layer — the renderer sorts frames_in_order by ItemLayer before painting (Q-10), so bring-to-front cannot lift an item above one on a higher layer; that is InDesign's model, where crossing layers is a property write, not an Arrange. 24 new tests (paged-mutate 17, paged-canvas 7), every order assertion reading the renderer's emitted command stream so a reorder that permuted a vec nothing walks would fail",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "note": "reorderElement{elementId, to} at PROTOCOL_VERSION 59. Create-then-arrange in ONE undo step is a batch of [insert..., bindCreated{handle}, reorderElement{'$h:<handle>','back'}] — the bindCreated child is load-bearing, because the bare v34 $created sentinel is understood by setPluginMetadata/setElementProperty only, while the generic C-15 handle resolver is armed by a bindCreated child being present. PUBLISH STATE CORRECTED 2026-08-09 by audit: `npm view @paged-media/canvas-wasm dist-tags` returns latest 0.61.1, so every protocol up to 61 IS published and reachable without sync-wasm.sh. Was: PUBLISH STATE: PUBLISHED SINCE (canvas-wasm latest 0.61.1, verified 2026-08-09). Was: UNPUBLISHED - npm canvas-wasm was 0.51.0 and the editor reached protocol 60 only through a local sync-wasm.sh file: override, so the editor's live-probed capability table (94 ops) does not yet carry reorderElement; the claim rests on core's crate reality and its 24 tests",
          "status": "shipped"
        },
        {
          "component": "plugin.publish",
          "note": "Z-ORDER SURVIVES SAVE-BACK across all three sibling lists — Spread::frames_in_order, Group::members and Spread::nested_children — in a new reorder.rs. This is what makes the v59 op survive an IDML round-trip: the writer is a byte-preserving splice that re-emits existing source elements untouched, so a reshuffled document used to be deliberately left alone ('z-reorder save-back is a different lane'). Core's own test a_reorder_survives_paged_and_is_lost_on_idml_export then FAILED — which is exactly the signal it existed to give — and was renamed and flipped to pin the fix",
          "status": "shipped"
        },
        {
          "component": "editor.shell",
          "note": "the four Arrange verbs reach the user through paged.object.* (editor-tools.object-commands); before that, bringToFront/sendToBack existed nowhere in the app",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:10:31.502Z",
        "headline": "All linked evidence is passing (19 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 19,
          "passed": 19,
          "skipped": 0
        }
      },
      "id": "layers.z-ordering",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Cross-shape z-sort (layer + XML order) + Arrange (reorderElement)"
    },
    {
      "chapter": "layout-model",
      "claims": [
        {
          "component": "core.renderer",
          "note": "Measured against InDesign 2025 (paged-gen `start-paragraph`, 10 cases, 40 pages in the fidelity gate): a paragraph that already opens the unit stays; odd/even also enforce parity, even at a story start; with no unit left the story oversets and a growing chain adds pages. core 6b85b5d.",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "paragraphStartParagraph at paragraph and paragraph-style level (protocol 64).",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "Paragraph panel Keep options \"Start paragraph\" (editor a2b3e91).",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:58:35.911Z",
        "headline": "All linked evidence is passing (4 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 4,
          "passed": 4,
          "skipped": 0
        }
      },
      "id": "layout-model.break-before",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Break before (StartParagraph — next column, frame, page, odd/even page)"
    },
    {
      "chapter": "layout-model",
      "claims": [
        {
          "component": "core.parser",
          "note": "idml-import reads the ColumnRule* attributes of <TextFramePreference> into TextFrame::column_rule when ColumnRuleOverride=\"true\" (plugin-publish crates/idml-import/src/spread.rs `column_rule_attrs`); the export keeps an existing frame's attributes. An object style's column rules are not read.",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "core ADR 155: one rule per gutter at the gutter's centre plus ColumnRuleOffset, from the text area's top plus the top inset to the deepest line's baseline plus its face's descent minus the bottom inset, butt caps, dashes fitted like any open path. Asked of InDesign 20.0.1 (sample column-rules, nine cases): every rule's extent to the pixel, page mean 0.521 -> 0.331. Not measured under vertical justification; span and split regions draw no rule.",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "no property path writes the rules yet; a frame the engine mints writes none to IDML.",
          "status": "planned"
        }
      ],
      "health": {
        "changed_at": "2026-10-15T07:45:51Z",
        "evidence_at": "2026-10-18T07:57:51.986Z",
        "headline": "All linked evidence is passing (3 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 3,
          "passed": 3,
          "skipped": 0
        }
      },
      "id": "layout-model.column-rules",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Column rules between a text frame's columns"
    },
    {
      "chapter": "layout-model",
      "claims": [
        {
          "component": "core.canvas-wasm",
          "note": "NewBlankDocument (File ▸ New, CanvasClient.newBlankDocument) takes a page size and a font only: no facing-pages setting, and the new document is one single-page spread.",
          "status": "planned"
        },
        {
          "component": "core.mutation",
          "note": "InsertPage adds a single-page spread and RemovePage removes only single-page spreads; no operation puts a page into a spread beside another or sets the document's FacingPages.",
          "status": "planned"
        },
        {
          "component": "editor.panel",
          "note": "File ▸ New offers no facing pages, and the Pages and Spreads panels cannot pair pages into spreads.",
          "status": "planned"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "layout-model.facing-pages-authoring",
      "importance": "normal",
      "since": null,
      "status": "planned",
      "title": "Facing pages in documents made in the editor"
    },
    {
      "chapter": "layout-model",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "textFrame.firstBaseline enum path (W0.3)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:06.212Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "layout-model.first-baseline-offset",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "FirstBaselineOffset modes"
    },
    {
      "chapter": "layout-model",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "SHIPPED — both named sub-gaps closed by W1.7 Phase B. `compute_auto_sized_bounds` (paged-renderer pipeline/text_frame.rs:644) honours AutoSizingType, ReferencePoint and both Minimum floors; the frame paints at the grown extent (build_engine.rs:361,1012); and neighbours WRAP around the grown box (pipeline/geom.rs:419), which was the second half. Was: heuristic estimate; box stretch + wrap pending",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "textFrame.autoSizing enum path (W0.3)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:06.212Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "layout-model.frame-autosizing",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "TextFrame AutoSizing (width/height growth)"
    },
    {
      "chapter": "layout-model",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "surfaced on SpreadSummary.guides for overlay (W3.A0); never present in the exported display list (W1.8, 3 tests)",
          "status": "partial"
        },
        {
          "component": "core.mutation",
          "note": "Guide CRUD with positional ids Guide/<spread>/<idx> (W0.5)",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "note": "SpreadSummary.guides read surface (W3.A0)",
          "status": "shipped"
        },
        {
          "component": "editor.canvas",
          "note": "ruler-guides overlay renders them",
          "status": "shipped"
        },
        {
          "component": "editor.gesture",
          "status": "planned"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:58:06.034Z",
        "headline": "All linked evidence is passing (8 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 8,
          "passed": 8,
          "skipped": 0
        }
      },
      "id": "layout-model.guides",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Ruler guides (parse, snap targets, editing)"
    },
    {
      "chapter": "layout-model",
      "claims": [
        {
          "component": "core.renderer",
          "note": "Measured against InDesign 2025 (paged-gen `keeps`, 8 cases, in the fidelity gate): keep-with-next moves only the LAST line; KeepLinesTogether switches keeps on and All/First/Last decide which lines. Forced breaks decided from real placement in the body-story fixpoint (pipeline/keeps.rs). core 4ef7b21.",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "paragraphKeepWithNext / KeepLinesTogether (W0.2) plus KeepAllLinesTogether / KeepFirstLines / KeepLastLines at paragraph and paragraph-style level (protocol 64, core v0.64.0).",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "Paragraph panel \"Keep options\" section (editor a2b3e91); paragraph-flow-ops.spec.ts drives each control and checks layout + undo.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:58:14.585Z",
        "headline": "All linked evidence is passing (4 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 4,
          "passed": 4,
          "skipped": 0
        }
      },
      "id": "layout-model.keep-options",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Keep options (keep with next, keep lines together, first/last lines)"
    },
    {
      "chapter": "layout-model",
      "claims": [
        {
          "component": "core.renderer",
          "note": "FlowGrowRule on Story; generated pages are derived (never saved), stable ids, margin-box frame per InDesign 2025 Smart Text Reflow (reflow fixture matches InDesign's reflowed PDF). The chain-length guard no longer truncates growth (ADR 027 step 2). Known: lowering max_pages after growth is being fixed.",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "setFlowGrowRule {storyId, grow, maxPages, copyFrameOptions} (protocol 64).",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "Stories panel \"Add pages while overset\" + \"Max added pages\" (editor a2b3e91); the rule has no wire READ yet, so the control shows the session's last write.",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": "2026-10-11T06:35:17Z",
        "evidence_at": "2026-10-18T07:58:23.587Z",
        "headline": "All linked evidence is passing (8 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 8,
          "passed": 8,
          "skipped": 0
        }
      },
      "id": "layout-model.page-growth",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Page growth — a story's frame chain adds pages while it oversets (Smart Text Reflow)"
    },
    {
      "chapter": "layout-model",
      "claims": [
        {
          "component": "core.renderer",
          "note": "Measured against InDesign 2025: span-columns (17 cases, 256 lines within 0.13 pt) and split-boundaries (24 cases, 306 lines within 0.12 pt); a new block starts where count or a gutter changes, each balances by line count. core a3e5a0c + d4311c7 (its message is wrong; see its git note).",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "Six span/split property paths at paragraph and paragraph-style level (protocol 64).",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "Paragraph panel \"Span columns\" section (editor a2b3e91).",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:58:32.743Z",
        "headline": "All linked evidence is passing (7 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 7,
          "passed": 7,
          "skipped": 0
        }
      },
      "id": "layout-model.span-split-columns",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Span and split columns (SpanColumnType)"
    },
    {
      "chapter": "layout-model",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "translation + rotation/scale (W1.9 spread_linear_transform; core tests/spread_transform.rs)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:58:35.881Z",
        "headline": "All linked evidence is passing (7 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 7,
          "passed": 7,
          "skipped": 0
        }
      },
      "id": "layout-model.spread-item-transform",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Spread / MasterSpread ItemTransform"
    },
    {
      "chapter": "layout-model",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "insertPage/deletePage/resizePage + DuplicatePage (fresh u<hex> ids, W0.5) (single-page spreads v1)",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "Pages list new/delete; navigator filmstrip",
          "status": "shipped"
        },
        {
          "component": "editor.gesture",
          "note": "page tool: click select, Alt-insert, drag resize",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:06.339Z",
        "headline": "All linked evidence is passing (13 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 13,
          "passed": 13,
          "skipped": 0
        }
      },
      "id": "layout-model.spreads-pages",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Spreads & pages (geometry, stacking, page items)"
    },
    {
      "chapter": "layout-model",
      "claims": [
        {
          "component": "core.parser",
          "note": "idml-import reads all three attributes onto the frame (plugin-publish crates/idml-import/src/spread.rs); the exporter preserves them on round-trip.",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "textFrameColumnCount / textFrameColumnGutter / textFrameColumnBalance property paths (W0.3).",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "SHIPPED 2026-09-10 (the W0.3 \"later wave\"). Before it, all three paths were accepted, read back, round-tripped and ADVERTISED in settablePaths while changing nothing on the page: one column and two rendered byte-identically in both the raster and PDF lanes. Columns are now expanded into the frame chain the emitter already walks (paged-renderer build_engine.rs `expand_column_chain`), with the column arithmetic living once in `paged_flow::RegionGeometry::column_boxes`. Balance follows InDesign's measured rule — it levels the LINE COUNT: 44 single-line paragraphs come back 22/22 balanced against 35/9 unbalanced. Verified against InDesign's own export on four new corpus pages (layout 7-10) plus the pre-existing 2col page, whose mean deltaE halved (0.480 -> 0.218).",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "The Text Frame Options panel has bound all three paths as LIVE scrub controls since W2.3 (apps/canvas/src/panels/ text-frame-options.composition.ts). They wrote and read back correctly the whole time and moved nothing on the canvas — a dead affordance behind a live control — until the renderer stage above landed. No editor change was needed; the same paged-renderer backs the canvas.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-09-10T10:15:38Z",
        "evidence_at": "2026-10-18T07:51:54.791Z",
        "headline": "All linked evidence is passing (7 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 8,
          "passed": 7,
          "skipped": 1
        }
      },
      "id": "layout-model.text-columns",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Text-frame columns (count, gutter, vertical balance)"
    },
    {
      "chapter": "layout-model",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "LinkFrames (to-must-be-empty + cycle rejection) / UnlinkFrames + InsertTextFrame (W0.5/W2.0-found). Protocol 62 adds UnlinkFrames.restore_target, which hands the unlinked frame's content back to a named story instead of stranding it. Protocol 62 also FIXES LinkFrames, which had been reporting success while threading nothing: the serde-skipped text_frame_index was never rebuilt after a mutation, so a link against a frame created in the same session resolved against a stale index (found by the render-effect sweep; the fix rebuilds the indexes per top-level apply, 351us against a 2.66s mutation)",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "note": "wire read surface: nextTextFrame/previousTextFrame reflect LinkFrames and are write-rejected (paged-canvas w3a0_read_surface.rs next_and_previous_text_frame_entries_reflect_link_frames / next_text_frame_is_read_only_via_set_property); bad-target LinkFrames errors serialize (wire_robustness.rs)",
          "status": "shipped"
        },
        {
          "component": "editor.gesture",
          "note": "the TH-* port-threading gesture DID land (W2.9) — this note said 'no port-threading gesture yet' long after shell/overlays/threading-ports.tsx + cockpit/canvas-frame/ThreadingController.tsx + state/threading-context.tsx shipped and gesture-threading.spec.ts went green (TH-PORTS/TH-01/TH-02/TH-03, each @feat-tagged onto this row). Kept PARTIAL, not shipped, to stay in step with the owning row editor-tools.text.threading-ports: TH-04's overset badge is still a fixme awaiting an overset fixture",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:06.382Z",
        "headline": "All linked evidence is passing (15 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 15,
          "passed": 15,
          "skipped": 0
        }
      },
      "id": "layout-model.text-frame-chain",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Text-frame threading chain (NextTextFrame story distribution)"
    },
    {
      "chapter": "layout-model",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "textFrame.verticalJustification enum path (W0.3)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:06.212Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "layout-model.vertical-justification",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Frame vertical justification (top/center/bottom/justify)"
    },
    {
      "chapter": "master-spreads-overrides",
      "claims": [
        {
          "component": "core.mutation",
          "note": "ApplyMasterToPage (W0.5)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:06.318Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "master-spreads-overrides.apply-master-op",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Apply master to page (mutation)"
    },
    {
      "chapter": "master-spreads-overrides",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "Master pages list; the Apply trail button remains an inert seam — the wire op applyMasterToPage IS live + probe-verified supported, the panel just never dispatches it (the panel-side wiring is the remaining half of gap 25)",
          "status": "shipped"
        },
        {
          "component": "plugin.publish",
          "note": "MASTER SPREADS ARE NOW WRITTEN (2026-08-05). Until this campaign the IDML writer iterated doc.spreads ONLY — master spreads were parsed and then silently DROPPED, so an edit to a master page simply VANISHED on save. The master loop is sorted by src so a save cannot depend on HashMap order; 316 corpus master entries now rewrite byte-identically and two integration tests assert that a master page-item edit and a master item-transform edit each survive save-and-reopen",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:58:19.793Z",
        "headline": "All linked evidence is passing (5 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 5,
          "passed": 5,
          "skipped": 0
        }
      },
      "id": "master-spreads-overrides.master-stamping",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Master spread stamping (applied master, MasterPageTransform)"
    },
    {
      "chapter": "master-spreads-overrides",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "Q-14 override suppression verified + tested; no double-paint (W1.8)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:58:19.762Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "master-spreads-overrides.override-resolution",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Local override suppression of master items"
    },
    {
      "chapter": "master-spreads-overrides",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:58:19.793Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "master-spreads-overrides.show-master-items",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "ShowMasterItems suppression"
    },
    {
      "chapter": "package-anatomy",
      "claims": [
        {
          "component": "core.canvas-wasm",
          "note": "paged-composition (document.pgd model) + DOCUMENT_PGD_PATH; CanvasModel:: {composition, refresh_composition_part, read_composition_part} over the v51 parts door (S7); composition_part test (container round-trip + render-equivalence).",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "paged-composition-render renders a composition to pixels (PageRelative, single page); other position kinds, multi-page, cross-region fragmentation are later slices, and nothing drives the live render from document.pgd yet.",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:44:46.639Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "package-anatomy.composition-part",
      "importance": "normal",
      "since": "2026-07",
      "status": "stable",
      "title": "Composition part (paged/core/composition/document.pgd — arrangement)"
    },
    {
      "chapter": "package-anatomy",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "status": "shipped"
        },
        {
          "component": "core.sdk",
          "status": "shipped"
        },
        {
          "component": "editor.canvas",
          "note": "worker load path",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:58:59.67Z",
        "headline": "All linked evidence is passing (4 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 4,
          "passed": 4,
          "skipped": 0
        }
      },
      "id": "package-anatomy.core-import",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Full package IMPORT — IDML adapter (designmap, spreads, stories, resources)"
    },
    {
      "chapter": "package-anatomy",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:01:17.278Z",
        "headline": "All linked evidence is passing (5 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 5,
          "passed": 5,
          "skipped": 0
        }
      },
      "id": "package-anatomy.graphic-part",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Graphic part (colors, swatches, gradients, inks, stroke styles)"
    },
    {
      "chapter": "package-anatomy",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:03.735Z",
        "headline": "All linked evidence is passing (4 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 4,
          "passed": 4,
          "skipped": 0
        }
      },
      "id": "package-anatomy.inline-image-contents",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Inline base64 image bytes (<Image><Properties><Contents>)"
    },
    {
      "chapter": "package-anatomy",
      "claims": [
        {
          "component": "core.canvas-wasm",
          "note": "paged-store codec (to_bytes/from_bytes + DOCUMENT_PGM_PATH); CanvasModel::{composition, refresh_model_part, read_model_part} + the load-time sniff + auto-embed on export_paged; Document::rebuild_indexes rebuilds the derived caches. paged-store + paged-canvas model_part/load_sniff nextest; corpus-fidelity gated (byte-identical).",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "the model reconstructed from native bytes renders to an identical display-list digest — raw source bytes are render-dead (paged-mutate already renders with an empty Container).",
          "status": "shipped"
        },
        {
          "component": "editor.canvas",
          "note": "editor-side native save/open surface rides a future publish (the editor consumes published wasm); publish-gated, like the .paged container row.",
          "status": "planned"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:47:21.388Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "package-anatomy.native-model-part",
      "importance": "normal",
      "since": "2026-07",
      "status": "stable",
      "title": "Native model part (paged/core/model/document.pgm — the self-owning document)"
    },
    {
      "chapter": "package-anatomy",
      "claims": [
        {
          "component": "core.parser",
          "note": "paged-parse Container retains EVERY zip entry (entries: BTreeMap) — foreign / plugin parts are never dropped on parse, the round-trip foundation.",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "note": "paged-write::write_paged emits a valid IDML package + the model-held paged/ parts + a MERGE-rewritten manifest.json (unknown fields AND other plugins' metadata preserved → forward-compatible with any number of third-party plugins carrying data) + idml_parts_hash (the §3.1 data-loss guard, excludes paged/ + tracks IDML-part changes). CanvasModel parts overlay (set/get/list_paged_part, namespace-gated) + export_paged. 5 nextest (foreign-part survival, append+manifest, hash, multi-tenant, model round-trip).",
          "status": "shipped"
        },
        {
          "component": "editor.canvas",
          "note": ".paged save/open surface + the near-free Export-for-InDesign — rides the v51 publish (the editor consumes published wasm); publish-gated.",
          "status": "planned"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:47:04.52Z",
        "headline": "All linked evidence is passing (3 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 3,
          "passed": 3,
          "skipped": 0
        }
      },
      "id": "package-anatomy.paged-container",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "The .paged container (ZIP-as-valid-IDML + plugin namespaces + manifest.json)"
    },
    {
      "chapter": "package-anatomy",
      "claims": [
        {
          "component": "core.canvas-wasm",
          "note": "v51 wire ops WritePagedPart / ReadPagedPart / ListPagedParts / ExportPaged → the CanvasModel container methods; PROTOCOL_VERSION 50→51 (additive, handshake catches a stale pair); wire serde round-trip test.",
          "status": "shipped"
        },
        {
          "component": "plugin.api",
          "note": "contributes.partTypes[] declaration SHIPPED ({type, role: spec|source|derived, format, linkable?} — plugin-api type + manifest.schema.json + plugin-cli validation). The host.parts PartsSurface type + the plugin-sdk host-impl runtime (via the generic client.send) are PUBLISH-GATE LIFTED — corrected 2026-08-09 by audit. canvas-wasm is published at 0.61.1 (npm dist-tags, verified), far past v51, and `PartsSurface` is live in plugin-api host.ts with parts.spec.ts asserting that supports('storage.parts@1') is true. The editor routes readPagedPart/listPagedParts from plugin-native-document.ts. Was: PUBLISH-GATED: the vendored wire.d.ts needs the v51 variants (publish v51 or build under sync-wasm.sh) and the no-speculative-surface rule keeps the type with its runtime.",
          "status": "partial"
        },
        {
          "component": "editor.canvas",
          "note": "editor packages/client + apps/canvas worker routing + the editor's PROTOCOL_VERSION pin → 51; publish-gated on v51, ships paired.",
          "status": "planned"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T06:37:31.123Z",
        "headline": "All linked evidence is passing (5 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 5,
          "passed": 5,
          "skipped": 0
        }
      },
      "id": "package-anatomy.paged-parts-door",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "host.parts plugin persistence door (namespaced container parts that travel with the file)"
    },
    {
      "chapter": "package-anatomy",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:01:17.08Z",
        "headline": "All linked evidence is passing (6 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 6,
          "passed": 6,
          "skipped": 0
        }
      },
      "id": "package-anatomy.story-parts",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Story parts (ParagraphStyleRange / CharacterStyleRange trees)"
    },
    {
      "chapter": "package-anatomy",
      "claims": [
        {
          "component": "core.parser",
          "note": "re-verified 2026-08-17: no BackingStory/XMLElement consumer in core (only paged-gen builders + the File▸New stub part in paged-canvas blank.rs); conformance greens on this cell = documents carrying an XML folder parse tolerantly, not a modeled backing store — do not promote on them",
          "status": "planned"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "package-anatomy.tagged-xml-backing-store",
      "importance": "normal",
      "since": null,
      "status": "planned",
      "title": "Tagged XML backing store (XMLElement/XMLStory/Mapping)"
    },
    {
      "chapter": "parser-internals",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:01:17.313Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "parser-internals.streaming-xml",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Streaming XML parse (quick_xml event-driven)"
    },
    {
      "chapter": "parser-internals",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:01:17.313Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "parser-internals.zip-entry-streaming",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "ZIP entry streaming (per-part decompression)"
    },
    {
      "chapter": "plugin-platform",
      "claims": [
        {
          "component": "plugin.api",
          "note": "cockpit scripts/object-matrix.mjs generates object-model/matrix.json + matrix.md: one row per core (address kind x property path) from the catalog propertySchemas (ADR 132), per core wire op (classified in object-model/core-ops.json, fails on an unclassified op), and per plugin (kind x path) from contributes.objectModel; one cell per surface (ui, binding, boa, node-cli, plugin-api, rust-cli). Only `named` counts as reached. Sources are read at the git refs in object-model/refs.json (one cat-file batch per repo; no checkout touched); since the protocol-71 release (2026-10-11) every source reads origin/main; the committed matrix was generated at the revisions matrix.md lists (core 7cad959, plugin-sdk 12f8fc3, editor 2158984, plugin-draw a625f82, plugin-sheets e85ead7). Against origin/main on 2026-10-15 `--check` fails, and the failures are real gaps, not a stale file. (1) 26 core catalog rows were added since 7cad959: 15 by core 0.71.8 (frame fill/stroke/content opacity and blend, drop-shadow details, group isolate/knockout, document global light), the cell-style fill tint in 0.71.11, and ten paragraph/character rows on core main after 0.71.11. 24 of them have no editor PropertyField (ui), and the 11 newest are not in the SDK's vendored catalog (canvas-wasm 0.71.8), so host.objects, the Node CLI and bindings cannot write them. (2) paged.web webFrame html/css/vars/flow have no ui property row by decision (2026-10-15): web's panel pass (web canary.13) moved them to the Web source panel, the one editor for a frame's source, which saves through the bundle's own metadata write; scripts, the Node CLI and bindings still reach them through host.objects. object-model/exceptions.json records the four ui cells as exceptions, so the check reads them as deliberate rather than as gaps. The pin stays as generated at the revisions above until the core gaps are closed or accepted in review (--accept-growth). The shrink-only pin object-model/unreached.baseline.json fails `--check` on a new unreached row, a loose pin, or a vanished row without removed.json; object-model/exceptions.json holds reasoned per-row, per-surface decisions the owning source cannot state, and `--check` fails when one of them no longer names a gap (its row vanished, or its cell is reached or n/a). Generated 2026-10-06: 902 rows, 1813 unreached cells pinned. Final round 2026-10-07 (protocol-71 SDK seam: core rows reach host.objects through the vendored catalog + address grammar, creates/deletes from planCreate/planCoreDelete; SchemaSections + style-target files + the More section in ui; plugin schema-panel property rows in ui; Boa reaches plugin rows through the host bridge, answered by the editor and the headless host; Rust CLI plugin rows n/a): 548 unreached cells pinned.",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "platform.object-model.parity",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Object-model parity matrix (every property x every surface, shrink-only gate)"
    },
    {
      "chapter": "plugin-doc",
      "claims": [
        {
          "component": "plugin.doc",
          "note": "each text step (insertText + its style/anchor ranges) now tries ONE {op:batch} mutation — a single undo step per paragraph instead of an op-per-write. Probe-by-trying with a cached verdict: core PR #46 let Batch carry text ops WITHOUT a protocol bump, and a whole-batch rejection applies nothing, so the sequential replay fallback on older engines is safe and exact. Ships on plugin-doc main (535dd51)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-17T17:14:08.822Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "plugin-doc.atomic-pour",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Atomic pour — the place path adopts Mutation::Batch for text steps (C-14)"
    },
    {
      "chapter": "plugin-doc",
      "claims": [
        {
          "component": "plugin.doc",
          "note": "`wordDocument` is declared in both directions: an objectType (bakedFallback group, matching a frame that carries the bundle's metadata envelope) bound to an editContext entered by doubleClick. ADR-024's point is that the declaration is NOT empty here — a DOCX is lowered to NATIVE stories, so the editor owns the caret and the HOST'S own text tools apply (toolIds paged.tool.type / paged.tool.select), the opposite answer to paged.sheet's and paged.web's empty rail from the same rule. The panel is DECLARED rather than raised from an onEnter hook, because only the declaration can withhold a raise that would displace a shared panel (ADR-023). Asserted by doc-bundle activate.spec.ts (3 vitest over a hand-rolled recording host — the SDK's headless harness refuses to boot without a resolvable canvas-wasm, and paying for that to read a declaration back is the wrong trade); the commit records the toolIds assertion as mutation-checked",
          "status": "shipped"
        },
        {
          "component": "editor.shell",
          "note": "nothing ENTERS the context in a real editor yet. The doc journey drives the place command and the exporter registry, not the double-click entry, so the declaration is verified as a declaration only. Same door paged.sheet's shipped context uses, and @paged-media/doc is unpublished (E404 2026-08-09), so there is no published lane either",
          "status": "planned"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-17T17:14:10.664Z",
        "headline": "All linked evidence is passing (3 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 3,
          "passed": 3,
          "skipped": 0
        }
      },
      "id": "plugin-doc.edit-context",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "The wordDocument surface — objectType + editContext declaration (ADR-024)"
    },
    {
      "chapter": "plugin-doc",
      "claims": [
        {
          "component": "plugin.doc",
          "note": "doc-host-model is a pure LoweredDoc → Mutation[] translator (styles + swatches + an offset-tracked pour + a phased story plan for tables, because insertTable's outcome mints the tableId mid-execution). Story offsets are CONTIGUOUS — the engine consumes the paragraph-break \\n on insertText split, so style/anchor ranges must not count it",
          "status": "shipped"
        },
        {
          "component": "editor.shell",
          "note": "UNDERSTATED UNTIL 2026-08-05, and found by this registry's own impl-without-claim rule: this stage was marked `planned` while carrying a GREEN editor journey — journey/plugins/doc.journey.spec.ts, 'a designer places a Word document and its text renders as native content'. The bundle registers importer / exporter / objectType / editContext / command / panel against the shipped contract, and the place-and-render path is exercised END TO END in the editor, not merely to the seam. PARTIAL, and the reason CORRECTED 2026-08-09: the ENGINE side is no longer the blocker — canvas-wasm 0.61.1 is published and pinned by editor apps/canvas + packages/client. What is still local is the BUNDLE: `npm view @paged-media/doc` is E404 (a deliberate maintainer deferral), so the editor consumes it through a pnpm override in its root package.json (`@paged-media/doc: link:../plugins/plugin-doc/packages/doc-bundle`). Nothing here is reachable from a clean editor install. Save-back stays on its own row (plugin-doc.save-back)",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": "2026-10-01T15:07:28Z",
        "evidence_at": "2026-10-18T05:03:21.254Z",
        "headline": "All linked evidence is passing (28 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 28,
          "passed": 28,
          "skipped": 0
        }
      },
      "id": "plugin-doc.embedded-placement",
      "importance": "normal",
      "since": "2026-07",
      "status": "stable",
      "title": "Embedded placement — a .docx poured into a host frame via host mutations"
    },
    {
      "chapter": "plugin-doc",
      "claims": [
        {
          "component": "plugin.doc",
          "note": "two doors share one `ingest()` (boot the wasm → lower → place → raise the outline panel). The COMMAND media.paged.doc.command.placeDoc picks through host.shell.pickFile; the IMPORTER media.paged.doc.importer.docx claims .docx/.dotx plus the OOXML wordprocessing mime and is what File▸Open and drag-drop resolve through the importer-registry union. Both are capability-gated and degrade with a log line. PARTIAL because only the command path is driven: doc-bundle's activate.spec.ts records editContexts and passes a noop for importer/exporter/command, so the importer REGISTRATION is asserted by nothing in this repo",
          "status": "partial"
        },
        {
          "component": "editor.shell",
          "note": "the command lane is green in the editor — journey/plugins/ doc.journey.spec.ts invokes media.paged.doc.command.placeDoc through the host command registry and asserts the placed text renders. The .docx route through File▸Open / FileDrop is registration-only: the editor's shell takes the importer union (packages/shell FileDrop.tsx, file-commands.ts) and no test feeds it a .docx. Both lanes need the pnpm link: override — @paged-media/doc is not on npm (E404 2026-08-09)",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:50:45.892Z",
        "headline": "All linked evidence is passing (8 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 8,
          "passed": 8,
          "skipped": 0
        }
      },
      "id": "plugin-doc.file-entry",
      "importance": "normal",
      "since": "2026-07",
      "status": "experimental",
      "title": "File entry — the .docx/.dotx importer and the \"Place Word document…\" command"
    },
    {
      "chapter": "plugin-doc",
      "claims": [
        {
          "component": "plugin.doc",
          "note": "the invariant the rest of save-back rests on, and it was a real defect twice. diff aligns at THREE levels by IDENTITY, never by index: story blocks (block_key), table rows (row_key) and a paragraph's runs (align_runs, keyed on (text, char_style_id)). Index pairing produced the correct TEXT and the wrong NODE — deleting a middle paragraph rewrote its successor and destroyed the original's `<w:pPr>`, rsids and every child paged does not model; the identical defect one level down did the same to `<w:tr>`/`w:trPr`. Runs compare on RESOLVED effective formatting rather than lowered style ids, because synthesized docx-auto-cN ids are POSITIONAL and renumber between two independent lowerings, so an id comparison would report every run as changed. plugin-doc.save-back's note names the run-level LCS in one clause; this row is the invariant itself, at all three levels",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "plugin-doc.identity-alignment",
      "importance": "normal",
      "since": "2026-07",
      "status": "stable",
      "title": "Identity-based LCS alignment — the preservation invariant under every derived edit"
    },
    {
      "chapter": "plugin-doc",
      "claims": [
        {
          "component": "plugin.doc",
          "note": "the locator counts `<w:tbl>` nesting depth and addresses ONLY depth 1. A nested table's `<w:tr>`/`<w:tc>`/`<w:gridCol>` sit under their own `<w:tbl>`, so a flat counter counts them against the OUTER table and lands an outer-row edit inside the nested one — a corruption that reads as a successful save. HONEST BOUND: this makes deeper tables non-targets, not addressable. An edit aimed inside a nested table is not patched at all",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "plugin-doc.nested-table-locator",
      "importance": "normal",
      "since": "2026-07",
      "status": "stable",
      "title": "Depth-aware table locator — a nested table cannot misdirect an outer edit"
    },
    {
      "chapter": "plugin-doc",
      "claims": [
        {
          "component": "plugin.doc",
          "note": "docxDocument, block, run and mergeField kinds with typed commands and a headless lane; a run write changes one run, a bad address or path is refused; Word properties are host PropertyFields; 10 run writes cost 1 commit (ADR 323).",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-17T17:16:17.804Z",
        "headline": "All linked evidence is passing (8 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 20,
          "passed": 8,
          "skipped": 12
        }
      },
      "id": "plugin-doc.object-model",
      "importance": "normal",
      "since": "2026-10",
      "status": "stable",
      "title": "Object model — Word document, blocks, runs and merge fields addressable through host.objects"
    },
    {
      "chapter": "plugin-doc",
      "claims": [
        {
          "component": "plugin.doc",
          "note": "zip+quick-xml OPC container, _rels graph, [Content_Types].xml, and the ooxmlsdk typed-DOM bridge. Zero-edit round-trip is per-part byte-identical (decompressed) INCLUDING parts paged does not model — the 'Paged never destroys a document' invariant. MPL-2.0 OR PMEL (the shared OOXML foundation; the docx-* crates are AGPL OR PMEL)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-17T17:16:17.601Z",
        "headline": "All linked evidence is passing (18 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 18,
          "passed": 18,
          "skipped": 0
        }
      },
      "id": "plugin-doc.opc-foundation",
      "importance": "normal",
      "since": "2026-07",
      "status": "stable",
      "title": "OPC container + verbatim carry-through preservation (paged-ooxml)"
    },
    {
      "chapter": "plugin-doc",
      "claims": [
        {
          "component": "plugin.doc",
          "note": "the lowering is RETAINED past placement and the panel renders it: document summary (paragraph/table/image/link counts + page geometry), the heading/table outline (style-id AND style-name detection), the synthesized style table + minted swatch chips, every ADR-007 diagnostic severity-sorted (footnotes, headers/footers, frozen fields, styled-only anchors — previously computed and DISCARDED), an honest save-back readiness line, and a select-placed-frame affordance (host.selection). Pure derivations, 4 vitest",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "verified through the editor doc journey against the link: override; the published lane lands with the @paged-media/doc first publish (trusted-publisher setup pending)",
          "status": "planned"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-17T17:14:10.671Z",
        "headline": "All linked evidence is passing (9 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 9,
          "passed": 9,
          "skipped": 0
        }
      },
      "id": "plugin-doc.outline-panel",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Outline/styles panel — the retained LoweredDoc surfaced in the editor"
    },
    {
      "chapter": "plugin-doc",
      "claims": [
        {
          "component": "plugin.doc",
          "note": "Tier-0 body/styles/sections + docDefaults, tabs, keeps, underline; Tier-1b lists (numbering.xml → paragraphListType/bullet/format — NO core change, the door already existed); Tier-2 tables (tblGrid, gridSpan, vMerge) + inline images (typed blip navigation, NOT to_xml — that links ooxmlsdk's serializer and blows the 8 MiB wasm budget); Tier-3 hyperlinks in BOTH Word encodings (w:hyperlink AND HYPERLINK fields — fldSimple + the complex fldChar/instrText state machine); footnotes/endnotes parsed + honestly diagnosed (never inlined into the flow). Direct Word formatting is lowered by SYNTHESIZING named styles, because applyStyle is the only range-styling mutation",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-17T17:16:18.545Z",
        "headline": "All linked evidence is passing (50 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 50,
          "passed": 50,
          "skipped": 0
        }
      },
      "id": "plugin-doc.read-path",
      "importance": "normal",
      "since": "2026-07",
      "status": "stable",
      "title": "DOCX read path — body, styles, lists, tables, images, hyperlinks, notes"
    },
    {
      "chapter": "plugin-doc",
      "claims": [
        {
          "component": "plugin.doc",
          "note": "quick-xml is used ONLY as a LOCATOR: a single pass computes the source byte ranges to replace, everything outside them is raw-copied, and hand-built fragments are spliced into the holes — so untouched bytes are identical BY CONSTRUCTION (no serializer; the ooxmlsdk write_to path is never linked, guarded in build-wasm.sh). Covers run text + properties, structural insert/delete of runs and paragraphs (diff derives them via an LCS alignment over runs' (text,style) identity), and table-cell content. Synthesized docx-auto-* styles project back to DIRECT w:rPr so Word gets no synthetic-style clutter",
          "status": "shipped"
        },
        {
          "component": "editor.shell",
          "note": "the exporter probes supports('document.readStory@1') → storyContent → saveEditedFromContent and degrades to the verbatim source otherwise; verified end-to-end with a MOCK read. BLOCKER RE-DIAGNOSED 2026-08-09 — the old note ('needs the canvas-wasm v54+ publish + the plugin-sdk sync-wire') names two preconditions that are now MET: canvas-wasm is published at 0.61.1, plugin-sdk pins 0.61.0, and its vendored plugin-api wire.d.ts already carries `requestStoryContent` / `storyContentResult` / `StoryContent`. What actually remains is one unwritten forward: plugin-sdk host-impl.ts `storyContent()` still throws PluginApiNotImplemented citing that satisfied precondition, and `document.readStory@1` is in no HOST_FEATURES list — so the bundle's probe answers false in every real host and the verbatim lane is the only one that can run. Cross-repo debt on plugin-sdk, not on core",
          "status": "planned"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-17T17:16:18.507Z",
        "headline": "All linked evidence is passing (51 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 51,
          "passed": 51,
          "skipped": 0
        }
      },
      "id": "plugin-doc.save-back",
      "importance": "normal",
      "since": "2026-07",
      "status": "stable",
      "title": "Edited save-back — targeted byte-level patch of word/document.xml"
    },
    {
      "chapter": "plugin-doc",
      "claims": [
        {
          "component": "plugin.doc",
          "note": "apply_edits' doc comment promised 'the caller surfaces them as diagnostics' while every skip was a bare continue. It now returns a labelled note per refused edit (non-patchable runs, gridSpan column ops with the widen-vs-split ambiguity named, resolution failures) through save_edited*'s (bytes, skips) shape + the wasm last_save_skips() export; the bundle publishes them under media.paged.doc/save-back via host.diagnostics (ADR-007 posture: refusals are told, never silent). The gridSpan conformance test asserts the note text",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-17T17:16:18.337Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "plugin-doc.save-back-feedback",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Save-back refusal ledger — the patcher's skips reach the Problems panel"
    },
    {
      "chapter": "plugin-doc",
      "claims": [
        {
          "component": "plugin.doc",
          "note": "run-level `<w:rPr>` had a lane; the PARAGRAPH level did not. EditSet.paragraphs carries a block's effective DIRECT paragraph formatting plus the real `<w:pStyle>` it should carry (`Some(Some(id))` sets, `Some(None)` clears — the tri-state matters, an Option alone cannot say 'clear'). rpr.rs renders the element; splice.rs REPLACES an existing `<w:pPr>` or inserts one before the first run when the paragraph had none, which is the case a replace-only patcher silently drops. A synthesized paragraph style is projected into direct formatting exactly as run styles are, so Word never sees the docx-auto-* names",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "plugin-doc.save-back-paragraph-props",
      "importance": "normal",
      "since": "2026-07",
      "status": "stable",
      "title": "Paragraph-property save-back — a targeted `<w:pPr>` rewrite"
    },
    {
      "chapter": "plugin-doc",
      "claims": [
        {
          "component": "plugin.doc",
          "note": "the table save-back that plugin-doc.save-back's note stops short of: DeleteRow/InsertRow splice a whole `<w:tr>` subtree, and DeleteColumn/InsertColumn touch BOTH the `<w:gridCol>` and the matching `<w:tc>` in every row (an insert copies the reference column's width). All indices address the BASELINE document, so ops never shift each other's addresses. The interesting half is the REFUSAL: column ops are applied only to a uniform grid — on a `gridSpan` table the widen-vs-split intent is undecidable from the grid alone, so the op is skipped and named in the skip ledger (plugin-doc.save-back-feedback) rather than guessed. Row deletions are also DERIVED, not only hand-authored: diff's row alignment emits them (diff_derives_a_row_deletion)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "plugin-doc.save-back-table-structure",
      "importance": "normal",
      "since": "2026-07",
      "status": "stable",
      "title": "Table-structure save-back — rows and columns inserted/deleted in the source bytes"
    },
    {
      "chapter": "plugin-doc",
      "claims": [
        {
          "component": "plugin.doc",
          "note": "File▸Open of a .docx opens an IDML skeleton the plugin writes itself (docx-skeleton, no core contact: per skeleton story a page of the section's size with its margins, the margin box as frame, an empty story) through host.nativeDocument.open, sets each story's grow rule on the wire (setFlowGrowRule, protocol 64), pours each section into its story, and save-back merges the section stories into the one Word body. Verified in the editor against Word's own page map (doc-standalone-open.spec.ts) on a LOCAL engine: the spec skips on canvas-wasm < 0.63.1 because page growth postdates the v0.63.0 tag. Hosts without document.openNative@1 get the embedded placement. Continuous sections join the previous story where Word keeps them on the page (e56375f); a refused style batch falls back op-by-op (8797c80).",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": "2026-10-01T15:25:13Z",
        "evidence_at": "2026-10-17T17:16:18.551Z",
        "headline": "All linked evidence is passing (17 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 17,
          "passed": 17,
          "skipped": 0
        }
      },
      "id": "plugin-doc.standalone-open",
      "importance": "high",
      "since": "2026-10",
      "status": "experimental",
      "title": "Standalone open — a Word document becomes the whole document, a page per section that grows"
    },
    {
      "chapter": "plugin-doc",
      "claims": [
        {
          "component": "plugin.doc",
          "note": "the retained OPC package is re-emitted UNCHANGED. conformance.rs doc_session_loads_and_emits_json asserts save_verbatim() equals the source bytes; save_back.rs zero_edit_save_back_still_byte_identical asserts an EMPTY EditSet patches nothing, per part. The bundle returns it whenever the edited path cannot run — no document.readStory@1, no story id, or a throw anywhere in read/overlay/diff/save — so a host without the read door exports a valid UNEDITED .docx instead of a wrong one. This is plugin-doc.opc-foundation's preservation invariant DELIVERED through the exporter door, not a second measurement of it",
          "status": "shipped"
        },
        {
          "component": "editor.shell",
          "note": "driven in the editor: journey/plugins/doc.journey.spec.ts pulls media.paged.doc.exporter.docx out of the host exporter registry and asserts the reply is a PK ZIP. The journey deliberately does NOT distinguish the lanes — its assertion holds on both — so what it proves is that the exporter registers and returns a valid package; since plugin-sdk's storyContent still throws and `document.readStory@1` is in no HOST_FEATURES list, the lane that can actually run today is this one. Published delivery blocked on the @paged-media/doc first publish (E404 2026-08-09)",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:50:55.3Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "plugin-doc.verbatim-export",
      "importance": "normal",
      "since": "2026-07",
      "status": "stable",
      "title": "Verbatim export — the zero-edit .docx exporter and its degrade path"
    },
    {
      "chapter": "plugin-doc",
      "claims": [
        {
          "component": "plugin.doc",
          "note": "Each rule is measured against WORD (docx-conformance/fixtures/*.word.json via scripts/word-*-probe.sh): line spacing (single = the font's hhea line height; auto multiples, exact, atLeast; 36 cases within 0.1 pt), pageBreakBefore / page and column breaks / oddPage and evenPage sections lowered to the engine's break-before rule (14-page map, matches Word), keepNext / keepLines through synthesized styles (needs core f6ba605, in v0.64.0). The break rule needs protocol 64 (canvas-wasm 0.64.0, on npm since 2026-10-01); the editor runs 0.71.11, so its specs run, and they skip only below 64. @paged-media/doc itself is not on npm: the editor links it from source. Open: empty paragraphs cannot take a paragraph style yet; a plain w:br splits the paragraph.",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-17T17:16:18.533Z",
        "headline": "All linked evidence is passing (45 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 45,
          "passed": 45,
          "skipped": 0
        }
      },
      "id": "plugin-doc.word-pagination",
      "importance": "high",
      "since": "2026-10",
      "status": "experimental",
      "title": "Word pagination — line spacing, breaks and keeps as Word lays them out"
    },
    {
      "chapter": "plugin-draw",
      "claims": [
        {
          "component": "plugin.draw",
          "note": "planAnchorAdd; anchor-machine.spec (interior split, off-curve null, closing-edge bookkeeping)",
          "status": "shipped"
        },
        {
          "component": "plugin.api",
          "note": "registered live via drawBundle.activate (D3, B-11 resolved); handlers in draw-bundle/src/handlers/anchors.ts, now on host.* facades (B-17 resolved — host.document.hitTest/pathAnchors/mutate, host.selection.get, host.viewport.pxToPt; isolate-safe)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:05:42.933Z",
        "headline": "All linked evidence is passing (16 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 16,
          "passed": 16,
          "skipped": 0
        }
      },
      "id": "plugin-draw.anchor-add",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Add-anchor plan (split segment at on-curve click)"
    },
    {
      "chapter": "plugin-draw",
      "claims": [
        {
          "component": "plugin.draw",
          "note": "planAnchorConvert; anchor-machine.spec (corner→smooth, smooth→corner)",
          "status": "shipped"
        },
        {
          "component": "plugin.api",
          "note": "registered live via drawBundle.activate (D3, B-11 resolved); on host.* facades (B-17)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:05:42.933Z",
        "headline": "All linked evidence is passing (20 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 20,
          "passed": 20,
          "skipped": 0
        }
      },
      "id": "plugin-draw.anchor-convert",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Convert-anchor plan (corner ↔ smooth)"
    },
    {
      "chapter": "plugin-draw",
      "claims": [
        {
          "component": "plugin.draw",
          "note": "planAnchorDelete; anchor-machine.spec (tolerance, refuses to shrink a 2-anchor contour)",
          "status": "shipped"
        },
        {
          "component": "plugin.api",
          "note": "registered live via drawBundle.activate (D3, B-11 resolved); on host.* facades (B-17)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:05:42.933Z",
        "headline": "All linked evidence is passing (12 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 12,
          "passed": 12,
          "skipped": 0
        }
      },
      "id": "plugin-draw.anchor-delete",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Delete-anchor plan (nearest within tolerance, contour floor)"
    },
    {
      "chapter": "plugin-draw",
      "claims": [
        {
          "component": "plugin.draw",
          "note": "Phase 9 Tier B: multiple fills/strokes per object as the honest \"metadata + baked path\" the concept names — an appearance envelope in this plugin's x-paged:media.paged.draw carrier lists the extra fill/stroke LAYERS, and a bake lowers the FRONT-MOST layer to the frame's real frameFillColor/frameFillTint/frameStrokeColor/frameStrokeWeight. Three commands manage the stack (Add fill / Add stroke / Clear), seeding a new layer from the frame's current paint; round-trip helpers (appearanceOf/withAppearance) preserve other draw metadata",
          "status": "shipped"
        },
        {
          "component": "plugin.api",
          "note": "RFI B-24 CLOSED by the GROUP BAKE (commands/appearance-bake.ts, registry row frames-paths.appearance-bake): bakeAppearance lowers the whole stack to a group of stacked page items, one paint each, sharing the source geometry — ordinary IDML, so it round-trips; releaseAppearance restores the single frame. The engine still has ONE fill and ONE stroke slot per frame by design (IDML has exactly one of each), so an UNBAKED stack is metadata and shows only its front-most layer — that half of the old note is still true. Corrected 2026-10-02: this row said only the top layer could ever bake. Cost: bake = 2 undo steps, a re-bake = 3",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:08:36.101Z",
        "headline": "All linked evidence is passing (32 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 32,
          "passed": 32,
          "skipped": 0
        }
      },
      "id": "plugin-draw.appearance",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Appearance model — multi fill/stroke metadata stack + baked top layer (Tier B)"
    },
    {
      "chapter": "plugin-draw",
      "claims": [
        {
          "component": "plugin.draw",
          "note": "React panel over the x-paged appearance metadata: add/remove/reorder fills and strokes, front-most-first, plus Bake and Release buttons over commands/appearance-bake.ts. UNBAKED, only the front-most fill and stroke show (one slot each in the engine) and the panel tags that row; BAKED, every row is real — a group of stacked page items. The inline note (APPEARANCE_BAKE_NOTE, wording pinned by a test) states both the unbaked limit and what a bake costs. Corrected 2026-10-02: this row said PARTIAL because only the front-most layer could bake (RFI B-24) — the group bake closed that. Not in the stack: effects",
          "status": "shipped"
        },
        {
          "component": "plugin.api",
          "note": "media.paged.draw.panel.appearance + appearanceRemoveLayer/appearanceMoveLayer commands",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:08:36.101Z",
        "headline": "All linked evidence is passing (24 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 24,
          "passed": 24,
          "skipped": 0
        }
      },
      "id": "plugin-draw.appearance-panel",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Appearance panel v0 — the metadata stack, with the bake limit stated in the UI"
    },
    {
      "chapter": "plugin-draw",
      "claims": [
        {
          "component": "plugin.draw",
          "note": "8b7b283, and it GROWS the one-clause 'blend v0' in plugin-draw.tool-wave-2 rather than sitting beside it: v0 was ONE command (two structurally matching paths → 3 interpolated intermediates, in TWO batches) and everything §16.2 actually names — the three spacing modes, a SPINE, the two reverse verbs, easing, expand/release — was absent. The v0 exports other code reads (blendSourceFrom, blendStructureMatches) survive unchanged; the two v0 batch builders do not. v1 = 8 commands (blendSelected / updateBlend / replaceBlendSpine / reverseBlendSpine / reverseBlendFrontToBack / selectBlendObjects / expandBlend / releaseBlend — the v0 command id is deliberately UNCHANGED, since renaming a published command for cosmetics breaks callers) + panel media.paged.draw.panel.blend + partType blendRecipe, over the arc-length kernel draw-geometry/src/along-path.ts SHARED with objects-on-path. THE THREE SPACING MODES all reduce to a step count: specified-steps IS the count; specified-distance derives it from the SPINE's arc length; smooth-color derives it from the largest per-channel difference between the two key fills, and falls back to the default count SAYING SO when neither fill resolves to RGB. A count over BLEND_MAX_STEPS is REFUSED when it was typed and CLAMPED when it was derived — a typo should not build a 5000-op batch, but a black-to-white smooth blend asking for 255 is data. THE DEFAULT SPINE IS PROVABLY INERT: measureSegment(cA, cB) sampled at the same eased parameter the shapes interpolate at is EXACTLY the lerp v0 produced, which makes 'the default changed nothing' an assertion rather than a claim. Second consumer of C-15's bindCreated after repeats, on the SAME v59-wire seam rather than a second copy of the cast — make / update / expand / release / reverse-spine / reverse-front-to-back are ONE undo step each (asserted), select is 0. Reverse-front-to-back costs no reorderElement and no second batch because INSERTION ORDER IS PAINT ORDER (the pattern-v1 finding). Two deliberate differences from Illustrator, named rather than hidden: a replaced spine KEEPS ITS OWN PAINT (clearing a stroke the user drew unasked is the worse surprise), and the spine is NOT in the blend's group so it stays selectable. PARTIAL, pinned by BLEND_LIVE_NOTE (5 assertions): a blend is NOT a live object — the engine has no blend node, editing a key does not re-render the intermediates, Update does; the panel's preview is a preview of the PLAN (resolved step count, spine length, where the count came from) and NOT of the artwork, because re-rendering per keystroke would be one document mutation and one undo step per keystroke; STRUCTURE MISMATCH IS STILL A REFUSAL (resampling both keys to a common parameterization is a real feature and is NOT built); text frames are refused (no wire op copies a story, insertPath mints Polygons); and an intermediate is artwork, so its element ids are NEW on every Update",
          "status": "partial"
        },
        {
          "component": "plugin.api",
          "note": "8 commands via host.contribute.command + the Blend Options panel via host.contribute.panel (React expert-leaf, icon panel-object-transform SHARED with the repeat panel — a shared REAL glyph is honest where an invented specific one renders the dock tab iconless). The protocol-ahead ops ride commands/v59-wire.ts; the casts that seam used to explain are GONE as of 3bac483 (repin 0.2.25-canary.0 → 0.2.28-canary.0), but the seam survives because bindCreated / pasteInto / releaseFrom live in PendingMutation, not Mutation, and the door that takes both is MutationInput. Ships in draw 0.1.0-canary.8",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:41:51.205Z",
        "headline": "All linked evidence is passing (73 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 73,
          "passed": 73,
          "skipped": 0
        }
      },
      "id": "plugin-draw.blends",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Blends v1 — three spacing modes over a real spine (grew the tool-wave-2 blend v0)"
    },
    {
      "chapter": "plugin-draw",
      "claims": [
        {
          "component": "plugin.draw",
          "note": "calligraphicWidth(tangent, nib{angle,roundness,size}, pressure) → per-anchor widths over a PencilMachine-derived centerline; paintbrush bakes via outlineStrokeVariable, blob brush unites with the selection via pathfinderBoolean, eraser subtracts. Honest v0 scopes stated in code: blob unites with the FIRST selected same-fill element (no proximity detection); eraser erases from SELECTED elements only",
          "status": "shipped"
        },
        {
          "component": "plugin.api",
          "note": "registered live (shift+j / shift+k / shift+i, verified unique across built-ins + every bundle by the editor's INV-REG-1)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-16T07:57:37.783Z",
        "headline": "All linked evidence is passing (26 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 26,
          "passed": 26,
          "skipped": 0
        }
      },
      "id": "plugin-draw.brush-tools",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Brush tools v0 — paintbrush, blob brush, eraser (calligraphic nib sweep)"
    },
    {
      "chapter": "plugin-draw",
      "claims": [
        {
          "component": "plugin.draw",
          "note": "manifest validates (document scoped-write, overlay+hitTest, editContext vectorGraphic, importers/exporters); activate registers ids matching contributes (activate.spec); B-02 RESOLVED — the vectorGraphic editContext is LIVE (W3.2). B-19 RESOLVED (fill-ref read via the typed elementProperties facade, escape hatch retired). The original RFI §6 API punch-list (B-01..B-17) is drained. COUNTS REFRESHED 2026-08-09 by audit, at 20d6c70: the manifest contributes 92 commands, 19 tools, 10 panels and 7 partTypes (graphicStyleLibrary / symbolLibrary / livePaintRecipe / patternRecipe / repeatRecipe / blendRecipe / objectsOnPathRecipe), and the suite is 940 vitest (draw-geometry 210 / draw-tools 126 / draw-bundle 604), all green, plus the same 26 cargo test (draw-trace 22, trace-js 4 — the crates are untouched since c4c26bc). The 2026-08-05 numbers this note used to carry (73 commands, 18 tools, 7 panels, 4 partTypes, 775 vitest) were taken four hours before the Illustrator Phase-3 wave landed — see plugin-draw.repeats / .blends / .objects-on-path. WAS, and still true as history: AS OF 2026-08-05 the manifest contributes 73 commands, 18 tools, 7 panels and 4 partTypes plus a capabilities.wasm entry for the Image Trace kernel (name 'trace', 2 MiB budget). Command count went 76 -> 73 when the three structural commands retired to the host (see editor-tools.object-commands); panels went 8 -> 7 when the Layers panel retired behind the ADR-023 seam. Three named engine walls survive as honest subsets: B-22 closed but C-30 (no document-resident Live Paint object) and C-31 (no pattern paint type) remain, alongside B-24's one-paint-slot limit now bounded by a bake",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:40:55.206Z",
        "headline": "All linked evidence is passing (16 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 16,
          "passed": 16,
          "skipped": 0
        }
      },
      "id": "plugin-draw.bundle-manifest",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "paged.draw bundle manifest (media.paged.draw) + activate skeleton"
    },
    {
      "chapter": "plugin-draw",
      "claims": [
        {
          "component": "plugin.draw",
          "note": "needed no new wire op: the framePath write door, per-contour parse and core's own apply_pathfinder already build this shape. The real problem was WINDING, not concatenation — the engine fills non-zero and the PDF exporter emits `f`, never `f*`, so contours concatenated as authored paint a solid coin; contours are re-oriented by nesting depth first. Undo counts asserted, not claimed (make 1, release 2 because insertPath mints the ids the paint batch addresses) Updated 2026-10-05: Make Compound Path re-winds by paint order, Illustrator's non-zero rule (draw fa6f731, recorded in the Illustrator oracle lane, plugin-draw.illustrator-oracle); Release is one batch for the whole selection.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:41:15.99Z",
        "headline": "All linked evidence is passing (109 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 109,
          "passed": 109,
          "skipped": 0
        }
      },
      "id": "plugin-draw.compound-paths",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Compound paths — make / release, with real non-zero holes"
    },
    {
      "chapter": "plugin-draw",
      "claims": [
        {
          "component": "plugin.draw",
          "note": "media.paged.draw.tool.cornerRadius (shift+r, transform section): drag near a rectangle corner sets a RoundedCorner radius via cornerRadiiMutationFor — the per-corner builder the live-corners row exported 'for a future on-canvas handle', now consumed. Pure machine (cornerAt/maxRadius/radiusFromDrag) in draw-tools with 4 vitest; rectangles-only + axis-aligned-only (transformed frames debug-skip — the engine apply arm is Rectangle-only, RFI B-23). Ships in draw canary.5 Updated 2026-10-05: the tool also works on rotated and transformed frames, text frames and polygons (draw 02dd7f7).",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-16T07:57:37.789Z",
        "headline": "All linked evidence is passing (18 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 18,
          "passed": 18,
          "skipped": 0
        }
      },
      "id": "plugin-draw.corner-radius-tool",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "On-canvas corner-radius tool — the live-corners row's predicted handle"
    },
    {
      "chapter": "plugin-draw",
      "claims": [
        {
          "component": "plugin.draw",
          "note": "On draw main since f58fd50 and pinned to canvas-wasm 0.66.0 in 223660a; published in @paged-media/draw 0.1.0-canary.13 (2026-10-05). conformance/create-outlines.spec green on that main.",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "note": "RequestTextOutlines → TextOutlines (C-69), tagged and published in v0.66.0 (core fcffd2d, 2026-10-05).",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "plugin-draw.create-outlines",
      "importance": "normal",
      "since": "2026-10",
      "status": "stable",
      "title": "Create Outlines — a text frame's glyphs as paths"
    },
    {
      "chapter": "plugin-draw",
      "claims": [
        {
          "component": "plugin.draw",
          "note": "DirectSelectMachine in draw-tools (exported as @paged-media/draw/machines): anchor / handle / segment hit, multi-anchor selection, anchor marquee, drag of anchors, handles and segments (reshapeSegmentByDrag in draw-geometry bezier), click counting for the double-click convert. A committed plan is ONE batch and one undo step (pathEditBatch / targetedPathEditBatch); apply(ops) previews an insert and a convert at once. direct-select-machine, segment-drag, path-edit-ops, machine-apply and conformance/direct-select specs green at draw c880079 (engine 0.65.0).",
          "status": "shipped"
        },
        {
          "component": "editor.gesture",
          "note": "The editor is the shim over the machine (editor 701f6ad, on main): DirectSelectSession per path-edit target, the Direct Selection tool enters path-edit mode for a single path-bearing selection, arrows nudge selected anchors. Driven end to end by e2e/path-editing.spec.ts. Snapping for it is on editor feat/protocol-66-consumers, not main.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-05T09:37:27Z",
        "evidence_at": "2026-10-18T04:40:09.4Z",
        "headline": "All linked evidence is passing (172 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 172,
          "passed": 172,
          "skipped": 0
        }
      },
      "id": "plugin-draw.direct-selection",
      "importance": "normal",
      "since": "2026-10",
      "status": "stable",
      "title": "Direct Selection machine — drag anchors, handles and segments; multi-anchor select, marquee, nudge"
    },
    {
      "chapter": "plugin-draw",
      "claims": [
        {
          "component": "plugin.draw",
          "note": "registered via contribute.editContext (W3.2); enter/exit + write-scope narrowing live in the editor",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:03:53.245Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "plugin-draw.edit-context-live",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "vectorGraphic edit context — live enter/exit via contribute.editContext"
    },
    {
      "chapter": "plugin-draw",
      "claims": [
        {
          "component": "plugin.draw",
          "note": "hitTest → B-19 elementProperties read (fill/tint/stroke/weight/opacity) → ONE batch of setElementProperty (one undo). Alt+click samples only. Honest scope: samples element PROPERTIES, not composited pixels. Found RFI C-17 — property support is kind-dependent and read≠write (Polygon exposes frameFillTint on read but rejects its write), so the apply intersects the sample with each target's own readable set (batches are atomic)",
          "status": "shipped"
        },
        {
          "component": "plugin.api",
          "note": "shift+d",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-16T07:57:37.801Z",
        "headline": "All linked evidence is passing (5 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 5,
          "passed": 5,
          "skipped": 0
        }
      },
      "id": "plugin-draw.eyedropper",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Eyedropper — sample element properties and apply to the selection"
    },
    {
      "chapter": "plugin-draw",
      "claims": [
        {
          "component": "plugin.draw",
          "note": "the SECOND v1 declarative schema panel (49ac0f8, Phase 2d), converted from the panels/fill.panel.json prototype once gradient assignment was VERIFIED engine-side (B-03; the wire-path proof is test/conformance/gradient-fill.spec.ts — a setElementProperty {frameFillColor, colorRef 'Gradient/…'} is a plain ref assignment, because gradients share the swatch namespace). Applies the stroke-panel pattern: rows are CATALOG widgets with value bindings on the §11.5 ceiling (selectionProperty + coerce), and the GRADIENT section's visibility is a PUBLISHED BINDING the bundle computes from real document state (media.paged.draw.gradientControlsVisible — is the first selected element's fill a Gradient ref?) rather than a visibleWhen conditional, which is the B-01 rule. Gradient ASSIGNMENT is a multi-mutation array-valued flow ABOVE the scalar binding ceiling, so it is COMMAND-driven (the dash.ts precedent) in commands/fill-gradient.ts; the angle/length scrubs in the panel steer an ALREADY-gradient fill, which is scalar and on the ceiling",
          "status": "shipped"
        },
        {
          "component": "plugin.api",
          "note": "contributeSchemaPanel(host, fillPanel) + installFillPanelBindings — panel id media.paged.draw.panel.fill, one of the ten panels the manifest contributes",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-16T07:57:37.983Z",
        "headline": "All linked evidence is passing (26 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 26,
          "passed": 26,
          "skipped": 0
        }
      },
      "id": "plugin-draw.fill-schema-panel",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Fill panel as a declarative schema panel — the second W3.1 consumer, with a published binding"
    },
    {
      "chapter": "plugin-draw",
      "claims": [
        {
          "component": "plugin.draw",
          "note": "splitSegmentDeCasteljau/evalCubic/closestTOnCubic/flattenAnchorRun; bezier.spec. B-06 resolved — requestNearestPathPoint worker query landed (v30); the local closest-t stays as the synchronous in-gesture fallback",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-16T07:56:24.187Z",
        "headline": "All linked evidence is passing (30 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 30,
          "passed": 30,
          "skipped": 0
        }
      },
      "id": "plugin-draw.geometry-bezier",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Cubic bezier kernel — de Casteljau split, eval, closest-t, flatten"
    },
    {
      "chapter": "plugin-draw",
      "claims": [
        {
          "component": "plugin.draw",
          "note": "cornerAnchor/mirrorHandle/smoothAnchorFromDrag/constrainAngle + affine ops; handles.spec",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-16T07:56:24.204Z",
        "headline": "All linked evidence is passing (9 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 9,
          "passed": 9,
          "skipped": 0
        }
      },
      "id": "plugin-draw.geometry-handles",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Anchor/handle math — corner, mirror, smooth-from-drag, angle constrain, affine"
    },
    {
      "chapter": "plugin-draw",
      "claims": [
        {
          "component": "plugin.draw",
          "note": "fast-check property suites over affine, along-path, bezier, blend, compound, constrain, parametric, polygon, rdp, repeat, spline handles, stroke width and the SVG arc / colour / doc / path / shapes parsers. They found 41 defects, all fixed (draw 4b88f38). *.property.spec green at draw c880079.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-16T07:56:24.232Z",
        "headline": "All linked evidence is passing (287 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 287,
          "passed": 287,
          "skipped": 0
        }
      },
      "id": "plugin-draw.geometry-properties",
      "importance": "normal",
      "since": "2026-10",
      "status": "stable",
      "title": "Property tests for the draw-geometry kernel"
    },
    {
      "chapter": "plugin-draw",
      "claims": [
        {
          "component": "plugin.draw",
          "note": "simplifyRdp/segmentDistance; rdp.spec (collinear, deviation, copy-safety); editor pencil re-imports from here (D1 seam)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-16T07:56:24.218Z",
        "headline": "All linked evidence is passing (17 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 17,
          "passed": 17,
          "skipped": 0
        }
      },
      "id": "plugin-draw.geometry-rdp",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "RDP polyline simplification + perpendicular segment distance"
    },
    {
      "chapter": "plugin-draw",
      "claims": [
        {
          "component": "plugin.draw",
          "note": "The Gradient Annotator draws the stops on its line and a drag moves one (gradient-stop-machine). Uses raw engine requests outside the host doors (docs/status.md, limits). gradient-stop-machine and conformance/gradient-stops green at draw c880079.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-16T07:57:37.805Z",
        "headline": "All linked evidence is passing (15 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 15,
          "passed": 15,
          "skipped": 0
        }
      },
      "id": "plugin-draw.gradient-stops",
      "importance": "normal",
      "since": "2026-10",
      "status": "stable",
      "title": "On-canvas gradient stops on the annotator line"
    },
    {
      "chapter": "plugin-draw",
      "claims": [
        {
          "component": "plugin.draw",
          "note": "the substance is the LINK, stated in the module header as 'a style that only copies values is a clipboard, not a style'. The library is a document-resident .paged container part (partType graphicStyleLibrary); the link is a reference on the element's own x-paged envelope. A direct appearance edit marks the element OVERRIDDEN *without* breaking the link, and the override is DETECTED, NEVER STORED (a digest comparison) — so the appearance panel, the fill panel, the eyedropper and a plain undo all yield a truthful badge without knowing the feature exists. 6 commands (saveGraphicStyle / applyGraphicStyle / redefineGraphicStyle / breakGraphicStyleLink / renameGraphicStyle / deleteGraphicStyle) + panel media.paged.draw.panel.graphicStyles",
          "status": "shipped"
        },
        {
          "component": "plugin.api",
          "note": "React expert-leaf panel via contribute.panel — the ADR-023 escape hatch for a genuinely plugin-specific surface with no host counterpart",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:41:41.645Z",
        "headline": "All linked evidence is passing (46 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 46,
          "passed": 46,
          "skipped": 0
        }
      },
      "id": "plugin-draw.graphic-styles",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Graphic styles — a named, LINKED complete appearance with override DETECTION"
    },
    {
      "chapter": "plugin-draw",
      "claims": [
        {
          "component": "plugin.draw",
          "note": "runs over createHeadlessHost + published canvas-wasm; 45 bundle vitest green 2026-06-11 (the B-13 harness un-stubbed)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-16T07:57:37.895Z",
        "headline": "All linked evidence is passing (27 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 30,
          "passed": 27,
          "skipped": 3
        }
      },
      "id": "plugin-draw.headless-conformance",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Headless conformance corpus over published canvas-wasm"
    },
    {
      "chapter": "plugin-draw",
      "claims": [
        {
          "component": "plugin.draw",
          "note": "A runner records probes in Adobe Illustrator 30.1.0 and the specs replay them closed-form against the engine: Offset Path (17 cases), Outline Stroke (11), the four Pathfinder booleans (8), the six region verbs (9; Minus Back is Minus Front reversed) and compound-path winding (9; Illustrator is non-zero by paint order, which Make Compound Path now follows). The recordings are committed; replay needs no Illustrator. oracle/* green at draw c880079.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-16T07:57:37.952Z",
        "headline": "All linked evidence is passing (150 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 150,
          "passed": 150,
          "skipped": 0
        }
      },
      "id": "plugin-draw.illustrator-oracle",
      "importance": "normal",
      "since": "2026-10",
      "status": "stable",
      "title": "Illustrator oracle lane — path ops recorded in Illustrator 30.1.0, replayed against the engine"
    },
    {
      "chapter": "plugin-draw",
      "claims": [
        {
          "component": "plugin.draw",
          "note": "the first Rust/wasm lane in this repo — colour clustering and Schneider fitting from visioncortex (MIT), 173 KB against a declared 2 MiB. Emits COMPOUND paths reusing the compound-path winding logic, so traced holes are real. Blocking cost was MEASURED, not guessed: a noisy 2048² photo takes 41s and the cost is clustering, not geometry — so the kernel caps stay hard refusals and a decoder-side 1 MP budget is what a trace actually runs at, downsampling with the factor logged. PARTIAL: one-shot, fills only — no centreline/stroke detection, no palette targeting, not transparency-aware, and fitted to the frame's bounds because the placed image's own fit/crop transform is exposed by no read door (RFI C-27) Updated 2026-10-05: the kernel runs in a host worker where the host can spawn one, on the calling thread otherwise (draw bb7cd75; the editor registers the worker, editor a378828, e2e/draw-image-trace-worker.spec.ts).",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:51:24.616Z",
        "headline": "All linked evidence is passing (48 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 48,
          "passed": 48,
          "skipped": 0
        }
      },
      "id": "plugin-draw.image-trace",
      "importance": "normal",
      "since": "2026-08",
      "status": "experimental",
      "title": "Image Trace v0 — raster to editable vector over visioncortex"
    },
    {
      "chapter": "plugin-draw",
      "claims": [
        {
          "component": "plugin.draw",
          "note": "27 IDML files authored from draw output, InDesign 20.0.1's reading of each and its own re-export are committed; the specs replay InDesign's reading (stacking order included) and classify every difference. Four cases re-recorded on engine 0.65.0. PARTIAL: arrowheads, stroke attributes and linear / radial gradients are owed a re-generate + InDesign re-record at the v66 pin move (C-62 / C-83b). roundtrip/* green at draw c880079.",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-16T07:57:38.003Z",
        "headline": "All linked evidence is passing (213 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 240,
          "passed": 213,
          "skipped": 27
        }
      },
      "id": "plugin-draw.indesign-roundtrip",
      "importance": "normal",
      "since": "2026-10",
      "status": "stable",
      "title": "InDesign round-trip lane — 27 IDML cases read by InDesign 20.0.1"
    },
    {
      "chapter": "plugin-draw",
      "claims": [
        {
          "component": "plugin.draw",
          "note": "the first phase-D consumer. Registers on the vectorGraphic edit context with provides.collections ['layers'], provides.paths ['layerVisible','layerLocked'] and provides.ops ['layerMove','layerSetVisible','layerSetLocked']. THE ROWS ARE NOT THE DOCUMENT'S LAYERS — it serves the OBJECT STACK the user is inside (the entered element and its siblings in engine paint order, z = sibling index 0-backmost, parentId null, one flat list), which is what makes it a real test of retargeting rather than the same rows served twice. Path translation is explicit (layerVisible→elementVisible, layerLocked→elementLocked; the panel's layerMove becomes core's reorderElement on the element handed out — 'one vocabulary at the panel, two ops at the engine'). layerName / layerPrintable are DELIBERATELY not declared: an element has neither in core, so the host disables rename rather than writing a layer op with an element id. MAX_ROWS = 200, past which the provider DECLINES the collection so the host falls back to the document's real layers — never a truncated list. adr023-seam.ts is the repo's third escape-hatch seam (after planar-regions K-11 and v58-wire) and returns null on a pre-phase-A host, which is what this repo's own suite runs against",
          "status": "shipped"
        },
        {
          "component": "plugin.api",
          "note": "consumes the phase-A binding-provider contract (plugin-sdk ee778c5)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-16T07:57:37.833Z",
        "headline": "All linked evidence is passing (15 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 15,
          "passed": 15,
          "skipped": 0
        }
      },
      "id": "plugin-draw.layers-binding-provider",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Layers BINDING PROVIDER — paged.draw serves the HOST Layers panel (ADR 023 phase D)"
    },
    {
      "chapter": "plugin-draw",
      "claims": [
        {
          "component": "plugin.draw",
          "note": "RETIRED 2026-08-05 (commit 2339bce, ADR 023 phase D step 2) — the panel file, its host.contribute.panel call and its contributes.panels entry are deleted, eight bundle panels became seven, and BOTH specs now assert its ABSENCE (activate.spec.ts 'and NO Layers panel'). It is kept here as a retired row rather than deleted because the capability did not go away: it moved behind the ADR-023 seam as plugin-draw.layers-binding-provider, and the migration (not deletion) is the record. WHAT IT WAS: live top-first layer list over documentCollection('layers'), refreshed on document.onDidChange; per-row visibility/lock/printable toggles, double-click rename, z-order move, add/remove — all through the existing layer wire mutations. First React panel in the bundle (the v1 schema had no list widget — B-01's honest limit). Shipped in draw canary.3",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "plugin-draw.layers-panel",
      "importance": "normal",
      "since": "2026-08",
      "status": "deprecated",
      "title": "Layers panel — the panels/layers.panel.json prototype made real (expert-leaf React)"
    },
    {
      "chapter": "plugin-draw",
      "claims": [
        {
          "component": "plugin.draw",
          "note": "Phase 9 Tier B: five corner-style preset commands, each committing ONE batch of eight setElementProperty writes (four frameCornerOption* Text + four frameCornerRadius* Length) to the selected rectangle, polygon or text frame (LIVE_CORNER_KINDS in commands/live-corners.ts; one undoable step), and stamping a §13.3 liveCorners flag into this plugin's x-paged metadata envelope (the \"live\" marker; the baked IDML corners are always valid). A per-corner builder (cornerRadiiMutationFor) is exported for a future on-canvas handle. 11 conformance tests",
          "status": "shipped"
        },
        {
          "component": "plugin.api",
          "note": "RFI B-23 and C-18 CLOSED (2026-08-04): the corner option/radius apply arms cover all six page-item kinds (paged-mutate find_corners_mut), and the presets drive rectangle, polygon and textFrame — the three kinds whose corners the renderer actually draws. Corrected 2026-10-02: this row still said Rectangle-only. What IS still rectangle-only is the separate corner-radius DRAG tool (plugin-draw.corner-radius-tool), for a handle-geometry reason: it hit-tests the four corners of an axis-aligned box",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:03:33.692Z",
        "headline": "All linked evidence is passing (13 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 13,
          "passed": 13,
          "skipped": 0
        }
      },
      "id": "plugin-draw.live-corners",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Live Corners — Rounded/Inverse/Bevel/Fancy/None presets via frameCornerOption*/Radius* + live marker (Tier B)"
    },
    {
      "chapter": "plugin-draw",
      "claims": [
        {
          "component": "plugin.draw",
          "note": "the bucket resolves the face under the cursor and fills it; a recipe part (partType livePaintRecipe) lets a Regenerate command re-derive after a member edit. 6 commands (makeLivePaintGroup / fillLivePaintFace / regenerateLivePaint / selectLivePaintFaces / deleteLivePaintFace / releaseLivePaint) + 2 tools (livePaintBucket, livePaintSelect) + panel media.paged.draw.panel.livePaint, over the shared K-11 planar-regions seam. PARTIAL — 'REGENERABLE, NOT LIVE', and the difference is stated in the module header, in the panel note, in the user-facing refusals and in conformance assertions. Two measured limits beyond the wall: GAP OPTIONS ARE UNREACHABLE — and worse than 'no tolerance argument', because idml_path_to_flo never sees subpathOpen, so every open member is implicitly CHORDED SHUT, which is a different arrangement rather than gap bridging — and EDGES ARE UNREACHABLE, a face carrying no edge id on the v57 wire, so the catalog's 'stroke edges' half has nothing to name Updated 2026-10-05: a fill and a regenerate are each one batch and one undo step (draw 0a28a55); the bucket moved to its canonical key, k (draw db23755).",
          "status": "partial"
        },
        {
          "component": "core.canvas-wasm",
          "note": "RFI C-30, the wall this row is built against: there is NO document-resident Live Paint object — only a per-call planar QUERY. (a) PlanarFace.id is '<signature>#<component>' where the signature indexes the REQUEST'S OWN elementIds, so re-ordering the inputs re-points every id and moving a member can renumber components under an unchanged id — there is nowhere to store a paint that survives either. (b) The wire carries face outlines and NO edge id or half-edge. (c) RequestPlanarRegions takes no gap tolerance and PLANAR_ACCURACY is the C-21 robustness grid, not a gap-closing one. Two smaller walls the v0 had to work around and could not: an inserted face fill lands at the TOP of the z-order (paged.draw measured and pinned it) where Illustrator paints faces UNDER edges, and rebuilding a fill mints a new element id. Closing it is a real LivePaintGroup node kind — CORE work, sized L",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:41:51.205Z",
        "headline": "All linked evidence is passing (76 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 76,
          "passed": 76,
          "skipped": 0
        }
      },
      "id": "plugin-draw.live-paint",
      "importance": "normal",
      "since": "2026-08",
      "status": "experimental",
      "title": "Live Paint v0 — regenerable face paint over the planar arrangement (NOT live)"
    },
    {
      "chapter": "plugin-draw",
      "claims": [
        {
          "component": "plugin.draw",
          "note": "guarded by supports('overlay.text@1') with the binding retained as fallback. PARTIAL, and the reason changed: the tool still shows the line while dragging and the text once frozen, never both — but no longer because the platform cannot. RFI K-9 SHIPPED host.overlay.setToolPreviews (a LIST of shapes) on 2026-08-04 and handlers/measure.ts does not call it (verified 2026-10-02: no setToolPreviews caller anywhere in draw-bundle/src). Plugin-only work. Also still on the raw host.editor.client.send escape hatch for nearestPathPoint — plugin-api has no facade for it Updated 2026-10-05: the measure line and its readout are drawn together (draw ec35d85).",
          "status": "partial"
        },
        {
          "component": "plugin.api",
          "note": "consumes the ToolPreviewText variant; the multi-shape door (K-9, overlay.multiPreview@1) exists and is unconsumed here",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-16T07:57:37.853Z",
        "headline": "All linked evidence is passing (14 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 14,
          "passed": 14,
          "skipped": 0
        }
      },
      "id": "plugin-draw.measure-overlay-text",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Measure readout drawn on-canvas via the overlay text primitive"
    },
    {
      "chapter": "plugin-draw",
      "claims": [
        {
          "component": "plugin.draw",
          "note": "Published: on plugin-draw main and in @paged-media/draw since 0.1.0-canary.16 (npm, 2026-10-11); the current canary, 0.1.0-canary.20 (2026-10-14, the version the editor pins), peers plugin-api/plugin-sdk >=0.2.55-canary.0. The contract is plugin-sdk DESIGN.md §21 (§21.8 the fix-ups from the first consumers, §12.7 the panel property row). Ten kinds under plugin:media.paged.draw/: path (points, contour starts over core framePath), appearance (fills/strokes stack + graphic-style link; stamp + bake in one batch), graphicStyle and symbol (library entries, label-hash state; a style edit re-applies to every follower and a delete unlinks, in the same commit), symbolInstance, and the five generator recipes pattern/repeat/blend/livePaint/objectsOnPath (params derived from the recipe types). Every write is one undo step; recipe and library writes are undoable through the document label x-paged:media.paged.draw (content-addressed parts are a cache; small libraries ride inline in the label), and every draw op of a batch is planned together by the plugin-level planner. 97 typed command twins (untyped 107 -> 10). Budget: a 10-op mixed batch = 1 engine mutation, 1 undo step, pinned per-door reads. The Draw settings panel shows every writable row as a host PropertyField (points and the fill/stroke stacks as list rows); Boa scripts reach every row through the editor's host bridge, and the Node CLI (@paged-media/cli in plugin-sdk, not on npm yet) through the same registry. Limits: a recipe write changes the recipe, not the artwork; the typed update command rebuilds the artwork as a second undo step, because an update mints new ids the recipe records. Illustrator graph data is not built in paged.draw: a graph-data variable would bind to paged.sheet's chart series rows. Two libraries in one batch are refused.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-12T07:33:33Z",
        "evidence_at": "2026-10-16T07:57:37.995Z",
        "headline": "All linked evidence is passing (42 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 42,
          "passed": 42,
          "skipped": 0
        }
      },
      "id": "plugin-draw.objects",
      "importance": "normal",
      "since": "2026-10",
      "status": "stable",
      "title": "Object model — paths, appearance stacks, graphic styles, symbols and recipes addressable through host.objects"
    },
    {
      "chapter": "plugin-draw",
      "claims": [
        {
          "component": "plugin.draw",
          "note": "8b7b283, built WITH blends because both ask the same question — place N objects along a path by count or by spacing — and share draw-geometry/src/along-path.ts (flatten → cumulative length → point + tangent at a fraction, plus the distribution rule and the easing curves). Nothing above that line is shared. 5 commands (makeObjectsOnPath / updateObjectsOnPath / selectObjectsOnPath / expandObjectsOnPath / releaseObjectsOnPath) + panel media.paged.draw.panel.objectsOnPath + partType objectsOnPathRecipe. IT MOVES OBJECTS AND CREATES NONE, which is what makes it different from every other arranging row here — repeats, patterns, symbols, live paint and blends all INSERT artwork and leave a source behind; this one writes ONE frameTransform per object, so element ids SURVIVE, another plugin's metadata on an object survives, TEXT IS NOT REFUSED (nothing copies a story because nothing is copied), and RELEASE IS EXACT — each object's original transform is remembered on the object's own link, so it works even on a host with no .paged container writer. Three door facts MEASURED against the booted engine at protocol 60: the write REPLACES the item transform rather than composing with it (which is what makes Update idempotent and Release exact), elementGeometry.bounds is the frame box in the element's OWN space and is not recomputed by a transform (so page space is transformBounds(bounds, itemTransform) and this module never reads bounds as page space), and N writes in one batch is ONE undo step — nothing here needs bindCreated because nothing is created. Undo counts asserted: make / update / expand / release = 1 each, select = 0, no clipped case and no second batch anywhere on the lane. TWO C-23 COROLLARIES WERE WRITTEN INTO THIS FILE AS FACT AND THEN DELETED WHEN MEASURED: getMetadata is NOT page-keyed and neither is document.tree(), so an off-page object is FINDABLE (what is lost is its geometry, which is exactly what re-distribution needs); and there is no simple outside-the-rect threshold, so the guard is simply stricter than the engine's and never needs to know where the line is. PARTIAL — three named deviations from §16.3, the first pinned in OBJECTS_ON_PATH_NOTE (8 assertions): NOTHING IS GROUPED (Illustrator's version behaves like one object; grouping elements the user already owns would move them in the layer tree and Release would then have to undo a structural change it never made — the links are the index); an object is NOT resized or scaled to the path, deliberately; and editing an object does not re-distribute by itself, Update does. An object the artboard fit refuses is simply NOT MOVED — it stays home, readable, in the association, with the refusal reported",
          "status": "partial"
        },
        {
          "component": "plugin.api",
          "note": "5 commands via host.contribute.command + the distribution panel via host.contribute.panel (React expert-leaf, icon panel-align — a REAL glyph and the honest metaphor). PIVOT reuses §16.1's nine-point registration grid IMPORTED from symbols rather than re-derived. Ships in draw 0.1.0-canary.8. FOUND 2026-08-09 while writing this row, and left for the plugin repo: activate.ts's inline comment for this panel says an off-page object 'answers NOTHING at all, not even its own metadata', which is the claim the module header records as MEASURED AND FALSE — the pinned user-facing note is correct, only the activate comment is stale",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:42:01.475Z",
        "headline": "All linked evidence is passing (120 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 120,
          "passed": 120,
          "skipped": 0
        }
      },
      "id": "plugin-draw.objects-on-path",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Objects on a Path — distribute / align / pivot / reorder by TRANSFORM; it moves your objects and creates none"
    },
    {
      "chapter": "plugin-draw",
      "claims": [
        {
          "component": "plugin.draw",
          "note": "2 commands (makeOpacityMask / releaseOpacityMask) over the shared v58-wire seam, which is the one place carrying the four protocol-58 ops (applyOpacityMask / releaseOpacityMask / attachTextToPath / detachTextFromPath), their capability probes and their refusal reader. RETRACTION RECORDED (commit cb64ab3): the shipped note, the command title and the conformance pin all said EXPORT ONLY — 'the canvas draws the artwork UNMASKED' — which was FALSE and is now inverted. The current pinned sentence is 'the mask renders on canvas and in the exported PDF alike — the Vello backend matches the CPU rasterizer byte-for-byte across luminosity, alpha and both inverted forms'. Cost of the original misread, recorded in the commit: ONE unverified reading produced five wrong artifacts across four repos, including the test that held it in place. Residual (intended, visible): applying a mask removes the MASK artwork from the page z-order",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:42:11.607Z",
        "headline": "All linked evidence is passing (21 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 21,
          "passed": 21,
          "skipped": 0
        }
      },
      "id": "plugin-draw.opacity-mask-commands",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Opacity-mask commands — the paged.draw consumer of the C-28 protocol-58 door"
    },
    {
      "chapter": "plugin-draw",
      "claims": [
        {
          "component": "plugin.draw",
          "note": "Knife: a freehand cut splits every closed path it crosses into pieces (draw-geometry knife + knife-machine). Scissors: cut a path at any point on a segment, not only at anchors. Both one batch. knife, knife-machine, scissors and conformance/knife + conformance/scissors green at draw c880079.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-16T07:57:37.899Z",
        "headline": "All linked evidence is passing (51 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 51,
          "passed": 51,
          "skipped": 0
        }
      },
      "id": "plugin-draw.path-cutting",
      "importance": "normal",
      "since": "2026-10",
      "status": "stable",
      "title": "Knife and Scissors at any point on a path"
    },
    {
      "chapter": "plugin-draw",
      "claims": [
        {
          "component": "plugin.draw",
          "note": "the named engine-op gap is closed: two open paths WELD into one element (leaf count drops, anchors merge, one undo restores both) and a single open path CLOSES (one undo reopens). The '(coincide)' caveat is gone from the title; endpoint-coincide remains the documented fallback for older engines. Feature detection without version sniffing: one deliberately-unknown op makes serde reply with the engine's op vocabulary (cached, never applies, never touches undo)",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "requires core protocol 56 (ClosePath/JoinPaths) — PUBLISHED since; canvas-wasm latest is 0.61.1 and the editor pins it, so the sync-wasm.sh detour this row described is no longer needed (corrected 2026-08-09)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:42:11.607Z",
        "headline": "All linked evidence is passing (10 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 10,
          "passed": 10,
          "skipped": 0
        }
      },
      "id": "plugin-draw.path-weld",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "True Join — closePath / joinPaths over the v56 engine ops"
    },
    {
      "chapter": "plugin-draw",
      "claims": [
        {
          "component": "core.renderer",
          "note": "RFI C-31, the wall — and closing it is genuinely CORE work, not a plugin gap, in three decisions not one: (a) a new PAINT KIND in the engine model (a tile definition — content + bounding box + placement transform — that a Graphic fill can reference); (b) RENDERER support in BOTH backends, meaning tiling in the Vello/WebGPU path as well as the CPU rasterizer, plus a PDF /Pattern colour space — the opacity-mask row is the standing warning that a model field honoured in only one backend is a half-shipped feature; and (c) an IDML REPRESENTATION decision, because InDesign has no pattern swatch either, so this is a paged-only extension and the choice is between an out-of-band part in the .paged container (round-trips only to ourselves) and something an IDML consumer can at least ignore safely",
          "status": "planned"
        },
        {
          "component": "plugin.draw",
          "note": "v1 (2026-08-04) grew the v0 bake into a RE-EDITABLE tile field: grid / brick / hex lattices, tile size, spacing (negative = real geometric overlap), overlap ORDER, copy counts, dimming, ARTBOARD-AWARE tile counts (which retires the v0 out-of-page residual — RFI C-23 no longer bites the default path) and a re-plan / release / un-bake path over a recipe in a .paged container part. 5 commands (makePatternFromSelection, editPatternField, selectPatternTiles, deletePatternTiles, releasePatternField) + panel media.paged.draw.panel.pattern + partType patternRecipe. A compound source still tiles WITH its hole. PARTIAL BY CONSTRUCTION — THE SWATCH HALF IS A WALL (RFI C-31): IDML gives a page item a FillColor resolving to a Color / Gradient / Mixed-Ink swatch; there is no pattern paint kind in the FORMAT, none in paged_model::Graphic and none on the WIRE (SwatchSpec and GradientSpec are the only two shapes createSwatch/createGradient accept), so a bundle has nothing to mint and nothing to reference. Inventing a third kind would render nothing on canvas and lie on save. What ships is therefore ARTWORK — real page items you can re-plan, release or delete, but NOT a fill you can apply to another object — and that boundary is stated in the module header, the command title, the panel note and the exported PATTERN_SWATCH_NOTE constant",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:42:01.475Z",
        "headline": "All linked evidence is passing (60 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 60,
          "passed": 60,
          "skipped": 0
        }
      },
      "id": "plugin-draw.pattern-bake",
      "importance": "normal",
      "since": "2026-08",
      "status": "experimental",
      "title": "Pattern editing v1 — a re-editable tile field (step-and-repeat artwork, NOT a pattern swatch)"
    },
    {
      "chapter": "plugin-draw",
      "claims": [
        {
          "component": "plugin.draw",
          "note": "PenMachine v2 works on paths that exist: press an open endpoint to continue it, end on its other endpoint to close it, end on another open path's endpoint to join the two; click an anchor or segment of the selected path to delete or add one. penPlanMutation lowers each plan to ONE mutation (insertPath or one batch), so each is one undo step. The Pen ranks several hits itself. pen-machine-existing-paths, machine-requests and conformance/pen-existing-paths green at draw c880079.",
          "status": "shipped"
        },
        {
          "component": "editor.gesture",
          "note": "The editor's built-in Pen is now the host half over PenMachine (editor a4865d5, on main) instead of a hand-written copy; new-path behaviour unchanged (gesture-pen, draw-plugin and the pen journey pass).",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-05T09:37:27Z",
        "evidence_at": "2026-10-18T04:39:58.048Z",
        "headline": "All linked evidence is passing (98 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 98,
          "passed": 98,
          "skipped": 0
        }
      },
      "id": "plugin-draw.pen-existing-paths",
      "importance": "normal",
      "since": "2026-10",
      "status": "stable",
      "title": "Pen v2 — continue, close and join existing paths; add / delete on the selected path"
    },
    {
      "chapter": "plugin-draw",
      "claims": [
        {
          "component": "plugin.draw",
          "note": "PenMachine pure pt-in/snapshot-out; pen-machine.spec (clicks, smooth drags, closing, preview). NOT registered by the bundle since decision 14 (W2.5 division: the built-in Pen authors paths; the bundle contributes anchor editing) — machine retained as the reference implementation",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:40:14.51Z",
        "headline": "All linked evidence is passing (32 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 32,
          "passed": 32,
          "skipped": 0
        }
      },
      "id": "plugin-draw.pen-machine",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Pen tool state machine (click/drag anchors, modifier matrix, close, commit)"
    },
    {
      "chapter": "plugin-draw",
      "claims": [
        {
          "component": "plugin.draw",
          "note": "the machine samples pointer pressure alongside points; RDP gains an index-form (simplifyRdpIndices) so kept anchors keep their pressures 1:1; the commit carries pressures[] and the handler maps them onto a width profile (0.35–4pt) applied as ONE post-insertPath setElementProperty(outlineStrokeVariable) — the wire op existed all along, the gap was machine-side sampling. Open contours with varying pressure only; constant pressure stays a plain stroke (no fake variance). Ships in draw canary.4",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-16T07:57:37.802Z",
        "headline": "All linked evidence is passing (19 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 19,
          "passed": 19,
          "skipped": 0
        }
      },
      "id": "plugin-draw.pencil-pressure",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Pressure-aware pencil — variable-width strokes via outlineStrokeVariable"
    },
    {
      "chapter": "plugin-draw",
      "claims": [
        {
          "component": "plugin.draw",
          "note": "Counts the work a tool or command does at the host doors (engine reads, batches, tree reads) on documents big enough to show it, and pins a budget per flow: freehand, region, command and import suites plus geometry benches (`pnpm bench`). A budget is a count, not a timing, so it is stable on CI. perf/perf-budgets* green at draw c880079.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-16T07:57:37.997Z",
        "headline": "All linked evidence is passing (85 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 85,
          "passed": 85,
          "skipped": 0
        }
      },
      "id": "plugin-draw.perf-budgets",
      "importance": "normal",
      "since": "2026-10",
      "status": "stable",
      "title": "Work budgets for every suspected hot path"
    },
    {
      "chapter": "plugin-draw",
      "claims": [
        {
          "component": "plugin.draw",
          "note": "7 tools + 17 commands (manifest 0.2.3 -> 0.3.0). Host-agnostic machines in draw-tools (curvature/pencil/measure) + a draw-geometry spline fit; bundle handlers + commands drive the EXISTING v43 wire ops (outlineStroke/offsetPath/simplifyPath/pathfinder*/join/average + a gradient annotator + an arrowheads panel). Conformance against the real engine, ~190 tests. RENDER-VERIFY (editor render journey draw-render.journey, 2026-06-18): the bundle's linear-gradient fill (fillGradientLinear) + Appearance stroke + pathfinder-unite produce VERIFIED rendered change on the page (before/after requestSnapshot pixel diff) on BOTH the CPU and real-WebGPU lanes — the vector output reaches the page, not just the model. A built-in negative control (blank page render-stable) proves the oracle does not false-positive",
          "status": "shipped"
        },
        {
          "component": "plugin.api",
          "note": "ENGINE FINDINGS pinned for the RFI (honest gaps): RESOLVED (2026-06-18, core fix, ships paired on the next publish) — outlineStroke/offsetPath used to no-op on a PLAIN rectangle because an editor-created rect carries EMPTY anchors (the renderer draws it from bounds) and the kernels reject empty/<3-anchor input; the apply layer (paged-mutate apply_path_kernel_op) now synthesizes the closed rect path from the frame bounds before the kernel (bounds_to_rectangle_anchors, the renderer's Geometry::Rect equivalent), so a plain rect now strokes/offsets (kernel_ops nextest; HARD under the sync-wasm override, soft-noted in draw-pathops.journey on published 0.49.0 until the paired publish). offsetPath still requires a CLOSED path for the inset/outset lane (an OPEN path delegates to the both-sides stroke band, by design); simplifyPath is accepted+undoable but a no-op on anchor count at v0.43 (the decimation kernel does nothing on corner polylines — the plugin emits the op honestly, the geometry reduction is core's contract); true join/weld + a nearest-point facade door are named gaps (Join uses pathPointSet coincidence; measure uses the v0 escape hatch)",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:05:36.604Z",
        "headline": "All linked evidence is passing (238 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 238,
          "passed": 238,
          "skipped": 0
        }
      },
      "id": "plugin-draw.pro-path-toolset",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Phase 4c pro path toolset — curvature/pencil/measure machines + simplify/join/average/offset/outline/pathfinder/gradient/arrowheads commands"
    },
    {
      "chapter": "plugin-draw",
      "claims": [
        {
          "component": "plugin.draw",
          "note": "Reflect about a horizontal, vertical or typed axis, optionally as a copy, and Transform Again repeating the last transform; affine algebra in draw-geometry. Reflect Copy re-pinned on engine 0.65.0 (draw 4855349). conformance/transform, affine and panels/reflect-section green at draw c880079.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-16T07:57:37.983Z",
        "headline": "All linked evidence is passing (54 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 54,
          "passed": 54,
          "skipped": 0
        }
      },
      "id": "plugin-draw.reflect-transform",
      "importance": "normal",
      "since": "2026-10",
      "status": "stable",
      "title": "Reflect (horizontal, vertical, a typed axis, a copy) and Transform Again"
    },
    {
      "chapter": "plugin-draw",
      "claims": [
        {
          "component": "plugin.draw",
          "note": "eeee71b. 7 commands (makeRadialRepeat / makeGridRepeat / makeMirrorRepeat / updateRepeat / selectRepeatInstances / expandRepeat / releaseRepeat) + tool media.paged.draw.tool.repeat + panel media.paged.draw.panel.repeat + partType repeatRecipe (the FIFTH recipe part in this repo). A REPEAT IS NOT A PATTERN and the bundle keeps them apart: a pattern is a swatch-shaped FILL (whose swatch half is a wall, RFI C-31), a repeat is an object TRANSFORM with expand/release, so the placement algebra is a full AFFINE per instance in draw-geometry/src/repeat.ts — it ROTATES and REFLECTS where pattern v1's tile field only translates. THE FIRST ONE-UNDO BAKE IN THIS REPO: every earlier bake here pays two steps to RFI C-15, and plugin-sdk bc52766 put bindCreated in the contract, so one batch now inserts, binds, paints, links and groups (200 instances apply in ~12 ms; ONE undo removes all of them). Four bindCreated facts MEASURED against the booted engine (protocol 59) and pinned: the bind must FOLLOW its creating child (before it the batch is refused by name), it is its OWN op (a handle inside a creating op's args is silently ignored), the handle reference resolves in every id position this module uses (including a bare-string deleteFrame.frameId and the framePath compound re-merge door), and a bind placed after a createGroup resolves inconsistently when an EARLIER bind exists — so Update reads the previous group out of the scene tree first. CLIPPING is real over B-18's pasteInto and costs the SECOND batch for a stated reason that is not contract skew: pasteInto is what hides an instance from document.tree(), and a batch outcome carries ONE createdId, so the honest enumeration of what was minted has to be taken BEFORE the paste. Undo counts are ASSERTED, not claimed: make/update = 1 (2 when clipped), expand = 1, release = 1, select = 0. EXPAND and RELEASE are Illustrator's two verbs and mean different things (expand keeps every instance as artwork and drops the recipe; release removes the instances and keeps the source) — pattern v1's pair spelled the other way round. PARTIAL, bounded exactly where the engine bounds it and pinned verbatim by REPEAT_LIVE_NOTE (7 assertions): (a) a repeat is NOT a live linked object — the engine has no repeat node, editing a source does not move the instances, Update does, the same honesty Live Paint's REGENERABLE carries; (b) the on-canvas drag steers ONE parameter per kind and the overlay draws a GUIDE, because overlay.setToolPreview takes one polyline and cannot draw N instance outlines, and the artwork is rebuilt once on pointer-up because a re-plan per pointer-move is an undo step per sample; (c) a clipped instance is INVISIBLE to document.tree() while still answering geometry and metadata by id, so the recipe is its only index and clipping DEGRADES OFF with a warning on a host with no container writer rather than minting artwork nothing can enumerate again",
          "status": "partial"
        },
        {
          "component": "plugin.api",
          "note": "7 commands via host.contribute.command, the panel via host.contribute.panel (React expert-leaf, icon panel-object-transform — a REAL token in the host glyph map; the stroke panel's recorded lesson is that an invented token renders the dock tab iconless), and the tool via the host-free PRO_TOOL_IDS pattern in tools.ts. THE REPEAT TOOL SHIPS WITH NO SHORTCUT and that is a decision recorded at the tool definition, not an omission: the three registers the editor freed by retiring its dead rail entries (editor 2e6f835 — shift+t, i, k) are each the canonical key of a paged.draw tool currently sitting on a substitute (typeOnPath on shift+h, eyedropper on shift+d, livePaintBucket on shift+o), and shift+z reads as an undo variant on every platform. Ships in draw 0.1.0-canary.8, which is what editor apps/canvas pins",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:41:51.205Z",
        "headline": "All linked evidence is passing (116 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 116,
          "passed": 116,
          "skipped": 0
        }
      },
      "id": "plugin-draw.repeats",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Repeats v1 — radial / grid / mirror, and the repo's first ONE-undo build"
    },
    {
      "chapter": "plugin-draw",
      "claims": [
        {
          "component": "plugin.draw",
          "note": "Reverses the anchor order of the selected paths, one batch. Visible only in the winding (a compound fill, an arrowhead end, a dash phase); conformance/reverse-path green at draw c880079.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-16T07:57:37.897Z",
        "headline": "All linked evidence is passing (12 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 12,
          "passed": 12,
          "skipped": 0
        }
      },
      "id": "plugin-draw.reverse-path",
      "importance": "normal",
      "since": "2026-10",
      "status": "stable",
      "title": "Reverse Path Direction"
    },
    {
      "chapter": "plugin-draw",
      "claims": [
        {
          "component": "plugin.draw",
          "note": "Phase 9 Tier A: three pure-SELECTION commands (no mutation) — read the first-selected element's fill/stroke/stroke-weight via host.document.elementProperties, flatten the scene tree to its leaf elements (groups descended), read each candidate's same property, and host.selection.set the matches (the reference included). Stroke-weight compares with a small pt tolerance; colors compare exactly. HONEST engine finding (pinned): a GraphicLine reads back a null frameFillColor even when authored FillColor=Black, so Select-same-by-fill matches the fillable kinds (rectangle + polygon), not the line — correct (Illustrator ignores unfilled lines), and the spec pins it so a future engine change fails loudly. 6 conformance tests Updated 2026-10-05: Select Same stroke weight takes a tolerance; all three criteria answer in one parallel pass, kept per revision (draw ed9c5a5, e60a972).",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:04:50.605Z",
        "headline": "All linked evidence is passing (14 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 14,
          "passed": 14,
          "skipped": 0
        }
      },
      "id": "plugin-draw.select-same",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Select-same by fill / stroke / stroke-weight (Tier A — pure selection, no mutation)"
    },
    {
      "chapter": "plugin-draw",
      "claims": [
        {
          "component": "plugin.draw",
          "note": "Region level: hover outlines the face of the planar arrangement under the cursor, a drag collects the faces it crosses in first-cross order, an Alt-drag deletes them. At release a pure planner (draw-tools planShapeBuild) merges the crossed faces into one path owned by the topmost input it crossed and keeps every other face of a touched input as a piece, in the paint of the input on top of it there; an input the gesture never touched is left alone. The build is one batch and one undo step (an input that keeps a piece is rewritten in place, further pieces are inserted, the merged faces are united by the engine's pathfinderFaces); a gesture that crosses every face of what it touches sends pathfinderFaces { keep } as before. Ovals are merged along their curve: at release each oval is read through an unpainted stand-in path of its ellipse, probed and withdrawn with undo, so history gains only the build. Fixed in @paged-media/draw 0.1.0-canary.19 (plugin-draw #2; the editor pins it): a drag across part of a cross-shaped overlap used to leave only the crossed faces (the centre and the other arms vanished), and two overlapping ellipses merged into a rectangle. Limits: over a selection that holds an oval no face is highlighted while hovering (the bundle resolves oval sets only at release, through the stand-ins, although core answers ovals with their ellipse since v0.71.8); with nothing selected the element-level sweep (hit-test → pathfinderBoolean) still hit-tests every move. Hover over a selection costs no round trip per move (200 moves: 0 hitTest, 2 planarRegions, 1 pathAnchors; budgets in test/perf/perf-budgets.spec.ts).",
          "status": "shipped"
        },
        {
          "component": "plugin.api",
          "note": "RFI B-22 CLOSED and K-11 shipped: host.document.planarRegions(elementIds, point?) is the published facade and the handler calls it (handlers/planar-regions.ts); the per-face boolean is the pathfinderFaces wire op. Caps REFUSE at 12 inputs / 256 faces. Since core v0.71.8 (canvas-wasm 0.71.8; the editor pins 0.71.9) pathAnchors and the arrangement behind planarRegions, pathfinderFaces and pathfinderBoolean answer an oval with its four-arc ellipse; before, an oval had no path and the arrangement used its bounding box. Not provided: gap detection (RFI C-30)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-12T18:29:40Z",
        "evidence_at": "2026-10-18T07:47:24.309Z",
        "headline": "All linked evidence is passing (69 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 69,
          "passed": 69,
          "skipped": 0
        }
      },
      "id": "plugin-draw.shape-builder",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Shape Builder gesture tool — drag merges the faces it crosses, Alt-drag deletes them"
    },
    {
      "chapter": "plugin-draw",
      "claims": [
        {
          "component": "plugin.draw",
          "note": "snapPoint in draw-geometry: snaps to the page's edges, centre lines and corners, alignment lines, segments and the tool's own placed anchors (6 screen px; Cmd turns it off). Among the bundle's tools only Curvature snaps; snapPoint is exported for a host's Pen and Direct Selection (RFI C-68). snap and conformance/snapping green at draw c880079. v67 — RELEASED 2026-10-05: core v0.67.0 (715e130), plugin-sdk 0.2.39-canary.0, draw 0.1.0-canary.15, editor main 072e3df. Curvature asks the engine through host.document.snapPoint (plugin-sdk 0.2.39) and falls back to snapPoint when the engine does not answer; conformance/snapping proves a point landing on another path's anchor.",
          "status": "shipped"
        },
        {
          "component": "editor.gesture",
          "note": "Pen + Direct Selection snap through snapPoint, with View ▸ Snap to points as the toggle (editor 8667db0, on main since 445788c). v67 — RELEASED 2026-10-05: core v0.67.0 (715e130), plugin-sdk 0.2.39-canary.0, draw 0.1.0-canary.15, editor main 072e3df. Both ask the engine first (AC-SNAP-3: a dragged anchor lands on another path's anchor).",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "note": "v67 — RELEASED 2026-10-05: core v0.67.0 (715e130), plugin-sdk 0.2.39-canary.0, draw 0.1.0-canary.15, editor main 072e3df. The engine resolves a snap: requestSnapPoint → snapPoint and setSnapSettings snap a point against every visible element's anchors, centres and outlines, the page, ruler guides, the grid and the x / y lines through them (core ADR 125); unit, engine and wire tests in core.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-05T13:01:45Z",
        "evidence_at": "2026-10-18T07:49:30.71Z",
        "headline": "All linked evidence is passing (30 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 30,
          "passed": 30,
          "skipped": 0
        }
      },
      "id": "plugin-draw.snapping",
      "importance": "normal",
      "since": "2026-10",
      "status": "stable",
      "title": "Point snapping — page edges, centre lines, corners, own anchors"
    },
    {
      "chapter": "plugin-draw",
      "claims": [
        {
          "component": "plugin.draw",
          "note": "B-12 — dashMutationFor builds setElementProperty{frameStrokeDashArray, lengths[]}; dash-commands.spec (per-preset wire shape, Solid clears) Updated 2026-10-05: a dash editor with up to three dash/gap pairs, read back for the selection, sits beside the four presets (draw 4c8393a; panels/dash-editor.spec).",
          "status": "shipped"
        },
        {
          "component": "plugin.api",
          "note": "4 commands media.paged.draw.command.strokeDash{Solid,Dashed,Dotted,DashDot} registered via host.contribute.command; each lands applied:true on a selected stroked rect at the real engine (dash-commands.spec)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:03:47.175Z",
        "headline": "All linked evidence is passing (20 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 20,
          "passed": 20,
          "skipped": 0
        }
      },
      "id": "plugin-draw.stroke-dash-commands",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Stroke dash-pattern command presets (Solid/Dashed/Dotted/DashDot)"
    },
    {
      "chapter": "plugin-draw",
      "claims": [
        {
          "component": "plugin.draw",
          "note": "first consumer of the W3.1 schema-panel + bindings door (visibleWhen/enabledWhen); replaces the React expert-leaf for stroke controls",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-11T17:03:11Z",
        "evidence_at": "2026-10-16T07:57:37.986Z",
        "headline": "All linked evidence is passing (18 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 18,
          "passed": 18,
          "skipped": 0
        }
      },
      "id": "plugin-draw.stroke-schema-panel",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Stroke panel as a declarative schema panel (W3.1 adoption)"
    },
    {
      "chapter": "plugin-draw",
      "claims": [
        {
          "component": "plugin.draw",
          "note": "Phase 8: a pure host-free SVG substrate in draw-geometry — svg-path.ts (`d` parser → the cubic anchor model: M/L/H/V/C/S/Q/T/A/Z absolute+relative, implicit-lineto-after-moveto, compound subpaths; quadratics elevate to cubic, smooth S/T reflect the prior control point; the inverse serializer emits M/L/C/Z, topology round-trip stable), svg-arc.ts (A/a via endpoint→center parameterization, sliced ≤90° + cubic-approximated by k=4/3·tan(θ/4), λ-corrected radii), svg-shapes.ts (rect/rounded-rect/circle/ellipse/line/polyline/polygon → path equivalents, κ corners), svg-doc.ts (a dependency-light hand-rolled XML walk) Updated 2026-10-05: one imported file is one batch and one undo step, and the created ids come off the batch reply (draw bb2a4f2, f23a8f2).",
          "status": "shipped"
        },
        {
          "component": "plugin.api",
          "note": "registered via contribute.importer (media.paged.draw.importer.svg, joins File/Open + drag-drop) + contribute.exporter (media.paged.draw.exporter.svg, the Export Center) — the K-2 IO surface (plugin-platform.importer-exporter); the second importer/exporter consumer after sheets .xlsx",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:05:06.374Z",
        "headline": "All linked evidence is passing (203 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 203,
          "passed": 203,
          "skipped": 0
        }
      },
      "id": "plugin-draw.svg-io",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "SVG import + export — the K-2 interchange round-trip (Phase 8)"
    },
    {
      "chapter": "plugin-draw",
      "claims": [
        {
          "component": "plugin.draw",
          "note": "definition + instance with redefine, break-link and reset-transform, stored beside the graphic-style library (partType symbolLibrary). 7 commands (defineSymbol / placeSymbolInstance / redefineSymbol / breakSymbolLink / resetSymbolTransform / renameSymbol / deleteSymbol) + panel media.paged.draw.panel.symbols. PARTIAL — three honest ceilings stated in code: instances are STATIC (no per-instance overrides; redefine overwrites every one); there is no duplicateElement wire op, so an instance is re-emitted via insertPath and a rebuild MINTS NEW element ids; and TEXT IS REFUSED AT DEFINE TIME because no mutation duplicates a story. The eight symbol-SET tools are catalog P2 and explicitly not built",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:41:41.645Z",
        "headline": "All linked evidence is passing (58 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 58,
          "passed": 58,
          "skipped": 0
        }
      },
      "id": "plugin-draw.symbols",
      "importance": "normal",
      "since": "2026-08",
      "status": "experimental",
      "title": "Symbols v0 — a named artwork DEFINITION and instances that follow it"
    },
    {
      "chapter": "plugin-draw",
      "claims": [
        {
          "component": "plugin.draw",
          "note": "Pencil, Paintbrush, Blob Brush, Eraser, Width and Lasso Select publish their settings (nib, eraser size, fidelity, width profile, lasso by intersection or centre) to the host's tool-options popover; setOptions() changes the zoom-dependent tolerances in place. The menu's \"…\" entries open a Path Options panel where Offset (miter / round / bevel joins, miter limit), Simplify and the inserts take typed parameters. conformance/tool-options and panels/path-options-panel green at draw c880079.",
          "status": "shipped"
        },
        {
          "component": "plugin.api",
          "note": "Rides the host tool-options door and host.contribute.panel; no engine change.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-16T07:57:37.977Z",
        "headline": "All linked evidence is passing (52 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 52,
          "passed": 52,
          "skipped": 0
        }
      },
      "id": "plugin-draw.tool-options",
      "importance": "normal",
      "since": "2026-10",
      "status": "stable",
      "title": "Tool options and the Path Options panel"
    },
    {
      "chapter": "plugin-draw",
      "claims": [
        {
          "component": "plugin.draw",
          "note": "width v0 (drag builds a peaked per-anchor profile, baked destructively via outlineStrokeVariable — single-contour open paths only); blend v0 (anchor/handle interpolation + fill interpolation, structure-matched, honest diagnostic on mismatch — TWO batches because RFI C-15 makes setDocumentDefaults unbatchable) — SUPERSEDED 2026-08-05 by plugin-draw.blends, which grew this one clause into the whole §16.2 row (spine, three spacing modes, two reverse verbs, easing, expand/release) and pays ONE undo step; the v0 exports blendSourceFrom/blendStructureMatches survive unchanged, the two v0 batch builders do not; lassoSelect (element-center point-in-polygon over the scene tree); selectParentGroup (nearest group ancestor — raised RFI C-16, no per-element parentOf read exists so it re-reads the tree per press); insertArc/insertSpiral/insertRectGrid/insertPolarGrid as fixed-geometry v0 commands Updated 2026-10-05: the lasso selects by outline intersection by default, by centre on request (draw 53a60c5).",
          "status": "shipped"
        },
        {
          "component": "plugin.api",
          "note": "shift+s (width), shift+q (lasso); the rest are commands",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:41:10.345Z",
        "headline": "All linked evidence is passing (96 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 96,
          "passed": 96,
          "skipped": 0
        }
      },
      "id": "plugin-draw.tool-wave-2",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Width, blend, lasso-select, parent-group cycling, parametric generators"
    },
    {
      "chapter": "plugin-draw",
      "claims": [
        {
          "component": "plugin.draw",
          "note": "Attach an existing, unflowed story to a path and detach it (C-29, engine protocol 58). No story is created; options cannot be edited in place — detach and apply again. conformance/text-on-path green at draw c880079.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-16T07:57:37.923Z",
        "headline": "All linked evidence is passing (28 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 28,
          "passed": 28,
          "skipped": 0
        }
      },
      "id": "plugin-draw.type-on-path",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Type on a path — attach an existing story to a path, detach it"
    },
    {
      "chapter": "plugin-draw",
      "claims": [
        {
          "component": "plugin.draw",
          "note": "Every multi-step flow is one batch and one undo step where the engine allows it (pattern, symbols, live paint, blend, repeat, compound release, brush strokes, SVG import); what a write created comes off the batch reply through one seam, not two tree reads. The document's links are read once per revision into one shared link index; panels reload once per burst, newest wins. Exceptions that stay two steps: appearance bake, image trace, a clipped repeat. conformance/minted, conformance/link-index and panels/reload green at draw c880079. On protocol-66-consumers (branch) the link index reads pluginMetadata off the tree.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-16T07:57:37.983Z",
        "headline": "All linked evidence is passing (51 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 51,
          "passed": 51,
          "skipped": 0
        }
      },
      "id": "plugin-draw.write-batching",
      "importance": "normal",
      "since": "2026-10",
      "status": "stable",
      "title": "One batch per flow — created ids off the reply, one shared link index"
    },
    {
      "chapter": "plugin-pdf",
      "claims": [
        {
          "component": "plugin.pdf",
          "note": "PDFium's per-char text lane yields PositionedItems (point coordinates, top-left origin) and extract.ts — PURE, no reader dependency, which is why it is unit-testable without a PDF or a browser — turns them into TextFrameIr: baseline clustering into lines, word/ space recovery from kerning gaps, same-style adjacent run merging, position-preserving per-line frames with the baseline restored, and a column-gutter split that does NOT fire on ordinary word spacing. 14 vitest across word/space recovery, style runs, line grouping, position-preserving frames, geometry + confidence, and the PDFium per-glyph size normalization. PARTIAL and honestly labelled in the source: v1 is ONE paragraph per visual line (preserve-layout — no line-merging that could wrongly join paragraphs); reflowable paragraph grouping (leading/indent detection) and columns are Phase 3. A page whose content does not clear the per-page confidence gate keeps a full-page raster background beneath it (PageIr.raster).",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:01:24.176Z",
        "headline": "All linked evidence is passing (14 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 14,
          "passed": 14,
          "skipped": 0
        }
      },
      "id": "plugin-pdf.editable-text",
      "importance": "normal",
      "since": "2026-07",
      "status": "experimental",
      "title": "Editable text reconstruction — positioned glyphs → paragraphs of styled runs"
    },
    {
      "chapter": "plugin-pdf",
      "claims": [
        {
          "component": "plugin.pdf",
          "note": "The mixed-page path (slice 2, 0c64e7d; coalescing added by the PDFium migration): PDFium path objects become VectorIr with real segments (moveTo/lineTo/bezierTo) and fill/stroke colours, and consecutive objects sharing fill/stroke/width coalesce into ONE compound polygon via vectorStyleKey — an image or form object breaks the run. That is safe because same-colour disjoint fills paint order-independently, which is what lets ~1000 identical halftone dots become ~1 editable polygon. On the Rust side build.rs maps each VectorIr to a paged_model::Polygon whose anchors + subpath boundaries carry the geometry, and a Palette DEDUPES PDF sRGB into ColorEntry swatches keyed 'Color/pdf_r_g_b' that fill_color/stroke_color reference — so an imported PDF colour is a real document swatch, not a literal. Split verification, stated rather than blurred: the RUST half (VectorIr → Polygon + deduped swatch) is pinned by pdf-import's vector_maps_to_polygon_with_swatch nextest; the TS half (extractPath + the vectorStyleKey coalescing) has NO test in the bundle's vitest lane and rests on the in-browser frame counts recorded in 85a24a6.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:01:17.326Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "plugin-pdf.editable-vector",
      "importance": "normal",
      "since": "2026-07",
      "status": "stable",
      "title": "Editable vectors + PDF colours mapped to document swatches"
    },
    {
      "chapter": "plugin-pdf",
      "claims": [
        {
          "component": "plugin.pdf",
          "note": "the image IR was DCTDecode-gated (every other filter silently dropped the image — the coverage spec's one real remaining import gap). Non-JPEG image objects now decode through FPDFImageObj_GetBitmap (PDFium applies the filter chain), swizzle Gray/BGR/BGRx/BGRA to RGBA, and encode PNG via the raster fallback's encoder; unknown formats stay honest skips. JPEG passthrough unchanged. Ships in pdf canary 0.2.5 (the bundle is at 0.2.6-canary.0 as of 2026-08-09)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "plugin-pdf.image-filters",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Non-JPEG image import — PDFium bitmap decode for every filter chain"
    },
    {
      "chapter": "plugin-pdf",
      "claims": [
        {
          "component": "plugin.pdf",
          "note": "The mapper is PDF-BLIND by design — it takes a reading-ordered Document IR (JSON) and owns no heuristics; those live in the TS bundle next to the reader. build.rs lowers IR → paged_scene::Document (image frames are Rectangles with inline image_bytes, text frames are a TextFrame + Story pair honouring the model's one hard contract that TextFrame.parent_story == ParsedStory.self_id, cross-kind draw order recorded in Spread::frames_in_order), and ocf.rs wraps it in the exact ZIP skeleton the engine's load sniff requires: STORED-first `mimetype` == the IDML-package constant, a designmap.xml entry, and the truth in paged/core/model/document.pgm — which paged_store::from_bytes decodes with NO IDML parse. The designmap/Graphic/Styles/BackingStory parts are a deliberate VALID-BUT-EMPTY fallback: if the pgm ever fails to decode (version drift) the load degrades to a blank one-page document instead of a parse error. Cross-crate coupling stated in the source: the skeleton mirrors paged-canvas::blank::blank_idml, which this crate cannot call (paged-canvas is not a dependency), so the two must be kept in sync BY HAND. wasm surface is target-gated (cfg wasm32) so the pure mapper stays native-testable — 3 nextest, green locally 2026-08-09: ir_maps_to_paged_ocf_with_editable_text_and_images, vector_maps_to_polygon_with_swatch, pgm_round_trips_are_stable.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:01:17.319Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "plugin-pdf.native-ocf-writer",
      "importance": "normal",
      "since": "2026-07",
      "status": "stable",
      "title": "crates/pdf-import writes the native `.paged` OCF the engine load path expects"
    },
    {
      "chapter": "plugin-pdf",
      "claims": [
        {
          "component": "plugin.pdf",
          "note": "The editable path is the DEFAULT, not the next slice — io/pdf.ts importPdf tries tryEditableImport first and only rasterizes on a null/ throw, and the whole reader is PDFium. Both halves are real: the raster fallback (buildIdmlFromRasters, 4 vitest) and the editable reconstruction (extract.ts, 14 vitest; crates/pdf-import, 3 nextest). PARTIAL, two named reasons: (a) the importer's own ORCHESTRATION — the best-first choice, the fallback trigger, the capability gates, the fail-LOUD-never-throw catch — has NO test at all; io/pdf.ts is verified by reading only, and it is the file the two tested halves hang off. (b) Reconstruction is v1 scope by its own admission (extract.ts): one paragraph per visual line, preserve-layout; reflowable paragraph grouping and columns are Phase 3. Was: 'Phase 0 SHIPPED (published to npm): per-page pdf.js rasterization → inline-image IDML → host.nativeDocument.open; opens a PDF as one full-page image per page. Phase 1 core landed (pdf-import crate: Document IR → native .paged, round-trip tested). PARTIAL: text/vector reconstruction (extract.ts/reconstruct.ts) is the in-progress next slice.' — the named file `reconstruct.ts` DOES NOT EXIST and never did in this repo; the heuristics live in extract.ts and the PDF I/O that feeds them lives in pdfium.ts. Stale `reconstruct.ts` references survive in three source comments (io/pdf.ts, extract.ts, activate.ts), which is where the row got it from.",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:09:29.102Z",
        "headline": "All linked evidence is passing (3 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 3,
          "passed": 3,
          "skipped": 0
        }
      },
      "id": "plugin-pdf.pdf-import",
      "importance": "normal",
      "since": "2026-07",
      "status": "experimental",
      "title": "Open/read PDF files in the editor (.pdf importer → native document)"
    },
    {
      "chapter": "plugin-pdf",
      "claims": [
        {
          "component": "plugin.pdf",
          "note": "85a24a6 (2026-07-24) replaced pdf.js with Google's PDFium (paulocoutinhox/pdfium-lib release wasm, vendored into bundle bin/, lazy-loaded on first open). The reason is architectural, not performance: PDFium exposes a structured CONTENT model — typed page objects with matrices, colours, path segments and ORIGINAL encoded image bytes — where pdf.js exposes a rendering op-list, and faithful decomposition needs the former. The IR seam HELD: only the TS extraction layer changed; crates/pdf-import and the editor were untouched. Three fixes rode along, each with a measured number in the commit: the hot per-char loop moved off Emscripten cwrap onto raw wasmExports (extraction 16s → ~1ms); consecutive same-style path objects coalesce into one compound polygon (meldezettel 426 → 157 frames, brochure 1962 → 1000); and normalizeSegmentSize snaps PDFium's per-glyph ink-height font sizes to a line-segment's dominant size before run-merge (2880 runs → 148 for 6318 chars; import 8.7s → 2.95s), which is MORE faithful, not less. Verified in-browser at the time on two real documents. Tests here are the heuristics', not the reader's: extract.test.ts pins the size-snapping in both forms (2 of its 14). The reader itself is browser-only (the 5.2 MB wasm) and has no headless test — stated rather than implied.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "plugin-pdf.pdfium-reader",
      "importance": "normal",
      "since": "2026-07",
      "status": "stable",
      "title": "PDFium is the reader engine (pdf.js dropped)"
    },
    {
      "chapter": "plugin-pdf",
      "claims": [
        {
          "component": "plugin.pdf",
          "note": "importPdf is a typed command listed with its schema's args and result, opening from bytes alone (every page, mode auto) or with maxPages; the image path needs a canvas and fails honestly under Node, keeping the document (ADR 323).",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:01:24.171Z",
        "headline": "All linked evidence is passing (8 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 8,
          "passed": 8,
          "skipped": 0
        }
      },
      "id": "plugin-pdf.typed-commands",
      "importance": "normal",
      "since": "2026-10",
      "status": "stable",
      "title": "PDF import as a typed command"
    },
    {
      "chapter": "plugin-platform",
      "claims": [
        {
          "component": "plugin.api",
          "note": "the published contract had no arm for core's v57 batch-handle op. bindCreated names the id a creating sibling is ABOUT to mint so a later child of the SAME batch can address it as '$h:<handle>' — collapsing what were two batches, and therefore two undo steps, into one. Generalises the v34 $created sentinel from 'the most recent creating child' to any named one. Three rules stated on the type: the bind must come AFTER the creating child (else the batch is refused BY NAME); it is its own OP, not a field (a handle inside a creating op's args is silently ignored); and its scope is the declaring batch — visible to later children including nested batches, never outward, and '$h:' inside TEXT content is content",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:03:58.661Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "plugin-platform.bind-created",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "bindCreated — naming a to-be-minted id so a two-batch flow collapses to one undo (C-15)"
    },
    {
      "chapter": "plugin-platform",
      "claims": [
        {
          "component": "plugin.api",
          "note": "RFI B-26 closed. The platform had exactly two ways to put a panel on screen (contributeSchemaPanel, contributePanel) and BOTH mint a NEW one — so 'write another Layers panel' was the correct local decision every time, and Layers existed THREE times. ADR 023 inverts it: the host owns the panel, the plugin registers as the value PROVIDER for the current selection. host.contribute.bindingProvider(contextType, provider) returns a BindingProviderHandle whose lifetime is BORROWED from contributeEditContext's onEnter/onExit — no new manifest field and no new capability, the edit-context declaration is the capability. Four declaration lanes on provides — paths / writablePaths / collections / ops — each requiring its callback iff non-empty, checked by three loud registration gates (the context must be declared in the manifest, must be registered by THIS bundle, and the declaration shape must match). The answer vocabulary is what makes it honest: value | mixed | absent | decline are four DIFFERENT facts, and 'absent' must never fall through to core. writablePaths (c3bbc83) is the VALUE-axis addition: it narrows paths to the subset accepting writes, omitted means all of them, and [] is the honest read-only declaration — before it, the property-write lane had NO declaration at all, because writeProperty is a callback and a callback's absence never reaches activeProviders(). Providers resolve CORE vocabulary ONLY, decided in phase A: paged.image's raster RGB/CMYK and paged.web's CSS colour do NOT become swatch providers, because a synthetic path is identity-shaped by construction. ADR primitive 2 (panel SLOTS) is deliberately unbuilt until a real consumer asks",
          "status": "shipped"
        },
        {
          "component": "editor.shell",
          "note": "the host half (08e2ff4) is a deliberate STRUCTURAL MIRROR of plugin-sdk's backend type — host-facing members only, so the editor can never write activation — because @paged-media/shell sits below plugin-api and must not depend on it. One registry object is injected into every createBundleHost AND into PagedShell, so the adapter end and the panel end are the same map. Fall-through to core lives in the seam module ONCE, not per panel. NO HOST PANEL CONTAINS if (pluginId === ...) — grepped across all three; the only plugin-id comparison anywhere is p.plugin === provider inside writableByDeclaration, comparing the answering provider to itself. A LIVE PRE-EXISTING DEFECT the proof exposed: a writability lookup keyed on plugin id shipped an ENABLED control over a READ-ONLY provider, because paged.sheet registers TWO providers on one context and 'the first entry from that plugin' read the swatches one's declaration — the key is (plugin AND declares-the-path). PARTIAL: EditContextContribution.panelIds fights a retargeting panel (all three slices hit it and each re-raises the tab in its own spec) — a context needs a way to declare which SHARED panels it serves, distinct from which of its OWN to raise",
          "status": "partial"
        },
        {
          "component": "plugin.draw",
          "note": "consumer 1 of 3, the COLLECTION axis (plugin-draw.layers-binding-provider)",
          "status": "shipped"
        },
        {
          "component": "plugin.sheet",
          "note": "consumers 2 and 3, the SCOPE and VALUE axes (sheet.binding-providers) — and the reason the (plugin AND path) key defect surfaced, since it registers two providers on one context",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "plugin-platform.binding-providers",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Binding providers — a plugin serves the values a HOST panel binds to (ADR 023, B-26)"
    },
    {
      "chapter": "plugin-platform",
      "claims": [
        {
          "component": "plugin.api",
          "note": "K-4 landed (no publish yet): OPFS-backed store with declared quotas; consumer: sheets S-08 workbook persistence; image Tier-2 residency is the next consumer (M4)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T06:37:31.064Z",
        "headline": "All linked evidence is passing (8 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 8,
          "passed": 8,
          "skipped": 0
        }
      },
      "id": "plugin-platform.blob-store",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Persistent blob store (host.blob, OPFS-backed, per-plugin quota)"
    },
    {
      "chapter": "plugin-platform",
      "claims": [
        {
          "component": "plugin.api",
          "note": "contract + defineBundle/loadBundle shipped; B-09 wall-clock budgets RESOLVED; four bundles (draw/web/sheet/data) compose in the editor. PARTIAL: bundles run in-process — no worker/isolate boundary yet (trust-line gate)",
          "status": "partial"
        },
        {
          "component": "plugin.draw",
          "note": "drawBundle.activate registers 4 tools via contributeTool; dispose-leaves-shell-clean smoke test (activate.spec)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:05:42.933Z",
        "headline": "All linked evidence is passing (38 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 38,
          "passed": 38,
          "skipped": 0
        }
      },
      "id": "plugin-platform.bundle-lifecycle",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Bundle lifecycle — activate(host) → BundleHandle.dispose()"
    },
    {
      "chapter": "plugin-platform",
      "claims": [
        {
          "component": "core.canvas-wasm",
          "note": "Two previously UNGATED owner doors get an optional `caller`, and dispatch routes through set_paged_part_as / set_scene_layer_as. The refusal names the subtree it protected rather than failing anonymously (c34_write_paged_part_confines_a_named_caller_to_its_own_subtree). NO PROTOCOL BUMP, and that is the recorded story of 4d8ecf3: the author bumped 61→62 out of caution, protocol_version_is_v61 failed, and reading why surfaced governance rule 1 stated in the same file — a back-compatible #[serde(default)] field on an EXISTING message kind is not a new message kind. Reverting deleted a whole coordination sequence (core tag, editor pin bump, protocol.ts, sync-wire) that had already been written into the RFI. It ships as a patch to 0.61.x, which is what the 0.<protocol>.<patch> scheme is for. The round-trip test pins what makes that safe: an omitted caller does not appear on the wire AT ALL (not a serialized null), so an existing sender's bytes are unchanged. Note for a future reader: a `(v62)` comment survives in the wire test header — a leftover of the reverted bump; core main's PROTOCOL_VERSION is 61",
          "status": "shipped"
        },
        {
          "component": "plugin.api",
          "note": "The SENDER half, and the reason the gate is worth anything — a gate no sender can open proves nothing. `caller` is filled by the ADAPTER from manifest.id and NEVER passed by the bundle: a value a bundle states about itself is not evidence, so the one place that knows a bundle's true identity is the only place allowed to say it. host.parts.write (15a1909) already built paged/<id>/ paths, but by a convention held in one TS file — a bundle reaching the raw editor handle bypassed it entirely — so declaring the caller moves the check into the engine, which cannot be routed around. The scene-layer half followed (a4b0b1d). Deliberately NO supports() probe: the wire has no deny_unknown_fields, so an engine without the gate ignores the field and behaves exactly as before — there is no degraded path to choose between, and a probe would imply one exists. Carries a NAMED `as never` cast because wire.d.ts is vendored from the published canvas-wasm 0.61.1, whose writePagedPart has no caller; the field exists and is committed in core today and the cast deletes itself at the next publish + sync-wire. STILL OPEN, from the core commit's own words: sceneLayers.submit reaches the wire through a typed PagedEditor member, so having paged.image/draw declare a caller there is not yet wired",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "plugin-platform.caller-gated-doors",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "C-34 — the parts and scene-layer doors name their CALLER (the adapter, never the bundle)"
    },
    {
      "chapter": "plugin-platform",
      "claims": [
        {
          "component": "plugin.api",
          "note": "W3.10 in-process enforcement landed — PluginCapabilityError at the host chokepoints, capabilityMode=enforce default. PARTIAL: engine-boundary enforcement open (trust-line gate)",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T06:37:31.13Z",
        "headline": "All linked evidence is passing (48 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 48,
          "passed": 48,
          "skipped": 0
        }
      },
      "id": "plugin-platform.capability-scopes",
      "importance": "normal",
      "since": null,
      "status": "experimental",
      "title": "Capability declaration — document read/write, rendering surfaces, editContext, network, clipboard"
    },
    {
      "chapter": "plugin-platform",
      "claims": [
        {
          "component": "plugin.api",
          "note": "ADR 019, two halves that share one principle — a contract copied into this repo must be provably identical to its source. (1) VENDORING: sync-catalog.mjs is sync-wire's sibling — where sync-wire vendors the engine WIRE TYPES, this vendors the engine CAPABILITY CATALOG (host fns, settable property paths, id grammar, constraints) that core generates as catalog.json inside @paged-media/introspect-wasm, into packages/plugin-api/src/catalog.json. Same no-warn-skip rule: an unresolvable source EXITS NONZERO, because a check that cannot see its source is not a passing check. (2) SINGLE SOURCE: manifest.schema.json is declared the one home of every CLOSED capability vocabulary (rendering surfaces, asset kinds, clipboard grants, wasm purposes, edit-context scopes/entries, baked fallbacks) and the plugin-api TS unions plus plugin-cli's runtime Sets are PROJECTIONS of it — capability-vocabulary.spec.ts (9) gates all three against the schema, which is what turns 'change the vocabulary in the schema first' from a convention into a failing test. RESIDUAL, named honestly: unlike sync-wire, sync-catalog --check is NOT run in CI — publish.yml gates the wire and not the catalog — and the published-package resolution path stays unexercised until @paged-media/introspect-wasm actually ships catalog.json (the script says so itself and calls that the cross-repo release-ordering signal, not a bug). The vendored copy is dated 2026-06-19",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "plugin-platform.catalog-vendoring",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Capability-catalog vendoring + the single-source vocabulary gate (ADR 019)"
    },
    {
      "chapter": "plugin-platform",
      "claims": [
        {
          "component": "plugin.api",
          "note": "K-6 / S-14 — the home the sheets-mode range copy/paste had nowhere to land. `host.clipboard.read()/write()` carry a `{ text?, tabular? }` payload where `tabular` is a RECTANGULAR grid of cell DISPLAY strings (already number-formatted; the consumer owns re-parsing on paste), plain strings throughout so the door proxies across the future isolate boundary unchanged. Three grant tiers off the existing `capabilities.clipboard` manifest enum, and the tiers are the honest part: 'full' grants text + tabular; 'vector' grants text ONLY (a tabular payload it writes is stripped with one warn, a tabular payload it reads is never surfaced); 'none'/absent DENIES the door, which is the manifest default — so a bundle that never declares clipboard cannot touch the system clipboard by accident. Degrades honestly: no backend wired ⇒ read answers null, write no-ops, supports('clipboard@1') false. 10 vitest (clipboard.spec.ts). TWO live consumers, both declaring clipboard:'full': paged.sheet (sheet-bundle activate.ts + 8 of its own clipboard specs) and paged.web (web-bundle panels/ingest.ts, which gates its clipboard-ingest path on supports('clipboard@1') and falls back to a paste box otherwise)",
          "status": "shipped"
        },
        {
          "component": "editor.shell",
          "note": "apps/canvas/src/plugin-clipboard.ts builds the backend over navigator.clipboard and main.tsx injects it (line 1079). The write path lays down BOTH `text/plain` TSV and a `text/html` <table> via ClipboardItem, so a paste into Excel/Sheets/Word lands a real grid, with a writeText-only fallback and an honest no-op when the realm exposes no clipboard at all (insecure contexts). PARTIAL because the coverage is: the sheet-edit journey drives the grid copy/paste buttons inside a try/catch and explicitly does not assert the result ('the OS-clipboard backend may be unavailable headless, so these collect rather than gate'), so no test currently asserts that the editor backend moved any bytes",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "plugin-platform.clipboard-door",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "System-clipboard door (host.clipboard — K-6 / S-14, text + tabular)"
    },
    {
      "chapter": "plugin-platform",
      "claims": [
        {
          "component": "plugin.api",
          "note": "CommandContribution/KeybindingContribution re-exported from shell. (Was 'draw contributes none yet' — stale since paged.draw 0.5.0, whose manifest declares 92 commands.)",
          "status": "shipped"
        },
        {
          "component": "editor.shell",
          "note": "Every one of paged.draw's 92 declared commands is reachable from the palette AND only from there, and none throws when invoked (plugin-surface/draw.spec.ts, two tests).",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T06:37:31.09Z",
        "headline": "All linked evidence is passing (62 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 62,
          "passed": 62,
          "skipped": 0
        }
      },
      "id": "plugin-platform.command-registration",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Command + keybinding contribution registration"
    },
    {
      "chapter": "plugin-platform",
      "claims": [
        {
          "component": "plugin.api",
          "note": "D-11: host.secrets is a REFERENCE-only credential store — set/forget/exists but DELIBERATELY NO get (secret bytes never cross into the plugin realm). The trust line: a plugin holds a credentialRef string and maps a data source to it; the HOST resolves the ref + injects the live connection (pairs with the D-03 consent door + the data.source.db-attach indirection). Capability-gated on capabilities.secrets (the host gate throws on an undeclared use); without an injected backend, set/forget reject, exists is false, supports('secrets@1') false. Consumer: data DB-attach. v1 secret shape = a connection credential",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T06:37:31.133Z",
        "headline": "All linked evidence is passing (6 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 6,
          "passed": 6,
          "skipped": 0
        }
      },
      "id": "plugin-platform.credential-store",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Host credential store (host.secrets — D-11, reference-only, DELIBERATELY no get)"
    },
    {
      "chapter": "plugin-platform",
      "claims": [
        {
          "component": "plugin.api",
          "note": "D-09 end-to-end: SDK-mediated registry (zero inter-plugin contact), manifest capabilities.dataProviders.publish/consume, editor registry injection; provider: data; consumer: sheets sheetFromDataset (S-15, 2026-06-11). Residual: live cross-plugin e2e + the per-cell encoding amendment",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T06:37:31.07Z",
        "headline": "All linked evidence is passing (6 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 6,
          "passed": 6,
          "skipped": 0
        }
      },
      "id": "plugin-platform.data-providers",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Data-provider registry (host.dataProviders — publish/consume RecordSet across plugins)"
    },
    {
      "chapter": "plugin-platform",
      "claims": [
        {
          "component": "core.parser",
          "note": "Properties/Label KeyValuePairs parsed into the Spread::labels side map, entity-normalized (v33)",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "PluginMetadata op — x-paged: namespace gate, 64 KiB cap, JSON envelope {v,data,engine?}, snapshot inverse incl. was-absent; no render invalidation. B-16 (v36): optional caller field — when set, apply_plugin_metadata enforces key == x-paged:<caller> server-side (caller=None keeps prefix-only back-compat)",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "note": "SetPluginMetadata wire op + x-paged:* entries on elementProperties (v33); $created batch sentinel (v34); caller field (v36, B-16). Polygon/GraphicLine element_properties arms (af54c7c) so polygon metadata now reads back, not just rectangles",
          "status": "shipped"
        },
        {
          "component": "plugin.api",
          "note": "document.getMetadata/setMetadata (0.2.5/0.2.6-canary) — key derived from manifest id, own namespace only; raw-mutate chokepoint rejects foreign keys incl. nested batches. SDK door now passes caller: manifest.id (B-16) so the engine cross-checks even a raw-handle write 2026-10-06: document-scoped plugin metadata — getDocumentMetadata/setDocumentMetadata (protocol 69, ADR 127, capability document.documentMetadata@1), undoable and firing onDidChange; from protocol 70 an IDML export keeps it as a designmap Document Label (plugin-publish 47ab9d9). paged.web keeps its template values there.",
          "status": "shipped"
        },
        {
          "component": "plugin.web",
          "note": "W-02 resolved — WebFrameSource persists as document metadata, round-trips IDML, one-time legacy-storage migration",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:11.759Z",
        "headline": "All linked evidence is passing (65 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 65,
          "passed": 65,
          "skipped": 0
        }
      },
      "id": "plugin-platform.document-metadata",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Plugin document metadata — x-paged Label carrier + get/setMetadata doors"
    },
    {
      "chapter": "plugin-platform",
      "claims": [
        {
          "component": "plugin.api",
          "note": "host.document.onDidOpen(listener) — fires on every document load (File > Open, New, importer opens, documents.open) with docId, page ids and sizes; requires document.read; not replayed for a document already open at activation; capability document.onDidOpen@1 (SDK 0.2.42-canary.0). paged.web (auto-render, parts collection) and paged.sheet use it, with the raw client message as the fallback on older hosts.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T06:37:31.074Z",
        "headline": "All linked evidence is passing (5 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 5,
          "passed": 5,
          "skipped": 0
        }
      },
      "id": "plugin-platform.document-opened",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Document-opened event for plugins"
    },
    {
      "chapter": "plugin-platform",
      "claims": [
        {
          "component": "plugin.api",
          "note": "B-02 RESOLVED — W3.2 un-reserved contribute.editContext/objectType; K-1 modal-session + content-pointer members on EditContext (9c9c074; now in the published plugin-sdk canary, 0.2.24); editor edit-context stack commit/cancel/isActiveDirty + ViewportCanvas pageToContentPoint; consumers: draw vectorGraphic, web source, sheet sheetFrame 2026-10-06 (SDK 0.2.42-canary.0): EnteredEditContext carries pageId/pagePoint/contentPoint on a pointer entry (double-click, Type-tool click), capability editContext.enterPoint@1; paged.web places its caret on the entering double-click.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T06:37:31.081Z",
        "headline": "All linked evidence is passing (20 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 20,
          "passed": 20,
          "skipped": 0
        }
      },
      "id": "plugin-platform.edit-context",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Edit-context registry (enter/exit, write-scope narrowing, breadcrumb)"
    },
    {
      "chapter": "plugin-platform",
      "claims": [
        {
          "component": "plugin.api",
          "note": "K-5 landed with W3 IO; capability-gated; consumer: sheets S-11 (.xlsx open); data's panels not yet migrated onto it",
          "status": "shipped"
        },
        {
          "component": "editor.shell",
          "note": "file-picker backend behind the host door",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T06:37:31.089Z",
        "headline": "All linked evidence is passing (3 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 3,
          "passed": 3,
          "skipped": 0
        }
      },
      "id": "plugin-platform.file-picker",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Host file picker (host.shell.pickFile)"
    },
    {
      "chapter": "plugin-platform",
      "claims": [
        {
          "component": "plugin.api",
          "note": "W3.7 font registration + @font-face composition landed (consumer: web). PARTIAL: editor-served asset BYTES lane incomplete 2026-10-06 (protocol 70, ADR 130; SDK 0.2.42-canary.0): host.assets.registerFont(bytes, family, style?) → Disposable — the one write on the asset door; faces are SCENE-LAYER scoped (RegisterFont.scope \"sceneLayer\"), never seen by document layout, isMissing or preflight; capability assets.registerFont@1.",
          "status": "partial"
        },
        {
          "component": "plugin.web",
          "note": "the paged.web bundle consumes the font door — @font-face parity badge + asset-store font bytes exercised end-to-end (e2e/web-plugin.spec.ts)",
          "status": "partial"
        },
        {
          "component": "editor.panel",
          "note": "font substitution/parity diagnostics surface in the editor Problems panel via the web bundle (e2e/web-plugin.spec.ts AC-WEB-5)",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T06:37:31.061Z",
        "headline": "All linked evidence is passing (12 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 12,
          "passed": 12,
          "skipped": 0
        }
      },
      "id": "plugin-platform.font-asset-serving",
      "importance": "normal",
      "since": "2026-06",
      "status": "experimental",
      "title": "Plugin font registration + asset serving (@font-face parity)"
    },
    {
      "chapter": "plugin-platform",
      "claims": [
        {
          "component": "core.canvas-wasm",
          "note": "C-2 frameChain(storyId) + reflow change events (content-box resize only), v0.38.0",
          "status": "shipped"
        },
        {
          "component": "plugin.api",
          "note": "consumer: sheets live multi-frame pagination (S-05). Residual: D-12 — frameChain for a plugin's OWN owned frames (data's record flow)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T06:37:31.089Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "plugin-platform.frame-chain-read",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Frame-chain read + reflow events (host.document.frameChain, DocumentChangeEvent.reflow)"
    },
    {
      "chapter": "plugin-platform",
      "claims": [
        {
          "component": "plugin.api",
          "note": "ADDED 2026-08-09 by audit — the repo existed since 2026-06 with no registry row at all. Depends on @paged-media/plugin-api + plugin-sdk at the `canary` tag and @paged-media/plugin-cli for manifest validation; 2 vitest (test/activate.spec.ts) + a schedule-driven ci.yml. PARTIAL, and the reason is a real risk rather than a formality: HEAD is 2026-06-07, so the template has not been re-verified against the ~20 contract canaries published since, and its floating dependency means a break would surface only on the next scheduled run. Whether that lane is still green is unverified here.",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "plugin-platform.fresh-checkout-proof",
      "importance": "normal",
      "since": "2026-06",
      "status": "experimental",
      "title": "plugin-template — the living proof a fresh clone builds on published canaries alone"
    },
    {
      "chapter": "plugin-platform",
      "claims": [
        {
          "component": "plugin.api",
          "note": "B-13 RESOLVED — W3.5 createHeadlessHost runs over the published canvas-wasm (Decision B); conformance corpora live in draw (B-13 + W4.15) and web (W-10); draw 45-green evidence",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T06:37:31.084Z",
        "headline": "All linked evidence is passing (28 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 28,
          "passed": 28,
          "skipped": 0
        }
      },
      "id": "plugin-platform.headless-host-conformance",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Headless host conformance harness (createHeadlessHost)"
    },
    {
      "chapter": "plugin-platform",
      "claims": [
        {
          "component": "editor.shell",
          "note": "The HOST samples the composited render and writes the result to DocumentMeta.defaultFillColor — a read every bundle already has, so one host-owned sampler serves paged.draw and paged.image with no new contract surface. Sampling the same colour twice reuses one swatch (find-or-create) rather than growing near-duplicates. Registered 2026-08-19: the capability and its e2e (e2e/eyedropper-host.spec.ts, AC-EYE-1..4) both shipped, but the @feat: tag named an id no registry row defined, so the evidence pull refused the payload — which is the gate doing its job.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:54:02.805Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "plugin-platform.host-eyedropper",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Host-owned eyedropper — sample composited pixels into the document defaults (C-32)"
    },
    {
      "chapter": "plugin-platform",
      "claims": [
        {
          "component": "plugin.api",
          "note": "host.widgets CodeEditor (consumer: web source panel since f1e63a9) + diagnostics sink (web linter problems lane, W-05)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T06:37:31.089Z",
        "headline": "All linked evidence is passing (9 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 9,
          "passed": 9,
          "skipped": 0
        }
      },
      "id": "plugin-platform.host-widgets",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Host widgets + diagnostics sink (CodeEditor widget, problems lane)"
    },
    {
      "chapter": "plugin-platform",
      "claims": [
        {
          "component": "core.canvas-wasm",
          "note": "C-6 / I-06: the v44 resource-provider wire — a plugin claims a selected placed image's pyramid and serves tiles (level,x,y) back to the renderer, which composites them into the placed frame. Pairs with C-5 placed-asset-bytes (v42, the encoded-original READ) as the two halves of paged.image's in-frame loop. PROTOCOL_VERSION 44",
          "status": "shipped"
        },
        {
          "component": "plugin.api",
          "note": "host.images.claimImageResource (C-6) hands the plugin a ClaimedImageResource it backs with a tile source(level,x,y); capability-gated, the door warns + returns an inert handle when the host wires no resource channel. Consumer: plugin-image C-6 tile provider (image.editor.tile-provider), today an HONEST level-0 subset (image-js exposes only DecodedImage::tile_window_rgba8, not Engine-B mip eval). PARTIAL: the tiled mip eval (+ rgba16f→rgba8 downconvert) across the wasm boundary is the M2/Stage-B follow-on",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T06:37:31.096Z",
        "headline": "All linked evidence is passing (7 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 7,
          "passed": 7,
          "skipped": 0
        }
      },
      "id": "plugin-platform.image-resource",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Placed-image resource provider door (host.images.claimImageResource — C-6 / I-06, v44 tile lane)"
    },
    {
      "chapter": "plugin-platform",
      "claims": [
        {
          "component": "plugin.api",
          "note": "K-2 (W3/v0.40 cycle, editor-side, no core footprint): importers join open/drag-drop, exporters join the Export Center; consumers: sheets .xlsx both directions; web + data eligible (unconsumed); publish .idml (via host.nativeDocument); pdf .pdf (raster Phase 0)",
          "status": "shipped"
        },
        {
          "component": "editor.shell",
          "note": "document-io.ts registries — importer-aware File/Open + drag-drop, Export Center exporter slots",
          "status": "shipped"
        },
        {
          "component": "editor.canvas",
          "note": "importer/exporter exercised through the editor export integration (export-family.spec.ts green)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:57:47.397Z",
        "headline": "All linked evidence is passing (10 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 12,
          "passed": 10,
          "skipped": 2
        }
      },
      "id": "plugin-platform.importer-exporter",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Importer/exporter registration (contribute.importer/exporter + Export Center)"
    },
    {
      "chapter": "plugin-platform",
      "claims": [
        {
          "component": "plugin.api",
          "note": "The SMALL half of the storage pair, and the one with no row until now: get/set/delete/keys over JSON values, every key prefixed `paged.plugin.<manifest.id>.` by the adapter so a bundle can neither read nor stomp another's. DELIBERATELY UNGATED — host-impl.spec.ts asserts 'viewport / storage / diagnostics need no capability' — which is the line between the two tiers: this is small preferences, and the capability-gated `capabilities.storage: { blob: true }` OPFS store (plugin-platform.blob-store) is for bytes too large for it. Backing is injectable (StorageBacking), defaulting to localStorage when present and an in-memory Map otherwise, which is what makes it work headless. supports('storage@1'). Consumers verified: paged.sheet persists its workbook blob NAME here while the bytes live in host.blob (sheet-bundle/src/session.ts), paged.web reads + clears its legacy source key during the one-time metadata migration (web-bundle/src/panels/web-source-panel.tsx), and the D-03 consent door's remembered grants persist through it",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "plugin-platform.kv-storage",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Namespaced key-value store (host.storage — ungated, JSON values)"
    },
    {
      "chapter": "plugin-platform",
      "claims": [
        {
          "component": "plugin.api",
          "note": "plugin-cli hand-mirrors manifest.schema.json 1:1; namespace + panel-path checks",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T06:37:31.14Z",
        "headline": "All linked evidence is passing (41 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 41,
          "passed": 41,
          "skipped": 0
        }
      },
      "id": "plugin-platform.manifest-schema",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Plugin manifest schema + CLI validator (paged-plugin validate)"
    },
    {
      "chapter": "plugin-platform",
      "claims": [
        {
          "component": "plugin.api",
          "note": "SHIPPED 2026-08-23 in plugin-api 0.2.33-canary.0. `contribute.menu({path, command, order, group, scope, mode})`. `scope` is \"document\" or {editContext} and becomes a `when` predicate — the host's existing vocabulary for \"does this apply where you are standing\" — so a context-scoped entry is simply one whose predicate asks what context is active. `mode` is augment|replace because paged.doc disproves replace-only: a DOCX lowers to real text frames and real stories, so the host's Type and Edit menus genuinely still apply inside it. The SDK REFUSES an entry naming a command the bundle has not registered, on the tool-rail principle that an affordance which accepts a click and does nothing is worse than an absent one, and returns an inert Disposable on an older host rather than throwing. NOT a manifest field but a RUNTIME call during activate, so no manifest-reading gate can count menu entries — the editor's surface-coverage gate says so explicitly rather than reporting zero.",
          "status": "shipped"
        },
        {
          "component": "editor.shell",
          "note": "THE DOCUMENT HALF IS DONE; THE IN-CONTEXT HALF IS NOT, and the split is worth stating because the door supports both. DONE (2026-08-23): the host wires ShellRegistries.menus through to bundles and all six now contribute — draw 72 entries, image 20, sheet 12, web 7, data 7, doc 1 — where before every one of ~150 plugin verbs was Cmd+K-only, shown as a raw command id. They mint Draw/Image/Sheet/Web top levels and MERGE into the host's Object, Edit and Data menus. The four `Object > Insert …` entries the host used to curate by naming four bundles now carry `fallbackFor` and stand down when the plugin declares its own — replacing at the same path rather than throwing, since the thrower is a bundle's activate. A courtesy is removed when someone walks THROUGH the door, not when it opens, so a bundle with no menu keeps its front door. NOT DONE: MenuBar still does not scope to the active edit context — it references editContext nowhere — so the nine host menus remain fully live inside a plugin context. And no bundle uses `scope: {editContext}` or `mode: \"replace\"`: every one of the 119 entries is document-scoped, deliberately (draw's verbs act on the page SELECTION, not inside a graphic you double-clicked into), which means those two contract features have no consumer and are therefore unproven end to end.",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": "2026-09-09T14:20:18Z",
        "evidence_at": "2026-10-18T04:59:11.338Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "plugin-platform.menu-contribution",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Menu contribution — a plugin placing its own verbs in the menu bar"
    },
    {
      "chapter": "plugin-platform",
      "claims": [
        {
          "component": "plugin.api",
          "note": "K-1 BUILT end-to-end (no publish): double-click entry → modal session + content-pointer delivery → cell selection → keystroke cell editor (consumer: sheets in-frame grid). The 2026-08-17 red was ROOT-CAUSED and FIXED 2026-08-18: not a session failure at all — the spring-loaded Meta override (bare Meta keydown of Cmd-Z) flipped effectiveTool and the ADR-024 leave-by-tool rule committed the toolIds:[] context before the z arrived; the editor controller now watches the deliberate base tool (edit-context-controller.tsx) and AC-K1-1/2/3 + AC-C4 + the sheet journey are green on BOTH CPU and GPU lanes, plus a spring-load regression spec (edit-context-spring-loaded.spec.ts) pins the class. PARTIAL: the §8.0 seamless-undo boundary and editContext.modal@1 feature-flagging remain open",
          "status": "partial"
        },
        {
          "component": "editor.shell",
          "note": "K-1 editor slices (94c5753, 86a76e0): content-pointer dispatch, Esc/Enter commit/cancel, dirty gate. the 2026-08-18 base-tool fix in edit-context-controller.tsx resolved the AC-K1-2/3 red (spring-loaded Meta override tripping leave-by-tool); 4/4 green + regression spec. PARTIAL: live-validation depth beyond the shipped loop",
          "status": "partial"
        },
        {
          "component": "editor.canvas",
          "note": "Type-tool click enters an owned modal edit session on the canvas (e2e/sheet-modal-session.spec.ts AC-C4; the full K-1 loop incl. Cmd-Z journal undo green since the 2026-08-18 base-tool fix)",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": "2026-06-18T11:50:06Z",
        "evidence_at": "2026-10-18T05:11:26.042Z",
        "headline": "All linked evidence is passing (11 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 11,
          "passed": 11,
          "skipped": 0
        }
      },
      "id": "plugin-platform.modal-edit-session",
      "importance": "normal",
      "since": "2026-06",
      "status": "experimental",
      "title": "Modal edit session (K-1) — entry gesture, content pointer, in-frame editing loop"
    },
    {
      "chapter": "plugin-platform",
      "claims": [
        {
          "component": "plugin.api",
          "note": "host.nativeDocument SDK door lets a plugin read/open a NATIVE .paged document (paged/core/model/document.pgm) — capabilities document.readNative + document.openNative. Capability-gated; returns an inert handle when the host wires no backend. First consumers: the @paged-media/publish .idml importer + the @paged-media/pdf .pdf importer.",
          "status": "shipped"
        },
        {
          "component": "editor.shell",
          "note": "editor wires the nativeDocument backend behind the host door (open a plugin-built native document through the document-io / File-Open path).",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:01:28.575Z",
        "headline": "All linked evidence is passing (4 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 4,
          "passed": 4,
          "skipped": 0
        }
      },
      "id": "plugin-platform.native-document",
      "importance": "normal",
      "since": "2026-07",
      "status": "stable",
      "title": "Native-document read/open door (host.nativeDocument — document.readNative / document.openNative)"
    },
    {
      "chapter": "plugin-platform",
      "claims": [
        {
          "component": "plugin.api",
          "note": "the SDK door + structured {origins,purpose} consent CONTRACT — default-deny, allow-list filter, remembered-grant persistence (per-bundle storage). supports('network.consent@1') answers from the injected backend. Residual is a consumer, not the contract: no bundle declares capabilities.network yet (all network:false → door dormant); the M1 remote adapter is the first caller",
          "status": "shipped"
        },
        {
          "component": "editor.canvas",
          "note": "D-03 enforcement (2026-06-12): the editor wires a ConsentBackend (apps/canvas plugin-consent.ts + ConsentDialog.tsx — default-deny, per-origin grant, data-source-manifest review, flips supports('network.consent@1') true) AND the CSP connect-src HARD wall (vite.config.ts dev header + public/_headers + build-injected meta; 'self' blob: data' only — no external origin reachable, exact today since all bundles network:false). e2e network-consent.spec 3/3: external fetch CSP-blocked, same-origin works, grant subset resolves, Deny grants nothing. PARTIAL: dynamic CSP loosening for an open-ended (origins:'consent') granted origin is server-mediated M1 (a meta/_headers CSP can't loosen post-load) — the door records intent; the wall stays conservative",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:06:50.369Z",
        "headline": "All linked evidence is passing (3 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 3,
          "passed": 3,
          "skipped": 0
        }
      },
      "id": "plugin-platform.network-consent",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Network capability + consent door (host.network, default-deny)"
    },
    {
      "chapter": "plugin-platform",
      "claims": [
        {
          "component": "plugin.api",
          "note": "ObjectTypeBaker/BakeContext contract types shipped; contribute.objectType is LIVE (W3.2 — webFrame/sheetFrame registered). PARTIAL: the bake-on-change loop, save gate and badge/expand UX remain the open W-03 lane",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T06:37:31.09Z",
        "headline": "All linked evidence is passing (58 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 58,
          "passed": 58,
          "skipped": 0
        }
      },
      "id": "plugin-platform.object-type-baking",
      "importance": "normal",
      "since": "2026-06",
      "status": "experimental",
      "title": "Object-type bake contract (live metadata -> derived IDML children)"
    },
    {
      "chapter": "plugin-platform",
      "claims": [
        {
          "component": "plugin.api",
          "note": "A REMOVAL registered as a capability, because the rule it encodes is the capability: any plugin that exposes content to the canvas is entered by DOUBLE-CLICKING its frame, and `entry` is a one-member union so the compiler enforces it. `entry` used to accept 'doubleClick' | 'command', the second documented as programmatic/menu-driven — and NOTHING implemented it: EditContextRegistry is register-only, BundleHost has no enter member, the shell's `enter` is a React hook no bundle can reach, and the editor's only wired path is tryEnterEditContext(DoubleClickHit). So a bundle could declare it, pass validation, register — and be dead on arrival, which is the same 'absent' lie ADR-023 exists to prevent, one layer up. Removed rather than implemented on a product argument (a user learns 'double-click to go inside' once and it must hold for a vector group, a spreadsheet, a web frame, a Word document and a raster image alike) made safe by an audit: 5 of 5 content plugins were already on doubleClick and 'command' had ZERO users ever. Kept as a one-member union so an author sees the choice was MADE. THE SECOND COMMIT IS THE LESSON: the first landed the narrowing in the CLI only and left the schema, the TS mirror and the drift gate's own VOCAB on the old vocabulary — so a manifest declaring entry:'command' still SCHEMA-validated and only the CLI rejected it — because `pnpm -r typecheck` was run and `pnpm -r test` was not. Fixing it also exposed a real hole in the gate: TS_UNIONS was built from a regex requiring at least one `|`, so a vocabulary that legitimately narrows to ONE could never satisfy it; TS_LITERALS was added, anchored on a property position so prose in doc comments — which quotes removed values freely — cannot satisfy it. Mutation-checked in both directions",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:01:19.12Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "plugin-platform.one-entry-gesture",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "K-13 — one entry gesture for canvas content (entry narrowed to a one-member union)"
    },
    {
      "chapter": "plugin-platform",
      "claims": [
        {
          "component": "plugin.api",
          "note": "{kind:'text', pageId, x, y, text, size?, anchor?, background?} — page-local pt, y = baseline; supports('overlay.text@1'). First explicit discriminant in the preview union (older variants discriminate structurally) 2026-10-06 (SDK 0.2.42-canary.0): host.overlay.layer(id) — data-only per-plugin layers (set/clear/dispose), stacked in creation order below the tool preview, removed with the bundle; capability overlay.layers@1; the shared tool-preview slot stays for tools.",
          "status": "shipped"
        },
        {
          "component": "editor.shell",
          "note": "renders via the page-caption idiom (translate + inverse-scale so the label stays constant screen-size), optional rounded backing plate, control chars stripped, 160-char cap, React-escaped. K-9 CLOSED 2026-08-04: setToolPreviews takes an ARRAY (a new member, not a widened parameter — the renderer discriminates variants structurally, so an array handed to an older host would draw garbage), which also makes its flag dynamic rather than static. Measure can now show its line and its label together, and Shape Builder can shade every collected face",
          "status": "partial"
        },
        {
          "component": "plugin.draw",
          "note": "consumer: the Measure readout now draws on-canvas instead of only publishing a binding",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T06:37:31.119Z",
        "headline": "All linked evidence is passing (15 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 15,
          "passed": 15,
          "skipped": 0
        }
      },
      "id": "plugin-platform.overlay-channel",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Overlay TEXT primitive on the tool-preview channel"
    },
    {
      "chapter": "plugin-platform",
      "claims": [
        {
          "component": "plugin.api",
          "note": "B-07 RESOLVED — ToolPreviewPath carries retained cubics + pressure/tilt (W3.3/W3.4). Manifest contributes still lacks an overlays section (declarative gap only)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T06:37:31.09Z",
        "headline": "All linked evidence is passing (58 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 58,
          "passed": 58,
          "skipped": 0
        }
      },
      "id": "plugin-platform.overlay-registration",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Overlay contribution into the camera-space SVG surface"
    },
    {
      "chapter": "plugin-platform",
      "claims": [
        {
          "component": "plugin.api",
          "note": "PanelContribution grows rail?: boolean (opt into the editor's panel rail; Window menu + palette stay the baseline discovery surfaces) and iconSvg?: string (inner 24x24 SVG markup, currentColor). Type-only — the host adapter already passes contributions through whole. plugin-sdk 0.2.27-canary.1",
          "status": "shipped"
        },
        {
          "component": "editor.shell",
          "note": "the PanelRail renders registry-derived launcher items for rail-opted panels after the app built-ins, live-subscribed (activate appears / dispose drops). iconSvg renders through a scanner-sanitizer (script/foreignObject subtrees excised, event attrs + javascript:/data: URLs dropped) in the rail item AND the dock tab; the compile-time icon registry stays closed. E2E panel-rail-door.spec.ts: runtime registration, hostile payload never executes, tab toggle, dispose. On editor main (8c80148, 962dd44, merged from feat/plugin-panel-surfaces)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-08-17T14:24:11Z",
        "evidence_at": "2026-10-18T04:36:13.885Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "plugin-platform.panel-rail-door",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Panel-rail + icon contribution door (K-8 — rail launcher + sanitized SVG glyph)"
    },
    {
      "chapter": "plugin-platform",
      "claims": [
        {
          "component": "plugin.api",
          "note": "B-01 RESOLVED — W3.1 schema panels + visibleWhen/enabledWhen bindings door; draw stroke panel adopted it. PARTIAL: React expert-leaf panels remain legal (the isolate-exit half is open)",
          "status": "partial"
        },
        {
          "component": "editor.shell",
          "note": "All 10 of paged.draw's declared panels open as dock tabs and mount a body (plugin-surface/draw.spec.ts) — a registered panel that opens empty is the panel equivalent of the dead rail slot.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T06:37:31.13Z",
        "headline": "All linked evidence is passing (78 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 78,
          "passed": 78,
          "skipped": 0
        }
      },
      "id": "plugin-platform.panel-registration",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Panel contribution registration (expert-leaf React in v0)"
    },
    {
      "chapter": "plugin-platform",
      "claims": [
        {
          "component": "plugin.api",
          "note": "C-16, and the finding is that NO core door was needed: the scene-tree reply already carries parentage, so what was missing was a per-element QUESTION, not an engine capability. No protocol bump. Pages and spreads are transparent (they carry no ElementId), and a memoized child→parent index dropped on mutation/undo/redo/load moves the O(document) cost from once-per-keypress to once-per-edit",
          "status": "shipped"
        },
        {
          "component": "plugin.draw",
          "note": "THE CONSUMER MOVED, so this stage is now unlikely to be the one that closes: media.paged.draw.command.selectParentGroup RETIRED 2026-08-05 to the host as paged.object.selectParentGroup (editor-tools.object-commands). paged.draw keeps parentGroupOf as a helper other commands compose, but the user-facing verb is the editor's",
          "status": "planned"
        },
        {
          "component": "editor.shell",
          "note": "the host verb still re-reads the whole scene tree per press — document.tree() is the only CLICK-FREE parentage read (hitTest's groupChain carries ancestry but needs a pointer event, and elementProperties exposes no parent member). The editor's own comment names a targeted parentOf(id) door as the RFI candidate 'if it ever bites', which at a menu verb's cadence it does not — so this is a deliberate non-adoption of a shipped SDK door, not an oversight",
          "status": "planned"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "plugin-platform.parent-of",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Per-element parentage read (host.document.parentOf)"
    },
    {
      "chapter": "plugin-platform",
      "claims": [
        {
          "component": "plugin.api",
          "note": "DECLARED INTO A VOID. Eleven partTypes are declared across the eight loaded bundles (draw 7, doc 2, sheet 1, web 1) and there is NO `contribute.partType` in the SDK and no partType registry in the shell — nothing reads them. A declaration that reaches nothing can never be wrong and can never be checked. paged.doc's `docx` part even declares `linkable: true`, which nothing can act on, and which is exactly the relink affordance its own audit found missing from the UI. Found 2026-08-22 by two independent plugin audits; the surface-coverage gate counts these ids as surface, which is why they now carry specs asserting the manifest declaration rather than any behaviour.",
          "status": "planned"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "plugin-platform.part-types",
      "importance": "normal",
      "since": null,
      "status": "planned",
      "title": "Part types — a plugin declaring the container parts it owns"
    },
    {
      "chapter": "plugin-platform",
      "claims": [
        {
          "component": "core.canvas-wasm",
          "note": "v42 RequestPlacedAssetBytes{element_id} -> PlacedAssetBytes{found,uri,width,height,encoded} wire query LANDED in core: the worker resolves the frame's image_link URI (Rectangle/Oval/Polygon) and returns the ORIGINAL encoded file (PSD/JPEG/PNG) the build decoded + cached, so paged.image can parse it with its own codecs (the INPUT side of image M4, paired with the v41 image scene layer). Pure READ; 1 model test (element->URI, cache hit/miss) + the found:false envelope. PUBLISHED (canvas-wasm 0.42.0; the editor now pins 0.48.0). PARTIAL: render-miss fallback to the live AssetResolver is a follow-on (today resolves from the decode cache, so the image must have rendered)",
          "status": "partial"
        },
        {
          "component": "plugin.api",
          "note": "UNDERSTATED UNTIL 2026-08-05, and found by this registry's own impl-without-claim rule: this stage was marked `planned` while carrying green evidence. The SDK half SHIPPED — capabilities.assets ∋ 'images' is un-reserved in the manifest schema (AssetKind = 'fonts' | 'images') and AssetSurface.getPlacedImage(elementId) is a real door, pinned by plugin-sdk assets-placed-image.spec.ts AGAINST THE REAL PUBLISHED ENGINE (canvas-wasm >= 0.42.0): the feature probe, the capability gate (images undeclared -> the read throws) and the honest null path through a live requestPlacedAssetBytes wire round-trip. PARTIAL, two named halves open: the bytes-POSITIVE path needs a fixture with a resolvable placed image (it lands with the image-plugin M4 conformance corpus), and the EDITOR host does not serve this door at all — createEditorAssetSource implements getFontFace only, so in the editor the read answers through no backend. Co-design with web W-06 embedded-resource read",
          "status": "partial"
        },
        {
          "component": "editor.shell",
          "note": "the missing half named precisely: apps/canvas/src/plugin-asset-source.ts carries no placed-image arm, so the editor's injected asset source backs host.assets.getFontFace and nothing else",
          "status": "planned"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T06:37:31.06Z",
        "headline": "All linked evidence is passing (4 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 4,
          "passed": 4,
          "skipped": 0
        }
      },
      "id": "plugin-platform.placed-asset-bytes",
      "importance": "normal",
      "since": "2026-06",
      "status": "experimental",
      "title": "Placed-asset bytes read door (host.assets.getPlacedImage — C-5 / I-04)"
    },
    {
      "chapter": "plugin-platform",
      "claims": [
        {
          "component": "plugin.api",
          "note": "The SDK half of K-11. The engine side has been registered since the B-22 wave (frames-paths.planar-arrangement, core.mutation + core.canvas-wasm at protocol 57) but the bundle-facing FACADE had no row anywhere. Landed in 0.2.28-canary.0 (698289b). `document.planarRegions(elementIds, point?)` answers a PlanarRegionsResult that is deliberately NOT a bare face array, because the door has three answers a caller must be able to tell apart: found with faces (possibly empty — a point query outside every input), found:false + the engine's OWN reason (a refusal, never a truncated answer), and complete:false (the faces are all real but do not tile the union). A host whose engine predates v57 gets a TYPED refusal, not a throw. Face ids are stable across calls with the same ordered inputs, which is what lets a hovered face id ride straight into pathfinderFaces without a second query; coordinates are RAW path space, uncomposed itemTransforms, exactly like pathfinderBoolean. CONSUMER MIGRATED: paged.draw's draw-bundle/src/handlers/planar-regions.ts now reads through the facade (readPlanarRegions) instead of the wire-level host.editor.client.send escape hatch it used before, and the note in that file records what the migration bought — `null` narrowed to only the undeclared-capability case, because a host that cannot serve the door now arrives as a well-formed refusal carrying a reason to show the user",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:05:20.986Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "plugin-platform.planar-regions-door",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Planar-region read door for bundles (host.document.planarRegions — K-11)"
    },
    {
      "chapter": "plugin-platform",
      "claims": [
        {
          "component": "plugin.api",
          "note": "K-10. Answers a BOOLEAN, never a path (a lie in a browser, a trust surface anywhere) and never a rejection (a refused save is a result, not an unhandled promise in a click handler). Option fields mirror PickedFile so picked bytes hand straight back after an edit; suggestedName states the honest promise — the host sanitizes it and a bundle can never choose a path. The shell option became a BACKEND the bundle-facing surface wraps, so a host predating the door answers false instead of 'saveFile is not a function'",
          "status": "shipped"
        },
        {
          "component": "editor.shell",
          "note": "reuses the app's own anchor-download rather than adding a second mechanism — plugin bytes and export-target bytes now leave through one path",
          "status": "shipped"
        },
        {
          "component": "plugin.image",
          "note": "the consumer: 'Save adjusted copy…' — blocked on a plugin-sdk canary publish + repin",
          "status": "planned"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "plugin-platform.save-file",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Host save-file door (host.shell.saveFile)"
    },
    {
      "chapter": "plugin-platform",
      "claims": [
        {
          "component": "core.renderer",
          "note": "scene-layer items lower into the ONE render path (CPU-rasterise-tested, both backends). Vector paths/fills v39; single-line TEXT v40 (default face); RGBA IMAGE v41 (Stage A — pixels interned to the image pool → the DisplayCommand::Image lane); pyramid-tile resource provider v44 (C-6). CSS surface added v45→v48: linear/radial gradient fill (v45), conic gradient via a new SweepGradient pool on both backends + per-fill BLEND (15 CSS modes via FillPathBlend) + DROP SHADOW (v46), INSET shadow (v47), gradient STROKE + gradient-under-BLEND (v48) — all additive, all wiring to render support that already existed Protocol 68 (core v0.68.0, c7c16a7, ADR 126; canvas-wasm 0.68.0 on npm since 2026-10-06): text runs draw in their own face — family plus weight/italic resolve through the host-registered fonts, the default face only as a fallback, reported as SceneLayerApplied.fontFallbacks; register_font/clear_font_registry rebuild frames whose scene text names an affected family. Protocol 70 (v0.70.0, ADR 130): faces registered with scope sceneLayer resolve scene-layer text first and never reach document layout.",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "note": "the sceneLayer wire (additive SceneItem variants on SubmitSceneLayer — no new messages, the v40-Text/v41-Image precedent) is PUBLISHED through canvas-wasm 0.48.0 (protocol v48); the editor now pins 0.61.1 (corrected 2026-08-09 — this row said it pinned 0.48.0) and plugin-sdk re-syncs the wire types (0.2.24-canary)",
          "status": "shipped"
        },
        {
          "component": "plugin.api",
          "note": "contribute.sceneLayer shipped; consumers: sheets in-frame grid, paged.image M4 in-frame placement (Stage A + C-6), and paged.web's Blitz→sceneLayer rendering (a broad CSS surface end-to-end). Note: C-1 STAGE B (shared GPUDevice/external-texture, the gpu capability) is DEFERRED record-only (ADR-018) — a SceneItem::Texture the renderer cannot composite would be a fake, so the gpu capability stays REJECTED until Vello exposes an external-texture composite path AND a real-GPU harness exists; the realm-local capabilities.gpu bless-it (I-07) DID land. Open follow-ons: raw-GPUTexture (Stage B), per-run face selection + rotated-frame affine glyphs Protocol 68 contract in plugin-api/plugin-sdk 0.2.40-canary.0 (npm, 2026-10-06): SceneLayerSurface.submit resolves { fontFallbacks }; supports(\"rendering.sceneLayer.faces@1\"). The editor passes the reply through (on main; journey scene-layer-faces.journey.spec.ts).",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-05T15:55:18Z",
        "evidence_at": "2026-10-18T06:37:31.089Z",
        "headline": "All linked evidence is passing (5 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 6,
          "passed": 5,
          "skipped": 1
        }
      },
      "id": "plugin-platform.scene-layer",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "In-frame plugin scene layers (contribute.sceneLayer — vector/text/image/gradient/blend/shadow)"
    },
    {
      "chapter": "plugin-platform",
      "claims": [
        {
          "component": "plugin.api",
          "note": "A PUBLISHED-ARTIFACT DEFECT, not a source one. schema-panel.tsx and widgets-fallback.tsx each resolved React with a TOP-LEVEL `await import(\"react\")`; tsup emitted it verbatim at target es2022, which makes dist/index.js an ASYNC MODULE. Vite's dep-optimizer compiles pre-bundled deps down to its ES2020 floor, where top-level await does not exist — so pre-bundling the published package FAILED A CONSUMING DEV SERVER OUTRIGHT. Fix: one module (react-optional.ts) kicks the dynamic import off at module scope and deliberately does NOT await it, so evaluation stays synchronous and render paths read createElement from the cache, with a named seam that throws if a render somehow beats the microtask. Plus a BUILD-LEVEL GATE — the tsup target moved to es2020, so esbuild refuses to emit the construct and a future one fails `pnpm build` HERE rather than in a consumer's dev server. Residual (unactioned): the editor's corresponding optimizeDeps.exclude entry for @paged-media/plugin-sdk is still in apps/canvas/vite.config.ts even though the commit note says it is no longer needed",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "plugin-platform.sdk-bundle-shape",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "The published plugin-sdk stays a SYNCHRONOUS module (no top-level await)"
    },
    {
      "chapter": "plugin-platform",
      "claims": [
        {
          "component": "plugin.api",
          "note": "host.text.caret() → {storyId, offset} | null + supports('text.caret@1'); injectable backend (CreateBundleHostOptions.textCaret), null when the host provides none. Offsets use the story-local convention insertText/deleteRange consume, so the value feeds mutate directly; a range answers its START",
          "status": "shipped"
        },
        {
          "component": "editor.shell",
          "note": "reads the live ContentSelectionProvider state — no text-editing refactor needed. Cell-qualified carets answer null by design so cell-local offsets never leak as story-local (e2e-pinned)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:48:37.035Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "plugin-platform.text-caret-door",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "C-9 — caret/selection-offset read door for bundles"
    },
    {
      "chapter": "plugin-platform",
      "claims": [
        {
          "component": "plugin.api",
          "note": "S-13 measureString shipped (consumer: sheets column sizing via lower.ts). The K-7 editor-side metrics RPC is REAL since v0.38.0 (requestMeasureText → CanvasWorker.measureText → measureTextResult; PagedEditor.text.measure wired, supports('text.measure@1') true) — the 'still estimate' claim here was stale. E2E-pinned 2026-08-03 (editor tests/e2e/text-measure.spec.ts): real Open Sans metrics ≠ the 0.5em estimate, unresolved families ride font-fallback, no-document normalises to honest zeros",
          "status": "shipped"
        },
        {
          "component": "editor.shell",
          "note": "PagedEditor.text.measure → client.measureText → the worker shaper (v0.38.0, commit 2806f8c); e2e text-measure.spec.ts green on the chromium lane",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T06:37:31.089Z",
        "headline": "All linked evidence is passing (4 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 4,
          "passed": 4,
          "skipped": 0
        }
      },
      "id": "plugin-platform.text-measurement",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Text measurement door (host.text.measureString + editor metrics RPC)"
    },
    {
      "chapter": "plugin-platform",
      "claims": [
        {
          "component": "plugin.api",
          "note": "B-11/B-14/B-15 resolved — gesture kit + contributeTool in plugin-sdk 0.2; ToolContribution.slotOrder rail hint (B-14) and registry-derived shortcuts/panel commands (B-15) live host-side",
          "status": "shipped"
        },
        {
          "component": "editor.shell",
          "note": "All 19 of paged.draw's declared tools reach the rail and activate (plugin-surface/draw.spec.ts). The rail already shipped the opposite defect once — 15 of 31 entries accepted a click and did nothing — so the assertion is per-slot, not a sample.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T06:37:31.138Z",
        "headline": "All linked evidence is passing (66 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 66,
          "passed": 66,
          "skipped": 0
        }
      },
      "id": "plugin-platform.tool-registration",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Tool contribution + gesture-handler registration surface"
    },
    {
      "chapter": "plugin-platform",
      "claims": [
        {
          "component": "plugin.api",
          "note": "the bundle-realm wasm-bindgen glue path is the RATIFIED v1 loader (S-10/D-07a); consumers: sheets 1.0 MiB + data 541 KiB boot in tests. PARTIAL: D-07b large-artifact budget class (DuckDB 36 MiB undeclarable) open",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T06:37:31.139Z",
        "headline": "All linked evidence is passing (12 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 12,
          "passed": 12,
          "skipped": 0
        }
      },
      "id": "plugin-platform.wasm-packaging",
      "importance": "normal",
      "since": "2026-06",
      "status": "experimental",
      "title": "Bundle wasm packaging + loader contract (bundle-realm glue path)"
    },
    {
      "chapter": "plugin-platform",
      "claims": [
        {
          "component": "plugin.api",
          "note": "AnchorTriple ⊆ PathAnchorSpec asserted at typecheck — the protocol-break alarm",
          "status": "shipped"
        },
        {
          "component": "plugin.draw",
          "note": "type-only @paged-media/plugin-api import; a wire break fails typecheck in-repo",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:47:05.769Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "plugin-platform.wire-compat-assertions",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Wire-compatibility type assertions (machine output ⊆ engine wire types)"
    },
    {
      "chapter": "plugin-platform",
      "claims": [
        {
          "component": "plugin.api",
          "note": "Distinct from plugin-platform.wire-compat-assertions, which is a typecheck assertion in plugin-draw; this is the vendoring gate in plugin-sdk. plugin-api OWNS its published types, so it VENDORS core's generated wire .d.ts verbatim as src/wire.d.ts and stamps the source package version in the header. publish.yml runs it as a HARD gate the only way that proves anything: read the stamped version out of the vendored file, npm-install THAT EXACT @paged-media/canvas-wasm into a scratch dir, and run `sync-wire.mjs --check` against it — so the gate compares against the real published artifact rather than whatever happens to be in the workspace. Two rules the specs pin: an unresolvable source FAILS (never a warn-skip — a protocol coupling checked against nothing is not checked), and a version-stamp change fails even when the body is byte-identical. Currently stamped @paged-media/canvas-wasm 0.61.1 (re-synced in d3dddc9). The K-13 commit pair is this gate's own worked example of the failure mode it exists for — see plugin-platform.one-entry-gesture",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "plugin-platform.wire-drift-gate",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Wire-vendoring drift gate (sync-wire --check, hard in publish CI)"
    },
    {
      "chapter": "plugin-platform",
      "claims": [
        {
          "component": "plugin.api",
          "note": "K-3 DONE (plugin-sdk 0.2.18-canary; NO wire change — K-3 carried no protocol bump; the protocol later reached v48 for unrelated sceneLayer CSS features). host.workers.spawn(WorkerSpawnOptions) over an injected WorkerBackend; capability-gated on capabilities.workers (the host gate throws on an undeclared spawn); concurrency() = min(declared.max, hardwareConcurrency, 8); SharedArrayBuffer hand-off declarable. Degrades HONESTLY: no WorkerBackend → spawn rejects, concurrency()=0, supports('workers@1') false. The deferral condition (a real threading consumer) was met by the plugin-image decode pool (image.editor.decode-pool). 11 plugin-sdk host.workers tests",
          "status": "shipped"
        },
        {
          "component": "editor.shell",
          "note": "the editor injects the WorkerBackend behind the host door",
          "status": "shipped"
        },
        {
          "component": "editor.canvas",
          "note": "render worker boot + SAB cross-origin isolation verified in the editor (worker-boot.spec.ts green)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:56:41.363Z",
        "headline": "All linked evidence is passing (5 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 5,
          "passed": 5,
          "skipped": 0
        }
      },
      "id": "plugin-platform.worker-pool",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Worker spawn + SharedArrayBuffer (host.workers — K-3, off-main-thread compute)"
    },
    {
      "chapter": "plugin-publish",
      "claims": [
        {
          "component": "plugin.publish",
          "note": "MEASURED, not asserted-by-hope. Scope went from 823 entries (Spreads/*.xml — about 7% of the document) to 11,876 across five lanes: spread 823, story 10,539, master 316, graphic 99, styles 99. Every lane now reports 0 gaps, 0 malformed, 0 panics, 0 errors, +0 byte growth; the whole-package lane (import_idml_doc + write_idml, the real save path) reports 99 of 99 packages BYTE-IDENTICAL, 0 with differing entries. Equally load-bearing: it PRINTS ITS OWN BLIND SPOTS — 792 unmeasured entries with a reason each (META-INF 164, Resources 198, XML/ 232, designmap 99, mimetype 99), and 792 + 11,876 = 12,668 = the corpus total, so the coverage claim is arithmetic rather than adjectival. Added en route: a MALFORMED column (well-formedness check) and per-entry catch_unwind, which is how two save-killing panics surfaced. HONEST NOTE ON THE HEADLINE: '596 -> 0' is the SPREAD lane's arc (594 -> 596 -> 87 -> 57 -> 0); once the denominator broadened the honest combined baseline was 905 gaps over 11,560 entries with 48 of 99 packages not identical, so the campaign closed MORE than 596, not less",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "plugin-publish.byte-identity-sweep",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Corpus byte-identity sweep — 99/99 packages identical, and what it does NOT measure"
    },
    {
      "chapter": "plugin-publish",
      "claims": [
        {
          "component": "plugin.publish",
          "note": "idml-import + idml-export crates live in plugin-publish; unchanged parse/serialize behaviour, byte-identical unmutated round-trips preserved (see round-tripping.idml-reserialization).",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:01:17.313Z",
        "headline": "All linked evidence is passing (3 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 3,
          "passed": 3,
          "skipped": 0
        }
      },
      "id": "plugin-publish.idml-adapter-crates",
      "importance": "normal",
      "since": "2026-07",
      "status": "stable",
      "title": "IDML interchange adapter (idml-import + idml-export crates, re-homed to plugin-publish)"
    },
    {
      "chapter": "plugin-publish",
      "claims": [
        {
          "component": "plugin.publish",
          "note": "contributeIdmlIo registers media.paged.publish.exporter.idml (title 'IDML package', extension .idml, mime application/vnd.adobe.indesign-idml-package) alongside the importer, capability-gated on contribute.exporter@1 and warning rather than throwing on an older host. It does NOT serialize IDML: it reuses the engine's existing writer through the host.editor escape hatch (client.send {kind:'exportIdml'} -> idmlExported), a DELIBERATE transitional bridge under ADR-022 Option A that Phase 5 retires once a first-class export door lands; anything other than an idmlExported reply returns null (nothing to save) instead of empty bytes. The fidelity of what it emits is plugin-publish.byte-identity-sweep + plugin-publish.save-back-defects, not this row.",
          "status": "shipped"
        },
        {
          "component": "editor.shell",
          "note": "reachable from a CLEAN install, unlike most plugin rows here: apps/canvas/src/main.tsx loadBundle()s publishBundle, and @paged-media/publish resolves from npm (0.1.0-canary.0 is the published latest) with no pnpm link: override. Driven by apps/canvas/tests/journey/plugins/publish.journey.spec.ts — author a filled rectangle, pull this exporter out of __canvas.registries.exporters (the Export Center path, not a menu click), assert a PK ZIP, then feed the bytes straight back through the importer registry and poll the pixel delta, so the export is proven by a REIMPORT THAT RENDERS rather than by a magic number. Registered 2026-08-09 from source + spec; state has no payload for the journey lane (the editor's tests.yml merges every shard into one playwright-results artifact), so it will read claimed-not-verified until that lane is split.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "plugin-publish.idml-exporter",
      "importance": "normal",
      "since": "2026-07",
      "status": "stable",
      "title": ".idml exporter — the outbound half of the bundle (media.paged.publish.exporter.idml)"
    },
    {
      "chapter": "plugin-publish",
      "claims": [
        {
          "component": "plugin.publish",
          "note": "the @paged-media/publish bundle registers a .idml importer that opens a document through host.nativeDocument (contribute.importer + document.openNative). The interchange door for bringing IDML into a native .paged.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:09:34.892Z",
        "headline": "All linked evidence is passing (3 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 3,
          "passed": 3,
          "skipped": 0
        }
      },
      "id": "plugin-publish.idml-importer",
      "importance": "normal",
      "since": "2026-07",
      "status": "stable",
      "title": ".idml importer via host.nativeDocument (@paged-media/publish bundle)"
    },
    {
      "chapter": "plugin-publish",
      "claims": [
        {
          "component": "plugin.publish",
          "note": "OBJECT STYLES survive save-back — definition AND reference, with the styles part staying registered in the designmap. MASTER SPREADS are WRITTEN: until this campaign the writer iterated doc.spreads only, so an edit to a master page simply VANISHED (parsed, then silently dropped); the master loop is sorted by src so a save cannot depend on HashMap order, and 316 corpus master entries rewrite byte-identically. Z-ORDER survives across all THREE sibling lists — Spread::frames_in_order, Group::members and Spread::nested_children (the B-18 paste-into container) — in a new 601-line reorder.rs, which is what makes core's v59 Arrange survive a round-trip. RUN/PARAGRAPH MISALIGNMENT: story ranges are now keyed by BYTE OFFSET through a new StoryProvenance rather than by ordinal, with a verbatim path on a miss; four premise_* tests pin the four ways ordinals drifted. STYLE DUPLICATION: a pre-pass establishes what the part already defines and where the LAST root group of each kind is, so no style is defined twice. A SAVE-KILLING PANIC: a <PathPointArray> contour index drift (subpath_starts[7] on an 8-contour polygon with an empty 8th) aborted the wasm worker — panic = abort means it killed the save outright; found in two real corpus templates. THE <Label> ESCAPER: escape_attr did not emit &#x9;/&#xA;/&#xD;, and XML 1.0 §3.3.3 requires a parser to collapse a literal tab/LF/CR in an attribute value to a SPACE — so every save mangled plugin metadata; write_item_label held a THIRD copy of the same bug and now routes through the one escaper. F32 PRECISION: ItemTransform (32,630 rewritten attrs -> 0), StrokeWeight and eight story-run properties now take a preserving patch, and format_f32's '4 decimals' contract was silently false above 2^24/10,000 = 1677.72 because (v*10_000.0).round() was done in f32. Also fixed in the same sweep: nested <Group> duplication (one spread 832 KB -> 1.37 MB; 7.4 MB across six), U+2028 turning a forced line break into a paragraph break (283 entries), two page items DELETED on save, and three false-absence classes (FillTint='-1' sentinel, Nonprinting='false', GraphicLine FillColor)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "plugin-publish.save-back-defects",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Save-path defect class — nine defects between a parsed document and its bytes"
    },
    {
      "chapter": "plugin-publish",
      "claims": [
        {
          "component": "plugin.publish",
          "note": "importIdml and the exporters are typed commands with schema-checked args (never coerced), struct defaults for partial args (SDK 0.2.43), 1-based page ranges mapped onto the engine's 0-based ones; File ▸ Export shares the typed path (ADR 323).",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:01:26.107Z",
        "headline": "All linked evidence is passing (13 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 13,
          "passed": 13,
          "skipped": 0
        }
      },
      "id": "plugin-publish.typed-commands",
      "importance": "normal",
      "since": "2026-10",
      "status": "stable",
      "title": "IDML import and export as typed commands"
    },
    {
      "chapter": "plugin-slide",
      "claims": [
        {
          "component": "plugin.slide",
          "note": "slide-chart lays out bar (clustered/stacked/percent, both directions), line (smoothed), area, pie/doughnut, gridlines, axes, tick/data labels and legends as PowerPoint does; 44 corpus bars within 3 pt of PowerPoint (oracle_charts). Not yet: scatter, radar, 3-D, measured label widths.",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-17T17:16:21.165Z",
        "headline": "All linked evidence is passing (3 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 3,
          "passed": 3,
          "skipped": 0
        }
      },
      "id": "plugin-slide.charts",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "PowerPoint charts drawn as native shapes from their cached values"
    },
    {
      "chapter": "plugin-slide",
      "claims": [
        {
          "component": "plugin.slide",
          "note": "pptx-fonts decodes the faces PowerPoint embeds (Embedded OpenType, MicroType Express: LZCOMP + Compact Table Format; a bare OpenType part as is), written from the 2008 W3C member submissions. The licence (fsType) decides before decompressing: installable and editable faces go to the document through host.assets.registerDocumentFont before it opens; preview-and-print, restricted, bitmap-only and site-bound faces are not used and are named. A reopened deck document gets them back from the kept source deck. Verified: PowerPoint-authored fixtures fonts/fonts-full (whole faces identical to the source fonts glyph for glyph, subsets hold the deck's text), 350 real-world faces decode, engine headless (embedded families missing before, none after). The SDK door (plugin-sdk ADR 328) is published in plugin-api/plugin-sdk 0.2.55-canary.0 (2026-10-14) and implemented by plugin-sdk's host adapter, which the editor pins; @paged-media/slide itself is not on npm (the editor links it from source). Open: editable faces live for the session, not the document; preview-and-print waits for a read-only view.",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": "2026-10-14T20:47:13Z",
        "evidence_at": "2026-10-17T17:18:22.217Z",
        "headline": "All linked evidence is passing (40 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 40,
          "passed": 40,
          "skipped": 0
        }
      },
      "id": "plugin-slide.embedded-fonts",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "A deck's embedded fonts reach the document, within their licences"
    },
    {
      "chapter": "plugin-slide",
      "claims": [
        {
          "component": "plugin.slide",
          "note": "slide-resolve + slide-geom + slide-chart + slide-idml write the whole deck as IDML; the bundle importer opens it via host.nativeDocument.open and keeps the source + report as parts (plugin-slide 3ba464b). Engine conformance headless (5 fixtures, items within 0.5 pt); corpus SSIM median 0.80-0.995 per deck. Open: editor wiring (canary), fonts door, radial gradient placement + master pictures in the engine, SmartArt, table styles.",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-17T17:18:22.214Z",
        "headline": "All linked evidence is passing (24 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 24,
          "passed": 24,
          "skipped": 0
        }
      },
      "id": "plugin-slide.import-native",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "A PPTX opens as native slides (generated IDML, inheritance resolved)"
    },
    {
      "chapter": "plugin-slide",
      "claims": [
        {
          "component": "plugin.slide",
          "note": "M3: new slide from a layout is built (plugin-slide ee5b6a7) — the Slides panel offers the layouts the deck uses; a new slide copies one with only its placeholders kept and emptied, carrying PowerPoint placeholder identity as item metadata. Not yet: editing layouts (master spreads) from the plugin, and layouts no slide uses (not imported).",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": "2026-10-12T08:02:38Z",
        "evidence_at": null,
        "headline": "No evidence linked yet; implementation last changed 6 d ago",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "plugin-slide.layouts",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "New slide from layout; layout editing"
    },
    {
      "chapter": "plugin-slide",
      "claims": [
        {
          "component": "plugin.slide",
          "note": "Slides, layouts and placeholders are typed objects read from and written to the importer's page labels (notes, hidden flag, transition); a batch across slides is one undo step; the slide panels are host PropertyFields covering every writable row (ADR 323).",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-17T17:18:22.221Z",
        "headline": "All linked evidence is passing (27 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 27,
          "passed": 27,
          "skipped": 0
        }
      },
      "id": "plugin-slide.object-model",
      "importance": "normal",
      "since": "2026-10",
      "status": "stable",
      "title": "Object model — slides, layouts and placeholders addressable through host.objects"
    },
    {
      "chapter": "plugin-slide",
      "claims": [
        {
          "component": "plugin.slide",
          "note": "Engine (protocol 70, ADR 129): movePage, setPageMetadata, insertHyperlink.page, requestSnapshot.hideItems, onMaster, create/delete/renameMaster. Host (plugin-sdk 0.2.43, ADR 323): host.render.snapshot (render.snapshot@1) and host.viewport.goToPage / activePage / onDidChangeActivePage (viewport.pages@1), backed by the editor. Not yet: a fullscreen presentation surface and presenter window.",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": "2026-10-06T08:42:33Z",
        "evidence_at": null,
        "headline": "No evidence linked yet; implementation last changed 12 d ago",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "plugin-slide.page-doors",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Engine and host features for slides — page order, page metadata, masters, page images, go to page"
    },
    {
      "chapter": "plugin-slide",
      "claims": [
        {
          "component": "plugin.slide",
          "note": "scripts/ppt-export-probe.sh, ppt-geometry-probe.sh, ppt-record-corpus.sh, ppt-author-fixtures.sh drive PowerPoint 16.113.3: PDF + per-shape geometry + provenance for the 7 corpus decks (private corpus 4cf1a7b) and 4 fixtures authored through PowerPoint's dictionary (geometry, text, placeholders, motion). Tables cannot be authored that way (make new table: -2710).",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-17T17:16:21.94Z",
        "headline": "All linked evidence is passing (16 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 16,
          "passed": 16,
          "skipped": 0
        }
      },
      "id": "plugin-slide.powerpoint-oracle",
      "importance": "normal",
      "since": "2026-10",
      "status": "stable",
      "title": "PowerPoint as the oracle — authored fixtures and recorded answers"
    },
    {
      "chapter": "plugin-slide",
      "claims": [
        {
          "component": "plugin.slide",
          "note": "M4 first part (ADR 704), plugin-slide d0abc29: File ▸ Export as PowerPoint writes the deck back from the kept original — page order, duplicated slides as slide parts, deleted slides dropped with their notes, hidden flags and speaker notes (a notes slide is created from the notes master when a slide gains notes), sections kept contiguous; an unedited document exports the original bytes. Verified by PowerPoint 16.113.3 opening the written decks without a repair prompt, every page pixel-identical to the expected slide. Not yet: slide CONTENT edits are reported, not written (M4 second part).",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": "2026-10-12T08:03:19Z",
        "evidence_at": null,
        "headline": "No evidence linked yet; implementation last changed 6 d ago",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "plugin-slide.pptx-export",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Export a .pptx (original bytes if unedited, else regenerated)"
    },
    {
      "chapter": "plugin-slide",
      "claims": [
        {
          "component": "plugin.slide",
          "note": "pptx-import reads the package (presentation, masters with text styles and colour map, layouts, themes, slides, shapes/pictures/groups/connectors/tables/chart and SmartArt references, text with list styles, backgrounds, notes, transitions with their XML, timing verbatim, sections, embedded font references). All 7 corpus decks and the 4 PowerPoint-authored fixtures read (plugin-slide 1249e87, 3b23d6e).",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-17T17:16:20.204Z",
        "headline": "All linked evidence is passing (7 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 7,
          "passed": 7,
          "skipped": 0
        }
      },
      "id": "plugin-slide.pptx-read",
      "importance": "normal",
      "since": "2026-10",
      "status": "stable",
      "title": "Read a .pptx into the slide IR"
    },
    {
      "chapter": "plugin-slide",
      "claims": [
        {
          "component": "plugin.slide",
          "note": "slide-geom evaluates all 187 ECMA-376 presets + custom geometry; 412 fixture and 1504 corpus shapes within 1 pt of PowerPoint (slide-resolve oracle_geometry).",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-17T17:16:21.389Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "plugin-slide.preset-geometry",
      "importance": "normal",
      "since": "2026-10",
      "status": "stable",
      "title": "DrawingML preset geometries become native paths"
    },
    {
      "chapter": "plugin-slide",
      "claims": [
        {
          "component": "plugin.slide",
          "note": "Slides panel: a thumbnail per slide from host.render.snapshot, click to go there, drag to reorder (movePage), duplicate, delete, hide from the slideshow, each one undo step (slide-bundle slides-model.spec, editor journey plugins/slide). Not yet: multi-select, keyboard reorder.",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "plugin-slide.slide-sorter",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Slide sorter — thumbnails, reorder, duplicate, delete, hide"
    },
    {
      "chapter": "plugin-slide",
      "claims": [
        {
          "component": "plugin.slide",
          "note": "M5; shell.presentation door, snapshot hideItems (protocol 68).",
          "status": "planned"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "plugin-slide.slideshow",
      "importance": "normal",
      "since": "2026-10",
      "status": "planned",
      "title": "Slideshow with transitions and builds, presenter view"
    },
    {
      "chapter": "plugin-slide",
      "claims": [
        {
          "component": "plugin.slide",
          "note": "The diagram's dsp drawing part becomes a group of native shapes (text boxes, style text colours); oracle: fixture smartart, every block within 1 pt, text colours exact. The data model is kept but not re-laid-out; editing the diagram as SmartArt is not supported.",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-17T17:16:21.171Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "plugin-slide.smartart",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "SmartArt drawn as native shapes from PowerPoint's laid-out drawing"
    },
    {
      "chapter": "plugin-slide",
      "claims": [
        {
          "component": "plugin.slide",
          "note": "Notes are page plugin metadata (x-paged:media.paged.slide), written by the importer and edited in the Notes panel (one undo step per edit); they move, duplicate and undo with the slide. Plain text: run formatting in notes is not kept.",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "plugin-slide.speaker-notes",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Speaker notes per slide"
    },
    {
      "chapter": "plugin-slide",
      "claims": [
        {
          "component": "plugin.slide",
          "note": "All 74 built-in styles + a deck's own tableStyles.xml, resolved per cell in PowerPoint's layer order; written as native TableStyle (region cell styles, alternating fills) with local values only where the cascade differs. Oracle: 150 tables (fixtures tables, tablestyles), fills within 4 RGB, text exact, borders by colour and width. Open: double cell borders (engine reads no cell-edge stroke type).",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-17T17:16:21.94Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "plugin-slide.table-styles",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "PowerPoint table styles resolved as PowerPoint draws them, kept as native table styles"
    },
    {
      "chapter": "plugin-web",
      "claims": [
        {
          "component": "plugin.web",
          "note": "Web frames re-render on activation, document open, save, undo and resize (keyed by label and frame size; a move renders nothing); frames that leave a flow or are deleted are cleared; grow-frame resizes only after an edit.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-06T03:06:17Z",
        "evidence_at": "2026-10-18T05:12:24.127Z",
        "headline": "All linked evidence is passing (23 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 23,
          "passed": 23,
          "skipped": 0
        }
      },
      "id": "plugin-web.auto-render",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Canvas follows the document"
    },
    {
      "chapter": "plugin-web",
      "claims": [
        {
          "component": "plugin.web",
          "note": "flatten a rendered web frame (or a whole flow) into NATIVE Paged page items so it exports to IDML/PDF and a foreign open sees content with NO plugin engine. Pure sceneLayerToBakePlan (SceneLayer→BakePlan) + an impure orchestrator (mirroring sheets S-03 lower.ts): createSwatch per colour with a deterministic Color/wb- selfId → colorRef; a native rectangle per solid fill; a native text frame per run (measure → insertTextFrame → resolve the minted story via a stories-diff → insertText → font size + fill colour). bakeWebFrame dispatches single-frame OR whole-flow. VERIFIED through the REAL headless host + REAL Blitz (bake conformance: native items created, a Color/wb- swatch really lands, a flow bakes across both frames) and BROWSER-verified as the EXPORT CAPSTONE (Phase E): bake → editor client.exportIdml() = a ~3.7 KB IDML that RE-PARSES → the baked story text survives re-import. Phase F (2026-07-19): non-rectangular SINGLE-subpath fills (rounded rects, decorative shapes) now bake as native PATHS (insertPath; ScenePathSeg→PathAnchorSpec bezier mapping) — verified on real-Blitz border-radius output. PARTIAL (honest v0): text (document DEFAULT face — SceneTextItem.family is a hint) + solid rects + single-subpath paths; MULTI-subpath fills, images, gradients, strokes are COUNTED in the plan's `deferred` + reported as a warning, never faked; a bake is multi-undo; baseline placement is estimated without measureString. The reserved ObjectTypeBaker.bake() host hook stays reserved (command-driven for now) 2026-10-05: ONE mutate batch per bake (bindCreated handles, story ids from minted; 1,410 door calls / 801 undo steps → 207 / 1 for 200 runs, count-budgeted); each run carries its font family and style; strokes, linear/radial gradients (native gradient swatches), opacity, drop shadows on rectangles, multi-subpath paths and images (image frames) kept; sweep gradients, gradient strokes, blends, inner shadows and shadows on rounded boxes are counted, not baked (W-17). InDesign lane (packages/web-conformance/indesign): InDesign 2025 reads geometry, text, faces, strokes, opacity, shadows and images where the bake put them; IB-02 (bold italic Inter arrives as Italic) pinned.",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:11:54.96Z",
        "headline": "All linked evidence is passing (106 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 106,
          "passed": 106,
          "skipped": 0
        }
      },
      "id": "plugin-web.bake-to-native",
      "importance": "normal",
      "since": "2026-07",
      "status": "experimental",
      "title": "Bake to native content — flatten a rendered web frame/flow to Paged page items (IDML/PDF export path)"
    },
    {
      "chapter": "plugin-web",
      "claims": [
        {
          "component": "plugin.web",
          "note": "33 hand-authored layout fixtures and 7 flow fixtures recorded in headless Chromium with the engine's own Inter (box rects, line boxes, text per line, PNG), replayed in Rust against Blitz without a browser: boxes ±0.5 px, same line breaks, line positions ±0.5 px, painted text. Disagreements are pinned defects (CW-/FW- ids) or documented divergences in packages/web-conformance/chrome/PARITY.md.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-17T07:19:41.904Z",
        "headline": "All linked evidence is passing (66 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 66,
          "passed": 66,
          "skipped": 0
        }
      },
      "id": "plugin-web.chrome-parity",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Layout parity with Chrome (recorded oracle)"
    },
    {
      "chapter": "plugin-web",
      "claims": [
        {
          "component": "plugin.web",
          "note": "Templates read {{doc.title}}, {{doc.pages}}, {{doc.<key>}} (a document value map in a container part), {{frame.page}} and {{data.<field>}} from a paged.data dataset provider; the canvas re-renders when a bound value changes; sources without bindings make no extra host calls.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-17T07:20:12.979Z",
        "headline": "All linked evidence is passing (13 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 13,
          "passed": 13,
          "skipped": 0
        }
      },
      "id": "plugin-web.data-binding",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Bound data in templates"
    },
    {
      "chapter": "plugin-web",
      "claims": [
        {
          "component": "core.renderer",
          "note": "the Blitz-lowered web content rasterises through the C-1 sceneLayer lane (one render path, CPU-testable). The IR grew to carry a broad CSS surface: v45 linear/radial gradient, v46 conic (new sweep pool, both backends) + per-fill blend (15 CSS modes) + drop shadow, v47 inset shadow, v48 gradient stroke + gradient-in-blend — each additive, wiring to existing render support, each CPU-rasterise-tested",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "note": "the sceneLayer wire carries the full CSS surface, PUBLISHED through canvas-wasm 0.48.0 (protocol v48); the editor now pins 0.61.1 (corrected 2026-08-09 — this row said it pinned 0.48.0)",
          "status": "shipped"
        },
        {
          "component": "plugin.web",
          "note": "the Blitz wasm engine SHIPS (web-render crate + bin/blitz_web*.wasm: the full Stylo/Taffy/Parley stack compiled to wasm32, ~9.2 MiB / 2.43 MiB brotli, vendored OFL font, DOM run-text recovery). Lowers END-TO-END + VISIBLE against real Blitz HTML: rects, strokes, transform-correct multi-run text, images, linear/radial/conic gradients, drop+inset shadows, spread. ON-CANVAS RENDER IN THE EDITOR NOW LOADS + PAINTS (Phase 1, 2026-07-18) — the prior RENDER-VERIFY 'submits but 0 visible px / web render BLANK in editor' finding (2026-06-18) was a wasm-LOAD bug, NOT a paint gap: the glue imported via new URL(import.meta.url) + @vite-ignore 404'd the moment Vite dep-optimised the bundle into .vite/deps. FIXED (in plugin-web, mirroring sheet-bundle): a relative import('../bin/blitz_web.js') → tsup dist chunk + a ?url wasm import passed to glue.default({module_or_path}) + tsup external:[/\\?url$/]. BROWSER-VERIFIED (Chromium+WebGPU): renderWebFrame paints 13,888 visible px (pixel-diff via the editor's CPU requestSnapshot); the same loader fix lights up the flow render too. ACCEPT-DORMANT: per-fill blend, gradient strokes, gradient-in-blend, clip-in-blend are built + contract-tested but Blitz emits no such layers yet (reachability tests flip live the day it does) 2026-10-05: font context built once per engine, run matching by index (815,409 → 903 comparisons on a 300-row table), zero-width border subpaths and the transparent canvas fill no longer cross the wire (bytes out 815 KB → 191 KB on that table); colour-only spans no longer repeat their line; text runs carry family/weight/italic (drawn in their own face by a protocol-68 host, ADR 126, core v0.68.0, on npm since 2026-10-06); layout checked against Chrome recordings (plugin-web.chrome-parity).",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:12:50.099Z",
        "headline": "All linked evidence is passing (165 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 165,
          "passed": 165,
          "skipped": 0
        }
      },
      "id": "plugin-web.engine-rendering",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "On-canvas vector rendering of web frames (Blitz/WASM → C-1 sceneLayer lane)"
    },
    {
      "chapter": "plugin-web",
      "claims": [
        {
          "component": "plugin.web",
          "note": "fragment one HTML flow across the frame chain with per-frame re-line-breaking + overset reporting (ADR-020 scoped extension on the shipped Blitz→C-1 sceneLayer lane; NO engine fork). The ladder: rung 1 equal-width display-list slice; rung 2 variable-width re-resolve-remainder via DocumentMutator (delete consumed prefix → re-resolve at the next width); rung 3 mid-block PARAGRAPH line split (Parley line geometry + a non-whitespace-count DOM map — plain text AND inline <b>/<span>); rung 3b NESTED/recursive CONTAINER fragmentation (a tall <ul>/<div>/<section> splits between its children at any depth); rung 3c TABLE row-split between body <tr>s with the <thead> REPEATING each frame (cell-derived row bands, since Taffy 0.11-exp lays out no <tr> box). Atomic by design: replaced elements (img/svg/canvas/video). web-render 115 nextest (parallel-deterministic, SHAPE_LOCK), run in CI by plugin-web rust.yml since 2026-10-05 (before that by no workflow) + web-bundle real-wasm smokes. rung 4 (the full forked engine) stays the last resort per ADR-020 2026-10-05: a flow that fits is no longer reported overset (FW-01); non-last frames paint only their band (12-frame article: 1,360 → 250 painted commands); an overset last frame is cut at its last fitting block/line/row (FW-03); list markers recover their own text (FW-02); break-before/after/inside, orphans/widows and @page margins honoured (plugin-web.page-breaks); every Chrome flow fixture agrees on breaks, overset and text conservation; a property test conserves text across random frame chains.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-17T07:20:12.98Z",
        "headline": "All linked evidence is passing (30 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 30,
          "passed": 30,
          "skipped": 0
        }
      },
      "id": "plugin-web.flow-fragmentation",
      "importance": "normal",
      "since": "2026-07",
      "status": "stable",
      "title": "Flow fragmentation — re-line-break one flow across N frames (ADR-020 ladder)"
    },
    {
      "chapter": "plugin-web",
      "claims": [
        {
          "component": "plugin.web",
          "note": "thread ONE web source across an ordered chain of publication frames (the CSS-Regions / IDML-story behaviour, host-chain-bound). The persisted region chain rides the source envelope so a flow survives reopen (WebFlowChain; not the earlier ephemeral selection-only chain). 7 bundle commands: insert/render web frame, renderWebFlow, thread/unthread/threadNamed WebFlow, bakeWebFrame. CSS `flow-into` NAMED flows (multi-flow): flowGroups splits recipients into primary + each named flow, flowSelectorFor renders each to its own frames. The source PANEL flow picker (flowThreadOptions) makes an ARBITRARY named flow (the 3rd/4th) reachable — the commands only reach the primary + the 2nd. web-bundle vitest (flow-command, bake, activate) + web-model (source-chain, css-flow) green. BROWSER-VERIFIED end-to-end (Chromium+WebGPU): thread persists to the source metadata + the panel picker renders all declared flows + clicking threads targets into an arbitrary named flow 2026-10-05: flows re-render by themselves on save, undo, resize and document open (plugin-web.auto-render); frames that leave a flow or are deleted are cleared; frame geometry for every flow group is read in one call and matched by id, skipping frames that no longer exist; editor journey web-flow-resize (on editor main) proves re-breaking with text conserved.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:12:14.71Z",
        "headline": "All linked evidence is passing (35 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 35,
          "passed": 35,
          "skipped": 0
        }
      },
      "id": "plugin-web.flow-threading",
      "importance": "normal",
      "since": "2026-07",
      "status": "stable",
      "title": "Flow threading — one web source across a chain of publication frames"
    },
    {
      "chapter": "plugin-web",
      "claims": [
        {
          "component": "plugin.web",
          "note": "sourceFromHtmlFile (scanner, not parser — total on any input) splits <style> bodies into the css lane, takes the <body> inner, and runs sanitizeHtml ON INGEST per the 6.1 product stance (scripts/ handlers/javascript: URLs never reach the source; removals logged). 4 web-model vitest. The bundle's first importer (media.paged.web.importer.html); ships in web canary.3 2026-10-05: every hand-authored conformance fixture runs through importer, linter, scanners and sanitizer in CI (hand-authored-corpus.spec.ts); sanitizer property tests (fast-check) pin three defects found by random search (S-01 unterminated script at the end, S-02 removal joining neighbours into a new construct, S-03 empty onerror kept). Sources over 64 KiB import (pointer label + part, ADR 409). Linked stylesheets/images/fonts are not yet brought into the container.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-17T07:20:12.995Z",
        "headline": "All linked evidence is passing (95 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 104,
          "passed": 95,
          "skipped": 9
        }
      },
      "id": "plugin-web.html-importer",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": ".html file importer — File/Open + drag-drop an HTML file into a web frame"
    },
    {
      "chapter": "plugin-web",
      "claims": [
        {
          "component": "plugin.web",
          "note": "Inside the web frame's edit context a click on rendered text edits the DOM text node it came from (render_web_frame_inspect maps paint to DOM); typing, Backspace/Delete, arrows, Home/End with a live re-render and a caret; Enter writes the source as one undo step, Esc restores; markup and entities around the edit stay byte-identical. One text node at a time.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-06T03:06:17Z",
        "evidence_at": "2026-10-18T05:12:24.127Z",
        "headline": "All linked evidence is passing (31 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 31,
          "passed": 31,
          "skipped": 0
        }
      },
      "id": "plugin-web.in-frame-edit",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "In-frame text editing"
    },
    {
      "chapter": "plugin-web",
      "claims": [
        {
          "component": "plugin.web",
          "note": "ONE batch via the v34 $created sentinel — insertFrame + setPluginMetadata atomically; single undo removes both (AC-WEB-1 E2E) 2026-10-05: a source over the 64 KiB label cap is inserted as a pointer label plus a content-addressed part (ADR 409) instead of failing the whole batch; a refusal is a visible diagnostic.",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "exercised through the editor panel surface (web-plugin.spec e2e)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-06-18T11:50:06Z",
        "evidence_at": "2026-10-18T05:12:44.489Z",
        "headline": "All linked evidence is passing (23 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 23,
          "passed": 23,
          "skipped": 0
        }
      },
      "id": "plugin-web.insert-command",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Insert web frame (one undo step, frame + source metadata)"
    },
    {
      "chapter": "plugin-web",
      "claims": [
        {
          "component": "plugin.web",
          "note": "W-02 resolved 2026-06-07 — x-paged:media.paged.web Label entry via host doors; one-time legacy plugin-storage migration (write-through then delete). 2026-10-05 (plugin-web ADR 409): the label is the truth undo restores; a source over the 64 KiB label cap goes to a content-addressed container part behind a pointer label — undo of a save now restores the previous source through every reader, and large sources insert/import/save (source-persistence.spec + conformance/persistence.spec, red first against the old reader) Unreachable source parts are dropped on save (ADR 410); a legacy-part read is skipped for labels the new writer wrote.",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "persistence round-trip exercised via the panel surface (web-plugin.spec e2e)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-06-18T11:50:06Z",
        "evidence_at": "2026-10-18T05:12:44.489Z",
        "headline": "All linked evidence is passing (44 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 44,
          "passed": 44,
          "skipped": 0
        }
      },
      "id": "plugin-web.metadata-persistence",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Source persistence in the document (IDML round-trip, foreign-open safe)"
    },
    {
      "chapter": "plugin-web",
      "claims": [
        {
          "component": "plugin.web",
          "note": "Web frames, their source and CSS custom properties are typed objects; one plugin-level planner over all three kinds, a batch over several paths is one step and a bad value applies nothing; web properties are host PropertyFields (ADR 323, contract 0.2.43, engine 0.70).",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-17T07:20:13.001Z",
        "headline": "All linked evidence is passing (18 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 18,
          "passed": 18,
          "skipped": 0
        }
      },
      "id": "plugin-web.object-model",
      "importance": "normal",
      "since": "2026-10",
      "status": "stable",
      "title": "Object model — web frames, their HTML and CSS properties addressable through host.objects"
    },
    {
      "chapter": "plugin-web",
      "claims": [
        {
          "component": "plugin.web",
          "note": "Clicking a tag in the Web frame panel's outline outlines its painted box on the canvas (overlay door); a click inside the entered frame highlights its tag in the outline.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-17T07:20:12.983Z",
        "headline": "All linked evidence is passing (20 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 20,
          "passed": 20,
          "skipped": 0
        }
      },
      "id": "plugin-web.outline-canvas",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Outline and canvas highlight"
    },
    {
      "chapter": "plugin-web",
      "claims": [
        {
          "component": "plugin.web",
          "note": "clip, shrink to fit, grow frame (one undoable resizeFrame) and continue into thread, persisted in the source, each checked against the real engine.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-17T07:20:12.992Z",
        "headline": "All linked evidence is passing (11 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 11,
          "passed": 11,
          "skipped": 0
        }
      },
      "id": "plugin-web.overflow-policies",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Overflow policies"
    },
    {
      "chapter": "plugin-web",
      "claims": [
        {
          "component": "plugin.web",
          "note": "break-before/after (page, column, avoid), break-inside: avoid, orphans and widows (initial 2, as Chrome) and @page margins honoured when a flow is cut into frames; a frame is a page box (ADR 412); checked against Chrome print fragmentation fixtures.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-17T07:15:37.29Z",
        "headline": "All linked evidence is passing (11 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 11,
          "passed": 11,
          "skipped": 0
        }
      },
      "id": "plugin-web.page-breaks",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Break rules and @page in flows"
    },
    {
      "chapter": "plugin-web",
      "claims": [
        {
          "component": "plugin.web",
          "note": "A draft history shared by the Web frame panel and the web frame edit context, wired through the ADR 012 onUndo/onRedo/onCanUndo/onCanRedo hooks; edits within a second are one step.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-17T07:20:12.953Z",
        "headline": "All linked evidence is passing (5 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 5,
          "passed": 5,
          "skipped": 0
        }
      },
      "id": "plugin-web.panel-undo",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Host undo reaches panel edits"
    },
    {
      "chapter": "plugin-web",
      "claims": [
        {
          "component": "plugin.web",
          "note": "perf-counters feature in web-render (parses, resolves, paint captures, painted commands, run-match comparisons, font-context builds, bytes across the boundary) with Rust count budgets in tests/perf_budgets.rs; a counting host over the real engine in web-bundle test/perf/ (door calls per render, flow, bake, font watch, cold boot). Budgets pinned at measured values (baseline docs/design/perf-baseline-2026-10-05.md); lowered only by the commit that earns it.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-05T15:53:59Z",
        "evidence_at": "2026-10-17T07:20:13.002Z",
        "headline": "All linked evidence is passing (7 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 7,
          "passed": 7,
          "skipped": 0
        }
      },
      "id": "plugin-web.perf-budgets",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Work counters and count budgets"
    },
    {
      "chapter": "plugin-web",
      "claims": [
        {
          "component": "plugin.web",
          "note": "a bundle-scoped report store retains the last command outcome (submitted frames, the flow overset flag, bake deferred-kind counts, diagnostics) and the source panel renders it — the readout the editor-ui-coverage spec ranked M; namedFlowDiagnostics joins the panel lint lane (was built + unreferenced). 5 vitest 2026-10-05: the flow overset flag is now correct (FW-01 fixed) and resource misses and font fallbacks join the problems lane.",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "the editor pins @paged-media/web 0.1.0-canary.16 (verified 2026-10-15; the readout shipped in canary.2), and the editor web-render journey asserts the readout ([data-web-render-report=\"renderFrame\"]) after a render",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-17T07:20:12.957Z",
        "headline": "All linked evidence is passing (3 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 3,
          "passed": 3,
          "skipped": 0
        }
      },
      "id": "plugin-web.render-readout",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Flow/render readout — the last render/flow/bake outcome in the source panel"
    },
    {
      "chapter": "plugin-web",
      "claims": [
        {
          "component": "plugin.web",
          "note": "img, CSS backgrounds, linked stylesheets, @import and @font-face url() load from container parts (resources/<path>), data: URIs and paged-image:<id> asset-store images; network URLs and misses are reported; images are scene images on the canvas and image frames in the bake.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-17T07:20:12.917Z",
        "headline": "All linked evidence is passing (14 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 14,
          "passed": 14,
          "skipped": 0
        }
      },
      "id": "plugin-web.resources",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Sub-resources from the document only"
    },
    {
      "chapter": "plugin-web",
      "claims": [
        {
          "component": "plugin.web",
          "note": "WebFrameSource {html,css,options}; envelopeFor/sourceFromEnvelope (version-gated, validating); diagnoseHtml policy linter (script/iframe/net rejections); since 2026-10-05 also the storage model (inline label vs content-addressed part behind a pointer: storeSource/sourceRefOf/sourceFromPartText). web-model vitest 278 passed + 9 skipped (opt-in corpus lane), measured 2026-10-05 after the campaign merges; also the overflow policy, bound-data and document-value models",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-17T07:19:43.191Z",
        "headline": "All linked evidence is passing (59 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 59,
          "passed": 59,
          "skipped": 0
        }
      },
      "id": "plugin-web.source-model",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "WebFrameSource model + metadata envelope + diagnostics linter"
    },
    {
      "chapter": "plugin-web",
      "claims": [
        {
          "component": "plugin.web",
          "note": "token-styled editors riding the host.widgets CodeEditor since f1e63a9 (W-04 resolved); sandbox=\"\" iframe preview — page JS never executes; async metadata reads with stale guard; debounced undoable writes; undo/redo re-read; AC-WEB-1..3 E2E 2026-10-05: unsaved drafts kept per frame across selection changes and undo remounts; the preview takes the frame's content size; host Cmd+Z steps panel drafts (plugin-web.panel-undo); the font watch reads once per burst of changes (1,001 → 2 reads per 1,000 edits); outline click highlights the painted box (plugin-web.outline-canvas); overflow policy select is live.",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "hosted as an editor panel (source panel UX; web-plugin.spec e2e)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-06-18T11:50:06Z",
        "evidence_at": "2026-10-18T05:12:44.489Z",
        "headline": "All linked evidence is passing (47 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 47,
          "passed": 47,
          "skipped": 0
        }
      },
      "id": "plugin-web.source-panel",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Source panel — HTML/CSS editors, sandboxed preview, options, diagnostics"
    },
    {
      "chapter": "plugin-web",
      "claims": [
        {
          "component": "plugin.web",
          "note": "Faces from @font-face and the document (asset store, container parts) are registered once into the engine's cached font context; bold/italic/weights shape with real faces (Inter weight axis); each scene text run carries family, weight and italic so the engine draws it in its own face (protocol 68), fontFallbacks surface as problems except the bundled Inter (no font-registration door yet).",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-06T03:06:17Z",
        "evidence_at": "2026-10-18T05:12:38.615Z",
        "headline": "All linked evidence is passing (95 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 95,
          "passed": 95,
          "skipped": 0
        }
      },
      "id": "plugin-web.web-fonts",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Web fonts and faces on the canvas"
    },
    {
      "chapter": "round-tripping",
      "claims": [
        {
          "component": "core.mutation",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "note": "v34 — a batch child addressing the literal $created resolves to the most recent creating child's minted id (create + attach metadata/properties as ONE undo step); batch reports the last creating child as createdId",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:12.577Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "round-tripping.batch-atomicity",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Batch operations (atomic, rollback-on-error, $created sentinel)"
    },
    {
      "chapter": "round-tripping",
      "claims": [
        {
          "component": "core.canvas-wasm",
          "note": "exactly 0 or 1 operations per session (INV-1)",
          "status": "shipped"
        },
        {
          "component": "editor.canvas",
          "status": "shipped"
        },
        {
          "component": "editor.gesture",
          "note": "five gesture updates commit as one atomic transaction (e2e/gesture-atomicity.spec.ts AC-E2E-GEST-ATOM-1 green)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-06-16T14:08:38Z",
        "evidence_at": "2026-10-18T05:00:54.957Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "round-tripping.gesture-transactions",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Gesture transaction channel (begin/update/commit/cancel)"
    },
    {
      "chapter": "round-tripping",
      "claims": [
        {
          "component": "core.mutation",
          "note": "byte-identical unmutated round-trips, now PROVEN ON THE CORPUS — 99/99 packages, 11,876 entries, 0 gaps, +0 byte growth (2026-08-05; plugin-publish.byte-identity-sweep). Mutated carry-through for item transforms, fills/strokes/threading, para/char range attrs + single-Content text, and Properties/Label plugin metadata (v33), and since 2026-08-05 also object styles, master spreads, z-order across all three sibling lists, and byte-preserving f32 for ItemTransform / StrokeWeight / eight story-run properties. LOSSES still open: PathGeometry-frame bounds, group-member transforms, multi-Content runs, table-cell text, structural edits (W3.B2 scope)",
          "status": "partial"
        },
        {
          "component": "editor.canvas",
          "note": "client.exportIdml — ExportIdml wire → IdmlExported bytes (W3.B2)",
          "status": "shipped"
        },
        {
          "component": "editor.shell",
          "note": "File ▸ Save As IDML (client.exportIdml → browser download), W3.A2",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-09-09T14:20:18Z",
        "evidence_at": "2026-10-18T07:47:02.411Z",
        "headline": "All linked evidence is passing (14 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 14,
          "passed": 14,
          "skipped": 0
        }
      },
      "id": "round-tripping.idml-reserialization",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Re-serialize a PARSED document to IDML (save back)"
    },
    {
      "chapter": "round-tripping",
      "claims": [
        {
          "component": "core.canvas-wasm",
          "note": "CanvasModel native-first load (sniff) + refresh/read_model_part + auto-embed on export_paged; paged-store codec. N1–N4 merged, corpus-fidelity gated.",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "native reconstruction renders identically to the source-parsed model (digest-equal; raw bytes render-dead).",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:47:57.148Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "round-tripping.native-reserialization",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Native document round-trip (self-owning .paged model, no source parse)"
    },
    {
      "chapter": "round-tripping",
      "claims": [
        {
          "component": "core.mutation",
          "note": "every operation captures inverse state; byte-identical restore",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "status": "shipped"
        },
        {
          "component": "editor.canvas",
          "note": "Cmd+Z / Cmd+Shift+Z",
          "status": "shipped"
        },
        {
          "component": "editor.script",
          "note": "paged.undo()/redo()",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:10:59.766Z",
        "headline": "All linked evidence is passing (135 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 135,
          "passed": 135,
          "skipped": 0
        }
      },
      "id": "round-tripping.undo-redo",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Undo / redo (invertible operation log)"
    },
    {
      "chapter": "scripting",
      "claims": [
        {
          "component": "editor.script",
          "note": "A typed object-model command the host registers; a script reaches it through the protocol-71 host bridge: JSON.parse(await paged.invoke(\"paged.ai.complete\", { prompt })). Editor answers through /api/ai, the CLI with the caller's credential; 20 calls a minute per host. No engine change.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-11T17:52:49Z",
        "evidence_at": "2026-10-18T04:37:32.807Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "scripting.ai-complete",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Scripts ask Claude for text (paged.ai.complete)"
    },
    {
      "chapter": "scripting",
      "claims": [
        {
          "component": "editor.script",
          "note": "Script panel \"Ask Claude\" bar (claude-haiku-5-5): the agent writes a script, runs it through client.executeScript, reads error / output / overset stories back and corrects. Model turns go through the same-origin /api/ai proxy (editor ADR 221). Canned-turn e2e in apps/canvas/tests/ai-script.spec.ts; no live model run in CI.",
          "status": "shipped"
        },
        {
          "component": "plugin.api",
          "note": "@paged-media/ai (plugin-sdk packages/ai, ADR 325): prompt, tools, loop, transports, proxy handler; CLI `paged ai <doc> <brief>`. Not yet on npm (first publish is a manual step); the editor vendors it in packages/ai.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-11T17:52:49Z",
        "evidence_at": "2026-10-18T04:37:28.192Z",
        "headline": "All linked evidence is passing (3 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 3,
          "passed": 3,
          "skipped": 0
        }
      },
      "id": "scripting.ai-script-writing",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Claude writes and runs paged.* scripts (Script panel Ask bar, `paged ai`)"
    },
    {
      "chapter": "scripting",
      "claims": [
        {
          "component": "core.canvas-wasm",
          "note": "insertFrame/insertTextFrame return the new kind:id address (was bool) and auto-select it; insertPage returns the minted page selfId. Create-then-address with no editor.* fallback. Core v0.51.2 carries it; 0.51.x never reached npm, so the first published release with it is canvas-wasm 0.61.0 (2026-08-06).",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:58:58.616Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "scripting.author-returns-id",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Insert fns return the created id + auto-select"
    },
    {
      "chapter": "scripting",
      "claims": [
        {
          "component": "core.canvas-wasm",
          "note": "core crates/paged-cli builds one binary, `paged`, that drives WorkerCore::dispatch, the typed door the editor's worker uses, so it cannot apply a mutation or load with options the editor cannot express. Subcommands: render, inspect, export (IDML, .paged, PDF), script (runs a .js file through the paged.* bridge; 60 s wall clock by default; an edit without -o says it was not saved), new, gen, diff (ΔE2000 + SSIM), read (the engine's diagnostic reads), place (an image into a frame with a fit), set and get (one property of any object), invoke and ops (any wire operation by its tag), parts (a .paged container's parts), query (selector → canonical addresses), describe (the capability catalog), digest, and session (the headless NDJSON protocol; paged-run is its shim). Text that names no font, or one the machine lacks, is set in the bundled default face. Which engine message kinds it reaches is a gate (tests/cli_surface.rs: 39 of 70). Built from source (cargo build --release -p paged-cli); no prebuilt binary or package is published. Documented at docs.paged.media (Command line), generated from the binary's own parser (cli.json).",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:51:33.457Z",
        "headline": "All linked evidence is passing (51 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 51,
          "passed": 51,
          "skipped": 0
        }
      },
      "id": "scripting.cli",
      "importance": "normal",
      "since": "2026-09",
      "status": "stable",
      "title": "The paged command line — open, author, render, export, query and verify documents from a shell"
    },
    {
      "chapter": "scripting",
      "claims": [
        {
          "component": "editor.script",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:59:15.074Z",
        "headline": "All linked evidence is passing (11 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 11,
          "passed": 11,
          "skipped": 0
        }
      },
      "id": "scripting.collections",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Collection reads (swatches, styles, links, conditions, gradients, colorGroups, generic collection())"
    },
    {
      "chapter": "scripting",
      "claims": [
        {
          "component": "editor.script",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:59:15.074Z",
        "headline": "All linked evidence is passing (4 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 4,
          "passed": 4,
          "skipped": 0
        }
      },
      "id": "scripting.console",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "console.log/warn/error/info capture"
    },
    {
      "chapter": "scripting",
      "claims": [
        {
          "component": "core.canvas-wasm",
          "note": "The named seed preludes are pure paged.* that scaffold a starter document through the engine; a paged-script test runs each (and the docs validate:scripting gate runs the same preludes through paged-run).",
          "status": "shipped"
        },
        {
          "component": "editor.script",
          "note": "?embed=script&seed=<name> runs a pure-paged.* prelude before the snippet (named seed registry), with per-run reseed; the editable snippet stays pure paged.*.",
          "status": "shipped"
        },
        {
          "component": "editor.styleguide",
          "note": "116 DTP-professional examples (every one of the 109 host fns + 8 composite workflows + 10 themed settable-path showcases), each an editable working playground against a seeded doc. Corpus pinned to the catalog (check:scripting), execution-validated against the real engine via paged-run (validate:scripting + CI scripting.yml), each linked to its scripting.* registry row (feature field) and backed by a paged-script engine test.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:58:58.715Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "scripting.docs-playground-seeds",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Docs scripting playground — seeded starter documents + per-function examples"
    },
    {
      "chapter": "scripting",
      "claims": [
        {
          "component": "core.canvas-wasm",
          "note": "Comprehensive wiring of every meaningful Mutation variant + selection setters as paged.* host fns (109 total), so the scripting layer exposes the complete engine surface (reads were already 100%). Catalog kept in lockstep (paged-introspect); proven end-to-end by 116 docs examples executed headlessly through paged-run + paged-script engine tests. Native path proven; the browser/editor path is pending the v0.51.x wasm publish + play.paged.media redeploy.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:58:58.432Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "scripting.full-mutation-surface",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Full mutation surface wired into paged.* (delete/duplicate/dissolve, tables, paths, style CRUD, sections, conditions, layers, guides, selection setters)"
    },
    {
      "chapter": "scripting",
      "claims": [
        {
          "component": "core.canvas-wasm",
          "note": "native paged-run binary: stdio NDJSON session holding one CanvasModel — load/new-blank, run-script (execute_script), inspect, digest (DisplayList::digest GPU-faithful oracle), render (CPU/tiny-skia), export idml|paged. The headless host for the paged.* Boa surface outside the browser worker; 2 integration tests, clippy-clean.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:58:51.73Z",
        "headline": "All linked evidence is passing (6 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 6,
          "passed": 6,
          "skipped": 0
        }
      },
      "id": "scripting.headless-run",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Headless engine session (paged-run NDJSON CLI)"
    },
    {
      "chapter": "scripting",
      "claims": [
        {
          "component": "editor.script",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:59:15.074Z",
        "headline": "All linked evidence is passing (6 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 6,
          "passed": 6,
          "skipped": 0
        }
      },
      "id": "scripting.inspection",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "paged.inspect / paged.tree / paged.stories / documentMeta / selection"
    },
    {
      "chapter": "scripting",
      "claims": [
        {
          "component": "core.canvas-wasm",
          "note": "executeScript request",
          "status": "shipped"
        },
        {
          "component": "editor.script",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:59:15.074Z",
        "headline": "All linked evidence is passing (4 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 4,
          "passed": 4,
          "skipped": 0
        }
      },
      "id": "scripting.mutation-parity",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Script ↔ wire mutation parity (same Operation channel)"
    },
    {
      "chapter": "scripting",
      "claims": [
        {
          "component": "plugin.api",
          "note": "plugin-sdk packages/cli: a `paged` bin (Node 22 or later) that loads core (@paged-media/canvas-wasm) and every installed plugin bundle through the headless host and drives them through host.objects. Verbs: get, set, create, delete, query, schema, batch, commands, invoke, run, export, apply, merge, plugins, describe, and ai: `paged ai <doc> <brief>` (or --new <size>) lets Claude write and run paged.* scripts against the document until the brief is done, through @paged-media/ai, with the caller's own Anthropic credential. Partial: neither @paged-media/cli nor @paged-media/ai is on npm (both answer 404; a first publish is a manual step), and plugin-sdk CI does not run the CLI's own tests (packages/cli/test); the @paged-media/ai suite runs there but uploads no JUnit. The record therefore holds no test evidence for this surface.",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "scripting.node-cli",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "The Node CLI over core and installed plugins, with `paged ai` (@paged-media/cli)"
    },
    {
      "chapter": "scripting",
      "claims": [
        {
          "component": "core.canvas-wasm",
          "note": "New read host fn over CanvasModel::pages() → PageSummary[] (selfId/index/sizePt). Closes the 'no page id obtainable' gap: a script can feed a real page id to insertFrame/insertTextFrame/insertPage. Core v0.51.2 carries it; 0.51.x never reached npm, so the first published release with it is canvas-wasm 0.61.0 (2026-08-06).",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:58:58.649Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "scripting.page-enumeration",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "paged.pages() — page-id enumeration for authoring"
    },
    {
      "chapter": "scripting",
      "claims": [
        {
          "component": "editor.script",
          "note": "every write lands as an undoable Operation",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-09-09T14:13:29Z",
        "evidence_at": "2026-10-18T05:08:26.116Z",
        "headline": "All linked evidence is passing (42 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 42,
          "passed": 42,
          "skipped": 0
        }
      },
      "id": "scripting.property-readwrite",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "paged.set / paged.get / paged.frame proxy (all property paths)"
    },
    {
      "chapter": "scripting",
      "claims": [
        {
          "component": "editor.panel",
          "status": "shipped"
        },
        {
          "component": "editor.script",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:59:15.074Z",
        "headline": "All linked evidence is passing (6 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 6,
          "passed": 6,
          "skipped": 0
        }
      },
      "id": "scripting.repl",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "REPL panel (inspect/set/undo/redo command line)"
    },
    {
      "chapter": "scripting",
      "claims": [
        {
          "component": "core.canvas-wasm",
          "note": "Boa RuntimeLimits (loop 10M, recursion 512) via budgeted_context; RuntimeLimit surfaces as 'runtime budget exceeded' instead of a worker-aborting panic (B-09 core side)",
          "status": "shipped"
        },
        {
          "component": "editor.script",
          "note": "runaway scripts return a script error; the worker survives",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:59:15.074Z",
        "headline": "All linked evidence is passing (4 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 4,
          "passed": 4,
          "skipped": 0
        }
      },
      "id": "scripting.runtime-budgets",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Script runtime budgets (loop/recursion limits, clean errors)"
    },
    {
      "chapter": "scripting",
      "claims": [
        {
          "component": "editor.panel",
          "status": "shipped"
        },
        {
          "component": "editor.script",
          "status": "shipped"
        },
        {
          "component": "editor.styleguide",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:59:15.074Z",
        "headline": "All linked evidence is passing (14 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 14,
          "passed": 14,
          "skipped": 0
        }
      },
      "id": "scripting.script-editor",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Script editor panel (multi-line JS, Cmd+Enter, output log)"
    },
    {
      "chapter": "scripting",
      "claims": [
        {
          "component": "core.canvas-wasm",
          "note": "Stage 2 grow — host fns expose Mutation::InsertFrame / InsertPage / PlaceImage / ApplyStyle (scope inferred from the style ref) / CreateGroup (member ids via parse_element_id). Enables from-blank authoring through the Boa bridge; 2 integration tests.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:58:56.83Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "scripting.structural-authoring",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "paged.insertFrame / insertPage / placeImage / applyStyle / createGroup"
    },
    {
      "chapter": "scripting",
      "claims": [
        {
          "component": "core.canvas-wasm",
          "note": "Stage 1 grow — host fns expose Mutation::InsertText / DeleteRange / InsertTextFrame through the Boa bridge (was: scripts could only restyle, not author). Same apply_mutation channel as the Type tool; undo/redo parity. 3 integration tests on the geometry-groups fixture.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:58:56.946Z",
        "headline": "All linked evidence is passing (3 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 3,
          "passed": 3,
          "skipped": 0
        }
      },
      "id": "scripting.text-authoring",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "paged.insertText / deleteRange / insertTextFrame (text authoring)"
    },
    {
      "chapter": "scripting",
      "claims": [
        {
          "component": "editor.script",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:59:15.074Z",
        "headline": "All linked evidence is passing (4 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 4,
          "passed": 4,
          "skipped": 0
        }
      },
      "id": "scripting.undo-redo",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "paged.undo / paged.redo"
    },
    {
      "chapter": "scripting",
      "claims": [
        {
          "component": "core.canvas-wasm",
          "note": "ExecuteScript dispatch sets CacheEffect::ClearAll so the GPU scene cache rebuilds; previously a paged.* mutation landed in the model but the stale Vello scene was reused.",
          "status": "shipped"
        },
        {
          "component": "editor.script",
          "note": "worker.ts marks the renderer dirty on a scriptResult reply (markDirty([])), so both GPU + CPU backends repaint. Fixes the docs playground 'nothing happens' symptom.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:58:58.68Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "scripting.visible-results",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Script mutations repaint the canvas (ClearAll + markDirty on scriptResult)"
    },
    {
      "chapter": "sections-numbering-variables",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:36:53.618Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "sections-numbering-variables.page-number-resolution",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Page-number marker resolution (current/next/previous)"
    },
    {
      "chapter": "sections-numbering-variables",
      "claims": [
        {
          "component": "core.mutation",
          "note": "Insert/Edit/DeleteSection (W0.5)",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "SHIPPED — corrected 2026-08-09. The seam is gone: apps/canvas/src/panels/cockpit/document-map-panel.tsx fires the v28 `insertSection` Operation at :256 behind the Add-section button (:431,:451), and the panel header (:31) says so. Covered by apps/canvas/tests/document-map-panel.spec.ts. Was: Document map 'Add section' seam",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:06.354Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "sections-numbering-variables.section-ops",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Section CRUD (add section, ranges)"
    },
    {
      "chapter": "sections-numbering-variables",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "every page-numbering style of InDesign 20.0.1 (Roman edition, 13): Arabic, upper/lower Roman (past 3999 as InDesign writes it), upper/lower letters, Kanji, Arabic Alif Ba Tah and Abjad, Hebrew Biblical and non-standard, single/double/triple leading zeros, formatted as InDesign labels its pages (core ADR 157, 191 labels replayed from a DOM probe) + prefix labels; footnotes and chapter numbers use the same table. Other editions' styles (full-width, Hindi, Thai, Farsi) fall back to Arabic",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:57:31.462Z",
        "headline": "All linked evidence is passing (11 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 11,
          "passed": 11,
          "skipped": 0
        }
      },
      "id": "sections-numbering-variables.sections",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Sections (prefix, label styles, start numbers)"
    },
    {
      "chapter": "sections-numbering-variables",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "resolved live at render (pipeline/links.rs): custom text, running headers (paragraph and character style match, second pass), last page number, page count, chapter number, file name, creation/modification/output dates from a deterministic document clock. a cross-reference's page (ADR 169) and chapter number print live, and a live caption reading the link Name prints the name of the image its frame touches, live after a relink (core v0.73.4, asked of InDesign 20.0.1: xref-chapter-probe, caption-probe). OPEN: caption metadata providers other than the link Name (XMP description, …) print the stored ResultText",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:58:19.362Z",
        "headline": "All linked evidence is passing (3 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 3,
          "passed": 3,
          "skipped": 0
        }
      },
      "id": "sections-numbering-variables.text-variables",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Text variables (definition + instance substitution)"
    },
    {
      "chapter": "server",
      "claims": [
        {
          "component": "server.api",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "server.accounts",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Account management"
    },
    {
      "chapter": "server",
      "claims": [
        {
          "component": "server.api",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "server.auth",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Authentication (better-auth)"
    },
    {
      "chapter": "server",
      "claims": [
        {
          "component": "server.api",
          "note": "POST /api/v1/generate enqueues an agent_generate job; the job drives the core paged-run engine via a stdio bridge (PagedRunSession) and persists the export to the user's documents namespace + a render thumbnail. Route + job + handler + engine bridge SHIPPED and tested (typecheck clean, offline suite green, engine integration test vs the real binary). PARTIAL: the document agent is the deterministic StubDocumentAgent — the live @anthropic-ai/sdk Tool-Runner agent (and ANTHROPIC_API_KEY-gated activation) is pending.",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "server.automation",
      "importance": "normal",
      "since": "2026-06",
      "status": "experimental",
      "title": "Document automation API (brief → agent job → engine → export)"
    },
    {
      "chapter": "server",
      "claims": [
        {
          "component": "server.api",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "server.billing",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Billing (Stripe)"
    },
    {
      "chapter": "server",
      "claims": [
        {
          "component": "server.api",
          "note": "Stage 3 — pure, unit-tested reducer turning plugin-data's PaginatedFlow IR (frames → header/record/footer text blocks) into per-frame text and a paged.insertText Boa script (the headless equivalent of the plugin bundle's host-mutation step; enabled by Stage 1). 4 unit tests. PARTIAL: the paged-data-batch (data-cli) shell-out + frame→story resolution + the data-merge job variant are the remaining wires.",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "server.data-merge",
      "importance": "normal",
      "since": "2026-06",
      "status": "experimental",
      "title": "Data-merge apply bridge (PaginatedFlow → Boa script)"
    },
    {
      "chapter": "server",
      "claims": [
        {
          "component": "server.api",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "server.fonts-proxy",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Fonts proxy"
    },
    {
      "chapter": "server",
      "claims": [
        {
          "component": "server.api",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "server.image-search",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Image search"
    },
    {
      "chapter": "server",
      "claims": [
        {
          "component": "server.api",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "server.openapi",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "OpenAPI spec + generated client (drift-gated)"
    },
    {
      "chapter": "server",
      "claims": [
        {
          "component": "server.api",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "server.storage",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Document storage (local:// offline default + remote drivers)"
    },
    {
      "chapter": "sheet",
      "claims": [
        {
          "component": "plugin.sheet",
          "note": "TWO providers on ONE edit context, which is itself the finding that exposed a live host defect (a writability lookup keyed on plugin id alone read the wrong provider's declaration). SWATCHES — document-scoped and WRITABLE for exactly one verb: provides.collections ['swatches'], provides.ops ['editSwatch'], and DELIBERATELY no paths and no readProperty, because core models no swatch PropertyPath and there is nothing honest to declare. It serves the WORKBOOK PALETTE (chart-series, data-bar and now cell-fill swatches at deterministic Color/uPagedSheet<Kind><HEX> ids), not the document's list, and an empty palette DECLINES so the user sees core's swatches rather than empty rows; editSwatch mints on first edit because core refuses a duplicate createSwatch. createSwatch / deleteSwatch / importSwatchLibrary / editColorGroup are each left undeclared with a stated reason. CHARACTER/PARAGRAPH — selection-scoped and STRICTLY READ-ONLY (writablePaths: [], and the missing writeProperty callback is the consequence, not the declaration): 3 VALUED paths (characterFontFamily / FontStyle / FontSize) and 28 DECLARED-BUT-ABSENT (14 character, 13 paragraph, plus characterFillColor), which is a different fact from not claiming them. The mixed rule is strict — any disagreement across cells answers 'mixed', never a winner, and a partial declaration also answers mixed. PARTIAL: characterFillColor stays ABSENT and the reason MOVED — it used to be 'the cell's text colour is a raw hex core cannot resolve', which sheet.lower.cell-colour-swatches invalidated; the value is now expressible as a minted id, but NOTHING APPLIES StyleEmission, so the id would name a swatch the document does not carry, and a colour chip for a swatch that does not exist is the same lie one axis over. When a driver lands the path moves from ABSENT_PATHS to VALUED_PATHS and nothing else changes",
          "status": "partial"
        },
        {
          "component": "plugin.api",
          "note": "consumes the ADR-023 phase-A contract; the two providers are consumers 2 and 3 of the three-shape proof set (SCOPE and VALUE)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-12T07:33:32Z",
        "evidence_at": "2026-10-18T04:47:29.821Z",
        "headline": "All linked evidence is passing (33 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 33,
          "passed": 33,
          "skipped": 0
        }
      },
      "id": "sheet.binding-providers",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "paged.sheet serves the HOST Swatches and Character/Paragraph panels (ADR 023 phase D)"
    },
    {
      "chapter": "sheet",
      "claims": [
        {
          "component": "plugin.sheet",
          "note": "the spike SHIPPED behind the exact-decimal cargo feature (rust_decimal drop-in via the Numeric trait, ~40 KiB wasm cost, divergence corpus + golden TSV); the RULING is DEFER the user-facing mode to v2 opt-in (f64 stays default — Excel-oracle conformance + the 15-sig-digit display rule already prevent visible float noise in print). Registry rulings sheet.calc.decimal.*",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:46:27.281Z",
        "headline": "All linked evidence is passing (3 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 3,
          "passed": 3,
          "skipped": 0
        }
      },
      "id": "sheet.calc.decimal",
      "importance": "normal",
      "since": "2026-06",
      "status": "experimental",
      "title": "Exact-decimal numeric mode (D-6 spike) — seam proven, deferred to v2 opt-in"
    },
    {
      "chapter": "sheet",
      "claims": [
        {
          "component": "plugin.sheet",
          "note": "topo-order dirty recalc over the dependency graph; Excel-compat rulings recorded per-row (1900 leap serial 60, 15-sig-digit display, f64 per D-6 with the Numeric trait seam proven by the decimal spike); iterative (circular) calc lands OFF by default (D-7). Single-threaded by design until workers (S-07/K-3 deferred — no consumer yet) Wave 4: NOW/TODAY follow the HOST clock (the facade hands Date.now + the timezone offset to sheet-js set_clock before every recalculating write and at boot; load carries it into the load recalc) — they evaluated at serial 0 before.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-12T07:33:32Z",
        "evidence_at": "2026-10-18T05:10:42.701Z",
        "headline": "All linked evidence is passing (97 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 97,
          "passed": 97,
          "skipped": 0
        }
      },
      "id": "sheet.calc.engine",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Calculation engine — dependency graph, dirty recalc, f64 numeric core (D-6), coercion, error values"
    },
    {
      "chapter": "sheet",
      "claims": [
        {
          "component": "plugin.sheet",
          "note": "5 registry rows implemented (sheet.calc.iterative.*). D-7: iterative calc is OFF by default — with the flag off a cycle keeps the sheet.calc.circular ruling (#REF! stored, reported on RecalcResult::circular). The flag is a CalcSettings field round-tripped from calcPr (iterate/iterateCount/iterateDelta). With it ON, a detected cycle iterates to a fixed point (seeded at 0, recomputed in stable sorted order up to max_iter, stopping early when the largest cell delta falls below max_change — Excel defaults 100/0.001); RecalcResult::circular is then empty and a non-settling cycle is reported on RecalcResult::non_converged. ECMA-376 §18.2.2 Wave 4 CORRECTION (2026-10-04): the claim that the flag round-trips from calcPr was not true — sheet-xlsx never parsed <calcPr>, so a workbook with iteration on recalculated its cycle as #REF!. Now parsed (iterate/iterateCount/iterateDelta → CalcSettings) and written back as a byte splice of <calcPr> only when the knobs changed (zero-edit workbook part stays verbatim); the workbook panel has an Iterative calculation checkbox (session.setIterative).",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-12T07:33:32Z",
        "evidence_at": "2026-10-18T04:47:29.798Z",
        "headline": "All linked evidence is passing (5 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 5,
          "passed": 5,
          "skipped": 0
        }
      },
      "id": "sheet.calc.iterative",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Iterative (circular) calculation — OFF by default (D-7), converges to a fixed point when enabled"
    },
    {
      "chapter": "sheet",
      "claims": [
        {
          "component": "plugin.sheet",
          "note": "Wave 4 (2026-10-04, plugin-sheets fundamentals/wave-4): the door ships — sheet-js structural_edit over sheet-calc apply_edit (insert/delete rows/cols; references rewritten, #REF! for deleted spans), the moved sheet re-encoded from the model and every formula whose text moved re-printed (also on other sheets); the sheet-bundle Insert/Delete rows/columns commands + workbook-panel buttons act at the grid selection. PARTIAL by design: REFUSED (model untouched, the reason named) on a sheet carrying preserved cell-addressed content the writer cannot shift — conditional formats, data validations, hyperlinks, autoFilter, tables, drawings/comments, defined names naming the sheet, unparsed formulas — preservation over reach. Bound page ranges do not shift with the edit.",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": "2026-10-12T07:33:32Z",
        "evidence_at": "2026-10-18T04:47:29.798Z",
        "headline": "All linked evidence is passing (6 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 6,
          "passed": 6,
          "skipped": 0
        }
      },
      "id": "sheet.calc.rewrite.structural-door",
      "importance": "normal",
      "since": "2026-08",
      "status": "experimental",
      "title": "Row/col insert-delete wasm door — structural re-emission, refused over preserved cell addresses"
    },
    {
      "chapter": "sheet",
      "claims": [
        {
          "component": "plugin.sheet",
          "note": "5 registry rows implemented; spill ranges materialize from the anchor, blocked spills produce #SPILL!, re-spill on recalc; array-function rows (registry functions/array.yaml) feed it",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:10:42.701Z",
        "headline": "All linked evidence is passing (14 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 14,
          "passed": 14,
          "skipped": 0
        }
      },
      "id": "sheet.calc.spill",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Dynamic-array spill (anchor + spill range,"
    },
    {
      "chapter": "sheet",
      "claims": [
        {
          "component": "plugin.sheet",
          "note": "add_chart(sheet, values, categories, kind, title, series_in) -> chart id: one Series per values column (or per ROW — the transpose control, see sheet.chart.graphs) appended to the model's charts — PAGE-SIDE ONLY by construction (the xlsx writer never re-derives chart parts, so authored charts lower to native vectors but do not save back into the workbook; the panel says so). Workbook panel gains an Author-chart section + a per-chart Lower list; conformance pins authoring never dirties the preservation ladder (chart.rs, 16 tests). Ships in sheets canary.3",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-08-05T06:59:05Z",
        "evidence_at": "2026-10-18T04:46:27.129Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "sheet.chart.authoring",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Chart authoring — create a chart from the editor (page-side lowering)"
    },
    {
      "chapter": "sheet",
      "claims": [
        {
          "component": "plugin.sheet",
          "note": "one pure geometry generator, two projections: sheets-mode grid view AND page lowering as native vector content via the CORE wire (insertPath/insertLine/insertOval — S-12 verified; paged.draw reached as a core surface, never a plugin, §2.1); xlsx chart-part fidelity rows on the preservation ladder; 14 chart vitest + 17 registry rows. The KIND SET (10, a superset of Illustrator's nine graph types) and its 2026-08 additions are sheet.chart.graphs Wave 4: a placed chart REFRESHES after edits that change its geometry (its elements, recorded by a scene-tree diff, are deleted and the chart re-lowered in place; an edit outside its series redraws nothing) and lands at the current selection.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-12T07:33:32Z",
        "evidence_at": "2026-10-18T05:09:41.76Z",
        "headline": "All linked evidence is passing (63 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 63,
          "passed": 63,
          "skipped": 0
        }
      },
      "id": "sheet.chart.engine",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Charts — plotters-backed custom DrawingBackend → frozen ChartGeometry IR → grid view + paged.draw page lowering"
    },
    {
      "chapter": "sheet",
      "claims": [
        {
          "component": "plugin.sheet",
          "note": "Closes the Illustrator §16.4 Graphs row INSIDE paged.sheet (paged.draw cannot: its isolation rule forbids importing sheet-chart, so a graphs adapter there would need a second chart engine — catalog §33 names the reuse route). The curated kind set went 7 -> 10 and is now a SUPERSET of Illustrator's nine: added StackedColumn, StackedBar, Radar. All three are expressed in the EXISTING frozen Primitive vocabulary (stacked = Rects with stacked arithmetic over a totals-based value scale; radar = Line polylines for grid rings, spokes and closed series outlines), so chartGeometryToMutations and the wire protocol needed NO change — proven on the real wasm. Also: Axis::title now DRAWS (modelled since M2, drew nothing — horizontal placement, since the frozen Primitive::Text has no rotation); c:grouping/percentStacked + c:radarChart now parse on import (an Excel stacked chart used to import as a grouped one and render wrong silently); add_chart gained series_in (columns|rows) — the catalog's transpose, a re-read of the same cells, never a rewrite of the user's data; the panel reads the kind list FROM the engine. Already met, not rebuilt: the graph data editor IS the workbook (panel + formula bar + K-1 in-frame session over live-to-recalc series ranges) and import/paste data is three shipped doors (xlsx importer, range clipboard, data provider). NOT faked: design markers / graph designs need a core element-INSTANCE op that does not exist — filed as RFI S-16, registry row sheet.chart.design-markers = planned. 1254 -> 1270 Rust, 311 -> 317 vitest, coverage gate 375 -> 383 rows / 0 gaps",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:46:27.169Z",
        "headline": "All linked evidence is passing (6 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 6,
          "passed": 6,
          "skipped": 0
        }
      },
      "id": "sheet.chart.graphs",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Graph types — stacked column/bar + radar, axis titles, transpose rows/columns, c:grouping import"
    },
    {
      "chapter": "sheet",
      "claims": [
        {
          "component": "plugin.sheet",
          "note": "S-15 RESOLVED 2026-06-11: dataProviders.consume capability + datasets panel + sheetFromDataset; discoverDatasets()/sourceFromDataset() seed a fresh workbook from the resolved snapshot (no network on the sheet's side, §1.1); (providerId,revision) remembered, onDidChange marks STALE, re-sourcing explicit; isolation §2.1 intact (only the neutral contract). 12 vitest. Follow-up: the per-cell encoding (plain vs tagged {t,v}) is under-specified in the contract — amendment filed in S-15",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:09:48.694Z",
        "headline": "All linked evidence is passing (3 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 3,
          "passed": 3,
          "skipped": 0
        }
      },
      "id": "sheet.data.consumer",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Data-provider CONSUMER — source a sheet from a governed dataset (host.dataProviders)"
    },
    {
      "chapter": "sheet",
      "claims": [
        {
          "component": "plugin.sheet",
          "note": "Wave 5: copy writes a tabular grid + TSV and snapshots the range inputs (get_range_inputs); pasting our own copy carries formulas re-addressed by the engine (shift_formulas over sheet_parser rewrite_fill — $ honoured, off-grid #REF!), a whole-multiple selection tiles the block, text from other apps pastes as values the engine types. One setCells write, one undo step; the pasted cells become the selection.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-04T13:42:41Z",
        "evidence_at": "2026-10-18T05:10:26.426Z",
        "headline": "All linked evidence is passing (6 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 6,
          "passed": 6,
          "skipped": 0
        }
      },
      "id": "sheet.edit.clipboard",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Multi-cell copy/paste — formulas re-addressed on paste, tabular payload, TSV interop, tiling"
    },
    {
      "chapter": "sheet",
      "claims": [
        {
          "component": "plugin.sheet",
          "note": "Wave 5: the fill-handle knob on the selection (panel + in-frame drag) and Cmd+D / Cmd+R through sheet-js fill_range: one number copies, two or more continue the least-squares trend; dates step by day or by month (day clamped); en/de weekday and month names continue their list keeping case; Item 1 counts on; formulas repeat re-addressed; source formats travel. Written in ONE set_cells batch (one recalc), one undo step. Known bound: undo restores inputs, not formats a fill brought along.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-04T13:42:41Z",
        "evidence_at": "2026-10-18T05:10:26.426Z",
        "headline": "All linked evidence is passing (15 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 15,
          "passed": 15,
          "skipped": 0
        }
      },
      "id": "sheet.edit.fill",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Fill handle and fill down/right — number, date, weekday/month and numbered-text series; formulas re-addressed"
    },
    {
      "chapter": "sheet",
      "claims": [
        {
          "component": "plugin.sheet",
          "note": "12 registry rows implemented (sheet.edit.*): stable key-column sort (numbers before text/bools/errors, payload rows move whole, dependents recalc, has_header pins row 1, blanks sink in BOTH directions, honest no-ICU collation ruling) that REFUSES over formula cells or spill output rather than corrupt references silently; find matches display text (values) or input text (formulas) with case + entire-cell toggles; replace rewrites the inputs lane only, an unparseable replacement leaves the cell intact and spilled cells are reported, not shadowed. Surfaced via the bundle's sort command + find-replace panel (sheet.plugin.sort.command / find-replace.panel rows)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-07T03:42:36Z",
        "evidence_at": "2026-10-18T05:07:40.619Z",
        "headline": "All linked evidence is passing (41 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 41,
          "passed": 41,
          "skipped": 0
        }
      },
      "id": "sheet.edit.ops",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Session edit ops — stable range sort (values lane), find over values/formulas, replace with skip reporting"
    },
    {
      "chapter": "sheet",
      "claims": [
        {
          "component": "plugin.sheet",
          "note": "Wave 5: refreshes are held while a sheet frame is entered and land as one refresh on exit; the journal survives the exit stamped with content versions, and a follower on document undoApplied/redoApplied unwinds/replays the workbook to the version the frame binding shows. Inside a session Cmd-Z stops at its start; the panel grid owns Cmd-Z while focused; every multi-cell op (paste, fill, clear, sort, replace) is one undo step written in one batch. Caveat: a refresh is still several document mutations (structure/text/decor, then the binding stamp), so the page text reverts one Cmd-Z after the workbook does — one-batch refresh is the remaining step.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:47:29.804Z",
        "headline": "All linked evidence is passing (8 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 8,
          "passed": 8,
          "skipped": 0
        }
      },
      "id": "sheet.edit.undo",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Host Cmd-Z reaches sheet edits outside the modal session (ADR-012 Tier 2)"
    },
    {
      "chapter": "sheet",
      "claims": [
        {
          "component": "plugin.sheet",
          "note": "every implemented row of registry/functions (the count is a docs fact, sheet.functions) (agg/array/database/date/fin/info/logical/lookup/math/stat/text + M1 tracks); build.rs codegen — no row, no dispatch, unregistered functions uncallable by construction; the coverage gate enforces 100% tests-per-implemented-row. LibreOffice differential oracle is a CI-container follow-up",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:10:42.701Z",
        "headline": "All linked evidence is passing (779 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 779,
          "passed": 779,
          "skipped": 0
        }
      },
      "id": "sheet.fn.library",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Function library — registered functions by family, registry-driven dispatch"
    },
    {
      "chapter": "sheet",
      "claims": [
        {
          "component": "plugin.sheet",
          "note": "Wave 6 (2026-10-04, plugin-sheets fundamentals/wave-6): sheet-js set_style / get_style apply a partial style (number-format code, font family/size/bold/italic/underline/colour, solid fill, per-edge border style + colour, horizontal/vertical alignment, wrap) to a range, a defined name or a table. styles.xml is byte-spliced: numFmt/font/fill/border/xf records are appended after a canonical dedup search, existing records never rewritten; a package without a styles part gets one. Style ids are positional (StyleId == cellXfs index), which also fixed a dirty re-encode writing the wrong s= index for xfs differing only in an unmodelled attribute. The style parser now reads Excel's <left style><color/></left> border form, underline, alignment and wrap. The page lowering carries underline, vAlign, wrap and border lines; placed tables get per-edge weights + colour swatches, cellVerticalJustification and characterUnderline. Workbook panel: number-format presets + custom code, B/I/U, font, fill, borders (all/outline/sides/none), alignment, wrap. PARTIAL: horizontal alignment reaches the IR (LoweredCell.align) and the grid but a placed table does not map it to paragraph justification; theme/indexed colours read back as empty in get_style; formatting is not on the in-session undo journal.",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": "2026-10-12T07:33:14Z",
        "evidence_at": "2026-10-18T05:10:34.267Z",
        "headline": "All linked evidence is passing (18 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 18,
          "passed": 18,
          "skipped": 0
        }
      },
      "id": "sheet.format.cell-style",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Cell formatting — number format, font, fill, borders, alignment, wrap (setStyle) persisted to styles.xml"
    },
    {
      "chapter": "sheet",
      "claims": [
        {
          "component": "plugin.sheet",
          "note": "16 format rows (core + M1 FORMAT2 advanced track) + 6 locale rows implemented; en/de v1 set per D-8; the formatted value is THE value both surfaces render (grid text == lowered text, one render path)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:10:42.701Z",
        "headline": "All linked evidence is passing (96 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 96,
          "passed": 96,
          "skipped": 0
        }
      },
      "id": "sheet.format.engine",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Number-format engine + en/de locale (D-8)"
    },
    {
      "chapter": "sheet",
      "claims": [
        {
          "component": "plugin.sheet",
          "note": "the en/de v1 locale set (D-8) extended with a Western-European Latin tier (fr/es/it) as data-table rows — separators, month/day names, am/pm, locale-from-workbook, number-parse-locale per locale. CJK is a recorded follow-up (sheet.format.locale.cjk-followup, planned)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-06-13T09:23:38Z",
        "evidence_at": "2026-10-18T04:46:28.383Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "sheet.format.locale.latin-tier",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Western-European Latin locale tier (fr/es/it) — display formatting data rows"
    },
    {
      "chapter": "sheet",
      "claims": [
        {
          "component": "plugin.sheet",
          "note": "Wave 5: Cmd+F / Find in sheet… focus the grid panel find field; Enter / Next / Prev select the next match after the active cell (row-major, wrapping; matching is the engine findAll) and scroll the in-frame grid to it; Find next in sheet repeats the last search.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:47:29.733Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "sheet.grid.find",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Find in sheet — a find field in the grid panel; next/previous select and reveal the match in-frame"
    },
    {
      "chapter": "sheet",
      "claims": [
        {
          "component": "plugin.sheet",
          "note": "sheet.grid.freeze.parse reads the worksheet <sheetViews>/<pane> split (xSplit/ySplit/topLeftCell/state=frozen) into the grid model; sheet.grid.freeze.render renders the frozen rows/columns as a pinned band in the sheets-mode grid (the frozen region stays fixed while the body scrolls). Parsed bytes round-trip preserved",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:46:28.286Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "sheet.grid.freeze.render",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Frozen panes — parse sheetViews/pane + render frozen rows/columns in-grid"
    },
    {
      "chapter": "sheet",
      "claims": [
        {
          "component": "plugin.sheet",
          "note": "the grid RENDERS in-frame via host.contribute.sceneLayer (C-1 vector v0.39.0 + text v0.40.0; windowed GridScene, O(visible) on 1M rows) and is EDITABLE end-to-end via K-1 (double-click entry → content-coord click-select → keystroke cell editor → Enter/Esc through the edit-context controller; editor inversion wiring on main). The K-1 live-validation e2e LANDED (editor tests/e2e/sheet-modal-session.spec.ts: AC-K1-1/2/3 + AC-C4 incl. the 30-degree-rotated content inversion + ADR-012 in-session Cmd-Z) — the old 'live validation outstanding' clause is history. CURRENT reality 2026-08-17 (truth pass): the loop is REGRESSED at HEADs — AC-K1-2/AC-K1-3 fail incl. retries in the local run (under a no-WebGPU harness, 'initGpu: no compatible wgpu adapter' — needs a GPU rerun before calling it a product regression) and the hands-on editor session fell back to the interim panel grid ('In-frame sheets mode awaits the SDK rendering surface (S-02)', sheet-bundle grid-panel.tsx). PARTIAL: that red; §8.0 seamless-undo boundary TBD; in-frame text v1 caveats (default face, upright glyphs); full sheets-MODE (S-01) + owned-content interception (S-09) + range clipboard (S-14) open. The interim SVG grid panel is retained (one geometry, two surfaces)",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": "2026-06-18T11:50:06Z",
        "evidence_at": "2026-10-18T05:11:26.042Z",
        "headline": "All linked evidence is passing (30 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 30,
          "passed": 30,
          "skipped": 0
        }
      },
      "id": "sheet.grid.inframe",
      "importance": "normal",
      "since": "2026-06",
      "status": "experimental",
      "title": "In-frame grid — sceneLayer render + K-1 modal select/edit (sheets-mode lite)"
    },
    {
      "chapter": "sheet",
      "claims": [
        {
          "component": "plugin.sheet",
          "note": "Wave 5: one key map (gridKeyAction) for both surfaces: arrows (shift extends), Tab/Shift-Tab, Enter/Shift-Enter (commit and move while editing), F2 edits the formula, Esc cancels, Delete/Backspace clear the selection as one undo step, PageUp/Down, Home, Cmd+C/V/D/R/A/F/Z; the panel grid takes focus and owns Cmd+Z while focused. Editor-side caveat: the edit-context controller forwards only printable keys, Backspace and Delete while no cell edit is open, so in-frame arrows/Tab/Enter/F2 work mid-edit only until the editor forwards them (shim recorded in the Wave 5 report); Cmd+D and Tab are bound by the editor first.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-04T13:42:41Z",
        "evidence_at": "2026-10-18T05:10:34.267Z",
        "headline": "All linked evidence is passing (11 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 11,
          "passed": 11,
          "skipped": 0
        }
      },
      "id": "sheet.grid.keyboard",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Grid keys — arrows, Tab/Enter, F2, Delete, Esc, PageUp/Down and Cmd shortcuts in-frame and in the panel"
    },
    {
      "chapter": "sheet",
      "claims": [
        {
          "component": "plugin.sheet",
          "note": "gridSceneToSvg render, click to select, double-click to edit, Enter commits through engine.setCell over the scene (the interim panel grid). Imported from the plugin-sheets registry (registry/features/grid.yaml).",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-12T07:53:22Z",
        "evidence_at": "2026-10-18T04:47:29.83Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "sheet.grid.panel-edit-contract",
      "importance": "normal",
      "since": "2026-10",
      "status": "stable",
      "title": "Grid panel edit and commit contract"
    },
    {
      "chapter": "sheet",
      "claims": [
        {
          "component": "plugin.sheet",
          "note": "Wave 5 (2026-10-04, plugin-sheets fundamentals/wave-5): one selection model (anchor, moving focus, active cell; grid-nav.ts) shared by the grid panel and the in-frame grid. Drag and shift-click select ranges (in-frame via onContentPointerDown/Move/Up in frame-content coordinates), shift-arrows extend, Tab/Enter walk inside a multi-cell selection. The in-frame window scrolls to keep the active cell visible.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:47:29.733Z",
        "headline": "All linked evidence is passing (5 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 5,
          "passed": 5,
          "skipped": 0
        }
      },
      "id": "sheet.grid.selection",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Range selection — drag, shift-click and shift-arrows in the grid panel and in-frame"
    },
    {
      "chapter": "sheet",
      "claims": [
        {
          "component": "plugin.sheet",
          "note": "Wave 4 (2026-10-04, plugin-sheets fundamentals/wave-4): a .csv/.tsv importer beside .xlsx (and the picker accepts them). sheet-js load_csv builds an ordinary XLSX package from the text (RFC 4180 quoting, BOM, delimiter sniffing tab/;/,) and loads it, so the import edits, lowers and saves like any workbook. Typing in Rust by the host language: sheet-format locale separators for numbers, ISO and locale-order dates rendered in the locale pattern (LCID-tagged, so the workbook display locale follows), percents, booleans; a leading = stays text.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-12T07:33:32Z",
        "evidence_at": "2026-10-18T04:47:29.798Z",
        "headline": "All linked evidence is passing (3 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 3,
          "passed": 3,
          "skipped": 0
        }
      },
      "id": "sheet.import.csv",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "CSV / TSV import as a one-sheet workbook, typed by locale"
    },
    {
      "chapter": "sheet",
      "claims": [
        {
          "component": "plugin.sheet",
          "note": "Wave 6 (2026-10-04, plugin-sheets fundamentals/wave-6): sheet-js set_freeze(sheet, rows, cols) rewrites the captured <sheetViews>: the old <pane> and pane-bound selections go, a frozen pane becomes the first child of the first <sheetView>, everything else in the view stays; a sheet without views gets one in schema position (the dirty re-encode now writes <dimension> before <sheetViews>). 0,0 clears. The grid's freeze map follows (sheet.grid.freeze.render). Workbook panel Freeze panes / Unfreeze toggle at the grid selection (no selection: the first row).",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:47:29.802Z",
        "headline": "All linked evidence is passing (3 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 3,
          "passed": 3,
          "skipped": 0
        }
      },
      "id": "sheet.layout.freeze",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Freeze panes — set / clear (sheetView pane) from the panel"
    },
    {
      "chapter": "sheet",
      "claims": [
        {
          "component": "plugin.sheet",
          "note": "Wave 6 (2026-10-04, plugin-sheets fundamentals/wave-6): sheet-js merge / unmerge. Merging keeps the top-left content and clears the other cells through the entry lane (dependents recalc; the cleared inputs are returned); refused on an overlapping merge or a spilled array. Persisted as <mergeCells> (the dirty re-encode now keeps <mergeCells> after a captured autoFilter/sheetProtection, schema order); the page lowering emits the span and placed tables setCellSpan. Workbook panel Merge / Unmerge on the grid selection or range.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-12T07:33:14Z",
        "evidence_at": "2026-10-18T04:47:29.802Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "sheet.layout.merge",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Merge / unmerge cells (model, xlsx mergeCells, page spans)"
    },
    {
      "chapter": "sheet",
      "claims": [
        {
          "component": "plugin.sheet",
          "note": "Wave 6 (2026-10-04, plugin-sheets fundamentals/wave-6): sheet-js set_col_width (characters, 0-255) / set_row_height (pt, 0-409), None resets; persisted to <cols> and <row ht customHeight> (a height-only row with no cells used to be dropped by a dirty re-encode — fixed); the lowering and pagination use them; get_layout reports them. Workbook panel width/height inputs with Default. PARTIAL: values only — no drag-resize in the grid (the grid panel is Wave 5's).",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:47:29.802Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "sheet.layout.sizes",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Column width / row height — set and reset, persisted, used by the page geometry"
    },
    {
      "chapter": "sheet",
      "claims": [
        {
          "component": "plugin.sheet",
          "note": "SETTLED BY RENDERING, NOT BY READING. Both cell fill and cell text colour used to hand a RAW HEX into a {type:'colorRef'} value, which core resolves by SWATCH ID via Graphic::resolve — so the ref named nothing. Measured: cellFillColor='#FFEE00' -> 0 non-white pixels, 'Color/Black' -> 10,494 px at (0,0,0), a minted Color/uPagedSheetCellFillFFEE00 -> 10,494 px at (255,238,0); characterFillColor absent -> 2,590 px at (0,0,0), '#FF0000' -> 2,590 px at (0,0,0) IDENTICAL, a minted swatch -> 2,590 px at (255,0,0). Two different failure modes, both silent: the table painter DROPS an unresolved cell fill entirely, while the glyph paint picker ends in unwrap_or(default) so an unresolved text colour renders as the default — which is why reading the code could not settle it. Both lanes now mint deterministic ids and lead the decor batch with the mints. PARTIAL, two honest limits: the fix was LATENT, not live — no cellFillColor op had ever actually shipped from the page lane, so previously-saved documents carry no dangling refs; and only the FILL lane has a production driver (sheet-bundle lower.ts calls cellFillSwatchOps), while cellTextSwatchOps is built and tested with ZERO production call sites, which is exactly why the text provider still declares characterFillColor ABSENT",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": "2026-10-12T07:33:32Z",
        "evidence_at": "2026-10-18T04:47:29.753Z",
        "headline": "All linked evidence is passing (7 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 7,
          "passed": 7,
          "skipped": 0
        }
      },
      "id": "sheet.lower.cell-colour-swatches",
      "importance": "normal",
      "since": "2026-08",
      "status": "experimental",
      "title": "Cell fill / text colours are MINTED swatches — a raw hex in a colorRef paints nothing"
    },
    {
      "chapter": "sheet",
      "claims": [
        {
          "component": "plugin.sheet",
          "note": "native InsertTable (S-03, protocol v37) with three-phase lower (frame → table → per-cell insertText via TextCellAddr); IR-v2 character-level style emission with honest BLOCKED facets; columns sized via host.text.measureString (S-13). Residual: per-cell fill/borders need a tableCell ElementId kind; the editor-side metrics RPC (K-7) still returns the estimate; tab-text degradation retired to old-host fallback. RENDER-VERIFY FINDING — ROOT-CAUSED + FIXED (editor render journey sheet-render.journey, 2026-06-18): the lowered table rendered BLANK; its phase-3 cell pour never applied. THREE causes, all fixed: (1) the bundle cast insertTable's createdId.id `as string`, but it is a STRUCTURED {story_id, table_id} — so the whole id object was nested as the cell's table_id and the engine rejected every cell op with 'invalid type: map, expected a string'; (2) the bundle combined the text pour (insertText) + decor (setElementProperty on tableCell) in ONE batch, but Operation::Batch carries only frame ops (text is a separate apply lane) → the engine rejected the whole batch (Mutation::Batch NotImplemented); (3) pouring text into a fresh ZERO-paragraph cell PANICKED in core (index out of bounds, mutate.rs insert_one_segment), poisoning the wasm. FIXES: plugin-sheets fix/tablecell-id (tableIdOf extracts the bare table_id + pourCellContent splits the text lane from the decor batch) + core fix/empty-cell-text-pour (apply_insert_text seeds an empty paragraph for an empty cell stream). With both, the table RENDERS (verified under the protocol-50 sync-wasm override). NOT a publish-only gap and NOT a wire-contract gap (the earlier framing was the symptom). The two fixes must ship as a PAIR (the bundle two-lane pour would PANIC against the unpatched 0.49.0 engine); until then the published editor shows the table blank. The in-frame K-1 grid (sheet.grid.inframe) renders independently via the C-1 sceneLayer Wave 4 (2026-10-04): the placed table reads the PAGE door (sheet-js get_range_page = real styles with conditional formatting folded on top; it read the frozen key-0 door, so no fill/border/condfmt reached the page); cell text gets the workbook's fonts via content-addressed character styles applied cell-qualified; placed tables REFRESH IN PLACE after edits (debounced; only changed cells re-poured, tail rows/cols reshaped, lost fills/edges reset; binding contentVersion counts edits — it was always 0); new frames land at the current selection. Residual: conditional-format data bars are not drawn on the native table; placements made in an earlier session do not refresh (tracked in memory).",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-12T07:33:32Z",
        "evidence_at": "2026-10-18T05:11:26.042Z",
        "headline": "All linked evidence is passing (84 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 84,
          "passed": 84,
          "skipped": 0
        }
      },
      "id": "sheet.lower.page",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Page-surface lowering — native <Table> + IR-v2 style map + measured column widths"
    },
    {
      "chapter": "sheet",
      "claims": [
        {
          "component": "plugin.sheet",
          "note": "S-05 RESOLVED via protocol v38: host.document.frameChain(storyId) + DocumentChangeEvent.reflow (content-box resize only, never a pure transform, §8.5); greedy row packing with repeated headers, continued markers, keep-together, tall-row convergence (property-tested); re-paginates on chain reflow; all threading math in Rust Wave 4 (2026-10-04): it was NOT reachable — no command called lowerPaginatedToChain. Now: the Paginate into threaded frames command/menu/panel button (selected frame → its chain by story scan); every page goes into the chain's shared story (the per-frame hitTest at a box re-based to the page origin was wrong); a reflow is debounced and REFRESHES the placement's own tables (each reflow used to ADD a full set); edits refresh the chain. PARTIAL: tables a re-pagination no longer needs are emptied, not removed (no deleteTable on the wire — RFI candidate); paginated pages used the unstyled key-0 lowering until Wave 6 (2026-10-04, fundamentals/wave-6): pagination now lowers once through the page lane (styles + conditional formatting), so the repeated header band keeps its formatting on every frame; the Paginate button has a Header rows input (repeatHeaderRows, kept for the chain's refreshes).",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": "2026-10-12T07:33:32Z",
        "evidence_at": "2026-10-18T05:10:53.278Z",
        "headline": "All linked evidence is passing (18 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 18,
          "passed": 18,
          "skipped": 0
        }
      },
      "id": "sheet.lower.paginate",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Live multi-frame pagination across the host frame chain"
    },
    {
      "chapter": "sheet",
      "claims": [
        {
          "component": "plugin.sheet",
          "note": "Wave 6 (2026-10-04, plugin-sheets fundamentals/wave-6): a workbook <definedName> whose target is a reference loaded as raw text, so every formula using one read #NAME?; it now becomes a range name on load. sheet-js define_name / delete_name / list_names (Excel name rules, workbook or sheet scope; a redefinition keeps the slot so formulas follow; a deleted name parks its slot and formulas read #NAME?), written by byte-splicing <definedNames>. Every lowering / paginate / sort door accepts A1, Sheet!A1:B2, a defined name or a table name (resolve_range). Workbook panel name manager: define (target = selection or typed), place (the placement's binding keeps the name), delete.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-12T07:33:14Z",
        "evidence_at": "2026-10-18T04:47:29.802Z",
        "headline": "All linked evidence is passing (7 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 7,
          "passed": 7,
          "skipped": 0
        }
      },
      "id": "sheet.names.define",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Defined names — resolve on load, define / redefine / delete, place a name or table"
    },
    {
      "chapter": "sheet",
      "claims": [
        {
          "component": "plugin.sheet",
          "note": "Published: on plugin-sheets main and in @paged-media/sheet since 0.1.0-canary.15 (npm, 2026-10-11); the current canary, 0.1.0-canary.20 (2026-10-13, the version the editor pins), peers plugin-api/plugin-sdk >=0.2.45-canary.0. The contract is plugin-sdk DESIGN.md §21 (§21.8 the fix-ups from the first consumers, §12.7 the panel property row). Seven kinds under plugin:media.paged.sheet/: workbook, sheet, cell (value/formula/input/display + number format, font, fill, alignment, wrap), range (values, inputs, formula fill, display + format rows), namedRange, table (read-only: the xlsx table part re-emits verbatim), chart (type, title, legend, series, axis titles/bounds). Every op of a batch reaches one plugin-level planner; a write stores a content-addressed version (versions/<hash>.xlsx + .json) and sets the document label x-paged:media.paged.sheet = {wb} through a doc-hosted state write under the sub-key .wb: one undo step, and undo/redo reloads the labelled version. 16 typed commands (untyped 24 -> 8) + publishDataset/unpublishDataset; live dataset re-pull; real bundle boots headless with the real engine. Budget: 100-cell range set = 1 setCells, 1 save, 1 commit, 2 part writes. The Properties panel shows every writable row as a host PropertyField (the Workbook and Grid panels keep their own controls); Boa scripts reach every row through the editor's host bridge, and the Node CLI (@paged-media/cli in plugin-sdk, not on npm yet) through the same registry. Limits: the page refresh after a write is a second document step (debounced in the session, not part of the write's undo step); charts authored in the panel are not in the chart-op journal.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-12T07:33:33Z",
        "evidence_at": "2026-10-18T04:47:29.764Z",
        "headline": "All linked evidence is passing (31 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 31,
          "passed": 31,
          "skipped": 0
        }
      },
      "id": "sheet.objects",
      "importance": "normal",
      "since": "2026-10",
      "status": "stable",
      "title": "Object model — workbook, sheets, cells, ranges, names, tables and charts addressable through host.objects"
    },
    {
      "chapter": "sheet",
      "claims": [
        {
          "component": "plugin.sheet",
          "note": "8 registry rows implemented (sheet.parser.*): Excel-first grammar (D-2, OpenFormula maps onto the same AST later); functions resolve via the generated registry name table (unknown name -> ParseError, the T0 ruling — an unregistered FuncId is unrepresentable); union/intersection + array literals parse-only at T0 (evaluation oracle-gated); parse∘print is an AST fixpoint (property-tested). Evidence lane: sheet-conformance parser_roundtrip + sheet-parser unit tests",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:46:30.974Z",
        "headline": "All linked evidence is passing (64 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 64,
          "passed": 64,
          "skipped": 0
        }
      },
      "id": "sheet.parser.dialect",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Formula dialect parser — literals, A1 refs, operator precedence, functions, union/intersection, array literals, defined names, canonical print fixpoint"
    },
    {
      "chapter": "sheet",
      "claims": [
        {
          "component": "plugin.sheet",
          "note": "manifest validates via plugin-cli (sceneLayer + blob + dataProviders.consume capabilities); activate registers 3 panels (workbook, grid, datasets), 12 commands (importXlsx, lowerToFrame, lowerChartToFrame, openGrid, showGridInFrame, hideGridInFrame, sortRange, findReplace, sheetFromDataset, copySelection, pasteSelection, styleFromCell), the .xlsx importer + exporter, and the sheet editContext/objectType/partType; the 1.0 MiB sheet_js wasm ships in bin/ and boots via the bundle-realm glue path (S-10), with a real-wasm boot spec (engine-real.spec.ts). Counted 2026-10-04 from manifest.json at plugin-sheets f7e84e1.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-12T07:33:32Z",
        "evidence_at": "2026-10-18T05:11:17.382Z",
        "headline": "All linked evidence is passing (45 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 45,
          "passed": 45,
          "skipped": 0
        }
      },
      "id": "sheet.plugin.bundle",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "paged.sheet bundle (media.paged.sheet) — manifest + activate + 3 panels + 7 commands + importer/exporter + engine boot"
    },
    {
      "chapter": "sheet",
      "claims": [
        {
          "component": "plugin.sheet",
          "note": "The bar prefills the cell's re-enterable input (engine.getCellInput), commits through the journaled editCell lane, and completes function names from the engine registry (engine.listFunctions), never a TypeScript name list. Imported from the plugin-sheets registry (registry/features/plugin.yaml).",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-12T07:53:22Z",
        "evidence_at": "2026-10-18T04:47:29.83Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "sheet.plugin.formula-bar",
      "importance": "normal",
      "since": "2026-10",
      "status": "stable",
      "title": "Formula bar bound to the selected cell, with function autocompletion from the engine"
    },
    {
      "chapter": "sheet",
      "claims": [
        {
          "component": "plugin.sheet",
          "note": "the function library gains a searchable, family-grouped browser fed by engine.listFunctions (never a hand-kept list; click copies NAME() via the clipboard door with an honest no-door message); the three list* inventories that had no panel caller surface in a 'Workbook inventory' section (freeze panes — rendered, no toggle yet; data validations — preserved, not enforced, the §1 ruling verbatim; comments — read-only); and session.newCellStyleFromSelection (S-04) gains its palette command media.paged.sheet.command.styleFromCell. Ships in sheet canary.2",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:47:29.711Z",
        "headline": "All linked evidence is passing (3 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 3,
          "passed": 3,
          "skipped": 0
        }
      },
      "id": "sheet.plugin.function-browser",
      "importance": "normal",
      "since": "2026-08",
      "status": "stable",
      "title": "Function browser + workbook inventory + the styleFromCell command (workbook panel)"
    },
    {
      "chapter": "sheet",
      "claims": [
        {
          "component": "plugin.sheet",
          "note": "S-08 RESOLVED: persists + restores the workbook via host.blob (OPFS-backed, 32 MiB declared quota) with honest panel wording; frame binding rides setPluginMetadata (x-paged:media.paged.sheet, 64 KiB envelope) round-tripping IDML. PARTIAL: per-plugin keyed, NOT document-scoped — the LAST imported workbook is restored",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:11:00.137Z",
        "headline": "All linked evidence is passing (30 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 30,
          "passed": 30,
          "skipped": 0
        }
      },
      "id": "sheet.plugin.persistence",
      "importance": "normal",
      "since": "2026-06",
      "status": "experimental",
      "title": "Workbook persistence — host.blob (OPFS) + the metadata binding envelope"
    },
    {
      "chapter": "sheet",
      "claims": [
        {
          "component": "plugin.sheet",
          "note": "the M1 style-map track ('the most important property of the whole plugin'): styles.xml xf records parse into visual cell styles (font/fill/border/alignment), resolve into the deduped LoweredContent.styles table with per-cell styleKey (IR v2), and the SAME StyleResolver feeds the grid scene — a cell's style is identical on the page lowering and in-grid (property sheet_style_cross_surface_ parity). The apply-style pour prepares host overrides on both lanes (native tableCell props; fallback lane reports fill/border blocked). PARTIAL: sheet.style.doc-style-group stays PLANNED — mapping a LoweredStyle to a named document style group is blocked on the S-04 style-management capability (create*Style from selection); the swatch half of the story is the separate sheet.lower.cell-colour-swatches row",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:46:28.634Z",
        "headline": "All linked evidence is passing (4 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 4,
          "passed": 4,
          "skipped": 0
        }
      },
      "id": "sheet.style.mapping",
      "importance": "normal",
      "since": "2026-06",
      "status": "experimental",
      "title": "Workbook visual styles — xlsx parse -> deduped IR-v2 style table -> both surfaces (one resolver, cross-surface parity)"
    },
    {
      "chapter": "sheet",
      "claims": [
        {
          "component": "plugin.sheet",
          "note": "6 registry rows implemented: table model, structured-reference parsing (parse↔print fixpoint property), tableN.xml XLSX part round-trip; conformance sheet_table_xlsx_part green",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:11:17.382Z",
        "headline": "All linked evidence is passing (11 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 11,
          "passed": 11,
          "skipped": 0
        }
      },
      "id": "sheet.table.structured",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Excel structured tables / ListObjects — model + structured references + tableN.xml part"
    },
    {
      "chapter": "sheet",
      "claims": [
        {
          "component": "plugin.sheet",
          "note": "returns the LoweredContent IR with the workbook's real visual styles resolved (bold/italic, font size + face, cell fill, text colour, borders), as opposed to get_range_lowered which is the same lowering over NoStyles. IT IS A SEPARATE DOOR ON PURPOSE: get_range_lowered feeds the PAGE lowering, which is a committed contract, and flipping real styles on underneath it would change what lands in the document. Same T0_LOWER_CELL_CAP materialization cap; a junk range or an out-of-bounds sheet is a boundary error. One production consumer — activate.ts feeds it to the Character/Paragraph text provider's lowerRange callback. PARTIAL: BASE styles only — conditional formatting (lower_range_condfmt) is not applied through this door",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:46:28.323Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "sheet.wasm.range-styled-door",
      "importance": "normal",
      "since": "2026-08",
      "status": "experimental",
      "title": "get_range_styled — the workbook's REAL per-cell visual styles, as a door of its own"
    },
    {
      "chapter": "sheet",
      "claims": [
        {
          "component": "plugin.sheet",
          "note": "Wave 4 (2026-10-04, plugin-sheets fundamentals/wave-4): New blank workbook command + panel button; add/rename/delete sheet through sheet-js (Excel name rules; references follow a rename, become #REF! on delete, later sheet ids shift) and the xlsx writer (byte-spliced <sheet> rows, workbook relationships, content-type overrides, defined names incl. localSheetId, activeTab; delete drops the parts only that sheet reached). PARTIAL: delete is refused while the workbook has charts (series ranges not re-pointed).",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": "2026-10-12T07:33:32Z",
        "evidence_at": "2026-10-18T04:47:29.798Z",
        "headline": "All linked evidence is passing (4 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 4,
          "passed": 4,
          "skipped": 0
        }
      },
      "id": "sheet.workbook.sheets",
      "importance": "normal",
      "since": "2026-10",
      "status": "experimental",
      "title": "Workbook structure — new blank workbook, add / rename / delete sheet"
    },
    {
      "chapter": "sheet",
      "claims": [
        {
          "component": "plugin.sheet",
          "note": "PRESERVE-FIRST: the worksheet's commentsN.xml part (resolved through its own .rels) + its companion vmlDrawing*.vml stay OPAQUE (never promoted) so they re-emit BYTE-IDENTICAL on round-trip. ADDITIVE + READ-ONLY: the <authors> table + <commentList> parse into a SheetComments display model (cell ref, resolved author, flattened text); the grid surface marks commented cells with a display indicator (sheet.grid.comments.indicator) and lists the text. Authoring is preserve-first — we read + display, never rewrite the part; an unparsable ref / out-of-range authorId degrades gracefully",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-06-13T06:14:36Z",
        "evidence_at": "2026-10-18T04:46:27.186Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "sheet.xlsx.comments",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Cell comments — preserve opaquely + display (grid indicator + panel list), preserve-first"
    },
    {
      "chapter": "sheet",
      "claims": [
        {
          "component": "plugin.sheet",
          "note": "XLSX half shipped: cfRules parse and preserve byte-identical on the ladder; the engine lowering (sheet_lower::lower_range_condfmt: cellIs/colorScale to dxf-derived overrides, data bars to DataBarRect geometry) is tested. PARTIAL (corrected 2026-10-04 against plugin-sheets f7e84e1): sheet-js never calls lower_range_condfmt and the page lowering door (getRangeLowered) emits no overrides and no bars, so conditional formatting never reaches the PAGE. Only the in-frame grid draws data bars (sheet_grid::grid_scene_with_cf). iconSet stays at the preserved floor.",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": "2026-10-12T07:33:32Z",
        "evidence_at": "2026-10-18T04:47:29.804Z",
        "headline": "All linked evidence is passing (12 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 12,
          "passed": 12,
          "skipped": 0
        }
      },
      "id": "sheet.xlsx.condfmt",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Conditional formatting — xlsx part fidelity + lowering to style overrides"
    },
    {
      "chapter": "sheet",
      "claims": [
        {
          "component": "plugin.sheet",
          "note": "PUBLISHING-FIRST non-goal made honest: <dataValidations> round-trips BYTE-IDENTICAL (preservation invariant) and is NEVER interpreted, evaluated, enforced, or rendered as a runtime dropdown (the spec forbids it). sheet.xlsx.data-validation = the preserve-only round-trip; sheet.xlsx.data-validation.inventory = a READ-ONLY ADDITIVE parse (kind, ranges, raw un-evaluated operand formulas) exposed via list_data_validations as {sheet,count,kinds} so a panel can SHOW that the workbook carries validations Paged preserves but does not enforce (strictly less than a dropdown). The parse never writes back. ECMA-376 §18.3.1.33",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-06-13T06:04:19Z",
        "evidence_at": "2026-10-18T04:46:27.273Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "sheet.xlsx.data-validation",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "XLSX data validation — preserve byte-faithfully + read-only inventory (NEVER enforced)"
    },
    {
      "chapter": "sheet",
      "claims": [
        {
          "component": "plugin.sheet",
          "note": "NON-GOAL, recorded as an explicit preserve-only ruling (not a deferral): pivotCache*/pivotTable* parts (and slicers) round-trip BYTE-IDENTICAL but are NEVER interpreted, recomputed, or rendered. paged.sheet is a publishing instrument, not an analysis tool (§1.1/§11). Covered by the sheet.xlsx.preserve.unknown-parts lane which exercises pivot caches; ECMA-376 + spec §1.1.1/§10.2",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-06-19T08:44:32Z",
        "evidence_at": "2026-10-18T04:46:28.683Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "sheet.xlsx.pivot",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Pivot tables — preserve-only, NEVER interpreted (publishing-first non-goal)"
    },
    {
      "chapter": "sheet",
      "claims": [
        {
          "component": "plugin.sheet",
          "note": "\"Paged never destroys a workbook\": unknown parts byte-identical, unknown subtrees re-emitted in place, lazy-verbatim untouched parts; zero-edit round-trip byte-identical except the recorded calcChain.xml drop ruling (sheet.xlsx.calcchain.drop). .xlsx opens via the plugin (S-06 importer + drag-drop + S-11 pickFile) and exports via the Export Center; external-workbook link READS (M3) preserved, never fetched (§1.1)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:11:17.382Z",
        "headline": "All linked evidence is passing (142 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 142,
          "passed": 142,
          "skipped": 0
        }
      },
      "id": "sheet.xlsx.roundtrip",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "XLSX I/O on the fidelity ladder — preservation invariant + import/export through the platform"
    },
    {
      "chapter": "stories-text",
      "claims": [
        {
          "component": "core.canvas-wasm",
          "note": "setSelection + caret/selection geometry",
          "status": "shipped"
        },
        {
          "component": "editor.canvas",
          "note": "W2.11 — click caret + typing live; caret Up/Down via requestCaretNav (engine desired-x), Home/End via requestLineBounds, Shift-extends with stable anchor (caret-nav e2e green)",
          "status": "shipped"
        },
        {
          "component": "editor.gesture",
          "note": "char-level drag select + double/triple-click → LINE granularity (W2.11); word-bounds wire missing (fixmes CARET-NAV-6/7)",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:01:11.35Z",
        "headline": "All linked evidence is passing (14 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 14,
          "passed": 14,
          "skipped": 0
        }
      },
      "id": "stories-text.caret-selection",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Text caret + range selection (geometry, content selection)"
    },
    {
      "chapter": "stories-text",
      "claims": [
        {
          "component": "editor.gesture",
          "note": "no wire path yet (deep-defer-records.md §editorial-text)",
          "status": "planned"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "stories-text.drag-drop-text",
      "importance": "normal",
      "since": null,
      "status": "planned",
      "title": "Drag-and-drop text"
    },
    {
      "chapter": "stories-text",
      "claims": [
        {
          "component": "core.parser",
          "note": "endnotes read as InDesign 20.0.1 writes them (core ADR 170): <Endnote Self EndnoteTextRange> references as a marker character, the endnote story (IsEndnoteStory) in its <EndnoteTextFrame>, each <EndnoteRange SourceEndnote> number (<?ACE 4?>), <EndnoteOption> with its typed Properties",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "references and the endnote story print each note's number as InDesign numbers them (its place in the endnote story, from StartEndnoteNumberAt, in EndnoteNumberingStyle); the endnotes fixture is text-gated against InDesign's PDF (iii, iv, v). OPEN: section scope and restarts",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "insertEndnote / deleteEndnote / convertFootnoteToEndnote (core ADR 174, endnote-probe), each one undo step: a new range goes after the range of the reference before it; the first endnote makes the endnote story on a page of its own in an EndnoteTextFrame over the margin box, as InDesign does; an edited range saves; InDesign reads the saves back as made. OPEN: section scope and restarts, threading more endnote frames",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:46:13.357Z",
        "headline": "All linked evidence is passing (12 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 12,
          "passed": 12,
          "skipped": 0
        }
      },
      "id": "stories-text.endnotes",
      "importance": "normal",
      "since": null,
      "status": "experimental",
      "title": "Endnotes"
    },
    {
      "chapter": "stories-text",
      "claims": [
        {
          "component": "core.mutation",
          "note": "InsertField (U+E018/E019) + DeleteField inverse (W0.5)",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "note": "envelope on the wire",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:06.368Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "stories-text.fields.insert",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Insert field (page-number marker etc.) at offset"
    },
    {
      "chapter": "stories-text",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "a note is set at the foot of the frame holding its reference line, reservation capped so a note never pushes out its reference (bee1e5d1, 4fa49151); separator rule and per-run styled bodies shipped; the importer anchors each note and prints its reference mark, and reads FootnoteOption from Preferences.xml. Numbering as InDesign 20.0.1 numbers notes (core ADR 156, 2026-10-15): through each STORY, restarted per page, spread or section by RestartNumbering, Prefix/Suffix where ShowPrefixSuffix says; footnote-numbering and footnote-restart are text-gated. In a frame with columns notes stand under each column or span the frame per EnableStraddling and the frame's FootnotesEnableOverrides (with InDesign's frame defaults 6 pt / 12 pt; core ADR 160, footnote-columns and footnote-straddle text-gated). A note too tall for the room under its reference keeps the whole lines that fit and continues at the head of the next column or frame under the continuing rule, or with NoSplitting moves its reference line on (core ADR 161, footnote-split and footnote-nosplit text-gated). A story that grows pages grows one for a note that does not fit, and a note continued in a frame of another width resumes from the text it stopped at (core ADR 178, v0.73.3; footnote-split-narrow text-gated). OPEN: end-of-story placement not read; reservation after a keep pass not re-planned (C-45, unverified)",
          "status": "partial"
        },
        {
          "component": "core.mutation",
          "note": "the document's footnote options are editable: twelve documentFootnote* paths on doc, saved as InDesign writes FootnoteOption (core ADR 162). insertFootnote / deleteFootnote (v0.71.13, core ADR 163) add and remove a note and its reference, a text delete over a reference takes its note, a paragraph merge keeps the second paragraph's notes, undo restores the note exactly, and a save writes both. OPEN: a note's own text is not addressable by the text ops; frame-level footnote overrides have no path",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:58:02.058Z",
        "headline": "All linked evidence is passing (21 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 21,
          "passed": 21,
          "skipped": 0
        }
      },
      "id": "stories-text.footnotes",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Footnote bodies"
    },
    {
      "chapter": "stories-text",
      "claims": [
        {
          "component": "core.parser",
          "note": "NestedGrepStyle / AllGREPStyles are not read",
          "status": "planned"
        },
        {
          "component": "core.renderer",
          "note": "no GREP pass after nested and line styles (core parity plan R-25, A-11)",
          "status": "planned"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "stories-text.grep-styles",
      "importance": "normal",
      "since": null,
      "status": "planned",
      "title": "GREP styles (character style by regular expression)"
    },
    {
      "chapter": "stories-text",
      "claims": [
        {
          "component": "core.parser",
          "note": "<HiddenText> recognised + content suppressed (story.rs:1187; track5c_hidden_text_block_drops_content); not output, list surface deep-deferred (deep-defer-records.md §editorial-text)",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:01:17.135Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "stories-text.hidden-text",
      "importance": "normal",
      "since": null,
      "status": "experimental",
      "title": "Hidden text handling"
    },
    {
      "chapter": "stories-text",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:57:30.167Z",
        "headline": "All linked evidence is passing (6 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 6,
          "passed": 6,
          "skipped": 0
        }
      },
      "id": "stories-text.nested-styles",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Nested character styles (per-delimiter run restyling)"
    },
    {
      "chapter": "stories-text",
      "claims": [
        {
          "component": "core.parser",
          "note": "read as InDesign 20.0.1 keeps a note (core ADR 172, notes-probe): one character of the text where the <Note> stands (NOTE_MARKER; InDesign's U+FEFF), its text (<Br/> as U+000D), UserName, AppliedDocumentUser, CreationDate/ModificationDate, Collapsed; an unedited story saves byte-identically",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "a note prints nothing, as in InDesign's PDF; the notes fixture is text-gated against it",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "insertNote / editNote / deleteNote / convertNoteToText (core ADR 172), each one undo step, saved as IDML; a text delete over a note's place takes the note. OPEN: an edited note's own formatting, new document users",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:51:57.916Z",
        "headline": "All linked evidence is passing (26 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 26,
          "passed": 26,
          "skipped": 0
        }
      },
      "id": "stories-text.notes",
      "importance": "normal",
      "since": null,
      "status": "experimental",
      "title": "Editorial notes (Note element)"
    },
    {
      "chapter": "stories-text",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "clipped to match InDesign; diagnostic reported",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "note": "StorySummary.overset + CollectionName::Stories read surface (W0.6/W3.A0)",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "W2.9/W2.12 — overset badge wired via paged.stories() (wire signal landed W0.6); full verification blocked on an overset fixture (TH-04 fixme)",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:50:23.819Z",
        "headline": "All linked evidence is passing (3 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 3,
          "passed": 3,
          "skipped": 0
        }
      },
      "id": "stories-text.overset",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Overset handling (last-frame overflow clipped + reported)"
    },
    {
      "chapter": "stories-text",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:01:17.068Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "stories-text.page-number-markers",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Auto page-number markers (current/next page)"
    },
    {
      "chapter": "stories-text",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:01:11.35Z",
        "headline": "All linked evidence is passing (3 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 3,
          "passed": 3,
          "skipped": 0
        }
      },
      "id": "stories-text.story-model",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Story model (paragraph/character run trees)"
    },
    {
      "chapter": "stories-text",
      "claims": [
        {
          "component": "core.mutation",
          "note": "Mutation::ApplyStyle ranges via run splitters (W0.5)",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "note": "envelope on the wire",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "the character/paragraph panels' content-scope bindings write onto an ElementId::StoryRange derived from the text selection — the panel-to-range apply boundary (shell catalog/binding-hook.ts); journeys typography-depth / styles / text-flow drive it",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:06.325Z",
        "headline": "All linked evidence is passing (3 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 3,
          "passed": 3,
          "skipped": 0
        }
      },
      "id": "stories-text.style-apply-range",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Apply style attributes to a text range (applyStyle op)"
    },
    {
      "chapter": "stories-text",
      "claims": [
        {
          "component": "core.parser",
          "note": "TextWrapSide, ApplyToMasterPageOnly and a text frame's IgnoreWrap read (core ADR 151); a <Rectangle>'s non-box path kept",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "Asked of InDesign 20.0.1 (core ADR 151, `text-wrap-sides`): text stands on the named side (left, right, larger area, towards / away from the spine), a contour follows the outline grown by the offset and the stroke's outer half with its zone floored on the text area's whole-point grid, IgnoreWrap is honoured and a master item wraps the pages that show it. Pages 1-14 mean 0.33-0.54. Inverse is measured, not modelled.",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "frameTextWrapMode/frameTextWrapOffsets + textWrapInvert structural path (W0.3); TextWrapContourType/Side deferred",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "Text wrap panel; wrap-to/contour seamed (gap 14)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:06.219Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "stories-text.text-wrap",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Text wrap (bounding box / contour / jump modes + offsets)"
    },
    {
      "chapter": "stories-text",
      "claims": [
        {
          "component": "core.mutation",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "note": "deleteRange",
          "status": "shipped"
        },
        {
          "component": "editor.canvas",
          "note": "Backspace/Delete keys; caret + range",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:44:43.05Z",
        "headline": "All linked evidence is passing (8 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 8,
          "passed": 8,
          "skipped": 0
        }
      },
      "id": "stories-text.text.delete",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Delete story range (Backspace/Delete + range)"
    },
    {
      "chapter": "stories-text",
      "claims": [
        {
          "component": "core.mutation",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "note": "insertText",
          "status": "shipped"
        },
        {
          "component": "editor.canvas",
          "note": "typing via useTextEditing, undoable",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "Glyphs panel inserts at caret; scope/font selects pending",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:44:43.055Z",
        "headline": "All linked evidence is passing (10 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 10,
          "passed": 10,
          "skipped": 0
        }
      },
      "id": "stories-text.text.insert",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Insert text at story offset"
    },
    {
      "chapter": "stories-text",
      "claims": [
        {
          "component": "core.parser",
          "note": "a tracked story reads as InDesign prints it, every change accepted: <Change ChangeType=\"DeletedText\"> (with its nested ranges) is skipped, inserted and moved text is read (asked of InDesign 20.0.1; plugin-publish r09/tracked-changes). a deletion is kept as a character that prints nothing with its author, date and text, written back on save, and listed (trackedChanges); an edit in a story with a tracked insertion keeps it wrapped (core ADR 184, plugin-publish #53/#57). Protocol 74 (core main, untagged): acceptTrackedChange / rejectTrackedChange. OPEN: insertions are not listed or resolvable; typing does not record changes",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:01:17.137Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "stories-text.tracked-changes",
      "importance": "normal",
      "since": null,
      "status": "planned",
      "title": "Tracked changes (insertions/deletions, author/date)"
    },
    {
      "chapter": "styles",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:58:37.941Z",
        "headline": "All linked evidence is passing (5 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 5,
          "passed": 5,
          "skipped": 0
        }
      },
      "id": "styles.based-on-chain",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Based-on style inheritance chains"
    },
    {
      "chapter": "styles",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "multi-level lists (core ADR 158, 2026-10-15): levels 1-9, a level restarts only when a higher level came after its own last paragraph (per its NumberingRestartPolicies), a higher level never resets the lower ones, ^1..^9 print each level in the format of the paragraph that last set it, NumberingApplyRestartPolicy=false keeps counting; numbering-multilevel is text-gated. Not across stories or documents above level 1",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "per-paragraph list-type/bullet-char/numbering-format paths (W0.2), paragraphNumberingLevel / paragraphNumberingApplyRestartPolicy (ADR 158), PLUS list-DEFINITION CRUD — createNumberingList/editNumberingList/ deleteNumberingList over the <NumberingList> resource surface landed W1.22 (protocol v35, editor capabilities.ts supported, 2026-06-07); gap 22 closed",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "W2.4 — B&N panel live (listType/bulletChar/numberingFormat; bullets-ops + bullets-panel e2e green) on the list-definition surface (W1.22); see editor-shell.panels.bullets-numbering",
          "status": "partial"
        },
        {
          "component": "editor.canvas",
          "note": "list assign + numbering format applied through the editor (e2e/numbering-list-ops.spec.ts green)",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:57:48.254Z",
        "headline": "All linked evidence is passing (22 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 22,
          "passed": 22,
          "skipped": 0
        }
      },
      "id": "styles.bullets-numbering",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Bullets & numbering (lists + numbering expressions)"
    },
    {
      "chapter": "styles",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-12T17:16:47Z",
        "evidence_at": "2026-10-18T07:58:37.941Z",
        "headline": "All linked evidence is passing (6 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 6,
          "passed": 6,
          "skipped": 0
        }
      },
      "id": "styles.cascade",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Style cascade (direct > character style > paragraph style)"
    },
    {
      "chapter": "styles",
      "claims": [
        {
          "component": "core.parser",
          "note": "<CellStyle> read into Styles.cell_styles (styles.rs:68); corpus-verified, 61 envato packs (W4.17). Fill/per-edge strokes/ VerticalJustification parsed; insets/diagonals/gap-colors defer (renderer-gaps.md)",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "cell fill + per-edge strokes + VerticalJustification cascade-applied via CellStyleDef::merge_below (W4.17)",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "CRUD + appliedCellStyle via the cell path on the NodeId::TableCell surface (W3.A1)",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "read-only ApplyList until table selection lands (gap 8)",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:05.646Z",
        "headline": "All linked evidence is passing (5 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 5,
          "passed": 5,
          "skipped": 0
        }
      },
      "id": "styles.cell.crud",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Cell styles (create/rename/delete + apply)"
    },
    {
      "chapter": "styles",
      "claims": [
        {
          "component": "core.parser",
          "note": "<CharacterStyle> read into Styles.character_styles (styles.rs:55); corpus-verified, 61 envato packs (W4.17)",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "cascade-applied to runs (color_paint per-run fill + merge_below) (W4.17)",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "ApplyList; seams as paragraph styles",
          "status": "shipped"
        },
        {
          "component": "editor.script",
          "note": "paged.characterStyles()",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:05.216Z",
        "headline": "All linked evidence is passing (16 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 16,
          "passed": 16,
          "skipped": 0
        }
      },
      "id": "styles.character.crud",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Character styles (create/rename/delete + apply)"
    },
    {
      "chapter": "styles",
      "claims": [
        {
          "component": "core.parser",
          "note": "NextStyle attr parsed into ParagraphStyleDef (styles.rs); authoring-only (affects next-paragraph-on-type, no render effect — renderer-gaps.md). Verified by generated styles-cascade.idml (W4.17)",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "the CHAINING half ships: SetStyleProperty with PropertyPath::ParagraphStyleNextStyle sets a paragraph style's NextStyle (empty string clears it), prior captured as the current ref so the inverse round-trips — paged-mutate set_style_property_next_style_round_trips covers set/clear/undo, and ParagraphStyleSummary.next_style carries it to the editor on the read side. Still absent: OVERRIDE INDICATORS and REDEFINE-from-selection — no such op or property path exists anywhere in paged-mutate/paged-canvas",
          "status": "partial"
        },
        {
          "component": "editor.panel",
          "status": "planned"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:06.002Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "styles.next-style-overrides",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Next-style chaining + override indicators + redefine"
    },
    {
      "chapter": "styles",
      "claims": [
        {
          "component": "core.parser",
          "note": "<ObjectStyle> read into Styles.object_styles (styles.rs:63); corpus-verified, 61 envato packs (W4.17)",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "frame attrs cascade-applied via ObjectStyleDef::merge_below (W4.17)",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "An edited object style is written back on save (fill, stroke and corners, the corners in both spellings); a source style used to pass through with its source attributes, losing the edit (plugin-publish idml-export object_style_attr_patch).",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "status": "shipped"
        },
        {
          "component": "editor.script",
          "note": "paged.objectStyles()",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:05.646Z",
        "headline": "All linked evidence is passing (17 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 17,
          "passed": 17,
          "skipped": 0
        }
      },
      "id": "styles.object.crud",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Object styles (create/rename/delete + apply)"
    },
    {
      "chapter": "styles",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:01:17.227Z",
        "headline": "All linked evidence is passing (4 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 4,
          "passed": 4,
          "skipped": 0
        }
      },
      "id": "styles.paragraph-border",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Paragraph border (per-corner radius + offsets)"
    },
    {
      "chapter": "styles",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "ParagraphRuleAbove whole-struct path (W0.2)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:05.774Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "styles.paragraph-rules",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Paragraph rules above/below"
    },
    {
      "chapter": "styles",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:01:17.228Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "styles.paragraph-shading",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Paragraph shading (color/width/offset/corners)"
    },
    {
      "chapter": "styles",
      "claims": [
        {
          "component": "core.parser",
          "note": "<ParagraphStyle> definitions read into Styles.paragraph_styles (styles.rs:56) — corpus-verified across 61 envato packs (W4.17)",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "applied via the cascade (styles.cascade) — definition resolution + merge_below (W4.17)",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "ApplyList apply/create/delete; redefine/groups seamed (gap 15)",
          "status": "shipped"
        },
        {
          "component": "editor.script",
          "note": "paged.paragraphStyles()",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:05.275Z",
        "headline": "All linked evidence is passing (19 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 19,
          "passed": 19,
          "skipped": 0
        }
      },
      "id": "styles.paragraph.crud",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Paragraph styles (create/rename/delete + apply)"
    },
    {
      "chapter": "styles",
      "claims": [
        {
          "component": "core.mutation",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:05.995Z",
        "headline": "All linked evidence is passing (13 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 13,
          "passed": 13,
          "skipped": 0
        }
      },
      "id": "styles.set-style-property",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Edit a style definition property (setStyleProperty)"
    },
    {
      "chapter": "styles",
      "claims": [
        {
          "component": "core.parser",
          "note": "<TableStyle> read into Styles.table_styles (styles.rs:73); corpus-verified, 61 envato packs (W4.17). Region cell styles + border strokes + start/end row fills parsed; StrokeOrder/gap-colors/ column-fills defer (renderer-gaps.md)",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "table border strokes + region cell styles + alternating row/col fills cascade-applied via TableStyleDef::merge_below (W4.17)",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "CRUD + appliedTableStyle via the table path on the NodeId::Table surface (W3.A1)",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "read-only ApplyList (gap 8)",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:06.033Z",
        "headline": "All linked evidence is passing (4 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 4,
          "passed": 4,
          "skipped": 0
        }
      },
      "id": "styles.table.crud",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Table styles (create/rename/delete + apply)"
    },
    {
      "chapter": "tables",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "axis discriminator, skip counts, BasedOn merge (W1.5)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:58:40.016Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "tables.alternating-fills",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Alternating row/column fills"
    },
    {
      "chapter": "tables",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "rows win at crossings",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:58:39.909Z",
        "headline": "All linked evidence is passing (7 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 7,
          "passed": 7,
          "skipped": 0
        }
      },
      "id": "tables.borders-strokes",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Table borders + row/column dividers"
    },
    {
      "chapter": "tables",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "A cell's first baseline (Ascent / CapHeight / XHeight / Leading / EmBox / Fixed plus its minimum) follows the frame rules, from the cell override or through the cell-style cascade (plugin-publish reads CellStyle FirstBaselineOffset / MinimumFirstBaselineOffset). table-cell-baseline p2 1.074 -> 0.129. A cell's FillTint, else its style's through BasedOn, tints its fill and a style's edge tints apply (core ADR 152, `table-cell-tints`: all twelve cells within 1/255 of InDesign).",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-14T06:31:27Z",
        "evidence_at": "2026-10-18T07:58:40.247Z",
        "headline": "All linked evidence is passing (4 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 4,
          "passed": 4,
          "skipped": 0
        }
      },
      "id": "tables.cell-composition",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Per-cell text composition (cell as mini-frame)"
    },
    {
      "chapter": "tables",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "diagonal strokes with tints + DiagonalLineInFront ordering (W1.5)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:58:40.216Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "tables.cell-diagonals",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Cell diagonal lines"
    },
    {
      "chapter": "tables",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "content rotated; borders unrotated",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:57:17.141Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "tables.cell-rotation",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Cell rotation"
    },
    {
      "chapter": "tables",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:58:40.203Z",
        "headline": "All linked evidence is passing (5 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 5,
          "passed": 5,
          "skipped": 0
        }
      },
      "id": "tables.cell-vertical-justification",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Cell vertical justification"
    },
    {
      "chapter": "tables",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "insertHeaderRow/removeHeaderRow/insertFooterRow/removeFooterRow band ops (W1.13, protocol v35; editor capabilities.ts supported, 2026-06-07)",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "note": "header/footer band ops on the wire (W1.13)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:58:42.652Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "tables.header-footer-repeat",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Repeating header/footer rows across chain breaks"
    },
    {
      "chapter": "tables",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "NodeId::Table/TableCell surface (protocol 29); cell paths (fill color/tint, insets×4, vjust, cell+table style) + ops SetRowHeight/SetColumnWidth/Insert+DeleteTableRow/Column (W3.A1); span + header/footer ops now wired (setCellSpan→tables.spans, insertHeaderRow/removeHeaderRow/insertFooterRow/removeFooterRow→tables.header-footer-repeat, W1.11-13, protocol v35, 2026-06-07); Mutation::InsertTable — native <Table> create with createdId=tableId (S-03, protocol v37, the sheets/data lowering door). DEFERRED (list corrected by the 2026-08-09 audit): cell TEXT editing is NO LONGER deferred — `InsertText.cell` (protocol 54) and `ApplyStyle.cell` (protocol 55) ship at paged-mutate operation.rs:3218-3222, and the latter's own doc says it \"closes the 'cell text can only pour at the default formatting' gap\". These are the doors the plugin-doc campaign was built against. STILL deferred: cell-text restore on undo (operation.rs:1704), cell vjust RENDERING, per-cell stroke mutation",
          "status": "partial"
        },
        {
          "component": "core.canvas-wasm",
          "note": "table_context hit-test (cell_rects) + cell/table read entries + cell ops on the wire (W3.A1); cell-text editing IS on the wire since the 2026-08-09 audit corrected this — the cell qualifiers ride at paged-canvas channel.rs:3215,3221 (`InsertText.cell`, W1.13) and :3237 (the v55 `ApplyStyle.cell` mirror). Was: cell-text editing deferred",
          "status": "partial"
        },
        {
          "component": "editor.panel",
          "note": "W3.A2 — Table concept panel live (cell fill/tint/insets/vjust, row/col sizing, insert/delete row+column + spans/header-footer ops; table-ops e2e green; see editor-shell.panels.table); cell-text editing seamed",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:06.141Z",
        "headline": "All linked evidence is passing (22 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 22,
          "passed": 22,
          "skipped": 0
        }
      },
      "id": "tables.model",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Table model (rows, columns, cells)"
    },
    {
      "chapter": "tables",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:01:17.062Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "tables.nested-tables",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Tables inside cells"
    },
    {
      "chapter": "tables",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:58:42.595Z",
        "headline": "All linked evidence is passing (3 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 3,
          "passed": 3,
          "skipped": 0
        }
      },
      "id": "tables.row-col-sizing",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Row height constraints + column widths"
    },
    {
      "chapter": "tables",
      "claims": [
        {
          "component": "core.parser",
          "note": "Row@StartRow (the StartParagraph values) and Row@KeepWithNextRow read in InDesign 20.0.1's spelling, written for a new row and patched back onto a source row only when changed (plugin-publish row_attrs; core ADR 175)",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "a row's Start Row moves it and the rows after it as a paragraph's StartParagraph moves its first line (next column, frame or page, next odd or even page past an empty frame), asked of InDesign 20.0.1 (table-start-row-probe, table-start-row fixture, text-gated); a run of KeepWithNextRow rows moves together (tables-rows page 11)",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "rowStartRow / rowKeepWithNext property paths, written through any cell of the row (core 0.73.1, ADR 175); one undo step each",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:58:39.478Z",
        "headline": "All linked evidence is passing (15 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 15,
          "passed": 15,
          "skipped": 0
        }
      },
      "id": "tables.row-keep-options",
      "importance": "normal",
      "since": null,
      "status": "experimental",
      "title": "Row keep options (Start Row, Keep With Next Row)"
    },
    {
      "chapter": "tables",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "setCellSpan merge/split on the TableCell surface (W1.12, protocol v35; editor capabilities.ts supported, 2026-06-07)",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "note": "setCellSpan on the wire (W1.12)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-12T17:16:47Z",
        "evidence_at": "2026-10-18T07:58:40.064Z",
        "headline": "All linked evidence is passing (9 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 9,
          "passed": 9,
          "skipped": 0
        }
      },
      "id": "tables.spans",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Row/column spans (cell merges)"
    },
    {
      "chapter": "tables",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "StrokeOrder pending",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:01:17.277Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "tables.style-cascade",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Table/cell style cascade (region defaults)"
    },
    {
      "chapter": "tagged-xml",
      "claims": [
        {
          "component": "core.parser",
          "note": "backing store skipped by design; no XMLElement consumer (deep-defer-records.md §tagged-xml). Re-verified 2026-08-17: no XMLElement/XmlStory type in the parse AST (paged-model) and no BackingStory reader anywhere in core — only paged-gen fixture builders and the File▸New stub part (paged-canvas blank.rs). The green conformance rows on this cell prove tolerant parsing of tagged-XML-BEARING documents, not structure modeling — do not promote on them",
          "status": "planned"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "tagged-xml.structure",
      "importance": "normal",
      "since": null,
      "status": "planned",
      "title": "Tagged XML structure (XMLElement/XMLStory/Mapping)"
    },
    {
      "chapter": "test-corpus",
      "claims": [
        {
          "component": "core.canvas-wasm",
          "note": "probes every wire Mutation op",
          "status": "shipped"
        },
        {
          "component": "editor.canvas",
          "note": "capability-matrix.spec.ts + checked-in table",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:47:05.769Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "test-corpus.capability-matrix",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Wire-op capability matrix (live-probed supported/unsupported)"
    },
    {
      "chapter": "test-corpus",
      "claims": [
        {
          "component": "core.sdk",
          "note": "state/conformance over paged-inspect",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "test-corpus.conformance-harness",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Corpus conformance harness (parse/render levels vs core.pin)"
    },
    {
      "chapter": "test-corpus",
      "claims": [
        {
          "component": "core.sdk",
          "note": "paged-fidelity + paged-diff CLI",
          "status": "shipped"
        },
        {
          "component": "editor.canvas",
          "note": "61-pack Playwright sweep, per-pack thresholds",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 202,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "test-corpus.fidelity-gate",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Fidelity gate (ΔE2000 + SSIM + glyph misplacement vs InDesign PDFs)"
    },
    {
      "chapter": "test-corpus",
      "claims": [
        {
          "component": "core.sdk",
          "note": "paged-gen builders; write_idml byte-deterministic",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:02.682Z",
        "headline": "All linked evidence is passing (33 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 33,
          "passed": 33,
          "skipped": 0
        }
      },
      "id": "test-corpus.generated-fixtures",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Deterministic generated fixtures (13 feature-mapped IDMLs)"
    },
    {
      "chapter": "the-renderer",
      "claims": [
        {
          "component": "core.canvas-wasm",
          "status": "shipped"
        },
        {
          "component": "editor.script",
          "note": "paged.collection(name) + named helpers",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:59:15.074Z",
        "headline": "All linked evidence is passing (11 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 11,
          "passed": 11,
          "skipped": 0
        }
      },
      "id": "the-renderer.collections-read",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Typed collections read surface (21 collections)"
    },
    {
      "chapter": "the-renderer",
      "claims": [
        {
          "component": "core.renderer",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:58:29.378Z",
        "headline": "All linked evidence is passing (102 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 102,
          "passed": 102,
          "skipped": 0
        }
      },
      "id": "the-renderer.cpu-backend",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "CPU backend (tiny-skia, full feature coverage)"
    },
    {
      "chapter": "the-renderer",
      "claims": [
        {
          "component": "core.sdk",
          "note": "paged-introspect(-wasm); devtools app",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:03.197Z",
        "headline": "All linked evidence is passing (11 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 11,
          "passed": 11,
          "skipped": 0
        }
      },
      "id": "the-renderer.devtools-introspection",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Scene-graph introspection (tree + property descriptors)"
    },
    {
      "chapter": "the-renderer",
      "claims": [
        {
          "component": "core.renderer",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "note": "per-page display list + LOD tiers",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:58:29.298Z",
        "headline": "All linked evidence is passing (9 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 9,
          "passed": 9,
          "skipped": 0
        }
      },
      "id": "the-renderer.display-list",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Versioned display list IR (41 command kinds, path interning)"
    },
    {
      "chapter": "the-renderer",
      "claims": [
        {
          "component": "core.renderer",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "Preflight panel renders dry-export findings; severity/page refs pending (gap 20)",
          "status": "partial"
        },
        {
          "component": "editor.canvas",
          "note": "live preflight/export diagnostics surfaced in the editor export flow (export-family.spec.ts green)",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:51:37.053Z",
        "headline": "All linked evidence is passing (9 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 11,
          "passed": 9,
          "skipped": 2
        }
      },
      "id": "the-renderer.export-diagnostics",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Export diagnostics (preflight findings)"
    },
    {
      "chapter": "the-renderer",
      "claims": [
        {
          "component": "core.canvas-wasm",
          "status": "shipped"
        },
        {
          "component": "core.sdk",
          "note": "register_font",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "Fonts panel families-in-use; missing/embedded flags pending (gap 4)",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": "2026-06-16T14:08:38Z",
        "evidence_at": "2026-10-18T07:59:00.026Z",
        "headline": "All linked evidence is passing (4 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 4,
          "passed": 4,
          "skipped": 0
        }
      },
      "id": "the-renderer.font-registry",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Font registration (family/style → bytes, persists across loads)"
    },
    {
      "chapter": "the-renderer",
      "claims": [
        {
          "component": "core.renderer",
          "note": "Held to the CPU lane page by page (corpus/generated/parity-diff.sh, budgets in parity-thresholds.json; 77/77 generated fixtures within budget on Metal and on lavapipe, a hard step in gpu.yml; ADR 146). Shadows, dashes and dots, and blend modes on paper match the CPU lane. Open: knockout groups and non-isolated translucent groups with blending members draw isolated; ink separations and coverage are CPU only.",
          "status": "partial"
        },
        {
          "component": "core.sdk",
          "note": "WebGPU-only ViewerSession (rejects without navigator.gpu)",
          "status": "shipped"
        },
        {
          "component": "editor.canvas",
          "note": "worker GPU attach + CPU fallback",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:16:44.22Z",
        "headline": "All linked evidence is passing (5 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 5,
          "passed": 5,
          "skipped": 0
        }
      },
      "id": "the-renderer.gpu-backend",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "WebGPU backend (Vello)"
    },
    {
      "chapter": "the-renderer",
      "claims": [
        {
          "component": "core.canvas-wasm",
          "status": "shipped"
        },
        {
          "component": "editor.canvas",
          "note": "pointer pipeline routes hit/marquee/pan",
          "status": "shipped"
        },
        {
          "component": "editor.gesture",
          "note": "click/marquee selection gestures",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:49:31.055Z",
        "headline": "All linked evidence is passing (6 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 6,
          "passed": 6,
          "skipped": 0
        }
      },
      "id": "the-renderer.hit-testing",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Hit testing (oriented containment, layer-aware) + marquee"
    },
    {
      "chapter": "the-renderer",
      "claims": [
        {
          "component": "core.renderer",
          "note": "BUILT 2026-08-18, published with the core patches since: per-run substituted flags traced at the resolver seam (ResolvedFont) → StyledRun/PositionedGlyph (the underline pattern), pink substitution highlight painted behind glyphs + stroke-only ghost-cross for inline-EPS/PDF missing links, FontSubstituted/ImageContentUnrenderable diagnostics; all behind degraded_asset_markers (default false), enabled only in canvas interactive builds; pinned calibration tests both ways + the fidelity gate re-proven pixel-identical.",
          "status": "shipped"
        },
        {
          "component": "editor.canvas",
          "note": "the default-font invariant SHIPPED 2026-08-18: CanvasClient defaultFontProvider covers EVERY load door (corpus picker, file input, bare client.loadDocument) — text renders in a fallback face instead of blank pages (default-font-invariant.spec.ts 3/3); fetchDefaultFont failure is loud. PARTIAL: the pink/ghost-cross markers render only once the editor pins the next core patch",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T04:45:41.894Z",
        "headline": "All linked evidence is passing (3 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 3,
          "passed": 3,
          "skipped": 0
        }
      },
      "id": "the-renderer.missing-asset-fallback",
      "importance": "normal",
      "since": "2026-08",
      "status": "experimental",
      "title": "Missing-asset degraded render (fallback font + placed-link placeholder)"
    },
    {
      "chapter": "the-renderer",
      "claims": [
        {
          "component": "core.canvas-wasm",
          "note": "camera + gesture SABs, protocol version reconcile",
          "status": "shipped"
        },
        {
          "component": "editor.canvas",
          "note": "transferControlToOffscreen; protocolMismatch warning",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:50:36.533Z",
        "headline": "All linked evidence is passing (6 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 6,
          "passed": 6,
          "skipped": 0
        }
      },
      "id": "the-renderer.offscreen-worker",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "OffscreenCanvas worker rendering (COOP/COEP + SAB)"
    },
    {
      "chapter": "the-renderer",
      "claims": [
        {
          "component": "core.canvas-wasm",
          "note": "`paged package <doc> -o <dir>` (core v0.73.3, L-13) writes InDesign's package folder: <name>.idml with its links re-pointed into Links/, every linked file copied (or written from embedded bytes), the faces of each used family from those the run was given into Document fonts/ (an OS/2 fsType Restricted face is listed, not copied), Instructions.txt and a report of fonts, links, spot inks and IDML losses (tests: paged-cli package_folder). OPEN: the wasm/editor door (no Package message kind yet). Earlier ruling, kept for the record: THE 'IMPOSSIBLE' RULING WAS WRONG AND IS RETRACTED. An earlier revision put packaging out of scope on the grounds that 'the fonts third is impossible — the editor is a browser app with no filesystem font access.' Chromium ships the LOCAL FONT ACCESS API: queryLocalFonts() returns FontData whose blob() yields the actual font file bytes, behind a permission prompt. And the editor is ALREADY Chromium-class — it requires WebGPU (navigator.gpu), SharedArrayBuffer with COOP/COEP, and OffscreenCanvas — so font collection is available on exactly the platform the editor already demands. It was never a platform impossibility, only an API nobody had looked up. WHAT REMAINS TRUE AND UNCHANGED: font binaries are NOT in an IDML package (Fonts.xml NAMES them, it does not carry them), so the fonts must come from the host; the editor has NO ZIP LIBRARY AT ALL, which is why a faithful Package is a CORE door rather than editor UI; and 'where licensing permits' cannot be determined automatically — InDesign has the same problem and answers it with a WARNING rather than a check, which is the honest precedent to copy. Packaging is therefore IN SCOPE and BUILDABLE, gated on a permission prompt and shipping a licensing warning rather than a licensing decision",
          "status": "partial"
        },
        {
          "component": "editor.shell",
          "note": "the editor-side collect flow + the eight-section Print dialog, which is UI over a prepress substrate that ALREADY EXISTS (marks, bleed, separations, output intents, preflight — see the-renderer.pdf-export-marks and editor-shell.panels.preflight). Print Tiling and Flattener Preview are independently sanctioned as drops. Today the Export Center's package row is a seam (editor-shell.panels.export-center)",
          "status": "planned"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:50:42.602Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "the-renderer.package-collect",
      "importance": "normal",
      "since": "2026-08",
      "status": "experimental",
      "title": "Package / collect for output (links + fonts + report)"
    },
    {
      "chapter": "the-renderer",
      "claims": [
        {
          "component": "core.renderer",
          "note": "native color, live transparency; text-as-text in the canvas export session only since core PR #51 (before it every run was drawn as outlines: the face-bytes lookup used the outline id)",
          "status": "shipped"
        },
        {
          "component": "core.canvas-wasm",
          "note": "session-streamed export",
          "status": "shipped"
        },
        {
          "component": "editor.shell",
          "note": "File ▸ Export PDF dialog (standards, profiles, marks, ranges, downsample)",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "Export center / outputs / preflight panels — PDF/X-4 target real, rest seamed",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:51:48.551Z",
        "headline": "All linked evidence is passing (12 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 13,
          "passed": 12,
          "skipped": 1
        }
      },
      "id": "the-renderer.pdf-export",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "PDF export (PDF 1.7 + PDF/X-4, deterministic)"
    },
    {
      "chapter": "the-renderer",
      "claims": [
        {
          "component": "core.renderer",
          "status": "shipped"
        },
        {
          "component": "editor.shell",
          "note": "dialog options persisted",
          "status": "shipped"
        },
        {
          "component": "editor.canvas",
          "note": "export marks/bleed produced via the editor export integration (export-family.spec.ts green)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:51:48.551Z",
        "headline": "All linked evidence is passing (16 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 18,
          "passed": 16,
          "skipped": 2
        }
      },
      "id": "the-renderer.pdf-export-marks",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Export marks + bleed (crop/registration/color bars/page info)"
    },
    {
      "chapter": "the-renderer",
      "claims": [
        {
          "component": "core.renderer",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:51:48.551Z",
        "headline": "All linked evidence is passing (3 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 3,
          "passed": 3,
          "skipped": 0
        }
      },
      "id": "the-renderer.pdf-font-embedding",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Export font subsetting + embedding (fsType policy)"
    },
    {
      "chapter": "the-renderer",
      "claims": [
        {
          "component": "core.renderer",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:51:36.972Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "the-renderer.pdf-image-downsampling",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Export image downsampling + JPEG re-encode"
    },
    {
      "chapter": "the-renderer",
      "claims": [
        {
          "component": "core.renderer",
          "note": "Create Tagged PDF (core ADR 182, v0.73.3): a structure tree Document > Art per story > Sect > P per paragraph with MCIDs, ParentTree, /StructParents, MarkInfo, /Lang and DisplayDocTitle, the tree InDesign 20.0.1 writes for the same document (tagged-pdf-probe); bookmarks as the PDF outline (core ADR 171). `paged export --tagged / --bookmarks` and the editor's Export PDF dialog ask for both. OPEN: paragraph-style export tags (H1, …), tables and figures with alt text, artifacts for page furniture, article reading order, PDF/UA-1",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:50:42.757Z",
        "headline": "All linked evidence is passing (6 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 6,
          "passed": 6,
          "skipped": 0
        }
      },
      "id": "the-renderer.pdf-tagged",
      "importance": "normal",
      "since": null,
      "status": "experimental",
      "title": "Tagged PDF, bookmarks and accessibility (PDF/UA)"
    },
    {
      "chapter": "the-renderer",
      "claims": [
        {
          "component": "core.renderer",
          "status": "shipped"
        },
        {
          "component": "core.sdk",
          "note": "ViewerSession load/render",
          "status": "shipped"
        },
        {
          "component": "editor.canvas",
          "note": "worker render + perf baseline",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:58:59.993Z",
        "headline": "All linked evidence is passing (3 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 3,
          "passed": 3,
          "skipped": 0
        }
      },
      "id": "the-renderer.pipeline",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Render pipeline (parse → scene → text → compose → raster)"
    },
    {
      "chapter": "the-renderer",
      "claims": [
        {
          "component": "core.canvas-wasm",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "raw Inspector panel",
          "status": "shipped"
        },
        {
          "component": "editor.script",
          "note": "paged.inspect/get",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T05:00:52.309Z",
        "headline": "All linked evidence is passing (12 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 12,
          "passed": 12,
          "skipped": 0
        }
      },
      "id": "the-renderer.properties-read",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Element properties read (authored/computed/source)"
    },
    {
      "chapter": "the-renderer",
      "claims": [
        {
          "component": "core.canvas-wasm",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:50:38.738Z",
        "headline": "All linked evidence is passing (6 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 6,
          "passed": 6,
          "skipped": 0
        }
      },
      "id": "the-renderer.scene-cache",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "GPU scene cache (LRU, invalidation hints)"
    },
    {
      "chapter": "the-renderer",
      "claims": [
        {
          "component": "core.renderer",
          "note": "DisplayList::digest() FNV-1a over commands+paths+pools+image bytes; native equivalence test pins viewer_build == stock build_document per page. Cross-artifact (browser vs native) lane is the V2 follow-up",
          "status": "shipped"
        },
        {
          "component": "editor.canvas",
          "note": "navigator thumbnails + fidelity suite",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:58:59.437Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "the-renderer.scene-digest",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Display-list structural digest (same-code-same-scene keystone)"
    },
    {
      "chapter": "the-renderer",
      "claims": [
        {
          "component": "core.canvas-wasm",
          "note": "translate-phase snap + snap-line notifications — v67 — RELEASED 2026-10-05: core v0.67.0 (715e130), plugin-sdk 0.2.39-canary.0, draw 0.1.0-canary.15, editor main 072e3df. translate aligns with all five leaf kinds (was text frames and rectangles), resize snaps the edges its handle moves, a path edit snaps the dragged point, all through the shared snap index and the session's snap settings.",
          "status": "shipped"
        },
        {
          "component": "editor.canvas",
          "note": "violet guide overlays; Ctrl bypass",
          "status": "shipped"
        },
        {
          "component": "editor.gesture",
          "note": "snap participates in translate gestures",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:49:30.71Z",
        "headline": "All linked evidence is passing (16 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 16,
          "passed": 16,
          "skipped": 0
        }
      },
      "id": "the-renderer.snapping",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Snap engine (page edges + sibling targets, 4 px tolerance)"
    },
    {
      "chapter": "the-renderer",
      "claims": [
        {
          "component": "core.canvas-wasm",
          "note": "requestSnapshot",
          "status": "shipped"
        },
        {
          "component": "core.sdk",
          "note": "render_to_bytes headless RGBA; render_page_to_bytes(index, width) thumbnails",
          "status": "shipped"
        },
        {
          "component": "editor.canvas",
          "note": "snapshot request/readback driven from the canvas app (export + E2E harness)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:44:43.187Z",
        "headline": "All linked evidence is passing (6 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 6,
          "passed": 6,
          "skipped": 0
        }
      },
      "id": "the-renderer.snapshots",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Page PNG snapshots (dpi-configurable)"
    },
    {
      "chapter": "the-renderer",
      "claims": [
        {
          "component": "core.sdk",
          "note": "present(zoom,scroll,dpr,only_page) continuous-stack camera with culling + scene cache; page_layout(); single load path viewer_build shared with the native digest test; published @paged-media/sdk 0.1.0-canary (2.21 MB brotli baseline). A .paged container opens from its native model part, as the editor opens it, and falls back to its IDML parts when the part is from a newer engine (core ADR 181, v0.73.3; container_open)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:58:59.959Z",
        "headline": "All linked evidence is passing (5 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 5,
          "passed": 5,
          "skipped": 0
        }
      },
      "id": "the-renderer.viewer-session",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "Viewer session — camera present, page layout, events (the public SDK)"
    },
    {
      "chapter": "the-renderer",
      "claims": [
        {
          "component": "core.sdk",
          "note": "createViewer: load (structured ViewerError), zoom-to-anchor/fit/clamps, layoutMode single|continuous, thumbnails, loaded/pageChanged/zoomChanged/scrollChanged/error events, disableable input lanes, dispose; 25 vitest; published 0.1.0-canary with bundled wasm",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:16:44.22Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "the-renderer.viewer-wrapper",
      "importance": "normal",
      "since": "2026-06",
      "status": "stable",
      "title": "@paged-media/idml-viewer — TS viewer (camera, pages, input lanes, events)"
    },
    {
      "chapter": "typography",
      "claims": [
        {
          "component": "core.parser",
          "note": "the typed <BalanceRaggedLines> enumeration child read on styles and ranges (core ADR 153); the attribute spelling is ignored by InDesign",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "FullyBalanced keeps the line count and evens the lines, the last included, as InDesign 20.0.1 does (`paragraph-attrs` D); PyramidShape / VeeShape not modelled",
          "status": "partial"
        },
        {
          "component": "core.mutation",
          "note": "paragraphBalanceRaggedLines (IDML enum, \"\" clears) on paragraphs and paragraph styles; written back as the <Properties> child (plugin-publish 788cff5)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:59:02.343Z",
        "headline": "All linked evidence is passing (8 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 8,
          "passed": 8,
          "skipped": 0
        }
      },
      "id": "typography.balance-ragged",
      "importance": "normal",
      "since": null,
      "status": "planned",
      "title": "Balance ragged lines"
    },
    {
      "chapter": "typography",
      "claims": [
        {
          "component": "core.parser",
          "note": "GridAlignment and GridAlignFirstLineOnly on paragraph styles and ranges, BaselineFrameGridOption on text frames, GridPreference from Preferences.xml; written back where InDesign writes them (plugin-publish #26).",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "Core ADR 140: an aligned line stands on the first grid line at or below where its leading put it, the lines after it placed from there; first line only where it says so; no grid line above the first; the document grid from the page or margin, or the frame's own. All 173 body baselines of baseline-grid and baseline-grid-margin match InDesign 20.0.1 to 0.001 pt. Open: the CJK layout-grid alignments; rotated or scaled frames.",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "No settable path for a paragraph's GridAlignment or GridAlignFirstLineOnly, a text frame's baseline grid options, or the document grid: DocumentSettings reports GridPreference read-only. A document that aligns to the grid opens, renders and round-trips; nothing can make one.",
          "status": "planned"
        },
        {
          "component": "editor.panel",
          "note": "The Paragraph panel's \"Align to baseline grid\" row is a disabled seam with no path to bind, and no control sets or shows the document baseline grid.",
          "status": "planned"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": null,
        "headline": "No evidence linked yet",
        "state": "unknown",
        "tests": {
          "failed": 0,
          "linked": 0,
          "passed": 0,
          "skipped": 0
        }
      },
      "id": "typography.baseline-grid",
      "importance": "normal",
      "since": null,
      "status": "planned",
      "title": "Align to baseline grid (document and frame grids)"
    },
    {
      "chapter": "typography",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "character.baseline-shift path (W0.1)",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "character panel LIVE binding on characterBaselineShift (character.composition.ts, W2.1); journey typography-depth",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:05.113Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "typography.baseline-shift",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Baseline shift"
    },
    {
      "chapter": "typography",
      "claims": [
        {
          "component": "core.renderer",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:59:02.607Z",
        "headline": "All linked evidence is passing (6 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 6,
          "passed": 6,
          "skipped": 0
        }
      },
      "id": "typography.bidi",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Bidirectional text reordering"
    },
    {
      "chapter": "typography",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "character.case path (W0.1)",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "character panel Case toggle LIVE on characterCase but offers Normal/AllCaps/SmallCaps only — CapToSmallCap is not in the segment set (character.composition.ts, W2.1); journey typography-depth",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:05.119Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "typography.capitalization",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Capitalization (AllCaps/SmallCaps/CapToSmallCap)"
    },
    {
      "chapter": "typography",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "Set as InDesign 20.0.1 sets it (core ADR 167, vertical-text-probe, every glyph within 0.02 pt): the horizontal layout of the turned frame (width and height swapped, insets turned with it) turned a quarter clockwise, so lines become columns from the right. Kanji, kana and CJK punctuation stand upright, shaped top to bottom in their vertical forms (vert, vertical metrics, vpal and vkrn under Metrics kerning); Latin and digits turn with the column; ruby and kenten stand to the right. Until 2026-10-16 the whole horizontal layout was turned, so kanji lay on their side and punctuation kept its horizontal forms. Not yet: tate-chu-yoko, warichu, non-rectangular vertical frames, text wrap, tables and footnotes in vertical stories.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:57:41.916Z",
        "headline": "All linked evidence is passing (3 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 3,
          "passed": 3,
          "skipped": 0
        }
      },
      "id": "typography.cjk-vertical-writing",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "CJK vertical writing"
    },
    {
      "chapter": "typography",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "The cap is sized so its cap height spans the dropped lines at the paragraph's leading, stated or auto, using the face's own cap height (core ADR 142 consequences: nested-styles n13 and text-advanced p1 now start the body where InDesign does, within 0.004 pt); the ink of the cap sits on the margin.",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "ParagraphDropCapCharacters + ParagraphDropCapLines paths (W0.2)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:05.675Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "typography.drop-caps",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Drop caps (characters/lines + detail scaling)"
    },
    {
      "chapter": "typography",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "size + family + style paths (W0.1; style splits runs on partial range)",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "size/leading/tracking/fill live; family/style selects seamed",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:05.153Z",
        "headline": "All linked evidence is passing (10 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 10,
          "passed": 10,
          "skipped": 0
        }
      },
      "id": "typography.font-selection",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Font selection + fallback (AppliedFont/FontStyle/PointSize)"
    },
    {
      "chapter": "typography",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "per-paragraph ParagraphHyphenation toggle path (W0.2)",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:05.689Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "typography.hyphenation",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Hyphenation (TeX patterns; Proximity licensed)"
    },
    {
      "chapter": "typography",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "The zone keeps hyphenation out of a word that starts inside it, in ragged text; justified text ignores it, as InDesign does. It no longer widens the word-space stretch (core ADR 141): InDesign fills 18 + 8 words at zone 0, 36 and 100 where the widened stretch set 14 + 12. A paragraph that states no zone has none (the tints labels hyphenate as at zone 0).",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:59:00.741Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "typography.hyphenation-zone",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Hyphenation zone"
    },
    {
      "chapter": "typography",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "Justified text breaks by least demerits with an over-shrunk line priced rather than forbidden and no stretch limit (core ADR 144): 99 of 160 probed paragraphs match InDesign 20.0.1 line for line (73 with Knuth-Plass at TeX settings). Ragged text: ADR 142. A justified line of one word follows SingleWordJustification (absent: letter-spaced to the measure) and LastLineIndent narrows the last line's measure (ADR 153).",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "paragraphSingleWordJustification and paragraphLastLineIndent (core ADR 153), on paragraphs and paragraph styles, written back to IDML; the word/letter/glyph spacing bands have no property path yet",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:59:02.546Z",
        "headline": "All linked evidence is passing (15 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 15,
          "passed": 15,
          "skipped": 0
        }
      },
      "id": "typography.justification-spacing",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Word/letter spacing + glyph scaling (min/desired/max)"
    },
    {
      "chapter": "typography",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "All ten KentenKind shapes and custom characters, size, scale, placement, position and alignment, measured against InDesign 20.0.1 (ruby-kenten-options-probe). Punctuation takes no mark (core ADR 167: of 24 marked characters InDesign marked none of 。、「」). In vertical text the marks stand upright to the right of the column (left for BelowLeft). A kenten change between two characters keeps their kerning pair (runs the shaper cannot tell apart kern across their boundary; InDesign kerned す。 1.4 pt closer). Not yet: kenten colours and strokes.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:57:29.254Z",
        "headline": "All linked evidence is passing (3 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 3,
          "passed": 3,
          "skipped": 0
        }
      },
      "id": "typography.kenten",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Kenten emphasis marks"
    },
    {
      "chapter": "typography",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "Japanese text breaks between any two characters (core ADR 167; the line composer used to break CJK only where hyphenation found a word, so a paragraph with no spaces ran past its frame). Kinsoku is enforced only when the paragraph's KinsokuSet names a table: InDesign writes it as a typed Properties child, Nothing by default (a line may then start with 。); it used to key on KinsokuType, which InDesign always writes. Not yet: the set's own table (HardKinsoku and SoftKinsoku use one hard table; InDesign 20.0.1's international build reads HardKinsoku as Nothing, so the tables cannot be measured there) and the KinsokuType push-in / push-out choice.",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:59:00.821Z",
        "headline": "All linked evidence is passing (9 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 9,
          "passed": 9,
          "skipped": 0
        }
      },
      "id": "typography.kinsoku",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Kinsoku CJK line-break rules"
    },
    {
      "chapter": "typography",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "Justified text breaks by Knuth–Plass with InDesign-calibrated penalties, and keeps a word overrunning its measure by 0.0466 em + 0.178/size pt (core ADR 141). Ragged text breaks by minimum raggedness in every frame (ADR 142): a line keeps its last word while it overruns by word/99 + size/320 · ln(1 + before/word), fitted on 45 InDesign lines within 0.12 pt, and the overrun comes out of the word spaces. Open: the Single-line and Paragraph composers differ from InDesign on some hyphenation points (TeX patterns against Adobe dictionaries).",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": "2026-10-12T17:16:47Z",
        "evidence_at": "2026-10-18T07:58:38.135Z",
        "headline": "All linked evidence is passing (5 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 5,
          "passed": 5,
          "skipped": 0
        }
      },
      "id": "typography.knuth-plass",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Knuth–Plass line breaking + justification modes"
    },
    {
      "chapter": "typography",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "auto leading is the line's largest size x AutoLeading per cent (core ADR 153; InDesign's 120 by default)",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "characterLeading; paragraphAutoLeading (per cent, core ADR 153) on paragraphs and paragraph styles, written back as AutoLeading",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "Character panel cluster",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:12.101Z",
        "headline": "All linked evidence is passing (10 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 10,
          "passed": 10,
          "skipped": 0
        }
      },
      "id": "typography.leading",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Leading (explicit pt / auto)"
    },
    {
      "chapter": "typography",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "OtfFeatures cascaded to rustybuzz (frac/ordn/swsh/dlig/zero/titl/calt-off/figure-style pairs/ss01-20) — W1.3",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "ligatures bool + otf_features opaque authoring string (W0.1)",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "character panel Ligatures switch LIVE; the OpenType chip row stays a seam — the opaque characterOtfFeatures tag string has no chip mapping (character-panel.tsx / character.composition.ts); journey typography-depth",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:59:02.51Z",
        "headline": "All linked evidence is passing (4 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 4,
          "passed": 4,
          "skipped": 0
        }
      },
      "id": "typography.ligatures-opentype",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Ligatures + OpenType feature toggles"
    },
    {
      "chapter": "typography",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "uniform half-width tightening MVP (layout.rs:502); table-driven per-adjacency deep-deferred (deep-defer-records.md §cjk-vertical-text)",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:59:02.228Z",
        "headline": "All linked evidence is passing (3 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 3,
          "passed": 3,
          "skipped": 0
        }
      },
      "id": "typography.mojikumi",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Mojikumi CJK spacing"
    },
    {
      "chapter": "typography",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "super/subscript honoured in layout (W1.3 hardening)",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "character.position path (W0.1)",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "character panel Position toggle LIVE on characterPosition (Normal/Superscript/Subscript — character.composition.ts, W2.1); journey typography-depth",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:05.174Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "typography.position-super-subscript",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Position (superscript/subscript)"
    },
    {
      "chapter": "typography",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "Set as InDesign 20.0.1 sets it, measured with InDesign-built probes: per-character and group ruby, size, scale, offsets, position, alignment and font (core #113), and ruby wider than its base (core ADR 166, ruby-parent-spacing-probe): it overhangs kana, brackets and Latin as far as RubyParentOverhangAmount allows (never a kanji, never past a line's start or end), narrows to RubyParentScalingPercent under RubyAutoScaling, then spaces the base by RubyParentSpacing (1-2-1, both sides, equal, full justify, none); a base at a line's start or end drops its outer share. In vertical text ruby stands upright to the right of the column (left for BelowLeft; ADR 167). All 40 probe cases within 0.012 pt of InDesign (an overhang never exceeds the neighbour's natural width; runs kern across a ruby base's boundary). Not yet: ruby colours and strokes, ruby tate-chu-yoko.",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:57:31.311Z",
        "headline": "All linked evidence is passing (3 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 3,
          "passed": 3,
          "skipped": 0
        }
      },
      "id": "typography.ruby",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Ruby annotations"
    },
    {
      "chapter": "typography",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "horizontal-scale + skew paths (W0.1)",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "character panel H/V-scale + skew 3-up LIVE on characterHorizontalScale/characterSkew (character.composition.ts, W2.1); journey typography-depth",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:12.163Z",
        "headline": "All linked evidence is passing (3 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 3,
          "passed": 3,
          "skipped": 0
        }
      },
      "id": "typography.scale-skew",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "HorizontalScale + Skew (false italic)"
    },
    {
      "chapter": "typography",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "whole-list TabStops value replace (W0.2)",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "SHIPPED — corrected 2026-08-09. The Tabs concept panel writes the real path: apps/canvas/src/panels/concept/tabs-panel.tsx:54 declares `path: \"paragraphTabStops\"`, the v28 whole-list replace. Was: Tabs concept panel seamed",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:05.823Z",
        "headline": "All linked evidence is passing (1 test current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 1,
          "passed": 1,
          "skipped": 0
        }
      },
      "id": "typography.tab-stops",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Tab stops (L/C/R/decimal + leaders)"
    },
    {
      "chapter": "typography",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "optical kerning → metrics fallback (true optical deep-deferred; thoughts/docs/paged/deep-defer-records.md §optical-kerning)",
          "status": "partial"
        },
        {
          "component": "core.mutation",
          "note": "characterTracking + kerning-method path (W0.1)",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "tracking live; kerning cluster seamed",
          "status": "partial"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:12.225Z",
        "headline": "All linked evidence is passing (3 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 3,
          "passed": 3,
          "skipped": 0
        }
      },
      "id": "typography.tracking-kerning",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Tracking + kerning (metrics; optical falls back to metrics)"
    },
    {
      "chapter": "typography",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "gap colors/tints not honoured yet",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "underline + strikethru paint-only paths (W0.1)",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "character panel Underline + Strikethrough switches LIVE on characterUnderline/characterStrikethru (character.composition.ts, W2.1); journey typography-depth",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:05.222Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "typography.underline-strikethru",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "Underline / strikethrough decorations"
    },
    {
      "chapter": "typography",
      "claims": [
        {
          "component": "core.parser",
          "status": "shipped"
        },
        {
          "component": "core.renderer",
          "note": "vertical-scale honoured in layout (W1.3 hardening; CJK writing-mode integration remains CJK Stage 3)",
          "status": "shipped"
        },
        {
          "component": "core.mutation",
          "note": "h/v scale paths (W0.1)",
          "status": "shipped"
        },
        {
          "component": "editor.panel",
          "note": "character panel LIVE binding on characterVerticalScale (character.composition.ts, W2.1); journey typography-depth",
          "status": "shipped"
        }
      ],
      "health": {
        "changed_at": null,
        "evidence_at": "2026-10-18T07:52:12.288Z",
        "headline": "All linked evidence is passing (2 tests current)",
        "state": "healthy",
        "tests": {
          "failed": 0,
          "linked": 2,
          "passed": 2,
          "skipped": 0
        }
      },
      "id": "typography.vertical-scale",
      "importance": "normal",
      "since": null,
      "status": "stable",
      "title": "VerticalScale"
    }
  ],
  "generated_at": "2026-10-18T09:05:22.002416184Z",
  "last_scan_at": "2026-10-18T09:05:18.504Z",
  "profile": "public",
  "project": {
    "id": "paged",
    "name": "Paged"
  },
  "repos": [
    {
      "id": "cockpit",
      "sha": "d63f9823299728d921f1b7f329875607350249d6"
    },
    {
      "id": "core",
      "sha": "ca7462ebcb5c172edb4bf3bd56cc28fb566eddac"
    },
    {
      "id": "docs",
      "sha": "58d225b24f00dea54e199ac055fdc52e2b98a672"
    },
    {
      "id": "editor",
      "sha": "e673996ff930b4613974d3edffa6d499ada3aa52"
    },
    {
      "id": "plugin-data",
      "sha": "c93810b30a5fc26e64275ce9b89f6f53787cc309"
    },
    {
      "id": "plugin-doc",
      "sha": "6049c94c851561d4d83492d3ec91fcf616a6c6ab"
    },
    {
      "id": "plugin-draw",
      "sha": "db15615372b0b4685888710720164b9b3de6c3bb"
    },
    {
      "id": "plugin-image",
      "sha": "19e3f4c9adb3d188823413389f863af18d50dbd0"
    },
    {
      "id": "plugin-publish",
      "sha": "a83e8441fd39af8fafe3ff0e9da99b67b3c384ad"
    },
    {
      "id": "plugin-sdk",
      "sha": "b6b3776d5e7a1805286305a6f80d84851ef45060"
    },
    {
      "id": "plugin-sheets",
      "sha": "cdea03433622f45755ab82e7774950c953afffd7"
    },
    {
      "id": "plugin-slide",
      "sha": "1a78e314b683b4cd9af70792376d6595d60cac76"
    },
    {
      "id": "plugin-web",
      "sha": "c64c98050c1e3882460fd94db35c63cdbbe30529"
    }
  ],
  "schema": "cockpit.export.v1",
  "totals": {
    "by_health": {
      "healthy": 626,
      "unknown": 79
    },
    "by_status": {
      "deprecated": 1,
      "experimental": 160,
      "planned": 20,
      "stable": 524
    },
    "features": 705
  }
}
