aboutsummaryrefslogtreecommitdiff
Commit message (Collapse)AuthorAgeFilesLines
* fix(install-lang): refuse a colliding second bundle instead of clobberingCraig Jennings15 hours4-0/+252
| | | | | | | | | | | | Installing a second language bundle into a project silently replaced the first one's config. settings.json and githooks are copied with cp -rT (always overwrite), so installing bash over elisp rewired the validate hook to validate-bash.sh and dropped check-parens from pre-commit, leaving validate-el.sh orphaned on disk. The output said [ok] for both. A project could lose its paren check or secret scan and read the install as success. The guard detects which bundles a project already has, by the same rule fingerprint sync-language-bundle uses, and refuses when the incoming bundle would overwrite a file another one ships. It names each file at risk. FORCE=1 still overrides, and the message says that also re-seeds CLAUDE.md, which is destructive on a customized project. Only three of the five shared filenames actually collide. gitignore-add.txt is appended and deduped, and CLAUDE.md is seed-only, so both compose across bundles already. This doesn't decide whether polyglot projects are supported, and the guard shouldn't be read as "no". A non-overlapping pair still installs: bash ships settings.json and githooks with no coverage fragment, python ships only a coverage fragment, so the two compose today. The real line is overlap, not polyglot, and nothing chose it. The open question, along with the identical coverage target names both fragments define, is filed. home reported this after scaffolding clock-panel with python and typescript, which hit the coverage fragment. The settings.json and githooks cases are worse and hadn't been noticed. No project was damaged: the three bundles that collide aren't doubled up anywhere.
* docs(design): record the subproject-pattern proposal, defer promotionCraig Jennings15 hours3-0/+343
| | | | | | | | | | home proposed promoting its subproject pattern (a folded-in standalone project living as a self-contained subdir under a parent's scope) into the rules layer. The pattern reads as sound. I deferred it on evidence rather than merit, and preserved both the proposal and home's worked instance here so the decision has its source. Two findings drove the deferral. It's N=1: across all 27 agent scopes, home is the only project with subprojects, and all nine came from one fold. There's no second instance to say which parts are general and which are home-shaped. The placement also contradicts the proposal's own argument. Rules load into every session of every project, and the doc argues that layer is a tax paid whether or not it's relevant, with depth belonging one open away. At 282 lines it would be the third-largest rule and add ~11% to the layer, so every unrelated project would carry a one-project convention. I want to scout which projects would actually get subprojects, and why, before shaping anything. That evidence decides whether the pattern gets dropped or adjusted. Two things to fix if it ever promotes: the doc cites a privacy-model rule that doesn't exist (the real content is the gitignore-vs-track and public-reachability decision), and about half of it is home's instance rather than rule content.
* fix(githooks): match fixed-case secret tokens case-sensitivelyCraig Jennings18 hours4-15/+195
| | | | | | | | | | The secret scan ran every pattern through grep -iE, so AKIA[0-9A-Z]{16} matched any mixed-case 20-char run. Random base64 in an embedded image blob hits that about 6% of the time per 100KB, enough to block a real commit and force --no-verify. takuzu hit it on a PNG sprite. AWS keys are uppercase, sk- keys lowercase, and PEM headers fixed, so those three now match case-sensitively. Only the keyword=value patterns still need -i. I measured both causes first. Across ~10MB of random base64 the old pattern produced 7 matches and would have false-positived 6 of 100 sprite-sized blobs. Case-sensitive matching produced none. takuzu's report also proposed skipping any added line containing ";base64,". I left that out. It guards against an uppercase AKIA run surviving the case fix, which never occurred in the sample and runs about one in a million per blob. The cost is real: minified bundles put a whole file on one line, so a data URI and a live key can share it, and skipping the line hides the key. A test covers that. The three variants share one scan block, so elisp, bash, and go all carry the change. Splitting one grep into two meant a line matching both passes got reported twice, which reads as two separate leaks. awk dedupes it.
* fix(triage-intake): anchor the gmail residue probe to the epochCraig Jennings18 hours2-6/+14
| | | | | | | | The residue probe cut off at before:<anchor-YYYY/MM/DD> while the scan started at after:<anchor-epoch>. Gmail's before:<date> excludes the named day, so the anchor day fell between the two queries. Each sweep then advanced the anchor past that window and never looked back, so an evening anchor hid most of a day for good. That's the exact failure the probe was added on 2026-07-08 to prevent. The probe now cuts off at the epoch, matching the scan. I hoisted the epoch rule to cover both anchored queries once rather than twice. I scoped it to the anchor windows so the date-slice walk's deliberate day resolution doesn't read as a bug. home caught it: after acting on 38 messages the probe reported zero while a plain is:unread in:inbox still returned two messages from the anchor day. personal-gmail is the only source pairing an anchored scan with a residue probe, so nothing else needed the fix.
* feat(spec): sentry supervisor workflow spec, review-completeCraig Jennings3 days6-20/+549
| | | | Spec for the overnight hygiene supervisor: ten serialized passes, mkdir-atomic locks with heartbeat under the runtime dir, a host-suffixed unpushed branch, interactive entry gates with no report-only mode, and roam-sync kept as the roam repo's only committer. All 10 decisions and 12 adversarial-review findings are resolved in the file; status stays DRAFT pending a deep read. The build task and three deferrals are filed.
* chore(workflows): drop retired Signal-client name from triage history noteCraig Jennings3 days2-2/+2
|
* fix(workflows): find and transcribe .flac recordingsCraig Jennings3 days2-24/+24
| | | | | | The meeting recorder switched to lossless FLAC on 2026-07-01, and the transcript workflow's scan globbed only .mkv and .m4a, so a .flac recording was silently never found. FLAC now threads through the audio-only path: found by the scan, transcribed natively by AssemblyAI with no ffmpeg extraction, kept on cleanup, and archived with its own gitignore pattern. Adopted verbatim from work's handoff (2026-07-14-0105), diff-verified against the canonical.
* fix(scripts): stop org table helpers rewriting block contentCraig Jennings3 days13-74/+370
| | | | | | Both helpers treated any pipe-led line as a table row, so ASCII art in an example block got mangled into a bordered table (a work file took a 1949-line reformat on 2026-07-09). The scanners now track the open block's type and skip its content. Only the matching end marker closes a block, so a literal end_src quoted inside an example block can't re-expose it. The deeper cause was wrap-org-table's load-time dispatch: it fired when lint-org merely required the library, running the reformatter over files lint-org was only asked to report on. It now dispatches only when named as the entry script. lint-org's CLI is also report-only by default now. Writes require --fix, and the wrap-up workflow and lint command pass it.
* chore: archive completed tasks and close out the day's recordsCraig Jennings3 days4-186/+377
| | | | The archive sweep moves the finished routing feature and the cancelled ntfy proposal into Resolved, todo.org carries the day's review stamps and dated build entries, and the session record lands under its dated name.
* chore(todo): finish the task review — full backlog now currentCraig Jennings3 days1-9/+21
| | | | Eleven tasks reviewed: the ntfy two-way-comms proposal is CANCELLED (home tore that channel down on 07-04; the Signal pager task and agent-page are its living successors), the triage-intake phone-push task is re-pointed at agent-page so its send half is buildable now, coverage-summary's docs-only remainder gained quick and solo tags, and the rest were confirmed as-is. Zero top-level tasks remain unreviewed.
* fix(elisp): validate-el.sh prefers source over stale bytecodeCraig Jennings3 days1-0/+2
| | | | Both emacs invocations in the hook now set load-prefer-newer, matching the project-side Makefile and early-init guards. Without it, the PostToolUse hook after a pull that changed a module ran tests against the module's stale .elc and every test failed void-function on the newly-added symbols. From .emacs.d's 2026-07-13 handoff, repro-verified there; inbox/2026-07-13-1712-from-.emacs.d.
* feat(launcher): bare ai picks the agent before the projectsCraig Jennings3 days3-8/+88
| | | | The interactive flow now opens with a runtime picker: claude first (Enter-Enter preserves the old two-keystroke habit), codex labeled as ChatGPT, and one local:<model> line per ollama model, queried live with a short timeout so a dead server just drops those lines. The pick feeds the existing runtime map, then the annotated project multi-select runs as before. --runtime and AI_RUNTIME skip the question, single-directory mode is unchanged, and --print-runtimes exposes the choice list for the two new bats tests.
* feat(launcher): wire the local runtime — codex --oss over ollamaCraig Jennings3 days3-18/+30
| | | | ai --runtime local now launches codex against the machine's ollama (explicit --local-provider=ollama, model from AI_LOCAL_MODEL, default gpt-oss:120b). Verified end to end with a codex exec completion through the local model. The dependency check probes AGENT_BIN now that AGENT_CMD carries flags. This makes codex-over-ollama the de-facto answer to the runtime spec's first-supported-local-CLI blocker.
* chore(todo): record the verified local-inference stack on the model-floor taskCraig Jennings3 days1-1/+3
| | | | ollama 0.31.2 with the Vulkan backend forced by systemd override (ROCm first-touch loads were unusable at under 25 MB/s), gpt-oss:120b and qwen3-coder:30b verified at 37.9 and 76 tok/s on 100% GPU, four legacy dense models dropped, the orphaned user store purged. The child's remaining work is purely the instruction-following evaluation.
* feat(pager): add agent-page, the runtime-neutral phone pagerCraig Jennings3 days11-14/+188
| | | | The Signal pager reconcile found one pager identity, registered in velox's signal-cli, reachable from other machines only by hand-rolled ssh. agent-page wraps that: direct signal-cli on velox, ssh relay over the tailnet from everywhere else, a desktop-fallback hint when the relay fails, and the UUID target baked in so nobody pages the unregistered phone number again. Four bats tests stub ssh/uname/signal-cli to verify command construction; the real path was verified with a live phone push today. protocols.org's Paging Craig section now teaches both channels (notify for the desk, agent-page for the phone) and demotes signal-mcp to a velox-local nicety; page-me.org, work-the-backlog, and the INDEX carry the same two-channel story. Every project inherits on its next startup sync and make install.
* chore(todo): task-review batch of seven, plus the roam-capture filingsCraig Jennings3 days1-2/+20
| | | | Signal pager re-graded C to B: the runtime-portability inventory showed the claude.ai-side signal-mcp is the only paging path, home currently has no live channel at all, and off-Claude agents have none, so the doc-and-reconcile task now gates real capability rather than tidiness. install-ai-on-PATH tagged quick and solo. The launcher-hardening task and the agent-switching fold-in came from today's roam capture. The other five reviewed tasks were confirmed as-is.
* docs(runtime): session plumbing assessed, no build neededCraig Jennings4 days2-2/+13
| | | | session-context-path already carries a runtime segment, the anchor cycle is plain files, and self-inject is harness-agnostic with only its payload Claude-shaped. A codex auto-flush is a documented payload variant whose second injected line carries the resume instruction, replacing the SessionStart hook. Wire it when a codex session first wants it.
* feat(launcher): add ai --runtime for choosing the agent CLICraig Jennings4 days3-16/+146
| | | | The ai launcher can now start codex sessions: --runtime claude|codex (or AI_RUNTIME), a runtime-to-CLI map with local reserved behind a clear error until the local-model evaluation picks a CLI, and a runtime-aware dependency check. A new --print-launch mode prints the pane launch command without touching tmux or fzf, and is the seam the six new bats tests drive. Both current CLIs take the opening line as a positional prompt, so only the command name varies.
* feat(runtime): add the thin-pointer AGENTS.md agent entry fileCraig Jennings4 days8-4/+119
| | | | claude-templates/AGENTS.md points any harness at protocols.org, the rules locations, and the /name-to-file resolution rule, and tells it to degrade per each rule's documented fallback instead of skipping gates. make install links it to ~/.codex/AGENTS.md (new CODEX_DIR stanza) and install-ai.sh seeds a project-owned copy at bootstrap, never overwriting an existing one. The rulesets root carries a tracked symlink as dogfood. Five new bats tests cover the link idiom and the seeding. This resolves the generic-instruction-file blocker from the runtime spec, and velox picks up the global link on its next session's make install.
* docs(runtime): skill parity resolves to one bootstrap resolution ruleCraig Jennings4 days2-2/+17
| | | | All 29 skill and command bodies are markdown, so a single sentence in the future non-Claude entry file (a /name reference resolves to the skill or command file, read and follow it) carries the whole library. Auto-invocation degrades to by-name, which the publish flow already uses. flush stays Claude-only, owned by the session-plumbing child.
* feat(runtime): complete the hook, MCP, and memory portability inventoriesCraig Jennings4 days3-7/+71
| | | | The new inventories doc maps what's actually Claude-bound. Hooks: only PreCompact and Stop-teardown carry porting decisions, both now decided (prose downgrade, Codex notify or manual). MCP: everything ports mechanically except paging, since signal-mcp exists only claude.ai-side, so the Signal-pager task gains runtime portability as a second motivation. Memory: the KB is already the cross-agent store, and knowledge-base.md now tells agents without harness memory to capture into the session log. Three inventory child tasks closed as dated entries under the generic-agent-runtime parent.
* fix: pin the session model to fable as intendedCraig Jennings4 days1-1/+1
| | | | d5bc9b3 was meant to keep the opus-to-fable flip but recorded only a case change (opus to Opus) — the harness had rewritten the file before staging. This lands the value Craig actually chose.
* chore(todo): file runtime-portability child tasks and stamp a reviewCraig Jennings4 days1-0/+28
| | | | Eight child tasks under the generic-agent-runtime parent decompose what's left to run a non-Claude agent (Codex-style or local LLM) in this system: instruction bootstrap, skill parity, hook parity, launcher flag, session plumbing, memory story, MCP portability, local model floor. Each names the spec blocker decision it feeds. Also stamps the build-to-prototype task's review.
* chore(todo): fold home's Signal-pager status into the pager doc taskCraig Jennings4 days1-0/+3
|
* chore: switch session model from opus to fableCraig Jennings4 days1-1/+1
|
* feat(rules): add visuals-display rule for EAT and a zsh word-split noteCraig Jennings4 days3-0/+23
| | | | interaction.md gains a Showing Craig Visuals section: EAT renders SendUserFile and inline images as a text line, so visuals open in the browser or imv (per desktop-capture.md), with inline sixel as the quick-glance lane behind a capability gate (patched EAT answering CSI 14 t plus tmux terminal-features sixel, diagnosed 2026-07-13). protocols.org gains the zsh no-word-split gotcha next to the exa alias note. takuzu sent the proposal as inbox handoff 2026-07-11-2137; the sixel findings correct its capability claim.
* feat(rules): add SVG-first rendering guidance for Emacs app UIsCraig Jennings4 days2-0/+23
| | | | emacs.md gains an SVG Rendering section: default to svg.el for Emacs app UIs with real visual structure, prototype in HTML first, and stay inside the librsvg-safe constraint sheet from jotto's Emacs 30.2 pgtk spike. ui-prototyping.md points Emacs UI prototypes at the svg.el port target. jotto sent the proposal as inbox handoff 2026-07-11-0248.
* chore(todo): archive completed tasks, file build-to-prototype follow-upCraig Jennings6 days5-112/+249
|
* chore(todo): file three rulesets ideas from the roam inboxCraig Jennings6 days1-0/+18
|
* chore: file processed inbox handoffs and add two backlog tasksCraig Jennings6 days13-1/+332
|
* fix(task-review): accept org-native LAST_REVIEWED stamps, warn on bad onesCraig Jennings6 days6-20/+170
| | | | | | task-review-staleness.sh expected a bare 2026-07-09 and treated an org inactive timestamp ([2026-07-09 Thu], the form matching the CREATED:/CLOSED: cookies in the same drawer) as unparseable. The count branch folded that into the stale count, so a freshly-reviewed task reported as never-reviewed, and a full review pass never dropped the startup nudge. Both the bare and bracketed forms now normalize to the ISO date. A value that is neither warns loudly to stderr (file:line:value) and stays out of the count, since a data error shouldn't hide as "never reviewed." task-review.org documents the accepted format. Tested red/green: 5 new bats cases (bracketed fresh and stale, list-mode sort by real date, malformed warns and is excluded). Full suite 273 ok.
* docs(todo-format): warn against double-counting rarity in the bug matrixCraig Jennings6 days1-0/+23
| | | | | | Grading a bug's severity by how rare it is to enter the failure state double-counts the frequency axis, which already carries that rarity. It buries the compounding bugs, the ones where a rare trigger produces unbounded harm. Three rules added to the severity × frequency section: grade severity by the rate of harm once the failure state is entered, record the band, row, and arithmetic in the task body so a misgrade can be re-checked against the source, and disagree with a grade by fixing an input rather than overriding the letter. A chime watchdog bug graded [#D] moved to [#C] an hour later, once the read was written down and re-checked.
* docs: add off-workspace window/capture ruleCraig Jennings6 days2-0/+59
| | | | | | When an agent opens a window or takes a screenshot to verify its own work, it shouldn't hijack the workspace the user is actively using. New claude-rules/desktop-capture.md: render self-captures on an off-screen output (a Hyprland headless output) and tear them down. When showing the user something, open it on a separate real workspace and name it. verification.md points at it. The principle is environment-general. The recipe is Hyprland-specific. Verified non-disruptive on ratio 2026-07-06.
* docs(spec): add UI-prototyping process for non-trivial-UI specsCraig Jennings6 days5-0/+94
| | | | | | For a spec whose deliverable is a real UI, a design argued on paper is a guess. New claude-rules/ui-prototyping.md: research the category first, brainstorm the UX in the spec, build ~5 distinct working prototypes over one engine, iterate one to a final, and record a UI decision only once it's been seen working in a prototype. spec-create gains the step for non-trivial-UI specs. spec-review gates on it: research cited, final prototype linked, iterations in history, decisions backed by a prototype. Prototypes live at docs/prototypes/<spec-name>-prototype-<N>.html. Worked example: archsetup's timer-panel spec and its three prototypes.
* docs(review): drop praise from approve summaries, print inline text at the gateCraig Jennings6 days4-19/+27
| | | | | | | | An approve summary now carries no praise, not even a bare positive like "Clean." or "Solid fix." Lead with the substantive pointer (the design note pinned inline) and close with the verdict. This removes the bare-positive carve-out from review-code's Posted Summary Voice and from voice pattern #40, and updates the worked examples. The review gate must also print the full prose of every inline comment, never a description of it. The exact words post under my name, so I have to see them before I approve. Made explicit in review-code Phase 5 and commits.md Shape 1. From a DeepSat review session where I approved "One design note inline, not a blocker. Approving."
* fix(triage-intake): guard personal-gmail sweeps against the Gmail 100-capCraig Jennings6 days2-0/+30
| | | | | | The google-docs MCP listMessages tool caps at maxResults=100 and exposes no pageToken, so an unread pile over 100 truncates silently and every later anchored sweep skips the tail below the cap. That's how a 300+ backlog built up unseen by 2026-07-08. Two Scan-section rules close it: when a scan returns exactly 100, walk the tail in date slices and dedupe by id, and never report resultSizeEstimate as a count. A cheap backlog-residue probe each sweep (before:<anchor>, maxResults=5) surfaces pre-anchor unread loudly, so an anchored "no changes" can't mask a window the scan never saw. personal-gmail is the only gmail-family plugin here, so it's the only file that changed.
* chore: make remove.sh executableCraig Jennings7 days1-0/+0
|
* chore: drop AI co-author from generated-document headersCraig Jennings8 days104-103/+132
| | | | | | | | | | Every org document an agent writes carried `#+AUTHOR: Craig Jennings & Claude`. No template stamps that line. Agents copy it from a neighboring file, so one stray header propagates through everything generated afterward. My own repos tolerate the co-author line. Employers whose policy is that work product carries employee names alone do not. An `#+AUTHOR:` line survives conversion into docx, a wiki page, or a PDF that reaches a customer. I rewrote the header to `Craig Jennings` across the workflows, templates, specs, and design docs. The rule now lives in commits.md, so the next generated document starts correct rather than inheriting the mistake. Archived session logs keep their original headers as a record of what happened. The two Codex-authored design docs keep their byline, because Codex wrote them and relabeling would be a false attribution rather than the removal of one.
* chore(todo): archive three audit-closed tasks to ResolvedCraig Jennings13 days4-185/+254
|
* fix(startup): skip the .ai/ template sync when the project branch is behindCraig Jennings13 days2-10/+42
| | | | | | | | | | Phase A step 3 guarded its template rsync only on whether rulesets' own source was clean, never on whether the project branch was current. When a branch is diverged or behind-and-dirty, Phase A.0 correctly declines to fast-forward, but the rsync then landed templates on the stale committed .ai/ baseline. The diff came out huge (measured against old content) and conflicted once the branch reconciled to upstream's newer templates. home hit it today: a 3-ahead/46-behind divergence produced ~25 files of phantom drift nobody authored. I added a second guard: after Phase A.0's reconcile, it re-checks git rev-list @{u}...HEAD and skips the rsync when behind>0. It composes with the rulesets-clean guard, so both a stable source and a current branch are required before the sync runs. No-upstream and ahead-only both fall through and sync, which is correct. It's deliberately not an auto-discard: a legitimate local stopgap in a synced file can't be told from accidental drift by content alone, so prevention is safe where blind cleanup isn't. Phase C's churn safety net still surfaces pre-existing dirt. home proposed this via inbox handoff.
* chore(todo): close three tasks the audit confirmed doneCraig Jennings13 days4-27/+36
| | | | | | | | | | The audit found three tasks code-complete with only validation left, so I closed each and flipped its spec to IMPLEMENTED: - Docs-lifecycle convention: all four build phases plus the file-to-id link conversion had shipped. Manual validation and the org-agenda vNext moved out to their own tasks. - Wrap-up inbox/transcript routing: the recommendation engine, the route-candidate marker, the wrap-up router, and route-batch all shipped green. Manual end-to-end validation and transcript filing moved out. - Cross-machine memory sync: verified the sync round-trip live across velox and ratio (a probe made on one reached the other, its deletion propagated back, both clones on one HEAD, no conflicts). The work and unknown-project write-refusal checks moved out. I also stamped the audit marker.
* chore(settings): switch default model from fable to opusCraig Jennings13 days1-1/+1
|
* chore(todo): archive five completed tasksCraig Jennings2026-07-023-93/+200
| | | | Host-identity guard, the no-approvals speedrun, the inbox-send collision fix, page styling, and template-sync policy all moved to Resolved. The vNext agenda-view child keeps its deliberate [#D] under a :no-sync: tag.
* chore(metrics): record the first loop cycle's two task outcomesCraig Jennings2026-07-021-0/+2
|
* fix(page): pages are info-level, not alarm-redCraig Jennings2026-07-026-30/+102
| | | | | | Craig's verdict on the all-red page styling: it reads like the system is about to crash. page-me and the work-the-backlog end-of-set page now use notify info --persist, still persistent and audible, never crash-scary. status-check's success and fail notifications keep their types, since a job outcome isn't a page. The commit also carries the two loop-filed task records and the archive sweep counterpart from earlier tonight.
* fix(inbox-send): never overwrite on filename collisionCraig Jennings2026-07-024-4/+188
| | | | Two sends in the same minute whose text starts with the same phrase derived identical filenames, and the second silently replaced the first. A message was lost this way in the wild. An existing target now gets a -2/-3 stem suffix, extension preserved, on both the text and file paths. Four red-first tests reproduce the loss with a fixed timestamp so the same-minute case is deterministic.
* docs(spec): flip autonomous-batch to IMPLEMENTED after the live trialCraig Jennings2026-07-023-7/+10
| | | | The first speedrun ran Craig's three-task set end to end: pre-flight Q&A once, three reviewed commits, one metrics record per task, one end-of-set page. He confirmed the run and granted :LOOP_MAY_COMMIT:, so the recurring loop may now commit here alongside the existing :COMMIT_AUTONOMY: waiver.
* chore(metrics): record the first speedrun's three task outcomesCraig Jennings2026-07-021-0/+3
|
* feat(sync): never let untracked or gitignored files block template updatesCraig Jennings2026-07-025-5/+12
| | | | | | Projects were falling behind on templates because somebody sent them a task: an untracked inbox drop read as a dirty tree to the stricter gates. The policy is now stated where the gates live: dirty means tracked modifications only, and untracked or gitignored files never block a template pull, a fast-forward, or a monitoring gate. The audit found one offender. The inbox monitor's precondition used bare porcelain, counting the very drops it exists to process. It now checks tracked changes only, which is safe because the per-item commit already stages explicitly. The rsync WIP-guard keeps counting untracked files inside the synced source paths on purpose, since a half-written template is exactly the WIP it holds back.
* feat(rules): add the host-identity guard rule and startup probeCraig Jennings2026-07-024-1/+44
| | | | | | A tracked or synced doc asserting "this machine is X" is false on every machine but its origin, and an agent trusting it reasons backwards all session. It happened live: a stale "ratio" claim steered a session running on velox. The new rule bans fixed identity claims in tracked/synced docs and requires the runtime derivation instead (uname -n, since the hostname binary is often absent). Describing the fleet stays legal. Claiming the current member doesn't. startup gained a read-only probe that greps CLAUDE.md and notes.org for the pattern and surfaces hits as a judgment flag, never a block. Fixture-verified under bash and zsh.