fix(fleet): remove remaining agent-specific couplings - #74
Merged
Conversation
Where fleet may merge was a literal in scripts/lib/queue.py, so naming a repository meant committing it to a PUBLIC, agnostic repo — and every clone inherited the last operator's merge rights over repositories that are not theirs. Four such commits is what it took to notice. The list is now orchestration/auto-merge.conf: the operator's own file, gitignored beside owners.txt, voice.conf and session-glyphs.conf, read on every shepherd pass so an edit needs no reinstall and no restart. orchestration/auto-merge.example.conf is the tracked copy and names NOTHING, so a fresh clone merges nowhere until its operator says otherwise, and shepherd says so by name rather than producing the same silence a repo nobody listed would. FLEET_AUTO_MERGE_REPOS still REPLACES the set. Nothing about the merge gates moved: the head branch must live in that repo, the body must carry a no-mistakes attestation for its CURRENT head, every check must have concluded and passed, the forge must call it mergeable, and whoever opened it must be able to push there. Squash stays the only method. It also closes a latent bug the old comment named: the literal was never parsed, so a bare slug written into it would have matched nothing, refused nothing and failed no test. One parser now serves both sources, so the file can no more carry an unqualified entry than the environment can. Gated against regression. check.sh gains an `automerge` check that fails if the tracked copy ever names a repository or if a fresh clone would read a non-empty set — proved to fail by adding an entry. queue-selftest.sh's 9i and 9j, which existed to assert that two named operator repositories were on the list, become one section about the mechanism: a repository the file names merges on exactly the old gates, an unvetted one is still handed back, a bare slug is refused in the file as in the environment, no file at all merges nothing and says which file would name one, and the tracked copy names none. The run reads a throwaway list via FLEET_AUTO_MERGE_ROOT so its verdicts no longer depend on whose machine it ran on. The same pass de-personalises what was left: an operator's project name in a --condition help example and their GitHub login in the shepherd fixtures. ./scripts/check.sh exits 0 locally, every check included. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Hp6M6Hw8TYWtJHGfSuwd4F
A review of all five skills against two standards this repo already sets: fleet-queue §2's own style contract (cut rhetorical contrast and persuasion, keep every measured fact) and AGENTS.md's rule that a file points at the authority rather than repeating it. Audited for defects first — every referenced path, every queue.sh subcommand and every § cross-reference resolves, so nothing here is a correctness fix. What was wrong was density and one ordering bug. The structural cuts, which are where the value is: - fleet-pane §1 described what the pane RENDERS — fuel bars, the ⇡ row's composition, the order a narrow column drops parts in — which interface/fleet_queue.lua's header owns and the skill's own opening sentence says it owns. Kept what an agent acts on (the FUEL_GLYPH shear remedy, that `green` is not the ok colour, the name-probe that explains most of §7) and pointed at the file for the rest. - fleet-onboarding step 6 restated fleet-pane §4 almost verbatim: the same script flags, the same guarantees, the same guard explanation. It now keeps its own four-option ask and the block to print, and delegates the mechanics. - fleet-queue §5d repeated three of AGENTS.md's four reconciler bullets, including the same incident and duration — and AGENTS.md is always in the lead's context, so that was duplication inside one context window. Kept the two points that are about the lead rather than the loop. - update-fleet §5b was mine from the previous commit and was a 25-line section about a one-time migration in a skill about routine updates. Now six lines and a command. One real defect: fleet-queue's §5a sat AFTER §5d and at a different heading level, so §5d's own title referenced a section the reader had not reached. Moved into order, levelled to ###. fleet-queue's frontmatter description named no command, so "run shepherd" or "what is blocked" had only the skill's name to match on. It now lists the queue's verbs. Every edit was checked against a fact extractor that diffs code spans, fenced blocks, flags, dates and quantities before and after. thurbox-session and update-fleet lost none; the rest lost only rendering internals the lua header owns and strings that moved to the skill that owns them. Measured: 24,038 words to 22,822, and markdown issues 23 to 10 with every remaining one pre-existing and deliberate. The rhetorical-construction count barely moved, and that is the honest result rather than a miss — in this domain most `is not` clauses are correcting a belief the reader would otherwise hold, which is exactly what the style contract permits. ./scripts/check.sh exits 0, every check included. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Hp6M6Hw8TYWtJHGfSuwd4F
…t code queue.py's own comment said the publish methods are ARTIFACT SHAPES and not tool names, "which is the whole of fleet's agnosticism, and it lasts exactly as long as nothing branches on it". One of the three shapes was `no-mistakes` — one operator's pipeline — the code branched on it, the attestation format was that tool's, and tracked POLICY.md shipped it as every clone's default. The same sweep found the agent hardcoded the same way. THE PUBLISH SEAM. The third shape is `attested`: a pull request whose body carries an attestation for the commit that would merge. What an attestation LOOKS like is ATTESTATION_MARKER in the operator's gitignored orchestration/publish.conf, so fleet reads their pipeline's format rather than dictating one; PIPELINE_COMMIT_PREFIX is theirs too and is empty by default, so fleet never claims to know which commits a pipeline wrote. `no-mistakes` is accepted wherever a method is read and means `attested`, so existing task records load unedited. The default method and its free-text command moved out of tracked POLICY.md into the same conf — a block still in POLICY.md is honoured and warns once on stderr, so a live fleet keeps working while it moves. The fixer brief now quotes the task's own recorded `how` instead of a command this repo picked. THE AGENT SEAM. `--agent claude` is gone from all three dispatch sites: with no AGENT in orchestration/agent.conf, session create keeps thurbox's own agents.toml default, which is where that answer already lived. refuel no longer gates on a literal `claude` window — it takes FUEL_PROVIDER, or maps the agent in hand through AGENT_PROVIDERS (identity by default), and reports `undetermined` — which restarts nothing — when tasks disagree or when `quota-axi auth` holds no credential for the derived name. Fleet carries no provider list; it asks quota-axi, so the set grows with the tool. HOW AN AGENT SAYS IT RAN OUT is now a table with one entry per agent fleet has actually WATCHED do it — `claude` today — the same shape as session-trust.sh's per-agent dialog table. An agent with no entry answers `undetermined` rather than being guessed at, and LIMIT_BANNER / TRANSCRIPT_DIR teach fleet one without a code change. Nothing is matched here that nobody observed: a guessed pattern restarts a live worker mid-turn. Both tracked example files name nothing. check.sh's `automerge` check now fails any tracked setting that carries a repository, a tool, a vendor or an agent, and reads every occurrence rather than the first — proved by appending a leak below a correct line and watching it fail. queue-selftest.sh gets its own throwaway publish and agent settings so its verdicts stop depending on whose machine ran it, and section 8h becomes the unconfigured-clone proof: no POLICY frontmatter and no publish.conf defaults to `pr` with no tool named anywhere in the brief, the shipped example names no METHOD but `pr` and no HOW at all, the three methods are artifact shapes, and the retired spelling still resolves. ./scripts/check.sh exits 0, every check included. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Hp6M6Hw8TYWtJHGfSuwd4F
… config
Three things fleet treated as facts about the world were facts about one
operator: which repositories it may merge, which pipeline produces a
publishable artifact, and which agent its workers run. All three were
literals in tracked code, in a PUBLIC repo, so every clone inherited the
last operator's answers.
THE MERGE SET. Where fleet may merge was a literal in scripts/lib/queue.py,
so naming a repository meant committing it — and every clone inherited merge
rights over repositories that are not theirs. It is now
orchestration/auto-merge.conf, the operator's own gitignored file beside
owners.txt and session-glyphs.conf, read on every shepherd pass so an edit
needs no reinstall and no restart. The tracked example names NOTHING, so a
fresh clone merges nowhere until its operator says otherwise, and shepherd
says which file would name one rather than producing the same silence as a
repo nobody listed. FLEET_AUTO_MERGE_REPOS still REPLACES the set. It also
closes a latent bug the old comment named: the literal was never parsed, so
a bare slug written into it would have matched nothing and failed no test.
One parser now serves both sources.
THE PUBLISH SEAM. queue.py's own comment said the publish methods are
ARTIFACT SHAPES and not tool names, "which is the whole of fleet's
agnosticism, and it lasts exactly as long as nothing branches on it". One of
the three shapes was `no-mistakes` — one operator's pipeline — the code
branched on it, the attestation format was that tool's, and tracked
POLICY.md shipped it as every clone's default. The third shape is now
`attested`: a change request whose body carries an attestation for the
commit that would merge. What an attestation LOOKS like is
ATTESTATION_MARKER in the operator's gitignored orchestration/publish.conf,
so fleet reads their pipeline's format rather than dictating one;
PIPELINE_COMMIT_PREFIX is theirs too and is empty by default, so fleet never
claims to know which commits a pipeline wrote. `no-mistakes` is accepted
wherever a method is read and means `attested`, so existing task records
load unedited. The default method and its free-text command moved out of
tracked POLICY.md into the same conf — a block still in POLICY.md is
honoured and warns once on stderr, so a live fleet keeps working while it
moves.
THE AGENT SEAM, in all four places an agent's name reached code:
- DISPATCH. `--agent claude` is gone from all three spawn sites. With no
AGENT in orchestration/agent.conf, session create keeps thurbox's own
agents.toml default, which is where that answer already lived.
- THE LEAD ITSELF, which the first pass missed: extension.toml.in carried
`agent = "claude"` in tracked configuration, so a fresh clone launched
Mission Control on one vendor's agent whatever its operator runs. It is now
`__LEAD_AGENT__`, rendered by install-extension.sh from the same AGENT
setting every worker reads, next to `__REPO_PATH__` and `__LEAD_GLYPH__`;
unset still renders thurbox's stock `claude`, because thurbox must be given
a name.
- REFUEL. It no longer gates on a literal `claude` window: FUEL_PROVIDER, or
the agent in hand mapped through AGENT_PROVIDERS (identity by default), and
`undetermined` — which restarts nothing — when tasks disagree or quota-axi
holds no credential for the derived name. Fleet carries no provider list; it
asks quota-axi, so the set grows with the tool. fleet_status.py's own
`FUEL_PROVIDER = ... or "claude"` was the last literal and is gone: the
screen leads with the operator's provider, and with none named reads
whichever one holds a credential rather than one this repo picked.
- THE TRUST DIALOG, which §12b below found. session-trust.sh keeps one row
per agent fleet has watched and REFUSES an unlisted one — correctly, since
a guessed Enter exits Claude Code — but had no way for an operator to teach
it theirs. So refuel restarted a worker on an untaught agent and never typed
the brief into it. TRUST_SIGNATURE and TRUST_KEYS in agent.conf now teach it
one, `TRUST_KEYS=none` says there is no dialog, and with neither set it
still refuses rather than guessing.
How an agent says it RAN OUT stays a table with one entry per agent fleet has
actually watched do it; an agent with no entry answers `undetermined` rather
than being guessed at. A guessed pattern restarts a live worker mid-turn.
DRIVEN, NOT ASSERTED. scripts/lib/forge.py is this repo's standard — an
interface, two implementations, and selftests that drive the second with the
first's CLI a tripwire on PATH. The agent seam did not meet it: §12 ran
`claude` end to end, so every built-in would have passed. queue-selftest.sh
§12b now runs the whole of §12 again for `nova`, an agent with no entry
anywhere in the code: the banner, the transcript layout, the provider and the
trust dialog all come from agent.conf, CLAUDE_CONFIG_DIR is unset and the
quota document names another provider, so a literal reached for on that path
fails the section. fleet-status-selftest.sh §6d does the same for the fuel
reader over three answers to "which provider" — the operator's agent, an
explicit pin, and an unconfigured clone — with a grep over fleet_status.py
as the tripwire.
check.sh's `automerge` check fails any tracked setting carrying a repository,
a tool, a vendor or an agent, reading every occurrence rather than the first,
and now covers the two trust keys.
CONTEXT, WHICH IS THE PRODUCT HERE. Measured in words, over the files a lead
loads every session:
59879e1 #74 base #74 head now
AGENTS+FLEET+CLAUDE 5,623 6,854 7,026 6,344
the five SKILL.md 21,312 24,028 23,748 23,762
The growth this branch was asked about is mostly not this branch: 16 commits
landed on main between 59879e1 and its base, and they are +1,231 of the
+1,403 always-loaded words and all +2,716 of the skills. The docs commit here
was -280 on the skills and +172 on the always-loaded set.
Two cuts, both against AGENTS.md's own rule that a file points at the
authority rather than repeating it. AGENTS.md's numbered loop restated
fleet-queue's §1-§7 and queue.sh's header in full; it is an index again, with
every rule checked to still exist in the file that owns it before its copy
went. FLEET.md's loop restated AGENTS.md's inside the same context window —
both are always loaded — so it keeps what is the lead's and defers the
mechanics. The forge bullet stopped summarising a docstring that says it owns
the subject. Net: the always-loaded set is 682 words below this PR's head and
510 below its base.
./scripts/check.sh exits 0, every check included.
…t; add missing README conf entries
…ension.toml.in and trust-dialog table
LeTuR
added a commit
that referenced
this pull request
Sep 12, 2026
…78) ## Intent Fleet task queue-check-alias/01-01-accept-legacy-alias. `./scripts/queue.sh check` rejected every task record whose publish method is `no-mistakes` — the method #74 renamed to `attested` — even though AGENTS.md's contract is that `no-mistakes` is still accepted wherever a method is read and means `attested`. Archived records are never rewritten, so the control plane's gate stayed red for good (23 problems on the live queue). Goal: make check accept the alias by normalising it to attested, and still reject a genuinely invalid method. Hard constraints from the brief: fix the validator, never rewrite the operator's gitignored records; do not widen the accepted set beyond attested, pr, push plus the no-mistakes alias; keep the alias in ONE place (PUBLISH_ALIASES / publish_method() in scripts/lib/queue.py) rather than a second spelling in the validator — that drift is the bug. Tests written first in scripts/queue-selftest.sh and seen failing on the old code for exactly that reason. Decision: an aliased method passes SILENTLY, not with a notice — the contract says it is accepted, nothing writes the old word any more, and the records carrying it are archived history nobody can or should edit, so a notice would be permanent noise with no action to take. Deliberate scope extension within the same bug class: two more readers bypassed publish_method() the same way and were fixed with tests that failed first — task_publish() let a no-mistakes record fall back to the operator default (a silent downgrade to pr on a fresh clone), and `queue.sh add --publish no-mistakes` was refused by argparse choices despite the code comment saying it works (fixed with type=publish_method, which argparse applies before choices, so the record stores attested). Reach: queue_root() anchors to the checkout the script lives in, so a worker worktree, a pipeline worktree or CI validates its own empty queue — this bug only turned check.sh red on the control-plane checkout itself, not in worker gates. ## What Changed - `cmd_check()` in `scripts/lib/queue.py` now normalizes a record's `publish.method` through `publish_method()` before validating it against `PUBLISH_METHODS`, so archived records still carrying the retired `no-mistakes` word pass `queue.sh check` instead of being flagged as invalid, while a genuinely unknown method is still rejected. - `task_publish()` normalizes the task's declared publish method through `publish_method()` before checking membership in `PUBLISH_METHODS`, so a record written as `no-mistakes` resolves to `attested` instead of silently falling back to the operator's default publish method. - `queue.sh add --publish` now uses `type=publish_method` on the argparse argument (applied before `choices`), so passing `--publish no-mistakes` is accepted and stored as `attested` instead of being rejected by argparse. - `scripts/queue-selftest.sh` adds test coverage for all three fixes: `check` accepting the `no-mistakes` alias while still rejecting an unknown method, `add --publish no-mistakes` succeeding and recording `attested`, and `task_publish`/`show` resolving a `no-mistakes`-tagged record to `attested` rather than falling back to `pr`. ## Risk Assessment ✅ Low: Small, well-scoped fix: all three readers of the publish method (cmd_check, task_publish, --publish argparse) now route through the single publish_method()/PUBLISH_ALIASES normalisation, the validator still rejects genuinely unknown methods, the alias is accepted silently as required, no records are rewritten, and new tests reproduce the original failure (red check on an aliased record) and pass after the fix. ## Testing Baseline `./scripts/check.sh` had already passed on the target commit. To directly test the user intent, I ran the full `scripts/queue-selftest.sh` on the target commit (9e359dc) and confirmed all six new alias-regression assertions pass: `check` silently accepts a task record carrying the retired `no-mistakes` method (normalizing it to `attested` with no notice, as the intent specifies), still rejects a genuinely bogus method (`carrier-pigeon`) without also flagging the alias, `queue.sh add --publish no-mistakes` succeeds and persists `attested` (never the old word), and `task_publish`/`show` read a `no-mistakes`-tagged record as `attested` rather than silently downgrading to the clone's `pr` default. To prove this is a real regression test and not a tautology, I copied that same test file into a throwaway worktree checked out at the pre-fix base commit (a81bf77) and reran it: it failed for exactly the reasons described in the brief — `check` reported `publish method 'no-mistakes' is not one of attested, pr, push`, `add --publish no-mistakes` was refused by argparse's `choices`, and `show` reported the record as verified `pr` instead of `attested`. This end-to-end before/after comparison demonstrates the fix resolves the reported control-plane gate failure without widening the accepted method set or duplicating the alias spelling outside `scripts/lib/queue.py`. The worktree was left clean; the temporary comparison worktree was removed. ## Pipeline Updates from [git push no-mistakes](https://github.com/kunchenguid/no-mistakes) <!-- no-mistakes-pipeline-attestation:v1 {"head_sha":"9e359dc6a5dd591d62ad07106aab2f51eedf578e","steps":[{"step":"intent","status":"completed"},{"step":"rebase","status":"completed"},{"step":"review","status":"completed"},{"step":"test","status":"completed"},{"step":"document","status":"completed"},{"step":"lint","status":"completed"},{"step":"push","status":"completed"},{"step":"pr","status":"running"},{"step":"ci","status":"pending"}]} --> <details> <summary>✅ **intent** - passed</summary> ✅ No issues found. </details> <details> <summary>✅ **Rebase** - passed</summary> ✅ No issues found. </details> <details> <summary>✅ **Review** - passed</summary> ✅ No issues found. </details> <details> <summary>✅ **Test** - passed</summary> ✅ No issues found. - `./scripts/check.sh` - <code>`./scripts/queue-selftest.sh` on target commit 9e359dc (full pass, ~147s)</code> - <code>`./scripts/queue-selftest.sh` (target's version of the file) run against pre-fix queue.py at base commit a81bf77 in a temporary worktree — reproduced the exact 5 reported failures, confirming the regression test is genuine</code> - <code>Manual inspection of `PUBLISH_ALIASES`/`publish_method()` in scripts/lib/queue.py:388-395 confirming the alias is defined once and routed through by `cmd_check`, `task_publish()`, and the `--publish` argparse `type=`</code> </details> <details> <summary>✅ **Document** - passed</summary> ✅ No issues found. </details> <details> <summary>✅ **Lint** - passed</summary> ✅ No issues found. </details> <details> <summary>✅ **Push** - passed</summary> ✅ No issues found. </details>
LeTuR
added a commit
that referenced
this pull request
Sep 13, 2026
…84) ## Intent Fleet task remote-host-support/02-02-psmux-and-windows. The operator asked, verbatim: 'Fleet disaptching to a psmux should work, maybe running fleet on windows should work also ?'. The brief required a decision with evidence and the smallest change that makes it real: verify each of the three couplings the old refusal cited (probes, brief copy, result fetch) against the real Windows host windows-hp (PowerShell 5, psmux 3.3.6), answer A (dispatch TO Windows) and B (fleet RUNNING on Windows) separately, and build A only if contained, as a seam per coupling with the POSIX implementation unchanged and a PowerShell one beside it, driven by a test to the standard scripts/lib/forge.py set (second implementation driven with a tripwire on PATH), proven against windows-hp. Hard constraints: do not regress the POSIX path (existing remote selftests unchanged), do not weaken the refusal to make the error go away (a half-working spawn would leave a worktree on a Windows box), no new dependency (no PowerShell module, Cygwin or busybox), say what could not be tested. B was to be answered, not implemented. Findings and decisions: all three couplings were real (printf/[ -d ] do not exist in PowerShell; cat > path writes nothing; PS5 stdin/stdout go through the ibm850 code page so non-ASCII does not survive), plus a fourth the refusal never named: the trust handoff (psmux captures panes with spaces removed, so session-trust.sh's signature never matched; Claude Code 2.1.247 on Windows pre-selects 'Yes', so the table's 'down enter' chose 'No, exit' and the agent exited - observed live). Built: a HostShell seam in scripts/lib/queue.py selected by hosts.toml's multiplexer (the same proxy thurbox uses, HostDef::is_windows): PosixShell issues byte-for-byte the previous commands including the /bin/sh -lc login wrap; PowerShell sends UTF-16LE base64 -EncodedCommand (thurbox's own host_powershell_c rule, because -Command quoting is rewritten by the host's default shell), carries file bytes as base64 both directions, joins paths with backslash, and runs the forge probe's ssh key check via bounded Start-Process because inline '& ssh' hangs inside an ssh session on Windows (measured: & ssh, ssh -n and cmd /c <NUL all hung past 45s). Any multiplexer other than tmux/psmux is still refused by name at add; a Windows box that hosts.toml calls tmux is still caught by the first probe. Deliberately, exit codes carry no meaning beyond zero/non-zero and ssh's 255, because Windows folds a PowerShell exit code; answers are words on stdout sharing one line protocol with the POSIX scripts. reportable() strips OpenSSH's post-quantum advisory and PowerShell CLIXML / error trailer so failure reasons name the real error - the only POSIX-visible change is that error text. session-trust.sh now matches whitespace-insensitively and sends Enter alone when the selector already sits on the accepting option; a dialog defaulting to No still gets 'down enter'. Tests in scripts/queue-selftest.sh §11(i)/(j): a fake Windows host (windows-host.py in the ssh stub) decodes every -EncodedCommand, moves real bytes into a fake C:\ tree, and logs any non-encoded (POSIX) command as a tripwire, with a claim proving the tripwire fires; §11(a-h) unchanged except the refusal row now uses oddbox (unknown multiplexer) instead of winbox. The Windows reap claim holds its topic open with a condition-blocked task because of a pre-existing, deliberately unfixed bug: a topic whose last remote task lands while its host is down archives in the same pass and reap then never revisits the kept session. Known limits, accepted: thurbox gates hook status off on psmux hosts so Windows workers stay 'unreported' and reap never releases them; windows-hp has no GitHub credentials so no full worker run was possible; GitLab origin, non-22 ssh port and PowerShell 7 on Windows are untested. B answered in docs/PR only: yes with WSL, native is a port of 22 bash scripts plus the setsid supervisor. Docs updated: scripts/queue.sh header, fleet-queue and thurbox-session skills. This run exists to attest PR #80 (task was queued as 'pr' by the #74 bug; the operator's standing method is attested). ## What Changed - Added a `HostShell` seam in `scripts/lib/queue.py` (`PosixShell` plus a new `PowerShell` implementation) so remote dispatch's three probes, brief copy, and result fetch each speak the shell a host actually runs: POSIX for a `tmux` host (byte-for-byte unchanged, including the `/bin/sh -lc` login wrap), PowerShell for a `psmux` (native Windows) host via UTF-16LE base64 `-EncodedCommand`, base64 file transfer in both directions, backslash path joining, and a bounded `Start-Process` SSH key check to avoid an inline `& ssh` hang observed on Windows. `host_entry()` now refuses any multiplexer other than `tmux`/`psmux` by name instead of refusing every non-`tmux` host. - Added `reportable()` to strip OpenSSH's post-quantum advisory and PowerShell's CLIXML/error-trailer noise from probe output so reported failures name the real error, on both POSIX and Windows hosts. - Updated `scripts/session-trust.sh` to match trust-dialog signatures with whitespace stripped from both sides (psmux captures panes with spaces removed) and to send `Enter` alone whenever the dialog's selector is already on the accepting option, instead of always sending the fixed `down enter` sequence that would otherwise pick "No, exit" on Claude Code's Windows-defaulted dialog. - Extended `scripts/queue-selftest.sh` with a fake Windows host (decoding `-EncodedCommand`, writing into a fake `C:\` tree, and tripwiring any non-encoded POSIX command) covering the new PowerShell path, updated the existing refusal-row test to use an unknown multiplexer name instead of assuming any non-`tmux` host is refused, and updated `scripts/queue.sh`'s header comment plus the `fleet-queue`/`thurbox-session` skill docs to describe the two-shell behavior. ## Risk Assessment ✅ Low: The fix-round change (scripts/lib/queue.py:2515, switching to [Console]::Out.Write) correctly bypasses PowerShell's console-width formatter, resolving the line-wrap truncation risk flagged in round 1; the rest of the HostShell seam, session-trust.sh's whitespace-insensitive/selector-aware matching, and the behavioral (non-string-matching) selftest coverage are consistent, well-scoped, and match the stated intent with no regression to the POSIX path. ## Testing Baseline ./scripts/check.sh (full gate, including the complete queue-selftest.sh with its Windows/psmux dispatch and result-fetch coverage) already passed with no regressions. Because that suite's fake Windows-host stub doesn't model PowerShell's real non-interactive line-wrapping and no PowerShell interpreter is available on this machine, I wrote and ran a focused manual verification against the real decode()/last_out_of() functions using the actual pre-fix and post-fix read() output shapes and the suite's own realistic result.md fixture: the pre-fix shape silently decodes to wrong bytes (the reported bug, reproduced), and the post-fix shape decodes to the exact original bytes, confirming the fix closes the gap for a realistic payload. <details> <summary>Evidence: Manual verification script (models pre-fix vs post-fix PowerShell output shapes through the real decode/last_out_of)</summary> ```text #!/usr/bin/env python3 """Manual verification for the psmux/Windows result-fetch line-wrap fix. Real PowerShell 5, run non-interactively over an ssh exec channel (no pty), cannot query a console width and falls back to a default host buffer width of 80 columns. A plain string emitted as the pipeline's *implicit* output goes through the default formatter (Out-Default), which hard-wraps continuous text (no whitespace to break on) at that width when writing it out. `[Console]:: Out.Write(...)` instead writes straight to the stdout stream, bypassing the formatter/pipeline entirely, so it is never wrapped. `scripts/lib/queue.py`'s `PowerShell.decode()` recovers the base64 payload with `last_out_of(stdout)`, which keeps only the LAST non-empty line. Wrapped output would silently discard every earlier chunk. This script imports the real `last_out_of`/`PowerShell.decode` from the target commit (unchanged across both commits) and feeds it two prepared stdout strings that model what real PowerShell would hand back for the two `read()` implementations: - pre-fix (commit 5efbde3): bare `[Convert]::ToBase64String(...)` as the script's last expression -> implicit pipeline output -> wrapped. - post-fix (commit 873c14c): `[Console]::Out.Write(...)` -> direct stream write -> not wrapped. No real PowerShell interpreter is available on this machine (no pwsh/ powershell, and installing one is out of scope for this worktree), so the wrapping step is a documented-behavior model, not a captured real PowerShell run. It is not a substitute for running this against a real Windows host; the operator's own PR notes that windows-hp validation already covered the happy path with a small enough payload to not trigger the wrap. """ import base64 import sys sys.path.insert(0, "scripts/lib") import queue as q # noqa: E402 BUFFER_WIDTH = 80 # The exact fixture queue-selftest.sh §11(i) writes as the Windows worker's # result.md (a realistic ~140-byte real task result, non-ASCII included). RESULT_MD = ( "---\noutcome: shipped\n" "artifact: https://github.com/remote-owner/app/pull/4343\n---\n" "Built it on winbox — café ✓.\r\nWritten by Windows.\r\n" ).encode() def wrapped_stdout(payload_b64: str) -> str: """What PowerShell's default formatter emits for a bare-expression string result when no real console width can be queried: hard-wrapped at 80 columns, one physical line per chunk.""" return "\n".join( payload_b64[i : i + BUFFER_WIDTH] for i in range(0, len(payload_b64), BUFFER_WIDTH) ) def direct_stdout(payload_b64: str) -> str: """What `[Console]::Out.Write(...)` emits: the raw string, one line.""" return payload_b64 payload_b64 = base64.b64encode(RESULT_MD).decode("ascii") print(f"fixture: {len(RESULT_MD)} raw bytes -> {len(payload_b64)} base64 chars " f"(> {BUFFER_WIDTH}-column buffer width, so wrapping would trigger)\n") ps = q.PowerShell() pre_fix_stdout = wrapped_stdout(payload_b64) post_fix_stdout = direct_stdout(payload_b64) print("--- pre-fix behavior model (bare expression -> Out-Default wraps) ---") print(f"stdout lines: {len(pre_fix_stdout.splitlines())}") decoded = ps.decode(pre_fix_stdout) if decoded is None: print("decode() result: None (ValueError swallowed) -- fetch_result would " "report a loud, safe failure") elif decoded.encode("utf-8", "surrogateescape") == RESULT_MD: print("decode() result: UNEXPECTEDLY matched original bytes") else: print("decode() result: silently WRONG (truncated/garbage) bytes -- the bug") print() print("--- post-fix behavior model ([Console]::Out.Write -> single line) ---") print(f"stdout lines: {len(post_fix_stdout.splitlines())}") decoded = ps.decode(post_fix_stdout) ok = decoded is not None and decoded.encode("utf-8", "surrogateescape") == RESULT_MD print(f"decode() result matches original {len(RESULT_MD)}-byte result.md exactly: {ok}") sys.exit(0 if ok else 1) ``` </details> <details> <summary>Evidence: Verification run output: pre-fix shape decodes to wrong bytes, post-fix shape decodes exactly</summary> <code>fixture: 137 raw bytes -> 184 base64 chars (> 80-column buffer width, so wrapping would trigger) --- pre-fix behavior model (bare expression -> Out-Default wraps) --- stdout lines: 3 decode() result: silently WRONG (truncated/garbage) bytes -- the bug --- post-fix behavior model ([Console]::Out.Write -> single line) --- stdout lines: 1 decode() result matches original 137-byte result.md exactly: True</code> ```text fixture: 137 raw bytes -> 184 base64 chars (> 80-column buffer width, so wrapping would trigger) --- pre-fix behavior model (bare expression -> Out-Default wraps) --- stdout lines: 3 decode() result: silently WRONG (truncated/garbage) bytes -- the bug --- post-fix behavior model ([Console]::Out.Write -> single line) --- stdout lines: 1 decode() result matches original 137-byte result.md exactly: True ``` </details> - Outcome:⚠️ 1 info across 1 run (9m2s) ## Pipeline Updates from [git push no-mistakes](https://github.com/kunchenguid/no-mistakes) <!-- no-mistakes-pipeline-attestation:v1 {"head_sha":"873c14c703b870c946e67b7ab5284e75b116b3e9","steps":[{"step":"intent","status":"completed"},{"step":"rebase","status":"completed"},{"step":"review","status":"completed"},{"step":"test","status":"completed"},{"step":"document","status":"completed"},{"step":"lint","status":"completed"},{"step":"push","status":"completed"},{"step":"pr","status":"running"},{"step":"ci","status":"pending"}]} --> <details> <summary>✅ **intent** - passed</summary> ✅ No issues found. </details> <details> <summary>✅ **Rebase** - passed</summary> ✅ No issues found. </details> <details> <summary>🔧 **Review** - 1 issue found → auto-fixed ✅</summary> -⚠️ `scripts/lib/queue.py:2515` - PowerShell.read() emits the base64 payload via the pipeline's implicit output (`[Convert]::ToBase64String(...)` as the script's last expression), which goes through PowerShell's default `Out-Default`/console-formatting layer. When powershell.exe runs non-interactively over an exec-channel ssh session (no pty is allocated here — ssh_argv has no `-t`), it cannot query a real console width and falls back to a default buffer width (commonly 80), and that formatter wraps long string output at that width even though it's being redirected to a pipe, not a terminal — a well-known PowerShell gotcha for exactly this invocation shape. `PowerShell.decode()` (line 2518-2522) recovers the payload with `base64.b64decode(last_out_of(stdout), ...)`, and `last_out_of` (line 2235) returns only the LAST non-empty line of stdout. Any result.md whose base64 encoding exceeds one wrapped line (roughly >60 raw bytes — true of essentially any real task result, e.g. the test's own ~140-byte fixture result) would have its earlier lines silently discarded, leaving only the tail chunk to decode. This mechanism isn't exercised by the new queue-selftest.sh (i) coverage because the fake windows-host.py stub emits the base64 with a single Python `print()` and never simulates PowerShell's own line-wrapping, so the gap is untested. Depending on the wrapped tail's length modulo 4, decode() will usually raise ValueError (safe, loud failure — fetch_result reports "the host answered, but not with the file's bytes") but could also occasionally decode to truncated-but-valid-looking bytes, silently returning a wrong (truncated) result. Either way this would make the freshly-built "result fetch" coupling non-functional for realistic Windows task outputs, contradicting the intent's claim that this coupling was verified end-to-end. The fix is mechanical: have the PowerShell script write the base64 string directly to the output stream (e.g. `[Console]::Out.Write(...)`) instead of relying on the formatted pipeline output, bypassing the width-based wrapping entirely. 🔧 Fix: fix(queue): write PowerShell result base64 with Console.Out to avoid wrap truncation ✅ Re-checked - no issues remain. </details> <details> <summary>⚠️ **Test** - 1 info</summary> - ℹ️ The existing queue-selftest.sh section 11(i) Windows result-fetch coverage still round-trips base64 through a plain Python print() in its ssh stub and does not model PowerShell's non-interactive console-width line-wrapping, so it cannot regression-guard this specific fix on its own; it did not need to per the recorded review decision (code-only auto-fix), and real end-to-end confirmation would require a real Windows/PowerShell host, which the operator's own PR notes was only available for a payload too small to trigger the wrap. - `./scripts/check.sh` - `./scripts/check.sh (baseline, already run successfully before this phase - includes the full scripts/queue-selftest.sh, notably section 11(i)/(j) Windows/psmux dispatch and result-fetch coverage)` - `git show 5efbde3:scripts/lib/queue.py and git show 873c14c:scripts/lib/queue.py - diffed PowerShell.read() across the pre-fix and post-fix commits to confirm the exact code shapes exercised below` - `python3 /home/magicletur/.no-mistakes/evidence/01M2CGKZ3AQ6MJ4M38AX997FEP/verify_wrap_fix.py - manual verification importing the real queue.py PowerShell.decode()/last_out_of(), feeding it a modeled pre-fix (wrapped) vs post-fix (unwrapped, [Console]::Out.Write) stdout for the real ~137-byte non-ASCII result.md fixture used by queue-selftest.sh section 11(i)` </details> <details> <summary>✅ **Document** - passed</summary> ✅ No issues found. </details> <details> <summary>✅ **Lint** - passed</summary> ✅ No issues found. </details> <details> <summary>✅ **Push** - passed</summary> ✅ No issues found. </details>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Intent
Squash PR #74 to one commit reauthored as LeTuR (dropping the +claude.ai address), then finish what it started on two fronts the operator named as hard requirements.
FRONT 1 - fleet MUST be tool- and agent-agnostic. #74 moved the auto-merge allowlist, the publish method and the worker agent out of tracked code into gitignored operator config. A review against scripts/lib/forge.py's bar (an interface, two implementations, and selftests that DRIVE the second with the first's CLI a tripwire on PATH) found the seams asserted but not exercised, plus four remaining couplings, all fixed here:
The tests are the point, not the assertion: queue-selftest.sh gains 12b, which re-runs the whole of section 12 for 'nova' - an agent with no entry anywhere in the code, whose banner, transcript layout, provider and trust dialog all come from agent.conf, with CLAUDE_CONFIG_DIR unset and the quota document naming a different provider, so any built-in literal on that path fails the section. fleet-status-selftest.sh gains 6d, which drives the fuel reader over three answers to 'which provider' with a grep over fleet_status.py as the tripwire. Two existing quota-axi stubs gained the 'auth' branch they had been relying on the literal to paper over. check.sh's automerge check now also rejects the two new trust keys in the tracked example.
Deliberate and NOT to be flagged: .no-mistakes.yaml is this repo's own gate config and correctly names its own tools; orchestration/session-profiles.yaml's 'claude' profile, session-trust.sh's per-agent table and queue.py's AGENT_LIMIT_SIGNALS are documented single-adapter tables with explicit refusal for anything absent, which is the intended design; .claude/skills, .claude/settings.json and CLAUDE.md are file paths this repo genuinely has.
FRONT 2 - per-session context is the product. Measured in words: the always-loaded set (AGENTS.md + FLEET.md + CLAUDE.md) went 5,623 at 59879e1 -> 6,854 at this PR's base -> 7,026 at its head -> 6,344 now; the five SKILL.md files went 21,312 -> 24,028 -> 23,748 -> 23,762. The growth this branch was asked about is mostly not this branch: 16 commits landed on main between 59879e1 and the PR base and account for +1,231 of the +1,403 always-loaded words and all +2,716 of the skills. Two cuts here, both against AGENTS.md's own stated rule that a file points at the authority rather than repeating it: AGENTS.md's numbered loop restated fleet-queue SKILL.md sections 1-7 and queue.sh's header in full and is an index again, and FLEET.md's loop restated AGENTS.md's inside the same context window since both are always loaded, so it keeps what is the lead's and defers the mechanics. Every rule was checked to still exist in the file that owns it before its copy was removed - the explicit constraint was not to lose a rule while cutting its restatement, and FLEET.md's 'How you report' accuracy rules were deliberately left untouched.
Deliberate constraints from the operator: do not rewrite .no-mistakes.yaml to avoid naming its own tools; do not cut FLEET.md's How-you-report accuracy rules; squash-merge only, so the PR title must match the squashed commit subject; force-push only this branch. ./scripts/check.sh exits 0 locally with every check included.
What Changed
extension.toml.in/scripts/install-extension.shnow render the Mission Control lead's agent from theAGENTsetting inorchestration/agent.conf(falling back to thurbox's stock"claude"only when unset), andscripts/lib/fleet_status.pyreadsFUEL_PROVIDER(orAGENT) fromagent.confinstead of an environment-variable/code default, with the stale Claude-only docstrings and prose inFLEET.md/fleet-queue/SKILL.mdcorrected.scripts/session-trust.shan escape hatch —TRUST_SIGNATURE/TRUST_KEYSinagent.conf— so it can be taught an agent outside its built-in per-agent dialog table (or toldTRUST_KEYS=none) instead of always refusing; add the tracked, name-nothingorchestration/agent.example.conf,auto-merge.example.conf, andpublish.example.conf, and reject the new trust keys incheck.sh's automerge check when they appear in the tracked example.queue-selftest.sh(section 12b) andfleet-status-selftest.sh(section 6d) to drive the new agent-agnostic paths end-to-end for a fictitious "nova" agent defined only in config; trimAGENTS.md/FLEET.md's queue-loop sections to point atfleet-queue/SKILL.mdandqueue.shinstead of restating their steps, and update the other SKILL.md files,.gitignore, andREADME.mdaccordingly.Risk Assessment
✅ Low: This fix round only replaces a source-grep tripwire in fleet-status-selftest.sh with a genuine behavioral test and adds two missing entries to README.md; both changes were verified against the code they describe and match round-1's requested fixes, with no new functional risk. Spot-checks of the broader diff's front1/front2 claims (fuel provider discovery, lead-agent rendering/validation, session-trust escape hatch, automerge check coverage, and the AGENTS.md/FLEET.md dedup with rules relocated intact into fleet-queue/SKILL.md) all held up under inspection.
Testing
Baseline
./scripts/check.sh(full suite) already passed; on top of that I ran the three checks most directly tied to the stated intent —status,queue, andautomerge— and all three passed cleanly, including the specific new behavioral tests the PR describes (fleet-status-selftest.sh §6d's credential-discovery test with a fictitious provider, and queue-selftest.sh §12b's full second-agent 'nova' run exercising the trust-dialog escape hatch and transcript/quota seams). Manual inspection confirmed the four named code fixes (extension.toml.in's LEAD_AGENT placeholder, fleet_status.py's operator-driven provider lookup, session-trust.sh's TRUST_SIGNATURE/TRUST_KEYS, and the automerge check covering the two new keys) are present as described, the previously-flagged source-grep tripwire was removed and replaced with a behavioral assertion, README.md's conf list is complete, and the always-loaded doc word count (6,344) matches the intent's claimed figure exactly. No test failures, no missing evidence, no findings.Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
🔧 **Rebase** - 6 issues found → auto-fixed ✅
.agents/skills/fleet-queue/SKILL.md- merge conflict rebasing onto origin/claude/fleet-gitignore-config-rgb2e3AGENTS.md- merge conflict rebasing onto origin/claude/fleet-gitignore-config-rgb2e3FLEET.md- merge conflict rebasing onto origin/claude/fleet-gitignore-config-rgb2e3orchestration/agent.example.conf- merge conflict rebasing onto origin/claude/fleet-gitignore-config-rgb2e3scripts/check.sh- merge conflict rebasing onto origin/claude/fleet-gitignore-config-rgb2e3scripts/lib/fleet_status.py- merge conflict rebasing onto origin/claude/fleet-gitignore-config-rgb2e3🔧 Fix applied.
✅ Re-checked - no issues remain.
🔧 **Review** - 2 issues found → auto-fixed ✅
scripts/fleet-status-selftest.sh:650- The new §6d tripwire ends withif grep -qiE 'claude|anthropic' scripts/lib/fleet_status.py; then fail .... This is a source-content-only assertion (grepping implementation source for a vendor string) rather than an observation of behavior — the exact anti-pattern the test-quality rule forbids ('Never add a test whose only evidence is that it ... greps ... implementation source code and finds or omits particular strings'). It sits alongside genuinely behavioral assertions in the same section (three real invocations of fleet-status.sh / probe_fuel() over different agent.conf settings), so it isn't load-bearing for the section's real coverage, but it is itself a new source-grep assertion and should be removed or replaced with a behavioral check (e.g., asserting probe_fuel()/authenticated_providers() never falls back to a hardcoded vendor on a machine with no operator config and no 'claude' credential, which the three cases above already partially cover).README.md:126- The new paragraph lists the operator's gitignored settings and their tracked.examplecounterparts (owners.txt, voice.conf, session-glyphs.conf, auto-merge.conf) but omits the two new ones added by this same change,orchestration/publish.confandorchestration/agent.conf, even though both are documented in.gitignoreright above with the same rationale. Minor completeness gap, not a functional issue.🔧 Fix: Replace grep tripwire with behavioral test; add missing README conf entries
✅ Re-checked - no issues remain.
✅ **Test** - passed
✅ No issues found.
./scripts/check.sh./scripts/check.sh status— runs scripts/fleet-status-selftest.sh including new §6d, which stubs quota-axi with a fictitious 'glorbnak' provider (never named in fleet_status.py) plus AGENT/FUEL_PROVIDER settings from a relocated agent.conf, proving the fuel reader follows the operator's config/credentials rather than a 'claude' literal; passed./scripts/check.sh queue— runs scripts/queue-selftest.sh including new §12b, which re-runs the full refuel/trust/transcript flow of §12 for 'nova' (an agent absent from AGENT_LIMIT_SIGNALS and every code table) driven entirely by a relocated agent.conf (AGENT, FUEL_PROVIDER, LIMIT_BANNER, TRANSCRIPT_DIR, TRUST_SIGNATURE, TRUST_KEYS), with CLAUDE_CONFIG_DIR unset and a quota document naming a different provider as a tripwire; passed (queue: ok)./scripts/check.sh automerge— validates orchestration/auto-merge.example.conf, publish.example.conf and agent.example.conf ship no operator-specific values, now including the new TRUST_SIGNATURE/TRUST_KEYS keys; passedManual inspection: extension.toml.in line 368 usesagent = "__LEAD_AGENT__"rendered by scripts/install-extension.sh from orchestration/agent.conf's AGENT (falling back to thurbox's stock 'claude' only when unset), confirming the described placeholder wiringManual inspection: scripts/check.sh's check_automerge() now checks TRUST_SIGNATURE and TRUST_KEYS in the AGENT key loop, confirming the fixed automerge checkwc -w AGENTS.md FLEET.md CLAUDE.md— confirmed the always-loaded doc set is 6,344 words, matching the intent's stated post-cut figure exactlyConfirmed the previously-flagged grep tripwire (grep -qiE 'claude|anthropic' scripts/lib/fleet_status.py) is no longer present in scripts/fleet-status-selftest.sh, replaced by the behavioral §6d discovery testConfirmed README.md now documents both orchestration/publish.conf and orchestration/agent.conf alongside the other gitignored settings✅ **Document** - passed
✅ No issues found.
✅ **Lint** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.