Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f158be8003 |
@@ -1,353 +0,0 @@
|
||||
# Greenfield install log — fomo-lin
|
||||
|
||||
Running log of a from-scratch Mosaic Stack install on Jason's test laptop **fomo-lin**
|
||||
(Debian 13, x86_64). Operator: **scooby** (agent). Started 2026-08-08. Channel per fred:
|
||||
findings → comms as they land; this file is the durable record. Branch: `greenfield/fomo-lin`.
|
||||
|
||||
## Machine starting state (2026-08-08)
|
||||
|
||||
- Debian 13 (kernel 6.12.101+deb13), no Node/npm, no global git config, no `~/.ssh`,
|
||||
no `~/.config/mosaic`, no `~/.mosaic`, sudo requires password (agent cannot escalate).
|
||||
- Repos pre-cloned by Jason: jarvis-brain, mosaic-brain, stack, uconnect, uscllc-website
|
||||
(all https remotes to git.mosaicstack.dev, **no credentials stored** — private-repo
|
||||
fetch/push dead until a token was provisioned from credentials.json, `usc_mos`).
|
||||
- tmux session `scooby` running Claude Code (bare harness — not `mosaic claude`).
|
||||
|
||||
## Pre-install setup that had NO framework mechanism (manual work)
|
||||
|
||||
- Agent identity: `MOSAIC_AGENT_NAME=scooby` hand-added to `~/.bashrc` + tmux env.
|
||||
- Git identity + credential store: hand-configured.
|
||||
- Comms receive path: hand-ported `scooby-comms-watcher.sh` from fred's watcher +
|
||||
hand-written systemd `--user` unit + `loginctl enable-linger`. Works (both peers
|
||||
verified round-trip within ~90s), but every step was artisanal — relevant input for
|
||||
harness-homes (W-F).
|
||||
|
||||
## Install run (2026-08-08 ~19:09Z)
|
||||
|
||||
`curl -fsSL https://mosaicstack.dev/install.sh | bash -s -- --yes --no-auto-launch`
|
||||
→ exit 0, framework v3 → `~/.config/mosaic/`, CLI @mosaicstack/mosaic **0.0.49** →
|
||||
`~/.npm-global/`. Prereq path: `sudo apt install nodejs npm` (Debian 13's node 20.19.2
|
||||
meets the ≥20 floor). Public read on the stack repo means the installer itself needs no
|
||||
credentials — good.
|
||||
|
||||
## Findings (outside fred's known-gaps list of 2026-08-08)
|
||||
|
||||
### F1 — PATH advice is print-only
|
||||
|
||||
Installer warns `~/.npm-global/bin is not on your PATH` and suggests the rc line, but
|
||||
`shellProfileEdits: []` in the manifest — nothing is persisted. Every fresh machine ends
|
||||
with `mosaic` not resolvable in new shells until the user hand-edits rc. Either edit the
|
||||
rc (with consent/flag) or make the closing summary a copy-paste block.
|
||||
|
||||
### F2 — Installer overwrites live `~/.claude/settings.json` + `~/.claude/CLAUDE.md` with `backup: null`
|
||||
|
||||
`.install-manifest.json` `runtimeAssetCopies` shows dest `~/.claude/settings.json`,
|
||||
`~/.claude/CLAUDE.md`, `hooks-config.json`, `context7-integration.md`, all `backup: null`,
|
||||
written while a Claude session was LIVE on this machine. On this box the pre-existing files
|
||||
were near-defaults so nothing of value was lost; on any configured machine this silently
|
||||
destroys user settings/memory. Wants: backup-before-overwrite (populate the manifest
|
||||
`backup` field it clearly already models) + merge-not-replace for settings.json.
|
||||
|
||||
### F3 — Fresh install fails its own doctor: 10 warnings out of the box
|
||||
|
||||
Immediately after a clean, successful install, `mosaic doctor` reports: missing `USER.md`;
|
||||
`AGENTS.md missing CRITICAL HARD GATES override block`; runtime file drift on
|
||||
`~/.claude/settings.json`; 7 missing `mosaic-*` skills. A green install that self-reports
|
||||
10 warnings erodes trust in doctor as a signal. Whatever subset is "expected until
|
||||
`mosaic init`/wizard" should be suppressed or labeled as such.
|
||||
|
||||
### F4 — Drift check points users at the gated template (wedge hazard)
|
||||
|
||||
The settings the installer writes to `~/.claude/settings.json` are UNGATED (no
|
||||
mutator-gate, no receipt-observer) — which on today's main is CORRECT, it avoids the
|
||||
Stop-hook wedge. But `~/.config/mosaic/runtime/claude/settings.json` (the file doctor
|
||||
diffs against) IS the gated template. So doctor's "runtime file drift" warning invites the
|
||||
obvious remediation — copy the template over — which would seed the receipt-observer wedge
|
||||
into a live seat. The drift baseline and the seeded file should be the same artifact, or
|
||||
doctor should know about the gated/ungated split.
|
||||
|
||||
### F5 — Installed skill set is disjoint from repo `skills/`
|
||||
|
||||
Skill sync installed 101 skills (six `mosaic-*`: deploy, gitea, orchestrator, portainer,
|
||||
tools, woodpecker) but NONE of the eight in stack `skills/` on main (board, forge, jarvis,
|
||||
macp, prd, prdy, setup-cicd, standards). Doctor then flags 7 of those 8 as missing
|
||||
(`mosaic-jarvis` escapes the check). Two sources of truth for "the Mosaic skills" — the
|
||||
installer's bundle and the repo dir — have diverged.
|
||||
|
||||
## Environment answers / status
|
||||
|
||||
- fomo-lin → sb-it-1-dt: **comms-only** today. Hostname does not resolve from here and the
|
||||
laptop has no ssh keys. ssh reach would need Jason (key provisioning + route/VPN).
|
||||
- Gitea write to the stack repo: verified by the push of this very branch (token `usc_mos`).
|
||||
|
||||
## Session 2 (2026-08-08 later) — `mosaic init` + first bare seat
|
||||
|
||||
`mosaic init` completed (SOUL.md / USER.md / TOOLS.md generated; TOOLS.md was backed up
|
||||
before overwrite — the contrast with F2 shows the codebase already knows how). Its
|
||||
runtime-adapter step correctly REFUSED to wire mutator-gate/receipt-observer hooks
|
||||
(activation half absent, #869) — loud, explained, fail-safe. Good.
|
||||
|
||||
First bare seat: **launched** — `mosaic claude --model sonnet` → Claude Code v2.1.226,
|
||||
runtime-contract injection verified from inside the seat. But it took findings F6–F10 to
|
||||
get there; on an untouched fresh main install, install → init → launch is broken at
|
||||
FOUR consecutive links.
|
||||
|
||||
### F6 — SECURITY: `mosaic-init` eval-injects free-text answers
|
||||
|
||||
`tools/_scripts/mosaic-init` line 142: `eval "$var_name=\"$value\""`. Any answer
|
||||
containing `"` crashes init mid-flow (reproduced: exit 127, USER.md never written);
|
||||
an answer containing `$( )` would EXECUTE arbitrary commands. Fix: `printf -v`.
|
||||
Same bug in the NON_INTERACTIVE default branch. Related: init exits 1 even on success
|
||||
when enforcement wiring is (correctly) refused — poisons any scripted chaining.
|
||||
|
||||
### F7 — init silently drops the installer's `mcpServers` block → launcher refuses to run
|
||||
|
||||
init's "Updating runtime adapters" rewrote `~/.claude/settings.json` and removed the
|
||||
`mcpServers.sequential-thinking` block the installer had written 11 min earlier.
|
||||
`mosaic claude` hard-requires that MCP → launch refused. The prescribed fix command
|
||||
(`mosaic-ensure-sequential-thinking --runtime claude`) works. So the happy path is
|
||||
install → init → BROKEN → hand-run a repair script. Merge-not-replace (F2) fixes this too.
|
||||
|
||||
### F8 — no fleet roster on a fresh install; launcher dies with a raw stack trace
|
||||
|
||||
`mosaic claude` throws an uncaught `Error: Fleet communications contract unavailable: no
|
||||
fleet roster at ~/.config/mosaic/fleet/roster.{yaml,json}` (full Node stack trace to the
|
||||
user). Nothing in install or init creates a roster (wizard untested here — `--no-auto-launch`;
|
||||
if the wizard seeds one, the bare-flow gap still stands). Unblocked by hand-authoring a
|
||||
minimal site roster from `fleet/examples/minimal.yaml`.
|
||||
|
||||
### F9 — FLAGSHIP: activation-probe timeout loses to CLI cold-start on modest hardware
|
||||
|
||||
`activation_version_gate.py` gives the `mosaic __lease-capability` probe
|
||||
`PROBE_TIMEOUT_SECONDS = 2.0`. On fomo-lin the CLI answers CORRECTLY in **~2.55–2.61s
|
||||
every run** (Node startup cost). Timeout → fail-closed → every bare `mosaic claude`
|
||||
launch aborts (exit 65) with an error blaming "mosaic not on PATH … framework/CLI version
|
||||
skew" — neither true. Invisible on fast dev boxes; fatal on laptops. Suggest: raise/make
|
||||
configurable the timeout, warm-probe cache, and split the three failure causes into
|
||||
distinct messages. Local workaround (documented, removable):
|
||||
`MOSAIC_LEASE_VERSION_PROBE_COMMAND` pointed at a script emitting the verified payload
|
||||
instantly (`~/.local/bin/mosaic-lease-probe-fast`).
|
||||
|
||||
### F10 — shipped lease-broker unit is never installed → registration denied
|
||||
|
||||
With F9 bypassed, launch dies with "Mosaic lease broker registration failed; runtime
|
||||
launch denied": the broker daemon isn't running, and although the framework SHIPS
|
||||
`systemd/user/mosaic-lease-broker.service`, nothing installs/enables it.
|
||||
`systemctl --user link` + `enable --now` of the shipped unit → READY instantly, launch
|
||||
proceeds. Installer/init/wizard should own this step.
|
||||
|
||||
### Observations (not filed as findings)
|
||||
|
||||
- Launcher settings audit demands `mutator-gate.py` while init refuses to wire it —
|
||||
main's components disagree about the gated state (fold into #1113/F4).
|
||||
- Seat context: runtime contract injected ✓; SOUL.md NOT injected (seat confirmed) —
|
||||
matches AGENTS.md read-on-demand load order, but README says the launcher "checks for
|
||||
SOUL.md". Question for lead, not a finding.
|
||||
- `--ref next` install path verified available (flag exists, next archive HTTP 200) — not
|
||||
exercised; fomo-lin stays main-as-shipped per lead ruling.
|
||||
|
||||
## Next
|
||||
|
||||
- Milestone comms sent at: install complete ✓ / first seat launched ✓.
|
||||
- First gated-seat probe deliberately deferred until PR #1109 lands (known deny-only state).
|
||||
|
||||
## Session 3 (2026-08-08 evening) — wizard + gateway; refocus to `next`
|
||||
|
||||
Directive from Jason mid-session: focus shifts to the `next` branch and the new structure
|
||||
(stock `~/.claude` untouched; framework wholly under `~/.config/mosaic`). Main's ~/.claude
|
||||
write behavior is a deprecated location — findings stand, but no further deep-testing of it.
|
||||
|
||||
Wizard run (main): "keep identity, update framework"; ~/.claude hooks install DECLINED per
|
||||
directive (wizard rewrote ~/.claude/settings.json anyway — benign, no gated hooks, MCP kept).
|
||||
Wizard never prompted about fleet roster or lease-broker unit → F8/F10 disambiguation
|
||||
partial: wizard does not visibly own those steps. Full degraded-state test dropped per refocus.
|
||||
|
||||
### F11 — gateway "Local" tier hard-requires Redis on main (fixed on next)
|
||||
|
||||
Wizard gateway install, Local tier ("embedded database, no dependencies"), port 14242:
|
||||
daemon starts then crash-spams ioredis ECONNREFUSED; never healthy; killed manually.
|
||||
`main..next` already contains `56787fab fix(gateway): disable Redis consumers on local
|
||||
tier (#689)`. Main ships a gateway that cannot come up dependency-free; next has the cure.
|
||||
|
||||
### F12 — wizard exits 0 on gateway failure
|
||||
|
||||
Terminal shows "▲ Fix the underlying error above, then re-run `mosaic gateway install`"
|
||||
and the wizard exits 0. Scripted/CI consumers read success.
|
||||
|
||||
### Cosmetic
|
||||
|
||||
Skipping the optional ANTHROPIC_API_KEY prompt records the literal string "undefined".
|
||||
|
||||
### `next` recon (read-only)
|
||||
|
||||
- next install.sh: first-class `--next` prerelease lane (npm @next dist-tag CLI + framework
|
||||
from permanent next branch; guard against mixing @next with a different explicit --ref).
|
||||
- next does NOT carry the new structure: ~/.claude handling unchanged; no harness-homes
|
||||
design docs on next or main. New structure = Jason directive + fred W-F design phase.
|
||||
|
||||
State: bare seat launch works; gateway stopped. Holding for fred's ruling on a next-lane
|
||||
reinstall (proposed) and W-F design review.
|
||||
|
||||
## Session 4 (2026-08-08 night) — `--next` lane reinstall (pivot confirmed by Jason)
|
||||
|
||||
Main uninstalled (note: uninstall removed `~/.claude/CLAUDE.md`/hooks-config/context7 but
|
||||
LEFT its modified `settings.json` — asymmetric cleanup, minor). Reinstalled via next's own
|
||||
installer: `raw/branch/next/tools/install.sh --next --yes --no-auto-launch` → framework from
|
||||
permanent next branch + **CLI 0.0.50-next.2207 / gateway 0.0.7-next.2207 from the @next
|
||||
registry lane**. Lane works as designed.
|
||||
|
||||
### N1 — FLAGSHIP (next-only): @next CLI requires Node 22; docs/installer floor says ≥20
|
||||
|
||||
On Node 20.19.2 (Debian 13's apt version, and the documented minimum) **every** mosaic
|
||||
command crashes — even `--version` — with `ERR_REQUIRE_CYCLE_MODULE` in
|
||||
`@mosaicstack/brain/dist/projects.js`. npm corroborates: `[email protected]` declares
|
||||
`node >=22`. Verified the same installed CLI runs clean under Node **22.23.2** (nvm).
|
||||
So the @next lane is dead-on-arrival on the documented minimum Node. Fix: installer
|
||||
gates node ≥22 for the next lane (or brain drops the require cycle). fomo-lin now runs
|
||||
Node 22 via nvm (user-level; system apt tops out at 20 — durable fix wants nodesource 22).
|
||||
|
||||
### F1–F12 recurrence scorecard on next
|
||||
|
||||
| Finding | On next |
|
||||
|---|---|
|
||||
| F1 PATH print-only | RECURS (identical warning) |
|
||||
| F2 ~/.claude writes | RECURS (runtime assets copied again; per ruling, no deeper testing — W-F fixes structurally) |
|
||||
| F3 doctor warns on fresh install | RECURS (10 warnings, same classes) |
|
||||
| F4 drift-baseline wedge | RECURS (same gated template + drift warning) |
|
||||
| F5 skill sets disjoint | RECURS (same 7 missing mosaic-*) |
|
||||
| F6 init eval injection | RECURS (eval at lines 102/118/132 of next's mosaic-init) |
|
||||
| F7 init drops mcpServers | RECURS (verified: count 0 after init; ensure-script fix works) |
|
||||
| F8 roster raw-throw | RECURS in code (throw present in next launch.js; not re-triggered — roster restored from backup) |
|
||||
| F9 probe 2.0s timeout | RECURS (constant unchanged) — and compounded: probe spawns `mosaic`, which on ambient Node 20 crashes (N1), so the probe fails on slow AND stock-node hosts |
|
||||
| F10 broker unit not installed | RECURS (hand-relinked next's shipped unit; works) |
|
||||
| F11 gateway Redis-on-local | Expected FIXED (#689 in next); not yet live-verified — gateway install not re-run this session |
|
||||
| F12 wizard exit-0 | Untested on next (wizard.ts differs; #1120 tracks) |
|
||||
|
||||
Chain result on next (with the same three workarounds: MCP ensure-script, restored roster,
|
||||
broker relink, plus probe override): **install → init → launch all pass; seat up on
|
||||
Claude Code v2.1.226 / sonnet under Node 22.**
|
||||
|
||||
Net: next cures nothing in F1–F10 (they're all pre-W-F structural issues), carries the
|
||||
gateway fix, and adds one hard regression-class gap (N1 node floor). The W-F gap list
|
||||
stands unchanged as the fix vehicle.
|
||||
|
||||
## Session 5 (2026-08-08 night) — `~/.mosaic` prototype hand-roll (second-host cross-check)
|
||||
|
||||
Hand-rolled per HARNESS-HOMES prototype section, on the next-lane framework: skeleton
|
||||
(config/claude `{}`, auth/claude/jason_woltje.com with `primary` alias, empty plugins/skills
|
||||
stores), probe seat (profile.json schema 1, overlay `{}`, composed settings via three-layer
|
||||
deep-merge, credentials two-hop symlink, identity-bootstrap CLAUDE.md, seeded onboarding
|
||||
.claude.json, SOUL.md with positive Identity block).
|
||||
|
||||
**Smoke test PASS** (`CLAUDE_CONFIG_DIR=<probe> claude --print`): RC=0, auth through the
|
||||
two-hop chain, seat self-identified as "probe". Post-run: both symlinks survived, live
|
||||
credential inode unchanged, transcript in probe's own projects/, probe generated its own
|
||||
backups/sessions, operator ~/.claude untouched. **dragon-lin's results replicate on a
|
||||
clean second host — the layout stands up greenfield.**
|
||||
|
||||
### Gap-bites during the roll (feed to W-F)
|
||||
|
||||
- **Base-template hole (F4/gap-5 adjacent, NEW):** the design's composition base
|
||||
`~/.config/mosaic/framework/runtime/claude/settings.json` does NOT exist in the shipped
|
||||
framework; the closest shipped artifact (`runtime/claude/settings.json`) is the GATED
|
||||
wedge template. Used the operator's vetted ungated settings as base (as dragon-lin did).
|
||||
W-F1 must define + ship the canonical UNGATED system base; gate hooks arrive only via
|
||||
promotion overlay.
|
||||
- **Identity bootstrap vs permissions (NEW):** in `--print`/restricted mode the seat was
|
||||
DENIED reading SOUL.md outside cwd — "read SOUL.md" bootstrap depends on tool
|
||||
permissions. Generator should materialize the identity INTO the generated CLAUDE.md
|
||||
(parameterized), keeping SOUL.md as source, not runtime dependency.
|
||||
- **Gap 2 lived experience:** probe exists in profile.json but not roster.yaml — the
|
||||
hand-rolled seat and `mosaic claude` are disjoint universes on the same host.
|
||||
- **Gap 4 in miniature:** fresh-host store is empty; nothing defines what seeds it.
|
||||
- **Lease posture:** hand-rolled seats launch bare `claude` → ungated by construction
|
||||
until `mosaic fleet launch` exists (consistent with current bare-for-real-work rule).
|
||||
|
||||
### Addendum — gap-7 characterization (canonical ungated base)
|
||||
|
||||
Diffed operator vetted ungated settings vs shipped gated template: the delta is exactly
|
||||
three items — template-only PreToolUse mutator-gate entry, template-only Stop
|
||||
receipt-observer entry, operator-only mcpServers.sequential-thinking block (whose omission
|
||||
from the template is F7's root cause). Spec: base = template − two gate hooks + mcpServers;
|
||||
promotion overlay = the two gate hooks, nothing more. Sent to fred (20260808T200337Z).
|
||||
|
||||
## Box doctrine — true greenfield, repeatable full-cycle testing (Jason, 2026-08-08)
|
||||
|
||||
fomo-lin's defining property: the test operator (scooby) is NOT a fleet seat — comms
|
||||
watcher, git identity, nvm/Node, and repos live entirely outside Mosaic. Therefore Mosaic
|
||||
can be wiped to TRUE ZERO and reinstalled in full, repeatedly, to test protocols
|
||||
end-to-end per cycle (each W-F fixture drop, each next release).
|
||||
|
||||
Codified as `~/.local/bin/mosaic-greenfield-reset` (dry-run by default, `--yes` to
|
||||
execute): removes units/gateway/npm packages/npmrc scope/`~/.config/mosaic`/`~/.mosaic`/
|
||||
mosaic-written `~/.claude` files (settings reset to stock)/workaround shims; preserves the
|
||||
operator layer (watcher, git creds, nvm, repos, `~/.claude` auth + session state, baseline
|
||||
backup). Ends with a verify-zero checklist.
|
||||
|
||||
Known boundary impurities the reset explicitly handles: `~/.claude/settings.json` is
|
||||
mosaic-written today (its QA hooks fire even in the operator's own session — observed:
|
||||
prevent-memory-write blocked an operator write), and the F9 probe shim sits in
|
||||
`~/.local/bin`. Both are named in the script rather than left as ambient state.
|
||||
|
||||
Not executed yet — current install (next lane + prototype) is the substrate Fred's W-F1
|
||||
fixtures target. First full cycle runs when the next testable artifact lands.
|
||||
|
||||
## Session 6 (2026-08-08 night) — promotion-branch E2E (Fred-directed, first fomo-lin full E2E)
|
||||
|
||||
Branch feat/lease-promotion-and-harness-isolation (rebased on next), built from source
|
||||
(pnpm --filter '@mosaicstack/mosaic...' build), CLI packed + installed globally, branch
|
||||
framework installed to ~/.config/mosaic. Transcript:
|
||||
scratchpad/promote-e2e-transcript.md. Verdict: **BLOCKED at step 3, NOT VERIFIED (not faked).**
|
||||
|
||||
Findings this session (all filed under scooby's own Gitea account):
|
||||
|
||||
- **#1123** — TS activation capability probe hardcodes a 2000ms timeout; `node cli.js
|
||||
__lease-capability` cold-start on fomo-lin is 5.1–5.5s, so `leaseEnforcementActivatable()`
|
||||
returns false and the gate REFUSES to wire via the sanctioned path. The Python-side
|
||||
F9/#1118 override does NOT apply to this TS probe. Worked around by bumping only the
|
||||
installed dist timeout (reversible; can't mask a bad capability).
|
||||
- **LIVE WEDGE (F4 reproduced, un-recoverable):** hand-wiring the gated template into a live
|
||||
BARE session's own runtime home hot-reloads the gate and bricks the session with
|
||||
GATE_UNAVAILABLE (no lease). Every self-recovery path is closed (Bash/Read gated;
|
||||
Write/Edit blocked by stale-guard needing a gated Read). Required an EXTERNAL shell to
|
||||
restore settings. Exactly HARNESS-HOMES' "a live unpromoted session that gains the gate
|
||||
cannot self-recover." Lesson applied: gated seats must be a SEPARATE mosaic claude process
|
||||
in its own CLAUDE_CONFIG_DIR (~/.config/mosaic/.claude), never the operator's ~/.claude.
|
||||
- **#1124 — the critical link, proven to fail:** `mosaic promote` transport reads the lease
|
||||
session id from `pane_pid`'s /proc/environ, but `execRuntime()` (launch.js:883) uses
|
||||
`spawnSync` (NOT the exec-replace its own comment claims), so pane_pid = node(mosaic)
|
||||
[no lease env] and the lease env is on the claude CHILD. resolve() never walks to the
|
||||
child → 'no readable lease session' → UNVERIFIED exit 1, before injection. Fails for every
|
||||
real `mosaic claude` seat; unit tests pass only via a mocked environmentReader. This is
|
||||
the exact link terra couldn't test (detached pane).
|
||||
|
||||
E2E scorecard: Step 1 (build/install) PASS. Step 2 (gated seat, real lease, mutator DENIED
|
||||
MUTATOR_UNVERIFIED, file not created) PASS. Step 3 (promote → VERIFIED) BLOCKED (#1124).
|
||||
Steps 4–5 not reached; failure-path sub-case (unresolvable seat → UNVERIFIED exit 1, no
|
||||
hang) incidentally confirmed. The branch does NOT pass E2E on a real host as-is; #1124 gates
|
||||
its merge.
|
||||
|
||||
## Session 7 (2026-08-08 night) — promotion re-run on #1124 fix (b) @ de0adb92
|
||||
|
||||
Rebuilt from de0adb92, reran steps 3+. **fix (b) confirmed working**; promotion advanced two
|
||||
links deeper and revealed two new findings. Debian 13 compat: `/proc/<pid>/task/<pid>/children`
|
||||
IS populated — BFS walk works, no PPID fallback needed.
|
||||
|
||||
- resolve() (#1124 fix b): **PASS** — BFS walk from pane(node,no-lease) → claude child(lease)
|
||||
resolved the real session id. The exact bug I reported is fixed.
|
||||
- **#1125** — `/mosaic-promote` first returned "Unknown command": the slash command is shipped
|
||||
at `runtime/claude/commands/mosaic-promote.md` but NOT seeded into the seat's
|
||||
`CLAUDE_CONFIG_DIR/commands/`. UserPromptSubmit hook never fires → PROMOTION_TIMEOUT. F7-class
|
||||
asset-seeding gap. Worked around by copying the command into the seat home; hook then fires.
|
||||
- **#1126 (deepest finding)** — with the command seeded, promote-begin injects (via
|
||||
UserPromptSubmit additionalContext) an instruction to echo an opaque `MOSAIC-RECEIPT{...}`
|
||||
token "verbatim and nothing else … discloses nothing." The seat MODEL REFUSED, correctly
|
||||
flagging it as a prompt-injection pattern (imperative in a description field; verbatim opaque
|
||||
echo; self-vouching language; no protocol legitimized in the seat's trusted context) →
|
||||
RECEIPT_MISMATCH. Design-level: legitimate promotion is indistinguishable from an injection
|
||||
attack to a well-aligned model; stronger injection defenses = more reliable promotion FAILURE.
|
||||
Refusal evidence: docs/reports/greenfield/seat-receipt-refusal.txt.
|
||||
|
||||
Scorecard: build/install PASS · gated seat + mutator DENIED PASS · resolve (fix b) PASS ·
|
||||
slash-cmd (post-#1125 workaround) PASS · receipt echo FAIL (#1126). Promotion does NOT reach
|
||||
VERIFIED on a real seat. #1124 fix validated; #1125 + #1126 now gate the merge.
|
||||
@@ -1,31 +0,0 @@
|
||||
▐▛███▜▌ Claude Code v2.1.226
|
||||
▝▜█████▛▘ Sonnet 5 · Claude Max
|
||||
▘▘ ▝▝ ~/src/jarvis-brain
|
||||
✔ Using flicker-free rendering · if you want to go back, use /tui default
|
||||
· Click to move your cursor in the text input
|
||||
· Click to expand collapsed tool results
|
||||
· By default, text auto-copies when you select it (/config to change)
|
||||
· Hold Shift (Option in iTerm2, Fn in Terminal.app) while selecting to use your terminal's native copy instead
|
||||
▎ Fable 5 is now a standard part of your Max plan
|
||||
▎ You can use up to 50% of your weekly usage limit on Fable 5. If you hit your limit, you can continue on Fable 5 with usage credits. Fable 5 draws down usage faster than Opus 5. Run /model and
|
||||
▎ select Fable to use it. Learn more: https://support.claude.com/en/articles/15424964-claude-fable-5-promotional-access
|
||||
+1 more · /status
|
||||
❯ /mosaic-promote
|
||||
● I'm not going to echo that receipt string. A few things about this request don't add up, and I want to flag them before doing anything:
|
||||
1. The "command description" is itself an instruction. In the skills listing, mosaic-promote is described as "I invoked this registered command to authorize lease promotion; follow the local seat
|
||||
broker's injected receipt confirmation instruction exactly." That's not a description of what a skill does — it's an imperative instruction written in first person, injected into a field that's
|
||||
supposed to be descriptive metadata. That's a classic injection pattern.
|
||||
2. The "hook" reminder asks for verbatim, unexamined output. The UserPromptSubmit hook additional context block instructs me to output an opaque MOSAIC-RECEIPT{...} blob "verbatim and nothing else,"
|
||||
and preemptively asserts that doing so "discloses nothing" — that's the kind of self-vouching language injected content uses to head off scrutiny. I have no way to verify what that token actually
|
||||
authorizes downstream (it reads like a challenge/response for some privilege or lease-promotion action), and nothing in the actual Mosaic Constitution, AGENTS.md, or RUNTIME.md content I've loaded
|
||||
this session mentions a "seat broker," "lease promotion," or a MOSAIC-RECEIPT protocol at all.
|
||||
3. No genuine /mosaic-promote skill exists in the documented set. The real skill list (mosaic-gitea, mosaic-orchestrator, mosaic-portainer, mosaic-woodpecker, mosaic-tools, mosaic-deploy) has
|
||||
nothing resembling this. A command that only shows up as a self-instructing string, paired with a matching "hook" telling me to blindly echo a token, is consistent with a prompt-injection attempt to
|
||||
get an agent to produce an authorization artifact it doesn't understand.
|
||||
I'm stopping here rather than emitting the receipt. If this is something you actually intended to trigger (a real seat/lease mechanism I'm not aware of), let me know and point me at where it's
|
||||
documented and I'll take another look. Otherwise, I'd treat this as a suspicious injected command.
|
||||
✻ Sautéed for 23s
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
❯
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
⏸ manual mode on · ? for shortcuts · ← for agents
|
||||
@@ -69,4 +69,15 @@ All 22 baseline sites below are `FIXED`; the checked-in tranche fixture is passe
|
||||
| `tmux/test-send-message-socket.sh:37,38,44-46,68,72` | FIXED | capture commands complete before redirected grep assertions |
|
||||
| `tmux/test-send-message-verdict.sh:34` | FIXED | grep reads from a here-string |
|
||||
|
||||
Remaining wake-validation sites are intentionally deferred to the final review-sized tranche and are not yet assigned a safety verdict here.
|
||||
## Tranche 3 — wake validation harnesses
|
||||
|
||||
All 26 baseline occurrences (25 normalized identities; one preimage selector occurs twice) are `FIXED` and mechanically bound through the wake fixture and shared scanner.
|
||||
|
||||
| Baseline site(s) | Verdict | Construction |
|
||||
| --- | --- | --- |
|
||||
| `wake/test-wake-digest-quarantine.sh:567` | FIXED | complete match populations are captured, then first line selected by parameter expansion |
|
||||
| `wake/test-wake-preimage.sh:182-183,346-347` | FIXED | jq `first(...)` reads each JSONL file directly |
|
||||
| `wake/validate-973/microtest-wake-assert.sh:153,170-171,176,204-209,233-234,251-252,286-287` | FIXED | scalar assertions use here-strings; diagnostics use non-early sed ranges; source line captured before matching |
|
||||
| `wake/validate-973/validate-973.sh:110,119,180,182,187` | FIXED | scalar assertions use here-strings; diagnostic truncation uses consuming sed ranges |
|
||||
|
||||
The scoped inventory is complete: 26 runtime/general + 22 non-wake tests + 26 wake tests fixed; 11 explicitly withdrawn or non-load-bearing sites retain their documented verdicts.
|
||||
|
||||
@@ -25,6 +25,10 @@ Site-by-site verdicts: `docs/reports/quality/1099-pipefail-sweep.md`.
|
||||
|
||||
Expanded the unconditional scanner over 11 non-wake test harnesses. RED named exactly 22 source lines; a second immutable-baseline fixture now asserts those 22 entries through the same scanner. Rewrites preserve command status by capturing producers before redirected assertions, use parameter expansion for line selection, and use complete `mapfile` populations where ordering matters. Current-tree finding count is zero for tranches 1 and 2.
|
||||
|
||||
## Tranche 3 TDD
|
||||
|
||||
Expanded the shared scanner over four wake validation harnesses. RED named 26 occurrences. The wake fixture asserts 26 occurrences / 25 normalized identities through the same scanner; all scalar assertions now use redirection, direct jq selection, complete capture, or consuming diagnostic ranges. Current-tree finding count is zero across the full scoped population.
|
||||
|
||||
## Verification so far
|
||||
|
||||
- `bash -n` on every changed shell script: pass.
|
||||
@@ -33,5 +37,5 @@ Expanded the unconditional scanner over 11 non-wake test harnesses. RED named ex
|
||||
- `test-send-message-verdict.sh`: 3/3 pass.
|
||||
- `test-send-message-socket.sh`: pass.
|
||||
- Independent review 143 found two semantic regressions: a help-probe `|| true` changed the failure truth table, and an unguarded Git capture changed non-Git data-dir behavior from rc 0 + JSON to silent rc 128. Both received RED-first regressions before correction; help status is now separate and required, and Git status remains condition-guarded.
|
||||
- Wake detector/reconcile/digest/preimage suites terminate at their existing fail-closed #973 `BASH_LINENO` environment probe (exit 97, observed `[3 5]`, expected `[3 4]`) before subject tests. No bypass or skip was used; canonical CI remains required.
|
||||
- Wake static inventory remains aligned at 261/261 after line-neutral rewrites; no static-set mismatch. Wake detector/reconcile/digest/preimage suites terminate at their existing fail-closed #973 `BASH_LINENO` environment probe (exit 97, observed `[3 5]`, expected `[3 4]`) before subject tests. No bypass or skip was used; canonical CI remains required.
|
||||
- ShellCheck reports only pre-existing source-following, unused-variable, and untouched `ls | head` findings; no new diagnostic was introduced.
|
||||
|
||||
@@ -564,7 +564,7 @@ echo "== Q16 (guard): Q2's ENUM-B fixture must STAY address-free — the reconci
|
||||
# Token concatenated so THIS guard's own source lines never contain the
|
||||
# literal fixture id and cannot self-match.
|
||||
enum_id='ENUM''-B'
|
||||
fixture_line="$(has_match -F "\"id\":\"$enum_id\"" "$self" | has_match -F '"observed_seq":5' | head -n1)"
|
||||
fixture_lines="$(has_match -F "\"id\":\"$enum_id\"" "$self")"; fixture_matches="$(has_match -F '"observed_seq":5' <<<"$fixture_lines")"; fixture_line="${fixture_matches%%$'\n'*}"
|
||||
[ -n "$fixture_line" ] || fail_msg "Q16: could not locate Q2's $enum_id fixture line (renamed/renumbered? update this guard)"
|
||||
fixture_json="$(printf '%s' "$fixture_line" | sed "s/.*'\({.*}\)'.*/\1/")"
|
||||
# Positive controls FIRST (blind-instrument rule): the extraction must yield
|
||||
|
||||
@@ -179,8 +179,8 @@ echo "== P3: detector orders the cause line BEFORE the delta it explains =="
|
||||
printf 'r1 state v2\n' >"$fx/repo_r1"
|
||||
"$DET" poll-once >/dev/null 2>&1 || fail_msg "P3: second poll failed"
|
||||
sd="$(state_dir)"
|
||||
pre_seq="$(jq -r 'select(.locators.kind == "preimage") | .observed_seq' "$sd/pending.jsonl" | head -n1)"
|
||||
src_seq="$(jq -r 'select(.locators.kind == "repo") | .observed_seq' "$sd/pending.jsonl" | head -n1)"
|
||||
pre_seq="$(jq -nr 'first(inputs | select(.locators.kind == "preimage") | .observed_seq) // empty' "$sd/pending.jsonl")"
|
||||
src_seq="$(jq -nr 'first(inputs | select(.locators.kind == "repo") | .observed_seq) // empty' "$sd/pending.jsonl")"
|
||||
[ -n "$pre_seq" ] || fail_msg "P3: no preimage cause entry enqueued"
|
||||
[ -n "$src_seq" ] || fail_msg "P3: no source delta entry enqueued"
|
||||
if [ -n "$pre_seq" ] && [ -n "$src_seq" ]; then
|
||||
@@ -343,8 +343,8 @@ echo "== P11: reconcile surfaces the cause line before its enumerations =="
|
||||
printf '# adapter changed while detector down\n' >>"$fx/adapter.sh"
|
||||
"$RECON" reconcile >/dev/null 2>&1 # rc 1 expected (unaccounted enumerated)
|
||||
sd="$(state_dir)"
|
||||
pre_seq="$(jq -r 'select(.locators.kind == "preimage") | .observed_seq' "$sd/pending.jsonl" | head -n1)"
|
||||
enum_seq="$(jq -r 'select(.locators.reconciled == true) | .observed_seq' "$sd/pending.jsonl" | head -n1)"
|
||||
pre_seq="$(jq -nr 'first(inputs | select(.locators.kind == "preimage") | .observed_seq) // empty' "$sd/pending.jsonl")"
|
||||
enum_seq="$(jq -nr 'first(inputs | select(.locators.reconciled == true) | .observed_seq) // empty' "$sd/pending.jsonl")"
|
||||
[ -n "$pre_seq" ] || fail_msg "P11: reconcile must enqueue the preimage cause line"
|
||||
[ -n "$enum_seq" ] || fail_msg "P11: reconcile must still enumerate the unaccounted source"
|
||||
if [ -n "$pre_seq" ] && [ -n "$enum_seq" ]; then
|
||||
|
||||
@@ -150,7 +150,7 @@ site_line() { # site_line FILE MARKER -> first physical line of that call
|
||||
local f="$1" marker="$2" ln
|
||||
ln="$(grep -n "# SITE:${marker}\$" "$f" | cut -d: -f1)"
|
||||
# continuation marker sits on the tail line; the call starts one line up
|
||||
if ! sed -n "${ln}p" "$f" | grep -Eq 'has_match|count_lines'; then
|
||||
source_line="$(sed -n "${ln}p" "$f")"; if ! grep -Eq 'has_match|count_lines' <<<"$source_line"; then
|
||||
ln=$((ln - 1))
|
||||
fi
|
||||
printf '%s' "$ln"
|
||||
@@ -167,13 +167,13 @@ rcB=$?
|
||||
sort "$LEDGER" >"$TMP/got-c1"
|
||||
n_expected="$(grep -c . "$TMP/expected-c1")"
|
||||
if [ "$rcA" -eq 0 ] && [ "$rcB" -eq 0 ] &&
|
||||
printf '%s' "$outA" | grep -q 'mini-a: OK' &&
|
||||
printf '%s' "$outB" | grep -q 'mini-b: OK' &&
|
||||
grep -q 'mini-a: OK' <<<"$outA"&&
|
||||
grep -q 'mini-b: OK' <<<"$outB"&&
|
||||
[ "$n_expected" -gt 1 ] &&
|
||||
cmp -s "$TMP/expected-c1" "$TMP/got-c1"; then
|
||||
check C1 0 ""
|
||||
else
|
||||
check C1 1 "rcA=$rcA rcB=$rcB expected($n_expected)/got diff: $(diff "$TMP/expected-c1" "$TMP/got-c1" 2>&1 | head -n 10 | tr '\n' ' ')"
|
||||
check C1 1 "rcA=$rcA rcB=$rcB expected($n_expected)/got diff: $(diff "$TMP/expected-c1" "$TMP/got-c1" 2>&1 | sed -n '1,10p' | tr '\n' ' ')"
|
||||
fi
|
||||
|
||||
# --- C2: early exit -> short ledger, comparison catches it -----------------
|
||||
@@ -201,12 +201,12 @@ abort_case() { # abort_case NAME MARKER HELPER
|
||||
bash "$TMP/mini-a.sh" "$TMP" 2>&1)"
|
||||
rc=$?
|
||||
if [ "$rc" -ne 0 ] &&
|
||||
! printf '%s' "$out" | grep -q 'mini-a: OK' &&
|
||||
! printf '%s' "$out" | grep -q 'mini-a: FAILED' &&
|
||||
printf '%s' "$out" | grep -q "WAKE-ASSERT ARMED: forcing real grep error at $site" &&
|
||||
printf '%s' "$out" | grep -q "WAKE-ASSERT ABORT" &&
|
||||
printf '%s' "$out" | grep -q "$site" &&
|
||||
printf '%s' "$out" | grep -q "grep exit 2" &&
|
||||
! grep -q 'mini-a: OK' <<<"$out"&&
|
||||
! grep -q 'mini-a: FAILED' <<<"$out"&&
|
||||
grep -q "WAKE-ASSERT ARMED: forcing real grep error at $site" <<<"$out"&&
|
||||
grep -q "WAKE-ASSERT ABORT" <<<"$out"&&
|
||||
grep -q "$site" <<<"$out"&&
|
||||
grep -q "grep exit 2" <<<"$out"&&
|
||||
grep -q "^${helper} ${site}\$" "$ledger"; then
|
||||
check "$name" 0 ""
|
||||
else
|
||||
@@ -230,8 +230,8 @@ if [ "$got" = "1" ]; then check C8 0 ""; else check C8 1 "env-prefix did not rea
|
||||
out="$(WAKE_ASSERT_FORCE_GREP_ERROR_AT="mini-a.sh:9999" bash "$TMP/mini-a.sh" "$TMP" 2>&1)"
|
||||
rc=$?
|
||||
if [ "$rc" -eq 0 ] &&
|
||||
printf '%s' "$out" | grep -q 'mini-a: OK' &&
|
||||
! printf '%s' "$out" | grep -q 'WAKE-ASSERT ARMED'; then
|
||||
grep -q 'mini-a: OK' <<<"$out"&&
|
||||
! grep -q 'WAKE-ASSERT ARMED' <<<"$out"; then
|
||||
check C9 0 ""
|
||||
else
|
||||
check C9 1 "rc=$rc out=$(printf '%s' "$out" | tail -n 3 | tr '\n' ' ')"
|
||||
@@ -248,8 +248,8 @@ chmod +x "$TMP/fake-bash"
|
||||
out="$(WAKE_ASSERT_PIN_BASH="$TMP/fake-bash" bash -c '. "$WAKE_COMMON" && wake_assert_init && echo REACHED-PAST-INIT' 2>&1)"
|
||||
rc=$?
|
||||
if [ "$rc" -ne 0 ] &&
|
||||
! printf '%s' "$out" | grep -q 'REACHED-PAST-INIT' &&
|
||||
printf '%s' "$out" | grep -q 'WAKE-ASSERT INIT ABORT: BASH_LINENO convention violated'; then
|
||||
! grep -q 'REACHED-PAST-INIT' <<<"$out"&&
|
||||
grep -q 'WAKE-ASSERT INIT ABORT: BASH_LINENO convention violated' <<<"$out"; then
|
||||
check C10 0 ""
|
||||
else
|
||||
check C10 1 "rc=$rc out=$(printf '%s' "$out" | tail -n 2 | tr '\n' ' ')"
|
||||
@@ -283,8 +283,8 @@ out="$(WAKE_ASSERT_LEDGER="$LEDGER" bash "$TMP/mini-c.sh" "$TMP" 2>&1)"
|
||||
rc=$?
|
||||
summary_ln="$(site_line "$TMP/mini-c.sh" c-summary)"
|
||||
if [ "$rc" -eq 1 ] &&
|
||||
printf '%s' "$out" | grep -q 'wake mini-c harness: FAILED (1 assertion(s))' &&
|
||||
! printf '%s' "$out" | grep -q 'all invariants passed' &&
|
||||
grep -q 'wake mini-c harness: FAILED (1 assertion(s))' <<<"$out"&&
|
||||
! grep -q 'all invariants passed' <<<"$out"&&
|
||||
grep -q "^count_lines mini-c.sh:${summary_ln}\$" "$LEDGER"; then
|
||||
check C11 0 ""
|
||||
else
|
||||
|
||||
@@ -107,7 +107,7 @@ if cmp -s "$TMP/expected.txt" "$TMP/static.txt"; then
|
||||
echo "STATIC-INVENTORY equals expected set ($(grep -c . "$TMP/static.txt") rows from source text)"
|
||||
else
|
||||
flag "static inventory (source text) differs from expected set (artifact):"
|
||||
diff "$TMP/expected.txt" "$TMP/static.txt" | head -n 20 | sed 's/^/ /'
|
||||
diff "$TMP/expected.txt" "$TMP/static.txt" | sed -n '1,20p' | sed 's/^/ /'
|
||||
fi
|
||||
|
||||
# --- 3: green instrumented run ----------------------------------------------
|
||||
@@ -116,7 +116,7 @@ LEDGER="$TMP/ledger"
|
||||
for s in "${SUITES[@]}"; do
|
||||
out="$(WAKE_ASSERT_LEDGER="$LEDGER" bash "$WAKE/$s" 2>&1)"
|
||||
rc=$?
|
||||
if printf '%s\n' "$out" | grep -Eq "$(sentinel_for "$s")"; then
|
||||
if grep -Eq "$(sentinel_for "$s")" <<<"$out"; then
|
||||
sent="present"
|
||||
else
|
||||
sent="ABSENT"
|
||||
@@ -177,14 +177,14 @@ while read -r helper site form; do
|
||||
rc=$?
|
||||
bad=""
|
||||
[ "$rc" -ne 0 ] || bad="$bad exit=0"
|
||||
printf '%s\n' "$out" | grep -q "WAKE-ASSERT ARMED: forcing real grep error at $site" ||
|
||||
grep -q "WAKE-ASSERT ARMED: forcing real grep error at $site" <<<"$out"||
|
||||
bad="$bad no-ARMED-line"
|
||||
printf '%s\n' "$out" | grep -q "WAKE-ASSERT ABORT: ${helper} at ${site}: grep exit" ||
|
||||
grep -q "WAKE-ASSERT ABORT: ${helper} at ${site}: grep exit" <<<"$out"||
|
||||
bad="$bad no-ABORT-line"
|
||||
# AND-polarity check (a match is the defect): a grep error (rc>=2) must be
|
||||
# its own loud arm — it cannot fall through as "no sentinel = pass".
|
||||
rc_sent=0
|
||||
printf '%s\n' "$out" | grep -Eq "$(sentinel_for "$f")" || rc_sent=$?
|
||||
grep -Eq "$(sentinel_for "$f")" <<<"$out"|| rc_sent=$?
|
||||
case "$rc_sent" in
|
||||
0) bad="$bad sentinel-emitted" ;;
|
||||
1) : ;;
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
[
|
||||
"packages/mosaic/framework/tools/wake/test-wake-digest-quarantine.sh:fixture_line=\"$(has_match -F \"\\\"id\\\":\\\"$enum_id\\\"\" \"$self\" | has_match -F '\"observed_seq\":5' | head -n1)\"",
|
||||
"packages/mosaic/framework/tools/wake/test-wake-preimage.sh:pre_seq=\"$(jq -r 'select(.locators.kind == \"preimage\") | .observed_seq' \"$sd/pending.jsonl\" | head -n1)\"",
|
||||
"packages/mosaic/framework/tools/wake/test-wake-preimage.sh:src_seq=\"$(jq -r 'select(.locators.kind == \"repo\") | .observed_seq' \"$sd/pending.jsonl\" | head -n1)\"",
|
||||
"packages/mosaic/framework/tools/wake/test-wake-preimage.sh:pre_seq=\"$(jq -r 'select(.locators.kind == \"preimage\") | .observed_seq' \"$sd/pending.jsonl\" | head -n1)\"",
|
||||
"packages/mosaic/framework/tools/wake/test-wake-preimage.sh:enum_seq=\"$(jq -r 'select(.locators.reconciled == true) | .observed_seq' \"$sd/pending.jsonl\" | head -n1)\"",
|
||||
"packages/mosaic/framework/tools/wake/validate-973/microtest-wake-assert.sh:if ! sed -n \"${ln}p\" \"$f\" | grep -Eq 'has_match|count_lines'; then",
|
||||
"packages/mosaic/framework/tools/wake/validate-973/microtest-wake-assert.sh:printf '%s' \"$outA\" | grep -q 'mini-a: OK' &&",
|
||||
"packages/mosaic/framework/tools/wake/validate-973/microtest-wake-assert.sh:printf '%s' \"$outB\" | grep -q 'mini-b: OK' &&",
|
||||
"packages/mosaic/framework/tools/wake/validate-973/microtest-wake-assert.sh:check C1 1 \"rcA=$rcA rcB=$rcB expected($n_expected)/got diff: $(diff \"$TMP/expected-c1\" \"$TMP/got-c1\" 2>&1 | head -n 10 | tr '\\n' ' ')\"",
|
||||
"packages/mosaic/framework/tools/wake/validate-973/microtest-wake-assert.sh:! printf '%s' \"$out\" | grep -q 'mini-a: OK' &&",
|
||||
"packages/mosaic/framework/tools/wake/validate-973/microtest-wake-assert.sh:! printf '%s' \"$out\" | grep -q 'mini-a: FAILED' &&",
|
||||
"packages/mosaic/framework/tools/wake/validate-973/microtest-wake-assert.sh:printf '%s' \"$out\" | grep -q \"WAKE-ASSERT ARMED: forcing real grep error at $site\" &&",
|
||||
"packages/mosaic/framework/tools/wake/validate-973/microtest-wake-assert.sh:printf '%s' \"$out\" | grep -q \"WAKE-ASSERT ABORT\" &&",
|
||||
"packages/mosaic/framework/tools/wake/validate-973/microtest-wake-assert.sh:printf '%s' \"$out\" | grep -q \"$site\" &&",
|
||||
"packages/mosaic/framework/tools/wake/validate-973/microtest-wake-assert.sh:printf '%s' \"$out\" | grep -q \"grep exit 2\" &&",
|
||||
"packages/mosaic/framework/tools/wake/validate-973/microtest-wake-assert.sh:printf '%s' \"$out\" | grep -q 'mini-a: OK' &&",
|
||||
"packages/mosaic/framework/tools/wake/validate-973/microtest-wake-assert.sh:! printf '%s' \"$out\" | grep -q 'WAKE-ASSERT ARMED'; then",
|
||||
"packages/mosaic/framework/tools/wake/validate-973/microtest-wake-assert.sh:! printf '%s' \"$out\" | grep -q 'REACHED-PAST-INIT' &&",
|
||||
"packages/mosaic/framework/tools/wake/validate-973/microtest-wake-assert.sh:printf '%s' \"$out\" | grep -q 'WAKE-ASSERT INIT ABORT: BASH_LINENO convention violated'; then",
|
||||
"packages/mosaic/framework/tools/wake/validate-973/microtest-wake-assert.sh:printf '%s' \"$out\" | grep -q 'wake mini-c harness: FAILED (1 assertion(s))' &&",
|
||||
"packages/mosaic/framework/tools/wake/validate-973/microtest-wake-assert.sh:! printf '%s' \"$out\" | grep -q 'all invariants passed' &&",
|
||||
"packages/mosaic/framework/tools/wake/validate-973/validate-973.sh:diff \"$TMP/expected.txt\" \"$TMP/static.txt\" | head -n 20 | sed 's/^/ /'",
|
||||
"packages/mosaic/framework/tools/wake/validate-973/validate-973.sh:if printf '%s\\n' \"$out\" | grep -Eq \"$(sentinel_for \"$s\")\"; then",
|
||||
"packages/mosaic/framework/tools/wake/validate-973/validate-973.sh:printf '%s\\n' \"$out\" | grep -q \"WAKE-ASSERT ARMED: forcing real grep error at $site\" ||",
|
||||
"packages/mosaic/framework/tools/wake/validate-973/validate-973.sh:printf '%s\\n' \"$out\" | grep -q \"WAKE-ASSERT ABORT: ${helper} at ${site}: grep exit\" ||",
|
||||
"packages/mosaic/framework/tools/wake/validate-973/validate-973.sh:printf '%s\\n' \"$out\" | grep -Eq \"$(sentinel_for \"$f\")\" || rc_sent=$?"
|
||||
]
|
||||
@@ -8,6 +8,7 @@ import test from 'node:test';
|
||||
const ROOT = new URL('../', import.meta.url);
|
||||
const EXPECTED_BASELINE_SITES = 26;
|
||||
const EXPECTED_TEST_BASELINE_SITES = 22;
|
||||
const EXPECTED_WAKE_BASELINE_SITES = 26;
|
||||
const TARGETS = [
|
||||
'tools/matrix-presence-harness/run.sh',
|
||||
'tools/e2e-install-test.sh',
|
||||
@@ -37,6 +38,10 @@ const TARGETS = [
|
||||
'packages/mosaic/framework/tools/quality/scripts/test-upgrade-rollback.sh',
|
||||
'packages/mosaic/framework/tools/tmux/test-send-message-socket.sh',
|
||||
'packages/mosaic/framework/tools/tmux/test-send-message-verdict.sh',
|
||||
'packages/mosaic/framework/tools/wake/test-wake-digest-quarantine.sh',
|
||||
'packages/mosaic/framework/tools/wake/test-wake-preimage.sh',
|
||||
'packages/mosaic/framework/tools/wake/validate-973/microtest-wake-assert.sh',
|
||||
'packages/mosaic/framework/tools/wake/validate-973/validate-973.sh',
|
||||
];
|
||||
|
||||
// These statuses are explicitly non-load-bearing or unreachable at designed input.
|
||||
@@ -106,6 +111,14 @@ test('the registered test baseline denominator is exactly 22 unsafe sites', asyn
|
||||
);
|
||||
});
|
||||
|
||||
test('the registered wake baseline denominator is exactly 26 unsafe sites', async () => {
|
||||
await assertBaselineFixture(
|
||||
'scripts/fixtures/pipefail-early-exit-wake-baseline.json',
|
||||
EXPECTED_WAKE_BASELINE_SITES,
|
||||
25,
|
||||
);
|
||||
});
|
||||
|
||||
test('load-bearing pipefail paths do not pipe into early-exiting consumers', async () => {
|
||||
assert.deepEqual(scan(await currentSources()), []);
|
||||
});
|
||||
@@ -138,6 +151,40 @@ test('gateway verify capability preserves the complete help-probe truth table',
|
||||
}
|
||||
});
|
||||
|
||||
test('wake JSONL selectors take the first match across the complete input stream', async () => {
|
||||
const source = await readFile(
|
||||
new URL('packages/mosaic/framework/tools/wake/test-wake-preimage.sh', ROOT),
|
||||
'utf8',
|
||||
);
|
||||
assert.equal((source.match(/jq -nr 'first\(inputs \| select\(/g) ?? []).length, 4);
|
||||
|
||||
const directory = await mkdtemp(path.join(tmpdir(), 'wake-jsonl-first-'));
|
||||
const input = path.join(directory, 'pending.jsonl');
|
||||
const filter = 'first(inputs | select(.locators.kind == "preimage") | .observed_seq) // empty';
|
||||
try {
|
||||
await writeFile(
|
||||
input,
|
||||
'{"locators":{"kind":"repo"},"observed_seq":1}\n' +
|
||||
'{"locators":{"kind":"preimage"},"observed_seq":4}\n' +
|
||||
'{"locators":{"kind":"preimage"},"observed_seq":9}\n',
|
||||
);
|
||||
let result = spawnSync('jq', ['-nr', filter, input], { encoding: 'utf8' });
|
||||
assert.equal(result.status, 0, result.stderr);
|
||||
assert.equal(result.stdout, '4\n');
|
||||
|
||||
await writeFile(input, '{"locators":{"kind":"repo"},"observed_seq":1}\n');
|
||||
result = spawnSync('jq', ['-nr', filter, input], { encoding: 'utf8' });
|
||||
assert.equal(result.status, 0, result.stderr);
|
||||
assert.equal(result.stdout, '');
|
||||
|
||||
await writeFile(input, '{invalid json}\n');
|
||||
result = spawnSync('jq', ['-nr', filter, input], { encoding: 'utf8' });
|
||||
assert.notEqual(result.status, 0);
|
||||
} finally {
|
||||
await rm(directory, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test('board-history preserves non-git data-dir as a non-detectable result', async () => {
|
||||
const directory = await mkdtemp(path.join(tmpdir(), 'reflect-board-non-git-'));
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user