Files

9.6 KiB

C2-11a: an inert editor wikilink node

Base: aca16404c. Owner: Herb. Reviewer: Filbert. Issue: #1715. Authority: sage-2794, Q220/mos-1301. The assignment permits authoring the node and tests before the measurement slot. Runs, claim and pushes wait for Sage's CLEAR. After row253 lands, a new branch will carry the change onto its landing with every landed hunk retained.

Measurement before choosing the explanation

Run agents/herb/work/queue-214/measure.mjs only on a granted slot. It loads aca16404c's editor.js and markdown-preview.js directly from local Git objects. It verifies the editor bundle hash against that base's BUILD.json. It cannot accidentally measure a candidate rebuild. Its JSON records the full baseline SHA, bundle hash, timestamp, input, output and equality for each fixture. The first five are the brief's plain, labelled, heading, list and front-matter cases; extra cases cover a heading block, combined heading/label, Unicode/spacing, adjacency and an inert embed. No result is available yet. Static reasoning is not a measurement.

Outcome A, every baseline round-trip is equal: record that wikilinks do not trigger Source fallback for the measured inputs. Do not claim that bug exists. The source's raw/sig cache explains why an unchanged block can keep its bytes even when its canonical serializer escapes brackets. Add the node for structured wikilinks and dirty-block preservation, as the brief requests. A baseline structural/edit regression must fail even if all unchanged-file checks pass.

Outcome B, one or more differ: record the exact differing bytes and affected contexts. Keep the existing Source fallback fail-closed. The node restores the required spellings where the block is structured. Retain the same no-edit and dirty-block regressions. If the mismatch comes from a separate block, line-ending or front-matter defect, report it before widening scope.

Representation and conversion

The schema gains one inline atom, wikilink, with a single source attribute. Its source is the complete spelling, not reconstructed target/label parts. It has no href, navigation, fetch or click behavior. A span with data-source and a text child displays the spelling. Heading content includes the new atom; paragraphs, quotes and lists already accept inline content.

One markdown-it inline rule runs before ordinary links, after escapes and backticks. A sticky match reads a complete single-line ... or !... without copying every suffix or crossing state.posMax. The node writer writes its validated spelling without bracket escaping, so editing adjacent text cannot rewrite a wikilink. Front matter remains a raw block. No new file-read, save, routing, documentPath or target-resolution behavior is added.

The accepted atom grammar excludes brackets within the body, line breaks, angle brackets and ampersands. This is an editor representation boundary, not a library resolver or a refusal to open such a file. Malformed, HTML/entity-like and code syntax stays under the existing text/raw rules. This preserves the existing escaping of HTML/entities when a block changes. DOM paste accepts only that same grammar. Invalid attributes constructed programmatically are flattened to one line and escaped by the text writer. No input string is parsed as HTML and embeds never load an image.

Acceptance and review

The measurement JSON goes in BUILD.md and the final manifest packet. Ten fixture round-trips run with and without a final newline. A browser test inspects real ProseMirror atom types and attributes, edits the same block and reparses it, for each fixture. Two more browser tests cover code/escape/malformed/hostile syntax and DOM/programmatic-attribute safety. Tests use the existing fixture and Chromium harness and the shipped bundle. They do not export new production APIs just for testing.

Before rebuilding, run only the structural regression against the baseline bundle and observe it fail. Rebuild the candidate with the exact pinned esbuild0.28.2 binary, run the whole new test file, existing editor tests, editor browser tests and provenance test. Remove only the wikilink serializer handler for one mutant; the dirty-block assertion must fail. Restore, rebuild and rerun the focused regression. All execution needs Sage's CLEAR, including installation, checks, builds, browser tests and the measurement.

The checked-in bundle and editor/BUILD.json must be regenerated by the existing build script. No hand edits of generated JavaScript or provenance. The current shared esbuild is0.28.1 and the local webui install is absent. A granted npm ci --ignore-scripts --no-audit --no-fund in packages/webui is needed before building; the root node_modules remains its plain symlink. No dependency or lockfile change is planned.

Final gate: Filbert approves the manifest, then the whole webui suite and every scripts/test-*.sh on Sage's slot. Two failures stop a gate. A failure outside this row is reported to Sage before any rerun. Source, generated output, mode/hash manifest and raw granted logs identify the candidate.

