chore(tooling): shellcheck gate, gitleaks filename exemption, and orchestration observations - #99
Merged
Conversation
The repo lints YAML and Ansible and scans for secrets, but its 13 tracked shell scripts had no static analysis at all. Surfaced when a dev-services Task 1 worker reached for shellcheck to check the scripts it was writing and found the repo did not ship it. Pinned in mise.toml alongside the other tools, and wired into lefthook so it applies to every shell script rather than one task's. --severity=warning rather than the default: the tracked scripts carry info-level SC2015 notes (the deliberate 'A && B || C' assertion idiom in the *-test.sh harnesses) which would block every commit. Warning-level is green across the repo today, so the gate catches regressions instead of demanding a cleanup first. The one warning-level blocker was module-setup.sh, where SC2154 fires on initdir/moddir/systemdsystemunitdir. Those are dracut's module API, injected into the script by dracut itself, so the finding is a false positive and is silenced file-wide with the reason recorded inline. Claude-Session: https://claude.ai/code/session_01F8oKrXASMQSXMVjSvuJpn9
Adding `mise.local.toml` to .gitignore was blocked by the internal-hostname rule, which matched the literal `mise.local` inside the filename. The rule cannot tell a repo-owned path from a machine name, which is the exact case #97 already carved out a second exemption class for; this is its fourth member, not a new precedent. Anchored like its siblings, so the backstop still catches a real host: verified that `mise.local.toml` passes clean while `buildbox-mise.local` still fires. The file itself is the machine-local mise env at the repo root, carrying this host's absolute CLAUDE_PLUGIN_DATA and CLAUDE_PLUGIN_ROOT so planwright's fleet scripts can resolve their state home. Untracked for the usual reason: every value in it is specific to one machine. Claude-Session: https://claude.ai/code/session_01F8oKrXASMQSXMVjSvuJpn9
Every /orchestrate dispatch writes a timestamped marker under specs/<spec>/.orchestrate/, which is runtime state by design — the tower rebuilds it from git and process evidence rather than reading it back from a commit. Untracked but unignored, it left the working tree dirty after every run and showed up as an uncommitted-change warning when opening a PR. Claude-Session: https://claude.ai/code/session_01F8oKrXASMQSXMVjSvuJpn9
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.
Summary
Side-effects of orchestrating
specs/dev-servicesTask 1: two tooling fixes therun forced, and six observation fragments recording what it broke on. No spec
work — that is #98.
Tooling
shellcheckwired in (9afa7e3). The repo lints YAML and Ansible andscans for secrets, but its 13 tracked shell scripts had no static analysis.
Pinned in
mise.toml, wired intolefthook.yml.--severity=warningratherthan the default, because the tracked scripts carry deliberate info-level
SC2015 notes (
A && B || C) in the*-test.shharnesses that would blockevery commit; warning-level is green repo-wide today, so the gate catches
regressions instead of demanding a cleanup first. The one warning-level
blocker was
module-setup.sh, where SC2154 fires oninitdir/moddir/systemdsystemunitdir— dracut's module API, injected by dracut, so a falsepositive, silenced file-wide with the reason inline.
mise.local.tomlexempted from the hostname rule (f46cd08). Adding itto
.gitignorewas blocked byinternal-hostnamematching the literalmise.localinside the filename. This is the fourth member of therepo-owned-filenames exemption class feat(spec): remote-shell kickoff sign-off #97 introduced, not a new precedent.
Anchored like its siblings and verified both directions:
mise.local.tomlpasses clean,
buildbox-mise.localstill fires.Observations (six fragments)
Five are planwright defects found by tripping over them, one is a scope note.
worker-settings-deny-dash-c-blindspotworker-settings.jsonis agit <subcommand>prefix, so none matchgit -C <path> <subcommand>. A worker runninggit -C /path push --forceevades every force-push, merge, amend and reset guardrail while the deny block appears to protect them. Worse, an adopter fixing the allow-side matching gap with a broadBash(git -C:*)would silently disable the whole block.worker-settings-hook-never-fires"${CLAUDE_PLUGIN_ROOT}"/scripts/worker-command-guard.sh, which is not interpolated when the fragment is passed via--settings. The hook silently never fires and an unattended worker deadlocks on its first compound command with no diagnostic.streamjson-recover-hangs-silentlyfleet-streamjson.sh recoverwrites a zero-byte initial message, so a resumed worker blocks on stdin forever with no turn to take.statusreportsrunning, the journal shows no blocks, nothing reports the stall. Observed 36 minutes of silence read as healthy progress.worker-inherits-lc-all-cfleet-streamjson.shandfleet-dispatch-env.shexportLC_ALL=C, which the launched worker inherits, so project CI runs in the C locale.ansible-lintdies onansible-config dump; the same lint is green in an ordinary worktree on the same commit. Fails only inside the worker, so the human never reproduces it.fleet-home-env-gapCLAUDE_PLUGIN_DATAis not exported into the tool environment, so everyfleet-*.shfails to resolve its cross-spec home. Fixed here by the machine-localmise.local.toml.dev-services-consumer-acceptancetecpanover TLS bound to0.0.0.0) is outside this bundle —tasks.mdputs both remote reachability and consuming-project configuration Out of scope, and both services bind loopback. Seed for a successor bundle.Also here
origin/mainwas merged in (7e42d3a) because #97 landed while this branch wasin flight and had advanced
.gitleaks.tomlpast this branch's base. Merge, notrebase.
Verification
lefthook run pre-commitclean (shellcheck, yamllint, gitleaks, syntax-check,ansible-lint).
mise run lintclean. The gitleaks change verified in bothdirections rather than assumed.
Not done here
The
.gitignoreentry and the exemption that unblocks it are included; theidentifier file that would make #98's guard live is not — that needs the
private identifier set, which is the operator's and is deliberately absent from
every artifact an agent can read.