Compare commits
22
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1e629c892d | ||
|
|
84de3fbca3 | ||
|
|
1e4c1e6e94 | ||
|
|
5b846b048a | ||
|
|
59f0e44f47 | ||
|
|
bb031ec840 | ||
|
|
07315e6514 | ||
|
|
fb04cbc431 | ||
|
|
9d929911c8 | ||
|
|
bcf3c1dacf | ||
|
|
d52d9f1f2f | ||
|
|
830f3d8bff | ||
|
|
e48d602165 | ||
|
|
cabcb81d49 | ||
|
|
353d65f577 | ||
|
|
2c289f6f0d | ||
|
|
a863015a51 | ||
|
|
e551cd8466 | ||
|
|
c13c337e16 | ||
|
|
435a7458d3 | ||
|
|
b99ca86b9c | ||
|
|
0e057e05eb |
No files matched your search
@@ -5387,3 +5387,5 @@ Gander: after Duckworth-0100 design approval with C1-C5, prepared the two host-s
|
||||
Row 299 (573, #1806, Gander, Filbert): before, the P10 band test timed a fault run minus a separate baseline run, and a loaded whole-suite run went red. Mos approved direct timing from host /proc/uptime stamps with ambient load only (mos-1409). After: rebased onto 80793d824 as ffa93b054 (range-diff 3/3 =, manifest 277ace0b unchanged). Gate 07:26Z-07:50Z: case1 whole 3x 53/0/0, cli whole 917/0/0 (59 files, load 1.54-7.17), P10 inner 2.27 s, outer 4.01 s, M1-M6 killed. M4's planned guard return then exact outer-reason failure counts as its kill (sage-3271, mos-1426). Filbert APPROVE c31084 rev 3485, N1 M1 margin 0.02 s. 299 LANDED ffb2fedfb 08:03Z, done rev 3487, #1806 closed c31087; queue commit 8a35ffadb (revs 3474-3487). Row 303: Duckworth APPROVE c31079 rev 3480 with C1, the whole-file gate. Rows 219 (bfa421b6c) and 295 r2 (b16cef1d0) rebases: Dewey static clean. Corrections: Launchpad and Gander have no token, and the CLI refuses another seat's token file, so I posted both requests as sage (c31082, c31083). My sage-3265 wording led Duckworth to poll queue show into Gander's slot (07:20Z-07:26Z); I ruled it ambient load (sage-3274).
|
||||
2026-10-23T10:48:40Z | Sage, rows 302, 295 and 219 landings, the Docker pool diagnostic, rows for Vikunja 590 and 591 | Before: refactor 1ac655005. 302 (#1809, Gizmoduck, Dewey) LANDED 3ed9fd754 08:49Z: 7 commits onto 1ac655005, range-diff all =, tree = gated merge 783602e06; queue commit 07802dc57 (revs 3488-3494). Row 219's gate went 11/3 in test-release.sh: release activate's health check failed because Docker could not create sage-diag1437_default, since all predefined address pools were used up. Under mos-1437 I reproduced it on clean 07802dc57 (11/3, the same stderr). I then removed 18 unused gate _default networks, and the rerun went 14/0 with the run-task marker ok. Mos accepted the removal and ruled that a probe that can create something waits for the go and a removal waits for the report (mos-1438). 295 (#1801, Morgana, Dewey) round 2 gate on d68b4300d (manifest e94c9aeb) was green. It LANDED 2efd127ce 10:10Z: 9 commits onto 07802dc57, HEAD~1 tree = gated a86592ffc. Done rev 3502, #1801 c31105, P19 414 c911. 219 (#1720, Rocko, Dewey): the restart on the post-295 merge 054da642f (manifest 4a2271dd, my mos-1439 call) passed node 2523/0/2 and shells 449/0, with 24 mutants killed. It LANDED 5fdfd7275 10:40Z (8 row commits plus packet-only evidence, tree e86a84a85 = gated). Done rev 3509, #1720 c31110, P19 416 c913. Queue commit 90633c864 (revs 3495-3510). Brief docs/plans/2026-10-23_gate-env-rows.md names rows for 590 (claude-session slug) and 591 (compose network cleanup), and DEFERRED.md gets 219 N1 (U6). Row 272 (Huey) conflicts with 5fdfd7275 in code, so it moves back to in-progress for a rebase. Its new fallback is bounded to a session.list access-refused on an omitted-session route (sage-3357). Corrections: my network create probe ran before Mos's go, and the removal ran before the report. My first slot-order line put the diag start at 09:44Z; it was about 09:41Z. My 219 push piped its output through grep, but the after-fetch confirmed origin. Queue commit 90633c864 also carries Huey's rev 3510 (272 back to in-progress), and its message names only 3495-3509.
|
||||
|
||||
|
||||
2026-10-23T11:03:46Z | Gyro Vikunja571 mos-1444 PRD d8 follow-on | Branch gyro/editor-571-d8 from origin/refactor c16e943b9. Both designs now keep library.convert outside ACTIONS/ROUTED and broker processing, retain REQ-WAIT-1/hard-wait test unchanged, constrain unmarked story eligibility to mapped book/playground scope, and add whole-host pre-import restore recovery from remote import manifests plus the Gitea archive commit. Lookups refuse until one current version is proven; VM1012 restore needs Jason present under Q185. W7 no longer owns hard-waits.test.mjs. Canonical queue next gyro returned nothing; direct Mos assignment governs. Static diff, exact replacement and append-only checks pass. No code, tests, build, browser, private-file or live action. Fenton re-check requested after authorized branch push.
|
||||
@@ -0,0 +1,66 @@
|
||||
# Row 300 build notes (Launchpad): R6-N1, an HTTP-level test of the user-session option swap (#1807)
|
||||
|
||||
Brief: docs/plans/2026-10-23_r6-followup-rows.md § Brief: R6-N1. Reviewer Duckworth, gate owner Sage.
|
||||
Base c13c337e1 (refactor, after row 294 landed at d3059c2c3). Branch launchpad/row300.
|
||||
|
||||
## Change
|
||||
|
||||
Tests only. One new file, packages/webui/tests/user-option-swap.test.mjs. No product change and no
|
||||
existing assertion or fixture touched.
|
||||
|
||||
- **Gate stubbed open.** The file registers an in-thread load hook (`registerHooks`, as
|
||||
thread-claims.test and library-cli.test do through `--import`) keyed on the one URL
|
||||
`serve.mjs?gate=open`. On that URL alone it prepends `return true;` to `userRoute`'s body, then
|
||||
imports `startServer` from it. Every other import of serve.mjs, and every relative import inside
|
||||
the copy, is the real module. If the gate's signature line is not in serve.mjs exactly once, the
|
||||
hook throws. A renamed gate then fails loudly, instead of quietly testing the closed gate. The
|
||||
test also asserts that the copy's `userRoute` answers true for `POST /api/bus/message`.
|
||||
- **Setup.** This is granted-user.test.mjs's setup, cut to what the test needs: the fake OP, the
|
||||
in-process broker for `demo`, createSso with the access hook, and `userOptionsFor` over alice's
|
||||
transport. Every owner option is a stub that records its own name. The owner transport records
|
||||
`['transport', verb, args]`. One list, `owner`, holds both kinds of owner call.
|
||||
- **Positive control.** The owner signs in and posts `/api/bus/message`. Exactly
|
||||
`['option', 'send']` must be recorded, so the route reaches `send` and the count works.
|
||||
- **The check.** Alice signs in and walks G4's owner routes, copied, plus two chat reads. No answer
|
||||
may say `owner-only`, so the gate really is open. After each route, every owner record must be
|
||||
`['transport', 'plugins', {}]`, the read mos-1394 allowed. The message route must reach the user
|
||||
transport (`message.send`), and every transport opened is alice's.
|
||||
|
||||
Why a new file: granted-user.test.mjs's setup imports startServer statically. Passing the gate-open
|
||||
copy into it would change that fixture, and a fixture change goes to Mos. The route list and setup
|
||||
are copied, with a comment naming their source.
|
||||
|
||||
## Mutant
|
||||
|
||||
M12: serve.mjs:689 drops `send` from the user-session destructure, so a user session keeps the
|
||||
owner's `send`. Killed when "R6-N1" reports not ok. Expected failure: on `POST /api/bus/message`,
|
||||
the owner record holds `['option', 'send']`. run1/mutants.py applies it (anchor exactly once), runs
|
||||
the new file alone, restores with git checkout and checks the tree is clean.
|
||||
|
||||
## Gate
|
||||
|
||||
Duckworth approves the manifest. On Sage's CLEAR: run1/gate.sh does npm ci (root and webui), the new
|
||||
file whole, the webui suite whole, then M12. Logs go to /mnt/storage/scratch/launchpad-r300/run1,
|
||||
outside the tree. Not run yet.
|
||||
|
||||
MANIFEST.sha256 lists the one new path.
|
||||
|
||||
## Run 1 (sage-3408 CLEAR, 12:05Z)
|
||||
|
||||
npm ci root and webui were green. The new file was red on its first run (mos-1272, placed by me):
|
||||
`TypeError: Cannot read properties of undefined (reading 'set-cookie')` in signIn. When I cut down
|
||||
granted-user.test.mjs's `call`, I dropped `headers: res.headers` from what it resolves, so the
|
||||
sign-in found no cookies. The fix puts `headers` back, the same as granted-user.test.mjs:40. No
|
||||
assertion changed. The new file then passed alone (1/0). The fix is a new commit, and the manifest
|
||||
is re-hashed. The gate resumes at new-file on the new head.
|
||||
|
||||
Run 1 resumed (TAG -r2) on db5122920, manifest db8be52ce895603e687985acfae29e53e3e8782ae945c3cded6cca36141bfa3b:
|
||||
|
||||
- new file whole: 1 pass, 0 fail.
|
||||
- webui suite whole: 375 pass, 0 fail, 0 skipped, 0 cancelled, 58.2 s (limit 600).
|
||||
- M12 killed. "R6-N1" reports not ok at :151: `POST /api/bus/message: ["option","send"]`, where
|
||||
`["transport","plugins",{}]` is expected. The restore left the tree clean.
|
||||
|
||||
Duckworth N1 (duckworth-0263, non-blocking): the setup passes no `upstream`, so the swap's
|
||||
`upstream = null` has no recorder here. The test proves the swap for the options it stubs. It
|
||||
proves nothing about `upstream`.
|
||||
@@ -0,0 +1 @@
|
||||
03b5d60f68ea2bddd779e1df19519855a5f01cc28ec9ddb26ee7c45fcfe75cf4 packages/webui/tests/user-option-swap.test.mjs
|
||||
Executable
+31
@@ -0,0 +1,31 @@
|
||||
#!/usr/bin/env bash
|
||||
# Row 300 gate run 1 (brief R6-N1): npm ci (root and packages/webui), the new test file whole, the
|
||||
# webui suite whole, then mutant M12. Stops at the first red. FROM=<step> resumes at a step.
|
||||
set -u -o pipefail
|
||||
WT=/mnt/storage/scratch/launchpad-r300/wt
|
||||
OUT=$WT/agents/launchpad/work/queue-300/run1
|
||||
LOG=/mnt/storage/scratch/launchpad-r300/run1
|
||||
FROM=${FROM:-npm-root}; TAG=${TAG:-}
|
||||
mkdir -p $LOG
|
||||
export TMPDIR=$(mktemp -d /mnt/storage/scratch/tmp/gXXXXXX)
|
||||
echo "start $(date -u +%H:%M:%SZ) FROM=$FROM TAG=$TAG TMPDIR $TMPDIR head $(git -C $WT rev-parse HEAD)" >> $LOG/summary.txt
|
||||
[ -z "$(git -C $WT status --porcelain)" ] || { echo "STOP worktree not clean" | tee -a $LOG/summary.txt; exit 9; }
|
||||
on=0
|
||||
step() { # name limit dir cmd...
|
||||
local name=$1 limit=$2 dir=$3; shift 3
|
||||
[ $on = 1 ] || [ $name = $FROM ] || return 0; on=1
|
||||
local t0=$(date +%s%N)
|
||||
( cd $dir && timeout -k 10 $limit "$@" ) > $LOG/$name$TAG.log 2>&1
|
||||
local rc=$?
|
||||
local ms=$(( ($(date +%s%N) - t0) / 1000000 )); local dt=$((ms/1000)).$(printf %03d $((ms%1000)))
|
||||
echo "$name$TAG rc=$rc seconds=$dt limit=$limit $(date -u +%H:%M:%SZ)" | tee -a $LOG/summary.txt
|
||||
[ $rc -eq 0 ] || { echo "STOP at $name$TAG" | tee -a $LOG/summary.txt; exit $rc; }
|
||||
}
|
||||
step npm-root 900 $WT npm ci --ignore-scripts
|
||||
step npm-webui 900 $WT/packages/webui npm ci --ignore-scripts
|
||||
step new-file 300 $WT/packages/webui node --test tests/user-option-swap.test.mjs
|
||||
step webui 600 $WT/packages/webui node --test tests/
|
||||
[ -z "$(git -C $WT status --porcelain)" ] || { echo "STOP worktree dirty after the suite" | tee -a $LOG/summary.txt; git -C $WT status --porcelain >> $LOG/summary.txt; exit 9; }
|
||||
echo "SUITES GREEN $(date -u +%H:%M:%SZ)" | tee -a $LOG/summary.txt
|
||||
python3 -B $OUT/mutants.py
|
||||
rc=$?; echo "MUTANTS rc=$rc $(date -u +%H:%M:%SZ)" | tee -a $LOG/summary.txt; exit $rc
|
||||
@@ -0,0 +1,47 @@
|
||||
#!/usr/bin/env python3
|
||||
# Row 300 mutant M12 (BUILD.md "Mutant"): serve.mjs keeps the owner's send for a user session. The
|
||||
# edit must match exactly once; the new file runs alone under timeout -k 10 300 with the TAP
|
||||
# reporter; git checkout restores and the tree must be clean. Killed = "R6-N1" reports "not ok".
|
||||
import json, os, re, subprocess, sys, time
|
||||
|
||||
WT = '/mnt/storage/scratch/launchpad-r300/wt'
|
||||
LOG = '/mnt/storage/scratch/launchpad-r300/run1'
|
||||
SWAP = "({ bus, send, settingsWrite, projectFiles, projectFile, projectSave, chat, nameWrite, assistantStop, instancesWrite, routesWrite, agentWrite, processing } = userOptions(session));"
|
||||
MUTANTS = [
|
||||
('M12', 'packages/webui/src/serve.mjs', SWAP,
|
||||
"({ bus, settingsWrite, projectFiles, projectFile, projectSave, chat, nameWrite, assistantStop, instancesWrite, routesWrite, agentWrite, processing } = userOptions(session));",
|
||||
('packages/webui', 'tests/user-option-swap.test.mjs'), 'R6-N1'),
|
||||
]
|
||||
|
||||
def git(*a):
|
||||
return subprocess.run(['git', '-C', WT, *a], capture_output=True, text=True, check=True).stdout
|
||||
|
||||
def main():
|
||||
if git('status', '--porcelain'): raise SystemExit('worktree not clean at start')
|
||||
summary, bad = [], 0
|
||||
for mid, path, old, new, (pkg, test), killer in MUTANTS:
|
||||
p = os.path.join(WT, path)
|
||||
try:
|
||||
s = open(p).read()
|
||||
if s.count(old) != 1: raise SystemExit(f'{mid}: anchor count {s.count(old)} in {path}')
|
||||
open(p, 'w').write(s.replace(old, new))
|
||||
diff = git('diff')
|
||||
t0 = time.time()
|
||||
r = subprocess.run(['timeout', '-k', '10', '300', 'node', '--test', '--test-reporter=tap', test],
|
||||
cwd=os.path.join(WT, pkg), capture_output=True, text=True)
|
||||
dt = round(time.time() - t0, 1)
|
||||
finally:
|
||||
git('checkout', '--', path)
|
||||
if git('status', '--porcelain'): raise SystemExit('worktree not clean after restore')
|
||||
open(f'{LOG}/{mid}.diff', 'w').write(diff)
|
||||
open(f'{LOG}/{mid}.tap', 'w').write(r.stdout + '\n# stderr\n' + r.stderr)
|
||||
res = {m.group(2): m.group(1) == 'ok' for m in re.finditer(r'^(ok|not ok) \d+ - (.*)$', r.stdout, re.M)}
|
||||
hit = [n for n in res if n.startswith(killer)]
|
||||
killed = r.returncode not in (0, 124, 137) and bool(hit) and all(not res[n] for n in hit)
|
||||
bad += not killed
|
||||
print(f'{mid} rc={r.returncode} seconds={dt} killed={killed} red={[n for n, ok in res.items() if not ok]}', flush=True)
|
||||
summary.append({'mutant': mid, 'rc': r.returncode, 'seconds': dt, 'killed': killed, 'killer': killer})
|
||||
json.dump(summary, open(f'{LOG}/mutants.json', 'w'), indent=1)
|
||||
sys.exit(1 if bad else 0)
|
||||
|
||||
main()
|
||||
@@ -0,0 +1,67 @@
|
||||
# Row 301 build notes (Launchpad): R6-N4, fold user-bus.mjs back into bus.mjs (#1808)
|
||||
|
||||
Brief: docs/plans/2026-10-23_r6-followup-rows.md § Brief: R6-N4. Reviewer Duckworth, gate owner Sage.
|
||||
Base e551cd846 (refactor after row 303). Branch launchpad/row301.
|
||||
|
||||
## Change
|
||||
|
||||
- packages/webui/src/bus.mjs: the user transport moves in beside humanCall, after `busReader`:
|
||||
`USER_CLI`, `userCall`, `busBusiness` and `userCaller`, byte for byte from user-bus.mjs (checked
|
||||
with diff), under a three-line header. One import is added, `fileURLToPath` from node:url.
|
||||
`readHostState`, `spawn`, `randomBytes` and `BusReadError` were already in bus.mjs. humanCall and
|
||||
busCaller are untouched.
|
||||
- packages/webui/src/user-bus.mjs: deleted.
|
||||
- packages/webui/src/cli.mjs: the two imports become one, from './bus.mjs'.
|
||||
- packages/webui/src/console-bus.mjs: a comment names bus.mjs instead of user-bus.mjs.
|
||||
- packages/webui/tests/granted-user.test.mjs:22-23: the two imports become one, from '../src/bus.mjs'.
|
||||
Only the import path changes. Mos ruled (mos-1449, via sage-3399) that this is covered by "update
|
||||
its imports" and is not an old-test change.
|
||||
|
||||
No other reference to user-bus.mjs remains under packages/. tools/agent-watch's "user-bus" means
|
||||
something unrelated. The 294 packet's mutants name the old file as history only.
|
||||
|
||||
## Re-pin
|
||||
|
||||
channel-proof.test.mjs:28 pinned packages/webui/src/bus.mjs at 15e8123f…3313. After the fold the
|
||||
file hashes to 39fcb27c82e917ef05e1a7a650d522c9f3c656bf8dbade87b8ddb3661b67ad6c. A pin change is an
|
||||
old-test change. Mos approved it under mos-1272 in mos-1449 (via sage-3399), as its own commit, with
|
||||
row 301 making its own re-pin window. Mos checked fd4b8c36e read-only: bus.mjs hashes to 39fcb27c,
|
||||
its diff removes no lines, user-bus.mjs from `export const USER_CLI` to the end is in it byte for
|
||||
byte, and nothing else imports user-bus.mjs. The second commit changes that one value and adds one
|
||||
comment line in the row 165/171/194 style.
|
||||
|
||||
## Gate
|
||||
|
||||
Duckworth approves the manifest. channel-proof.test, the webui suite and the 294 user-transport
|
||||
tests (granted-user.test.mjs) run whole and green, on Sage's CLEAR.
|
||||
|
||||
MANIFEST.sha256 lists the changed paths. DELETED.txt names the one removed path.
|
||||
|
||||
## Run 1 (sage-3408 CLEAR, 12:08Z, head 04a179740)
|
||||
|
||||
npm ci root and webui green. channel-proof.test whole 2 pass 0 fail. The webui suite whole went
|
||||
374 tests, 372 pass, 2 fail, 0 skipped, so the gate stopped. granted-user.test whole, run after the
|
||||
stop to see whether the stop hid another red: 19 pass 0 fail. No file changed.
|
||||
|
||||
Both reds were in old tests: library-cli.test.mjs:10 (C2-04) and thread-claims.test.mjs:54 (row278
|
||||
Console adapter). Each failed with `SyntaxError: The requested module './bus.mjs' does not provide an
|
||||
export named 'busBusiness'` at cli.mjs:5. Each test spawns cli.mjs with a registerHooks load hook
|
||||
that replaces /webui/src/bus.mjs with a fixture exporting only BusReadError and busCaller. Before
|
||||
the fold, cli.mjs took busBusiness and userCaller from the real user-bus.mjs. Now they come from the
|
||||
stubbed bus.mjs, so cli.mjs fails to link. cli.mjs calls them only under --sso (:98, :103), and
|
||||
neither test passes --sso. A URL-keyed stub is not an import, so the "nothing else imports
|
||||
user-bus.mjs" check and the static reviews missed it. `git grep "webui/src/bus.mjs" --
|
||||
'packages/*/tests/*'` finds exactly channel-proof, library-cli and thread-claims.
|
||||
|
||||
## Fixture change (mos-1451 under mos-1272, via sage-3418)
|
||||
|
||||
Mos approved option A with one change and refused option B, a namespace import in cli.mjs. Each
|
||||
of the two busSource template strings (library-cli.test.mjs:17, thread-claims.test.mjs:58) gets
|
||||
this appended, and nothing else changes in either file:
|
||||
|
||||
export const busBusiness=()=>{throw Error('busBusiness outside --sso')};export const userCaller=()=>{throw Error('userCaller outside --sso')};
|
||||
|
||||
The stubs throw rather than return null, so a test that reaches them outside --sso fails loudly.
|
||||
The text has no backtick, `${` or backslash, so the template literal carries it unchanged. The
|
||||
manifest goes from five paths to seven. Sage's gate: webui 374/0, channel-proof 2/0,
|
||||
granted-user 19/0. Any other red stops the gate and goes to Mos.
|
||||
@@ -0,0 +1 @@
|
||||
deleted packages/webui/src/user-bus.mjs
|
||||
@@ -0,0 +1,7 @@
|
||||
db1f05e312b9bee7ece7df7df78047a2965024109a297cb8a24112135a6e5f04 packages/bus/tests/channel-proof.test.mjs
|
||||
39fcb27c82e917ef05e1a7a650d522c9f3c656bf8dbade87b8ddb3661b67ad6c packages/webui/src/bus.mjs
|
||||
0862b2b5bf6511d2c5deca1f318eee643edaee489c0ce8c83e57364f2f54a3d8 packages/webui/src/cli.mjs
|
||||
653d2087c0796a35c3884b7243d1adf8a78c52950a4924049317063f2d3c0974 packages/webui/src/console-bus.mjs
|
||||
34b6d052855369c8f7cf1faf8bab3c59ddf96ccd4c42731bd86a4e61e5e70640 packages/webui/tests/granted-user.test.mjs
|
||||
7e782c2b6b46b6b8b6e58cd3d148bf041d49e8fbf2cbbed51080cdb02fc11d89 packages/webui/tests/library-cli.test.mjs
|
||||
dd1e62d14a59bb8fe7cd4654dd1de8e518270c36d104ee356e165faee7f95a02 packages/webui/tests/thread-claims.test.mjs
|
||||
@@ -0,0 +1,26 @@
|
||||
# Row 303 build notes (Launchpad): R9 shows its output when rehearse makes no directory (#1810)
|
||||
|
||||
Brief: docs/plans/2026-10-23_r9-guard-row.md (Vikunja 589). Reviewer Duckworth, gate owner Sage.
|
||||
Base 80793d824 (refactor after row 294 landed at d3059c2c3). Branch launchpad/row303.
|
||||
|
||||
## Change
|
||||
|
||||
One added line in packages/cli/tests/stack-release.test.mjs, R9 only, at :2178:
|
||||
|
||||
assert.ok(r.dir!==null, r.out+r.err);
|
||||
|
||||
It sits right after R9's exit-code check and right before the `includes` check whose message builds
|
||||
`join(r.dir,'placeholders')`. rehearse() (:2040) sets `dir` to null when the output has no
|
||||
`rehearsal directory:` line, so the guard fails first and its message carries the rehearsal's
|
||||
output and stderr, including the FAIL line that stopped it. This is a test-only change to a
|
||||
pre-existing test, approved under mos-1272 and mos-1316 (mos-1422 point 2). It adds an assertion
|
||||
and changes none. No other line in R9 or any other test changes. The lines the brief names (:2058,
|
||||
:2113, :2126, :2267, :2280) put r.dir in a template, where a null prints as `null` and the message
|
||||
still carries r.out, so they get no guard.
|
||||
|
||||
MANIFEST.sha256 lists the one changed path, hashed from the commit that adds this file.
|
||||
|
||||
## Gate
|
||||
|
||||
Duckworth approves the diff. stack-release.test.mjs runs whole and green on Sage's gate, in a gap.
|
||||
Not run yet.
|
||||
@@ -0,0 +1 @@
|
||||
e936808bb12361acf164b675fe40ead416720dfd48c5d51ea5bfee1e1893cc33 packages/cli/tests/stack-release.test.mjs
|
||||
@@ -0,0 +1,49 @@
|
||||
# Row 304 / Vikunja 590 / Gitea #1811
|
||||
|
||||
Owner Rocko; reviewer Dewey; gate owner Sage. Base c16e943b90ef71ac96cfa5314bbea6040634d14d.
|
||||
Brief docs/plans/2026-10-23_gate-env-rows.md first section, approved exact
|
||||
one-line old-test change under mos-1442 point 1 / sage-3362.
|
||||
|
||||
265 is queued and has no active build, so 304 goes first. Slot requested;
|
||||
no Node, test, queue claim or push before Sage CLEAR.
|
||||
|
||||
Before the edit, run the unchanged whole claude-session.test.mjs with
|
||||
fixture-only HOME, CLAUDE_CONFIG_DIR and scripted API under an underscore
|
||||
TMPDIR and a hyphen TMPDIR. Expected: underscore 9/1 only at the unrestricted
|
||||
memory control (:298), hyphen 10/0. Record both raw results here. Stop for
|
||||
Sage if the installed CLI's mapping differs from [^A-Za-z0-9] -> '-'.
|
||||
|
||||
Only then replace /[/.]/g with /[^A-Za-z0-9]/g on the slug line. Every
|
||||
assertion and every other test line stays byte-identical. No product change.
|
||||
Acceptance is whole file green under mktemp, underscore, and space TMPDIR
|
||||
(if the CLI accepts space); old-regex mutant must fail the :298 assertion
|
||||
under underscore. Run scope and claim wait for Sage's slot grant.
|
||||
|
||||
Use short fixture TMPDIRs under /mnt/storage/scratch/tmp, process groups,
|
||||
explicit TAP and bounded runs. No Docker, browser, suites or live state.
|
||||
Keep token/config paths and real provider access out of every fixture.
|
||||
|
||||
## Evidence, sage-3366 (mos-1442 point 1)
|
||||
|
||||
queue next selected304; claim rev3521, rc0. Base test unchanged until both
|
||||
mapping probes finished. Correct restricted-check citation: :293, not :291.
|
||||
|
||||
| Run | TMPDIR | Exit | Pass/fail/skip |
|
||||
|---|---|---|---|
|
||||
| baseline-underscore | /mnt/storage/scratch/tmp/rk304_OV81Op | 1 | 9/1/0 |
|
||||
| baseline-hyphen | /mnt/storage/scratch/tmp/rk304-UnWJbm | 0 | 10/0/0 |
|
||||
| fixed-mktemp | /mnt/storage/scratch/tmp/rk304yw6LWT | 0 | 10/0/0 |
|
||||
| fixed-underscore | /mnt/storage/scratch/tmp/rk304_MyhpSs | 0 | 10/0/0 |
|
||||
| fixed-space | /mnt/storage/scratch/tmp/rk304 i5d7HA | 0 | 10/0/0 |
|
||||
| M1-old-regex | /mnt/storage/scratch/tmp/rk304_595gfj | 1 | 9/1/0 |
|
||||
|
||||
Baseline underscore and M1 both fail only the named unrestricted positive
|
||||
control at claude-session.test.mjs:298:10, ERR_ASSERTION, missing MARKER-MEMORY.
|
||||
Hyphen baseline is green. This confirms the expected installed-CLI mapping.
|
||||
Space is accepted and passes after the fix. All3 fixed whole-file runs30/0/0;
|
||||
baseline hyphen10/0/0; M1 one assertion kill (whole file9/1/0).0cancellations,
|
||||
0skips throughout. Tests11:05:01Z-11:05:36Z, no unexpected red or retry.
|
||||
Every other source byte, including all assertions, is unchanged, recorded in
|
||||
assertion-preservation.json. Fixed test sha2567690a4a945e45f5606633d36a3db9a160892be9d125d40351cd848f5bf3ea9c9.
|
||||
Cwd node/chromium/claude sweep empty; no Docker/browser/suite/live activity.
|
||||
Sage's separate gate and Dewey's review still required; no gate claimed here.
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"allOtherBytesUnchanged": true,
|
||||
"assertionLinesUnchanged": true,
|
||||
"baseSha256": "3a43f134d334e985e6dd50f641a7c1788de29beb86e2f9e2ac7f0ceaf924a8e0",
|
||||
"candidateSha256": "7690a4a945e45f5606633d36a3db9a160892be9d125d40351cd848f5bf3ea9c9"
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
c16e943b90ef71ac96cfa5314bbea6040634d14d
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/packages/harness/tests/claude-session.test.mjs b/packages/harness/tests/claude-session.test.mjs
|
||||
index bf5cb2d29..d9c45e446 100644
|
||||
--- a/packages/harness/tests/claude-session.test.mjs
|
||||
+++ b/packages/harness/tests/claude-session.test.mjs
|
||||
@@ -269,7 +269,7 @@ test("claude adapter: a directory pin exits 2 before it runs; a pin with a space
|
||||
|
||||
test("claude: CLAUDE.md files and auto-memory don't reach the model; without --restricted they do", { skip }, async (t) => {
|
||||
const { dir, workspace, env } = await session(t, []);
|
||||
- const slug = realpathSync(workspace).replace(/[/.]/g, "-");
|
||||
+ const slug = realpathSync(workspace).replace(/[^A-Za-z0-9]/g, "-");
|
||||
const plant = {
|
||||
"MARKER-USER": [join(env.HOME, ".claude", "CLAUDE.md"), join(env.CLAUDE_CONFIG_DIR, "CLAUDE.md")],
|
||||
"MARKER-PARENT": [join(dir, "CLAUDE.md")],
|
||||
@@ -0,0 +1 @@
|
||||
7690a4a945e45f5606633d36a3db9a160892be9d125d40351cd848f5bf3ea9c9 packages/harness/tests/claude-session.test.mjs
|
||||
@@ -0,0 +1 @@
|
||||
packages/harness/tests/claude-session.test.mjs
|
||||
@@ -0,0 +1 @@
|
||||
[]
|
||||
@@ -0,0 +1,14 @@
|
||||
START baseline-underscore 2026-10-23T11:05:01.371651+00:00 /mnt/storage/scratch/tmp/rk304_OV81Op
|
||||
END {"label": "baseline-underscore", "start": "2026-10-23T11:05:01.371651+00:00", "end": "2026-10-23T11:05:06.998843+00:00", "seconds": 5.627, "exit": 1, "counts": {"tests": 10, "pass": 9, "fail": 1, "cancelled": 0, "skipped": 0}, "tmpdir": "/mnt/storage/scratch/tmp/rk304_OV81Op", "head": "c16e943b90ef71ac96cfa5314bbea6040634d14d", "testSha256": "3a43f134d334e985e6dd50f641a7c1788de29beb86e2f9e2ac7f0ceaf924a8e0", "skips": [], "log": "agents/rocko/work/queue-304/logs/baseline-underscore.tap"}
|
||||
START baseline-hyphen 2026-10-23T11:05:07.003823+00:00 /mnt/storage/scratch/tmp/rk304-UnWJbm
|
||||
END {"label": "baseline-hyphen", "start": "2026-10-23T11:05:07.003823+00:00", "end": "2026-10-23T11:05:12.829873+00:00", "seconds": 5.826, "exit": 0, "counts": {"tests": 10, "pass": 10, "fail": 0, "cancelled": 0, "skipped": 0}, "tmpdir": "/mnt/storage/scratch/tmp/rk304-UnWJbm", "head": "c16e943b90ef71ac96cfa5314bbea6040634d14d", "testSha256": "3a43f134d334e985e6dd50f641a7c1788de29beb86e2f9e2ac7f0ceaf924a8e0", "skips": [], "log": "agents/rocko/work/queue-304/logs/baseline-hyphen.tap"}
|
||||
START fixed-mktemp 2026-10-23T11:05:12.834143+00:00 /mnt/storage/scratch/tmp/rk304yw6LWT
|
||||
END {"label": "fixed-mktemp", "start": "2026-10-23T11:05:12.834143+00:00", "end": "2026-10-23T11:05:18.560124+00:00", "seconds": 5.726, "exit": 0, "counts": {"tests": 10, "pass": 10, "fail": 0, "cancelled": 0, "skipped": 0}, "tmpdir": "/mnt/storage/scratch/tmp/rk304yw6LWT", "head": "c16e943b90ef71ac96cfa5314bbea6040634d14d", "testSha256": "7690a4a945e45f5606633d36a3db9a160892be9d125d40351cd848f5bf3ea9c9", "skips": [], "log": "agents/rocko/work/queue-304/logs/fixed-mktemp.tap"}
|
||||
START fixed-underscore 2026-10-23T11:05:18.564553+00:00 /mnt/storage/scratch/tmp/rk304_MyhpSs
|
||||
END {"label": "fixed-underscore", "start": "2026-10-23T11:05:18.564553+00:00", "end": "2026-10-23T11:05:24.340312+00:00", "seconds": 5.776, "exit": 0, "counts": {"tests": 10, "pass": 10, "fail": 0, "cancelled": 0, "skipped": 0}, "tmpdir": "/mnt/storage/scratch/tmp/rk304_MyhpSs", "head": "c16e943b90ef71ac96cfa5314bbea6040634d14d", "testSha256": "7690a4a945e45f5606633d36a3db9a160892be9d125d40351cd848f5bf3ea9c9", "skips": [], "log": "agents/rocko/work/queue-304/logs/fixed-underscore.tap"}
|
||||
START fixed-space 2026-10-23T11:05:24.344312+00:00 /mnt/storage/scratch/tmp/rk304 i5d7HA
|
||||
END {"label": "fixed-space", "start": "2026-10-23T11:05:24.344312+00:00", "end": "2026-10-23T11:05:30.170101+00:00", "seconds": 5.826, "exit": 0, "counts": {"tests": 10, "pass": 10, "fail": 0, "cancelled": 0, "skipped": 0}, "tmpdir": "/mnt/storage/scratch/tmp/rk304 i5d7HA", "head": "c16e943b90ef71ac96cfa5314bbea6040634d14d", "testSha256": "7690a4a945e45f5606633d36a3db9a160892be9d125d40351cd848f5bf3ea9c9", "skips": [], "log": "agents/rocko/work/queue-304/logs/fixed-space.tap"}
|
||||
START M1-old-regex 2026-10-23T11:05:30.173937+00:00 /mnt/storage/scratch/tmp/rk304_595gfj
|
||||
END {"label": "M1-old-regex", "start": "2026-10-23T11:05:30.173937+00:00", "end": "2026-10-23T11:05:36.100361+00:00", "seconds": 5.926, "exit": 1, "counts": {"tests": 10, "pass": 9, "fail": 1, "cancelled": 0, "skipped": 0}, "tmpdir": "/mnt/storage/scratch/tmp/rk304_595gfj", "head": "c16e943b90ef71ac96cfa5314bbea6040634d14d", "testSha256": "3a43f134d334e985e6dd50f641a7c1788de29beb86e2f9e2ac7f0ceaf924a8e0", "skips": [], "log": "agents/rocko/work/queue-304/logs/M1-old-regex.tap"}
|
||||
BUILD_GREEN 2026-10-23T11:05:36.103508+00:00
|
||||
CWD_SWEEP []
|
||||
@@ -0,0 +1,99 @@
|
||||
TAP version 13
|
||||
# Subtest: claude: typed tools through MCP, the hook blocks, builtins outside --tools don't exist
|
||||
ok 1 - claude: typed tools through MCP, the hook blocks, builtins outside --tools don't exist
|
||||
---
|
||||
duration_ms: 810.176546
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: claude: the hook alone blocks a path outside the workspace
|
||||
ok 2 - claude: the hook alone blocks a path outside the workspace
|
||||
---
|
||||
duration_ms: 503.543336
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: claude: a second turn resumes the first turn's session
|
||||
ok 3 - claude: a second turn resumes the first turn's session
|
||||
---
|
||||
duration_ms: 915.865915
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: claude adapter: --restricted is always passed
|
||||
ok 4 - claude adapter: --restricted is always passed
|
||||
---
|
||||
duration_ms: 4.807726
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: claude adapter: MOSAIC_CLAUDE_BIN wins over PATH and must be an absolute executable
|
||||
ok 5 - claude adapter: MOSAIC_CLAUDE_BIN wins over PATH and must be an absolute executable
|
||||
---
|
||||
duration_ms: 15.271577
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: claude adapter: a directory pin exits 2 before it runs; a pin with a space runs
|
||||
ok 6 - claude adapter: a directory pin exits 2 before it runs; a pin with a space runs
|
||||
---
|
||||
duration_ms: 5.309932
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: claude: CLAUDE.md files and auto-memory don't reach the model; without --restricted they do
|
||||
not ok 7 - claude: CLAUDE.md files and auto-memory don't reach the model; without --restricted they do
|
||||
---
|
||||
duration_ms: 888.601244
|
||||
type: 'test'
|
||||
location: '/mnt/storage/scratch/rocko-r304/packages/harness/tests/claude-session.test.mjs:270:1'
|
||||
failureType: 'testCodeFailure'
|
||||
error: |-
|
||||
Expected values to be strictly deep-equal:
|
||||
+ actual - expected
|
||||
|
||||
[
|
||||
'MARKER-USER',
|
||||
'MARKER-PARENT',
|
||||
'MARKER-WS',
|
||||
- 'MARKER-MEMORY'
|
||||
]
|
||||
|
||||
code: 'ERR_ASSERTION'
|
||||
name: 'AssertionError'
|
||||
expected:
|
||||
0: 'MARKER-USER'
|
||||
1: 'MARKER-PARENT'
|
||||
2: 'MARKER-WS'
|
||||
3: 'MARKER-MEMORY'
|
||||
actual:
|
||||
0: 'MARKER-USER'
|
||||
1: 'MARKER-PARENT'
|
||||
2: 'MARKER-WS'
|
||||
operator: 'deepStrictEqual'
|
||||
stack: |-
|
||||
TestContext.<anonymous> (file:///mnt/storage/scratch/rocko-r304/packages/harness/tests/claude-session.test.mjs:298:10)
|
||||
async Test.run (node:internal/test_runner/test:1409:7)
|
||||
async Test.processPendingSubtests (node:internal/test_runner/test:974:7)
|
||||
...
|
||||
# Subtest: claude: a missing hook or MCP file refuses before claude starts
|
||||
ok 8 - claude: a missing hook or MCP file refuses before claude starts
|
||||
---
|
||||
duration_ms: 9.250346
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: claude: row 66 a reply with a tell gets the correction notice on the next turn, a clean reply gets none, and the counts read after
|
||||
ok 9 - claude: row 66 a reply with a tell gets the correction notice on the next turn, a clean reply gets none, and the counts read after
|
||||
---
|
||||
duration_ms: 1764.786892
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: claude: row 66 a broken lists.json gives no clean verdict, the operator is told once, and the session still answers
|
||||
ok 10 - claude: row 66 a broken lists.json gives no clean verdict, the operator is told once, and the session still answers
|
||||
---
|
||||
duration_ms: 871.328606
|
||||
type: 'test'
|
||||
...
|
||||
1..10
|
||||
# tests 10
|
||||
# suites 0
|
||||
# pass 9
|
||||
# fail 1
|
||||
# cancelled 0
|
||||
# skipped 0
|
||||
# todo 0
|
||||
# duration_ms 5870.59342
|
||||
@@ -0,0 +1,70 @@
|
||||
TAP version 13
|
||||
# Subtest: claude: typed tools through MCP, the hook blocks, builtins outside --tools don't exist
|
||||
ok 1 - claude: typed tools through MCP, the hook blocks, builtins outside --tools don't exist
|
||||
---
|
||||
duration_ms: 780.047981
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: claude: the hook alone blocks a path outside the workspace
|
||||
ok 2 - claude: the hook alone blocks a path outside the workspace
|
||||
---
|
||||
duration_ms: 503.370776
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: claude: a second turn resumes the first turn's session
|
||||
ok 3 - claude: a second turn resumes the first turn's session
|
||||
---
|
||||
duration_ms: 848.822885
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: claude adapter: --restricted is always passed
|
||||
ok 4 - claude adapter: --restricted is always passed
|
||||
---
|
||||
duration_ms: 4.629499
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: claude adapter: MOSAIC_CLAUDE_BIN wins over PATH and must be an absolute executable
|
||||
ok 5 - claude adapter: MOSAIC_CLAUDE_BIN wins over PATH and must be an absolute executable
|
||||
---
|
||||
duration_ms: 15.371187
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: claude adapter: a directory pin exits 2 before it runs; a pin with a space runs
|
||||
ok 6 - claude adapter: a directory pin exits 2 before it runs; a pin with a space runs
|
||||
---
|
||||
duration_ms: 5.545444
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: claude: CLAUDE.md files and auto-memory don't reach the model; without --restricted they do
|
||||
ok 7 - claude: CLAUDE.md files and auto-memory don't reach the model; without --restricted they do
|
||||
---
|
||||
duration_ms: 947.229815
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: claude: a missing hook or MCP file refuses before claude starts
|
||||
ok 8 - claude: a missing hook or MCP file refuses before claude starts
|
||||
---
|
||||
duration_ms: 8.467575
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: claude: row 66 a reply with a tell gets the correction notice on the next turn, a clean reply gets none, and the counts read after
|
||||
ok 9 - claude: row 66 a reply with a tell gets the correction notice on the next turn, a clean reply gets none, and the counts read after
|
||||
---
|
||||
duration_ms: 1738.629452
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: claude: row 66 a broken lists.json gives no clean verdict, the operator is told once, and the session still answers
|
||||
ok 10 - claude: row 66 a broken lists.json gives no clean verdict, the operator is told once, and the session still answers
|
||||
---
|
||||
duration_ms: 857.865121
|
||||
type: 'test'
|
||||
...
|
||||
1..10
|
||||
# tests 10
|
||||
# suites 0
|
||||
# pass 10
|
||||
# fail 0
|
||||
# cancelled 0
|
||||
# skipped 0
|
||||
# todo 0
|
||||
# duration_ms 5790.544695
|
||||
@@ -0,0 +1,100 @@
|
||||
TAP version 13
|
||||
# Subtest: claude: typed tools through MCP, the hook blocks, builtins outside --tools don't exist
|
||||
ok 1 - claude: typed tools through MCP, the hook blocks, builtins outside --tools don't exist
|
||||
---
|
||||
duration_ms: 748.693342
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: claude: the hook alone blocks a path outside the workspace
|
||||
ok 2 - claude: the hook alone blocks a path outside the workspace
|
||||
---
|
||||
duration_ms: 499.864544
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: claude: a second turn resumes the first turn's session
|
||||
ok 3 - claude: a second turn resumes the first turn's session
|
||||
---
|
||||
duration_ms: 829.415272
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: claude adapter: --restricted is always passed
|
||||
ok 4 - claude adapter: --restricted is always passed
|
||||
---
|
||||
duration_ms: 4.710643
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: claude adapter: MOSAIC_CLAUDE_BIN wins over PATH and must be an absolute executable
|
||||
ok 5 - claude adapter: MOSAIC_CLAUDE_BIN wins over PATH and must be an absolute executable
|
||||
---
|
||||
duration_ms: 14.085981
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: claude adapter: a directory pin exits 2 before it runs; a pin with a space runs
|
||||
ok 6 - claude adapter: a directory pin exits 2 before it runs; a pin with a space runs
|
||||
---
|
||||
duration_ms: 4.998474
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: claude: CLAUDE.md files and auto-memory don't reach the model; without --restricted they do
|
||||
not ok 7 - claude: CLAUDE.md files and auto-memory don't reach the model; without --restricted they do
|
||||
---
|
||||
duration_ms: 863.150735
|
||||
type: 'test'
|
||||
location: '/mnt/storage/scratch/rocko-r304/packages/harness/tests/claude-session.test.mjs:270:1'
|
||||
failureType: 'testCodeFailure'
|
||||
error: |-
|
||||
Expected values to be strictly deep-equal:
|
||||
+ actual - expected
|
||||
|
||||
[
|
||||
'MARKER-USER',
|
||||
'MARKER-PARENT',
|
||||
'MARKER-WS',
|
||||
- 'MARKER-MEMORY'
|
||||
]
|
||||
|
||||
code: 'ERR_ASSERTION'
|
||||
name: 'AssertionError'
|
||||
expected:
|
||||
0: 'MARKER-USER'
|
||||
1: 'MARKER-PARENT'
|
||||
2: 'MARKER-WS'
|
||||
3: 'MARKER-MEMORY'
|
||||
actual:
|
||||
0: 'MARKER-USER'
|
||||
1: 'MARKER-PARENT'
|
||||
2: 'MARKER-WS'
|
||||
operator: 'deepStrictEqual'
|
||||
stack: |-
|
||||
TestContext.<anonymous> (file:///mnt/storage/scratch/rocko-r304/packages/harness/tests/claude-session.test.mjs:298:10)
|
||||
process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
async Test.run (node:internal/test_runner/test:1409:7)
|
||||
async Test.processPendingSubtests (node:internal/test_runner/test:974:7)
|
||||
...
|
||||
# Subtest: claude: a missing hook or MCP file refuses before claude starts
|
||||
ok 8 - claude: a missing hook or MCP file refuses before claude starts
|
||||
---
|
||||
duration_ms: 8.695573
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: claude: row 66 a reply with a tell gets the correction notice on the next turn, a clean reply gets none, and the counts read after
|
||||
ok 9 - claude: row 66 a reply with a tell gets the correction notice on the next turn, a clean reply gets none, and the counts read after
|
||||
---
|
||||
duration_ms: 1671.315421
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: claude: row 66 a broken lists.json gives no clean verdict, the operator is told once, and the session still answers
|
||||
ok 10 - claude: row 66 a broken lists.json gives no clean verdict, the operator is told once, and the session still answers
|
||||
---
|
||||
duration_ms: 823.801717
|
||||
type: 'test'
|
||||
...
|
||||
1..10
|
||||
# tests 10
|
||||
# suites 0
|
||||
# pass 9
|
||||
# fail 1
|
||||
# cancelled 0
|
||||
# skipped 0
|
||||
# todo 0
|
||||
# duration_ms 5552.702962
|
||||
@@ -0,0 +1,70 @@
|
||||
TAP version 13
|
||||
# Subtest: claude: typed tools through MCP, the hook blocks, builtins outside --tools don't exist
|
||||
ok 1 - claude: typed tools through MCP, the hook blocks, builtins outside --tools don't exist
|
||||
---
|
||||
duration_ms: 822.315273
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: claude: the hook alone blocks a path outside the workspace
|
||||
ok 2 - claude: the hook alone blocks a path outside the workspace
|
||||
---
|
||||
duration_ms: 473.785584
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: claude: a second turn resumes the first turn's session
|
||||
ok 3 - claude: a second turn resumes the first turn's session
|
||||
---
|
||||
duration_ms: 834.093184
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: claude adapter: --restricted is always passed
|
||||
ok 4 - claude adapter: --restricted is always passed
|
||||
---
|
||||
duration_ms: 4.587717
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: claude adapter: MOSAIC_CLAUDE_BIN wins over PATH and must be an absolute executable
|
||||
ok 5 - claude adapter: MOSAIC_CLAUDE_BIN wins over PATH and must be an absolute executable
|
||||
---
|
||||
duration_ms: 15.598032
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: claude adapter: a directory pin exits 2 before it runs; a pin with a space runs
|
||||
ok 6 - claude adapter: a directory pin exits 2 before it runs; a pin with a space runs
|
||||
---
|
||||
duration_ms: 5.369426
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: claude: CLAUDE.md files and auto-memory don't reach the model; without --restricted they do
|
||||
ok 7 - claude: CLAUDE.md files and auto-memory don't reach the model; without --restricted they do
|
||||
---
|
||||
duration_ms: 831.362199
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: claude: a missing hook or MCP file refuses before claude starts
|
||||
ok 8 - claude: a missing hook or MCP file refuses before claude starts
|
||||
---
|
||||
duration_ms: 8.237071
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: claude: row 66 a reply with a tell gets the correction notice on the next turn, a clean reply gets none, and the counts read after
|
||||
ok 9 - claude: row 66 a reply with a tell gets the correction notice on the next turn, a clean reply gets none, and the counts read after
|
||||
---
|
||||
duration_ms: 1687.124498
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: claude: row 66 a broken lists.json gives no clean verdict, the operator is told once, and the session still answers
|
||||
ok 10 - claude: row 66 a broken lists.json gives no clean verdict, the operator is told once, and the session still answers
|
||||
---
|
||||
duration_ms: 884.665674
|
||||
type: 'test'
|
||||
...
|
||||
1..10
|
||||
# tests 10
|
||||
# suites 0
|
||||
# pass 10
|
||||
# fail 0
|
||||
# cancelled 0
|
||||
# skipped 0
|
||||
# todo 0
|
||||
# duration_ms 5663.888591
|
||||
@@ -0,0 +1,70 @@
|
||||
TAP version 13
|
||||
# Subtest: claude: typed tools through MCP, the hook blocks, builtins outside --tools don't exist
|
||||
ok 1 - claude: typed tools through MCP, the hook blocks, builtins outside --tools don't exist
|
||||
---
|
||||
duration_ms: 740.530837
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: claude: the hook alone blocks a path outside the workspace
|
||||
ok 2 - claude: the hook alone blocks a path outside the workspace
|
||||
---
|
||||
duration_ms: 484.250736
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: claude: a second turn resumes the first turn's session
|
||||
ok 3 - claude: a second turn resumes the first turn's session
|
||||
---
|
||||
duration_ms: 857.991709
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: claude adapter: --restricted is always passed
|
||||
ok 4 - claude adapter: --restricted is always passed
|
||||
---
|
||||
duration_ms: 5.19059
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: claude adapter: MOSAIC_CLAUDE_BIN wins over PATH and must be an absolute executable
|
||||
ok 5 - claude adapter: MOSAIC_CLAUDE_BIN wins over PATH and must be an absolute executable
|
||||
---
|
||||
duration_ms: 16.380045
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: claude adapter: a directory pin exits 2 before it runs; a pin with a space runs
|
||||
ok 6 - claude adapter: a directory pin exits 2 before it runs; a pin with a space runs
|
||||
---
|
||||
duration_ms: 5.47195
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: claude: CLAUDE.md files and auto-memory don't reach the model; without --restricted they do
|
||||
ok 7 - claude: CLAUDE.md files and auto-memory don't reach the model; without --restricted they do
|
||||
---
|
||||
duration_ms: 869.204192
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: claude: a missing hook or MCP file refuses before claude starts
|
||||
ok 8 - claude: a missing hook or MCP file refuses before claude starts
|
||||
---
|
||||
duration_ms: 8.011154
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: claude: row 66 a reply with a tell gets the correction notice on the next turn, a clean reply gets none, and the counts read after
|
||||
ok 9 - claude: row 66 a reply with a tell gets the correction notice on the next turn, a clean reply gets none, and the counts read after
|
||||
---
|
||||
duration_ms: 1760.816087
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: claude: row 66 a broken lists.json gives no clean verdict, the operator is told once, and the session still answers
|
||||
ok 10 - claude: row 66 a broken lists.json gives no clean verdict, the operator is told once, and the session still answers
|
||||
---
|
||||
duration_ms: 951.447392
|
||||
type: 'test'
|
||||
...
|
||||
1..10
|
||||
# tests 10
|
||||
# suites 0
|
||||
# pass 10
|
||||
# fail 0
|
||||
# cancelled 0
|
||||
# skipped 0
|
||||
# todo 0
|
||||
# duration_ms 5778.476849
|
||||
@@ -0,0 +1,70 @@
|
||||
TAP version 13
|
||||
# Subtest: claude: typed tools through MCP, the hook blocks, builtins outside --tools don't exist
|
||||
ok 1 - claude: typed tools through MCP, the hook blocks, builtins outside --tools don't exist
|
||||
---
|
||||
duration_ms: 766.913918
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: claude: the hook alone blocks a path outside the workspace
|
||||
ok 2 - claude: the hook alone blocks a path outside the workspace
|
||||
---
|
||||
duration_ms: 496.038436
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: claude: a second turn resumes the first turn's session
|
||||
ok 3 - claude: a second turn resumes the first turn's session
|
||||
---
|
||||
duration_ms: 839.816577
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: claude adapter: --restricted is always passed
|
||||
ok 4 - claude adapter: --restricted is always passed
|
||||
---
|
||||
duration_ms: 4.571987
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: claude adapter: MOSAIC_CLAUDE_BIN wins over PATH and must be an absolute executable
|
||||
ok 5 - claude adapter: MOSAIC_CLAUDE_BIN wins over PATH and must be an absolute executable
|
||||
---
|
||||
duration_ms: 17.17544
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: claude adapter: a directory pin exits 2 before it runs; a pin with a space runs
|
||||
ok 6 - claude adapter: a directory pin exits 2 before it runs; a pin with a space runs
|
||||
---
|
||||
duration_ms: 5.448032
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: claude: CLAUDE.md files and auto-memory don't reach the model; without --restricted they do
|
||||
ok 7 - claude: CLAUDE.md files and auto-memory don't reach the model; without --restricted they do
|
||||
---
|
||||
duration_ms: 873.897762
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: claude: a missing hook or MCP file refuses before claude starts
|
||||
ok 8 - claude: a missing hook or MCP file refuses before claude starts
|
||||
---
|
||||
duration_ms: 8.282957
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: claude: row 66 a reply with a tell gets the correction notice on the next turn, a clean reply gets none, and the counts read after
|
||||
ok 9 - claude: row 66 a reply with a tell gets the correction notice on the next turn, a clean reply gets none, and the counts read after
|
||||
---
|
||||
duration_ms: 1738.792709
|
||||
type: 'test'
|
||||
...
|
||||
# Subtest: claude: row 66 a broken lists.json gives no clean verdict, the operator is told once, and the session still answers
|
||||
ok 10 - claude: row 66 a broken lists.json gives no clean verdict, the operator is told once, and the session still answers
|
||||
---
|
||||
duration_ms: 863.908172
|
||||
type: 'test'
|
||||
...
|
||||
1..10
|
||||
# tests 10
|
||||
# suites 0
|
||||
# pass 10
|
||||
# fail 0
|
||||
# cancelled 0
|
||||
# skipped 0
|
||||
# todo 0
|
||||
# duration_ms 5704.886282
|
||||
@@ -0,0 +1,16 @@
|
||||
0b487adf165fb6b83e9cba38fc49167c6ec54b66261d955052f7ee5a877fde07 BUILD.md
|
||||
a75846e9e3217b524ad09bb423033975da6c44074d8d74dc14193550ec1666b9 assertion-preservation.json
|
||||
5ddf1af4e2d23bc579e6a4005c2b355794dcc33fbd29c0421a6d1e9ca60f70ec base.txt
|
||||
85dc6f969a11d933b04cc7b2fe49199d745029779c66bf1815f549c51ec91568 build.patch
|
||||
a09b498872aaf4f3b3af9b297d839d4ddb6fdc57d7a01f05cc1a48200c30cfd4 candidate-manifest.sha256
|
||||
8588e890cdfadd6911fd3b473aad3ff826f6dd91a52e8d10aeb4a28f7929d995 changed-paths.txt
|
||||
37517e5f3dc66819f61f5a7bb8ace1921282415f10551d2defa5c3eb0985b570 cwd-sweep.json
|
||||
767c5c04bc6442bb6efc8de3bf86e5ef8d2bbdd90d20d72fd604d18e69290f91 driver.log
|
||||
f6f8a66ccc0c025782cc6932de9745720ca7574a0376d6122074275332abaf43 logs/M1-old-regex.tap
|
||||
dc290774a454c85033fdc8c71226f7cd56a07e52106b2868ca91ca5ea652ddca logs/baseline-hyphen.tap
|
||||
960114150fbf0a3069762d356b6cffe0068208a69e805bb0422de0da8825af35 logs/baseline-underscore.tap
|
||||
1d0836115cdf344e7cdcbe1ef9de7a3f2e57ca06a4cca42a07adb14ba82bd29a logs/fixed-mktemp.tap
|
||||
a69191da6fd3eb20ec99b92521df9418e9815b7879a159cc7b2abbd5b790261c logs/fixed-space.tap
|
||||
9776e07a392bc567609573a66a2a17619ded0863b8ee35cbc53482e42c58220b logs/fixed-underscore.tap
|
||||
3adeed328506abdeff0bb108ac16d306736e2caba1c8188d48173e5ea75b5c81 results.jsonl
|
||||
084a848241bf874a5873a881d2c47efd29deff7b6682947fc7738053556845d0 run-build.py
|
||||
@@ -0,0 +1,6 @@
|
||||
{"label": "baseline-underscore", "start": "2026-10-23T11:05:01.371651+00:00", "end": "2026-10-23T11:05:06.998843+00:00", "seconds": 5.627, "exit": 1, "counts": {"tests": 10, "pass": 9, "fail": 1, "cancelled": 0, "skipped": 0}, "tmpdir": "/mnt/storage/scratch/tmp/rk304_OV81Op", "head": "c16e943b90ef71ac96cfa5314bbea6040634d14d", "testSha256": "3a43f134d334e985e6dd50f641a7c1788de29beb86e2f9e2ac7f0ceaf924a8e0", "skips": [], "log": "agents/rocko/work/queue-304/logs/baseline-underscore.tap"}
|
||||
{"label": "baseline-hyphen", "start": "2026-10-23T11:05:07.003823+00:00", "end": "2026-10-23T11:05:12.829873+00:00", "seconds": 5.826, "exit": 0, "counts": {"tests": 10, "pass": 10, "fail": 0, "cancelled": 0, "skipped": 0}, "tmpdir": "/mnt/storage/scratch/tmp/rk304-UnWJbm", "head": "c16e943b90ef71ac96cfa5314bbea6040634d14d", "testSha256": "3a43f134d334e985e6dd50f641a7c1788de29beb86e2f9e2ac7f0ceaf924a8e0", "skips": [], "log": "agents/rocko/work/queue-304/logs/baseline-hyphen.tap"}
|
||||
{"label": "fixed-mktemp", "start": "2026-10-23T11:05:12.834143+00:00", "end": "2026-10-23T11:05:18.560124+00:00", "seconds": 5.726, "exit": 0, "counts": {"tests": 10, "pass": 10, "fail": 0, "cancelled": 0, "skipped": 0}, "tmpdir": "/mnt/storage/scratch/tmp/rk304yw6LWT", "head": "c16e943b90ef71ac96cfa5314bbea6040634d14d", "testSha256": "7690a4a945e45f5606633d36a3db9a160892be9d125d40351cd848f5bf3ea9c9", "skips": [], "log": "agents/rocko/work/queue-304/logs/fixed-mktemp.tap"}
|
||||
{"label": "fixed-underscore", "start": "2026-10-23T11:05:18.564553+00:00", "end": "2026-10-23T11:05:24.340312+00:00", "seconds": 5.776, "exit": 0, "counts": {"tests": 10, "pass": 10, "fail": 0, "cancelled": 0, "skipped": 0}, "tmpdir": "/mnt/storage/scratch/tmp/rk304_MyhpSs", "head": "c16e943b90ef71ac96cfa5314bbea6040634d14d", "testSha256": "7690a4a945e45f5606633d36a3db9a160892be9d125d40351cd848f5bf3ea9c9", "skips": [], "log": "agents/rocko/work/queue-304/logs/fixed-underscore.tap"}
|
||||
{"label": "fixed-space", "start": "2026-10-23T11:05:24.344312+00:00", "end": "2026-10-23T11:05:30.170101+00:00", "seconds": 5.826, "exit": 0, "counts": {"tests": 10, "pass": 10, "fail": 0, "cancelled": 0, "skipped": 0}, "tmpdir": "/mnt/storage/scratch/tmp/rk304 i5d7HA", "head": "c16e943b90ef71ac96cfa5314bbea6040634d14d", "testSha256": "7690a4a945e45f5606633d36a3db9a160892be9d125d40351cd848f5bf3ea9c9", "skips": [], "log": "agents/rocko/work/queue-304/logs/fixed-space.tap"}
|
||||
{"label": "M1-old-regex", "start": "2026-10-23T11:05:30.173937+00:00", "end": "2026-10-23T11:05:36.100361+00:00", "seconds": 5.926, "exit": 1, "counts": {"tests": 10, "pass": 9, "fail": 1, "cancelled": 0, "skipped": 0}, "tmpdir": "/mnt/storage/scratch/tmp/rk304_595gfj", "head": "c16e943b90ef71ac96cfa5314bbea6040634d14d", "testSha256": "3a43f134d334e985e6dd50f641a7c1788de29beb86e2f9e2ac7f0ceaf924a8e0", "skips": [], "log": "agents/rocko/work/queue-304/logs/M1-old-regex.tap"}
|
||||
@@ -0,0 +1,66 @@
|
||||
from pathlib import Path
|
||||
import subprocess,os,signal,re,json,hashlib,time
|
||||
from datetime import datetime,timezone
|
||||
ROOT=Path(__file__).resolve().parents[4];P=Path(__file__).resolve().parent;O=P/'logs';O.mkdir(exist_ok=True)
|
||||
FILE='packages/harness/tests/claude-session.test.mjs';source=ROOT/FILE
|
||||
BASE='c16e943b90ef71ac96cfa5314bbea6040634d14d'
|
||||
old='realpathSync(workspace).replace(/[/.]/g, "-")';new='realpathSync(workspace).replace(/[^A-Za-z0-9]/g, "-")'
|
||||
original=source.read_bytes();assert original.decode().count(old)==1
|
||||
fixed=original.decode().replace(old,new,1).encode()
|
||||
rows=[];temps=[]
|
||||
def stamp():return datetime.now(timezone.utc).isoformat()
|
||||
def verify(data):
|
||||
assert subprocess.check_output(['git','rev-parse','HEAD'],cwd=ROOT,text=True).strip()==BASE
|
||||
assert source.read_bytes()==data
|
||||
|
||||
def run(label,template,data):
|
||||
verify(data);tmp=subprocess.check_output(['mktemp','-d',template],text=True).strip();temps.append(tmp)
|
||||
env=os.environ.copy();env.update(TMPDIR=tmp,DOCKER_HOST='unix:///mnt/storage/scratch/rocko-r304-dead.sock',NO_COLOR='1');env.pop('MOSAIC_GITEA_CREDENTIAL_FILE',None);env.pop('MOSAIC_LIVE_PROVIDER',None)
|
||||
args=['timeout','-k','10','180','node','--test','--test-reporter=tap',FILE];log=O/(label+'.tap');start=stamp();begin=time.monotonic()
|
||||
print('START',label,start,tmp,flush=True)
|
||||
with log.open('wb') as f:
|
||||
child=subprocess.Popen(args,cwd=ROOT,env=env,stdout=f,stderr=subprocess.STDOUT,start_new_session=True)
|
||||
try:rc=child.wait(timeout=195)
|
||||
except subprocess.TimeoutExpired:
|
||||
os.killpg(child.pid,signal.SIGKILL);child.wait();rc=124
|
||||
finally:
|
||||
try:os.killpg(child.pid,signal.SIGTERM)
|
||||
except ProcessLookupError:pass
|
||||
text=log.read_text(errors='replace');counts={k:int(m[-1]) if (m:=re.findall(r'^# '+k+r' (\d+)\s*$',text,re.M)) else None for k in ['tests','pass','fail','cancelled','skipped']}
|
||||
row=dict(label=label,start=start,end=stamp(),seconds=round(time.monotonic()-begin,3),exit=rc,counts=counts,tmpdir=tmp,head=BASE,testSha256=hashlib.sha256(data).hexdigest(),skips=[x for x in text.splitlines() if re.search(r'^skip\b|# SKIP\b',x,re.I)],log=str(log.relative_to(ROOT)))
|
||||
rows.append(row)
|
||||
with (P/'results.jsonl').open('a') as f:f.write(json.dumps(row)+'\n')
|
||||
print('END',json.dumps(row),flush=True)
|
||||
assert rc not in [124,137,-9] and 'SyntaxError:' not in text,'invalid run '+label
|
||||
verify(data);return row,text
|
||||
|
||||
def red(label,template,data):
|
||||
r,t=run(label,template,data)
|
||||
assert r['exit']!=0 and r['counts']['tests']==10 and r['counts']['pass']==9 and r['counts']['fail']==1 and r['counts']['cancelled']==0 and r['counts']['skipped']==0,'unexpected red counts '+label
|
||||
names=re.findall(r'^not ok \d+ - (.*)$',t,re.M)
|
||||
assert len(names)==1 and "CLAUDE.md files and auto-memory" in names[0] and ':298:10' in t and 'ERR_ASSERTION' in t and 'MARKER-MEMORY' in t,'unexpected red '+label
|
||||
|
||||
def green(label,template,data):
|
||||
r,t=run(label,template,data)
|
||||
assert r['exit']==0 and r['counts']['tests']==10 and r['counts']['pass']==10 and r['counts']['fail']==0 and r['counts']['cancelled']==0 and r['counts']['skipped']==0,'unexpected mapping/refusal '+label
|
||||
|
||||
try:
|
||||
red('baseline-underscore','/mnt/storage/scratch/tmp/rk304_XXXXXX',original)
|
||||
green('baseline-hyphen','/mnt/storage/scratch/tmp/rk304-XXXXXX',original)
|
||||
# Both installed-CLI probes now confirm the approved mapping before editing.
|
||||
source.write_bytes(fixed)
|
||||
green('fixed-mktemp','/mnt/storage/scratch/tmp/rk304XXXXXX',fixed)
|
||||
green('fixed-underscore','/mnt/storage/scratch/tmp/rk304_XXXXXX',fixed)
|
||||
green('fixed-space','/mnt/storage/scratch/tmp/rk304 XXXXXX',fixed)
|
||||
source.write_bytes(original)
|
||||
try:red('M1-old-regex','/mnt/storage/scratch/tmp/rk304_XXXXXX',original)
|
||||
finally:source.write_bytes(fixed)
|
||||
verify(fixed);print('BUILD_GREEN',stamp(),flush=True)
|
||||
finally:
|
||||
found=[]
|
||||
for proc in Path('/proc').glob('[0-9]*'):
|
||||
try:
|
||||
name=(proc/'comm').read_text().strip();cwd=str((proc/'cwd').resolve())
|
||||
if any(x in name for x in ['node','chrom','claude']) and any(cwd==r or cwd.startswith(r+'/') for r in [str(ROOT),*temps]):found.append(dict(pid=int(proc.name),name=name,cwd=cwd))
|
||||
except (FileNotFoundError,PermissionError,ProcessLookupError):pass
|
||||
(P/'cwd-sweep.json').write_text(json.dumps(found,indent=2)+'\n');print('CWD_SWEEP',found,flush=True)
|
||||
@@ -12,6 +12,11 @@ export COMPOSE_PROJECT_NAME="$(basename "$(dirname "$G")")"
|
||||
trap 'docker network rm "${COMPOSE_PROJECT_NAME}_default" >/dev/null 2>&1 && echo "removed network ${COMPOSE_PROJECT_NAME}_default"' EXIT
|
||||
# A gate tree without the node_modules link fails cli tests with ENOENT (2026-10-13).
|
||||
[ -d "$G/node_modules" ] || { echo "STOP no node_modules in $G"; exit 8; }
|
||||
# Gate rule (mos-1455 item 4, 2026-10-23): every landing gate runs the bus, cli, plugins, webui and business
|
||||
# suites whatever the row touches. The 270+271 gate skipped plugins, and its red sat on refactor unseen.
|
||||
for p in bus cli plugins webui business; do [ -d "$G/packages/$p/tests" ] || { echo "STOP no packages/$p/tests"; exit 8; }; done
|
||||
# mos-1453: a gate on the canonical node_modules must not let the webui editor rebuild test skip (Vikunja 597).
|
||||
export MOSAIC_REQUIRE_EDITOR_BUILD=1
|
||||
echo "base $(git rev-parse --short HEAD) dirty $(git status --short | grep -vc node_modules) DOCKER_HOST=${DOCKER_HOST:-unset}"
|
||||
run() { local name="$1" dir="$2" cmd="$3"
|
||||
(cd "$G/$dir" && bash -c "$cmd") > "$OUT/$name.log" 2>&1; local rc=$?
|
||||
|
||||
@@ -0,0 +1,638 @@
|
||||
# PRD 1.17 draft: Case 2 story files are DOCX (Q235 to Q237, P1 to P6)
|
||||
|
||||
Sage, 2026-10-23, for mos-1366 item 1 and Vikunja 570. Draft 8. It is d7 plus Mos's d8 fix list mos-1443 (see "d8 changes"). d7 is d6 plus Mos's ruling mos-1379 on Fenton's 571 B2 (see "d7 changes"). d6 is d5 with Webby's round 3 (webby-0207) fixed: B1 to B3 and N10 to N13, with N14 left for Mos (see "d6 changes"). d5 folds in:
|
||||
- Mos's d4 check ruling mos-1372 (D1 to D14; D15 was dropped).
|
||||
- mos-1374, which replaces J-c.
|
||||
- The impact map mos-1368 (jason-master-restart d183246, `projects/mosaic-mvp/case2-docx-impact-map.md`: A1 to A16, critic items C1 to C6, items 2 to 7).
|
||||
- Mos's F4 measurement.
|
||||
|
||||
Review order (mos-1367 B5): Webby, then Mos, then Jason. Base: PRD 1.16, `docs/prd/mosaic-stack.md` blob a6db9b4a at 482e865ac.
|
||||
1.16 lands first (row 291), and this text is a new version on top of it. Nothing here is built or queued. The impact map's queue changes (A24, A57 to A59) go through `scripts/mosaic queue` after 1.17 is approved (C9).
|
||||
|
||||
Sources, verbatim in Mos's DECISIONS:
|
||||
- Q235, Jason's Q36 answer.
|
||||
- Q236: chapters are DOCX, Markdown for agent files and docs.
|
||||
- Q237: "It is correct for now (subject to future change)".
|
||||
- The P1 to P6 reading as restated to Jason in Q38.
|
||||
|
||||
## Settled points (mos-1367)
|
||||
|
||||
These stand as ruled. d5 changes only what mos-1372 changed.
|
||||
|
||||
- **O1. Where a converted chapter lives.** The DOCX lives in the writing store of
|
||||
O2 design D12, and the archived Markdown stays in the user's repository under a
|
||||
human-marked archive folder. Ruled yes in B1, on the condition that the text
|
||||
says plainly that a converted chapter leaves the Gitea-mirrored repository and
|
||||
lives in the stack's writing store. mos-1372 D7 and mos-1375 add a remote for
|
||||
the store, off the stack's host.
|
||||
- **O2. The unit of conversion.** One story file per import, refusing a file
|
||||
that is already converted. Ruled yes in B2: each file gets its own fidelity
|
||||
report, human confirmation, manifest and commit.
|
||||
- **O3. Whether conversion is a vocabulary action.** A human-only verb outside
|
||||
the agent vocabulary, with no role grant and no agent request path. Ruled yes
|
||||
in B3.
|
||||
- **O4. Reading archived Markdown.** A new `archived` scope. Lookups leave it out
|
||||
unless the caller asks, every answer marks it archived, and a canon citation
|
||||
can't name it. Ruled yes in B4.
|
||||
|
||||
## d3 choices after mos-1372
|
||||
|
||||
- **C1 is replaced by D1, keep-source-scope.** A DOCX keeps its source's scope:
|
||||
universe, book, and canon or playground. A playground DOCX follows the
|
||||
playground rules and is never returned as canon. The import accepts canon and
|
||||
playground story files. Type-folder files and supporting files stay Markdown
|
||||
(P4). This also settles the impact map's Q18, item 4. It leaves one narrowing
|
||||
for Jason, J-i. A playground DOCX can't become canon in 1.17.
|
||||
- **C2 stands.** The move keeps the file's path under the archive folder:
|
||||
`book1/ch3.md` goes to `<archive>/book1/ch3.md`. An archived file's universe
|
||||
and book are those of its old path.
|
||||
- **C3 is replaced by D3 and D14.** The archive commit is written by a new
|
||||
human-only bus step (Vikunja 571) on the C2-02 write plumbing and pushed through
|
||||
C2-03b. The order is the store commit and the remote's confirmation that it
|
||||
holds that commit, then the archive commit and its push, then publication to the
|
||||
lookups.
|
||||
- **N4 stands.** REQ-LIB-8 says "a real user's books". The PRD names no user
|
||||
anywhere in Case 2.
|
||||
|
||||
## Text changes
|
||||
|
||||
### Status and sources
|
||||
|
||||
In the Status bullet's first line, "- Status: approved, version 1.16." becomes
|
||||
"- Status: approved, version 1.17." Status gains, after the two 1.16 sentences at
|
||||
the end of the bullet: "Version 1.17 records Jason's rulings Q235 to Q237 of
|
||||
2026-10-23: Case 2 story files are DOCX, Markdown stays for agent files and
|
||||
documentation, and a human-started import converts a Markdown story file one way.
|
||||
It also 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)."
|
||||
|
||||
New source line, above the 1.16 one: "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
|
||||
creating writing use case is to use docx to handle versioning and WYSIWYG. We
|
||||
eliminate the potential for confusion based on the files. Agents must understand
|
||||
which files are what format and where they live. The system must allow for import
|
||||
and conversion. We will not run the systems in parallel. There will be an
|
||||
irreversible conversion of the files.' His Q236 says: 'For creative writing
|
||||
chapters, we need to utilize docx as the standard. Markdown is acceptable for
|
||||
agent files, instructions, PRDs, documentation, etc. The creative writing files
|
||||
should be docx for familiarity for the user.' His Q237 says 'Q38: It is correct
|
||||
for now (subject to future change)', which confirms Mos's reading P1 to P6.
|
||||
REQ-LIB-9 rests on his Q195 request for 'a full WYSIWYG editor within Mosaic
|
||||
Stack', his Q226 answer 'F41: We want to move editing into the WYSIWYG in the
|
||||
WebUI. All changes are instantly committed and available to the agents.' and his
|
||||
Q234 answer 'Q37: yes' to the exact security header for the editor's frame. The
|
||||
storage follows the O2 design D10 to D12 as rewritten under Vikunja 571, at
|
||||
b99ca86b9 on `refactor`."
|
||||
|
||||
### Creative library, section lead
|
||||
|
||||
Replace "The library is the user's own writing repository." with "The library is
|
||||
the user's own writing repository plus the stack's writing store, which holds the
|
||||
DOCX story files (REQ-LIB-8, version 1.17)."
|
||||
|
||||
### REQ-LIB-1
|
||||
|
||||
Add, before "(Design 1.1": "The mapping also names one archive folder, set by the
|
||||
human, where converted Markdown story files go. A book's DOCX story files are in
|
||||
the stack's writing store, under the same universe and book (REQ-LIB-8)." Version
|
||||
tag, inside the closing parenthesis: "Version 1.15: supporting folders.)" becomes
|
||||
"Version 1.15: supporting folders. Version 1.17: the archive folder and the
|
||||
writing store.)"
|
||||
|
||||
### REQ-LIB-2
|
||||
|
||||
Replace "A file's scope comes from its path alone, prefixes first: a Markdown file
|
||||
under a supporting prefix is supporting, one under a playground prefix at any level
|
||||
is playground, any other Markdown file in a book folder is canon, and everything
|
||||
else is unmapped and never returned. A folder in both lists is supporting." with:
|
||||
|
||||
"A Markdown file's scope comes from its path and from past imports, prefixes
|
||||
first: a Markdown file under the archive folder is archived, one under a
|
||||
supporting prefix is supporting, a Markdown file at a converted file's old path is
|
||||
unmapped under REQ-LIB-8 but flagged in the format map, one under a playground
|
||||
prefix at any level is playground, any other Markdown file in a book folder is
|
||||
canon, and everything else is unmapped and never returned. A folder in both lists
|
||||
is supporting. The archive folder follows the same placement rules and may not
|
||||
overlap another prefix or a book folder. An archived file keeps the path it had
|
||||
under the archive folder, and its universe and book are those of that old path. A
|
||||
DOCX document in the writing store keeps its source's scope: the universe, the
|
||||
book if there is one, and canon or playground. Its source is the Markdown file it
|
||||
was converted from or, for one the human starts in the editor, the universe and
|
||||
book or the playground the human picks (REQ-LIB-8). A playground DOCX follows the
|
||||
playground rules and is never returned as canon. It stays playground after its
|
||||
draft universe is made real. In version 1.17 no promotion or mapping change makes
|
||||
a playground DOCX canon. A later version of this PRD says how one becomes canon."
|
||||
|
||||
Replace "Lookups return canon unless the caller asks for playground or
|
||||
supporting." with "Lookups return canon unless the caller asks for playground,
|
||||
supporting or archived. Every answer marks an archived file as archived and names
|
||||
the DOCX that replaced it."
|
||||
|
||||
Replace "so an agent can't cite a playground or supporting file as canon" with "so
|
||||
an agent can't cite a playground, supporting or archived file as canon".
|
||||
|
||||
In REQ-LIB-2 only, replace "The Console shows each cite's scope." with "The Console shows each cite's
|
||||
scope and format, and whether a cited Markdown file was converted."
|
||||
|
||||
Version tag, inside the closing parenthesis: "Version 1.15: supporting.)" becomes
|
||||
"Version 1.15: supporting. Version 1.17: archived scope and DOCX scope.)"
|
||||
|
||||
### REQ-LIB-4
|
||||
|
||||
Replace "The user commits and pushes with their own tools, today Obsidian with
|
||||
obsidian-git." with "The user commits and pushes Markdown with their own tools,
|
||||
today Obsidian with obsidian-git. A converted story file's current text is no
|
||||
longer in that repository, which keeps only its archived Markdown (REQ-LIB-8)."
|
||||
|
||||
Add, after "Bus writes and pushes come in later rows (C2-02, C2-03b).": "A DOCX
|
||||
story file is saved in the Console's editor, and each accepted save is one commit
|
||||
by the human in the stack's writing store (REQ-LIB-9). Each writing-store commit
|
||||
is also pushed to a remote off the stack's host, never by force-push. The remote
|
||||
is a setting only a human changes, and it starts unset. A repository on the
|
||||
stack's host never counts as that remote. A missing remote or a failed push never
|
||||
undoes a save, and the user sees each push that is pending or failed. Only the
|
||||
stack's host pushes to that remote, and no agent can reach it with any credential.
|
||||
Agents read the local commit."
|
||||
|
||||
Replace "Agents read only the mirror's served commit and never hold a copy of
|
||||
their own, so they see the user's push within about a minute." with "Agents read
|
||||
the user's repository only at the mirror's served commit, except a story file
|
||||
during its import under REQ-LIB-8. They read a DOCX story file only at a committed
|
||||
version in the writing store, under REQ-LIB-5. They never hold a copy of their
|
||||
own, so they see the user's Markdown push within about a minute and a DOCX save at
|
||||
their next lookup after the editor reports it as committed."
|
||||
|
||||
Version tag: "and the sync items.)" becomes "and the sync items. Version 1.17,
|
||||
with Q222 for the remote setting.)"
|
||||
|
||||
### REQ-LIB-5
|
||||
|
||||
Add, after "Each answer names the commit it read.": "Each answer also names the
|
||||
file's format and where it lives: its path, or its document id in the writing
|
||||
store. A DOCX story file is read from its latest committed save, never from
|
||||
unsaved text. A lookup that starts after the editor reports a save as committed
|
||||
returns that save or a later one, even while its push to the remote is pending or
|
||||
has failed, and the answer names the version. Get, search and manuscript read a
|
||||
DOCX story file as its extracted text with DOCX locators, never as lines. That
|
||||
text is the file with its tracked changes accepted. An agent reads a story file's
|
||||
comments, or its text with tracked deletions shown, only after a human turns on
|
||||
the matching setting. Both settings are off by default, and only a human changes
|
||||
them. The reader in this version can't read some things a DOCX story file can
|
||||
hold, such as a web link, a text box, a field or hidden text. The editor warns the
|
||||
human when an edit adds one, and the human can still save it. Until a later save
|
||||
takes it out, lookups give agents none of the text of the part that holds it,
|
||||
whether that is the chapter's body, a note or a comment. The manuscript lookup
|
||||
follows the book's chapter order across both formats (O2 design D10), and the next
|
||||
chapter it names is never an archived file. Links out and in resolve across both
|
||||
formats. The universe lookup returns the format map (REQ-LIB-8)."
|
||||
|
||||
Replace "The user sees them in Obsidian's graph and backlinks." with "The user sees
|
||||
a Markdown file's links in Obsidian's graph and backlinks. A DOCX story file's
|
||||
wikilinks stay clickable in the Console's editor (O2 design D11), and no graph
|
||||
shows them yet."
|
||||
|
||||
Version tag: 'and "Connections are always visible".)' becomes 'and "Connections
|
||||
are always visible". Version 1.17, with Q222 for the two settings.)'
|
||||
|
||||
### REQ-LIB-6
|
||||
|
||||
Replace "A reply may cite lines of the repository at a commit or pages the agent
|
||||
fetched, and the broker checks every citation before it stores the message." with
|
||||
"A reply may cite lines of a Markdown file at a commit, a place in a committed
|
||||
DOCX story file, or pages the agent fetched, and the broker checks every citation
|
||||
before it stores the message. A DOCX citation never calls a paragraph a line. It
|
||||
names the document id, the committed version and a DOCX locator. A citation that
|
||||
names an archived file as current is refused. The `requireCitation` setting below
|
||||
applies to Markdown and DOCX citations alike, and so does the rule for a citation
|
||||
marked as canon."
|
||||
|
||||
Add, after "the broker refuses such a write before any git call.": "The writing
|
||||
store and the archive folder are read-only to a creative agent as well, and the
|
||||
broker's path check treats the archive folder as story. No agent creates, edits,
|
||||
deletes, converts, archives or restores a story file in any format, and no agent
|
||||
writes a DOCX. No creative agent starts or undoes a conversion, and none asks for
|
||||
one through a tool or a decision it raises, as REQ-LIB-8 says. An agent may still
|
||||
suggest a conversion to the human in chat. Each creative role's contract states
|
||||
which files are DOCX and which are Markdown, and cites a DOCX story file by its
|
||||
locator, never by line."
|
||||
|
||||
Version tag, inside the closing parenthesis: "Version 1.15.)" becomes "Version
|
||||
1.15. Version 1.17.)"
|
||||
|
||||
### New REQ-LIB-8
|
||||
|
||||
This bullet goes after REQ-LIB-7 and before the "### Installer" heading.
|
||||
|
||||
"- **REQ-LIB-8.** Story files become DOCX. DOCX is the standard for chapters and
|
||||
the other story text in a Case 2 writing project, canon and playground alike. A
|
||||
story file becomes DOCX through a human's import (below), or when the human
|
||||
starts it in the editor. From then on DOCX is its only working format, edited in
|
||||
the Console's editor (REQ-LIB-9). A Markdown story file stays Markdown until its
|
||||
import. Files in a type folder (REQ-LIB-1), such as character and place notes,
|
||||
stay Markdown. So do supporting files, agent files, instructions, PRDs and
|
||||
documentation, including the character sheets, summaries and cross-links agents
|
||||
keep under REQ-LIB-6.
|
||||
- A converted story file's current text leaves the user's Gitea-mirrored
|
||||
repository. Its DOCX lives in the stack's writing store, on the stack's host,
|
||||
and not in the repository the user pushes or opens in Obsidian. Only its
|
||||
archived Markdown stays there.
|
||||
- An import converts one Markdown story file, canon or playground, outside every
|
||||
type folder (REQ-LIB-1), to a DOCX document in the writing store. It refuses
|
||||
an unmapped, supporting, archived or type-folder file, and a file that is
|
||||
already converted. Each import has its own fidelity report, the human's
|
||||
confirmation of that report, a manifest naming the source file and its commit,
|
||||
and one commit in the store. The DOCX keeps every wiki link of the source as
|
||||
written, and a lost link to another library file fails the fidelity report. A
|
||||
web link becomes plain text that keeps its label and address, and the fidelity
|
||||
report lists it for the human to confirm. A web link that can't be kept safely
|
||||
as plain text refuses the import.
|
||||
- The human can also start a new story file in the editor, in a universe and
|
||||
book or a playground the human picks. It is a DOCX document in the writing
|
||||
store from its first commit, with no Markdown copy, and it opens under the
|
||||
same gate as the import.
|
||||
- The same import marks the Markdown file converted, naming the document, and
|
||||
moves it to the archive folder under its old path (REQ-LIB-1, REQ-LIB-2).
|
||||
The mark and the move are one commit authored by the human in the user's
|
||||
repository. A new human-only bus step (Vikunja 571) on the C2-02 write
|
||||
plumbing writes it, and C2-03b pushes it. The move keeps the bytes and the
|
||||
git history, so it isn't `data.delete`.
|
||||
- The store commit comes first, and the writing store's remote must confirm it
|
||||
holds that commit. Then come the archive commit and its push, then publication
|
||||
to the lookups. Until publication the Markdown at its old path keeps its canon
|
||||
or playground scope. Agents read that file at the commit the import started
|
||||
from, even after the mirror's served commit reaches the archive commit or a
|
||||
later one. No lookup returns the file's archived copy before publication, even
|
||||
one that asks for archived files. A refusal or failure before the archive push
|
||||
leaves the Markdown unmoved and no DOCX that a lookup returns. A failure after
|
||||
the archive push leaves a pending import that the human sees and can resume,
|
||||
never a claim that nothing moved. An archive push whose result is unknown
|
||||
counts as a failure after the archive push until the stack reads the result
|
||||
back from the user's remote.
|
||||
- Each stage of an import writes a logged event. The import's record on the
|
||||
stack's host names the source file's path, commit and blob and the new
|
||||
document id, and each event points to that record.
|
||||
- After a conversion, a Markdown file at the converted file's old path is neither
|
||||
canon nor playground. Lookups leave it out, the format map flags it, and the human is told.
|
||||
- A new Markdown story file in a book folder that holds converted files stays
|
||||
canon until its import, unless it sits at a converted file's old path or under
|
||||
a playground prefix. The Console flags it and offers the import. Nothing
|
||||
converts it on its own.
|
||||
- A hybrid's exclusion that covers a converted file's old path also covers its
|
||||
DOCX document (REQ-LIB-3). An exclusion of a whole book folder also covers
|
||||
every DOCX document in that book, including one started in the editor. An
|
||||
exclusion can also name a DOCX document by its id.
|
||||
- Only a human starts an import or a new story file, from the CLI or a Console
|
||||
session signed in as that human. Both are human verbs outside the agent action
|
||||
vocabulary. Neither is added to `ACTIONS` in the business vocabulary or to the
|
||||
broker's always-gated list, including the import's action, `library.convert`.
|
||||
No role holds them, no agent can request them, and the hard-wait list in
|
||||
REQ-WAIT-1 doesn't change. In an organization with several humans, who may
|
||||
start them is a setting only a human changes, and the default is the owner
|
||||
only.
|
||||
- Conversion is one way, with no parallel run. Neither the Console nor any agent
|
||||
edits a converted story file as Markdown, and no DOCX edit syncs back to
|
||||
Markdown. The Console's Markdown save refuses every file under the archive
|
||||
folder. The user's own tools can still change the archived Markdown, and no
|
||||
such change reaches the DOCX. The old Markdown stays in git history, and
|
||||
nothing rewrites history. A promotion into a book folder (C2-19) runs the
|
||||
import for a Markdown story file or is refused. A promotion of a playground
|
||||
DOCX is refused in version 1.17.
|
||||
- Agents get a format map: for each mapped folder and story file, its format,
|
||||
scope and where it lives, archived files, flagged files and pending imports
|
||||
included.
|
||||
- The import and new story files ship closed. They open only after four things.
|
||||
The editor ships. The human has set the writing store's remote, and the stack
|
||||
has checked that it can push there. Agents can read committed DOCX story files
|
||||
through the lookups and cite them (REQ-LIB-5, REQ-LIB-6). Any earlier agent
|
||||
system that writes the user's files has stopped, which for Case 2 is the
|
||||
Hermes retirement (#423 step 5). So the import itself never cuts agents off
|
||||
from a chapter, the chapter's current text never lives only on the stack's
|
||||
host, and no second system writes the story. The first import of a real user's
|
||||
books waits for Jason's go.
|
||||
(Q235, Q236, Q237: P1 to P6. Q222 for the setting. O2 design D10 to D12.
|
||||
Version 1.17.)"
|
||||
|
||||
### New REQ-LIB-9
|
||||
|
||||
This bullet goes after REQ-LIB-8.
|
||||
|
||||
"- **REQ-LIB-9.** The Console's DOCX editor. The human edits a DOCX story file in
|
||||
the Console's editor, in a split pane with the agent chat alongside it. The
|
||||
human can pin an agent chat to a document. The human can select a word,
|
||||
right-click it, and choose a synonym from a thesaurus. Only a human saves. Saves
|
||||
happen on their own while the human types (Q226), at intervals only a human
|
||||
changes, and each accepted save is one commit with the human as author, pushed
|
||||
as REQ-LIB-4 says. A save made against an older version than the current one is
|
||||
refused. Agents never write a DOCX. Versioning is the editor's tracked changes
|
||||
plus one git commit per save. The editor's code is vendored and pinned, with a
|
||||
hash per file and the licenses that its independent security review accepted. At
|
||||
runtime the editor loads only files that the stack itself serves, and it
|
||||
requests nothing from any other host. The editor's frame sends exactly the
|
||||
content security policy Jason approved in Q234, and the WebUI shell's own header
|
||||
is unchanged. REQ-PROJ-3's project docs editor is unchanged. (Q195, Q226, Q234,
|
||||
Q236. O2 design D3, D4, D6, D8, D10, D13, D14, D16 and D22. Version 1.17.)"
|
||||
|
||||
### Version 1.17 notes
|
||||
|
||||
Added after the 1.16 notes:
|
||||
|
||||
"- Version 1.17:
|
||||
- Jason confirmed the reading 'for now (subject to future change)' (Q237). The
|
||||
rulings stand until he revises them.
|
||||
- The 10-27 Case 2 release ships on Markdown as built (P6). The 1.11 line 'The
|
||||
Case 2 MVP for 10-27 is the Markdown library (#416). The WYSIWYG editor (Q195,
|
||||
Q196) is an add-on that doesn't gate it.' still holds. Conversion opens after
|
||||
10-27, and only once REQ-LIB-8's four conditions are met. The editor ships,
|
||||
the writing store's remote is set and the stack has checked that it can push
|
||||
there, agents can read and cite committed DOCX story files through the
|
||||
lookups, and Hermes is retired, which is #423 step 5.
|
||||
- The 1.12 note says five bus write and Console editor rows, C2-02, C2-03b,
|
||||
C2-11a, C2-11b and C2-19, don't gate 10-27. Jason's Q226 F42 later moved
|
||||
C2-02, C2-03b and C2-11b into the 10-27 slice. The Case 2 design brings C2-11a
|
||||
in with them, because C2-11b needs it. C2-19 stays outside the slice. Mos's
|
||||
reading of F42 is that C2-02 and C2-03b now gate 10-27, because design 9.2
|
||||
puts them in 'the smallest slice that lets her start'. C2-11a and C2-11b are
|
||||
slice rows that still don't gate 10-27, because the editor is the Q195 add-on.
|
||||
The DOCX change in 1.17 leaves this slice as it is, and C2-11b's Markdown
|
||||
editor never edits a converted story file.
|
||||
- Q236 narrows Q176's editor choice to DOCX for story files (REQ-LIB-9). The
|
||||
1.12 note that the Case 2 design keeps Obsidian now holds only for Markdown
|
||||
files, including story files not yet imported. A DOCX story file is edited
|
||||
only in the Console's editor under REQ-LIB-9.
|
||||
- This replaces two parts of the O2 design at 25c09f19f. The superseded-notice
|
||||
proposal from Fenton's F1 in D12 is withdrawn, and the
|
||||
keep-Markdown-at-its-path model of D11 is replaced. Gyro's rewrite of the O2
|
||||
design for Vikunja 571, landed on `refactor` at b99ca86b9, carries the
|
||||
details, and where it differs from this text, this text holds:
|
||||
- the import and archive steps, the mark, and the new bus step;
|
||||
- links from supporting files to a moved chapter;
|
||||
- the DOCX reader;
|
||||
- the user's unpushed or later changes to an archived file or at its old path,
|
||||
and pushing and closing the file in Obsidian before each import;
|
||||
- the writing store's remote in W4 section 7.3, which replaces the no-remote
|
||||
and no-backup rule that W4 section 7.4 held before the rewrite, at
|
||||
062646487;
|
||||
- import events counted by a bus restore, and recovery when a bus restore or a
|
||||
restore of the whole host predates an import;
|
||||
- manifest order for the manuscript lookup and its next chapter;
|
||||
- `library.convert` kept out of `ACTIONS`, out of the hard-wait note and out
|
||||
of the pinned `GATED_ONLY` list in packages/business/src/vocabulary.mjs.
|
||||
- Agents read a converted story file through the Case 2 lookups under the same
|
||||
project and universe access as Markdown canon, in any thread or room. W4's
|
||||
earlier O2-only conversations, per-document grants and per-send sharing don't
|
||||
gate these lookups. The 571 rewrite replaced them in W4 sections 7.1 and 7.2.
|
||||
The creative tool list in REQ-LIB-6 doesn't change.
|
||||
- The six creative role contracts row 213 landed say 'Every item cites the
|
||||
lines of the user's material it is about'. A build row under 571 changes that
|
||||
line for DOCX story files (REQ-LIB-6).
|
||||
- A new Markdown file in a book folder is still canon in 1.17. The 1.17
|
||||
exception is a file at a converted file's old path, which REQ-LIB-2 makes
|
||||
unmapped.
|
||||
- Story files convert whether canon or playground. Type-folder and supporting
|
||||
files stay Markdown.
|
||||
- Measured 2026-10-23 at about 01:21Z: the Case 2 user's library repository
|
||||
tracks 213 files, 203 of them Markdown, and none is a .docx or .doc. It has no
|
||||
untracked files. The user's .doc files (Q196) are outside it. So the landed
|
||||
code has no DOCX there to report as unreadable at 10-27.
|
||||
- Text replaced in 1.17, kept here as in earlier notes:
|
||||
- Creative library lead, the 1.16 sentence: "The library is the user's own
|
||||
writing repository."
|
||||
- REQ-LIB-2, the 1.16 sentences: "A file's scope comes from its path alone,
|
||||
prefixes first: a Markdown file under a supporting prefix is supporting,
|
||||
one under a playground prefix at any level is playground, any other
|
||||
Markdown file in a book folder is canon, and everything else is unmapped
|
||||
and never returned. A folder in both lists is supporting." "Lookups return
|
||||
canon unless the caller asks for playground or supporting." "so an agent
|
||||
can't cite a playground or supporting file as canon" "The Console shows
|
||||
each cite's scope."
|
||||
- REQ-LIB-4, the 1.16 sentences: "The user commits and pushes with their own
|
||||
tools, today Obsidian with obsidian-git." "Agents read only the mirror's
|
||||
served commit and never hold a copy of their own, so they see the user's
|
||||
push within about a minute."
|
||||
- REQ-LIB-5, the 1.16 sentence: "The user sees them in Obsidian's graph and
|
||||
backlinks." A converted chapter's connections show in Obsidian only as they
|
||||
stood at conversion, until a Console view of their links exists. Q226
|
||||
records that view as a later option.
|
||||
- REQ-LIB-6, the 1.16 sentence: "A reply may cite lines of the repository at
|
||||
a commit or pages the agent fetched, and the broker checks every citation
|
||||
before it stores the message."
|
||||
- REQ-LIB-1 only gains sentences.
|
||||
- REQ-LIB-3's text and REQ-LIB-7 are unchanged. REQ-LIB-8 adds one form of
|
||||
exclusion REQ-LIB-3 doesn't have: naming a DOCX document by its id. A path
|
||||
exclusion also covers DOCX documents, through a converted file's old path or a
|
||||
whole book folder. An archived file and a DOCX document keep a universe, and a
|
||||
book where their source has one, so every lookup still names one universe.
|
||||
REQ-PROJ-3, the Console editor for a project's spec and docs, is unchanged.
|
||||
C2-11b opens the library's Markdown files in that same editor.
|
||||
- In `packages/tasks/requirements.json` the titles of REQ-LIB-1, REQ-LIB-2 and
|
||||
REQ-LIB-4 change, and those of REQ-LIB-5 and REQ-LIB-6 stay. REQ-LIB-1 becomes
|
||||
'A human-set library maps the user's repository onto universes, books, types,
|
||||
playgrounds, supporting and archive folders'. REQ-LIB-2 becomes 'Scope comes
|
||||
from the path or a DOCX's source, lookups default to canon, and only canon
|
||||
files are ever cited as canon'. REQ-LIB-4 becomes 'Git holds every change. The
|
||||
bus fast-forwards a read-only mirror every 60 seconds, the one place agents
|
||||
read Markdown'. REQ-LIB-8 and REQ-LIB-9 add two ids, so the count goes from 85
|
||||
to 87. REQ-LIB-8 is titled 'Case 2 story files become DOCX by a human-only
|
||||
one-way import or in the editor; Markdown stays for notes and docs', and
|
||||
REQ-LIB-9 'The Console's DOCX editor: only a human saves, each save is one
|
||||
commit, and the editor code is vendored and pinned'.
|
||||
- Open points O1 to O4 of draft 1 were ruled by Mos in mos-1367 B1 to B4.
|
||||
- Sage files the build rows under Vikunja 570 and 571 after the rewrite is
|
||||
approved. This amendment adds the requirements only."
|
||||
|
||||
### Risks
|
||||
|
||||
The new lines go at the end of "## Risks and open questions", after the bullet
|
||||
that begins "Trusted mode lets an agent" and before the "## Milestones" heading.
|
||||
- "A DOCX file is a zip archive, so git can't show a line diff of a chapter.
|
||||
Review of a story change goes through the editor and the committed reader,
|
||||
not `git diff`. (Version 1.17.)"
|
||||
- "A converted chapter's DOCX is not in Obsidian's graph. Obsidian shows the
|
||||
chapter only through its archived Markdown, and no DOCX edit reaches that file's
|
||||
links. The DOCX's current links show only in the Console's editor until a
|
||||
Console view of them exists. (Version 1.17.)"
|
||||
- "Conversion can't be undone in place. Front matter, wiki link forms and
|
||||
unsupported blocks may not survive it, and the mirror can't refuse the user's
|
||||
push to an archived file. The controls are the fidelity report, the human's
|
||||
confirmation, an untouched original on refusal, and the kept archive.
|
||||
(Version 1.17.)"
|
||||
- "A restore of the stack's bus alone from a backup taken before an import drops
|
||||
the import's logged events, while the archive commit and the DOCX stay. A
|
||||
restore of the stack's whole host from such a backup also takes the writing
|
||||
store and the host's import records back to before the import. The archive
|
||||
commit stays in the user's Gitea repository and the DOCX stays on the writing
|
||||
store's remote, but the host keeps no record of the import. A bus restore counts
|
||||
import events, and the import's recovery covers both cases (Vikunja 571).
|
||||
(Version 1.17.)"
|
||||
|
||||
## d8 changes (Mos's d8 fix list, check wf_63bba879-180)
|
||||
|
||||
- R1: REQ-LIB-4's tail covers DOCX saves, which agents see at their next lookup after the editor reports them as committed. The notes quote the whole 1.16 sentence. Source: nit/jason/F10.
|
||||
- R2: REQ-LIB-4's new title says the mirror is "the one place agents read Markdown". Source: decisions.n14.
|
||||
- R3: J-b asks whether 1.17 requires the Console Connections view, with Mos's recommendation of yes. Sources: mos1372/F9, jason/F2, anchors/F5.
|
||||
- R4: C2-02 and C2-03b gate 10-27, C2-11a and C2-11b are slice rows that don't, and "F42 wins" is gone. New J-k. Sources: anchors/F6, jason/F5.
|
||||
- R5: where the landed 571 design differs, this text holds. Sources: anchors/F8, adversarial/F8 item 1, jason/F8, critic/C6, G16's design follow-on.
|
||||
- R6: `library.convert` stays out of `ACTIONS`, and REQ-WAIT-1's hard-wait list doesn't change. Sources: gyro571/F1, adversarial/F8 item 3, critic/C6.
|
||||
- R7: a repository on the stack's host never counts as the writing store's remote, and Item 2 says so. Sources: mos1372/F2, adversarial/F1, gyro571/F2.
|
||||
- R8: "no agent credential reaches it" becomes "no agent can reach it with any credential". Source: Fenton's B1 under mos-1379.
|
||||
- R9: no playground DOCX becomes canon in 1.17, and a promotion of one is refused. New J-i. Sources: mos1372/F8, adversarial/F2.
|
||||
- R10: REQ-LIB-9 restates Q195's pin and right-click thesaurus, and its cite gains D4 and D22. Source: critic/C4.
|
||||
- R11: REQ-LIB-9's license bar is the licenses its independent security review accepted. New J-l. Source: critic/C3.
|
||||
- R12: no text for D16's narrow-window tabs. Mos records that reading in STATE. Sources: jason/F1, second verifier vote.
|
||||
- R13: no lookup returns a file's archived copy before publication. Source: adversarial/F5 part 3.
|
||||
- R14: a restore of the whole host from a backup taken before an import is a fourth recovery case. Source: adversarial/F6.
|
||||
- M1: REQ-LIB-4's writing-store sentences follow the later bus rows sentence and add the off-host remote rules and DOCX read timing. The note quotes both 1.16 sentences. Sources: mos1372/F2, adversarial/F1, gyro571/F2, anchors/F4, nit/jason/F10, adversarial/F5 part 1, Fenton B1.
|
||||
- M2: REQ-LIB-8's order bullet adds the remote's confirmation, the old-path read rule and "archive push". Sources: mos1372/F2 and the other remote findings, mos1372/F7, adversarial/F5 parts 2 to 4.
|
||||
- M3: REQ-LIB-8's gate bullet has four conditions, adding the remote. Sources: the remote findings, critic/C1 and C2 edit 2.
|
||||
- M4: the 1.17 note on when conversion opens restates the four conditions and drops W4b. Sources: jason/F5, the remote findings.
|
||||
- M5: Item 2 rewritten with the remote rules. Sources: the remote findings.
|
||||
- M6: the 1.12 gating note rewritten. Sources: anchors/F6, jason/F5.
|
||||
- M7: REQ-LIB-9 gains the split pane, the pin, the thesaurus, the license bar and the runtime load rule, and cites more of the design. Sources: jason/F1, critic/C4, critic/C3, nit/anchors/F12, nit/gyro571/F9.
|
||||
- M8: REQ-LIB-2, the one-way bullet and the C1 note keep a playground DOCX out of canon in 1.17. Sources: mos1372/F8, adversarial/F2.
|
||||
- M9: REQ-LIB-5 says what the reader can't read, and the import turns web links into plain text. New J-j. Sources: critic/C1, critic/C2.
|
||||
- M10: an exclusion of a whole book folder covers that book's DOCX documents. Source: adversarial/F3, with B5.
|
||||
- M11: the human-verbs sentence and the 571 list item name `library.convert` and REQ-WAIT-1. Sources: gyro571/F1, adversarial/F8 item 3, critic/C6.
|
||||
- M12: J-b rewritten. Sources: mos1372/F9, jason/F2, anchors/F5, gyro571/F3, critic/C6.
|
||||
- M13: J-d puts the agent read model to Jason as his call. Source: mos1372/F1.
|
||||
- M14: Status moves to version 1.17 and names REQ-LIB-9. Sources: anchors/F2, mos1372/F4, critic/C7.
|
||||
- M15: the 1.17 source line quotes Q235 in full (mos-1446) and the REQ-LIB-9 sources, at b99ca86b9. Sources: critic/C5, anchors/F9, jason/F8 pin part, nit/gyro571/F8, critic/C6 pin part.
|
||||
- M16: the 571 list lead names the two replaced design parts and the landed design. Sources: anchors/F9, anchors/F8, adversarial/F8 item 1, jason/F8, critic/C6.
|
||||
- Landing swap: Gyro's docs round for R5, R6 and R14 landed as b99ca86b9 (Fenton fenton-0015), so M15 and M16 cite b99ca86b9 in place of 767bb62c7, as the d8 fix list's header says. J-b keeps 767bb62c7 for its line numbers.
|
||||
- M17: W4 section numbers in the remote item and the lookups note. Sources: gyro571/F5, nit/adversarial/F11.
|
||||
- M18: the D7 landing line. Sources: gyro571/F5, the remote findings.
|
||||
- M19: a logged event for each import stage, in REQ-LIB-8 and A2. Source: gyro571/F7.
|
||||
- M20: the Risks line on restores adds the whole-host case. Sources: adversarial/F6, gyro571/F7.
|
||||
- M21: the 571 restore item adds the whole-host case. Source: adversarial/F6.
|
||||
- M22: the graph Risks line and the REQ-LIB-5 note name a Console view. Sources: anchors/F5, jason/F2.
|
||||
- M23: the d6 B3 line drops "F42 wins". Source: anchors/F6.
|
||||
- M24: new Markdown in a converted book, with the old-path and playground exceptions. Source: mos1372/F6.
|
||||
- M25: J-c's last sentence is Mos's reading. Source: mos1372/F10.
|
||||
- M26: J-h rewritten with the D13 defaults. Source: jason/F3.
|
||||
- M27: Item 4 rewritten. Sources: jason/F4, gyro571/F4, decisions.unmarked_folders.
|
||||
- M28: the "Not taken" note on unmarked folders. Sources: jason/F4, gyro571/F4, decisions.unmarked_folders.
|
||||
- M29: the import refuses an unmapped file. Sources: gyro571/F4, decisions.unmarked_folders.
|
||||
- M30: placement notes for REQ-LIB-8, REQ-LIB-9 and the Risks lines. Sources: anchors/F3, nit/anchors/F14.
|
||||
- M31: REQ-LIB-4's title and the D11 landing line. Source: decisions.n14.
|
||||
- M32: the Q236 note limits the 1.12 Obsidian note to Markdown files. Source: critic/C8.
|
||||
- M33: the exclusion note adds path exclusions of DOCX documents. Source: anchors/F7.
|
||||
- M34: A9 moves from "Mine" to the impact-map list. Source: decisions.exclusion_by_id.
|
||||
- M35: the D5 landing line. Sources: anchors/F10, mos1372/F5, nit/adversarial/F12.
|
||||
- M36: the D9 and D14 landing lines. Source: mos1372/F1.
|
||||
- M37: the O1 and C3 notes add the off-host remote and its confirmation. Sources: the remote findings.
|
||||
- M38: the REQ-PROJ-3 note. Source: nit/anchors/F11.
|
||||
- M39: REQ-LIB-5's freshness sentence keys on the editor reporting a save as committed. Source: nit/adversarial/F9.
|
||||
- M40: REQ-LIB-6 bars agent requests for a conversion and allows a suggestion in chat. Source: nit/jason/F9.
|
||||
- M41: REQ-LIB-2's opening and old-path rule, and the "Mine" note. Source: nit/adversarial/F10.
|
||||
- M42: REQ-LIB-8's lead sentence on Markdown story files. Source: nit/mos1372/F12.
|
||||
- M43: REQ-LIB-6's citation sentence. Source: nit/anchors/F13.
|
||||
- M44: REQ-LIB-5's version tag uses plain quotes. Source: nit/anchors/F15.
|
||||
- M45: reference codes B5, D12, D10 and B4 name their source. Source: nit/mos1372/F11.
|
||||
- Files the row would own: four files, with the id count going to 87. Sources: anchors/F1, adversarial/F4, decisions.n14 F4.
|
||||
|
||||
## d7 changes (mos-1379, Fenton's 571 B2)
|
||||
|
||||
- REQ-LIB-5 gains Mos's three sentences after the extracted-text sentence. The text agents read has tracked changes accepted. Comments, and text with tracked deletions shown, each wait for a setting only a human turns on, off by default. Its version tag cites Q222 for the two settings, which is the only text here beyond Mos's.
|
||||
- J-d gains Mos's sentence on comments and tracked deletions.
|
||||
- Fenton's B1 is design only. d6's "no agent credential reaches it" holds, and the shared helper stays #431.
|
||||
|
||||
## d6 changes (Webby round 3, webby-0207)
|
||||
|
||||
- B1: titles for REQ-LIB-8 and REQ-LIB-9 in the requirements.json note and the file list. REQ-LIB-9 takes Webby's title. REQ-LIB-8 keeps mine, because Webby's ("...one-way import that archives the Markdown under its old path") leaves out D2's story files started in the editor.
|
||||
- B2: REQ-LIB-2 makes a Markdown file at a converted file's old path unmapped, ahead of the playground and canon rules, so a playground conversion is covered too. REQ-LIB-8's old-path bullet says "neither canon nor playground".
|
||||
- B3: the slice note names Q226 F42's three rows (C2-02, C2-03b, C2-11b). C2-11a joined the slice with C2-11b, because C2-11b needs it. Both are slice rows that don't gate 10-27, as design 9.2, case2-rows.md:1655 and the impact map's A57 say. The 'F42 wins' sentence is gone from the note in d8, so no queue change follows from it.
|
||||
- N10: the cite-scope replacement is scoped to REQ-LIB-2. The 1.14 note's copy stays.
|
||||
- N11: the notes say REQ-LIB-8 adds the by-id exclusion form REQ-LIB-3 doesn't have.
|
||||
- N12: J-h marks the autosave intervals for Jason.
|
||||
- N13: J-b names REQ-LIB-5's "Connections are always visible" tag and Q226's "another way to handle the visual brain map".
|
||||
- N14, not taken: REQ-LIB-4's title stays internal F3's text, as mos-1372 D11 ruled. Webby's alternative, "...and agents read Markdown from it alone" (120 chars), is Mos's call.
|
||||
|
||||
## Where each mos-1372 item landed
|
||||
|
||||
- D1: REQ-LIB-2's DOCX sentences, the REQ-LIB-8 lead and import bullet, C1 above, and the notes. "Playground files stay Markdown in 1.17" is gone.
|
||||
- D2: REQ-LIB-2's "for one the human starts in the editor" and the REQ-LIB-8 new-story-file bullet. The universe-and-book note is now true.
|
||||
- D3: the REQ-LIB-8 order bullet. This also settles N8.
|
||||
- D4: REQ-LIB-8's one-way bullet, which carries the archive refusal and consequence F2's text, and the 571 list item.
|
||||
- D5: it landed in REQ-LIB-2's "a Markdown file at a converted file's old path is unmapped" in Webby's B2 form, and in the REQ-LIB-8 old-path bullet.
|
||||
- D6: REQ-LIB-5's freshness sentences.
|
||||
- D7 and mos-1375: REQ-LIB-4's remote sentences, the remote step in REQ-LIB-8's order bullet, the fourth opening condition and the 1.17 note on when conversion opens, plus the 571 list item on W4 section 7.3, which replaces the no-remote rule of the old W4 section 7.4. The impact map's item 2 adds "only the stack's host pushes, and no agent credential reaches it".
|
||||
- D8: the REQ-LIB-5 replacement, the import bullet's wiki-link sentence, the Risks line, the note quoting the old sentence, and "REQ-LIB-1 only gains sentences".
|
||||
- D9: the 1.17 note that agents read a converted story file through the Case 2 lookups, from the d4 check's consistency B2, and J-d, which puts the read model to Jason as his call.
|
||||
- D10: the d4 check's B5 sentence, in REQ-LIB-8.
|
||||
- D11: internal F3's clause in the REQ-LIB-4 section, and in the notes the title as Mos amended it for N14.
|
||||
- D12: the whole 1.16 sentence replaced, and quoted in full in the notes.
|
||||
- D13: consequence F1's text, in REQ-LIB-4 and REQ-LIB-8's first bullet.
|
||||
- D14: the row text from the d4 check's consistency B1, in the mark-and-move bullet and C3 above.
|
||||
|
||||
## What d5 adds beyond mos-1372 (for Webby and Mos to check)
|
||||
|
||||
From the impact map, folded as mos-1368 item 1 asked:
|
||||
- A1: the Console shows a cite's format and converted state (REQ-LIB-2).
|
||||
- A2: the logged events and the several-humans setting, default owner only, under Q222.
|
||||
- A3: the import is outside the vocabulary, the hard-wait list is unchanged, and the archive move isn't `data.delete`. The C6 code site is in the 571 list.
|
||||
- A4: the section lead and REQ-LIB-1's writing-store sentence.
|
||||
- A6: format and location in every answer, extracted text with DOCX locators, links across formats.
|
||||
- A7: read-only extends to the store and the archive, no agent action on a story file in any format, no agent DOCX write, and role contracts state the formats. C1's role contract line is a 571 build item.
|
||||
- A8: the DOCX cite shape, and a cite naming an archived file as current is refused.
|
||||
- A9: a hybrid sees a source's DOCX canon through REQ-LIB-2's source scope, and REQ-LIB-8 lets an exclusion name a DOCX document by its id. The id form lets the human exclude a single DOCX started in the editor, which has no old path. An archived file isn't canon, so no hybrid sees it.
|
||||
- A10: REQ-LIB-9, so the count is 87, not 86.
|
||||
- A11: the Hermes gate, from item 3.
|
||||
- A12: the C2-19 promotion sentence.
|
||||
- A13: the broker's path check treats the archive folder as story.
|
||||
- A14 and C5: two Risks lines.
|
||||
- A15: the flagged-new-file bullet. It sits beside the note from the d4 check's B4, "a new Markdown file in a book folder is still canon". The file stays canon and is offered for import.
|
||||
- A16: three titles change and two stay.
|
||||
- C2: manifest order for the manuscript lookup.
|
||||
- F3: the C2-02 gating note.
|
||||
- F4: Mos's measurement.
|
||||
|
||||
Mine, beyond either source:
|
||||
- REQ-LIB-2 opens "A Markdown file's scope comes from its path and from past imports". DOCX scope comes from the source, so "a file's" would be false.
|
||||
- REQ-LIB-9's autosave sentence. It is Mos's reading of F41's "instantly committed", with human-set intervals. The exact intervals stay in the design.
|
||||
|
||||
Not taken:
|
||||
- Impact map item 4's "files in unmarked folders are eligible one at a time", for folders outside every book folder and playground only. Their files are unmapped under REQ-LIB-2, so a DOCX made from one would have no universe or book to keep. The human maps such a folder into a book folder or a playground first, under REQ-LIB-1. A file in an unmarked folder inside a book folder or a playground is canon or playground, and the import takes it one file at a time.
|
||||
- A6's named chapter-links view. D8 takes B8's "no graph shows them yet", and J-b puts the view to Jason.
|
||||
|
||||
## Marked for Jason (additions in the text he sees)
|
||||
|
||||
- J-a. Whether "We will not run the systems in parallel" applies per chapter or as one switch per book. His call. As written, it applies per chapter.
|
||||
- J-b. A converted chapter's DOCX is not in Obsidian's graph. Obsidian shows the chapter only through its archived Markdown, and no DOCX edit reaches that file's links. REQ-LIB-5 still cites two TARGET lines, "Visualization of data via Obsidian or equivalent is required" and "Connections are always visible". Mos's reading is that 1.17 as written meets neither line for converted chapters. Their current links show to the user only as clickable text inside the editor, and no graph or list shows them. Q226 leaves room for "another way to handle the visual brain map". The O2 design that landed for Vikunja 571 at 767bb62c7 holds a Console Connections view and its tests as later work that waits for this answer. His call whether 1.17 requires that view. As written, it does not. Mos recommends that it does, because impact map A6 says "Recommended: name the view" and TARGET says "Connections are always visible".
|
||||
- J-c (mos-1374): "The store pushes to a remote (D7), and the Case 2 VM is in the nightly PBS backup. For a fixed restore point before her first real import, a manual backup of VM 1012 with PBS protect runs first. That step is reversible and needs no reboot." Mos's reading is that this sits inside the P5 go step, so it needs no new question.
|
||||
- J-d. How agents read a converted chapter, D9. His call. As written, an agent reads it through the ordinary Case 2 lookups in any thread or room, under the same project and universe access as Markdown canon. This is Mos's reading of F41 and REQ-LIB-7. The other choice is the model in the earlier O2 design, W4 sections 7.1 and 7.2. Under it, an agent reads a converted chapter only in a new writing conversation that holds one human and one agent, only after the business owner grants that agent access to that document, and by default only what the human attaches to each message. Comments and tracked deletions stay hidden from agents until a human turns on a setting for each. Markdown has neither, so the default keeps agents at the access Markdown gives them. Those two sentences are Mos's ruling mos-1379. A yes to the text as written settles it.
|
||||
- J-e. Versioning means the editor's tracked changes plus one git commit per save.
|
||||
- J-f. A converted chapter can be edited only in the Console while the stack is reachable. Obsidian no longer edits it offline.
|
||||
- J-g. The import takes Markdown only. Importing .doc files stays a separate task.
|
||||
- J-h. Autosave under REQ-LIB-9. Jason's Q226 F41 says "All changes are instantly committed and available to the agents." Mos's reading, recorded with that ruling, is "Each save is a commit at once, and agents read it at their next lookup." At the O2 design's D13 defaults, a save commits at most 2 seconds after a pause in typing or 15 seconds into steady typing, with no human action needed. Mos's reading in mos-1359 counts that delay as "instantly committed". Only a human changes the intervals. The PRD names no values, and the defaults stay in the design. Whether a delay of seconds fits his word "instantly" is his call, so this is marked for him.
|
||||
- J-i. In 1.17 a playground story that is DOCX can't become canon. That covers a playground Markdown file the human imports, a story the human starts in a playground in the editor, and the DOCX of a draft universe after it is made real. Today Melanie promotes a playground file by moving it into a book folder in Obsidian, and a DOCX in the writing store can't be moved that way. She can start a new story in the book and paste the text in, but it gets a new document id and the playground copy keeps the history. A later version adds DOCX promotion with C2-19, which comes after the slice.
|
||||
- J-j. Web links, text boxes and fields in a converted chapter. Jason's Q226 F41 says "All changes are instantly committed and available to the agents." The reader in this version can't read a web link, a text box, a field or hidden text. As written, the import turns each web link into plain text that keeps its label and address, and the human confirms that in the fidelity report. A web link that can't be kept safely as plain text refuses the import. If a later edit adds one of these, the save keeps it and the editor warns the human. Agents then get none of that part's text until a save takes it out.
|
||||
- J-k. The 1.12 note Jason approved says bus writes, C2-02 and C2-03b, don't gate 10-27. His Q226 "F42: ok" moved them into the 10-27 slice, and design 9.2 counts them in the smallest slice that lets Melanie start. Mos reads that as making them gate 10-27. The editor rows C2-11a and C2-11b still don't gate it, per his Q195 words "we need to get it moving as an add-on".
|
||||
- J-l. REQ-LIB-9 does not use REQ-PROJ-3's "permissive license" bar. It takes the licenses that the editor's independent security review accepted, because that review cleared fonts under the SIL Open Font License and the Ubuntu Font License, which not every reader counts as permissive. Mos's reading.
|
||||
|
||||
Marked as changes beyond P1 to P6, from the impact map (Q17, item 5):
|
||||
- A1 (archived scope and canon cites)
|
||||
- A2 (the conversion requirement, REQ-LIB-8)
|
||||
- A3 (outside the vocabulary, hard-wait list unchanged)
|
||||
- A6 (lookups across formats)
|
||||
- A7 (agent bounds)
|
||||
- A8 (DOCX cites)
|
||||
- Item 2 and mos-1375: each writing-store commit is pushed to a remote off the stack's host. Only a human sets it, it starts unset, and a repository on the stack's host doesn't count. Only the stack's host pushes to it, and no agent can reach it with any credential. Import and new story files stay closed until the remote is set and checked, and each import's archive push waits until the remote holds the DOCX commit. This keeps "Git is used for all changes -- local and remote" true for DOCX chapters.
|
||||
- Item 3 (the Hermes gate)
|
||||
- Item 4. Canon and playground story files convert one file at a time, including files in unmarked folders inside a book folder or a playground. Type-folder and supporting files stay Markdown. A story file outside every book folder and playground has no universe or book, so it converts only after a human maps its folder into a book folder or a playground. The mapping step is Mos's reading of P1 and Q30.
|
||||
|
||||
The F4 measurement goes with them.
|
||||
|
||||
## Files the row would own
|
||||
|
||||
The id count goes from 85 to 87. 1.16 owned three files because its count stayed at 85. Like 1.13, the last version that added ids, the row owns four files:
|
||||
- `docs/prd/mosaic-stack.md`
|
||||
- `packages/tasks/requirements.json`: the version 1.17, the two new ids REQ-LIB-8 and REQ-LIB-9 with their titles, and the changed titles of REQ-LIB-1, REQ-LIB-2 and REQ-LIB-4. The new ids go after REQ-LIB-7 and before REQ-INST-1, in the same order as the PRD's bullets.
|
||||
- `packages/tasks/tests/requirements.test.mjs`: the version pin 1.17, the id count of 87, and the duplicate-id message, which goes from `requirements[85].id: REQ-ROLE-1 twice` to `requirements[87].id: REQ-ROLE-1 twice`.
|
||||
- `packages/bus/tests/requirements.test.mjs`: the list length, which goes from 85 to 87.
|
||||
|
||||
The gate would match 1.16's:
|
||||
1. A reviewer approves the text.
|
||||
2. Jason says yes to the exact text through Mos.
|
||||
3. Both `requirements.test.mjs` files, in `packages/tasks/tests` and `packages/bus/tests`, and every `scripts/test-*.sh` that reads the PRD or `requirements.json` are green on Sage's gate.
|
||||
4. Mos gets the landing commit.
|
||||
@@ -0,0 +1,747 @@
|
||||
--- DRAFT-d7.md 2026-10-22 20:44:39.909606789 -0500
|
||||
+++ DRAFT.md 2026-10-23 06:16:54.370223847 -0500
|
||||
@@ -1,12 +1,12 @@
|
||||
# PRD 1.17 draft: Case 2 story files are DOCX (Q235 to Q237, P1 to P6)
|
||||
|
||||
-Sage, 2026-10-23, for mos-1366 item 1 and Vikunja 570. Draft 7. It is d6 plus Mos's ruling mos-1379 on Fenton's 571 B2 (see "d7 changes"). d6 is d5 with Webby's round 3 (webby-0207) fixed: B1 to B3 and N10 to N13, with N14 left for Mos (see "d6 changes"). d5 folds in:
|
||||
+Sage, 2026-10-23, for mos-1366 item 1 and Vikunja 570. Draft 8. It is d7 plus Mos's d8 fix list mos-1443 (see "d8 changes"). d7 is d6 plus Mos's ruling mos-1379 on Fenton's 571 B2 (see "d7 changes"). d6 is d5 with Webby's round 3 (webby-0207) fixed: B1 to B3 and N10 to N13, with N14 left for Mos (see "d6 changes"). d5 folds in:
|
||||
- Mos's d4 check ruling mos-1372 (D1 to D14; D15 was dropped).
|
||||
- mos-1374, which replaces J-c.
|
||||
- The impact map mos-1368 (jason-master-restart d183246, `projects/mosaic-mvp/case2-docx-impact-map.md`: A1 to A16, critic items C1 to C6, items 2 to 7).
|
||||
- Mos's F4 measurement.
|
||||
|
||||
-Review order (B5): Webby, then Mos, then Jason. Base: PRD 1.16, `docs/prd/mosaic-stack.md` blob a6db9b4a at 482e865ac.
|
||||
+Review order (mos-1367 B5): Webby, then Mos, then Jason. Base: PRD 1.16, `docs/prd/mosaic-stack.md` blob a6db9b4a at 482e865ac.
|
||||
1.16 lands first (row 291), and this text is a new version on top of it. Nothing here is built or queued. The impact map's queue changes (A24, A57 to A59) go through `scripts/mosaic queue` after 1.17 is approved (C9).
|
||||
|
||||
Sources, verbatim in Mos's DECISIONS:
|
||||
@@ -19,11 +19,12 @@
|
||||
|
||||
These stand as ruled. d5 changes only what mos-1372 changed.
|
||||
|
||||
-- **O1. Where a converted chapter lives.** The DOCX lives in the O2 writing store
|
||||
- (D12), and the archived Markdown stays in the user's repository under a
|
||||
+- **O1. Where a converted chapter lives.** The DOCX lives in the writing store of
|
||||
+ O2 design D12, and the archived Markdown stays in the user's repository under a
|
||||
human-marked archive folder. Ruled yes in B1, on the condition that the text
|
||||
says plainly that a converted chapter leaves the Gitea-mirrored repository and
|
||||
- lives in the stack's writing store. mos-1372 D7 adds a remote for the store.
|
||||
+ lives in the stack's writing store. mos-1372 D7 and mos-1375 add a remote for
|
||||
+ the store, off the stack's host.
|
||||
- **O2. The unit of conversion.** One story file per import, refusing a file
|
||||
that is already converted. Ruled yes in B2: each file gets its own fidelity
|
||||
report, human confirmation, manifest and commit.
|
||||
@@ -40,15 +41,16 @@
|
||||
universe, book, and canon or playground. A playground DOCX follows the
|
||||
playground rules and is never returned as canon. The import accepts canon and
|
||||
playground story files. Type-folder files and supporting files stay Markdown
|
||||
- (P4). This also settles the impact map's Q18 (item 4) without a narrowing for
|
||||
- Jason.
|
||||
+ (P4). This also settles the impact map's Q18, item 4. It leaves one narrowing
|
||||
+ for Jason, J-i. A playground DOCX can't become canon in 1.17.
|
||||
- **C2 stands.** The move keeps the file's path under the archive folder:
|
||||
`book1/ch3.md` goes to `<archive>/book1/ch3.md`. An archived file's universe
|
||||
and book are those of its old path.
|
||||
- **C3 is replaced by D3 and D14.** The archive commit is written by a new
|
||||
- human-only bus step (Vikunja 571) on the C2-02 write plumbing and pushed
|
||||
- through C2-03b. The order is the store commit, then the archive commit and its
|
||||
- push, then publication to the lookups.
|
||||
+ human-only bus step (Vikunja 571) on the C2-02 write plumbing and pushed through
|
||||
+ C2-03b. The order is the store commit and the remote's confirmation that it
|
||||
+ holds that commit, then the archive commit and its push, then publication to the
|
||||
+ lookups.
|
||||
- **N4 stands.** REQ-LIB-8 says "a real user's books". The PRD names no user
|
||||
anywhere in Case 2.
|
||||
|
||||
@@ -56,19 +58,33 @@
|
||||
|
||||
### Status and sources
|
||||
|
||||
-Status gains, after the 1.16 sentence: "Version 1.17 records Jason's rulings Q235
|
||||
-to Q237 of 2026-10-23: Case 2 story files are DOCX, Markdown stays for agent files
|
||||
-and documentation, and a human-started import converts a Markdown story file one
|
||||
-way. 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)."
|
||||
+In the Status bullet's first line, "- Status: approved, version 1.16." becomes
|
||||
+"- Status: approved, version 1.17." Status gains, after the two 1.16 sentences at
|
||||
+the end of the bullet: "Version 1.17 records Jason's rulings Q235 to Q237 of
|
||||
+2026-10-23: Case 2 story files are DOCX, Markdown stays for agent files and
|
||||
+documentation, and a human-started import converts a Markdown story file one way.
|
||||
+It also 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)."
|
||||
|
||||
New source line, above the 1.16 one: "Version 1.17 source: Jason's Q235, answering
|
||||
-Q36, his Q236 'For creative writing chapters, we need to utilize docx as the
|
||||
-standard. Markdown is acceptable for agent files, instructions, PRDs,
|
||||
-documentation, etc. The creative writing files should be docx for familiarity for
|
||||
-the user.', and his Q237 'Q38: It is correct for now (subject to future change)',
|
||||
-confirming Mos's reading P1 to P6. The storage follows the O2 design D10 and D12,
|
||||
-25c09f19f, as rewritten under Vikunja 571."
|
||||
+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
|
||||
+creating writing use case is to use docx to handle versioning and WYSIWYG. We
|
||||
+eliminate the potential for confusion based on the files. Agents must understand
|
||||
+which files are what format and where they live. The system must allow for import
|
||||
+and conversion. We will not run the systems in parallel. There will be an
|
||||
+irreversible conversion of the files.' His Q236 says: 'For creative writing
|
||||
+chapters, we need to utilize docx as the standard. Markdown is acceptable for
|
||||
+agent files, instructions, PRDs, documentation, etc. The creative writing files
|
||||
+should be docx for familiarity for the user.' His Q237 says 'Q38: It is correct
|
||||
+for now (subject to future change)', which confirms Mos's reading P1 to P6.
|
||||
+REQ-LIB-9 rests on his Q195 request for 'a full WYSIWYG editor within Mosaic
|
||||
+Stack', his Q226 answer 'F41: We want to move editing into the WYSIWYG in the
|
||||
+WebUI. All changes are instantly committed and available to the agents.' and his
|
||||
+Q234 answer 'Q37: yes' to the exact security header for the editor's frame. The
|
||||
+storage follows the O2 design D10 to D12 as rewritten under Vikunja 571, at
|
||||
+b99ca86b9 on `refactor`."
|
||||
|
||||
### Creative library, section lead
|
||||
|
||||
@@ -92,20 +108,22 @@
|
||||
is playground, any other Markdown file in a book folder is canon, and everything
|
||||
else is unmapped and never returned. A folder in both lists is supporting." with:
|
||||
|
||||
-"A Markdown file's scope comes from its path alone, prefixes first: a Markdown
|
||||
-file under the archive folder is archived, one under a supporting prefix is
|
||||
-supporting, a Markdown file at a converted file's old path is unmapped
|
||||
-(REQ-LIB-8), one under a playground prefix at any level is playground, any other
|
||||
-Markdown file in a book folder is canon, and everything else is unmapped and never
|
||||
-returned. A folder in
|
||||
-both lists is supporting. The archive folder follows the same placement rules and
|
||||
-may not overlap another prefix or a book folder. An archived file keeps the path
|
||||
-it had under the archive folder, and its universe and book are those of that old
|
||||
-path. A DOCX document in the writing store keeps its source's scope: the
|
||||
-universe, the book if there is one, and canon or playground. Its source is the
|
||||
-Markdown file it was converted from or, for one the human starts in the editor,
|
||||
-the universe and book or the playground the human picks (REQ-LIB-8). A playground
|
||||
-DOCX follows the playground rules and is never returned as canon."
|
||||
+"A Markdown file's scope comes from its path and from past imports, prefixes
|
||||
+first: a Markdown file under the archive folder is archived, one under a
|
||||
+supporting prefix is supporting, a Markdown file at a converted file's old path is
|
||||
+unmapped under REQ-LIB-8 but flagged in the format map, one under a playground
|
||||
+prefix at any level is playground, any other Markdown file in a book folder is
|
||||
+canon, and everything else is unmapped and never returned. A folder in both lists
|
||||
+is supporting. The archive folder follows the same placement rules and may not
|
||||
+overlap another prefix or a book folder. An archived file keeps the path it had
|
||||
+under the archive folder, and its universe and book are those of that old path. A
|
||||
+DOCX document in the writing store keeps its source's scope: the universe, the
|
||||
+book if there is one, and canon or playground. Its source is the Markdown file it
|
||||
+was converted from or, for one the human starts in the editor, the universe and
|
||||
+book or the playground the human picks (REQ-LIB-8). A playground DOCX follows the
|
||||
+playground rules and is never returned as canon. It stays playground after its
|
||||
+draft universe is made real. In version 1.17 no promotion or mapping change makes
|
||||
+a playground DOCX canon. A later version of this PRD says how one becomes canon."
|
||||
|
||||
Replace "Lookups return canon unless the caller asks for playground or
|
||||
supporting." with "Lookups return canon unless the caller asks for playground,
|
||||
@@ -126,89 +144,110 @@
|
||||
Replace "The user commits and pushes with their own tools, today Obsidian with
|
||||
obsidian-git." with "The user commits and pushes Markdown with their own tools,
|
||||
today Obsidian with obsidian-git. A converted story file's current text is no
|
||||
-longer in that repository, which keeps only its archived Markdown (REQ-LIB-8). A
|
||||
-DOCX story file is saved in the Console's editor, and each accepted save is one
|
||||
-commit by the human in the stack's writing store (REQ-LIB-9). Each writing-store
|
||||
-commit is also pushed to a remote the human sets, never by force-push, and the
|
||||
-user sees a push that is pending or failed. Only the stack's host pushes to that
|
||||
-remote, and no agent credential reaches it. Agents read the local commit."
|
||||
-
|
||||
-Replace "Agents read only the mirror's served commit and never hold a copy of their
|
||||
-own" with "Agents read the user's repository only at the mirror's served commit,
|
||||
-read a DOCX story file only at a committed version in the writing store
|
||||
-(REQ-LIB-5), and never hold a copy of their own". The rest of that sentence, ", so
|
||||
-they see the user's push within about a minute.", is unchanged.
|
||||
+longer in that repository, which keeps only its archived Markdown (REQ-LIB-8)."
|
||||
|
||||
-Version tag: "and the sync items.)" becomes "and the sync items. Version 1.17.)"
|
||||
+Add, after "Bus writes and pushes come in later rows (C2-02, C2-03b).": "A DOCX
|
||||
+story file is saved in the Console's editor, and each accepted save is one commit
|
||||
+by the human in the stack's writing store (REQ-LIB-9). Each writing-store commit
|
||||
+is also pushed to a remote off the stack's host, never by force-push. The remote
|
||||
+is a setting only a human changes, and it starts unset. A repository on the
|
||||
+stack's host never counts as that remote. A missing remote or a failed push never
|
||||
+undoes a save, and the user sees each push that is pending or failed. Only the
|
||||
+stack's host pushes to that remote, and no agent can reach it with any credential.
|
||||
+Agents read the local commit."
|
||||
+
|
||||
+Replace "Agents read only the mirror's served commit and never hold a copy of
|
||||
+their own, so they see the user's push within about a minute." with "Agents read
|
||||
+the user's repository only at the mirror's served commit, except a story file
|
||||
+during its import under REQ-LIB-8. They read a DOCX story file only at a committed
|
||||
+version in the writing store, under REQ-LIB-5. They never hold a copy of their
|
||||
+own, so they see the user's Markdown push within about a minute and a DOCX save at
|
||||
+their next lookup after the editor reports it as committed."
|
||||
+
|
||||
+Version tag: "and the sync items.)" becomes "and the sync items. Version 1.17,
|
||||
+with Q222 for the remote setting.)"
|
||||
|
||||
### REQ-LIB-5
|
||||
|
||||
Add, after "Each answer names the commit it read.": "Each answer also names the
|
||||
file's format and where it lives: its path, or its document id in the writing
|
||||
store. A DOCX story file is read from its latest committed save, never from
|
||||
-unsaved text. A lookup that starts after a save is acknowledged returns that save
|
||||
-or a later one, and the answer names the version. Get, search and manuscript read
|
||||
-a DOCX story file as its extracted text with DOCX locators, never as lines. That
|
||||
+unsaved text. A lookup that starts after the editor reports a save as committed
|
||||
+returns that save or a later one, even while its push to the remote is pending or
|
||||
+has failed, and the answer names the version. Get, search and manuscript read a
|
||||
+DOCX story file as its extracted text with DOCX locators, never as lines. That
|
||||
text is the file with its tracked changes accepted. An agent reads a story file's
|
||||
comments, or its text with tracked deletions shown, only after a human turns on
|
||||
the matching setting. Both settings are off by default, and only a human changes
|
||||
-them. The manuscript lookup follows the book's chapter order across both formats (O2 design
|
||||
-D10), and the next chapter it names is never an archived file. Links out and in
|
||||
-resolve across both formats. The universe lookup returns the format map
|
||||
-(REQ-LIB-8)."
|
||||
+them. The reader in this version can't read some things a DOCX story file can
|
||||
+hold, such as a web link, a text box, a field or hidden text. The editor warns the
|
||||
+human when an edit adds one, and the human can still save it. Until a later save
|
||||
+takes it out, lookups give agents none of the text of the part that holds it,
|
||||
+whether that is the chapter's body, a note or a comment. The manuscript lookup
|
||||
+follows the book's chapter order across both formats (O2 design D10), and the next
|
||||
+chapter it names is never an archived file. Links out and in resolve across both
|
||||
+formats. The universe lookup returns the format map (REQ-LIB-8)."
|
||||
|
||||
Replace "The user sees them in Obsidian's graph and backlinks." with "The user sees
|
||||
a Markdown file's links in Obsidian's graph and backlinks. A DOCX story file's
|
||||
wikilinks stay clickable in the Console's editor (O2 design D11), and no graph
|
||||
shows them yet."
|
||||
|
||||
-Version tag: "and \"Connections are always visible\".)" becomes "and \"Connections
|
||||
-are always visible\". Version 1.17, with Q222 for the two settings.)"
|
||||
+Version tag: 'and "Connections are always visible".)' becomes 'and "Connections
|
||||
+are always visible". Version 1.17, with Q222 for the two settings.)'
|
||||
|
||||
### REQ-LIB-6
|
||||
|
||||
Replace "A reply may cite lines of the repository at a commit or pages the agent
|
||||
fetched, and the broker checks every citation before it stores the message." with
|
||||
-"A reply may cite lines of a Markdown file at a commit, a place in a committed DOCX
|
||||
-story file, or pages the agent fetched, and the broker checks every citation
|
||||
+"A reply may cite lines of a Markdown file at a commit, a place in a committed
|
||||
+DOCX story file, or pages the agent fetched, and the broker checks every citation
|
||||
before it stores the message. A DOCX citation never calls a paragraph a line. It
|
||||
names the document id, the committed version and a DOCX locator. A citation that
|
||||
-names an archived file as current is refused. `requireCitation` and a citation
|
||||
-marked as canon work the same way for both kinds."
|
||||
+names an archived file as current is refused. The `requireCitation` setting below
|
||||
+applies to Markdown and DOCX citations alike, and so does the rule for a citation
|
||||
+marked as canon."
|
||||
|
||||
Add, after "the broker refuses such a write before any git call.": "The writing
|
||||
store and the archive folder are read-only to a creative agent as well, and the
|
||||
broker's path check treats the archive folder as story. No agent creates, edits,
|
||||
deletes, converts, archives or restores a story file in any format, and no agent
|
||||
-writes a DOCX. No creative agent starts, requests or undoes a conversion
|
||||
-(REQ-LIB-8). Each creative role's contract states which files are DOCX and which
|
||||
-are Markdown, and cites a DOCX story file by its locator, never by line."
|
||||
+writes a DOCX. No creative agent starts or undoes a conversion, and none asks for
|
||||
+one through a tool or a decision it raises, as REQ-LIB-8 says. An agent may still
|
||||
+suggest a conversion to the human in chat. Each creative role's contract states
|
||||
+which files are DOCX and which are Markdown, and cites a DOCX story file by its
|
||||
+locator, never by line."
|
||||
|
||||
Version tag, inside the closing parenthesis: "Version 1.15.)" becomes "Version
|
||||
1.15. Version 1.17.)"
|
||||
|
||||
### New REQ-LIB-8
|
||||
|
||||
+This bullet goes after REQ-LIB-7 and before the "### Installer" heading.
|
||||
+
|
||||
"- **REQ-LIB-8.** Story files become DOCX. DOCX is the standard for chapters and
|
||||
the other story text in a Case 2 writing project, canon and playground alike. A
|
||||
story file becomes DOCX through a human's import (below), or when the human
|
||||
- starts it in the editor. From then on DOCX is its only working format, edited
|
||||
- in the Console's editor (REQ-LIB-9). Until its import a story file stays
|
||||
- Markdown. Files in a type folder (REQ-LIB-1), such as character and place
|
||||
- notes, stay Markdown. So do supporting files, agent files, instructions, PRDs
|
||||
- and documentation, including the character sheets, summaries and cross-links
|
||||
- agents keep under REQ-LIB-6.
|
||||
+ starts it in the editor. From then on DOCX is its only working format, edited in
|
||||
+ the Console's editor (REQ-LIB-9). A Markdown story file stays Markdown until its
|
||||
+ import. Files in a type folder (REQ-LIB-1), such as character and place notes,
|
||||
+ stay Markdown. So do supporting files, agent files, instructions, PRDs and
|
||||
+ documentation, including the character sheets, summaries and cross-links agents
|
||||
+ keep under REQ-LIB-6.
|
||||
- A converted story file's current text leaves the user's Gitea-mirrored
|
||||
repository. Its DOCX lives in the stack's writing store, on the stack's host,
|
||||
and not in the repository the user pushes or opens in Obsidian. Only its
|
||||
archived Markdown stays there.
|
||||
- - An import converts one Markdown story file, canon or playground, outside
|
||||
- every type folder (REQ-LIB-1), to a DOCX document in the writing store. It
|
||||
- refuses a supporting, archived or type-folder file, and a file that is
|
||||
+ - An import converts one Markdown story file, canon or playground, outside every
|
||||
+ type folder (REQ-LIB-1), to a DOCX document in the writing store. It refuses
|
||||
+ an unmapped, supporting, archived or type-folder file, and a file that is
|
||||
already converted. Each import has its own fidelity report, the human's
|
||||
- confirmation of that report, a manifest naming the source file and its
|
||||
- commit, and one commit in the store. The DOCX keeps every wiki link of the
|
||||
- source as written, and a lost link fails the fidelity report.
|
||||
+ confirmation of that report, a manifest naming the source file and its commit,
|
||||
+ and one commit in the store. The DOCX keeps every wiki link of the source as
|
||||
+ written, and a lost link to another library file fails the fidelity report. A
|
||||
+ web link becomes plain text that keeps its label and address, and the fidelity
|
||||
+ report lists it for the human to confirm. A web link that can't be kept safely
|
||||
+ as plain text refuses the import.
|
||||
- The human can also start a new story file in the editor, in a universe and
|
||||
book or a playground the human picks. It is a DOCX document in the writing
|
||||
store from its first commit, with no Markdown copy, and it opens under the
|
||||
@@ -219,61 +258,81 @@
|
||||
repository. A new human-only bus step (Vikunja 571) on the C2-02 write
|
||||
plumbing writes it, and C2-03b pushes it. The move keeps the bytes and the
|
||||
git history, so it isn't `data.delete`.
|
||||
- - The store commit comes first, then the archive commit and its push, then
|
||||
- publication to the lookups. Until publication the Markdown stays canon at
|
||||
- its old path, and agents read the source at the commit the import started
|
||||
- from. A refusal or failure before the push leaves the Markdown unmoved and
|
||||
- no DOCX that a lookup returns. A failure after the push leaves a pending
|
||||
- import that the human sees and can resume, never a claim that nothing moved.
|
||||
- - Each import writes one logged event naming the source file's path, commit
|
||||
- and blob and the new document id.
|
||||
+ - The store commit comes first, and the writing store's remote must confirm it
|
||||
+ holds that commit. Then come the archive commit and its push, then publication
|
||||
+ to the lookups. Until publication the Markdown at its old path keeps its canon
|
||||
+ or playground scope. Agents read that file at the commit the import started
|
||||
+ from, even after the mirror's served commit reaches the archive commit or a
|
||||
+ later one. No lookup returns the file's archived copy before publication, even
|
||||
+ one that asks for archived files. A refusal or failure before the archive push
|
||||
+ leaves the Markdown unmoved and no DOCX that a lookup returns. A failure after
|
||||
+ the archive push leaves a pending import that the human sees and can resume,
|
||||
+ never a claim that nothing moved. An archive push whose result is unknown
|
||||
+ counts as a failure after the archive push until the stack reads the result
|
||||
+ back from the user's remote.
|
||||
+ - Each stage of an import writes a logged event. The import's record on the
|
||||
+ stack's host names the source file's path, commit and blob and the new
|
||||
+ document id, and each event points to that record.
|
||||
- After a conversion, a Markdown file at the converted file's old path is neither
|
||||
canon nor playground. Lookups leave it out, the format map flags it, and the human is told.
|
||||
- A new Markdown story file in a book folder that holds converted files stays
|
||||
- canon until its import. The Console flags it and offers the import. Nothing
|
||||
+ canon until its import, unless it sits at a converted file's old path or under
|
||||
+ a playground prefix. The Console flags it and offers the import. Nothing
|
||||
converts it on its own.
|
||||
- A hybrid's exclusion that covers a converted file's old path also covers its
|
||||
- DOCX document (REQ-LIB-3). An exclusion can also name a DOCX document by its
|
||||
- id.
|
||||
+ DOCX document (REQ-LIB-3). An exclusion of a whole book folder also covers
|
||||
+ every DOCX document in that book, including one started in the editor. An
|
||||
+ exclusion can also name a DOCX document by its id.
|
||||
- Only a human starts an import or a new story file, from the CLI or a Console
|
||||
- session signed in as that human. Both are human verbs outside the agent
|
||||
- action vocabulary, so no role holds them, no agent can request them, and
|
||||
- REQ-WAIT-2's list is unchanged. In an organization with several humans, who
|
||||
- may start them is a setting only a human changes, and the default is the
|
||||
- owner only.
|
||||
- - Conversion is one way, with no parallel run. Neither the Console nor any
|
||||
- agent edits a converted story file as Markdown, and no DOCX edit syncs back
|
||||
- to Markdown. The Console's Markdown save refuses every file under the archive
|
||||
+ session signed in as that human. Both are human verbs outside the agent action
|
||||
+ vocabulary. Neither is added to `ACTIONS` in the business vocabulary or to the
|
||||
+ broker's always-gated list, including the import's action, `library.convert`.
|
||||
+ No role holds them, no agent can request them, and the hard-wait list in
|
||||
+ REQ-WAIT-1 doesn't change. In an organization with several humans, who may
|
||||
+ start them is a setting only a human changes, and the default is the owner
|
||||
+ only.
|
||||
+ - Conversion is one way, with no parallel run. Neither the Console nor any agent
|
||||
+ edits a converted story file as Markdown, and no DOCX edit syncs back to
|
||||
+ Markdown. The Console's Markdown save refuses every file under the archive
|
||||
folder. The user's own tools can still change the archived Markdown, and no
|
||||
such change reaches the DOCX. The old Markdown stays in git history, and
|
||||
nothing rewrites history. A promotion into a book folder (C2-19) runs the
|
||||
- import for a Markdown story file or is refused.
|
||||
+ import for a Markdown story file or is refused. A promotion of a playground
|
||||
+ DOCX is refused in version 1.17.
|
||||
- Agents get a format map: for each mapped folder and story file, its format,
|
||||
scope and where it lives, archived files, flagged files and pending imports
|
||||
included.
|
||||
- - The import and new story files ship closed. They open only after three things.
|
||||
- The editor ships. Agents can read committed DOCX story files through the
|
||||
- lookups and cite them (REQ-LIB-5, REQ-LIB-6). Any earlier agent system that
|
||||
- writes the user's files has stopped, which for Case 2 is the Hermes
|
||||
- retirement (#423 step 5). So agents never lose the current chapter, and no
|
||||
- second system writes the story. The first import of a real user's books
|
||||
- waits for Jason's go.
|
||||
+ - The import and new story files ship closed. They open only after four things.
|
||||
+ The editor ships. The human has set the writing store's remote, and the stack
|
||||
+ has checked that it can push there. Agents can read committed DOCX story files
|
||||
+ through the lookups and cite them (REQ-LIB-5, REQ-LIB-6). Any earlier agent
|
||||
+ system that writes the user's files has stopped, which for Case 2 is the
|
||||
+ Hermes retirement (#423 step 5). So the import itself never cuts agents off
|
||||
+ from a chapter, the chapter's current text never lives only on the stack's
|
||||
+ host, and no second system writes the story. The first import of a real user's
|
||||
+ books waits for Jason's go.
|
||||
(Q235, Q236, Q237: P1 to P6. Q222 for the setting. O2 design D10 to D12.
|
||||
Version 1.17.)"
|
||||
|
||||
### New REQ-LIB-9
|
||||
|
||||
+This bullet goes after REQ-LIB-8.
|
||||
+
|
||||
"- **REQ-LIB-9.** The Console's DOCX editor. The human edits a DOCX story file in
|
||||
- the Console's editor, on a page of its own. Only a human saves. Saves happen
|
||||
- on their own while the human types (Q226), at intervals only a human changes,
|
||||
- and each accepted save is one commit with the human as author, pushed as
|
||||
- REQ-LIB-4 says. A save made against an older version than the current one is
|
||||
+ the Console's editor, in a split pane with the agent chat alongside it. The
|
||||
+ human can pin an agent chat to a document. The human can select a word,
|
||||
+ right-click it, and choose a synonym from a thesaurus. Only a human saves. Saves
|
||||
+ happen on their own while the human types (Q226), at intervals only a human
|
||||
+ changes, and each accepted save is one commit with the human as author, pushed
|
||||
+ as REQ-LIB-4 says. A save made against an older version than the current one is
|
||||
refused. Agents never write a DOCX. Versioning is the editor's tracked changes
|
||||
- plus one git commit per save. The editor's code is vendored and pinned, with
|
||||
- its license and a hash per file, and nothing loads at runtime. The editor's
|
||||
- frame sends exactly the content security policy Jason approved in Q234, and
|
||||
- the WebUI shell's own header is unchanged. REQ-PROJ-3's project docs editor
|
||||
- is unchanged. (Q195, Q226, Q234, Q236. O2 design D10 and D14. Version 1.17.)"
|
||||
+ plus one git commit per save. The editor's code is vendored and pinned, with a
|
||||
+ hash per file and the licenses that its independent security review accepted. At
|
||||
+ runtime the editor loads only files that the stack itself serves, and it
|
||||
+ requests nothing from any other host. The editor's frame sends exactly the
|
||||
+ content security policy Jason approved in Q234, and the WebUI shell's own header
|
||||
+ is unchanged. REQ-PROJ-3's project docs editor is unchanged. (Q195, Q226, Q234,
|
||||
+ Q236. O2 design D3, D4, D6, D8, D10, D13, D14, D16 and D22. Version 1.17.)"
|
||||
|
||||
### Version 1.17 notes
|
||||
|
||||
@@ -285,40 +344,52 @@
|
||||
- The 10-27 Case 2 release ships on Markdown as built (P6). The 1.11 line 'The
|
||||
Case 2 MVP for 10-27 is the Markdown library (#416). The WYSIWYG editor (Q195,
|
||||
Q196) is an add-on that doesn't gate it.' still holds. Conversion opens after
|
||||
- 10-27, after W4b, and after Hermes is retired (#423 step 5).
|
||||
- - The 1.12 note says bus writes and the Console editor (C2-02, C2-03b, C2-11a,
|
||||
- C2-11b, C2-19) don't gate 10-27. Jason's Q226 F42 later moved three of them,
|
||||
- C2-02, C2-03b and C2-11b, into the 10-27 slice, and that later ruling holds.
|
||||
- C2-11a and C2-19 still don't gate 10-27. Where a plan or queue record names
|
||||
- a different set, F42 wins, and the record is corrected through its own
|
||||
- process.
|
||||
- 1.17 changes neither, and C2-11b's Markdown editor never edits a converted
|
||||
- story file.
|
||||
- - Q236 narrows Q176's editor choice to DOCX for story files (REQ-LIB-9).
|
||||
- - This replaces the superseded-notice proposal in O2 design D12 (Fenton F1),
|
||||
- which is withdrawn, and the keep-Markdown-at-its-path model of D11. Gyro's
|
||||
- rewrite of the O2 design (Vikunja 571) carries the details:
|
||||
+ 10-27, and only once REQ-LIB-8's four conditions are met. The editor ships,
|
||||
+ the writing store's remote is set and the stack has checked that it can push
|
||||
+ there, agents can read and cite committed DOCX story files through the
|
||||
+ lookups, and Hermes is retired, which is #423 step 5.
|
||||
+ - The 1.12 note says five bus write and Console editor rows, C2-02, C2-03b,
|
||||
+ C2-11a, C2-11b and C2-19, don't gate 10-27. Jason's Q226 F42 later moved
|
||||
+ C2-02, C2-03b and C2-11b into the 10-27 slice. The Case 2 design brings C2-11a
|
||||
+ in with them, because C2-11b needs it. C2-19 stays outside the slice. Mos's
|
||||
+ reading of F42 is that C2-02 and C2-03b now gate 10-27, because design 9.2
|
||||
+ puts them in 'the smallest slice that lets her start'. C2-11a and C2-11b are
|
||||
+ slice rows that still don't gate 10-27, because the editor is the Q195 add-on.
|
||||
+ The DOCX change in 1.17 leaves this slice as it is, and C2-11b's Markdown
|
||||
+ editor never edits a converted story file.
|
||||
+ - Q236 narrows Q176's editor choice to DOCX for story files (REQ-LIB-9). The
|
||||
+ 1.12 note that the Case 2 design keeps Obsidian now holds only for Markdown
|
||||
+ files, including story files not yet imported. A DOCX story file is edited
|
||||
+ only in the Console's editor under REQ-LIB-9.
|
||||
+ - This replaces two parts of the O2 design at 25c09f19f. The superseded-notice
|
||||
+ proposal from Fenton's F1 in D12 is withdrawn, and the
|
||||
+ keep-Markdown-at-its-path model of D11 is replaced. Gyro's rewrite of the O2
|
||||
+ design for Vikunja 571, landed on `refactor` at b99ca86b9, carries the
|
||||
+ details, and where it differs from this text, this text holds:
|
||||
- the import and archive steps, the mark, and the new bus step;
|
||||
- links from supporting files to a moved chapter;
|
||||
- the DOCX reader;
|
||||
- the user's unpushed or later changes to an archived file or at its old path,
|
||||
and pushing and closing the file in Obsidian before each import;
|
||||
- - the writing store's remote, with W4 7.4's no-remote and no-backup rule taken
|
||||
- out;
|
||||
- - import events counted by a stack restore, and recovery when a restore
|
||||
- predates an import;
|
||||
+ - the writing store's remote in W4 section 7.3, which replaces the no-remote
|
||||
+ and no-backup rule that W4 section 7.4 held before the rewrite, at
|
||||
+ 062646487;
|
||||
+ - import events counted by a bus restore, and recovery when a bus restore or a
|
||||
+ restore of the whole host predates an import;
|
||||
- manifest order for the manuscript lookup and its next chapter;
|
||||
- - the import verb kept out of `ACTIONS` (packages/business/src/vocabulary.mjs,
|
||||
- the hard-wait note and the pinned `GATED_ONLY` list).
|
||||
+ - `library.convert` kept out of `ACTIONS`, out of the hard-wait note and out
|
||||
+ of the pinned `GATED_ONLY` list in packages/business/src/vocabulary.mjs.
|
||||
- Agents read a converted story file through the Case 2 lookups under the same
|
||||
project and universe access as Markdown canon, in any thread or room. W4's
|
||||
- O2-only conversations, per-document grants and per-send sharing (W4 7.1, 7.2)
|
||||
- don't gate these lookups, and the 571 rewrite brings W4 into line. The
|
||||
- creative tool list in REQ-LIB-6 doesn't change.
|
||||
+ earlier O2-only conversations, per-document grants and per-send sharing don't
|
||||
+ gate these lookups. The 571 rewrite replaced them in W4 sections 7.1 and 7.2.
|
||||
+ The creative tool list in REQ-LIB-6 doesn't change.
|
||||
- The six creative role contracts row 213 landed say 'Every item cites the
|
||||
lines of the user's material it is about'. A build row under 571 changes that
|
||||
line for DOCX story files (REQ-LIB-6).
|
||||
- - A new Markdown file in a book folder is still canon in 1.17.
|
||||
+ - A new Markdown file in a book folder is still canon in 1.17. The 1.17
|
||||
+ exception is a file at a converted file's old path, which REQ-LIB-2 makes
|
||||
+ unmapped.
|
||||
- Story files convert whether canon or playground. Type-folder and supporting
|
||||
files stay Markdown.
|
||||
- Measured 2026-10-23 at about 01:21Z: the Case 2 user's library repository
|
||||
@@ -336,52 +407,130 @@
|
||||
canon unless the caller asks for playground or supporting." "so an agent
|
||||
can't cite a playground or supporting file as canon" "The Console shows
|
||||
each cite's scope."
|
||||
- - REQ-LIB-4, the 1.16 sentence: "The user commits and pushes with their own
|
||||
- tools, today Obsidian with obsidian-git." and the clause "Agents read only
|
||||
- the mirror's served commit and never hold a copy of their own".
|
||||
+ - REQ-LIB-4, the 1.16 sentences: "The user commits and pushes with their own
|
||||
+ tools, today Obsidian with obsidian-git." "Agents read only the mirror's
|
||||
+ served commit and never hold a copy of their own, so they see the user's
|
||||
+ push within about a minute."
|
||||
- REQ-LIB-5, the 1.16 sentence: "The user sees them in Obsidian's graph and
|
||||
backlinks." A converted chapter's connections show in Obsidian only as they
|
||||
- stood at conversion, until a WebUI view exists (Q226).
|
||||
+ stood at conversion, until a Console view of their links exists. Q226
|
||||
+ records that view as a later option.
|
||||
- REQ-LIB-6, the 1.16 sentence: "A reply may cite lines of the repository at
|
||||
a commit or pages the agent fetched, and the broker checks every citation
|
||||
before it stores the message."
|
||||
- REQ-LIB-1 only gains sentences.
|
||||
- REQ-LIB-3's text and REQ-LIB-7 are unchanged. REQ-LIB-8 adds one form of
|
||||
- exclusion REQ-LIB-3 doesn't have: naming a DOCX document by its id. An archived file and a DOCX document
|
||||
- keep a universe, and a book where their source has one, so every lookup still
|
||||
- names one universe. REQ-PROJ-3, the project docs editor, is not Case 2 and is
|
||||
- unchanged.
|
||||
+ exclusion REQ-LIB-3 doesn't have: naming a DOCX document by its id. A path
|
||||
+ exclusion also covers DOCX documents, through a converted file's old path or a
|
||||
+ whole book folder. An archived file and a DOCX document keep a universe, and a
|
||||
+ book where their source has one, so every lookup still names one universe.
|
||||
+ REQ-PROJ-3, the Console editor for a project's spec and docs, is unchanged.
|
||||
+ C2-11b opens the library's Markdown files in that same editor.
|
||||
- In `packages/tasks/requirements.json` the titles of REQ-LIB-1, REQ-LIB-2 and
|
||||
REQ-LIB-4 change, and those of REQ-LIB-5 and REQ-LIB-6 stay. REQ-LIB-1 becomes
|
||||
'A human-set library maps the user's repository onto universes, books, types,
|
||||
playgrounds, supporting and archive folders'. REQ-LIB-2 becomes 'Scope comes
|
||||
from the path or a DOCX's source, lookups default to canon, and only canon
|
||||
- files are ever cited as canon'. REQ-LIB-4 becomes 'Git holds every change;
|
||||
- the bus fast-forwards a read-only mirror every 60 seconds and agents read
|
||||
- Markdown only there'. REQ-LIB-8 and REQ-LIB-9 add two ids, so the count goes
|
||||
- from 85 to 87. REQ-LIB-8 is titled 'Case 2 story files become DOCX by a human-only one-way import or in the editor; Markdown stays for notes and docs', and REQ-LIB-9 'The Console's DOCX editor: only a human saves, each save is one commit, and the editor code is vendored and pinned'.
|
||||
+ files are ever cited as canon'. REQ-LIB-4 becomes 'Git holds every change. The
|
||||
+ bus fast-forwards a read-only mirror every 60 seconds, the one place agents
|
||||
+ read Markdown'. REQ-LIB-8 and REQ-LIB-9 add two ids, so the count goes from 85
|
||||
+ to 87. REQ-LIB-8 is titled 'Case 2 story files become DOCX by a human-only
|
||||
+ one-way import or in the editor; Markdown stays for notes and docs', and
|
||||
+ REQ-LIB-9 'The Console's DOCX editor: only a human saves, each save is one
|
||||
+ commit, and the editor code is vendored and pinned'.
|
||||
- Open points O1 to O4 of draft 1 were ruled by Mos in mos-1367 B1 to B4.
|
||||
- Sage files the build rows under Vikunja 570 and 571 after the rewrite is
|
||||
approved. This amendment adds the requirements only."
|
||||
|
||||
### Risks
|
||||
|
||||
-New lines:
|
||||
+The new lines go at the end of "## Risks and open questions", after the bullet
|
||||
+that begins "Trusted mode lets an agent" and before the "## Milestones" heading.
|
||||
- "A DOCX file is a zip archive, so git can't show a line diff of a chapter.
|
||||
Review of a story change goes through the editor and the committed reader,
|
||||
not `git diff`. (Version 1.17.)"
|
||||
-- "Converted chapters leave Obsidian's graph. Their links show only in the
|
||||
- editor until a graph view exists. (Version 1.17.)"
|
||||
+- "A converted chapter's DOCX is not in Obsidian's graph. Obsidian shows the
|
||||
+ chapter only through its archived Markdown, and no DOCX edit reaches that file's
|
||||
+ links. The DOCX's current links show only in the Console's editor until a
|
||||
+ Console view of them exists. (Version 1.17.)"
|
||||
- "Conversion can't be undone in place. Front matter, wiki link forms and
|
||||
unsupported blocks may not survive it, and the mirror can't refuse the user's
|
||||
push to an archived file. The controls are the fidelity report, the human's
|
||||
confirmation, an untouched original on refusal, and the kept archive.
|
||||
(Version 1.17.)"
|
||||
-- "A stack restore from a backup taken before an import drops the import's
|
||||
- logged event, while the archive commit and the DOCX stay. Restore counts
|
||||
- import events, and the import's recovery covers this case (Vikunja 571).
|
||||
+- "A restore of the stack's bus alone from a backup taken before an import drops
|
||||
+ the import's logged events, while the archive commit and the DOCX stay. A
|
||||
+ restore of the stack's whole host from such a backup also takes the writing
|
||||
+ store and the host's import records back to before the import. The archive
|
||||
+ commit stays in the user's Gitea repository and the DOCX stays on the writing
|
||||
+ store's remote, but the host keeps no record of the import. A bus restore counts
|
||||
+ import events, and the import's recovery covers both cases (Vikunja 571).
|
||||
(Version 1.17.)"
|
||||
|
||||
+## d8 changes (Mos's d8 fix list, check wf_63bba879-180)
|
||||
+
|
||||
+- R1: REQ-LIB-4's tail covers DOCX saves, which agents see at their next lookup after the editor reports them as committed. The notes quote the whole 1.16 sentence. Source: nit/jason/F10.
|
||||
+- R2: REQ-LIB-4's new title says the mirror is "the one place agents read Markdown". Source: decisions.n14.
|
||||
+- R3: J-b asks whether 1.17 requires the Console Connections view, with Mos's recommendation of yes. Sources: mos1372/F9, jason/F2, anchors/F5.
|
||||
+- R4: C2-02 and C2-03b gate 10-27, C2-11a and C2-11b are slice rows that don't, and "F42 wins" is gone. New J-k. Sources: anchors/F6, jason/F5.
|
||||
+- R5: where the landed 571 design differs, this text holds. Sources: anchors/F8, adversarial/F8 item 1, jason/F8, critic/C6, G16's design follow-on.
|
||||
+- R6: `library.convert` stays out of `ACTIONS`, and REQ-WAIT-1's hard-wait list doesn't change. Sources: gyro571/F1, adversarial/F8 item 3, critic/C6.
|
||||
+- R7: a repository on the stack's host never counts as the writing store's remote, and Item 2 says so. Sources: mos1372/F2, adversarial/F1, gyro571/F2.
|
||||
+- R8: "no agent credential reaches it" becomes "no agent can reach it with any credential". Source: Fenton's B1 under mos-1379.
|
||||
+- R9: no playground DOCX becomes canon in 1.17, and a promotion of one is refused. New J-i. Sources: mos1372/F8, adversarial/F2.
|
||||
+- R10: REQ-LIB-9 restates Q195's pin and right-click thesaurus, and its cite gains D4 and D22. Source: critic/C4.
|
||||
+- R11: REQ-LIB-9's license bar is the licenses its independent security review accepted. New J-l. Source: critic/C3.
|
||||
+- R12: no text for D16's narrow-window tabs. Mos records that reading in STATE. Sources: jason/F1, second verifier vote.
|
||||
+- R13: no lookup returns a file's archived copy before publication. Source: adversarial/F5 part 3.
|
||||
+- R14: a restore of the whole host from a backup taken before an import is a fourth recovery case. Source: adversarial/F6.
|
||||
+- M1: REQ-LIB-4's writing-store sentences follow the later bus rows sentence and add the off-host remote rules and DOCX read timing. The note quotes both 1.16 sentences. Sources: mos1372/F2, adversarial/F1, gyro571/F2, anchors/F4, nit/jason/F10, adversarial/F5 part 1, Fenton B1.
|
||||
+- M2: REQ-LIB-8's order bullet adds the remote's confirmation, the old-path read rule and "archive push". Sources: mos1372/F2 and the other remote findings, mos1372/F7, adversarial/F5 parts 2 to 4.
|
||||
+- M3: REQ-LIB-8's gate bullet has four conditions, adding the remote. Sources: the remote findings, critic/C1 and C2 edit 2.
|
||||
+- M4: the 1.17 note on when conversion opens restates the four conditions and drops W4b. Sources: jason/F5, the remote findings.
|
||||
+- M5: Item 2 rewritten with the remote rules. Sources: the remote findings.
|
||||
+- M6: the 1.12 gating note rewritten. Sources: anchors/F6, jason/F5.
|
||||
+- M7: REQ-LIB-9 gains the split pane, the pin, the thesaurus, the license bar and the runtime load rule, and cites more of the design. Sources: jason/F1, critic/C4, critic/C3, nit/anchors/F12, nit/gyro571/F9.
|
||||
+- M8: REQ-LIB-2, the one-way bullet and the C1 note keep a playground DOCX out of canon in 1.17. Sources: mos1372/F8, adversarial/F2.
|
||||
+- M9: REQ-LIB-5 says what the reader can't read, and the import turns web links into plain text. New J-j. Sources: critic/C1, critic/C2.
|
||||
+- M10: an exclusion of a whole book folder covers that book's DOCX documents. Source: adversarial/F3, with B5.
|
||||
+- M11: the human-verbs sentence and the 571 list item name `library.convert` and REQ-WAIT-1. Sources: gyro571/F1, adversarial/F8 item 3, critic/C6.
|
||||
+- M12: J-b rewritten. Sources: mos1372/F9, jason/F2, anchors/F5, gyro571/F3, critic/C6.
|
||||
+- M13: J-d puts the agent read model to Jason as his call. Source: mos1372/F1.
|
||||
+- M14: Status moves to version 1.17 and names REQ-LIB-9. Sources: anchors/F2, mos1372/F4, critic/C7.
|
||||
+- M15: the 1.17 source line quotes Q235 in full (mos-1446) and the REQ-LIB-9 sources, at b99ca86b9. Sources: critic/C5, anchors/F9, jason/F8 pin part, nit/gyro571/F8, critic/C6 pin part.
|
||||
+- M16: the 571 list lead names the two replaced design parts and the landed design. Sources: anchors/F9, anchors/F8, adversarial/F8 item 1, jason/F8, critic/C6.
|
||||
+- Landing swap: Gyro's docs round for R5, R6 and R14 landed as b99ca86b9 (Fenton fenton-0015), so M15 and M16 cite b99ca86b9 in place of 767bb62c7, as the d8 fix list's header says. J-b keeps 767bb62c7 for its line numbers.
|
||||
+- M17: W4 section numbers in the remote item and the lookups note. Sources: gyro571/F5, nit/adversarial/F11.
|
||||
+- M18: the D7 landing line. Sources: gyro571/F5, the remote findings.
|
||||
+- M19: a logged event for each import stage, in REQ-LIB-8 and A2. Source: gyro571/F7.
|
||||
+- M20: the Risks line on restores adds the whole-host case. Sources: adversarial/F6, gyro571/F7.
|
||||
+- M21: the 571 restore item adds the whole-host case. Source: adversarial/F6.
|
||||
+- M22: the graph Risks line and the REQ-LIB-5 note name a Console view. Sources: anchors/F5, jason/F2.
|
||||
+- M23: the d6 B3 line drops "F42 wins". Source: anchors/F6.
|
||||
+- M24: new Markdown in a converted book, with the old-path and playground exceptions. Source: mos1372/F6.
|
||||
+- M25: J-c's last sentence is Mos's reading. Source: mos1372/F10.
|
||||
+- M26: J-h rewritten with the D13 defaults. Source: jason/F3.
|
||||
+- M27: Item 4 rewritten. Sources: jason/F4, gyro571/F4, decisions.unmarked_folders.
|
||||
+- M28: the "Not taken" note on unmarked folders. Sources: jason/F4, gyro571/F4, decisions.unmarked_folders.
|
||||
+- M29: the import refuses an unmapped file. Sources: gyro571/F4, decisions.unmarked_folders.
|
||||
+- M30: placement notes for REQ-LIB-8, REQ-LIB-9 and the Risks lines. Sources: anchors/F3, nit/anchors/F14.
|
||||
+- M31: REQ-LIB-4's title and the D11 landing line. Source: decisions.n14.
|
||||
+- M32: the Q236 note limits the 1.12 Obsidian note to Markdown files. Source: critic/C8.
|
||||
+- M33: the exclusion note adds path exclusions of DOCX documents. Source: anchors/F7.
|
||||
+- M34: A9 moves from "Mine" to the impact-map list. Source: decisions.exclusion_by_id.
|
||||
+- M35: the D5 landing line. Sources: anchors/F10, mos1372/F5, nit/adversarial/F12.
|
||||
+- M36: the D9 and D14 landing lines. Source: mos1372/F1.
|
||||
+- M37: the O1 and C3 notes add the off-host remote and its confirmation. Sources: the remote findings.
|
||||
+- M38: the REQ-PROJ-3 note. Source: nit/anchors/F11.
|
||||
+- M39: REQ-LIB-5's freshness sentence keys on the editor reporting a save as committed. Source: nit/adversarial/F9.
|
||||
+- M40: REQ-LIB-6 bars agent requests for a conversion and allows a suggestion in chat. Source: nit/jason/F9.
|
||||
+- M41: REQ-LIB-2's opening and old-path rule, and the "Mine" note. Source: nit/adversarial/F10.
|
||||
+- M42: REQ-LIB-8's lead sentence on Markdown story files. Source: nit/mos1372/F12.
|
||||
+- M43: REQ-LIB-6's citation sentence. Source: nit/anchors/F13.
|
||||
+- M44: REQ-LIB-5's version tag uses plain quotes. Source: nit/anchors/F15.
|
||||
+- M45: reference codes B5, D12, D10 and B4 name their source. Source: nit/mos1372/F11.
|
||||
+- Files the row would own: four files, with the id count going to 87. Sources: anchors/F1, adversarial/F4, decisions.n14 F4.
|
||||
+
|
||||
## d7 changes (mos-1379, Fenton's 571 B2)
|
||||
|
||||
- REQ-LIB-5 gains Mos's three sentences after the extracted-text sentence. The text agents read has tracked changes accepted. Comments, and text with tracked deletions shown, each wait for a setting only a human turns on, off by default. Its version tag cites Q222 for the two settings, which is the only text here beyond Mos's.
|
||||
@@ -392,7 +541,7 @@
|
||||
|
||||
- B1: titles for REQ-LIB-8 and REQ-LIB-9 in the requirements.json note and the file list. REQ-LIB-9 takes Webby's title. REQ-LIB-8 keeps mine, because Webby's ("...one-way import that archives the Markdown under its old path") leaves out D2's story files started in the editor.
|
||||
- B2: REQ-LIB-2 makes a Markdown file at a converted file's old path unmapped, ahead of the playground and canon rules, so a playground conversion is covered too. REQ-LIB-8's old-path bullet says "neither canon nor playground".
|
||||
-- B3: the slice note names Q226 F42's three rows (C2-02, C2-03b, C2-11b). C2-11a doesn't gate 10-27. F42 wins where case2-rows.md:1284-1285 or the impact map's A57 says otherwise.
|
||||
+- B3: the slice note names Q226 F42's three rows (C2-02, C2-03b, C2-11b). C2-11a joined the slice with C2-11b, because C2-11b needs it. Both are slice rows that don't gate 10-27, as design 9.2, case2-rows.md:1655 and the impact map's A57 say. The 'F42 wins' sentence is gone from the note in d8, so no queue change follows from it.
|
||||
- N10: the cite-scope replacement is scoped to REQ-LIB-2. The 1.14 note's copy stays.
|
||||
- N11: the notes say REQ-LIB-8 adds the by-id exclusion form REQ-LIB-3 doesn't have.
|
||||
- N12: J-h marks the autosave intervals for Jason.
|
||||
@@ -405,57 +554,61 @@
|
||||
- D2: REQ-LIB-2's "for one the human starts in the editor" and the REQ-LIB-8 new-story-file bullet. The universe-and-book note is now true.
|
||||
- D3: the REQ-LIB-8 order bullet. This also settles N8.
|
||||
- D4: REQ-LIB-8's one-way bullet, which carries the archive refusal and consequence F2's text, and the 571 list item.
|
||||
-- D5: REQ-LIB-2's "unless its path is a converted file's old path" and the REQ-LIB-8 old-path bullet.
|
||||
+- D5: it landed in REQ-LIB-2's "a Markdown file at a converted file's old path is unmapped" in Webby's B2 form, and in the REQ-LIB-8 old-path bullet.
|
||||
- D6: REQ-LIB-5's freshness sentences.
|
||||
-- D7: REQ-LIB-4's remote sentences, plus the 571 list item that takes out W4 7.4's rule. The impact map's item 2 adds "only the stack's host pushes, and no agent credential reaches it".
|
||||
+- D7 and mos-1375: REQ-LIB-4's remote sentences, the remote step in REQ-LIB-8's order bullet, the fourth opening condition and the 1.17 note on when conversion opens, plus the 571 list item on W4 section 7.3, which replaces the no-remote rule of the old W4 section 7.4. The impact map's item 2 adds "only the stack's host pushes, and no agent credential reaches it".
|
||||
- D8: the REQ-LIB-5 replacement, the import bullet's wiki-link sentence, the Risks line, the note quoting the old sentence, and "REQ-LIB-1 only gains sentences".
|
||||
-- D9: B2's note.
|
||||
-- D10: B5's sentence, in REQ-LIB-8.
|
||||
-- D11: internal F3's clause and title, in the REQ-LIB-4 section and the notes.
|
||||
+- D9: the 1.17 note that agents read a converted story file through the Case 2 lookups, from the d4 check's consistency B2, and J-d, which puts the read model to Jason as his call.
|
||||
+- D10: the d4 check's B5 sentence, in REQ-LIB-8.
|
||||
+- D11: internal F3's clause in the REQ-LIB-4 section, and in the notes the title as Mos amended it for N14.
|
||||
- D12: the whole 1.16 sentence replaced, and quoted in full in the notes.
|
||||
- D13: consequence F1's text, in REQ-LIB-4 and REQ-LIB-8's first bullet.
|
||||
-- D14: B1's row text, in the mark-and-move bullet and C3 above.
|
||||
+- D14: the row text from the d4 check's consistency B1, in the mark-and-move bullet and C3 above.
|
||||
|
||||
## What d5 adds beyond mos-1372 (for Webby and Mos to check)
|
||||
|
||||
From the impact map, folded as mos-1368 item 1 asked:
|
||||
- A1: the Console shows a cite's format and converted state (REQ-LIB-2).
|
||||
-- A2: the logged event and the several-humans setting, default owner only (Q222).
|
||||
+- A2: the logged events and the several-humans setting, default owner only, under Q222.
|
||||
- A3: the import is outside the vocabulary, the hard-wait list is unchanged, and the archive move isn't `data.delete`. The C6 code site is in the 571 list.
|
||||
- A4: the section lead and REQ-LIB-1's writing-store sentence.
|
||||
- A6: format and location in every answer, extracted text with DOCX locators, links across formats.
|
||||
- A7: read-only extends to the store and the archive, no agent action on a story file in any format, no agent DOCX write, and role contracts state the formats. C1's role contract line is a 571 build item.
|
||||
- A8: the DOCX cite shape, and a cite naming an archived file as current is refused.
|
||||
+- A9: a hybrid sees a source's DOCX canon through REQ-LIB-2's source scope, and REQ-LIB-8 lets an exclusion name a DOCX document by its id. The id form lets the human exclude a single DOCX started in the editor, which has no old path. An archived file isn't canon, so no hybrid sees it.
|
||||
- A10: REQ-LIB-9, so the count is 87, not 86.
|
||||
- A11: the Hermes gate, from item 3.
|
||||
- A12: the C2-19 promotion sentence.
|
||||
- A13: the broker's path check treats the archive folder as story.
|
||||
- A14 and C5: two Risks lines.
|
||||
-- A15: the flagged-new-file bullet. It sits beside B4's "a new Markdown file in a book folder is still canon" note: the file stays canon and is offered for import.
|
||||
+- A15: the flagged-new-file bullet. It sits beside the note from the d4 check's B4, "a new Markdown file in a book folder is still canon". The file stays canon and is offered for import.
|
||||
- A16: three titles change and two stay.
|
||||
- C2: manifest order for the manuscript lookup.
|
||||
- F3: the C2-02 gating note.
|
||||
- F4: Mos's measurement.
|
||||
|
||||
Mine, beyond either source:
|
||||
-- REQ-LIB-2 opens "A Markdown file's scope comes from its path alone". DOCX scope comes from the source, so "a file's" would be false.
|
||||
-- REQ-LIB-8's exclusion-by-id sentence. A DOCX started in the editor has no old path, so without it a hybrid couldn't exclude one.
|
||||
+- REQ-LIB-2 opens "A Markdown file's scope comes from its path and from past imports". DOCX scope comes from the source, so "a file's" would be false.
|
||||
- REQ-LIB-9's autosave sentence. It is Mos's reading of F41's "instantly committed", with human-set intervals. The exact intervals stay in the design.
|
||||
|
||||
Not taken:
|
||||
-- Impact map item 4's "files in unmarked folders are eligible one at a time". D1 names canon and playground story files only, and an unmapped file has no scope for a DOCX to keep. If Mos wants unmarked folders in, the human would map them first.
|
||||
+- Impact map item 4's "files in unmarked folders are eligible one at a time", for folders outside every book folder and playground only. Their files are unmapped under REQ-LIB-2, so a DOCX made from one would have no universe or book to keep. The human maps such a folder into a book folder or a playground first, under REQ-LIB-1. A file in an unmarked folder inside a book folder or a playground is canon or playground, and the import takes it one file at a time.
|
||||
- A6's named chapter-links view. D8 takes B8's "no graph shows them yet", and J-b puts the view to Jason.
|
||||
|
||||
## Marked for Jason (additions in the text he sees)
|
||||
|
||||
- J-a. Whether "We will not run the systems in parallel" applies per chapter or as one switch per book. His call. As written, it applies per chapter.
|
||||
-- J-b. Converted chapters leave Obsidian's graph, and 1.17 has no other graph. REQ-LIB-5 still carries its "Connections are always visible" tag, and Q226 leaves room for "another way to handle the visual brain map". As written, that tag holds for Markdown files and for links inside the editor, and a graph of DOCX links waits for that other way.
|
||||
-- J-c (mos-1374): "The store pushes to a remote (D7), and the Case 2 VM is in the nightly PBS backup. For a fixed restore point before her first real import, a manual backup of VM 1012 with PBS protect runs first. That step is reversible and needs no reboot." This belongs to the P5 go step, so no new question.
|
||||
-- J-d. The agent read model in D9. A converted chapter is read through the ordinary Case 2 lookups in any thread or room, not through W4's per-document grants. Mos's reading of F41 and REQ-LIB-7. Comments and tracked deletions stay hidden from agents until a human turns on a setting for each. Markdown has neither, so the default keeps agents at the access Markdown gives them (mos-1379).
|
||||
+- J-b. A converted chapter's DOCX is not in Obsidian's graph. Obsidian shows the chapter only through its archived Markdown, and no DOCX edit reaches that file's links. REQ-LIB-5 still cites two TARGET lines, "Visualization of data via Obsidian or equivalent is required" and "Connections are always visible". Mos's reading is that 1.17 as written meets neither line for converted chapters. Their current links show to the user only as clickable text inside the editor, and no graph or list shows them. Q226 leaves room for "another way to handle the visual brain map". The O2 design that landed for Vikunja 571 at 767bb62c7 holds a Console Connections view and its tests as later work that waits for this answer. His call whether 1.17 requires that view. As written, it does not. Mos recommends that it does, because impact map A6 says "Recommended: name the view" and TARGET says "Connections are always visible".
|
||||
+- J-c (mos-1374): "The store pushes to a remote (D7), and the Case 2 VM is in the nightly PBS backup. For a fixed restore point before her first real import, a manual backup of VM 1012 with PBS protect runs first. That step is reversible and needs no reboot." Mos's reading is that this sits inside the P5 go step, so it needs no new question.
|
||||
+- J-d. How agents read a converted chapter, D9. His call. As written, an agent reads it through the ordinary Case 2 lookups in any thread or room, under the same project and universe access as Markdown canon. This is Mos's reading of F41 and REQ-LIB-7. The other choice is the model in the earlier O2 design, W4 sections 7.1 and 7.2. Under it, an agent reads a converted chapter only in a new writing conversation that holds one human and one agent, only after the business owner grants that agent access to that document, and by default only what the human attaches to each message. Comments and tracked deletions stay hidden from agents until a human turns on a setting for each. Markdown has neither, so the default keeps agents at the access Markdown gives them. Those two sentences are Mos's ruling mos-1379. A yes to the text as written settles it.
|
||||
- J-e. Versioning means the editor's tracked changes plus one git commit per save.
|
||||
- J-f. A converted chapter can be edited only in the Console while the stack is reachable. Obsidian no longer edits it offline.
|
||||
- J-g. The import takes Markdown only. Importing .doc files stays a separate task.
|
||||
-- J-h. Autosave (REQ-LIB-9) runs at intervals only a human changes. F41 says saves are "instantly committed". The intervals are text beyond that, so they are marked for him. The exact values stay in the design.
|
||||
+- J-h. Autosave under REQ-LIB-9. Jason's Q226 F41 says "All changes are instantly committed and available to the agents." Mos's reading, recorded with that ruling, is "Each save is a commit at once, and agents read it at their next lookup." At the O2 design's D13 defaults, a save commits at most 2 seconds after a pause in typing or 15 seconds into steady typing, with no human action needed. Mos's reading in mos-1359 counts that delay as "instantly committed". Only a human changes the intervals. The PRD names no values, and the defaults stay in the design. Whether a delay of seconds fits his word "instantly" is his call, so this is marked for him.
|
||||
+- J-i. In 1.17 a playground story that is DOCX can't become canon. That covers a playground Markdown file the human imports, a story the human starts in a playground in the editor, and the DOCX of a draft universe after it is made real. Today Melanie promotes a playground file by moving it into a book folder in Obsidian, and a DOCX in the writing store can't be moved that way. She can start a new story in the book and paste the text in, but it gets a new document id and the playground copy keeps the history. A later version adds DOCX promotion with C2-19, which comes after the slice.
|
||||
+- J-j. Web links, text boxes and fields in a converted chapter. Jason's Q226 F41 says "All changes are instantly committed and available to the agents." The reader in this version can't read a web link, a text box, a field or hidden text. As written, the import turns each web link into plain text that keeps its label and address, and the human confirms that in the fidelity report. A web link that can't be kept safely as plain text refuses the import. If a later edit adds one of these, the save keeps it and the editor warns the human. Agents then get none of that part's text until a save takes it out.
|
||||
+- J-k. The 1.12 note Jason approved says bus writes, C2-02 and C2-03b, don't gate 10-27. His Q226 "F42: ok" moved them into the 10-27 slice, and design 9.2 counts them in the smallest slice that lets Melanie start. Mos reads that as making them gate 10-27. The editor rows C2-11a and C2-11b still don't gate it, per his Q195 words "we need to get it moving as an add-on".
|
||||
+- J-l. REQ-LIB-9 does not use REQ-PROJ-3's "permissive license" bar. It takes the licenses that the editor's independent security review accepted, because that review cleared fonts under the SIL Open Font License and the Ubuntu Font License, which not every reader counts as permissive. Mos's reading.
|
||||
|
||||
Marked as changes beyond P1 to P6, from the impact map (Q17, item 5):
|
||||
- A1 (archived scope and canon cites)
|
||||
@@ -464,21 +617,22 @@
|
||||
- A6 (lookups across formats)
|
||||
- A7 (agent bounds)
|
||||
- A8 (DOCX cites)
|
||||
-- Item 2 (the remote only the stack's host pushes)
|
||||
+- Item 2 and mos-1375: each writing-store commit is pushed to a remote off the stack's host. Only a human sets it, it starts unset, and a repository on the stack's host doesn't count. Only the stack's host pushes to it, and no agent can reach it with any credential. Import and new story files stay closed until the remote is set and checked, and each import's archive push waits until the remote holds the DOCX commit. This keeps "Git is used for all changes -- local and remote" true for DOCX chapters.
|
||||
- Item 3 (the Hermes gate)
|
||||
-- Item 4 (canon and playground convert, type folders stay Markdown)
|
||||
+- Item 4. Canon and playground story files convert one file at a time, including files in unmarked folders inside a book folder or a playground. Type-folder and supporting files stay Markdown. A story file outside every book folder and playground has no universe or book, so it converts only after a human maps its folder into a book folder or a playground. The mapping step is Mos's reading of P1 and Q30.
|
||||
|
||||
The F4 measurement goes with them.
|
||||
|
||||
## Files the row would own
|
||||
|
||||
-As with 1.16, the row owns three files:
|
||||
+The id count goes from 85 to 87. 1.16 owned three files because its count stayed at 85. Like 1.13, the last version that added ids, the row owns four files:
|
||||
- `docs/prd/mosaic-stack.md`
|
||||
-- `packages/tasks/requirements.json`: the version, the two new ids with their titles (REQ-LIB-8 and REQ-LIB-9), and the changed titles of REQ-LIB-1, REQ-LIB-2 and REQ-LIB-4.
|
||||
-- `packages/tasks/tests/requirements.test.mjs`: the version pin and the id count of 87.
|
||||
+- `packages/tasks/requirements.json`: the version 1.17, the two new ids REQ-LIB-8 and REQ-LIB-9 with their titles, and the changed titles of REQ-LIB-1, REQ-LIB-2 and REQ-LIB-4. The new ids go after REQ-LIB-7 and before REQ-INST-1, in the same order as the PRD's bullets.
|
||||
+- `packages/tasks/tests/requirements.test.mjs`: the version pin 1.17, the id count of 87, and the duplicate-id message, which goes from `requirements[85].id: REQ-ROLE-1 twice` to `requirements[87].id: REQ-ROLE-1 twice`.
|
||||
+- `packages/bus/tests/requirements.test.mjs`: the list length, which goes from 85 to 87.
|
||||
|
||||
The gate would match 1.16's:
|
||||
1. A reviewer approves the text.
|
||||
2. Jason says yes to the exact text through Mos.
|
||||
-3. `requirements.test.mjs` and every `scripts/test-*.sh` that reads the PRD or `requirements.json` are green on Sage's gate.
|
||||
+3. Both `requirements.test.mjs` files, in `packages/tasks/tests` and `packages/bus/tests`, and every `scripts/test-*.sh` that reads the PRD or `requirements.json` are green on Sage's gate.
|
||||
4. Mos gets the landing commit.
|
||||
@@ -1575,3 +1575,5 @@ are never rewritten or removed; corrections are new entries.
|
||||
- 2026-10-23T07:59:57Z | filbert (Claude Opus 5.5, T3 048f3c63) | row 299 manifest round 1 (#1806, Vikunja 573, Gander, manifest 277ace0b on ffa93b054) | approve in 31084, rev 3485; static on git objects (blobs = reviewed 1ec2b9765, product = base 80793d824); gate receipts 63db96d8b read, not rerun: case1 53/0 x3, cli 917/0 (59 files), M1-M6 killed; N1 M1 margin 0.02 s
|
||||
- 2026-10-23T08:10:29Z | sage (Opus 5.5, 1ef1e4f8) | 299 gate and landing, 303 review, 219/295 rebases | 299 LANDED ffb2fedfb 08:03Z (done 3487, #1806 c31087); queue-commit 8a35ffadb (revs 3474-3487); 303 Duckworth approve rev 3480; requests posted as sage for 303 (c31082) and 299 (c31083); 219 full restart on bfa421b6c from 08:08Z (sage-3283)
|
||||
- 2026-10-23T10:48:40Z | sage (Opus 5.5, 1ef1e4f8) | 302/295/219 landings, Docker pool diag, 590/591 briefs, 272 rebase | 302 LANDED 3ed9fd754, 295 LANDED 2efd127ce, 219 LANDED 5fdfd7275 (#1720 c31110, P19 416 c913); test-release 11/3 from exhausted Docker pools, 18 networks removed (mos-1437/1438); queue-commit 90633c864 (revs 3495-3510, 3510 Huey's); 272 rebase and fallback ruled (sage-3357)
|
||||
2026-10-23T11:03:46Z | Gyro (Codex gpt-6.1-sol, mos-1444) | Vikunja571 PRD d8 docs alignment | R6 vocabulary boundary, mapped-folder eligibility and R14 whole-host recovery updated in parent/W4; static checks only, no tests or live changes. Fenton reviews; Sage lands.
|
||||
- 2026-10-23T11:47:32Z | filbert (Claude Opus 5.5, T3 048f3c63) | row 305 round 1 (#1812, Vikunja 591, Gizmoduck, candidate 7e8218a2e) | approve in 31127, rev 3534; static on git objects (scripts = 3bce852d5; traps kept, no compose down, ls failure fails, skip only on docker info); gate receipts read, not rerun: Gizmoduck run1 + Sage gate task 92/0, release 15/0, mutants a/b red, c survives
|
||||
@@ -265,7 +265,9 @@ After each file's conversion publication, the authorized format map is a typed h
|
||||
sourceCommit,conversionCommit}`. kind is chapter or supporting-document; current chapter
|
||||
format is docx, supporting format markdown; state is current or converted-archive. scope distinguishes canon, playground, supporting
|
||||
and archived without format-based promotion. Type-folder entity notes remain Markdown;
|
||||
an unmarked-folder story is eligible for human selection, one file at a time (mos-1368 Q18). location
|
||||
a story file in an unmarked folder inside a book or playground is eligible for human selection,
|
||||
one file at a time. A story file outside every book and playground is unmapped and converts
|
||||
only after the human maps its folder, per PRD 1.17 REQ-LIB-8 and mos-1368 Q18. location
|
||||
is an opaque owned location, never a host path/capability. Publish only entries the caller's
|
||||
live scopes allow, no title/path leak from denied documents. Agents get this map through
|
||||
reviewed library/universe lookup results and normal authorized Case 2 context so they know formats
|
||||
@@ -321,8 +323,9 @@ amendment, W4b PASS and live committed-reader/citation readiness, W7 reviewed im
|
||||
release, Hermes retirement (#423 step 5, with its old write/store access removed), verified
|
||||
host-only off-host durability and an authenticated human start. `library.convert` is a
|
||||
human-only CLI/Console verb run through the host async processing adapter, outside agent
|
||||
vocabulary/role grants and off the read-verb list. Its action remains a hard wait under
|
||||
REQ-WAIT-2/3 until the approved PRD says otherwise; it never joins ROUTED silently.
|
||||
vocabulary/role grants and off the read-verb list. It is not in ACTIONS or ROUTED and never
|
||||
reaches the broker. A role file naming an action outside the vocabulary is already refused.
|
||||
REQ-WAIT-1 and its hard-wait test remain unchanged (mos-1444 R6).
|
||||
`editor.conversionHumans` is human-only, default business owner only. Folder setting/mapping
|
||||
have one authoritative archive-prefix value; the library mapping and editor cannot disagree.
|
||||
Archive prefixes cannot overlap playground/supporting prefixes. Conversion preserves bytes
|
||||
@@ -1155,7 +1158,7 @@ The source archive and private DOCX repository cannot publish atomically through
|
||||
`packages/bus/tests/{writing-save.test.mjs,chapter-format.test.mjs,schema.test.mjs,conversion-events.test.mjs}`,
|
||||
`packages/harness/src/tools.mjs`, `packages/harness/tests/chapter-format.test.mjs`,
|
||||
`packages/business/src/{vocabulary.mjs,business.mjs}`,
|
||||
`packages/business/tests/{vocabulary.test.mjs,business.test.mjs,hard-waits.test.mjs}`,
|
||||
`packages/business/tests/{vocabulary.test.mjs,business.test.mjs}`,
|
||||
`scripts/mosaic`, `packages/cli/src/library.mjs`, `packages/cli/tests/library-convert.test.mjs`,
|
||||
`roles/{companion,continuity,fact-checker,ideas,proofreader,research}.md`,
|
||||
`packages/webui/src/{writing-http.mjs,library-http.mjs,cli.mjs,serve.mjs,bus.mjs}`,
|
||||
@@ -1214,6 +1217,11 @@ bus restore. Test the third case explicitly: convert, then rollback --restore a
|
||||
backup, losing the bus conversion event while archive and DOCX commits remain. The normal
|
||||
counted-event approval/loss protocol applies; reconcile immutable commits/journal with new
|
||||
recovery events before lookups resume. Never revive Markdown or replay authorization grants.
|
||||
The fourth recovery case restores the whole host from a backup taken before an import,
|
||||
losing its writing store and host import records as well as bus events. Rebuild the host
|
||||
record from the import manifests on the writing store's off-host remote and the archive
|
||||
commit in Gitea. Chapter lookups refuse until recovery proves one current version.
|
||||
Restoring VM 1012 boots it, so Jason is present under Q185 (mos-1444 R14).
|
||||
Old release capability preflight must refuse converted projects it cannot interpret.
|
||||
|
||||
Update the six creative .md role contracts' obsolete 'Every item cites the lines' text:
|
||||
@@ -1222,8 +1230,9 @@ source format/scope from the map, archived never canon, and no story/conversion
|
||||
still use only their approved lookups/message/supporting-write tools. Schema/role expansion
|
||||
waits for the approved PRD, and existing role JSON never grants library.convert. Expose the
|
||||
human CLI/Console conversion verb through runtime/processing.work and async readback, not
|
||||
the agent tool list. Hard-wait tests are at business/tests/hard-waits.test.mjs:22; the new
|
||||
action remains GATED_ONLY unless the approved PRD explicitly changes it.
|
||||
the agent tool list. library.convert is absent from ACTIONS and ROUTED; host processing
|
||||
never sends it to the broker. Role vocabulary validation already refuses an unknown action.
|
||||
REQ-WAIT-1 and packages/business/tests/hard-waits.test.mjs remain unchanged (mos-1444 R6).
|
||||
|
||||
The importer lives at `packages/writing/src/import-markdown.mjs`, owns a reviewed AST mapping
|
||||
from vendored markdown-it to the WordCraft DOCX model, and validates source token/metadata,
|
||||
|
||||
@@ -15,8 +15,9 @@ from the reviewed baseline except their replaced per-document-scope wording.
|
||||
Q235–Q237 P1–P6 and mos-1372 D1/D2 settle the design basis: canon and playground story
|
||||
files become DOCX, retaining universe/book and source scope. Type-folder notes, agent files,
|
||||
instructions, PRDs, character sheets, summaries and supporting documentation stay Markdown.
|
||||
An unmarked-folder story is eligible; the human selects one story file at a time. New
|
||||
stories start directly as DOCX in the human-selected universe/book or playground, without a
|
||||
A story file in an unmarked folder inside a book or playground is eligible, and the human
|
||||
selects one story file at a time. An unmapped story file converts only after the human maps
|
||||
its folder. New stories start directly as DOCX in the human-selected universe/book or playground, without a
|
||||
Markdown precursor, under the same production-opening gate as import.
|
||||
|
||||
The October 27 release stays Markdown. Production import/new-story opening waits for W4b,
|
||||
@@ -24,7 +25,9 @@ W7, the post-10-27 release, the approved Case 2 PRD/brief amendment, Hermes reti
|
||||
(#423 step 5) and configured off-host durability. The first real use on Melanie's books
|
||||
also waits for Jason's scoped go and mos-1374's protected manual VM1012 backup before the
|
||||
first real import, handled by its authorized human operator, not this design seat. All starts are human-only; `library.convert` has no role
|
||||
grant or agent request path and stays a hard wait unless an approved PRD changes it.
|
||||
grant or agent request path. It is not in ACTIONS or ROUTED; the human host processing
|
||||
adapter never sends it to the broker. A role file naming an action outside the vocabulary
|
||||
is already refused. REQ-WAIT-1 and its hard-wait test remain unchanged (mos-1444 R6).
|
||||
Nothing in this document authorizes conversion, new private story creation or deployment.
|
||||
Q234's exact frame CSP, mos-1363 and S0 M1–M14 remain unchanged implementation gates.
|
||||
|
||||
@@ -592,9 +595,10 @@ join stack-release COUNTED and T46 exactly once, so post-backup conversion event
|
||||
to --events and the existing restore approval/counting behavior. No new private conversation,
|
||||
per-document grant or per-send share tables are created for D9.
|
||||
|
||||
Three recovery cases are required: (1) crash after durable DOCX staging but before archive
|
||||
Four recovery cases are required: (1) crash after durable DOCX staging but before archive
|
||||
push, (2) crash after archive push before publication, and (3) rollback --restore from a
|
||||
pre-conversion bus backup while archive commits and O2 documents remain. The source is pinned
|
||||
pre-conversion bus backup while archive commits and O2 documents remain, and (4) a whole-host
|
||||
restore from a backup taken before an import. The source is pinned
|
||||
until final publication in cases1/2; after archive push state is visibly pending, never
|
||||
'original untouched'. Case3 drops conversion rows only through the counted/approved restore
|
||||
path. The durable host conversion journal/format registry is not restored from that older bus
|
||||
@@ -606,6 +610,12 @@ pending operation to the human. Do not replay chapter text into an old index or
|
||||
Incompatible old binaries cannot activate converted projects through the supported controller;
|
||||
raw privileged rollback tampering is outside the guarantee. Never rewrite git/run history.
|
||||
|
||||
Case4 also loses the local writing store and host import records. Recovery rebuilds the
|
||||
host record from the import manifests on the writing store's off-host remote and the
|
||||
archive commit in Gitea. Chapter lookups refuse until it proves one current version;
|
||||
missing or inconsistent remote evidence cannot revive old Markdown as current. Restoring
|
||||
VM 1012 boots it, so Jason is present under Q185 (mos-1444 R14).
|
||||
|
||||
Hermes retirement (#423 step5) is a production-opening prerequisite, with old story-write and
|
||||
store-read access removed before any conversion/new-story operation. W7 checks its retirement
|
||||
receipt and host capability preflight; no design seat retires services or accesses Melanie's
|
||||
@@ -644,7 +654,7 @@ These are required future fixture gates, not implementation results.
|
||||
| A24 | Full page and oversized paragraph | end before overflow; first oversized row refuses without text; no unreachable subrange claim |
|
||||
| A25 | Every read/write/tool surface uses existing Case 2 isolation | same document ID across business/universe or replayed cursor refuses |
|
||||
| A26 | Host-only off-host remote and per-save push | unset/local-only/helper-readable/unexpected remote refuses opening; real helper denial tested; push outage visible, local next lookup still fresh |
|
||||
| A27 | Conversion restore accounting and durable journal reconciliation | pre-conversion --restore loses counted row but cannot revive Markdown; no lost-event concealment |
|
||||
| A27 | Conversion restore accounting and durable journal reconciliation | pre-conversion bus --restore or whole-host restore reconciles remote manifests and archive commit; lookups refuse until one current version is proven; no lost-event concealment |
|
||||
| A28 | Reopened M1 supports same-thread DOCX-read then supporting-note write | any room/project-authorized caller works; no per-document/share token; story/archive write refuses |
|
||||
| A29 | Readiness is reviewed runtime/extractor plus approved PRD, not caller fields | no request/go/setting can assert release or Hermes retirement |
|
||||
| A30 | Canonical schemas and complete selections | unknown keys, inconsistent ranges, xmlId null mismatch and noncanonical story IDs refuse |
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
# Row 270's pi compaction reading moves behind the harness kind
|
||||
|
||||
Found during row 272's gate (#1777) on 10-23 (sage-3432). Mos ruled the move in mos-1455 item 3 and
|
||||
declined an EXCEPT entry. Vikunja 598.
|
||||
|
||||
## Brief: row 270's pi compaction reading moves behind the harness kind (Vikunja 598)
|
||||
|
||||
Gitea #1814. Owner Gizmoduck, who wrote row 270. Reviewer Duckworth, who reviewed it. Gate owner Sage.
|
||||
Base: refactor at this brief's commit. Design first, in `agents/gizmoduck/work/queue-307/DESIGN.md`.
|
||||
|
||||
`packages/plugins/tests/harness.test.mjs:192` (row 65) checks that no core source outside
|
||||
`packages/plugins` names a harness. It is red on refactor 1e4c1e6e9. Its `named` list is three lines
|
||||
of `packages/seat/src/compactions.mjs`:
|
||||
|
||||
- :232, where `readLedger` marks a ledger unsupported when a collection's harness isn't `"pi"`;
|
||||
- :283 and :287, where `collectCompactions` branches on `harness === "pi"`.
|
||||
|
||||
Row 270 (#1775, 8c28ee731) added them. The 270+271 gate didn't run the plugins suite.
|
||||
|
||||
REQ-PLUG-2 (`docs/prd/mosaic-stack.md:1001`) sends new work that calls out to a part of a plug-in kind
|
||||
through that kind from the start. Row 270 was new work, so its pi reading belongs in the pi harness
|
||||
module, not in core.
|
||||
|
||||
Change:
|
||||
|
||||
- The harness kind (`packages/plugins/src/kinds.mjs:52`) gains an export that collects a session
|
||||
store's compactions. A core change through `kinds.mjs` is in scope, as are its callers and its
|
||||
contract in `packages/plugins/README.md`. The design names the export, its arguments, its answer,
|
||||
and whether `interfaceVersion` moves.
|
||||
- pi's module owns the stream reading (`scanPi`, `PI_VERSION`, the Claude-history check) and answers
|
||||
what `collectCompactions` writes for pi today. claude-code's module answers harness-unsupported, as
|
||||
it does now.
|
||||
- Core keeps the ledger: `readLedger`, `compactionCount`, the O_APPEND write and the `collected` line.
|
||||
No core line names a harness. `readLedger`'s unsupported test reads a field the module's answer
|
||||
carries, not a harness name.
|
||||
- `packages/cli/src/launcher.mjs:312` calls through the kind. `packages/bus/src/runtime.mjs:282` and
|
||||
`broker.mjs` keep reading counts through `compactionCount`.
|
||||
- A ledger that row 270's code wrote gives the same count after the move, including the pi,
|
||||
claude-code and Claude-history cases. Nothing rewrites a ledger.
|
||||
|
||||
Tests, red first under mos-1272:
|
||||
|
||||
- a fixture ledger written by the current code reads the same count before and after the move, one
|
||||
per reason row 270 writes;
|
||||
- pi's module counts a fixture stream the way `scanPi` does now, with the read budget and stream cap;
|
||||
- a harness module without the export refuses at load (`packages/plugins/src/loader.mjs:61-64`).
|
||||
|
||||
`harness.test.mjs` stays unchanged and goes green. Any change to an existing test goes to Mos through
|
||||
Sage first. A new required export breaks every existing test fixture that builds a harness module, so
|
||||
DESIGN.md lists those fixtures (grep the tests for `interfaceVersion` and the harness exports). Those
|
||||
edits go to Mos with the design.
|
||||
|
||||
Mutants:
|
||||
|
||||
- core branches on a harness name again;
|
||||
- pi's module drops the Claude-history check;
|
||||
- `readLedger` treats every collection as supported.
|
||||
|
||||
Gate: Duckworth approves the design and then the manifest. On Sage's gate, the bus, cli, plugins,
|
||||
webui and business suites run green, plugins with no failures at all, along with every
|
||||
`scripts/test-*.sh`, and the mutants are killed.
|
||||
+9
-5
@@ -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 3510 by `queue render`. Do not edit between the markers; change the queue with `scripts/mosaic queue`.
|
||||
Generated from `docs/plans/queue.json` revision 3571 by `queue render`. Do not edit between the markers; change the queue with `scripts/mosaic queue`.
|
||||
|
||||
| # | Piece | Owner | Issues | State | After | Gate | Brief | Note |
|
||||
|---|---|---|---|---|---|---|---|---|
|
||||
@@ -308,7 +308,7 @@ Generated from `docs/plans/queue.json` revision 3510 by `queue render`. Do not e
|
||||
| 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. |
|
||||
| 271 | 264d T1 item 7: clear context through row 194's lifecycle stop | gizmoduck; reviewers duckworth | #1776 | done | 194 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). Tests T4 and T6; rebased onto 194's landing with no 194 code copied (Q4). (sage) | `docs/plans/2026-10-21_t1-sessions.md` § T1 item 7: independent sessions, compaction counts and clear context | LANDED 7328da8ca (with 270, mos-1351) 02:13Z 10-23: tip 2b964a082 is in the 33-commit pick, range-diff all '='. Manifest 3ab1f5c8 (33 paths): 29 match the tip; BUILD.md(270), bus README, launcher.mjs and compactions.mjs moved under 270 and match 270's f4174458. Duckworth APPROVE rev 3380. Combined C2 run 5 GREEN, 271 M1-M14 killed. #1776 closes. |
|
||||
| 272 | 264e T1 item 7: mosaic session CLI and Console chooser | huey; reviewers scrooge | #1777 | in-progress | 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 T5 legacy single human and T7 Console half; controls for unlanded verbs refuse with a reason (Q1, Q7). (sage) | `docs/plans/2026-10-21_t1-sessions.md` § T1 item 7: independent sessions, compaction counts and clear context | — |
|
||||
| 272 | 264e T1 item 7: mosaic session CLI and Console chooser | huey; reviewers scrooge | #1777 | in-review, round 3, request posted | 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 T5 legacy single human and T7 Console half; controls for unlanded verbs refuse with a reason (Q1, Q7). (sage) | `docs/plans/2026-10-21_t1-sessions.md` § T1 item 7: independent sessions, compaction counts and clear context | — |
|
||||
| 273 | Q175 phase 2: stack-release.sh activate --send and --no-sso (F39, mos-1246) | darkwing; reviewers filbert | #1778 | done | — | Filbert approves the manifest. stack-release.test.mjs and stack-release-external.test.mjs run whole plus scripts/test-release.sh on Sage's slot, and the brief's mutants are killed. (sage) | `docs/plans/2026-10-22_activate-send-nosso.md` § Brief: activate --send and --no-sso | landed 0bbf0f6d1 03:35:55Z, FF of 378a76b85; rebase of cac7d0f67, 6 range-diff pairs =, patch-ids equal, manifest e1031023 unchanged. Gate by Darkwing on 0bbf0f6d1: stack-release.test 218/218, external 25/25, test-release.sh rc 0, T58 c0 pass c1 fail, M1-M7 killed. Filbert APPROVE rev 3111 c30308, carry c30319. Docs 5365adaef (TOOLS.md N1 + BUILD-LOG). #1778. |
|
||||
| 274 | FED R3: broker user principal (#414) | morgana; reviewers dewey | #1779 | done | — | Dewey approves the manifest. The bus and harness suites and every scripts/test-*.sh are green on Sage's gate, and the mutants are killed. (sage) | `docs/plans/2026-10-22_federation-r3.md` § Brief: FED R3, the broker user principal | LANDED 4c485c63b ff from 47a35bf43 (6 Morgana commits). Dewey APPROVE c30487 on 4c485c63b (manifest 00047b52, 13 paths). Gate 10:09-10:22Z: 23 touched files 0 fail, bus 787, harness 151, business 228, cli 832, seat 167, 10 scripts rc=0 (skips test-queue 1, test-task 2), 22/22 mutants. M3x wall code not-holder (kit comment said human-required). r5 f58cfc361 red, void. Overlap 13/257 clean. |
|
||||
| 275 | 193b1b: viewer verbs and board refusals (also after FED R4 when filed) | morgana; reviewers duckworth | #1780 | done | 205 done, 274 done | Duckworth approves the manifest. The bus suite and every scripts/test-*.sh are green on Sage's gate, and the mutants are killed. (sage) | `docs/plans/2026-10-20_human-users-build.md` § Brief: 193b1b, viewer verbs and board refusals | Landed 3410df7c3 on refactor (fast-forward from 48e450ce8, 3 commits; landed sha = candidate, so range-diff and patch-id are identical by construction). Manifest 39847b15 (4 paths) blobs match the commit. Gate: whole files 7/22/6, bus 834, cli 861, harness 151, business 231, seat 167, 0 fail; 10 scripts/test-*.sh rc 0 with 3 named skips; M6 and M10 killed. Duckworth APPROVE round 1, Gitea #1780 comment 30717. |
|
||||
@@ -336,10 +336,14 @@ Generated from `docs/plans/queue.json` revision 3510 by `queue render`. Do not e
|
||||
| 297 | GitHub grant method (Bushroot's half of #436b) | bushroot; reviewers dewey | #1803 | briefed | 198 done | Dewey approves the manifest. The touched suites and every scripts/test-*.sh are green on Sage's gate, and the mutants are killed. (sage) | `docs/plans/2026-10-23_mvp-readiness-rows.md` § Brief: the GitHub grant method (Bushroot's half of #436b) | — |
|
||||
| 298 | Q228: chief-of-staff grant scope | megavolt; reviewers dewey | #1804 | briefed | 252 done | Dewey approves the manifest. The touched suites and every scripts/test-*.sh are green on Sage's gate, and the mutants are killed. (sage) | `docs/plans/2026-10-23_mvp-readiness-rows.md` § Brief: the chief-of-staff grant scope (Q228) | — |
|
||||
| 299 | 573: the P10 hop band (Vikunja 573) | gander; reviewers filbert | #1806 | done | 293 done | Filbert approves. The whole file under load at least 3 times, then the cli suite, are green on Sage's gate. (sage) | `docs/plans/2026-10-23_mvp-readiness-rows.md` § Brief: 573, the P10 hop band (Vikunja 573) | landed ffb2fedfb 08:03Z on 15e688cc0 via Filbert SESSIONS c1f910b4c; 5 commits = gander/row299-gate-277ace0b 63db96d8b (range-diff 5/5 =, tree differs only BUILD-LOG/SESSIONS); manifest 277ace0b sha256 -c OK. Gate green on ffa93b054: case1 whole 3x 53/0/0, cli whole 917/0/0 (59 files), P10 inner 2.27 outer 4.01, M1-M6 killed (gander-0189). Filbert approve rev 3485 c31084; N1 M1 1.23 s margin. |
|
||||
| 300 | R6-N1: HTTP-level test of the user-session option swap (294 N1) | launchpad; reviewers duckworth | #1807 | briefed | 294 done | Duckworth approves the manifest; new test file whole, webui suite, mutant M12 killed (sage) | `docs/plans/2026-10-23_r6-followup-rows.md` § Brief: R6-N1, an HTTP-level test of the user-session option swap | — |
|
||||
| 301 | R6-N4: fold user-bus.mjs back into bus.mjs (294 N4) | launchpad; reviewers duckworth | #1808 | briefed | 294 done | Duckworth approves the manifest; channel-proof.test, webui suite and 294 user-transport tests whole and green; re-pin only with Mos go (sage) | `docs/plans/2026-10-23_r6-followup-rows.md` § Brief: R6-N4, fold user-bus.mjs back into bus.mjs | — |
|
||||
| 300 | R6-N1: HTTP-level test of the user-session option swap (294 N1) | launchpad; reviewers duckworth | #1807 | done | 294 done | Duckworth approves the manifest; new test file whole, webui suite, mutant M12 killed (sage) | `docs/plans/2026-10-23_r6-followup-rows.md` § Brief: R6-N1, an HTTP-level test of the user-session option swap | Landed on refactor as 9d929911c..07315e651 (from launchpad/row300 a00b25b37, range-diff equal), pushed 5b846b048 12:28Z. Duckworth approved r2 (c31158, rev 3559). Sage gate on bcf3c1dac: user-option-swap 1/0, webui 375/0 with pinned esbuild and MOSAIC_REQUIRE_EDITOR_BUILD=1, M12 killed at :151. Run 1 red placed under mos-1272 (headers). N1 deferred: board proxy upstream has no recorder. |
|
||||
| 301 | R6-N4: fold user-bus.mjs back into bus.mjs (294 N4) | launchpad; reviewers duckworth | #1808 | done | 294 done | Duckworth approves the manifest; channel-proof.test, webui suite and 294 user-transport tests whole and green; re-pin only with Mos go (sage) | `docs/plans/2026-10-23_r6-followup-rows.md` § Brief: R6-N4, fold user-bus.mjs back into bus.mjs | Landed on refactor as bb031ec84..5b846b048 (from launchpad/row301 acc97fb9c, range-diff equal), pushed 12:28Z. Re-pin of webui bus.mjs to 39fcb27c is its own commit 59f0e44f4 (mos-1449); fixture stubs per mos-1451. Duckworth approved r2 (c31159, rev 3560). Sage gate: channel-proof 2/0, webui 374/0 pinned esbuild, granted-user 19/0. Live unchanged; 301 makes its own re-pin window. |
|
||||
| 302 | Idle-stop race: an idle-released run's last call ends it session-invalid/22 (Vikunja 588) | gizmoduck; reviewers dewey | #1809 | done | 292 done | Dewey approves the design, then the manifest; forcing test, compactions-launch.test whole, bus/cli/harness suites, all scripts/test-*.sh; fix-drop mutant dies (sage) | `docs/plans/2026-10-23_idle-stop-race-row.md` § Brief: the idle-stop race (Vikunja 588) | Landed 3ed9fd754 08:49Z 10-23: Sage cherry-picked 0e30a85fa..2a8281a6f (7 commits) onto 1ac655005; range-diff all equal, patch-ids match, tree equals gated merge 783602e06 plus packet-only 2a8281a6f files. Gate run 2 green (bus 876, harness 151, cli 918, ten test-*.sh, M1-M5 M7-M9 killed). Dewey approve rev 3492 c31092. mos-1430 fixed launcher-released.test:89. mos-1411 rerun allowance ends. |
|
||||
| 303 | R9 shows its output when rehearse makes no directory (Vikunja 589) | launchpad; reviewers duckworth | #1810 | in-review, round 1, request posted | 294 done | Duckworth approves the diff; stack-release.test.mjs whole and green on Sage's gate, in a gap (sage) | `docs/plans/2026-10-23_r9-guard-row.md` § Brief: R9 shows its output when rehearse makes no directory (Vikunja 589) | — |
|
||||
| 303 | R9 shows its output when rehearse makes no directory (Vikunja 589) | launchpad; reviewers duckworth | #1810 | done | 294 done | Duckworth approves the diff; stack-release.test.mjs whole and green on Sage's gate, in a gap (sage) | `docs/plans/2026-10-23_r9-guard-row.md` § Brief: R9 shows its output when rehearse makes no directory (Vikunja 589) | Landed e551cd846 from launchpad 88ffecaf7, patch-id 559c802b equal, test blob e936808b matches the manifest. Gate: Duckworth approve c31079; Sage gate on c13c337e1 plus the test blob, stack-release.test.mjs whole 224/0 rc 0 in 595 s, alnum mktemp TMPDIR, default networks 6 before and 6 after. No overlap with in-review 272, 304, 305. |
|
||||
| 304 | claude-session test builds the memory slug the way the CLI does (Vikunja 590) | rocko; reviewers dewey | #1811 | done | — | Dewey approves the diff; claude-session.test.mjs whole and green 3x (mktemp, underscore, space TMPDIRs) on Sage's gate, in a gap; old-regex mutant red at :298 (sage) | `docs/plans/2026-10-23_gate-env-rows.md` § Brief: claude-session test builds the memory slug the way the CLI does (Vikunja 590) | Landed a863015a5 from rocko 5e8354ebb, range-diff equal, patch-id 808fa765. Gate: Dewey approve c31129 rev 3533; Sage gate on e551cd846 with file 7690a4a9, claude 2.1.309: claude-session.test.mjs whole 10/0 under mktemp, underscore and space TMPDIRs; old-regex mutant under underscore 9/1 at :298. Gates may drop the alnum TMPDIR rule (mos-1432 ends here). |
|
||||
| 305 | Shell suites remove the compose network they create (Vikunja 591) | gizmoduck; reviewers filbert | #1812 | done | — | Filbert approves the diff; test-release.sh and test-task.sh whole and green on Sage's gate, in a gap, project network absent after each run; dropped-rm mutant red (sage) | `docs/plans/2026-10-23_gate-env-rows.md` § Brief: shell suites remove the compose network they create (Vikunja 591) | Landed 353d65f57..d52d9f1f2 from gizmoduck 541dee2b7..7e8218a2e, range-diff all equal, on top of filbert 2c289f6f0. Gate: Filbert approve c31127 rev 3534; Sage gate on e551cd846 with task 7d6fbf67, release b29850ce: test-task 92/0, test-release 15/0, network check OK; mutants a and b red in both, c a known survivor; own mutant networks removed, none left. Gates stop the manual network rm (mos-1438 ends here). |
|
||||
| 306 | A revoked run's late bus call stops it instead of exiting 22 (295 N1) | morgana; reviewers dewey | #1813 | briefed | 272 done | Dewey approves the design, then the manifest; bus, harness and cli suites and every scripts/test-*.sh green on Sage's gate (sage) | `docs/plans/2026-10-23_gate-env-rows.md` § Brief: a revoked run's late bus call stops it instead of exiting 22 (295 N1) | — |
|
||||
| 307 | Row 270's pi compaction reading moves behind the harness kind (REQ-PLUG-2, Vikunja 598) | gizmoduck; reviewers duckworth | #1814 | briefed | — | Duckworth approves the design, then the manifest; bus, cli, plugins (0 failures), webui and business suites, every scripts/test-*.sh green and the mutants killed on Sage's gate (sage) | `docs/plans/2026-10-23_harness-kind-compactions.md` § Brief: row 270's pi compaction reading moves behind the harness kind (Vikunja 598) | — |
|
||||
|
||||
<!-- mosaic-queue:end -->
|
||||
|
||||
|
||||
+2077
-20
File diff suppressed because it is too large.
Load diff
@@ -25,7 +25,8 @@ const baselines = {
|
||||
// Row 171 (#1668): project-name-is-repo maps to 409; no proof or transport change.
|
||||
// Row 194 (#1692, brief item 7, sage-1610 a): organization-tracker-required 400 and
|
||||
// organization-tracker-bot-taken 409; no proof or transport change.
|
||||
"packages/webui/src/bus.mjs": "15e8123ffdbb6d067e8a516f5823efdb5de3c869963764f27cd14bb9e4ce3313",
|
||||
// Row 301 (#1808, mos-1449): the user transport moved from user-bus.mjs into bus.mjs; humanCall unchanged.
|
||||
"packages/webui/src/bus.mjs": "39fcb27c82e917ef05e1a7a650d522c9f3c656bf8dbade87b8ddb3661b67ad6c",
|
||||
};
|
||||
const repoFile = path => new URL("../../../" + path, import.meta.url);
|
||||
|
||||
|
||||
@@ -2175,6 +2175,7 @@ test('R9 mos-0963: a role reference left outside placeholders/, or left on an en
|
||||
ownScript(e,'for(const [name,r] of Object.entries(b.roles??{})){strip(r?.vars);swap(name,r?.credentials)}','for(const [name,r] of Object.entries(b.roles??{})){strip(r?.vars);if(name!=="pm")swap(name,r?.credentials)}');
|
||||
const r=await rehearse(e,B,'--backup',e.act,'--port',await freePort(),'--timeout','3');
|
||||
assert.equal(r.code,1,r.out+r.err);
|
||||
assert.ok(r.dir!==null, r.out+r.err);
|
||||
assert.ok(r.out.includes(`FAIL credential-ref-live: pm gitea is ${tok}, not a file in ${join(r.dir,'placeholders')}\n`),r.out);
|
||||
assert.deepEqual(r.names.slice(-3),['FAIL credential-ref-live','PASS backup-unchanged','PASS live-config-unchanged']);
|
||||
assert.equal(existsSync(rh(e,'host-args.log')),false);assert.equal(existsSync(rh(e,'console-args.log')),false);
|
||||
|
||||
@@ -269,7 +269,7 @@ test("claude adapter: a directory pin exits 2 before it runs; a pin with a space
|
||||
|
||||
test("claude: CLAUDE.md files and auto-memory don't reach the model; without --restricted they do", { skip }, async (t) => {
|
||||
const { dir, workspace, env } = await session(t, []);
|
||||
const slug = realpathSync(workspace).replace(/[/.]/g, "-");
|
||||
const slug = realpathSync(workspace).replace(/[^A-Za-z0-9]/g, "-");
|
||||
const plant = {
|
||||
"MARKER-USER": [join(env.HOME, ".claude", "CLAUDE.md"), join(env.CLAUDE_CONFIG_DIR, "CLAUDE.md")],
|
||||
"MARKER-PARENT": [join(dir, "CLAUDE.md")],
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
import { spawn } from 'node:child_process';
|
||||
import { randomBytes } from 'node:crypto';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { HUMAN_CLI } from '../../cli/src/transport.mjs';
|
||||
import { readHostState } from '../../cli/src/host.mjs';
|
||||
import { views } from '../../bus/src/views.mjs';
|
||||
@@ -72,6 +73,63 @@ export function busCaller({ dataRoot, socket, business = null, env = process.env
|
||||
}
|
||||
export const busReader = options => views({ call: busCaller(options) });
|
||||
|
||||
// FED R6 (#1800): a granted user's bus transport, R3's user-cli.mjs (#414, design 3.6). Same
|
||||
// protocol as humanCall: the request on stdin, JSON on stdout, or a bus error code as the last
|
||||
// `^[a-z-]{1,64}$` line of stderr. Row 301 (#1808) moved it here from user-bus.mjs.
|
||||
export const USER_CLI = fileURLToPath(new URL('../../bus/src/user-cli.mjs', import.meta.url));
|
||||
|
||||
// The Console sets the user nonce itself, so user-cli.mjs doesn't re-exec and the broker sees the
|
||||
// Console (`cli.mjs serve`) as its parent (principal.mjs). The human nonce is dropped, so this child
|
||||
// can never pass as human-cli, and it only ever runs USER_CLI.
|
||||
export function userCall({ socket, business, user, env = process.env, timeoutMs = 30000, maxBytes = 8 * 1024 * 1024 }) {
|
||||
const { MOSAIC_BUS_CLI_NONCE, ...rest } = env;
|
||||
return (verb, args = {}) => new Promise((resolve, reject) => {
|
||||
let child;
|
||||
try {
|
||||
child = spawn(process.execPath, [USER_CLI, socket], { env: { ...rest, MOSAIC_BUS_USER_NONCE: randomBytes(32).toString('hex') }, stdio: ['pipe', 'pipe', 'pipe'] });
|
||||
} catch { return reject(new BusReadError('outcome-unknown', 'the bus transport did not start')); }
|
||||
const out = [], err = [];
|
||||
let size = 0, settled = false;
|
||||
const done = (fn, value) => { if (!settled) { settled = true; clearTimeout(timer); fn(value); } };
|
||||
const timer = setTimeout(() => { child.kill('SIGKILL'); done(reject, new BusReadError('outcome-unknown', 'the bus transport did not finish in time')); }, timeoutMs);
|
||||
child.stdout.on('data', chunk => {
|
||||
size += chunk.length;
|
||||
if (size > maxBytes) { child.kill('SIGKILL'); return done(reject, new BusReadError('response-too-large')); }
|
||||
out.push(chunk);
|
||||
});
|
||||
child.stderr.on('data', chunk => { if (err.length < 64) err.push(chunk); });
|
||||
child.on('error', () => done(reject, new BusReadError('outcome-unknown', 'the bus transport did not start')));
|
||||
child.on('close', status => {
|
||||
if (status === null) return done(reject, new BusReadError('outcome-unknown', 'the bus transport did not finish'));
|
||||
if (status !== 0) {
|
||||
const code = Buffer.concat(err).toString('utf8').trim().split('\n').reverse().find(l => /^[a-z-]{1,64}$/.test(l)) ?? 'invalid-response';
|
||||
return done(reject, new BusReadError(code));
|
||||
}
|
||||
try { done(resolve, JSON.parse(Buffer.concat(out).toString('utf8'))); }
|
||||
catch { done(reject, new BusReadError('invalid-response')); }
|
||||
});
|
||||
child.stdin.on('error', () => {}); // A child that exits early reports through 'close'.
|
||||
child.stdin.end(`${JSON.stringify({ business, user, verb, args })}\n`);
|
||||
});
|
||||
}
|
||||
|
||||
// The business is the --business flag, or else the live bus host's, read on every call, as busCaller does.
|
||||
export function busBusiness({ dataRoot, business = null }) {
|
||||
return () => {
|
||||
if (business) return business;
|
||||
let state;
|
||||
try { state = readHostState(dataRoot); } catch { throw new BusReadError('no-bus-host', 'the bus host state file is unreadable'); }
|
||||
if (!state?.live) throw new BusReadError('no-bus-host', 'no bus host is running and no --business was given');
|
||||
return state.business;
|
||||
};
|
||||
}
|
||||
|
||||
// One granted user's caller, for that user's session only.
|
||||
export function userCaller({ dataRoot, socket, business = null, user, env = process.env, timeoutMs = 30000 }) {
|
||||
const which = busBusiness({ dataRoot, business });
|
||||
return async (verb, args) => userCall({ socket, business: which(), user, env, timeoutMs })(verb, args);
|
||||
}
|
||||
|
||||
// A send's error, sorted. The broker writes in one transaction, so any code it
|
||||
// returns means nothing was written. A transport that lost the answer, or an
|
||||
// answer that can't be read, leaves the outcome open: the message may exist.
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
// MOSAIC_TESTER_SOCKET_V1: private tester ingress, no tester TCP.
|
||||
import { createChat } from './chat.mjs';
|
||||
import { startServer, previewOptions, proofOptions, DEFAULT_BOARD } from './serve.mjs';
|
||||
import { busCaller } from './bus.mjs';
|
||||
import { busBusiness, userCaller } from './user-bus.mjs';
|
||||
import { busCaller, busBusiness, userCaller } from './bus.mjs';
|
||||
import { consoleBus, userOptionsFor, agentCaller, TESTER_AGENT_VERBS } from './console-bus.mjs';
|
||||
import { consoleAccess } from './access.mjs';
|
||||
import { accessContextFor } from '../../cli/src/access.mjs';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// FED R6 (#1800): the Console's bus-backed options for startServer, built over one transport
|
||||
// `call`. cli.mjs builds them once over the human transport. A granted user's session gets the same
|
||||
// set over its own user transport (user-bus.mjs userCaller), so no route a user reaches holds a
|
||||
// set over its own user transport (bus.mjs userCaller), so no route a user reaches holds a
|
||||
// human-transport object, and the broker decides every verb the user sends (design 3.1 D4).
|
||||
import { createChat } from './chat.mjs';
|
||||
import { views } from '../../bus/src/views.mjs';
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
// FED R6 (#1800): a granted user's bus transport, R3's user-cli.mjs (#414, design 3.6). Kept apart
|
||||
// from bus.mjs so the human transport's reviewed bytes stay as row 164 froze them
|
||||
// (bus/tests/channel-proof.test.mjs). Same protocol as humanCall: the request on stdin, JSON on
|
||||
// stdout, or a bus error code as the last `^[a-z-]{1,64}$` line of stderr.
|
||||
import { spawn } from 'node:child_process';
|
||||
import { randomBytes } from 'node:crypto';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { readHostState } from '../../cli/src/host.mjs';
|
||||
import { BusReadError } from './bus.mjs';
|
||||
|
||||
export const USER_CLI = fileURLToPath(new URL('../../bus/src/user-cli.mjs', import.meta.url));
|
||||
|
||||
// The Console sets the user nonce itself, so user-cli.mjs doesn't re-exec and the broker sees the
|
||||
// Console (`cli.mjs serve`) as its parent (principal.mjs). The human nonce is dropped, so this child
|
||||
// can never pass as human-cli, and it only ever runs USER_CLI.
|
||||
export function userCall({ socket, business, user, env = process.env, timeoutMs = 30000, maxBytes = 8 * 1024 * 1024 }) {
|
||||
const { MOSAIC_BUS_CLI_NONCE, ...rest } = env;
|
||||
return (verb, args = {}) => new Promise((resolve, reject) => {
|
||||
let child;
|
||||
try {
|
||||
child = spawn(process.execPath, [USER_CLI, socket], { env: { ...rest, MOSAIC_BUS_USER_NONCE: randomBytes(32).toString('hex') }, stdio: ['pipe', 'pipe', 'pipe'] });
|
||||
} catch { return reject(new BusReadError('outcome-unknown', 'the bus transport did not start')); }
|
||||
const out = [], err = [];
|
||||
let size = 0, settled = false;
|
||||
const done = (fn, value) => { if (!settled) { settled = true; clearTimeout(timer); fn(value); } };
|
||||
const timer = setTimeout(() => { child.kill('SIGKILL'); done(reject, new BusReadError('outcome-unknown', 'the bus transport did not finish in time')); }, timeoutMs);
|
||||
child.stdout.on('data', chunk => {
|
||||
size += chunk.length;
|
||||
if (size > maxBytes) { child.kill('SIGKILL'); return done(reject, new BusReadError('response-too-large')); }
|
||||
out.push(chunk);
|
||||
});
|
||||
child.stderr.on('data', chunk => { if (err.length < 64) err.push(chunk); });
|
||||
child.on('error', () => done(reject, new BusReadError('outcome-unknown', 'the bus transport did not start')));
|
||||
child.on('close', status => {
|
||||
if (status === null) return done(reject, new BusReadError('outcome-unknown', 'the bus transport did not finish'));
|
||||
if (status !== 0) {
|
||||
const code = Buffer.concat(err).toString('utf8').trim().split('\n').reverse().find(l => /^[a-z-]{1,64}$/.test(l)) ?? 'invalid-response';
|
||||
return done(reject, new BusReadError(code));
|
||||
}
|
||||
try { done(resolve, JSON.parse(Buffer.concat(out).toString('utf8'))); }
|
||||
catch { done(reject, new BusReadError('invalid-response')); }
|
||||
});
|
||||
child.stdin.on('error', () => {}); // A child that exits early reports through 'close'.
|
||||
child.stdin.end(`${JSON.stringify({ business, user, verb, args })}\n`);
|
||||
});
|
||||
}
|
||||
|
||||
// The business is the --business flag, or else the live bus host's, read on every call, as busCaller does.
|
||||
export function busBusiness({ dataRoot, business = null }) {
|
||||
return () => {
|
||||
if (business) return business;
|
||||
let state;
|
||||
try { state = readHostState(dataRoot); } catch { throw new BusReadError('no-bus-host', 'the bus host state file is unreadable'); }
|
||||
if (!state?.live) throw new BusReadError('no-bus-host', 'no bus host is running and no --business was given');
|
||||
return state.business;
|
||||
};
|
||||
}
|
||||
|
||||
// One granted user's caller, for that user's session only.
|
||||
export function userCaller({ dataRoot, socket, business = null, user, env = process.env, timeoutMs = 30000 }) {
|
||||
const which = busBusiness({ dataRoot, business });
|
||||
return async (verb, args) => userCall({ socket, business: which(), user, env, timeoutMs })(verb, args);
|
||||
}
|
||||
@@ -19,8 +19,7 @@ import { startServer, userRoute } from '../src/serve.mjs';
|
||||
import { createSso, SESSION_COOKIE } from '../src/sso.mjs';
|
||||
import { consoleAccess } from '../src/access.mjs';
|
||||
import { consoleBus, userOptionsFor } from '../src/console-bus.mjs';
|
||||
import { BusReadError } from '../src/bus.mjs';
|
||||
import { USER_CLI, userCall } from '../src/user-bus.mjs';
|
||||
import { BusReadError, USER_CLI, userCall } from '../src/bus.mjs';
|
||||
import { close, fixture } from './fixture.mjs';
|
||||
import { browser } from './browser.mjs';
|
||||
import { fakeOidc } from './helpers/fake-oidc.mjs';
|
||||
|
||||
@@ -14,7 +14,7 @@ test('C2-04 CLI: both listener callback pairs dispatch library verbs with their
|
||||
// The loader supplies a fixture bus and captures callbacks instead of
|
||||
// opening an HTTP listener. Production imports and routes are unchanged.
|
||||
const serveSource = "export const DEFAULT_BOARD='http://127.0.0.1:9';export const previewOptions=()=>null;export const proofOptions=()=>null;export async function startServer(o){if(typeof o.libraryFile!=='function'||typeof o.libraryStatus!=='function')throw Error('missing library callbacks');await o.libraryFile({project:'books',path:'Book One/A.md'});await o.libraryStatus({project:'books'});return {address:()=>({port:0}),close:()=>{}};}";
|
||||
const busSource = `import {appendFileSync} from 'node:fs';export class BusReadError extends Error{};export const busCaller=o=>async(verb,args)=>{appendFileSync(${JSON.stringify(log)},JSON.stringify({verb,args,cli:o.cli??${JSON.stringify(HUMAN_CLI)}})+'\\n');return {};};`;
|
||||
const busSource = `import {appendFileSync} from 'node:fs';export class BusReadError extends Error{};export const busCaller=o=>async(verb,args)=>{appendFileSync(${JSON.stringify(log)},JSON.stringify({verb,args,cli:o.cli??${JSON.stringify(HUMAN_CLI)}})+'\\n');return {};};export const busBusiness=()=>{throw Error('busBusiness outside --sso')};export const userCaller=()=>{throw Error('userCaller outside --sso')};`;
|
||||
writeFileSync(hook, `import {registerHooks} from 'node:module';
|
||||
registerHooks({load(url,context,next){
|
||||
if(url.endsWith('/webui/src/serve.mjs'))return {format:'module',shortCircuit:true,source:${JSON.stringify(serveSource)}};
|
||||
|
||||
@@ -55,7 +55,7 @@ test('row278 Console adapter calls argument-free thread.claims through the norma
|
||||
const root = tmp(t, 'claims-cli-'), f = fixture(root), log = join(root, 'calls.jsonl'), hook = join(root, 'hooks.mjs');
|
||||
writeFileSync(log, '');
|
||||
const serveSource = `export const DEFAULT_BOARD='http://127.0.0.1:9';export const previewOptions=()=>null;export const proofOptions=()=>null;export async function startServer(o){await o.bus['thread.claims']();return {address:()=>({port:0}),close:()=>{}};}`;
|
||||
const busSource = `import {appendFileSync} from 'node:fs';export class BusReadError extends Error{};export const busCaller=o=>async(verb,...args)=>{appendFileSync(${JSON.stringify(log)},JSON.stringify({verb,args,tester:!!o.cli})+'\\n');return [];};`;
|
||||
const busSource = `import {appendFileSync} from 'node:fs';export class BusReadError extends Error{};export const busCaller=o=>async(verb,...args)=>{appendFileSync(${JSON.stringify(log)},JSON.stringify({verb,args,tester:!!o.cli})+'\\n');return [];};export const busBusiness=()=>{throw Error('busBusiness outside --sso')};export const userCaller=()=>{throw Error('userCaller outside --sso')};`;
|
||||
writeFileSync(hook, `import {registerHooks} from 'node:module';registerHooks({load(url,context,next){
|
||||
if(url.endsWith('/webui/src/serve.mjs'))return {format:'module',shortCircuit:true,source:${JSON.stringify(serveSource)}};
|
||||
if(url.endsWith('/webui/src/bus.mjs'))return {format:'module',shortCircuit:true,source:${JSON.stringify(busSource)}};
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
// R6-N1 (#1807, row 294 N1): the user-session option swap in serve.mjs, driven through HTTP.
|
||||
// granted-user.test.mjs G4 stops every owner route at the userRoute gate, so it never reaches the
|
||||
// swap behind it. Here serve.mjs loads with that gate stubbed open (a load hook keyed on one query
|
||||
// string; the product file is unchanged), and every owner route a user session might try must reach
|
||||
// no owner option. The owner transport may carry only the plugins read mos-1394 allowed.
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { request } from 'node:http';
|
||||
import { registerHooks } from 'node:module';
|
||||
import { mkdtempSync, readFileSync, rmSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
import { Store } from '../../bus/src/store.mjs';
|
||||
import { Broker } from '../../bus/src/broker.mjs';
|
||||
import { createSso, SESSION_COOKIE } from '../src/sso.mjs';
|
||||
import { consoleAccess } from '../src/access.mjs';
|
||||
import { userOptionsFor } from '../src/console-bus.mjs';
|
||||
import { BusReadError } from '../src/bus.mjs';
|
||||
import { close } from './fixture.mjs';
|
||||
import { fakeOidc } from './helpers/fake-oidc.mjs';
|
||||
|
||||
const SERVE = new URL('../src/serve.mjs', import.meta.url).href, OPEN = `${SERVE}?gate=open`;
|
||||
const GATE = 'export function userRoute(path, method) {';
|
||||
registerHooks({
|
||||
load(url, context, next) {
|
||||
const r = next(url, context);
|
||||
if (url !== OPEN) return r;
|
||||
const source = String(r.source);
|
||||
// A renamed gate fails here rather than leaving the test on the closed gate.
|
||||
if (source.split(GATE).length !== 2) throw new Error('serve.mjs: the userRoute gate is not there exactly once');
|
||||
return { ...r, source: source.replace(GATE, `${GATE} return true;`) };
|
||||
},
|
||||
});
|
||||
const { startServer, userRoute } = await import(OPEN);
|
||||
|
||||
const HOST = 'console.example.test', ORIGIN = `https://${HOST}`;
|
||||
|
||||
function call(port, { method = 'GET', path = '/', headers = {}, body } = {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const length = body === undefined ? {} : { 'content-length': Buffer.byteLength(body) };
|
||||
const req = request({ host: '127.0.0.1', port, method, path, headers: { host: HOST, ...length, ...headers } }, res => {
|
||||
let text = '';
|
||||
res.on('data', c => (text += c));
|
||||
res.on('end', () => resolve({ status: res.statusCode, headers: res.headers, body: text }));
|
||||
});
|
||||
req.on('error', reject);
|
||||
req.end(body);
|
||||
});
|
||||
}
|
||||
const setCookies = r => [r.headers['set-cookie'] ?? []].flat();
|
||||
const writeHeaders = cookie => ({ cookie, origin: ORIGIN, 'sec-fetch-site': 'same-origin', 'content-type': 'application/json' });
|
||||
|
||||
// As granted-user.test.mjs setup: the fake OP, a broker for `demo`, createSso with the access hook,
|
||||
// counting owner stubs, and consoleBus over alice's transport for her session. Only startServer
|
||||
// differs: it is the gate-open copy.
|
||||
async function setup(t) {
|
||||
const op = await fakeOidc();
|
||||
t.after(() => op.close());
|
||||
const iss = op.issuer('a');
|
||||
const root = mkdtempSync(join(tmpdir(), 'webui-option-swap-'));
|
||||
const store = new Store(root);
|
||||
t.after(() => { store.close(); rmSync(root, { recursive: true, force: true }); });
|
||||
const doc = { accessVersion: 1, revision: 1, users: [{ id: 'alice', home: 'local', subjects: [{ provider: 'a', sub: 'alice-sub' }] }],
|
||||
grants: [{ id: 'g-1', user: 'alice', business: 'demo', projects: ['*'], instances: ['assistant'], level: 'collaborator' }] };
|
||||
const load = () => doc;
|
||||
const broker = new Broker({
|
||||
store,
|
||||
businesses: {
|
||||
demo: {
|
||||
id: 'demo', human: 'jason', arbiters: { technical: 'chief', delivery: 'chief' }, vars: {},
|
||||
roles: {
|
||||
chief: { definition: 'chief-of-staff', authority: { withinRole: ['message.send'], crossRole: [] } },
|
||||
assistant: { definition: 'assistant', authority: { withinRole: ['message.send'], crossRole: [] } },
|
||||
},
|
||||
definitions: { 'chief-of-staff': {}, assistant: { messaging: JSON.parse(readFileSync(new URL('../../../roles/assistant.json', import.meta.url), 'utf8')).messaging } },
|
||||
},
|
||||
},
|
||||
access: load,
|
||||
});
|
||||
const owner = [], user = [], opened = [];
|
||||
const userTransport = name => async (verb, args = {}) => {
|
||||
user.push(verb);
|
||||
try { return broker.request(broker.bindUser({ business: 'demo', user: name }), { verb, args }); }
|
||||
catch (e) { throw new BusReadError(e.code ?? 'invalid-response'); }
|
||||
};
|
||||
const ownerCall = async (verb, args) => { owner.push(['transport', verb, args]); return { plugins: { channel: ['webui'] } }; };
|
||||
const userOptions = userOptionsFor({ owner: ownerCall, send: true, transport: name => { opened.push(name); return userTransport(name); } });
|
||||
const lines = [];
|
||||
const config = { publicOrigin: ORIGIN, providers: [{ id: 'a', issuer: iss.issuer, clientId: iss.clientId, clientSecret: { slot: 'a' }, algs: ['RS256'], subjects: [{ sub: 'owner-sub', name: 'jason' }] }] };
|
||||
const sso = await createSso(config, { secrets: new Map([['a', iss.clientSecret]]), fetch: op.fetch, log: l => lines.push(l), users: consoleAccess({ load, business: () => 'demo' }) });
|
||||
t.after(() => sso.close());
|
||||
// Every owner option is a stub that records its own name.
|
||||
const stub = name => async () => { owner.push(['option', name]); return name === 'send' ? { id: 'msg-0001' } : [{ verb: name }]; };
|
||||
const options = {
|
||||
bus: Object.fromEntries(['inbox', 'tasks', 'agents', 'trail', 'decisions', 'names', 'instances', 'routes', 'thread.claims'].map(v => [v, stub(`bus.${v}`)])),
|
||||
send: stub('send'), settingsWrite: stub('settingsWrite'), projectFiles: stub('projectFiles'), projectFile: stub('projectFile'), projectSave: stub('projectSave'),
|
||||
nameWrite: stub('nameWrite'), assistantStop: stub('assistantStop'), instancesWrite: stub('instancesWrite'), routesWrite: stub('routesWrite'), agentWrite: stub('agentWrite'),
|
||||
processing: { read: stub('processing.read'), halt: stub('processing.halt'), resume: stub('processing.resume') },
|
||||
reads: Object.fromEntries(['settings', 'business', 'busStatus', 'queue', 'row'].map(v => [v, stub(`reads.${v}`)])),
|
||||
chat: Object.fromEntries(['projects', 'threads', 'messages', 'send', 'open', 'createProject', 'createThread'].map(v => [v, stub(`chat.${v}`)])),
|
||||
};
|
||||
const web = await startServer({ port: 0, board: 'http://127.0.0.1:9', sso, log: l => lines.push(l), ...options, userOptions });
|
||||
t.after(() => close(web));
|
||||
return { op, iss, owner, user, opened, lines, port: web.address().port };
|
||||
}
|
||||
|
||||
async function signIn(env, sub) {
|
||||
const start = await call(env.port, { path: '/auth/login' });
|
||||
assert.equal(start.status, 303);
|
||||
const cookie = setCookies(start)[0].split(';')[0];
|
||||
const location = start.headers.location;
|
||||
const g = env.op.grant(env.iss, location, { sub });
|
||||
const q = new URLSearchParams({ code: g.code, state: new URL(location).searchParams.get('state') });
|
||||
const r = await call(env.port, { path: `/auth/callback?${q}`, headers: { cookie } });
|
||||
const session = setCookies(r).find(c => c.startsWith(`${SESSION_COOKIE}=`) && !c.includes('Max-Age=0'))?.split(';')[0];
|
||||
assert.ok(session, `${sub}: no session cookie (${r.status})`);
|
||||
return session;
|
||||
}
|
||||
|
||||
// granted-user.test.mjs G4's owner routes, plus the chat routes a user session reaches anyway.
|
||||
const ROUTES = [
|
||||
['GET', '/api/bus/inbox'], ['GET', '/api/bus/tasks'], ['GET', '/api/bus/names'], ['GET', '/api/bus/thread.claims'],
|
||||
['POST', '/api/bus/message', { to: 'coder', body: 'hi' }],
|
||||
['GET', '/api/settings'], ['POST', '/api/settings', { revision: 'x' }],
|
||||
['POST', '/api/name', { role: 'assistant', name: 'x' }], ['POST', '/api/assistant/stop', {}],
|
||||
['POST', '/api/instances', { instance: 'x' }], ['POST', '/api/routes', { routes: [] }],
|
||||
['POST', '/api/agent/launch', { instance: 'x' }], ['GET', '/api/agent/request?id=x'],
|
||||
['GET', '/api/processing'], ['POST', '/api/processing/halt', {}],
|
||||
['GET', '/api/project-files?project=p'], ['GET', '/api/project-file?project=p&path=a'], ['POST', '/api/project-file', { project: 'p' }],
|
||||
['GET', '/api/board'], ['GET', '/api/conversations'], ['POST', '/api/seen', {}], ['POST', '/api/reply', {}],
|
||||
['GET', '/api/queue'], ['GET', '/api/queue/1'], ['GET', '/api/business'], ['GET', '/api/bus-status'], ['GET', '/api/task-fields'],
|
||||
['GET', '/api/chat/projects'], ['GET', '/api/chat/instances'],
|
||||
['GET', '/app.js'], ['GET', '/bus.js'], ['GET', '/agents.js'], ['GET', '/editor.js'], ['GET', '/shared/app.css'], ['GET', '/index.html'],
|
||||
];
|
||||
const go = (env, session, [method, path, value]) => call(env.port, { method, path, headers: value === undefined ? { cookie: session } : writeHeaders(session), body: value === undefined ? undefined : JSON.stringify(value) });
|
||||
|
||||
test('R6-N1 with the userRoute gate open, a user session reaches no owner option over HTTP', async t => {
|
||||
// The stub took: the gate-open copy lets an owner route through.
|
||||
assert.equal(userRoute('/api/bus/message', 'POST'), true);
|
||||
const env = await setup(t);
|
||||
// Positive control: the owner's session reaches the owner's send on this route.
|
||||
const ownerSession = await signIn(env, 'owner-sub');
|
||||
const sent = await go(env, ownerSession, ['POST', '/api/bus/message', { to: 'coder', body: 'hi' }]);
|
||||
assert.deepEqual(env.owner, [['option', 'send']], `the owner's send did not run: ${sent.status} ${sent.body}`);
|
||||
env.owner.length = 0;
|
||||
const session = await signIn(env, 'alice-sub');
|
||||
for (const route of ROUTES) {
|
||||
const r = await go(env, session, route);
|
||||
assert.doesNotMatch(r.body, /owner-only/, `${route[0]} ${route[1]}: the gate is still closed`);
|
||||
// Only the plugins read with {}, as granted-user.test.mjs G9 pins for the chat.
|
||||
for (const entry of env.owner) assert.deepEqual(entry, ['transport', 'plugins', {}], `${route[0]} ${route[1]}: ${JSON.stringify(entry)}`);
|
||||
}
|
||||
assert.ok(env.user.includes('message.send'), 'the message route never reached the user transport');
|
||||
assert.ok(env.opened.length > 0 && env.opened.every(name => name === 'alice'));
|
||||
});
|
||||
Reference in new issue
Block a user