Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5b2da2203f | ||
|
|
6ac8a1ecd5 | ||
|
|
d60d29aa7a |
No files matched your search
@@ -5585,7 +5585,3 @@ claim. Receipts and limitations in agents/lena/work/queue-296/BUILD.md and run-r
|
||||
2026-10-24T23:37:46Z | Sage | Row 329 (#1827, P1 pinned model proxy, Magica) LANDED 61ebf2c34, pushed 23:31:27Z 10-24. Before: refactor at 669cdf42c. After: Magica's 4 commits (94cde633e, c65fb34ee, d1f6f2b19, 61ebf2c34) cherry-picked onto 669cdf42c, diff vs the gated tree limited to drift paths, range-diff in scratch. Gate: one unchanged full landing gate in /mnt/storage/scratch/sage-r329l/wt (gate.sh 8b4978887 c8a9bcf94), started 23:08:12Z at load 14.35, GATE GREEN 23:29:14Z at load 8.67, no rerun, no test or timeout edit (mos-1634). Host note: two Codex workers cleaned T3 worktrees from about 23:14Z (mos-1639, Vikunja 632). Approvals: Dewey r2 c31643 rev 4047, Boyd r2 c31642 rev 4048. #1827 comment 31650, closed. Vikunja 237 comment 1109 (stays open for P2-P7); C4 P10 red filed as Vikunja 631. Queue: note rev 4063, done rev 4064, committed b24d917ec. DEFERRED: N2 and R2-N1 bound to row 331, N1 bound to P7. Overlap: row 284 (packages/bus/src/broker.mjs) and row 157 (scripts/mosaic-config.mjs) rebase onto 61ebf2c34 before their gates.
|
||||
2026-10-25T00:15:48Z | Sage | Row 284 (#1789, C2-05c library.get scope, Della) LANDED ce0b6ed41, pushed 00:09:52Z 10-25. Before: refactor at 9e2689dff (row 329 landing docs). After: fast-forward to Della's 7 rebased commits 78b932feb..ce0b6ed41; 8 paths, manifest-r4 de505cfd matches the landed tree. Gate: one unchanged full landing gate in /mnt/storage/scratch/sage-r284l/wt (the 329 gate.sh with D and the compose project renamed), started 23:51:19Z at load 1.62, GATE GREEN 00:09:04Z at load 5.20, no rerun, no test or timeout edit. Counts: bus 1020/0, business 283/0, cli 999 pass 1 allowed skip (FED R7), harness 177/0, library 156/0, seat 218/0, webui 458/0, every scripts/test-*.sh rc 0; queue 34 and task 87/93 skips are the standing ones. Della's whole-file runs on ce0b6ed41: library-lookups 12/0, library-tools 5/0, query 34/0. Approval: Webby round 2 delta c31654 rev 4070 (round 1 rev 4062). Overlap sweep: rows 13, 157, 309, gyro/row310 and fethry/row332-p1 clean. #1789 comment 31655, closed. No Vikunja task for the row. Queue: note rev 4071, done rev 4072, committed 7c8dc2033. DEFERRED: Webby N1 (whole-tree listing per get) bound to the next library read-path row; N2 needs nothing; N3 is this gate.
|
||||
2026-10-25T00:18:35Z | Sage | Follow-up tasks for mos-1647 item 3 (Q109): Vikunja 633 (P7 cutover, CLIProxyAPI 8.0.42 startup Grok-npm and antigravity version refreshes, from row 332 isolated runs) and 634 (row 284 Webby N1, library get lists the whole tree). DEFERRED gains the 633 entry and the 634 number for row 284 N1. Filing script and receipt in agents/sage/work/vikunja-setup/.
|
||||
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.
|
||||
2026-10-25T04:36:47Z | Sage | Row 214 (#1715, C2-11a editor wikilink node, Herb) LANDED 3a8e4abad, pushed 04:27Z 10-25. Before: refactor at 78b69e1e6. After: Herb's six commits cherry-picked onto 78b69e1e6 and fast-forwarded. Gate GREEN 04:10:34Z: 18 package suites rc 0 (webui 471/471, cli 999 plus the allowed FED R7 skip), every scripts/test-*.sh rc 0, queue package via test-queue.sh 156. Design skips: queue 34 (not the canonical root), task 87/93 (row 244). Filbert APPROVE c31679 rev 4093. Range-diff equal except commit 5's BUILD-LOG context; 40/41 paths same blob, BUILD-LOG additions only. #1715 closed c31680. Queue note 4094, done 4095, committed ce8bb4d8b.
|
||||
2026-10-25T04:36:47Z | Sage | Row 193 (#1691, threads user roles design, Morgana) LANDED 35f34ad27, doc-only: five design commits cherry-picked onto ce8bb4d8b, adding only agents/morgana/work/queue-193/DESIGN.md (verify-commit ok, 1 path). Duckworth APPROVE c31676 rev 4088 (posted and recorded by Sage; Duckworth has no Gitea credential). No test reads queue-193. Build rows from the design: 204/#1702, 205/#1703 (landed), 275/#1780, 206/#1704, 207/#1705, 208/#1706.
|
||||
2026-10-25T04:58:30Z | Sage | Row 255 (#1760, Vikunja 458, bundled Pocket ID design for Q203, Launchpad) LANDED de3e23dde, doc-only: Launchpad's three design commits e29d8ec90, 9f60021a0 and c8f2bc723 cherry-picked onto 775bbe236 (range-diff equal), adding only agents/launchpad/work/queue-255/DESIGN.md. No test reads queue-255. Webby APPROVE round 1 c31689 rev 4103 (posted by Sage; Webby has no Gitea token). Mos rulings in mos-1666: M1 approved with conditions (rollback removes components before the pointer goes back and the STATE check confirms the key is absent; the loader stays strict inside the key; four test-config.sh cases), R1 chosen, M2 dropped. Sage gate check against Q203, O6, REQ-WEB-2 and REQ-INST-1: consistent. One PRD text gap goes to Mos: REQ-INST-2 says the stack writes the providers entry, and R1 prints it for the user to paste. Webby N1 and N2 bind P4; N3-N8 carry into the P1-P6 build rows, all post-v1.
|
||||
@@ -1,193 +0,0 @@
|
||||
# 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
|
||||
©. 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.
|
||||
@@ -1,168 +0,0 @@
|
||||
# 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 ©, 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.
|
||||
@@ -1,572 +0,0 @@
|
||||
{
|
||||
"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
|
||||
}
|
||||
@@ -1,323 +0,0 @@
|
||||
{
|
||||
"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
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
# 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.
|
||||
@@ -1,49 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
# 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'
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
# 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
|
||||
@@ -1,25 +0,0 @@
|
||||
# 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
|
||||
@@ -1,14 +0,0 @@
|
||||
# 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
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"at": "2026-10-24T22:13:17.560692+00:00",
|
||||
"count": 0,
|
||||
"processes": []
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
# 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
|
||||
@@ -1,30 +0,0 @@
|
||||
# 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)>©\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)>©\n',
|
||||
expected: /(?<!\\)<img/,
|
||||
operator: 'doesNotMatch',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
# 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
|
||||
@@ -1,10 +0,0 @@
|
||||
# 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
|
||||
@@ -1,68 +0,0 @@
|
||||
{
|
||||
"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
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
[
|
||||
{
|
||||
"name": "serializer-removal",
|
||||
"killed": true,
|
||||
"restored": true,
|
||||
"restoredHashesEqual": true
|
||||
},
|
||||
{
|
||||
"name": "invalid-escaping",
|
||||
"killed": true,
|
||||
"restored": true,
|
||||
"restoredHashesEqual": true
|
||||
}
|
||||
]
|
||||
@@ -1,26 +0,0 @@
|
||||
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)
|
||||
@@ -1,3 +0,0 @@
|
||||
# grant sage-4286; nice 19; TMPDIR /mnt/storage/scratch/tmp/g49X; MOSAIC_REQUIRE_EDITOR_BUILD 1
|
||||
|
||||
added 2 packages in 425ms
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"count": 0,
|
||||
"processes": []
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
# 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
|
||||
@@ -1,18 +0,0 @@
|
||||
{"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"}
|
||||
@@ -1,15 +0,0 @@
|
||||
# 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
|
||||
@@ -1,31 +0,0 @@
|
||||
# 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)
|
||||
@@ -1,15 +0,0 @@
|
||||
# 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
|
||||
@@ -1,10 +0,0 @@
|
||||
# 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
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"root": "/home/jwoltje/.t3/worktrees/mosaic-stack/herb-row214-r2",
|
||||
"tmp": "/mnt/storage/scratch/tmp/g49X",
|
||||
"grant": "sage-4286",
|
||||
"candidate": "2ec3f5b5ddab0cb5f2716f8164c25861b741213e"
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"grep": "^# skip plus # SKIP and spec skipped-test lines",
|
||||
"names": []
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
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)
|
||||
@@ -1,489 +0,0 @@
|
||||
# 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
|
||||
@@ -1,69 +0,0 @@
|
||||
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
|
||||
@@ -1,38 +0,0 @@
|
||||
# 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
|
||||
@@ -1,22 +0,0 @@
|
||||
# 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
|
||||
@@ -1,27 +0,0 @@
|
||||
// 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));
|
||||
@@ -1,68 +0,0 @@
|
||||
{
|
||||
"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
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,402 +0,0 @@
|
||||
# Row 255 design: a bundled Pocket ID for users with no OIDC provider (Q203, Gitea #1760, Vikunja 458)
|
||||
|
||||
Launchpad, draft 1, 2026-10-25, on `launchpad/row255` off refactor 78b69e1e6. Brief
|
||||
`docs/plans/2026-10-21_bundled-pocket-id.md` (blob 26f34400). Reviewer Webby. Sage checks it against Q203, O6,
|
||||
REQ-WEB-2 and REQ-INST-1. REQ-INST-2 (PRD 1.10) is post-v1 (mos-1204), and so is everything here.
|
||||
|
||||
This is a design only. Nothing here installs, downloads or starts Pocket ID. It changes nothing on .53, .54 or
|
||||
dragon-lin, which keeps Authentik.
|
||||
|
||||
## Sources
|
||||
|
||||
- Q203 and O6 as the brief quotes them. PRD `docs/prd/mosaic-stack.md` REQ-WEB-2 (:902), REQ-INST-1 (:1509),
|
||||
REQ-INST-2 (:1513) and REQ-INST-3 (:1528). Lead decision 44 items 5 and 6
|
||||
(`docs/plans/2026-09-26_lead-decisions.md:655-663`).
|
||||
- The Q175 design `agents/sage/work/q175-sso/DESIGN.md` sections 4 and 10, and the code row 192 landed:
|
||||
`packages/webui/src/sso-config.mjs` (the `console.sso` shape), `packages/webui/src/sso.mjs` (the slot read,
|
||||
the discovery checks at :105-118, the client at :147-148, the callback at :133, the post-logout URI at :332).
|
||||
- Researcher's note `agents/researcher/work/2026-10-04_vikunja-pocketid.md` part 2 (v2.17.0).
|
||||
- A fresh read of upstream on 2026-10-25, from release metadata and source text only: no binary was fetched or
|
||||
run. The latest stable release is **v2.19.1** (2026-10-22). Source paths below are under
|
||||
`backend/internal/` at tag v2.19.1 in `github.com/pocket-id/pocket-id`, marked "P". Releases v2.18.0,
|
||||
v2.19.0 and v2.19.1 are not marked breaking.
|
||||
|
||||
## What the Console already needs from any provider
|
||||
|
||||
This design keeps the Console's sign-in and start checks exactly as row 192 built them. Pocket ID becomes one
|
||||
`console.sso.providers` entry, the same shape any compliant OP uses (Q175 section 4, "Several providers"). The
|
||||
Console gets no new sign-in path, no password and no proxy-header identity (O6, REQ-WEB-2, REQ-INST-2). The
|
||||
facts that drive the rest of the design:
|
||||
|
||||
- `publicOrigin` and every `issuer` must be https (`sso-config.mjs` `httpsURL`). A plain-http Pocket ID can't
|
||||
be a provider at all.
|
||||
- Each provider needs 1 to 64 `subjects`. A `console.sso` with no subject refuses to load. So the first
|
||||
person's subject has to exist before the Console starts with `--sso` (section 5).
|
||||
- The client authenticates with `client_secret_basic` (`sso.mjs:148`), so the client is confidential.
|
||||
- The slot is `<configDir>/secrets/mosaic-stack/<slot>`: a regular file, owned by the Console's uid, mode
|
||||
with no group or other bits, and outside the repo and every stack copy (`readSlot`).
|
||||
- `config.json` is user-authored. bootstrap.sh creates it and nothing overwrites it (canon invariant 2, Q175
|
||||
section 4). This matters for sections 1 and 5.
|
||||
|
||||
## 1. The choice
|
||||
|
||||
The installer is REQ-INST-3's install command (Vikunja #432; today `stack-upgrade.sh` and the per-case
|
||||
install scripts). It takes one sign-in option per instance:
|
||||
|
||||
- `--oidc existing`. The user points the Console at one or more OIDC providers they already have. Nothing
|
||||
is installed. The user writes `console.sso` themselves, as Q175 step 4 does today. The installer writes
|
||||
nothing into `console.sso`; it prints the entry shape and the two URIs to register
|
||||
(`<publicOrigin>/auth/callback`, `<publicOrigin>/auth/signed-out`).
|
||||
- `--oidc bundled`. The stack installs Pocket ID as an install component (section 2), and
|
||||
`console.sso.providers` holds exactly one entry, `id: "pocket-id"`, whose `issuer` is the component's
|
||||
origin.
|
||||
- No option keeps today's behaviour: no `console.sso`, and the Console runs without `--sso`.
|
||||
|
||||
How the bundled choice is recorded. REQ-INST-1 lets a component be switched on "in the organization file
|
||||
or system config". It has to be the system config. The organization file can be written by agents through
|
||||
`organizationWriter`, which is the reason Q175 B1 put `console.sso` in config.json, and the same reason
|
||||
holds for a component that issues the Console's identities. So config.json gets one new optional key:
|
||||
|
||||
```json
|
||||
"components": {
|
||||
"pocket-id": {
|
||||
"release": "v2.19.1",
|
||||
"origin": "https://id.example.internal",
|
||||
"port": 1411
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The loader (`scripts/mosaic-config.mjs`) checks the shape: `release` is a `vX.Y.Z` tag, and the component script refuses one that
|
||||
differs from the pin file (section 2), `origin` is an https origin, `port` is 1024 to 65535. It also cross-checks: when
|
||||
`components["pocket-id"]` is present, `console.sso` must have exactly one provider, `id` `pocket-id`,
|
||||
`issuer` equal to `origin`. A config with `console.sso` providers and no `components` is the existing
|
||||
choice and loads as today.
|
||||
|
||||
**M1, approved by Mos with three conditions (mos-1666 item 3).** `components` is a new top-level config key.
|
||||
The loader refuses unknown keys today, so this is a config schema change. The conditions:
|
||||
|
||||
1. **Rollback trap.** A copy older than P1 refuses a config that has the key, the same trap Q175 section 4
|
||||
names for `console.sso`. P1 documents the order. On a rollback past P1, first take `components` out of
|
||||
config.json (restore the backup made before the key went in), then roll back the pointer. The STATE check
|
||||
after the rollback reads config.json and confirms the key is absent.
|
||||
2. **Strict inside the key.** The loader refuses any name under `components` other than `pocket-id`, and any
|
||||
field under `pocket-id` other than `release`, `origin` and `port`. Each refusal names the key path and
|
||||
quotes no value, as `checkConsole` does.
|
||||
3. **test-config.sh cases:**
|
||||
- a valid `components` loads;
|
||||
- a malformed one fails, naming the bad key path;
|
||||
- a config without `components` loads;
|
||||
- the cross-check refuses a `console.sso` whose provider doesn't match `origin`.
|
||||
|
||||
What each choice writes into `console.sso` is in section 5. For the bundled choice, Pocket ID is the only
|
||||
providers entry. The design keeps it that way because REQ-INST-2 says "the Console's one providers entry".
|
||||
A user who later adds an existing provider moves to `--oidc existing` and removes the component (section 2).
|
||||
|
||||
## 2. The component, against REQ-INST-1
|
||||
|
||||
**Pinned.** A Pocket ID release is a set of raw binaries plus `checksums.txt` (sha256). From v2.16.0 there is
|
||||
an SPDX SBOM per binary, and the release workflow attests `checksums.txt` through Sigstore (`actions/attest`
|
||||
in `.github/workflows/release.yml`). The repo holds one pin file,
|
||||
`scripts/pocket-id-pins.sh`, sourced the way `scripts/case1-pins.sh` is: the tag, and for each supported platform (linux amd64 and arm64 to
|
||||
start) the asset name and its sha256. The build row copies the sha256 from `checksums.txt` at the tag and
|
||||
records the `gh attestation verify` output for each asset as its evidence. The pin is checked by hand at
|
||||
build time, not trusted at install time. No Pocket ID source and no binary goes in the repo. The binary is
|
||||
not in any image either, since the container install comes after v1 (REQ-INST-3).
|
||||
|
||||
Recommended pin: v2.19.1, the current release. The build row checks it again, because Pocket ID ships a
|
||||
minor every one to four weeks. Upgrading is a pin file change in a reviewed commit.
|
||||
|
||||
**Fetched and checked.** `scripts/component-pocket-id.sh fetch` downloads the pinned asset for the host's
|
||||
platform over https into a new 0700 staging directory, computes its sha256, and compares it with the pin.
|
||||
On a mismatch it deletes the file and refuses with `pocket-id-digest-mismatch`, naming the expected value
|
||||
and the computed one. On a match it installs the binary read-only (0555) at
|
||||
`<dataRoot>/components/pocket-id/bin/pocket-id-<tag>`. The fetch is the only network step, and the user
|
||||
runs it at install. Tests never run it against the network (section 9).
|
||||
|
||||
**Runs.** A systemd user unit, `mosaic-pocket-id.service` (`mosaic-pocket-id-NAME.service` for a named
|
||||
instance, as row 190 names the bus and webui units), as the instance's user:
|
||||
|
||||
- `ExecStart=<dataRoot>/components/pocket-id/bin/pocket-id-<tag>`.
|
||||
- `WorkingDirectory=<dataRoot>/components/pocket-id/run`, a directory the unit owns, so Pocket ID's habit of
|
||||
reading `.env` from the working directory reads nothing we didn't write. The build row checks there is no
|
||||
`.env` there at start.
|
||||
- Environment, all from the rendered unit:
|
||||
- `APP_URL=<origin>` and `HOST=127.0.0.1` (P `common/env_config.go`: HOST defaults to `0.0.0.0`).
|
||||
- `PORT=<port>`.
|
||||
- `DB_CONNECTION_STRING` and `UPLOAD_PATH` as absolute paths under `<dataRoot>/components/pocket-id/data/`
|
||||
(P defaults are `data/pocket-id.db` and `data/uploads`, relative to the working directory).
|
||||
- `ALLOW_USER_SIGNUPS=disabled` and `ALLOW_INSECURE_CALLBACK_URLS=false`.
|
||||
- `EMAIL_ONE_TIME_ACCESS_AS_UNAUTHENTICATED_ENABLED=false` and `..._AS_ADMIN_ENABLED=false`.
|
||||
- The `OUTBOUND_ALLOWED_HOSTS_*` allowlists set empty (added in 2.19.0).
|
||||
- No `TRUST_PROXY` unless section 6's proxy step sets it to the proxy's address.
|
||||
- `ENCRYPTION_KEY` (mandatory since v2.0, at least 16 bytes) never goes in the unit text. It lives in slot
|
||||
`<configDir>/secrets/mosaic-stack/pocket-id-encryption-key`, 0600. The component script generates it from
|
||||
`/dev/urandom` straight into the file and never prints it. The unit passes it with
|
||||
`LoadCredential=` and `ENCRYPTION_KEY_FILE=%d/...`. The build row confirms `ENCRYPTION_KEY_FILE` at the pinned
|
||||
tag; if it isn't there, it refuses rather than putting the key in `Environment=`.
|
||||
- The unit never carries `STATIC_API_KEY`. That key exists only during the enroll run (section 4).
|
||||
|
||||
The data directory holds the SQLite database, Pocket ID's signing key (stored encrypted with
|
||||
`ENCRYPTION_KEY`), and the passkey registrations. It is 0700. Agent sandboxes bind only an allowlist
|
||||
(`packages/seat/src/sandbox.mjs`), and `<dataRoot>/components/` is not on it. The build row adds a sandbox
|
||||
test that pins that.
|
||||
|
||||
**Health-checked.** `GET http://127.0.0.1:<port>/healthz` returns 204 (P `controller/healthz_controller.go`).
|
||||
`component-pocket-id.sh status` reports the unit state, the binary tag against the pin, the binary's sha256
|
||||
against the pin, and the health result. The Console's own `/healthz` already shows `sso: { "pocket-id": ready
|
||||
| unreachable | refused }` from its discovery retries, so an operator sees the provider from either side.
|
||||
|
||||
**Removable.** `component-pocket-id.sh remove` refuses while `console.sso` still names `pocket-id`, so the
|
||||
Console can't be left pointing at a provider that's gone. Once the user has taken the entry out (or moved to
|
||||
`--oidc existing`), the script stops and removes the unit, then moves the data directory to
|
||||
`<dataRoot>/components/pocket-id.removed-<utc>` with a receipt line in `<dataRoot>/state/components.log`
|
||||
(append-only). It never deletes the passkey database outright. Deleting the moved directory and the
|
||||
encryption key slot is a separate `purge` verb that names both paths and needs `--yes`. Removing the
|
||||
`components` key from config.json is the user's edit.
|
||||
|
||||
## 3. The Q175 start checks against the bundled instance
|
||||
|
||||
The Console checks are unchanged. Here is each one against Pocket ID, and the setting it needs:
|
||||
|
||||
| Check (row 192) | Pocket ID v2.19.1 | Default that fails | Fix |
|
||||
|---|---|---|---|
|
||||
| `issuer` is https (loader) | issuer = `APP_URL` exactly (P `controller/well_known_controller.go`) | `APP_URL` defaults to `http://localhost:1411` | `APP_URL=<https origin>`, section 6 |
|
||||
| discovery `issuer` equals config `issuer` | equal when `APP_URL` has no trailing slash; Pocket ID refuses a path | none | config `issuer` = `origin`, no trailing slash; the loader cross-check compares them |
|
||||
| `id_token_signing_alg_values_supported` overlaps `algs` | advertises only the current key's alg. RS256 (RSA-2048) by default (P `service/jwt_service.go`) | none | `algs: ["RS256"]` |
|
||||
| `code_challenge_methods_supported` has `S256` | advertises `plain` and `S256` | none | none. The Console always sends S256 (`sso.mjs:221`) |
|
||||
| JWKS has a usable key for `algs` | one key at `/.well-known/jwks.json` | none | none. The build row's fake OP pins `use`/`alg`/`kid` as Pocket ID emits them |
|
||||
| `end_session_endpoint` is https | `<APP_URL>/api/oidc/end-session` | http, if `APP_URL` is http | same fix as row 1 |
|
||||
| `redirect_uri` is registered | exact match first, then wildcard patterns; loopback callbacks match any port (P `utils/callback_url_util.go`) | `ALLOW_INSECURE_CALLBACK_URLS` defaults to true | register exactly `<publicOrigin>/auth/callback`, with no `*`; set `ALLOW_INSECURE_CALLBACK_URLS=false` |
|
||||
| post-logout URI | per-client `logoutCallbackURLs`, same matching | none | register exactly `<publicOrigin>/auth/signed-out` |
|
||||
| ID token alg in `algs`, `iss` match | as above | none | none |
|
||||
|
||||
Two points the Console doesn't check, set on the client anyway:
|
||||
|
||||
- `pkceEnabled: true`. Then Pocket ID refuses an authorize request that has no `code_challenge`. Pocket ID has
|
||||
no per-client S256-only setting (P `oidc/provider.go` turns on `plain` for the server). That doesn't relax
|
||||
anything: the Console sends S256 every time, and `plain` stays available only to other clients.
|
||||
- `isPublic: false`, which matches `client_secret_basic`.
|
||||
|
||||
Key rotation. `pocket-id key-rotate --alg ...` replaces the one signing key (P `cmds/key_rotate.go`). After a
|
||||
rotation to another alg, the Console's discovery check refuses on the next restart (no overlap with
|
||||
`algs`), and that is the intended fail-closed result. A rotation is a user step that updates `algs` in the
|
||||
same edit. The component script never rotates.
|
||||
|
||||
## 4. The client, and where its secret goes
|
||||
|
||||
The person running the install creates the client through `component-pocket-id.sh enroll`. No agent runs
|
||||
this verb. It refuses when any agent marker is in its environment: the `MARKERS` list in
|
||||
`packages/bus/src/human.mjs` (`MOSAIC_RUN_ID`, `MOSAIC_BUS_CAP`, `CLAUDECODE` and the rest) that the human
|
||||
boundary uses. A test pins the refusal.
|
||||
|
||||
Enroll needs admin rights before any person has a passkey. Pocket ID offers two ways to get them, and one of
|
||||
them is a hazard:
|
||||
|
||||
- `/setup` and its backing `POST /api/signup/setup` are unauthenticated: whoever calls first becomes admin
|
||||
(P `usersignup/service.go`). Sandboxed agents share the host's network namespace (`sandbox.mjs` unshares
|
||||
user, pid and ipc, not net), so any agent could reach a loopback Pocket ID and take that first admin.
|
||||
- `STATIC_API_KEY` is a fixed admin key with no expiry (P `apikey/service.go`). It doesn't count as
|
||||
completing setup, so `/setup` stays open while it's set.
|
||||
|
||||
So enroll never lets the real unit serve before an admin person exists. It runs the pinned binary once in a
|
||||
**private network namespace** (`unshare --user --net`, or `bwrap --unshare-net` with the binary and data
|
||||
directory bound), where only enroll's own loopback can reach it:
|
||||
|
||||
1. Generate a one-run static key into a 0600 temp file in a new 0700 directory, and start Pocket ID inside
|
||||
the namespace with `STATIC_API_KEY_FILE` pointing at it, plus the unit's environment.
|
||||
2. Wait for `/healthz` 204.
|
||||
3. Create the admin user, `POST /api/users` with `isAdmin: true` and the username and email given with
|
||||
`--admin-user` and `--admin-email`. Keep the returned `id`. That is the OIDC `sub` (P
|
||||
`oidc/claims_service.go` uses `user.ID`, a UUIDv4 set at creation, so it's stable).
|
||||
4. Create the Console client: client id `mosaic-console-<instance>`, callback and logout URLs exactly as in
|
||||
section 3, `pkceEnabled: true`, `isPublic: false`. Supply the secret instead of letting Pocket ID
|
||||
generate it. Enroll generates 48 random bytes, base64url, and writes them straight to slot
|
||||
`<configDir>/secrets/mosaic-stack/console-sso-pocket-id` (0600, `O_CREAT|O_EXCL`, not a symlink). It sends
|
||||
the same bytes in the create body over the namespace's loopback (`dto/oidc_dto.go` accepts a supplied
|
||||
secret of 16 or more characters). The value is never printed, logged or put in an argv, and a test pins
|
||||
that it appears in no output.
|
||||
5. Mint a one-time access token for the admin, `POST /api/users/<id>/one-time-access-token`.
|
||||
6. Stop the namespaced process, then shred and remove the static key file. The real unit has never had a
|
||||
static key, so no admin key survives enroll.
|
||||
7. Start the real unit and wait for `/healthz` 204.
|
||||
8. Print the sign-in link `<origin>/lc/<token>` to the user's terminal (section 5), and the `console.sso`
|
||||
entry (section 5).
|
||||
|
||||
The build row confirms each route and body against the pinned tag's source before it writes the client (the
|
||||
upstream read for this design didn't trace the create-client route). If any of steps 1 to 7 fails, enroll
|
||||
stops the process, removes the static key, leaves no slot file it created, and refuses with the step name.
|
||||
|
||||
The secret never enters the repo or an image. It sits in two places only: the slot file and Pocket ID's
|
||||
database (stored as a hash). The Console's `readSlot` reads it as it does any provider's.
|
||||
|
||||
## 5. The first person, and their subject in `console.sso`
|
||||
|
||||
Pocket ID is passkey-only for people (REQ-INST-2). The installing user enrolls like this:
|
||||
|
||||
1. Enroll prints `<origin>/lc/<token>`. The token is valid for one hour and is a credential, so it goes only
|
||||
to the terminal of the person who ran enroll. It goes in no log and no file.
|
||||
2. The user opens it in a browser that can reach `origin` (section 6). Pocket ID signs them in and asks them
|
||||
to add a passkey. Without one they can't sign in again. If the hour passes, `pocket-id
|
||||
one-time-access-token <username>` (P `cmds/one_time_access_token.go`), run by the user against the data
|
||||
directory, prints a new link. `component-pocket-id.sh relink` wraps that call.
|
||||
3. Enroll already has the subject, the `id` from step 3 of section 4. It doesn't wait for a sign-in or read a
|
||||
`subject-unknown` log line.
|
||||
|
||||
How the subject gets into `console.sso`. config.json is user-authored and never auto-written. Two ways
|
||||
to respect that; I recommend the first:
|
||||
|
||||
- **R1 (recommended).** Enroll prints the full entry and writes nothing:
|
||||
|
||||
```json
|
||||
"console": { "sso": { "publicOrigin": "<publicOrigin>", "providers": [ {
|
||||
"id": "pocket-id", "issuer": "<origin>", "clientId": "mosaic-console-<instance>",
|
||||
"clientSecret": { "slot": "console-sso-pocket-id" }, "algs": ["RS256"],
|
||||
"subjects": [ { "sub": "<id>", "name": "<admin-user>" } ] } ] } },
|
||||
"components": { "pocket-id": { "release": "<tag>", "origin": "<origin>", "port": <port> } }
|
||||
```
|
||||
|
||||
The user pastes it into config.json, as with Q175 step 4. Rerunning `scripts/bootstrap.sh` (or
|
||||
`--instance NAME`) then validates it through the loader, including the section 1 cross-check, and writes
|
||||
nothing. Nothing changes in invariant 2.
|
||||
- **R2 (rejected, Sage sage-4401: R1 chosen).** `enroll --write-config` would write those two keys itself, with a backup of config.json first, and
|
||||
refuse if `console` or `components` were already present. That would be the first tool to modify an
|
||||
existing config.json, against canon invariant 2's "never overwritten" practice. Not built.
|
||||
|
||||
Whose subject. Enroll creates one person, the one running it, and only that subject goes in the entry. On
|
||||
Jason's instances, only his subject goes into `console.sso` (mos-1197). Further people are added with
|
||||
Pocket ID's admin UI (a user plus a one-time link), and their subjects go into `console.sso` by the user's
|
||||
edit, never by an agent. Pocket ID users who aren't in `subjects` can sign in to Pocket ID, but the Console
|
||||
refuses them `subject-unknown`, so REQ-WEB-2's "must map to an authorized human" holds.
|
||||
|
||||
## 6. Reach: origin, TLS and proxy
|
||||
|
||||
WebAuthn takes its RP id from `APP_URL`'s host name, and its allowed origin is `APP_URL`
|
||||
(P `webauthn/module.go`). Browsers allow passkeys only on a secure origin. The Console also needs https for
|
||||
both `publicOrigin` and `issuer`. Pocket ID serves plain http, and I found no TLS listener setting in its
|
||||
environment config. So:
|
||||
|
||||
- Pocket ID always binds `127.0.0.1:<port>`. That is the default, and the component never binds anything
|
||||
else (`HOST=127.0.0.1`; the status verb refuses a unit whose `HOST` differs).
|
||||
- `origin` is an https origin served by a TLS front that the user already has for the Console. The Console
|
||||
needs one anyway, since `publicOrigin` is https. Pocket ID goes behind the same front under **its own host
|
||||
name**, for example `id.<console host>`. Pocket ID doesn't support a path prefix (`APP_URL` must have no
|
||||
path). A separate host name also keeps Pocket ID's cookies apart from the Console's
|
||||
`__Host-mosaic_*` cookies; the two share cookies if they sit on one host name on different ports.
|
||||
- The front reaches Pocket ID on loopback, so the front has to run on the same host. If it doesn't, Pocket
|
||||
ID would need a non-loopback bind, and that is a `network.expose` hard wait (REQ-WAIT-1), not a component
|
||||
option.
|
||||
- `TRUST_PROXY` is set to the front's address only, never `true`.
|
||||
- The Console fetches discovery and JWKS from `issuer` over https from the server side, so the Console's
|
||||
node has to trust the front's certificate. A public CA does that. A private CA needs
|
||||
`NODE_EXTRA_CA_CERTS` in the Console unit, which the build row adds as a render flag.
|
||||
|
||||
These steps are the user's, and on Jason's hosts they are Jason's, because each one changes who can reach
|
||||
the Console or Pocket ID, or who the host trusts:
|
||||
- J1: the DNS name for `origin`;
|
||||
- J2: the certificate for it, or a private CA and its trust in the browser and in `NODE_EXTRA_CA_CERTS`;
|
||||
- J3: the TLS front's route from `origin` to `127.0.0.1:<port>` (a Traefik change on Jason's hosts);
|
||||
- J4: any listener off loopback.
|
||||
|
||||
The component checks and refuses; it never does them itself. With none of them done, Pocket ID runs on
|
||||
loopback with an https `APP_URL` that nothing serves yet. Enroll's step 2 health probe goes straight to
|
||||
`127.0.0.1`, so enroll still works. The person can't add a passkey until J1 to J3 are done.
|
||||
|
||||
## 7. Agents
|
||||
|
||||
Unchanged. Agents keep their per-role service tokens (lead decision 44 item 6, REQ-INST-2). Pocket ID issues
|
||||
nothing an agent uses. The Console client is the only client enroll creates. No API, no client-credentials
|
||||
grant and no admin API key exist after enroll. Residuals, stated plainly:
|
||||
|
||||
- Agents can reach `127.0.0.1:<port>`, because the sandbox shares the host network. What they can reach there
|
||||
is the public sign-in pages, discovery and JWKS. Signup is disabled, email codes are off, `/setup` is
|
||||
closed because an admin exists, and there is no static key. A test of the rendered unit pins all four
|
||||
settings.
|
||||
- The data directory and the two slots are outside every sandbox bind (section 2).
|
||||
- A seat that ran as the same OS user outside a sandbox could read the data directory. The Console's
|
||||
existing slot has the same exposure; this design adds no new class of it.
|
||||
|
||||
## 8. Instances
|
||||
|
||||
One Pocket ID per named instance, never shared:
|
||||
|
||||
- Each instance has one system config (row 190), and `components` and `console.sso` live in it. A shared
|
||||
Pocket ID would make one instance's config depend on a unit, a data directory and an encryption key that
|
||||
belong to another instance's config and data root.
|
||||
- Unit names, ports, data directory, slots and removal are all per instance (`mosaic-pocket-id-NAME`,
|
||||
`<dataRoot of NAME>/components/pocket-id`), so removing one instance's component can't break another's
|
||||
sign-in.
|
||||
- Each instance needs its own `origin` (section 6), so passkeys are per instance too. A person enrolls once
|
||||
per instance.
|
||||
- An instance that wants to use another instance's Pocket ID can still do it with `--oidc existing`. Then it
|
||||
is just an OIDC provider, with its own client registered by that instance's owner. Nothing special-cases it.
|
||||
|
||||
Two instances on one host each need their own `port`. The loader can't see other instances' configs, so
|
||||
`component-pocket-id.sh` refuses at fetch and enroll when the port is already bound on loopback.
|
||||
|
||||
## 9. Tests, stand-ins only
|
||||
|
||||
No test downloads or starts a real Pocket ID, and no test touches auth.diversecanvas.com,
|
||||
mosaic.woltje.com or 127.0.0.1:7332. The stand-ins:
|
||||
|
||||
- **Fake Pocket ID OP**, a node server like row 192's fake OP, shaped from v2.19.1:
|
||||
- the discovery fields `well_known_controller.go` emits (issuer = origin, `plain` and `S256`,
|
||||
`end_session_endpoint` at `/api/oidc/end-session`, the three auth methods);
|
||||
- one RS256 JWK at `/.well-known/jwks.json`;
|
||||
- a token endpoint that takes only `client_secret_basic`;
|
||||
- `/healthz` 204.
|
||||
|
||||
The Console starts against it under `--sso` with the section 5 entry, with no check changed. Negative
|
||||
cases:
|
||||
- an http issuer refuses at load;
|
||||
- an ES256-only JWKS against `algs: ["RS256"]` refuses at discovery;
|
||||
- an issuer with a trailing slash refuses on the cross-check.
|
||||
- **Fake admin API** on the same stand-in: `POST /api/users`, the client create, the one-time token, and a
|
||||
static-key check. Enroll runs against it with the binary replaced by a stub script that serves the stand-in.
|
||||
The tests pin:
|
||||
- the namespace argv;
|
||||
- the slot file's mode and `O_EXCL`;
|
||||
- that the secret appears in no stdout, stderr or argv;
|
||||
- the static key file gone after success and after a failure at each step;
|
||||
- the refusal under an agent marker in the environment.
|
||||
- **Fetch**: a local file served by a test https server (self-signed, trusted only by the test) with a
|
||||
matching pin, a mismatched pin and a truncated file.
|
||||
- **Unit render**: the environment table in section 2. `HOST=127.0.0.1`, no `STATIC_API_KEY`, no
|
||||
`ENCRYPTION_KEY` value in the text, and `ALLOW_USER_SIGNUPS=disabled`.
|
||||
- **Loader**: test-config.sh cases for the `components` key and the cross-check, and the copy-rollback note.
|
||||
- **Sandbox**: `<dataRoot>/components/` is in no bind list.
|
||||
|
||||
## 10. Build rows, in landing order
|
||||
|
||||
All are post-v1 (REQ-INST-2). Each row has Webby or Sage's named reviewer and its own gate. None of them
|
||||
runs on .53, .54 or dragon-lin, and none installs Pocket ID on any host.
|
||||
|
||||
1. **P1, loader key.** `components["pocket-id"]` in `mosaic-config.mjs`, strict inside the key, and the
|
||||
cross-check against `console.sso` (section 1, M1 conditions from mos-1666 item 3). P1 also writes the
|
||||
rollback order into `docs/TOOLS.md` beside Q175's: remove `components` (restore the backup) before the
|
||||
pointer goes back, and the post-rollback STATE check confirms the key is absent from config.json. Gate:
|
||||
test-config.sh green with the four M1 cases.
|
||||
2. **P2, pin and fetch.** `scripts/pocket-id-pins.sh` with the build-time sha256 and the
|
||||
attestation evidence, and `component-pocket-id.sh fetch|verify`. Gate: the fetch tests against the local
|
||||
stand-in; the evidence file shows `gh attestation verify` output for each pinned asset.
|
||||
3. **P3, unit.** The unit template, `render|install|status|remove|purge`, and the encryption key slot. Gate:
|
||||
the render and remove tests, and the sandbox bind test.
|
||||
4. **P4, enroll.** The namespaced bootstrap, the client, the subject, `relink`, and printing the entry (R1),
|
||||
(R2 rejected). Gate: the enroll tests in section 9, and a secret-leak grep over every test log.
|
||||
5. **P5, Console against the fake Pocket ID.** The section 9 fake-OP start tests, and the
|
||||
`NODE_EXTRA_CA_CERTS` render flag for the Console unit. Gate: the webui suite, with no change to
|
||||
`sso.mjs` checks (a diff of `sso.mjs` and `sso-config.mjs` against the base shows none).
|
||||
6. **P6, installer option.** `--oidc existing|bundled` on REQ-INST-3's install command, calling P2 to P4 in
|
||||
order, and the docs: `docs/TOOLS.md`, the host run sheet's J1 to J4 steps. Gate: the installer's own
|
||||
suite against the stubs; the docs list J1 to J4 as the user's steps.
|
||||
|
||||
## Open items for review
|
||||
|
||||
- M1 approved by Mos with three conditions (mos-1666 item 3), written into section 1 and P1.
|
||||
- M2 dropped: Sage chose R1 and rejected R2 (sage-4401).
|
||||
- **Q1 (Webby):** the private network namespace for enroll (section 4). The other option is to start the
|
||||
real unit and race `/setup`, which I think is unsafe while agents share the host network.
|
||||
- **Q2 (Webby):** one-providers-entry for the bundled choice (section 1). REQ-INST-2's wording reads that
|
||||
way. Mixing a bundled Pocket ID with existing providers would need Sage's ruling.
|
||||
- Not covered: Pocket ID for Vikunja or Gitea people sign-in (the Researcher note's part 3), and
|
||||
client-credentials between Mosaic services. Neither is in Q203.
|
||||
@@ -1,482 +0,0 @@
|
||||
# Row 193 design: many humans per business, human roles, private and shared threads
|
||||
|
||||
Morgana (Opus 5.5), 2026-10-20. Revision 5: sage-1373 item 1 (mos-1023 item 2), the user sentence names every record the owner sees. Revision 4: duckworth-0011 B3 and N5. Revision 3: duckworth-0008 B1, B2 and N1-N4, sage-1368. Revision 2: sage-1354 (5.7 private from the owner, 193b split) and gizmoduck-0018 (coverage and the R4 predicate in FED r4.1). Gitea #1691, queue row 193, brief `docs/plans/2026-10-20_human-users.md`
|
||||
(blob f7472d1e47b1). Reviewer Duckworth, gate owner Sage. Base: `refactor` at 53bb59462.
|
||||
|
||||
Read for this design: COMMON.md (53bb59462), the brief, Gizmoduck's federation design r4.4 (b67bc6de9 on
|
||||
gizmoduck/federation-design, cited as FED; r3.1 a2840a6be was read first), Duckworth's federation reviews (b402197ff, 03a663cd7,
|
||||
45cbe5896 on duckworth/reviews), Sage's Q175 SSO design (`agents/sage/work/q175-sso/DESIGN.md`), the row 192
|
||||
and row 196 briefs, Darkwing's row 190 packet (uncommitted, sha256 5493c9df3ec0...), and the bus, business,
|
||||
cli and webui code at the lines cited. Messages: morgana-0001 to 0003, sage-1338, gizmoduck-0009,
|
||||
gander-0002 and gander-0005.
|
||||
|
||||
## 1. Summary
|
||||
|
||||
- Three human roles: **owner**, **collaborator**, **viewer**. The owner is the business file's one `human`,
|
||||
as today. Collaborators and viewers are access.json users, the same store federation uses (FED 3.5).
|
||||
Their role is the `level` of a grant, so a role is per project and instance, not per person.
|
||||
- Every human principal is minted by one bus verifier, owned by row 196 (Gander). Row 193 states what it
|
||||
needs from that verifier (section 6) and adds no second mechanism.
|
||||
- Threads stay private to their owner by default. A thread owner may share a thread, read-only, with a
|
||||
named user, with the owner, or with everyone granted on it. Shares live in a new append-only bus table,
|
||||
`thread_shares`. This is a deviation from brief point 4, agreed by Sage (sage-1338), for Duckworth to rule
|
||||
on (section 5.1).
|
||||
- A share never widens a grant. It counts only while the reader's live grant covers the thread's project
|
||||
and instance, and while the thread's owner still has coverage. Revocation, expiry and unsharing take
|
||||
effect on the next request.
|
||||
- Existing businesses keep working unchanged: every existing thread is already owned by the business
|
||||
human, and an instance without access.json has exactly one human, the owner.
|
||||
- The build splits so the part without a schema change can land first. `thread_shares` needs schema 3j and
|
||||
a `--migrate` window with Jason.
|
||||
|
||||
## 2. What exists today (measured at 53bb59462)
|
||||
|
||||
- One `human` per business (`packages/business/src/business.mjs:160`). `bindHuman` refuses any other name
|
||||
(`packages/bus/src/broker.mjs:532-538`).
|
||||
- The owner's CLI proof is path-bound (#225): `verifyHuman` checks uid, start time, nonce
|
||||
(`MOSAIC_BUS_CLI_NONCE`), `argv[1]` equal to the tree's `human-cli.mjs`, then walks parents and refuses
|
||||
agent markers, launch records and agent binaries (`packages/bus/src/human.mjs:64-96`).
|
||||
`authenticateHuman` binds the proof to `businesses[business].human` (`packages/bus/src/runtime.mjs:175-182`).
|
||||
- Threads: `threads(seq,id,at,business,project,instance,owner,name)`, append-only
|
||||
(`packages/bus/schema.sql:247`, triggers at :310-312). `#owner(s)` returns `tester` or `s.human`
|
||||
(broker.mjs:364-366). `#thread` refuses a non-owner with `thread-not-owned` (:357-362).
|
||||
- Thread verbs are gated `if (!s.tester) this.#human(s)`: `project.list` (:2318), `thread.create` (:2325),
|
||||
`thread.list` (:2351, owner filter), `thread.open` (:2365), `thread.send` (:2374, writes `human.input`
|
||||
for the human only), `thread.messages` (:2401).
|
||||
- Board reads are business-wide and human or reader only: `inbox`, `agents`, `tasks`, `trail` (:2143-2167),
|
||||
`decisions` (:2174), `credentials` (:2195), `messages` (:2217, `to_role='human'`), `replies` (:2422).
|
||||
`trail` and `replies` return any message in the business by id (:2679-2700, :2422-2432).
|
||||
- The Console reads threads by polling the same verbs (`packages/webui/src/chat.mjs:22-26`). It has no
|
||||
stream, so a refusal on the next poll ends access.
|
||||
- The launcher restarts a thread with pending messages as the business human
|
||||
(`packages/cli/src/launcher.mjs:533-542`).
|
||||
|
||||
## 3. The human model (brief point 1)
|
||||
|
||||
### 3.1 Who the humans are
|
||||
|
||||
| Human | Where it is declared | How many | Identity |
|
||||
|---|---|---|---|
|
||||
| owner | business file `human` (unchanged) | exactly one per business | the business human's name |
|
||||
| user (collaborator or viewer) | access.json `users[]` with `home: "local"` (or a peer id under federation) | any number | `user:<id>` |
|
||||
| tester | not a human; row 177 test principal | one | `tester` |
|
||||
|
||||
The owner stays outside access.json (FED 9 Q2). A business has no second owner and no admin level. Adding
|
||||
one is out of scope; section 9 lists it as a follow-up for Jason.
|
||||
|
||||
### 3.2 How a sign-in or a call becomes exactly one human
|
||||
|
||||
| Entry | Becomes | Checked by |
|
||||
|---|---|---|
|
||||
| Console SSO session whose `(provider, sub)` is in `console.sso.providers[].subjects` (row 192) | the owner | the Console after OIDC checks, then the bus verifier (row 196) |
|
||||
| Console SSO session whose `(provider, sub)` is a `users[].subjects` entry in access.json with a live grant | that one user | same |
|
||||
| Console SSO session matching neither | nobody: refused | the Console, and the bus refuses any later claim |
|
||||
| CLI call through `human-cli.mjs` that passes the human proof | the owner | the bus (`verifyHuman`) |
|
||||
| Any other process, including an agent | nobody | the bus |
|
||||
|
||||
Why each is exactly one:
|
||||
|
||||
- The R1 loader refuses a `(provider, sub)` pair that appears twice in access.json or also in
|
||||
`console.sso.subjects` (FED 3.5). So one subject maps to at most one human.
|
||||
- A user id can't be `human`, `tester` or any business's human (FED 3.5). So a user can't be named as the
|
||||
owner.
|
||||
- A terminal maps only to the owner. The only identity the bus can verify for a local process is the
|
||||
instance's OS user, and that is the owner's. A user has no CLI path in row 193. This follows Mos's B1 rule
|
||||
(mos-0985): the principal comes from what the bus verifies, never the process's own claim.
|
||||
- The bus never reads a user name from a request body as identity. The verifier returns the principal
|
||||
(section 6).
|
||||
|
||||
## 4. Human roles (brief point 2)
|
||||
|
||||
### 4.1 The three roles
|
||||
|
||||
- **owner**: everything the business human does today. Only the owner restructures the organization
|
||||
(business file writer, project and role changes) and only the owner manages users and grants.
|
||||
- **collaborator**: FED 3.5's level, unchanged. Creates threads in granted projects on granted instances,
|
||||
sends into and opens its own threads, and in row 193 also shares its own threads.
|
||||
- **viewer**: new. Reads threads shared with it, within its grant. It creates, opens and sends nothing, so
|
||||
it never drives an instance. FED 3.5 notes that a collaborator's message drives the instance with the
|
||||
owner's role authority (F10). A viewer has none of that.
|
||||
|
||||
The word "collaborator" stays, so FED's tests and help text need no rename. The brief's example "member" is
|
||||
this level.
|
||||
|
||||
A role is the `level` on a grant, and a grant is per business, project set and instance set. A user may
|
||||
hold a collaborator grant on one project and a viewer grant on another. Every check asks "does a live
|
||||
grant of a level that allows this verb cover this project and instance". A user has no single role.
|
||||
|
||||
### 4.2 R1 widening
|
||||
|
||||
FED R1 refuses any level but `collaborator`, because the 10-27 slice ships only that (FED 9 Q2).
|
||||
Row 193 widens R1's level check to `collaborator` or `viewer`, with its own test that each is accepted and
|
||||
anything else refuses. Nothing else in the access file changes. If R1 has not landed when row 193 builds,
|
||||
this one-line widening goes into the row 193a candidate chained on R1's candidate (Q177).
|
||||
|
||||
### 4.3 Bus verbs per role
|
||||
|
||||
Every verb not in a user's column fails `access-refused` before its own checks, as FED 3.5 requires for
|
||||
collaborators. That covers every owner verb, every board read, `message.*`, `decision.*`, project files,
|
||||
`spec.read`, `assistant.stop`, `launch.*`, `name.set`, `usage.*` and `access.reload`.
|
||||
|
||||
| Verb | owner | collaborator | viewer |
|
||||
|---|---|---|---|
|
||||
| `project.list` | all | projects covered by any live grant | projects covered by any live grant |
|
||||
| `thread.create` | yes | collaborator grant covers project and instance | refused |
|
||||
| `thread.list` | own threads | own threads still covered | own threads still covered (from an earlier collaborator grant) |
|
||||
| `thread.list {shared: true}` (new) | threads shared with the owner | threads shared with me and readable (5.4) | same |
|
||||
| `thread.open` | own | own, collaborator grant still covers | refused |
|
||||
| `thread.send` | own | own, collaborator grant still covers, no `human.input` | refused |
|
||||
| `thread.messages` | own, or shared with the owner | own (any level still covering), or shared and readable | same as collaborator |
|
||||
| `thread.share`, `thread.unshare` (new) | own threads | own threads, collaborator grant still covers | refused |
|
||||
| `thread.shares` (new) | own threads | own threads | refused |
|
||||
| `names` | all | instances covered by any live grant | instances covered by any live grant |
|
||||
| every other verb | as today | refused | refused |
|
||||
|
||||
Notes:
|
||||
|
||||
- `thread.list` keeps its response shape exactly. Shared threads come only from `thread.list {shared: true}`,
|
||||
whose rows add `owner`. The Console and existing tests read the plain list unchanged.
|
||||
- A viewer can still read its own threads when a collaborator grant is lowered to viewer, but can't send
|
||||
or open them. This is the downgrade path.
|
||||
- The tester keeps its row 177 verbs. It can't share or be shared with: `thread.share`, `thread.unshare`
|
||||
and `thread.shares` fail `tester-refused`, and a share target is never `tester`. Agent testers (Q173) test
|
||||
sharing through the bus suites, not the live 7333 listener.
|
||||
- `reader` sessions (Console read caps, broker.mjs:1303) are the owner's board view and stay owner-only.
|
||||
A user never gets a reader session.
|
||||
|
||||
### 4.4 Console per role
|
||||
|
||||
- owner: as today, plus a share control on each own thread and a "Shared with me" list.
|
||||
- collaborator: the chat view only, for granted projects (FED R6's granted-user sign-in), plus share
|
||||
controls on its own threads and "Shared with me". No board.
|
||||
- viewer: "Shared with me" and its own old threads, read-only. No composer, no open, no share control.
|
||||
- The Console hides controls a role can't use, but the bus check is authoritative. A Console bug can't
|
||||
widen a grant (FED 3.6). Each role gets a webui test with the UI check removed (mutant) that the bus still
|
||||
refuses.
|
||||
|
||||
### 4.5 Who manages humans and roles
|
||||
|
||||
Only the owner, through `mosaic access` (FED R5), which checks the human proof and refuses tester and agent
|
||||
callers. Row 193 needs from R5:
|
||||
|
||||
- `mosaic access user add <id> --home local --subject <provider>:<sub>` and `user remove <id>`;
|
||||
- `--level viewer` on `grant`;
|
||||
- the receipt line in `<dataRoot>/state/access-log.jsonl` and the `access.reload` call, as FED R5 has.
|
||||
|
||||
If R5 has no `user` verbs when row 193 builds, row 193c adds them on R5's candidate. The Console gets no
|
||||
user or grant writes in row 193: a write from the Console would need a new owner verb that writes
|
||||
access.json from the bus, and access.json is a config file only the owner's CLI writes. A read-only users
|
||||
page for the owner is a follow-up (section 9).
|
||||
|
||||
The owner keeps every restructuring power (TARGET Case 1). Users can't touch the business file, projects,
|
||||
roles or access.json.
|
||||
|
||||
## 5. Private and shared threads (brief point 3)
|
||||
|
||||
### 5.1 Deviation from brief point 4, agreed by Sage (sage-1338)
|
||||
|
||||
Brief point 4 says access.json is the one store. Users, roles and grants live there. Per-thread shares do
|
||||
not, for three reasons (morgana-0002):
|
||||
|
||||
- F1. Thread ids are bus-generated UUIDs. A hand-typed share with a typo would fail the loader, and the
|
||||
loader refuses the whole file on any failure, which revokes every user.
|
||||
- F2. Only the owner writes access.json. A collaborator could never share its own thread.
|
||||
- F3. Threads are append-only bus rows, and a share is a runtime act by a principal that wants a trail.
|
||||
|
||||
So shares go in an append-only bus table, gated by the grant. Sage agreed (sage-1338). Gizmoduck had no
|
||||
objection (gizmoduck-0009) and recorded it as FED 9 Q2. Duckworth rules on it in this review.
|
||||
|
||||
### 5.2 Schema 3j
|
||||
|
||||
```sql
|
||||
CREATE TABLE thread_shares (
|
||||
seq INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
at TEXT NOT NULL,
|
||||
business TEXT NOT NULL,
|
||||
thread TEXT NOT NULL REFERENCES threads(id),
|
||||
op TEXT NOT NULL CHECK (op IN ('share','unshare')),
|
||||
target TEXT NOT NULL,
|
||||
by TEXT NOT NULL
|
||||
) STRICT;
|
||||
CREATE INDEX thread_shares_thread ON thread_shares (thread, target, seq);
|
||||
-- no_update, no_delete and no_replace triggers, as thread_opens has (schema.sql:319-321).
|
||||
```
|
||||
|
||||
- `target` is an owner string: `user:<id>`, the business human's name, or `*`. A share whose target is a
|
||||
name that is no longer the business's `human` (after a rename) is void: rule 1 in 5.4 compares against
|
||||
the current owner string, so it matches nobody (Duckworth N4).
|
||||
- `by` is the sharer's owner string, which is always the thread's owner.
|
||||
- A share's state is its latest row per `(thread, target)` (Duckworth N12, REVIEW-r3.md:74-76). Shares
|
||||
are not assumed to only grow.
|
||||
- No existing row changes. Existing threads are already owned by the business human (`#owner` at
|
||||
broker.mjs:364-366), so they are the owner's and private. The brief's "all become the current human's"
|
||||
is already true.
|
||||
|
||||
The migration `3i-3j.sql` creates the table, index and triggers. `thread_shares` joins every table list that
|
||||
holds timed append-only tables: `timedTables` in store.mjs (:60-74), the broker's clock list
|
||||
(broker.mjs:223-235), the restore guard's counted tables (row 181) and `COUNTED` in
|
||||
`scripts/stack-release.sh:315`. The schema digest for 3j is pinned as the other versions are.
|
||||
|
||||
### 5.3 Verbs
|
||||
|
||||
- `thread.share {thread, to}`: the caller must own the thread (`thread-not-owned`). For a user, a
|
||||
collaborator grant must still cover the thread. `to` is `user:<id>`, the business human's name or `*`.
|
||||
A `user:<id>` target must have a live grant, of any level, covering the thread's project and instance,
|
||||
else `share-not-covered`. Sharing with yourself fails `invalid-request`. A share that is already in force
|
||||
fails `share-exists`. Writes one `share` row.
|
||||
- `thread.unshare {thread, to}`: the caller must own the thread. The share must be in force, else
|
||||
`share-not-found`. Writes one `unshare` row. Unsharing the owner's or `*` shares works the same way.
|
||||
- `thread.shares {thread}`: the caller must own the thread. Returns the shares in force, latest row each.
|
||||
- Both writes go through `refuse()` on failure, so each refusal records `action.refused` with `by` set
|
||||
(FED 3.6 item 6).
|
||||
|
||||
### 5.4 The read check, one function
|
||||
|
||||
`#readable(s, t)` decides whether a session may read thread `t` it doesn't own. True only when all hold:
|
||||
|
||||
1. a share for `t` is in force whose target is the caller's owner string, or `*`;
|
||||
2. the caller is the owner, or the caller has a live grant of any level covering `t.project` and
|
||||
`t.instance`;
|
||||
3. if `t.owner` is a user, that user still has a live grant covering `t` (a revoked owner hides the
|
||||
thread, sage-1338);
|
||||
4. `t.owner` is not `tester`.
|
||||
|
||||
`#thread(s, id)` keeps its owner check for `thread.open`, `thread.send`, `thread.share`, `thread.unshare`
|
||||
and `thread.shares`. A new `#threadRead(s, id)` admits the owner or `#readable`, and serves only
|
||||
`thread.messages`. A sharee fails `thread-not-owned` on every write verb.
|
||||
|
||||
### 5.5 Every read path and its filter
|
||||
|
||||
| Read path | Today | After row 193 |
|
||||
|---|---|---|
|
||||
| `thread.list` | `owner=?` | unchanged for the owner; for users also each row's thread still covered |
|
||||
| `thread.list {shared: true}` | none | rows where `#readable` holds |
|
||||
| `thread.messages` | `#thread` | `#threadRead` |
|
||||
| `thread.open`, `thread.send` | `#thread` | `#thread`, plus a collaborator grant for users |
|
||||
| `project.list`, `names` | business-wide | per FED 3.5 for users, unchanged for the owner |
|
||||
| `trail`, `replies` | human, reader or tester; any message by id | users refused (`access-refused`); for the others, messages in other users' unshared threads hidden (5.7) |
|
||||
| `messages`, `inbox`, `agents`, `tasks`, `decisions`, `credentials` | human or reader | users refused (`access-refused`); owner unchanged. A user-thread run can't send to the human (`user-thread-human`, FED 3.6), so `messages` holds none of a user's thread |
|
||||
| `message.receive` by a run | its own thread (:1933) | unchanged: runs read their own thread, never a share |
|
||||
| `threadsState` (launcher) | all threads | adds `revoked` per 5.6 |
|
||||
|
||||
### 5.6 Revocation, expiry and unsharing
|
||||
|
||||
Gizmoduck asked that a grant loss end access to threads shared with that user (gizmoduck-0009 point 2).
|
||||
|
||||
- A sharee holds no claim and no run. Its access is only reads, and every read runs `#readable` against the
|
||||
access state the broker already refreshes on each request (mtime, size, inode), on its 30 s timer, and on
|
||||
`access.reload` (FED 3.7). So a removed, expired or invalid grant ends share reads on the next request,
|
||||
with nothing to tear down. The Console polls, so a revoked sharee's next poll fails.
|
||||
- An unshare row ends access on the next request in the same way.
|
||||
- A user who loses coverage of their own thread also loses its shares: rule 3 in 5.4.
|
||||
|
||||
The downgrade case. With viewer, "no live grant covers" would be wrong for runs: a viewer grant covers the
|
||||
thread but must not let a run continue. FED puts this in R4: `revoked`, claim end and the launch check call
|
||||
`covers` with level `collaborator`, so a downgrade to viewer ends the run within one launcher tick. FED r4.4
|
||||
(b67bc6de9, mos-1020) chains R4 after row 193's viewer level (193a) and keeps the level-loss test and its
|
||||
mutant in R4. Row 193 carries neither (Duckworth N3), so the mutant has one owner.
|
||||
|
||||
### 5.7 What "private" means, stated plainly
|
||||
|
||||
Sage ruled (sage-1354): a user's thread is private from the owner on the bus too. Jason's words are "private
|
||||
agent sessions per user", and in Case 2 the owner of Melanie's hub is not the user whose thread it is.
|
||||
Duckworth's B1 asked where the line sits, since a user-thread run acts with the owner's role authority (F10).
|
||||
I took Duckworth's option (a): **the user's messages are private, and the run's governance is not.** The
|
||||
run spends the owner's authority, so its decisions, task writes and claims are the owner's to see and
|
||||
answer. Filtering them too (option b) would need a thread link on `decisions`, more schema, and would hide
|
||||
from the owner what is done in the owner's name.
|
||||
|
||||
Every read an owner or reader session has, and its side of the line:
|
||||
|
||||
| Read | Shows | Side |
|
||||
|---|---|---|
|
||||
| `thread.list`, `thread.messages`, `thread.open` | own threads only, plus shares to the owner from 193b2 | private: other users' threads never appear |
|
||||
| `trail` | events, messages, decisions and deliveries by subject | messages filtered (below); decision rows and their events stay visible |
|
||||
| `replies` | messages replying to an id | filtered (below) |
|
||||
| `messages`, `message.receive`, `message.read` | messages `to_role='human'` (broker.mjs:1931-1936, :1953) | private by construction: a user-thread run can't send to the human (`user-thread-human`, FED 3.6 item 7) |
|
||||
| `inbox` | open decisions routed to the human | governance, visible |
|
||||
| `decisions {list: roles \| recent}` | decision rows with question, options and recommendation | governance, visible |
|
||||
| `agents` | role claims and each held role's state | governance, visible |
|
||||
| `tasks`, `state` | tracker tasks and launch state | governance, visible |
|
||||
| `credentials`, `usage` | credential state, usage records | governance, visible; neither holds message text |
|
||||
| `project.files`, `project.file` | project files, including records a run writes with `record.write` (broker.mjs:1395-1407, :1441-1450) | governance, visible |
|
||||
| `project.list`, `names` | projects and instance names | business-wide for the owner, visible |
|
||||
|
||||
So a decision a user-thread run raises shows in the owner's `inbox` or `decisions`, with the free text the
|
||||
run wrote. Its role claims show in `agents`, its task writes in `tasks`, and a record it writes in
|
||||
`project.file`. Any of these can quote the user, and 5.7 doesn't call them private. The users' help text
|
||||
(R5) and the Console's share and thread pages say so in one sentence, which FED R6 reuses (mos-1023 item 2,
|
||||
duckworth-0012 N21):
|
||||
|
||||
> The business owner sees your thread's agent's decisions, inbox entries, claims, tasks and project files,
|
||||
> but not your messages.
|
||||
|
||||
The gated-raise route. A user-thread run's uncited gated raise or hard wait routes to the human (broker.mjs:1677-1680).
|
||||
FED r4.5 (acd5f1f42, B9) settled this with no guard: the tester guard at :1683 (`tester-thread-human`) is not
|
||||
copied, and the decision reaches the owner's inbox and no other (sage-1368, mos-1021, mos-1022). FED R3 tests it.
|
||||
Row 193 adds nothing here.
|
||||
|
||||
The message filter. A message is hidden from a session when it is in a thread (`message_threads`) whose owner
|
||||
is a `user:<id>` other than the caller, and `#readable(s, t)` is false. Threads owned by the business human or
|
||||
by `tester` are not affected, so row 177's behaviour is unchanged.
|
||||
|
||||
- `trail`: drops hidden messages, their `deliveries` rows, and `events` rows whose `subject` is a hidden
|
||||
message id. A `subject` that is itself a hidden message returns `message-not-found`, as an unknown id does,
|
||||
so the reply doesn't confirm the id exists.
|
||||
- `replies {id}`: a hidden `id` fails `message-not-found`, and hidden messages are dropped from the result.
|
||||
- It applies to every session that can call these verbs: the owner, reader sessions and the tester.
|
||||
- It lands in 193b1, with no schema (Duckworth B2). Before 193b2 there are no shares, so `#readable` is false
|
||||
for anyone but the thread's owner and b1 hides every message in another user's thread. 193b2 adds only the
|
||||
share exception. So no build order can put a user thread live without the filter.
|
||||
|
||||
What it does not do. The owner holds the store as the instance's OS user and can read it with sqlite. That is
|
||||
the same-uid limit in section 6, not a bus path. An owner audit verb, explicit and logged, is a follow-up row
|
||||
only if Jason asks for one (sage-1354).
|
||||
|
||||
## 6. The bus human boundary (brief point 5)
|
||||
|
||||
Row 196 (Gander, #1694, brief `docs/plans/2026-10-20_human-boundary.md` at 96f243faf) owns P19 333 and the
|
||||
verifier. Row 193 cites it and designs no boundary of its own (sage-1338: one mechanism).
|
||||
|
||||
Agreed with Gander (gander-0002, morgana-0003, gander-0005):
|
||||
|
||||
- One bus verifier, `verifyPrincipal(request, connection) -> {kind, business, id}`, kind `owner`, `user` or
|
||||
`tester`. It mints one transient principal per request. It returns no role. The broker reads the level
|
||||
from the live grant on every request, so a grant change acts without a new principal.
|
||||
- Gander recommends FED 3.6's shape for users (path-bound `user-cli.mjs`, direct parent the bus tree's
|
||||
`packages/webui/src/cli.mjs serve`) plus kernel peer PID binding, and weighs bus-side ID-token
|
||||
verification as the larger alternative.
|
||||
- Correction from Gander, accepted: the tester proof doesn't run the full agent walk, because agents bind
|
||||
tester by design (Q173). Managed runs still fail it. The tester listener moves from TCP 7333 to a private
|
||||
Unix socket, with `bus.tester` unchanged (agreed by Sage with Gander).
|
||||
|
||||
What row 193 needs from row 196:
|
||||
|
||||
- H1. Owner on the CLI keeps today's proof. Owner in the Console is a `console.sso.subjects` entry.
|
||||
- H2. A user principal comes only from an OIDC sign-in, through a path the bus verifies. No CLI path for
|
||||
users.
|
||||
- H3. The user binding fails closed on no live grant, an unknown subject, a subject also in
|
||||
`console.sso.subjects`, or a missing or invalid access.json. A broken access.json never stops the owner.
|
||||
- H4. Agent processes never get an owner or user principal.
|
||||
- H5. Revocation ends a bound user within 30 s and at once on `access.reload`, from the same access state
|
||||
row 193's share check reads.
|
||||
|
||||
The same-uid limit. Within one instance, a non-agent process on the instance's uid can claim the owner, and
|
||||
can start its own Console copy and claim any granted user (FED 3.6). An unsandboxed same-uid agent that
|
||||
detaches from its ancestry can do the same. The walk doesn't stop that, and Gander's design won't claim it
|
||||
does (gander-0005). Several humans don't make this worse: a user principal is no stronger than the owner
|
||||
principal that the same process could already take. Users themselves are not on the uid. They reach the
|
||||
bus only through the Console, so the limit is about local processes, not other humans.
|
||||
|
||||
Does row 190 narrow it? Row 190 runs several instances under one uid, so no. Row 190b puts an instance (the
|
||||
hub) under its own Linux user (mos-0986). That narrows reach across instances: another instance's uid fails
|
||||
the uid check (human.mjs:67) and can't read the hub's store or access.json. It does not narrow reach within
|
||||
one instance. Private threads are private from other humans using the bus, not from processes running as
|
||||
the instance's uid.
|
||||
|
||||
## 7. Migration and rollback (brief point 6)
|
||||
|
||||
- No access.json: the business has exactly its owner, every verb behaves as today, and the share verbs
|
||||
work for the owner only (target `*` or none). A broken access.json revokes users and leaves the owner.
|
||||
- Existing threads: already owned by the business human. Nothing is rewritten.
|
||||
- Schema: 3i to 3j adds `thread_shares` only. A re-pin past a schema bump needs a `--migrate` window with
|
||||
Jason, and row 185's backup before activate covers it.
|
||||
- Rollback before any share is written: activate the previous copy with the store restored from the
|
||||
pre-activate backup. A 3i copy refuses a 3j store, as it does for every bump.
|
||||
- Rollback after shares are written: the same restore loses shares made since the window. Grants are in
|
||||
access.json and survive. Users re-share. Row 181's restore guard counts `thread_shares`, so the loss is
|
||||
shown, not silent.
|
||||
- To keep that window small, the build splits (section 9): 193a, 193b1, 193c and 193d have no schema
|
||||
change and can go live in any window. 193b2 carries 3j.
|
||||
|
||||
## 8. Tests and mutants
|
||||
|
||||
Bus, each in `packages/bus/tests/` (193b1: viewer, mixed grants, board reads, the 5.7 message filter and governance pin; 193b2: shares, the share exception to the filter, migration):
|
||||
|
||||
- Viewer: each allowed verb works, every other verb refuses `access-refused`, including `thread.create`,
|
||||
`thread.open`, `thread.send` and `thread.share`.
|
||||
- Mixed grants: collaborator on project A and viewer on B. Create in A works, create in B refuses.
|
||||
- Downgrade: not here. FED R4 owns the level-loss test and its mutant (r4.4, mos-1020, N3).
|
||||
- Shares: owner shares with `user:<id>`, the user reads `thread.messages` and gets `thread-not-owned` on
|
||||
`thread.send` and `thread.open`. Share with `*` reaches every covered user and the owner. Share with a user
|
||||
whose grant doesn't cover the project or instance refuses `share-not-covered`.
|
||||
- A share never widens: the target's grant drops the project, the next read refuses.
|
||||
- Revoke, expiry and an invalid file each end share reads on the next request. The 30 s timer and
|
||||
`access.reload` paths each have a case.
|
||||
- A revoked thread owner hides the thread from sharees.
|
||||
- Unshare ends access. Share, unshare, share again leaves it shared (latest row wins).
|
||||
- Non-owner `thread.share`, `thread.unshare`, `thread.shares` refuse. Tester refuses on all three.
|
||||
- `thread.list` plain shape unchanged. `thread.list {shared: true}` returns only readable threads.
|
||||
- Board reads refuse users and viewers.
|
||||
- Governance pin (B1, sage-1368): a user-thread run raises a decision; it shows in the owner's
|
||||
`decisions recent` with its question text, and `inbox` lists it when routed to the human; the run's
|
||||
role claim shows in the owner's `agents`, its task write in `tasks`, and its `record.write` in
|
||||
`project.file` (B3, sage-1373); the user's message bodies stay hidden from the owner's `trail` and
|
||||
`replies`. The test names each item of the 5.7 user sentence, so the sentence and the pin match.
|
||||
- `trail`, for the owner, a reader session and the tester (193b1): a user's thread message as `subject`
|
||||
fails `message-not-found`; a decision trail that reaches such a message omits it, its deliveries and its
|
||||
events. Once the thread is shared with the owner, the owner's trail shows it.
|
||||
- `replies`, same three sessions (193b1): a hidden `id` fails `message-not-found`; hidden replies are dropped. The
|
||||
owner's own and the tester's threads are unchanged.
|
||||
- Migration: a 3i store with threads migrates to 3j, row counts unchanged, `thread_shares` empty and
|
||||
append-only (update, delete, replace each abort). Restore-guard and `COUNTED` include it.
|
||||
|
||||
Business (193a): R1 accepts `viewer`, refuses any other new level.
|
||||
|
||||
CLI (193c): `mosaic access user add/remove` and `grant --level viewer` refuse tester and agent callers,
|
||||
write a receipt line and call `access.reload`.
|
||||
|
||||
Webui (193d): a viewer has no composer or share control. With each UI check removed, the bus still refuses.
|
||||
|
||||
Mutants, each must be killed:
|
||||
|
||||
1. `#readable` rule 1 dropped (any covered user reads any thread).
|
||||
2. Rule 2 dropped (a share widens the grant).
|
||||
3. Rule 3 dropped (a revoked owner's thread stays visible).
|
||||
4. Latest-row lookup replaced by "any share row" (unshare ignored).
|
||||
5. (Moved to FED R4, N3: the level-loss mutant is R4's.)
|
||||
6. Viewer allowed `thread.send`.
|
||||
7. `#threadRead` used for `thread.send` (sharee writes).
|
||||
8. `share-not-covered` check removed.
|
||||
9. `thread.share` owner check removed (sharee re-shares).
|
||||
10. A user allowed `trail`.
|
||||
11. The `trail` filter removed (the owner reads a user's unshared message by id).
|
||||
12. The `replies` filter removed (the owner reads replies in a user's unshared thread).
|
||||
|
||||
## 9. Build rows and order
|
||||
|
||||
| Row | Work | Chains on | Schema |
|
||||
|---|---|---|---|
|
||||
| 193a | R1 level check widened to viewer | FED R1 candidate | none |
|
||||
| 193b1 | Bus: viewer verb list, per-grant level checks, users refused on board reads, the 5.7 `trail` and `replies` filter with `#readable` as owner-only | FED R3, R4 candidates, row 196 verifier | none |
|
||||
| 193b2 | Bus: `thread_shares`, share verbs, the share rules of `#readable`, `#threadRead`, the 5.5 share filters | 193b1 candidate | 3j |
|
||||
| 193c | `mosaic access` user verbs and `--level viewer`, if R5 lacks them | FED R5 candidate | none |
|
||||
| 193d | Console: shared list, share controls, viewer read-only | FED R6, row 192 candidates, 193b2 for share controls | none |
|
||||
|
||||
Sage ruled the split (sage-1354): 193b1 has no schema change and can go live first. 193b2 carries 3j, and
|
||||
Sage takes its migrate window to Mos when 193b2 is near. The 5.7 message filter is in 193b1 (Duckworth B2,
|
||||
sage-1368), so it is live before any user thread can be, whatever order FED R8 and 193b2 land in. 193b2 adds
|
||||
only the share exception. FED R4 chains on 193a (viewer level), and 193b1 chains on R4.
|
||||
|
||||
Coverage, settled in FED r4.1 (73677b87d, gizmoduck-0018, still under Duckworth's review). A grant's
|
||||
`projects` is `["*"]` or bus project UUIDs (`projects.id`, broker.mjs:2306; `threads.project` references it).
|
||||
R1 exports one pure `covers(grant, {business, project, instance}, now, {level})`. Row 193 uses only that
|
||||
function and adds no second one (Sage confirmed, sage-1354):
|
||||
|
||||
- share reads (5.4 rules 2 and 3) and `share-not-covered`: `covers(g, thread, now, {})`, any level;
|
||||
- `thread.create`, `thread.open`, `thread.send`, `thread.share`: `covers(g, thread, now, {level: 'collaborator'})`;
|
||||
- `project.list` and `names` for users: any level, as FED 3.5.
|
||||
|
||||
`covers` needs no further argument for row 193. If r4.1 changes in review, 193b1 follows it.
|
||||
|
||||
Follow-ups, not in this row:
|
||||
|
||||
- A read-only users and grants page for the owner in the Console.
|
||||
- A second owner or admin level, if Jason wants one.
|
||||
- An explicit, logged owner audit verb for user threads, only if Jason asks for one (sage-1354).
|
||||
|
||||
## 10. Gate
|
||||
|
||||
Per the brief: Duckworth approves this design, then the manifest. The bus, business, cli and webui suites and
|
||||
every `scripts/test-*.sh` pass on Sage's gate, and the section 8 mutants are killed. No test touches the live
|
||||
directories. No product code before Duckworth approves.
|
||||
@@ -0,0 +1,171 @@
|
||||
# Row 267 (C2-15, #1772): hybrid universes, exclusions, pins and the crossover. Build notes
|
||||
|
||||
Morgana, 2026-10-25. Brief: `docs/plans/2026-10-20_case2-rows.md` "C2-15 hybrid universes: exclusions,
|
||||
pins and the end-to-end crossover" (blob 85e6fe2e). Reviewer Filbert, gate owner Sage. Claimed at rev 4087.
|
||||
Branch `morgana/row267` from refactor 78b69e1e6. Design 1.4 is `agents/gosalyn/work/case2/DESIGN.md`.
|
||||
|
||||
Status: draft design. Nothing is built or run. No runs before Sage's CLEAR.
|
||||
|
||||
## 1. What the code does today (78b69e1e6)
|
||||
|
||||
- `mapping.mjs:119-134` checks only the shape of a constraint. `exclude` is any folder path, and
|
||||
`pin.entity` is any folder path with a scalar value. Nothing checks either against the sources.
|
||||
- `query.mjs:52-57` `sees()` reads `exclude` as a path prefix only (`inside(c.exclude, file.path)`). The
|
||||
type form `second/Characters` (`mapping.test.mjs:203`) passes the validator but hides nothing, because
|
||||
no file path starts with `second/`.
|
||||
- `reader.mjs:206-210` gives R6 only for a canon link into a universe that is neither the file's own nor
|
||||
one of its sources. A hybrid canon file that links into an excluded source path gets no problem.
|
||||
- `guard.mjs:91-97` lets any mix of one hybrid and its sources pass. It doesn't check exclusions.
|
||||
- `problems.mjs` has R1-R7 and no R8.
|
||||
|
||||
## 2. Design
|
||||
|
||||
### 2.1 Validator (`mapping.mjs`)
|
||||
|
||||
The validator runs after every universe is read, alongside the existing sources check at `:141-146`,
|
||||
because it needs each source's books, playground and supporting lists. For each constraint of a hybrid:
|
||||
|
||||
- **Type form:** the first segment is one of the hybrid's `sources`, and the rest is exactly one key of
|
||||
`types`.
|
||||
- **Path form:** the path equals, or lies inside, a book folder of one of the sources.
|
||||
- Both forms fit: refuse `library-mapping-invalid` ("... reads as both a source/type and a source path").
|
||||
- Neither form fits: refuse `library-mapping-invalid` ("... lies inside no book of the hybrid's
|
||||
sources"). This covers a path in the hybrid's own books, in a non-source universe, or unmapped.
|
||||
- **Path form under a playground:** a path equal to or inside one of a source's playground prefixes
|
||||
refuses ("... lies under a playground of universes.X, which the hybrid never reads"). "Under" means the
|
||||
path equals the prefix without its trailing `/`, or starts with the prefix.
|
||||
- **`pin.entity`:** the same path-form rules apply, with no type form. An entity is one file.
|
||||
|
||||
The stored shape stays `{ exclude }` / `{ pin }`. I don't add a `form` field. `library_universe`
|
||||
returns `constraints` as they are now, and the index cache digest doesn't change for existing mappings.
|
||||
`excluded()` works out the form again each time. It's pure and cheap.
|
||||
|
||||
### 2.2 `excluded(mapping, hybrid, path)` (`scope.mjs`)
|
||||
|
||||
This returns true when any `exclude` of universe `hybrid` covers `path`:
|
||||
|
||||
- **Type form `S/T`:** `path` starts with `${book}/${T}/` for some book of source S.
|
||||
- **Path form P:** `path === P` or `path` starts with `${P}/`.
|
||||
|
||||
It returns false when `hybrid` isn't a hybrid. It reads only the path (D2). It has three callers:
|
||||
|
||||
- `query.mjs` `sees()` replaces its own `inside(c.exclude, ...)` reading with this helper.
|
||||
- `reader.mjs` uses it for the excluded-link R6.
|
||||
- `guard.mjs` uses it for the excluded cite.
|
||||
|
||||
`query-fixture.mjs` `EXCLUSION` (`Book One/Research`) is path form and still fits, so the existing
|
||||
C2-05a query tests keep their constants.
|
||||
|
||||
### 2.3 Reader: excluded-link R6 and R8
|
||||
|
||||
- **Excluded-link R6:** in the canon branch at `:206`, a hybrid file's link to a target where
|
||||
`excluded(mapping, file.universe, to.path)` is true gives `problem('R6', file.path, link.line)`, with
|
||||
the default message. A custom message isn't possible: `mirror.mjs:196-201` `validProblem` accepts a
|
||||
sync-state problem only when `message === RULES[code]`, so a new message would make every later sync
|
||||
state read refuse `library-state-invalid`. The link stays in `file.links`, as the other R-rules leave
|
||||
it. `sees()` already drops it from the hybrid's `links_out`. A link that is both foreign and excluded
|
||||
can't occur, because an excluded path always lies in a source, so each link gets at most one R6.
|
||||
- **R8:** a new `RULES.R8` entry reads "pinned value changed". The check runs once per commit, after the
|
||||
link pass, for each pin of each hybrid:
|
||||
- **Entity missing** from `byPath`: R8 at `entity`, with no line. This covers a file that is absent,
|
||||
R1, R2 or R7.
|
||||
- **Entity present:** R8 at `entity`, on the `field` key's line where there is one, unless the parsed
|
||||
front matter holds `field` as a scalar (string, number or boolean) and `String(parsed) ===
|
||||
String(value)`. A list, null, a missing key and unparsed (R3) front matter all count as changed. Under
|
||||
a plain `String()` comparison, `['grey']` would equal `'grey'`, so this rule closes that hole.
|
||||
- **Hybrid versions:** each canon file of the hybrid whose `type` (from `classify`) equals the entity's
|
||||
type, and whose name matches the entity's, is a version of it. Names are the trimmed front matter
|
||||
`name` on both sides. When either file has no text `name`, the file names (stems) compare instead.
|
||||
A version that holds `field` with another value gets R8 at its own path, on that key's line. A version
|
||||
without `field` gets nothing, and neither does any other file that only links to the entity.
|
||||
- The entity's type and name come from `classify(mapping, entity)` and the entity's parsed file. When the
|
||||
entity is missing, its stem stands in for the name.
|
||||
|
||||
### 2.4 Guard (`guard.mjs`)
|
||||
|
||||
After the existing span check at `:91-97`, the guard looks for a hybrid among the cited universes. If
|
||||
there is one, and some cite lies in one of its sources while `excluded(mapping, hybrid, c.path)` is true,
|
||||
the guard refuses `citation-cross-universe` ("citation N (path) is excluded from hybrid H"). A reply that
|
||||
cites only source paths, excluded ones included, has no hybrid among its universes, so it's unchanged.
|
||||
|
||||
### 2.5 Setup sheet
|
||||
|
||||
`docs/case2/SETUP-SHEET.md` gets one section after "Supporting folders", headed "Making a crossover
|
||||
(hybrid) universe". It covers `kind`, `name`, `sources`, `books`, `playground`, `constraints`
|
||||
(`exclude` in both forms, `pin`) and `notes`, and says that the continuity role reads and cites the notes.
|
||||
It also says that only she edits this entry (C2-04 rule M9).
|
||||
|
||||
## 3. Tests and mutants
|
||||
|
||||
New files and cases, all on scratch repos under TMPDIR (never /tmp):
|
||||
|
||||
- **`packages/library/tests/hybrid.test.mjs` (new):**
|
||||
- A business-file-shaped mapping (fixture `mapping.json` plus a `cross` hybrid) on a repo in her
|
||||
layout's shape.
|
||||
- `find` and `search` in `cross` return its own files plus the sources' canon, each carrying its source
|
||||
universe. They never return a source playground or an excluded path, and this runs once for each
|
||||
exclude form.
|
||||
- `get` keeps `links_out` into the sources and drops excluded targets.
|
||||
- A source universe's lookups never return a `cross` file.
|
||||
- `universeInfo` returns the sources, constraints and notes.
|
||||
- `excluded()` gets unit cases for both forms and for a non-hybrid.
|
||||
- **`mapping.test.mjs`:** one case per new refusal: both forms fit, neither fits, the hybrid's own book,
|
||||
an exclude under a source playground, and a pin under a source playground. `second/Characters` at
|
||||
`:203` still checks.
|
||||
- **`reader.test.mjs`:**
|
||||
- R8 cases: entity missing, entity changed, a same-name hybrid version with no link, and a list value.
|
||||
- Non-R8 cases: a hybrid place file with `age: 300` that links the entity, and a different hybrid
|
||||
character that links it.
|
||||
- The excluded-link R6, once for each exclude form.
|
||||
- **`guard.test.mjs`:** a hybrid cite with source canon passes, and a hybrid cite beside an excluded path
|
||||
refuses in both forms. A cite of only an excluded source path passes as that universe's item.
|
||||
- **`packages/bus/tests/citation-guard.test.mjs`:** through `citationWorld` with `mapChange` adding the
|
||||
hybrid, the excluded-beside-hybrid send refuses and the `messages` count stays the same.
|
||||
- **`packages/bus/tests/library-lookups.test.mjs`:** one broker case, where `library.find` and
|
||||
`library.universe` in the hybrid return what the library tests above assert.
|
||||
- **C2-04 M9 rule (agents can't write `projects`), not a mutant:** a case in `packages/business/tests/library-projects-protected.test.mjs` where an organization
|
||||
write that adds a new hybrid universe refuses `organization-projects-protected`. This is outside the
|
||||
brief's "tests in library and bus" list (§4 Q1).
|
||||
|
||||
Mutants. Each runs only its own named tests, and the tree is restored after each one.
|
||||
|
||||
| # | Mutation | Killed by |
|
||||
|---|---|---|
|
||||
| X1 | `excluded()` ignores the type form | hybrid.test lookups, type form |
|
||||
| X2 | `excluded()` ignores the path form | hybrid.test lookups, path form |
|
||||
| X3 | `sees()` keeps its old `inside()` reading | hybrid.test type form |
|
||||
| X4 | the validator accepts "both forms fit" | mapping.test |
|
||||
| X5 | the validator skips the playground check | mapping.test |
|
||||
| X6 | R8 compares with plain `String()` (list equals scalar) | reader.test list value |
|
||||
| X7 | a version is matched by link instead of name | reader.test non-R8 cases |
|
||||
| X8 | the reader drops the excluded-link R6 | reader.test |
|
||||
| X9 | the guard skips the excluded check | guard.test "C2-15 a hybrid cite beside an excluded source path refuses citation-cross-universe, both forms" (the mutant run); bus citation-guard "C2-15 excluded cite beside a hybrid refuses and inserts nothing" also fails |
|
||||
|
||||
## 4. Open questions for Sage and Filbert
|
||||
|
||||
- **Q1 (manifest):** the C2-04 M9 test lives in `packages/business/tests`, which the brief's file list leaves
|
||||
out. I recommend adding it there, beside the existing C2-04 M9 cases, and putting the business suite on the
|
||||
gate. The alternative is a bus-level `organization.set` test, which needs more setup and tests the
|
||||
same writer.
|
||||
- **Q2 (supporting):** the brief refuses a path under a source's playground. A source's supporting
|
||||
prefix is never read by the hybrid either (`sees()` passes canon only). I recommend refusing that too,
|
||||
under the same REQ-LIB-3 reason. That widens the brief by one rule, so I need a yes.
|
||||
- **Q3 (R8 reaches other readers):** adding `RULES.R8` goes beyond the reader:
|
||||
- `survey.mjs:70` counts problems per `RULES` key, so the survey line gains `R8 0`.
|
||||
`survey.test.mjs:75` pins that line and has to change. It's in the library tests, so it's in scope.
|
||||
- `mirror.mjs` `validProblem` accepts R8 through `RULES`, so a sync state written after this row
|
||||
carries R8 problems.
|
||||
- A copy running older code would refuse such a state. That only matters if a re-pin goes backwards.
|
||||
- I'll grep `docs/case2/SURVEY-RUN.md` and the Console for R-code lists before in-review.
|
||||
- **Q4 (version scope):** I count only the hybrid's canon files as versions. Playground is "what if" and
|
||||
is exempt, as it is from R5 and R6. A hybrid with `books: []` therefore never gets a version R8. It
|
||||
still gets the entity R8.
|
||||
- **Q5 (`pin.entity` not `.md`):** a non-`.md` entity is never indexed, so R8 would report it missing at
|
||||
every commit. I recommend refusing it in the validator. The brief leaves existence to the reader, but
|
||||
this is a shape check, not an existence check.
|
||||
|
||||
## 5. Gate
|
||||
|
||||
Filbert approves the manifest. The library and bus suites, plus business if Q1 stands, run green on
|
||||
Sage's gate along with every `scripts/test-*.sh`. Hash pins on the changed files are grepped before
|
||||
in-review.
|
||||
@@ -0,0 +1,22 @@
|
||||
# Row 267: one existing assertion changes (mos-1272)
|
||||
|
||||
File: `packages/library/tests/survey.test.mjs:75`, in the test that checks the survey's last three lines
|
||||
with `--mapping`.
|
||||
|
||||
Before:
|
||||
|
||||
```
|
||||
'problems R1 0, R2 3, R3 0, R4 0, R5 0, R6 0, R7 0']);
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```
|
||||
'problems R1 0, R2 3, R3 0, R4 0, R5 0, R6 0, R7 0, R8 0']);
|
||||
```
|
||||
|
||||
Why: R8 ("pinned value changed") is a new rule in `problems.mjs` `RULES`, and the survey line lists a
|
||||
count for every `RULES` key (`survey.mjs:70`), so it gains `R8 0`. The fixture has no hybrid and no pin,
|
||||
so the count is 0 and no other count moves.
|
||||
|
||||
No other existing assertion in the row changes.
|
||||
@@ -1694,5 +1694,3 @@ are never rewritten or removed; corrections are new entries.
|
||||
2026-10-24 | magica | row329 R2 #1827 | ASCII management-key fix and docs verified: package66/66,bus readers261/261,CLI readers726/726,config55/55; no required failures/skips, slot returned. Preparing authorized ff review candidate; old tests unchanged.
|
||||
2026-10-24 | Sage 1ef1e4f8 | row329 landing #1827 (mos-1634) | Gate GREEN 23:29:14Z unchanged, landed 61ebf2c34 pushed 23:31:27Z; #1827 closed c31650; queue note 4063, done 4064 (b24d917ec); DEFERRED N2/R2-N1 to 331, N1 to P7; Vikunja 631 filed, 237 c1109; 284 and 157 must rebase.
|
||||
2026-10-25 | Sage 1ef1e4f8 | row284 landing #1789 | Gate GREEN 00:09:04Z unchanged, landed ce0b6ed41 pushed 00:09:52Z; #1789 closed c31655; queue note 4071, done 4072 (7c8dc2033); DEFERRED Webby N1 to the next library read-path row; row 332 PROXY_CODEX_API ruled openai-responses (sage-4331), P3 holds for row 331.
|
||||
2026-10-25 | Sage 1ef1e4f8 | rows 214 and 193 landing (#1715, #1691) | 214 gate GREEN 04:10:34Z, landed 3a8e4abad, #1715 closed c31680, queue 4094/4095 (ce8bb4d8b); 193 doc-only landed 35f34ad27 after Duckworth approve 4088; 198 group A and 267 assertion diffs to Mos (sage-4397, sage-4403); CLEAR to Bubba for 288.
|
||||
2026-10-25 | Sage 1ef1e4f8 | row255 landing #1760 | Doc-only design landed de3e23dde after Webby approve 4103 (c31689); 288 r6 gate green from Bubba (2604/0, 12 scripts, 6 mutants), in-review move handed back to the claimant.
|
||||
@@ -1,70 +0,0 @@
|
||||
# PRD 1.18 brief: REQ-INST-2 prints the providers entry
|
||||
|
||||
Sage, 2026-10-25, for Mos's ruling mos-1671 on sage-4418. This follows
|
||||
`docs/plans/BRIEF-TEMPLATE.md`. Vikunja 458 (bundled Pocket ID).
|
||||
|
||||
## PRD 1.18 REQ-INST-2 prints the providers entry (mos-1671)
|
||||
|
||||
### Problem
|
||||
|
||||
REQ-INST-2's second bullet says the stack installs the bundled Pocket ID "and
|
||||
writes it as the Console's one providers entry". That sentence is Mos's reading
|
||||
(mos-1202), not Jason's words. The row 255 design, landed at de3e23dde, chose R1:
|
||||
enroll prints the entry and writes nothing, because config.json is user-authored
|
||||
and never overwritten (canon invariant 2, Q175). Mos approved R1 in mos-1666 and
|
||||
ruled the amendment in mos-1671. What the requirement allows or forbids is
|
||||
unchanged.
|
||||
|
||||
### Owner and reviewer
|
||||
|
||||
- Owner: Gosalyn.
|
||||
- Reviewer: Webby.
|
||||
- After: row 255 (landed).
|
||||
|
||||
### Files owned
|
||||
|
||||
- `docs/prd/mosaic-stack.md`.
|
||||
- `packages/tasks/requirements.json`: version 1.18 only. No id or title changes,
|
||||
so the count stays 87.
|
||||
- `packages/tasks/tests/requirements.test.mjs`: the version pin 1.18 (:24).
|
||||
|
||||
### What ships
|
||||
|
||||
- In REQ-INST-2's second bullet (PRD :1519-1520), replace
|
||||
"and writes it as the Console's one providers entry."
|
||||
with
|
||||
"and prints the Console's one providers entry for the user to add to
|
||||
config.json. The stack never writes config.json (canon invariant 2)."
|
||||
The rest of the bullet stays, and so does Jason's Q203 quote.
|
||||
- Status line: version 1.18.
|
||||
- The header gets a 1.18 sentence: Version 1.18 corrects Mos's reading in
|
||||
REQ-INST-2 to match the approved row 255 design. The stack prints the
|
||||
providers entry and never writes config.json. What the requirement allows or
|
||||
forbids is unchanged (mos-1671, sage-4418). Doc-only, so it takes the 1.11
|
||||
path: one seat drafts, a second reviews, Sage lands.
|
||||
- A "Version 1.18 source" line, placed before the 1.17 one: Mos's ruling
|
||||
mos-1671 on 2026-10-25, answering sage-4418, with the old and new text.
|
||||
|
||||
### Out of scope
|
||||
|
||||
- The P1 to P6 build rows from the row 255 design. Sage files them after this
|
||||
lands (mos-1671).
|
||||
- Any other REQ-INST-2 wording, and REQ-INST-1 and REQ-INST-3.
|
||||
|
||||
### Gate
|
||||
|
||||
- Webby checks that the PRD diff is the replacement, the Status line, the 1.18
|
||||
sentence and the source line, and nothing else.
|
||||
- On Sage's gate:
|
||||
- tasks, run whole;
|
||||
- bus, run whole, because the broker loads requirements.json;
|
||||
- cli `stack-copy.test.mjs`;
|
||||
- every `scripts/test-*.sh`, under `timeout -k 10`.
|
||||
- Mos gets the landing commit.
|
||||
|
||||
### Handoffs
|
||||
|
||||
Every seat hands off directly (DECISIONS Q210, mos-1221). When the owner pushes a
|
||||
round or the reviewer posts a verdict, that seat messages the next seat's T3
|
||||
thread and Sage's with `mcp__t3__t3_send_message`. Mos has one step here, which
|
||||
is receiving the landing commit.
|
||||
+9
-9
@@ -33,7 +33,7 @@ Gaps found while working go to `docs/plans/DEFERRED.md`, not here.
|
||||
|
||||
<!-- mosaic-queue:begin -->
|
||||
|
||||
Generated from `docs/plans/queue.json` revision 4095 by `queue render`. Do not edit between the markers; change the queue with `scripts/mosaic queue`.
|
||||
Generated from `docs/plans/queue.json` revision 4072 by `queue render`. Do not edit between the markers; change the queue with `scripts/mosaic queue`.
|
||||
|
||||
| # | Piece | Owner | Issues | State | After | Gate | Brief | Note |
|
||||
|---|---|---|---|---|---|---|---|---|
|
||||
@@ -229,7 +229,7 @@ Generated from `docs/plans/queue.json` revision 4095 by `queue render`. Do not e
|
||||
| 190 | several Mosaic Stack instances on one host: per-instance config, data root, units and ports (MVP T1.1, mos-0974, Vikunja #414) | darkwing; reviewers filbert | #1688 | done | — | Filbert approves DESIGN.md then the manifest; cli suite and every scripts/test-*.sh green on Sage's gate; the design mutants killed (sage) | `docs/plans/2026-10-20_multi-instance.md` § Brief: several Mosaic Stack instances on one host | Landed 00d4cc1f6 on refactor (base 99a406ed3). Patch-id 5ec610a7 matches git diff 176a6bf4b..df65a3d4c on the 25 manifest paths. Manifest cc0ea9e8, Filbert approve r3 29713. Gate on d0bbc5c1c: packages 2664/0, scripts 429/0. Mutants 20/20. Unpushed. |
|
||||
| 191 | one kill switch for the bus, every run, Discord and new launches, from the CLI and the Console (MVP T1.4, mos-0974, Vikunja #414) | rocko; reviewers dewey | #1689 | done | — | Dewey approves DESIGN.md then the manifest; cli, bus, discord and webui suites and every scripts/test-*.sh green on Sage's gate; the mutants killed (sage) | `docs/plans/2026-10-20_kill-switch.md` § Brief: one kill switch for the bus, every run, Discord and new launches | Landed 0fa7bf421 fast-forward on 86f95bb27 (10 commits, 89 paths; manifest 0bcbf337 = 88 paths without BUILD.md). Dewey manifest APPROVE c30359 rev 3118. Gate: 77 mutants killed (75 assert + SR08, KS54 timeout with controls), suites green on Sage's window; N7 closed by runs suite 43/0/0 on 0fa7bf421. N6, clock fix, :52 assertion, generic cause go to Vikunja 502 (mos-1267/1268). Rebase next: 205, 236, 194, 253, 274, 268. |
|
||||
| 192 | Q175 phase 1: the Console verifies SSO itself (--sso), OIDC RP with oauth4webapi 3.8.8, no live change (MVP T1.2, #412) | launchpad; reviewers webby | #1690 | done | — | Webby approves the manifest; webui and cli suites, scripts/test-config.sh and every scripts/test-*.sh green on Sage's gate; design section 9 mutants killed (sage) | `docs/plans/2026-10-20_sso-phase1.md` § Brief: Q175 phase 1, the Console verifies SSO itself (--sso) | Landed 4429a104f on refactor (squash of launchpad/row192 53bb59462..37582e820, round 2 manifest 6f8cee14...7787). patch-id 4a4869026 matches the reviewed diff on all 18 paths. Gate on 088087ac9: package suites 2758/0, scripts 429/0, mutants 22 of 23 killed (#23 equivalent, sage-1407). npm ci in canonical, oauth4webapi 3.8.8. Live re-pin is a window with Mos (mos-1099 item 3). |
|
||||
| 193 | many humans per business, human roles, private and shared threads, one access.json store (MVP T1.3, Vikunja #414/#415) | morgana; reviewers duckworth | #1691 | in-review, round 1, request posted | — | Duckworth approves DESIGN.md then the manifest; bus, business, cli and webui suites and every scripts/test-*.sh green on Sage's gate; the mutants killed (sage) | `docs/plans/2026-10-20_human-users.md` § Brief: many humans per business, human roles, private and shared threads | — |
|
||||
| 193 | many humans per business, human roles, private and shared threads, one access.json store (MVP T1.3, Vikunja #414/#415) | morgana; reviewers duckworth | #1691 | in-progress | — | Duckworth approves DESIGN.md then the manifest; bus, business, cli and webui suites and every scripts/test-*.sh green on Sage's gate; the mutants killed (sage) | `docs/plans/2026-10-20_human-users.md` § Brief: many humans per business, human roles, private and shared threads | — |
|
||||
| 194 | agent lifecycle from the Console and the CLI (create, launch, terminate, regenerate, delete) with a per-agent hire/fire veto (MVP T1.5, Vikunja #414/#415) | honker; reviewers webby | #1692 | done | — | Webby approves DESIGN.md then the manifest; bus, cli, business and webui suites and every scripts/test-*.sh green on Sage's gate; the mutants killed (sage) | `docs/plans/2026-10-20_agent-lifecycle.md` § Brief: agent lifecycle from the Console and the CLI, with a per-agent hire/fire veto | Landed fd6dd327c on refactor, a fast-forward on e122aad8e. b18 is b17 3f451772d rebased: 16 commits =, series patch-id 64303253f8e6, packages equal b17. Webby approve webby-0120 on manifest 29f131ab. Honker gate on fd6dd327c: runb8 39 files 503/0/0, mutants 17/17; gate.sh 18 packages 3668/0, ten scripts rc=0, Docker up, skips environment-only and named. Overlap with 13 and 257 clean. #1692 closed. |
|
||||
| 195 | row 195: the Case 1 install on 10.1.1.53 as mosaic under /srv/mosaic, reusing C2-13's scripts by input under row 190's canon (Q181, Q182/Q183, Q185; ops-01 root steps under #423 step 1) | darkwing; reviewers duckworth | #1693 | done | 190 done, 226 done, 233 done | Duckworth approves DESIGN.md then the manifest; cli suite and every scripts/test-*.sh green on Sage's gate; the mutants killed (sage) | `docs/plans/2026-10-20_hub-own-uid.md` § Brief: the Case 1 install on 10.1.1.53 (row 195) | LANDED b65014647 (pushed 361ba2387 tip). Patch-id 656e43ff25de = 4ced5e63d..37171f65b; verify-commit ok, 5 paths. Combined gate with 213 on 38b83ea5c+paths 09:45:52-09:57:52Z: business 200/0 bus 542/0 harness 140/0 cli 663/0, all 10 scripts green, L1 killed. N1-N3 to DEFERRED next; follow-up darkwing/row195-n1 1e3c4767c. |
|
||||
| 196 | the human boundary across uids and humans: Console, tester listener, broker.sock and the path-bound human proof (P19 333, before federation R3, mos-0989) | gander; reviewers duckworth | #1694 | done | — | Duckworth approves DESIGN.md then the manifest; bus, webui and cli suites and every scripts/test-*.sh green on Sage's gate; the mutants killed (sage) | `docs/plans/2026-10-20_human-boundary.md` § Brief: the human boundary across uids and humans (P19 333) | Landed 7247d80c7 on 666173292: 39 manifest paths of 527b9a1e6 (CANDIDATE.sha256 537d30ae), patch-id 6f3777d593e6 matches f48725cfc..527b9a1e6 on the manifest, blob diff empty, tree matches merge-tree. Gate gander-0066 2334/0/1, all 10 shells, 38/38 mutants. Approved rev 2973 and rev 2986 (Duckworth, c30156). Overlap: 202 cli README and 194 Honker branches rebase onto it. BUILD-LOG and C1 DEFERRED follow doc-only. |
|
||||
@@ -250,7 +250,7 @@ Generated from `docs/plans/queue.json` revision 4095 by `queue render`. Do not e
|
||||
| 211 | Force-push guard: scripts/git-hooks/pre-push refuses non-fast-forward and delete pushes from every worktree, and scripts/slot-busy.sh exits 1 on BUSY (#1709, Vikunja #426, mos-1078/1083) | darkwing; reviewers dewey | #1709 | in-progress | — | Dewey approves the manifest; the new or changed scripts/test-*.sh and every other scripts/test-*.sh green on Sage's gate; the mutants killed; hook installed in the canonical checkout with a scripted refused force-push (sage) | `docs/plans/2026-10-20_force-push-guard.md` § Brief: a pre-push hook that refuses force-pushes, and a busy check that fails on BUSY (#426) | — |
|
||||
| 212 | Case 2 C2-01 packages/library: mapping, scope, reader, resolver, R-rules, renderItems, survey | gosalyn; reviewers webby | #1713 | done | — | Webby approves the manifest. The library tests and the cli suite are green on Sage's gate, along with every `scripts/test-*.sh`. (sage) | `docs/plans/2026-10-20_case2-rows.md` § C2-01 packages/library: mapping, scope, reader, resolver, R-rules, renderItems, survey | Landed ee7b2f137 (pushed 09:13Z) on f6c70f944, alone per mos-1162: the 21 manifest paths from a2c72ac95. Patch-id ea84d3e35470 equals the candidate's; blobs and modes match; verify-commit ok (21 paths). No 212 path changed after c771a36f4. Gate: Sage's landing gate 08:51-09:03Z on 4ced5e63d plus 212's and 197's paths, GATE_PASSED, node 981/0, selftests 429/0. Gosalyn r3 slot 451/0, test-queue 28/0. Row 197 waits for r6 (lane proof stopped, sage-1881). |
|
||||
| 213 | Case 2 C2-08 creative roles, persona file and block, M1, M2, M6, M10, prompt passed by file | morgana; reviewers webby | #1714 | done | — | Webby approves the manifest. The business, bus and harness suites, `scripts/test-config.sh` and every `scripts/test-*.sh` are green on Sage's gate, and the `models` mutant is killed. (sage) | `docs/plans/2026-10-20_case2-rows.md` § C2-08 creative roles, persona file and block, M1, M2, M6, M10, prompt passed by file | LANDED 361ba2387 (39 paths incl. 4 packet files). Patch-id f313e69b6f7e = c771a36f4..2b3e67e36; verify-commit ok, 35 manifest paths. Same combined gate as 195: business 200/0 bus 542/0 harness 140/0 cli 663/0, test-config 31/0 and all 10 scripts green, models mutant L1 killed. Webby N2 is row 235 acceptance (mos-1163). |
|
||||
| 214 | Case 2 C2-11a editor wikilink node, after measuring whether [[ round-trips | herb; reviewers filbert | #1715 | done | — | Filbert approves the manifest. The webui suite is green on Sage's gate, along with every `scripts/test-*.sh`. (sage) | `docs/plans/2026-10-20_case2-rows.md` § C2-11a editor wikilink node, after measuring whether [[ round-trips | LANDED 3a8e4abad (pushed 78b69e1e6..3a8e4abad, 6 commits cherry-picked onto 78b69e1e6). Gate GREEN 04:10:34Z, all package suites and scripts; queue package runs via test-queue.sh (156). Filbert approve c31679 rev 4093. Range-diff equal except BUILD-LOG context. |
|
||||
| 214 | Case 2 C2-11a editor wikilink node, after measuring whether [[ round-trips | herb; reviewers filbert | #1715 | in-progress | — | Filbert approves the manifest. The webui suite is green on Sage's gate, along with every `scripts/test-*.sh`. (sage) | `docs/plans/2026-10-20_case2-rows.md` § C2-11a editor wikilink node, after measuring whether [[ round-trips | — |
|
||||
| 215 | Case 2 C2-04 the library entry in the business file, consumer skips, M9, mirror, library.file and library.status | herb; reviewers filbert | #1716 | done | — | Filbert approves the manifest. The business, seat, cli, project, bus and webui suites are green on Sage's gate, along with every `scripts/test-*.sh`. (sage) | `docs/plans/2026-10-20_case2-rows.md` § C2-04 the library entry in the business file, consumer skips, M9, mirror, library.file and library.status | LANDED e54e05463 on 6096b051b (sage-2094). Patch-id 2f68188b6c9d equals a36523713..dde3445ab; 118 paths, blobs and modes equal; tree 5b1613245 equals merge-tree. No path overlap with rows 66/243; no hash pins either way. Gate: Filbert APPROVE rev 2924; herb-0095 grant sage-2071 2634 pass 0 fail, 1 webui skip (editor rebuild needs esbuild 0.28.2, env, not a 215 path), accepted. |
|
||||
| 216 | Case 2 C2-05a lookup index and queries | researcher; reviewers filbert | #1717 | done | 268 done | Filbert approves the manifest. The library suite is green on Sage's gate, along with every `scripts/test-*.sh`. (sage) | `docs/plans/2026-10-20_case2-rows.md` § C2-05a lookup index and queries | LANDED fbe57a05b (replay of r5 dae76ccc2 onto 8e2885d26, 3 commits, 0 patch-id mismatches), pushed 21:09Z with 582b86935. Sage joint gate GREEN 21:05:34Z on fbe57a05b, every package suite but queue and every scripts/test-*.sh green. Filbert APPROVE r2 c31384 rev 3697. N3 (get returns a universe own supporting files) binds the C2-05c row; N1 doc, N2 universeInfo echo and BUILD.md pronoun nit go to a later docs commit. |
|
||||
| 217 | Case 2 C2-06 the citation guard, the page store, the runner texts and the retry | beakley; reviewers webby | #1718 | done | 268 done, 283 done | Webby approves the manifest. The library, bus and harness suites are green on Sage's gate, along with every `scripts/test-*.sh`. (sage) | `docs/plans/2026-10-20_case2-rows.md` § C2-06 the citation guard, the page store, the runner texts and the retry | LANDED d0a6a87cd (pushed 11:15Z 10-24, with 296): 9 cherry-picks on 296, range-diff 5 = and 4 ! (BUILD-LOG/SESSIONS append conflicts kept both), blobs equal 96659ccf4 (79 paths); Webby c31549 rev 3901; gate GREEN 11:14:53Z own npm ci, library 142 bus 974 business 270 cli 988 harness 172 0 fail, all scripts rc 0; PUSH_READY; #1718 closed c31559; Vikunja 533 comment 1054. |
|
||||
@@ -291,7 +291,7 @@ Generated from `docs/plans/queue.json` revision 4095 by `queue render`. Do not e
|
||||
| 252 | Console inbox Approve and Deny for grant requests (#435, J2) | scrooge; reviewers webby | #1753 | done | — | Webby approves DESIGN.md then manifest; webui, bus, cli suites and every test-*.sh green on Sage's gate; DESIGN mutants killed; REQ-WEB-2 PRD amendment landed first (sage) | `docs/plans/2026-10-21_console-inbox-approve.md` § Gate | Landed 45ea3fa0d (replay of Scrooge 580037105 onto 30e6bb680, range-diff all =, 10 commits). Webby APPROVE c31229 rev 3612. Sage gate on 45ea3fa0d: every package suite 0 fail 0 skipped (bus 907, cli 943, webui 410), every scripts/test-*.sh rc 0, J1-J19 all killed except J11c and J18 (equivalent per BUILD.md), all restores match. Old-test numstat 1 1 vs fe4ee8c7b held. |
|
||||
| 253 | F27: case2-unit.sh and case1-accept.sh move to --tester-socket and refuse --tester-port with stack-release's migration wording; no .53/.54 copy with 196 until this lands (mos-1198 item 4) | gander; reviewers duckworth | #1758 | done | 196 done | reviewer approves DESIGN.md then the manifest; cli and webui suites whole and every scripts/test-*.sh on Sage's slot; mutants per DESIGN.md; static check covers scripts and tests (sage) | `docs/plans/2026-10-21_tester-socket-scripts.md` § Brief: case2-unit.sh and case1-accept.sh move to --tester-socket (F27) | Landed 48e450ce8 on refactor (fast-forward from aca16404c, 22 commits; landed sha = candidate, so range-diff and patch-id are identical by construction). Manifest 591020a1 (7 paths) rechecked against the commit: 0 mismatches. Gate: run 5 whole, cli 55 files, webui 58 files, all 10 scripts/test-*.sh green; M1-M40 killed 40/40. Duckworth APPROVE round 1, Gitea #1758 comment 30696. |
|
||||
| 254 | Library sync follow-up: client timeout, STATUS codes, README (222 deferrals, #1759) | megavolt; reviewers dewey | #1759 | done | 196 done | Dewey approves the manifest; bus, library and webui suites whole and every scripts/test-*.sh green on Sage's slot; the timeout and STATUS mutants killed (sage) | `docs/plans/2026-10-21_library-sync-followup.md` § Gate | Landed 331597709 (candidate b31300d79 blobs and modes on 41b0cf755; patch-id 96216216 equal, range-diff differs only in the message) plus doc eb9672c91 (Dewey N1 README sentence closed; N2 four 502 codes to DEFERRED, binding the Console library routes row). Merged-tree rerun on Sage's slot 23:44-23:55Z: library 77/0, bus 628/0, webui 305/0 with 1 skip, cli 749/0. Megavolt's gate megavolt-0102 covers scripts and both mutants. |
|
||||
| 255 | Bundled Pocket ID for users with no OIDC provider, design (Q203, #1760) | launchpad; reviewers webby | — | in-progress | 252 done | Webby approves DESIGN.md; design only, build rows follow after Sage checks it against Q203, O6, REQ-WEB-2 and REQ-INST-1 (sage) | `docs/plans/2026-10-21_bundled-pocket-id.md` § Gate | Vikunja 458, Gitea #1760, mos-1202 item 2. REQ-INST-2 lands in PRD 1.10 as post-v1 (mos-1204). Design only: no install, no download, no .53/.54, dragon-lin keeps Authentik. |
|
||||
| 255 | Bundled Pocket ID for users with no OIDC provider, design (Q203, #1760) | launchpad; reviewers webby | — | queued | 252 done | Webby approves DESIGN.md; design only, build rows follow after Sage checks it against Q203, O6, REQ-WEB-2 and REQ-INST-1 (sage) | `docs/plans/2026-10-21_bundled-pocket-id.md` § Gate | Vikunja 458, Gitea #1760, mos-1202 item 2. REQ-INST-2 lands in PRD 1.10 as post-v1 (mos-1204). Design only: no install, no download, no .53/.54, dragon-lin keeps Authentik. |
|
||||
| 256 | the human deletes a creative instance; any other actor refused, fire unchanged (REQ-COS-3, before the v1 tag) | honker; reviewers webby | #1761 | done | 194 done | Webby approves the manifest; touched test files whole plus business, bus, cli and webui suites and every scripts/test-*.sh green on Sage's slot; both mutants killed (sage) | `docs/plans/2026-10-21_creative-delete-human.md` § Brief: the human deletes a creative instance (REQ-COS-3, after row 194) | LANDED f12441d4f ff from 0165be266 (4 Honker commits, patch-id b28e6374). Webby APPROVE manifest-r3 01eb75aa (10 paths), request c30518. Gate 10:33-10:52Z: 4 touched files 31/0, 18 packages 3757/0 (bus 788, business 231, cli 832, webui 335), 10 scripts rc=0, mutants M1-M4b killed; skips named, env only (webui esbuild pin, test-queue root, test-task live x2). Overlap 13/257 clean. Lands before the 10-27 v1 tag. |
|
||||
| 257 | Q198 S1: pressure parser and pure rules evaluator (post-v1) | morgana; reviewers filbert | #1762 | done | — | Reviewer approves the manifest; touched test files whole plus the package suite green on Sage's slot; both brief mutants killed (post-v1, MVP keeps slot priority) (sage) | `docs/plans/2026-10-21_post-v1-row-briefs.md` § Brief: Q198 S1 pressure parser and pure rules evaluator | Landed 73692e1bb, pushed 17:41:58Z 10-24: Morgana's 4 commits cherry-picked onto 487500a16, patches unchanged; 9 supervisor paths match Filbert r2 manifest b176a88f (APPROVE c30190, rev 3037). Gate RED 17:20Z (webui row278 sign-out timing, old test, row touches no webui), full rerun GREEN 17:41:11Z, cli skip FED R7 allowed. push-check clean. #1762 c31613, closed. Carry 447286192 lands doc-only. |
|
||||
| 258 | Email E01: mail PRD addendum draft and schemas (post-v1) | ludwig; reviewers della | #1763 | in-progress | — | Reviewer approves the manifest; the row's named tests whole plus the mail suite and touched suites green on Sage's slot; reviewer-named mutants killed (post-v1, MVP keeps slot priority) (sage) | `docs/plans/2026-10-21_post-v1-row-briefs.md` § Brief: E01 mail PRD addendum draft and schemas | — |
|
||||
@@ -303,7 +303,7 @@ Generated from `docs/plans/queue.json` revision 4095 by `queue render`. Do not e
|
||||
| 264 | T1 item 7: independent sessions, compaction counts and clear context | gizmoduck; reviewers duckworth | #1769 | done | — | The named reviewer approves the manifest. The design's T1-T7 acceptance tests run whole plus every touched suite on Sage's slot; T3 waits on Vikunja 466 (N2, Claude's compaction count format and the managed dataRoot reader). The design's section 6 mutants are killed. (sage) | `docs/plans/2026-10-21_t1-sessions.md` § T1 item 7: independent sessions, compaction counts and clear context | LANDED 84f53540d ff on e479521d3 14:18:23Z, 21 commits = gizmoduck/row264-r13 tip (no rewrite, range-diff identity). Manifest 257408e9 approved Duckworth rev 3214 (c30640). Gate r13 2cc34aef8: 20 files, bus 827, cli 837, webui 345/1 skip, 10 shells; M1-M4 assertion kills (mos-1279 B, M2 4 files). Overlap none (13, 257). N1 actor_run binds row 269; N2 esbuild skip later gate (sage-2695). |
|
||||
| 265 | Case 2 C2-09b rooms: room.open, room.send with @tags, room.read | goldie; reviewers boyd | #1770 | done | 219 done | Boyd approves the manifest. The brief's tests run whole plus the bus suite and every touched suite on Sage's slot. Grep for hash pins on changed files before in-review. (sage) | `docs/plans/2026-10-20_case2-rows.md` § C2-09b rooms: room.open, room.send with @tags, room.read | LANDED 200dd396c (cherry-pick of ef4c93853, 50 blobs equal, log appends equal) 05:13Z on full gate 04:57-05:12Z: all package suites except queue 0 fail 0 skip (bus 957, cli 986, seat 218), every scripts/test-*.sh green. Boyd c31501 incl mos-1567 mount read. 22 gzip logs scanned decompressed, clean. #1770 closed c31510. Bundle 02c3c286. |
|
||||
| 266 | Case 2 C2-10c Console room view: merged timeline and @name completion | fethry; reviewers boyd, dewey | #1771 | done | 225 done, 265 done | Dewey approves the manifest. The webui suite is green on Sage's gate, along with every touched suite. Grep for hash pins on changed files before in-review. (sage) | `docs/plans/2026-10-20_case2-rows.md` § C2-10c Console room view: merged timeline and @name completion | LANDED 3e5041b43 18:00:49Z 10-24, patch-ids equal to Fethry b8b26ae4d..6418ec063; gate GREEN 18:00:08Z first run (bus 1000/0, webui 458/0, cli 989+1 FED R7 allowed, 12 scripts rc 0); Boyd c31609, Dewey c31610; #1771 c31615 closed; Dewey N2 T9 race goes to Mos |
|
||||
| 267 | Case 2 C2-15 hybrid universes: exclusions, pins and the end-to-end crossover | morgana; reviewers filbert | #1772 | in-progress | 216 done, 217 done, 224 done | Filbert approves the manifest. The brief's tests and the end-to-end crossover run whole plus every touched suite on Sage's slot. Grep for hash pins on changed files before in-review. (sage) | `docs/plans/2026-10-20_case2-rows.md` § C2-15 hybrid universes: exclusions, pins and the end-to-end crossover | R8 accepted (sage-2218). Webby's R8 noise note (gosalyn-0127) is for the builder. |
|
||||
| 267 | Case 2 C2-15 hybrid universes: exclusions, pins and the end-to-end crossover | morgana; reviewers filbert | #1772 | queued | 216 done, 217 done, 224 done | Filbert approves the manifest. The brief's tests and the end-to-end crossover run whole plus every touched suite on Sage's slot. Grep for hash pins on changed files before in-review. (sage) | `docs/plans/2026-10-20_case2-rows.md` § C2-15 hybrid universes: exclusions, pins and the end-to-end crossover | R8 accepted (sage-2218). Webby's R8 noise note (gosalyn-0127) is for the builder. |
|
||||
| 268 | Case 2 C2-01c playground levels: universe-level prefixes and draft universes | gizmoduck; reviewers webby | #1773 | done | — | Webby approves the manifest. The brief's tests run whole plus the library suite and every touched suite on Sage's slot; the brief's gate grep over packages and docs/case2 finds no library-playground-outside-book. Grep for hash pins on changed files before in-review. (sage) | `docs/plans/2026-10-20_case2-rows.md` § C2-01c playground levels: universe-level prefixes and draft universes | Landed c27207f6e fast-forward on ce6a96ad6 (1 commit, 9 paths, digest caa0fa4e). r4 is a carry of r3 aca53c4f0: range-diff =, patch-id 2942acc8 equal, blobs equal. Webby APPROVE round 2 on ecd9b35eb stands; carries webby-0112 (r3) and webby-0113 (r4, notes 34e1ab2bf). Gate: Gizmoduck r4 rerun brief 14/14, library 83/83, business 209/209, 0 skips; gate grep empty. 194 rebases next (business.test.mjs). |
|
||||
| 269 | 264b T1 item 7: per-session native store, workspace and single-writer fence | gizmoduck; reviewers duckworth | #1774 | done | 264 done | Duckworth approves this slice's manifest. Its design tests run whole plus every touched suite on Sage's slot, and its mutants are killed (RULINGS Q1). Tests T1 run half, T2 workspace and path mutants; whichever of 191 and 264b lands second wires 191's admission checks (Q5). (sage) | `docs/plans/2026-10-21_t1-sessions.md` § T1 item 7: independent sessions, compaction counts and clear context | Landed efcc9ea8d fast-forward on 94c33a43c (13 commits, 12 paths, manifest 455c12ee). r3 is a carry of r2 aafcc857c, range-diff = (Duckworth 0194, c30756, rev 3307). Gate r3 18:37-18:50Z all green, 0 skips: bus 834, seat 172, cli 869, case2-unit 43, singles 38. M1-M12 kills carry. Re-pin needs the Console restart by Jason or Mos right after the host. A 264a rollback refuses a v2 sessions.json, see BUILD.md Rollback. |
|
||||
| 270 | 264c T1 item 7: compaction count, exact for Pi | gizmoduck; reviewers duckworth | #1775 | done | 269 done | Duckworth approves this slice's manifest. Its design tests run whole plus every touched suite on Sage's slot, and its mutants are killed (RULINGS Q1). Test T3 Pi half; Claude shows unknown with the Vikunja 466 reason (Q6). (sage) | `docs/plans/2026-10-21_t1-sessions.md` § T1 item 7: independent sessions, compaction counts and clear context | LANDED 7328da8ca (with 271, mos-1351) 02:13Z 10-23 on 767bb62c7: 33 commits cherry-picked from gizmoduck/row270-271-r4 49cee846e (base 25c09f19f), range-diff all '=', 40 paths identical to 49cee846e. Manifest f4174458 (15 paths) matches the tip. Duckworth APPROVE c30930 rev 3389. C2 run 5 GREEN on 53406f3e4: bus 873, seat 202, cli 905, ten scripts, 38/38 mutants killed. #1775 closes. |
|
||||
@@ -324,7 +324,7 @@ Generated from `docs/plans/queue.json` revision 4095 by `queue render`. Do not e
|
||||
| 285 | Case 2 C2-08b role changes for agent writes and the chief's read grant | magica; reviewers poe, webby | #1790 | done | 213 done, 291 done | Webby approves the manifest. Business suite, test-config.sh and every scripts/test-*.sh green on Sage's gate. Grep for CREATIVE_KINDS and 64-hex pins of changed role files. Sage checks it against mos-1309 item 5. (sage) | `docs/plans/2026-10-20_case2-rows.md` § C2-08b role changes for agent writes and the chief's read grant (mos-1309 item 5) | LANDED 1f3d0350f 13:33:42Z: candidate 2e4503dc7 (14 commits) cherry-picked onto 25874bc86. Logs kept both sides; otherwise the tree equals the candidate except on 7 base paths. Poe c31568 rev 3919, Webby c31569 rev 3920. Sage gate GREEN 13:28:46Z with its own npm ci: 4236 package tests, 0 fail, 1 allowed FED R7 skip; 12 scripts rc 0. No CREATIVE_KINDS, no role-file hash pins. Roles match mos-1309 item 5. 286 rebases as r5. |
|
||||
| 286 | Case 2 C2-03b push and per-seat credentials for the library mirror (with R4, mos-1323) | rockerduck; reviewers boyd, dewey | #1791 | done | 222 done | Dewey approves the manifest. Library and bus suites and every scripts/test-*.sh green on Sage's gate, mutant killed. A search of the run output for the fixture token finds nothing. (sage) | `docs/plans/2026-10-20_case2-rows.md` § C2-03b push and per-seat credentials for the library mirror | LANDED 5d7801dc1 14:37:02Z: r6 75f6d56b1 (19 commits) picked onto d55944edb; logs kept both sides, code equals the candidate. Boyd c31587 rev 3942, Dewey c31588 rev 3943 (r2 c31584/c31585). Sage gate GREEN 14:36:42Z, own npm ci: 4251 tests, 0 fail, 1 allowed FED R7 skip; 12 scripts rc 0; fixture values in 0 of 29 outputs; B285-M1 kills T7/T9. r6 shortened two fixtures after push-check STOP secrets (sage-4112). Chief seed per sage-4100. |
|
||||
| 287 | Case 2 C2-21 the grant executor for a library project | steelbeak; reviewers boyd, dewey | #1792 | done | 226 done, 285 done | Dewey approves the manifest. Bus suite, Gitea Docker test, install test and every scripts/test-*.sh green on Sage's gate. A search of the run output for the fixture admin and seat tokens finds nothing. (sage) | `docs/plans/2026-10-20_case2-rows.md` § C2-21 the grant executor for a library project | LANDED 0e935776e 16:12:35Z 10-24 (cand c2338ed58 + a5370b198 on 9b6345466). Boyd APPROVE c31602 rev 3967, Dewey APPROVE c31603 rev 3968. Sage gate GREEN 16:11:38Z: bus 998/0 (Gitea Docker F-A/F-B + X-G3, minted 4 hits 0), business 283/0, cli 989 +1 allowed FED R7 skip, webui 442/0, 12 scripts rc 0. F-C negative record-only follow-up. Dewey N1-N3 non-blocking. #1792 closed c31606. No P19 task. |
|
||||
| 288 | Case 2 C2-02 agent writes, the notice, revert and her Console saves | bubba; reviewers webby | #1793 | in-progress | 215 done, 282 done, 283 done, 284 done, 285 done, 286 done | Webby approves the manifest. Library, bus and harness suites and every scripts/test-*.sh green on Sage's gate, mutants killed. A search of the run output for the fixture tokens finds nothing. (sage) | `docs/plans/2026-10-20_case2-rows.md` § C2-02 agent writes, the notice, revert and her Console saves | A2 revert contract (morgana-0199, sage-3596): library.revert is a human verb. Args {project, commit}, strict keys; project is the business.projects key (the chat repo link), not a UUID. Result {project, path, reverted, commit}. Refusals: 409 stale-base, and 409 library-revert-not-agent for an agent caller. Owner Bubba under Q245 (mos-1506). |
|
||||
| 288 | Case 2 C2-02 agent writes, the notice, revert and her Console saves | bubba; reviewers webby | #1793 | briefed | 215 done, 282 done, 283 done, 284 done, 285 done, 286 done | Webby approves the manifest. Library, bus and harness suites and every scripts/test-*.sh green on Sage's gate, mutants killed. A search of the run output for the fixture tokens finds nothing. (sage) | `docs/plans/2026-10-20_case2-rows.md` § C2-02 agent writes, the notice, revert and her Console saves | A2 revert contract (morgana-0199, sage-3596): library.revert is a human verb. Args {project, commit}, strict keys; project is the business.projects key (the chat repo link), not a UUID. Result {project, path, reverted, commit}. Refusals: 409 stale-base, and 409 library-revert-not-agent for an agent caller. Owner Bubba under Q245 (mos-1506). |
|
||||
| 289 | Case 2 C2-11b Console library pages and her saves (Haiku subagent, decision 129) | researcher; reviewers filbert | #1794 | briefed | 214 done, 288 done | Filbert approves the manifest. The webui suite and every scripts/test-*.sh are green on Sage's gate. Grep for hash pins on bus.js and serve.mjs before in-review. (sage) | `docs/plans/2026-10-20_case2-rows.md` § C2-11b Console library pages and her saves | — |
|
||||
| 290 | Case 2 C2-12b the setup sheet rewrite for free chat, supporting folders and saves | louie; reviewers webby | #1795 | done | 217 done, 218 done, 285 done, 291 done | Doc only. Webby approves it against the ruling. Before landing, grep packages/*/tests and scripts/test-*.sh to confirm no test reads the file. (sage) | `docs/plans/2026-10-20_case2-rows.md` § C2-12b the setup sheet rewrite for free chat, supporting folders and saves | LANDED 3d7dff345 on refactor, pushed 14:55:11Z: cherry-pick of 963303403..22510988d onto 18334d713, four commits equal by patch-id and range-diff, SETUP-SHEET.md blob 37b451c6 as approved. Gate per brief (doc only): Webby approve c31596 rev 3954; grep of packages/*/tests, scripts/test-*.sh and tests/ for SETUP-SHEET finds nothing (positive control hits). No suite run: no test reads the file (decision 137). #1795 closed c31597. |
|
||||
| 291 | PRD 1.16 library.write routes under REQ-LIB-6 (mos-1328) | gosalyn; reviewers webby | #1796 | done | 282 done | Webby approves the text. Jason says yes to the exact text (Mos asks him, mos-1328 step 3); no landing before that. requirements.test.mjs and every scripts/test-*.sh that reads the PRD or requirements.json are green on Sage's gate. Mos gets the landing commit. (sage) | `docs/plans/2026-10-22_prd-1-16-brief.md` § PRD 1.16 library.write routes under REQ-LIB-6 (mos-1328) | Landed 4874d1e83 on e909ef51a: 482e865ac replayed, patch-id equal (cef7de74), range-diff =. Webby APPROVE c30848 on 482e865ac; Jason approved the text in Q229. Gate 16:15Z on the tip tree: every package suite but queue, 0 fail 0 skipped (tasks 100, bus 891, cli 941, plugins 119, webui 392, business 231), every scripts/test-*.sh rc 0 (queue-root and row 244 skips only). PRD 1.16. 1.17 is row 308. |
|
||||
@@ -366,11 +366,11 @@ Generated from `docs/plans/queue.json` revision 4095 by `queue render`. Do not e
|
||||
| 327 | Push check in the repository: scripts/push-check.sh passes a binary only when an approved review candidate lists its path and sha256, plus scripts/test-push-check.sh (#1825, mos-1572 A) | bradford; reviewers dewey, poe | #1825 | done | — | Poe gives the first Codex review, Dewey reviews the landing candidate; scripts/test-push-check.sh and every other scripts/test-*.sh green on Sage's gate; the mutants killed; Sage's next push runs through scripts/push-check.sh (sage) | `docs/plans/2026-10-24_push-guard-binary-manifest.md` § Brief: push-check.sh passes a binary only when the reviewed candidate lists its path and sha256 | LANDED 68c08b4e7 (pushed 07:18:03Z 10-24): 3 commits on 6a5e12511, blobs equal round 3 d858c9f8b; Poe c31525 rev 3882, Dewey c31526 rev 3883; gate GREEN 07:15:49Z with its own npm ci (first run red on empty canonical node_modules, sage-4023); 6 push-check mutants KILLED; next push 42ba0f5bc ran through scripts/push-check.sh --fetch, PUSH_READY; #1825 closed c31530. |
|
||||
| 328 | T9 library-credential test: the second broker start passes Node's TIMEOUT_MAX interval so no timer tick overlaps the verb sync; mutant fails a 50-run stress loop (#1826, Vikunja 622, mos-1594) | fethry; reviewers boyd, dewey | #1826 | done | — | Boyd gives the first Codex review, Dewey reviews the landing candidate; touched tests run whole, the 50-run stress loop passes with the fix and M1 fails it; bus and every scripts/test-*.sh green on Sage's gate (sage) | `docs/plans/2026-10-24_t9-library-sync-race.md` § Brief: the T9 verb sync on the second start runs with no timer tick able to overlap it | LANDED 8bb5b3c73 19:40Z 10-24 (8 Fethry commits on 5e77cdd09). Gate GREEN 19:38:54Z on 8bb5b3c73, no drift. Boyd APPROVE c31621, Dewey APPROVE c31622. #1826 closed c31625; Vikunja 622 done. |
|
||||
| 329 | Model proxy P1: model-proxy package, pinned fetch with sha256 refusal, mosaic proxy init, proxy unit (237, mos-1600) | magica; reviewers boyd, dewey | #1827 | done | — | Boyd first review, re-measuring both release hashes; Dewey landing review; BUILD.md has the acceptance output. Sage's landing gate: every package suite but queue plus every scripts/test-*.sh green, touched test files whole, refusals each tested, named mutants killed (brief Gate). (sage) | `docs/plans/2026-10-24_model-proxy-rows.md` § Brief: P1 the model-proxy package, pinned fetch, proxy init and the proxy unit | Landed 61ebf2c34 (pushed 23:31:27Z 10-24); Magica r2 commits cherry-picked onto 669cdf42c. Boyd c31642 rev 4048, Dewey c31643 rev 4047. Sage fresh gate GREEN 23:29:14Z (mos-1634). #1827 closed c31650; Vikunja 237 comment 1109, stays open for P2-P7. DEFERRED: N2, R2-N1 to row 331; N1 to P7. |
|
||||
| 330 | Model proxy P4: Console proxy page (OAuth sign-in, API keys, priority, key list, usage) via bus verbs (Q250, mos-1606/1608) | poe; reviewers filbert, gladstone | #1828 | in-progress | — | Gladstone first review; Filbert landing review; starts now, lands after P1 rebased on it; webui with MOSAIC_REQUIRE_EDITOR_BUILD=1. Sage gate: all package suites but queue and every scripts/test-*.sh green; brief Gate. (sage) | `docs/plans/2026-10-24_proxy-page-brief.md` § Brief: P4 the Console proxy page | — |
|
||||
| 331 | Model proxy P2: per-agent model keys, reconcile and launch preflight (237, mos-1600) | magica; reviewers boyd, dewey | #1829 | in-progress | 329 done | Boyd first review; Dewey landing review; three named mutants killed. Sage gate: all package suites but queue and every scripts/test-*.sh green; brief Gate. (sage) | `docs/plans/2026-10-24_model-proxy-rows.md` § Brief: P2 per-agent model keys, reconcile and the launch preflight | P2 input from magica-0020 (acceptance-probe-4): 8.0.42 revocation lags ~105ms after v8 PUT (first call 200, then 401); reconcile/revoke must poll to 401, not assume instant. Per-key model objects give 422; no per-key model restriction. |
|
||||
| 330 | Model proxy P4: Console proxy page (OAuth sign-in, API keys, priority, key list, usage) via bus verbs (Q250, mos-1606/1608) | poe; reviewers filbert, gladstone | #1828 | briefed | — | Gladstone first review; Filbert landing review; starts now, lands after P1 rebased on it; webui with MOSAIC_REQUIRE_EDITOR_BUILD=1. Sage gate: all package suites but queue and every scripts/test-*.sh green; brief Gate. (sage) | `docs/plans/2026-10-24_proxy-page-brief.md` § Brief: P4 the Console proxy page | — |
|
||||
| 331 | Model proxy P2: per-agent model keys, reconcile and launch preflight (237, mos-1600) | sage; reviewers boyd, dewey | #1829 | briefed | 329 done | Boyd first review; Dewey landing review; three named mutants killed. Sage gate: all package suites but queue and every scripts/test-*.sh green; brief Gate. (sage) | `docs/plans/2026-10-24_model-proxy-rows.md` § Brief: P2 per-agent model keys, reconcile and the launch preflight | P2 input from magica-0020 (acceptance-probe-4): 8.0.42 revocation lags ~105ms after v8 PUT (first call 200, then 401); reconcile/revoke must poll to 401, not assume instant. Per-key model objects give 422; no per-key model restriction. |
|
||||
| 332 | Model proxy P3: harness config from the proxy (237, mos-1600) | fethry; reviewers dewey, gladstone | #1830 | in-progress | — | Gladstone first review; Dewey landing review; named mutant killed. Sage gate: all package suites but queue and every scripts/test-*.sh green; brief Gate. (sage) | `docs/plans/2026-10-24_model-proxy-rows.md` § Brief: P3 harness config from the proxy | mos-1616: amendment docs/plans/2026-10-24_p3-pi-routes-amendment.md (blob 079debd5605c, c8a9bcf94) replaces P3 item 4's non-Anthropic line and adds tests and a mutant. Starts now on a new Codex thread against P2's {baseUrl, key}; lands only after 329 and 331 land and its own gate (mos-1351). |
|
||||
| 333 | Model proxy P5: usage plug-in and per-key limits, per-key count only (237, mos-1600) | sage; reviewers boyd, duckworth | #1831 | briefed | 331 done | Boyd first review; Dewey landing review; named mutant killed. Sage gate: all package suites but queue and every scripts/test-*.sh green; brief Gate. (sage) | `docs/plans/2026-10-24_model-proxy-rows.md` § Brief: P5 usage plug-in and per-key limits | — |
|
||||
| 334 | Case 2 proxy unit: case2-unit.sh renders P1's mosaic-proxy.service and the bus Wants/After under --proxy (237, mos-1616) | rockerduck; reviewers boyd, webby | #1832 | in-progress | 329 done | Codex builder after 329 lands; Boyd first review, Webby Claude review; run list from imports, case1-accept off; mutants: bus loses After= under --proxy, proxy ExecStart skips run.mjs; Sage landing gate: every package suite but queue, every scripts/test-*.sh (sage) | `docs/plans/2026-10-24_case2-proxy-unit-brief.md` § Brief: Case 2 proxy unit in case2-unit.sh and case2-install.sh | — |
|
||||
| 334 | Case 2 proxy unit: case2-unit.sh renders P1's mosaic-proxy.service and the bus Wants/After under --proxy (237, mos-1616) | sage; reviewers boyd, webby | #1832 | queued | 329 done | Codex builder after 329 lands; Boyd first review, Webby Claude review; run list from imports, case1-accept off; mutants: bus loses After= under --proxy, proxy ExecStart skips run.mjs; Sage landing gate: every package suite but queue, every scripts/test-*.sh (sage) | `docs/plans/2026-10-24_case2-proxy-unit-brief.md` § Brief: Case 2 proxy unit in case2-unit.sh and case2-install.sh | — |
|
||||
|
||||
<!-- mosaic-queue:end -->
|
||||
|
||||
|
||||
+38
-724
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"version": 1,
|
||||
"canonicalRoot": "/mnt/storage/src/mosaic-stack",
|
||||
"revision": 4095,
|
||||
"revision": 4072,
|
||||
"rows": [
|
||||
{
|
||||
"id": 1,
|
||||
@@ -20532,7 +20532,7 @@
|
||||
"closes": [
|
||||
1691
|
||||
],
|
||||
"state": "in-review",
|
||||
"state": "in-progress",
|
||||
"previousState": null,
|
||||
"required": false,
|
||||
"requiredSince": null,
|
||||
@@ -20547,72 +20547,7 @@
|
||||
"reviewers": [
|
||||
"duckworth"
|
||||
],
|
||||
"review": {
|
||||
"rounds": [
|
||||
{
|
||||
"n": 1,
|
||||
"op": "morgana-193-inreview",
|
||||
"by": "morgana",
|
||||
"at": "2026-10-25T04:01:27.379Z",
|
||||
"issue": 1691,
|
||||
"candidate": {
|
||||
"kind": "commit",
|
||||
"digest": "e2d161e2e6354fe7f7083b95ccae8b202513f5e5",
|
||||
"text": null
|
||||
},
|
||||
"request": "comment",
|
||||
"attempts": [
|
||||
{
|
||||
"op": "morgana-193-inreview",
|
||||
"by": "morgana",
|
||||
"at": "2026-10-25T04:01:27.379Z",
|
||||
"state": "abandoned",
|
||||
"comment": null,
|
||||
"transport": null,
|
||||
"resolutions": [
|
||||
{
|
||||
"verb": "abandon",
|
||||
"op": "sage-4364-ab193",
|
||||
"by": "sage",
|
||||
"at": "2026-10-25T04:05:57.752Z",
|
||||
"comment": null,
|
||||
"reason": "pre-send credential check failed (morgana has no token file); outcome not logged under lock contention; no comment on #1691 (checked 04:05Z, 0 comments)"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"op": "sage-4364-rq193",
|
||||
"by": "sage",
|
||||
"at": "2026-10-25T04:08:08.349Z",
|
||||
"state": "posted",
|
||||
"comment": 31674,
|
||||
"transport": null,
|
||||
"resolutions": [
|
||||
{
|
||||
"verb": "resolve",
|
||||
"op": "sage-4372-rs193",
|
||||
"by": "sage",
|
||||
"at": "2026-10-25T04:13:06.534Z",
|
||||
"comment": 31674,
|
||||
"reason": null
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"duplicateRisk": true,
|
||||
"receipts": [
|
||||
{
|
||||
"reviewer": "duckworth",
|
||||
"op": "duckworth-193-verdict-r1",
|
||||
"at": "2026-10-25T04:19:54.404Z",
|
||||
"verdict": "approve",
|
||||
"comment": 31676,
|
||||
"candidate": "e2d161e2e6354fe7f7083b95ccae8b202513f5e5"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"review": null,
|
||||
"claim": {
|
||||
"seat": "morgana",
|
||||
"op": "morgana-193-start"
|
||||
@@ -20620,8 +20555,8 @@
|
||||
"note": null,
|
||||
"blockedReason": null,
|
||||
"createdAt": "2026-10-20T18:53:02.879Z",
|
||||
"updatedAt": "2026-10-25T04:19:54.404Z",
|
||||
"updatedBy": "duckworth"
|
||||
"updatedAt": "2026-10-20T18:57:44.367Z",
|
||||
"updatedBy": "morgana"
|
||||
},
|
||||
{
|
||||
"id": 194,
|
||||
@@ -23197,7 +23132,7 @@
|
||||
"closes": [
|
||||
1715
|
||||
],
|
||||
"state": "done",
|
||||
"state": "in-progress",
|
||||
"previousState": null,
|
||||
"required": false,
|
||||
"requiredSince": null,
|
||||
@@ -23212,73 +23147,15 @@
|
||||
"reviewers": [
|
||||
"filbert"
|
||||
],
|
||||
"review": {
|
||||
"rounds": [
|
||||
{
|
||||
"n": 1,
|
||||
"op": "herb-214-inreview",
|
||||
"by": "herb",
|
||||
"at": "2026-10-25T04:10:39.673Z",
|
||||
"issue": 1715,
|
||||
"candidate": {
|
||||
"kind": "commit",
|
||||
"digest": "584ee1e2067982edb71459af7c15e4ed7162279a",
|
||||
"text": null
|
||||
},
|
||||
"request": "comment",
|
||||
"attempts": [
|
||||
{
|
||||
"op": "herb-214-inreview",
|
||||
"by": "herb",
|
||||
"at": "2026-10-25T04:10:39.673Z",
|
||||
"state": "abandoned",
|
||||
"comment": null,
|
||||
"transport": null,
|
||||
"resolutions": [
|
||||
{
|
||||
"verb": "abandon",
|
||||
"op": "sage-4379-ab214",
|
||||
"by": "sage",
|
||||
"at": "2026-10-25T04:20:59.781Z",
|
||||
"comment": null,
|
||||
"reason": "no comment on #1715 (comments empty at 04:14:08Z); herb has no Gitea token, so the pre-send step could not post; Sage posts the request"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"op": "sage-4379-rq214",
|
||||
"by": "sage",
|
||||
"at": "2026-10-25T04:22:04.588Z",
|
||||
"state": "posted",
|
||||
"comment": 31678,
|
||||
"transport": {
|
||||
"outcome": "posted",
|
||||
"status": 201,
|
||||
"comment": 31678,
|
||||
"detail": "created"
|
||||
},
|
||||
"resolutions": []
|
||||
}
|
||||
],
|
||||
"duplicateRisk": true,
|
||||
"receipts": [
|
||||
{
|
||||
"reviewer": "filbert",
|
||||
"op": "filbert-214-r1-record",
|
||||
"at": "2026-10-25T04:26:01.460Z",
|
||||
"verdict": "approve",
|
||||
"comment": 31679,
|
||||
"candidate": "584ee1e2067982edb71459af7c15e4ed7162279a"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
"review": null,
|
||||
"claim": {
|
||||
"seat": "herb",
|
||||
"op": "herb-214-start"
|
||||
},
|
||||
"claim": null,
|
||||
"note": "LANDED 3a8e4abad (pushed 78b69e1e6..3a8e4abad, 6 commits cherry-picked onto 78b69e1e6). Gate GREEN 04:10:34Z, all package suites and scripts; queue package runs via test-queue.sh (156). Filbert approve c31679 rev 4093. Range-diff equal except BUILD-LOG context.",
|
||||
"note": null,
|
||||
"blockedReason": null,
|
||||
"createdAt": "2026-10-20T20:45:11.283Z",
|
||||
"updatedAt": "2026-10-25T04:30:41.788Z",
|
||||
"updatedAt": "2026-10-24T22:05:24.948Z",
|
||||
"updatedBy": "sage"
|
||||
},
|
||||
{
|
||||
@@ -27518,7 +27395,7 @@
|
||||
"owner": "launchpad",
|
||||
"issues": [],
|
||||
"closes": [],
|
||||
"state": "in-progress",
|
||||
"state": "queued",
|
||||
"previousState": null,
|
||||
"required": false,
|
||||
"requiredSince": null,
|
||||
@@ -27539,15 +27416,12 @@
|
||||
"webby"
|
||||
],
|
||||
"review": null,
|
||||
"claim": {
|
||||
"seat": "launchpad",
|
||||
"op": "launchpad-255-start"
|
||||
},
|
||||
"claim": null,
|
||||
"note": "Vikunja 458, Gitea #1760, mos-1202 item 2. REQ-INST-2 lands in PRD 1.10 as post-v1 (mos-1204). Design only: no install, no download, no .53/.54, dragon-lin keeps Authentik.",
|
||||
"blockedReason": null,
|
||||
"createdAt": "2026-10-21T20:25:14.463Z",
|
||||
"updatedAt": "2026-10-25T04:24:42.333Z",
|
||||
"updatedBy": "launchpad"
|
||||
"updatedAt": "2026-10-21T20:25:14.463Z",
|
||||
"updatedBy": "sage"
|
||||
},
|
||||
{
|
||||
"id": 256,
|
||||
@@ -28319,7 +28193,7 @@
|
||||
"closes": [
|
||||
1772
|
||||
],
|
||||
"state": "in-progress",
|
||||
"state": "queued",
|
||||
"previousState": null,
|
||||
"required": false,
|
||||
"requiredSince": null,
|
||||
@@ -28348,15 +28222,12 @@
|
||||
"filbert"
|
||||
],
|
||||
"review": null,
|
||||
"claim": {
|
||||
"seat": "morgana",
|
||||
"op": "morgana-267-start"
|
||||
},
|
||||
"claim": null,
|
||||
"note": "R8 accepted (sage-2218). Webby's R8 noise note (gosalyn-0127) is for the builder.",
|
||||
"blockedReason": null,
|
||||
"createdAt": "2026-10-21T22:44:03.355Z",
|
||||
"updatedAt": "2026-10-25T04:18:49.322Z",
|
||||
"updatedBy": "morgana"
|
||||
"updatedAt": "2026-10-24T22:11:36.776Z",
|
||||
"updatedBy": "sage"
|
||||
},
|
||||
{
|
||||
"id": 268,
|
||||
@@ -30888,7 +30759,7 @@
|
||||
"closes": [
|
||||
1793
|
||||
],
|
||||
"state": "in-progress",
|
||||
"state": "briefed",
|
||||
"previousState": null,
|
||||
"required": false,
|
||||
"requiredSince": null,
|
||||
@@ -30929,15 +30800,12 @@
|
||||
"webby"
|
||||
],
|
||||
"review": null,
|
||||
"claim": {
|
||||
"seat": "bubba",
|
||||
"op": "bubba-288-start"
|
||||
},
|
||||
"claim": null,
|
||||
"note": "A2 revert contract (morgana-0199, sage-3596): library.revert is a human verb. Args {project, commit}, strict keys; project is the business.projects key (the chat repo link), not a UUID. Result {project, path, reverted, commit}. Refusals: 409 stale-base, and 409 library-revert-not-agent for an agent caller. Owner Bubba under Q245 (mos-1506).",
|
||||
"blockedReason": null,
|
||||
"createdAt": "2026-10-22T18:33:12.519Z",
|
||||
"updatedAt": "2026-10-25T04:04:36.422Z",
|
||||
"updatedBy": "bubba"
|
||||
"updatedAt": "2026-10-24T21:59:54.957Z",
|
||||
"updatedBy": "sage"
|
||||
},
|
||||
{
|
||||
"id": 289,
|
||||
@@ -34738,7 +34606,7 @@
|
||||
"closes": [
|
||||
1828
|
||||
],
|
||||
"state": "in-progress",
|
||||
"state": "briefed",
|
||||
"previousState": null,
|
||||
"required": false,
|
||||
"requiredSince": null,
|
||||
@@ -34755,27 +34623,24 @@
|
||||
"gladstone"
|
||||
],
|
||||
"review": null,
|
||||
"claim": {
|
||||
"seat": "poe",
|
||||
"op": "poe-330-start"
|
||||
},
|
||||
"claim": null,
|
||||
"note": null,
|
||||
"blockedReason": null,
|
||||
"createdAt": "2026-10-24T19:09:06.624Z",
|
||||
"updatedAt": "2026-10-25T03:59:15.459Z",
|
||||
"updatedBy": "poe"
|
||||
"updatedAt": "2026-10-24T21:06:08.273Z",
|
||||
"updatedBy": "sage"
|
||||
},
|
||||
{
|
||||
"id": 331,
|
||||
"piece": "Model proxy P2: per-agent model keys, reconcile and launch preflight (237, mos-1600)",
|
||||
"owner": "magica",
|
||||
"owner": "sage",
|
||||
"issues": [
|
||||
1829
|
||||
],
|
||||
"closes": [
|
||||
1829
|
||||
],
|
||||
"state": "in-progress",
|
||||
"state": "briefed",
|
||||
"previousState": null,
|
||||
"required": false,
|
||||
"requiredSince": null,
|
||||
@@ -34797,15 +34662,12 @@
|
||||
"dewey"
|
||||
],
|
||||
"review": null,
|
||||
"claim": {
|
||||
"seat": "magica",
|
||||
"op": "magica-331-start"
|
||||
},
|
||||
"claim": null,
|
||||
"note": "P2 input from magica-0020 (acceptance-probe-4): 8.0.42 revocation lags ~105ms after v8 PUT (first call 200, then 401); reconcile/revoke must poll to 401, not assume instant. Per-key model objects give 422; no per-key model restriction.",
|
||||
"blockedReason": null,
|
||||
"createdAt": "2026-10-24T19:11:25.454Z",
|
||||
"updatedAt": "2026-10-25T03:57:40.286Z",
|
||||
"updatedBy": "magica"
|
||||
"updatedAt": "2026-10-24T19:28:58.606Z",
|
||||
"updatedBy": "sage"
|
||||
},
|
||||
{
|
||||
"id": 332,
|
||||
@@ -34886,14 +34748,14 @@
|
||||
{
|
||||
"id": 334,
|
||||
"piece": "Case 2 proxy unit: case2-unit.sh renders P1's mosaic-proxy.service and the bus Wants/After under --proxy (237, mos-1616)",
|
||||
"owner": "rockerduck",
|
||||
"owner": "sage",
|
||||
"issues": [
|
||||
1832
|
||||
],
|
||||
"closes": [
|
||||
1832
|
||||
],
|
||||
"state": "in-progress",
|
||||
"state": "queued",
|
||||
"previousState": null,
|
||||
"required": false,
|
||||
"requiredSince": null,
|
||||
@@ -34915,15 +34777,12 @@
|
||||
"webby"
|
||||
],
|
||||
"review": null,
|
||||
"claim": {
|
||||
"seat": "rockerduck",
|
||||
"op": "rockerduck-334-start"
|
||||
},
|
||||
"claim": null,
|
||||
"note": null,
|
||||
"blockedReason": null,
|
||||
"createdAt": "2026-10-24T23:14:14.427Z",
|
||||
"updatedAt": "2026-10-25T04:02:42.539Z",
|
||||
"updatedBy": "rockerduck"
|
||||
"updatedAt": "2026-10-24T23:14:14.427Z",
|
||||
"updatedBy": "sage"
|
||||
}
|
||||
],
|
||||
"log": [
|
||||
@@ -141131,551 +140990,6 @@
|
||||
"receipt": "ok sage-284-done-landed rev 4072 row 284 in-review→done round 2"
|
||||
},
|
||||
"viewSha": "546679f234a78b2d4582ed5a50b90c7427bf82cf2bd75243eeccad1f5730fa79"
|
||||
},
|
||||
{
|
||||
"rev": 4073,
|
||||
"op": "sage-4334-a331",
|
||||
"verb": "assign",
|
||||
"args": {
|
||||
"id": 331,
|
||||
"seat": "magica"
|
||||
},
|
||||
"by": "sage",
|
||||
"at": "2026-10-25T03:52:17.506Z",
|
||||
"semantics": 2,
|
||||
"result": {
|
||||
"row": 331,
|
||||
"field": "owner",
|
||||
"from": "sage",
|
||||
"to": "magica",
|
||||
"receipt": "ok sage-4334-a331 rev 4073 row 331 owner: sage→magica"
|
||||
},
|
||||
"viewSha": "1e9c8f52178b479578892e5663e3527a9dae656170ed3438cddeca2c78866955"
|
||||
},
|
||||
{
|
||||
"rev": 4074,
|
||||
"op": "sage-4334-a334",
|
||||
"verb": "assign",
|
||||
"args": {
|
||||
"id": 334,
|
||||
"seat": "rockerduck"
|
||||
},
|
||||
"by": "sage",
|
||||
"at": "2026-10-25T03:53:46.926Z",
|
||||
"semantics": 2,
|
||||
"result": {
|
||||
"row": 334,
|
||||
"field": "owner",
|
||||
"from": "sage",
|
||||
"to": "rockerduck",
|
||||
"receipt": "ok sage-4334-a334 rev 4074 row 334 owner: sage→rockerduck"
|
||||
},
|
||||
"viewSha": "2d93ddcefd3cf8b25beede74eaba414a8da4edee72a9735413ee095e15435a50"
|
||||
},
|
||||
{
|
||||
"rev": 4075,
|
||||
"op": "sage-4334-b334",
|
||||
"verb": "move",
|
||||
"args": {
|
||||
"id": 334,
|
||||
"to": "briefed",
|
||||
"reason": null,
|
||||
"candidate": null,
|
||||
"evidence": null,
|
||||
"issue": null
|
||||
},
|
||||
"by": "sage",
|
||||
"at": "2026-10-25T03:55:04.114Z",
|
||||
"semantics": 2,
|
||||
"result": {
|
||||
"row": 334,
|
||||
"from": "queued",
|
||||
"to": "briefed",
|
||||
"round": null,
|
||||
"issue": null,
|
||||
"candidate": null,
|
||||
"receipt": "ok sage-4334-b334 rev 4075 row 334 queued→briefed"
|
||||
},
|
||||
"viewSha": "8cd19c9b7486a91e219faf7ae2d731bcdafc4a92bb4aaf2422f55ef9edf5ff8f"
|
||||
},
|
||||
{
|
||||
"rev": 4076,
|
||||
"op": "magica-331-start",
|
||||
"verb": "move",
|
||||
"args": {
|
||||
"id": 331,
|
||||
"to": "in-progress",
|
||||
"reason": null,
|
||||
"candidate": null,
|
||||
"evidence": null,
|
||||
"issue": null
|
||||
},
|
||||
"by": "magica",
|
||||
"at": "2026-10-25T03:57:40.286Z",
|
||||
"semantics": 2,
|
||||
"result": {
|
||||
"row": 331,
|
||||
"from": "briefed",
|
||||
"to": "in-progress",
|
||||
"round": null,
|
||||
"issue": null,
|
||||
"candidate": null,
|
||||
"receipt": "ok magica-331-start rev 4076 row 331 briefed→in-progress"
|
||||
},
|
||||
"viewSha": "9667805c4557587ca1b7071e345711cc5ae97d6e3e41fa73b99dcde3425d6dad"
|
||||
},
|
||||
{
|
||||
"rev": 4077,
|
||||
"op": "poe-330-start",
|
||||
"verb": "move",
|
||||
"args": {
|
||||
"id": 330,
|
||||
"to": "in-progress",
|
||||
"reason": null,
|
||||
"candidate": null,
|
||||
"evidence": null,
|
||||
"issue": null
|
||||
},
|
||||
"by": "poe",
|
||||
"at": "2026-10-25T03:59:15.459Z",
|
||||
"semantics": 2,
|
||||
"result": {
|
||||
"row": 330,
|
||||
"from": "briefed",
|
||||
"to": "in-progress",
|
||||
"round": null,
|
||||
"issue": null,
|
||||
"candidate": null,
|
||||
"receipt": "ok poe-330-start rev 4077 row 330 briefed→in-progress"
|
||||
},
|
||||
"viewSha": "35201392103cc4340b3b15202e2823900cd14b363d2510fa5bc5ffc1bb29091b"
|
||||
},
|
||||
{
|
||||
"rev": 4078,
|
||||
"op": "morgana-193-inreview",
|
||||
"verb": "move",
|
||||
"args": {
|
||||
"id": 193,
|
||||
"to": "in-review",
|
||||
"reason": null,
|
||||
"candidate": "e2d161e2e6354fe7f7083b95ccae8b202513f5e5",
|
||||
"evidence": null,
|
||||
"issue": null
|
||||
},
|
||||
"by": "morgana",
|
||||
"at": "2026-10-25T04:01:27.379Z",
|
||||
"semantics": 2,
|
||||
"result": {
|
||||
"row": 193,
|
||||
"from": "in-progress",
|
||||
"to": "in-review",
|
||||
"round": 1,
|
||||
"issue": 1691,
|
||||
"candidate": {
|
||||
"kind": "commit",
|
||||
"digest": "e2d161e2e6354fe7f7083b95ccae8b202513f5e5",
|
||||
"text": null
|
||||
},
|
||||
"receipt": "ok morgana-193-inreview rev 4078 row 193 in-progress→in-review round 1 on #1691; request morgana-193-inreview requesting"
|
||||
},
|
||||
"viewSha": "0895b680ea6319a5468aeef70e79d3c349354640c0aac5c70c70cd719d311181"
|
||||
},
|
||||
{
|
||||
"rev": 4079,
|
||||
"op": "rockerduck-334-start",
|
||||
"verb": "move",
|
||||
"args": {
|
||||
"id": 334,
|
||||
"to": "in-progress",
|
||||
"reason": null,
|
||||
"candidate": null,
|
||||
"evidence": null,
|
||||
"issue": null
|
||||
},
|
||||
"by": "rockerduck",
|
||||
"at": "2026-10-25T04:02:42.539Z",
|
||||
"semantics": 2,
|
||||
"result": {
|
||||
"row": 334,
|
||||
"from": "briefed",
|
||||
"to": "in-progress",
|
||||
"round": null,
|
||||
"issue": null,
|
||||
"candidate": null,
|
||||
"receipt": "ok rockerduck-334-start rev 4079 row 334 briefed→in-progress"
|
||||
},
|
||||
"viewSha": "d555c4376c5ec2157757a0dd3e7348fe712a9b16610a0fec972e8e281b7d9007"
|
||||
},
|
||||
{
|
||||
"rev": 4080,
|
||||
"op": "bubba-288-start",
|
||||
"verb": "move",
|
||||
"args": {
|
||||
"id": 288,
|
||||
"to": "in-progress",
|
||||
"reason": null,
|
||||
"candidate": null,
|
||||
"evidence": null,
|
||||
"issue": null
|
||||
},
|
||||
"by": "bubba",
|
||||
"at": "2026-10-25T04:04:36.422Z",
|
||||
"semantics": 2,
|
||||
"result": {
|
||||
"row": 288,
|
||||
"from": "briefed",
|
||||
"to": "in-progress",
|
||||
"round": null,
|
||||
"issue": null,
|
||||
"candidate": null,
|
||||
"receipt": "ok bubba-288-start rev 4080 row 288 briefed→in-progress"
|
||||
},
|
||||
"viewSha": "fcf44e3b271c36c7b8336d2c2b7427bf973e7edafffed4148689a03a50df399a"
|
||||
},
|
||||
{
|
||||
"rev": 4081,
|
||||
"op": "sage-4364-ab193",
|
||||
"verb": "review-abandon",
|
||||
"args": {
|
||||
"id": 193,
|
||||
"attempt": "morgana-193-inreview",
|
||||
"reason": "pre-send credential check failed (morgana has no token file); outcome not logged under lock contention; no comment on #1691 (checked 04:05Z, 0 comments)"
|
||||
},
|
||||
"by": "sage",
|
||||
"at": "2026-10-25T04:05:57.752Z",
|
||||
"semantics": 2,
|
||||
"result": {
|
||||
"row": 193,
|
||||
"round": 1,
|
||||
"attempt": "morgana-193-inreview",
|
||||
"state": "abandoned",
|
||||
"receipt": "ok sage-4364-ab193 rev 4081 row 193 round 1 request morgana-193-inreview requesting→abandoned; a duplicate request comment may exist"
|
||||
},
|
||||
"viewSha": "ec04ba69967bd3628db5b75098f42e139e17d06cec910e0366a0c03908aeda6a"
|
||||
},
|
||||
{
|
||||
"rev": 4082,
|
||||
"op": "sage-4364-rq193",
|
||||
"verb": "review-request",
|
||||
"args": {
|
||||
"id": 193
|
||||
},
|
||||
"by": "sage",
|
||||
"at": "2026-10-25T04:08:08.349Z",
|
||||
"semantics": 2,
|
||||
"result": {
|
||||
"row": 193,
|
||||
"round": 1,
|
||||
"attempt": "sage-4364-rq193",
|
||||
"state": "requesting",
|
||||
"receipt": "ok sage-4364-rq193 rev 4082 row 193 round 1 request sage-4364-rq193 requesting"
|
||||
},
|
||||
"viewSha": "f91e69002059e8eb12773c3cfd47e23d206193a522fba36874cc548a19b66bbe"
|
||||
},
|
||||
{
|
||||
"rev": 4083,
|
||||
"op": "herb-214-inreview",
|
||||
"verb": "move",
|
||||
"args": {
|
||||
"id": 214,
|
||||
"to": "in-review",
|
||||
"reason": null,
|
||||
"candidate": "584ee1e20",
|
||||
"evidence": null,
|
||||
"issue": null
|
||||
},
|
||||
"by": "herb",
|
||||
"at": "2026-10-25T04:10:39.673Z",
|
||||
"semantics": 2,
|
||||
"result": {
|
||||
"row": 214,
|
||||
"from": "in-progress",
|
||||
"to": "in-review",
|
||||
"round": 1,
|
||||
"issue": 1715,
|
||||
"candidate": {
|
||||
"kind": "commit",
|
||||
"digest": "584ee1e2067982edb71459af7c15e4ed7162279a",
|
||||
"text": null
|
||||
},
|
||||
"receipt": "ok herb-214-inreview rev 4083 row 214 in-progress→in-review round 1 on #1715; request herb-214-inreview requesting"
|
||||
},
|
||||
"viewSha": "ddbe9b2723ef7a5094fef624eba20eae4d698da1dc1665154f60b3bfa3afad0c"
|
||||
},
|
||||
{
|
||||
"rev": 4084,
|
||||
"op": "sage-4372-rs193",
|
||||
"verb": "review-resolve",
|
||||
"args": {
|
||||
"id": 193,
|
||||
"attempt": "sage-4364-rq193",
|
||||
"comment": 31674
|
||||
},
|
||||
"by": "sage",
|
||||
"at": "2026-10-25T04:13:06.534Z",
|
||||
"semantics": 2,
|
||||
"result": {
|
||||
"row": 193,
|
||||
"round": 1,
|
||||
"attempt": "sage-4364-rq193",
|
||||
"state": "posted",
|
||||
"receipt": "ok sage-4372-rs193 rev 4084 row 193 round 1 request sage-4364-rq193 requesting→posted comment 31674"
|
||||
},
|
||||
"viewSha": "a37e0e3dbf4f805f4faadf0bceaa933f650682de7f83eca5658caae2be58fec4"
|
||||
},
|
||||
{
|
||||
"rev": 4085,
|
||||
"op": "sage-4379-brief267",
|
||||
"verb": "move",
|
||||
"args": {
|
||||
"id": 267,
|
||||
"to": "briefed",
|
||||
"reason": null,
|
||||
"candidate": null,
|
||||
"evidence": null,
|
||||
"issue": null
|
||||
},
|
||||
"by": "sage",
|
||||
"at": "2026-10-25T04:16:34.092Z",
|
||||
"semantics": 2,
|
||||
"result": {
|
||||
"row": 267,
|
||||
"from": "queued",
|
||||
"to": "briefed",
|
||||
"round": null,
|
||||
"issue": null,
|
||||
"candidate": null,
|
||||
"receipt": "ok sage-4379-brief267 rev 4085 row 267 queued→briefed"
|
||||
},
|
||||
"viewSha": "fb6a2d1191896825f5da39afcc7fc85caf29d43d9cb1b6e4b2189ae2662fab16"
|
||||
},
|
||||
{
|
||||
"rev": 4086,
|
||||
"op": "sage-4382-brief255",
|
||||
"verb": "move",
|
||||
"args": {
|
||||
"id": 255,
|
||||
"to": "briefed",
|
||||
"reason": null,
|
||||
"candidate": null,
|
||||
"evidence": null,
|
||||
"issue": null
|
||||
},
|
||||
"by": "sage",
|
||||
"at": "2026-10-25T04:17:44.008Z",
|
||||
"semantics": 2,
|
||||
"result": {
|
||||
"row": 255,
|
||||
"from": "queued",
|
||||
"to": "briefed",
|
||||
"round": null,
|
||||
"issue": null,
|
||||
"candidate": null,
|
||||
"receipt": "ok sage-4382-brief255 rev 4086 row 255 queued→briefed"
|
||||
},
|
||||
"viewSha": "edaa038070e1e52673ca4a3afc6d985da95741d4fa299f6f18502de4a1704bb0"
|
||||
},
|
||||
{
|
||||
"rev": 4087,
|
||||
"op": "morgana-267-start",
|
||||
"verb": "move",
|
||||
"args": {
|
||||
"id": 267,
|
||||
"to": "in-progress",
|
||||
"reason": null,
|
||||
"candidate": null,
|
||||
"evidence": null,
|
||||
"issue": null
|
||||
},
|
||||
"by": "morgana",
|
||||
"at": "2026-10-25T04:18:49.322Z",
|
||||
"semantics": 2,
|
||||
"result": {
|
||||
"row": 267,
|
||||
"from": "briefed",
|
||||
"to": "in-progress",
|
||||
"round": null,
|
||||
"issue": null,
|
||||
"candidate": null,
|
||||
"receipt": "ok morgana-267-start rev 4087 row 267 briefed→in-progress"
|
||||
},
|
||||
"viewSha": "e69727d2206984c63a740a4a58cd661a3c2fe8dff8f5dbc4d9fbeff0bdc8e5cb"
|
||||
},
|
||||
{
|
||||
"rev": 4088,
|
||||
"op": "duckworth-193-verdict-r1",
|
||||
"verb": "review-record",
|
||||
"args": {
|
||||
"id": 193,
|
||||
"verdict": "approve",
|
||||
"comment": 31676,
|
||||
"candidate": "e2d161e2e6354fe7f7083b95ccae8b202513f5e5"
|
||||
},
|
||||
"by": "duckworth",
|
||||
"at": "2026-10-25T04:19:54.404Z",
|
||||
"semantics": 2,
|
||||
"result": {
|
||||
"row": 193,
|
||||
"round": 1,
|
||||
"verdict": "approve",
|
||||
"receipt": "ok duckworth-193-verdict-r1 rev 4088 row 193 round 1 approve by duckworth (comment 31676)"
|
||||
},
|
||||
"viewSha": "93d42ed21fd861328bc26344d776f52521c7c64a2fe95887b2365538792f0c1f"
|
||||
},
|
||||
{
|
||||
"rev": 4089,
|
||||
"op": "sage-4379-ab214",
|
||||
"verb": "review-abandon",
|
||||
"args": {
|
||||
"id": 214,
|
||||
"attempt": "herb-214-inreview",
|
||||
"reason": "no comment on #1715 (comments empty at 04:14:08Z); herb has no Gitea token, so the pre-send step could not post; Sage posts the request"
|
||||
},
|
||||
"by": "sage",
|
||||
"at": "2026-10-25T04:20:59.781Z",
|
||||
"semantics": 2,
|
||||
"result": {
|
||||
"row": 214,
|
||||
"round": 1,
|
||||
"attempt": "herb-214-inreview",
|
||||
"state": "abandoned",
|
||||
"receipt": "ok sage-4379-ab214 rev 4089 row 214 round 1 request herb-214-inreview requesting→abandoned; a duplicate request comment may exist"
|
||||
},
|
||||
"viewSha": "8c679893953a60aa0ccf8fa39feb205548c08b77fd3adba021a2de7814d4bec5"
|
||||
},
|
||||
{
|
||||
"rev": 4090,
|
||||
"op": "sage-4379-rq214",
|
||||
"verb": "review-request",
|
||||
"args": {
|
||||
"id": 214
|
||||
},
|
||||
"by": "sage",
|
||||
"at": "2026-10-25T04:22:04.588Z",
|
||||
"semantics": 2,
|
||||
"result": {
|
||||
"row": 214,
|
||||
"round": 1,
|
||||
"attempt": "sage-4379-rq214",
|
||||
"state": "requesting",
|
||||
"receipt": "ok sage-4379-rq214 rev 4090 row 214 round 1 request sage-4379-rq214 requesting"
|
||||
},
|
||||
"viewSha": "6ac50da826437793bac24dd1abc31e4be56a4d26af10de2a33b3f7c05a973c50"
|
||||
},
|
||||
{
|
||||
"rev": 4091,
|
||||
"op": "sage-4379-rq214.outcome",
|
||||
"verb": "review-outcome",
|
||||
"args": {
|
||||
"id": 214,
|
||||
"attempt": "sage-4379-rq214",
|
||||
"outcome": "posted",
|
||||
"status": 201,
|
||||
"comment": 31678,
|
||||
"detail": "created"
|
||||
},
|
||||
"by": "sage",
|
||||
"at": "2026-10-25T04:23:18.136Z",
|
||||
"semantics": 2,
|
||||
"result": {
|
||||
"row": 214,
|
||||
"round": 1,
|
||||
"attempt": "sage-4379-rq214",
|
||||
"state": "posted",
|
||||
"receipt": "ok sage-4379-rq214.outcome rev 4091 row 214 round 1 request sage-4379-rq214 posted comment 31678"
|
||||
},
|
||||
"viewSha": "c74e53658781138532ed40adac8a3fc476204ef6c2dbbb5328401e572d12c986"
|
||||
},
|
||||
{
|
||||
"rev": 4092,
|
||||
"op": "launchpad-255-start",
|
||||
"verb": "move",
|
||||
"args": {
|
||||
"id": 255,
|
||||
"to": "in-progress",
|
||||
"reason": null,
|
||||
"candidate": null,
|
||||
"evidence": null,
|
||||
"issue": null
|
||||
},
|
||||
"by": "launchpad",
|
||||
"at": "2026-10-25T04:24:42.333Z",
|
||||
"semantics": 2,
|
||||
"result": {
|
||||
"row": 255,
|
||||
"from": "briefed",
|
||||
"to": "in-progress",
|
||||
"round": null,
|
||||
"issue": null,
|
||||
"candidate": null,
|
||||
"receipt": "ok launchpad-255-start rev 4092 row 255 briefed→in-progress"
|
||||
},
|
||||
"viewSha": "a92a599f528ff198285660a6054200e69018054f2815f77f0dc2b5cf73d96b8d"
|
||||
},
|
||||
{
|
||||
"rev": 4093,
|
||||
"op": "filbert-214-r1-record",
|
||||
"verb": "review-record",
|
||||
"args": {
|
||||
"id": 214,
|
||||
"verdict": "approve",
|
||||
"comment": 31679,
|
||||
"candidate": "584ee1e2067982edb71459af7c15e4ed7162279a"
|
||||
},
|
||||
"by": "filbert",
|
||||
"at": "2026-10-25T04:26:01.460Z",
|
||||
"semantics": 2,
|
||||
"result": {
|
||||
"row": 214,
|
||||
"round": 1,
|
||||
"verdict": "approve",
|
||||
"receipt": "ok filbert-214-r1-record rev 4093 row 214 round 1 approve by filbert (comment 31679)"
|
||||
},
|
||||
"viewSha": "442af8295ba6c7bba26cf032a67f7b60e4875b90cdb7953e4a6f3ae168764123"
|
||||
},
|
||||
{
|
||||
"rev": 4094,
|
||||
"op": "sage-214-land-note",
|
||||
"verb": "note",
|
||||
"args": {
|
||||
"id": 214,
|
||||
"text": "LANDED 3a8e4abad (pushed 78b69e1e6..3a8e4abad, 6 commits cherry-picked onto 78b69e1e6). Gate GREEN 04:10:34Z, all package suites and scripts; queue package runs via test-queue.sh (156). Filbert approve c31679 rev 4093. Range-diff equal except BUILD-LOG context."
|
||||
},
|
||||
"by": "sage",
|
||||
"at": "2026-10-25T04:29:32.289Z",
|
||||
"semantics": 2,
|
||||
"result": {
|
||||
"row": 214,
|
||||
"field": "note",
|
||||
"receipt": "ok sage-214-land-note rev 4094 row 214 note"
|
||||
},
|
||||
"viewSha": "72cb37f91dfb33937bcfb4df842aacf3768e9fd54265ff65efbecbb0a4a61b6c"
|
||||
},
|
||||
{
|
||||
"rev": 4095,
|
||||
"op": "sage-214-land-done",
|
||||
"verb": "move",
|
||||
"args": {
|
||||
"id": 214,
|
||||
"to": "done",
|
||||
"reason": null,
|
||||
"candidate": null,
|
||||
"evidence": null,
|
||||
"issue": null
|
||||
},
|
||||
"by": "sage",
|
||||
"at": "2026-10-25T04:30:41.788Z",
|
||||
"semantics": 2,
|
||||
"result": {
|
||||
"row": 214,
|
||||
"from": "in-review",
|
||||
"to": "done",
|
||||
"round": 1,
|
||||
"issue": null,
|
||||
"candidate": null,
|
||||
"receipt": "ok sage-214-land-done rev 4095 row 214 in-review→done round 1"
|
||||
},
|
||||
"viewSha": "c89e09440ea27e853823e0138c92bb8429a623cb1d41fa41037c0513813404d7"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
# PRD: Mosaic Stack
|
||||
|
||||
- Status: approved, version 1.18. Jason approved draft 0.4 unchanged as
|
||||
- Status: approved, version 1.17. Jason approved draft 0.4 unchanged as
|
||||
1.0 on 2026-10-05 (lead decision 61). They approved version 1.1, the
|
||||
Organization requirements, in decision 03130d08-ddce-480d-a601-e59f64744fc3
|
||||
on 2026-10-14. They approved version 1.2, the chief of staff, the
|
||||
@@ -54,17 +54,7 @@
|
||||
adds REQ-LIB-9, the Console's DOCX editor, from Jason's rulings Q195,
|
||||
Q226 and Q234. It changes what Case 2 requirements allow, so Mos puts
|
||||
the text to Jason as written, and it lands only after his yes (mos-1366,
|
||||
Vikunja 570). Version 1.18 corrects Mos's reading in REQ-INST-2 to match
|
||||
the approved row 255 design. The stack prints the providers entry and
|
||||
never writes config.json. What the requirement allows or forbids is
|
||||
unchanged (mos-1671, sage-4418). Doc-only, so it takes the 1.11 path:
|
||||
one seat drafts, a second reviews, Sage lands.
|
||||
- Version 1.18 source: Mos's ruling mos-1671 on 2026-10-25, answering
|
||||
sage-4418. REQ-INST-2's second bullet said the stack installs the
|
||||
bundled Pocket ID "and writes it as the Console's one providers entry."
|
||||
It now says the stack "prints the Console's one providers entry for the
|
||||
user to add to config.json. The stack never writes config.json (canon
|
||||
invariant 2)."
|
||||
Vikunja 570).
|
||||
- Version 1.17 source: Jason's Q235, answering Q36: 'How about we see a
|
||||
markdown file, we convert the markdown file to docx, then archive or
|
||||
mark the markdown file as converted. The new standard for files in the
|
||||
@@ -1527,10 +1517,8 @@ Added in version 1.4 (decision 1ea053cc).
|
||||
- The installer offers two choices: point the Console at one or more
|
||||
existing OIDC providers, or deploy a bundled Pocket ID.
|
||||
- The stack installs that Pocket ID as an install component under
|
||||
REQ-INST-1 and prints the Console's one providers entry for the user
|
||||
to add to config.json. The stack never writes config.json (canon
|
||||
invariant 2). No Pocket ID source goes in the repository, and the
|
||||
Console gets no
|
||||
REQ-INST-1 and writes it as the Console's one providers entry. No
|
||||
Pocket ID source goes in the repository, and the Console gets no
|
||||
sign-in path apart from its own OIDC sign-in (REQ-WEB-2).
|
||||
- Pocket ID is passkey-only for people. Agents keep per-role service
|
||||
tokens.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"prd": "docs/prd/mosaic-stack.md",
|
||||
"version": "1.18",
|
||||
"version": "1.17",
|
||||
"requirements": [
|
||||
{ "id": "REQ-ROLE-1", "title": "Each role is a reviewed roles/<name>.json: contract, authority map, credentials" },
|
||||
{ "id": "REQ-ROLE-2", "title": "One holder per role instance, one per assistant session; the chief of staff may end a run, others need a decision" },
|
||||
|
||||
@@ -21,7 +21,7 @@ test("the list holds the PRD's requirement ids, in its order, at its version", (
|
||||
assert.equal(ids.length, 87);
|
||||
assert.deepEqual(list.requirements.map((r) => r.id), ids);
|
||||
assert.match(prd, new RegExp(`^- Status: approved, version ${list.version.replace('.', '\\.')}\\.`, 'm'));
|
||||
assert.deepEqual([list.prd, list.version], ['docs/prd/mosaic-stack.md', '1.18']);
|
||||
assert.deepEqual([list.prd, list.version], ['docs/prd/mosaic-stack.md', '1.17']);
|
||||
});
|
||||
|
||||
test('the committed list loads frozen', () => {
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
"options": "bundle, iife, es2022, browser, legal-comments inline, unminified; aliases in editor/build.mjs",
|
||||
"outputs": {
|
||||
"src/public/editor.js": {
|
||||
"sha256": "bd167899a551c70dc07e500fcdd1853d7fb710b23c217ba5b42daf5785c38a72",
|
||||
"bytes": 759959
|
||||
"sha256": "9c881b13740fae2408717ad75fa1d85bfcfe3fa8b3af0f1b3f2e706c90e80fee",
|
||||
"bytes": 758420
|
||||
},
|
||||
"src/public/editor.css": {
|
||||
"sha256": "8654ac57d1e7a561110138ecd7f701c34a074017ff8c4cd2768d8083908ee61f",
|
||||
|
||||
@@ -23,13 +23,6 @@ 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 });
|
||||
|
||||
@@ -41,7 +34,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 | wikilink)*', group: 'block', defining: true,
|
||||
content: '(text | image)*', 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]; },
|
||||
},
|
||||
@@ -86,16 +79,6 @@ 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: {
|
||||
@@ -135,16 +118,6 @@ 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.
|
||||
@@ -178,7 +151,6 @@ 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 }) },
|
||||
@@ -202,13 +174,6 @@ 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,
|
||||
@@ -216,20 +181,17 @@ 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) { escapedText(state, node.text, !state.inAutolink); },
|
||||
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; }
|
||||
},
|
||||
bullet_list(state, node) { state.renderList(node, ' ', () => '- '); },
|
||||
image(state, node) {
|
||||
const a = node.attrs;
|
||||
const title = oneLine(a.title);
|
||||
state.write(']/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,
|
||||
|
||||
@@ -18176,9 +18176,6 @@
|
||||
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({
|
||||
@@ -18195,7 +18192,7 @@
|
||||
} },
|
||||
heading: {
|
||||
attrs: block2({ level: { default: 1 } }),
|
||||
content: "(text | image | wikilink)*",
|
||||
content: "(text | image)*",
|
||||
group: "block",
|
||||
defining: true,
|
||||
parseDOM: [1, 2, 3, 4, 5, 6].map((level) => ({ tag: "h" + level, attrs: { level } })),
|
||||
@@ -18269,20 +18266,6 @@
|
||||
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"];
|
||||
} }
|
||||
@@ -18331,17 +18314,6 @@
|
||||
});
|
||||
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) {
|
||||
@@ -18377,7 +18349,6 @@
|
||||
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 }) },
|
||||
@@ -18389,15 +18360,6 @@
|
||||
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,
|
||||
@@ -18406,7 +18368,13 @@
|
||||
defaultMarkdownSerializer.nodes.code_block(state, params === node.attrs.params ? node : node.type.create({ ...node.attrs, params }, node.content));
|
||||
},
|
||||
text(state, node) {
|
||||
escapedText(state, node.text, !state.inAutolink);
|
||||
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;
|
||||
}
|
||||
},
|
||||
bullet_list(state, node) {
|
||||
state.renderList(node, " ", () => "- ");
|
||||
@@ -18416,11 +18384,6 @@
|
||||
const title = oneLine(a.title);
|
||||
state.write("]/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);
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
// 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©]]', null].map(source => attrs(source));
|
||||
const bad = e.view.state.schema.nodes.wikilink.create({source:'[[a]]\\n\\n<img src=x onerror=alert(1)>©'});
|
||||
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, /(?<!\\)©/);
|
||||
assert.match(result.output, /\\©/);
|
||||
});
|
||||
@@ -1,13 +0,0 @@
|
||||
// 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]]'] },
|
||||
];
|
||||
Reference in new issue
Block a user