fix(lib,tests): mu-plugin perms self-heal + effective-prompt .actual cleanup#136
Merged
Merged
Conversation
…me-signature cli_channel_ensure_mu_plugin_file / cli_channel_register / cli_channel_unregister and the runtime-signature equivalents wrote their mu-plugin files using the caller's umask. Root cron/systemd contexts default to umask 0077, producing mode 0600 files that PHP-FPM (www-data) cannot read — every WordPress request then logs 'Permission denied' from wp-settings.php and the datamachine_code_cli_channels / datamachine_code_worktree_runtime_signatures filters return empty arrays, breaking dispatch-message and worktree origin attribution respectively. Force mode 0644 after every write path in both helpers: * Initial scaffold write (cat > $file) * Block register (mktemp + mv — mktemp creates at 0600, mv preserves) * Block unregister (same mktemp + mv path) The chmod after mktemp+mv also self-heals legacy installs that already have a 0600 file on disk — the next register/unregister upgrades it. Add tests/cli-channel-perms.sh covering all three write paths under a hostile umask (umask 077) plus the self-heal scenario. Extend tests/runtime-signature.sh with the same assertions: fresh write under umask 077 lands as 0644, sibling register self-heals a 0600 file, and unregister preserves 0644. Closes #133
…tive-prompt runs
tests/effective-prompt/run.mjs writes .actual diff-intermediates next to
the canonical .baseline.txt / .filtered.txt / .raw.txt snapshots whenever
a scenario fails snapshot comparison, but never removes them. They
accumulate in tests/effective-prompt/__snapshots__/ and dirty the working
tree, which blocks 'homeboy release wp-coding-agents' on its
preflight.working_tree check.
Two-part fix matching the issue's recommendation:
1. Add tests/effective-prompt/__snapshots__/.gitignore ignoring *.actual.
Canonical snapshots stay tracked; diff intermediates become invisible
to git but still land on disk for reviewer inspection when a test
fails. Snapshot drift still surfaces loudly because run.mjs prints
'snapshot drift — run with --update to refresh' in the FAIL block and
exits non-zero; the .gitignore only hides the intermediate copies of
the new content, not the failure signal itself.
2. In run.mjs, after the main loop completes with zero failures, unlink
every *.actual file in __snapshots__/. On any failure, leave them in
place so the reviewer can inspect the diff. This means a clean run
produces a clean working tree even without the .gitignore, and the
.gitignore handles the failure case.
Verified end-to-end: drifted snapshots produce a FAIL with .actual files
left on disk but absent from 'git status'; a passing run after --update
clears the .actual files entirely.
Closes #134
Member
Author
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
Two paper-cut bugs blocking real workflows on the extrachill.com VPS.
#133 — mu-plugin perms (
lib/cli-channel.sh,lib/runtime-signature.sh)cli_channel_ensure_mu_plugin_file/cli_channel_register/cli_channel_unregisterand the runtime-signature equivalents wrote their mu-plugin files using the caller's umask. Root cron/systemd contexts default to umask0077, producing mode0600files that PHP-FPM (www-data) cannot read — every WordPress request then logs "Permission denied" fromwp-settings.phpand thedatamachine_code_cli_channels/datamachine_code_worktree_runtime_signaturesfilters return empty arrays, breaking dispatch-message and worktree origin attribution respectively.Implementation chosen:
chmod 0644after every write path. This integrates most cleanly with the existingcat > "$file"andmktemp + mvpatterns the helpers already use, requires no restructuring aroundinstall -m 0644, and keeps the umask of the calling context untouched (no risk of leakingumask 022into other helpers that share the bash process).Three write paths covered in both helpers:
cat > $file→chmod 0644)mktempcreates at0600,mvpreserves, thenchmod 0644)mktemp + mvpath)The chmod after
mktemp + mvis what gives us self-heal: a legacy0600file from a pre-#133 install gets upgraded the next time the helper touches it.#134 —
.actualsnapshot files (tests/effective-prompt/run.mjs)The effective-prompt harness writes
.actualdiff-intermediate files alongside the canonical snapshots when a scenario fails, but never cleans them up. They accumulated intests/effective-prompt/__snapshots__/and dirtied the working tree, which blockshomeboy release wp-coding-agentsonpreflight.working_tree.Two-part fix matching the issue's recommendation:
tests/effective-prompt/__snapshots__/.gitignoreignoring*.actual. Canonical.baseline.txt/.filtered.txt/.raw.txtsnapshots stay tracked; only the diff intermediates become invisible to git.run.mjs: after the main loop with zero failures, unlink every*.actualin__snapshots__/. On any failure, leave them in place for reviewer inspection.Snapshot mismatches still surface as visible test failures. The harness already prints
<label> snapshot drift — run with --update to refreshin the FAIL block and exits non-zero. The.gitignoreonly hides the intermediate copies of the new content, not the failure signal itself. Confirmed end-to-end on this branch: drifted snapshots produced loud FAIL output with.actualfiles left on disk for inspection but absent fromgit status.Verification
bash -non modified scriptsPerms test (umask 077 + self-heal scenarios)
Regression-validity check (test fails without the fix)
Stashed the fix and re-ran
tests/cli-channel-perms.shagainst unpatchedlib/cli-channel.sh:Test catches the exact bug from the bug report.
Effective-prompt cleanup
Ran the harness against drifted snapshots (kimaki on host has moved ahead of committed snapshots — unrelated to this PR).
.actualfiles were written to disk on failure but were absent fromgit statusthanks to the new.gitignore. After--updateto refresh snapshots, the next clean run unlinked every.actualfile and lefttests/effective-prompt/__snapshots__/containing only the six canonical.txtfiles. Working tree clean.No release/version churn
CHANGELOG.mdeditsVERSIONeditshomeboy release/homeboy deployrunCloses #133, closes #134