Static review follow-through (Filbert, 2026-10-22T16:43:35Z)

R1: the focused editor-provenance run and Sage's whole webui gate must set MOSAIC_REQUIRE_EDITOR_BUILD=1. A missing, wrong-version or unpinned binary then fails. Check every log for ^# skip, as well as TAP # SKIP directives and the spec reporter's skipped-test lines; record each skipped name and reason. A skipped rebuild is not proof of bundle reproducibility. The CSS output must stay byte-identical and match BUILD.json.

R2: no-edit roundTrip checks are expected to pass on the baseline because parse sets sig=canonical and an unchanged serialise returns lead+raw. Their green result is compatibility evidence, never evidence that the atom was needed or installed. The same-block edit/structural regression is the red that proves the new node. The required measurement still runs and is kept.

N1: bus.js checks visual mode at949 on this base and also requires MosaicMarkdown. The measurement uses the existing helper's bare-window VM shape, markdown-preview.js first, then editor.js, both from pinned Git blobs.

N2: the exclusion of & and angle brackets is deliberate: the existing text serializer escapes entities/HTML when it rewrites a block for another renderer. An atom's verbatim writer must not bypass that rule, including for pasted/programmatic values. This keeps those targets as ordinary text; Q&A will still be escaped if its own block changes. It is a known representation limit beyond the five brief cases, not a resolver change. It is included among the text-only cases. Unedited bytes remain intact.

N3: the reference-definition fixture a with [a]: u deliberately becomes one inert wikilink rather than a bracket around a reference link. Literal spelling and the separate definition block remain intact.

N4: x [[a]] is an explicit fixture. The atom inherits the enclosing ordinary link mark, whose existing target gate still applies. The atom itself supplies no href or new click behavior; the fixture checks its structure and spelling before/after a same-block edit.

N6: every granted browser/test process gets TMPDIR from mktemp -d /mnt/storage/scratch/tmp/gXXX. Never /tmp. Sweep owned Chromium and Node processes by cwd and the temp-root path after the mutant, after any timeout, and before clearing the slot. Retain/report uncertain owners.

R2 current-base check, sage-4164 (2026-10-24)

The execution base is now487500a16, on herb/row214-r2, not the historical aca16404c above. The two accepted document commits were cherry-picked cleanly. The model, build script, toolchain, BUILD.json, editor.js, view, entry and editor test loader are byte-identical between those bases. The current fixture's new configDirectory isolation is retained; it is not copied from the old worktree. The visual-mode check is now bus.js:1054. The current C2-11a brief section is unchanged. Row266's chat modules, public/chat.js and public/chat.css are outside this row, with no overlap.

measure.mjs now pins487500a16's Git objects and BUILD.json. The baseline bundle still lacks the atom, and the first executed structural regression will use it before any rebuild. The required measurement, red/green, pinned rebuild, mutant kill and full gate remain unrun and required. Runtime validation is not inferred from the unchanged source or this static check.

Sage-4164 explicitly authorizes committing the preserved local node/tests on the new branch before execution. The authored source commit is static work, not a green candidate. No Node, npm, rebuild or browser starts until Sage sends CLEAR. HASH-PINS.json records the test-tree audit and row266 intersection. The gate still requires MOSAIC_REQUIRE_EDITOR_BUILD=1, a named skip list, whole webui and every scripts/test-*.sh.

R2 static follow-up, Filbert B1/N1 (2026-10-24T17:14:55Z)

B1: state.text(..., true) escapes Markdown punctuation, not HTML/entities. The invalid-atom writer now calls the same escapedText helper as ordinary text nodes, including the <& step. Valid atoms still preserve their source verbatim. The malformed-attribute regression now asserts both an escaped <img and an escaped ©, plus flattened line breaks. A second mutant will replace only the invalid atom's escapedText call with state.text, leaving normal text escaping intact; this browser regression must kill it. This additional mutant needs Sage's grant alongside the serializer-removal mutant. No runtime result is claimed for either.

N1: backticks are excluded from the atom body, preserving code-span precedence. The text-only fixture includes [[a]] and must produce no wikilink atom. All other malformed/HTML/entity boundaries remain as before.