feat(fleet): rewrite lead's reporting voice and make operator/assistant names configurable - #60
Merged
Conversation
The TUI queue pane draws the board live, so a table pasted into a reply repeats what the operator is already looking at. `FLEET.md`'s "How you report" now makes the default reply one or two lines, naming a task only when something changed or surprised. The register the section already described is now written around two names the operator owns rather than a literal: `FLEET.md` carries `@OPERATOR_NAME@` and `@ASSISTANT_NAME@`, `orchestration/voice.example.conf` holds the tracked defaults (SLAYER, VEGA) with a gitignored `voice.conf` beside it, and `scripts/install-extension.sh` renders both into a gitignored `FLEET.rendered.md` that the manifest ships as its payload — the same tracked/generated split the session glyph already uses, and for the same reason: an operator who renames themselves dirties no tracked file that `sync-checkout.sh` would then refuse to fast-forward. Two rules go in beside it: register, not costume, and a register is free where a bit costs a paragraph. Every accuracy rule survives unchanged. `scripts/check.sh voice` is the gate — it asserts the placeholders are in FLEET.md and the defaults are not, then renders through `install-extension.sh --render-only` off a throwaway conf and checks the names landed. Claude-Session: https://claude.ai/code/session_01ByfHVZg4W61Zw5Hv9g15pH
…prevent placeholder collision
…ent with the & and @ rejections
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
Rewrite FLEET.md's "How you report" section — the standing instruction block the Mission Control lead session runs under — with three changes the operator asked for in their own words:
(1) "Showing the table is no more required, we do have fleet queue custom pane for that." The section used to MANDATE a status table with a worked example; interface/fleet_queue.lua now draws the queue live in a thurbox column, so a table in a reply repeats it. The table and its example are deleted deliberately. The default reply is now one or two lines, naming a task only when something CHANGED or surprised. The ./scripts/fleet-status.sh bullet was deliberately KEPT — one call for fuel/queue/sessions/PRs/checkout is still the right way to answer "where are we", and that is not the same thing as pasting a table unprompted.
(2) "For a bit more fun, lets improve voice, with doom inspired theme. You must call me SLAYER, you are mission-control (alias vega). The SLAYER do not have much time to waste with long text. Be concise." The existing do/do-not tables were deliberately kept as tables and only their CONTENTS rewritten. Two rules are stated explicitly because they are what decays first: register-not-costume (no quoted lines, no roleplay narration, no in-fiction preamble, no emoji or ASCII art — it lives in verb choice and terseness), and "the register is free; a bit is not" — the operator's own "Keep our fun part efficient in token". Every accuracy rule already in the section survives unchanged in force: uncertainty is a state word not a hedge, no estimates, report the artifact not the intention, never restate the brief, say what you did not do, one fact in one place.
(3) "(We should allow user to customize there name if they wants too)." SLAYER and VEGA are therefore NOT hard-coded. New tracked orchestration/voice.example.conf carries OPERATOR_NAME=SLAYER and ASSISTANT_NAME=VEGA; a gitignored voice.conf beside it overrides. Neutral defaults were explicitly ruled out — the operator asked for SLAYER and VEGA, so those ship as the tracked defaults.
The mechanism follows the precedent this repo already set for the session glyphs (orchestration/session-glyphs.example.conf, rendered into the manifest by scripts/install-extension.sh) rather than having the agent read a conf file itself: the lead FREEZES its instructions at launch, so a file it must read every session to learn its operator's name is a read it will forget. So FLEET.md carries @OPERATOR_NAME@ / @ASSISTANT_NAME@ placeholders and install-extension.sh renders them into a NEW gitignored FLEET.rendered.md, which extension.toml.in now ships as its [[files]] payload with the three context-file symlinks retargeted at it. Rendering to a second file rather than in place is deliberate and is the whole reason for the tracked/generated split: an operator who renames themselves must not leave scripts/sync-checkout.sh a dirty tree it refuses to fast-forward.
The placeholders are @-delimited rather than __-delimited like the manifest's REPO_PATH/LEAD_GLYPH because markdown reads x as bold emphasis and rumdl (MD050) rejects it — this was found by the linter, not guessed.
install-extension.sh gained --render-only
, which renders both files into a directory and stops, needing neither thurbox-cli nor jq. That exists so the gate can exercise the substitution end to end without installing anything or overwriting the operator's own rendered payload; FLEET_VOICE_CONF is the seam it renders through.scripts/check.sh gained a check, run as part of the default gate: it asserts the placeholders are in FLEET.md and the default names are NOT (one place spells a setting), then actually runs --render-only against a throwaway conf in a temp dir and asserts the names landed and no placeholder survived.
Supporting doc updates: .gitignore entries for FLEET.rendered.md (GENERATED) and orchestration/voice.conf (WORKING STATE) with the why-lines that file's header requires; sync-checkout.sh's WIRING_PATHS gains voice.example.conf so a sync that changes the defaults reports reinstall-extension; CONTRIBUTING.md's tracked/ignored table and extension-manifest section; the fleet-onboarding and update-fleet skills. extension.toml.in's version bumped 1.2.0 -> 1.3.0.
AGENTS.md was deliberately NOT touched — it is the repo's operating guide, not the lead's voice, and another worker is editing prose in it concurrently. Applying this change needs a re-install plus a lead restart; neither was run here, by instruction.
./scripts/check.sh passes locally.
What Changed
fleet_queue.luapane now draws the queue live) in favor of a one-to-two-line default reply that only names a task when something changed or surprised, while keeping the./scripts/fleet-status.shbullet; rewrote the do/do-not tables' contents with a doom-inspired register (terse, no roleplay/emoji/ASCII art, "the register is free; a bit is not") while preserving every existing accuracy rule.@OPERATOR_NAME@/@ASSISTANT_NAME@placeholders, added trackedorchestration/voice.example.conf(defaultsSLAYER/VEGA) with a gitignoredvoice.confoverride, andinstall-extension.shrenders both files into a new gitignoredFLEET.rendered.mdthatextension.toml.innow ships as its[[files]]payload (context-file symlinks retargeted at it); addedinstall-extension.sh --render-only <dir>to exercise the substitution without thurbox-cli/jq.scripts/check.shgate check asserting the placeholders are present inFLEET.md, the default names are absent from it, and that--render-onlycorrectly substitutes names with no leftover placeholders; updated.gitignore,sync-checkout.sh'sWIRING_PATHS,CONTRIBUTING.md, and the fleet-onboarding/update-fleet skills accordingly; bumpedextension.toml.inversion 1.2.0 -> 1.3.0.Risk Assessment
✅ Low: The round-1 finding (sed substitution chaining allowing an @ASSISTANT_NAME@-valued OPERATOR_NAME to silently collide) is verifiably fixed — reproduced the attack against the current code and confirmed it is now rejected with a clear error; the full local gate (scripts/check.sh, including the new voice check) passes, FLEET.md contains only placeholders with no literal names, AGENTS.md is untouched as required, and a live --render-only run correctly substitutes SLAYER/VEGA end to end.
Testing
Baseline
./scripts/check.shhad already passed; I additionally reproduced the specific review-1 collision bug manually and confirmed it is now refused, and ran a normal render plus the isolatedvoicegate check to confirm the FLEET.md rewrite and rendering pipeline work end-to-end as the user intent describes — all passed with no issues found.Evidence: Rendered 'How you report' section (SLAYER/VEGA)
Evidence: Fix verification: collision scenario now refused
Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
scripts/install-extension.sh:195- The two placeholder substitutions run as two -e scripts in the same sed invocation, so they apply sequentially over the same pattern space rather than independently. If OPERATOR_NAME's value happens to contain the literal text "@ASSISTANT_NAME@" (not blocked by the current denylist, which only rejects |, , &, ' and "), the first substitution injects that string into the line, and the second substitution then rewrites it again with ASSISTANT_NAME's value — silently replacing the operator's name with the assistant's name with no error and no surviving-placeholder detection. Reproduced: with OPERATOR_NAME=@ASSISTANT_NAME@ and ASSISTANT_NAME=VEGA,--render-onlyproduces "You answer to VEGA; the operator is VEGA." instead of failing or preserving the operator's chosen text. Fix by rejecting '@' in the name denylist, or by performing both substitutions in a single non-chaining pass (e.g. one sed/awk expression keyed on both placeholders at once, or two independent passes over the original source rather than over each other's output).🔧 Fix: Reject '@' in operator/assistant name to prevent placeholder collision
1 info still open:
orchestration/voice.example.conf:42- The trailing comment says the renderer refuses a name carrying only 'a quote, a pipe or a backslash', but the fix round widened the denylist in scripts/install-extension.sh to also reject '&' and '@' (closing the placeholder-collision bug from round 1). The comment is now stale and undersells the protection.✅ **Test** - passed
✅ No issues found.
./scripts/check.shReproduced review-1's exact collision scenario:FLEET_VOICE_CONF=/tmp/voice-repro/voice.conf ./scripts/install-extension.sh --render-only /tmp/voice-repro/outwith OPERATOR_NAME=@ASSISTANT_NAME@, ASSISTANT_NAME=VEGA — confirmed it now dies with 'a name ... contains ... an @' instead of silently rendering a swapped nameNormal render:FLEET_VOICE_CONF=/tmp/voice-repro/voice-good.conf ./scripts/install-extension.sh --render-only /tmp/voice-repro/out2with OPERATOR_NAME=SLAYER, ASSISTANT_NAME=VEGA — confirmed FLEET.rendered.md has 0 surviving @NAME@ placeholders and the 'How you report' section text matches the intended rewrite (no mandated table, fleet-status.sh bullet kept, do/do-not tables kept with new content)./scripts/check.sh voicerun in isolation — passedgit status --porcelainafter testing — confirmed clean working tree, no stray generated files (extension.toml, FLEET.rendered.md, voice.conf) left in the repo✅ **Document** - passed
✅ No issues found.
✅ **Lint** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.