fix(gates): resolve the content screener at generation time; count aliased context files once - #3
Merged
Conversation
The emitted resolution carried a bare `[ -x ./dist/cli.js ]` check ahead of the etymd on PATH. That path is where a great many CLI projects build, so any repo building its own binary there had the hook invoke that binary as the screener. It does not know `screen`: the commit door failed closed on every commit, and the push door — which ignores the screen's exit status by design — skipped the whole-tree pass in silence. A plain dependency install armed it, since that runs the repo's build. Resolution is now CONTENT_GATE, then the etymd on PATH. The dev-build arm survives for the repo developing the screener itself, where gating on an unreleased build is the point, but `isSelfBuildRepo` decides it at generation time from the MANIFEST name — not `facts.name`, which falls back to the directory basename and would re-arm the same coincidence. Pack v12; this repo's own hooks regenerated in the same commit.
Symlinking AGENTS.md to CLAUDE.md is the ordinary way to serve harnesses that read different names. Both were measured separately, doubling the reported always-loaded footprint — enough to manufacture an over-budget finding out of nothing — and firing the heavy-file finding twice for a single file. Candidates resolving to one inode (symlink or hardlink) are counted once and reported under every name they answer to, in the lens and `etymd context` alike. The finding ID stays the primary path so a ledger entry survives a repo adding or dropping a name, and the merge is stated in the disclosures rather than applied silently.
The screen doors resolve their checker at run time from outside the repo, so whether the gate will run is the one thing reading the hook cannot answer. When the resolved checker is not a screener, the commit door failed closed on that program's bare error and the push door — advisory by design — skipped its pass in silence. Two halves of one class. The emitted hooks now probe `screen --help` AFTER a failure only, and print the version floor and the override rather than leaving "unknown command" as the last word; a screener reporting a real finding is left to speak for itself. And gate integrity resolves the runner the way the hook does and asks it the same question, as a risk/truth finding so `doctor` surfaces it too. No checker installed stays the designed no-op — disclosed, never a finding — and a probe that could not be carried out claims nothing.
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.
Two defects, both cases of a measurement or a gate being wrong about the repo it runs in.
1. Generated hooks guessed at the content screener by path
The emitted content-screen resolution carried a bare
[ -x ./dist/cli.js ]check ahead of theetymdon PATH. That path is simply where a great many CLI projects build, so any repo thatbuilds its own binary there had the hook invoke that binary as the screener. It does not know
screen, so:error: unknown command 'screen', rc=1);pass in silence — the worse of the two, since the repo still looks screened.
The trap armed itself on a plain dependency install, because that runs the repo's build. Measured
in three repos that build to that path; a fourth was unaffected only because nothing had built it.
The fix. Resolution is now
CONTENT_GATE, then theetymdon PATH — in every repo. Thedev-build arm survives for the repo that develops the screener itself, where gating on an
unreleased build is the whole point, but
isSelfBuildRepodecides it at generation time fromthe manifest name and emits it nowhere else. It keys on the manifest name rather than
facts.name, which falls back to the directory basename and would re-arm the same coincidencefor any directory that happens to share the name.
Consumer hooks get strictly less shell than before. A repo needing a different runner for one
invocation still has
CONTENT_GATE.Pinned by four tests, including the reported symptom end to end: a repo with a foreign executable
dist/cli.jsmakes a realgit committhat runs the screen through the ambient etymd with noCONTENT_GATEoverride, and never reaches its own binary.2. Context economy counted a symlinked instruction file twice
AGENTS.mdsymlinked toCLAUDE.mdis the ordinary way to serve harnesses that read differentnames. Both names were measured separately, which doubled the reported always-loaded footprint —
enough to manufacture an over-budget finding out of nothing — and fired the heavy-file finding
twice for one file.
Candidates resolving to the same inode (symlink or hardlink) are now counted once and reported
under every name they answer to (
AGENTS.md → CLAUDE.md), inetymd auditandetymd contextalike. The finding ID stays the primary path so a ledger entry survives a repo adding or dropping
a name, and the merge is stated in the lens disclosures rather than applied silently.
Notes
is above all of them, so any merge order resolves to it.
dogfood case.
format:check,typecheck, and the self-audit clean at exit 0.Baseline was approved under pack v8; current pack is v12—re-approving the baseline is a separate call.
src/pack/version.ts; mechanical, as noted there.