Compare commits

...
41 changed files with 2674 additions and 16 deletions

No files matched your search

+1
View File
@@ -5554,3 +5554,4 @@ claim. Receipts and limitations in agents/lena/work/queue-296/BUILD.md and run-r
2026-10-24T15:55:22Z | Sage | Row 225 (#1726, C2-10b, Fethry on Codex, Dewey) LANDED 9b6345466. Before: refactor at c63801959. After: Fethry's r2 candidate c1f9dfd3c (11 commits on 963303403) cherry-picked onto c63801959. 8 commits are equal by range-diff; the other 3 differ only in the append-only log hunks, kept both sides with 0 lines removed against either parent. All 13 manifest paths match manifest.r2.sha256 (517af41b). Dewey APPROVE c31599 rev 3960; request c31600 revs 3961-3962, posted by Sage because Fethry has no token. Sage gate GREEN 15:54:58Z with its own npm ci and MOSAIC_REQUIRE_EDITOR_BUILD=1: every package suite except queue 0 fail (webui 442, library 149, bus 981, cli 988 plus the allow-listed FED R7 skip, whose runner passed 1/1), all 12 scripts rc 0. The excluded M3 timeout attempt was not counted; the unchanged retry killed cleanly. Dewey N1-N3 non-blocking: the Discord relay must pass check once it calls renderReply; the M3 hang was in the new status test's after-hook teardown; the pre-217 residual stands under sage-3669. #1726 closed c31604. Not re-pinned.
2026-10-24T16:12:35Z | Sage | Row 287 (#1792, C2-21, Steelbeak on Codex, Boyd and Dewey) LANDED 0e935776e. Before: refactor at 9b6345466. After: Steelbeak's c2338ed58 and doc follow-up a5370b198 cherry-picked onto 9b6345466. One commit equal by range-diff, the other differs only in log hunks, kept both sides. 56 manifest paths match 3683b40a. Boyd APPROVE c31602 rev 3967, Dewey APPROVE c31603 rev 3968; request c31601 revs 3965-3966 posted by Sage. Sage gate GREEN 16:11:38Z: bus 998 (Gitea Docker F-A/F-B and X-G3, minted 4, hits 0), business 283, webui 442, cli 989 plus the allowed FED R7 skip, every other suite 0 fail, all 12 scripts rc 0; fixture credential values in 0 of 29 run files with a positive control. Rulings applied: sage-4125 (library grants mint declared scopes, chief read), mos-1590 (new-test fixture fixes are the lead's call when no production code or old test changes and every fail-closed refusal keeps its own test), sage-4139 (fixture fixes) and sage-4141 (test-land-work.sh is mode 100644, run via bash). Follow-up: F-C negative, a protected Story/** rule refuses the owner too; binds the first row that needs owner pushes to a protected branch. Dewey N1-N3 non-blocking: the block's last line "can write every repository this user can write" contradicts the chief's read grant; a collaborator entry outlives a failed step 5, which still fails closed; #publishFacts would accept a library project, unreachable today. Closes Dewey's 286 r2 N2 (T7/T9 real mint). #1792 closed c31606. Not re-pinned.
2026-10-24T22:15:07Z | Herb | row 214 #1715 C2-11a focused evidence | Before: wikilinks are ordinary editor text; raw/sig retention keeps no-edit bytes, but the model has no inert node. After: inert inline wikilink atom preserves spelling through neighbouring edits; invalid attrs share HTML/entity escaping. On herb/row214-r2 base487500a16, grant sage-4286 lane B: baseline10/10 roundTrips equal (outcome A); baseline structural0/1 expected red; four whole editor files40/0, both mutants killed0/1 and restored1/0, whole webui69 files455/0 in174.189s with MOSAIC_REQUIRE_EDITOR_BUILD=1, nice19, no skips/cancellations. A mistaken preliminary7-file subset28/0 is logged and excluded from gate evidence. CSS byte-identical, final process sweep0, scratch TMPDIR removed. Filbert candidate review and every scripts/test-*.sh gate remain pending; no click, target resolution or library save change.
+193
View File
@@ -0,0 +1,193 @@
# Row214 C2-11a work record
Assignment sage-2794 at2026-10-22T16:33:20Z, Q220/mos-1301, issue#1715.
New isolated branch herb/row214 on aca16404c. The earlier197/215 worktrees
and their dirty/untracked historical packets are untouched. node_modules is
the required plain symlink to /mnt/storage/src/mosaic-stack/node_modules.
Read C2-11a in both pinned briefs. The section is unchanged at51cc0445c;
Q222 changes C2-06's reply-limit settings, not wikilinks. The editor model,
entry, view, existing model/browser/provenance tests, build/toolchain and
vendored markdown parser/serializer were read. No third_party source changes.
## Static finding and authored work
model.mjs stores a canonical sig and the raw original block, and serialise
returns raw when the sig matches. The vendored text writer escapes brackets
(to_markdown.ts:433-437). It is therefore possible for no-edit round-trips
to pass while dirty-block output escapes a wikilink. This is a source-based
hypothesis, not a measured result. Both outcomes are in DESIGN.md.
Authored, unrun: the pinned-bundle measurement; ten fixture round-trips;
three browser regressions; inert wikilink schema/tokenizer/serializer. The
schema includes headings, and front matter remains raw. The serializer
validates its spelling before bypassing escaping. HTML/entity-like targets
stay text under the existing escaping rules. No clicking or resolution.
Local static git diff --check is clean. No Node, npm, compiler, browser,
measurement, test, Docker, HTTPS Git or push has run. The queue claim waits
for a gap because scripts/mosaic invokes Node. No candidate or pass claim.
Only design/path documents may be committed before the granted red/green
checks; product, tests and measurement remain uncommitted until validated.
## Next granted steps
1. Claim from canonical during a CLEAR/gap.
2. Run measure.mjs against aca16404c's pinned Git blobs; save JSON unchanged.
3. Run the structural/edit regression alone on the baseline bundle (red).
4. Install the already-pinned esbuild0.28.2 in this worktree's packages/webui
with npm ci --ignore-scripts --no-audit --no-fund; no root install or lock edit.
5. Build with the existing editor/build.mjs, then the whole new test file and
existing editor, editor-browser and editor-provenance files, each singly.
6. Kill the serializer-removal mutant, restore, rebuild, focused green.
7. Commit only after the applicable checks, push only when Sage clears HTTPS
Git, send Filbert and Mos the candidate SHA/hash manifest directly, cc Sage.
8. After253 lands, new branch on its tip preserving all landed changes.
9. Filbert and Sage's whole webui and scripts gate finish the row.
All runs wait for explicit Sage CLEAR. Gander's row253 run5 owns the slot.
The whole suite gate and any unexpected failure use mos-0823's stop rule.
Proposed SESSIONS text for Sage's post-253 doc sweep (do not append here):
2026-10-22 | Herb (gpt-6.1-sol, f6cf4287) | row214 C2-11a editor wikilinks authoring | On herb/row214 at aca16404c, authored baseline round-trip measurement, designs for equal/unequal results, inert inline node and regression tests under sage-2794/Q220. Claim, runs and pushes held while253 owns the slot; measurement and test results unavailable.
## Filbert static design/path review accepted, R1 and R2 required
Review at2026-10-22T16:43:35Z read dee9f3d8d's packet only, no product or
runs. R1 is now in the plan: set MOSAIC_REQUIRE_EDITOR_BUILD=1 on focused
provenance and whole webui runs, grep ^# skip plus TAP/spec skip directives,
and name every skip. Missing/wrong pinned esbuild must fail, not skip.
R2: baseline no-edit roundTrips are expected to pass. They test retention
of raw bytes and are not red evidence for the node. Only the baseline
same-block structural/edit regression is expected red.
Two added fixtures cover a reference-definition collision and an atom
inside ordinary link text, taking the planned file to10 no-edit tests and
3 browser tests. The explicit & exclusion preserves the existing escaped
HTML/entity writer for dirty blocks; Q&A stays text and is a known limit.
Measurement VM setup matches loadEditor. Browser TMPDIR must use the short
non-/tmp scratch root. Sweep Node/Chromium after mutant/timeout and at clear.
All remain authored and unrun while253 owns the slot.
Filbert follow-up at2026-10-22T16:45:34Z: the authored N3 browser fixture
now explicitly asserts the separate reference-definition block ends in its
original exact bytes after the same-block edit. This test change is unrun
and uncommitted with the other product/test authoring, pending CLEAR.
## R2 restart on current refactor, sage-4164
New worktree: /home/jwoltje/.t3/worktrees/mosaic-stack/herb-row214-r2.
Branch herb/row214-r2, base487500a16f4ab5902c318509fdec6acc56a543ca.
Carried dee9f3d8d and e2710e92a as67ac3c25a andde42a59d4. Copied only
Herb's local model, new editor test/helper, measure.mjs and pending BUILD
note. The old worktree remains untouched. No stash or force push.
The current editor model/build/toolchain/BUILD.json/shipped bundle and
loader are identical to the old base. Entry/view and package dependencies
are unchanged. The current test fixture adds configDirectory:root; its
landed change is retained. The C2-11a brief section is unchanged. The
visual-mode condition now lives at public/bus.js:1054. No product behavior
beyond the accepted inert inline node is added by the rebase.
Row266 overlap: none. Our paths do not touch chat*.mjs, chat.js or chat.css,
and the intersection with origin/fethry/row266's actual diff is empty.
HASH-PINS.json records that ref's SHA and merge base.
Hash audit: grepped packages/*/tests for each actual/planned changed file
and filename, and scanned495 test/fixture files for literal prior SHA256
or Git-blob pins. No such pins found. Existing editor-provenance checks
read BUILD.json dynamically; regenerating it and the bundle is required.
The loader and browser/route tests use editor.js but pin no source hash.
No tests or pin asserts were weakened. The complete references and planned
generated paths are in HASH-PINS.json.
Sage-4164 explicitly permits the unrun node/tests to be committed now. This
supersedes the earlier packet's instruction to keep them uncommitted until
green. Static authoring is not a tested candidate. SOURCE.json records its
source hashes and the unbuilt generated-output status. The measurement now
pins this current execution base, not the historical accepted-doc base.
No Node, npm, measurement, compiler, test, browser, Docker or mutant has
run. Claim already succeeded at rev3234 on10-22; no new queue move is
needed. Push authorization is only herb/row214-r2. Row257's landing gate
and then266 own the slot; all execution remains held for Sage's CLEAR.
Planned focused grant: baseline measurement; the same-block structural
red; pinned npm ci/build; whole editor-wikilink.test.mjs (10 no-edit tests
with/without a final newline and3 browser tests), editor.test.mjs,
editor-browser.test.mjs and editor-provenance.test.mjs singly; one
serializer-removal mutant and restored focused green. Set
MOSAIC_REQUIRE_EDITOR_BUILD=1 for provenance and the later whole webui
gate. Capture all skip names, raw logs, short scratch TMPDIR and final
Node/Chromium process sweep. Two failures stop a gate; no rerun after an
out-of-row failure without reporting to Sage.
## Filbert R2 static B1 fixed before execution
Review at2026-10-24T17:14:55Z confirmed a defect in the authored invalid
atom path: state.text alone lacks the text-node override's HTML/entity
escaping. Factored that existing policy into escapedText and reused it for
invalid atoms. Extended the test payload/assertions to cover both <img and
&copy;. Valid atom behavior is unchanged. Excluded backticks from atom
recognition and added a text/code precedence fixture for N1.
Requested focused-grant addition: a second mutant that swaps only the
invalid-atom escapedText call for vendored state.text, dropping the <&
escape for this path. The existing malformed-attrs browser test must kill
it. The original serializer-removal mutant is still required. No runs,
checks, installation or rebuild have occurred; all remain held for CLEAR.
## Granted execution and candidate, sage-4286, 2026-10-24
Granted lane B until22:40Z. Ran on2ec3f5b5d's source in this worktree.
Every child inherited TMPDIR=/mnt/storage/scratch/tmp/g49X, obtained by
mktemp -d /mnt/storage/scratch/tmp/gXXX, never /tmp. Each command used
`nice -n "$((19 - $(nice)))" timeout -k 10 ...` with pipefail and tee;
raw logs record actual absolute nice19 and MOSAIC_REQUIRE_EDITOR_BUILD=1.
Node test concurrency was1 for each focused whole file and2 for webui.
Measurement22:07:22Z: all10 baseline forms round-trip byte for byte. The
JSON is copied unchanged to measurement.json. Adopted outcome A: no
measured file falls to Source just because of its wikilink. The atom is
proved by structure and same-block edits, not no-edit raw retention.
Baseline structural case:0/1 expected red, no atom in old shipped bundle.
Pinned npm ci --ignore-scripts --no-audit --no-fund succeeded locally in
packages/webui, then editor/build.mjs ran with esbuild0.28.2. The existing
upstream sideEffects warning for mark.ts appeared; no vendored file changed.
No package/lockfile change. Rebuild provenance ran and passed, not skipped.
| Whole file | Pass/fail | Wall seconds |
| --- | ---: | ---: |
| editor-wikilink.test.mjs | 13/0 | 2.348 |
| editor.test.mjs | 5/0 | 2.053 |
| editor-browser.test.mjs | 16/0 | 14.984 |
| editor-provenance.test.mjs | 6/0 | 0.343 |
Two mutants, each independently restored and rebuilt: serializer removal
failed the structural case0/1, restore1/0; invalid-path escapedText swapped
for vendored state.text failed malformed attrs0/1, restore1/0. The model
and generated outputs match their original good hashes after both.
Post-mutant cwd/temp-root sweep0. Both failures are expected evidence.
Full webui: exactly69 files,455/0,0 skipped/cancelled,174.189s, ended
22:12:48.880975Z. The log contains the required-build env and nice19.
Skip scan for ^# skip, TAP # SKIP and spec skipped-test lines found none,
so skip names are []. A preliminary command mistakenly selected7 files;
its28/0 in17.787s is archived as webui-whole.log and explicitly NOT used
as full gate evidence. The corrected69-file run is webui-69.log.
Final cwd/TMPDIR/argv sweep22:13:17.560692Z:0 processes. No owned Node or
Chromium remains. Removed the private TMPDIR only after that zero sweep.
Clear was sent to Sage as herb-0102 at22:13:46Z; no further execution.
Bundle SHA256 bd167899a551c70dc07e500fcdd1853d7fb710b23c217ba5b42daf5785c38a72.
CSS SHA2568654ac57d1e7a561110138ecd7f701c34a074017ff8c4cd2768d8083908ee61f,
byte-identical to2ec3f5b5d. BUILD.json outputs match those hashes. Source
commit a530a60798bf48eb18bb9e225d099faf5c2975cf adds generated outputs and
an append-only phase log; packet changes follow separately. No code or
assertion changed after these results. The webui gate requirement is met
on this tree; Filbert review and every scripts/test-*.sh remain pending.
Sage re-pinned the brief to case2-rows.md blob85e6fe2e under sage-4286;
its C2-11a section is unchanged. No case1-accept file was in any run.
+168
View File
@@ -0,0 +1,168 @@
# 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]]](u) 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 &copy;, 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.
+572
View File
@@ -0,0 +1,572 @@
{
"grant": "sage-4286",
"candidateBeforeBuild": "2ec3f5b5ddab0cb5f2716f8164c25861b741213e",
"nice": 19,
"tmpRoot": "/mnt/storage/scratch/tmp/g49X",
"requireEditorBuild": "1",
"measurement": {
"base": "487500a16f4ab5902c318509fdec6acc56a543ca",
"bundleSha256": "9c881b13740fae2408717ad75fa1d85bfcfe3fa8b3af0f1b3f2e706c90e80fee",
"measuredAt": "2026-10-24T22:07:22.210Z",
"allEqual": true,
"results": [
{
"name": "plain",
"input": "[[a]]\n",
"output": "[[a]]\n",
"equal": true
},
{
"name": "labelled",
"input": "[[a|b]]\n",
"output": "[[a|b]]\n",
"equal": true
},
{
"name": "heading target",
"input": "[[a#h]]\n",
"output": "[[a#h]]\n",
"equal": true
},
{
"name": "list item",
"input": "- [[a]]\n",
"output": "- [[a]]\n",
"equal": true
},
{
"name": "next to front matter",
"input": "---\nrelated: \"[[metadata]]\"\n---\n\n[[a]]\n",
"output": "---\nrelated: \"[[metadata]]\"\n---\n\n[[a]]\n",
"equal": true
},
{
"name": "heading block and combined label",
"input": "# See [[a#h|Title]]\n",
"output": "# See [[a#h|Title]]\n",
"equal": true
},
{
"name": "spaces, Unicode and adjacent links",
"input": "See [[ H\u00e9llo there # A heading | a label ]][[next]]\n",
"output": "See [[ H\u00e9llo there # A heading | a label ]][[next]]\n",
"equal": true
},
{
"name": "reference-definition collision",
"input": "[[a]]\n\n[a]: u\n",
"output": "[[a]]\n\n[a]: u\n",
"equal": true
},
{
"name": "inside an ordinary link",
"input": "[x [[a]]](u)\n",
"output": "[x [[a]]](u)\n",
"equal": true
},
{
"name": "inert embed",
"input": "![[cover.png]]\n",
"output": "![[cover.png]]\n",
"equal": true
}
]
},
"results": [
{
"name": "measurement",
"argv": [
"node",
"agents/herb/work/queue-214/measure.mjs"
],
"cwd": "/home/jwoltje/.t3/worktrees/mosaic-stack/herb-row214-r2",
"rc": 0,
"wallSeconds": 0.093,
"counts": {},
"nice": 19,
"tmpRoot": "/mnt/storage/scratch/tmp/g49X",
"requireEditorBuild": "1",
"timeoutSeconds": 60,
"ended": "2026-10-24T22:07:22.215700+00:00"
},
{
"name": "baseline-red",
"argv": [
"node",
"--test",
"--test-concurrency=1",
"--test-name-pattern=wikilinks are inline atoms and survive",
"packages/webui/tests/editor-wikilink.test.mjs"
],
"cwd": "/home/jwoltje/.t3/worktrees/mosaic-stack/herb-row214-r2",
"rc": 1,
"wallSeconds": 1.151,
"counts": {
"tests": 1,
"pass": 0,
"fail": 1,
"cancelled": 0,
"skipped": 0
},
"nice": 19,
"tmpRoot": "/mnt/storage/scratch/tmp/g49X",
"requireEditorBuild": "1",
"timeoutSeconds": 180,
"ended": "2026-10-24T22:07:27.998436+00:00"
},
{
"name": "npm-ci",
"argv": [
"npm",
"ci",
"--ignore-scripts",
"--no-audit",
"--no-fund"
],
"cwd": "/home/jwoltje/.t3/worktrees/mosaic-stack/herb-row214-r2/packages/webui",
"rc": 0,
"wallSeconds": 0.514,
"counts": {},
"nice": 19,
"tmpRoot": "/mnt/storage/scratch/tmp/g49X",
"requireEditorBuild": "1",
"timeoutSeconds": 240,
"ended": "2026-10-24T22:07:41.534116+00:00"
},
{
"name": "build",
"argv": [
"node",
"editor/build.mjs"
],
"cwd": "/home/jwoltje/.t3/worktrees/mosaic-stack/herb-row214-r2/packages/webui",
"rc": 0,
"wallSeconds": 0.108,
"counts": {},
"nice": 19,
"tmpRoot": "/mnt/storage/scratch/tmp/g49X",
"requireEditorBuild": "1",
"timeoutSeconds": 120,
"ended": "2026-10-24T22:07:44.654385+00:00"
},
{
"name": "wikilink-green",
"argv": [
"node",
"--test",
"--test-concurrency=1",
"packages/webui/tests/editor-wikilink.test.mjs"
],
"cwd": "/home/jwoltje/.t3/worktrees/mosaic-stack/herb-row214-r2",
"rc": 0,
"wallSeconds": 2.348,
"counts": {
"tests": 13,
"pass": 13,
"fail": 0,
"cancelled": 0,
"skipped": 0
},
"nice": 19,
"tmpRoot": "/mnt/storage/scratch/tmp/g49X",
"requireEditorBuild": "1",
"timeoutSeconds": 180,
"ended": "2026-10-24T22:07:52.583345+00:00"
},
{
"name": "editor-green",
"argv": [
"node",
"--test",
"--test-concurrency=1",
"packages/webui/tests/editor.test.mjs"
],
"cwd": "/home/jwoltje/.t3/worktrees/mosaic-stack/herb-row214-r2",
"rc": 0,
"wallSeconds": 2.053,
"counts": {
"tests": 5,
"pass": 5,
"fail": 0,
"cancelled": 0,
"skipped": 0
},
"nice": 19,
"tmpRoot": "/mnt/storage/scratch/tmp/g49X",
"requireEditorBuild": "1",
"timeoutSeconds": 240,
"ended": "2026-10-24T22:07:59.286086+00:00"
},
{
"name": "editor-browser-green",
"argv": [
"node",
"--test",
"--test-concurrency=1",
"packages/webui/tests/editor-browser.test.mjs"
],
"cwd": "/home/jwoltje/.t3/worktrees/mosaic-stack/herb-row214-r2",
"rc": 0,
"wallSeconds": 14.984,
"counts": {
"tests": 16,
"pass": 16,
"fail": 0,
"cancelled": 0,
"skipped": 0
},
"nice": 19,
"tmpRoot": "/mnt/storage/scratch/tmp/g49X",
"requireEditorBuild": "1",
"timeoutSeconds": 240,
"ended": "2026-10-24T22:08:20.029050+00:00"
},
{
"name": "provenance-green",
"argv": [
"node",
"--test",
"--test-concurrency=1",
"packages/webui/tests/editor-provenance.test.mjs"
],
"cwd": "/home/jwoltje/.t3/worktrees/mosaic-stack/herb-row214-r2",
"rc": 0,
"wallSeconds": 0.343,
"counts": {
"tests": 6,
"pass": 6,
"fail": 0,
"cancelled": 0,
"skipped": 0
},
"nice": 19,
"tmpRoot": "/mnt/storage/scratch/tmp/g49X",
"requireEditorBuild": "1",
"timeoutSeconds": 180,
"ended": "2026-10-24T22:08:39.003420+00:00"
},
{
"name": "serializer-removal-build",
"argv": [
"node",
"editor/build.mjs"
],
"cwd": "/home/jwoltje/.t3/worktrees/mosaic-stack/herb-row214-r2/packages/webui",
"rc": 0,
"wallSeconds": 0.093,
"counts": {},
"nice": 19,
"tmpRoot": "/mnt/storage/scratch/tmp/g49X",
"requireEditorBuild": "1",
"timeoutSeconds": 90,
"ended": "2026-10-24T22:08:51.422097+00:00"
},
{
"name": "serializer-removal-red",
"argv": [
"node",
"--test",
"--test-concurrency=1",
"--test-name-pattern=wikilinks are inline atoms and survive",
"packages/webui/tests/editor-wikilink.test.mjs"
],
"cwd": "/home/jwoltje/.t3/worktrees/mosaic-stack/herb-row214-r2",
"rc": 1,
"wallSeconds": 0.887,
"counts": {
"tests": 1,
"pass": 0,
"fail": 1,
"cancelled": 0,
"skipped": 0
},
"nice": 19,
"tmpRoot": "/mnt/storage/scratch/tmp/g49X",
"requireEditorBuild": "1",
"timeoutSeconds": 180,
"ended": "2026-10-24T22:08:52.340936+00:00"
},
{
"name": "serializer-removal-restore-build",
"argv": [
"node",
"editor/build.mjs"
],
"cwd": "/home/jwoltje/.t3/worktrees/mosaic-stack/herb-row214-r2/packages/webui",
"rc": 0,
"wallSeconds": 0.115,
"counts": {},
"nice": 19,
"tmpRoot": "/mnt/storage/scratch/tmp/g49X",
"requireEditorBuild": "1",
"timeoutSeconds": 90,
"ended": "2026-10-24T22:08:52.498613+00:00"
},
{
"name": "serializer-removal-restore-green",
"argv": [
"node",
"--test",
"--test-concurrency=1",
"--test-name-pattern=wikilinks are inline atoms and survive",
"packages/webui/tests/editor-wikilink.test.mjs"
],
"cwd": "/home/jwoltje/.t3/worktrees/mosaic-stack/herb-row214-r2",
"rc": 0,
"wallSeconds": 0.979,
"counts": {
"tests": 1,
"pass": 1,
"fail": 0,
"cancelled": 0,
"skipped": 0
},
"nice": 19,
"tmpRoot": "/mnt/storage/scratch/tmp/g49X",
"requireEditorBuild": "1",
"timeoutSeconds": 180,
"ended": "2026-10-24T22:08:53.514962+00:00"
},
{
"name": "invalid-escaping-build",
"argv": [
"node",
"editor/build.mjs"
],
"cwd": "/home/jwoltje/.t3/worktrees/mosaic-stack/herb-row214-r2/packages/webui",
"rc": 0,
"wallSeconds": 0.102,
"counts": {},
"nice": 19,
"tmpRoot": "/mnt/storage/scratch/tmp/g49X",
"requireEditorBuild": "1",
"timeoutSeconds": 90,
"ended": "2026-10-24T22:08:53.655583+00:00"
},
{
"name": "invalid-escaping-red",
"argv": [
"node",
"--test",
"--test-concurrency=1",
"--test-name-pattern=wikilink DOM paste accepts",
"packages/webui/tests/editor-wikilink.test.mjs"
],
"cwd": "/home/jwoltje/.t3/worktrees/mosaic-stack/herb-row214-r2",
"rc": 1,
"wallSeconds": 0.979,
"counts": {
"tests": 1,
"pass": 0,
"fail": 1,
"cancelled": 0,
"skipped": 0
},
"nice": 19,
"tmpRoot": "/mnt/storage/scratch/tmp/g49X",
"requireEditorBuild": "1",
"timeoutSeconds": 180,
"ended": "2026-10-24T22:08:54.668225+00:00"
},
{
"name": "invalid-escaping-restore-build",
"argv": [
"node",
"editor/build.mjs"
],
"cwd": "/home/jwoltje/.t3/worktrees/mosaic-stack/herb-row214-r2/packages/webui",
"rc": 0,
"wallSeconds": 0.105,
"counts": {},
"nice": 19,
"tmpRoot": "/mnt/storage/scratch/tmp/g49X",
"requireEditorBuild": "1",
"timeoutSeconds": 90,
"ended": "2026-10-24T22:08:54.809629+00:00"
},
{
"name": "invalid-escaping-restore-green",
"argv": [
"node",
"--test",
"--test-concurrency=1",
"--test-name-pattern=wikilink DOM paste accepts",
"packages/webui/tests/editor-wikilink.test.mjs"
],
"cwd": "/home/jwoltje/.t3/worktrees/mosaic-stack/herb-row214-r2",
"rc": 0,
"wallSeconds": 0.985,
"counts": {
"tests": 1,
"pass": 1,
"fail": 0,
"cancelled": 0,
"skipped": 0
},
"nice": 19,
"tmpRoot": "/mnt/storage/scratch/tmp/g49X",
"requireEditorBuild": "1",
"timeoutSeconds": 180,
"ended": "2026-10-24T22:08:55.831793+00:00"
},
{
"name": "webui-whole",
"argv": [
"node",
"--test",
"--test-concurrency=2",
"tests/agents-browser.test.mjs",
"tests/agents.test.mjs",
"tests/board-held.test.mjs",
"tests/browser-edge.test.mjs",
"tests/browser.test.mjs",
"tests/bus-browser.test.mjs",
"tests/bus.test.mjs"
],
"cwd": "/home/jwoltje/.t3/worktrees/mosaic-stack/herb-row214-r2/packages/webui",
"rc": 0,
"wallSeconds": 17.787,
"counts": {
"tests": 28,
"pass": 28,
"fail": 0,
"cancelled": 0,
"skipped": 0
},
"nice": 19,
"tmpRoot": "/mnt/storage/scratch/tmp/g49X",
"requireEditorBuild": "1",
"timeoutSeconds": 600,
"ended": "2026-10-24T22:09:39.039897+00:00"
},
{
"name": "webui-69",
"argv": [
"node",
"--test",
"--test-concurrency=2",
"tests/agents-browser.test.mjs",
"tests/agents.test.mjs",
"tests/assistant.test.mjs",
"tests/board-held.test.mjs",
"tests/browser-edge.test.mjs",
"tests/browser.test.mjs",
"tests/bus-browser.test.mjs",
"tests/bus.test.mjs",
"tests/channel-chat.test.mjs",
"tests/chat-browser.test.mjs",
"tests/chat-instances.test.mjs",
"tests/chat-replies-browser.test.mjs",
"tests/chat-sessions-access.test.mjs",
"tests/chat.test.mjs",
"tests/conversation.test.mjs",
"tests/decide-browser.test.mjs",
"tests/decide-sso.test.mjs",
"tests/decision-http.test.mjs",
"tests/discord.test.mjs",
"tests/docs-browser.test.mjs",
"tests/editor-browser.test.mjs",
"tests/editor-provenance.test.mjs",
"tests/editor-wikilink.test.mjs",
"tests/editor.test.mjs",
"tests/granted-user.test.mjs",
"tests/history-return-flow.test.mjs",
"tests/instances-browser.test.mjs",
"tests/instances.test.mjs",
"tests/library-cli.test.mjs",
"tests/library-routes.test.mjs",
"tests/library-status.test.mjs",
"tests/limits-browser.test.mjs",
"tests/limits-route.test.mjs",
"tests/markdown-preview.test.mjs",
"tests/messages.test.mjs",
"tests/pinned-copy.test.mjs",
"tests/preview.test.mjs",
"tests/processing-browser.test.mjs",
"tests/processing-ui.test.mjs",
"tests/processing.test.mjs",
"tests/project-file.test.mjs",
"tests/project-name-repo.test.mjs",
"tests/proof-cli.test.mjs",
"tests/proof.test.mjs",
"tests/reads.test.mjs",
"tests/relaunch.test.mjs",
"tests/return-flow.test.mjs",
"tests/routes-browser.test.mjs",
"tests/routes.test.mjs",
"tests/send-browser.test.mjs",
"tests/send.test.mjs",
"tests/serve.test.mjs",
"tests/service.test.mjs",
"tests/settings-form.test.mjs",
"tests/settings-write.test.mjs",
"tests/shell.test.mjs",
"tests/sso-browser.test.mjs",
"tests/sso.test.mjs",
"tests/tester-browser.test.mjs",
"tests/tester-cli.test.mjs",
"tests/tester-listener.test.mjs",
"tests/tester-socket.test.mjs",
"tests/thread-claims-browser.test.mjs",
"tests/thread-claims.test.mjs",
"tests/thread-sharing.test.mjs",
"tests/usage-browser.test.mjs",
"tests/usage.test.mjs",
"tests/user-option-swap.test.mjs",
"tests/views.test.mjs"
],
"cwd": "/home/jwoltje/.t3/worktrees/mosaic-stack/herb-row214-r2/packages/webui",
"rc": 0,
"wallSeconds": 174.189,
"counts": {
"tests": 455,
"pass": 455,
"fail": 0,
"cancelled": 0,
"skipped": 0
},
"nice": 19,
"tmpRoot": "/mnt/storage/scratch/tmp/g49X",
"requireEditorBuild": "1",
"timeoutSeconds": 600,
"ended": "2026-10-24T22:12:48.880975+00:00"
}
],
"mutants": [
{
"name": "serializer-removal",
"killed": true,
"restored": true,
"restoredHashesEqual": true
},
{
"name": "invalid-escaping",
"killed": true,
"restored": true,
"restoredHashesEqual": true
}
],
"skipNames": [],
"sweep": {
"at": "2026-10-24T22:13:17.560692+00:00",
"count": 0,
"processes": []
},
"generated": {
"src/public/editor.js": {
"sha256": "bd167899a551c70dc07e500fcdd1853d7fb710b23c217ba5b42daf5785c38a72",
"bytes": 759959
},
"src/public/editor.css": {
"sha256": "8654ac57d1e7a561110138ecd7f701c34a074017ff8c4cd2768d8083908ee61f",
"bytes": 4462
}
},
"unchangedCss": true,
"modelRestored": true,
"notes": [
"webui-whole was an accidental seven-file subset, 28/0, not gate evidence; superseded by webui-69 455/0",
"no scripts/test-*.sh ran in this grant; later gate remains pending"
],
"tmpRemoved": true
}
+323
View File
@@ -0,0 +1,323 @@
{
"base": "487500a16f4ab5902c318509fdec6acc56a543ca",
"testFilesScanned": 495,
"plannedPaths": [
"packages/webui/editor/model.mjs",
"packages/webui/tests/editor-wikilink.test.mjs",
"packages/webui/tests/helpers/wikilink-cases.mjs",
"agents/herb/work/queue-214/measure.mjs",
"agents/herb/work/queue-214/DESIGN.md",
"agents/herb/work/queue-214/MANIFEST.md",
"agents/herb/work/queue-214/BUILD.md",
"packages/webui/src/public/editor.js",
"packages/webui/editor/BUILD.json",
"agents/herb/work/queue-214/HASH-PINS.json"
],
"literalPriorBlobPins": [],
"pathReferences": [
{
"test": "packages/webui/tests/editor-wikilink.test.mjs",
"line": 6,
"text": "import { WIKILINK_CASES } from './helpers/wikilink-cases.mjs';"
},
{
"test": "packages/webui/tests/browser.test.mjs",
"line": 21,
"text": "// Rows 129, 154, 191, 194 and 294: chat.js, editor.js, processing.js, agents.js and federation.js are the scripts added, same origin; the list is the pin, so another script is a visible change."
},
{
"test": "packages/webui/tests/browser.test.mjs",
"line": 22,
"text": "assert.deepEqual(await b.evaluate('[...document.querySelectorAll(\"script\")].map(s => s.getAttribute(\"src\"))'), ['/editor.js', '/processing.js', '/federation.js', '/brand.js', '/app.js', '/markdown-preview.js', '/chat.js', '/agents.js', '/bus.js']);"
},
{
"test": "packages/webui/tests/sso.test.mjs",
"line": 1,
"text": "// Q175 phase 1 (#1690, design agents/sage/work/q175-sso/DESIGN.md section 9): the Console as an"
},
{
"test": "packages/webui/tests/user-option-swap.test.mjs",
"line": 133,
"text": "['GET', '/app.js'], ['GET', '/bus.js'], ['GET', '/agents.js'], ['GET', '/editor.js'], ['GET', '/shared/app.css'], ['GET', '/index.html'],"
},
{
"test": "packages/webui/tests/serve.test.mjs",
"line": 38,
"text": "for (const path of ['/', '/app.js', '/brand.js', '/console.css', '/live.css', '/editor.js', '/editor.css', '/shared/app.css', '/assets/fonts/manrope-400.woff2']) {"
},
{
"test": "packages/webui/tests/editor-provenance.test.mjs",
"line": 18,
"text": "const build = JSON.parse(readFileSync(join(webui, 'editor/BUILD.json'), 'utf8'));"
},
{
"test": "packages/webui/tests/editor-provenance.test.mjs",
"line": 92,
"text": "const bundle = readFileSync(join(webui, 'src/public/editor.js'), 'utf8');"
},
{
"test": "packages/webui/tests/editor-provenance.test.mjs",
"line": 167,
"text": "const info = await run(out, join(out, 'BUILD.json'));"
},
{
"test": "packages/webui/tests/editor-provenance.test.mjs",
"line": 171,
"text": "assert.equal(readFileSync(join(out, 'BUILD.json'), 'utf8'), readFileSync(join(webui, 'editor/BUILD.json'), 'utf8'));"
},
{
"test": "packages/webui/tests/editor-browser.test.mjs",
"line": 103,
"text": "assert.equal(await h.q('typeof window.MosaicEditor.mount'), 'function', 'editor.js ran under script-src self');"
},
{
"test": "packages/webui/tests/editor-browser.test.mjs",
"line": 423,
"text": "// (The editor's own reading of a file can give a single line break in a title or alt, never a blank line; see BUILD.md.)"
},
{
"test": "packages/webui/tests/granted-user.test.mjs",
"line": 231,
"text": "['GET', '/app.js'], ['GET', '/bus.js'], ['GET', '/agents.js'], ['GET', '/editor.js'], ['GET', '/shared/app.css'], ['GET', '/index.html'],"
},
{
"test": "packages/webui/tests/editor.test.mjs",
"line": 20,
"text": "for (const [path, type] of [['/editor.js', 'text/javascript'], ['/editor.css', 'text/css']]) {"
},
{
"test": "packages/webui/tests/editor.test.mjs",
"line": 29,
"text": "assert.ok(index.indexOf('src=\"/editor.js\"') < index.indexOf('src=\"/bus.js\"'), 'the editor loads before the page script, so bus.js can mount it');"
},
{
"test": "packages/webui/tests/editor.test.mjs",
"line": 60,
"text": "for (const file of ['entry.mjs', 'model.mjs', 'view.mjs']) {"
},
{
"test": "packages/webui/tests/editor.test.mjs",
"line": 64,
"text": "const bundle = readFileSync(join(webui, 'src/public/editor.js'), 'utf8');"
},
{
"test": "packages/webui/tests/editor.test.mjs",
"line": 65,
"text": "assert.doesNotMatch(bundle, /\\beval\\s*\\(|\\bnew\\s+Function\\b/, 'src/public/editor.js');"
},
{
"test": "packages/webui/tests/editor.test.mjs",
"line": 69,
"text": "const model = readFileSync(join(webui, 'editor/model.mjs'), 'utf8');"
},
{
"test": "packages/webui/tests/helpers/editor-bundle.mjs",
"line": 1,
"text": "// Loads src/public/editor.js (and markdown-preview.js, the link gate it needs) into a vm context with a"
},
{
"test": "packages/webui/tests/helpers/editor-bundle.mjs",
"line": 15,
"text": "for (const f of ['markdown-preview.js', 'editor.js']) vm.runInContext(readFileSync(join(pub, f), 'utf8'), sandbox, { filename: f });"
},
{
"test": "packages/plugins/tests/harness.test.mjs",
"line": 44,
"text": "// pi 0.85.1 --thinking and claude 2.1.302 --effort, as probed (BUILD.md); claude has no off or minimal."
},
{
"test": "packages/bus/tests/revoke-window.test.mjs",
"line": 4,
"text": "// a limit stub and a usage stub for the limit stop. BUILD.md (agents/morgana/work/queue-306) names"
},
{
"test": "packages/bus/tests/access-revoke.test.mjs",
"line": 5,
"text": "// lstat key; `edit` changes the document and the stamp together, as a hand edit does. BUILD.md"
},
{
"test": "packages/bus/tests/land.test.mjs",
"line": 410,
"text": "// landHeld still counts it, so it stays listed (BUILD.md follow-up 5)."
},
{
"test": "packages/bus/tests/session-clear.test.mjs",
"line": 13,
"text": "// docs/plans/2026-10-21_t1-sessions.md, the contract in agents/gizmoduck/work/queue-271/BUILD.md."
},
{
"test": "packages/bus/tests/grant-executor.test.mjs",
"line": 1027,
"text": "// credential-unavailable; BUILD.md records the difference)."
},
{
"test": "packages/bus/tests/host-run.test.mjs",
"line": 343,
"text": "// the options are checked here and the call runs with an alias no user file holds (BUILD.md)."
},
{
"test": "packages/business/tests/workflow-roles.test.mjs",
"line": 123,
"text": "// of staff launches it. The broker and launcher states are row 137's tests (BUILD.md, round 3)."
},
{
"test": "packages/business/tests/access.test.mjs",
"line": 1,
"text": "// FED R1 (#414, row 201): the access file loader and validator, DESIGN.md"
},
{
"test": "packages/cli/tests/revoke-window.test.mjs",
"line": 9,
"text": "// poll, with threadsState replaced by the caller. BUILD.md (agents/morgana/work/queue-306) names the"
},
{
"test": "packages/cli/tests/stack-instances.test.mjs",
"line": 3,
"text": "// T table: agents/darkwing/work/queue-190/DESIGN.md D7. The unit, port and release cases are T56 in"
},
{
"test": "packages/cli/tests/session-launch.test.mjs",
"line": 297,
"text": "// Q2 (BUILD.md:177-179, t1-sessions.md:109): an earlier host's sessionless run of the instance is"
},
{
"test": "packages/cli/tests/case1-accept.test.mjs",
"line": 2,
"text": "// (agents/darkwing/work/queue-195/DESIGN.md); deviations in agents/darkwing/work/queue-195/BUILD.md."
},
{
"test": "packages/cli/tests/stack-release.test.mjs",
"line": 4,
"text": "// agents/darkwing/work/queue-162/BUILD.md. Every run uses a temp data root, temp units and stand-in"
},
{
"test": "packages/cli/tests/stack-release.test.mjs",
"line": 915,
"text": "// COUNTED or dropped from it shows here. Row 181's BUILD.md names each writer."
},
{
"test": "packages/cli/tests/stack-release.test.mjs",
"line": 928,
"text": "task_comment_cursors:'no time column; a restore drops its advances uncounted (BUILD.md)',"
},
{
"test": "packages/cli/tests/stack-release.test.mjs",
"line": 1668,
"text": "// refusal is the guard for a file that changes after that (BUILD.md, round 2)."
},
{
"test": "packages/cli/tests/stack-release.test.mjs",
"line": 2331,
"text": "// agents/darkwing/work/queue-190/DESIGN.md. T-I1, T-I2, T-I5, T-I10 and the loader half of T-I7 are in"
},
{
"test": "packages/cli/tests/publish-socket.test.mjs",
"line": 236,
"text": "// sage-1440 on BUILD.md 14: a push between checkPublish and the git push refuses, with one code"
},
{
"test": "packages/cli/tests/stack-upgrade.test.mjs",
"line": 3,
"text": "// agents/darkwing/work/queue-195/DESIGN.md, as the I3 brief changes them; deviations:"
},
{
"test": "packages/cli/tests/stack-upgrade.test.mjs",
"line": 4,
"text": "// agents/darkwing/work/queue-233/BUILD.md. The stand-ins and the module fixture are"
},
{
"test": "packages/cli/tests/stack-release-external.test.mjs",
"line": 2,
"text": "// test list, agents/darkwing/work/queue-195/DESIGN.md; deviations: agents/darkwing/work/queue-232/BUILD.md."
},
{
"test": "packages/cli/tests/case1-hostcheck.test.mjs",
"line": 3,
"text": "// (agents/darkwing/work/queue-195/DESIGN.md at b7239bb66). Every case copies the script into a scratch"
},
{
"test": "packages/cli/tests/thread-revoke.test.mjs",
"line": 5,
"text": "// The bus half is packages/bus/tests/access-revoke.test.mjs; BUILD.md"
},
{
"test": "packages/cli/tests/access.test.mjs",
"line": 308,
"text": "// outside R5 (BUILD.md). The test pins it, and nothing is written."
},
{
"test": "packages/cli/tests/access.test.mjs",
"line": 316,
"text": "// Row 193c (#1705): `user add` and `user remove`. BUILD.md (agents/megavolt/work/queue-207) names"
},
{
"test": "packages/cli/tests/access.test.mjs",
"line": 455,
"text": "// refuses access.reload alone, as a bus would refuse a call it can't take. BUILD.md"
},
{
"test": "packages/cli/tests/launcher.test.mjs",
"line": 799,
"text": "// The run directory exists by then (BUILD.md residual); the persona is read before any bundle file."
},
{
"test": "packages/cli/tests/lifecycle.test.mjs",
"line": 778,
"text": "// N6 (sage-2246, DESIGN.md:118-120): an end signals the runs the verb named and the instance's runs"
}
],
"row266": {
"ref": "origin/fethry/row266",
"tip": "6418ec063389728e0d8883c94812b12d2074eb43",
"diffBase": "487500a16f4ab5902c318509fdec6acc56a543ca",
"overlap": []
},
"interpretation": "No literal SHA256 or Git-blob hash pins found for planned changed files. Editor provenance compares generated output to BUILD.json and requires regeneration/rebuild, not an external fixed hash.",
"postBuildAudit": {
"paths": [
"BUILD-LOG.md",
"agents/herb/work/queue-214/BUILD.md",
"agents/herb/work/queue-214/DESIGN.md",
"agents/herb/work/queue-214/EVIDENCE.json",
"agents/herb/work/queue-214/HASH-PINS.json",
"agents/herb/work/queue-214/MANIFEST.md",
"agents/herb/work/queue-214/SOURCE.json",
"agents/herb/work/queue-214/granted-4286/baseline-red.log",
"agents/herb/work/queue-214/granted-4286/build.log",
"agents/herb/work/queue-214/granted-4286/editor-browser-green.log",
"agents/herb/work/queue-214/granted-4286/editor-green.log",
"agents/herb/work/queue-214/granted-4286/final-sweep.json",
"agents/herb/work/queue-214/granted-4286/invalid-escaping-build.log",
"agents/herb/work/queue-214/granted-4286/invalid-escaping-red.log",
"agents/herb/work/queue-214/granted-4286/invalid-escaping-restore-build.log",
"agents/herb/work/queue-214/granted-4286/invalid-escaping-restore-green.log",
"agents/herb/work/queue-214/granted-4286/measurement.log",
"agents/herb/work/queue-214/granted-4286/mutants.json",
"agents/herb/work/queue-214/granted-4286/mutants.py",
"agents/herb/work/queue-214/granted-4286/npm-ci.log",
"agents/herb/work/queue-214/granted-4286/post-mutant-sweep.json",
"agents/herb/work/queue-214/granted-4286/provenance-green.log",
"agents/herb/work/queue-214/granted-4286/results.jsonl",
"agents/herb/work/queue-214/granted-4286/serializer-removal-build.log",
"agents/herb/work/queue-214/granted-4286/serializer-removal-red.log",
"agents/herb/work/queue-214/granted-4286/serializer-removal-restore-build.log",
"agents/herb/work/queue-214/granted-4286/serializer-removal-restore-green.log",
"agents/herb/work/queue-214/granted-4286/settings.json",
"agents/herb/work/queue-214/granted-4286/skips.json",
"agents/herb/work/queue-214/granted-4286/step.py",
"agents/herb/work/queue-214/granted-4286/webui-69.log",
"agents/herb/work/queue-214/granted-4286/webui-files.txt",
"agents/herb/work/queue-214/granted-4286/webui-whole.log",
"agents/herb/work/queue-214/granted-4286/wikilink-green.log",
"agents/herb/work/queue-214/measure.mjs",
"agents/herb/work/queue-214/measurement.json",
"packages/webui/editor/BUILD.json",
"packages/webui/editor/model.mjs",
"packages/webui/src/public/editor.js",
"packages/webui/tests/editor-wikilink.test.mjs",
"packages/webui/tests/helpers/wikilink-cases.mjs"
],
"literalCurrentOrPriorHashPins": [],
"buildOutputsValidated": true
}
}
+42
View File
@@ -0,0 +1,42 @@
# Row214 candidate manifest
Owner: Herb. Reviewer: Filbert. Issue#1715, C2-11a. Execution base
487500a16f4ab5902c318509fdec6acc56a543ca on herb/row214-r2. Current
brief blob85e6fe2e has the same C2-11a section as the accepted old pin.
Source commit a530a60798bf48eb18bb9e225d099faf5c2975cf.
| Path | Final change |
| --- | --- |
| packages/webui/editor/model.mjs | inert inline atom, heading content, tokenizer and validated serializer; invalid attrs share complete text escaping |
| packages/webui/tests/editor-wikilink.test.mjs | ten byte round-trips and three browser regressions, including same-block edits and malformed attrs |
| packages/webui/tests/helpers/wikilink-cases.mjs | exact fixtures shared with measurement |
| packages/webui/src/public/editor.js | generated with pinned esbuild0.28.2 |
| packages/webui/editor/BUILD.json | generated provenance and output hashes |
| BUILD-LOG.md | one append-only phase receipt |
| agents/herb/work/queue-214/measure.mjs | baseline measurement from pinned Git blobs |
| agents/herb/work/queue-214/measurement.json | unchanged measurement JSON,10/10 equal |
| agents/herb/work/queue-214/DESIGN.md | accepted design, both outcomes and static review follow-through |
| agents/herb/work/queue-214/BUILD.md | rulings, actual granted commands, timings and limits |
| agents/herb/work/queue-214/HASH-PINS.json | static hash-pin/row266 audit, repeated after build |
| agents/herb/work/queue-214/SOURCE.json | actual source/output hashes and unchanged CSS dependency |
| agents/herb/work/queue-214/EVIDENCE.json | grant, exact argv/counts, measurement, mutants, skips and sweeps |
| agents/herb/work/queue-214/granted-4286/ | raw tee logs, commands, file list and sweep/skip/mutant records |
The CSS output is byte-identical and matches BUILD.json. No entry/view,
third_party, dependency/lockfile, library, route, save, click or documentPath
change. No row266 chat path changes. No test assertion changed after runs.
Outcome A: all ten measured no-edit cases were already preserved by the
baseline's raw/sig behavior. Their green result is compatibility evidence,
not proof of the node. The baseline structural case was0/1 expected red.
Grant sage-4286: four whole editor files40/0; whole webui69 files455/0
in174.189s; zero skips/cancellations. Both named mutants killed0/1,
each restored/rebuilt and focused1/0 green. Absolute nice19, required-build
env and short scratch TMPDIR are in raw logs. Provenance rebuilt, not
skipped. Final process sweep0, TMPDIR removed. The mistaken seven-file
subset28/0 is archived but excluded from gate evidence.
The candidate hash manifest is rebuilt from the pushed tip outside its
own path set. Filbert's candidate review and every scripts/test-*.sh gate
remain pending. No Case1 acceptance test was run. The row is not done.
+49
View File
@@ -0,0 +1,49 @@
{
"base": "487500a16f4ab5902c318509fdec6acc56a543ca",
"status": "focused and 69-file webui green under sage-4286; candidate review and shell gate pending",
"sourcePaths": [
{
"path": "packages/webui/editor/model.mjs",
"mode": "100644",
"sha256": "81ac44d852cb54213a9644ebbde26630ec49614e468f376d71b471395e3dd9f7"
},
{
"path": "packages/webui/tests/editor-wikilink.test.mjs",
"mode": "100644",
"sha256": "e144e7dbf23c68c136eef8f0fb2e8979c27e908c7cc14ee4ab4f722220e67d77"
},
{
"path": "packages/webui/tests/helpers/wikilink-cases.mjs",
"mode": "100644",
"sha256": "afa4dc7188548457b2d1966b9437561ba411d7bc9aaaa38bff626c235a64e19c"
},
{
"path": "agents/herb/work/queue-214/measure.mjs",
"mode": "100644",
"sha256": "f81987cdae5c4f7e5ce353162674b3c3510e44f22d6d3beb262a5d198db4e88d"
},
{
"path": "packages/webui/src/public/editor.js",
"mode": "100644",
"sha256": "bd167899a551c70dc07e500fcdd1853d7fb710b23c217ba5b42daf5785c38a72"
},
{
"path": "packages/webui/editor/BUILD.json",
"mode": "100644",
"sha256": "4d73af728cea60eaa6fadb4650b9d42b663c90ef7f59bb2b787e1f3d9acd8278"
},
{
"path": "BUILD-LOG.md",
"mode": "100644",
"sha256": "30cc179e2b76c5aceeec35d5ab20339988241a4cb04d26d1cc9b2a5437796236"
}
],
"pendingGeneratedPaths": [],
"unchangedGeneratedCss": {
"path": "packages/webui/src/public/editor.css",
"sha256": "8654ac57d1e7a561110138ecd7f701c34a074017ff8c4cd2768d8083908ee61f",
"mode": "100644"
},
"runtimeEvidence": "agents/herb/work/queue-214/EVIDENCE.json",
"sourceCommit": "a530a60798bf48eb18bb9e225d099faf5c2975cf"
}
@@ -0,0 +1,38 @@
# grant sage-4286; nice 19; TMPDIR /mnt/storage/scratch/tmp/g49X; MOSAIC_REQUIRE_EDITOR_BUILD 1
✖ wikilinks are inline atoms and survive a neighbouring edit in every required context (932.607342ms)
ℹ tests 1
ℹ suites 0
ℹ pass 0
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 1108.548729
✖ failing tests:
test at packages/webui/tests/editor-wikilink.test.mjs:35:1
✖ wikilinks are inline atoms and survive a neighbouring edit in every required context (932.607342ms)
AssertionError [ERR_ASSERTION]: plain
+ actual - expected
+ []
- [
- {
- atom: true,
- inline: true,
- source: '[[a]]'
- }
- ]
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/herb-row214-r2/packages/webui/tests/editor-wikilink.test.mjs:39:12)
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
at async Test.run (node:internal/test_runner/test:1409:7)
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: [],
expected: [ { source: '[[a]]', atom: true, inline: true } ],
operator: 'deepStrictEqual',
diff: 'simple'
}
@@ -0,0 +1,15 @@
# grant sage-4286; nice 19; TMPDIR /mnt/storage/scratch/tmp/g49X; MOSAIC_REQUIRE_EDITOR_BUILD 1
▲ [WARNING] Ignoring this import because "third_party/editor/prosemirror-transform/src/mark.ts" was marked as having no side effects [ignored-bare-import]
third_party/editor/prosemirror-transform/src/index.ts:10:7:
10 │ import "./mark"
╵ ~~~~~~~~
"sideEffects" is false in the enclosing "package.json" file:
third_party/editor/prosemirror-transform/package.json:13:2:
13 │ "sideEffects": false,
╵ ~~~~~~~~~~~~~
editor.js bd167899a551c70dc07e500fcdd1853d7fb710b23c217ba5b42daf5785c38a72 (133 inputs)
editor.css 8654ac57d1e7a561110138ecd7f701c34a074017ff8c4cd2768d8083908ee61f
@@ -0,0 +1,25 @@
# grant sage-4286; nice 19; TMPDIR /mnt/storage/scratch/tmp/g49X; MOSAIC_REQUIRE_EDITOR_BUILD 1
✔ the Console's CSP stands and the editor runs under it; hostile markdown on the load path runs and loads nothing (928.130513ms)
✔ hostile content pasted into the editor runs and loads nothing, and a bad link target is refused in the link bar (803.464018ms)
✔ a pasted image span is kept only for a target the link gate accepts, and a save writes only those (635.018754ms)
✔ a switch of view reads nothing; a reload, and a trip away and back, each read the file exactly once (2555.185634ms)
✔ an edit rewrites only the block touched; a save sends the rest byte for byte; the Source view carries the draft (1077.608366ms)
✔ a file the editor cannot write back byte for byte opens as Source, says so, cannot save, and the Visual button works once it can (2053.81983ms)
✔ a link title pasted with line breaks in it is written on one line, so no HTML block comes of it (761.717564ms)
✔ an image alt pasted with line breaks in it is written on one line, so no HTML block comes of it (677.209658ms)
✔ an image title pasted with line breaks in it is written on one line, so no HTML block comes of it (681.501055ms)
✔ a code block info string pasted with line breaks in it is written on one line, so no HTML block comes of it (664.552078ms)
✔ a link title holding a quote after a backslash then a tag is written with the backslash escaped, so the title stays closed (653.763504ms)
✔ an image title holding a quote after a backslash then a tag is written with the backslash escaped, so the title stays closed (680.145608ms)
✔ a link title holding a trailing backslash is written with the backslash escaped, so the title stays closed (681.870693ms)
✔ an image title holding a trailing backslash is written with the backslash escaped, so the title stays closed (686.810545ms)
✔ a code block info string with a backtick, pasted, is dropped on Save, so the fence stays closed (631.067849ms)
✔ a code block info string with a backtick, as a tilde fence can have, is kept as source, not rewritten without it (631.071266ms)
ℹ tests 16
ℹ suites 0
ℹ pass 16
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 14945.374491
@@ -0,0 +1,14 @@
# grant sage-4286; nice 19; TMPDIR /mnt/storage/scratch/tmp/g49X; MOSAIC_REQUIRE_EDITOR_BUILD 1
✔ the editor routes serve the built files with their types and no cache (36.796766ms)
✔ open and save with no edit writes the same bytes: the goals review and a doc with every element (16.912796ms)
✔ open and save with no edit writes the same bytes: every repository doc under docs/ and spec/ (1791.779235ms)
✔ the editor source builds markup only with DOM calls, and the bundle has no eval (2.568423ms)
✔ the link gate and the image rule are the preview's (0.573105ms)
ℹ tests 5
ℹ suites 0
ℹ pass 5
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 2016.291596
@@ -0,0 +1,5 @@
{
"at": "2026-10-24T22:13:17.560692+00:00",
"count": 0,
"processes": []
}
@@ -0,0 +1,15 @@
# grant sage-4286; nice 19; TMPDIR /mnt/storage/scratch/tmp/g49X; MOSAIC_REQUIRE_EDITOR_BUILD 1
▲ [WARNING] Ignoring this import because "third_party/editor/prosemirror-transform/src/mark.ts" was marked as having no side effects [ignored-bare-import]
third_party/editor/prosemirror-transform/src/index.ts:10:7:
10 │ import "./mark"
╵ ~~~~~~~~
"sideEffects" is false in the enclosing "package.json" file:
third_party/editor/prosemirror-transform/package.json:13:2:
13 │ "sideEffects": false,
╵ ~~~~~~~~~~~~~
editor.js 52c00310b3c32b2f5ca99243f9bbe54f8a76ef0a87f597d8a2220cf1baaa8adc (133 inputs)
editor.css 8654ac57d1e7a561110138ecd7f701c34a074017ff8c4cd2768d8083908ee61f
@@ -0,0 +1,30 @@
# grant sage-4286; nice 19; TMPDIR /mnt/storage/scratch/tmp/g49X; MOSAIC_REQUIRE_EDITOR_BUILD 1
✖ wikilink DOM paste accepts only bounded single-line inert syntax; malformed attrs serialise as escaped text (780.97333ms)
ℹ tests 1
ℹ suites 0
ℹ pass 0
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 939.392901
✖ failing tests:
test at packages/webui/tests/editor-wikilink.test.mjs:65:1
✖ wikilink DOM paste accepts only bounded single-line inert syntax; malformed attrs serialise as escaped text (780.97333ms)
AssertionError [ERR_ASSERTION]: The input was expected to not match the regular expression /(?<!\\)<img/. Input:
'\\[\\[a\\]\\] <img src=x onerror=alert(1)>&copy;\n'
at TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/herb-row214-r2/packages/webui/tests/editor-wikilink.test.mjs:78:10)
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
at async Test.run (node:internal/test_runner/test:1409:7)
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: '\\[\\[a\\]\\] <img src=x onerror=alert(1)>&copy;\n',
expected: /(?<!\\)<img/,
operator: 'doesNotMatch',
diff: 'simple'
}
@@ -0,0 +1,15 @@
# grant sage-4286; nice 19; TMPDIR /mnt/storage/scratch/tmp/g49X; MOSAIC_REQUIRE_EDITOR_BUILD 1
▲ [WARNING] Ignoring this import because "third_party/editor/prosemirror-transform/src/mark.ts" was marked as having no side effects [ignored-bare-import]
third_party/editor/prosemirror-transform/src/index.ts:10:7:
10 │ import "./mark"
╵ ~~~~~~~~
"sideEffects" is false in the enclosing "package.json" file:
third_party/editor/prosemirror-transform/package.json:13:2:
13 │ "sideEffects": false,
╵ ~~~~~~~~~~~~~
editor.js bd167899a551c70dc07e500fcdd1853d7fb710b23c217ba5b42daf5785c38a72 (133 inputs)
editor.css 8654ac57d1e7a561110138ecd7f701c34a074017ff8c4cd2768d8083908ee61f
@@ -0,0 +1,10 @@
# grant sage-4286; nice 19; TMPDIR /mnt/storage/scratch/tmp/g49X; MOSAIC_REQUIRE_EDITOR_BUILD 1
✔ wikilink DOM paste accepts only bounded single-line inert syntax; malformed attrs serialise as escaped text (752.4905ms)
ℹ tests 1
ℹ suites 0
ℹ pass 1
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 941.178679
@@ -0,0 +1,68 @@
{
"base": "487500a16f4ab5902c318509fdec6acc56a543ca",
"bundleSha256": "9c881b13740fae2408717ad75fa1d85bfcfe3fa8b3af0f1b3f2e706c90e80fee",
"measuredAt": "2026-10-24T22:07:22.210Z",
"allEqual": true,
"results": [
{
"name": "plain",
"input": "[[a]]\n",
"output": "[[a]]\n",
"equal": true
},
{
"name": "labelled",
"input": "[[a|b]]\n",
"output": "[[a|b]]\n",
"equal": true
},
{
"name": "heading target",
"input": "[[a#h]]\n",
"output": "[[a#h]]\n",
"equal": true
},
{
"name": "list item",
"input": "- [[a]]\n",
"output": "- [[a]]\n",
"equal": true
},
{
"name": "next to front matter",
"input": "---\nrelated: \"[[metadata]]\"\n---\n\n[[a]]\n",
"output": "---\nrelated: \"[[metadata]]\"\n---\n\n[[a]]\n",
"equal": true
},
{
"name": "heading block and combined label",
"input": "# See [[a#h|Title]]\n",
"output": "# See [[a#h|Title]]\n",
"equal": true
},
{
"name": "spaces, Unicode and adjacent links",
"input": "See [[ Héllo there # A heading | a label ]][[next]]\n",
"output": "See [[ Héllo there # A heading | a label ]][[next]]\n",
"equal": true
},
{
"name": "reference-definition collision",
"input": "[[a]]\n\n[a]: u\n",
"output": "[[a]]\n\n[a]: u\n",
"equal": true
},
{
"name": "inside an ordinary link",
"input": "[x [[a]]](u)\n",
"output": "[x [[a]]](u)\n",
"equal": true
},
{
"name": "inert embed",
"input": "![[cover.png]]\n",
"output": "![[cover.png]]\n",
"equal": true
}
]
}
@@ -0,0 +1,14 @@
[
{
"name": "serializer-removal",
"killed": true,
"restored": true,
"restoredHashesEqual": true
},
{
"name": "invalid-escaping",
"killed": true,
"restored": true,
"restoredHashesEqual": true
}
]
@@ -0,0 +1,26 @@
from pathlib import Path
import subprocess,sys,json,hashlib
out=Path(__file__).parent;root=Path('/home/jwoltje/.t3/worktrees/mosaic-stack/herb-row214-r2');model=root/'packages/webui/editor/model.mjs';good=model.read_bytes();(out/'model.good.mjs').write_bytes(good)
initial={str(p.relative_to(root)):hashlib.sha256(p.read_bytes()).hexdigest() for p in [model,root/'packages/webui/src/public/editor.js',root/'packages/webui/editor/BUILD.json',root/'packages/webui/src/public/editor.css']}
def step(name,cwd,cap,args):return subprocess.run([sys.executable,str(out/'step.py'),name,str(cwd),str(cap),*args]).returncode
specs=[('serializer-removal','wikilinks are inline atoms and survive'),('invalid-escaping','wikilink DOM paste accepts')]
results=[]
for name,pattern in specs:
changed=good.decode()
if name=='serializer-removal':
start=changed.index(' wikilink(state, node) {');end=changed.index(' raw_block(state, node)',start);changed=changed[:start]+changed[end:]
else:
old="else escapedText(state, oneLine(String(source ?? '')));";assert changed.count(old)==1;changed=changed.replace(old,"else state.text(oneLine(String(source ?? '')));")
try:
model.write_text(changed)
assert step(name+'-build',root/'packages/webui',90,['node','editor/build.mjs'])==0
rc=step(name+'-red',root,180,['node','--test','--test-concurrency=1','--test-name-pattern='+pattern,'packages/webui/tests/editor-wikilink.test.mjs'])
if rc!=1:raise RuntimeError('mutant not killed or timed out: '+str(rc))
finally:
model.write_bytes(good)
if step(name+'-restore-build',root/'packages/webui',90,['node','editor/build.mjs'])!=0:raise RuntimeError('restore build failed')
assert step(name+'-restore-green',root,180,['node','--test','--test-concurrency=1','--test-name-pattern='+pattern,'packages/webui/tests/editor-wikilink.test.mjs'])==0
for p,h in initial.items():assert hashlib.sha256((root/p).read_bytes()).hexdigest()==h,p
results.append({'name':name,'killed':True,'restored':True,'restoredHashesEqual':True})
(out/'mutants.json').write_text(json.dumps(results,indent=2)+'\n')
print('MUTANT_COMPLETE',name,flush=True)
@@ -0,0 +1,3 @@
# grant sage-4286; nice 19; TMPDIR /mnt/storage/scratch/tmp/g49X; MOSAIC_REQUIRE_EDITOR_BUILD 1
added 2 packages in 425ms
@@ -0,0 +1,4 @@
{
"count": 0,
"processes": []
}
@@ -0,0 +1,27 @@
# grant sage-4286; nice 19; TMPDIR /mnt/storage/scratch/tmp/g49X; MOSAIC_REQUIRE_EDITOR_BUILD 1
✔ the imported source is the tagged upstream source plus the recorded local changes, and nothing else (27.800323ms)
✔ every package names its origin and license, and NOTICE lists each (0.831005ms)
✔ the build reads only imported source and the editor's own files; the bundle is the recorded build (7.478515ms)
✔ MOSAIC_REQUIRE_EDITOR_BUILD: a missing or wrong esbuild skips without it and fails with it; a wrong binary always fails (6.189875ms)
✔ the toolchain check, run as a program, exits non-zero without esbuild when the build is required (143.672155ms)
▲ [WARNING] Ignoring this import because "third_party/editor/prosemirror-transform/src/mark.ts" was marked as having no side effects [ignored-bare-import]
third_party/editor/prosemirror-transform/src/index.ts:10:7:
10 │ import "./mark"
╵ ~~~~~~~~
"sideEffects" is false in the enclosing "package.json" file:
third_party/editor/prosemirror-transform/package.json:13:2:
13 │ "sideEffects": false,
╵ ~~~~~~~~~~~~~
✔ rebuilding from the source gives the same bytes (needs the pinned esbuild; MOSAIC_REQUIRE_EDITOR_BUILD=1 makes its absence a failure) (67.600627ms)
ℹ tests 6
ℹ suites 0
ℹ pass 6
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 305.965249
@@ -0,0 +1,18 @@
{"name": "measurement", "argv": ["node", "agents/herb/work/queue-214/measure.mjs"], "cwd": "/home/jwoltje/.t3/worktrees/mosaic-stack/herb-row214-r2", "rc": 0, "wallSeconds": 0.093, "counts": {}, "nice": 19, "tmpRoot": "/mnt/storage/scratch/tmp/g49X", "requireEditorBuild": "1", "timeoutSeconds": 60, "ended": "2026-10-24T22:07:22.215700+00:00"}
{"name": "baseline-red", "argv": ["node", "--test", "--test-concurrency=1", "--test-name-pattern=wikilinks are inline atoms and survive", "packages/webui/tests/editor-wikilink.test.mjs"], "cwd": "/home/jwoltje/.t3/worktrees/mosaic-stack/herb-row214-r2", "rc": 1, "wallSeconds": 1.151, "counts": {"tests": 1, "pass": 0, "fail": 1, "cancelled": 0, "skipped": 0}, "nice": 19, "tmpRoot": "/mnt/storage/scratch/tmp/g49X", "requireEditorBuild": "1", "timeoutSeconds": 180, "ended": "2026-10-24T22:07:27.998436+00:00"}
{"name": "npm-ci", "argv": ["npm", "ci", "--ignore-scripts", "--no-audit", "--no-fund"], "cwd": "/home/jwoltje/.t3/worktrees/mosaic-stack/herb-row214-r2/packages/webui", "rc": 0, "wallSeconds": 0.514, "counts": {}, "nice": 19, "tmpRoot": "/mnt/storage/scratch/tmp/g49X", "requireEditorBuild": "1", "timeoutSeconds": 240, "ended": "2026-10-24T22:07:41.534116+00:00"}
{"name": "build", "argv": ["node", "editor/build.mjs"], "cwd": "/home/jwoltje/.t3/worktrees/mosaic-stack/herb-row214-r2/packages/webui", "rc": 0, "wallSeconds": 0.108, "counts": {}, "nice": 19, "tmpRoot": "/mnt/storage/scratch/tmp/g49X", "requireEditorBuild": "1", "timeoutSeconds": 120, "ended": "2026-10-24T22:07:44.654385+00:00"}
{"name": "wikilink-green", "argv": ["node", "--test", "--test-concurrency=1", "packages/webui/tests/editor-wikilink.test.mjs"], "cwd": "/home/jwoltje/.t3/worktrees/mosaic-stack/herb-row214-r2", "rc": 0, "wallSeconds": 2.348, "counts": {"tests": 13, "pass": 13, "fail": 0, "cancelled": 0, "skipped": 0}, "nice": 19, "tmpRoot": "/mnt/storage/scratch/tmp/g49X", "requireEditorBuild": "1", "timeoutSeconds": 180, "ended": "2026-10-24T22:07:52.583345+00:00"}
{"name": "editor-green", "argv": ["node", "--test", "--test-concurrency=1", "packages/webui/tests/editor.test.mjs"], "cwd": "/home/jwoltje/.t3/worktrees/mosaic-stack/herb-row214-r2", "rc": 0, "wallSeconds": 2.053, "counts": {"tests": 5, "pass": 5, "fail": 0, "cancelled": 0, "skipped": 0}, "nice": 19, "tmpRoot": "/mnt/storage/scratch/tmp/g49X", "requireEditorBuild": "1", "timeoutSeconds": 240, "ended": "2026-10-24T22:07:59.286086+00:00"}
{"name": "editor-browser-green", "argv": ["node", "--test", "--test-concurrency=1", "packages/webui/tests/editor-browser.test.mjs"], "cwd": "/home/jwoltje/.t3/worktrees/mosaic-stack/herb-row214-r2", "rc": 0, "wallSeconds": 14.984, "counts": {"tests": 16, "pass": 16, "fail": 0, "cancelled": 0, "skipped": 0}, "nice": 19, "tmpRoot": "/mnt/storage/scratch/tmp/g49X", "requireEditorBuild": "1", "timeoutSeconds": 240, "ended": "2026-10-24T22:08:20.029050+00:00"}
{"name": "provenance-green", "argv": ["node", "--test", "--test-concurrency=1", "packages/webui/tests/editor-provenance.test.mjs"], "cwd": "/home/jwoltje/.t3/worktrees/mosaic-stack/herb-row214-r2", "rc": 0, "wallSeconds": 0.343, "counts": {"tests": 6, "pass": 6, "fail": 0, "cancelled": 0, "skipped": 0}, "nice": 19, "tmpRoot": "/mnt/storage/scratch/tmp/g49X", "requireEditorBuild": "1", "timeoutSeconds": 180, "ended": "2026-10-24T22:08:39.003420+00:00"}
{"name": "serializer-removal-build", "argv": ["node", "editor/build.mjs"], "cwd": "/home/jwoltje/.t3/worktrees/mosaic-stack/herb-row214-r2/packages/webui", "rc": 0, "wallSeconds": 0.093, "counts": {}, "nice": 19, "tmpRoot": "/mnt/storage/scratch/tmp/g49X", "requireEditorBuild": "1", "timeoutSeconds": 90, "ended": "2026-10-24T22:08:51.422097+00:00"}
{"name": "serializer-removal-red", "argv": ["node", "--test", "--test-concurrency=1", "--test-name-pattern=wikilinks are inline atoms and survive", "packages/webui/tests/editor-wikilink.test.mjs"], "cwd": "/home/jwoltje/.t3/worktrees/mosaic-stack/herb-row214-r2", "rc": 1, "wallSeconds": 0.887, "counts": {"tests": 1, "pass": 0, "fail": 1, "cancelled": 0, "skipped": 0}, "nice": 19, "tmpRoot": "/mnt/storage/scratch/tmp/g49X", "requireEditorBuild": "1", "timeoutSeconds": 180, "ended": "2026-10-24T22:08:52.340936+00:00"}
{"name": "serializer-removal-restore-build", "argv": ["node", "editor/build.mjs"], "cwd": "/home/jwoltje/.t3/worktrees/mosaic-stack/herb-row214-r2/packages/webui", "rc": 0, "wallSeconds": 0.115, "counts": {}, "nice": 19, "tmpRoot": "/mnt/storage/scratch/tmp/g49X", "requireEditorBuild": "1", "timeoutSeconds": 90, "ended": "2026-10-24T22:08:52.498613+00:00"}
{"name": "serializer-removal-restore-green", "argv": ["node", "--test", "--test-concurrency=1", "--test-name-pattern=wikilinks are inline atoms and survive", "packages/webui/tests/editor-wikilink.test.mjs"], "cwd": "/home/jwoltje/.t3/worktrees/mosaic-stack/herb-row214-r2", "rc": 0, "wallSeconds": 0.979, "counts": {"tests": 1, "pass": 1, "fail": 0, "cancelled": 0, "skipped": 0}, "nice": 19, "tmpRoot": "/mnt/storage/scratch/tmp/g49X", "requireEditorBuild": "1", "timeoutSeconds": 180, "ended": "2026-10-24T22:08:53.514962+00:00"}
{"name": "invalid-escaping-build", "argv": ["node", "editor/build.mjs"], "cwd": "/home/jwoltje/.t3/worktrees/mosaic-stack/herb-row214-r2/packages/webui", "rc": 0, "wallSeconds": 0.102, "counts": {}, "nice": 19, "tmpRoot": "/mnt/storage/scratch/tmp/g49X", "requireEditorBuild": "1", "timeoutSeconds": 90, "ended": "2026-10-24T22:08:53.655583+00:00"}
{"name": "invalid-escaping-red", "argv": ["node", "--test", "--test-concurrency=1", "--test-name-pattern=wikilink DOM paste accepts", "packages/webui/tests/editor-wikilink.test.mjs"], "cwd": "/home/jwoltje/.t3/worktrees/mosaic-stack/herb-row214-r2", "rc": 1, "wallSeconds": 0.979, "counts": {"tests": 1, "pass": 0, "fail": 1, "cancelled": 0, "skipped": 0}, "nice": 19, "tmpRoot": "/mnt/storage/scratch/tmp/g49X", "requireEditorBuild": "1", "timeoutSeconds": 180, "ended": "2026-10-24T22:08:54.668225+00:00"}
{"name": "invalid-escaping-restore-build", "argv": ["node", "editor/build.mjs"], "cwd": "/home/jwoltje/.t3/worktrees/mosaic-stack/herb-row214-r2/packages/webui", "rc": 0, "wallSeconds": 0.105, "counts": {}, "nice": 19, "tmpRoot": "/mnt/storage/scratch/tmp/g49X", "requireEditorBuild": "1", "timeoutSeconds": 90, "ended": "2026-10-24T22:08:54.809629+00:00"}
{"name": "invalid-escaping-restore-green", "argv": ["node", "--test", "--test-concurrency=1", "--test-name-pattern=wikilink DOM paste accepts", "packages/webui/tests/editor-wikilink.test.mjs"], "cwd": "/home/jwoltje/.t3/worktrees/mosaic-stack/herb-row214-r2", "rc": 0, "wallSeconds": 0.985, "counts": {"tests": 1, "pass": 1, "fail": 0, "cancelled": 0, "skipped": 0}, "nice": 19, "tmpRoot": "/mnt/storage/scratch/tmp/g49X", "requireEditorBuild": "1", "timeoutSeconds": 180, "ended": "2026-10-24T22:08:55.831793+00:00"}
{"name": "webui-whole", "argv": ["node", "--test", "--test-concurrency=2", "tests/agents-browser.test.mjs", "tests/agents.test.mjs", "tests/board-held.test.mjs", "tests/browser-edge.test.mjs", "tests/browser.test.mjs", "tests/bus-browser.test.mjs", "tests/bus.test.mjs"], "cwd": "/home/jwoltje/.t3/worktrees/mosaic-stack/herb-row214-r2/packages/webui", "rc": 0, "wallSeconds": 17.787, "counts": {"tests": 28, "pass": 28, "fail": 0, "cancelled": 0, "skipped": 0}, "nice": 19, "tmpRoot": "/mnt/storage/scratch/tmp/g49X", "requireEditorBuild": "1", "timeoutSeconds": 600, "ended": "2026-10-24T22:09:39.039897+00:00"}
{"name": "webui-69", "argv": ["node", "--test", "--test-concurrency=2", "tests/agents-browser.test.mjs", "tests/agents.test.mjs", "tests/assistant.test.mjs", "tests/board-held.test.mjs", "tests/browser-edge.test.mjs", "tests/browser.test.mjs", "tests/bus-browser.test.mjs", "tests/bus.test.mjs", "tests/channel-chat.test.mjs", "tests/chat-browser.test.mjs", "tests/chat-instances.test.mjs", "tests/chat-replies-browser.test.mjs", "tests/chat-sessions-access.test.mjs", "tests/chat.test.mjs", "tests/conversation.test.mjs", "tests/decide-browser.test.mjs", "tests/decide-sso.test.mjs", "tests/decision-http.test.mjs", "tests/discord.test.mjs", "tests/docs-browser.test.mjs", "tests/editor-browser.test.mjs", "tests/editor-provenance.test.mjs", "tests/editor-wikilink.test.mjs", "tests/editor.test.mjs", "tests/granted-user.test.mjs", "tests/history-return-flow.test.mjs", "tests/instances-browser.test.mjs", "tests/instances.test.mjs", "tests/library-cli.test.mjs", "tests/library-routes.test.mjs", "tests/library-status.test.mjs", "tests/limits-browser.test.mjs", "tests/limits-route.test.mjs", "tests/markdown-preview.test.mjs", "tests/messages.test.mjs", "tests/pinned-copy.test.mjs", "tests/preview.test.mjs", "tests/processing-browser.test.mjs", "tests/processing-ui.test.mjs", "tests/processing.test.mjs", "tests/project-file.test.mjs", "tests/project-name-repo.test.mjs", "tests/proof-cli.test.mjs", "tests/proof.test.mjs", "tests/reads.test.mjs", "tests/relaunch.test.mjs", "tests/return-flow.test.mjs", "tests/routes-browser.test.mjs", "tests/routes.test.mjs", "tests/send-browser.test.mjs", "tests/send.test.mjs", "tests/serve.test.mjs", "tests/service.test.mjs", "tests/settings-form.test.mjs", "tests/settings-write.test.mjs", "tests/shell.test.mjs", "tests/sso-browser.test.mjs", "tests/sso.test.mjs", "tests/tester-browser.test.mjs", "tests/tester-cli.test.mjs", "tests/tester-listener.test.mjs", "tests/tester-socket.test.mjs", "tests/thread-claims-browser.test.mjs", "tests/thread-claims.test.mjs", "tests/thread-sharing.test.mjs", "tests/usage-browser.test.mjs", "tests/usage.test.mjs", "tests/user-option-swap.test.mjs", "tests/views.test.mjs"], "cwd": "/home/jwoltje/.t3/worktrees/mosaic-stack/herb-row214-r2/packages/webui", "rc": 0, "wallSeconds": 174.189, "counts": {"tests": 455, "pass": 455, "fail": 0, "cancelled": 0, "skipped": 0}, "nice": 19, "tmpRoot": "/mnt/storage/scratch/tmp/g49X", "requireEditorBuild": "1", "timeoutSeconds": 600, "ended": "2026-10-24T22:12:48.880975+00:00"}
@@ -0,0 +1,15 @@
# grant sage-4286; nice 19; TMPDIR /mnt/storage/scratch/tmp/g49X; MOSAIC_REQUIRE_EDITOR_BUILD 1
▲ [WARNING] Ignoring this import because "third_party/editor/prosemirror-transform/src/mark.ts" was marked as having no side effects [ignored-bare-import]
third_party/editor/prosemirror-transform/src/index.ts:10:7:
10 │ import "./mark"
╵ ~~~~~~~~
"sideEffects" is false in the enclosing "package.json" file:
third_party/editor/prosemirror-transform/package.json:13:2:
13 │ "sideEffects": false,
╵ ~~~~~~~~~~~~~
editor.js bf55e22b945eda1e51ecbe9185eadd9ff21a7902713b24417ed7d697c7347bba (133 inputs)
editor.css 8654ac57d1e7a561110138ecd7f701c34a074017ff8c4cd2768d8083908ee61f
@@ -0,0 +1,31 @@
# grant sage-4286; nice 19; TMPDIR /mnt/storage/scratch/tmp/g49X; MOSAIC_REQUIRE_EDITOR_BUILD 1
✖ wikilinks are inline atoms and survive a neighbouring edit in every required context (698.378144ms)
ℹ tests 1
ℹ suites 0
ℹ pass 0
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 850.87157
✖ failing tests:
test at packages/webui/tests/editor-wikilink.test.mjs:35:1
✖ wikilinks are inline atoms and survive a neighbouring edit in every required context (698.378144ms)
Error: Uncaught: Error: Token type `wikilink` not supported by Markdown renderer
at MarkdownSerializerState.render (http://127.0.0.1:43723/editor.js:18015:17)
at progress (http://127.0.0.1:43723/editor.js:18096:18)
at _Fragment.forEach (http://127.0.0.1:43723/editor.js:484:9)
at _Node.forEach (http://127.0.0.1:43723/editor.js:1178:20)
at MarkdownSerializerState.renderInline (http://127.0.0.1:43723/editor.js:18101:14)
at Object.paragraph (http://127.0.0.1:43723/editor.js:17859:13)
at MarkdownSerializerState.render (http://127.0.0.1:43723/editor.js:18012:35)
at http://127.0.0.1:43723/editor.js:18025:44
at _Fragment.forEach (http://127.0.0.1:43723/editor.js:484:9)
at _Node.forEach (http://127.0.0.1:43723/editor.js:1178:20)
at Object.evaluate (file:///home/jwoltje/.t3/worktrees/mosaic-stack/herb-row214-r2/packages/webui/tests/browser.mjs:49:35)
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
at async TestContext.<anonymous> (file:///home/jwoltje/.t3/worktrees/mosaic-stack/herb-row214-r2/packages/webui/tests/editor-wikilink.test.mjs:38:5)
at async Test.run (node:internal/test_runner/test:1409:7)
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3)
@@ -0,0 +1,15 @@
# grant sage-4286; nice 19; TMPDIR /mnt/storage/scratch/tmp/g49X; MOSAIC_REQUIRE_EDITOR_BUILD 1
▲ [WARNING] Ignoring this import because "third_party/editor/prosemirror-transform/src/mark.ts" was marked as having no side effects [ignored-bare-import]
third_party/editor/prosemirror-transform/src/index.ts:10:7:
10 │ import "./mark"
╵ ~~~~~~~~
"sideEffects" is false in the enclosing "package.json" file:
third_party/editor/prosemirror-transform/package.json:13:2:
13 │ "sideEffects": false,
╵ ~~~~~~~~~~~~~
editor.js bd167899a551c70dc07e500fcdd1853d7fb710b23c217ba5b42daf5785c38a72 (133 inputs)
editor.css 8654ac57d1e7a561110138ecd7f701c34a074017ff8c4cd2768d8083908ee61f
@@ -0,0 +1,10 @@
# grant sage-4286; nice 19; TMPDIR /mnt/storage/scratch/tmp/g49X; MOSAIC_REQUIRE_EDITOR_BUILD 1
✔ wikilinks are inline atoms and survive a neighbouring edit in every required context (782.721982ms)
ℹ tests 1
ℹ suites 0
ℹ pass 1
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 940.955124
@@ -0,0 +1,6 @@
{
"root": "/home/jwoltje/.t3/worktrees/mosaic-stack/herb-row214-r2",
"tmp": "/mnt/storage/scratch/tmp/g49X",
"grant": "sage-4286",
"candidate": "2ec3f5b5ddab0cb5f2716f8164c25861b741213e"
}
@@ -0,0 +1,4 @@
{
"grep": "^# skip plus # SKIP and spec skipped-test lines",
"names": []
}
@@ -0,0 +1,19 @@
import os,sys,subprocess,pathlib,json,time,datetime,shlex,re
out=pathlib.Path(__file__).parent;s=json.loads((out/'settings.json').read_text());name=sys.argv[1];cwd=sys.argv[2];cap=int(sys.argv[3]);argv=sys.argv[4:]
left=datetime.datetime(2026,10,24,22,40,tzinfo=datetime.timezone.utc).timestamp()-time.time()-20
if left<15:raise SystemExit('hard-stop cleanup margin reached')
limit=min(cap,int(left));env=os.environ.copy();env.pop('NODE_TEST_CONTEXT',None)
env.update(TMPDIR=s['tmp'],TMP=s['tmp'],TEMP=s['tmp'],MOSAIC_REQUIRE_EDITOR_BUILD='1',MOSAIC_GATE_LANE='b',MOSAIC_GATE_STATE='/mnt/storage/scratch/mosaic-gate-lanes',NO_COLOR='1',MOSAIC_CONFIG=s['tmp']+'/missing-config.json',XDG_CACHE_HOME=s['tmp']+'/cache',JITI_FS_CACHE=s['tmp']+'/jiti')
header='' if name=='measurement' else 'printf "# grant sage-4286; nice %s; TMPDIR %s; MOSAIC_REQUIRE_EDITOR_BUILD %s\\n" "$(nice)" "$TMPDIR" "$MOSAIC_REQUIRE_EDITOR_BUILD"; '
inner=header+'exec "$@"'
log=out/(name+'.log');command='nice -n "$((19 - $(nice)))" timeout -k 10 '+str(limit)+' bash -c '+shlex.quote(inner)+' gate-run '+shlex.join(argv)+' 2>&1 | tee '+shlex.quote(str(log))
begin=time.time();print('START',name,datetime.datetime.now(datetime.timezone.utc).isoformat(),flush=True)
r=subprocess.run(['bash','-o','pipefail','-c',command],cwd=cwd,env=env,stdout=subprocess.DEVNULL)
text=log.read_text();counts={}
for key in ['tests','pass','fail','cancelled','skipped']:
hits=re.findall(r'^[ℹ#] '+key+r' (\d+)',text,re.M)
if hits:counts[key]=int(hits[-1])
record={'name':name,'argv':argv,'cwd':cwd,'rc':r.returncode,'wallSeconds':round(time.time()-begin,3),'counts':counts,'nice':19,'tmpRoot':s['tmp'],'requireEditorBuild':'1','timeoutSeconds':limit,'ended':datetime.datetime.now(datetime.timezone.utc).isoformat()}
with (out/'results.jsonl').open('a') as f:f.write(json.dumps(record)+'\n')
print(json.dumps(record),flush=True);print('\n'.join(text.splitlines()[-20:]),flush=True)
raise SystemExit(r.returncode)
@@ -0,0 +1,489 @@
# grant sage-4286; nice 19; TMPDIR /mnt/storage/scratch/tmp/g49X; MOSAIC_REQUIRE_EDITOR_BUILD 1
✔ row 194 T16 Settings Agents: unavailable read-only, launch follows its request, confirms, reason and refusal (17431.415001ms)
✔ row 256 T7 T8 the human page offers Delete on a creative instance, the tester page never does (1196.894618ms)
✔ row 194 T16: each agent action makes one call with only its keys, logs the user and answers the request (25.920868ms)
✔ row 194 T16: the boundary refuses before any bus call (13.773722ms)
✔ row 194 T16: read-only when send is off or no writer is wired; no route has a --send exception (6.006105ms)
✔ row 194 T16: a bus refusal names its reason and nothing changed; an unknown outcome or a bad shape is never retried (4.80649ms)
✔ row 194 T16: the request read takes one id, adds the refusal reason and refuses an unexpected shape (9.010999ms)
✔ row 194 T16: delete joins add and change on /api/instances with only revision, instance and op (4.535523ms)
✔ row 194 T16 D4.1: the tester listener launches, terminates and reads requests; regenerate, veto and delete are refused with no call (6.985924ms)
✔ row 194 N5: agents.js is served as JavaScript and index.html loads it once (2.138169ms)
✔ row 239 T6: a delete closed -revoked says the tokens may already be revoked, and outcome-unknown-revoked is an open outcome (3.807253ms)
✔ Q169 the signed-in human names a seat and stops the assistant, one bus call each, logged with the user (20.467126ms)
✔ Q169 the name and stop routes need send on, the public name, the user, the exact Origin and same-origin Fetch Metadata (10.75691ms)
✔ Q169 the name and stop routes take JSON within 4 KiB and only their own keys, all text (7.456ms)
✔ Row 167 a null body on the name and stop routes is 400 invalid-request with no write, and the next request is answered (4.218653ms)
✔ Q169 a bus refusal says nothing changed; a lost or odd answer says the change may have been made (6.353476ms)
✔ Q169 the names read is a bus read like the others (1.642206ms)
✔ the Board: held roles with no machine sessions, a claim with no address, the toggle kept across a reload, and Message addressed to the role (6930.836448ms)
✔ a Seen click while a Board read is in flight waits for that read, then posts (1178.550164ms)
✔ a redirect answer on a Board read, a bus read, a conversation read, a board POST, a module read and a send signs the page out: one line, nothing stale, no further reads (5341.444261ms)
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (1618.401633ms)
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (1723.682265ms)
✔ the README says a refused field table names the file or the key at fault, never a path (6.362709ms)
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (3554.138366ms)
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (124.549917ms)
✔ decisions route serves the roles and recent lists from the reader view (46.444658ms)
✔ credentials route serves the broker credential status from the reader view (19.448846ms)
✔ replies route serves the replies to one message from the reader view (45.806351ms)
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (55.698955ms)
✔ bus refusals map to statuses and never carry a path or a stack (27.537764ms)
✔ humanCall speaks the human transport protocol without blocking the server (1707.350793ms)
✔ busReader takes --business, else the live bus host, else refuses (257.530467ms)
✔ humanCall kills a transport that runs past its timeout (529.025642ms)
✔ serve refuses a --business value that is not a business id (463.414608ms)
✔ WebUI channel off returns 503 and named recovery for every chat route without a chat operation (38.716248ms)
✔ WebUI only selection keeps web chat enabled while retaining the old read envelope (2.635502ms)
✔ WebUI channel-disabled recovery text is fixed and never echoes transport text (1.545115ms)
✔ chat browser: list/create named projects and threads, hostile labels inert and no implicit writes (836.234777ms)
✔ chat browser: thread drafts, receipts and cursor history stay isolated; refresh never writes (1150.355116ms)
✔ chat browser: uncertain writes retain draft, ownership refusal clears it, read-only cannot send (1316.24955ms)
✔ chat browser: a late read from A cannot paint B; sign-out clears private state and stops reads (1284.31592ms)
✔ chat browser: page forward and deduplicate replies; pending and byte-limit submissions do not duplicate (953.60414ms)
✔ chat browser: scrolled history stays put, New messages and an own send return to the tail (923.155974ms)
✔ chat browser: automatic polling follows a reply without a POST or a lost draft (10953.35212ms)
✔ chat browser: display stays bounded while every forward page remains reachable (1095.68322ms)
✔ chat browser: a confirmed visible message replaces its pending receipt; malformed receipts stay unknown (963.350843ms)
✔ chat browser: a refusal elsewhere forgets private chat drafts (882.872916ms)
✔ chat browser: a late project creation receipt cannot navigate away from another thread (1050.661257ms)
✔ C2-09a picker: no silent choice, duplicate hostile labels stay inert, selection survives refresh and reaches POST (1062.688519ms)
✔ C2-09a picker: metadata failure, empty set and removed pick explain disabled state and recover without switching (924.988913ms)
✔ C2-09a picker: a single instance is chosen explicitly but read-only mode cannot create a thread (781.216726ms)
✔ 264e chooser: two sessions keep drafts, cursors, deep links and counts independent (921.256121ms)
✔ 264e clear confirmation and status never resend, force-stop or change selection (1103.840292ms)
✔ 264e new session keeps its operation key after an unknown response and uses no implicit open (1124.509192ms)
✔ 264e cancel shows before/after retirement and never starts or replays work (1599.216909ms)
✔ 272 B1: a bad-session deep link preserves the draft and lost clear operation on A (1281.091753ms)
✔ C2-09a metadata uses the human listener bus, projects labels only and refuses unauthenticated reads (23.460794ms)
✔ C2-09a metadata uses the private tester listener own bus and no human transport (5.122028ms)
✔ C2-09a metadata refuses missing or malformed reads and never echoes untrusted fields (5.504868ms)
✔ C2-09a metadata stays behind the SSO session check and a GET never refreshes session activity (4.184632ms)
✔ chat replies: free text, cites with badges, the agent label, changed since and safe links (898.153564ms)
✔ chat replies: a write notice reads as its line with a Revert; a refused revert shows its code (1170.744713ms)
✔ chat replies: a read-only Console shows the notice with Revert disabled (850.32112ms)
✔ chat status line: served commit and age, divergence, fetch error and the problems one click away (897.997997ms)
✔ chat status line: limits when the status carries them, no problems, and refusals (1133.380317ms)
✔ chat status line: a project with no repository link reads no library status (854.88335ms)
✔ chat status line: unreadable reply limits show their problem and both fallback keys (882.924328ms)
✔ chat replies: unchanged file at a different library commit and incomplete checks stay unmarked (768.120005ms)
✔ chat status line: an invalid library mapping remains visible for diagnosis (737.619858ms)
✔ chat replies: a checked notice-shaped agent message is plain text with no Revert (725.56059ms)
✔ chat replies: owner-only without a sent field is a definite Revert refusal (811.129366ms)
✔ 272 fallback: granted legacy route keeps default history, send and open without session controls (715.832685ms)
✔ 272 fallback guard: granted explicit-session route refuses without default history or writes (631.667619ms)
✔ 272 fallback guard: human-required catalog failure never enables default-thread access (663.54337ms)
✔ 272 fallback guard: timeout catalog failure never enables default-thread access (537.528495ms)
✔ 272 fallback guard: no-bus-host catalog failure never enables default-thread access (622.030953ms)
✔ 272 fallback guard: malformed catalog failure never enables default-thread access (624.379398ms)
✔ 272 fallback guard: owner route retains chooser and explicit default-session addressing (635.748276ms)
✔ chat reads map only fixed verbs, strict forward paging and pure GET (40.110234ms)
✔ chat refuses malformed and repeated query/id values before transport (7.000459ms)
✔ chat project.create: one verified write, fixed identity/class and private log (2.986922ms)
✔ chat project.create: each existing Console write fence makes zero bus calls (8.933369ms)
✔ chat project.create: refusal or lost/malformed receipt never retries or invents success (5.056788ms)
✔ chat thread.create: one verified write, fixed identity/class and private log (1.581993ms)
✔ chat thread.create: each existing Console write fence makes zero bus calls (8.197701ms)
✔ chat thread.create: refusal or lost/malformed receipt never retries or invents success (3.965463ms)
✔ chat thread.send: one verified write, fixed identity/class and private log (1.353806ms)
✔ chat thread.send: each existing Console write fence makes zero bus calls (6.740756ms)
✔ chat thread.send: refusal or lost/malformed receipt never retries or invents success (4.970022ms)
✔ chat thread.open: one verified write, fixed identity/class and private log (1.438093ms)
✔ chat thread.open: each existing Console write fence makes zero bus calls (6.978012ms)
✔ chat thread.open: refusal or lost/malformed receipt never retries or invents success (3.64445ms)
✔ chat createThread receipt requires an instance even when its id is valid (2.535217ms)
✔ chat write receipts expose only the fields belonging to their operation (3.886809ms)
✔ chat validates names, body, project and strict empty open payload (5.948932ms)
✔ chat read failures redact ownership and refuse invalid transport arrays (7.572799ms)
✔ REQ-USER-2 real broker: owned catalog, create/name/filter, isolated history and 403/404 without mutations (108.639676ms)
✔ chat repeats every POST boundary even when called behind an admitted request (1.210711ms)
✔ configured Console process injects chat through its existing human transport (201.842342ms)
✔ chat assets, GET purity, method and unknown-route refusals are explicit (15.908528ms)
✔ the chat seam names the transport it is given: the human one and the tester one both pass, anything else is a TypeError (0.594861ms)
✔ C2-09a createThread forwards an explicit instance through the existing channel (4.877049ms)
✔ C2-09a malformed instance and extra thread fields refuse before the channel write (4.621594ms)
✔ 264e session catalog/create and explicit selectors keep the human transport and strict keys (6.446ms)
✔ 264e session writes retain Origin/identity fences and explicit selector errors (3.221617ms)
✔ 264e clear envelope is projected, pending is preserved and GET never repeats the clear (5.920053ms)
✔ 264e cancel is an authenticated, confirmed human write and retains retirement distinction (7.831208ms)
✔ 272 N1: malformed clear/cancel write receipts get one redacted outcome-unknown audit line (5.831884ms)
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (1306.1032ms)
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (1836.119679ms)
✔ conversation view: a newer session with no readable history keeps the marker (1285.484193ms)
✔ conversation view: seats without history say so and offer no reply (1047.035848ms)
✔ J16: no controls without sso and decide, read-only reasons, terminal lines kept, and only an eligible grant gets the panel (5771.215266ms)
✔ J17: one POST per click, confirm then answer, cancel, refusals and an unknown outcome never retry (5272.178631ms)
✔ a sign-in redirect at either step signs the page out and answers nothing (1989.670657ms)
✔ a signed-in owner confirms and answers, with the session's provider, issuer and sub as the subject (213.942672ms)
✔ the listener gates: no session, a cross-origin page, a loopback Host, a GET, and a sign-out between the steps (77.826712ms)
✔ J16: without a decision transport the routes are read-only and /api/config says no; startServer refuses decide without sso or send (481.428976ms)
✔ J19: a granted user's session gets decide false and the user page config, and a decision POST stops at owner-only (54.985489ms)
✔ N4: decision.resolve has no Console route, the --send caller names no decision verb, and consoleCall takes only the two console verbs (80.240021ms)
✔ a confirmation then a resolve answers once with the held choice and digest, and a second resolve refuses (3.814984ms)
✔ deny resolves without a grant field (0.482281ms)
✔ the gates before the body: method, read-only, sign-in and same origin (0.49765ms)
✔ a bad body, id or content type refuses before the bus (0.762162ms)
✔ a choice that is neither key is decision-stale, and a malformed preview is confirmation-unavailable (0.681931ms)
✔ bus refusals map by code, and an unknown or missing code is unavailable at preview and outcome-unknown at resolve (2.862415ms)
✔ a confirmation binds the session, decision and choice, and any mismatch spends it (0.81855ms)
✔ a session that ends between the steps refuses before the bus (0.438239ms)
✔ confirmations expire after the TTL, cap at the limit without dropping a live one, and are one use (0.323839ms)
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (1065.529279ms)
✔ the editor: the list, hostile markdown that runs nothing, the draft, and each save receipt (2140.805191ms)
✔ the Console's CSP stands and the editor runs under it; hostile markdown on the load path runs and loads nothing (1354.955091ms)
✔ hostile content pasted into the editor runs and loads nothing, and a bad link target is refused in the link bar (1547.923208ms)
✔ a pasted image span is kept only for a target the link gate accepts, and a save writes only those (1266.979424ms)
✔ a switch of view reads nothing; a reload, and a trip away and back, each read the file exactly once (2940.297024ms)
✔ an edit rewrites only the block touched; a save sends the rest byte for byte; the Source view carries the draft (1214.212642ms)
✔ a file the editor cannot write back byte for byte opens as Source, says so, cannot save, and the Visual button works once it can (2204.177601ms)
✔ a link title pasted with line breaks in it is written on one line, so no HTML block comes of it (954.989036ms)
✔ an image alt pasted with line breaks in it is written on one line, so no HTML block comes of it (876.671026ms)
✔ an image title pasted with line breaks in it is written on one line, so no HTML block comes of it (876.342459ms)
✔ a code block info string pasted with line breaks in it is written on one line, so no HTML block comes of it (851.548483ms)
✔ a link title holding a quote after a backslash then a tag is written with the backslash escaped, so the title stays closed (973.526659ms)
✔ an image title holding a quote after a backslash then a tag is written with the backslash escaped, so the title stays closed (730.164235ms)
✔ a link title holding a trailing backslash is written with the backslash escaped, so the title stays closed (705.431094ms)
✔ an image title holding a trailing backslash is written with the backslash escaped, so the title stays closed (716.592245ms)
✔ a code block info string with a backtick, pasted, is dropped on Save, so the fence stays closed (764.08523ms)
✔ a code block info string with a backtick, as a tilde fence can have, is kept as source, not rewritten without it (846.538059ms)
✔ the imported source is the tagged upstream source plus the recorded local changes, and nothing else (26.175796ms)
✔ every package names its origin and license, and NOTICE lists each (1.097644ms)
✔ the build reads only imported source and the editor's own files; the bundle is the recorded build (8.957963ms)
✔ MOSAIC_REQUIRE_EDITOR_BUILD: a missing or wrong esbuild skips without it and fails with it; a wrong binary always fails (7.544758ms)
✔ the toolchain check, run as a program, exits non-zero without esbuild when the build is required (302.664165ms)
▲ [WARNING] Ignoring this import because "third_party/editor/prosemirror-transform/src/mark.ts" was marked as having no side effects [ignored-bare-import]
third_party/editor/prosemirror-transform/src/index.ts:10:7:
10 │ import "./mark"
╵ ~~~~~~~~
"sideEffects" is false in the enclosing "package.json" file:
third_party/editor/prosemirror-transform/package.json:13:2:
13 │ "sideEffects": false,
╵ ~~~~~~~~~~~~~
✔ rebuilding from the source gives the same bytes (needs the pinned esbuild; MOSAIC_REQUIRE_EDITOR_BUILD=1 makes its absence a failure) (172.641917ms)
✔ wikilink round-trip: plain (9.175577ms)
✔ wikilink round-trip: labelled (1.129295ms)
✔ wikilink round-trip: heading target (1.25613ms)
✔ wikilink round-trip: list item (1.645832ms)
✔ wikilink round-trip: next to front matter (1.202128ms)
✔ wikilink round-trip: heading block and combined label (1.18696ms)
✔ wikilink round-trip: spaces, Unicode and adjacent links (2.377579ms)
✔ wikilink round-trip: reference-definition collision (2.902031ms)
✔ wikilink round-trip: inside an ordinary link (4.241793ms)
✔ wikilink round-trip: inert embed (0.63341ms)
✔ wikilinks are inline atoms and survive a neighbouring edit in every required context (1556.425084ms)
✔ code, escaped openers, malformed syntax and HTML-like targets stay outside wikilink atoms (1452.825353ms)
✔ wikilink DOM paste accepts only bounded single-line inert syntax; malformed attrs serialise as escaped text (1000.711061ms)
✔ the editor routes serve the built files with their types and no cache (46.893995ms)
✔ open and save with no edit writes the same bytes: the goals review and a doc with every element (21.572081ms)
✔ open and save with no edit writes the same bytes: every repository doc under docs/ and spec/ (2160.196477ms)
✔ the editor source builds markup only with DOM calls, and the bundle has no eval (4.311024ms)
✔ the link gate and the image rule are the preview's (1.175245ms)
✔ R6 G1 a granted user signs in, gets the user page and reaches the broker as itself (172.773172ms)
▶ R6 G2 a subject with no live grant in this business is refused subject-unknown
✔ a listed user with no grant (92.602053ms)
✔ a grant in another business (104.491345ms)
✔ an expired grant (114.256083ms)
✔ another user's grant (55.98006ms)
✔ a sub in no file (64.883816ms)
✔ a file the loader refuses (90.210241ms)
✔ R6 G2 a subject with no live grant in this business is refused subject-unknown (523.697455ms)
▶ R6 G3 a revoke counts on the next request: 401 and the session is gone
✔ grant removed (62.972504ms)
✔ subject removed (93.888865ms)
✔ grant expired (106.472694ms)
✔ file now refused (112.582013ms)
✔ R6 G3 a revoke counts on the next request: 401 and the session is gone (377.178827ms)
✔ R6 G4 every owner route answers 403 owner-only before any bus call (127.865636ms)
✔ R6 G5 the bus refuses every owner verb over the user transport on its own (123.119351ms)
✔ R6 G9 a user chat sends only the plugins read over the owner transport (130.557956ms)
✔ R6 G6 the user page holds the notice word for word (75.808533ms)
✔ R6 G7 the hub label and plain peer links render from config on both pages (984.121102ms)
✔ R6 G8 userCall spawns user-cli directly with the user nonce, no human nonce, and the user request (51.702992ms)
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (52842.755773ms)
✔ row 165 Settings: role instances read, add with a restart named, change without one, a refusal keeps the draft (2069.883646ms)
✔ row 165 Console: the signed-in human adds an instance through a real broker; validate exits 0, the log names the human and no restart is needed (row 194) (261.303304ms)
✔ row 165 Console: the instance write needs send on, the public name, the user, the exact Origin and same-origin Fetch Metadata (10.054646ms)
✔ row 165 Console: the instance write takes JSON within 16 KiB and only its own keys; holder, credentials and an actor are refused (9.747938ms)
✔ row 165 Console: a bus refusal says nothing changed; a lost or odd answer says the change may have been made (15.879235ms)
✔ row 165 Console: the instances read takes no arguments, needs a bus and refuses an answer without a revision (6.37516ms)
✔ C2-04 CLI: both listener callback pairs dispatch library verbs with their own transport (208.823243ms)
✔ a signed-in same-origin revert reaches exactly one library.revert and returns the new commit (25.090439ms)
✔ a revert needs send, the named Host, the user, the exact Origin and same-origin Fetch Metadata (8.67138ms)
✔ a revert takes a project and a 40-hex commit, nothing else, before the bus is asked (7.475126ms)
✔ a bus refusal is reported as not reverted with its code; before C2-02 that is the verb refusal (9.725112ms)
✔ a revert whose answer is lost or unusable is reported unknown and is never retried (7.701918ms)
✔ the status read takes one project query and answers the bus result for that project (4.432751ms)
✔ a status answer for another project or of the wrong shape is refused; a refusal keeps its status (9.479692ms)
✔ the status read needs no send; a read-only Console still has no revert (2.316938ms)
✔ library routes are the owner's: a granted user is refused them, and the tester never reverts (0.874467ms)
✔ the page gets the one library renderer, byte for byte, as a JavaScript module (1.946831ms)
✔ a revert receipt for another project is unknown and is never retried (1.698516ms)
✔ a granted-user HTTP session cannot reach owner library callbacks (2.11729ms)
✔ a configured revert callback remains unreachable when send is off (1.619535ms)
✔ row 254: each library sync code maps to its status (0.875525ms)
✔ REQ-LIMIT-3 REQ-LIMIT-4 Settings shows token limits counted, not enforced and check failed; Reset posts one path; a save keeps the token limits (1283.613274ms)
✔ Row 167 with an assistant draft open, a token limit reset gives the assistant submit its revision (951.959609ms)
✔ REQ-LIMIT-3 limits read returns only the token rows; no query or write is accepted (36.003397ms)
✔ REQ-LIMIT-3 a limits answer without a token list is refused, not shown as no limits (11.374745ms)
2026-10-24T22:11:16.186Z settings user="jason" changed
✔ REQ-LIMIT-3 a Settings reset reaches the human write with its paths; a reset alone is enough (7.465903ms)
✔ REQ-LIMIT-3 the Console reader asks the bus limits verb with no arguments (0.460687ms)
✔ the preview source names none of the four string-to-markup sinks (1.2239ms)
✔ headings, paragraphs, emphasis and inline code render (1.52146ms)
✔ lists, block quotes and code blocks render (0.569678ms)
✔ a link is made for http, https and relative targets, with rel and the target shown (1.167267ms)
✔ a link with any other scheme, or an evasion of the scheme, stays source text (1.071465ms)
✔ an image shows alt text and URL, is never an img element and never loads (0.176798ms)
✔ raw HTML, entities and unsupported blocks show as source text, never as elements (0.390592ms)
✔ markup inside code and inside link text stays text (0.199256ms)
✔ hostile and pathological input stays within its look-ahead budget and under the nesting cap (48.760884ms)
✔ trailing blanks and closing hashes are cut by a walk from the end, not a regex that retries inside a blank run (35.531804ms)
✔ a link opener with no closer searches at most SPAN characters ahead (188.875275ms)
✔ the look-ahead work of one render is a fixed multiple of the text, whatever the opener (848.275978ms)
✔ past the budget an opener is source text, through the path all other text takes (17.235886ms)
✔ a normal 200 KiB document renders exactly as it did without a budget, and spends little of it (25.044171ms)
✔ a line separator inside a heading, a list item or a fence line does not end the marker line (0.595681ms)
✔ a thematic-break line is a paragraph, every other marker line opens a list (0.433323ms)
✔ every short line over space, tab, - * _ and a letter is a break exactly when the old regex said so (48.393695ms)
✔ one line of marks does not throw, at 4 MiB, whatever the mark or the spacing (7867.726504ms)
✔ CRLF and tabs are read like LF and spaces; empty input renders nothing (0.182135ms)
✔ messages HTTP read pages and preserves metadata; bad arguments refuse before a read; other reads unchanged (114.721485ms)
✔ Console Messages lists late unsolicited messages on polling with sender class time read state and trail; nav count includes beyond the page; reads never deliver (1227.204744ms)
✔ Messages nav count polls on Tasks and Board and clears on a Board refusal; messages 403 refuses local pages (2003.078514ms)
✔ copy writes the Console paths at the full id, read-only, and leaves an existing copy alone (1488.436066ms)
✔ copy refuses a commit off refactor, an unknown one, a worktree and a bad copies directory (1597.975802ms)
✔ copy writes nothing when an archived path is missing at the commit (579.510216ms)
✔ a copy that loses the rename race leaves no read-only temp directory behind (673.135629ms)
✔ a copy whose rename fails with no target refuses, writes no copy and leaves no temp directory (710.289827ms)
✔ a copy that meets a target holding files says exists: and moves nothing into it (732.806597ms)
✔ a copy that meets an empty target directory replaces it by rename, not by moving into it (612.835517ms)
✔ serve runs from a copy: /healthz 200, the config check, roles and RELEASE from the copy, the queue from --checkout (1149.224132ms)
✔ serve refuses a --checkout that is not an absolute directory path (375.216061ms)
✔ consoleReads runs the queue read under queueRoot and reads RELEASE under root (69.061909ms)
✔ preview options are all three or none, and each is checked (11.596651ms)
✔ the CLI refuses a partial or invalid preview flag set at start (945.562554ms)
✔ the CLI with all three flags serves the preview mode (193.560054ms)
✔ a request for the public name needs the header equal to the user id, else 401 and nothing read (21.565508ms)
✔ the header name matches without regard to case, and the value compare is exact (3.95808ms)
✔ the public name with the header reads the bus views, and the Host and Origin are checked first (33.216972ms)
✔ in preview mode every write answers 403 read-only, from the public name and loopback, and reaches nothing (13.946391ms)
✔ loopback requests are unchanged in preview mode, and the bus views work with the board down (21.317768ms)
✔ a proxy that rewrites Host to the loopback port gets 403 on its first request, whatever the user (20.422653ms)
✔ a repeated Host is 400 before any other check, in preview mode and without it (11.215391ms)
✔ without the preview flags the public name is refused and writes still reach the board (5.587922ms)
✔ processing browser: keyboard Stop, confirmed Resume, unknown outcome and persistent banner (718.01676ms)
✔ processing browser: Q175 read-only interim keeps both writes disabled and names CLI recovery at 320px (695.148762ms)
✔ processing browser: sign-out from config redirect blocks later refresh and writes (824.165228ms)
✔ processing browser: sign-out from state redirect blocks later refresh and writes (860.253673ms)
✔ processing browser: sign-out from write redirect blocks later refresh and writes (806.441355ms)
✔ processing browser: sign-out from config sign-in required blocks later refresh and writes (864.004948ms)
✔ processing browser: sign-out from state sign-in required blocks later refresh and writes (886.919472ms)
✔ processing browser: sign-out from write sign-in required blocks later refresh and writes (960.067268ms)
ROW191_LAYOUT {"width":400,"height":1000,"controls":97,"tree":324.0625,"main":1450.703125,"top":181.5,"bottom":217.5,"barBottom":129,"projectsTop":352.59375,"order":"0","visible":true}
ROW191_LAYOUT {"width":1440,"height":1000,"controls":127,"tree":914,"main":914,"top":135.5,"bottom":171.5,"barBottom":53,"projectsTop":636.59375,"order":"0","visible":true}
ROW191_LAYOUT {"width":1440,"height":420,"controls":127,"tree":334,"main":907.734375,"top":135.5,"bottom":171.5,"barBottom":53,"projectsTop":636.59375,"order":"0","visible":true}
ROW191_LAYOUT {"width":800,"height":420,"controls":127,"tree":334,"main":1131.65625,"top":135.5,"bottom":171.5,"barBottom":53,"projectsTop":618.59375,"order":"0","visible":true}
✔ processing browser: static nav controls keep Stop visible, ordered and keyboard reachable (764.42746ms)
✔ interim read-only controls show CLI recovery, disable both writes and retain a halted banner across views (8.191348ms)
✔ Stop sends one empty halt and Resume confirms before sending the displayed generation (2.947603ms)
✔ a cancelled resume never calls the writer (1.219134ms)
✔ unknown writes are not retried or displayed as success and partial stops remain explicit (2.112188ms)
✔ the persistent shell loads the control outside route-specific content with accessible status and native buttons (0.471441ms)
✔ read-only Console can read processing state but neither human control writes without --send (23.636966ms)
✔ Console halt and generation-bound resume use only the management writer and keep partial stops honest (4.923203ms)
✔ processing write fences refuse cross-site, wrong origin, unnamed host, duplicate identity and bad payload before writer (7.499038ms)
✔ tester cannot invoke either control writer and failures never echo backend text (5.000005ms)
✔ processing writes use the existing SSO signer and still require --send and same-origin (5.29471ms)
✔ processing handler independently refuses wrong origin and cross-site before its writer (0.741847ms)
✔ a signed-in same-origin save reaches exactly one human operation and returns the new commit (22.355345ms)
✔ a save needs send, the named Host, the user, the exact Origin and same-origin Fetch Metadata (8.492884ms)
✔ a save validates JSON, strict keys, text values and the 1 MiB body before the bus is asked (19.453599ms)
✔ a body that is JSON but not an object is refused 400, not thrown on (5.777613ms)
✔ the 1 MiB limit is this route only; Settings and messages keep 16 KiB (2.581226ms)
✔ a bus refusal is reported as not saved with its own status; stale-base tells the user to reload (6.148622ms)
✔ a save whose answer is lost or unusable is reported unknown and is never retried (3.873987ms)
✔ the file list and one file read take strict queries and arrive in the expected shape (4.947705ms)
✔ reads work without --send; an unusable bus answer is a bad response, a refusal keeps its status (3.061043ms)
✔ /api/config says edit only where a save can pass (2.093648ms)
✔ a repository-name project refusal is an HTTP conflict (0.589542ms)
✔ proof startup refuses partial flags non-preview and invalid fixture file metadata or bytes (1372.358151ms)
✔ serve argv carries only fixture proof path and header; startup explicitly reports proof on or off (296.532456ms)
✔ CLI applies proof on the main listener and rejects that header on its tester listener (281.757503ms)
✔ proxy proof admits named reads and sends, reads its fixture once, and never exposes the value (34.129527ms)
✔ proxy proof refuses missing wrong short or repeated proof before auth and before any read (10.977149ms)
✔ proxy proof header is refused on other Hosts and on the tester listener (5.374102ms)
✔ proof options reject unsafe or colliding header names without disclosing file contents (0.797857ms)
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (1.703483ms)
✔ credential state from the date alone, as the bus states it (0.208011ms)
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (414.58832ms)
✔ queue: a refused read fails closed with the store's text, redacted (210.374206ms)
✔ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (858.43953ms)
✔ business: names, vars on the allowlist, authority, credential metadata only (17.500331ms)
✔ business: missing or invalid file refuses with the module's text, redacted (8.383354ms)
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (8.244873ms)
✔ settings: release, board, loopback address, notifier binding name only (129.222379ms)
✔ task fields: the committed table with no reads configured, GET only; a bad table refuses with no path (18.325509ms)
✔ bus-status: a refused boot shows reason, file and last good load; a live host or an ok boot clears it (8.620563ms)
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (912.269858ms)
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (20871.244615ms)
organization: {"event":"reload-needs-restart","business":"acme"}
organization: {"event":"reload-needs-restart","business":"acme"}
✔ row 65 Settings: the routing table lists every instance, saves a harness, model, thinking and fallback to the file, and keeps the draft on each refusal (1456.057342ms)
organization: {"event":"reload-needs-restart","business":"acme"}
✔ row 65 Console: the signed-in human saves a route through a real broker; each refusal says nothing changed and leaves the file and the log (124.733405ms)
✔ row 65 Console: the routes write needs send on, a writer, the public name, the user, the exact Origin and same-origin Fetch Metadata (7.984846ms)
✔ row 65 Console: the routes write takes JSON within 16 KiB with a revision and routes only; an actor is refused and routes are never logged (9.931147ms)
✔ row 65 Console: a bus refusal maps to its status and says nothing changed; a lost or odd answer says the change may have been made (24.176558ms)
✔ row 65 Console: the routes read takes no arguments, needs a bus and refuses an odd answer (7.91025ms)
✔ the send box: held roles, the draft and caret across a refresh, each receipt, one POST per Send, and the trail link; the board Reply on a 4xx and a 5xx (2577.785116ms)
✔ a sent receipt lists its replies as the page refreshes, oldest first and capped, and marks none delivered or read (11216.600397ms)
✔ startServer takes send only as a function and only with the preview mode (8.305533ms)
✔ the CLI refuses --send without the preview flags, and without a configured bus (622.972543ms)
✔ the CLI with --send and a configured bus sends through the human transport and logs one line (188.71069ms)
✔ without --send the route is read-only like every other write, and config says no send (10.412469ms)
✔ with --send every other write is still 403 read-only, and nothing reaches the board or bus (8.881331ms)
✔ config says send only on the public name with --send on (4.624696ms)
✔ a send without the signed-in user is 401 and is neither sent nor logged, loopback included (6.736458ms)
✔ each request check refuses before anything is sent, and logs the refusal without the body (23.776724ms)
✔ a send passes exactly to, body and in_reply_to, answers the id, and logs it (3.435425ms)
✔ a crafted role name cannot forge a log line (2.520457ms)
✔ a bus refusal is not sent, with its status; an outcome the bus could not confirm is unknown and never retried (8.782419ms)
✔ through the human transport the request is exactly the business, message.send and the keys (100.281561ms)
✔ against a real broker: the message is written once as a human send, and its refusals map (31.579317ms)
✔ loopback host and board origin fail closed (6.349957ms)
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (85.238919ms)
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (49.199627ms)
✔ unreachable board reports URL; CLI rejects unsupported options (479.978274ms)
✔ tester service render carries the tester port alongside existing flags (134.959305ms)
✔ tester service refuses forwarded 7332 even with another main port (111.314937ms)
✔ tester service invalid ports refuse render and install with exit 4 and leave the unit alone (1140.951065ms)
✔ tester service counts the digits as written before it strips leading zeros (106.037477ms)
✔ tester service install and status show the tester port without changing send reporting (550.730447ms)
✔ render prints the unit without the preview flags (50.043501ms)
✔ render carries every serve flag, the preview set included (48.3057ms)
✔ a partial preview set is refused (116.014202ms)
✔ a value the unit cannot carry is refused, for every flag (2672.644014ms)
✔ the rendered unit names none of the human proof agent markers and sets no agent variable (239.356908ms)
✔ install into --dir with --no-reload writes the file whole, then reports unchanged (287.45407ms)
✔ install without --no-reload runs daemon-reload and nothing that starts the unit (62.23267ms)
✔ uninstall refuses while the unit is running, with or without --no-reload, and removes it once it is not (564.150662ms)
✔ status prints the unit state and GET /healthz on the installed port (418.000891ms)
✔ usage: an unknown verb or flag, and start or enable, exit 4 (748.152129ms)
✔ without --send, render is the template with its placeholders filled, byte for byte (191.946387ms)
✔ render --send with the preview set ends ExecStart with --send and changes nothing else (314.493899ms)
✔ --send without the whole preview set is refused and writes nothing (731.918561ms)
✔ install --send into --dir with --no-reload writes the sending unit, and status says whether it sends (513.043402ms)
✔ uninstall and status take no --send (164.863424ms)
✔ install refuses while the unit is running, in any state but inactive or failed, and changes nothing (697.765957ms)
✔ when the state read fails, install and uninstall refuse with one line and change nothing (238.392922ms)
✔ install needs --copy naming a copy that exists, and refuses outside the canonical checkout without --allow-worktree (355.132277ms)
✔ install refuses unless the bus unit runs from the same copy, naming both paths, and changes nothing (735.389915ms)
✔ render --copy runs the copy and reads the queue in the checkout; without --copy the unit is unchanged (114.218835ms)
✔ the uid header takes only an Authentik uid; the username header still takes a username (2531.254234ms)
✔ a preview value cannot start with - or . (295.955168ms)
✔ status names the copy and whether refactor has moved past it, and says when the unit runs from no copy (877.622422ms)
✔ proxy proof render carries both flags but never opens the fixture file (89.223556ms)
✔ proxy proof render refuses incomplete non-preview missing unsafe-mode and repo/copy paths (565.728452ms)
✔ proxy proof status retains send and tester reporting with both proof flags (170.319152ms)
✔ row196 socket flag renders only for supporting targets and refuses mixed transports (114.439527ms)
✔ Settings form shows every scope and held role; gated save, exact payload, retained draft and no automatic retry (1360.394114ms)
✔ Q169 Assistant section: gated, changed fields only, name and stop posts, retained draft and no automatic retry (1339.787079ms)
✔ Row 167 a refused names read gives the refused usage page; another names failure keeps the Assistant section (1001.13625ms)
✔ Row 167 with a limits draft open, an assistant save gives the limits submit its revision and the typed limit stays (961.46178ms)
✔ Row 167 with an assistant draft open, a launch save gives the assistant submit its revision, and reload reads the current one (919.499255ms)
2026-10-24T22:12:18.574Z settings user="jason" changed
✔ REQ-WEB-1 signed-in same-origin Settings write reaches exactly one human operation (20.814698ms)
✔ REQ-WEB-1 Settings writes require enabled send, named Host, identity, exact Origin and same-origin Fetch Metadata (7.338296ms)
✔ REQ-WEB-1 Settings validates JSON, size, revision and strict writable keys before broker call (5.704514ms)
✔ Q169 a Console change of the assistant reaches the human operation unchanged, and the log names the signed-in user (3.12922ms)
✔ tokens.css and icons.svg are byte copies of docs/design (2.466959ms)
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.202837ms)
✔ shipped strings are unchanged, and the web keeps its write boundary (3.483166ms)
ROW191_LAYOUT {"width":1440,"height":1000,"controls":212.5,"tree":914,"main":914}
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (5724.337234ms)
✔ five states on Board, Inbox, Tasks, Agents and Trail (2781.461551ms)
✔ a 401 sign-in required on each fetch path signs the page out with a sign-in link and stops the refresh; another 401 stays an error (5951.403032ms)
✔ the Sign out form shows only when /api/config says sso, and posts to /auth/logout (1240.276745ms)
✔ positive: an RS256 provider signs in and sends one message, and the session reads (144.348303ms)
✔ positive: an ES256 provider signs in and sends (20.592669ms)
✔ positive: two providers each sign in their own subjects, and one provider's subject is unknown to the other (61.371982ms)
✔ 1: a forged x-authentik header without a cookie is 401 on the API and a sign-in redirect on a page (43.593455ms)
✔ 2: an HS256 ID token keyed with the client secret and a real RSA kid is refused (73.089469ms)
✔ 3: a wrong aud, and an aud list holding another client with no azp, are refused (33.086381ms)
✔ 4: a token that expired 10 minutes ago is refused (63.584637ms)
✔ 5: another issuer, and the issuer without its trailing slash, are refused (48.311745ms)
✔ 6: a nonce mismatch and a missing nonce are refused (27.913813ms)
✔ 7: state mismatch, no login cookie, a replay, an expired record and two login cookies are refused (67.213632ms)
✔ 8: the token request carries the PKCE verifier, and the OP refuses a wrong one (46.450232ms)
✔ 9: alg none, a changed payload under a real kid, and an unknown kid even after the refetch are refused (208.50079ms)
✔ 10: a valid token for a subject not configured is 403 with one subject-unknown line (114.43639ms)
✔ 11: start refusals: HS or none algs, reserved names, HS-only discovery, an empty JWKS, no S256, an issuer mismatch, a short RSA key (276.730279ms)
✔ 11: slot files: a symlink, mode 0644, mode 0640 and a file under the repository are refused, and no message holds the value (2.126877ms)
✔ 11: the CLI refuses --sso with the old flags and without console.sso, and starts with a good one (1317.326217ms)
✔ 12: under --sso the tester listener refuses each kind of sign-in, has no /auth, and startServer refuses tester with sso (61.815926ms)
✔ 13: idle and absolute expiry, logout then reuse, a cross-origin logout, and background reads that don't keep a session (112.338051ms)
✔ 13: the CSP lets the Sign out form reach the provider's end-session origin, and only under --sso (136.264868ms)
✔ 14: open redirects land on /, and a path on the Console lands there (125.077087ms)
✔ 15: a callback for A carrying B's code and iss, and a provider= query, is refused (114.160441ms)
✔ 16: a provider with RS256 and ES256 takes either, and refuses PS256 with a usable PS256 key in the JWKS (111.659664ms)
✔ 17: 33 started logins leave 32 records, and the first one's callback finds none (59.820684ms)
✔ 18: after a key rotation a sign-in works through one refetch, and the removed kid is refused (119.662583ms)
✔ 20 and 21: the store holds no raw session id, and the cookie strings are exact (36.447359ms)
✔ 22: a loopback Host is 403 on a page, a read and a write; /healthz on loopback is 200; a redirect_uri never comes from the Host (64.398495ms)
✔ 23: a provider down at start leaves the Console up and 503, and the retry makes sign-in work (225.671097ms)
✔ 19: no captured log line holds a token, code, state, nonce, verifier, client secret or session id (4.654356ms)
✔ tester Console banner exposes only instance route and name editors and saves a route as tester (1004.885424ms)
✔ row 177: the tester Console chat composer is enabled and a send shows in its own thread (882.644221ms)
✔ tester-port validates range tunnel main port and requires configured bus before listening (1166.339822ms)
✔ CLI tester listener binds a private Unix socket and spawns TESTER_CLI for every Console bus read (4245.099179ms)
✔ main listener refuses x-mosaic-tester before any board or bus read for every Host (28.708091ms)
✔ tester listener rejects preview binding and requires its fixed logical Host and no proxy auth header (145.683675ms)
✔ tester listener writes only instances routes and names with tester receipts and actor (158.995936ms)
✔ tester listener refuses every human-only write with no bus call (108.260486ms)
✔ tester-off and tester-required reads return HTTP 403 (109.020476ms)
✔ tester credentials routes and committed project reads keep secrets and paths bounded (194.378994ms)
✔ row 177 W1: the tester chat makes, sends to, opens and reads its own thread as tester (30.75864ms)
✔ row 177 W2: tester chat writes refuse a missing or foreign Origin, cross-site, text/plain and the proxy header with no bus call (16.544702ms)
✔ row 177 W3: the human thread through the tester chat is thread-not-owned, and bus.tester off answers tester-off (38.09277ms)
✔ row 177 W4: the main listener refuses x-mosaic-tester on chat reads and writes before the chat (3.174145ms)
✔ Row 252: the tester listener has no decision answer routes, and its config never offers them (115.156837ms)
✔ private tester HTTP serves on 0600 socket in 0700 parent and fixed origin (92.027264ms)
✔ private tester refuses symlink, unsafe parent and long path without repairs (1.361981ms)
✔ tester cleanup must leave a replacement inode untouched (1.910765ms)
✔ row278 browser lists held claims and release preserves role rows and totals (847.359938ms)
✔ row278 browser claims 403 stays local and role section remains byte-equal (894.870976ms)
✔ row278 browser renders only four escaped claim fields and never instance or other extras (843.980149ms)
✔ row278 browser sign-out removes claims and prevents reuse of cached answers (893.668522ms)
✔ row278 tester browser keeps role rows without requesting the owner claims section (787.543169ms)
✔ row278 browser discards a late claims answer after sign-out (921.720447ms)
✔ row278 HTTP read returns held claims without send and refuses arguments before the bus (53.230904ms)
✔ row278 HTTP read preserves bus refusal and rejects a malformed result (16.041697ms)
✔ row278 tester socket refuses thread claims explicitly with zero bus calls (7.920469ms)
✔ row278 Console adapter calls argument-free thread.claims through the normal transport (234.138591ms)
✔ 193d HTTP shares use the caller, plain/shared lists, exact args and write fences (101.289213ms)
✔ 193d config supplies only the signed-in user live collaborator scopes and updates on downgrade (42.033352ms)
✔ 193d viewer UI has no composer, resume, share, session mutations or create controls (924.760539ms)
✔ 193d mixed grants keep viewer project read-only and collaborator own thread writable; downgrade hides controls (953.979736ms)
✔ 193d owner has share controls on own threads and shared threads remain read-only (790.551799ms)
✔ 193d bypassed UI checks still reach a refusing bus for every viewer write (788.861611ms)
✔ 193d collaborator shares stay read-only and never expose reshare or wake controls (581.454324ms)
✔ REQ-LIMIT-4 Settings shows all token kinds and unknown turns beside every total, as a read-only separate section (995.953487ms)
✔ REQ-WEB-1 usage read keeps unknown counts and each token kind; no query or write is accepted (45.120159ms)
✔ REQ-WEB-1 malformed usage is refused instead of shown as zero (8.503528ms)
✔ R6-N1 with the userRoute gate open, a user session reaches no owner option over HTTP (253.639899ms)
✔ Queue, Business and Settings in their five states, with no move button (3486.52394ms)
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (1584.02624ms)
✔ the server log names no config path when the system config refuses (369.222452ms)
ℹ tests 455
ℹ suites 0
ℹ pass 455
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 174122.853398
@@ -0,0 +1,69 @@
tests/agents-browser.test.mjs
tests/agents.test.mjs
tests/assistant.test.mjs
tests/board-held.test.mjs
tests/browser-edge.test.mjs
tests/browser.test.mjs
tests/bus-browser.test.mjs
tests/bus.test.mjs
tests/channel-chat.test.mjs
tests/chat-browser.test.mjs
tests/chat-instances.test.mjs
tests/chat-replies-browser.test.mjs
tests/chat-sessions-access.test.mjs
tests/chat.test.mjs
tests/conversation.test.mjs
tests/decide-browser.test.mjs
tests/decide-sso.test.mjs
tests/decision-http.test.mjs
tests/discord.test.mjs
tests/docs-browser.test.mjs
tests/editor-browser.test.mjs
tests/editor-provenance.test.mjs
tests/editor-wikilink.test.mjs
tests/editor.test.mjs
tests/granted-user.test.mjs
tests/history-return-flow.test.mjs
tests/instances-browser.test.mjs
tests/instances.test.mjs
tests/library-cli.test.mjs
tests/library-routes.test.mjs
tests/library-status.test.mjs
tests/limits-browser.test.mjs
tests/limits-route.test.mjs
tests/markdown-preview.test.mjs
tests/messages.test.mjs
tests/pinned-copy.test.mjs
tests/preview.test.mjs
tests/processing-browser.test.mjs
tests/processing-ui.test.mjs
tests/processing.test.mjs
tests/project-file.test.mjs
tests/project-name-repo.test.mjs
tests/proof-cli.test.mjs
tests/proof.test.mjs
tests/reads.test.mjs
tests/relaunch.test.mjs
tests/return-flow.test.mjs
tests/routes-browser.test.mjs
tests/routes.test.mjs
tests/send-browser.test.mjs
tests/send.test.mjs
tests/serve.test.mjs
tests/service.test.mjs
tests/settings-form.test.mjs
tests/settings-write.test.mjs
tests/shell.test.mjs
tests/sso-browser.test.mjs
tests/sso.test.mjs
tests/tester-browser.test.mjs
tests/tester-cli.test.mjs
tests/tester-listener.test.mjs
tests/tester-socket.test.mjs
tests/thread-claims-browser.test.mjs
tests/thread-claims.test.mjs
tests/thread-sharing.test.mjs
tests/usage-browser.test.mjs
tests/usage.test.mjs
tests/user-option-swap.test.mjs
tests/views.test.mjs
@@ -0,0 +1,38 @@
# grant sage-4286; nice 19; TMPDIR /mnt/storage/scratch/tmp/g49X; MOSAIC_REQUIRE_EDITOR_BUILD 1
✔ row 194 T16 Settings Agents: unavailable read-only, launch follows its request, confirms, reason and refusal (13526.912326ms)
✔ row 256 T7 T8 the human page offers Delete on a creative instance, the tester page never does (917.27163ms)
✔ row 194 T16: each agent action makes one call with only its keys, logs the user and answers the request (26.28546ms)
✔ row 194 T16: the boundary refuses before any bus call (13.102463ms)
✔ row 194 T16: read-only when send is off or no writer is wired; no route has a --send exception (6.394437ms)
✔ row 194 T16: a bus refusal names its reason and nothing changed; an unknown outcome or a bad shape is never retried (4.282632ms)
✔ row 194 T16: the request read takes one id, adds the refusal reason and refuses an unexpected shape (5.922485ms)
✔ row 194 T16: delete joins add and change on /api/instances with only revision, instance and op (2.724511ms)
✔ row 194 T16 D4.1: the tester listener launches, terminates and reads requests; regenerate, veto and delete are refused with no call (4.723645ms)
✔ row 194 N5: agents.js is served as JavaScript and index.html loads it once (1.551741ms)
✔ row 239 T6: a delete closed -revoked says the tokens may already be revoked, and outcome-unknown-revoked is an open outcome (2.702385ms)
✔ the Board: held roles with no machine sessions, a claim with no address, the toggle kept across a reload, and Message addressed to the role (3264.641316ms)
✔ a Seen click while a Board read is in flight waits for that read, then posts (1197.183961ms)
✔ a redirect answer on a Board read, a bus read, a conversation read, a board POST, a module read and a send signs the page out: one line, nothing stale, no further reads (5156.528249ms)
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (1309.276425ms)
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (1518.992742ms)
✔ the README says a refused field table names the file or the key at fault, never a path (104.682461ms)
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (3538.625499ms)
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (83.53196ms)
✔ decisions route serves the roles and recent lists from the reader view (37.23684ms)
✔ credentials route serves the broker credential status from the reader view (13.661469ms)
✔ replies route serves the replies to one message from the reader view (37.146766ms)
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (57.970332ms)
✔ bus refusals map to statuses and never carry a path or a stack (25.682155ms)
✔ humanCall speaks the human transport protocol without blocking the server (1672.176975ms)
✔ busReader takes --business, else the live bus host, else refuses (188.376745ms)
✔ humanCall kills a transport that runs past its timeout (527.833381ms)
✔ serve refuses a --business value that is not a business id (331.000231ms)
ℹ tests 28
ℹ suites 0
ℹ pass 28
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 17747.118568
@@ -0,0 +1,22 @@
# grant sage-4286; nice 19; TMPDIR /mnt/storage/scratch/tmp/g49X; MOSAIC_REQUIRE_EDITOR_BUILD 1
✔ wikilink round-trip: plain (5.135516ms)
✔ wikilink round-trip: labelled (0.532763ms)
✔ wikilink round-trip: heading target (0.47601ms)
✔ wikilink round-trip: list item (0.674052ms)
✔ wikilink round-trip: next to front matter (0.620153ms)
✔ wikilink round-trip: heading block and combined label (0.718517ms)
✔ wikilink round-trip: spaces, Unicode and adjacent links (0.543729ms)
✔ wikilink round-trip: reference-definition collision (1.267657ms)
✔ wikilink round-trip: inside an ordinary link (0.565479ms)
✔ wikilink round-trip: inert embed (0.373131ms)
✔ wikilinks are inline atoms and survive a neighbouring edit in every required context (773.2922ms)
✔ code, escaped openers, malformed syntax and HTML-like targets stay outside wikilink atoms (686.084659ms)
✔ wikilink DOM paste accepts only bounded single-line inert syntax; malformed attrs serialise as escaped text (675.536422ms)
ℹ tests 13
ℹ suites 0
ℹ pass 13
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 2305.540306
+27
View File
@@ -0,0 +1,27 @@
// Run only under Sage's explicit CLEAR. Reads the pinned baseline Git objects,
// not the edited model or rebuilt candidate bundle. Never writes product files.
import { execFileSync } from 'node:child_process';
import { createHash } from 'node:crypto';
import { fileURLToPath } from 'node:url';
import { dirname, resolve } from 'node:path';
import vm from 'node:vm';
import { WIKILINK_CASES } from '../../../../packages/webui/tests/helpers/wikilink-cases.mjs';
const repo = resolve(dirname(fileURLToPath(import.meta.url)), '../../../..');
const base = '487500a16f4ab5902c318509fdec6acc56a543ca';
const git = (...args) => execFileSync('git', args, { cwd: repo, encoding: 'utf8', maxBuffer: 16 * 1024 * 1024 });
const read = path => git('show', `${base}:${path}`);
const source = read('packages/webui/src/public/editor.js');
const build = JSON.parse(read('packages/webui/editor/BUILD.json'));
const sha256 = createHash('sha256').update(source).digest('hex');
if (sha256 !== build.outputs['src/public/editor.js'].sha256) throw Error('baseline bundle differs from its recorded build');
const context = { console }; context.window = context; context.globalThis = context;
vm.createContext(context);
for (const [name, text] of [['markdown-preview.js', read('packages/webui/src/public/markdown-preview.js')], ['editor.js', source]])
vm.runInContext(text, context, { filename: name });
const results = WIKILINK_CASES.map(({ name, text }) => {
const output = context.MosaicEditor.roundTrip(text);
return { name, input: text, output, equal: output === text };
});
console.log(JSON.stringify({ base: git('rev-parse', base).trim(), bundleSha256: sha256,
measuredAt: new Date().toISOString(), allEqual: results.every(r => r.equal), results }, null, 2));
@@ -0,0 +1,68 @@
{
"base": "487500a16f4ab5902c318509fdec6acc56a543ca",
"bundleSha256": "9c881b13740fae2408717ad75fa1d85bfcfe3fa8b3af0f1b3f2e706c90e80fee",
"measuredAt": "2026-10-24T22:07:22.210Z",
"allEqual": true,
"results": [
{
"name": "plain",
"input": "[[a]]\n",
"output": "[[a]]\n",
"equal": true
},
{
"name": "labelled",
"input": "[[a|b]]\n",
"output": "[[a|b]]\n",
"equal": true
},
{
"name": "heading target",
"input": "[[a#h]]\n",
"output": "[[a#h]]\n",
"equal": true
},
{
"name": "list item",
"input": "- [[a]]\n",
"output": "- [[a]]\n",
"equal": true
},
{
"name": "next to front matter",
"input": "---\nrelated: \"[[metadata]]\"\n---\n\n[[a]]\n",
"output": "---\nrelated: \"[[metadata]]\"\n---\n\n[[a]]\n",
"equal": true
},
{
"name": "heading block and combined label",
"input": "# See [[a#h|Title]]\n",
"output": "# See [[a#h|Title]]\n",
"equal": true
},
{
"name": "spaces, Unicode and adjacent links",
"input": "See [[ Héllo there # A heading | a label ]][[next]]\n",
"output": "See [[ Héllo there # A heading | a label ]][[next]]\n",
"equal": true
},
{
"name": "reference-definition collision",
"input": "[[a]]\n\n[a]: u\n",
"output": "[[a]]\n\n[a]: u\n",
"equal": true
},
{
"name": "inside an ordinary link",
"input": "[x [[a]]](u)\n",
"output": "[x [[a]]](u)\n",
"equal": true
},
{
"name": "inert embed",
"input": "![[cover.png]]\n",
"output": "![[cover.png]]\n",
"equal": true
}
]
}
+2 -2
View File
@@ -4,8 +4,8 @@
"options": "bundle, iife, es2022, browser, legal-comments inline, unminified; aliases in editor/build.mjs",
"outputs": {
"src/public/editor.js": {
"sha256": "9c881b13740fae2408717ad75fa1d85bfcfe3fa8b3af0f1b3f2e706c90e80fee",
"bytes": 758420
"sha256": "bd167899a551c70dc07e500fcdd1853d7fb710b23c217ba5b42daf5785c38a72",
"bytes": 759959
},
"src/public/editor.css": {
"sha256": "8654ac57d1e7a561110138ecd7f701c34a074017ff8c4cd2768d8083908ee61f",
+44 -6
View File
@@ -23,6 +23,13 @@ export const linkTarget = raw => {
return typeof gate === 'function' ? gate(raw) : null;
};
// C2-11a: an inert single-line atom keeps its exact source, including labels
// and heading fragments. HTML/entity-like targets stay ordinary text so the
// existing escaped-text serialiser still protects rewritten blocks.
const wikilinkSource = /^!?\[\[[^\[\]\r\n<>&`]+\]\]$/;
const wikilinkAt = /!?\[\[[^\[\]\r\n<>&`]+\]\]/y;
const isWikilink = value => typeof value === 'string' && wikilinkSource.test(value);
const kept = { raw: { default: null }, sig: { default: null }, lead: { default: '' } };
const block = attrs => ({ ...kept, ...attrs });
@@ -34,7 +41,7 @@ export const schema = new Schema({
horizontal_rule: { group: 'block', attrs: block({}), parseDOM: [{ tag: 'hr' }], toDOM() { return ['div', ['hr']]; } },
heading: {
attrs: block({ level: { default: 1 } }),
content: '(text | image)*', group: 'block', defining: true,
content: '(text | image | wikilink)*', group: 'block', defining: true,
parseDOM: [1, 2, 3, 4, 5, 6].map(level => ({ tag: 'h' + level, attrs: { level } })),
toDOM(node) { return ['h' + +node.attrs.level, 0]; },
},
@@ -79,6 +86,16 @@ export const schema = new Schema({
return ['span', { class: 'md-image', 'data-src': a.src, 'data-alt': a.alt, 'data-title': a.title }, shown];
},
},
// No href, image, target resolution or click handler. C2-11b owns opening.
wikilink: {
inline: true, group: 'inline', atom: true,
attrs: { source: {} },
parseDOM: [{ tag: 'span.md-wikilink[data-source]', getAttrs: dom => {
const source = dom.getAttribute('data-source');
return isWikilink(source) ? { source } : false;
} }],
toDOM(node) { return ['span', { class: 'md-wikilink', 'data-source': node.attrs.source }, node.attrs.source]; },
},
hard_break: { inline: true, group: 'inline', selectable: false, parseDOM: [{ tag: 'br' }], toDOM() { return ['br']; } },
},
marks: {
@@ -118,6 +135,16 @@ export const schema = new Schema({
// ------------------------------------------------------------------ Markdown to nodes
const splitter = MarkdownIt('commonmark', { html: true }).enable('table').enable('strikethrough');
const tokenizer = MarkdownIt('commonmark', { html: false }).enable('table').enable('strikethrough');
// The escape and backtick rules run first. A sticky match avoids copying the
// paragraph suffix at every bracket, and never crosses a link-label boundary.
tokenizer.inline.ruler.before('link', 'wikilink', (state, silent) => {
wikilinkAt.lastIndex = state.pos;
const found = wikilinkAt.exec(state.src);
if (!found || state.pos + found[0].length > state.posMax) return false;
if (!silent) { const token = state.push('wikilink', '', 0); token.content = found[0]; }
state.pos += found[0].length;
return true;
});
// Soft breaks stay line breaks in the text, so a wrapped paragraph keeps its wrapping. A link whose
// target the gate refuses makes the whole block source text (`refused`), so nothing rewrites it.
@@ -151,6 +178,7 @@ const parser = new MarkdownParser(schema, prepared, {
hr: { node: 'horizontal_rule' },
image: { node: 'image', getAttrs: tok => ({ src: tok.attrGet('src'), title: tok.attrGet('title') || null, alt: tok.content || null }) },
hardbreak: { node: 'hard_break' },
wikilink: { node: 'wikilink', getAttrs: tok => ({ source: tok.content }) },
em: { mark: 'em' },
strong: { mark: 'strong' },
link: { mark: 'link', getAttrs: tok => ({ href: tok.attrGet('href'), title: tok.attrGet('title') || null }) },
@@ -174,6 +202,13 @@ const fenceInfo = value => {
const info = typeof value === 'string' ? value.split(/[\r\n]/, 1)[0] : '';
return info.includes('`') ? '' : info;
};
// Share the complete text policy with invalid wikilink attributes. The
// vendored state.text(..., true) alone does not escape HTML or entities.
function escapedText(state, text, escape = true) {
const esc = state.esc;
state.esc = (str, start) => esc.call(state, str, start).replace(/[<&]/g, '\\$&');
try { state.text(text, escape); } finally { delete state.esc; }
}
const link = defaultMarkdownSerializer.marks.link;
const serializer = new MarkdownSerializer({
...defaultMarkdownSerializer.nodes,
@@ -181,17 +216,20 @@ const serializer = new MarkdownSerializer({
const params = fenceInfo(node.attrs.params);
defaultMarkdownSerializer.nodes.code_block(state, params === node.attrs.params ? node : node.type.create({ ...node.attrs, params }, node.content));
},
text(state, node) {
const esc = state.esc;
state.esc = (str, start) => esc.call(state, str, start).replace(/[<&]/g, '\\$&');
try { state.text(node.text, !state.inAutolink); } finally { delete state.esc; }
},
text(state, node) { escapedText(state, node.text, !state.inAutolink); },
bullet_list(state, node) { state.renderList(node, ' ', () => '- '); },
image(state, node) {
const a = node.attrs;
const title = oneLine(a.title);
state.write('![' + state.esc(oneLine(a.alt) || '') + '](' + a.src.replace(/[()]/g, '\\$&') + (title ? ' "' + title.replace(/[\\"]/g, '\\$&') + '"' : '') + ')');
},
wikilink(state, node) {
const source = node.attrs.source, valid = isWikilink(source);
// A programmatic invalid atom must not bypass the same text escaping that
// protects pasted HTML and one-line attributes elsewhere in this model.
if (valid) state.text(source, false);
else escapedText(state, oneLine(String(source ?? '')));
},
raw_block(state, node) { state.text(node.textContent, false); state.closeBlock(node); },
}, {
...defaultMarkdownSerializer.marks,
+45 -8
View File
@@ -18176,6 +18176,9 @@
const gate = globalThis.MosaicMarkdown?.linkTarget;
return typeof gate === "function" ? gate(raw) : null;
};
var wikilinkSource = /^!?\[\[[^\[\]\r\n<>&`]+\]\]$/;
var wikilinkAt = /!?\[\[[^\[\]\r\n<>&`]+\]\]/y;
var isWikilink = (value) => typeof value === "string" && wikilinkSource.test(value);
var kept = { raw: { default: null }, sig: { default: null }, lead: { default: "" } };
var block2 = (attrs2) => ({ ...kept, ...attrs2 });
var schema2 = new Schema({
@@ -18192,7 +18195,7 @@
} },
heading: {
attrs: block2({ level: { default: 1 } }),
content: "(text | image)*",
content: "(text | image | wikilink)*",
group: "block",
defining: true,
parseDOM: [1, 2, 3, 4, 5, 6].map((level) => ({ tag: "h" + level, attrs: { level } })),
@@ -18266,6 +18269,20 @@
return ["span", { class: "md-image", "data-src": a.src, "data-alt": a.alt, "data-title": a.title }, shown];
}
},
// No href, image, target resolution or click handler. C2-11b owns opening.
wikilink: {
inline: true,
group: "inline",
atom: true,
attrs: { source: {} },
parseDOM: [{ tag: "span.md-wikilink[data-source]", getAttrs: (dom) => {
const source = dom.getAttribute("data-source");
return isWikilink(source) ? { source } : false;
} }],
toDOM(node) {
return ["span", { class: "md-wikilink", "data-source": node.attrs.source }, node.attrs.source];
}
},
hard_break: { inline: true, group: "inline", selectable: false, parseDOM: [{ tag: "br" }], toDOM() {
return ["br"];
} }
@@ -18314,6 +18331,17 @@
});
var splitter = lib_default("commonmark", { html: true }).enable("table").enable("strikethrough");
var tokenizer = lib_default("commonmark", { html: false }).enable("table").enable("strikethrough");
tokenizer.inline.ruler.before("link", "wikilink", (state, silent) => {
wikilinkAt.lastIndex = state.pos;
const found2 = wikilinkAt.exec(state.src);
if (!found2 || state.pos + found2[0].length > state.posMax) return false;
if (!silent) {
const token = state.push("wikilink", "", 0);
token.content = found2[0];
}
state.pos += found2[0].length;
return true;
});
var refused = false;
var prepared = {
parse(text2, env) {
@@ -18349,6 +18377,7 @@
hr: { node: "horizontal_rule" },
image: { node: "image", getAttrs: (tok) => ({ src: tok.attrGet("src"), title: tok.attrGet("title") || null, alt: tok.content || null }) },
hardbreak: { node: "hard_break" },
wikilink: { node: "wikilink", getAttrs: (tok) => ({ source: tok.content }) },
em: { mark: "em" },
strong: { mark: "strong" },
link: { mark: "link", getAttrs: (tok) => ({ href: tok.attrGet("href"), title: tok.attrGet("title") || null }) },
@@ -18360,6 +18389,15 @@
const info = typeof value === "string" ? value.split(/[\r\n]/, 1)[0] : "";
return info.includes("`") ? "" : info;
};
function escapedText(state, text2, escape2 = true) {
const esc = state.esc;
state.esc = (str, start) => esc.call(state, str, start).replace(/[<&]/g, "\\$&");
try {
state.text(text2, escape2);
} finally {
delete state.esc;
}
}
var link2 = defaultMarkdownSerializer.marks.link;
var serializer = new MarkdownSerializer({
...defaultMarkdownSerializer.nodes,
@@ -18368,13 +18406,7 @@
defaultMarkdownSerializer.nodes.code_block(state, params === node.attrs.params ? node : node.type.create({ ...node.attrs, params }, node.content));
},
text(state, node) {
const esc = state.esc;
state.esc = (str, start) => esc.call(state, str, start).replace(/[<&]/g, "\\$&");
try {
state.text(node.text, !state.inAutolink);
} finally {
delete state.esc;
}
escapedText(state, node.text, !state.inAutolink);
},
bullet_list(state, node) {
state.renderList(node, " ", () => "- ");
@@ -18384,6 +18416,11 @@
const title = oneLine(a.title);
state.write("![" + state.esc(oneLine(a.alt) || "") + "](" + a.src.replace(/[()]/g, "\\$&") + (title ? ' "' + title.replace(/[\\"]/g, "\\$&") + '"' : "") + ")");
},
wikilink(state, node) {
const source = node.attrs.source, valid = isWikilink(source);
if (valid) state.text(source, false);
else escapedText(state, oneLine(String(source ?? "")));
},
raw_block(state, node) {
state.text(node.textContent, false);
state.closeBlock(node);
@@ -0,0 +1,82 @@
// C2-11a. Unchanged files alone cannot detect escaped wikilinks: the editor
// keeps raw source. The browser checks the structured node and a dirty block.
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { loadEditor } from './helpers/editor-bundle.mjs';
import { WIKILINK_CASES } from './helpers/wikilink-cases.mjs';
import { fixture } from './fixture.mjs';
import { browser } from './browser.mjs';
const editor = loadEditor();
for (const { name, text } of WIKILINK_CASES) test(`wikilink round-trip: ${name}`, () => {
assert.equal(editor.roundTrip(text), text);
assert.equal(editor.roundTrip(text.trimEnd()), text.trimEnd(), 'no trailing newline');
});
const page = async t => {
const f = await fixture(); t.after(() => f.close());
const b = await browser(); t.after(() => b.close());
await b.navigate(f.base);
return b;
};
const mount = text => `(() => {
window.__wikiEditor?.destroy();
const host = document.createElement('div'); document.body.append(host);
window.__wikiEditor = window.MosaicEditor.mount(host, {text: ${JSON.stringify(text)}});
return true;
})()`;
const atoms = `(() => {
const result = [];
window.__wikiEditor.view.state.doc.descendants(node => {
if (node.type.name === 'wikilink') result.push({source: node.attrs.source, atom: node.isAtom, inline: node.isInline});
}); return result;
})()`;
test('wikilinks are inline atoms and survive a neighbouring edit in every required context', { timeout: 120000 }, async t => {
const b = await page(t);
for (const { name, text, sources } of WIKILINK_CASES) {
await b.evaluate(mount(text));
assert.deepEqual(await b.evaluate(atoms), sources.map(source => ({ source, atom: true, inline: true })), name);
assert.equal(await b.evaluate('document.querySelectorAll(".md-wikilink a, .md-wikilink img, .md-wikilink iframe").length'), 0, 'no navigation or resource elements');
const changed = await b.evaluate(`(() => {
const e = window.__wikiEditor; let end;
e.view.state.doc.descendants((node, pos) => { if (node.isTextblock && node.type.name !== 'raw_block') end = pos + node.nodeSize - 1; });
e.view.dispatch(e.view.state.tr.insertText(' changed', end));
const output = e.text(); e.setText(output); return output;
})()`);
assert.ok(changed.includes(' changed'), name);
for (const source of sources) assert.ok(changed.includes(source), `${name}: spelling survives dirty-block serialisation`);
assert.deepEqual(await b.evaluate(atoms), sources.map(source => ({ source, atom: true, inline: true })), `${name}: survives reparse`);
if (text.startsWith('---')) assert.ok(changed.startsWith(text.slice(0, text.indexOf('\n\n') + 2)), 'front matter is untouched');
if (name === 'reference-definition collision')
assert.ok(changed.endsWith('\n\n[a]: u\n'), 'the separate reference-definition block keeps its exact bytes');
}
});
test('code, escaped openers, malformed syntax and HTML-like targets stay outside wikilink atoms', { timeout: 120000 }, async t => {
const b = await page(t);
const text = '\\[[escaped]] `[[inline-code]]`\n\n```\n[[fenced-code]]\n```\n\n[[open\n\n[[]]\n\n[[Q&A]]\n\n[[a`]]`\n\n[[<img src=x onerror=alert(1)>]]\n';
assert.equal(editor.roundTrip(text), text);
await b.evaluate(mount(text));
assert.deepEqual(await b.evaluate(atoms), []);
assert.equal(await b.evaluate('document.querySelector(".ProseMirror").querySelectorAll("img, iframe, script").length'), 0);
});
test('wikilink DOM paste accepts only bounded single-line inert syntax; malformed attrs serialise as escaped text', { timeout: 120000 }, async t => {
const b = await page(t); await b.evaluate(mount('[[a]]\n'));
const result = await b.evaluate(`(() => {
const e = window.__wikiEditor, spec = e.view.state.schema.nodes.wikilink.spec;
const attrs = source => spec.parseDOM[0].getAttrs({getAttribute: () => source});
const invalid = ['not a link', '[[a]]\\n\\n<script>x</script>', '[[<img src=x>]]', '[[a&copy;]]', null].map(source => attrs(source));
const bad = e.view.state.schema.nodes.wikilink.create({source:'[[a]]\\n\\n<img src=x onerror=alert(1)>&copy;'});
e.view.dispatch(e.view.state.tr.replaceWith(1, e.view.state.doc.content.size - 1, bad));
return {valid:attrs('[[a#h|label]]'), invalid, output:e.text()};
})()`);
assert.deepEqual(result.valid, { source: '[[a#h|label]]' });
assert.deepEqual(result.invalid, [false, false, false, false, false]);
assert.ok(!result.output.includes('\n\n<img'));
assert.doesNotMatch(result.output, /(?<!\\)<img/);
assert.match(result.output, /\\<img/);
assert.doesNotMatch(result.output, /(?<!\\)&copy;/);
assert.match(result.output, /\\&copy;/);
});
@@ -0,0 +1,13 @@
// C2-11a byte fixtures shared by the baseline measurement and regression tests.
export const WIKILINK_CASES = [
{ name: 'plain', text: '[[a]]\n', sources: ['[[a]]'] },
{ name: 'labelled', text: '[[a|b]]\n', sources: ['[[a|b]]'] },
{ name: 'heading target', text: '[[a#h]]\n', sources: ['[[a#h]]'] },
{ name: 'list item', text: '- [[a]]\n', sources: ['[[a]]'] },
{ name: 'next to front matter', text: '---\nrelated: "[[metadata]]"\n---\n\n[[a]]\n', sources: ['[[a]]'] },
{ name: 'heading block and combined label', text: '# See [[a#h|Title]]\n', sources: ['[[a#h|Title]]'] },
{ name: 'spaces, Unicode and adjacent links', text: 'See [[ Héllo there # A heading | a label ]][[next]]\n', sources: ['[[ Héllo there # A heading | a label ]]', '[[next]]'] },
{ name: 'reference-definition collision', text: '[[a]]\n\n[a]: u\n', sources: ['[[a]]'] },
{ name: 'inside an ordinary link', text: '[x [[a]]](u)\n', sources: ['[[a]]'] },
{ name: 'inert embed', text: '![[cover.png]]\n', sources: ['![[cover.png]]'] },
];