snappy-resident skill
$ npx snappy-skills install snappy-resident
$ npx snappy-skills install --all
$ npx snappy-skills update
Fable is the app's resident USER — non-stop. Between every fleet event: at
least one usage act through the product doors (builder multi-turn drives,
page reads, captures, test-steps, keeps; NEVER approve/apply/send —
stage-only absolute). Stopwatch everything; grade into docs/GAUNTLET-*.md
with evidence; every defect becomes a mini lane (snappy-jcode) the same
cycle; after each install, re-drive what changed (landed-but-unwired is
defect class #1). Enumerate response keys before calling a field missing.
Real wants over synthetic tests. Typed driver: api.ts (mintSession,
builderConversation with per-turn timing, readRoute, capture).
The usage act is BROWSER-BORNE: claude-in-chrome / agent-browser on the
real UI at 127.0.0.1:3147 — real composer, real buttons, real pixels.
api.ts wire calls are verification probes and batch instruments ONLY; a
wire-only usage act violates the skill. Auth wall in the browser = a
defect to mint, never a silent wire fallback.
<!-- SKILL-INDEX-START -->
[snappy-resident Index]|root: ~/.claude/skills/snappy-resident|IMPORTANT: Prefer these files over pre-training assumptions for this domain. Read the relevant file when the AGENTS.md summary is insufficient.|root:{SKILL.md,want-ledger.md}
<!-- SKILL-INDEX-END -->
Nothing in the collection names this skill.
<!-- SNAPPY-CONTRACT-VERBS-START -->
Generated from api.ts HAND_CONTRACT. Do not hand-edit this block.
This skill declares no executable verbs. Its instruction-only label is intentional.
When an answer carries face_hint, show it with one snappy_present(<answer>) call.
See /snappy-faces for face selection. Human-facing images must crop to the
element, render at 2x on Retina, and fill the destination channel instead of
placing a small card in a full-page screenshot.
<!-- SNAPPY-CONTRACT-VERBS-END -->
# snappy-resident (agent loader)
Fable is the app's resident USER — non-stop. Between every fleet event: at
least one usage act through the product doors (builder multi-turn drives,
page reads, captures, test-steps, keeps; NEVER approve/apply/send —
stage-only absolute). Stopwatch everything; grade into docs/GAUNTLET-*.md
with evidence; every defect becomes a mini lane (snappy-jcode) the same
cycle; after each install, re-drive what changed (landed-but-unwired is
defect class #1). Enumerate response keys before calling a field missing.
Real wants over synthetic tests. Typed driver: api.ts (mintSession,
builderConversation with per-turn timing, readRoute, capture).
## BROWSER MANDATE (founder order 2026-08-15)
The usage act is BROWSER-BORNE: claude-in-chrome / agent-browser on the
real UI at 127.0.0.1:3147 — real composer, real buttons, real pixels.
api.ts wire calls are verification probes and batch instruments ONLY; a
wire-only usage act violates the skill. Auth wall in the browser = a
defect to mint, never a silent wire fallback.
<!-- SKILL-INDEX-START -->
[snappy-resident Index]|root: ~/.claude/skills/snappy-resident|IMPORTANT: Prefer these files over pre-training assumptions for this domain. Read the relevant file when the AGENTS.md summary is insufficient.|root:{SKILL.md,want-ledger.md}
<!-- SKILL-INDEX-END -->
## Used by
Nothing in the collection names this skill.
<!-- SNAPPY-CONTRACT-VERBS-START -->
## Contract verbs
Generated from `api.ts` `HAND_CONTRACT`. Do not hand-edit this block.
This skill declares no executable verbs. Its `instruction-only` label is intentional.
## Show the result
When an answer carries `face_hint`, show it with one `snappy_present(<answer>)` call.
See `/snappy-faces` for face selection. Human-facing images must crop to the
element, render at 2x on Retina, and fill the destination channel instead of
placing a small card in a full-page screenshot.
<!-- SNAPPY-CONTRACT-VERBS-END -->
Verbatim intent: "it should be inside of the TODAY PAGE - ERRANDS - BUILDER -
NEEDS YOU - CHAT - those are the most important pages obviously." The three
seats' beats are drawn from those FIVE pages first, every cycle — at least one
seat visibly IN the Builder at all times. Settings/library/connections/voice
are rotation EXTRAS taken only when the five are covered, never a whole seat's
primary beat. A seat found camped in Settings while the Builder sat undriven
is the recorded violation that minted this law.
Three Opus 5 resident seats out AT ALL TIMES — never fewer. The orchestrator
checks the live count every tick; when a seat reports, its findings are minted
into mini lanes THE SAME TICK and the replacement seat launches IN THE SAME
TURN with a rotated beat. A tick with <3 seats live and no relaunch is a named
violation (it happened 2026-08-16 and Robert caught it within two ticks).
Every pass carries the /snappy-storybook leg: workshop grade, DESIGNED/
MOUNTED/PROVEN verdicts, and a port list — the design cycle is research →
dogfood → storybook → port, and a pass that skips the workshop leg is
recorded as a violation. Screenshots are mandatory evidence on every pass.
Robert's ruling: the dogfooding runs as THREE parallel Opus agents (Agent
tool, model:"opus"), each browser-borne via agent-browser/claude-in-chrome,
each owning a named coverage beat so every edge gets whipped 3x faster —
while the orchestrator (Fable) stays on installs, merges, matrix runs, and
the mini fleet. THE MAP IS THE WHOLE APP — founder correction 2026-08-15:
"it is a lot more than just slides, stop limiting the scope." The full
coverage territory, ALL of it owned by the fleet and rotated until every
inch is driven: builder conversations + workflow lifecycle (compose,
refine, answer, keep, test-step, safe-test, run, receipts); the
paste-anything import road; every page and its doors (Today, Errands,
Needs You, Activity, Chat, Build, Connections, Settings, library,
workbench, search); the Chat spine with EVERY output kind the app can
produce — decks, docs, PDFs, sheets, images, GenUI live apps, dashboards,
artifacts of every registered type, their viewers and edit doors;
connections/grants/auth flows (read-only probing — never pop auth);
runs → receipts → approvals honesty (read, never approve real sends);
voice doors where drivable; computer-use doors (observe only). The three
agents SPLIT the territory fresh each cycle — beats are rotating slices
of the whole map, never a fixed menu; anything undriven for two cycles
becomes the next slice by default.
Each agent: own browser tab ONLY (tabs_create_mcp, never touch another
tab), own drives, CRITICAL grading (the founder bar — "it technically
worked" is not a pass; wishy-washy words, empty layouts, estimated
numbers where real ones were asked = FAIL), stage-only absolute, findings
reported as evidence (screenshot + record probe + verdict) for the
orchestrator to verify and mint into mini lanes. Launch all three each
cycle; collect reports; the orchestrator never outsources the minting
judgment.
Robert's ruling (2026-08-16, verbatim intent): "YOU accept it, YOU use it,
YOU dogfood it... USING the thing... non stop and i mean non stop." This
skill is the DISCIPLINE: Fable IS the app's resident user, driving it
HEAVILY through the same doors the frontend presses, while the mini fleet
(snappy-jcode, Luna xhigh) does ALL fixing. Central uses; mini fixes;
founder outranks everything.
Every wait (between sentinel wakes, between installs, between collections)
is USAGE time. Idle orchestration is a violation. One full cycle:
want-ledger; wants come from the actual job (watch HN for Anthropic
posts; brief Robert; track meetings; follow the fleet's own outputs).
Real wants find real defects; synthetic asks find synthetic ones.
/snappy-resident so that it FORCES you to use AGENT-BROWSER"). The
usage act happens in a REAL browser on the REAL UI: claude-in-chrome
tabs (tabs_context_mcp → tabs_create_mcp → navigate to
http://127.0.0.1:3147) or the snappy-browse/agent-browser rig — type
in the actual composer, press the actual buttons, watch the actual
render. Multi-turn always: build, then refine, then refine again —
the back-and-forth IS the product. THE WIRE IS NOT THE SEAT: api.ts
HTTP drives are permitted ONLY as (a) verification probes of stored
records against on-screen/prose claims (the silent-write catch), and
(b) batch instruments (the gauntlet matrix). A wake whose sole "usage
act" was wire-only VIOLATES this skill. Robert's screenshot catches
(orphaned buttons, "Did 1 thing" over a 26-refusal storm) were ALL
browser-layer — the wire can never see them. If the browser seat hits
an auth/session wall at :3147, that wall is itself a DEFECT to mint
(an operator door the UI must offer) — never pop an auth browser at
Robert, never fall back silently to the wire.
ledger (turn seconds, silence gaps, confusion moments) is data no test
produces.
the driven tab) of every screen you touched, read brutally; headless
captures (state/bin/head-screen/capture.ts) are the supplement for
sweep coverage, never the substitute for looking at what YOU drove.
Words are the product.
UNTESTABLE + the exact words/screenshot/keys). The probe-keys law:
before calling a field missing, enumerate the ACTUAL response keys —
your driver script can lie.
road), with the evidence inline in the brief. Never fix here.
changed. Landed-but-unwired is the #1 defect class; only a re-drive
catches it.
errands, threads, projects that serve the orchestration and Robert).
The app must be USED to be real.
Stage-only everywhere: never approve/apply/send anything external. Sends
to Robert's own Telegram ride HIS approval, not yours. Keep requires the
typed keep control. Drafts, reads, rehearsals, captures: unlimited.
session_token → x-snappy-operator-session header.
destinations:[]} at POST /workflow-drafts/:id, then turns {said,
workflow_spec, idempotency_key} at /workflow-drafts/:id/builder-turn
(poll on 202). See api.ts.
provider, cost_usd, duration_ms, pending_question, draft_patched,
draft_receipt (+ draft_diff/checkpoint_id when wired).
cost ~$0.01-0.04/turn. The bar (BUILDER-DOCTRINE): first response <1s
streamed, full turn ≤30-60s p95. Grade against the bar, not the past.
heavy (suites, storybook, builds beyond install) is the mini's.
Between any two fleet events: ≥1 usage act. Menu: a new want built ·
a refinement conversation on a kept record · a page driven and its words
read · a run followed to its receipts · a wait answered · a Needs You
review (read-only judgment of the queue's honesty) · a capture door-press
sweep · **a founder-reported failure re-driven verbatim (the highest-value
want that exists — his transcript is a recorded red flight)**. Rotate
surfaces; the gauntlet ledger's emptiest GLASS column picks the next act.
~/Library/Application Support/Snappy OS/state/log/trace-events.ndjson
for the thread id (or distinctive words), enumerate HarnessToolRun
events, count ok/fail per tool, read the refusal words in sequence.
27-calls-26-refused was invisible in the UI and fully recorded there.
Then find each refusal's words in code (grep the exact sentence) —
the gate files ARE the defect surface.
catastrophically ("Did 1 thing · 29ms" over a 26-refusal storm).
Never grade a turn from its summary words alone; cross-check the
trace. Receipt honesty is itself a gauntlet row.
("suggests chat 725804410") while draft_patched=false, destinations
empty, and the typed field null is a FAIL-shaped PARTIAL. Words-know
≠ typed-bind. Check draft_patched + spec delta + the wire field, in
that order, every regrade.
a pending question is answered. Turn-1-only regrades miss them; drive
the full question→answer arc.
lanes once executed a stale wave-30 brief (fleetctl ignored the brief
arg; worktree had no LANE-BRIEF.md). A result reporting different work
than assigned = brief-install defect: park the commits, fix the launch
road, relaunch. Never merge mismatched work.
glass, resume its jcode session with the exact wire evidence (keys,
values, file:line of the verified-fine parts) — context intact, no
re-explaining. Rename the collected result file first so the sentinel
sees the new one.
npm run app:install from repo root (immutableinstall: build→candidate-proof→daemon-handover→swap+live-gates,
~5-7min, foreground). Manual build+cp is a trap: the daemon outlives
app quit and keeps serving the OLD bundle while healthz answers green.
Verify bundle hash CHANGED in /healthz after every install.
(convergent strengthening) → resolve keeping HEAD at the seat. Two
lanes inventing the same field with different types → semantic; resume
the lane to reconcile ON its branch to ONE representation. Check the
lane's full numstat on the conflicted file before checkout --ours —
auto-merged hunks die silently if the file had more edits.
wrong shell and print empty); scp results here rather than cat-ing
them remotely.
Every resident grading pass asks of each interaction pattern: "what does
the best product in the world do here, and are we at or above it?" A
pattern with no research pedigree gets flagged AS A FINDING even when it
technically works — "Say Keep" worked in the words and was still wrong.
The evolution loop is: RESEARCH → dogfood (this skill) → iterate in
Storybook → port to glass — and a cycle that skips the research leg or
the storybook leg is recorded as a violation in the gauntlet ledger.
Operate on the assumption that every surface contains at least one defect a
first-time user would hit in their first minute. Your job each pass is to
find it before they do. Concretely:
control is supposed to do, no credit for intent. If the purpose of an
element is not evident from what is rendered, that is a finding.
for, what state things are in, and what to press next? Only then grade
individual elements. Elements may all pass while the story fails; the
story verdict wins.
pass, not a healthy surface. State explicitly what you tried that SHOULD
have broken it and did not.
defect class here; regrade on every bundle change.
world references (R13-Connectors) out-produced three-beat passes every
time. Default pass shape: each agent owns ONE surface/arc to exhaustion,
with zapier/make/linear-grade references screenshot-compared in-session.
moved the product (R16 regex-floor probe, R24 voice-transcription.ts:64,
R13 adapt.ts:260/capability-state.ts:477) hunted the defect into code
from the browser seat. A FAIL without an attempted file:line is half a
report. Grep the exact on-screen words into the repo; read the owner.
fire→staged approval→felt payoff. Months of green parts hid a dead arc.
Every pass drives at least one FULL arc and grades the weakest hop.
for tool storms, builder-flights/<date>/<draft>-turn-N for seeded context
+ usage (input_tokens per dial × dial count = the cost story).
lane is hours out but the arc can be fired manually via operator roads
(POST /scripts/:id/run staged the founder's real Telegram approval,
$0.0005), fire it NOW, deliver the felt result, keep the lane for the
mechanism. Never let plumbing sequencing delay a deliverable payoff.
any turn > $0.10 pull the flight usage (cached vs billed) before calling
it done. The $1.45-haiku-turn class hides in "it worked".
Defects and design are two different frames; a pass that returns only
defects has done half its job. Every immersion report ends with a DESIGN
VERDICT for its surface: (1) name the 2-3 best products in the world for
this surface's job and what they do that we do not — from their real,
current UIs, not memory; (2) state whether our surface is at, near, or a
generation behind that bar, with the single structural change that closes
most of the gap; (3) name which existing Storybook stories cover this
surface and which stories the next-generation version needs. This section
feeds the DESIGN CYCLE (research → storybook iteration → resident grade →
port), which is dispatched as real lanes — a surface whose design verdict
says "generation behind" twice without a design cycle being dispatched is
an orchestration failure.
Every resident pass includes at least one agent whose driving goal is an
UNACTIONED founder want from the mined corpus (docs/FOUNDER-INPUT-LEDGER) —
driven as a real journey through the product with every fork documented
(what the Builder proposes, which doors exist, how it tests, how it looks)
plus the design answer for how a world-class version would handle it. The
founder never re-states a want that is already mined; the fleet pulls them.
All browser seats run background tabs (visibilityState "hidden"): Chrome
throttles setInterval/setTimeout there and pauses rAF entirely. A 100ms
timer sampler logs ZERO samples; the app's own timer-driven UI (elapsed
clocks, 250ms re-attach polls, rAF-gated releases) freezes — R46 caught
"Still running · 15s elapsed" while 75s had passed and correctly did NOT
file it. THE LAW: felt-latency and timer-driven-UI claims from a hidden tab
are contaminated — measure event-driven (MutationObserver /
PerformanceObserver / scroll events), never timers; state the caveat on any
timing number you cannot event-source; a defect in timer-driven UI needs a
visible-tab regrade before minting as P0. Also: proxies must PIPE streams
(an arrayBuffer() proxy holds SSE to completion and fakes the void).
A background tab's screenshot can show a blank panel while the DOM holds
65,941px of content — paint is frozen, not the app. Never file a blank-
screenshot defect from a hidden tab without a DOM probe confirming absence.
Robert: "every single workflow i ever try to make none of them neither work
nor is the experience making any sense" — while ~60 per-defect fixes were
"confirmed on glass". Both are true: the seats graded SURFACES; he lives the
CHAIN. THE LAW: every resident pass ENDS with one full arc attempt — one real
sentence → compose → resolve asks → keep → run (test/safe) → open the
delivered artifact — and reports THE LINK WHERE IT BROKE as the pass's
headline (the arc ledger). A pass whose surfaces all passed but whose arc
broke is a FAILING pass. Fixes are judged by which arc link they unblock;
an install's regrade seat drives the ARC first, surfaces second.
These describe themselves in enough of the same words that a reader could pick
the wrong door. This skill is the one that BEHAVES AS THE USER in a real
browser. Reach instead for snappy-agent-host, snappy-jcode, snappy-swarm
or snappy-nightshift when the job is running or orchestrating agents rather
than being the person; snappy-os-operator and snappy-xano-mcp when the job
is driving Snappy's control plane through its MCP rather than its screens;
snappy-ax, snappy-dom-cartographer, snappy-desktop and
snappy-voice-control when the job is inspecting or actuating a surface rather
than judging it; snappy-watchtower
when the job is standing monitors rather than a seat; snappy-content,
snappy-docs, snappy-image and snappy-video when the job is producing the
artefact rather than exercising the app; and snappy-imessage,
snappy-telegram, snappy-infra, snappy-box and when
the job is a channel, a credential road or one client's context.
---
name: snappy-resident
instruction-only: true
description: "The non-stop user seat: drive the Snappy OS app as a real user through a real browser (agent-browser), with seat beats from the five priority pages (Today, Errands, Builder, Needs You, Chat), at least one seat always in the Builder, and fleet-floor rotation. Use when Robert says: /snappy-resident, \"why dont you have /snappy-resident going\", \"keep dogfooding the frontend\", \"3 opus 5 agents out ALWAYS doing dogfooding and beating the app from the frontend\", \"keep driving the frontend yourself personally\", \"use the app heavily and be critical\", \"your job is to represent ME\", \"we dont have enough agents doing /snappy-resident\". Triggers on: resident, dogfood the app, user seat, seat beat, agent-browser seat, drive the frontend. NOT the coding lane workers on the Mac mini (see snappy-jcode). NOT the agent-browser CLI reference or browser house rules (see agent-browser, snappy-browse)."
---
# snappy-resident — the non-stop user seat
## SEAT BEATS: THE FIVE PAGES FIRST (founder order 2026-08-16, ~2:50am)
Verbatim intent: "it should be inside of the TODAY PAGE - ERRANDS - BUILDER -
NEEDS YOU - CHAT - those are the most important pages obviously." The three
seats' beats are drawn from those FIVE pages first, every cycle — at least one
seat visibly IN the Builder at all times. Settings/library/connections/voice
are rotation EXTRAS taken only when the five are covered, never a whole seat's
primary beat. A seat found camped in Settings while the Builder sat undriven
is the recorded violation that minted this law.
## THE FLEET FLOOR IS A LAW (founder order 2026-08-16, ~2:30am)
Three Opus 5 resident seats out AT ALL TIMES — never fewer. The orchestrator
checks the live count every tick; when a seat reports, its findings are minted
into mini lanes THE SAME TICK and the replacement seat launches IN THE SAME
TURN with a rotated beat. A tick with <3 seats live and no relaunch is a named
violation (it happened 2026-08-16 and Robert caught it within two ticks).
Every pass carries the /snappy-storybook leg: workshop grade, DESIGNED/
MOUNTED/PROVEN verdicts, and a port list — the design cycle is research →
dogfood → storybook → port, and a pass that skips the workshop leg is
recorded as a violation. Screenshots are mandatory evidence on every pass.
## THE THREE-SEAT RESIDENT FLEET (founder order 2026-08-15)
Robert's ruling: the dogfooding runs as THREE parallel Opus agents (Agent
tool, model:"opus"), each browser-borne via agent-browser/claude-in-chrome,
each owning a named coverage beat so every edge gets whipped 3x faster —
while the orchestrator (Fable) stays on installs, merges, matrix runs, and
the mini fleet. THE MAP IS THE WHOLE APP — founder correction 2026-08-15:
"it is a lot more than just slides, stop limiting the scope." The full
coverage territory, ALL of it owned by the fleet and rotated until every
inch is driven: builder conversations + workflow lifecycle (compose,
refine, answer, keep, test-step, safe-test, run, receipts); the
paste-anything import road; every page and its doors (Today, Errands,
Needs You, Activity, Chat, Build, Connections, Settings, library,
workbench, search); the Chat spine with EVERY output kind the app can
produce — decks, docs, PDFs, sheets, images, GenUI live apps, dashboards,
artifacts of every registered type, their viewers and edit doors;
connections/grants/auth flows (read-only probing — never pop auth);
runs → receipts → approvals honesty (read, never approve real sends);
voice doors where drivable; computer-use doors (observe only). The three
agents SPLIT the territory fresh each cycle — beats are rotating slices
of the whole map, never a fixed menu; anything undriven for two cycles
becomes the next slice by default.
Each agent: own browser tab ONLY (tabs_create_mcp, never touch another
tab), own drives, CRITICAL grading (the founder bar — "it technically
worked" is not a pass; wishy-washy words, empty layouts, estimated
numbers where real ones were asked = FAIL), stage-only absolute, findings
reported as evidence (screenshot + record probe + verdict) for the
orchestrator to verify and mint into mini lanes. Launch all three each
cycle; collect reports; the orchestrator never outsources the minting
judgment.
Robert's ruling (2026-08-16, verbatim intent): "YOU accept it, YOU use it,
YOU dogfood it... USING the thing... non stop and i mean non stop." This
skill is the DISCIPLINE: Fable IS the app's resident user, driving it
HEAVILY through the same doors the frontend presses, while the mini fleet
(snappy-jcode, Luna xhigh) does ALL fixing. Central uses; mini fixes;
founder outranks everything.
## THE ZONE — the loop that never stops
Every wait (between sentinel wakes, between installs, between collections)
is USAGE time. Idle orchestration is a violation. One full cycle:
1. **WANT** — a real operator desire, never a synthetic test. Keep a
want-ledger; wants come from the actual job (watch HN for Anthropic
posts; brief Robert; track meetings; follow the fleet's own outputs).
Real wants find real defects; synthetic asks find synthetic ones.
2. **DRIVE — IN THE BROWSER, ALWAYS** (founder order 2026-08-15: "UPDATE
/snappy-resident so that it FORCES you to use AGENT-BROWSER"). The
usage act happens in a REAL browser on the REAL UI: claude-in-chrome
tabs (tabs_context_mcp → tabs_create_mcp → navigate to
http://127.0.0.1:3147) or the snappy-browse/agent-browser rig — type
in the actual composer, press the actual buttons, watch the actual
render. Multi-turn always: build, then refine, then refine again —
the back-and-forth IS the product. THE WIRE IS NOT THE SEAT: api.ts
HTTP drives are permitted ONLY as (a) verification probes of stored
records against on-screen/prose claims (the silent-write catch), and
(b) batch instruments (the gauntlet matrix). A wake whose sole "usage
act" was wire-only VIOLATES this skill. Robert's screenshot catches
(orphaned buttons, "Did 1 thing" over a 26-refusal storm) were ALL
browser-layer — the wire can never see them. If the browser seat hits
an auth/session wall at :3147, that wall is itself a DEFECT to mint
(an operator door the UI must offer) — never pop an auth browser at
Robert, never fall back silently to the wire.
3. **STOPWATCH** — time every turn and every page read. Feel it. The felt
ledger (turn seconds, silence gaps, confusion moments) is data no test
produces.
4. **LOOK** — screenshots FROM THE BROWSER SEAT (computer screenshot on
the driven tab) of every screen you touched, read brutally; headless
captures (state/bin/head-screen/capture.ts) are the supplement for
sweep coverage, never the substitute for looking at what YOU drove.
Words are the product.
5. **GRADE** — update docs/GAUNTLET-*.md rows with evidence (PASS/FAIL/
UNTESTABLE + the exact words/screenshot/keys). The probe-keys law:
before calling a field missing, enumerate the ACTUAL response keys —
your driver script can lie.
6. **MINT** — every defect becomes a mini lane THE SAME CYCLE (snappy-jcode
road), with the evidence inline in the brief. Never fix here.
7. **REGRADE** — after every install window, re-drive exactly what
changed. Landed-but-unwired is the #1 defect class; only a re-drive
catches it.
8. **INHABIT** — keep real workflows alive in the app as the user (kept
errands, threads, projects that serve the orchestration and Robert).
The app must be USED to be real.
## Governance walls (absolute)
Stage-only everywhere: never approve/apply/send anything external. Sends
to Robert's own Telegram ride HIS approval, not yours. Keep requires the
typed keep control. Drafts, reads, rehearsals, captures: unlimited.
## The proven mechanics (2026-08-16, all live-validated)
- Session: POST /operator/session with SNAPPY_MASTER_KEY from .env.cache →
session_token → x-snappy-operator-session header.
- Multi-turn driver: seed draft {version, intent, trigger:manual, steps:[],
destinations:[]} at POST /workflow-drafts/:id, then turns {said,
workflow_spec, idempotency_key} at /workflow-drafts/:id/builder-turn
(poll on 202). See api.ts.
- Response truth: keys = ok, workflow_draft_id, thread_id, reply, model,
provider, cost_usd, duration_ms, pending_question, draft_patched,
draft_receipt (+ draft_diff/checkpoint_id when wired).
- The felt baseline (2026-08-16): compose turn ~17s, refinement ~6-33s,
cost ~$0.01-0.04/turn. The bar (BUILDER-DOCTRINE): first response <1s
streamed, full turn ≤30-60s p95. Grade against the bar, not the past.
- Light-seat law: driving doors is LIGHT (HTTP) — allowed here. Anything
heavy (suites, storybook, builds beyond install) is the mini's.
## Cadence contract
Between any two fleet events: ≥1 usage act. Menu: a new want built ·
a refinement conversation on a kept record · a page driven and its words
read · a run followed to its receipts · a wait answered · a Needs You
review (read-only judgment of the queue's honesty) · a capture door-press
sweep · **a founder-reported failure re-driven verbatim (the highest-value
want that exists — his transcript is a recorded red flight)**. Rotate
surfaces; the gauntlet ledger's emptiest GLASS column picks the next act.
## Hard-won laws (2026-08-15/16 wave — each bought with a real miss)
- **Triage road for any founder-reported turn failure**: grep
`~/Library/Application Support/Snappy OS/state/log/trace-events.ndjson`
for the thread id (or distinctive words), enumerate HarnessToolRun
events, count ok/fail per tool, read the refusal words in sequence.
27-calls-26-refused was invisible in the UI and fully recorded there.
Then find each refusal's words in code (grep the exact sentence) —
the gate files ARE the defect surface.
- **Surface words vs trace truth**: the receipt can understate
catastrophically ("Did 1 thing · 29ms" over a 26-refusal storm).
Never grade a turn from its summary words alone; cross-check the
trace. Receipt honesty is itself a gauntlet row.
- **Grade the BIND, not the prose**: a reply that names the right value
("suggests chat 725804410") while draft_patched=false, destinations
empty, and the typed field null is a FAIL-shaped PARTIAL. Words-know
≠ typed-bind. Check draft_patched + spec delta + the wire field, in
that order, every regrade.
- **Regrade the ANSWER turn too**: fixes named "answer-*" only fire when
a pending question is answered. Turn-1-only regrades miss them; drive
the full question→answer arc.
- **Before merging any lane, verify the result matches ITS brief.** Two
lanes once executed a stale wave-30 brief (fleetctl ignored the brief
arg; worktree had no LANE-BRIEF.md). A result reporting different work
than assigned = brief-install defect: park the commits, fix the launch
road, relaunch. Never merge mismatched work.
- **Resume beats re-mint**: when a merged lane's fix grades PARTIAL on
glass, resume its jcode session with the exact wire evidence (keys,
values, file:line of the verified-fine parts) — context intact, no
re-explaining. Rename the collected result file first so the sentinel
sees the new one.
- **The install road is `npm run app:install` from repo root** (immutable
install: build→candidate-proof→daemon-handover→swap+live-gates,
~5-7min, foreground). Manual build+cp is a trap: the daemon outlives
app quit and keeps serving the OLD bundle while healthz answers green.
Verify bundle hash CHANGED in /healthz after every install.
- **Conflict triage at merge**: read the hunk. Both-sides-identical
(convergent strengthening) → resolve keeping HEAD at the seat. Two
lanes inventing the same field with different types → semantic; resume
the lane to reconcile ON its branch to ONE representation. Check the
lane's full numstat on the conflicted file before `checkout --ours` —
auto-merged hunks die silently if the file had more edits.
- **Ship scripts, never quoted ssh one-liners** (variables expand in the
wrong shell and print empty); scp results here rather than cat-ing
them remotely.
## Grade against the world, not against yesterday (founder law 2026-08-15)
Every resident grading pass asks of each interaction pattern: "what does
the best product in the world do here, and are we at or above it?" A
pattern with no research pedigree gets flagged AS A FINDING even when it
technically works — "Say Keep" worked in the words and was still wrong.
The evolution loop is: RESEARCH → dogfood (this skill) → iterate in
Storybook → port to glass — and a cycle that skips the research leg or
the storybook leg is recorded as a violation in the gauntlet ledger.
## CALIBRATION — the broken-until-proven prior (ratified 2026-08-16)
Operate on the assumption that every surface contains at least one defect a
first-time user would hit in their first minute. Your job each pass is to
find it before they do. Concretely:
- Open every surface as if you have never seen it: no memory of what a
control is supposed to do, no credit for intent. If the purpose of an
element is not evident from what is rendered, that is a finding.
- Grade the 60-second story first: can a newcomer say what this page is
for, what state things are in, and what to press next? Only then grade
individual elements. Elements may all pass while the story fails; the
story verdict wins.
- A pass that returns zero findings is treated as evidence of a shallow
pass, not a healthy surface. State explicitly what you tried that SHOULD
have broken it and did not.
- Never let a previous PASS carry forward: regressions are the dominant
defect class here; regrade on every bundle change.
## THE TACTICS THAT WON (2026-08-16 wave — supersede older habits)
1. **Immersion beats rotation.** One agent living in ONE surface with named
world references (R13-Connectors) out-produced three-beat passes every
time. Default pass shape: each agent owns ONE surface/arc to exhaustion,
with zapier/make/linear-grade references screenshot-compared in-session.
2. **Root-cause to file:line IN the resident report.** The passes that
moved the product (R16 regex-floor probe, R24 voice-transcription.ts:64,
R13 adapt.ts:260/capability-state.ts:477) hunted the defect into code
from the browser seat. A FAIL without an attempted file:line is half a
report. Grep the exact on-screen words into the repo; read the owner.
3. **Grade ARCS, not parts.** The unit is sentence→draft→keep→schedule→
fire→staged approval→felt payoff. Months of green parts hid a dead arc.
Every pass drives at least one FULL arc and grades the weakest hop.
4. **Trace + flight triage FIRST** for any turn oddity: trace-events.ndjson
for tool storms, builder-flights/<date>/<draft>-turn-N for seeded context
+ usage (input_tokens per dial × dial count = the cost story).
5. **Wire-fire the felt payoff when a lane blocks it.** If the mechanism
lane is hours out but the arc can be fired manually via operator roads
(POST /scripts/:id/run staged the founder's real Telegram approval,
$0.0005), fire it NOW, deliver the felt result, keep the lane for the
mechanism. Never let plumbing sequencing delay a deliverable payoff.
6. **The cost stopwatch is part of every grade**: cost_usd per turn, and on
any turn > $0.10 pull the flight usage (cached vs billed) before calling
it done. The $1.45-haiku-turn class hides in "it worked".
## THE DESIGN VERDICT (mandatory section in every immersion report)
Defects and design are two different frames; a pass that returns only
defects has done half its job. Every immersion report ends with a DESIGN
VERDICT for its surface: (1) name the 2-3 best products in the world for
this surface's job and what they do that we do not — from their real,
current UIs, not memory; (2) state whether our surface is at, near, or a
generation behind that bar, with the single structural change that closes
most of the gap; (3) name which existing Storybook stories cover this
surface and which stories the next-generation version needs. This section
feeds the DESIGN CYCLE (research → storybook iteration → resident grade →
port), which is dispatched as real lanes — a surface whose design verdict
says "generation behind" twice without a design cycle being dispatched is
an orchestration failure.
## THE CORPUS WANT (standing, 2026-08-16)
Every resident pass includes at least one agent whose driving goal is an
UNACTIONED founder want from the mined corpus (docs/FOUNDER-INPUT-LEDGER) —
driven as a real journey through the product with every fork documented
(what the Builder proposes, which doors exist, how it tests, how it looks)
plus the design answer for how a world-class version would handle it. The
founder never re-states a want that is already mined; the fleet pulls them.
## MEASUREMENT LAW: background tabs lie about time (R46, 2026-08-16)
All browser seats run background tabs (visibilityState "hidden"): Chrome
throttles setInterval/setTimeout there and pauses rAF entirely. A 100ms
timer sampler logs ZERO samples; the app's own timer-driven UI (elapsed
clocks, 250ms re-attach polls, rAF-gated releases) freezes — R46 caught
"Still running · 15s elapsed" while 75s had passed and correctly did NOT
file it. THE LAW: felt-latency and timer-driven-UI claims from a hidden tab
are contaminated — measure event-driven (MutationObserver /
PerformanceObserver / scroll events), never timers; state the caveat on any
timing number you cannot event-source; a defect in timer-driven UI needs a
visible-tab regrade before minting as P0. Also: proxies must PIPE streams
(an arrayBuffer() proxy holds SSE to completion and fakes the void).
## Hidden tabs also freeze PAINT (R55, 2026-08-16)
A background tab's screenshot can show a blank panel while the DOM holds
65,941px of content — paint is frozen, not the app. Never file a blank-
screenshot defect from a hidden tab without a DOM probe confirming absence.
## THE ARC IS THE GRADED UNIT (founder truth, 2026-08-16 ~1pm)
Robert: "every single workflow i ever try to make none of them neither work
nor is the experience making any sense" — while ~60 per-defect fixes were
"confirmed on glass". Both are true: the seats graded SURFACES; he lives the
CHAIN. THE LAW: every resident pass ENDS with one full arc attempt — one real
sentence → compose → resolve asks → keep → run (test/safe) → open the
delivered artifact — and reports THE LINK WHERE IT BROKE as the pass's
headline (the arc ledger). A pass whose surfaces all passed but whose arc
broke is a FAILING pass. Fixes are judged by which arc link they unblock;
an install's regrade seat drives the ARC first, surfaces second.
## Near neighbours
These describe themselves in enough of the same words that a reader could pick
the wrong door. This skill is the one that BEHAVES AS THE USER in a real
browser. Reach instead for `snappy-agent-host`, `snappy-jcode`, `snappy-swarm`
or `snappy-nightshift` when the job is running or orchestrating agents rather
than being the person; `snappy-os-operator` and `snappy-xano-mcp` when the job
is driving Snappy's control plane through its MCP rather than its screens;
`snappy-ax`, `snappy-dom-cartographer`, `snappy-desktop` and
`snappy-voice-control` when the job is inspecting or actuating a surface rather
than judging it; `snappy-watchtower`
when the job is standing monitors rather than a seat; `snappy-content`,
`snappy-docs`, `snappy-image` and `snappy-video` when the job is producing the
artefact rather than exercising the app; and `snappy-imessage`,
`snappy-telegram`, `snappy-infra`, `snappy-box` and when
the job is a channel, a credential road or one client's context.
// snappy-resident/api.ts — the resident user's typed driver (live-validated 2026-08-16).
import { env } from "../snappy-settings/load.ts";
import { annotationsForClass } from "../snappy-settings/tool-annotations.ts";
import { refusalTable } from "../snappy-settings/refusal-codes.ts";
import { serviceUrl } from "../snappy-settings/hand-resources.ts";
// THE ADDRESS COMES FROM THE ONE REGISTRY ⟨lane mini-reads, 2026-09-09⟩. This
// held its own literal and its own override key (`SNAPPY_BASE`), one of five
// spellings the collection had for the same daemon.
const BASE = serviceUrl("snappy-os-app");
export async function mintSession(): Promise<Record<string, string>> {
const key = env("SNAPPY_MASTER_KEY");
const r = await fetch(`${BASE}/operator/session`, { method: "POST", headers: { authorization: `Bearer ${key}`, "content-type": "application/json" }, body: JSON.stringify({ principal: { kind: "human_ui", surface: "browser-cockpit" } }) });
const token = ((await r.json()) as { session_token?: string }).session_token;
if (!token) throw new Error("operator session mint failed");
return { "content-type": "application/json", "x-snappy-operator-session": token, "x-snappy-caller-id": "fable-resident" };
}
export interface TurnResult { said: string; seconds: number; reply: string; costUsd: number; spec: unknown; raw: Record<string, unknown>; }
/** Multi-turn builder conversation on one draft; per-turn stopwatch. Drafts only — never keeps. */
export async function builderConversation(intent: string, turns: string[]): Promise<TurnResult[]> {
const H = await mintSession();
const draftId = `resident-${Date.now().toString(36)}`;
let spec: unknown = { version: 1, intent, trigger: { title: "when you ask for it", source: { kind: "manual" } }, steps: [], destinations: [] };
await fetch(`${BASE}/workflow-drafts/${draftId}`, { method: "POST", headers: H, body: JSON.stringify({ workflow_spec: spec, revision: 0 }) });
const out: TurnResult[] = [];
for (const [i, said] of turns.entries()) {
const t0 = Date.now(); let body: Record<string, unknown> = {};
for (let a = 0; a < 40; a++) {
const r = await fetch(`${BASE}/workflow-drafts/${draftId}/builder-turn`, { method: "POST", headers: H, body: JSON.stringify({ said, workflow_spec: spec, idempotency_key: `${draftId}-t${i}` }), signal: AbortSignal.timeout(180000) });
body = (await r.json().catch(() => ({}))) as Record<string, unknown>;
if (r.status !== 202) break;
await new Promise(res => setTimeout(res, 3000));
}
const d = await fetch(`${BASE}/workflow-drafts/${draftId}`, { headers: H });
const dr = (await d.json()) as { workflow_spec?: unknown; draft?: { workflow_spec?: unknown } };
spec = dr.workflow_spec ?? dr.draft?.workflow_spec ?? spec;
out.push({ said, seconds: (Date.now() - t0) / 1000, reply: String(body.reply ?? ""), costUsd: Number(body.cost_usd ?? 0), spec, raw: body });
}
return out;
}
/** One refinement turn on an EXISTING draft (fetches its current spec first). Drafts only. */
export async function builderTurnOn(draftId: string, said: string): Promise<TurnResult> {
const H = await mintSession();
const d0 = await fetch(`${BASE}/workflow-drafts/${draftId}`, { headers: H });
const dr0 = (await d0.json()) as { workflow_spec?: unknown; draft?: { workflow_spec?: unknown } };
let spec: unknown = dr0.workflow_spec ?? dr0.draft?.workflow_spec;
if (!spec) throw new Error(`draft ${draftId} has no workflow_spec (status ${d0.status})`);
const t0 = Date.now(); let body: Record<string, unknown> = {};
for (let a = 0; a < 40; a++) {
const r = await fetch(`${BASE}/workflow-drafts/${draftId}/builder-turn`, { method: "POST", headers: H, body: JSON.stringify({ said, workflow_spec: spec, idempotency_key: `${draftId}-r${Date.now().toString(36)}` }), signal: AbortSignal.timeout(180000) });
body = (await r.json().catch(() => ({}))) as Record<string, unknown>;
if (r.status !== 202) break;
await new Promise(res => setTimeout(res, 3000));
}
const d1 = await fetch(`${BASE}/workflow-drafts/${draftId}`, { headers: H });
const dr1 = (await d1.json()) as { workflow_spec?: unknown; draft?: { workflow_spec?: unknown } };
spec = dr1.workflow_spec ?? dr1.draft?.workflow_spec ?? spec;
return { said, seconds: (Date.now() - t0) / 1000, reply: String(body.reply ?? ""), costUsd: Number(body.cost_usd ?? 0), spec, raw: body };
}
/** Read any governed GET route as the user; returns status + body. */
export async function readRoute(path: string): Promise<{ status: number; body: unknown }> {
const H = await mintSession();
const r = await fetch(`${BASE}${path}`, { headers: H, signal: AbortSignal.timeout(30000) });
return { status: r.status, body: await r.json().catch(() => null) };
}
/** WHAT THIS HAND ANSWERS: NOTHING, AND IT SAYS SO ⟨lane CONTRACTS, 2026-09-07⟩.
* This file is a library other skills import — it has no CLI dispatch, so it
* implements no verb a person or an AI could call. Declaring `verbs: {}` is
* the honest reading: the census counts it as a skill with a contract and zero
* callable verbs, rather than as a hand whose contract could not be read,
* which is a different fact and would send someone looking for a bug. */
/** THE HOST-FACING FACTS ⟨lane CONTRACTS N–Z, 2026-09-09⟩. `class` is the
* closed effect set snappy-tool-design rule 18 grades; `annotations` are
* DERIVED from it by the ONE derivation in
* `snappy-settings/tool-annotations.ts`, never written per verb, so a class
* and its published hints cannot disagree; `refusals` projects the ONE closed
* table in `snappy-settings/refusal-codes.ts`; `requires` is exactly the
* credential keys this file's own executable reads name, and nothing else. */
export const HAND_CONTRACT = {
skill: "snappy-resident",
description: "The non-stop user seat: drive the Snappy OS app as a real user through a real browser (agent-browser), with seat beats from the five priority pages (Today, Errands, Builder, Needs You, Chat), at least one seat always in the Builder, and fleet-floor rotation. Use when Robert says: /snappy-resident, \\\"why dont you have /snappy-resident going\\\", \\\"keep dogfooding the frontend\\\", \\\"3 opus 5 agents out ALWAYS doing dogfooding and beating the app from the frontend\\\", \\\"keep driving the frontend yourself personally\\\", \\\"use the app heavily and be critical\\\", \\\"your job is to represent ME\\\", \\\"we dont have enough agents doing /snappy-resident\\\". Triggers on: resident, dogfood the app, user seat, seat beat, agent-browser seat, drive the frontend. NOT the coding lane workers on the Mac mini (see snappy-jcode). NOT the agent-browser CLI reference or browser house rules (see agent-browser, snappy-browse).",
managed: false,
requires: ["SNAPPY_MASTER_KEY"] as string[],
refusals: refusalTable("missing_credential", "unknown_verb", "upstream_error"),
verbs: {
},
} as const;
if (process.argv[1] !== undefined && process.argv[2] === "contract") {
try {
const { realpathSync } = await import("node:fs");
const { fileURLToPath } = await import("node:url");
if (realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url))) {
console.log(JSON.stringify(HAND_CONTRACT, null, 2));
process.exit(0);
}
} catch { /* not invoked directly */ }
}
// snappy-resident/api.ts — the resident user's typed driver (live-validated 2026-08-16).
import { env } from "../snappy-settings/load.ts";
import { annotationsForClass } from "../snappy-settings/tool-annotations.ts";
import { refusalTable } from "../snappy-settings/refusal-codes.ts";
import { serviceUrl } from "../snappy-settings/hand-resources.ts";
// THE ADDRESS COMES FROM THE ONE REGISTRY ⟨lane mini-reads, 2026-09-09⟩. This
// held its own literal and its own override key (`SNAPPY_BASE`), one of five
// spellings the collection had for the same daemon.
const BASE = serviceUrl("snappy-os-app");
export async function mintSession(): Promise<Record<string, string>> {
const key = env("SNAPPY_MASTER_KEY");
const r = await fetch(`${BASE}/operator/session`, { method: "POST", headers: { authorization: `Bearer ${key}`, "content-type": "application/json" }, body: JSON.stringify({ principal: { kind: "human_ui", surface: "browser-cockpit" } }) });
const token = ((await r.json()) as { session_token?: string }).session_token;
if (!token) throw new Error("operator session mint failed");
return { "content-type": "application/json", "x-snappy-operator-session": token, "x-snappy-caller-id": "fable-resident" };
}
export interface TurnResult { said: string; seconds: number; reply: string; costUsd: number; spec: unknown; raw: Record<string, unknown>; }
/** Multi-turn builder conversation on one draft; per-turn stopwatch. Drafts only — never keeps. */
export async function builderConversation(intent: string, turns: string[]): Promise<TurnResult[]> {
const H = await mintSession();
const draftId = `resident-${Date.now().toString(36)}`;
let spec: unknown = { version: 1, intent, trigger: { title: "when you ask for it", source: { kind: "manual" } }, steps: [], destinations: [] };
await fetch(`${BASE}/workflow-drafts/${draftId}`, { method: "POST", headers: H, body: JSON.stringify({ workflow_spec: spec, revision: 0 }) });
const out: TurnResult[] = [];
for (const [i, said] of turns.entries()) {
const t0 = Date.now(); let body: Record<string, unknown> = {};
for (let a = 0; a < 40; a++) {
const r = await fetch(`${BASE}/workflow-drafts/${draftId}/builder-turn`, { method: "POST", headers: H, body: JSON.stringify({ said, workflow_spec: spec, idempotency_key: `${draftId}-t${i}` }), signal: AbortSignal.timeout(180000) });
body = (await r.json().catch(() => ({}))) as Record<string, unknown>;
if (r.status !== 202) break;
await new Promise(res => setTimeout(res, 3000));
}
const d = await fetch(`${BASE}/workflow-drafts/${draftId}`, { headers: H });
const dr = (await d.json()) as { workflow_spec?: unknown; draft?: { workflow_spec?: unknown } };
spec = dr.workflow_spec ?? dr.draft?.workflow_spec ?? spec;
out.push({ said, seconds: (Date.now() - t0) / 1000, reply: String(body.reply ?? ""), costUsd: Number(body.cost_usd ?? 0), spec, raw: body });
}
return out;
}
/** One refinement turn on an EXISTING draft (fetches its current spec first). Drafts only. */
export async function builderTurnOn(draftId: string, said: string): Promise<TurnResult> {
const H = await mintSession();
const d0 = await fetch(`${BASE}/workflow-drafts/${draftId}`, { headers: H });
const dr0 = (await d0.json()) as { workflow_spec?: unknown; draft?: { workflow_spec?: unknown } };
let spec: unknown = dr0.workflow_spec ?? dr0.draft?.workflow_spec;
if (!spec) throw new Error(`draft ${draftId} has no workflow_spec (status ${d0.status})`);
const t0 = Date.now(); let body: Record<string, unknown> = {};
for (let a = 0; a < 40; a++) {
const r = await fetch(`${BASE}/workflow-drafts/${draftId}/builder-turn`, { method: "POST", headers: H, body: JSON.stringify({ said, workflow_spec: spec, idempotency_key: `${draftId}-r${Date.now().toString(36)}` }), signal: AbortSignal.timeout(180000) });
body = (await r.json().catch(() => ({}))) as Record<string, unknown>;
if (r.status !== 202) break;
await new Promise(res => setTimeout(res, 3000));
}
const d1 = await fetch(`${BASE}/workflow-drafts/${draftId}`, { headers: H });
const dr1 = (await d1.json()) as { workflow_spec?: unknown; draft?: { workflow_spec?: unknown } };
spec = dr1.workflow_spec ?? dr1.draft?.workflow_spec ?? spec;
return { said, seconds: (Date.now() - t0) / 1000, reply: String(body.reply ?? ""), costUsd: Number(body.cost_usd ?? 0), spec, raw: body };
}
/** Read any governed GET route as the user; returns status + body. */
export async function readRoute(path: string): Promise<{ status: number; body: unknown }> {
const H = await mintSession();
const r = await fetch(`${BASE}${path}`, { headers: H, signal: AbortSignal.timeout(30000) });
return { status: r.status, body: await r.json().catch(() => null) };
}
/** WHAT THIS HAND ANSWERS: NOTHING, AND IT SAYS SO ⟨lane CONTRACTS, 2026-09-07⟩.
* This file is a library other skills import — it has no CLI dispatch, so it
* implements no verb a person or an AI could call. Declaring `verbs: {}` is
* the honest reading: the census counts it as a skill with a contract and zero
* callable verbs, rather than as a hand whose contract could not be read,
* which is a different fact and would send someone looking for a bug. */
/** THE HOST-FACING FACTS ⟨lane CONTRACTS N–Z, 2026-09-09⟩. `class` is the
* closed effect set snappy-tool-design rule 18 grades; `annotations` are
* DERIVED from it by the ONE derivation in
* `snappy-settings/tool-annotations.ts`, never written per verb, so a class
* and its published hints cannot disagree; `refusals` projects the ONE closed
* table in `snappy-settings/refusal-codes.ts`; `requires` is exactly the
* credential keys this file's own executable reads name, and nothing else. */
export const HAND_CONTRACT = {
skill: "snappy-resident",
description: "The non-stop user seat: drive the Snappy OS app as a real user through a real browser (agent-browser), with seat beats from the five priority pages (Today, Errands, Builder, Needs You, Chat), at least one seat always in the Builder, and fleet-floor rotation. Use when Robert says: /snappy-resident, \\\"why dont you have /snappy-resident going\\\", \\\"keep dogfooding the frontend\\\", \\\"3 opus 5 agents out ALWAYS doing dogfooding and beating the app from the frontend\\\", \\\"keep driving the frontend yourself personally\\\", \\\"use the app heavily and be critical\\\", \\\"your job is to represent ME\\\", \\\"we dont have enough agents doing /snappy-resident\\\". Triggers on: resident, dogfood the app, user seat, seat beat, agent-browser seat, drive the frontend. NOT the coding lane workers on the Mac mini (see snappy-jcode). NOT the agent-browser CLI reference or browser house rules (see agent-browser, snappy-browse).",
managed: false,
requires: ["SNAPPY_MASTER_KEY"] as string[],
refusals: refusalTable("missing_credential", "unknown_verb", "upstream_error"),
verbs: {
},
} as const;
if (process.argv[1] !== undefined && process.argv[2] === "contract") {
try {
const { realpathSync } = await import("node:fs");
const { fileURLToPath } = await import("node:url");
if (realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url))) {
console.log(JSON.stringify(HAND_CONTRACT, null, 2));
process.exit(0);
}
} catch { /* not invoked directly */ }
}
/**
* COVERAGE FOR SNAPPY-RESIDENT'S DECLARED REFUSAL CODES
* (snappy-tool-design rule 33: "refusal codes form one closed table and each
* row has coverage").
*
* Two things are graded here, and the second is the one that matters. The
* first is that the hand's table is a PROJECTION of the collection's one
* closed table in snappy-settings/refusal-codes.ts — same object, not a copy
* that can drift. The second is that every declared code is GROUNDED: the
* evidence that justified declaring it is re-checked here, because a refusal
* code with no path that emits it is a branch the reader waits for and never
* sees, and a table of those passes a lint while teaching a lie.
*
* The code list is spelled out rather than read from the contract: a test that
* iterates the thing it grades passes for an empty table.
*/
import { strict as assert } from "node:assert";
import { test } from "node:test";
import { readFileSync } from "node:fs";
import { join, dirname } from "node:path";
import { fileURLToPath } from "node:url";
import { HAND_CONTRACT } from "./api.ts";
import { REFUSAL_CODES } from "../snappy-settings/refusal-codes.ts";
const SOURCE = readFileSync(join(dirname(fileURLToPath(import.meta.url)), "api.ts"), "utf8");
/** Every refusal code snappy-resident declares. */
const DECLARED = [
"missing_credential",
"unknown_verb",
"upstream_error",
] as const;
test("snappy-resident declares exactly these refusal codes", () => {
assert.deepEqual(Object.keys(HAND_CONTRACT.refusals).sort(), [...DECLARED].sort());
});
test("every declared code is the SAME row as the one closed table's, never a copy", () => {
const table = HAND_CONTRACT.refusals as Record<string, unknown>;
for (const code of DECLARED) {
assert.equal(table[code], REFUSAL_CODES[code], `${code} is not the shared row`);
}
});
test("missing_credential is grounded: this hand declares credential keys", () => {
assert.ok(HAND_CONTRACT.requires.length > 0);
});
test("unknown_verb is grounded: the contract closes the verb set, so a word outside it is refusable", () => {
assert.ok(Object.keys(HAND_CONTRACT.verbs).length >= 0);
assert.ok(!Object.keys(HAND_CONTRACT.verbs).includes("no-such-verb"));
});
test("upstream_error is grounded: the hand calls a provider that can answer with its own failure", () => {
assert.ok(/\bfetch\(/.test(SOURCE));
assert.ok(HAND_CONTRACT.requires.length > 0);
});
/**
* COVERAGE FOR SNAPPY-RESIDENT'S DECLARED REFUSAL CODES
* (snappy-tool-design rule 33: "refusal codes form one closed table and each
* row has coverage").
*
* Two things are graded here, and the second is the one that matters. The
* first is that the hand's table is a PROJECTION of the collection's one
* closed table in snappy-settings/refusal-codes.ts — same object, not a copy
* that can drift. The second is that every declared code is GROUNDED: the
* evidence that justified declaring it is re-checked here, because a refusal
* code with no path that emits it is a branch the reader waits for and never
* sees, and a table of those passes a lint while teaching a lie.
*
* The code list is spelled out rather than read from the contract: a test that
* iterates the thing it grades passes for an empty table.
*/
import { strict as assert } from "node:assert";
import { test } from "node:test";
import { readFileSync } from "node:fs";
import { join, dirname } from "node:path";
import { fileURLToPath } from "node:url";
import { HAND_CONTRACT } from "./api.ts";
import { REFUSAL_CODES } from "../snappy-settings/refusal-codes.ts";
const SOURCE = readFileSync(join(dirname(fileURLToPath(import.meta.url)), "api.ts"), "utf8");
/** Every refusal code snappy-resident declares. */
const DECLARED = [
"missing_credential",
"unknown_verb",
"upstream_error",
] as const;
test("snappy-resident declares exactly these refusal codes", () => {
assert.deepEqual(Object.keys(HAND_CONTRACT.refusals).sort(), [...DECLARED].sort());
});
test("every declared code is the SAME row as the one closed table's, never a copy", () => {
const table = HAND_CONTRACT.refusals as Record<string, unknown>;
for (const code of DECLARED) {
assert.equal(table[code], REFUSAL_CODES[code], `${code} is not the shared row`);
}
});
test("missing_credential is grounded: this hand declares credential keys", () => {
assert.ok(HAND_CONTRACT.requires.length > 0);
});
test("unknown_verb is grounded: the contract closes the verb set, so a word outside it is refusable", () => {
assert.ok(Object.keys(HAND_CONTRACT.verbs).length >= 0);
assert.ok(!Object.keys(HAND_CONTRACT.verbs).includes("no-such-verb"));
});
test("upstream_error is grounded: the hand calls a provider that can answer with its own failure", () => {
assert.ok(/\bfetch\(/.test(SOURCE));
assert.ok(HAND_CONTRACT.requires.length > 0);
});
- 2026-08-15 krisp action-items→telegram brief: draft resident-msuktm5h (T1 0.2s, T2 76.3s)