Compare commits
51
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5f5b51dd65 | ||
|
|
da9555ca2f | ||
|
|
0dbb242905 | ||
|
|
ea6989d4ed | ||
|
|
db1e7732aa | ||
|
|
5d2437f899 | ||
|
|
28e3d82265 | ||
|
|
524e169b8c | ||
|
|
5f73b388c4 | ||
|
|
52f36e921e | ||
|
|
6ba8a582e8 | ||
|
|
aa772d731d | ||
|
|
3dcd3651ed | ||
|
|
eb017d77f1 | ||
|
|
f842462bdd | ||
|
|
ad9cefcf2d | ||
|
|
5c91c96df5 | ||
|
|
1509c7cc57 | ||
|
|
74dea06d92 | ||
|
|
3576afffb3 | ||
|
|
73222011f2 | ||
|
|
ea0b2a16d6 | ||
|
|
cca206cdce | ||
|
|
f437973d79 | ||
|
|
33029f3a8a | ||
|
|
e69260a3ce | ||
|
|
3a853f35dc | ||
|
|
111ec559c8 | ||
|
|
25ffaae832 | ||
|
|
d372102882 | ||
|
|
b79ad5f2cb | ||
|
|
3fb00018e0 | ||
|
|
f7a77721a9 | ||
|
|
829e5f8340 | ||
|
|
abb352d27e | ||
|
|
c3d88840c5 | ||
|
|
7247d80c7a | ||
|
|
6661732925 | ||
|
|
21def28502 | ||
|
|
add0f1aae5 | ||
|
|
7d08237e17 | ||
|
|
f48725cfc1 | ||
|
|
0de97f429d | ||
|
|
0b33f5e9db | ||
|
|
3586955286 | ||
|
|
a54a17f3d8 | ||
|
|
0cdead66cd | ||
|
|
1b6158ce26 | ||
|
|
081ff6b128 | ||
|
|
be86adbff2 | ||
|
|
d163afc47f |
@@ -5179,3 +5179,43 @@ Before: managed project/thread verbs had no Console conversation area. After: fi
|
||||
2026-10-21T06:37:41Z | Darkwing | row 240 #1746 installer I4 build, round 1 in review | Before: root had no host check for the bwrap AppArmor profile, its pins or a boot unit, and stack-upgrade.sh exited 1 args-failed when the args move after A5 failed, while exit 1 means the old copy runs. After: scripts/case1-hostcheck.sh (seven items, --check, --enforce, --upstream), scripts/case1-pins.sh, mosaic-hostcheck.service and 16 tests; the args move failure exits 5 args-failed, documented in TOOLS.md and run sheet section 6 and tested; run sheet section 3 puts the pinned npm on PATH and names the bin/claude.exe --claude path; sections 8 and 10 have precheck lines. Verification: slot run r1 on 47ec9fe2f failed at the singles (the new N1 test's after hook threw ENOENT after the fixture cleanup removed the root); b45852daa drops the hook; r2 on b45852daa green: singles 16/0 and 21/0, c0 plus M1-M8 8/8 killed, cli 639/0, scripts 585/0. Candidate darkwing/row240-r3 ad1b23ab2 on c771a36f4, manifest 573ece95 (9 paths), revs 2694 and 2695, comment 29943.
|
||||
2026-10-21T09:00Z | Herb | row 215 C2-04 implementation started | Before: business files reject library entries and root consumers assume code projects; the organization writer can change all project mappings. Plan: approved manifest 2522025af on C2-01 a2c72ac95, Filbert reply to herb-0056, scope sage-1848/1852/1860. Focused slot sage-1869 observed expected red mapping and M9 cases, then 14/0 green for new T1/T2 cases, no skips; clear herb-0062 with empty test/fixture process scan. After that clear, T3/T4 tests and host-only mirror/bus implementation continue under sage-1874, unrun while Sage holds the slot. Full singles, suites and implementation review remain pending. No source commit, push or live change.
|
||||
2026-10-21T13:29Z | Herb | row 215 C2-04 source verified for candidate review | Before: business code assumed every project had a root and organization writes could replace project mappings. After: library mappings load without agent checkouts or publish grants, project mappings are protected before and after serialization, host-only bare mirrors share a bounded publication lock with sync, and library.file/library.status are human-only async bus reads. Base f8276c5eb preserves all nine landed overlaps. Granted sage-2008 whole singles: 16 files, 451/0, no skips or cancellations, ended 13:27:27.279728Z; all source hashes unchanged, process scan empty, no own-path drift through 8a04f4aa4. Full landing gate and implementation review remain pending; no HTTP, sync writer or live change.
|
||||
|
||||
2026-10-20 19:09:44 UTC | Gander row 196 #1694 design phase | Before: tester TCP reaches every local uid and human proofs do not bind the socket peer. After: design draft at agents/gander/work/queue-196/DESIGN.md inventories listeners, proposes private tester HTTP and SO_PEERCRED binding, aligns the per-request principal contract with Morgana and Gizmoduck, and lists migration, tests, mutants and same-uid limits. Python versus other credential options and Console provenance require Duckworth ruling. Documentation only. No product code, suite, live probe, secret access or deployment.
|
||||
|
||||
2026-10-20 19:16:20 UTC | Gander row 196 #1694 design r2 | Before: Duckworth r1 requested concurrent helper-fd evidence (B1) and selected O1 with interpreter, proof-only and image conditions. After: design adds eight concurrent genuine proof processes plus a racing copier, M20 swapped socket and post-helper socket survival, root-owned system ELF pinning and activation evidence, safe gid validation, cap traffic without helpers, and unchanged P19 R2 text. Mos accepted socket-only tester access (mos-1015 via sage-1363); any authenticated browser relay is a separate row. Documentation coverage checks passed. No product code, suites or live action.
|
||||
|
||||
2026-10-20 19:21:19 UTC | Gander row 196 #1694 design approval and N5 | Before: Duckworth approved r2 (duckworth-0009, cb6d840df) with a nonblocking interpreter-upgrade note. After: r3 chooses startup safety revalidation, appends actual interpreter identity to bus/peercred-starts.jsonl and retains old activation evidence. Operator re-pin uses the next reviewed copy, respecting already-active refusal. Added task-by-task PLAN.md for inline implementation and independent manifest review. Documentation checks passed. No product code, suites or live action.
|
||||
|
||||
2026-10-20 19:34:28 UTC | Gander row 196 build paused at design mismatch | Before: approved design promised Unix tester cleanup only for the owned inode. After: scratch test shows Node/libuv close unlinks a replacement inode before JS close handlers. Evidence agents/gander/work/queue-196/evidence/tester-cleanup-blocker.txt. Sent Sage/Duckworth gander-0016/0017, recommending explicit same-uid residual with private 0700 parent retained. Interpreter, concurrent peer, principal, human, peer-transport, transport, tester and process single files passed. Partial code uncommitted, tester transport not wired. No suites, mutant batches or live actions.
|
||||
|
||||
2026-10-21 18:45 UTC | Gander row 196 preservation before rebase | Sage-2051, Mos order mos-1193, explicitly authorizes commit and fast-forward push of the current build before deferred full verification. Product and evidence preserved together; prior focused checks and stopped gates are recorded in the row196 packet, with no claim of full acceptance. Next: rebase onto a36523713 on new gander/row196-r2, report range-diff/patch-id/conflicts, then await Sage's clear for M1-M29 and all requested suites. No live action, force push, branch deletion or tests during hold.
|
||||
|
||||
2026-10-21T16:34:44Z | Huey | Q198/Q199 system-manager design, mos-1183, Vikunja 452 | Before: pinned foundation 6c75887b0 has separate bus, development queue, run evidence, turn/cohort controls and launch/usage paths; the assignment requests a mechanical manager design. After: docs/plans/2026-10-21_q198-system-manager.md proposes admission/cooldown, priority and safe interruption, durable requests, bounded retry/DLQ/replay/rerouting, Console monitoring, optional decision adapters with USD reservations, six ordered slices and three recommended owner decisions. Verification: 26 pinned file:line references resolve, ten-line summary, manual source/contract review, git diff --check and installed queue guard pass. No builds/suites, runtime writes, credential reads, service changes or paid calls. Huey queue next refused the linked worktree, then returned nothing from canonical checkout; direct brief controls this assignment. Gitea issue and queue-row capture are handed to Sage under the no-credential-read/no-queue-write boundary.
|
||||
|
||||
2026-10-21T18:45:47Z | Huey | Q198 system-manager design r2, mos-1194, Vikunja 452 | Before: Scrooge requested changes on add7cb506 for decision-key worker exposure, cap scope and absent sampler ownership. After: distinct MOSAIC_DECISION_OPENROUTER_KEY with S6 sessionEnv canary requirement; exclusive per-instance key with per-key caps and provider limit on every enablement; S2 owns supervisor sampler and waits until after MVP/re-pin. Also records P1 interpretation, missing-confidence fallback and stand-in timeout selection, explains verified Noul spelling, excludes notifier/gateway retry loops with citations, and cites lifecycle revocation requirement. Verification: 30 pinned citation spans resolve, ten-line summary, manual diff/source review and whitespace/queue-guard checks. No builds, suites, live changes, credential reads or paid calls.
|
||||
|
||||
2026-10-21T19:56:11Z | Huey | Q198 design r3, mos-1201/1201a, Vikunja 452 | Before: Scrooge approved r2 7e785a08f with three notes. After: N1 names proposed system.health bus read verb and includes broker handler/authorization in S2 scope and review; N2 labels exclusive ownership as a setup attestation that cannot be mechanically proved and identifies provider key limit as real ceiling; N3 adds recommended Q4 for P1 narrowing and removes the Mos-flag instruction. Other design content retained. Verification: manual r2-to-r3 delta review, ten-line summary, whitespace and installed queue-guard checks. No builds, suites, live changes or paid calls.
|
||||
|
||||
2026-10-21T20:19:53Z | Huey | Q198 design r4, mos-1203, DECISIONS Q204, Vikunja 452 | Before: r3 a36952564 presented Q1–Q4 for owner approval. After: section 9 records Q20a–Q20c verbatim as approved; D11 labels Huey’s reading of configurable daily/monthly caps with $0.10/$1 defaults in the one instance config via reviewed schema; provider key ceiling and explicit enable/key requirements retained; Q4 pending, Jason restated 2026-10-21, P1 unchanged. Verification: quotes match DECISIONS ledger, unrelated sections and P1 equal r3, ten-line summary, whitespace and installed queue guard checked. No builds, suites, live changes or paid calls.
|
||||
|
||||
2026-10-21T20:48:49Z | Huey | Q198 design r5, mos-1206, DECISIONS Q205/Q206, Vikunja 452 | Before: r4 1e8bccff1 left Q4 pending and long outages could exhaust request retry budgets. After: protected mode separate from unchanged D1 human kill authority; mechanical detection, safe-point pause/proof, JEV-first endpoint-before-headless probes at 60-second increments to 600 seconds without attempt limit, upstream reset waits, shared D11 probe accounting, one test session then batches, proposed three-failed-exit escalation, frozen outage clocks/counters without DLQ, advisory online-service routing, Q4 superseded and Q22 pending. Thresholds/deadlines/batches/escalation explicitly labelled Huey readings. Acceptance proposals updated, no implementation. Verification: complete Q205/Q206 read, quotes match ledger, D1/inventory/Q1–Q3 unchanged, 30 pinned citation references resolve, 11-line summary, manual consistency/diff review, whitespace and installed queue guard checked. No builds, suites, real probes, live changes or paid calls.
|
||||
|
||||
2026-10-21T21:04:37Z | Huey | Q198 design r6, mos-1208/mos-1210, DECISIONS Q207, scrooge-0003, Vikunja 452 | Before: r5 fd44f7c60 gated probes on JEV and lacked request-local resume failure separation. After: parallel independent probes; central JEV/smaller-model/rules endpoint chain, per-model timeout/calibration, 4 KiB metadata limit and isolated fallback credentials; subscription quota vs metered USD accounting and separately enabled paid-probe budget (proposed $0 defaults). S-F1–S-F7: bounded outage classification, request-local D6/two-resume DLQ rule and batch definition; escalation latch with authorized revision-bound system.recovery.release separate from kill clear; cooperative F5 pause/F6 proof only; proposed configurable 24-hour cumulative pause expiry; full Q205 kill sentence with labelled protected-mode interpretation; r3–r6 history. S5/S6 acceptance proposals updated. Verification: Q205/Q207 quotes match ledger, D1/inventory/Q1–Q3 unchanged, 30 pinned references resolve, summary within 15 lines, manual consistency/delta review, whitespace and installed queue guard checked. No builds, suites, real probes, live changes, credential reads or paid calls.
|
||||
|
||||
2026-10-21T21:39:25Z | Huey | Q198 r6.1 touch-up, mos-1222, scrooge-0004 N1–N3, Vikunja 452 | Before: approved r6 2ee68cd69 had unprovable session-call denial wording, implicit queue/late-call behavior and a partial Q207 paragraph. After: rule/S6 tests require session decision calls/output have no routing authority; non-waiting dispatchers use internal rules immediately; timed-out JEV does not occupy fallback decision slot but retains spend reservation; full Q207 paragraph copied exactly from DECISIONS; matching Q22 reading corrected. ENV_ALLOW and all runtime code unchanged. Verification: exact full-quote equality, obsolete wording absent, D1 unchanged, summary within 15 lines, manual N1–N3 delta review, whitespace and installed queue guard checked. No builds, suites, probes, live changes or paid calls. Review/handoff via T3 as Mos authorized.
|
||||
|
||||
2026-10-21T21:11:44Z | Della mos-1213 / Vikunja 460 planning phase | Before: PRD 1.10 has no email feature; MP 95b0298 and EP 7a64921 are read-only prior inputs. Scope: independent post-v1 email plan on della/email-plan, no implementation/live access/MVP or queue changes. Gitea issue enrollment belongs to Mos/Sage because this seat has no API credential.
|
||||
|
||||
2026-10-21T21:12:18Z | Della mos-1213 / Vikunja 460 planning verification | After: docs/plans/2026-10-21_email-plan-della.md is 341 lines, with 13 post-v1 rows and six Jason questions. Verified 42 pinned citation anchors, ten requested coverage areas, identical prior src trees, append-only records, clean whitespace and repository queue guard. No configured CI pipeline or CI-queue wrapper found in the current foundation; documentation-only validation applies, no product suites or live tests run. Phase B/C and Gitea issue/queue enrollment remain with Mos/Sage. Only della/email-plan is authorized for push.
|
||||
|
||||
2026-10-21T21:37:07Z | Della mos-1220 / Vikunja 460 phase B cross-review | Before: independent plans Della 191dff32f and Ludwig 4a27b73ac delivered. After: docs/plans/2026-10-21_email-review-by-della.md reviews all 218 Ludwig plan lines, records ten findings, five grafts, six retained strengths, nine disagreements and eleven merged Jason questions. Ludwig review 143415779 read in full after ludwig-0002 arrived; reconciliation records new mail-access action, no agent reroute and correction of Della dataRoot credential example. Original plan unchanged. Planning only, no product/MVP/queue/live change.
|
||||
|
||||
2026-10-21T21:43:02Z | Della mos-1223 / Vikunja 460 phase C synthesis | Before: independent plans/reviews complete, Mos selected Della base. After: docs/plans/2026-10-21_email-plan.md merges Ludwig grafts and accepted corrections, with 16 post-v1 rows (eight READY, eight WAIT/optional), eleven deduplicated Jason questions, per-row test paths and explicit Q210 non-v1 scheduling. Original Della plan/review preserved; Ludwig originals remain on their branch. Static checks cover graft/row/question/citation anchors and original blobs. No source code, suite, mailbox, secret file, service, MVP or queue change. Candidate goes to Ludwig before Sage landing/queue handoff, copying Mos.
|
||||
|
||||
2026-10-21T21:45:20Z | Della mos-1223 / ludwig-0004 focused corrections | Before: b096a16cc left content-key location implicit and E05 core registration marked ready. After: merged plan 399 lines fixes external configDir secret key/lifecycle namespace outside dataRoot backup/restore, E01 schema and E03 missing/expired-key restore tests, and E05/E06 test-local registry readiness with core kinds.mjs registration gated on approved E01/Q4. Sixteen rows and eleven questions retained; originals unchanged. Focused static checks and queue guard apply, no product/live changes. Reviewer will check changed lines only.
|
||||
|
||||
2026-10-21T22:24:12Z | Huey | T1 item 7 sessions design r1, mos-1232, Vikunja 463 | Before: origin/refactor cca206cdc keys assistant claims/stores/messages by thread and renders Pi compaction markers; row 194 r4 excludes thread context clear. After: docs/plans/2026-10-21_t1-sessions.md proposes independent per-agent sessions within threads on Pi/Claude, scoped claims/stores/messages/workspaces, completed compaction counts, owner-write/RBAC matrix, idempotent proven-stop/fresh-successor clear, common admission/kill checks, migration and seven acceptance groups, replacement REQ-ROLE-2/REQ-THREAD-2 text and three owner questions. PRD 1.12 USER-3/4 candidate 1509c7cc5 cited separately from base. Verification: 22 pinned references resolve, eight-line summary and three exact owner quotes; manual source/consistency review, whitespace and installed queue guard checked. No code, queue/PRD edits, builds, suites, credential reads, live changes or paid calls. MVP priority ahead of Q198 S1. Review via Scrooge, then Sage/Ludwig as Mos authorized.
|
||||
|
||||
2026-10-21T22:28:56Z | Huey | T1 sessions design r2, scrooge-0006, mos-1235/1236, Vikunja 463 | Before: r1 347278044 lacked the exact per-session checkout/single-writer invariant and safe runner drain ordering, and admitted other role instances. After: F1 exact workspace path, fenced checkout setup, legacy shared-workspace quiescence, clear retains old dirty/untracked/unpushed checkout and creates fresh approved checkout, T2 mutant; F2 dequeue fence and generation-bound turn-boundary idle proof before runner signal, busy drain stays clearing, T4 cases; F3 THREAD_DEFINITION only per Mos scope ruling, no role eligibility change; N1 private titles/history vs owner-visible count/activity projection; N2 Claude offline adapter/managed-store-reader dependency flagged as MVP risk. Section 8 records Mos’s Q1–Q3 readings, no questions to Jason. Verification: 27 pinned references resolve, summary within 15 lines, source/delta consistency review, whitespace and installed queue guard checked. No code, queue/PRD edits, builds/suites/live changes/provider calls/credential reads.
|
||||
|
||||
2026-10-21T22:30:20Z | Huey | T1 sessions design r3, scrooge-0007 R1, Vikunja 463 | Before: r2 e19e30df2 nested new session checkouts under the retained legacy instance workspace, allowing project-name collision or historical parent inspection to reach live siblings. After: exact path is dataRoot/session-workspaces/business/instance/session, outside the legacy root; canonicalized ancestor/descendant overlap refuses; T2 nesting mutant covers historical mount/inspection and a legacy project named sessions. All other r2 content retained as approved, revision label updated. Verification: old nested path absent, manual R1-only delta, whitespace and installed queue guard checked; no code/builds/suites/live changes/provider calls/credential reads.
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
# Row 251 build: T27 lists no identities right after `ssh-add -t 1` (Vikunja #453)
|
||||
|
||||
Brief `docs/plans/2026-10-21_t27-ssh-add-lifetime.md` (rev 2931), Mos ruling mos-1185 item 2, assigned in
|
||||
sage-2075. Owner Darkwing, reviewer Dewey, gate owner Sage. Branch `darkwing/row251` from 3b0aef995,
|
||||
claimed in-progress at rev 2938.
|
||||
|
||||
## Step 1: the failure names its cause (e5e4b2e5a)
|
||||
|
||||
T27's two lists now carry a message that prints only when they fail. It gives the time from addKey's
|
||||
start to its resolve and to each `ssh-add -l` call, the hrtime at each with how far into its second it
|
||||
fell, `/proc/uptime` at each, and whether the agent process and its socket were still there. The checks
|
||||
themselves did not change. The commit holds nothing else.
|
||||
|
||||
## Step 2: the cause
|
||||
|
||||
ssh-agent counts a key's lifetime in whole seconds of CLOCK_BOOTTIME, at the add and at the reap. I read
|
||||
OpenSSH 10.5p1, the version on dragon-lin (`ssh -V`: OpenSSH_10.5p1), at tag V_10_5_P1 (ssh-agent.c
|
||||
sha256 873691c4, misc.c sha256 ee467f43):
|
||||
|
||||
- `misc.c:1790-1798` and `:1828`: `monotime()` returns `ts.tv_sec` of `clock_gettime(CLOCK_BOOTTIME)`,
|
||||
falling back to MONOTONIC. The fraction of the second is dropped.
|
||||
- `ssh-agent.c:1315`: the `-t` constraint sets `*deathp = monotime() + seconds`. Lines 1410 and 1598
|
||||
do the same for the agent's default lifetime.
|
||||
- `ssh-agent.c:1064`: the reaper drops a key when `now >= id->death`, `now` being `monotime()` again.
|
||||
- `ssh-agent.c:2650`: the main loop runs `reaper()` after every poll, before it reads requests. A list
|
||||
that arrives after the boundary sees the key gone.
|
||||
|
||||
So a key added at second s plus a fraction f dies at s + N, and `-t N` keeps it N - f seconds, between
|
||||
N - 1 and N. With `-t 1`, an add late in a second leaves the key milliseconds to live. That matches
|
||||
Sage's probe in the brief: 8 of 80 `-t 1` adds missed, each one made at 0.9947-0.9973 of a monotonic
|
||||
second and finished at 0.998-0.0008, and none earlier in the second missed. It also matches proof 5's
|
||||
failure, where the first list ran 31.9 ms into the test and found no identities.
|
||||
|
||||
Node's hrtime is CLOCK_MONOTONIC. BOOTTIME differs from it by the time the host spent suspended. On
|
||||
dragon-lin the difference read 0.000001 s at 19:09Z, so a monotonic second boundary is a BOOTTIME one
|
||||
here.
|
||||
|
||||
`addKey` and the agent start are not the cause. `addKey` passes the lifetime through as `-t`
|
||||
(`host-run.mjs:90`), and the agent starts with no default lifetime (`:175`). Production adds the key
|
||||
with `timeout + 30` (`:180`), where losing up to a second makes no difference. The only caller that
|
||||
can't afford the lost fraction is T27, which asks for 1 s. The fix belongs in the test's lifetime choice,
|
||||
and `host-run.mjs` doesn't change.
|
||||
|
||||
The slot confirms this against T27 itself (below), not just from the source.
|
||||
|
||||
## Step 3: the fix (this commit)
|
||||
|
||||
T27 adds the key with `-t 2`. The agent then keeps it more than 1 s and at most 2 s. The first list
|
||||
runs milliseconds after the add, so it has over a second of margin even under load. The sleep before
|
||||
the second list grows from 2.5 s to 3.5 s, which keeps the old 1.5 s margin past the new 2 s ceiling.
|
||||
Both checks stay: listed after the add, gone after the lifetime. A comment above the add says why the
|
||||
lifetime is 2.
|
||||
|
||||
I considered `-t 3` with a 4.5 s sleep for a 2 s floor. One second of floor is far above anything the
|
||||
first list has needed (31.9 ms in proof 5), and the concurrent-suite run below tests it under load, so I
|
||||
took the smaller change.
|
||||
|
||||
## Base
|
||||
|
||||
The branch stays on 3b0aef995. By 19:15Z origin/refactor was at 6096b051b, after rows 66 and 243
|
||||
landed. `git diff 3b0aef995 6096b051b` touches neither `packages/bus/src/host-run.mjs` nor
|
||||
`packages/bus/tests/host-run.test.mjs`, so there's no rebase (sage-2084). Row 243 did change
|
||||
`packages/bus/tests/process.test.mjs`. My slot's bus suites run that file as it was at 3b0aef995, and the
|
||||
gate rerun on Sage's slot runs it as landed.
|
||||
|
||||
## Prediction for part A on e5e4b2e5a (written before the slot)
|
||||
|
||||
The hook starts `ssh-add -t 1` at the given fraction of a monotonic second. From the brief's probe, the
|
||||
agent stores the key about 3 ms later. The first list reaches the agent a few ms after addKey resolves:
|
||||
Node's exit event, the mark, and `ssh-add -l`'s own start. A red needs the add to land before the second
|
||||
boundary and the list to land after it.
|
||||
|
||||
- 0.990: green. The add and the list both land before the boundary.
|
||||
- 0.993, 0.995, 0.996, 0.997: red expected. The add lands before the boundary, the list after. At 0.993
|
||||
and 0.997 I'm less sure, because the list (at 0.993) or the add (at 0.997) can land right at the
|
||||
boundary. A green at either is in line with the cause, and so is a red.
|
||||
- 0.998: green. The add lands after the boundary, so the key gets almost a full second.
|
||||
|
||||
Every red should show "first list got" an answer with no SHA256 line. It should also show addKey
|
||||
resolving before a second boundary and the first list starting after it or within a few ms of it, with
|
||||
the agent alive and its socket present.
|
||||
|
||||
Under sage-2084's rule, on e5e4b2e5a only a red at 0.990 or 0.998 counts toward the stop, and two such
|
||||
reds stop the run. On the fix commit any red counts, and two stop it. In parts B to D, the first red stops
|
||||
the run.
|
||||
|
||||
## Slot plan
|
||||
|
||||
All scripts are in `/mnt/storage/scratch/darkwing-251-out/`, launched from `/`, each behind the busy
|
||||
checks and chained with `&&`, TMPDIR under `/mnt/storage/scratch/tmp`.
|
||||
|
||||
1. Confirmation. `align-hooks.mjs` is a load hook, outside the repo, that serves `host-run.mjs` with
|
||||
`addKey` wrapped to start `ssh-add` at a chosen fraction of a monotonic second. T27 runs unchanged
|
||||
under it, alone (`--test-name-pattern`). It runs at fractions 0.990, 0.993, 0.995, 0.996, 0.997 and
|
||||
0.998, three times each:
|
||||
- on e5e4b2e5a (step 1, `-t 1`). Expected: some first lists fail, and the step 1 message shows the
|
||||
add's resolve and the list on either side of a second boundary, with the agent alive and the
|
||||
socket present. These reds are the point of the run.
|
||||
- on the fix commit (`-t 2`). Expected: all 18 pass.
|
||||
2. `host-run.test.mjs` whole, once, on the fix commit.
|
||||
3. The bus suite whole, three times in a row.
|
||||
4. The bus suite whole in two clean worktrees at the same commit, started together.
|
||||
|
||||
## Slot results
|
||||
|
||||
Sage's CLEAR came in sage-2154 at 20:58:55Z. `slot-251.sh` ran parts A and B from 20:59:22Z to 21:01:33Z,
|
||||
on node v26.8.1 and OpenSSH_10.5p1, with TMPDIR `/mnt/storage/scratch/tmp/r251`. At the start, BOOTTIME
|
||||
minus MONOTONIC read 0.000001 s, then 0.000000 s twice (dewey-0769 N1). So the hrtime fractions below
|
||||
put the agent's second boundary in the right place.
|
||||
|
||||
### Part A: T27 alone under `align-hooks.mjs`, 3 runs per fraction
|
||||
|
||||
The fraction is when the hook started `ssh-add`. The hook logged each start within 0.0001 of its target.
|
||||
|
||||
| fraction | e5e4b2e5a (`-t 1`) | 68f2dfd4b (`-t 2`) |
|
||||
|---|---|---|
|
||||
| 0.990 | 3 pass | 3 pass |
|
||||
| 0.993 | 3 pass | 3 pass |
|
||||
| 0.995 | 3 red | 3 pass |
|
||||
| 0.996 | 3 red | 3 pass |
|
||||
| 0.997 | 3 pass | 3 pass |
|
||||
| 0.998 | 3 pass | 3 pass |
|
||||
|
||||
e5e4b2e5a went red 6 times out of 18, every time at a fraction the prediction named, so none of them
|
||||
counted toward the stop. 68f2dfd4b passed all 18. The fix runs used 68f2dfd4b, whose test blob is the same
|
||||
as c19b4167e's (280132c7).
|
||||
|
||||
Every red failed on the first list and got `"The agent has no identities.\n"`, with the agent alive and
|
||||
its socket present. The step 1 message gave these hrtime fractions. "addKey start" is the mark before the
|
||||
hook's wait, so its fraction is earlier than the ssh-add start.
|
||||
|
||||
| run | addKey resolved | first list mark |
|
||||
|---|---|---|
|
||||
| 0.995 rep 1 | 0.9992 (+317.6 ms) | 0.9993 |
|
||||
| 0.995 rep 2 | 0.9991 (+846.5 ms) | 0.9991 |
|
||||
| 0.995 rep 3 | 0.9988 (+845.0 ms) | 0.9988 |
|
||||
| 0.996 rep 1 | 0.9999 (+839.8 ms) | 1.0000 |
|
||||
| 0.996 rep 2 | 0.0004 (+845.0 ms) | 0.0004 |
|
||||
| 0.996 rep 3 | 0.0002 (+835.9 ms) | 0.0002 |
|
||||
|
||||
At 0.995 the add finished about 4 ms in, before the boundary, and the list mark came just before it. The
|
||||
`ssh-add -l` process then reached the agent after the boundary. At 0.996 the agent stored the key before
|
||||
the boundary, and addKey resolved just after it. In both cases the key's whole second ended between the
|
||||
add and the list, which is the cause step 2 gives. The two edge fractions came out green on the first
|
||||
build. At 0.993 the list still reached the agent before the boundary, and at 0.997 the add landed after
|
||||
it, so the key got almost a full second. The prediction allowed either result at those two.
|
||||
|
||||
### Part B: `host-run.test.mjs` whole on 68f2dfd4b
|
||||
|
||||
24 tests, 24 passed (21:01:18Z, 14.0 s).
|
||||
|
||||
### The stop before C1, and the resume
|
||||
|
||||
At 21:01:33Z the script stopped before C1, with exit 4 and no red. My own `busy.sh` matched two `sleep 60`
|
||||
processes, mine, in `darkwing-r251/packages/bus`. Part B left them. They come from T27 "the start sweep
|
||||
keeps a live bus dir and kills only a matching live process" (`host-run.test.mjs:528` and `:536`). Its
|
||||
`t.after` SIGKILLs the `/bin/sh -c 'sleep 60; :'` parent, but the `sleep` child lives on for up to 60 s.
|
||||
That code is outside this row's diff, so the row leaves it alone, and it's recorded below as a follow-up.
|
||||
Sage confirmed (sage-2159) that the right move is to resume only the scope that hadn't run.
|
||||
|
||||
`slot-251-cd.sh` runs C and D as `slot-251.sh` has them, with the same two busy checks. Before each check,
|
||||
it waits up to 75 s for those orphans to exit: my uid, my trees, a command line of exactly `sleep 60`.
|
||||
It logs them as it waits. Any other process still trips the checks.
|
||||
|
||||
### Parts C and D: the bus suite whole on 68f2dfd4b
|
||||
|
||||
| run | start | tests | pass | fail | time |
|
||||
|---|---|---|---|---|---|
|
||||
| C1 | 21:02:48Z | 598 | 598 | 0 | 21.6 s |
|
||||
| C2 | 21:04:06Z | 598 | 598 | 0 | 21.4 s |
|
||||
| C3 | 21:05:26Z | 598 | 598 | 0 | 22.1 s |
|
||||
| D1, darkwing-r251 | 21:06:44Z | 598 | 598 | 0 | 24.2 s |
|
||||
| D2, darkwing-r251b, TMPDIR `r251-b` | 21:06:44Z | 598 | 598 | 0 | 24.2 s |
|
||||
|
||||
The script waited 33 s before C2, 34 s before C3, 33 s before D and 32 s before the sweep. Each time it
|
||||
was waiting for that run's `sleep 60` orphans (two per suite, four after D), and nothing else. After the
|
||||
wait, the sweep of my trees, the out dir and TMPDIR found 0 processes left (21:08:04Z). Sage confirmed
|
||||
(sage-2159, sage-2160) that the short gitea-api and `node --check` runs they made during the slot weren't
|
||||
stops. None of my busy checks refused on any of them.
|
||||
|
||||
These runs used the bus files as they were at 3b0aef995. Since then, origin/refactor (666173292, then
|
||||
7247d80c7 and c3d88840c with row 196) has changed `broker.mjs`, `runtime.mjs` and `server.mjs`, which T27's
|
||||
file imports, along with other bus files. None of those commits touch this row's two paths or
|
||||
`host-run.mjs`, and `git merge-tree` of c3d88840c with 68f2dfd4b is clean. The gate rerun in
|
||||
a clean worktree on Sage's slot runs T27 against them.
|
||||
|
||||
### Follow-up, not in this row
|
||||
|
||||
T27's start-sweep test leaves the `sleep 60` child of each planted shell running for up to 60 s after
|
||||
the test. Killing the process group (`process.kill(-pid)`; both shells are spawned `detached`) or
|
||||
`exec sleep 60` in the shell would end the child along with its parent.
|
||||
@@ -0,0 +1,100 @@
|
||||
# Row 250 build notes (Gizmoduck)
|
||||
|
||||
Vikunja #450, Mos ruling mos-1182 item 3. Brief: docs/plans/2026-10-21_k12-cohort-events.md. Reviewer
|
||||
Filbert, gate owner Sage. Branch gizmoduck/row250 from origin/refactor 3b0aef995. Line numbers below are on
|
||||
this branch unless a commit is named.
|
||||
|
||||
## Step 1
|
||||
|
||||
cbba6ff07 changes only the K12 assertion (packages/conversation/tests/cohort.test.mjs:292 on 3b0aef995).
|
||||
On failure it prints the shim's answer, with `ok` and `unavailable`, and `h.ctrl.evidence.releases`.
|
||||
|
||||
## Step 2: the cause
|
||||
|
||||
K12 reads the shim after the controller has started to release it.
|
||||
|
||||
- The force stop sets the binding `stopped` (packages/conversation/src/controller.mjs:1615). Then it pauses at
|
||||
`scope-release` (:1623) and calls `#releaseScope` (:1624).
|
||||
- `#releaseScope` calls `releaseCohort` (:1639). That sends `hello`, then `release`
|
||||
(packages/conversation/src/cohort.mjs:261-265).
|
||||
- The shim answers `release` and exits 10 ms later. It unlinks its socket first (shim.mjs:165-176). systemd
|
||||
then collects the empty scope, `engine` included.
|
||||
- K12's `forceStop` helper returns once it sees `stopped` (cohort.test.mjs:148, polling every 10 ms). The
|
||||
test then reads `engine/cgroup.freeze` (:281 on 3b0aef995) and asks the shim for `events` (:291).
|
||||
|
||||
Nothing in K12 orders those reads before the release. Unloaded, the test usually sends its `events` request
|
||||
within the 10 ms before the shim exits, so it passes. Under load the release wins. `shimRequest` then
|
||||
returns `{ ok: false, unavailable: "the shim socket is gone" }` (cohort.mjs:46), or "unreachable" or
|
||||
"closed the connection" (:62-63), and `ev.populated` is undefined. Both recorded failures fit this
|
||||
pattern:
|
||||
|
||||
- Row 197 proof 4, pair1-b: K12 failed in 339 ms on `undefined !== 0` at :292. 339 ms is far below the
|
||||
3 s `shimRequest` timeout (cohort.mjs:56), so the request didn't time out. The `cgroup.freeze` read at
|
||||
:281 passed, so the scope still existed then.
|
||||
- BUILD-LOG.md:4092, row 56's gate: "`populated` undefined with load around 12".
|
||||
|
||||
Row 50 (a9cc522a0, #1536) added the release after a proven stop. K12 is older. Its comment still says
|
||||
"the shim holds the scope", and that stopped being true in row 50. E1 (cohort.test.mjs:923-925) already
|
||||
holds `scope-release` so it can read the shim while the shim still answers. K12 never got the hold. No
|
||||
other test reads the shim after a proven stop and expects an answer. R1 expects the shim gone, R3 is an
|
||||
uncertain stop, and K15 reads before its stop.
|
||||
|
||||
The cgroup read itself has no race. The kill's `waitFor` (shim.mjs:117-126) had already read `populated 0`
|
||||
for the stop to be proven. The read is the shim's call to `events()` (shim.mjs:83-96), and it reads one
|
||||
kernel file. So the brief's allowance for a bounded retry doesn't apply, and there is none.
|
||||
|
||||
The fix goes in the test's setup, which the brief allows ("Use the shim, the cohort controller or the
|
||||
test's setup and teardown, whichever the evidence points to"). The controller's release is correct and
|
||||
specified (README "Scope release", R1). Delaying it for a reader would change row 50's behavior.
|
||||
|
||||
## Step 2: the change
|
||||
|
||||
packages/conversation/tests/cohort.test.mjs, K12:
|
||||
|
||||
- It runs with `gate()`, holds `scope-release` before the stop, and waits until the hold is reached. Then it
|
||||
does every read that needs the scope: `cgroup.freeze`, `events` and `members`.
|
||||
- After the reads it lets the release go and asserts one entry, `["force-stop", "released"]`. This is new
|
||||
coverage: K12 now also shows the forking cohort's scope released.
|
||||
- The assertions are unchanged. `populated` must be 0 and the member list must be empty. The step 1 message
|
||||
stays.
|
||||
|
||||
packages/conversation/src/shim.mjs:167-168, `release` (:167 on 3b0aef995). It answered a failed `events()` with "the engine
|
||||
cgroup is not empty", so it reported unavailable as not empty. The refusal was already safe, but the reason
|
||||
was false. Now an `events()` answer that isn't `ok` is returned as it is, with the read's own reason. Only
|
||||
`populated` 1 gives "not empty". Nothing else matched that string (grep of packages/).
|
||||
|
||||
## Tests
|
||||
|
||||
- K12 as above.
|
||||
- R13, the brief's new case, placed after R3. After a proven stop, with `scope-release` held, the test makes
|
||||
`engine/cgroup.events` mode 000. Then:
|
||||
- `events` returns `ok: false`, has no `populated`, and the reason is "engine/cgroup.events unreadable
|
||||
(EACCES)".
|
||||
- On release, the controller records one entry `["force-stop", "unavailable", <that reason>]`.
|
||||
- The unit stays active, and the shim still answers `hello`.
|
||||
|
||||
Then `close()` reuses the stop's release, so there is still one entry. The `finally` closes the
|
||||
controller again (a second close is a no-op), restores mode 444, the kernel's mode for cgroup.events,
|
||||
and releases the scope. On
|
||||
3b0aef995's shim.mjs, R13 fails on the reason, because it reads "the engine cgroup is not empty".
|
||||
- K15 already covers a stop, as opposed to a release, with `events` unavailable: the shim gone, the file
|
||||
mode 000, or `engine` missing, each ending `uncertain` with its reason.
|
||||
|
||||
## File added beyond the brief
|
||||
|
||||
packages/conversation/README.md:599 listed "R1–R3, R5–R12" for cohort.test.mjs. With R13 it now reads
|
||||
"R1–R3, R5–R13". The brief's files don't include the README. Sage added it to this row in sage-2082.
|
||||
|
||||
## Runs
|
||||
|
||||
None yet. They need Sage's slot. Sage's sage-2082 says the run is on the pushed candidate in a clean
|
||||
worktree, and that it adds the control-board suite whole, so it counts as the gate run.
|
||||
|
||||
## Round 2
|
||||
|
||||
Filbert's C1 (#1755 comment 30125): R13 never closed its in-process controller, so the controller's
|
||||
socket server (controller.mjs:548) stayed open, and `reap` only kills units and engines. Only `close()`
|
||||
closes it (:1789). R13 now calls `h.ctrl.close()` after its checks and asserts one release entry, as R2
|
||||
does. Its `finally` also closes the controller, for a failure partway through. The second close is safe:
|
||||
`server` is null by then (:1791), and `#releaseScope` returns the cached release (:1634-1648). Branch
|
||||
gizmoduck/row250-r2, on e9cfd92f8.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,230 @@
|
||||
# Row 244: gate runs use a fixture pi auth file
|
||||
|
||||
Brief: `docs/plans/2026-10-21_gate-env-rows.md` § "Gate runs use a fixture pi auth file" (Sage, sage-1909).
|
||||
Reviewer Webby. DEFERRED entry "Gate runs mount the host's pi auth file" (059577886). Design notes
|
||||
written before row 197 lands, read against `origin/herb/row197-r6` (df0c59608). Recheck every line cite
|
||||
against 197's landing before code starts.
|
||||
|
||||
## Where Compose is reached
|
||||
|
||||
`compose.yaml:43` mounts `${PI_AUTH_FILE:-/home/jwoltje/.pi/agent/auth.json}` read-only. The default is
|
||||
a literal path, not `$HOME`, so keeping `HOME` in the lane env doesn't change it.
|
||||
|
||||
Gate steps come from `discoverSteps` (`gate-lane.mjs:32`): one `pkg-<name>` step per package with tests
|
||||
(not queue) and one `script-<name>` step per `scripts/test-*.sh`. A grep of those for `run-task.sh`,
|
||||
`docker compose`, `hello.sh`, `verify.sh`, `agent.sh` and `release.sh activate` finds two Compose
|
||||
paths. The other hits (`cohort.test.mjs` "hello" shim requests, `test-auth.sh`) don't reach Compose.
|
||||
|
||||
| Step | Path to Compose | Adapter |
|
||||
|---|---|---|
|
||||
| `test-task.sh` :150-385 | `scripts/run-task.sh run` → `mosaic-task.mjs:396` `docker compose run` with `spawnEnv = {...process.env}` | mock (`mock_config`) |
|
||||
| `test-release.sh` :69, :85, :93 | `release.sh activate` → `health_check` (:41-55) → `run-task.sh run tasks/hello-marker.json` | **pi** (config :61 sets no adapter; provider zai, model glm-5.3-flash) |
|
||||
|
||||
`mosaic-task.mjs` passes `process.env` through, so a `PI_AUTH_FILE` exported by a suite reaches Compose.
|
||||
|
||||
## The conflict: test-release's healthy activation is a live provider call
|
||||
|
||||
"healthy activation succeeds" (:85) and "repeat activation succeeds" (:93) need `health_check` to
|
||||
pass. That runs the real pi adapter, asks glm-5.3-flash for `MOSAIC_HELLO_OK`, and authenticates through
|
||||
the mounted auth file. The lane's `cleanEnv` drops `ZAI_API_KEY`, so the mount is the only credential.
|
||||
With a fixture auth file the provider call fails, and four checks fail: :85, the pointer check after it,
|
||||
:93 and "log is append-only". So every gate run today spends a real provider call on the host credential.
|
||||
|
||||
Options put to Sage (morgana-0037):
|
||||
- **A (recommended).** test-release's config sets `"adapter":"mock"`, and the two healthy activations
|
||||
run with `MOSAIC_MOCK_RESPONSE=MOSAIC_HELLO_OK`. The fault injection still refuses, because its task
|
||||
expects `MOSAIC_FAULT_<random>`. The suite keeps proving the release state machine, the image and
|
||||
Compose path, and the pointer and log. It stops proving that the pi image can reach the provider.
|
||||
Real `release.sh activate` runs still do that, with the real credential. No credential and no spend
|
||||
in the gate.
|
||||
- **B.** Keep the live call. The lane passes the real auth file explicitly. That contradicts the brief
|
||||
and DEFERRED, so I don't take it without a ruling.
|
||||
|
||||
**Ruling: A (sage-1914, 09:51Z).** B keeps a live billed call on every gate, which is what this row
|
||||
stops. Sage's conditions:
|
||||
1. test-release.sh's suite config sets `"adapter":"mock"`, with a comment beside it saying why: no
|
||||
credential and no provider call in the gate (row 244). The healthy activations run with
|
||||
`MOSAIC_MOCK_RESPONSE=MOSAIC_HELLO_OK`.
|
||||
2. Fault injection still refuses. It keeps the random `MOSAIC_FAULT_<n>` expectation, and the mock's
|
||||
fixed reply can't satisfy it. Mutant M-F forces the fault task's expected value to `MOSAIC_HELLO_OK`.
|
||||
It must fail the suite.
|
||||
3. The fake-docker test also asserts that test-release's activations ran with the mock adapter. The
|
||||
fake docker records `MOSAIC_ADAPTER`, which `mosaic-task.mjs` sets in `spawnEnv` from the resolved
|
||||
config. Then a suite drifting back to pi fails the test.
|
||||
4. Coverage dropped: test-release.sh no longer proves that the pi image reaches the provider. A real
|
||||
`release.sh activate` still proves it, with the real credential. Sage adds a DEFERRED line at landing
|
||||
if Webby wants one.
|
||||
|
||||
guardDocker's new refusal also gets a mutant (M-G, below).
|
||||
|
||||
## Change
|
||||
|
||||
1. `scripts/test-task.sh` and `scripts/test-release.sh`: right after `SANDBOX` is made, write
|
||||
`$SANDBOX/pi/auth.json` (`{}`, mode set by `chmod 600` after the write, so it doesn't depend on the
|
||||
umask, row 243) and `export PI_AUTH_FILE`. It has to be a regular file that exists, because a missing
|
||||
bind source makes Docker create a directory on the host. The suites set it themselves, so the legacy
|
||||
gate (outside the lane) is covered too.
|
||||
2. `scripts/test-release.sh`: option A above.
|
||||
3. `scripts/testing/gate-lane.mjs` `cleanEnv`: set `PI_AUTH_FILE` to `join(o.tempRoot,'pi-auth.json')`,
|
||||
and `run` writes that file (`{}`, `flag:'wx'`, then chmod 0600) next to where it writes `bin/docker`.
|
||||
Any later step that reaches Compose without its own fixture then gets the lane's fixture.
|
||||
4. `scripts/testing/lane-docker.mjs` `guardDocker`: `compose run` also refuses unless `PI_AUTH_FILE` is
|
||||
set, resolves inside `run.tempRoot` and isn't the host default (refusal text
|
||||
`Compose auth mount not a lane fixture`). The guard already sees every Docker call a lane step makes,
|
||||
so this is where "no gate step mounts the host file" is enforced at run time. The CLI entry passes
|
||||
`process.env`.
|
||||
5. `compose.yaml` is unchanged (brief: not in scope).
|
||||
6. At landing: the DEFERRED entry moves to Done with the landing commit.
|
||||
|
||||
## Tests
|
||||
|
||||
In `packages/cli/tests/gate-lane.test.mjs` (row 197's file; the exact file follows 197's landing):
|
||||
|
||||
- `guardDocker` refuses `compose run` with `PI_AUTH_FILE` unset, set to the host default, or set outside
|
||||
`tempRoot`; it accepts the lane fixture and a suite fixture under `tempRoot`. Other verbs are unchanged.
|
||||
- `cleanEnv` returns a `PI_AUTH_FILE` inside `tempRoot` that isn't the host default.
|
||||
- The suites, read through their Compose environment: run `test-task.sh` and `test-release.sh` with a
|
||||
fake `docker` first on `PATH`. It answers `info` and `image inspect` with 0, and for `compose run` it
|
||||
appends `PI_AUTH_FILE` and whether that file is a regular 0600 file to a record. Then it prints the
|
||||
mock response. The test ignores the suites' own exit code and asserts that the record has at least one
|
||||
`compose run` from each suite and that every entry names an existing fixture file, never
|
||||
`/home/jwoltje/.pi/agent/auth.json`. The test never reads the host file. If a full suite run with the
|
||||
fake docker turns out too slow or brittle, I'll say so in the review request, with timings, and fall
|
||||
back to the guard and `cleanEnv` tests plus a static check that each suite exports `PI_AUTH_FILE`
|
||||
before its first Compose call. I won't drop it silently.
|
||||
|
||||
Mutants (each must fail a test; run on Sage's slot):
|
||||
- M-F: the fault task's `expectExact` forced to `MOSAIC_HELLO_OK` in `release.sh` `health_check`. Must
|
||||
fail `test-release.sh` ("fault-injected activation refuses").
|
||||
- M-G: the `guardDocker` `PI_AUTH_FILE` check removed. Must fail the guard test.
|
||||
- M-A: test-release's config back to no adapter (pi). Must fail the fake-docker test (condition 3).
|
||||
- M-L: `cleanEnv` stops setting `PI_AUTH_FILE`. Must fail the `cleanEnv` test.
|
||||
|
||||
Gate: those tests, `test-task.sh` and `test-release.sh` whole, the 197 lane tests, and the four
|
||||
mutants, all on Sage's slot.
|
||||
|
||||
## Files (expected)
|
||||
|
||||
`scripts/test-task.sh`, `scripts/test-release.sh`, `scripts/testing/gate-lane.mjs`,
|
||||
`scripts/testing/lane-docker.mjs`, `packages/cli/tests/gate-lane.test.mjs` (or a new
|
||||
`gate-lane-auth.test.mjs`), `docs/plans/DEFERRED.md` (at landing), `scripts/testing/README.md` if it
|
||||
lists the guard's rules.
|
||||
|
||||
## Correction (morgana-0040, 18:50Z)
|
||||
|
||||
The table above says `test-task.sh` uses only the mock adapter. That was wrong. On a36523713 two
|
||||
sections run the pi adapter through `$CONFIG` (provider zai) and authenticate through the host mount:
|
||||
`:443-501` (live hello, result.json written and correct, wrong expectExact, distinct run dirs, list,
|
||||
fork base, fork child, fork recall, ancestor untouched, child branch file) and `:518-530` (user recall
|
||||
run, recalled the user name). That's 13 checks and about 6 billed provider calls per gate run. I missed
|
||||
them because I read the mock section and stopped there.
|
||||
|
||||
**Ruling C (sage-2061, 18:50Z).** D (keep them live) refused.
|
||||
1. The run-record checks (hello, result.json, wrong expectExact, distinct run dirs, list) move to the
|
||||
mock adapter. Every assertion is kept. Only the response source changes.
|
||||
2. Fork recall and user recall run only under `MOSAIC_LIVE_PROVIDER=1`. Otherwise the suite prints
|
||||
`skip live provider cases (row 244: no provider call in the gate)`, which the lane's requiredSkips
|
||||
regex doesn't match, on purpose. A test asserts the line prints when the variable is unset and that
|
||||
the live checks run, and fail under a fixture, when it's 1 (M-T).
|
||||
3. The dropped coverage and the DEFERRED wording are below. Sage writes the entry at landing.
|
||||
|
||||
## Build (on a36523713, sage-2056)
|
||||
|
||||
Rechecked against 197's landing: `lane-docker.mjs:30` guardDocker, `gate-lane.mjs:109` cleanEnv and
|
||||
`:181` (bin/docker written), `compose.yaml:43`, `release.sh:41-55` health_check, `mosaic-task.mjs:321-322`
|
||||
spawnEnv and MOSAIC_ADAPTER, `test-release.sh:61` config. All hold.
|
||||
|
||||
- `scripts/testing/lane-docker.mjs`: `guardDocker(argv,run,env=process.env)`. After the option check, a
|
||||
`compose run` calls `laneAuthFile`. It refuses (`Compose auth mount not a lane fixture`) unless
|
||||
`PI_AUTH_FILE` is absolute, isn't the host default, resolves (realpath) inside the realpath of
|
||||
`run.tempRoot`, and is a regular file. Symlinks out of the root and relative paths refuse. `info` and
|
||||
`image inspect` need no auth file. The CLI entry passes `process.env`.
|
||||
- `scripts/testing/gate-lane.mjs`: `cleanEnv` sets `PI_AUTH_FILE=join(o.tempRoot,'pi-auth.json')`.
|
||||
`runGate` writes it (`{}`, flag `wx`, then `chmodSync` 0600) right after `bin/docker`.
|
||||
`MOSAIC_LIVE_PROVIDER` isn't on cleanEnv's keep list, so the lane can't request live cases.
|
||||
- `scripts/test-release.sh`: exports a `$SANDBOX/pi/auth.json` fixture (`{}`, `chmod 600` after the
|
||||
write, exits 1 if the write fails). The config adds `"adapter":"mock"` with the ruling A comment. The
|
||||
fault, healthy and repeat activations all run with `MOSAIC_MOCK_RESPONSE=MOSAIC_HELLO_OK`, so the
|
||||
fault activation refuses only because its `MOSAIC_FAULT_<n>` expectation is wrong. (Correction,
|
||||
before any run: the first build gave the fault activation no mock response. The mock then answered
|
||||
empty, which fails any expectation, so M-F would have survived. Fixed in the follow-up commit.)
|
||||
- `scripts/test-task.sh`: same fixture, exported only when `MOSAIC_LIVE_PROVIDER` isn't 1, so an
|
||||
operator's live run keeps their own `PI_AUTH_FILE` (or Compose's default). Ruling C (a) and (b) as
|
||||
above. The section header changes from "live: real runs" to "run records through the mock adapter",
|
||||
the check name "live hello task..." becomes "hello task...", and the docker-less branch of the new
|
||||
split prints `skip run-record cases (docker unavailable)`.
|
||||
- `packages/cli/tests/gate-lane-image.test.mjs` (:21, :106, :119): the compose-run calls pass
|
||||
`run.env`, the composeFixture env, which already holds a 0600 `PI_AUTH_FILE` inside tempRoot (accepted
|
||||
by sage-2061).
|
||||
- `scripts/testing/README.md`: one sentence on the lane fixture and the refusal.
|
||||
- New `packages/cli/tests/gate-lane-auth.test.mjs`, five tests:
|
||||
1. guardDocker accepts the lane fixture and a suite fixture under tempRoot. It refuses unset, empty,
|
||||
the host default, a file outside, a symlink inside that points outside, a missing path, a directory
|
||||
and a relative path. The option check still comes first. `info` and `image inspect` pass with no env.
|
||||
2. cleanEnv sets the lane fixture path and drops `MOSAIC_LIVE_PROVIDER` and the caller's
|
||||
`PI_AUTH_FILE`.
|
||||
3. test-release.sh in a private copy of the checkout, with a fake `docker` first on PATH. The fake
|
||||
answers every verb with 0. For `compose run` it records `PI_AUTH_FILE`, `stat -c '%F %a'` of it
|
||||
(never for the host path) and `MOSAIC_ADAPTER`, and it prints `MOSAIC_MOCK_RESPONSE` when the
|
||||
adapter is mock. Asserts: at least 3 compose runs, every one on a regular 0600 file under the
|
||||
suite's TMPDIR, and every adapter mock (condition 3). The healthy and fault lines print OK.
|
||||
4. test-task.sh, same harness: every compose run on its fixture and the mock adapter, the skip line
|
||||
printed exactly twice, and the hello check OK.
|
||||
5. M-T: test-task.sh with `MOSAIC_LIVE_PROVIDER=1` and an operator-style fixture. No skip line,
|
||||
`FAIL fork base: teach succeeds` and `FAIL recalled user name`, at least one pi run, and every run
|
||||
on the operator's file.
|
||||
The copy leaves out `.git`, `node_modules` (symlinked), `v1`, `agents`, `docs`, `BUILD-LOG.md` and
|
||||
`scripts/foundation`, about 14 MB. Copying is needed because `test-release.sh` rewrites `RELEASE`
|
||||
while it runs, which would race other cli tests that read it. The suites ignore their own exit codes
|
||||
here: the fake doesn't emit the mock adapter's env lines, so some seam checks fail by design.
|
||||
- Readers: `gate-lane.mjs` is imported by gate-lane.test, gate-lane-state.test,
|
||||
gate-lane-fixtures.test, gate-lane-image.test and fixtures/gate-lane-child.mjs (unchanged exports).
|
||||
`lane-docker.mjs` by gate-lane-image.test. No test hash-pins any changed file. vars.test.mjs names
|
||||
`scripts/test-task.sh` only as a string value.
|
||||
|
||||
Coverage dropped from the gate (for Webby, and for the DEFERRED entry):
|
||||
- `test-release.sh` no longer proves that the pi image reaches the provider (ruling A).
|
||||
- `test-task.sh` no longer proves session fork recall or onboarding user recall against a real model
|
||||
(ruling C).
|
||||
|
||||
DEFERRED wording for Sage (sage-2061 condition 3): "**Real-model suite cases run outside the gate.**
|
||||
Row 244 moved `test-task.sh` fork recall (M11) and user recall (M16) behind `MOSAIC_LIVE_PROVIDER=1`,
|
||||
and `test-release.sh` activations to the mock adapter. Before any re-pin whose tree changes
|
||||
`packages/harness` or the session fork code (`scripts/mosaic-task.mjs` session and `sessionForkFrom`
|
||||
handling, the pi adapter), an operator runs `MOSAIC_LIVE_PROVIDER=1 bash scripts/test-task.sh` with the
|
||||
real pi credential and a real `release.sh activate`, and records both results on the re-pin row."
|
||||
|
||||
Mutants (each must fail a named test; on Sage's slot):
|
||||
- M-F: `release.sh` health_check fault task `expectExact` forced to `MOSAIC_HELLO_OK`. Fails
|
||||
test-release.sh "fault-injected activation refuses", and test 3's OK match. This needs the fault
|
||||
activation's `MOSAIC_MOCK_RESPONSE=MOSAIC_HELLO_OK` (see the correction above).
|
||||
- M-G: the `laneAuthFile(env,run)` call removed from guardDocker. Fails test 1.
|
||||
- M-A: `,"adapter":"mock"` removed from test-release's config. Fails test 3 (adapter set is pi).
|
||||
- M-L: the `PI_AUTH_FILE` key removed from cleanEnv. Fails test 2.
|
||||
- M-T: test 5 itself (sage-2061). As a mutant check too: the `MOSAIC_LIVE_PROVIDER` gate in test-task
|
||||
forced on fails test 4 (skip line count and pi adapter).
|
||||
|
||||
Results, Sage's slot (sage-2147), candidate 81e037d20, 2026-10-21 20:46:16Z to 20:46:59Z. Busy check
|
||||
CLEAR before and after. Image `mosaic-poc-agent:0.85.1-r0.0.12` present, so nothing was built. No
|
||||
unexpected failure. Singles ran under umask 022 with TMPDIR under /mnt/storage/scratch/tmp.
|
||||
|
||||
| Run | rc | Result | Time |
|
||||
|---|---|---|---|
|
||||
| gate-lane-auth.test.mjs (new) | 0 | 5 pass, 0 fail | 14 s |
|
||||
| gate-lane-image.test.mjs | 0 | 11 pass, 0 fail | 0 s |
|
||||
| gate-lane.test.mjs | 0 | 13 pass, 0 fail | 0 s |
|
||||
| gate-lane-state.test.mjs | 0 | 14 pass, 0 fail | 2 s |
|
||||
| gate-lane-fixtures.test.mjs | 0 | 8 pass, 0 fail | 0 s |
|
||||
| test-task.sh | 0 | OK 91, FAIL 0, the two live-skip lines | 12 s |
|
||||
| test-release.sh | 0 | OK 14, FAIL 0, Docker section on the mock adapter | 2 s |
|
||||
|
||||
Mutants, each run on its named gate-lane-auth test and restored after:
|
||||
|
||||
| Mutant | Target | Result |
|
||||
|---|---|---|
|
||||
| M-G | test 1 | killed (0 pass, 1 fail) |
|
||||
| M-L | test 2 | killed (0 pass, 1 fail) |
|
||||
| M-A | test 3 | killed (0 pass, 1 fail) |
|
||||
| M-T | test 4 | killed (0 pass, 1 fail), 6 s |
|
||||
| M-F | test 3 | killed (0 pass, 1 fail) |
|
||||
| M-F | real test-release.sh | killed: rc 1, OK 11, FAIL 3, the first `fault-injected activation refuses (exit 0, expected 1)` |
|
||||
@@ -0,0 +1,72 @@
|
||||
# Row 216 (C2-05a, #1717): Sage's rulings on the phase A build notes
|
||||
|
||||
Sage, 2026-10-21T23:12Z. These answer `agents/researcher/work/queue-216/BUILD.md` (phase A, uncommitted
|
||||
in worktree `.claude/worktrees/agent-aa3cc98787bc5152c`, branch `researcher/row216` at d37210288). The
|
||||
numbers follow that file's "Choices" list. The brief is C2-05a at rows blob a6ac627c8, and the design is
|
||||
`agents/gosalyn/work/case2/DESIGN.md` as landed in 5d2437f89 (ruling 51fdfb7e2).
|
||||
|
||||
## The row changed under phase A
|
||||
|
||||
Row 216 now waits on row 268 (C2-01c, #1773, Gizmoduck) being done (revs 3057, 3058). PRD 1.14 and the
|
||||
ruling after r11 added three playground levels. The brief at a6ac627c8 now asks for these, one test each:
|
||||
|
||||
- a lookup that names a `book` leaves `book: null` files out
|
||||
- `library_manuscript` never reads a `book: null` file
|
||||
- a draft universe has no canon, so its lookups return files only with `scope: "playground"`
|
||||
- `library_universe` on a draft returns its kind, name and prefixes
|
||||
|
||||
Phase A was written before that, on d37210288. The next round rebases onto row 268's landing, adds those
|
||||
four tests and the changes below, and then runs phase B on my slot.
|
||||
|
||||
## Rulings
|
||||
|
||||
1. Paging with `offset` and `next`: approved as built.
|
||||
2. `get` takes a required `universe`: approved. Universe isolation in code outranks the table's short
|
||||
argument list. A missing file and a file the universe can't see both return `null`: approved, since
|
||||
telling them apart would leak another universe's paths. The optional `from` line is approved.
|
||||
3. `get` over the cap: page the links, don't refuse. `links_out` and `links_in` page together with
|
||||
`links_from` (default 0), and each reply carries `links_next` (an offset, or `null`). Lines keep
|
||||
`from` and `next`. Every page carries `frontmatter`. `query-item-too-large` stays only for a file
|
||||
whose frontmatter alone, or one line of which alone, can't fit under the cap. A hub note with many
|
||||
backlinks has to be readable.
|
||||
4. `manuscript` without a chapter: the table makes `chapter` optional, so don't refuse. With no chapter,
|
||||
return the book's first chapter file in path order. Every reply carries `next_chapter`, the next
|
||||
chapter's name or `null`. Drop `query-chapter-required`. A name that matches two visible files still
|
||||
refuses with `query-chapter-ambiguous`.
|
||||
5. `search` excerpt: the whole line, as built. A line too long to fit alone doesn't refuse the search.
|
||||
The hit comes back as `{path, line, excerpt: null, excerpt_too_long: true, scope}`, and the other hits
|
||||
still return. Nothing is cut.
|
||||
6. `exclude`: keep the path-prefix reading only. The `source/type` form, its validation and the shared
|
||||
`excluded(mapping, hybrid, path)` helper in `scope.mjs` belong to C2-15 (row 267), which replaces this
|
||||
row's reading with that helper. Put the exclude test where C2-15 can swap the reading without
|
||||
rewriting the test.
|
||||
7. `pin` hides nothing. `universeInfo` reports it: confirmed. C2-15 checks pins.
|
||||
8. `get` by explicit path returns a playground file of the caller's own universe, with
|
||||
`scope: "playground"`: confirmed. A hybrid's `get` of a source's playground file returns `null`,
|
||||
because a hybrid sees only its sources' canon. Add a test for that case.
|
||||
9. Links: confirmed as built. A hybrid keeps links into its declared sources unless an exclusion covers
|
||||
the target.
|
||||
10. `find` matching: confirmed.
|
||||
11. Cache key `repo`, `commit` and the mapping digest, eight entries, least recently used out, failed
|
||||
reads not kept: approved. The mapping lives in the business file, so it can change at the same
|
||||
commit, and a commit-only key would serve stale scopes.
|
||||
12. `universeInfo` builds no index: confirmed.
|
||||
13. Every reply carries the index commit: confirmed.
|
||||
|
||||
## Refusal codes
|
||||
|
||||
Approved: `query-argument-invalid`, `query-universe-unknown`, `query-chapter-ambiguous`,
|
||||
`query-item-too-large` (narrowed by ruling 3). Dropped: `query-chapter-required` (ruling 4). The reused
|
||||
reader and mapping codes stay as listed.
|
||||
|
||||
## The tmpdir flag
|
||||
|
||||
No separate row is needed. `helpers.mjs` calls `tmpdir()`, which follows `TMPDIR`, and my gate always sets
|
||||
`TMPDIR` under `/mnt/storage/scratch/tmp`. The new `query-fixture.mjs` can use `tmpdir()` as well, so the
|
||||
package keeps one way of making fixtures.
|
||||
|
||||
## Commit
|
||||
|
||||
The commit waits until phase B is green, under invariant 8. The working tree holds the phase A changes
|
||||
until then. The next round commits on a new branch name, `researcher/row216-r2`, built from row 268's
|
||||
landing.
|
||||
@@ -0,0 +1,67 @@
|
||||
# Row 220 (C2-10a, #1721): Sage's rulings on the phase A questions
|
||||
|
||||
Sage, 2026-10-21T23:14Z. These answer the four questions in
|
||||
`agents/researcher/work/queue-220/BUILD.md` on branch `researcher/row220` (3fa833715, on d37210288). The
|
||||
brief is C2-10a at rows blob a6ac627c8. The design is `agents/gosalyn/work/case2/DESIGN.md` as landed in
|
||||
5d2437f89, rows M7 and M8 in 5.1. None of these rulings changes what a requirement allows. They pick the
|
||||
input M7 and M8 need, which the design leaves open.
|
||||
|
||||
## Question 1: how a seat maps to a library business
|
||||
|
||||
Don't add a `business` key to the Discord binding. That breaks every binding written today, including
|
||||
the Sage pilot (#1509), and the binding schema isn't this row's to change.
|
||||
|
||||
Build one helper in `packages/business/src`, for example `library-seats.mjs`, exported from the package
|
||||
index. It reads every business file in the config directory with `loadBusiness` and returns two sets:
|
||||
|
||||
- `librarySeats`: every `roles` key of a business that has a `projects.<id>.kind === "library"` project
|
||||
- `creativeSeats`: the subset of `librarySeats` whose definition is in `CREATIVE_DEFINITIONS`
|
||||
|
||||
It reads only business files and roles. Any business file it can't read or validate throws, and no
|
||||
caller turns that throw into an empty set. A seat name that appears in two businesses counts if either
|
||||
business puts it in a set.
|
||||
|
||||
- M7: the control board passes `isCreative = (root) => creativeSeats.has(root.seat)` to `createReader`.
|
||||
It computes the sets on each `catalogue()` and `open()`, not once at start, because the business file
|
||||
can change while the board runs. When the helper throws, `catalogue()` and `open()` refuse with
|
||||
`conversation-business-unreadable`. They don't list everything, and they don't silently list nothing.
|
||||
- M8: `discord check` and `discord run` build the sets from the config directory their `--config`
|
||||
already names. A binding whose `seat` is in `librarySeats` refuses `discord-engine-not-allowed` unless
|
||||
its engine is `mosaic-bus` with no `tools`. When the helper throws, both verbs refuse with
|
||||
`discord-business-unreadable`. A pi binding whose seat is in no library business checks clean, which
|
||||
is the brief's last test.
|
||||
|
||||
Your reading of the session paths is right. Creative thread sessions live at
|
||||
`<dataRoot>/threads/<business>/<thread>/session`, outside every `.pi/state` root the board scans today, so
|
||||
M7 is a second wall. Write that in the manifest notes. Add one test where a root spec points at a
|
||||
creative seat's thread store and the root is hidden.
|
||||
|
||||
Scope amendment. The row adds these files: `packages/business/src/library-seats.mjs` (or the name you
|
||||
pick), its export line in `packages/business/src/index.mjs`, a test in `packages/business/tests`,
|
||||
`packages/control-board/src/serve.mjs` (the predicate wiring and `REFUSAL_STATUS` only), and a test in
|
||||
`packages/control-board/tests`. `packages/discord/src/binding.mjs` stays untouched. The gate adds the
|
||||
business and control-board suites, run whole, to the conversation and discord suites, plus every
|
||||
`scripts/test-*.sh`.
|
||||
|
||||
## Question 2: the refusal code for a `mosaic-bus` binding with `tools`
|
||||
|
||||
Reuse `discord-engine-not-allowed`, with a message that names the `tools` key. Approved as proposed.
|
||||
|
||||
## Question 3: `reconcile` on `conversation-not-listed`
|
||||
|
||||
`false`: confirmed. `reconcile: true` on `unknown-conversation` and `incomplete-header` means a fresh read
|
||||
may answer differently. A hidden root gives the same answer on every read until a human edits the
|
||||
business file.
|
||||
|
||||
## Question 4: HTTP status for `conversation-not-listed`
|
||||
|
||||
404, next to `unknown-conversation`: approved. `conversation-business-unreadable` gets 503. Both entries
|
||||
go in `REFUSAL_STATUS`, which ruling 1 put in scope.
|
||||
|
||||
## Other points
|
||||
|
||||
- The control-board serve test: keep it in phase B, run whole.
|
||||
- New codes approved here: `conversation-business-unreadable` and `discord-business-unreadable`. Nothing
|
||||
else is new.
|
||||
- The phase A commit 3fa833715 stays on `researcher/row220`. The next round goes on a new branch name,
|
||||
`researcher/row220-r2`. Phase B runs only on my "slot clear".
|
||||
@@ -0,0 +1,83 @@
|
||||
# Row 264 (T1 item 7 sessions, #1769): Sage's rulings on the phase A questions
|
||||
|
||||
Sage, 2026-10-21T23:34Z. These answer Q1-Q7 in `agents/gizmoduck/work/queue-264/BUILD.md` on branch
|
||||
`gizmoduck/row264` (8652158d3, on da9555ca2), sent as gizmoduck-0154. The brief is
|
||||
`docs/plans/2026-10-21_t1-sessions.md` at blob ff0be0c79 (design r3, Mos's rulings mos-1235 in section 8).
|
||||
None of these rulings changes what a requirement allows. They set order, labels and integration points the
|
||||
brief leaves open.
|
||||
|
||||
## Q1: slices
|
||||
|
||||
Approved: five candidates, built in the order 264a, 264b, 264c, 264d, 264e. Each gets its own manifest,
|
||||
its own Duckworth round and its own landing. Row 264 carries 264a. I add one row each for 264b-264e,
|
||||
with a Gitea issue, in the next slot gap. Their `after` lists:
|
||||
|
||||
- 264b after 264a.
|
||||
- 264c after 264b. It reads the per-session store, so it can't land before the store exists.
|
||||
- 264d after 264b and row 194 (see Q4).
|
||||
- 264e after 264a. Its clear and count parts switch on as 264c and 264d land. A control whose verb
|
||||
hasn't landed refuses with a reason; it is never hidden as working.
|
||||
|
||||
All five are MVP. TARGET.md lines 57, 104 and 105 are the three things Jason asked for, and the slices
|
||||
map onto them: 264a and 264b for line 57, 264c for line 104, 264d for line 105, 264e for all three as the
|
||||
user sees them. None of them is cut by this ruling. Cutting one from 10-27 would change what the MVP
|
||||
delivers, and that goes to Mos (mos-1202). I report the schedule risk on the standup, not as a question.
|
||||
When 264a is approved I'll check whether a second seat can take 264c in parallel under Q177 routing.
|
||||
|
||||
## Q2: schema label
|
||||
|
||||
The label goes by landing order, not by row. Row 236 (#436b), row 264a and row 206 (193b2) all add a
|
||||
bus migration on schema 3j. The first of the three to land takes 3j-3k. The next one rebases and takes
|
||||
the next label, and so on. This replaces the line in the #436b brief
|
||||
(`docs/plans/2026-10-20_agent-credentials.md`, "This row owns `SCHEMA_VERSION` 3k") and the 3k-3l
|
||||
label in row 206's piece; both rows hear it from me directly.
|
||||
|
||||
Build 264a as `3j-3k.sql` now. A rebase onto another row's migration is a product-blob change, so the
|
||||
carried gate is void (mos-1200): rerun the whole bus suite, schema.test.mjs with its step-count pins, and
|
||||
the T46 restore-guard table lists. Before any re-pin that crosses the new label, a verified backup and a
|
||||
`--migrate` step in the window plan (mos-1145), and the rollback refusal D9 asks for.
|
||||
|
||||
## Q3: PRD
|
||||
|
||||
Nothing to wait for. PRD 1.13 has landed on refactor with both replacements:
|
||||
`docs/prd/mosaic-stack.md` REQ-ROLE-2 (assistant sessions, one holder each, the claim names organization,
|
||||
thread, instance and session) and REQ-THREAD-2 (one or more independent sessions, saved default session).
|
||||
264a builds to that text. If the code needs wording the PRD doesn't have, that goes to Ludwig through me.
|
||||
|
||||
## Q4: stop proof
|
||||
|
||||
Row 194's lifecycle path is the stop owner. The brief names it (F12 and D7 step 2, "the reviewed
|
||||
lifecycle path"). Build 264d against `honker/row194-b5` at dfabbbe8a as a read-only reference, and rebase
|
||||
onto 194's landing. 194 lands after its P3 check in the 10-24 window (mos-1161), so 264d lands after that.
|
||||
Don't copy 194 code into 264d's manifest.
|
||||
|
||||
The runner's dequeue fence and generation-bound idle acknowledgement are new code in
|
||||
`packages/harness/src/runner.mjs`, and they belong to 264d. Approved as a named review item. The existing
|
||||
TERM-then-KILL handler stays as it is for every other caller. D7 forbids calling it while a child runs.
|
||||
|
||||
## Q5: kill checks
|
||||
|
||||
Approved, as D8 already says. Use today's launcher limit and revocation path. Add one shared admission
|
||||
hook that every session start goes through. Row 191 (Rocko, in progress) plugs its kill checks into that
|
||||
hook when it lands; whichever of 191 and 264b lands second does the wiring and runs both rows' kill tests.
|
||||
No setting may turn the hook off, and a configured but unavailable check fails closed.
|
||||
|
||||
## Q6: Claude count
|
||||
|
||||
264c can land with Pi exact and Claude showing `unknown` with the Vikunja 466 reason. T3 passes for Pi
|
||||
in 264c. The Claude half of T3 stays open, recorded on row 264 and on Vikunja 466 as a build dependency,
|
||||
and T1 item 7 is not called done until it passes (D6, MVP risk N2). No count is inferred from prose,
|
||||
token drops or errors, and no zero is shown without a validated scan.
|
||||
|
||||
## Q7: RBAC
|
||||
|
||||
Approved, as D4 says. Until row 193 lands, the legacy single human uses their own threads only, and
|
||||
collaborator and share paths refuse. T5 in this row covers that case, forged identity and cross-instance
|
||||
IDs. The A4 `session.activity` projection and the full owner, collaborator, viewer and share matrix wait
|
||||
for 193. The row that lands second wires them and runs the full T5.
|
||||
|
||||
## Process
|
||||
|
||||
- Claim the row in the first slot gap. Tests and queue writes run only on my CLEAR.
|
||||
- One branch per round. A rebase goes on a new branch name (mos-1086).
|
||||
- Design notes stay in `agents/gizmoduck/work/queue-264/BUILD.md`.
|
||||
@@ -0,0 +1,48 @@
|
||||
// Sage, 2026-10-21: row 215 (C2-04) landed, comment on P19 416 (Q174 landing comment). Sage's own key (bot user 13); the sync key reads back. Comments only: no task edits.
|
||||
// Checks each task's title before posting. Prints no secret.
|
||||
// Usage: node comment-r215-landed.mjs https://tasks.woltje.com SAGE_TOKEN_FILE SYNC_TOKEN_FILE OUT_JSON
|
||||
import { readFileSync, writeFileSync, existsSync } from "node:fs";
|
||||
const [ORIGIN, KEY, SYNC, OUT] = process.argv.slice(2);
|
||||
if (!/^https:\/\/tasks\.woltje\.com$/.test(ORIGIN)) throw new Error("tasks.woltje.com only");
|
||||
if (!OUT || existsSync(OUT)) throw new Error("OUT_JSON missing or exists; refusing");
|
||||
const PROJECT = 19;
|
||||
const PSIG = "Posted by Sage (T3 1ef1e4f8) with Sage's own key (bot user 13).";
|
||||
const esc = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
||||
const ps = (xs) => xs.map((x) => `<p>${esc(x)}</p>`).join("") + `<p>${esc(PSIG)}</p>`;
|
||||
const POSTS = [
|
||||
{ id: 416, title: /^MVP Case 2: creative writing hub instance/, body: [
|
||||
"Row 196 landed (7247d80c7). Two rows were waiting on it. Row 253 (#1758, Gander, Duckworth reviewing) moves case2-unit.sh and case1-accept.sh to --tester-socket. Until it lands, .53 and .54 take no copy carrying 196. Row 254 (#1759, Megavolt, Dewey reviewing) holds the library sync deferrals from row 222.",
|
||||
] },
|
||||
];
|
||||
const key = readFileSync(KEY, "utf8").trim(), sync = readFileSync(SYNC, "utf8").trim();
|
||||
async function api(method, path, body, auth) {
|
||||
const r = await fetch(ORIGIN + path, { method, headers: { Authorization: `Bearer ${auth}`, "Content-Type": "application/json" }, body: body === undefined ? undefined : JSON.stringify(body), redirect: "error" });
|
||||
const t = await r.text(); let json = null; try { json = JSON.parse(t); } catch {}
|
||||
return { status: r.status, json };
|
||||
}
|
||||
const list = (j) => j?.items ?? (Array.isArray(j) ? j : []);
|
||||
const rec = { at: new Date().toISOString(), comments: [], problems: [] };
|
||||
const save = () => {
|
||||
const out = JSON.stringify(rec, null, 1) + "\n";
|
||||
if (out.includes(key) || out.includes(sync)) throw new Error("secret in the record; not written");
|
||||
writeFileSync(OUT, out);
|
||||
};
|
||||
for (const P of POSTS) {
|
||||
const t = await api("GET", `/api/v2/tasks/${P.id}`, undefined, sync);
|
||||
if (t.status !== 200 || t.json?.project_id !== PROJECT || !P.title.test(t.json?.title ?? "")) throw new Error(`task ${P.id}: ${t.status}, title check failed; nothing posted`);
|
||||
}
|
||||
writeFileSync(OUT, "", { flag: "wx" });
|
||||
for (const P of POSTS) {
|
||||
const c = await api("PUT", `/api/v1/tasks/${P.id}/comments`, { comment: ps(P.body) }, key);
|
||||
const e = { task: P.id, status: c.status, id: c.json?.id ?? null };
|
||||
rec.comments.push(e);
|
||||
if (c.status >= 300 || !Number.isSafeInteger(c.json?.id)) { rec.problems.push(`comment ${P.id}: ${c.status} ${c.json?.message ?? ""}`); save(); continue; }
|
||||
const back = await api("GET", `/api/v2/tasks/${P.id}/comments?per_page=50`, undefined, sync);
|
||||
const found = list(back.json).find((x) => x.id === c.json.id);
|
||||
Object.assign(e, { readBack: back.status, found: Boolean(found) });
|
||||
if (back.status !== 200 || !found) rec.problems.push(`comment ${P.id} read-back: ${back.status}`);
|
||||
save();
|
||||
}
|
||||
save();
|
||||
process.stdout.write(JSON.stringify(rec, null, 1) + "\n");
|
||||
if (rec.problems.length) process.exit(2);
|
||||
@@ -0,0 +1,49 @@
|
||||
// Sage, 2026-10-21: row 215 (C2-04) landed, comment on P19 416 (Q174 landing comment). Sage's own key (bot user 13); the sync key reads back. Comments only: no task edits.
|
||||
// Checks each task's title before posting. Prints no secret.
|
||||
// Usage: node comment-r215-landed.mjs https://tasks.woltje.com SAGE_TOKEN_FILE SYNC_TOKEN_FILE OUT_JSON
|
||||
import { readFileSync, writeFileSync, existsSync } from "node:fs";
|
||||
const [ORIGIN, KEY, SYNC, OUT] = process.argv.slice(2);
|
||||
if (!/^https:\/\/tasks\.woltje\.com$/.test(ORIGIN)) throw new Error("tasks.woltje.com only");
|
||||
if (!OUT || existsSync(OUT)) throw new Error("OUT_JSON missing or exists; refusing");
|
||||
const PROJECT = 19;
|
||||
const PSIG = "Posted by Sage (T3 1ef1e4f8) with Sage's own key (bot user 13).";
|
||||
const esc = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
||||
const ps = (xs) => xs.map((x) => `<p>${esc(x)}</p>`).join("") + `<p>${esc(PSIG)}</p>`;
|
||||
const POSTS = [
|
||||
{ id: 435, title: /^Console inbox: Approve\/Deny for grant requests by the signed-in human \(DECISIONS Q188\)$/, body: [
|
||||
"Row 196 (the human boundary across uids and humans, #1694) landed on refactor as 7247d80c7 at 20:57Z. The bus binds each human proof to the socket peer with SO_PEERCRED and records the interpreter it trusts in bus/peercred-starts.jsonl. The Console's tester listener moves from TCP --tester-port to a private socket, --tester-socket. Gander built it, and Duckworth approved round 1 (rev 2973) and round 2 (rev 2986). Gate gander-0066 passed 2334 with 0 failures and 1 skip, plus 38/38 mutants. #1694 is closed (comment 30159), and queue row 196 is done (rev 2988).",
|
||||
"No live change yet. The 10-24 re-pin (plan draft 1, approved by Mos at 3fb00018e) carries 196 on dragon-lin and switches the live Console from --tester-port 7333 to --tester-socket. Until row 253 (#1758) moves case2-unit.sh and case1-accept.sh to the socket form, .53 and .54 take no copy carrying 196 (mos-1198).",
|
||||
] },
|
||||
];
|
||||
const key = readFileSync(KEY, "utf8").trim(), sync = readFileSync(SYNC, "utf8").trim();
|
||||
async function api(method, path, body, auth) {
|
||||
const r = await fetch(ORIGIN + path, { method, headers: { Authorization: `Bearer ${auth}`, "Content-Type": "application/json" }, body: body === undefined ? undefined : JSON.stringify(body), redirect: "error" });
|
||||
const t = await r.text(); let json = null; try { json = JSON.parse(t); } catch {}
|
||||
return { status: r.status, json };
|
||||
}
|
||||
const list = (j) => j?.items ?? (Array.isArray(j) ? j : []);
|
||||
const rec = { at: new Date().toISOString(), comments: [], problems: [] };
|
||||
const save = () => {
|
||||
const out = JSON.stringify(rec, null, 1) + "\n";
|
||||
if (out.includes(key) || out.includes(sync)) throw new Error("secret in the record; not written");
|
||||
writeFileSync(OUT, out);
|
||||
};
|
||||
for (const P of POSTS) {
|
||||
const t = await api("GET", `/api/v2/tasks/${P.id}`, undefined, sync);
|
||||
if (t.status !== 200 || t.json?.project_id !== PROJECT || !P.title.test(t.json?.title ?? "")) throw new Error(`task ${P.id}: ${t.status}, title check failed; nothing posted`);
|
||||
}
|
||||
writeFileSync(OUT, "", { flag: "wx" });
|
||||
for (const P of POSTS) {
|
||||
const c = await api("PUT", `/api/v1/tasks/${P.id}/comments`, { comment: ps(P.body) }, key);
|
||||
const e = { task: P.id, status: c.status, id: c.json?.id ?? null };
|
||||
rec.comments.push(e);
|
||||
if (c.status >= 300 || !Number.isSafeInteger(c.json?.id)) { rec.problems.push(`comment ${P.id}: ${c.status} ${c.json?.message ?? ""}`); save(); continue; }
|
||||
const back = await api("GET", `/api/v2/tasks/${P.id}/comments?per_page=50`, undefined, sync);
|
||||
const found = list(back.json).find((x) => x.id === c.json.id);
|
||||
Object.assign(e, { readBack: back.status, found: Boolean(found) });
|
||||
if (back.status !== 200 || !found) rec.problems.push(`comment ${P.id} read-back: ${back.status}`);
|
||||
save();
|
||||
}
|
||||
save();
|
||||
process.stdout.write(JSON.stringify(rec, null, 1) + "\n");
|
||||
if (rec.problems.length) process.exit(2);
|
||||
@@ -0,0 +1,52 @@
|
||||
// Sage, 2026-10-21: row 215 (C2-04) landed, comment on P19 416 (Q174 landing comment). Sage's own key (bot user 13); the sync key reads back. Comments only: no task edits.
|
||||
// Checks each task's title before posting. Prints no secret.
|
||||
// Usage: node comment-r215-landed.mjs https://tasks.woltje.com SAGE_TOKEN_FILE SYNC_TOKEN_FILE OUT_JSON
|
||||
import { readFileSync, writeFileSync, existsSync } from "node:fs";
|
||||
const [ORIGIN, KEY, SYNC, OUT] = process.argv.slice(2);
|
||||
if (!/^https:\/\/tasks\.woltje\.com$/.test(ORIGIN)) throw new Error("tasks.woltje.com only");
|
||||
if (!OUT || existsSync(OUT)) throw new Error("OUT_JSON missing or exists; refusing");
|
||||
const PROJECT = 19;
|
||||
const PSIG = "Posted by Sage (T3 1ef1e4f8) with Sage's own key (bot user 13).";
|
||||
const esc = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
||||
const ps = (xs) => xs.map((x) => `<p>${esc(x)}</p>`).join("") + `<p>${esc(PSIG)}</p>`;
|
||||
const POSTS = [
|
||||
{ id: 435, title: /J2|uid|separat|human boundary|peer/i, body: [
|
||||
"Row 196 (the human boundary across uids and humans, #1694) landed on refactor as 7247d80c7 at 20:57Z. The bus binds each human proof to the socket peer with SO_PEERCRED and records the interpreter it trusts in bus/peercred-starts.jsonl. The Console's tester listener moves from TCP --tester-port to a private socket, --tester-socket. Gander built it, and Duckworth approved round 1 (rev 2973) and round 2 (rev 2986). Gate gander-0066 passed 2334 with 0 failures and 1 skip, plus 38/38 mutants. #1694 is closed (comment 30159), and queue row 196 is done (rev 2988).",
|
||||
"No live change yet. The 10-24 re-pin carries 196 on dragon-lin and switches the live Console from --tester-port 7333 to --tester-socket. Until row 253 (#1758) moves case2-unit.sh and case1-accept.sh to the socket form, .53 and .54 take no copy carrying 196 (mos-1198).",
|
||||
] },
|
||||
{ id: 416, title: /^MVP Case 2: creative writing hub instance/, body: [
|
||||
"Row 196 landed (7247d80c7). Two rows were waiting on it. Row 253 (#1758, Gander, Duckworth reviewing) moves case2-unit.sh and case1-accept.sh to --tester-socket. Until it lands, .53 and .54 take no copy carrying 196. Row 254 (#1759, Megavolt, Dewey reviewing) holds the library sync deferrals from row 222.",
|
||||
] },
|
||||
];
|
||||
const key = readFileSync(KEY, "utf8").trim(), sync = readFileSync(SYNC, "utf8").trim();
|
||||
async function api(method, path, body, auth) {
|
||||
const r = await fetch(ORIGIN + path, { method, headers: { Authorization: `Bearer ${auth}`, "Content-Type": "application/json" }, body: body === undefined ? undefined : JSON.stringify(body), redirect: "error" });
|
||||
const t = await r.text(); let json = null; try { json = JSON.parse(t); } catch {}
|
||||
return { status: r.status, json };
|
||||
}
|
||||
const list = (j) => j?.items ?? (Array.isArray(j) ? j : []);
|
||||
const rec = { at: new Date().toISOString(), comments: [], problems: [] };
|
||||
const save = () => {
|
||||
const out = JSON.stringify(rec, null, 1) + "\n";
|
||||
if (out.includes(key) || out.includes(sync)) throw new Error("secret in the record; not written");
|
||||
writeFileSync(OUT, out);
|
||||
};
|
||||
for (const P of POSTS) {
|
||||
const t = await api("GET", `/api/v2/tasks/${P.id}`, undefined, sync);
|
||||
if (t.status !== 200 || t.json?.project_id !== PROJECT || !P.title.test(t.json?.title ?? "")) throw new Error(`task ${P.id}: ${t.status}, title check failed; nothing posted`);
|
||||
}
|
||||
writeFileSync(OUT, "", { flag: "wx" });
|
||||
for (const P of POSTS) {
|
||||
const c = await api("PUT", `/api/v1/tasks/${P.id}/comments`, { comment: ps(P.body) }, key);
|
||||
const e = { task: P.id, status: c.status, id: c.json?.id ?? null };
|
||||
rec.comments.push(e);
|
||||
if (c.status >= 300 || !Number.isSafeInteger(c.json?.id)) { rec.problems.push(`comment ${P.id}: ${c.status} ${c.json?.message ?? ""}`); save(); continue; }
|
||||
const back = await api("GET", `/api/v2/tasks/${P.id}/comments?per_page=50`, undefined, sync);
|
||||
const found = list(back.json).find((x) => x.id === c.json.id);
|
||||
Object.assign(e, { readBack: back.status, found: Boolean(found) });
|
||||
if (back.status !== 200 || !found) rec.problems.push(`comment ${P.id} read-back: ${back.status}`);
|
||||
save();
|
||||
}
|
||||
save();
|
||||
process.stdout.write(JSON.stringify(rec, null, 1) + "\n");
|
||||
if (rec.problems.length) process.exit(2);
|
||||
@@ -0,0 +1,50 @@
|
||||
// Sage, 2026-10-21: row 210 landed, comment on P19 424, per mos-0882. Sage's own key (bot user 13); the sync key reads back. Comments only: no task edits.
|
||||
// Checks each task's title before posting. Prints no secret.
|
||||
// Usage: node comment-r210-landed.mjs https://tasks.woltje.com SAGE_TOKEN_FILE SYNC_TOKEN_FILE OUT_JSON
|
||||
import { readFileSync, writeFileSync, existsSync } from "node:fs";
|
||||
const [ORIGIN, KEY, SYNC, OUT] = process.argv.slice(2);
|
||||
if (!/^https:\/\/tasks\.woltje\.com$/.test(ORIGIN)) throw new Error("tasks.woltje.com only");
|
||||
if (!OUT || existsSync(OUT)) throw new Error("OUT_JSON missing or exists; refusing");
|
||||
const PROJECT = 19;
|
||||
const PSIG = "Posted by Sage (T3 1ef1e4f8) with Sage's own key (bot user 13).";
|
||||
const esc = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
||||
const ps = (xs) => xs.map((x) => `<p>${esc(x)}</p>`).join("") + `<p>${esc(PSIG)}</p>`;
|
||||
const POSTS = [
|
||||
{ id: 424, title: /^Credential store: per-agent host access/, body: [
|
||||
"Row 210 (agent host access with sudo through the credential store) landed on refactor as f5753c0e8, pushed. Gitea #1707 closed with the landing comment 29853.",
|
||||
"Built by Gizmoduck (r5 0485384e9), reviewed by Duckworth (approve rounds 2 and 3, comment 29851). Mutants 52 of 53 killed, M22b equivalent with coverage moved to row 235. Gate green: 15 package suites 2796 pass, 10 script suites 429 pass, 0 fail.",
|
||||
"No live change yet. It goes live in the 10-24 re-pin window with rows 190, 199, 237 and 234 (mos-1145). Next on this line: row 234 (machine grant row a, Gizmoduck) is briefed, then row 235 (row b) and row 236 (#436b).",
|
||||
] },
|
||||
];
|
||||
const key = readFileSync(KEY, "utf8").trim(), sync = readFileSync(SYNC, "utf8").trim();
|
||||
async function api(method, path, body, auth) {
|
||||
const r = await fetch(ORIGIN + path, { method, headers: { Authorization: `Bearer ${auth}`, "Content-Type": "application/json" }, body: body === undefined ? undefined : JSON.stringify(body), redirect: "error" });
|
||||
const t = await r.text(); let json = null; try { json = JSON.parse(t); } catch {}
|
||||
return { status: r.status, json };
|
||||
}
|
||||
const list = (j) => j?.items ?? (Array.isArray(j) ? j : []);
|
||||
const rec = { at: new Date().toISOString(), comments: [], problems: [] };
|
||||
const save = () => {
|
||||
const out = JSON.stringify(rec, null, 1) + "\n";
|
||||
if (out.includes(key) || out.includes(sync)) throw new Error("secret in the record; not written");
|
||||
writeFileSync(OUT, out);
|
||||
};
|
||||
for (const P of POSTS) {
|
||||
const t = await api("GET", `/api/v2/tasks/${P.id}`, undefined, sync);
|
||||
if (t.status !== 200 || t.json?.project_id !== PROJECT || !P.title.test(t.json?.title ?? "")) throw new Error(`task ${P.id}: ${t.status}, title check failed; nothing posted`);
|
||||
}
|
||||
writeFileSync(OUT, "", { flag: "wx" });
|
||||
for (const P of POSTS) {
|
||||
const c = await api("PUT", `/api/v1/tasks/${P.id}/comments`, { comment: ps(P.body) }, key);
|
||||
const e = { task: P.id, status: c.status, id: c.json?.id ?? null };
|
||||
rec.comments.push(e);
|
||||
if (c.status >= 300 || !Number.isSafeInteger(c.json?.id)) { rec.problems.push(`comment ${P.id}: ${c.status} ${c.json?.message ?? ""}`); save(); continue; }
|
||||
const back = await api("GET", `/api/v2/tasks/${P.id}/comments?per_page=50`, undefined, sync);
|
||||
const found = list(back.json).find((x) => x.id === c.json.id);
|
||||
Object.assign(e, { readBack: back.status, found: Boolean(found) });
|
||||
if (back.status !== 200 || !found) rec.problems.push(`comment ${P.id} read-back: ${back.status}`);
|
||||
save();
|
||||
}
|
||||
save();
|
||||
process.stdout.write(JSON.stringify(rec, null, 1) + "\n");
|
||||
if (rec.problems.length) process.exit(2);
|
||||
@@ -0,0 +1,50 @@
|
||||
// Sage, 2026-10-21: row 215 (C2-04) landed, comment on P19 416 (Q174 landing comment). Sage's own key (bot user 13); the sync key reads back. Comments only: no task edits.
|
||||
// Checks each task's title before posting. Prints no secret.
|
||||
// Usage: node comment-r215-landed.mjs https://tasks.woltje.com SAGE_TOKEN_FILE SYNC_TOKEN_FILE OUT_JSON
|
||||
import { readFileSync, writeFileSync, existsSync } from "node:fs";
|
||||
const [ORIGIN, KEY, SYNC, OUT] = process.argv.slice(2);
|
||||
if (!/^https:\/\/tasks\.woltje\.com$/.test(ORIGIN)) throw new Error("tasks.woltje.com only");
|
||||
if (!OUT || existsSync(OUT)) throw new Error("OUT_JSON missing or exists; refusing");
|
||||
const PROJECT = 19;
|
||||
const PSIG = "Posted by Sage (T3 1ef1e4f8) with Sage's own key (bot user 13).";
|
||||
const esc = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
||||
const ps = (xs) => xs.map((x) => `<p>${esc(x)}</p>`).join("") + `<p>${esc(PSIG)}</p>`;
|
||||
const POSTS = [
|
||||
{ id: 416, title: /^MVP Case 2: creative writing hub instance/, body: [
|
||||
"Row 215 (C2-04: the library entry in the business file, consumer skips, M9 organization-projects-protected, the bare mirror, library.file and library.status) landed on refactor as e54e05463 at 19:24Z. Gitea #1716 is closed (comment 30133). Queue row 215 is done (rev 2955).",
|
||||
"Herb built it, and Filbert approved the merge-scoped round 2 (rev 2924). The landing patch-id equals the candidate dde3445ab on a36523713. Herb ran the gate on grant sage-2071, with the business, seat, cli, project, bus and webui suites whole plus every scripts/test-*.sh: 2634 pass, 0 fail, and one environment skip in webui.",
|
||||
"Row 222 (C2-03a fetch-only sync, Megavolt) was waiting on this landing and now rebases onto e54e05463. Nothing is re-pinned yet.",
|
||||
] },
|
||||
];
|
||||
const key = readFileSync(KEY, "utf8").trim(), sync = readFileSync(SYNC, "utf8").trim();
|
||||
async function api(method, path, body, auth) {
|
||||
const r = await fetch(ORIGIN + path, { method, headers: { Authorization: `Bearer ${auth}`, "Content-Type": "application/json" }, body: body === undefined ? undefined : JSON.stringify(body), redirect: "error" });
|
||||
const t = await r.text(); let json = null; try { json = JSON.parse(t); } catch {}
|
||||
return { status: r.status, json };
|
||||
}
|
||||
const list = (j) => j?.items ?? (Array.isArray(j) ? j : []);
|
||||
const rec = { at: new Date().toISOString(), comments: [], problems: [] };
|
||||
const save = () => {
|
||||
const out = JSON.stringify(rec, null, 1) + "\n";
|
||||
if (out.includes(key) || out.includes(sync)) throw new Error("secret in the record; not written");
|
||||
writeFileSync(OUT, out);
|
||||
};
|
||||
for (const P of POSTS) {
|
||||
const t = await api("GET", `/api/v2/tasks/${P.id}`, undefined, sync);
|
||||
if (t.status !== 200 || t.json?.project_id !== PROJECT || !P.title.test(t.json?.title ?? "")) throw new Error(`task ${P.id}: ${t.status}, title check failed; nothing posted`);
|
||||
}
|
||||
writeFileSync(OUT, "", { flag: "wx" });
|
||||
for (const P of POSTS) {
|
||||
const c = await api("PUT", `/api/v1/tasks/${P.id}/comments`, { comment: ps(P.body) }, key);
|
||||
const e = { task: P.id, status: c.status, id: c.json?.id ?? null };
|
||||
rec.comments.push(e);
|
||||
if (c.status >= 300 || !Number.isSafeInteger(c.json?.id)) { rec.problems.push(`comment ${P.id}: ${c.status} ${c.json?.message ?? ""}`); save(); continue; }
|
||||
const back = await api("GET", `/api/v2/tasks/${P.id}/comments?per_page=50`, undefined, sync);
|
||||
const found = list(back.json).find((x) => x.id === c.json.id);
|
||||
Object.assign(e, { readBack: back.status, found: Boolean(found) });
|
||||
if (back.status !== 200 || !found) rec.problems.push(`comment ${P.id} read-back: ${back.status}`);
|
||||
save();
|
||||
}
|
||||
save();
|
||||
process.stdout.write(JSON.stringify(rec, null, 1) + "\n");
|
||||
if (rec.problems.length) process.exit(2);
|
||||
@@ -0,0 +1,50 @@
|
||||
// Sage, 2026-10-21: row 232 (installer I1) landed, comment on P19 432, per mos-0882. Sage's own key (bot user 13); the sync key reads back. Comments only: no task edits.
|
||||
// Checks each task's title before posting. Prints no secret.
|
||||
// Usage: node comment-r232-landed.mjs https://tasks.woltje.com SAGE_TOKEN_FILE SYNC_TOKEN_FILE OUT_JSON
|
||||
import { readFileSync, writeFileSync, existsSync } from "node:fs";
|
||||
const [ORIGIN, KEY, SYNC, OUT] = process.argv.slice(2);
|
||||
if (!/^https:\/\/tasks\.woltje\.com$/.test(ORIGIN)) throw new Error("tasks.woltje.com only");
|
||||
if (!OUT || existsSync(OUT)) throw new Error("OUT_JSON missing or exists; refusing");
|
||||
const PROJECT = 19;
|
||||
const PSIG = "Posted by Sage (T3 1ef1e4f8) with Sage's own key (bot user 13).";
|
||||
const esc = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
||||
const ps = (xs) => xs.map((x) => `<p>${esc(x)}</p>`).join("") + `<p>${esc(PSIG)}</p>`;
|
||||
const POSTS = [
|
||||
{ id: 432, title: /^Installer row: one-command install and upgrade for a fresh host/, body: [
|
||||
"I1 landed: row 232 (stack-release --units external for root-owned units, with confirm, revert and gate --since; stack-copy copy --bundle and wrapper --create) is on refactor as e65a3ab4d, pushed. Gitea #1734 closed with the landing comment 29874.",
|
||||
"Built by Darkwing (round 2 da2da2f6c, a rebase over row 230's README hunk), reviewed by Filbert (approve rounds 1 and 2, comments 29829 and 29865). Mutants 17 of 17 killed. Landing gate green: packages/cli 593 pass, 10 script suites 429 pass, 0 fail.",
|
||||
"No live change; --units user stays the default. Installer line: I2 is rows 230 (landed c8bc5cf4c) and 226, I3 is row 233 (briefed, Darkwing), I4 is row 240 (hostcheck).",
|
||||
] },
|
||||
];
|
||||
const key = readFileSync(KEY, "utf8").trim(), sync = readFileSync(SYNC, "utf8").trim();
|
||||
async function api(method, path, body, auth) {
|
||||
const r = await fetch(ORIGIN + path, { method, headers: { Authorization: `Bearer ${auth}`, "Content-Type": "application/json" }, body: body === undefined ? undefined : JSON.stringify(body), redirect: "error" });
|
||||
const t = await r.text(); let json = null; try { json = JSON.parse(t); } catch {}
|
||||
return { status: r.status, json };
|
||||
}
|
||||
const list = (j) => j?.items ?? (Array.isArray(j) ? j : []);
|
||||
const rec = { at: new Date().toISOString(), comments: [], problems: [] };
|
||||
const save = () => {
|
||||
const out = JSON.stringify(rec, null, 1) + "\n";
|
||||
if (out.includes(key) || out.includes(sync)) throw new Error("secret in the record; not written");
|
||||
writeFileSync(OUT, out);
|
||||
};
|
||||
for (const P of POSTS) {
|
||||
const t = await api("GET", `/api/v2/tasks/${P.id}`, undefined, sync);
|
||||
if (t.status !== 200 || t.json?.project_id !== PROJECT || !P.title.test(t.json?.title ?? "")) throw new Error(`task ${P.id}: ${t.status}, title check failed; nothing posted`);
|
||||
}
|
||||
writeFileSync(OUT, "", { flag: "wx" });
|
||||
for (const P of POSTS) {
|
||||
const c = await api("PUT", `/api/v1/tasks/${P.id}/comments`, { comment: ps(P.body) }, key);
|
||||
const e = { task: P.id, status: c.status, id: c.json?.id ?? null };
|
||||
rec.comments.push(e);
|
||||
if (c.status >= 300 || !Number.isSafeInteger(c.json?.id)) { rec.problems.push(`comment ${P.id}: ${c.status} ${c.json?.message ?? ""}`); save(); continue; }
|
||||
const back = await api("GET", `/api/v2/tasks/${P.id}/comments?per_page=50`, undefined, sync);
|
||||
const found = list(back.json).find((x) => x.id === c.json.id);
|
||||
Object.assign(e, { readBack: back.status, found: Boolean(found) });
|
||||
if (back.status !== 200 || !found) rec.problems.push(`comment ${P.id} read-back: ${back.status}`);
|
||||
save();
|
||||
}
|
||||
save();
|
||||
process.stdout.write(JSON.stringify(rec, null, 1) + "\n");
|
||||
if (rec.problems.length) process.exit(2);
|
||||
@@ -0,0 +1,50 @@
|
||||
// Sage, 2026-10-21: row 233 (installer I3) landed, comment on P19 432, per mos-0882. Sage's own key (bot user 13); the sync key reads back. Comments only: no task edits.
|
||||
// Checks each task's title before posting. Prints no secret.
|
||||
// Usage: node comment-r233-landed.mjs https://tasks.woltje.com SAGE_TOKEN_FILE SYNC_TOKEN_FILE OUT_JSON
|
||||
import { readFileSync, writeFileSync, existsSync } from "node:fs";
|
||||
const [ORIGIN, KEY, SYNC, OUT] = process.argv.slice(2);
|
||||
if (!/^https:\/\/tasks\.woltje\.com$/.test(ORIGIN)) throw new Error("tasks.woltje.com only");
|
||||
if (!OUT || existsSync(OUT)) throw new Error("OUT_JSON missing or exists; refusing");
|
||||
const PROJECT = 19;
|
||||
const PSIG = "Posted by Sage (T3 1ef1e4f8) with Sage's own key (bot user 13).";
|
||||
const esc = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
||||
const ps = (xs) => xs.map((x) => `<p>${esc(x)}</p>`).join("") + `<p>${esc(PSIG)}</p>`;
|
||||
const POSTS = [
|
||||
{ id: 432, title: /^Installer row: one-command install and upgrade for a fresh host/, body: [
|
||||
"I3 landed: row 233 (scripts/stack-upgrade.sh, the upgrade driver, and docs/guides/host-run-sheet.md) is on refactor as 57f59e0c3, pushed 05:39Z. Gitea #1735 closed with the landing comment 29931.",
|
||||
"Built by Darkwing (round 1 1a1dc80ca), reviewed by Filbert (approve, comment 29922). Mutants m1-m8 killed. Landing gate on the landing tree: packages/cli 617 pass, 10 script suites 585 pass, 0 fail. Patch-id and the manifest match the candidate.",
|
||||
"No live change. Filbert's N1, N4 and N5 go to I4 (row 240, Darkwing, briefed now). N2 and N3 are in DEFERRED.md. Row 234 round 3 is rebuilt on this landing.",
|
||||
] },
|
||||
];
|
||||
const key = readFileSync(KEY, "utf8").trim(), sync = readFileSync(SYNC, "utf8").trim();
|
||||
async function api(method, path, body, auth) {
|
||||
const r = await fetch(ORIGIN + path, { method, headers: { Authorization: `Bearer ${auth}`, "Content-Type": "application/json" }, body: body === undefined ? undefined : JSON.stringify(body), redirect: "error" });
|
||||
const t = await r.text(); let json = null; try { json = JSON.parse(t); } catch {}
|
||||
return { status: r.status, json };
|
||||
}
|
||||
const list = (j) => j?.items ?? (Array.isArray(j) ? j : []);
|
||||
const rec = { at: new Date().toISOString(), comments: [], problems: [] };
|
||||
const save = () => {
|
||||
const out = JSON.stringify(rec, null, 1) + "\n";
|
||||
if (out.includes(key) || out.includes(sync)) throw new Error("secret in the record; not written");
|
||||
writeFileSync(OUT, out);
|
||||
};
|
||||
for (const P of POSTS) {
|
||||
const t = await api("GET", `/api/v2/tasks/${P.id}`, undefined, sync);
|
||||
if (t.status !== 200 || t.json?.project_id !== PROJECT || !P.title.test(t.json?.title ?? "")) throw new Error(`task ${P.id}: ${t.status}, title check failed; nothing posted`);
|
||||
}
|
||||
writeFileSync(OUT, "", { flag: "wx" });
|
||||
for (const P of POSTS) {
|
||||
const c = await api("PUT", `/api/v1/tasks/${P.id}/comments`, { comment: ps(P.body) }, key);
|
||||
const e = { task: P.id, status: c.status, id: c.json?.id ?? null };
|
||||
rec.comments.push(e);
|
||||
if (c.status >= 300 || !Number.isSafeInteger(c.json?.id)) { rec.problems.push(`comment ${P.id}: ${c.status} ${c.json?.message ?? ""}`); save(); continue; }
|
||||
const back = await api("GET", `/api/v2/tasks/${P.id}/comments?per_page=50`, undefined, sync);
|
||||
const found = list(back.json).find((x) => x.id === c.json.id);
|
||||
Object.assign(e, { readBack: back.status, found: Boolean(found) });
|
||||
if (back.status !== 200 || !found) rec.problems.push(`comment ${P.id} read-back: ${back.status}`);
|
||||
save();
|
||||
}
|
||||
save();
|
||||
process.stdout.write(JSON.stringify(rec, null, 1) + "\n");
|
||||
if (rec.problems.length) process.exit(2);
|
||||
@@ -0,0 +1,51 @@
|
||||
// Sage, 2026-10-21: row 234 (machine grant row a) landed, comment on P19 436, per mos-0882. Sage's own key (bot user 13); the sync key reads back. Comments only: no task edits.
|
||||
// Checks each task's title before posting. Prints no secret.
|
||||
// Usage: node comment-r234-landed.mjs https://tasks.woltje.com SAGE_TOKEN_FILE SYNC_TOKEN_FILE OUT_JSON
|
||||
import { readFileSync, writeFileSync, existsSync } from "node:fs";
|
||||
const [ORIGIN, KEY, SYNC, OUT] = process.argv.slice(2);
|
||||
if (!/^https:\/\/tasks\.woltje\.com$/.test(ORIGIN)) throw new Error("tasks.woltje.com only");
|
||||
if (!OUT || existsSync(OUT)) throw new Error("OUT_JSON missing or exists; refusing");
|
||||
const PROJECT = 19;
|
||||
const PSIG = "Posted by Sage (T3 1ef1e4f8) with Sage's own key (bot user 13).";
|
||||
const esc = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
||||
const ps = (xs) => xs.map((x) => `<p>${esc(x)}</p>`).join("") + `<p>${esc(PSIG)}</p>`;
|
||||
const POSTS = [
|
||||
{ id: 436, title: /^Guarded grant methods: on Approve, stack code creates the grant/, body: [
|
||||
"Row a landed: row 234 (machine grant row a: host enrollment, the hosts and host_grants store records, bus schema 3i to 3j) is on refactor as c771a36f4, pushed 06:16Z. Gitea #1740 closed with the landing comment 29938.",
|
||||
"Built by Gizmoduck (round 3 c783d5e39, one commit on the row 233 landing), reviewed by Duckworth (round 3 approve, comment 29935, rev 2683). 25 of 25 mutants killed. Sage's landing gate on the candidate tree: 15 packages 2890 pass and ten script suites 585 pass, 0 fail. Patch-id and the manifest match the candidate.",
|
||||
"No live change. The 3i to 3j migration runs in the 10-24 window (mos-1145), with a backup first. Duckworth's N10 and N12 go to row 235 (row b), which Gizmoduck rebuilds on this landing next.",
|
||||
] },
|
||||
];
|
||||
if (/LANDSHA|PUSHTIME|CLOSEID|GATE\b/.test(JSON.stringify(POSTS))) throw new Error("placeholder left; refusing");
|
||||
const key = readFileSync(KEY, "utf8").trim(), sync = readFileSync(SYNC, "utf8").trim();
|
||||
async function api(method, path, body, auth) {
|
||||
const r = await fetch(ORIGIN + path, { method, headers: { Authorization: `Bearer ${auth}`, "Content-Type": "application/json" }, body: body === undefined ? undefined : JSON.stringify(body), redirect: "error" });
|
||||
const t = await r.text(); let json = null; try { json = JSON.parse(t); } catch {}
|
||||
return { status: r.status, json };
|
||||
}
|
||||
const list = (j) => j?.items ?? (Array.isArray(j) ? j : []);
|
||||
const rec = { at: new Date().toISOString(), comments: [], problems: [] };
|
||||
const save = () => {
|
||||
const out = JSON.stringify(rec, null, 1) + "\n";
|
||||
if (out.includes(key) || out.includes(sync)) throw new Error("secret in the record; not written");
|
||||
writeFileSync(OUT, out);
|
||||
};
|
||||
for (const P of POSTS) {
|
||||
const t = await api("GET", `/api/v2/tasks/${P.id}`, undefined, sync);
|
||||
if (t.status !== 200 || t.json?.project_id !== PROJECT || !P.title.test(t.json?.title ?? "")) throw new Error(`task ${P.id}: ${t.status}, title check failed; nothing posted`);
|
||||
}
|
||||
writeFileSync(OUT, "", { flag: "wx" });
|
||||
for (const P of POSTS) {
|
||||
const c = await api("PUT", `/api/v1/tasks/${P.id}/comments`, { comment: ps(P.body) }, key);
|
||||
const e = { task: P.id, status: c.status, id: c.json?.id ?? null };
|
||||
rec.comments.push(e);
|
||||
if (c.status >= 300 || !Number.isSafeInteger(c.json?.id)) { rec.problems.push(`comment ${P.id}: ${c.status} ${c.json?.message ?? ""}`); save(); continue; }
|
||||
const back = await api("GET", `/api/v2/tasks/${P.id}/comments?per_page=50`, undefined, sync);
|
||||
const found = list(back.json).find((x) => x.id === c.json.id);
|
||||
Object.assign(e, { readBack: back.status, found: Boolean(found) });
|
||||
if (back.status !== 200 || !found) rec.problems.push(`comment ${P.id} read-back: ${back.status}`);
|
||||
save();
|
||||
}
|
||||
save();
|
||||
process.stdout.write(JSON.stringify(rec, null, 1) + "\n");
|
||||
if (rec.problems.length) process.exit(2);
|
||||
@@ -0,0 +1,53 @@
|
||||
// Sage, 2026-10-21: row 237 landed, rows 241 and 242 opened, comments on P19 440 and 441, per mos-0882. Sage's own key (bot user 13); the sync key reads back. Comments only: no task edits.
|
||||
// Checks each task's title before posting. Prints no secret.
|
||||
// Usage: node comment-r237-rows-440-441.mjs https://tasks.woltje.com SAGE_TOKEN_FILE SYNC_TOKEN_FILE OUT_JSON
|
||||
import { readFileSync, writeFileSync, existsSync } from "node:fs";
|
||||
const [ORIGIN, KEY, SYNC, OUT] = process.argv.slice(2);
|
||||
if (!/^https:\/\/tasks\.woltje\.com$/.test(ORIGIN)) throw new Error("tasks.woltje.com only");
|
||||
if (!OUT || existsSync(OUT)) throw new Error("OUT_JSON missing or exists; refusing");
|
||||
const PROJECT = 19;
|
||||
const PSIG = "Posted by Sage (T3 1ef1e4f8) with Sage's own key (bot user 13).";
|
||||
const esc = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
||||
const ps = (xs) => xs.map((x) => `<p>${esc(x)}</p>`).join("") + `<p>${esc(PSIG)}</p>`;
|
||||
const POSTS = [
|
||||
{ id: 440, title: /^Bus publish: a retry after publish-timeout/, body: [
|
||||
"Row 237 landed on refactor as d7d943199 (Gitea #1743, closed, comment 29836). checkPublish refuses publish-busy while an open ticket holds the decision. Gate green, mutant M1 killed.",
|
||||
"This task is now queue row 242 (Gitea #1748, Megavolt, reviewer Duckworth), briefed. Brief: docs/plans/2026-10-20_agent-credentials.md, \"Brief: row 237 follow-up N1, a retry after publish-timeout pushes once\" (9d165be8c). The fix either holds the decision until the bus's push ends or kills the push and waits, with a slow-push retry test and a mutant.",
|
||||
] },
|
||||
{ id: 441, title: /^Bus land: check whether checkLand has the same open-ticket race/, body: [
|
||||
"Megavolt's analysis (megavolt-0068, megavolt-0069): no end-to-end race. land-busy (git-socket.mjs:351) allows one landing per socket. finishLand moves main by compare-and-swap (seat working.mjs:391, base-moved). recordLand reruns #landFacts, which refuses decision-consumed (broker.mjs:945). Sage accepted it in sage-1651.",
|
||||
"This task is now queue row 241 (Gitea #1747, Megavolt, reviewer Duckworth). It changes no source. It adds a two-ticket broker land test and three mutants, one per guard. Brief (c25785d3d): \"Brief: row 237 follow-up, one decision lands once, with tests at the broker\". Megavolt claimed it at rev 2606.",
|
||||
] },
|
||||
];
|
||||
const key = readFileSync(KEY, "utf8").trim(), sync = readFileSync(SYNC, "utf8").trim();
|
||||
async function api(method, path, body, auth) {
|
||||
const r = await fetch(ORIGIN + path, { method, headers: { Authorization: `Bearer ${auth}`, "Content-Type": "application/json" }, body: body === undefined ? undefined : JSON.stringify(body), redirect: "error" });
|
||||
const t = await r.text(); let json = null; try { json = JSON.parse(t); } catch {}
|
||||
return { status: r.status, json };
|
||||
}
|
||||
const list = (j) => j?.items ?? (Array.isArray(j) ? j : []);
|
||||
const rec = { at: new Date().toISOString(), comments: [], problems: [] };
|
||||
const save = () => {
|
||||
const out = JSON.stringify(rec, null, 1) + "\n";
|
||||
if (out.includes(key) || out.includes(sync)) throw new Error("secret in the record; not written");
|
||||
writeFileSync(OUT, out);
|
||||
};
|
||||
for (const P of POSTS) {
|
||||
const t = await api("GET", `/api/v2/tasks/${P.id}`, undefined, sync);
|
||||
if (t.status !== 200 || t.json?.project_id !== PROJECT || !P.title.test(t.json?.title ?? "")) throw new Error(`task ${P.id}: ${t.status}, title check failed; nothing posted`);
|
||||
}
|
||||
writeFileSync(OUT, "", { flag: "wx" });
|
||||
for (const P of POSTS) {
|
||||
const c = await api("PUT", `/api/v1/tasks/${P.id}/comments`, { comment: ps(P.body) }, key);
|
||||
const e = { task: P.id, status: c.status, id: c.json?.id ?? null };
|
||||
rec.comments.push(e);
|
||||
if (c.status >= 300 || !Number.isSafeInteger(c.json?.id)) { rec.problems.push(`comment ${P.id}: ${c.status} ${c.json?.message ?? ""}`); save(); continue; }
|
||||
const back = await api("GET", `/api/v2/tasks/${P.id}/comments?per_page=50`, undefined, sync);
|
||||
const found = list(back.json).find((x) => x.id === c.json.id);
|
||||
Object.assign(e, { readBack: back.status, found: Boolean(found) });
|
||||
if (back.status !== 200 || !found) rec.problems.push(`comment ${P.id} read-back: ${back.status}`);
|
||||
save();
|
||||
}
|
||||
save();
|
||||
process.stdout.write(JSON.stringify(rec, null, 1) + "\n");
|
||||
if (rec.problems.length) process.exit(2);
|
||||
@@ -0,0 +1,50 @@
|
||||
// Sage, 2026-10-21: row 240 (installer I4) landed, comment on P19 432, per mos-0882. Sage's own key (bot user 13); the sync key reads back. Comments only: no task edits.
|
||||
// Checks each task's title before posting. Prints no secret.
|
||||
// Usage: node comment-r240-landed.mjs https://tasks.woltje.com SAGE_TOKEN_FILE SYNC_TOKEN_FILE OUT_JSON
|
||||
import { readFileSync, writeFileSync, existsSync } from "node:fs";
|
||||
const [ORIGIN, KEY, SYNC, OUT] = process.argv.slice(2);
|
||||
if (!/^https:\/\/tasks\.woltje\.com$/.test(ORIGIN)) throw new Error("tasks.woltje.com only");
|
||||
if (!OUT || existsSync(OUT)) throw new Error("OUT_JSON missing or exists; refusing");
|
||||
const PROJECT = 19;
|
||||
const PSIG = "Posted by Sage (T3 1ef1e4f8) with Sage's own key (bot user 13).";
|
||||
const esc = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
||||
const ps = (xs) => xs.map((x) => `<p>${esc(x)}</p>`).join("") + `<p>${esc(PSIG)}</p>`;
|
||||
const POSTS = [
|
||||
{ id: 432, title: /^Installer row: one-command install and upgrade for a fresh host/, body: [
|
||||
"I4 landed: row 240 (scripts/case1-hostcheck.sh with its pins in case1-pins.sh, the boot unit mosaic-hostcheck.service, and the I3 fixes in stack-upgrade.sh and the host run sheet) is on refactor as c515273db, pushed 06:57Z. Gitea #1746 closed with the landing comment 29954.",
|
||||
"Built by Darkwing (ad1b23ab2), reviewed by Filbert (approve, comment 29945). Mutants M1-M8 killed. Landing gate on the landing tree: packages/cli 639 pass, 10 script suites 585 pass, 0 fail. Patch-id and the manifest match the candidate.",
|
||||
"No live change; the host check is installed by hand on a Case 1 host from the run sheet. Filbert's N1 (the boot unit orders only mosaic-bus@mosaic-stack) and N2 (the apt hook goes in before the first --check) are in DEFERRED.md. N1 must be fixed before an install whose business id isn't mosaic-stack.",
|
||||
] },
|
||||
];
|
||||
const key = readFileSync(KEY, "utf8").trim(), sync = readFileSync(SYNC, "utf8").trim();
|
||||
async function api(method, path, body, auth) {
|
||||
const r = await fetch(ORIGIN + path, { method, headers: { Authorization: `Bearer ${auth}`, "Content-Type": "application/json" }, body: body === undefined ? undefined : JSON.stringify(body), redirect: "error" });
|
||||
const t = await r.text(); let json = null; try { json = JSON.parse(t); } catch {}
|
||||
return { status: r.status, json };
|
||||
}
|
||||
const list = (j) => j?.items ?? (Array.isArray(j) ? j : []);
|
||||
const rec = { at: new Date().toISOString(), comments: [], problems: [] };
|
||||
const save = () => {
|
||||
const out = JSON.stringify(rec, null, 1) + "\n";
|
||||
if (out.includes(key) || out.includes(sync)) throw new Error("secret in the record; not written");
|
||||
writeFileSync(OUT, out);
|
||||
};
|
||||
for (const P of POSTS) {
|
||||
const t = await api("GET", `/api/v2/tasks/${P.id}`, undefined, sync);
|
||||
if (t.status !== 200 || t.json?.project_id !== PROJECT || !P.title.test(t.json?.title ?? "")) throw new Error(`task ${P.id}: ${t.status}, title check failed; nothing posted`);
|
||||
}
|
||||
writeFileSync(OUT, "", { flag: "wx" });
|
||||
for (const P of POSTS) {
|
||||
const c = await api("PUT", `/api/v1/tasks/${P.id}/comments`, { comment: ps(P.body) }, key);
|
||||
const e = { task: P.id, status: c.status, id: c.json?.id ?? null };
|
||||
rec.comments.push(e);
|
||||
if (c.status >= 300 || !Number.isSafeInteger(c.json?.id)) { rec.problems.push(`comment ${P.id}: ${c.status} ${c.json?.message ?? ""}`); save(); continue; }
|
||||
const back = await api("GET", `/api/v2/tasks/${P.id}/comments?per_page=50`, undefined, sync);
|
||||
const found = list(back.json).find((x) => x.id === c.json.id);
|
||||
Object.assign(e, { readBack: back.status, found: Boolean(found) });
|
||||
if (back.status !== 200 || !found) rec.problems.push(`comment ${P.id} read-back: ${back.status}`);
|
||||
save();
|
||||
}
|
||||
save();
|
||||
process.stdout.write(JSON.stringify(rec, null, 1) + "\n");
|
||||
if (rec.problems.length) process.exit(2);
|
||||
@@ -0,0 +1,50 @@
|
||||
// Sage, 2026-10-21: row 232 (installer I1) landed, comment on P19 432, per mos-0882. Sage's own key (bot user 13); the sync key reads back. Comments only: no task edits.
|
||||
// Checks each task's title before posting. Prints no secret.
|
||||
// Usage: node comment-r232-landed.mjs https://tasks.woltje.com SAGE_TOKEN_FILE SYNC_TOKEN_FILE OUT_JSON
|
||||
import { readFileSync, writeFileSync, existsSync } from "node:fs";
|
||||
const [ORIGIN, KEY, SYNC, OUT] = process.argv.slice(2);
|
||||
if (!/^https:\/\/tasks\.woltje\.com$/.test(ORIGIN)) throw new Error("tasks.woltje.com only");
|
||||
if (!OUT || existsSync(OUT)) throw new Error("OUT_JSON missing or exists; refusing");
|
||||
const PROJECT = 19;
|
||||
const PSIG = "Posted by Sage (T3 1ef1e4f8) with Sage's own key (bot user 13).";
|
||||
const esc = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
||||
const ps = (xs) => xs.map((x) => `<p>${esc(x)}</p>`).join("") + `<p>${esc(PSIG)}</p>`;
|
||||
const POSTS = [
|
||||
{ id: 441, title: /^Bus land: check whether checkLand has the same open-ticket race/, body: [
|
||||
"Row 241 landed: 5d4a61776 on refactor, pushed. Gitea #1747 closed with the landing comment 29883.",
|
||||
"Answer: landing has no row 237 race. Three guards keep one decision from landing twice: land-busy at the git socket (cli git-socket.mjs:351), the CAS on main in finishLand (seat working.mjs:391), and the recordLand recheck of #spent (bus broker.mjs:963). Megavolt's new broker test opens two checkLand tickets on one decision and shows the second recordLand refuses decision-consumed and records nothing.",
|
||||
"Built by Megavolt (3e3ed303e, tests and packet only, no source change), reviewed by Duckworth (approve, comment 29870). Sage's mutants M1-M3 on the three guards all killed. Landing gate green: 15 packages 2797 pass, 10 script suites 429 pass, 0 fail.",
|
||||
] },
|
||||
];
|
||||
const key = readFileSync(KEY, "utf8").trim(), sync = readFileSync(SYNC, "utf8").trim();
|
||||
async function api(method, path, body, auth) {
|
||||
const r = await fetch(ORIGIN + path, { method, headers: { Authorization: `Bearer ${auth}`, "Content-Type": "application/json" }, body: body === undefined ? undefined : JSON.stringify(body), redirect: "error" });
|
||||
const t = await r.text(); let json = null; try { json = JSON.parse(t); } catch {}
|
||||
return { status: r.status, json };
|
||||
}
|
||||
const list = (j) => j?.items ?? (Array.isArray(j) ? j : []);
|
||||
const rec = { at: new Date().toISOString(), comments: [], problems: [] };
|
||||
const save = () => {
|
||||
const out = JSON.stringify(rec, null, 1) + "\n";
|
||||
if (out.includes(key) || out.includes(sync)) throw new Error("secret in the record; not written");
|
||||
writeFileSync(OUT, out);
|
||||
};
|
||||
for (const P of POSTS) {
|
||||
const t = await api("GET", `/api/v2/tasks/${P.id}`, undefined, sync);
|
||||
if (t.status !== 200 || t.json?.project_id !== PROJECT || !P.title.test(t.json?.title ?? "")) throw new Error(`task ${P.id}: ${t.status}, title check failed; nothing posted`);
|
||||
}
|
||||
writeFileSync(OUT, "", { flag: "wx" });
|
||||
for (const P of POSTS) {
|
||||
const c = await api("PUT", `/api/v1/tasks/${P.id}/comments`, { comment: ps(P.body) }, key);
|
||||
const e = { task: P.id, status: c.status, id: c.json?.id ?? null };
|
||||
rec.comments.push(e);
|
||||
if (c.status >= 300 || !Number.isSafeInteger(c.json?.id)) { rec.problems.push(`comment ${P.id}: ${c.status} ${c.json?.message ?? ""}`); save(); continue; }
|
||||
const back = await api("GET", `/api/v2/tasks/${P.id}/comments?per_page=50`, undefined, sync);
|
||||
const found = list(back.json).find((x) => x.id === c.json.id);
|
||||
Object.assign(e, { readBack: back.status, found: Boolean(found) });
|
||||
if (back.status !== 200 || !found) rec.problems.push(`comment ${P.id} read-back: ${back.status}`);
|
||||
save();
|
||||
}
|
||||
save();
|
||||
process.stdout.write(JSON.stringify(rec, null, 1) + "\n");
|
||||
if (rec.problems.length) process.exit(2);
|
||||
@@ -0,0 +1,50 @@
|
||||
// Sage, 2026-10-21: row 232 (installer I1) landed, comment on P19 432, per mos-0882. Sage's own key (bot user 13); the sync key reads back. Comments only: no task edits.
|
||||
// Checks each task's title before posting. Prints no secret.
|
||||
// Usage: node comment-r232-landed.mjs https://tasks.woltje.com SAGE_TOKEN_FILE SYNC_TOKEN_FILE OUT_JSON
|
||||
import { readFileSync, writeFileSync, existsSync } from "node:fs";
|
||||
const [ORIGIN, KEY, SYNC, OUT] = process.argv.slice(2);
|
||||
if (!/^https:\/\/tasks\.woltje\.com$/.test(ORIGIN)) throw new Error("tasks.woltje.com only");
|
||||
if (!OUT || existsSync(OUT)) throw new Error("OUT_JSON missing or exists; refusing");
|
||||
const PROJECT = 19;
|
||||
const PSIG = "Posted by Sage (T3 1ef1e4f8) with Sage's own key (bot user 13).";
|
||||
const esc = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
||||
const ps = (xs) => xs.map((x) => `<p>${esc(x)}</p>`).join("") + `<p>${esc(PSIG)}</p>`;
|
||||
const POSTS = [
|
||||
{ id: 440, title: /^Bus publish: a retry after publish-timeout/, body: [
|
||||
"Row 242 landed: 5fb736b92 on refactor, pushed. Gitea #1748 closed with the landing comment 29894.",
|
||||
"Built by Megavolt (635c10924), reviewed by Duckworth (approve, comment 29889). Mutants M1 and M2 killed. Landing gate green: 15 packages 2826 pass, 10 script suites 429 pass, 0 fail.",
|
||||
"Open follow-ups: Duckworth's doc notes N1, N2 and N4 (a doc-only commit by Megavolt), and the late-record notice to the run (comment 432 above).",
|
||||
] },
|
||||
];
|
||||
const key = readFileSync(KEY, "utf8").trim(), sync = readFileSync(SYNC, "utf8").trim();
|
||||
async function api(method, path, body, auth) {
|
||||
const r = await fetch(ORIGIN + path, { method, headers: { Authorization: `Bearer ${auth}`, "Content-Type": "application/json" }, body: body === undefined ? undefined : JSON.stringify(body), redirect: "error" });
|
||||
const t = await r.text(); let json = null; try { json = JSON.parse(t); } catch {}
|
||||
return { status: r.status, json };
|
||||
}
|
||||
const list = (j) => j?.items ?? (Array.isArray(j) ? j : []);
|
||||
const rec = { at: new Date().toISOString(), comments: [], problems: [] };
|
||||
const save = () => {
|
||||
const out = JSON.stringify(rec, null, 1) + "\n";
|
||||
if (out.includes(key) || out.includes(sync)) throw new Error("secret in the record; not written");
|
||||
writeFileSync(OUT, out);
|
||||
};
|
||||
for (const P of POSTS) {
|
||||
const t = await api("GET", `/api/v2/tasks/${P.id}`, undefined, sync);
|
||||
if (t.status !== 200 || t.json?.project_id !== PROJECT || !P.title.test(t.json?.title ?? "")) throw new Error(`task ${P.id}: ${t.status}, title check failed; nothing posted`);
|
||||
}
|
||||
writeFileSync(OUT, "", { flag: "wx" });
|
||||
for (const P of POSTS) {
|
||||
const c = await api("PUT", `/api/v1/tasks/${P.id}/comments`, { comment: ps(P.body) }, key);
|
||||
const e = { task: P.id, status: c.status, id: c.json?.id ?? null };
|
||||
rec.comments.push(e);
|
||||
if (c.status >= 300 || !Number.isSafeInteger(c.json?.id)) { rec.problems.push(`comment ${P.id}: ${c.status} ${c.json?.message ?? ""}`); save(); continue; }
|
||||
const back = await api("GET", `/api/v2/tasks/${P.id}/comments?per_page=50`, undefined, sync);
|
||||
const found = list(back.json).find((x) => x.id === c.json.id);
|
||||
Object.assign(e, { readBack: back.status, found: Boolean(found) });
|
||||
if (back.status !== 200 || !found) rec.problems.push(`comment ${P.id} read-back: ${back.status}`);
|
||||
save();
|
||||
}
|
||||
save();
|
||||
process.stdout.write(JSON.stringify(rec, null, 1) + "\n");
|
||||
if (rec.problems.length) process.exit(2);
|
||||
@@ -0,0 +1,50 @@
|
||||
// Sage, 2026-10-21: row 232 (installer I1) landed, comment on P19 432, per mos-0882. Sage's own key (bot user 13); the sync key reads back. Comments only: no task edits.
|
||||
// Checks each task's title before posting. Prints no secret.
|
||||
// Usage: node comment-r232-landed.mjs https://tasks.woltje.com SAGE_TOKEN_FILE SYNC_TOKEN_FILE OUT_JSON
|
||||
import { readFileSync, writeFileSync, existsSync } from "node:fs";
|
||||
const [ORIGIN, KEY, SYNC, OUT] = process.argv.slice(2);
|
||||
if (!/^https:\/\/tasks\.woltje\.com$/.test(ORIGIN)) throw new Error("tasks.woltje.com only");
|
||||
if (!OUT || existsSync(OUT)) throw new Error("OUT_JSON missing or exists; refusing");
|
||||
const PROJECT = 19;
|
||||
const PSIG = "Posted by Sage (T3 1ef1e4f8) with Sage's own key (bot user 13).";
|
||||
const esc = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
||||
const ps = (xs) => xs.map((x) => `<p>${esc(x)}</p>`).join("") + `<p>${esc(PSIG)}</p>`;
|
||||
const POSTS = [
|
||||
{ id: 440, title: /^Bus publish: a retry after publish-timeout/, body: [
|
||||
"Follow-up from row 242 (Megavolt's residual, Duckworth N3, ruled sage-1691). Row 242 makes a retry after publish-timeout push once: the bus holds the decision until its push ends and records a late push. But when a push is recorded after the agent was already told publish-timeout, the run gets no notice. The allowed event and the bus log line are the only trace, and a later publish on the decision gets decision-consumed.",
|
||||
"Risk: an agent told publish-timeout can tell the user the publish failed while the push is on the fork. That ends up as false evidence in the run.",
|
||||
"Proposed later row: when the bus records a late push, it sends the run a notice with the decision id and the ref, and a later decision-consumed refusal names the late record. This is a new message in the run protocol, so it gets its own brief. Not queued before 10-27 unless MVP work needs it.",
|
||||
] },
|
||||
];
|
||||
const key = readFileSync(KEY, "utf8").trim(), sync = readFileSync(SYNC, "utf8").trim();
|
||||
async function api(method, path, body, auth) {
|
||||
const r = await fetch(ORIGIN + path, { method, headers: { Authorization: `Bearer ${auth}`, "Content-Type": "application/json" }, body: body === undefined ? undefined : JSON.stringify(body), redirect: "error" });
|
||||
const t = await r.text(); let json = null; try { json = JSON.parse(t); } catch {}
|
||||
return { status: r.status, json };
|
||||
}
|
||||
const list = (j) => j?.items ?? (Array.isArray(j) ? j : []);
|
||||
const rec = { at: new Date().toISOString(), comments: [], problems: [] };
|
||||
const save = () => {
|
||||
const out = JSON.stringify(rec, null, 1) + "\n";
|
||||
if (out.includes(key) || out.includes(sync)) throw new Error("secret in the record; not written");
|
||||
writeFileSync(OUT, out);
|
||||
};
|
||||
for (const P of POSTS) {
|
||||
const t = await api("GET", `/api/v2/tasks/${P.id}`, undefined, sync);
|
||||
if (t.status !== 200 || t.json?.project_id !== PROJECT || !P.title.test(t.json?.title ?? "")) throw new Error(`task ${P.id}: ${t.status}, title check failed; nothing posted`);
|
||||
}
|
||||
writeFileSync(OUT, "", { flag: "wx" });
|
||||
for (const P of POSTS) {
|
||||
const c = await api("PUT", `/api/v1/tasks/${P.id}/comments`, { comment: ps(P.body) }, key);
|
||||
const e = { task: P.id, status: c.status, id: c.json?.id ?? null };
|
||||
rec.comments.push(e);
|
||||
if (c.status >= 300 || !Number.isSafeInteger(c.json?.id)) { rec.problems.push(`comment ${P.id}: ${c.status} ${c.json?.message ?? ""}`); save(); continue; }
|
||||
const back = await api("GET", `/api/v2/tasks/${P.id}/comments?per_page=50`, undefined, sync);
|
||||
const found = list(back.json).find((x) => x.id === c.json.id);
|
||||
Object.assign(e, { readBack: back.status, found: Boolean(found) });
|
||||
if (back.status !== 200 || !found) rec.problems.push(`comment ${P.id} read-back: ${back.status}`);
|
||||
save();
|
||||
}
|
||||
save();
|
||||
process.stdout.write(JSON.stringify(rec, null, 1) + "\n");
|
||||
if (rec.problems.length) process.exit(2);
|
||||
@@ -0,0 +1,51 @@
|
||||
// Sage, 2026-10-21: row 242 doc follow-up (P19 440) and the row 210 T26 follow-up (P19 424), per mos-0882. Sage's own key (bot user 13); the sync key reads back. Comments only: no task edits.
|
||||
// Checks each task's title before posting. Prints no secret.
|
||||
// Usage: node comment-r242d-r210-t26.mjs https://tasks.woltje.com SAGE_TOKEN_FILE SYNC_TOKEN_FILE OUT_JSON
|
||||
import { readFileSync, writeFileSync, existsSync } from "node:fs";
|
||||
const [ORIGIN, KEY, SYNC, OUT] = process.argv.slice(2);
|
||||
if (!/^https:\/\/tasks\.woltje\.com$/.test(ORIGIN)) throw new Error("tasks.woltje.com only");
|
||||
if (!OUT || existsSync(OUT)) throw new Error("OUT_JSON missing or exists; refusing");
|
||||
const PROJECT = 19;
|
||||
const PSIG = "Posted by Sage (T3 1ef1e4f8) with Sage's own key (bot user 13).";
|
||||
const esc = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
||||
const ps = (xs) => xs.map((x) => `<p>${esc(x)}</p>`).join("") + `<p>${esc(PSIG)}</p>`;
|
||||
const POSTS = [
|
||||
{ id: 440, title: /^Bus publish: a retry after publish-timeout/, body: [
|
||||
"Row 242 doc follow-up landed: fc1cb856b on refactor, pushed. This is Megavolt's c5a125f4b (Duckworth N1, N2, N4): comment lines in packages/cli/src/git-socket.mjs plus BUILD.md, no test hash pins, so no gate under the doc-only rule. Patch-id matches. The late-record notice (comment 432) is still the open follow-up.",
|
||||
] },
|
||||
{ id: 424, title: /^Credential store: per-agent host access/, body: [
|
||||
"Row 210 follow-up, test hygiene (found by Gizmoduck in the row 234 singles, 2026-10-21): T26 in packages/bus/tests/host-run.test.mjs spawns /bin/sh -c 'sleep 60; :' at :488 (planted) and :496 (decoy), detached. Each has a t.after (:489, :497) that SIGKILLs the sh pid only, so its sleep child lives on. Two sleeps outlive each run by up to 60 s. Fix: since both are detached, kill the process group instead (process.kill(-pid, 'SIGKILL')). It doesn't affect any result, but a mutant or busy check that counts processes sees them. A small follow-up, not a blocker.",
|
||||
] },
|
||||
];
|
||||
const key = readFileSync(KEY, "utf8").trim(), sync = readFileSync(SYNC, "utf8").trim();
|
||||
async function api(method, path, body, auth) {
|
||||
const r = await fetch(ORIGIN + path, { method, headers: { Authorization: `Bearer ${auth}`, "Content-Type": "application/json" }, body: body === undefined ? undefined : JSON.stringify(body), redirect: "error" });
|
||||
const t = await r.text(); let json = null; try { json = JSON.parse(t); } catch {}
|
||||
return { status: r.status, json };
|
||||
}
|
||||
const list = (j) => j?.items ?? (Array.isArray(j) ? j : []);
|
||||
const rec = { at: new Date().toISOString(), comments: [], problems: [] };
|
||||
const save = () => {
|
||||
const out = JSON.stringify(rec, null, 1) + "\n";
|
||||
if (out.includes(key) || out.includes(sync)) throw new Error("secret in the record; not written");
|
||||
writeFileSync(OUT, out);
|
||||
};
|
||||
for (const P of POSTS) {
|
||||
const t = await api("GET", `/api/v2/tasks/${P.id}`, undefined, sync);
|
||||
if (t.status !== 200 || t.json?.project_id !== PROJECT || !P.title.test(t.json?.title ?? "")) throw new Error(`task ${P.id}: ${t.status}, title check failed; nothing posted`);
|
||||
}
|
||||
writeFileSync(OUT, "", { flag: "wx" });
|
||||
for (const P of POSTS) {
|
||||
const c = await api("PUT", `/api/v1/tasks/${P.id}/comments`, { comment: ps(P.body) }, key);
|
||||
const e = { task: P.id, status: c.status, id: c.json?.id ?? null };
|
||||
rec.comments.push(e);
|
||||
if (c.status >= 300 || !Number.isSafeInteger(c.json?.id)) { rec.problems.push(`comment ${P.id}: ${c.status} ${c.json?.message ?? ""}`); save(); continue; }
|
||||
const back = await api("GET", `/api/v2/tasks/${P.id}/comments?per_page=50`, undefined, sync);
|
||||
const found = list(back.json).find((x) => x.id === c.json.id);
|
||||
Object.assign(e, { readBack: back.status, found: Boolean(found) });
|
||||
if (back.status !== 200 || !found) rec.problems.push(`comment ${P.id} read-back: ${back.status}`);
|
||||
save();
|
||||
}
|
||||
save();
|
||||
process.stdout.write(JSON.stringify(rec, null, 1) + "\n");
|
||||
if (rec.problems.length) process.exit(2);
|
||||
@@ -0,0 +1,50 @@
|
||||
// Sage, 2026-10-21: row 249 (grant undo hostLineLeft) landed, comment on P19 445, per mos-0882. Sage's own key (bot user 13); the sync key reads back. Comments only: no task edits.
|
||||
// Checks each task's title before posting. Prints no secret.
|
||||
// Usage: node comment-r249-landed.mjs https://tasks.woltje.com SAGE_TOKEN_FILE SYNC_TOKEN_FILE OUT_JSON
|
||||
import { readFileSync, writeFileSync, existsSync } from "node:fs";
|
||||
const [ORIGIN, KEY, SYNC, OUT] = process.argv.slice(2);
|
||||
if (!/^https:\/\/tasks\.woltje\.com$/.test(ORIGIN)) throw new Error("tasks.woltje.com only");
|
||||
if (!OUT || existsSync(OUT)) throw new Error("OUT_JSON missing or exists; refusing");
|
||||
const PROJECT = 19;
|
||||
const PSIG = "Posted by Sage (T3 1ef1e4f8) with Sage's own key (bot user 13).";
|
||||
const esc = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
||||
const ps = (xs) => xs.map((x) => `<p>${esc(x)}</p>`).join("") + `<p>${esc(PSIG)}</p>`;
|
||||
const POSTS = [
|
||||
{ id: 445, title: /^Grant undo can't prove the remote add has ended/, body: [
|
||||
"Row 249 landed on refactor as 9de5a57ab, pushed at 14:56Z with 7b9737953, which adds the DEFERRED.md entry. Gitea #1751 is closed with landing comment 30074.",
|
||||
"Gizmoduck built it (05595794b) and Duckworth approved it (comment 30070). The broker marks install done only after it returns. When it never returned, the undo records hostLineLeft, and hosts.remove refuses until the sweep clears the line.",
|
||||
"Gate on Sage's slot: the grant-executor test file passed 39/0 and the bus suite 598/0, with no stub left. A mutant that drops the new branch was killed by G8 and both G23 tests. Webby's N1, the sleep 30 test, is deferred and binds the next row that touches that file. The live stack is unchanged.",
|
||||
] },
|
||||
];
|
||||
const key = readFileSync(KEY, "utf8").trim(), sync = readFileSync(SYNC, "utf8").trim();
|
||||
async function api(method, path, body, auth) {
|
||||
const r = await fetch(ORIGIN + path, { method, headers: { Authorization: `Bearer ${auth}`, "Content-Type": "application/json" }, body: body === undefined ? undefined : JSON.stringify(body), redirect: "error" });
|
||||
const t = await r.text(); let json = null; try { json = JSON.parse(t); } catch {}
|
||||
return { status: r.status, json };
|
||||
}
|
||||
const list = (j) => j?.items ?? (Array.isArray(j) ? j : []);
|
||||
const rec = { at: new Date().toISOString(), comments: [], problems: [] };
|
||||
const save = () => {
|
||||
const out = JSON.stringify(rec, null, 1) + "\n";
|
||||
if (out.includes(key) || out.includes(sync)) throw new Error("secret in the record; not written");
|
||||
writeFileSync(OUT, out);
|
||||
};
|
||||
for (const P of POSTS) {
|
||||
const t = await api("GET", `/api/v2/tasks/${P.id}`, undefined, sync);
|
||||
if (t.status !== 200 || t.json?.project_id !== PROJECT || !P.title.test(t.json?.title ?? "")) throw new Error(`task ${P.id}: ${t.status}, title check failed; nothing posted`);
|
||||
}
|
||||
writeFileSync(OUT, "", { flag: "wx" });
|
||||
for (const P of POSTS) {
|
||||
const c = await api("PUT", `/api/v1/tasks/${P.id}/comments`, { comment: ps(P.body) }, key);
|
||||
const e = { task: P.id, status: c.status, id: c.json?.id ?? null };
|
||||
rec.comments.push(e);
|
||||
if (c.status >= 300 || !Number.isSafeInteger(c.json?.id)) { rec.problems.push(`comment ${P.id}: ${c.status} ${c.json?.message ?? ""}`); save(); continue; }
|
||||
const back = await api("GET", `/api/v2/tasks/${P.id}/comments?per_page=50`, undefined, sync);
|
||||
const found = list(back.json).find((x) => x.id === c.json.id);
|
||||
Object.assign(e, { readBack: back.status, found: Boolean(found) });
|
||||
if (back.status !== 200 || !found) rec.problems.push(`comment ${P.id} read-back: ${back.status}`);
|
||||
save();
|
||||
}
|
||||
save();
|
||||
process.stdout.write(JSON.stringify(rec, null, 1) + "\n");
|
||||
if (rec.problems.length) process.exit(2);
|
||||
@@ -0,0 +1,49 @@
|
||||
// Sage, 2026-10-21: row 215 (C2-04) landed, comment on P19 416 (Q174 landing comment). Sage's own key (bot user 13); the sync key reads back. Comments only: no task edits.
|
||||
// Checks each task's title before posting. Prints no secret.
|
||||
// Usage: node comment-r215-landed.mjs https://tasks.woltje.com SAGE_TOKEN_FILE SYNC_TOKEN_FILE OUT_JSON
|
||||
import { readFileSync, writeFileSync, existsSync } from "node:fs";
|
||||
const [ORIGIN, KEY, SYNC, OUT] = process.argv.slice(2);
|
||||
if (!/^https:\/\/tasks\.woltje\.com$/.test(ORIGIN)) throw new Error("tasks.woltje.com only");
|
||||
if (!OUT || existsSync(OUT)) throw new Error("OUT_JSON missing or exists; refusing");
|
||||
const PROJECT = 19;
|
||||
const PSIG = "Posted by Sage (T3 1ef1e4f8) with Sage's own key (bot user 13).";
|
||||
const esc = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
||||
const ps = (xs) => xs.map((x) => `<p>${esc(x)}</p>`).join("") + `<p>${esc(PSIG)}</p>`;
|
||||
const POSTS = [
|
||||
{ id: 450, title: /^K12 cohort test: populated undefined under load/, body: [
|
||||
"Row 250 landed on refactor as 1b6158ce2 at 19:42Z. K12 now names the cause when the cgroup.events read fails, holds the stop's release while it reads the shim, and the release answers with the events read's reason. R13 closes its controller. Gitea #1755 is closed (comment 30139). Queue row 250 is done (rev 2965).",
|
||||
"Gizmoduck built it, and Filbert approved round 2 (rev 2957). The landing patch-id equals the candidate c1a858975. Gizmoduck ran the gate on grant sage-2109: the probes failed and passed as predicted, conversation whole 3x 183/183 plus a concurrent pair, and control-board whole 124/124.",
|
||||
] },
|
||||
];
|
||||
const key = readFileSync(KEY, "utf8").trim(), sync = readFileSync(SYNC, "utf8").trim();
|
||||
async function api(method, path, body, auth) {
|
||||
const r = await fetch(ORIGIN + path, { method, headers: { Authorization: `Bearer ${auth}`, "Content-Type": "application/json" }, body: body === undefined ? undefined : JSON.stringify(body), redirect: "error" });
|
||||
const t = await r.text(); let json = null; try { json = JSON.parse(t); } catch {}
|
||||
return { status: r.status, json };
|
||||
}
|
||||
const list = (j) => j?.items ?? (Array.isArray(j) ? j : []);
|
||||
const rec = { at: new Date().toISOString(), comments: [], problems: [] };
|
||||
const save = () => {
|
||||
const out = JSON.stringify(rec, null, 1) + "\n";
|
||||
if (out.includes(key) || out.includes(sync)) throw new Error("secret in the record; not written");
|
||||
writeFileSync(OUT, out);
|
||||
};
|
||||
for (const P of POSTS) {
|
||||
const t = await api("GET", `/api/v2/tasks/${P.id}`, undefined, sync);
|
||||
if (t.status !== 200 || t.json?.project_id !== PROJECT || !P.title.test(t.json?.title ?? "")) throw new Error(`task ${P.id}: ${t.status}, title check failed; nothing posted`);
|
||||
}
|
||||
writeFileSync(OUT, "", { flag: "wx" });
|
||||
for (const P of POSTS) {
|
||||
const c = await api("PUT", `/api/v1/tasks/${P.id}/comments`, { comment: ps(P.body) }, key);
|
||||
const e = { task: P.id, status: c.status, id: c.json?.id ?? null };
|
||||
rec.comments.push(e);
|
||||
if (c.status >= 300 || !Number.isSafeInteger(c.json?.id)) { rec.problems.push(`comment ${P.id}: ${c.status} ${c.json?.message ?? ""}`); save(); continue; }
|
||||
const back = await api("GET", `/api/v2/tasks/${P.id}/comments?per_page=50`, undefined, sync);
|
||||
const found = list(back.json).find((x) => x.id === c.json.id);
|
||||
Object.assign(e, { readBack: back.status, found: Boolean(found) });
|
||||
if (back.status !== 200 || !found) rec.problems.push(`comment ${P.id} read-back: ${back.status}`);
|
||||
save();
|
||||
}
|
||||
save();
|
||||
process.stdout.write(JSON.stringify(rec, null, 1) + "\n");
|
||||
if (rec.problems.length) process.exit(2);
|
||||
@@ -0,0 +1,52 @@
|
||||
// Sage, 2026-10-21: row 215 (C2-04) landed, comment on P19 416 (Q174 landing comment). Sage's own key (bot user 13); the sync key reads back. Comments only: no task edits.
|
||||
// Checks each task's title before posting. Prints no secret.
|
||||
// Usage: node comment-r215-landed.mjs https://tasks.woltje.com SAGE_TOKEN_FILE SYNC_TOKEN_FILE OUT_JSON
|
||||
import { readFileSync, writeFileSync, existsSync } from "node:fs";
|
||||
const [ORIGIN, KEY, SYNC, OUT] = process.argv.slice(2);
|
||||
if (!/^https:\/\/tasks\.woltje\.com$/.test(ORIGIN)) throw new Error("tasks.woltje.com only");
|
||||
if (!OUT || existsSync(OUT)) throw new Error("OUT_JSON missing or exists; refusing");
|
||||
const PROJECT = 19;
|
||||
const PSIG = "Posted by Sage (T3 1ef1e4f8) with Sage's own key (bot user 13).";
|
||||
const esc = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
||||
const ps = (xs) => xs.map((x) => `<p>${esc(x)}</p>`).join("") + `<p>${esc(PSIG)}</p>`;
|
||||
const POSTS = [
|
||||
{ id: 458, title: /Pocket ID|OIDC|Q203/i, body: [
|
||||
"Queue row 255 (rev 2976), Gitea #1760. It's a design row. Launchpad owns it, Webby reviews, Sage owns the gate, and it runs after row 252. The brief is docs/plans/2026-10-21_bundled-pocket-id.md (7d08237e1). Jason's Q203 answer is quoted verbatim, and Mos's reading is labelled as his. The design covers the installer choice, the component under REQ-INST-1, the Q175 start checks with no relaxation, the client secret slot, first-person passkey enrollment, reach (the outside steps are Jason's), agents, instances, stand-in tests and the build rows.",
|
||||
"PRD 1.10 adds REQ-INST-2, post-v1, as Mos ruled in mos-1204. A 1.10 note records that the out-of-scope list still names 'Pocket ID and SSO'. Jason hasn't ruled on that line (vikunja:19/459).",
|
||||
] },
|
||||
{ id: 416, title: /^MVP Case 2: creative writing hub instance/, body: [
|
||||
"Row 222 (C2-03a, fetch-only library sync) landed on refactor as 0b33f5e9d at 20:13Z. It adds the library.sync bus verb, a 60 s timer per library entry with a sync user, and the askpass helper. Gitea #1723 is closed (comment 30147), and queue row 222 is done (rev 2972). Megavolt built it, Dewey approved it (rev 2966), and megavolt-0098 ran the gate green. Dewey's deferrals are row 254 (#1759): the client timeout, STATUS codes and the README line. It runs after row 196.",
|
||||
] },
|
||||
];
|
||||
const key = readFileSync(KEY, "utf8").trim(), sync = readFileSync(SYNC, "utf8").trim();
|
||||
async function api(method, path, body, auth) {
|
||||
const r = await fetch(ORIGIN + path, { method, headers: { Authorization: `Bearer ${auth}`, "Content-Type": "application/json" }, body: body === undefined ? undefined : JSON.stringify(body), redirect: "error" });
|
||||
const t = await r.text(); let json = null; try { json = JSON.parse(t); } catch {}
|
||||
return { status: r.status, json };
|
||||
}
|
||||
const list = (j) => j?.items ?? (Array.isArray(j) ? j : []);
|
||||
const rec = { at: new Date().toISOString(), comments: [], problems: [] };
|
||||
const save = () => {
|
||||
const out = JSON.stringify(rec, null, 1) + "\n";
|
||||
if (out.includes(key) || out.includes(sync)) throw new Error("secret in the record; not written");
|
||||
writeFileSync(OUT, out);
|
||||
};
|
||||
for (const P of POSTS) {
|
||||
const t = await api("GET", `/api/v2/tasks/${P.id}`, undefined, sync);
|
||||
if (t.status !== 200 || t.json?.project_id !== PROJECT || !P.title.test(t.json?.title ?? "")) throw new Error(`task ${P.id}: ${t.status}, title check failed; nothing posted`);
|
||||
}
|
||||
writeFileSync(OUT, "", { flag: "wx" });
|
||||
for (const P of POSTS) {
|
||||
const c = await api("PUT", `/api/v1/tasks/${P.id}/comments`, { comment: ps(P.body) }, key);
|
||||
const e = { task: P.id, status: c.status, id: c.json?.id ?? null };
|
||||
rec.comments.push(e);
|
||||
if (c.status >= 300 || !Number.isSafeInteger(c.json?.id)) { rec.problems.push(`comment ${P.id}: ${c.status} ${c.json?.message ?? ""}`); save(); continue; }
|
||||
const back = await api("GET", `/api/v2/tasks/${P.id}/comments?per_page=50`, undefined, sync);
|
||||
const found = list(back.json).find((x) => x.id === c.json.id);
|
||||
Object.assign(e, { readBack: back.status, found: Boolean(found) });
|
||||
if (back.status !== 200 || !found) rec.problems.push(`comment ${P.id} read-back: ${back.status}`);
|
||||
save();
|
||||
}
|
||||
save();
|
||||
process.stdout.write(JSON.stringify(rec, null, 1) + "\n");
|
||||
if (rec.problems.length) process.exit(2);
|
||||
@@ -0,0 +1,54 @@
|
||||
// Sage, 2026-10-21: row 66 (unslop check) landed, comment on P19 212 and 454, per mos-1186. Sage's own key (bot user 13); the sync key reads back. Comments only: no task edits.
|
||||
// Checks each task's title before posting. Prints no secret.
|
||||
// Usage: node comment-r249-landed.mjs https://tasks.woltje.com SAGE_TOKEN_FILE SYNC_TOKEN_FILE OUT_JSON
|
||||
import { readFileSync, writeFileSync, existsSync } from "node:fs";
|
||||
const [ORIGIN, KEY, SYNC, OUT] = process.argv.slice(2);
|
||||
if (!/^https:\/\/tasks\.woltje\.com$/.test(ORIGIN)) throw new Error("tasks.woltje.com only");
|
||||
if (!OUT || existsSync(OUT)) throw new Error("OUT_JSON missing or exists; refusing");
|
||||
const PROJECT = 19;
|
||||
const PSIG = "Posted by Sage (T3 1ef1e4f8) with Sage's own key (bot user 13).";
|
||||
const esc = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
||||
const ps = (xs) => xs.map((x) => `<p>${esc(x)}</p>`).join("") + `<p>${esc(PSIG)}</p>`;
|
||||
const POSTS = [
|
||||
{ id: 212, title: /^Row 66: Managed sessions run the unslop check on both harnesses/, body: [
|
||||
"Row 66 landed on refactor as 2c38325ec, pushed at 18:51Z. 3b0aef995 adds the DEFERRED.md entry for the two live checks. Queue row 66 is done (rev 2921).",
|
||||
"Round 4 (82c830779) was approved by Dewey (comment 30085) and Filbert (30086), then rebased onto a36523713 after row 197 (mos-1186 item 4). The cherry-pick had no conflict. The patch-id without agents/ is d67275ac4251 on both bases. The harness suite went 149/0 on the rebased tree. The r4d gate was green: unslop, harness, plugins and every scripts/test-*.sh, with cli 682/0 on the TMPDIR rerun.",
|
||||
"Landing sessions on a scratch root from the candidate tree: Claude Code run r4ab24630d323 put the operator note on the RESULT, the correction notice reached the next turn as hook context, and unslop.jsonl counted checked 2, flagged 1, notices 1. The pi run r1dd94171bede is launch only until #383. The live checks are task 454.",
|
||||
] },
|
||||
{ id: 454, title: /^Row 66 live checks: unslop notice on Claude Code/, body: [
|
||||
"Row 66 landed as 2c38325ec, and the DEFERRED.md entry is 3b0aef995. Both checks here are open. Claude Code runs in the first re-pin whose tree includes 2c38325ec, backup first (Q173). Pi runs one full session with a tell after #383.",
|
||||
"Reference from the scratch root (agents/sage/work/queue-66/LANDING-SESSION.md): Claude Code run r4ab24630d323 showed the note, the injected notice and counts checked 2, flagged 1, notices 1. The pi launch r1dd94171bede bundled the unslop extension but ran no turn.",
|
||||
] },
|
||||
];
|
||||
const key = readFileSync(KEY, "utf8").trim(), sync = readFileSync(SYNC, "utf8").trim();
|
||||
async function api(method, path, body, auth) {
|
||||
const r = await fetch(ORIGIN + path, { method, headers: { Authorization: `Bearer ${auth}`, "Content-Type": "application/json" }, body: body === undefined ? undefined : JSON.stringify(body), redirect: "error" });
|
||||
const t = await r.text(); let json = null; try { json = JSON.parse(t); } catch {}
|
||||
return { status: r.status, json };
|
||||
}
|
||||
const list = (j) => j?.items ?? (Array.isArray(j) ? j : []);
|
||||
const rec = { at: new Date().toISOString(), comments: [], problems: [] };
|
||||
const save = () => {
|
||||
const out = JSON.stringify(rec, null, 1) + "\n";
|
||||
if (out.includes(key) || out.includes(sync)) throw new Error("secret in the record; not written");
|
||||
writeFileSync(OUT, out);
|
||||
};
|
||||
for (const P of POSTS) {
|
||||
const t = await api("GET", `/api/v2/tasks/${P.id}`, undefined, sync);
|
||||
if (t.status !== 200 || t.json?.project_id !== PROJECT || !P.title.test(t.json?.title ?? "")) throw new Error(`task ${P.id}: ${t.status}, title check failed; nothing posted`);
|
||||
}
|
||||
writeFileSync(OUT, "", { flag: "wx" });
|
||||
for (const P of POSTS) {
|
||||
const c = await api("PUT", `/api/v1/tasks/${P.id}/comments`, { comment: ps(P.body) }, key);
|
||||
const e = { task: P.id, status: c.status, id: c.json?.id ?? null };
|
||||
rec.comments.push(e);
|
||||
if (c.status >= 300 || !Number.isSafeInteger(c.json?.id)) { rec.problems.push(`comment ${P.id}: ${c.status} ${c.json?.message ?? ""}`); save(); continue; }
|
||||
const back = await api("GET", `/api/v2/tasks/${P.id}/comments?per_page=50`, undefined, sync);
|
||||
const found = list(back.json).find((x) => x.id === c.json.id);
|
||||
Object.assign(e, { readBack: back.status, found: Boolean(found) });
|
||||
if (back.status !== 200 || !found) rec.problems.push(`comment ${P.id} read-back: ${back.status}`);
|
||||
save();
|
||||
}
|
||||
save();
|
||||
process.stdout.write(JSON.stringify(rec, null, 1) + "\n");
|
||||
if (rec.problems.length) process.exit(2);
|
||||
@@ -0,0 +1,63 @@
|
||||
// Sage, 2026-10-21: files row 249 (mos-1166, #1751) as a P19 task under #436, then relates it to 436 as a subtask.
|
||||
// Sage's own key (bot user 13); the sync key reads back. No labels, assignees, due dates or bucket moves. Refuses if
|
||||
// the title exists. Prints no secret.
|
||||
// Usage: node file-r249.mjs https://tasks.woltje.com SAGE_TOKEN_FILE SYNC_TOKEN_FILE OUT_JSON
|
||||
import { readFileSync, writeFileSync, existsSync } from "node:fs";
|
||||
const [ORIGIN, KEY, SYNC, OUT] = process.argv.slice(2);
|
||||
if (!/^https:\/\/tasks\.woltje\.com$/.test(ORIGIN)) throw new Error("tasks.woltje.com only");
|
||||
if (!OUT || existsSync(OUT)) throw new Error("OUT_JSON missing or exists; refusing");
|
||||
const PROJECT = 19;
|
||||
const SIG = "Created by Sage (T3 1ef1e4f8) with Sage's own key (bot user 13).";
|
||||
const esc = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
||||
const ps = (sig, xs) => xs.map((x) => `<p>${esc(x)}</p>`).join("") + `<p>${esc(sig)}</p>`;
|
||||
const TASKS = [
|
||||
{
|
||||
title: "Grant undo can't prove the remote add has ended: record hostLineLeft when install never returned (row 249, #1751)",
|
||||
description: ps(SIG, [
|
||||
"Filed under #436 by Mos's ruling mos-1166. Gizmoduck answered in gizmoduck-0137 and 0139, and Duckworth checked the answer in duckworth-0091. The row 197 r8 proof found the leftover stubs that show it.",
|
||||
"An abort or timeout during install kills the local ssh, but sshd sends the remote add no SIGHUP, so it keeps running. If it writes after the undo's single remove, hostLineLeft stays unset. hosts.status shows the host clean, and hosts.remove can drop the record while the line is still on the host. After that a sweep refuses host-unknown. The line authorizes a key nobody holds.",
|
||||
"Queue row 249, owner Gizmoduck, reviewer Duckworth, gate Sage. Brief: docs/plans/2026-10-20_host-credentials.md (6de1ebc62).",
|
||||
]),
|
||||
},
|
||||
];
|
||||
const key = readFileSync(KEY, "utf8").trim(), sync = readFileSync(SYNC, "utf8").trim();
|
||||
async function api(method, path, body, auth) {
|
||||
const r = await fetch(ORIGIN + path, { method, headers: { Authorization: `Bearer ${auth}`, "Content-Type": "application/json" }, body: body === undefined ? undefined : JSON.stringify(body), redirect: "error" });
|
||||
const t = await r.text(); let json = null; try { json = JSON.parse(t); } catch {}
|
||||
return { status: r.status, json };
|
||||
}
|
||||
const list = (j) => j?.items ?? (Array.isArray(j) ? j : []);
|
||||
async function existing() {
|
||||
const out = [];
|
||||
for (let page = 1; page < 20; page++) {
|
||||
const r = await api("GET", `/api/v2/projects/${PROJECT}/tasks?per_page=50&page=${page}`, undefined, sync);
|
||||
if (r.status !== 200) throw new Error(`list tasks: ${r.status}`);
|
||||
out.push(...list(r.json));
|
||||
if (list(r.json).length < 50) return out;
|
||||
}
|
||||
throw new Error("more than 19 pages");
|
||||
}
|
||||
const rec = { at: new Date().toISOString(), project: PROJECT, created: null, problems: [] };
|
||||
const save = () => {
|
||||
const out = JSON.stringify(rec, null, 1) + "\n";
|
||||
if (out.includes(key) || out.includes(sync)) throw new Error("secret in the record; not written");
|
||||
writeFileSync(OUT, out);
|
||||
};
|
||||
const have = await existing();
|
||||
for (const T of TASKS) if (have.some((t) => t.title === T.title)) throw new Error("title exists; refusing");
|
||||
writeFileSync(OUT, "", { flag: "wx" });
|
||||
rec.created = [];
|
||||
for (const T of TASKS) {
|
||||
const r = await api("PUT", `/api/v1/projects/${PROJECT}/tasks`, { title: T.title, description: T.description }, key);
|
||||
const c = { status: r.status, id: r.json?.id ?? null };
|
||||
rec.created.push(c);
|
||||
if (r.status >= 300 || !Number.isSafeInteger(r.json?.id)) { rec.problems.push(`create: ${r.status} ${r.json?.message ?? ""}`); continue; }
|
||||
const back = await api("GET", `/api/v2/tasks/${r.json.id}`, undefined, sync);
|
||||
Object.assign(c, { readBack: back.status, done: back.json?.done });
|
||||
if (back.status !== 200 || back.json?.title !== T.title || back.json?.done) rec.problems.push(`read-back differs for ${r.json.id}`);
|
||||
const rel = await api("PUT", `/api/v1/tasks/${r.json.id}/relations`, { other_task_id: 436, relation_kind: "parenttask" }, key);
|
||||
c.relation = rel.status;
|
||||
if (rel.status >= 300) rec.problems.push(`relation: ${rel.status} ${rel.json?.message ?? ""}`);
|
||||
}
|
||||
save();
|
||||
console.log(JSON.stringify(rec));
|
||||
@@ -0,0 +1,83 @@
|
||||
// Sage, 2026-10-21: re-pin window 4429a104f to 7247d80c7 W12 result (mos-1234) and the PRD 1.11 and 1.12 landings.
|
||||
// Comments on P19 454, 386, 412 and 435, then comments on 459 and 462 and marks those two done (mos-1234 item 4).
|
||||
// Sage's own key (bot user 13); the sync key reads back. A close sends the task as the v1 GET returned it with only
|
||||
// done changed, and the read-back checks that title and description are the same. Rollback is marking it undone.
|
||||
// Checks each task's title before anything is posted. Prints no secret.
|
||||
// Usage: node post-w12-prd.mjs https://tasks.woltje.com SAGE_TOKEN_FILE SYNC_TOKEN_FILE OUT_JSON
|
||||
import { readFileSync, writeFileSync, existsSync } from "node:fs";
|
||||
const [ORIGIN, KEY, SYNC, OUT] = process.argv.slice(2);
|
||||
if (!/^https:\/\/tasks\.woltje\.com$/.test(ORIGIN)) throw new Error("tasks.woltje.com only");
|
||||
if (!OUT || existsSync(OUT)) throw new Error("OUT_JSON missing or exists; refusing");
|
||||
const PROJECT = 19;
|
||||
const PSIG = "Posted by Sage (T3 1ef1e4f8) with Sage's own key (bot user 13).";
|
||||
const esc = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
||||
const ps = (xs) => xs.map((x) => `<p>${esc(x)}</p>`).join("") + `<p>${esc(PSIG)}</p>`;
|
||||
const WIN = "agents/sage/work/window-repin-7247d80c7/";
|
||||
const POSTS = [
|
||||
{ id: 454, title: /^Row 66 live checks: unslop notice on Claude Code/, close: false, body: [
|
||||
"The Claude Code half passed in W12 of the 7247d80c7 re-pin (w12b.sh, 22:24:48Z to 22:27:13Z, W12_PASS, mos-1234). It ran on the live chief r7646a7a4a2bd (claude-code). Baseline unslopCounts were checked 2, flagged 0, notices 0. Turn 1 asked for one sentence using four banned words, and its RESULT carried the operator note \"[unslop: 1 reply of this session flagged since the last note ... The session gets a correction notice on its next turn.]\". Turn 2 replied \"received\". After the turns: checked 5, flagged 1 (ms-unslop:7 x4), notices 1, broken null.",
|
||||
"The pi half still waits on #383, so this stays open.",
|
||||
"The pm requirements read is open, not a window failure (mos-1234 item 3). No tester path reaches a pm: the chief relayed the request as bus message ed1063ea but launched no pm, because a tester message authorizes nothing. The read runs at the first pm launch from a human message or a real bus notice, and ed1063ea waits unread for that pm. Nothing goes to Jason for it.",
|
||||
`Evidence: ${WIN}STATE.md (W12 section), W.txt, .w12b-counts.json, w12b.out. The W12 store backup is SHA256SUMS a4ecd7ba.`,
|
||||
] },
|
||||
{ id: 386, title: /^Q173 test system/, close: false, body: [
|
||||
"The 7247d80c7 re-pin window is done. W0 to W12 passed, nothing was rolled back, and the one counted failure was P2 attempt 1 (a script path bug before any live step). The live Console now serves the tester through the private socket ~/.mosaic-dev/console/tester.sock with --tester-socket, and nothing listens on 7333. Two tester reads through the socket matched the old listener's /api/config line. Every step ran as tester, with no --by jason, no Save, no mosaic name, and bus.tester untouched.",
|
||||
`STATE report: ${WIN}STATE.md. It goes into the window record commit next.`,
|
||||
] },
|
||||
{ id: 412, title: /^Console SSO/, close: false, body: [
|
||||
"Baseline for phase 2 (mos-1195), from W10 of the 7247d80c7 re-pin at 22:18:51Z: POST /api/seen and POST /api/reply through 127.0.0.1:7332 with no session and no proxy header both return 401 \"sign-in required\". Phase 2 (config entry with clientId fvchAepp9…, --sso --send re-pin, Jason's first sign-in, the sub read, the unsigned-write check, the sign-out landing check) runs when Jason starts it (mos-1217).",
|
||||
] },
|
||||
{ id: 435, title: /^Console inbox: Approve\/Deny/, close: false, body: [
|
||||
"Row 196 is live on dragon-lin since the 7247d80c7 re-pin (activation 20261021T215926Z-7247d80c7af7, store 3j). The tester reaches the Console only through the private socket now. Hosts and host_grants exist in the store with 0 rows, and the window added no grant.* event (mos-1225). Per mos-1198, .53 and .54 take no copy carrying 196 until row 253 lands.",
|
||||
] },
|
||||
{ id: 459, title: /^PRD v1 scope: 'Pocket ID and SSO'/, close: true, body: [
|
||||
"Closing: PRD 1.11 landed on refactor at 22:30:53Z as ea0b2a16d and 73222011f, a fast-forward from cca206cdc (Ludwig, mos-1227, mos-1229). It records Q211, so v1 is the 10-27 goal, and Mos's reading of the review path. The v1 VMs' OIDC provider stays open under 461. requirements.json moves from 1.10 with 70 ids to 1.11 with 71. Before the push, packages/tasks/tests/requirements.test.mjs and packages/bus/tests/requirements.test.mjs each passed 3/3 run whole on 73222011f, and verbs.test.mjs passed 31/31.",
|
||||
] },
|
||||
{ id: 462, title: /^PRD 1\.12: requirement ids/, close: true, body: [
|
||||
"Closing: PRD 1.12 landed on refactor at 22:30:59Z as 3576afffb, 74dea06d9 and 1509c7cc5, a fast-forward from 73222011f. Della approved it in della-0010. requirements.json is 1.12 with 82 ids, 11 of them new, and the REQ-DEC-3 title changed. Before the push, both requirements test files passed 3/3 each, run whole on 1509c7cc5 as the final candidate, and verbs.test.mjs passed 31/31. The follow-ups 463, 464 and 465 stay open.",
|
||||
] },
|
||||
];
|
||||
const key = readFileSync(KEY, "utf8").trim(), sync = readFileSync(SYNC, "utf8").trim();
|
||||
async function api(method, path, body, auth) {
|
||||
const r = await fetch(ORIGIN + path, { method, headers: { Authorization: `Bearer ${auth}`, "Content-Type": "application/json" }, body: body === undefined ? undefined : JSON.stringify(body), redirect: "error" });
|
||||
const t = await r.text(); let json = null; try { json = JSON.parse(t); } catch {}
|
||||
return { status: r.status, json };
|
||||
}
|
||||
const list = (j) => j?.items ?? (Array.isArray(j) ? j : []);
|
||||
const rec = { at: new Date().toISOString(), comments: [], closes: [], problems: [] };
|
||||
const save = () => {
|
||||
const out = JSON.stringify(rec, null, 1) + "\n";
|
||||
if (out.includes(key) || out.includes(sync)) throw new Error("secret in the record; not written");
|
||||
writeFileSync(OUT, out);
|
||||
};
|
||||
const before = {};
|
||||
for (const P of POSTS) {
|
||||
const t = await api("GET", `/api/v2/tasks/${P.id}`, undefined, sync);
|
||||
if (t.status !== 200 || t.json?.project_id !== PROJECT || !P.title.test(t.json?.title ?? "")) throw new Error(`task ${P.id}: ${t.status}, title check failed; nothing posted`);
|
||||
if (P.close && t.json.done) throw new Error(`task ${P.id} is already done; nothing posted`);
|
||||
before[P.id] = { title: t.json.title, description: t.json.description };
|
||||
}
|
||||
writeFileSync(OUT, "", { flag: "wx" });
|
||||
for (const P of POSTS) {
|
||||
const c = await api("PUT", `/api/v1/tasks/${P.id}/comments`, { comment: ps(P.body) }, key);
|
||||
const e = { task: P.id, status: c.status, id: c.json?.id ?? null };
|
||||
rec.comments.push(e);
|
||||
if (c.status >= 300 || !Number.isSafeInteger(c.json?.id)) { rec.problems.push(`comment ${P.id}: ${c.status} ${c.json?.message ?? ""}`); save(); continue; }
|
||||
const back = await api("GET", `/api/v2/tasks/${P.id}/comments?per_page=50`, undefined, sync);
|
||||
const found = list(back.json).find((x) => x.id === c.json.id);
|
||||
Object.assign(e, { readBack: back.status, found: Boolean(found) });
|
||||
if (back.status !== 200 || !found) { rec.problems.push(`comment ${P.id} read-back: ${back.status}`); save(); continue; }
|
||||
if (P.close) {
|
||||
const full = await api("GET", `/api/v1/tasks/${P.id}`, undefined, key);
|
||||
if (full.status !== 200 || full.json?.id !== P.id) { rec.problems.push(`v1 get ${P.id}: ${full.status}`); save(); continue; }
|
||||
const u = await api("POST", `/api/v1/tasks/${P.id}`, { ...full.json, done: true }, key);
|
||||
const b2 = await api("GET", `/api/v2/tasks/${P.id}`, undefined, sync);
|
||||
const x = { task: P.id, status: u.status, readBack: b2.status, done: b2.json?.done, titleSame: b2.json?.title === before[P.id].title, descriptionSame: b2.json?.description === before[P.id].description, inverse: `mark undone: POST /api/v1/tasks/${P.id} done false (sage key)` };
|
||||
rec.closes.push(x);
|
||||
if (u.status >= 300 || b2.status !== 200 || b2.json?.done !== true || !x.titleSame || !x.descriptionSame) rec.problems.push(`close ${P.id}: update or read-back differs`);
|
||||
}
|
||||
save();
|
||||
}
|
||||
save();
|
||||
process.stdout.write(JSON.stringify(rec, null, 1) + "\n");
|
||||
if (rec.problems.length) process.exit(2);
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"at": "2026-10-21T02:51:32.855Z",
|
||||
"comments": [
|
||||
{
|
||||
"task": 424,
|
||||
"status": 201,
|
||||
"id": 429,
|
||||
"readBack": 200,
|
||||
"found": true
|
||||
}
|
||||
],
|
||||
"problems": []
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"at": "2026-10-21T03:21:23.327Z",
|
||||
"comments": [
|
||||
{
|
||||
"task": 432,
|
||||
"status": 201,
|
||||
"id": 430,
|
||||
"readBack": 200,
|
||||
"found": true
|
||||
}
|
||||
],
|
||||
"problems": []
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"at": "2026-10-21T05:42:18.847Z",
|
||||
"comments": [
|
||||
{
|
||||
"task": 432,
|
||||
"status": 201,
|
||||
"id": 436,
|
||||
"readBack": 200,
|
||||
"found": true
|
||||
}
|
||||
],
|
||||
"problems": []
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"at": "2026-10-21T06:17:35.760Z",
|
||||
"comments": [
|
||||
{
|
||||
"task": 436,
|
||||
"status": 201,
|
||||
"id": 437,
|
||||
"readBack": 200,
|
||||
"found": true
|
||||
}
|
||||
],
|
||||
"problems": []
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"at": "2026-10-21T01:55:44.446Z",
|
||||
"comments": [
|
||||
{
|
||||
"task": 440,
|
||||
"status": 201,
|
||||
"id": 427,
|
||||
"readBack": 200,
|
||||
"found": true
|
||||
},
|
||||
{
|
||||
"task": 441,
|
||||
"status": 201,
|
||||
"id": 428,
|
||||
"readBack": 200,
|
||||
"found": true
|
||||
}
|
||||
],
|
||||
"problems": []
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"at": "2026-10-21T06:58:53.918Z",
|
||||
"comments": [
|
||||
{
|
||||
"task": 432,
|
||||
"status": 201,
|
||||
"id": 438,
|
||||
"readBack": 200,
|
||||
"found": true
|
||||
}
|
||||
],
|
||||
"problems": []
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"at": "2026-10-21T13:13:35.441Z",
|
||||
"project": 19,
|
||||
"created": [
|
||||
{
|
||||
"status": 201,
|
||||
"id": 445,
|
||||
"readBack": 200,
|
||||
"done": false,
|
||||
"relation": 201
|
||||
}
|
||||
],
|
||||
"problems": []
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"at": "2026-10-21T14:57:03.574Z",
|
||||
"comments": [
|
||||
{
|
||||
"task": 445,
|
||||
"status": 201,
|
||||
"id": 442,
|
||||
"readBack": 200,
|
||||
"found": true
|
||||
}
|
||||
],
|
||||
"problems": []
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"at": "2026-10-21T20:44:23.676Z",
|
||||
"comments": [
|
||||
{
|
||||
"task": 458,
|
||||
"status": 201,
|
||||
"id": 478,
|
||||
"readBack": 200,
|
||||
"found": true
|
||||
},
|
||||
{
|
||||
"task": 416,
|
||||
"status": 201,
|
||||
"id": 479,
|
||||
"readBack": 200,
|
||||
"found": true
|
||||
}
|
||||
],
|
||||
"problems": []
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
{
|
||||
"at": "2026-10-21T22:32:05.204Z",
|
||||
"comments": [
|
||||
{
|
||||
"task": 454,
|
||||
"status": 201,
|
||||
"id": 508,
|
||||
"readBack": 200,
|
||||
"found": true
|
||||
},
|
||||
{
|
||||
"task": 386,
|
||||
"status": 201,
|
||||
"id": 509,
|
||||
"readBack": 200,
|
||||
"found": true
|
||||
},
|
||||
{
|
||||
"task": 412,
|
||||
"status": 201,
|
||||
"id": 510,
|
||||
"readBack": 200,
|
||||
"found": true
|
||||
},
|
||||
{
|
||||
"task": 435,
|
||||
"status": 201,
|
||||
"id": 511,
|
||||
"readBack": 200,
|
||||
"found": true
|
||||
},
|
||||
{
|
||||
"task": 459,
|
||||
"status": 201,
|
||||
"id": 512,
|
||||
"readBack": 200,
|
||||
"found": true
|
||||
},
|
||||
{
|
||||
"task": 462,
|
||||
"status": 201,
|
||||
"id": 513,
|
||||
"readBack": 200,
|
||||
"found": true
|
||||
}
|
||||
],
|
||||
"closes": [
|
||||
{
|
||||
"task": 459,
|
||||
"status": 200,
|
||||
"readBack": 200,
|
||||
"done": true,
|
||||
"titleSame": true,
|
||||
"descriptionSame": true,
|
||||
"inverse": "mark undone: POST /api/v1/tasks/459 done false (sage key)"
|
||||
},
|
||||
{
|
||||
"task": 462,
|
||||
"status": 200,
|
||||
"readBack": 200,
|
||||
"done": true,
|
||||
"titleSame": true,
|
||||
"descriptionSame": true,
|
||||
"inverse": "mark undone: POST /api/v1/tasks/462 done false (sage key)"
|
||||
}
|
||||
],
|
||||
"problems": []
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
# 10-24 window, notes for draft 1
|
||||
|
||||
Sage, 2026-10-21T19:40Z. These are working notes, not a draft. Draft 1 is cut from them once NEW is fixed.
|
||||
Draft 0 is `repin-1024-draft0.md`.
|
||||
|
||||
## Changes since draft 0
|
||||
|
||||
- **Phase 2 order (mos-1197 item 3).** Phase 2 now runs after this window's re-pin is verified, not on
|
||||
10-23 before it, and Jason picks the hour. So this window's OLD is the live pin, not a phase 2 copy, and
|
||||
draft 0's line 14-17 and Q2 are out of date.
|
||||
- **Console flags.** They stay as they are: no `--sso` and no `--send`. `--send` goes on with `--sso` in the
|
||||
phase 2 window (mos-1197 2a). P0 checks that the window's activate line carries neither flag.
|
||||
- **NEW (mos-1161).** NEW is the latest code landing when draft 1 is cut. At 19:40Z that is e54e05463 (row
|
||||
215). 194 hasn't landed, so P3 has no lifecycle step unless it lands with a full gate first.
|
||||
- **Row 66 live check (Vikunja #454, DEFERRED 3b0aef995).** This is the first re-pin whose tree includes
|
||||
2c38325ec. Add a step after the window is verified. One Claude Code managed session with a prompt that
|
||||
draws a known tell, then record the operator note on the RESULT, the next-turn notice and the
|
||||
`unslop.jsonl` counts. Backup first (Q173). Pi isn't run (#383).
|
||||
|
||||
## OLD, read 19:38Z from `stack-release.sh status mosaic-stack` (read only)
|
||||
|
||||
- Copy 4429a104f6d65ccf165f6c83b5d1dce3147dfde0, schema 3i, pm true, activation 20261020T220206Z-4429a104f6d6.
|
||||
- Console flags: `--port 7332 --public-host mosaic.woltje.com --auth-header X-authentik-username
|
||||
--allow-user jason.woltje --tester-port 7333`. P1 re-reads these on the day.
|
||||
- Check pass, unit flags match the pointer, 123 commits of drift.
|
||||
|
||||
## Schema
|
||||
|
||||
`git diff --name-only 4429a104f origin/refactor` shows only `packages/bus/schema.sql` and
|
||||
`migrations/3i-3j.sql` (row 234). Nothing since c771a36f4 adds a step, so the migration is still 3i to 3j.
|
||||
Recheck this at the cut. Row 206 (3k-3l) hasn't landed.
|
||||
|
||||
## Rows NEW carries after c771a36f4 (first-parent, code paths)
|
||||
|
||||
| Row | Commit | Live effect to check |
|
||||
|---|---|---|
|
||||
| 240 | c515273db | Installer I4: root host check, pins, boot unit. This window doesn't run it. |
|
||||
| 228 | 86249ac55 | ZAI_API_KEY joins the session env allowlist. The live config sets no ZAI key, so the env is unchanged. Check in P3. |
|
||||
| 238 | d49b1f2bd | sso-config takes `--instance` on named instances. Default instance, `--sso` off, so there's no effect. |
|
||||
| 212 | ee7b2f137 | New package packages/library. The live business has no library entry. |
|
||||
| 195 | b65014647 | scripts/case1-accept.sh, root only. Not run. |
|
||||
| 213 | 361ba2387 | Creative roles (roles/companion.*), persona file and block, launch-settings, bundle.mjs. Live roles are chief, pm, coder and reviewer. P3: the chief's and pm's bundles are unchanged apart from the new code path, and one pm reply. |
|
||||
| 231 | 29ce1e360 | Library reader fixes. No live library. |
|
||||
| 235 | b1fece6f0 | Machine grant row b: raise binding, executor, reconcile, revoke and sweep (broker, host-grant, credentials, runtime). The live business has no hosts. P3: bus start runs the grant reconcile and sweep, and they change nothing. Show the log lines. |
|
||||
| 221 | 300766450 | Persona import script, with a count-only dry run. Not run. yaml 2.9 joins package-lock, so P3 runs npm ci on the copy. |
|
||||
| 247 | 856c1826b | Persona import follow-up. Not run. |
|
||||
| 245 | 6d5545d44 | case1-accept F9 follow-up. Not run. |
|
||||
| 248 | 8a04f4aa4 | Test stubs only. |
|
||||
| 249 | 9de5a57ab | broker: hostLineLeft on a grant whose install never returned. No grants live. The A22b look-alike count in draft 0 already covers `hostLineLeft`. |
|
||||
| 197 | a36523713 | Test lane fixture. No live effect. |
|
||||
| 66 | 2c38325ec | Managed sessions run the unslop check, runner.mjs, and stack-copy.sh COPY_PATHS adds tools/unslop-hook. P3 checks that the copy has tools/unslop-hook. The live check is above. |
|
||||
| 243 | 6096b051b | Tests only. |
|
||||
| 215 | e54e05463 | Library entry kind, consumer skips, M9, bare mirror, library.file and library.status. The live business has no library entry. M9 refuses any organization write that changes `projects`, so P3 checks that the chief's or pm's usual organization writes don't touch projects. |
|
||||
| 250 | 1b6158ce2 | Conversation shim and K12 test. No live effect on its own. |
|
||||
| 222 | 0b33f5e9d | Fetch-only library sync: library.sync bus verb, a 60 s timer per library entry with a sync user, the askpass helper, and stack-copy.sh COPY_PATHS. The live business has no library entry, so the timer starts nothing. P3: the copy has packages/library/bin/askpass with its mode, and bus start logs no `library:` line. |
|
||||
| 196 | 7247d80c7 | Human boundary: SO_PEERCRED on the bus socket, the interpreter receipt in bus/peercred-starts.jsonl, and the Console's private tester socket in place of `--tester-port`. The section "If 196 lands before the cut" applies. P3: bus start writes a receipt line, and the Console starts with `--tester-socket`. |
|
||||
|
||||
Added 2026-10-21T20:30Z. NEW at this hour is 0b33f5e9d (row 222, landed 20:13Z), the latest code
|
||||
landing. PRD 1.9 (f48725cfc) and 1.10 are doc and test changes, but `packages/tasks/requirements.json`
|
||||
is in the copy, so `task.create` takes REQ-WEB-2 and REQ-INST-2 as ids after the re-pin. P3: requirements
|
||||
returns 70 ids at 1.10. 196 r6 (527b9a1e6, on f48725cfc) is in its gate, so the section below may apply.
|
||||
|
||||
Added 2026-10-21T21:02Z. 196 landed at 7247d80c7 (20:57Z), and c3d88840c after it is doc-only. NEW at
|
||||
this hour is 7247d80c7, so the section below applies.
|
||||
|
||||
## If 196 lands before the cut
|
||||
|
||||
196 retires the Console's `--tester-port` (webui cli.mjs:30, "activate explicitly with --tester-socket").
|
||||
The live Console runs `--tester-port 7333`, so a NEW that carries 196 changes the Console's command line.
|
||||
Draft 1 would then need:
|
||||
- the activate line with `--tester-socket` and its path;
|
||||
- the P2 unit diff showing only that change;
|
||||
- a P3 rehearsal that starts the Console with it.
|
||||
|
||||
bus.tester stays Jason's. The tester path changes how the test client reaches the Console, but not who can
|
||||
reach it from outside, so it stays Q173 work. I confirm that with Mos in draft 1. If 196 hasn't landed, NEW
|
||||
leaves it out and none of this applies.
|
||||
|
||||
mos-1198 item 4: until row 253 (#1758, case2-unit.sh and case1-accept.sh to `--tester-socket`) lands, no
|
||||
Case 1 or Case 2 host (.53, .54) takes a copy that carries 196. This window is dragon-lin only, so it isn't
|
||||
blocked, but any .53 or .54 step that names NEW waits on 253.
|
||||
|
||||
Row 250 landed 1b6158ce2 at 19:42Z (conversation shim and K12 test). It has no live effect on its own, but
|
||||
it is now the latest code landing if the cut were made today.
|
||||
|
||||
## Still open from draft 0
|
||||
|
||||
- The P-step evidence for rows 199, 210 and now 235 at bus start, from a P3 rehearsal run.
|
||||
- Recount the restore-guard table list from NEW (T46).
|
||||
- Q3 operator: Darkwing if free.
|
||||
@@ -0,0 +1,168 @@
|
||||
# 10-24 window: re-pin 4429a104f to 7247d80c7 (rows 190, 199, 210, 234 and on, row 196 last), with 3i-3j (draft 1)
|
||||
|
||||
Sage, 2026-10-21, draft 1, for mos-1145 under DECISIONS Q173. Mos checks this file at its commit before P0.
|
||||
Any change to this file or to NEW after that check means Mos checks it again (mos-0849/0850). The step layout
|
||||
follows `repin-4429a104f.md`. Where a step is unchanged, I name it and say what differs. Draft 0 is
|
||||
`repin-1024-draft0.md`, and the working notes are in `repin-1024-draft1-prep.md`.
|
||||
|
||||
## Who, and what nobody does
|
||||
|
||||
- Q3 below names the operator: Darkwing if they're free, otherwise me. Mos verifies each step's output before
|
||||
the next one, and runs `rehearse` (P3).
|
||||
- Jason has no step. Q173 covers a re-pin of this test system with a migration, a backup first and a STATE
|
||||
report after. Nothing is pruned.
|
||||
- Nobody acts as Jason. No `--by jason`, no human shell or tmux pane, and every bus read and write carries
|
||||
`--tester`. `bus.tester` stays as it is. If it isn't `true`, stop at P1.
|
||||
- No pi (P19 #383). No Save in the human Console, no `mosaic name`.
|
||||
- Outside reach doesn't change. There's no `--sso` and no `--send`. Both go on together in the Q175 phase 2
|
||||
window, which runs after this window is verified, when Jason starts it (mos-1195, mos-1197). The proof pair
|
||||
stays unset. The tunnel still forwards 7332 only (P4 and W10).
|
||||
- One Console flag does change. Row 196 retires the tester's TCP port. The live Console runs
|
||||
`--tester-port 7333`, and NEW refuses that flag. So W5 converts it explicitly with `--tester-socket`, and
|
||||
the tester listener becomes the 0600 socket `$DATA/console/tester.sock`. Only 127.0.0.1 could reach port
|
||||
7333, so nobody outside gains or loses reach. Mos ruled this Q173 work in mos-1211 item 2. The socket is
|
||||
narrower than loopback TCP, and Mos's acceptance in mos-1015 (on sage-1359) already accepted losing
|
||||
browser access to 7333. DECISIONS has no Jason ruling on 7333 (mos-1214 item 2).
|
||||
- Under mos-1198, .53 and .54 take no copy carrying 196 until row 253 lands. This window is dragon-lin only.
|
||||
|
||||
## Names
|
||||
|
||||
- OLD, live: `4429a104f6d65ccf165f6c83b5d1dce3147dfde0`, schema 3i, pointer `pm: true`, activation
|
||||
`20261020T220206Z-4429a104f6d6`. The Console runs `--port 7332 --public-host mosaic.woltje.com --auth-header
|
||||
X-authentik-username --allow-user jason.woltje --tester-port 7333`, read from `status` at 19:38Z. P1
|
||||
re-reads all of these on the day. I don't copy them from this file.
|
||||
- NEW: `7247d80c7af7ffa70fc61557406bf491cdf3bfc7`, the row 196 landing, the latest code landing when this
|
||||
draft was cut (mos-1161). It is 135 commits past OLD (`git rev-list --count 4429a104f..7247d80c7`). The commit
|
||||
after it, c3d88840c, is doc-only and doesn't move NEW. If 244 or 251 land before P0, NEW stays here unless
|
||||
Mos rules otherwise (Q1).
|
||||
- 194 hasn't landed, so P3 has no lifecycle step (mos-1161).
|
||||
- `REPO`, `P`, `WIN=agents/sage/work/window-repin-7247d80c7/` and `SNAP` are as in `repin-4429a104f.md`, with
|
||||
the sha checks at P0 and before S1, S2 and any D3.
|
||||
|
||||
## What NEW carries over OLD
|
||||
|
||||
This is draft 0's table, rows 201 through 234 plus PRD 1.7 and 1.8, unchanged, together with these rows from
|
||||
`git log --first-parent c771a36f4..7247d80c7`, limited to packages, scripts, roles, contracts, adapters,
|
||||
skills, tools, migrations and the package files:
|
||||
|
||||
| Row | Commit | Live effect to check |
|
||||
|---|---|---|
|
||||
| 240 | c515273db | Installer I4: root host check, pins and the boot unit. This window doesn't run them. |
|
||||
| 228 | 86249ac55 | ZAI_API_KEY joins the session env allowlist. The live config sets no ZAI key, so the env doesn't change. P3 checks this. |
|
||||
| 238 | d49b1f2bd | sso-config takes `--instance`. Default instance with `--sso` off, so no effect. |
|
||||
| 212 | ee7b2f137 | New package, packages/library. The live business has no library entry. |
|
||||
| 195 | b65014647 | scripts/case1-accept.sh, root only. Not run. |
|
||||
| 213 | 361ba2387 | Creative roles, persona file and block, launch-settings, bundle.mjs. P3 checks that the chief's and the pm's bundles are unchanged apart from the new code path, and gets one pm reply. |
|
||||
| 231 | 29ce1e360 | Library reader fixes. No live library. |
|
||||
| 235 | b1fece6f0 | Machine grant row b. The live business has no hosts. P3: bus start runs the grant reconcile and sweep, they change nothing, and the log lines are shown. |
|
||||
| 221, 247 | 300766450, 856c1826b | Persona import script and follow-up. Not run. yaml 2.9 joins package-lock, and W0's npm ci covers it. |
|
||||
| 245, 248, 249 | 6d5545d44, 8a04f4aa4, 9de5a57ab | case1-accept F9, test stubs, and `hostLineLeft` on a grant whose install never returned. No grants are live. The look-alike count below covers `hostLineLeft`. |
|
||||
| 197 | a36523713 | Test lane fixture. No live effect. |
|
||||
| 66 | 2c38325ec | Managed sessions run the unslop check. COPY_PATHS adds tools/unslop-hook, and P3 checks the copy has it. W12 is the live check. |
|
||||
| 243 | 6096b051b | Tests only. |
|
||||
| 215 | e54e05463 | Library entry kind, consumer skips, M9 and the bare mirror. M9 refuses an organization write that changes `projects`, so P3 checks that the chief's and the pm's usual organization writes don't touch it. |
|
||||
| 250 | 1b6158ce2 | Conversation shim and K12 test. No live effect on its own. |
|
||||
| 222 | 0b33f5e9d | library.sync verb, a 60 s timer per library entry, and the askpass helper in COPY_PATHS. No library entry, so the timer starts nothing. P3: the copy has `packages/library/bin/askpass` with its mode, and bus start logs no `library:` line. |
|
||||
| PRD 1.9, 1.10 | f48725cfc, add0f1aae | `packages/tasks/requirements.json` is in the copy. W9: the pm's requirements read answers version 1.10 with 70 ids. |
|
||||
| 196 | 7247d80c7 | The human boundary. See the next section. |
|
||||
|
||||
## Row 196 in this window
|
||||
|
||||
What the code does (`scripts/stack-release.sh:791-845`, `docs/TOOLS.md` "Console tester listener"):
|
||||
|
||||
- An inherited `--tester-port` with no `--tester-socket` refuses `tester-transport-migration` before anything
|
||||
stops. So `--tester-socket` goes on W3 and W5, and the plan's drop line must read
|
||||
`--tester-port 7333 (replaced by private Unix socket)`.
|
||||
- Before the stop, activate checks the socket path: `$DATA/console/tester.sock` must fit 107 bytes, and
|
||||
`console/` must be absent or a directory with no symlink, owned by the instance's uid, with no group or
|
||||
other bits. A failure refuses `tester-socket-path` before the stop. P1 reads `ls -ld $DATA/console`
|
||||
(expected absent).
|
||||
- Activate preflights the pinned root-owned system Python, records its identity, and checks the bus's fresh
|
||||
start receipt. Each bus start appends one line to `$DATA/bus/peercred-starts.jsonl`. P3's rehearsal must show
|
||||
the preflight passing on dragon-lin, and W9 reads the one new receipt line.
|
||||
- `rehearse` starts NEW's Console with `--tester-socket` under its scratch data root when the copy carries
|
||||
the socket marker (`stack-release.sh:2153-2155`). It doesn't run activate's path check from line 815, so
|
||||
that check first runs live at W3. P1's `ls -ld $DATA/console` plus the path length are its only preview.
|
||||
If the rehearsal Console fails to start on the socket, P3 stops and the log goes to Mos.
|
||||
- Direct browser access to the tester is gone, with Mos's acceptance in mos-1015. W10's browser check on
|
||||
127.0.0.1:7333 becomes `node packages/cli/src/tester-http.mjs GET /api/config` and `GET /api/bus/agents`, run from
|
||||
the NEW copy against the live data root. Each must answer 200 with the same tester flags OLD's 7333 gave.
|
||||
`mosaic ... --tester` still uses broker.sock.
|
||||
|
||||
Rollback is where 196 matters most. Without `--sso`, rollback takes the Console flags from the pointer, and
|
||||
after W5 the pointer carries `--tester-socket`. OLD's Console doesn't support the socket. So rollback to OLD
|
||||
drops the tester listener, the plan prints `--tester-socket (tester disabled; no TCP fallback)`, and port 7333
|
||||
doesn't come back. The test client then has no tester listener until a new activate. Q4 asks Mos how to
|
||||
handle that.
|
||||
|
||||
## Schema and backups
|
||||
|
||||
- `SCHEMA_VERSION` is 3i at OLD and 3j at NEW (`packages/bus/src/store.mjs:17`). The only step is
|
||||
`packages/bus/src/migrations/3i-3j.sql` (row 234, tables `hosts` and `host_grants`). Nothing after c771a36f4
|
||||
changes schema.sql or adds a step. `--plan` must say `migration: 3i -> 3j`.
|
||||
- Restore guard, recounted at NEW (T46, `packages/cli/tests/stack-release.test.mjs:913`). COUNTED is the
|
||||
same 11 tables (`stack-release.sh:404`). The untimed list has six: `host_grants`, `hosts`, `meta`,
|
||||
`message_threads`, `sqlite_sequence` and `task_comment_cursors`. Each write to `hosts` or `host_grants` goes
|
||||
with a `grant.*` event, which is counted. A D3 snapshot still covers them, as draft 0 says.
|
||||
- W5 takes the stopped store backup every activate takes (SHA256SUMS, SCHEMA 3i, STAMP) plus the rollback
|
||||
set. Before `--migrate` writes, I check that SCHEMA reads 3i and record the backup's SHA256SUMS hash. W0b
|
||||
copies the business file, as last time.
|
||||
- Grant look-alikes (row 234 A22b, gizmoduck-0107): P3, and again just before W5, on the backup copy. Count the
|
||||
`action.allowed` events whose body.action or body.operation starts with `grant.`, or whose body has
|
||||
`hostLineLeft`. Expect 0. Any nonzero count stops the window until I've read those rows.
|
||||
- Rollback after the migration isn't the plain rollback. OLD is 3i and the store is 3j, so going back needs
|
||||
`--restore <W5 activation>`, which loses the events written after W5. Mos decides any restore past W5.
|
||||
|
||||
## Before the window (nothing stopped)
|
||||
|
||||
| # | Who | Step |
|
||||
|---|-----|------|
|
||||
| P0 | Mos | The `$P` sha check, and this file's commit. |
|
||||
| P1 | Sage | `status`: pointer, activation, schema 3i, Console flags, unit flags match. The business file sha256 and `business-check.mjs` LOAD_OK for OLD now and NEW after W0. `ls -ld $DATA/console` (expected absent). `bus.tester` is `true`. |
|
||||
| W0 | Sage | `scripts/stack-copy.sh copy $NEW` in a gap between gate runs. The package-lock diff is empty. The copy has `tools/unslop-hook` and `packages/library/bin/askpass`, with the mode NEW's tree gives it. |
|
||||
| P2 | Sage | `console-check.sh $NEW` on a stub Console on a scratch port, once without and once with `--tester-socket` under a scratch data root. CONSOLE_PASS both. Render the Console unit for NEW from REPO's `webui-service.sh` and diff it against the live unit. The only difference allowed is `--tester-port 7333` becoming `--tester-socket`. Diff the bus unit too. It must match apart from the copy path. |
|
||||
| P3 | Mos | `stack-release.sh rehearse $NEW mosaic-stack --backup <latest live activation> --port <scratch>` with `--migrate` on the backup copy. It must show the 3j step, row counts before and after, the interpreter preflight passing, a receipt line, the grant reconcile and sweep changing nothing, no `library:` line, no publish or land decision open (rows 241, 242), the look-alike count 0, and one pm reply. As in mos-1107, Mos also starts NEW's Console by hand with the pointer's flags, with `--tester-socket` in place of `--tester-port`, and reads healthz, `/api/config` and the two tester-http reads. |
|
||||
| P4 | Sage | The tunnel read. |
|
||||
| P5 | Sage | The four service scripts are held at NEW (`git diff --quiet $NEW HEAD --` on them, and empty porcelain). If 244 or 251 lands and touches one of them, P5 fails and the window waits for Mos. |
|
||||
| P6 | Sage | The 7332 matrix (a)-(g) on OLD, saved as `$WIN/matrix-old.txt`, plus the two tester reads on 7333 with curl, for W10 to compare against. |
|
||||
|
||||
## The window
|
||||
|
||||
| # | Who | Step |
|
||||
|---|-----|------|
|
||||
| W0b | Sage | The business file copy. |
|
||||
| W1 | Sage | Idle check, the start stamp from `date -u`, then S1 with `$SNAP --held`. Open pm tasks from row 42: pm finishes or parks them before W5, or I record which piece crosses the window. |
|
||||
| W2 | Sage | `status`: pointer on OLD, `pm: true`, 3i, unit flags match. |
|
||||
| W3 | Sage | `stack-release.sh activate $NEW mosaic-stack --pm --migrate --tester-socket --plan`. It must show OLD to NEW, `migration: 3i -> 3j`, the backup line, the drop line for `--tester-port 7333`, and a Console line with OLD's other flags exactly: no `--send`, no `--sso`, no proof pair. Any other difference stops here. |
|
||||
| W4 | Sage | The unit reads, as in the 177-185 plan. |
|
||||
| W5 | Sage, on Mos's go | The same command without `--plan`. Exit 0, `result ok`, and every gate check passes, `pm_launched` included. Record the activation id, the backup path, STAMP and the backup SHA256SUMS sha256. Then `status`. |
|
||||
| W9 | Sage, after Mos verifies W5 | The 177-185 W9 list, plus: the webui journal since the W5 stamp (with `UTC`) has no `Sign-in:` line; `/proc/<Console pid>/cwd` names the NEW copy; `$DATA/console/tester.sock` is 0600 in a 0700 `console/`; `peercred-starts.jsonl` gained one line for this start; the bus journal has the grant reconcile and sweep lines and no `library:` line; the pm's requirements read gives 1.10 with 70 ids. |
|
||||
| W10 | Sage | The matrix again, as `$WIN/matrix-new.txt`, identical to `matrix-old.txt` apart from the lines the pin changes, each one named. The two tester reads through `tester-http.mjs` match P6's. Port 7333 has no listener (`ss -ltn`). Then P4 again. A write without a signed-in session must still be refused. That's Console POST through the main port, with no session. This is the check mos-1195 asks for before phase 2 is done, so I run it here too, as a baseline. |
|
||||
| W11 | Sage | STATE report in `$WIN/STATE.md`, posted on P19 386, 412 and 435 (J2, mos-1215), and on the Gitea issues for 190, 199, 210, 234 and 196. |
|
||||
| W12 | Sage, after W11 | Row 66's live check (Vikunja #454). One Claude Code managed session with a prompt that draws a known tell, then the operator note on the RESULT, the next-turn notice and the `unslop.jsonl` counts. Backup first (Q173). Pi isn't run (#383). |
|
||||
|
||||
## Stop conditions and rollback
|
||||
|
||||
The same as the 177-185 plan. Two failures in one window: stop and report (mos-0823).
|
||||
|
||||
Rollback before W5 writes is `rollback --plan`, then the real run. After the migration it needs `--restore
|
||||
<W5 activation>`, with Mos deciding. Both drop the tester listener (see Row 196 above). Keep every copy.
|
||||
Prune nothing.
|
||||
|
||||
## Questions for the plan check, answered (mos-1214 item 3)
|
||||
|
||||
- Q1: NEW stays 7247d80c7 unless a landing fixes something this window needs.
|
||||
- Q2: mos-1211 item 2 rules the `--tester-port 7333` to `--tester-socket` change Q173 work.
|
||||
- Q3: Darkwing operates if free, and I check. Otherwise I operate.
|
||||
- Q4: after a rollback, the tester listener stays off for the window's length, and the tester goes through
|
||||
broker.sock. A same-copy re-activation of OLD with `--tester-port 7333` goes to Mos only if a rollback
|
||||
holds past the window day.
|
||||
|
||||
## What Sage ran for this plan
|
||||
|
||||
Read only, 2026-10-21 between 20:40Z and 21:20Z: the path-limited first-parent log c771a36f4..7247d80c7,
|
||||
`git rev-list --count`, `SCHEMA_VERSION` at both ends, `git diff --name-only` on the schema and migrations,
|
||||
`stack-release.sh` lines 40-47, 125-158, 404, 791-845, 886-919 and 2140-2157, T46 in
|
||||
`stack-release.test.mjs:913`, `packages/cli/src/tester-http.mjs`, and TOOLS.md:1274-1310 on the tester
|
||||
listener. Nothing ran against
|
||||
live. OLD's values come from the 19:38Z `status` read, and P1 reads them again.
|
||||
@@ -0,0 +1,2 @@
|
||||
1ba3c7efbc84a1d7eb8ee0068d32b7917f4e75cdc4066703944cea4c20db5b93 console-stub.mjs
|
||||
93e7f9740cd330c4f6fdb6fc4fe35e3a4fc1d606322debb4e514ed6033365d8f console-stub-tester.mjs
|
||||
@@ -0,0 +1,25 @@
|
||||
// Re-pin 7247d80c7 P2, second run: console-stub.mjs's loopback listener plus NEW's tester listener on a
|
||||
// private socket under a scratch data root, as cli.mjs starts it for --tester-socket. Usage: COPY DATAROOT.
|
||||
// DATAROOT must be a fresh scratch directory, never ~/.mosaic-dev. projectSave, send and every tester
|
||||
// write record a call; the loopback POST must reach none of them.
|
||||
const [copy, dataRoot] = process.argv.slice(2);
|
||||
if (!dataRoot || dataRoot.includes('.mosaic-dev')) { console.error('usage: COPY SCRATCH_DATAROOT'); process.exit(2); }
|
||||
const { startServer } = await import(`${copy}/packages/webui/src/serve.mjs`);
|
||||
const { startTesterSocket } = await import(`${copy}/packages/webui/src/tester-socket.mjs`);
|
||||
const H = 'a'.repeat(40), calls = [];
|
||||
const files = async () => ({ head: H, files: [{ path: 'docs/README.md' }, { path: 'spec/north-star.md' }] });
|
||||
const file = async ({ path }) => ({ path, content: 'stub', commit: H, head: H });
|
||||
const record = name => async () => { calls.push(name); return { ok: true }; };
|
||||
const server = await startServer({ port: 0, board: 'http://127.0.0.1:9', log: () => {},
|
||||
preview: { host: 'mosaic.example', header: 'x-test-user', user: 'tester' },
|
||||
send: async () => { calls.push('send'); return { id: 1 }; },
|
||||
projectFiles: files, projectFile: file,
|
||||
projectSave: async () => { calls.push('save'); return { path: 'x', commit: H }; } });
|
||||
const tester = await startTesterSocket({ dataRoot, board: 'http://127.0.0.1:9', log: () => {},
|
||||
testerNoSignIn: false, testerProofHeader: null, testerAuthHeader: 'x-test-user',
|
||||
bus: { names: async () => ({}), instances: async () => ({}), routes: async () => ({}) },
|
||||
projectFiles: files, projectFile: file,
|
||||
instancesWrite: record('instances.set'), routesWrite: record('routes.set'), nameWrite: record('name.set') });
|
||||
console.log(`PORT ${server.address().port}`);
|
||||
console.log(`SOCK ${tester.address()}`);
|
||||
process.on('SIGTERM', () => { console.log(`CALLS ${JSON.stringify(calls)}`); server.close(); tester.close(() => process.exit(0)); });
|
||||
@@ -0,0 +1,13 @@
|
||||
// Positive control for console-check.sh: NEW's startServer with stub bus reads, on a free loopback port.
|
||||
// projectSave and send record a call; the loopback POST must reach neither.
|
||||
const [copy] = process.argv.slice(2);
|
||||
const { startServer } = await import(`${copy}/packages/webui/src/serve.mjs`);
|
||||
const H = 'a'.repeat(40), calls = [];
|
||||
const server = await startServer({ port: 0, board: 'http://127.0.0.1:9', log: () => {},
|
||||
preview: { host: 'mosaic.example', header: 'x-test-user', user: 'tester' },
|
||||
send: async () => { calls.push('send'); return { id: 1 }; },
|
||||
projectFiles: async () => ({ head: H, files: [{ path: 'docs/README.md' }, { path: 'spec/north-star.md' }] }),
|
||||
projectFile: async ({ path }) => ({ path, content: 'stub', commit: H, head: H }),
|
||||
projectSave: async () => { calls.push('save'); return { path: 'x', commit: H }; } });
|
||||
console.log(`PORT ${server.address().port}`);
|
||||
process.on('SIGTERM', () => { console.log(`CALLS ${JSON.stringify(calls)}`); process.exit(0); });
|
||||
@@ -0,0 +1,3 @@
|
||||
chief r097436c91816 claude-code since 2026-10-20T22:02:08.951Z
|
||||
pm rc9e993da24cf claude-code since 2026-10-20T22:40:47.969Z
|
||||
# exit 0
|
||||
@@ -0,0 +1,6 @@
|
||||
ActiveState=active
|
||||
DropInPaths=
|
||||
MainPID=721027
|
||||
ExecStart={ path=/home/jwoltje/.local/share/mosaic-stack/copies/7247d80c7af7ffa70fc61557406bf491cdf3bfc7/scripts/mosaic ; argv[]=/home/jwoltje/.local/share/mosaic-stack/copies/7247d80c7af7ffa70fc61557406bf491cdf3bfc7/scripts/mosaic bus start mosaic-stack --pm ; ignore_errors=no ; start_time=[Wed 2026-10-21 16:59:28 CDT] ; stop_time=[n/a] ; pid=721027 ; code=(null) ; status=0/0 }
|
||||
WorkingDirectory=/home/jwoltje/.local/share/mosaic-stack/copies/7247d80c7af7ffa70fc61557406bf491cdf3bfc7
|
||||
# exit 0
|
||||
@@ -0,0 +1,13 @@
|
||||
● jarvis-curation-gate.service loaded failed failed Jarvis curation-gate liveness check (wake the agent if staged pulls sit uncurated)
|
||||
● jarvis-retro-check.service loaded failed failed Jarvis weekly retrospective ARTIFACT check (did Sunday's tick produce a retro?)
|
||||
● jarvis-retro.service loaded failed failed Jarvis weekly retrospective tick
|
||||
● mosaic-install-drift.service loaded failed failed Assert the installed Mosaic binaries still match the committed source (drift, not failure)
|
||||
● mosaic-reconcile.service loaded failed failed intended-state reconciler (read-only divergence detection, 30-min cadence)
|
||||
● mosaic-standup-pm.service loaded failed failed Coordinator standup trigger (evening)
|
||||
● [email protected] loaded failed failed Escalate failed Mosaic unit jarvis-curation-gate.service to the local agent seat
|
||||
● [email protected] loaded failed failed Escalate failed Mosaic unit jarvis-retro-check.service to the local agent seat
|
||||
● [email protected] loaded failed failed Escalate failed Mosaic unit jarvis-retro.service to the local agent seat
|
||||
● [email protected] loaded failed failed Escalate failed Mosaic unit mosaic-install-drift.service to the local agent seat
|
||||
● [email protected] loaded failed failed Escalate failed Mosaic unit mosaic-reconcile.service to the local agent seat
|
||||
● setspark-tmp-guard.service loaded failed failed SetSpark: /tmp and scratch guard
|
||||
# exit 0
|
||||
@@ -0,0 +1,5 @@
|
||||
ActiveState=active
|
||||
SubState=running
|
||||
ActiveEnterTimestamp=Tue 2026-10-13 08:40:19 CDT
|
||||
MainPID=1317
|
||||
# exit 0
|
||||
@@ -0,0 +1,6 @@
|
||||
ActiveState=active
|
||||
DropInPaths=
|
||||
MainPID=721029
|
||||
ExecStart={ path=/usr/bin/node ; argv[]=/usr/bin/node /home/jwoltje/.local/share/mosaic-stack/copies/7247d80c7af7ffa70fc61557406bf491cdf3bfc7/packages/webui/src/cli.mjs serve --checkout /mnt/storage/src/mosaic-stack --port 7332 --public-host mosaic.woltje.com --auth-header X-authentik-username --allow-user jason.woltje --tester-socket ; ignore_errors=no ; start_time=[Wed 2026-10-21 16:59:28 CDT] ; stop_time=[n/a] ; pid=721029 ; code=(null) ; status=0/0 }
|
||||
WorkingDirectory=/home/jwoltje/.local/share/mosaic-stack/copies/7247d80c7af7ffa70fc61557406bf491cdf3bfc7
|
||||
# exit 0
|
||||
@@ -0,0 +1,4 @@
|
||||
sent 722f6eac to chief
|
||||
chief (RESULT, re 722f6eac) ced93621:
|
||||
I didn't launch a pm. Your message says it authorizes nothing, and I only launch on a bus notice, a staff request from the pm, or a cto merge message, so there's no pm run to name.
|
||||
# exit 0
|
||||
@@ -0,0 +1,6 @@
|
||||
{"ts":1792620126036,"ev":"checked","clean":true,"entryId":"","charsChecked":114}
|
||||
{"ts":1792621178375,"ev":"checked","clean":true,"entryId":"","charsChecked":180}
|
||||
{"ts":1792621498421,"ev":"checked","clean":true,"entryId":"","charsChecked":333}
|
||||
{"ts":1792621627986,"ev":"flagged","clean":false,"entryId":"","charsChecked":93,"findings":[{"rule":"word","source":"ms-unslop:7","detail":"banned word \"additionally\" x1","count":1},{"rule":"word","source":"ms-unslop:7","detail":"banned word \"crucial\" x1","count":1},{"rule":"word","source":"ms-unslop:7","detail":"banned word \"testament\" x1","count":1},{"rule":"word","source":"ms-unslop:7","detail":"banned word \"vibrant\" x1","count":1}]}
|
||||
{"ts":1792621630921,"ev":"notice_injected","entryId":"284c6414-1b70-4209-9870-19805fff823c","findings":[{"rule":"word","source":"ms-unslop:7","detail":"banned word \"additionally\" x1","count":1},{"rule":"word","source":"ms-unslop:7","detail":"banned word \"crucial\" x1","count":1},{"rule":"word","source":"ms-unslop:7","detail":"banned word \"testament\" x1","count":1},{"rule":"word","source":"ms-unslop:7","detail":"banned word \"vibrant\" x1","count":1}]}
|
||||
{"ts":1792621632110,"ev":"checked","clean":true,"entryId":"","charsChecked":8}
|
||||
@@ -0,0 +1 @@
|
||||
{"checked":5,"flagged":1,"patterns":{"ms-unslop:7":4},"notices":1,"broken":null}
|
||||
@@ -0,0 +1,2 @@
|
||||
r7646a7a4a2bd mosaic-stack/chief claude-code claude-opus-5-5 by jason since 2026-10-21T21:59:29.867Z running
|
||||
# exit 0
|
||||
@@ -0,0 +1,6 @@
|
||||
sent 06dda327 to chief
|
||||
chief (RESULT, re 06dda327) e16b31e9:
|
||||
Additionally, it is crucial that this sentence stand as a testament to a vibrant style check.
|
||||
|
||||
[unslop: 1 reply of this session flagged since the last note: banned word "additionally" x1; banned word "crucial" x1; banned word "testament" x1; banned word "vibrant" x1. The session gets a correction notice on its next turn.]
|
||||
# exit 0
|
||||
@@ -0,0 +1,4 @@
|
||||
sent 4699f80a to chief
|
||||
chief (RESULT, re 4699f80a) 9bde6703:
|
||||
received
|
||||
# exit 0
|
||||
@@ -0,0 +1,4 @@
|
||||
sent d4cfb554 to chief
|
||||
chief (RESULT, re d4cfb554) 966c5828:
|
||||
I sent your request to the pm as message ed1063ea-d046-4d64-b077-0d895beee2d7, but no pm session is running, so the message is waiting unread. I haven't launched one, for the same reason as before: your message authorizes nothing, so there's nothing that lets me start a pm session. It will be read the next time a pm session starts.
|
||||
# exit 0
|
||||
@@ -0,0 +1,2 @@
|
||||
chief r7646a7a4a2bd claude-code since 2026-10-21T21:59:29.874Z
|
||||
# exit 0
|
||||
@@ -0,0 +1,7 @@
|
||||
PASS markdown-preview.js is served and equals the copy's
|
||||
PASS /api/config on loopback: send=false edit=false
|
||||
PASS project.files through the Console: head=ae0b17c9586e51da7bc1b7e548c6acf913a55665 files=2 spec=true error=none
|
||||
PASS project.file spec/north-star.md through the Console: commit=ae0b17c9586e51da7bc1b7e548c6acf913a55665 length=99 error=none
|
||||
PASS loopback save refused: HTTP 403 error=read-only sent=false
|
||||
CONSOLE_PASS
|
||||
# exit 0
|
||||
@@ -0,0 +1,19 @@
|
||||
[
|
||||
{
|
||||
"business": "mosaic-stack",
|
||||
"instance": "chief",
|
||||
"run": "r7646a7a4a2bd",
|
||||
"launchedBy": "jason",
|
||||
"harness": "claude-code",
|
||||
"model": "claude-opus-5-5",
|
||||
"provider": "anthropic",
|
||||
"modelKey": "opus",
|
||||
"pid": 721383,
|
||||
"startTime": "72116004",
|
||||
"runnerPid": 721384,
|
||||
"runnerStartTime": "72116004",
|
||||
"runDir": "/home/jwoltje/.mosaic-dev/launches/mosaic-stack/r7646a7a4a2bd",
|
||||
"startedAt": "2026-10-21T21:59:29.867Z",
|
||||
"live": true
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,2 @@
|
||||
r7646a7a4a2bd mosaic-stack/chief claude-code claude-opus-5-5 by jason since 2026-10-21T21:59:29.867Z running
|
||||
# exit 0
|
||||
@@ -0,0 +1,2 @@
|
||||
[]
|
||||
# exit 0
|
||||
@@ -0,0 +1,2 @@
|
||||
created project window-20261021-2202 id aa7b4e88-2958-4a0e-a28c-63d0286d1e4c
|
||||
# exit 0
|
||||
@@ -0,0 +1 @@
|
||||
1792620136
|
||||
@@ -0,0 +1,21 @@
|
||||
pointer: /home/jwoltje/.mosaic-dev/state/stack-active.json
|
||||
{"copy":"7247d80c7af7ffa70fc61557406bf491cdf3bfc7","business":"mosaic-stack","pm":true,"claude":"/home/jwoltje/.local/bin/claude","console":["--port","7332","--public-host","mosaic.woltje.com","--auth-header","X-authentik-username","--allow-user","jason.woltje","--tester-socket"],"schema":"3j","activation":"20261021T215926Z-7247d80c7af7","at":"2026-10-21T21:59:31Z"}
|
||||
log (last five lines):
|
||||
{"at":"2026-10-20T19:30:00Z","op":"activate","from":"490e2771d3d057ab76991e2c7859cd251857e64c","to":"490e2771d3d057ab76991e2c7859cd251857e64c","backup":"20261020T192956Z-490e2771d3d0","activation":"20261020T192956Z-490e2771d3d0","result":"ok"}
|
||||
{"at":"2026-10-20T22:02:06Z","op":"activate","from":"490e2771d3d057ab76991e2c7859cd251857e64c","to":"4429a104f6d65ccf165f6c83b5d1dce3147dfde0","backup":null,"activation":"20261020T220206Z-4429a104f6d6","result":"started"}
|
||||
{"at":"2026-10-20T22:02:09Z","op":"activate","from":"490e2771d3d057ab76991e2c7859cd251857e64c","to":"4429a104f6d65ccf165f6c83b5d1dce3147dfde0","backup":"20261020T220206Z-4429a104f6d6","activation":"20261020T220206Z-4429a104f6d6","result":"ok"}
|
||||
{"at":"2026-10-21T21:59:26Z","op":"activate","from":"4429a104f6d65ccf165f6c83b5d1dce3147dfde0","to":"7247d80c7af7ffa70fc61557406bf491cdf3bfc7","backup":null,"activation":"20261021T215926Z-7247d80c7af7","result":"started","interpreter":"{\"path\":\"/usr/bin/python3.14\",\"version\":[3,14,6],\"dev\":66308,\"ino\":3968163,\"sha256\":\"8735f172622a7f8d27e293fd0fd36fc55c4cb9b663b6630d33b57870e36c24cc\"}"}
|
||||
{"at":"2026-10-21T21:59:31Z","op":"activate","from":"4429a104f6d65ccf165f6c83b5d1dce3147dfde0","to":"7247d80c7af7ffa70fc61557406bf491cdf3bfc7","backup":"20261021T215926Z-7247d80c7af7","activation":"20261021T215926Z-7247d80c7af7","result":"ok"}
|
||||
check:
|
||||
bus: /home/jwoltje/.local/share/mosaic-stack/copies/7247d80c7af7ffa70fc61557406bf491cdf3bfc7
|
||||
Console: /home/jwoltje/.local/share/mosaic-stack/copies/7247d80c7af7ffa70fc61557406bf491cdf3bfc7
|
||||
CLI: /home/jwoltje/.local/share/mosaic-stack/copies/7247d80c7af7ffa70fc61557406bf491cdf3bfc7
|
||||
result: pass
|
||||
bus status:
|
||||
host: mosaic-stack, pid 721027, running, since 2026-10-21T21:59:28.399Z, notifier off
|
||||
socket: present
|
||||
writer.lock: pid 721071
|
||||
boot: organization file loaded at 2026-10-21T21:59:28.397Z
|
||||
unit flags: match the pointer
|
||||
drift: 14 commit(s) on refactor since the pointer's copy
|
||||
# exit 0
|
||||
@@ -0,0 +1,4 @@
|
||||
sent b095e39b to chief
|
||||
chief (RESULT, re b095e39b) ca9b2db2:
|
||||
Tester message received: no action taken, nothing launched, re-pin 7247d80c7af7ffa70fc61557406bf491cdf3bfc7 noted.
|
||||
# exit 0
|
||||
@@ -0,0 +1,2 @@
|
||||
mosaic: pm isn't running, so the tester's message was refused and nothing was stored
|
||||
# exit 3
|
||||
@@ -0,0 +1,4 @@
|
||||
sent dd4f8410 to window-20261021-2202/w9
|
||||
assistant (RESULT, re dd4f8410) 4d488a27:
|
||||
Assistant here: I got your mos test message, and I'm taking no action because it authorizes nothing.
|
||||
# exit 0
|
||||
@@ -0,0 +1,32 @@
|
||||
[
|
||||
{
|
||||
"seq": 594,
|
||||
"id": "dd4f8410-e48c-48c0-a234-3c629b652b7e",
|
||||
"at": "2026-10-21T22:02:09.754Z",
|
||||
"business": "mosaic-stack",
|
||||
"from_role": "tester",
|
||||
"from_run": "tester-cli",
|
||||
"to_role": "assistant",
|
||||
"class": "REQUEST",
|
||||
"in_reply_to": null,
|
||||
"decision": null,
|
||||
"corrects": null,
|
||||
"body": "[mos test] reply with one line, then stop",
|
||||
"to_name": null
|
||||
},
|
||||
{
|
||||
"seq": 595,
|
||||
"id": "4d488a27-1092-4c3e-88dd-fac85593d4f7",
|
||||
"at": "2026-10-21T22:02:15.988Z",
|
||||
"business": "mosaic-stack",
|
||||
"from_role": "assistant",
|
||||
"from_run": "r4004e5e09433",
|
||||
"to_role": "tester",
|
||||
"class": "RESULT",
|
||||
"in_reply_to": "dd4f8410-e48c-48c0-a234-3c629b652b7e",
|
||||
"decision": null,
|
||||
"corrects": null,
|
||||
"body": "Assistant here: I got your mos test message, and I'm taking no action because it authorizes nothing.",
|
||||
"to_name": null
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,2 @@
|
||||
created window-20261021-2202/w9 id b89fcca4-6b82-48bd-9310-fb82c5ae8518 instance assistant
|
||||
# exit 0
|
||||
@@ -0,0 +1,4 @@
|
||||
window-20261020-0801/w9 assistant d0ba7a66
|
||||
window-20261020-2205/w9 assistant 446201de
|
||||
window-20261021-2202/w9 assistant b89fcca4
|
||||
# exit 0
|
||||
@@ -0,0 +1 @@
|
||||
b89fcca4-6b82-48bd-9310-fb82c5ae8518
|
||||
@@ -0,0 +1,189 @@
|
||||
{
|
||||
"total": {
|
||||
"input": 110,
|
||||
"output": 18895,
|
||||
"cacheWrite": 320507,
|
||||
"cacheRead": 981437,
|
||||
"unknownTurns": 1
|
||||
},
|
||||
"byRun": [
|
||||
{
|
||||
"run": "r8fba7e598156",
|
||||
"input": 0,
|
||||
"output": 0,
|
||||
"cacheWrite": 0,
|
||||
"cacheRead": 0,
|
||||
"unknownTurns": 1
|
||||
},
|
||||
{
|
||||
"run": "r4a96e2142e4d",
|
||||
"input": 2,
|
||||
"output": 52,
|
||||
"cacheWrite": 11750,
|
||||
"cacheRead": 0,
|
||||
"unknownTurns": 0
|
||||
},
|
||||
{
|
||||
"run": "rc78c9700c8e9",
|
||||
"input": 14,
|
||||
"output": 1519,
|
||||
"cacheWrite": 18187,
|
||||
"cacheRead": 89559,
|
||||
"unknownTurns": 0
|
||||
},
|
||||
{
|
||||
"run": "r293f33bcbc63",
|
||||
"input": 2,
|
||||
"output": 44,
|
||||
"cacheWrite": 18595,
|
||||
"cacheRead": 0,
|
||||
"unknownTurns": 0
|
||||
},
|
||||
{
|
||||
"run": "r34520a581318",
|
||||
"input": 6,
|
||||
"output": 323,
|
||||
"cacheWrite": 26349,
|
||||
"cacheRead": 14307,
|
||||
"unknownTurns": 0
|
||||
},
|
||||
{
|
||||
"run": "r7e99838f9f93",
|
||||
"input": 2,
|
||||
"output": 44,
|
||||
"cacheWrite": 9858,
|
||||
"cacheRead": 0,
|
||||
"unknownTurns": 0
|
||||
},
|
||||
{
|
||||
"run": "rc964611b2092",
|
||||
"input": 4,
|
||||
"output": 120,
|
||||
"cacheWrite": 26138,
|
||||
"cacheRead": 23870,
|
||||
"unknownTurns": 0
|
||||
},
|
||||
{
|
||||
"run": "rbb708846b605",
|
||||
"input": 12,
|
||||
"output": 2089,
|
||||
"cacheWrite": 17285,
|
||||
"cacheRead": 73823,
|
||||
"unknownTurns": 0
|
||||
},
|
||||
{
|
||||
"run": "rc36b17ef3788",
|
||||
"input": 16,
|
||||
"output": 5221,
|
||||
"cacheWrite": 36717,
|
||||
"cacheRead": 202573,
|
||||
"unknownTurns": 0
|
||||
},
|
||||
{
|
||||
"run": "r097436c91816",
|
||||
"input": 16,
|
||||
"output": 3340,
|
||||
"cacheWrite": 43923,
|
||||
"cacheRead": 102022,
|
||||
"unknownTurns": 0
|
||||
},
|
||||
{
|
||||
"run": "r497f1c817d4d",
|
||||
"input": 2,
|
||||
"output": 80,
|
||||
"cacheWrite": 9856,
|
||||
"cacheRead": 0,
|
||||
"unknownTurns": 0
|
||||
},
|
||||
{
|
||||
"run": "rc9e993da24cf",
|
||||
"input": 30,
|
||||
"output": 5871,
|
||||
"cacheWrite": 80018,
|
||||
"cacheRead": 475283,
|
||||
"unknownTurns": 0
|
||||
},
|
||||
{
|
||||
"run": "r7646a7a4a2bd",
|
||||
"input": 2,
|
||||
"output": 131,
|
||||
"cacheWrite": 11957,
|
||||
"cacheRead": 0,
|
||||
"unknownTurns": 0
|
||||
},
|
||||
{
|
||||
"run": "r4004e5e09433",
|
||||
"input": 2,
|
||||
"output": 61,
|
||||
"cacheWrite": 9874,
|
||||
"cacheRead": 0,
|
||||
"unknownTurns": 0
|
||||
}
|
||||
],
|
||||
"byInstance": [
|
||||
{
|
||||
"instance": "chief",
|
||||
"input": 52,
|
||||
"output": 7454,
|
||||
"cacheWrite": 129451,
|
||||
"cacheRead": 279711,
|
||||
"unknownTurns": 1
|
||||
},
|
||||
{
|
||||
"instance": "coder",
|
||||
"input": 2,
|
||||
"output": 44,
|
||||
"cacheWrite": 18595,
|
||||
"cacheRead": 0,
|
||||
"unknownTurns": 0
|
||||
},
|
||||
{
|
||||
"instance": "assistant",
|
||||
"input": 6,
|
||||
"output": 185,
|
||||
"cacheWrite": 29588,
|
||||
"cacheRead": 0,
|
||||
"unknownTurns": 0
|
||||
},
|
||||
{
|
||||
"instance": "pm",
|
||||
"input": 50,
|
||||
"output": 11212,
|
||||
"cacheWrite": 142873,
|
||||
"cacheRead": 701726,
|
||||
"unknownTurns": 0
|
||||
}
|
||||
],
|
||||
"byProvider": [
|
||||
{
|
||||
"provider": "anthropic",
|
||||
"input": 110,
|
||||
"output": 18895,
|
||||
"cacheWrite": 320507,
|
||||
"cacheRead": 981437,
|
||||
"unknownTurns": 1
|
||||
}
|
||||
],
|
||||
"byModel": [
|
||||
{
|
||||
"provider": "anthropic",
|
||||
"model": "claude-opus-5-5",
|
||||
"input": 110,
|
||||
"output": 18895,
|
||||
"cacheWrite": 320507,
|
||||
"cacheRead": 981437,
|
||||
"unknownTurns": 1
|
||||
}
|
||||
],
|
||||
"byAccount": [
|
||||
{
|
||||
"account": null,
|
||||
"input": 110,
|
||||
"output": 18895,
|
||||
"cacheWrite": 320507,
|
||||
"cacheRead": 981437,
|
||||
"unknownTurns": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
# exit 0
|
||||
@@ -0,0 +1,2 @@
|
||||
r7646a7a4a2bd mosaic-stack/chief claude-code claude-opus-5-5 by jason since 2026-10-21T21:59:29.867Z running
|
||||
# exit 0
|
||||
@@ -0,0 +1,19 @@
|
||||
[
|
||||
{
|
||||
"business": "mosaic-stack",
|
||||
"instance": "chief",
|
||||
"run": "r7646a7a4a2bd",
|
||||
"launchedBy": "jason",
|
||||
"harness": "claude-code",
|
||||
"model": "claude-opus-5-5",
|
||||
"provider": "anthropic",
|
||||
"modelKey": "opus",
|
||||
"pid": 721383,
|
||||
"startTime": "72116004",
|
||||
"runnerPid": 721384,
|
||||
"runnerStartTime": "72116004",
|
||||
"runDir": "/home/jwoltje/.mosaic-dev/launches/mosaic-stack/r7646a7a4a2bd",
|
||||
"startedAt": "2026-10-21T21:59:29.867Z",
|
||||
"live": true
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,65 @@
|
||||
# P1 start 2026-10-21T21:39:52.221Z, REPO HEAD 111ec559c8fa969691f4358d26e1de1c1e6ef32f
|
||||
# plan blob 0c5e1f1c6271f301d680edbc720432e51cddb071
|
||||
# business-check.mjs sha256 eece4d4671e1ce15ba81e1654c641925ac4988b343687b062897392285a8a1a7; SCRIPTS.sha256 line: eece4d4671e1ce15ba81e1654c641925ac4988b343687b062897392285a8a1a7 business-check.mjs
|
||||
# business file sha256 1e49bdf05e62607972958f5b7298cfd600c7825812370e26d7c2a842f2af9b93, mtime 2026-10-19 20:39:58.506635141 -0500
|
||||
|
||||
## P1 status
|
||||
# start 2026-10-21T21:39:52.232Z
|
||||
# command: (cd $REPO && <clean env> scripts/stack-release.sh status mosaic-stack)
|
||||
pointer: /home/jwoltje/.mosaic-dev/state/stack-active.json
|
||||
{"copy":"4429a104f6d65ccf165f6c83b5d1dce3147dfde0","business":"mosaic-stack","pm":true,"claude":"/home/jwoltje/.local/bin/claude","console":["--port","7332","--public-host","mosaic.woltje.com","--auth-header","X-authentik-username","--allow-user","jason.woltje","--tester-port","7333"],"schema":"3i","activation":"20261020T220206Z-4429a104f6d6","at":"2026-10-20T22:02:09Z"}
|
||||
log (last five lines):
|
||||
{"at":"2026-10-20T07:58:02Z","op":"activate","from":"89af38a83afa22a8a4f0d368a0a4030ad15a0efa","to":"490e2771d3d057ab76991e2c7859cd251857e64c","backup":"20261020T075800Z-490e2771d3d0","activation":"20261020T075800Z-490e2771d3d0","result":"ok"}
|
||||
{"at":"2026-10-20T19:29:56Z","op":"activate","from":"490e2771d3d057ab76991e2c7859cd251857e64c","to":"490e2771d3d057ab76991e2c7859cd251857e64c","backup":null,"activation":"20261020T192956Z-490e2771d3d0","result":"started"}
|
||||
{"at":"2026-10-20T19:30:00Z","op":"activate","from":"490e2771d3d057ab76991e2c7859cd251857e64c","to":"490e2771d3d057ab76991e2c7859cd251857e64c","backup":"20261020T192956Z-490e2771d3d0","activation":"20261020T192956Z-490e2771d3d0","result":"ok"}
|
||||
{"at":"2026-10-20T22:02:06Z","op":"activate","from":"490e2771d3d057ab76991e2c7859cd251857e64c","to":"4429a104f6d65ccf165f6c83b5d1dce3147dfde0","backup":null,"activation":"20261020T220206Z-4429a104f6d6","result":"started"}
|
||||
{"at":"2026-10-20T22:02:09Z","op":"activate","from":"490e2771d3d057ab76991e2c7859cd251857e64c","to":"4429a104f6d65ccf165f6c83b5d1dce3147dfde0","backup":"20261020T220206Z-4429a104f6d6","activation":"20261020T220206Z-4429a104f6d6","result":"ok"}
|
||||
check:
|
||||
bus: /home/jwoltje/.local/share/mosaic-stack/copies/4429a104f6d65ccf165f6c83b5d1dce3147dfde0
|
||||
Console: /home/jwoltje/.local/share/mosaic-stack/copies/4429a104f6d65ccf165f6c83b5d1dce3147dfde0
|
||||
CLI: /home/jwoltje/.local/share/mosaic-stack/copies/4429a104f6d65ccf165f6c83b5d1dce3147dfde0
|
||||
result: pass
|
||||
bus status:
|
||||
host: mosaic-stack, pid 2950923, running, since 2026-10-20T22:02:08.133Z, notifier off
|
||||
socket: present
|
||||
writer.lock: pid 2950981
|
||||
boot: organization file loaded at 2026-10-20T22:02:08.125Z
|
||||
unit flags: match the pointer
|
||||
drift: 144 commit(s) on refactor since the pointer's copy
|
||||
# exit 0, end 2026-10-21T21:39:52.532Z
|
||||
|
||||
## P1 values
|
||||
# start 2026-10-21T21:39:52.534Z
|
||||
# command: (cd $REPO && <clean env> node /mnt/storage/src/mosaic-stack/agents/sage/work/window-repin-7247d80c7/p1.mjs)
|
||||
bus.tester true
|
||||
plugins kinds ["channel","harness","usage-source"]
|
||||
plugins usage-source ["transcript"]
|
||||
plugins channel ["cli","webui"]
|
||||
plugins harness ["claude-code"]
|
||||
launch.by "chief"
|
||||
launch.instances includes assistant true
|
||||
role assistant vars.harness "claude-code"
|
||||
role chief vars.harness "claude-code"
|
||||
role coder vars.harness "claude-code"
|
||||
role cto vars.harness "claude-code"
|
||||
role pm vars.harness "claude-code"
|
||||
role reviewer vars.harness "claude-code"
|
||||
roles naming pi 0
|
||||
# exit 0, end 2026-10-21T21:39:52.558Z
|
||||
|
||||
## P1 business-check OLD
|
||||
# start 2026-10-21T21:39:52.582Z
|
||||
# command: (cd $REPO && <clean env> node /mnt/storage/src/mosaic-stack/agents/sage/work/window-plan/repin-de352dfaf/business-check.mjs 4429a104f6d65ccf165f6c83b5d1dce3147dfde0)
|
||||
copy 4429a104f: roles 6, projects 1, arbiters 2
|
||||
HOLDS chief instance.name yes
|
||||
HOLDS pm instance.name no
|
||||
LOAD_OK
|
||||
# exit 0, end 2026-10-21T21:39:52.625Z
|
||||
|
||||
## P1 data console
|
||||
# dataRoot /home/jwoltje/.mosaic-dev
|
||||
# ls -ld $DATA/console:
|
||||
ls: cannot access '/home/jwoltje/.mosaic-dev/console': No such file or directory
|
||||
# ls rc=2
|
||||
# tester.sock path length 45 bytes (limit 107)
|
||||
# P1 end 2026-10-21T21:39:52.650Z
|
||||
@@ -0,0 +1,174 @@
|
||||
{
|
||||
"schema": "3i",
|
||||
"tables": {
|
||||
"decision_events": {
|
||||
"rows": 22,
|
||||
"maxSeq": 22,
|
||||
"sha256": "b016d2a7cb0116252166016822c472af1cc520435a6538ecb7be5db62c6cb4b3"
|
||||
},
|
||||
"decisions": {
|
||||
"rows": 22,
|
||||
"maxSeq": 22,
|
||||
"sha256": "eafcd57d3531f7ac7c768e512632a3975df372c49ffd95abe7d858fd05ce93a8"
|
||||
},
|
||||
"deliveries": {
|
||||
"rows": 1797,
|
||||
"maxSeq": 1797,
|
||||
"sha256": "c152f315d262be6d95ed72e3b1380b814fe02846bf43cb6c670005e1e99411b6"
|
||||
},
|
||||
"events": {
|
||||
"rows": 1678,
|
||||
"maxSeq": 1678,
|
||||
"sha256": "b0f95d910e3cf9245b389290c364bb7d6194cdc63f6954de7619d09401ee5b99"
|
||||
},
|
||||
"instance_names": {
|
||||
"rows": 0,
|
||||
"maxSeq": null,
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
},
|
||||
"message_threads": {
|
||||
"rows": 4,
|
||||
"maxSeq": null
|
||||
},
|
||||
"messages": {
|
||||
"rows": 591,
|
||||
"maxSeq": 591
|
||||
},
|
||||
"meta": {
|
||||
"rows": 3,
|
||||
"maxSeq": null,
|
||||
"sha256": "89fec56e7293d19e0b2f6cd8c0ef745ebf99e4115abeaea7fbdbc98a261332d9"
|
||||
},
|
||||
"projects": {
|
||||
"rows": 2,
|
||||
"maxSeq": 2,
|
||||
"sha256": "3435f574ab94bc2fcc65fb8605251f30d4e9e63baf38e6fdf3c39c126531aa63"
|
||||
},
|
||||
"role_claims": {
|
||||
"rows": 102,
|
||||
"maxSeq": 102,
|
||||
"sha256": "8d9d55d41954626fb1f6bc8972a118f0117c6d86836f117cc4f85e675bdce5c9"
|
||||
},
|
||||
"task_comment_cursors": {
|
||||
"rows": 310,
|
||||
"maxSeq": 310,
|
||||
"sha256": "2b5d58aab2873619724f018163ca773d25063065c9d2e379de4f2759db837d40"
|
||||
},
|
||||
"task_snapshots": {
|
||||
"rows": 436,
|
||||
"maxSeq": 436,
|
||||
"sha256": "7980e1dd9651cfbf44c2d376db6099c1c593e6389f56eaaf16dadde9da7aa1b5"
|
||||
},
|
||||
"thread_claims": {
|
||||
"rows": 4,
|
||||
"maxSeq": 4,
|
||||
"sha256": "088b8236570d8afe03364b85803fdc5034cce2cef6ed7b16f8b99bf6366524ad"
|
||||
},
|
||||
"thread_opens": {
|
||||
"rows": 0,
|
||||
"maxSeq": null,
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
},
|
||||
"threads": {
|
||||
"rows": 2,
|
||||
"maxSeq": 2,
|
||||
"sha256": "819fd24c64e279d0debad5ab5dbbe533d8e6f26de58277c69510b9cbbaadbc3a"
|
||||
},
|
||||
"usage_records": {
|
||||
"rows": 56,
|
||||
"maxSeq": 56,
|
||||
"sha256": "7a2b4b5b67e504cf17815ca4d7c452ef0e98da03cc354b196ce298f3e9059525",
|
||||
"byRun": [
|
||||
{
|
||||
"business": "mosaic-stack",
|
||||
"run": "r8fba7e598156",
|
||||
"input": "0",
|
||||
"output": "0",
|
||||
"unknownTurns": 1
|
||||
},
|
||||
{
|
||||
"business": "mosaic-stack",
|
||||
"run": "r4a96e2142e4d",
|
||||
"input": "2",
|
||||
"output": "52",
|
||||
"unknownTurns": 0
|
||||
},
|
||||
{
|
||||
"business": "mosaic-stack",
|
||||
"run": "rc78c9700c8e9",
|
||||
"input": "14",
|
||||
"output": "1519",
|
||||
"unknownTurns": 0
|
||||
},
|
||||
{
|
||||
"business": "mosaic-stack",
|
||||
"run": "r293f33bcbc63",
|
||||
"input": "2",
|
||||
"output": "44",
|
||||
"unknownTurns": 0
|
||||
},
|
||||
{
|
||||
"business": "mosaic-stack",
|
||||
"run": "r34520a581318",
|
||||
"input": "6",
|
||||
"output": "323",
|
||||
"unknownTurns": 0
|
||||
},
|
||||
{
|
||||
"business": "mosaic-stack",
|
||||
"run": "r7e99838f9f93",
|
||||
"input": "2",
|
||||
"output": "44",
|
||||
"unknownTurns": 0
|
||||
},
|
||||
{
|
||||
"business": "mosaic-stack",
|
||||
"run": "rc964611b2092",
|
||||
"input": "4",
|
||||
"output": "120",
|
||||
"unknownTurns": 0
|
||||
},
|
||||
{
|
||||
"business": "mosaic-stack",
|
||||
"run": "rbb708846b605",
|
||||
"input": "12",
|
||||
"output": "2089",
|
||||
"unknownTurns": 0
|
||||
},
|
||||
{
|
||||
"business": "mosaic-stack",
|
||||
"run": "rc36b17ef3788",
|
||||
"input": "16",
|
||||
"output": "5221",
|
||||
"unknownTurns": 0
|
||||
},
|
||||
{
|
||||
"business": "mosaic-stack",
|
||||
"run": "r097436c91816",
|
||||
"input": "16",
|
||||
"output": "3340",
|
||||
"unknownTurns": 0
|
||||
},
|
||||
{
|
||||
"business": "mosaic-stack",
|
||||
"run": "r497f1c817d4d",
|
||||
"input": "2",
|
||||
"output": "80",
|
||||
"unknownTurns": 0
|
||||
},
|
||||
{
|
||||
"business": "mosaic-stack",
|
||||
"run": "rc9e993da24cf",
|
||||
"input": "30",
|
||||
"output": "5871",
|
||||
"unknownTurns": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"untimed": [
|
||||
"message_threads",
|
||||
"meta",
|
||||
"task_comment_cursors"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
{
|
||||
"schema": "3j",
|
||||
"tables": {
|
||||
"decision_events": {
|
||||
"rows": 22,
|
||||
"maxSeq": 22,
|
||||
"sha256": "b016d2a7cb0116252166016822c472af1cc520435a6538ecb7be5db62c6cb4b3"
|
||||
},
|
||||
"decisions": {
|
||||
"rows": 22,
|
||||
"maxSeq": 22,
|
||||
"sha256": "eafcd57d3531f7ac7c768e512632a3975df372c49ffd95abe7d858fd05ce93a8"
|
||||
},
|
||||
"deliveries": {
|
||||
"rows": 1812,
|
||||
"maxSeq": 1812,
|
||||
"sha256": "105cd22c375a628ffa019c29234be92fdca061b786a68c57041f9277cbcbfdce"
|
||||
},
|
||||
"events": {
|
||||
"rows": 1695,
|
||||
"maxSeq": 1695,
|
||||
"sha256": "3b3732100150c521f5f82249d9f63c6d96cf0349194dd756dab362362767ffdb"
|
||||
},
|
||||
"host_grants": {
|
||||
"rows": 0,
|
||||
"maxSeq": null,
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
},
|
||||
"hosts": {
|
||||
"rows": 0,
|
||||
"maxSeq": null,
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
},
|
||||
"instance_names": {
|
||||
"rows": 0,
|
||||
"maxSeq": null,
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
},
|
||||
"message_threads": {
|
||||
"rows": 6,
|
||||
"maxSeq": null
|
||||
},
|
||||
"messages": {
|
||||
"rows": 595,
|
||||
"maxSeq": 595
|
||||
},
|
||||
"meta": {
|
||||
"rows": 3,
|
||||
"maxSeq": null,
|
||||
"sha256": "3d1adf63d62c70add4a5f0b10cbcd4c8641417aca02915c40a64b30016168398"
|
||||
},
|
||||
"projects": {
|
||||
"rows": 3,
|
||||
"maxSeq": 3,
|
||||
"sha256": "3bc6becbc42cd204bb2be152fba2c1dfb169cef93abdd0874cfc0e5ac832f5c9"
|
||||
},
|
||||
"role_claims": {
|
||||
"rows": 105,
|
||||
"maxSeq": 105,
|
||||
"sha256": "9a9f322ef58434321f59687caf586946cde7f55c8dfc1496c8bf2d05c8b3574c"
|
||||
},
|
||||
"task_comment_cursors": {
|
||||
"rows": 316,
|
||||
"maxSeq": 316,
|
||||
"sha256": "55de22707d7689049ba9f61db59f02aae682a7f8dc224dc6202de86f9f7a7a1c"
|
||||
},
|
||||
"task_snapshots": {
|
||||
"rows": 438,
|
||||
"maxSeq": 438,
|
||||
"sha256": "94e1a82aae791e5675fa4c2ab6284d4a6bdc21c52ed551223f6ca47a7d172899"
|
||||
},
|
||||
"thread_claims": {
|
||||
"rows": 6,
|
||||
"maxSeq": 6,
|
||||
"sha256": "f7cc643a8c9ba2407b1f338e404306c279e9e0a73426a493f1b7cdc0ce2c94ee"
|
||||
},
|
||||
"thread_opens": {
|
||||
"rows": 0,
|
||||
"maxSeq": null,
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
},
|
||||
"threads": {
|
||||
"rows": 3,
|
||||
"maxSeq": 3,
|
||||
"sha256": "909e67a34c142eb2df626bac1e7e85a46bfec307686ee2333f86ecfdbff49bdf"
|
||||
},
|
||||
"usage_records": {
|
||||
"rows": 60,
|
||||
"maxSeq": 60,
|
||||
"sha256": "50e645efe6383478ab77c3fb24276f189a2988c72d146b5b745197d74e0cbaa0",
|
||||
"byRun": [
|
||||
{
|
||||
"business": "mosaic-stack",
|
||||
"run": "r8fba7e598156",
|
||||
"input": "0",
|
||||
"output": "0",
|
||||
"unknownTurns": 1
|
||||
},
|
||||
{
|
||||
"business": "mosaic-stack",
|
||||
"run": "r4a96e2142e4d",
|
||||
"input": "2",
|
||||
"output": "52",
|
||||
"unknownTurns": 0
|
||||
},
|
||||
{
|
||||
"business": "mosaic-stack",
|
||||
"run": "rc78c9700c8e9",
|
||||
"input": "14",
|
||||
"output": "1519",
|
||||
"unknownTurns": 0
|
||||
},
|
||||
{
|
||||
"business": "mosaic-stack",
|
||||
"run": "r293f33bcbc63",
|
||||
"input": "2",
|
||||
"output": "44",
|
||||
"unknownTurns": 0
|
||||
},
|
||||
{
|
||||
"business": "mosaic-stack",
|
||||
"run": "r34520a581318",
|
||||
"input": "6",
|
||||
"output": "323",
|
||||
"unknownTurns": 0
|
||||
},
|
||||
{
|
||||
"business": "mosaic-stack",
|
||||
"run": "r7e99838f9f93",
|
||||
"input": "2",
|
||||
"output": "44",
|
||||
"unknownTurns": 0
|
||||
},
|
||||
{
|
||||
"business": "mosaic-stack",
|
||||
"run": "rc964611b2092",
|
||||
"input": "4",
|
||||
"output": "120",
|
||||
"unknownTurns": 0
|
||||
},
|
||||
{
|
||||
"business": "mosaic-stack",
|
||||
"run": "rbb708846b605",
|
||||
"input": "12",
|
||||
"output": "2089",
|
||||
"unknownTurns": 0
|
||||
},
|
||||
{
|
||||
"business": "mosaic-stack",
|
||||
"run": "rc36b17ef3788",
|
||||
"input": "16",
|
||||
"output": "5221",
|
||||
"unknownTurns": 0
|
||||
},
|
||||
{
|
||||
"business": "mosaic-stack",
|
||||
"run": "r097436c91816",
|
||||
"input": "16",
|
||||
"output": "3340",
|
||||
"unknownTurns": 0
|
||||
},
|
||||
{
|
||||
"business": "mosaic-stack",
|
||||
"run": "r497f1c817d4d",
|
||||
"input": "2",
|
||||
"output": "80",
|
||||
"unknownTurns": 0
|
||||
},
|
||||
{
|
||||
"business": "mosaic-stack",
|
||||
"run": "rc9e993da24cf",
|
||||
"input": "30",
|
||||
"output": "5871",
|
||||
"unknownTurns": 0
|
||||
},
|
||||
{
|
||||
"business": "mosaic-stack",
|
||||
"run": "r7646a7a4a2bd",
|
||||
"input": "2",
|
||||
"output": "131",
|
||||
"unknownTurns": 0
|
||||
},
|
||||
{
|
||||
"business": "mosaic-stack",
|
||||
"run": "r4004e5e09433",
|
||||
"input": "2",
|
||||
"output": "61",
|
||||
"unknownTurns": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"untimed": [
|
||||
"host_grants",
|
||||
"hosts",
|
||||
"message_threads",
|
||||
"meta",
|
||||
"task_comment_cursors"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
# Re-pin window 4429a104f to 7247d80c7 (rows 196, 222, 215, 250, 243 and 66), STATE report (W12)
|
||||
|
||||
Operator and tester: Sage. Plan: `agents/sage/work/window-plan/repin-1024-draft1.md`, sha256
|
||||
111f54ee75426ddc589e89db3015f32ab9f9ba2588bc32fba209f596dea5a27e, checked before W9 (plan blob 0c5e1f1c6271 at P1
|
||||
and W0b). Tool blob f0c144fe5219 (`scripts/stack-release.sh`). P5 found the four release scripts the same at HEAD
|
||||
and NEW. OLD 4429a104f6d65ccf165f6c83b5d1dce3147dfde0, NEW 7247d80c7af7ffa70fc61557406bf491cdf3bfc7. NEW is 135
|
||||
commits past OLD (`git rev-list --count 4429a104f..7247d80c7`). Every stamp below comes from `date -u` or from
|
||||
the transcript `W.txt` in this directory. Mos's gos: mos-1226 (W5), mos-1228 (W9 to W12), mos-1230 (W9 accepted,
|
||||
pm read moved to W12). Q173 covers every step. Mos's mos-1226 ruling overrides decision 96 condition 2 ("Jason
|
||||
present") for this run.
|
||||
|
||||
## Outcome
|
||||
|
||||
NEW is live for mosaic-stack. The store migrated from 3i to 3j, and the Console swapped its 127.0.0.1:7333
|
||||
tester listener for the private socket `~/.mosaic-dev/console/tester.sock`. W0 to W11 passed, and nothing was
|
||||
rolled back. P2 failed once on a script bug, before any live step, and passed on the rerun. That counts as the
|
||||
window's one failure under mos-0823. W12 passed (W12_PASS, row 66 on the chief). The pm requirements read is
|
||||
open: the chief, as designed, won't staff a pm on a tester request, and Mos ruled that this is not a window
|
||||
failure (mos-1234). The W12 section below has the detail.
|
||||
|
||||
Nobody acted as Jason. There was no `--by jason`, no pi, no Save and no `mosaic name`, and `bus.tester` was not
|
||||
touched. Writes through 7332 without a signed-in session are refused (401), which is the baseline mos-1195 asks
|
||||
for before phase 2.
|
||||
|
||||
## Stamps and activation
|
||||
|
||||
| Step | Start (UTC) | Result |
|
||||
| --- | --- | --- |
|
||||
| P1 status, values | 21:39:52Z (HEAD 111ec559c) | pointer OLD, pm true, 3i; business file 1e49bdf0 (`P1.txt`) |
|
||||
| P2 console-check on a stub of NEW, attempt 1 | 21:43:33Z | STOP: the stub path was wrong in `p2.sh`, no check ran (`p2-attempt1.txt`) |
|
||||
| P2 rerun | 21:43:57Z | CONSOLE_PASS without the tester and with `--tester-socket` under a scratch data root (`p2.txt`) |
|
||||
| P3 rehearsal (Mos) | 21:43:38Z to 21:43:42Z | REHEARSAL_PASS, rc 0; migration 3i to 3j adds `hosts` and `host_grants`, no other count moves (`p3/`) |
|
||||
| P3 Console by hand (Mos) | 21:45:23Z to 21:45:24Z | healthz 200, public Host 401, socket 600 in a 700 dir, two tester reads 200, writer.lock released |
|
||||
| P4, P5, P6 | 21:42:06Z | P4 PASS, P5 PASS, `matrix-old.txt` and `tester-old.txt` |
|
||||
| W0 copy NEW, W0b business file copy | 21:48:58Z | copy present, LOAD_OK; W0b sha256 1e49bdf0 |
|
||||
| W1 idle check, S1 | 21:48:58.108Z | chief r097436c91816 and pm rc9e993da24cf idle; S1 c8ad2600 |
|
||||
| W2 status | 21:48:58.494Z | pointer OLD, pm true, 3i, unit flags match, check pass |
|
||||
| W3 plan | 21:48:58.780Z | exit 0, migration 3i to 3j, 135 commits (`W3-plan.txt` 4b6d2421) |
|
||||
| W4 unit read | 21:48:59Z | OLD units, no drop-ins, bus `--pm`, no quote or backslash |
|
||||
| W5 activate --migrate --tester-socket (mos-1226) | 21:59:26Z to 21:59:31Z | exit 0, gate passed, activate ok (`W5.txt`) |
|
||||
| W5 status | after 21:59:31Z | pointer NEW, pm true, 3j, unit flags match, check pass (`W5-status.txt`) |
|
||||
| W9 checks and turns (mos-1228) | 22:02:00Z to 22:02:18Z | pass; pm read refused, see W9 |
|
||||
| W9b run end and S2 | 22:02:43Z, end 22:17:17.542Z, S2 22:17:21Z | ended by the launcher (thread-idle); S2 91d4a5b7 |
|
||||
| W10 | 22:18:51Z to 22:18:52Z | W10_PASS |
|
||||
| W11 reads | 22:19:14Z | W11_READS_OK |
|
||||
| W12 backup and staffing ask | 22:19:31Z | backup ok; chief declined to staff, see W12 |
|
||||
|
||||
Activation id 20261021T215926Z-7247d80c7af7. Pointer `at` 2026-10-21T21:59:31Z. W5 dropped `--tester-port 7333`
|
||||
from the Console flags because the target replaces it with the socket.
|
||||
|
||||
## Backups and hashes
|
||||
|
||||
- Store backup before the migration: `~/.mosaic-dev/backups/stack/20261021T215926Z-7247d80c7af7`, SHA256SUMS
|
||||
sha256 730ca3d0dd9c02e489d05307117820180dc83570c7fe252428841fbd7794bbe7. The stopped store was 3i.
|
||||
- Rollback set: `~/.mosaic-dev/state/stack-rollback/20261021T215926Z-7247d80c7af7`. Rollback goes to 4429a104f.
|
||||
A restore from that backup now loses every event written after 21:59:26Z (mos-1228), so any restore goes to
|
||||
Mos first.
|
||||
- W12 store backup (Q173, before the managed session): `~/.mosaic-dev/backups/w12-20261021T221931Z`, an online
|
||||
copy through sqlite's backup API with the source opened read-only. integrity_check ok, events 1699, maxSeq
|
||||
1699, schema 3j. SHA256SUMS sha256 a4ecd7ba8e24b9f586634619643d72cbef356db683459a41998f513c83cad27a.
|
||||
- Business file: 1e49bdf05e62607972958f5b7298cfd600c7825812370e26d7c2a842f2af9b93 at P1, at W0b and again at
|
||||
W11. The W0b copy is `~/.config/mosaic-dev/businesses/mosaic-stack.json.pre-window-20261021T214858Z` with the
|
||||
same hash. Nothing in the window wrote it. `backups.txt` holds the W0b, W5 and W12 lines.
|
||||
|
||||
## Pointer before and after
|
||||
|
||||
Before (W2): copy 4429a104f6d6, pm true, schema 3i, activation 20261020T220206Z-4429a104f6d6. Console flags
|
||||
`--port 7332 --public-host mosaic.woltje.com --auth-header X-authentik-username --allow-user jason.woltje
|
||||
--tester-port 7333`.
|
||||
|
||||
After (W5, W9 and W11): copy 7247d80c7af7, pm true, schema 3j, activation 20261021T215926Z-7247d80c7af7. Console
|
||||
flags `--port 7332 --public-host mosaic.woltje.com --auth-header X-authentik-username --allow-user jason.woltje
|
||||
--tester-socket`. No `--send`, no `--sso` and no proof pair. Bus pid 721027 since 21:59:28.399Z, Console pid
|
||||
721029. Status shows drift of 14 commits on refactor since the pointer's copy (records, briefs and PRD drafts).
|
||||
|
||||
## Unit ExecStart lines (no Environment values)
|
||||
|
||||
W11, after. Both units active, DropInPaths empty, WorkingDirectory the NEW copy:
|
||||
|
||||
```
|
||||
bus: ~/.local/share/mosaic-stack/copies/7247d80c7af7ffa70fc61557406bf491cdf3bfc7/scripts/mosaic bus start mosaic-stack --pm (MainPID 721027)
|
||||
webui: /usr/bin/node ~/.local/share/mosaic-stack/copies/7247d80c7af7ffa70fc61557406bf491cdf3bfc7/packages/webui/src/cli.mjs serve --checkout /mnt/storage/src/mosaic-stack --port 7332 --public-host mosaic.woltje.com --auth-header X-authentik-username --allow-user jason.woltje --tester-socket (MainPID 721029)
|
||||
```
|
||||
|
||||
W4 read the same two lines on OLD's copy. The bus Environment has 2 assignments with no quote or backslash. The
|
||||
values are not recorded.
|
||||
|
||||
Tunnel: `mosaic-preview-tunnel.service` active, running, MainPID 1317, since 2026-10-13 08:40:19 CDT. The window
|
||||
didn't touch it. P4 before and after shows one R: spec, `R:0.0.0.0:7332:127.0.0.1:7332`.
|
||||
|
||||
## S1, S2 and the store
|
||||
|
||||
S1 (`S1.json`, sha256 c8ad2600f5c0bc12a14b287246a39ea651a8e92bd4c7f3836cf6ee91c5af05fb) is W1's held read on 3i
|
||||
at 21:48:58Z. S2 (`S2.json`, sha256 91d4a5b7ad198b8120628e5688c3ac9fb5c221e370beec55245767d22756d629) is the held
|
||||
read on 3j after the assistant run ended, at 22:17:21Z. SNAP (`snapshot.mjs`, 639fd28a) was checked before each.
|
||||
|
||||
| Table | S1 rows | S2 rows | S2 vs S1 |
|
||||
| --- | --- | --- | --- |
|
||||
| decisions, decision_events | 22, 22 | 22, 22 | same sha256 |
|
||||
| instance_names, thread_opens | 0, 0 | 0, 0 | same sha256 |
|
||||
| meta | 3 | 3 | schema_version 3i to 3j, the digest moves with it |
|
||||
| hosts, host_grants | (no table) | 0, 0 | new in 3j |
|
||||
| deliveries | 1797 | 1812 | +15 |
|
||||
| events | 1678 | 1695 | +17 |
|
||||
| role_claims | 102 | 105 | +3 |
|
||||
| messages | 591 | 595 | +4 (counts only) |
|
||||
| message_threads | 4 | 6 | +2 (counts only) |
|
||||
| usage_records | 56 | 60 | +4 |
|
||||
| projects, threads, thread_claims | 2, 2, 4 | 3, 3, 6 | +1, +1, +2 |
|
||||
| task_snapshots | 436 | 438 | +2 |
|
||||
| task_comment_cursors | 310 | 316 | +6 |
|
||||
|
||||
For every grown table with a seq, maxSeq grew by exactly the row delta. The last 60 events by kind at S2:
|
||||
action.allowed 2, action.refused 2, session.launched 2, session.ended 3, task.changed.external 51. There is no
|
||||
grant.* event (mos-1225), and the bus journal since W5 has no `library:` line. task_snapshots and
|
||||
task_comment_cursors grow from the bus's own Vikunja poll, as the 4429a104f window traced.
|
||||
|
||||
## W9 output
|
||||
|
||||
- Status, console-check (CONSOLE_PASS, d068fe02), the journal counts (one "Proxy: proxy proof off", no
|
||||
"Sign-in:"), the Console cwd on the NEW copy, `console/` drwx------ and `tester.sock` srw-------, one
|
||||
peercred-starts line for this start, and one running chief from this start (r7646a7a4a2bd) all passed.
|
||||
- The pm crossing: rc9e993da24cf ended with the restart and is not listed. No pm launch is live. Mos recorded it
|
||||
as "pm not relaunched by the restart, expected, chief staffs on request" (mos-1230).
|
||||
- `talk chief --tester` (b095e39b): RESULT ca9b2db2, "no action taken, nothing launched".
|
||||
- Project `window-20261021-2202`, thread b89fcca4-6b82-48bd-9310-fb82c5ae8518. `talk --thread` (dd4f8410) got one
|
||||
assistant reply from run r4004e5e09433. `thread read` shows the tester REQUEST and the assistant RESULT.
|
||||
- `threads`, `names` (empty) and `usage` read fine.
|
||||
- The pm read, `talk pm --tester`, exited 3: "pm isn't running, so the tester's message was refused and nothing
|
||||
was stored". Mos moved it to W12.
|
||||
- W9b: the launcher logged launch at 22:02:12.746Z, thread-idle at 22:17:17.517Z and end at 22:17:17.542Z
|
||||
(reason stopped, exit 0). Nobody stopped it by hand.
|
||||
|
||||
## W10
|
||||
|
||||
- Matrix (`matrix-new.txt` against `matrix-old.txt`): lines a to e are byte-identical. Lines f and g were OLD's
|
||||
127.0.0.1:7333 listener (403 "proxy header refused" and the 6575-byte page). NEW has no such listener, so both
|
||||
are 000. Nothing listens on 7333.
|
||||
- Tester reads through NEW's `packages/cli/src/tester-http.mjs` on the socket. The `/api/config` line is
|
||||
identical to `tester-old.txt`: 200, 73 bytes, sha256 e7ef9a40a725f356, tester true, send false, edit false.
|
||||
`/api/bus/agents` is 200 with 1 row, 320 bytes. OLD gave 709 bytes with two rows, the chief and the pm.
|
||||
- P4 again at 22:18:52Z: PASS.
|
||||
- Unsigned writes through 7332 with Host mosaic.woltje.com and no session: `POST /api/seen` 401 and
|
||||
`POST /api/reply` 401, both "sign-in required".
|
||||
|
||||
## W12
|
||||
|
||||
Row 66's live check (Vikunja #454) and the pm requirements read, planned as one pm turn (mos-1230).
|
||||
|
||||
- 22:19:31Z: the store backup above.
|
||||
- `talk chief --tester` (722f6eac) asked the chief to staff one pm. RESULT ced93621: "I didn't launch a pm. Your
|
||||
message says it authorizes nothing, and I only launch on a bus notice, a staff request from the pm, or a cto
|
||||
merge message, so there's no pm run to name."
|
||||
- `w12.sh` polled `launches list --json` for 300 s and stopped with no pm launch.
|
||||
|
||||
There's no tester path to a pm. A tester message to a role nobody holds is refused and nothing is stored (row 170,
|
||||
#1667, `packages/cli/src/cli.mjs:388-391` at NEW), so the chief gets no bus notice. `bus start --pm`
|
||||
(`cli.mjs:510`) launches the business's launch.by, which is the chief. The chief's log `unslop.jsonl` reads checked
|
||||
2, flagged 0, notices 0 before any W12 turn.
|
||||
|
||||
Mos's ruling mos-1234 (22:21:58Z, re sage-2210): one relay try first, then row 66 on the chief, and the pm read
|
||||
is recorded open if no pm launches. The W12 backup covers both steps. `w12b.sh` ran them (22:24:48Z to
|
||||
22:27:13Z, exit 0):
|
||||
|
||||
- Checks first: the wrapper is on NEW, the W12 backup line is in `backups.txt`, and r7646a7a4a2bd is the one
|
||||
live claude-code chief. Baseline unslopCounts `{"checked":2,"flagged":0,"patterns":{},"notices":0,"broken":null}`.
|
||||
- Step 1: `talk chief --tester` (d4cfb554) asked the chief to relay a pm requirements read. RESULT 966c5828: the
|
||||
chief sent it to the pm as message ed1063ea-d046-4d64-b077-0d895beee2d7 and launched no pm ("your message
|
||||
authorizes nothing"). No pm launched within 120 s. The pm read is open (mos-1234 item 3). It runs at the
|
||||
first pm launch from a human message or a real bus notice, and ed1063ea waits unread for that pm. No more
|
||||
chief turns went to it, and nothing goes to Jason for it.
|
||||
- Step 2, row 66 (Vikunja #454): turn 1 (06dda327) asked for one sentence using four banned words. RESULT
|
||||
e16b31e9 carried the operator note `[unslop: 1 reply of this session flagged since the last note: banned word
|
||||
"additionally" x1; ... "vibrant" x1. The session gets a correction notice on its next turn.]`. Turn 2
|
||||
(4699f80a) got RESULT 9bde6703, "received".
|
||||
- unslopCounts from NEW's `runner.mjs` after the turns: `{"checked":5,"flagged":1,"patterns":{"ms-unslop:7":4},
|
||||
"notices":1,"broken":null}`. PASS needs checked >= 4, flagged >= 1, notices >= 1 and broken null over the
|
||||
baseline. W12_PASS.
|
||||
|
||||
The log copy is `.w12b-chief-unslop.jsonl`. `W.txt` holds its events with the text stripped.
|
||||
|
||||
## Failed user units
|
||||
|
||||
The same legacy units as the 4429a104f window, all under P19 #393, plus the `mosaic-unit-failed@` markers for
|
||||
them and `setspark-tmp-guard.service`, which belongs to another project. The list is in `.w11-failed.txt`. The
|
||||
window changed none of them.
|
||||
|
||||
## Corrections
|
||||
|
||||
- P2 attempt 1 stopped on a path bug in `p2.sh` before any check ran. The fixed script passed. That is this
|
||||
window's one counted failure (mos-0823).
|
||||
- `w9.sh` held the pm read. Mos moved it to W12 (mos-1230).
|
||||
- mos-1225 replaced the plan's grant reconcile and sweep journal lines with "no grant.* event added".
|
||||
- W12 as planned stopped with no pm (`w12.sh` exit 1). That was a missing route, not a check failure, so it isn't
|
||||
counted under mos-0823. Mos split the step (mos-1234), and `w12b.sh` ran the rest.
|
||||
|
||||
## Files
|
||||
|
||||
`W.txt` (transcript), `S1.json`, `S2.json`, `matrix-old.txt`, `matrix-new.txt`, `tester-old.txt`, `backups.txt`,
|
||||
`W3-plan.txt`, `W5.txt`, `W5-status.txt`, `W5-launched.txt`, `P1.txt`, `p2-attempt1.txt`, `p2.txt`,
|
||||
`p2-units.txt`, `p4.txt`, `p5.txt`, `p3/` (Mos's rehearsal and hand Console check), `p456.sh`, `w0.sh`, `w9.sh`,
|
||||
`w9b.sh`, `w10.sh`, `w11.sh`, `w12.sh`, `w12b.sh`, `w9b.out`, `w12.out`, `w12b.out`, and the `.w9*`, `.w11*`,
|
||||
`.w12*` and `.w12b*` reads. The
|
||||
store snapshot tool is `snapshot.mjs` (639fd28a).
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,153 @@
|
||||
plan: activate for mosaic-stack
|
||||
from: 4429a104f6d65ccf165f6c83b5d1dce3147dfde0
|
||||
to: 7247d80c7af7ffa70fc61557406bf491cdf3bfc7
|
||||
store schema: 3i (read from the store the running bus holds)
|
||||
target schema: 3j
|
||||
migration: 3i to 3j, after a backup of the stopped store
|
||||
migration steps: 3i to 3j
|
||||
needs: --migrate (given)
|
||||
needs: Jason present for this run (decision 96 condition 2)
|
||||
backup: the run backs up the stopped store to /home/jwoltje/.mosaic-dev/backups/stack/<activation>/ before any migration
|
||||
commits: the run adds 135 (newest first, at most 200):
|
||||
7247d80c7 row 196: human boundary on peer credentials, interpreter receipt and the Console tester socket (#1694)
|
||||
666173292 sessions: Sage row 222 landing, PRD 1.9 and 1.10, Pocket ID row 255, 196 r6 grants, overlap class rule (mos-1204)
|
||||
21def2850 queue: revs 2968-2976; Gander 196 r5 manifest and r6 changes move, Sage 222 review request and done, Duckworth 196 m1 changes record, rows 254 and 255 added
|
||||
add0f1aae PRD 1.10: REQ-INST-2, a bundled Pocket ID for users with no OIDC provider, post-v1 (Q203, mos-1204)
|
||||
7d08237e1 brief: bundled Pocket ID for users with no OIDC provider, design (Q203, Vikunja #458, mos-1202)
|
||||
f48725cfc PRD 1.9: REQ-WEB-2, Console decide buttons for a human the Console signed in, and the REQ-DEC-3 amendment (mos-1202)
|
||||
0de97f429 brief: library sync follow-up, client timeout, STATUS codes and README (row 222 deferrals, #1723)
|
||||
0b33f5e9d row 222: C2-03a fetch-only library sync, askpass helper, bus verb and timer (#1723)
|
||||
358695528 sessions: Sage row 250 landing, 196 r5 run 4 and resume, F27 row 253; Dewey 66 r4, 251 r1 and 222 r1 lines; Darkwing 251 line
|
||||
a54a17f3d queue: revs 2905-2967 (rows 66, 215, 243, 250 landings; 196, 222, 244, 250, 251 rounds; row 253 added)
|
||||
0cdead66c brief: F27, case2-unit.sh and case1-accept.sh move to --tester-socket, chained after row 196 (#1758)
|
||||
1b6158ce2 row 250: K12 holds the stop's release while it reads the shim, the release answers the events read's reason, and R13 closes its controller (#1755)
|
||||
081ff6b12 sessions: Sage row 215 landing, 196 r4 grant after the base change, slot order for 250, 222, 244 and 251; Herb row 215 line
|
||||
be86adbff sessions: Filbert row 250 round 2 static review, approve
|
||||
d163afc47 sessions: Filbert row 250 round 1 static review, changes
|
||||
e54e05463 row 215: C2-04 the library entry in the business file, consumer skips, M9 organization-projects-protected, the bare mirror, library.file and library.status (#1716)
|
||||
6096b051b row 243: tests that depend on the gate umask set their file modes; stack-instances bootstraps under an explicit umask with a 077 subtest (#1754)
|
||||
9a8c8860d sessions: Sage row 66 landing, J2 brief and REQ-WEB-2 wording, gate coordination for 196 r2, 215, 243, 250, 251
|
||||
6195a00f8 sessions: Filbert row 215 round 2 merge-scoped review, approve
|
||||
3b0aef995 deferred: row 66 live checks, Claude Code at the first re-pin with row 66 and pi after #383, Vikunja #454 (mos-1186)
|
||||
2c38325ec row 66: managed sessions run the unslop check on both harnesses, the operator note on the RESULT, a correction notice on the next turn, and unslop.jsonl counts (#1559)
|
||||
640aa03f2 brief: Console inbox Approve and Deny for grant requests, Vikunja #435 (mos-1193)
|
||||
a36523713 test: resolve the real Docker binary inside lane fixtures
|
||||
5afff5490 brief: T27 row 251 records the monotonic-boundary check, 8 of 80 adds lost the key (mos-1185 item 2)
|
||||
f33932921 brief: T27 lists no identities right after ssh-add -t 1, Vikunja #453 (mos-1185 item 2); row 197 lane-b observation (item 3)
|
||||
1be656ac9 brief: K12 reads cgroup.events after cgroup.kill under load, Vikunja #450 (mos-1182 item 3)
|
||||
19d3d5f02 sessions: Filbert row 66 round 4 review, approve
|
||||
6c75887b0 sessions: Sage 10-21 afternoon (rows 249, 197, 222, 66, queue revs 2642-2904), with pending Dewey and Researcher lines
|
||||
0636721e6 queue: revs 2642-2904 (rows 234, 235, 249 landed; 212, 213, 218, 221, 231, 247 landed; 228, 233, 238, 240, 195, 245, 248 landed; 197 r8 approve, 215 r1 approve, 66 round 4; Case 2 and installer row notes)
|
||||
7b9737953 deferred: row 248 Webby N1, the grant executor test sleep 30 can outlive its test; Duckworth deferred it in the row 249 review (#1751 c30070)
|
||||
9de5a57ab row 249: row 235 follow-up, a grant whose install never returned records hostLineLeft, since the remote add can outlive an abort or a timeout (#1751)
|
||||
8ca064158 sessions: Filbert row 215 candidate review r1, approve
|
||||
8a04f4aa4 row 248: row 235 follow-up, the grant-executor hold-add and hold-remove stubs stop when their test dir is gone, so no stub outlives its test (#1750)
|
||||
bc579f7a8 docs: host run sheet section 4, the apt-get download W: line as root is expected, and the profile's sha256 is checked again at /etc/apparmor.d (mos-1168)
|
||||
6de1ebc62 plans: row 235 follow-up brief, the grant undo records a host line it can't prove gone (mos-1166, gizmoduck-0139, duckworth-0091)
|
||||
540239b6b plans: row 235 follow-up brief, the held grant-executor stubs stop when their test dir is gone
|
||||
6d5545d44 row 245: row 195 follow-up, F9 runs node as root only at the pinned root-only path, the header and TOOLS.md list what root runs, F6 refuses .. after claude-<pin>/ (Duckworth N1-N3, #1693)
|
||||
856c1826b row 247: C2-20b persona import follow-up, keys from config.yaml print as JSON strings, the dry run applies C2-08's fence and refuses, privateDir refuses an EEXIST race by name (#1749)
|
||||
f8276c5eb row 218: C2-12 setup sheet for Melanie and the library survey run sheet (#1719)
|
||||
b65f3a0e8 sessions: Darkwing row 245 slot green, round 1 in review
|
||||
5c6f73ed7 plans: C2-03a brief, the mirror HEAD is a symbolic ref to refs/heads/served so the C2-12 survey reads the served commit
|
||||
947101d8a sessions: Sage 235, 221 and C2-20b landings, 197 r7 request and proof prep
|
||||
335bb3e30 plans: C2-20b brief (row 221 follow-up, Webby N1, N2 and N4) and DEFERRED for N3, Morgana's 213 B3 anchor
|
||||
300766450 row 221: C2-20 Bean and Chet persona import, stage and install script with a count-only dry run, personaTextFault exported from bundle.mjs, yaml 2.9.0 exact, fixture Hermes home and read trace (#1722)
|
||||
b1fece6f0 row 235: machine grant row b, raise binding, executor, reconcile, revoke and sweep, creative roles refused at raise, recheck and host.run, N10 (#1741)
|
||||
aa19a2464 sessions: Darkwing row 245 built, slot asked
|
||||
29ce1e360 row 231: C2-01b library reader and survey fixes: control and bidi escapes, front matter list links, an unresolved body embed to a non-.md target is an attachment (#1733)
|
||||
62ddc96cc plans: row 195 follow-up (Duckworth N1-N3) and the .53 window row, which carries 195's done condition after its early done move
|
||||
d6cff0cdd deferred: Duckworth's N1-N3 on row 195, the Case 1 acceptance script's root parse order, header and prefix match
|
||||
361ba2387 row 213: Case 2 C2-08 creative roles, persona file and block, M1, M2, M6, M10, prompt passed by file (#1714)
|
||||
b65014647 row 195: scripts/case1-accept.sh, root's Case 1 acceptance (F1-F11, P1-P11), its test, TOOLS.md entry (#1693)
|
||||
4364dddb0 plans: row b (235) host.run also refuses a creative agent at spend time, grant record kept for revoke (mos-1164, sage-1908)
|
||||
01e1fe512 plans: row b (235) refuses a host grant to a creative role, after row 213 lands (mos-1163, Webby N2 on 213 r4)
|
||||
38b83ea5c sessions: Sage, 212 landing, 197 proof stop and r6, rows 243/244, row 66 r4b
|
||||
fefd54e13 deferred: N7 lane image base inspect; brief for two gate environment rows (mos-1162)
|
||||
ee7b2f137 row 212: packages/library, mapping, scope, reader, resolver, R-rules, renderItems, survey (#1713)
|
||||
f6c70f944 sessions: Darkwing, row 195 build in review (37171f65b, rev 2756), slot green 22/22, 12/12 mutants, gate 662/0 + 429/0
|
||||
b3b2374ea sessions: Filbert, row 215 manifest r2 approve
|
||||
696255f88 sessions: Filbert, row 215 manifest review, changes requested (B1-B3)
|
||||
059577886 deferred: gate runs mount the host pi auth file (Webby N4 on the row 197 proof driver, traced by Herb)
|
||||
4ced5e63d sessions: Sage, rows 240, 228 and 238 landed (c515273db, 86249ac55, d49b1f2bd), 197 r5 rebuild, Gosalyn 212 slot (sage-1788..1826)
|
||||
e7078e6a8 deferred: Webby's row 238 N1, the sso-config first-step hint has no business (#1744)
|
||||
d49b1f2bd row 238: stack-release's sso-config first step carries --instance NAME on a named instance (#1744)
|
||||
86249ac55 row 228: ZAI_API_KEY in the session env allowlist (#1729)
|
||||
59f817c32 sessions: Darkwing, row 195 DESIGN r7 pushed (5425ea2f3), Duckworth review requested (Gitea 29960), row 226 items to Gosalyn
|
||||
c515273db row 240: installer I4, root's host check, its pins and the boot unit; I3's N1, N4 and N5 (#1746, Vikunja #432)
|
||||
da3051ccc deferred: Filbert's row 240 N1 and N2 (29945, #1746); 10-24 window draft Q1 takes the mos-1161 ruling
|
||||
060a17b54 sessions: Filbert, row 240 round 1 approve (29945, rev 2696)
|
||||
ffd90e7fc sessions: Darkwing, row 240 in review round 1 (ad1b23ab2, manifest 573ece95, revs 2694 and 2695), with its BUILD-LOG entry
|
||||
02709125a sessions: Sage, row 234 landing c771a36f4, 235 briefed, rebuilt-row slot order, 10-24 draft NEW (sage-1766..1787)
|
||||
c771a36f4 row 234: machine grant row a, host enrollment, store records and bus schema 3i to 3j (#1740, Vikunja #436)
|
||||
caa4b5944 sessions: Sage, row 233 landing 57f59e0c3, 234 gate stop and r3, 240 briefed, 197/238 drift (sage-1742..1758)
|
||||
57f59e0c3 row 233: installer I3, stack-upgrade.sh and the host run sheet (#1735, Vikunja #432)
|
||||
5465fd52d sage: DEFERRED, checkCreative doesn't refuse credentials (row 213 r3 Webby N1, mos-1160)
|
||||
b00d7d94d sage: row 240 (I4) carries Filbert N1, N4 and N5 from the 233 review; N2 and N3 go to DEFERRED (29922, sage-1727)
|
||||
642a9b87d sessions: Filbert, row 233 round 1 approve (29922, rev 2659)
|
||||
16019b80e sessions: Darkwing, row 233 in review round 1 (1a1dc80ca, manifest 464ce9eb, revs 2644 and 2647), with its BUILD-LOG entry
|
||||
9b5362018 sage: 10-24 window draft 0 counts grant look-alike events before --migrate (row 234 A22b, gizmoduck-0107)
|
||||
7662b0490 sage: 10-24 window draft 0 adds rows 232 (e65a3ab4d), 241 (5d4a61776) and 242 (5fb736b92, fc1cb856b); 199 and 210 evidence list due in draft 1
|
||||
04ce8dfd5 queue: revs 2639-2641 (row 242 Duckworth approve record, landing note and done)
|
||||
fc1cb856b publish: row 242 follow-up docs, the git socket comments name the hold and the late record; BUILD.md residual and :311 cite (Duckworth N1, N2, N4, #1748)
|
||||
c0c4907af sessions: Sage, rows 241 (5d4a61776) and 242 (5fb736b92) landed, queue revs 2628-2641
|
||||
5fb736b92 publish: a retry after publish-timeout pushes once; the bus holds the decision until its push ends and records a late push (row 242, #1748, P19 440)
|
||||
96bac588f queue: revs 2628-2638 (row 241 approve record, landing note and done; row 232 landing note and done; row 233 brief re-pin and Darkwing claim; row 242 in review round 1, request 29888)
|
||||
5d4a61776 bus: row 241 one decision lands once; a broker test with two checkLand tickets on one decision, where the second recordLand refuses decision-consumed and records nothing (row 241, #1747, P19 441)
|
||||
e65a3ab4d stack-release: --units external for root-owned units, with confirm, revert and gate --since; stack-copy copy --bundle and wrapper --create (row 232, #1734, installer I1)
|
||||
0444acd08 queue: revs 2618-2627 (row 232 round 2 approve; row 241 in review round 1, request 29868; row 234 and row 242 claimed)
|
||||
052ba95c7 sessions: Filbert row 232 I1 review round 2, approve (#1734)
|
||||
a52d64ed4 sage: 10-24 window draft 0 names the landed commits for rows 210 (f5753c0e8), 237 (d7d943199) and the PRD (0adf8ddfe)
|
||||
8fede5fc6 sessions: Sage, row 210 landed f5753c0e8, queue revs 2604-2617
|
||||
c084d8d42 queue: revs 2604-2617 (row 210 done, landed f5753c0e8; rows 241 and 242 added and briefed, 241 claimed by Megavolt; row 234 briefed)
|
||||
f5753c0e8 host access: agent host sudo through the credential store, host.run over broker.sock (row 210, #1707)
|
||||
9d165be8c plans: row 237 follow-up N1 brief, a retry after publish-timeout pushes once (duckworth-0069, P19 440)
|
||||
c25785d3d plans: row 237 follow-up brief, one decision lands once, tests at the broker (megavolt-0069, sage-1651, P19 441)
|
||||
ad95c888c sessions: Sage, row 237 and PRD 1.7+1.8 landed, queue revs 2540-2603
|
||||
584e2bd82 queue: revs 2540-2603 (rows 199, 200, 230, 237 done; 210 r4 and 238 in review; 232 in review round 1 approve; 233 briefed; rows 239 and 240 added)
|
||||
0adf8ddfe prd: Mosaic Stack PRD versions 1.7 and 1.8, status approved version 1.8 (DECISIONS Q192, decision d52febb5, Vikunja #434)
|
||||
d7d943199 bus: one decision, one open publish; checkPublish refuses publish-busy while another ticket holds the decision (row 237, #1743)
|
||||
a0e3d7cfd plans: installer I4 hostcheck row brief, split out of I3 (darkwing row 233 G2, sage-1650)
|
||||
9d24ff9b2 sessions: Filbert row 232 I1 review round 1, approve (#1734)
|
||||
6c04b89da plans: row 194 follow-up brief, the revoke under the writer's lock and a fire test (duckworth-0074, mos-1158)
|
||||
6f7dacb41 sage: 10-24 window draft 0 carries PRD 1.7+1.8 (mos-1154, mos-1156, DECISIONS Q192); the live pm cites 1.6 until the window
|
||||
d21b2615c darkwing: row 232 in review round 1 (#1734), BUILD-LOG and SESSIONS lines
|
||||
816d48b03 sage: row 42 run log, Jason's pre-S7 checks (mos-1154); PRD 1.8 r7 raise dropped, Jason approved in T3 (DECISIONS Q192, NOTE.md)
|
||||
011d41ba6 sage: row 42 S7 dates in the run log (sage-1622, mos-1148 C1 C2, Vikunja 442), 10-24 window draft 0 (mos-1145; NEW open), Vikunja scripts and records: 412/424 landing comments, 237 follow-ups 440/441, P19 405-408 and 411 re-plan comments 416-420, read-tasks.mjs (#1524)
|
||||
c8bc5cf4c case2: unit render and host precheck, case2-unit.sh and case2-precheck.sh (row 230, #1732)
|
||||
390ea5261 business: README names nine hard waits, git.push.remote included; row 199 added it to GATED_ONLY (row 199 review N3, duckworth-0068, #1697)
|
||||
cb5fdb064 credentials: a credential store per agent, its own git identity, pushes to a remote (row 199, #1697)
|
||||
76134dfde plans: row 200 follow-up N1 brief (#1744, Launchpad, Webby): the sso-config hint carries --instance NAME
|
||||
9e1085816 stack-release: activate --sso for the Q175 phase 2 window (row 200, #1698)
|
||||
722136772 plans: row 194 item 7 takes the restart route (honker-0044) and item 8 revokes on fire and delete before the business file changes (row 199 D13), with tests and mutants
|
||||
a304dad94 plans: C2-03a askpass ruling (megavolt-0060 to -0063, dewey-0765, gosalyn-0068): fixed argv with followRedirects=false and credential.helper reset, LC_ALL=C, exact-prompt helper with row 199's HOST/NAME checks, no userinfo; tests and four mutants
|
||||
919a84738 plans: #436b build brief (#1742, schema 3j-3k, design r5 1be535aca) and row 199 N1 follow-up (#1743); row 194 carries 199 N2; 193b2 moves to 3k-3l after #436b
|
||||
2d8687bcf sessions: pending lines from Darkwing, Rocko, Launchpad, Morgana, Honker, Gander, Herb, Dewey and Sage (2026-10-20 18:45Z to 23:32Z)
|
||||
1c46d0abf queue: revs 2456-2539. Morgana row 213 in-review rounds 1-2 (2456-2476); Gizmoduck row 210 in-review 1 (2458-2459); Gosalyn row 212 in-review r1 and row 230 start (2464-2470); Duckworth row 210 record (2466); Webby row 213 verdicts r1-r2 and row 200 r3 approve (2469, 2500, 2539); Darkwing row 190 r3 in-progress through row 232 in-progress (2501-2514); Filbert row 190 record and row 66 r3 approve (2504, 2526); Dewey row 66 r3 approve (2529); Megavolt row 199 in-review round 1 (2535-2536); Launchpad row 200 r3 in-progress and in-review (2509-2525); Sage requests, row 190 done, rows 232/233 added and briefed, row 195 after 190 and 233, brief re-pins of 226/230/232/233, row 66 round 3, the row 200 r3 request as jarvis, machine grant rows 234 and 235 added and row 206 after 234 with its 3k brief and piece, the row 199 r1 request as jarvis (2460-2538, 57 revs)
|
||||
f519fb1d2 plans: machine grant rows a (#1740, schema 3i-3j) and b (#1741) briefs from design r6 f9039682e; 193b2 moves to schema 3j-3k after row a
|
||||
b8236a7e1 sessions: Filbert row 66 round 3 APPROVE
|
||||
c7675476b sage: P19 catch-up comments 411-413 on 413 (row 189 landed, no-send activate), 424 (row 210 status) and 414 (rows 201 and 204 landed)
|
||||
9de7ad829 sage: row 66 r3 packet, rebase of r2 onto c40d846cb (sage/row66-r3 2369c00e36874e1100b301e0d01cbd105c0c5d4f, same patch-id, manifest 42e3876ca168)
|
||||
c40d846cb sage: Vikunja comment scripts and read-backs for row 192 landing (P19 412) and the row 190 W11 comments 406-409
|
||||
01b40d357 plans: C2-13a rulings for gosalyn-0065. G1: default instance only, no --instance, a named instance is a follow-up. G2: library refuses --sso for 10-27, preview set and proof pair stay, --sso waits for Q175 phase 2. Line cites moved to their places after rows 190 and 192 (webui-service.sh, stack-copy.sh, bus-service.sh, serve.mjs), and the Console's WantedBy replaces :41
|
||||
ad6f0be7a plans: installer I1/I3 brief, the stack user's stack-release.sh runs from its own clone of the release bundle (darkwing question, sage-1561)
|
||||
ffc124bf6 plans: installer I2 re-brief of C2-13a (row 230, #1732) and C2-13b (row 226, #1727) for Vikunja #432. Q189: both units WantedBy=multi-user.target, root's 2c step enables both and starts the bus then the Console, no hand start at step 9 (gosalyn-0064, mos-1122, mos-1123). Row 190 landed first, so row 230 renders @CONFIG_ENV@ and @SESSION_ENV@
|
||||
8a14931fa plans: installer briefs I1 (Gitea #1734) and I3 (#1735) for Vikunja #432 (mos-1118, mos-1122). I1 builds on row 190's landing 00d4cc1f6; I3 carries the root lock finding, duckworth-0055's notes, the per-host enrollment step and the credential slot table
|
||||
0272383f5 sessions: Darkwing row 190 landed as 00d4cc1f6, row 195 r6 approved and held
|
||||
00d4cc1f6 stack: several Mosaic Stack instances on one host (row 190, #1688)
|
||||
99a406ed3 sage: re-pin window 490e2771d to 4429a104f (row 192) record, STATE.md (sha256 a4d0a7b12f431f15), W.txt (ca9ad4a09059d3b7), S1/S2, matrices, W10 browser check and diagnosis (screenshots not committed), Mos's P3 rehearsal and flags check, W11 reads. Plan corrections after the window: NEW is 103 commits past OLD (not 106), the P3 flags note (mos-1107), and W10's line g and corrected browser checks (mos-1135, mos-1137). PRD 1.8 raise record, decision 3a6c8fb9 (mos-1140) (#1690; P19 386, 412)
|
||||
d0bbc5c1c business: 193a access level check widened to viewer (row 204, #1702)
|
||||
c9f374284 sessions: Filbert row 190 manifest round 3 APPROVE
|
||||
4385b9ec1 plans: Case 2 briefs. C2-13a factory profile from gosalyn-0057 items 1-3 (--pm, --claude under the --node rule, no proxy flags), row 190 tokens to whichever lands second. New C2-01b for row 212's notes N1-N3, N4 to the C2-12 setup sheet, N5 to C2-10b (webby-0041, sage-1517)
|
||||
afad4993f business: FED R1, access.json loader and validator with covers (row 201, #1699)
|
||||
f41729178 plans: re-pin plan 490e2771d to 4429a104f (row 192, Q175 phase 1), npm ci in the new copy, for Mos's check (mos-1099 item 3)
|
||||
305857c8f queue: revs 2362-2455. Sage: Case 2 rows re-pinned to design r11 (5b8def2f1, row 226 to C2-13b), row 230 C2-13a added and briefed (#1732), row 215 (C2-04) assigned to Herb, row 192 landed 4429a104f with landing note and done, row 228 review request resolved, plus earlier Sage revs. Other seats' revs: Filbert 2362, 2447 (row 190 verdicts); Herb 2395-2396; Webby 2420, 2428 (rows 197/228 verdicts); Morgana 2421 (row 213 claim); Researcher subagent 2422-2424 (row 228); Gosalyn 2426 (row 212 claim); Darkwing 2431, 2433, 2438 (row 190)
|
||||
176a6bf4b sessions: Filbert row 190 manifest round 2 CHANGES (base drift on row 192)
|
||||
5b8def2f1 plans: Case 2 row briefs from Gosalyn's eac92d956 (design r11, approved webby-0038: C2-13 split into C2-13a unit render and host precheck and C2-13b install script and library precheck, C2-04 owner Herb, Console unit without [Install] per mos-1097)
|
||||
bus: scripts/bus-service.sh install --copy 7247d80c7af7ffa70fc61557406bf491cdf3bfc7 --copies /home/jwoltje/.local/share/mosaic-stack/copies --pm --claude /home/jwoltje/.local/bin/claude
|
||||
drop --tester-port 7333 (replaced by private Unix socket); the target Console does not support it
|
||||
proof: off
|
||||
send: off
|
||||
Console: scripts/webui-service.sh install --copy 7247d80c7af7ffa70fc61557406bf491cdf3bfc7 --copies /home/jwoltje/.local/share/mosaic-stack/copies --port 7332 --public-host mosaic.woltje.com --auth-header X-authentik-username --allow-user jason.woltje --tester-socket
|
||||
CLI: scripts/stack-copy.sh wrapper 7247d80c7af7ffa70fc61557406bf491cdf3bfc7 --copies /home/jwoltje/.local/share/mosaic-stack/copies --bin /home/jwoltje/.npm-global/bin
|
||||
interpreter: {"path":"/usr/bin/python3.14","version":[3,14,6],"dev":66308,"ino":3968163,"sha256":"8735f172622a7f8d27e293fd0fd36fc55c4cb9b663b6630d33b57870e36c24cc"}
|
||||
@@ -0,0 +1 @@
|
||||
launched chief: run r7646a7a4a2bd, claude-code claude-opus-5-5
|
||||
@@ -0,0 +1,20 @@
|
||||
pointer: /home/jwoltje/.mosaic-dev/state/stack-active.json
|
||||
{"copy":"7247d80c7af7ffa70fc61557406bf491cdf3bfc7","business":"mosaic-stack","pm":true,"claude":"/home/jwoltje/.local/bin/claude","console":["--port","7332","--public-host","mosaic.woltje.com","--auth-header","X-authentik-username","--allow-user","jason.woltje","--tester-socket"],"schema":"3j","activation":"20261021T215926Z-7247d80c7af7","at":"2026-10-21T21:59:31Z"}
|
||||
log (last five lines):
|
||||
{"at":"2026-10-20T19:30:00Z","op":"activate","from":"490e2771d3d057ab76991e2c7859cd251857e64c","to":"490e2771d3d057ab76991e2c7859cd251857e64c","backup":"20261020T192956Z-490e2771d3d0","activation":"20261020T192956Z-490e2771d3d0","result":"ok"}
|
||||
{"at":"2026-10-20T22:02:06Z","op":"activate","from":"490e2771d3d057ab76991e2c7859cd251857e64c","to":"4429a104f6d65ccf165f6c83b5d1dce3147dfde0","backup":null,"activation":"20261020T220206Z-4429a104f6d6","result":"started"}
|
||||
{"at":"2026-10-20T22:02:09Z","op":"activate","from":"490e2771d3d057ab76991e2c7859cd251857e64c","to":"4429a104f6d65ccf165f6c83b5d1dce3147dfde0","backup":"20261020T220206Z-4429a104f6d6","activation":"20261020T220206Z-4429a104f6d6","result":"ok"}
|
||||
{"at":"2026-10-21T21:59:26Z","op":"activate","from":"4429a104f6d65ccf165f6c83b5d1dce3147dfde0","to":"7247d80c7af7ffa70fc61557406bf491cdf3bfc7","backup":null,"activation":"20261021T215926Z-7247d80c7af7","result":"started","interpreter":"{\"path\":\"/usr/bin/python3.14\",\"version\":[3,14,6],\"dev\":66308,\"ino\":3968163,\"sha256\":\"8735f172622a7f8d27e293fd0fd36fc55c4cb9b663b6630d33b57870e36c24cc\"}"}
|
||||
{"at":"2026-10-21T21:59:31Z","op":"activate","from":"4429a104f6d65ccf165f6c83b5d1dce3147dfde0","to":"7247d80c7af7ffa70fc61557406bf491cdf3bfc7","backup":"20261021T215926Z-7247d80c7af7","activation":"20261021T215926Z-7247d80c7af7","result":"ok"}
|
||||
check:
|
||||
bus: /home/jwoltje/.local/share/mosaic-stack/copies/7247d80c7af7ffa70fc61557406bf491cdf3bfc7
|
||||
Console: /home/jwoltje/.local/share/mosaic-stack/copies/7247d80c7af7ffa70fc61557406bf491cdf3bfc7
|
||||
CLI: /home/jwoltje/.local/share/mosaic-stack/copies/7247d80c7af7ffa70fc61557406bf491cdf3bfc7
|
||||
result: pass
|
||||
bus status:
|
||||
host: mosaic-stack, pid 721027, running, since 2026-10-21T21:59:28.399Z, notifier off
|
||||
socket: present
|
||||
writer.lock: pid 721071
|
||||
boot: organization file loaded at 2026-10-21T21:59:28.397Z
|
||||
unit flags: match the pointer
|
||||
drift: 14 commit(s) on refactor since the pointer's copy
|
||||
@@ -0,0 +1 @@
|
||||
2026-10-21T21:59:26Z
|
||||
@@ -0,0 +1,11 @@
|
||||
stack-release: interpreter: {"path":"/usr/bin/python3.14","version":[3,14,6],"dev":66308,"ino":3968163,"sha256":"8735f172622a7f8d27e293fd0fd36fc55c4cb9b663b6630d33b57870e36c24cc"}
|
||||
stack-release: captured the rollback set: /home/jwoltje/.mosaic-dev/state/stack-rollback/20261021T215926Z-7247d80c7af7
|
||||
stack-release: drop --tester-port 7333 (replaced by private Unix socket); the target Console does not support it
|
||||
stack-release: stopped both units
|
||||
stack-release: stopped store schema: 3i
|
||||
stack-release: backup: /home/jwoltje/.mosaic-dev/backups/stack/20261021T215926Z-7247d80c7af7
|
||||
stack-release: migrated the store to 3j
|
||||
stack-release: installed 7247d80c7af7ffa70fc61557406bf491cdf3bfc7
|
||||
stack-release: gate passed on 7247d80c7af7ffa70fc61557406bf491cdf3bfc7
|
||||
stack-release: activate ok: 7247d80c7af7ffa70fc61557406bf491cdf3bfc7 is live for mosaic-stack
|
||||
# exit 0
|
||||
@@ -0,0 +1,8 @@
|
||||
W0b 2026-10-21T21:48:58Z business file copy /home/jwoltje/.config/mosaic-dev/businesses/mosaic-stack.json.pre-window-20261021T214858Z sha256 1e49bdf05e62607972958f5b7298cfd600c7825812370e26d7c2a842f2af9b93 (source /home/jwoltje/.config/mosaic-dev/businesses/mosaic-stack.json sha256 1e49bdf05e62607972958f5b7298cfd600c7825812370e26d7c2a842f2af9b93)
|
||||
W5 2026-10-21 start 21:59:26Z end 21:59:31Z exit 0
|
||||
activation 20261021T215926Z-7247d80c7af7
|
||||
backup /home/jwoltje/.mosaic-dev/backups/stack/20261021T215926Z-7247d80c7af7 SHA256SUMS sha256 730ca3d0dd9c02e489d05307117820180dc83570c7fe252428841fbd7794bbe7
|
||||
rollback set /home/jwoltje/.mosaic-dev/state/stack-rollback/20261021T215926Z-7247d80c7af7
|
||||
W1 pm session rc9e993da24cf crosses the window idle, no open request (Mos bus read, last seq 591), state read at W9 (mos-1226)
|
||||
# 2026-10-21T22:06:22Z mos-1230: pm not relaunched by the restart, expected, chief staffs on request. At W12 the tester asks the chief to staff a pm (Q173 end-to-end testing), then the pm requirements read, expecting 1.10 and 70 ids; that pm turn is W12's managed session.
|
||||
W12 2026-10-21T22:19:31Z store backup /home/jwoltje/.mosaic-dev/backups/w12-20261021T221931Z (ok events 1699 maxSeq 1699 schema 3j), SHA256SUMS sha256 a4ecd7ba8e24b9f586634619643d72cbef356db683459a41998f513c83cad27a
|
||||
@@ -0,0 +1,8 @@
|
||||
# W10 matrix 2026-10-21T22:18:51Z
|
||||
a 200 ok=true
|
||||
b 401 error="sign-in required"
|
||||
c 401 error="sign-in required"
|
||||
d 403 error="proxy Host refused"
|
||||
e 403 error="tester listener required"
|
||||
f 000 non-json 0 bytes
|
||||
g 000 non-json 0 bytes
|
||||
@@ -0,0 +1,8 @@
|
||||
# P6 2026-10-21T21:42:06Z
|
||||
a 200 ok=true
|
||||
b 401 error="sign-in required"
|
||||
c 401 error="sign-in required"
|
||||
d 403 error="proxy Host refused"
|
||||
e 403 error="tester listener required"
|
||||
f 403 error="proxy header refused"
|
||||
g 200 non-json 6575 bytes
|
||||
@@ -0,0 +1,21 @@
|
||||
// Re-pin 4429a104f to 7247d80c7 (repin-1024-draft1.md), P1: the live business file, read only. Prints the plan's P1 values and nothing else:
|
||||
// bus.tester, the plug-in lists, launch.by, whether launch.instances includes assistant, and each role's
|
||||
// vars.harness and vars["fallback.harness"]. No other value from the file.
|
||||
import { readFileSync } from 'node:fs';
|
||||
const f = `${process.env.HOME}/.config/mosaic-dev/businesses/mosaic-stack.json`;
|
||||
const b = JSON.parse(readFileSync(f, 'utf8'));
|
||||
console.log(`bus.tester ${JSON.stringify(b.vars?.['bus.tester'])}`);
|
||||
const p = b.plugins ?? {};
|
||||
console.log(`plugins kinds ${JSON.stringify(Object.keys(p).sort())}`);
|
||||
for (const k of ['usage-source', 'channel', 'harness']) console.log(`plugins ${k} ${JSON.stringify(p[k])}`);
|
||||
console.log(`launch.by ${JSON.stringify(b.launch?.by)}`);
|
||||
const inst = b.launch?.instances;
|
||||
const names = Array.isArray(inst) ? inst.map((i) => (typeof i === 'string' ? i : i?.name)) : Object.keys(inst ?? {});
|
||||
console.log(`launch.instances includes assistant ${names.includes('assistant')}`);
|
||||
let pi = 0;
|
||||
for (const [r, v] of Object.entries(b.roles ?? {}).sort()) {
|
||||
const h = v?.vars?.harness, fh = v?.vars?.['fallback.harness'];
|
||||
if (h === 'pi' || fh === 'pi') pi++;
|
||||
console.log(`role ${r} vars.harness ${JSON.stringify(h)}${fh === undefined ? '' : ` fallback.harness ${JSON.stringify(fh)}`}`);
|
||||
}
|
||||
console.log(`roles naming pi ${pi}`);
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
#!/usr/bin/env bash
|
||||
# Re-pin 4429a104f to 7247d80c7, P1 (repin-1024-draft1.md, blob 0c5e1f1c6271; sage-2179). Read only; nothing stopped.
|
||||
# business-check.mjs runs from the repo copy, checked against SCRIPTS.sha256. From the system config this
|
||||
# prints dataRoot only. A bus.tester other than true stops the window here.
|
||||
set -uo pipefail
|
||||
REPO=/mnt/storage/src/mosaic-stack WIN=$REPO/agents/sage/work/window-repin-7247d80c7
|
||||
OLD=4429a104f6d65ccf165f6c83b5d1dce3147dfde0
|
||||
S=$REPO/agents/sage/work/window-plan/repin-de352dfaf BC=$S/business-check.mjs
|
||||
B=$HOME/.config/mosaic-dev/businesses/mosaic-stack.json
|
||||
{ echo "# P1 start $(date -u +%FT%T.%3NZ), REPO HEAD $(git -C $REPO rev-parse HEAD)"
|
||||
echo "# plan blob $(git -C $REPO rev-parse HEAD:agents/sage/work/window-plan/repin-1024-draft1.md)"
|
||||
echo "# business-check.mjs sha256 $(sha256sum < $BC | cut -c1-64); SCRIPTS.sha256 line: $(grep ' business-check.mjs$' $S/SCRIPTS.sha256)"
|
||||
echo "# business file sha256 $(sha256sum < $B | cut -c1-64), mtime $(stat -c %y $B)"; } | tee -a $WIN/P1.txt
|
||||
(cd $S && grep ' business-check.mjs$' SCRIPTS.sha256 | sha256sum -c -) || { echo "STOP business-check.mjs sha mismatch" | tee -a $WIN/P1.txt; exit 1; }
|
||||
$WIN/w.sh P1.txt "P1 status" scripts/stack-release.sh status mosaic-stack
|
||||
$WIN/w.sh P1.txt "P1 values" node $WIN/p1.mjs
|
||||
grep -qx 'bus.tester true' <(node $WIN/p1.mjs) || { echo "STOP bus.tester is not true" | tee -a $WIN/P1.txt; exit 2; }
|
||||
$WIN/w.sh P1.txt "P1 business-check OLD" node $BC $OLD
|
||||
DATA=$(node -e 'const c=JSON.parse(require("fs").readFileSync(process.env.HOME+"/.config/mosaic-dev/config.json","utf8"));process.stdout.write(String(c.dataRoot))')
|
||||
DATA=${DATA/#\~/$HOME}
|
||||
{ echo; echo "## P1 data console"; echo "# dataRoot $DATA"
|
||||
echo "# ls -ld \$DATA/console:"; ls -ld "$DATA/console" 2>&1; echo "# ls rc=$?"
|
||||
sock="$DATA/console/tester.sock"; echo "# tester.sock path length $(printf %s "$sock" | wc -c) bytes (limit 107)"
|
||||
echo "# P1 end $(date -u +%FT%T.%3NZ)"; } | tee -a $WIN/P1.txt
|
||||
@@ -0,0 +1,24 @@
|
||||
start 2026-10-21T21:43:33Z
|
||||
console-check.sh: OK
|
||||
console-stub.mjs: OK
|
||||
console-stub-tester.mjs: OK
|
||||
## run 1: console-stub.mjs, no tester
|
||||
STOP stub did not start
|
||||
node:internal/modules/cjs/loader:1573
|
||||
throw err;
|
||||
^
|
||||
|
||||
Error: Cannot find module '/mnt/storage/scratch/tmp/rp7247/p2-run1'
|
||||
at Module._resolveFilename (node:internal/modules/cjs/loader:1569:15)
|
||||
at wrapResolveFilename (node:internal/modules/cjs/loader:1123:27)
|
||||
at defaultResolveImplForCJSLoading (node:internal/modules/cjs/loader:1147:10)
|
||||
at resolveForCJSWithHooks (node:internal/modules/cjs/loader:1174:12)
|
||||
at Module._load (node:internal/modules/cjs/loader:1346:5)
|
||||
at wrapModuleLoad (node:internal/modules/cjs/loader:261:19)
|
||||
at Module.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:154:5)
|
||||
at node:internal/main/run_main_module:33:47 {
|
||||
code: 'MODULE_NOT_FOUND',
|
||||
requireStack: []
|
||||
}
|
||||
|
||||
Node.js v26.8.1
|
||||
@@ -0,0 +1,50 @@
|
||||
# P2 unit diffs 2026-10-21T21:42:43Z, REPO HEAD 111ec559c; webui-service.sh and bus-service.sh blobs equal NEW's (P5)
|
||||
## webui: scripts/webui-service.sh render --copy 7247d80c7af7ffa70fc61557406bf491cdf3bfc7 --port 7332 --public-host mosaic.woltje.com --auth-header X-authentik-username --allow-user jason.woltje --tester-socket
|
||||
# render rc=0
|
||||
2c2
|
||||
< # Rendered by scripts/webui-service.sh from packages/webui/systemd/; /home/jwoltje/.local/share/mosaic-stack/copies/4429a104f6d65ccf165f6c83b5d1dce3147dfde0,
|
||||
---
|
||||
> # Rendered by scripts/webui-service.sh from packages/webui/systemd/; /home/jwoltje/.local/share/mosaic-stack/copies/7247d80c7af7ffa70fc61557406bf491cdf3bfc7,
|
||||
4c4
|
||||
< # /home/jwoltje/.local/share/mosaic-stack/copies/4429a104f6d65ccf165f6c83b5d1dce3147dfde0 is the read-only copy `install --copy ID` names, and the flags
|
||||
---
|
||||
> # /home/jwoltje/.local/share/mosaic-stack/copies/7247d80c7af7ffa70fc61557406bf491cdf3bfc7 is the read-only copy `install --copy ID` names, and the flags
|
||||
18c18
|
||||
< Documentation=file:///home/jwoltje/.local/share/mosaic-stack/copies/4429a104f6d65ccf165f6c83b5d1dce3147dfde0/packages/webui/README.md
|
||||
---
|
||||
> Documentation=file:///home/jwoltje/.local/share/mosaic-stack/copies/7247d80c7af7ffa70fc61557406bf491cdf3bfc7/packages/webui/README.md
|
||||
24c24
|
||||
< WorkingDirectory=/home/jwoltje/.local/share/mosaic-stack/copies/4429a104f6d65ccf165f6c83b5d1dce3147dfde0
|
||||
---
|
||||
> WorkingDirectory=/home/jwoltje/.local/share/mosaic-stack/copies/7247d80c7af7ffa70fc61557406bf491cdf3bfc7
|
||||
29c29
|
||||
< ExecStart=/usr/bin/node /home/jwoltje/.local/share/mosaic-stack/copies/4429a104f6d65ccf165f6c83b5d1dce3147dfde0/packages/webui/src/cli.mjs serve --checkout /mnt/storage/src/mosaic-stack --port 7332 --public-host mosaic.woltje.com --auth-header X-authentik-username --allow-user jason.woltje --tester-port 7333
|
||||
---
|
||||
> ExecStart=/usr/bin/node /home/jwoltje/.local/share/mosaic-stack/copies/7247d80c7af7ffa70fc61557406bf491cdf3bfc7/packages/webui/src/cli.mjs serve --checkout /mnt/storage/src/mosaic-stack --port 7332 --public-host mosaic.woltje.com --auth-header X-authentik-username --allow-user jason.woltje --tester-socket
|
||||
# diff live vs render rc=1
|
||||
## webui diff with the copy path normalized (OLD id -> NEW id in the live unit)
|
||||
29c29
|
||||
< ExecStart=/usr/bin/node /home/jwoltje/.local/share/mosaic-stack/copies/7247d80c7af7ffa70fc61557406bf491cdf3bfc7/packages/webui/src/cli.mjs serve --checkout /mnt/storage/src/mosaic-stack --port 7332 --public-host mosaic.woltje.com --auth-header X-authentik-username --allow-user jason.woltje --tester-port 7333
|
||||
---
|
||||
> ExecStart=/usr/bin/node /home/jwoltje/.local/share/mosaic-stack/copies/7247d80c7af7ffa70fc61557406bf491cdf3bfc7/packages/webui/src/cli.mjs serve --checkout /mnt/storage/src/mosaic-stack --port 7332 --public-host mosaic.woltje.com --auth-header X-authentik-username --allow-user jason.woltje --tester-socket
|
||||
# normalized diff rc=1
|
||||
## bus: scripts/bus-service.sh render --copy 7247d80c7af7ffa70fc61557406bf491cdf3bfc7 --pm --claude /home/jwoltje/.local/bin/claude
|
||||
# render rc=0
|
||||
2c2
|
||||
< # Rendered by scripts/bus-service.sh from packages/cli/systemd/; /home/jwoltje/.local/share/mosaic-stack/copies/4429a104f6d65ccf165f6c83b5d1dce3147dfde0,
|
||||
---
|
||||
> # Rendered by scripts/bus-service.sh from packages/cli/systemd/; /home/jwoltje/.local/share/mosaic-stack/copies/7247d80c7af7ffa70fc61557406bf491cdf3bfc7,
|
||||
8c8
|
||||
< Documentation=file:///home/jwoltje/.local/share/mosaic-stack/copies/4429a104f6d65ccf165f6c83b5d1dce3147dfde0/packages/cli/README.md
|
||||
---
|
||||
> Documentation=file:///home/jwoltje/.local/share/mosaic-stack/copies/7247d80c7af7ffa70fc61557406bf491cdf3bfc7/packages/cli/README.md
|
||||
14c14
|
||||
< WorkingDirectory=/home/jwoltje/.local/share/mosaic-stack/copies/4429a104f6d65ccf165f6c83b5d1dce3147dfde0
|
||||
---
|
||||
> WorkingDirectory=/home/jwoltje/.local/share/mosaic-stack/copies/7247d80c7af7ffa70fc61557406bf491cdf3bfc7
|
||||
30c30
|
||||
< ExecStart=/home/jwoltje/.local/share/mosaic-stack/copies/4429a104f6d65ccf165f6c83b5d1dce3147dfde0/scripts/mosaic bus start %i --pm
|
||||
---
|
||||
> ExecStart=/home/jwoltje/.local/share/mosaic-stack/copies/7247d80c7af7ffa70fc61557406bf491cdf3bfc7/scripts/mosaic bus start %i --pm
|
||||
# diff live vs render rc=1
|
||||
# normalized diff rc=0
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
#!/usr/bin/env bash
|
||||
# Re-pin 4429a104f to 7247d80c7, P2 stub runs (repin-1024-draft1.md; sage-2179, sage-2186). Launch from /.
|
||||
# Run 1: console-stub.mjs (NEW's startServer, no tester), console-check.sh NEW PORT. Run 2: console-stub-tester.mjs
|
||||
# adds NEW's startTesterSocket on $D/console/tester.sock under a fresh scratch data root; console-check.sh again,
|
||||
# then the socket checks. Both stubs must report CALLS [] on SIGTERM. console-check.sh reads NEW's copy under
|
||||
# the real HOME (W0 made it there) and only reads. Kills only the stub pids it started.
|
||||
set -uo pipefail
|
||||
REPO=/mnt/storage/src/mosaic-stack
|
||||
S=$REPO/agents/sage/work/window-plan/repin-de352dfaf
|
||||
NEW=7247d80c7af7ffa70fc61557406bf491cdf3bfc7
|
||||
C=$HOME/.local/share/mosaic-stack/copies/$NEW
|
||||
[ -n "${TMPDIR:-}" ] && [[ "$TMPDIR" == /mnt/storage/scratch/tmp/* ]] || { echo "STOP TMPDIR must be under /mnt/storage/scratch/tmp"; exit 9; }
|
||||
echo "start $(date -u +%FT%TZ)"
|
||||
(cd $S && grep -E ' (console-check.sh)$' SCRIPTS.sha256 | sha256sum -c -) || { echo "STOP console-check.sh sha"; exit 1; }
|
||||
(cd $S/fixture && sha256sum -c SHA256SUMS) || { echo "STOP fixture sha"; exit 1; }
|
||||
rc=0
|
||||
start_stub() { # out-file, then node args; sets spid and port
|
||||
local o="$1"; shift
|
||||
node "$@" > "$o.out" 2> "$o.err" &
|
||||
spid=$!
|
||||
for _ in $(seq 1 50); do grep -q '^PORT ' "$o.out" && break; sleep 0.1; done
|
||||
port=$(sed -n 's/^PORT //p' "$o.out")
|
||||
[ -n "$port" ] || { echo "STOP stub did not start"; cat "$o.err"; kill -TERM $spid 2>/dev/null; wait $spid 2>/dev/null; exit 1; }
|
||||
}
|
||||
stop_stub() { kill -TERM $spid; wait $spid 2>/dev/null; cat "$1.out"; grep -qx 'CALLS \[\]' "$1.out" && echo "PASS CALLS []" || { echo "FAIL CALLS not []"; rc=1; }; }
|
||||
|
||||
echo "## run 1: console-stub.mjs, no tester"
|
||||
O1=$TMPDIR/p2-run1
|
||||
start_stub $O1 $S/fixture/console-stub.mjs $C
|
||||
echo "stub pid $spid port $port"
|
||||
out=$(bash $S/console-check.sh $NEW $port); r=$?; echo "$out"; echo "console-check rc=$r"; [ $r = 0 ] || rc=1
|
||||
stop_stub $O1
|
||||
|
||||
echo "## run 2: console-stub-tester.mjs with --tester-socket's listener under a scratch data root"
|
||||
D=$(mktemp -d /mnt/storage/scratch/tmp/p2tXXXXXX); echo "data root $D"
|
||||
O2=$TMPDIR/p2-run2
|
||||
node $S/fixture/console-stub-tester.mjs $C $D > $O2.out 2> $O2.err &
|
||||
spid=$!
|
||||
for _ in $(seq 1 50); do grep -q '^SOCK ' $O2.out && break; sleep 0.1; done
|
||||
port=$(sed -n 's/^PORT //p' $O2.out); sock=$(sed -n 's/^SOCK //p' $O2.out)
|
||||
[ -n "$port" ] && [ -n "$sock" ] || { echo "STOP tester stub did not start"; cat $O2.err; kill -TERM $spid 2>/dev/null; wait $spid 2>/dev/null; exit 1; }
|
||||
echo "stub pid $spid port $port sock $sock"
|
||||
out=$(bash $S/console-check.sh $NEW $port); r=$?; echo "$out"; echo "console-check rc=$r"; [ $r = 0 ] || rc=1
|
||||
[ "$sock" = "$D/console/tester.sock" ] && echo "PASS SOCK is \$D/console/tester.sock" || { echo "FAIL SOCK $sock"; rc=1; }
|
||||
st=$(stat -c '%F %a' "$sock"); [ "$st" = "socket 600" ] && echo "PASS sock: $st" || { echo "FAIL sock: $st"; rc=1; }
|
||||
dt=$(stat -c '%F %a' "$D/console"); [ "$dt" = "directory 700" ] && echo "PASS console dir: $dt" || { echo "FAIL console dir: $dt"; rc=1; }
|
||||
code=$(curl -s -o /dev/null -w '%{http_code}' --unix-socket "$sock" http://mosaic-tester.invalid/api/config)
|
||||
[ "$code" = 200 ] && echo "PASS tester /api/config over the socket: 200" || { echo "FAIL tester /api/config: $code"; rc=1; }
|
||||
stop_stub $O2
|
||||
[ ! -e "$sock" ] && echo "PASS socket gone after SIGTERM" || { echo "FAIL socket still there"; rc=1; }
|
||||
[ -s $O1.err ] || [ -s $O2.err ] && { echo "# stub stderr:"; cat $O1.err $O2.err; }
|
||||
left=0; for p in /proc/[0-9]*; do c=$(tr '\0' ' ' < $p/cmdline 2>/dev/null); [[ "$c" == *fixture/console-stub* ]] && { echo "LEFT ${p#/proc/} $c"; left=1; }; done
|
||||
[ $left = 0 ] && echo "sweep: no console-stub process left"
|
||||
echo "end $(date -u +%FT%TZ) rc=$rc"; [ $rc = 0 ] && echo P2_STUBS_PASS || echo P2_STUBS_FAIL
|
||||
exit $rc
|
||||
@@ -0,0 +1,39 @@
|
||||
start 2026-10-21T21:43:57Z
|
||||
console-check.sh: OK
|
||||
console-stub.mjs: OK
|
||||
console-stub-tester.mjs: OK
|
||||
## run 1: console-stub.mjs, no tester
|
||||
stub pid 528928 port 33219
|
||||
PASS markdown-preview.js is served and equals the copy's
|
||||
PASS /api/config on loopback: send=false edit=false
|
||||
PASS project.files through the Console: head=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa files=2 spec=true error=none
|
||||
PASS project.file spec/north-star.md through the Console: commit=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa length=4 error=none
|
||||
PASS loopback save refused: HTTP 403 error=read-only sent=false
|
||||
CONSOLE_PASS
|
||||
console-check rc=0
|
||||
PORT 33219
|
||||
CALLS []
|
||||
PASS CALLS []
|
||||
## run 2: console-stub-tester.mjs with --tester-socket's listener under a scratch data root
|
||||
data root /mnt/storage/scratch/tmp/p2tKarNvB
|
||||
stub pid 529010 port 44335 sock /mnt/storage/scratch/tmp/p2tKarNvB/console/tester.sock
|
||||
PASS markdown-preview.js is served and equals the copy's
|
||||
PASS /api/config on loopback: send=false edit=false
|
||||
PASS project.files through the Console: head=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa files=2 spec=true error=none
|
||||
PASS project.file spec/north-star.md through the Console: commit=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa length=4 error=none
|
||||
PASS loopback save refused: HTTP 403 error=read-only sent=false
|
||||
CONSOLE_PASS
|
||||
console-check rc=0
|
||||
PASS SOCK is $D/console/tester.sock
|
||||
PASS sock: socket 600
|
||||
PASS console dir: directory 700
|
||||
PASS tester /api/config over the socket: 200
|
||||
PORT 44335
|
||||
SOCK /mnt/storage/scratch/tmp/p2tKarNvB/console/tester.sock
|
||||
CALLS []
|
||||
PASS CALLS []
|
||||
PASS socket gone after SIGTERM
|
||||
/mnt/storage/src/mosaic-stack/agents/sage/work/window-repin-7247d80c7/p2.sh: line 52: /proc/502127/cmdline: No such file or directory
|
||||
sweep: no console-stub process left
|
||||
end 2026-10-21T21:43:58Z rc=0
|
||||
P2_STUBS_PASS
|
||||
@@ -0,0 +1,16 @@
|
||||
import sqlite3, sys, json
|
||||
def counts(p):
|
||||
c = sqlite3.connect(f"file:{p}?mode=ro", uri=True)
|
||||
t = [r[0] for r in c.execute("select name from sqlite_master where type='table' order by name")]
|
||||
out = {n: c.execute(f'select count(*) from "{n}"').fetchone()[0] for n in t}
|
||||
la = c.execute("""select count(*) from events where json_extract(body,'$.action')='action.allowed' and (
|
||||
coalesce(json_extract(body,'$.body.action'),'') like 'grant.%' or coalesce(json_extract(body,'$.body.operation'),'') like 'grant.%'
|
||||
or instr(body,'hostLineLeft')>0)""").fetchone()[0] if 'events' in t else None
|
||||
la2 = c.execute("""select count(*) from events where (json_extract(body,'$.action') like 'grant.%' or json_extract(body,'$.operation') like 'grant.%' or instr(body,'hostLineLeft')>0)""").fetchone()[0]
|
||||
return out, la, la2
|
||||
a, laa, la2a = counts(sys.argv[1]); b, lab, la2b = counts(sys.argv[2])
|
||||
for k in sorted(set(a) | set(b)):
|
||||
mark = '' if a.get(k) == b.get(k) else ' CHANGED'
|
||||
print(f"{k}: {a.get(k)} -> {b.get(k)}{mark}")
|
||||
print("lookalike_action_allowed backup", laa, "rehearsal", lab)
|
||||
print("any_grant_event backup", la2a, "rehearsal", la2b)
|
||||
@@ -0,0 +1,21 @@
|
||||
decision_events: 21 -> 21
|
||||
decisions: 21 -> 21
|
||||
deliveries: 1726 -> 1726
|
||||
events: 1527 -> 1527
|
||||
host_grants: None -> 0 CHANGED
|
||||
hosts: None -> 0 CHANGED
|
||||
instance_names: 0 -> 0
|
||||
message_threads: 2 -> 2
|
||||
messages: 567 -> 567
|
||||
meta: 3 -> 3
|
||||
projects: 1 -> 1
|
||||
role_claims: 100 -> 100
|
||||
sqlite_sequence: 12 -> 12
|
||||
task_comment_cursors: 215 -> 215
|
||||
task_snapshots: 401 -> 401
|
||||
thread_claims: 2 -> 2
|
||||
thread_opens: 0 -> 0
|
||||
threads: 1 -> 1
|
||||
usage_records: 30 -> 30
|
||||
lookalike_action_allowed backup 0 rehearsal 0
|
||||
any_grant_event backup 0 rehearsal 0
|
||||
@@ -0,0 +1,8 @@
|
||||
import sqlite3, sys
|
||||
c = sqlite3.connect(f"file:{sys.argv[1]}?mode=ro", uri=True)
|
||||
cols = [r[1] for r in c.execute("pragma table_info(decision_events)")]
|
||||
print(cols)
|
||||
kind = "op"
|
||||
q = f"""select d.action, (select e.{kind} from decision_events e where e.decision=d.id order by e.seq desc limit 1) from decisions d order by d.seq"""
|
||||
from collections import Counter
|
||||
print(Counter(c.execute(q).fetchall()))
|
||||
@@ -0,0 +1,2 @@
|
||||
['seq', 'decision', 'at', 'op', 'by', 'choice', 'note', 'via']
|
||||
Counter({('prd.approve', 'resolved'): 7, ('git.merge.protected', 'resolved'): 6, ('policy.change', 'resolved'): 6, ('task.scope.change', 'resolved'): 1, ('decision.resolve.technical', 'resolved'): 1})
|
||||
@@ -0,0 +1 @@
|
||||
2026-10-21T21:43:42Z
|
||||
@@ -0,0 +1,17 @@
|
||||
2026-10-21T21:45:23Z
|
||||
host pid 557933 console pid 558039
|
||||
healthz: {"ok":true} 200
|
||||
api/config: {"board":"http://127.0.0.1:7331","send":false,"edit":false} 200
|
||||
api/config Host public: {"error":"sign-in required"} 401
|
||||
socket 600 jwoltje /home/jwoltje/.mosaic-dev/rehearsals/20261021T214338Z-7247d80c7af7/data/console/tester.sock
|
||||
dir 700 jwoltje /home/jwoltje/.mosaic-dev/rehearsals/20261021T214338Z-7247d80c7af7/data/console
|
||||
tester GET /api/config: {"status":200,"text":"{\"board\":\"http://127.0.0.1:7331\",\"tester\":true,\"send\":false,\"edit\":false}","body":{"board":"http://127.0.0.1:7331","tester":true,"send":false,"edit":false}}
|
||||
rc=0
|
||||
tester GET /api/bus/agents: {"status":200,"text":"{\"rows\":[],\"at\":\"2026-10-21T21:45:24.717Z\"}","body":{"rows":[],"at":"2026-10-21T21:45:24.717Z"}}
|
||||
rc=0
|
||||
console exit 0
|
||||
host exit 0
|
||||
writer.lock released
|
||||
receipt lines: 2
|
||||
0
|
||||
2026-10-21T21:45:24Z
|
||||
@@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env bash
|
||||
# P3 (Mos): NEW's bus host (--no-launch) and Console with the pointer's flags on the rehearsal root.
|
||||
# --port 7341 stands in for 7332, --tester-socket for --tester-port 7333. No request carries the auth header.
|
||||
set -u
|
||||
R=/home/jwoltje/.mosaic-dev/rehearsals/20261021T214338Z-7247d80c7af7
|
||||
C=/home/jwoltje/.local/share/mosaic-stack/copies/7247d80c7af7ffa70fc61557406bf491cdf3bfc7
|
||||
E=(env -i HOME="$HOME" PATH="$PATH" MOSAIC_CONFIG="$R/config/config.json")
|
||||
date -u +%FT%TZ
|
||||
"${E[@]}" "$C/scripts/mosaic" bus start mosaic-stack --no-launch > "$R/hand-host.log" 2>&1 & HP=$!
|
||||
for i in $(seq 1 30); do [ -S "$R/data/bus/broker.sock" ] && break; sleep 0.5; done
|
||||
"${E[@]}" node "$C/packages/webui/src/cli.mjs" serve --port 7341 --business mosaic-stack --public-host mosaic.woltje.com --auth-header X-authentik-username --allow-user jason.woltje --tester-socket > "$R/hand-console.log" 2>&1 & CP=$!
|
||||
for i in $(seq 1 30); do curl -s -o /dev/null http://127.0.0.1:7341/healthz && [ -S "$R/data/console/tester.sock" ] && break; sleep 0.5; done
|
||||
echo "host pid $HP console pid $CP"
|
||||
echo "healthz: $(curl -s -w ' %{http_code}' http://127.0.0.1:7341/healthz)"
|
||||
echo "api/config: $(curl -s -w ' %{http_code}' http://127.0.0.1:7341/api/config)"
|
||||
echo "api/config Host public: $(curl -s -w ' %{http_code}' -H 'Host: mosaic.woltje.com' http://127.0.0.1:7341/api/config)"
|
||||
stat -c 'socket %a %U %n' "$R/data/console/tester.sock"; stat -c 'dir %a %U %n' "$R/data/console"
|
||||
echo "tester GET /api/config: $("${E[@]}" node "$C/packages/cli/src/tester-http.mjs" GET /api/config 2>&1; echo " rc=$?")"
|
||||
echo "tester GET /api/bus/agents: $("${E[@]}" node "$C/packages/cli/src/tester-http.mjs" GET /api/bus/agents 2>&1 | cut -c1-400; echo " rc=${PIPESTATUS[0]}")"
|
||||
kill -TERM "$CP"; wait "$CP"; echo "console exit $?"
|
||||
kill -TERM "$HP"; wait "$HP"; echo "host exit $?"
|
||||
[ -e "$R/data/bus/writer.lock" ] && echo "writer.lock present" || echo "writer.lock released"
|
||||
echo "receipt lines: $(wc -l < "$R/data/bus/peercred-starts.jsonl")"
|
||||
grep -c 'library:' "$R/hand-host.log"
|
||||
date -u +%FT%TZ
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user