Skip to content

Client membrane: the client-repo declaration and the sticky-check waiver (ADR 0030 §11)#7

Merged
Antawari merged 2 commits into
mainfrom
feat/client-membrane-sticky-waiver
Jun 11, 2026
Merged

Client membrane: the client-repo declaration and the sticky-check waiver (ADR 0030 §11)#7
Antawari merged 2 commits into
mainfrom
feat/client-membrane-sticky-waiver

Conversation

@Antawari

Copy link
Copy Markdown
Contributor

STACKED ON PR #6 — MERGE PR #6 FIRST

This branch is cut from fix/gate-kit-defects-node24 (PR #6) because both lanes touch repo_config.py and its test contract. This PR's own diff is the last commit only (feat: client-repo declaration…); everything before it is PR #6. After #6 merges, this PR shows only the membrane work.

What merging this blesses

The contradiction this resolves: canon ADR 0030 §11 (the client membrane) says client repos receive gate-config only — never sticky/chrome, but cf-sticky-check fails STICKY_CLAUDE_MD_MISSING when no CLAUDE.md exists and the kit had no client-lane waiver (zero-inputs doctrine). A client repo's gate could therefore never go fully green — the doctrine forbade the very file the gate demanded. Evidence: the rov finish pass had everything green at the pinned kit with the sticky step the sole red (CI run 27370801157); notioncrm's gate red rides the same contradiction.

The waiver is a committed declaration, never a knob:

[tool.cf-quality]
client_repo = true

Same homes and typed validation as the layout keys (pyproject.toml or .cf-quality.toml, one home only). Adopting it is a reviewed diff — Wizard-gated like any doctrine surface — and the gate honors it loudly: the CLI prints client membrane declared — sticky intro not mounted per ADR 0030 §11 (client repos receive gate-config only, never chrome) on every green run. Impossible to adopt silently.

The three control rods (each pinned by test, each proven against the real client shapes)

  • R1 — repo with client_repo = true and no CLAUDE.md → sticky-check exits 0 with the loud notice. Proven against the rov shape (no CLAUDE.md): exit 0, notice printed.
  • R2 — repo WITHOUT the declaration and no CLAUDE.md → still fails STICKY_CLAUDE_MD_MISSING. Zero behavior change for the fleet; client_repo = false behaves byte-for-byte as undeclared.
  • R3 — repo WITH the declaration but carrying a sticky block anyway → fails STICKY_CLIENT_MEMBRANE_BREACHED (the membrane is two-way). Chrome is recognized by the canonical heading line, the kit's mount-marker header (any vintage — the v1 header included), and exact/near block match in raw bytes (a buried copy is chrome too); prose merely naming the law is NOT chrome. Proven against the real notioncrm CLAUDE.md (which carries the v1-era sticky): BREACH, exit 1.

Plus the hardening around the rods:

  • A tampered/incomplete declaration fails loud and typed: client_repo = "yes" or = 1 is GATE_CONFIG_INVALID (exit 2), never coerced.
  • cf-sticky-check mount refuses a declared client repo outright (STICKY_MOUNT_CLIENT_MEMBRANE, typed, nothing written) — the kit never pushes chrome through the membrane.

Honest residue (on the record, DESIGN.md §7)

The gate cannot verify WHO is a client: a non-client repo could commit the declaration to dodge the sticky mount. Defense as shipped: adoption is a reviewed diff, and the notice prints on EVERY gate run, so a wrongly-declared repo is loud in its own CI logs. The candidate mechanical closure (a Constable-cadence sweep of client_repo declarations against the known client list) is named, not built.

Consumer-side decisions this does NOT make (Anta-gated, carved out)

Verification

  • TDD: rods written RED first (14 failing for the right reasons), then GREEN. Tests 337 → 355 on this branch (310 on main).
  • Full self-ci battery green locally: ruff · ruff format · cf-sticky-check · cf-file-budget · cf-recursion-check · cf-exemptions · cf-import-contract · mypy (0 errors) · complexipy · pytest.
  • Layering held: sticky_check → repo_config is a downward edge inside the committed import contract; cf-import-contract green.

Files

  • src/cf_quality/repo_config.pyclient_repo key (strict TOML bool, typed failure on tamper)
  • src/cf_quality/sticky_check.py — the waiver, the two-way breach, the mount refusal, the loud notice
  • tests/test_repo_config.py · tests/test_sticky_check.py — the rods
  • DESIGN.md §7 — the membrane mechanism + honest residue

CI state

Will be reported on this PR's checks (self-ci runs the full battery on this branch, which includes PR #6's commits underneath).

