Skip to content

A gate for the Tier-3 path list: every entry exists, every sensitive file is listed #9

Description

@chiibitsu

Founder-ruled 2026-09-11: accept the gap now, build the gate later. Filed so it does not survive as a stale doc.

The gap

AGENTS.md in a consuming repo names Tier-3 paths — the files whose changes need the heavy process (audit pack, founder merge, no agent merge). That list is prose. Nothing checks it.

It went stale exactly the way an unchecked list does, and it took a reviewer to notice, in chiibitsu/nextjs-supabase-template#4:

  • It named src/lib/env.ts, deleted three PRs earlier when that module was split into env.shared.ts / env.client.ts / env.server.ts, and named none of the three. A pull request editing only env.server.tsthe module holding the service-role schema — matched no Tier-3 path and read as normal: one reviewer round, no audit pack, no founder merge.
  • It omitted REVIEW.md, an instruction file the prose above the list already covers, so a change to the review policy itself could take the gates-only path.

Both directions broken at once, in the document that states the rule "every list is checked in both directions, and each list exists exactly once."

The contents were fixed by hand. The mechanism that let them rot was not, and a hand-fixed list rots again. It also costs a review round every time a bot re-derives it — which is the practical argument for a gate over a doc.

What the gate asserts

Both directions, per the canon:

  1. Forward — every path pattern in the Tier-3 list resolves to something in the tree. A pattern matching nothing is either a deleted file or a typo, and both read identically today.

  2. Reverse — every file that should be Tier-3 appears in the list. This is the harder half and needs a definition rather than a vibe. Candidate rule, to be argued in the PR rather than assumed here:

    • every instruction file (AGENTS.md, CLAUDE.md, REVIEW.md, agent config such as opencode.json, .gemini/**)
    • every module whose source names a secret the nextjs-env denylist knows about
    • everything under .github/**, scripts/**, supabase/**

    The reverse rule is where this gate can go wrong. Too narrow and it asserts less than its name; too broad and it reddens on ordinary files and gets disabled. A reverse rule that cannot be stated precisely is a reason not to ship the reverse half, not a reason to ship a loose one — that would be the defect this toolkit exists to catch, in the gate written to catch it.

Fixtures

Bad trees, each shown to fail before the gate counts:

  • an entry naming a path that does not exist (the src/lib/env.ts case, reproduced)
  • a file replacing a listed one, present in the tree and absent from the list (the env.server.ts case)
  • an instruction file absent from the list (the REVIEW.md case)
  • a module naming a denylisted secret, absent from the list

And — per the class-3 gap already open in #2at least one valid tree as a first-class case, because the bad-trees-only fixture model cannot catch a regression that reddens a compliant list, and a gate that reddens on a correct tree gets switched off rather than fixed.

Where the config lives

The list is AGENTS.md prose today. A gate cannot parse prose reliably, so this needs a per-repo config file next to required-files.txt and denylist.txt, with AGENTS.md pointing at it rather than duplicating it — each list exists exactly once, or this issue recreates the problem it is filed about one file over.

Related

Origin: chiibitsu/nextjs-supabase-template#4, and the audit record product/audits/review-lane-adoption.md §7 in that repo, which carries the founder acceptance and links here.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions