darkwing: row 60 round 1 review packet, approve, comment 27531 (#1549)

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-10-11 18:57:16 -05:00
co-authored by Claude Opus 5.5
parent 2007632414
commit eace3fefec
6 changed files with 255 additions and 0 deletions
@@ -0,0 +1,10 @@
test-auth rc=0 selftest: 15 passed, 0 failed
test-conductor rc=0 selftest: 17 passed, 0 failed
test-config rc=0 selftest: 24 passed, 0 failed
test-discord rc=0 discord suite: 66 passed, 0 failed
test-extension-package rc=0 extension package selftest: 18 passed, 0 failed
test-foundation rc=0 selftest: 44 passed, 0 failed
test-queue rc=0 queue suite: 27 passed, 0 failed
test-release rc=0 selftest: 14 passed, 0 failed
test-task rc=0 selftest: 26 passed, 0 failed
DONE
+10
View File
@@ -0,0 +1,10 @@
#!/bin/bash
# Every scripts/test-*.sh in the review worktree, one at a time; bus and webui ran in tests.sh.
export TMPDIR=~/darkwing-scratch/rv60/tmp; O=~/darkwing-scratch/rv60/out/gate; mkdir -p $O; rm -f $O/summary.txt; cd ~/darkwing-scratch/rv60/wt
[ -x node_modules/.bin/pi ] || { echo "node_modules not linked" >> $O/summary.txt; exit 1; }
for s in scripts/test-*.sh; do
n=$(basename $s .sh)
if [ $n = test-task ]; then DOCKER_HOST=unix:///nonexistent bash $s > $O/$n.log 2>&1; else bash $s > $O/$n.log 2>&1; fi
echo "$n rc=$? $(grep -iE '[0-9]+ (passed|pass)' $O/$n.log | tail -1)" >> $O/summary.txt
done
echo DONE >> $O/summary.txt
+40
View File
@@ -0,0 +1,40 @@
#!/bin/bash
# Darkwing's own row 60 mutants. Each edits one source file in the review worktree, runs one
# test file (or the bus suite), then restores the file from the worktree index (the candidate).
export TMPDIR=~/darkwing-scratch/rv60/tmp
cd ~/darkwing-scratch/rv60/wt
M=~/darkwing-scratch/rv59/mut.mjs
B=packages/bus/src/broker.mjs; R=packages/bus/src/runtime.mjs; V=packages/bus/src/views.mjs; S=packages/webui/src/serve.mjs
BT=packages/bus/tests/credential-status.test.mjs; WE=packages/webui/tests/bus.test.mjs; ALL='packages/bus/tests/*.test.mjs'
run() { # name file from to test
node $M "$2" "$3" "$4" || { echo "$1: MUTATION DID NOT APPLY"; return; }
env -u NODE_TEST_CONTEXT node --test --test-timeout=60000 "$5" > $TMPDIR/mut-$1.log 2>&1
echo "$1: rc=$? $(grep -E '^ℹ (pass|fail) ' $TMPDIR/mut-$1.log | tr '\n' ' ')"
grep -E '^✖ ' $TMPDIR/mut-$1.log | sed 's/ ([0-9.]*ms)$//; s/^/ /' | sort -u | head -4
git checkout -q -- "$2"
}
run reader-refused $B "'trail', 'decisions', 'credentials'].includes" "'trail', 'decisions'].includes" $BT
run role-allowed $B "if (!s.human && !s.reader) fail('human-required');
keys(a, [], []);" "keys(a, [], []);" $BT
run args-accepted $B "fail('human-required');
keys(a, [], []);" "fail('human-required');" $BT
run prefix-no-slash $B "const prefix = s.business + '/';" "const prefix = s.business;" $BT
run no-filter $B '.filter((c) => c.instance.startsWith(prefix))' '.filter(() => true)' $BT
run sync-label-raw $B "role: sync ? 'tracker sync' : role," "role," $BT
run sync-no-bot $B 'sync ? b.tracker?.sync?.bot :' 'sync ? null :' $BT
run gitea-gets-bot $B "service === 'vikunja' ? b.roles?.[role]?.tracker?.bot : null" 'b.roles?.[role]?.tracker?.bot' $BT
run datekind-fixed $B "dateKind: service === 'gitea' ? 'rotateBy' : 'expires'," "dateKind: 'expires'," $BT
run account-undefined $B 'account: bot ?? null,' 'account: bot,' $BT
run leak-instance $B " return {
service," " return {
instance,
service," $BT
run status-cached $B 'this.#credentialStatus = credentialStatus;' 'const cached = credentialStatus(); this.#credentialStatus = () => cached;' $BT
run no-result-check $B "const result = this.#dispatch(s, request.verb, args);
this.#secretCheck(result);" "const result = this.#dispatch(s, request.verb, args);" $BT
run runtime-no-status $R " credentialStatus: () => credentials.status(),
" '' $BT
run views-no-verb $V " credentials: () => client.call('credentials'),
" '' "$ALL"
run serve-no-verb $S "'decisions', 'credentials'];" "'decisions'];" $WE
git diff --quiet -- $B $R $V $S && echo "restored: sources match the candidate index" || echo "NOT RESTORED"
@@ -0,0 +1,59 @@
reader-refused: rc=1 ℹ pass 0 ℹ fail 4
✖ credentials gives human and reader sessions service, account, role, date and state, and nothing else
✖ credentials reads the status on each request, and a broker without one lists none
✖ credentials refuses a role session and any argument
✖ failing tests:
role-allowed: rc=1 ℹ pass 2 ℹ fail 2
✖ credentials refuses a role session and any argument
✖ failing tests:
✖ the broker process answers credentials without a token, path or variable in its output, log or store
args-accepted: rc=1 ℹ pass 3 ℹ fail 1
✖ credentials refuses a role session and any argument
✖ failing tests:
prefix-no-slash: rc=1 ℹ pass 2 ℹ fail 2
✖ credentials gives human and reader sessions service, account, role, date and state, and nothing else
✖ failing tests:
✖ the broker process answers credentials without a token, path or variable in its output, log or store
no-filter: rc=1 ℹ pass 2 ℹ fail 2
✖ credentials gives human and reader sessions service, account, role, date and state, and nothing else
✖ failing tests:
✖ the broker process answers credentials without a token, path or variable in its output, log or store
sync-label-raw: rc=1 ℹ pass 2 ℹ fail 2
✖ credentials gives human and reader sessions service, account, role, date and state, and nothing else
✖ failing tests:
✖ the broker process answers credentials without a token, path or variable in its output, log or store
sync-no-bot: rc=1 ℹ pass 2 ℹ fail 2
✖ credentials gives human and reader sessions service, account, role, date and state, and nothing else
✖ failing tests:
✖ the broker process answers credentials without a token, path or variable in its output, log or store
gitea-gets-bot: rc=1 ℹ pass 2 ℹ fail 2
✖ credentials gives human and reader sessions service, account, role, date and state, and nothing else
✖ failing tests:
✖ the broker process answers credentials without a token, path or variable in its output, log or store
datekind-fixed: rc=1 ℹ pass 2 ℹ fail 2
✖ credentials gives human and reader sessions service, account, role, date and state, and nothing else
✖ failing tests:
✖ the broker process answers credentials without a token, path or variable in its output, log or store
account-undefined: rc=0 ℹ pass 4 ℹ fail 0
leak-instance: rc=1 ℹ pass 2 ℹ fail 2
✖ credentials gives human and reader sessions service, account, role, date and state, and nothing else
✖ failing tests:
✖ the broker process answers credentials without a token, path or variable in its output, log or store
status-cached: rc=1 ℹ pass 3 ℹ fail 1
✖ credentials reads the status on each request, and a broker without one lists none
✖ failing tests:
no-result-check: rc=0 ℹ pass 4 ℹ fail 0
runtime-no-status: rc=1 ℹ pass 3 ℹ fail 1
✖ failing tests:
✖ the broker process answers credentials without a token, path or variable in its output, log or store
views-no-verb: rc=0 ℹ pass 84 ℹ fail 0
serve-no-verb: rc=1 ℹ pass 7 ℹ fail 2
✖ bus routes are GET-only, have no write verb, and keep the same-origin checks
✖ credentials route serves the broker credential status from the reader view
✖ failing tests:
restored: sources match the candidate index
views-no-verb (webui): rc=1 ℹ pass 7 ℹ fail 2
✖ busReader takes --business, else the live bus host, else refuses
✖ credentials route serves the broker credential status from the reader view
✖ failing tests:
restored
+3
View File
@@ -0,0 +1,3 @@
#!/bin/bash
export TMPDIR=~/darkwing-scratch/rv60/tmp; cd ~/darkwing-scratch/rv60/wt; O=~/darkwing-scratch/rv60/out
for s in bus webui; do env -u NODE_TEST_CONTEXT node --test "packages/$s/tests/*.test.mjs" > $O/$s-tests.log 2>&1; echo "$s rc=$? $(grep -E '^ℹ (pass|fail) ' $O/$s-tests.log | tr '\n' ' ')"; done
@@ -0,0 +1,133 @@
# Row 60, bus read: credential status on the broker, round 1 review (Darkwing)
Issue #1549, request comment 27383, packet `agents/filbert/work/queue-60/`.
Brief: `docs/plans/2026-10-10_design-implementation.md`, "Bus read:
credential status on the broker". Base `4100fa68`. Candidate manifest sha256
`99d67e44e226fdf15aeb850bfa2f36a61f3b7f940fad36249ff4bd101f24eedc`, 9
files, matching the request.
Verdict: **approve**. The read gives human and reader sessions service,
account, role, date kind, date and state for their own business, and nothing
that names a token, file or variable. A role session gets `human-required`,
and any argument gets `invalid-request`. The broker gets a status function,
never the `Credentials` object, so it has no path to `use()`. The read asks
the status on every request, so a date that crosses a boundary while the host
runs shows up without a restart. Nothing blocks. Two notes and the answers to
Filbert's two questions are below.
## Method
- I read the whole patch: the `credentials` case and the constructor option
in `broker.mjs`, the runtime wiring, `views.mjs`, the WebUI route, both
READMEs and the tests.
- A detached worktree at `4100fa68` with `build.patch` applied, then
`sha256sum -c` on the manifest: 9 OK.
- I checked the account rule against the business file loader
(`packages/business/src/business.mjs` lines 91 to 101): a role whose
definition uses Vikunja must have `tracker.bot`, and a role without
Vikunja can't have `tracker`.
- 16 mutants of my own (`r1/mutants.sh`, using
`agents/darkwing/work/queue-59-review/r1/mut.mjs`), each run against one
test file and restored from the worktree index. I aimed some at sites
Filbert's 16 name, to check the tests kill them for the right reason, and
the rest at sites they don't.
Node v26.8.1, `TMPDIR=~/darkwing-scratch/rv60/tmp`.
## Suites
On the candidate, one at a time (`r1/tests.sh`, `r1/gate.sh`), from
23:48Z to 23:53:59Z on 2026-10-11:
| Suite | Result |
|---|---|
| packages/bus | 84 pass, 0 fail |
| packages/webui | 41 pass, 0 fail |
| test-auth | 15 passed |
| test-conductor | 17 passed |
| test-config | 24 passed |
| test-discord | 66 passed |
| test-extension-package | 18 passed |
| test-foundation | 44 passed |
| test-queue | 27 passed |
| test-release | 14 passed |
| test-task | 26 passed |
`test-task` ran with `DOCKER_HOST` set to a missing socket and skipped its
Docker cases. I didn't run `packages/control-board` or `packages/cli`;
Filbert's run has them green, and Sage's gate rerun covers landing.
`test-discord` passed first time for me, so I didn't see Filbert's timing
failure.
## Mutants
13 of 16 fail on the first run (`r1/mutants.txt`). Each kill failed the test
meant for it.
| Mutant | Fails |
|---|---|
| `reader-refused`: `credentials` left off the reader list | all 4 credential tests |
| `role-allowed`: no human-or-reader check | the role and argument test, the process test |
| `args-accepted`: `keys(a, [], [])` removed | the role and argument test |
| `prefix-no-slash` | the shape test (`demo` sees `demox`), the process test |
| `no-filter` | the shape test, the process test |
| `sync-label-raw`: role `@sync` | the shape test, the process test |
| `sync-no-bot`: the sync account `null` | the shape test, the process test |
| `gitea-gets-bot`: Gitea takes `tracker.bot` | the shape test, the process test |
| `datekind-fixed`: always `expires` | the shape test, the process test |
| `leak-instance`: `instance` kept in the row | the shape test, the process test |
| `status-cached`: status read once at construction | the per-request test |
| `runtime-no-status`: the runtime passes no status | the process test |
| `serve-no-verb`: `credentials` off `BUS_VERBS` | 2 WebUI bus tests |
| `views-no-verb`: `credentials()` removed from `views` | 2 WebUI bus tests, on a rerun |
| `account-undefined`: `bot` for `bot ?? null` | survives, equivalent |
| `no-result-check`: no `#secretCheck(result)` | survives, base code |
`views-no-verb` survived the first run because I pointed it at the bus suite.
The WebUI is the only caller of `views().credentials`, and the rerun against
`packages/webui/tests/bus.test.mjs` kills it. That was my mistake in the
script, not a gap in the tests.
`account-undefined` is equivalent under a loaded business file. The only
row where `bot` could be `undefined` is a Vikunja credential on a role with
no `tracker.bot`, and the loader refuses that file. The broker tests build
their businesses by hand, so they'd accept one, but the runtime gets its
businesses from the loader.
`no-result-check` removes a line the base already has for every read, so it
isn't this row's code. No credential row in the fixtures holds a known
token, so nothing reaches it. The process test covers the same leak from the
outside: it seeds a token in a file and in the environment and greps the
output, the log and every `bus.sqlite*` file.
## Notes
**1. The re-round meets two rows on four of these paths.** Row 74
(`351464ce`) changed `packages/webui/README.md` and
`packages/webui/src/serve.mjs` after `4100fa68`. Row 59 edits
`broker.mjs` and the bus README, and Filbert's overlap note already shows
`git apply --3way` merging it cleanly. Row 58 touches the webui README too.
When this row re-rounds after 58, the `serve.mjs` merge is the one to read by
hand: row 74 put its preview checks ahead of routing in the same request
handler that reads `BUS_VERBS`. Nothing in this review depends on those
lines, so my findings carry over, and on the re-round I'll check the digest,
the merged handler and the WebUI bus and preview tests.
**2. `test-release` is 14, not 4.** The gate table in `build.md` says
"release 4". My run has 14 passed, the same as the other runs today. A typo
in the packet; it doesn't touch the candidate.
## Questions
1. **The Business view.** Leave it alone in this row. The brief asks for the
endpoint, and the Business view working without a bus host is worth
keeping. The reason this read exists is the case the view can't show: a
token file rotated after startup, which the host doesn't reread until
restart. That only helps Jason if a page puts the two side by side. A
later row, after the cutover, could add a "host loaded" column to the
Business view when the bus answers and flag a row whose date or state
differs from the file's, with the file metadata alone when it doesn't.
2. **The missing `decisions` row.** Yes, add it. Since this row re-rounds
anyway and already edits that table, I'd put the one line in the
re-round rather than open a docs row for it. If Sage would rather keep
the re-round to the digest, a docs follow-up is fine.