DO NOT MERGE without Anta's review — and merge PR #6 first.

🤖 Generated with Claude Code

Antawari and others added 2 commits June 11, 2026 15:57
…t-party, honor source_root, Node-24 action pins

Four measured gate defects from the overnight burn's adversarial verifies,
plus the Node-20 deadline, each with a control rod that MUST fail:

1. Kit-pin honor — github.job_workflow_sha was OBSERVED empty at run time:
   the kit checkout received no ref and floated to kit MAIN (git checkout -B
   main refs/remotes/origin/main), so SHA-pinned consumers were gauged by an
   unpinned gauge (counts irreproducible, twice in one night). The gate now
   re-anchors the kit checkout to the pin the consumer COMMITTED in its
   caller stub, refuses two distinct pins, and refuses to run when no pin is
   determinable — fail-safe, never a silent float. Rod: the step's script
   runs against a real git fixture whose floating checkout must be moved to
   the declared pin (tests/test_gate_hardening.py).

2. complexipy joins the gate — the watermark was runbook-only; now a
   workflow step in both quality-gate.yml and self-ci.yml with the
   mypy-style presence rule (a Python repo without its committed snapshot
   FAILS; Python-free repos skip visibly; never piped). The kit ratchets
   FIRST: its own snapshot is booted and gated.

3. known-first-party derived per consumer — a shared gauge file cannot name
   every consumer's packages, and ruff's on-disk detection mis-files
   first-party imports that do not resolve (measured: fixing the kit's two
   I001 created two NEW legacy-isort I001 in the same repo). cf-repo-config
   grows a first-party field (derived from the resolved source root) and the
   gate feeds it inline to the pinned ruff gauge.

4. cf-exemptions honors the declared source_root — the scanner discovered
   the repo-root src/ (JS in the measured case) and never visited the
   declared tree, making every registered exemption documentation-grade.
   The scan surface now resolves through cf-repo-config exactly like the
   gauges. Rod: an unregistered suppression under a declared server/src
   must fail (it silently passed before).

5. Node-24 action pins — GitHub forces Node 24 on 2026-06-16; both pinned
   actions bumped to their Node-24 releases (checkout v6.0.3, setup-python
   v6.2.0, full-SHA pinned, releases verified). The deprecated Node-20 SHAs
   are denylisted ratchet-style.

Tests 310 -> 337; full self-ci battery green locally. Two test files split
at the file budget's own refusal — the gate caught its maker, the split is
the fix, not a baseline.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ent membrane (ADR 0030 §11)

The canon's client membrane and the gate contradicted each other: client
repos receive gate-config only — never sticky/chrome — but cf-sticky-check
failed STICKY_CLAUDE_MD_MISSING with no waiver knob (zero-inputs doctrine),
so a client repo's gate could never go fully green.

The waiver is COMMITTED state, never a knob: [tool.cf-quality]
client_repo = true (same homes and typed validation as the layout keys; a
tampered declaration — "yes", 1 — fails GATE_CONFIG_INVALID, never coerces).
Three control rods, each pinned by test and proven against the real client
shapes:

- R1: declared client + no CLAUDE.md -> check passes and the CLI prints the
  loud membrane notice; waived visibly, never silently.
- R2: no declaration -> behavior unchanged for the fleet, byte for byte;
  client_repo = false behaves exactly as undeclared.
- R3: declared client carrying ANY sticky chrome (canonical block, chewed
  copy, older-vintage block, or the kit's mount-marker header) fails
  STICKY_CLIENT_MEMBRANE_BREACHED — the membrane is two-way. mount refuses
  a declared client repo outright (STICKY_MOUNT_CLIENT_MEMBRANE).

Honest residue on the record (DESIGN.md §7): the gate cannot verify WHO is
a client — adoption is a reviewed diff, the notice prints on every run, and
a Constable-cadence declaration sweep is the candidate mechanical closure.

Tests 337 -> 355; full battery green locally.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Antawari Antawari merged commit fecabc6 into main Jun 11, 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