Skip to content

fix(gates): resolve the content screener at generation time; count aliased context files once - #3

Merged
alkisyuv merged 3 commits into
mainfrom
fix/screen-runner-and-context-aliases
Aug 24, 2026
Merged

fix(gates): resolve the content screener at generation time; count aliased context files once#3
alkisyuv merged 3 commits into
mainfrom
fix/screen-runner-and-context-aliases

Conversation

@alkisyuv

Copy link
Copy Markdown
Member

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 the
etymd on PATH. That path is simply where a great many CLI projects build, so any repo that
builds its own binary there had the hook invoke that binary as the screener.
It does not know
screen, so:

  • the commit door failed closed on every commit (error: unknown command 'screen', rc=1);
  • the push door, which ignores the screen's exit status by design, skipped the whole-tree
    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 the etymd on PATH — in every repo. The
dev-build arm survives for the repo that develops the screener itself, where gating on an
unreleased build is the whole point, but isSelfBuildRepo decides it at generation time from
the 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 coincidence
for 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.js makes a real git commit that runs the screen through the ambient etymd with no
CONTENT_GATE override, and never reaches its own binary.

2. Context economy counted a symlinked instruction file twice

AGENTS.md symlinked to CLAUDE.md is the ordinary way to serve harnesses that read different
names. 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), in etymd audit 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 lens disclosures rather than applied silently.

Notes

  • Pack v8 → v12. Numbers 9–11 are claimed by unreleased work on the other two open branches; 12
    is above all of them, so any merge order resolves to it.
  • This repo's own hooks are regenerated in the same change, and keep the dev-build arm — it is the
    dogfood case.
  • Green: 269 passed | 8 skipped, format:check, typecheck, and the self-audit clean at exit 0.
  • The self-audit discloses Baseline was approved under pack v8; current pack is v12
    re-approving the baseline is a separate call.
  • Conflicts with the other two open branches in the four generated hook files and
    src/pack/version.ts; mechanical, as noted there.

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.
@alkisyuv
alkisyuv merged commit 2d32c43 into main Aug 24, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant