Conversation
…(RED: gate not yet built) Failing-first battery for cf-import-contract, the layering-direction gate: - PASS 1 contract-file lint (before lint-imports): wildcard ignore_imports entries fail; ignore count ratchets shrink-only against a committed import-contract-baseline.json (absent = zero); every top-level package under the source root must be named in a contract clause (exhaustiveness); pinned settings may not be loosened (exclude_type_checking_imports=true, unmatched alerting downgraded). - PASS 2 lint-imports at the kit-pinned version: CONTRACT_BROKEN with named edge + line; enumerated ignore entries pass with the entry printed; stale ignores fail CONTRACT_STALE_IGNORE; packages-with-no-contract fail CONTRACT_MISSING; environment/config failures (uninstalled src-layout package, unreadable config or baseline) surface as typed CONTRACT_CONFIG_ERROR exit 2, never as a violation verdict. - PASS 3 companion AST scan: module-level importlib.import_module / __import__ inside contract-protected layers fail CONTRACT_DYNAMIC_IMPORT; function-body dynamic loading is out of scope by design. Control rods R1..R11 built inline as fixtures; kit wiring pinned (console script cf-import-contract, import-linter== dev pin, blessed templates/import-contract.toml carrying the pinned settings, honest-scope docstring disclosure). All behaviors anchored against import-linter 2.11 observed output (edge form 'core -> tenants.acme (l.1)'; unmatched-ignore default alerting=error; 'Could not find package' on uninstalled src layout). Suite state at this commit: 262 passed, this file RED (ModuleNotFoundError: cf_quality.import_contract). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…5 passed)
Implements the three-pass import-contract gate against the committed
failing-first battery (33 tests, all green; full suite 295 passed):
- PASS 1 lint_contract: wildcard ignore_imports fail CONTRACT_LINT_WILDCARD;
ignore count ratchets shrink-only against import-contract-baseline.json
(absent = zero) as CONTRACT_LINT_RATCHET; every top-level package under the
source root must be named in a contract clause
(CONTRACT_LINT_EXHAUSTIVENESS); pinned settings may not be loosened
(CONTRACT_LINT_PINNED_CONFIG). Unreadable contract/baseline raise typed
CONTRACT_CONFIG_ERROR. Pass-1 verdicts land BEFORE lint-imports runs.
- PASS 2 subprocess run of the kit-pinned lint-imports (cwd = repo root so
flat layouts resolve; --no-cache). Verdict classification anchored against
import-linter 2.11 observed output: BROKEN section edges re-emitted under
CONTRACT_BROKEN ('core -> tenants.acme (l.1)'); 'No matches for ignored
import' is CONTRACT_STALE_IGNORE; anything verdict-less (e.g. "Could not
find package" on an uninstalled src layout) is typed CONTRACT_CONFIG_ERROR
exit 2, never a violation. Honored enumerated ignores are printed on pass.
- PASS 3 scan_dynamic_imports: module-level importlib.import_module /
__import__ inside contract-protected layers fail CONTRACT_DYNAMIC_IMPORT;
function bodies are out of scope by design (iterative AST walk, no descent
into function scopes).
Kit wiring: console script cf-import-contract registered; import-linter==2.11
pinned exactly in [dev] (the pass-2 oracle may not drift); blessed
templates/import-contract.toml ships the pinned settings; honest
layering-direction-proxy scope disclosed in the module docstring.
The kit submits to its own gauge: the new S603 subprocess suppression is
registered in exemptions.json with frozen_count bumped 1 -> 2 (visible
ratchet decision, approver pending maintainer ratification); ruff check +
format, mypy (0 errors), cf-file-budget (399 < 500 lines),
cf-recursion-check, cf-exemptions all green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… format) — present at the previous commit but masked by a stale ruff cache; surfaced by a no-cache battery run Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ACT_MISSING) Running cf-import-contract against this repo itself reported CONTRACT_MISSING for cf_quality — the gauge did not yet carry the contract it ships. Fix, not baseline (the gate's only baseline surface is the ignore-count ratchet; a missing contract has no baseline path): - pyproject.toml: the kit's own [tool.importlinter] contract — the six gate modules sit above repo_config above errors, pinned settings per templates/import-contract.toml. Refuted against the live oracle: an injected errors -> repo_config edge draws CONTRACT_BROKEN with the named edge + line; reverting restores green. - self-ci: the battery now runs cf-import-contract --root . so the self-policing survives in CI, not just in one verifier run. - test_packaging.py: the clean-wheel harsh oracle extended to the new console script — a non-editable install still delivers the CONTRACT_MISSING verdict (exit 1), never an ImportError crash. Suite: 296 passed. Battery: ruff/format/sticky/file-budget/recursion/ exemptions/import-contract/mypy all green, no-cache. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.
cf-import-contract — layering direction under contract
New gate in the battery: every consumer repo commits ONE exhaustive import contract (import-linter form,
[tool.importlinter]in pyproject.toml) plus an ignore-count baseline, and the gate holds it in three passes — contract-file lint,lint-importsat the kit-pinned version (import-linter==2.11, exact pin so the parsed oracle cannot drift), and a companion AST scan for module-level dynamic imports the static contract cannot see.Budget
ignore_importsentries are named edges, never wildcards (CONTRACT_LINT_WILDCARD); their count ratchets against the committedimport-contract-baseline.json({"ignored_imports": N}) and an absent baseline means zero, never a free pass (CONTRACT_LINT_RATCHET).unmatched_ignore_imports_alertingis pinned to"error"— an ignore entry matching no real edge fails (CONTRACT_STALE_IGNORE), so the baseline can only shrink.exclude_type_checking_imports = truefails the contract file itself (CONTRACT_LINT_PINNED_CONFIG).CONTRACT_LINT_EXHAUSTIVENESS) — kills rename-evasion and vacuity.GateErroron stderr — environment trouble is never a violation verdict).Control rods (eleven, through the CLI's parsed-output surface)
R1 module-level upward edge →
CONTRACT_BROKENwith the named edge + line · R2 enumerated ignore entry passes with the entry printed · R3 packages with no contract →CONTRACT_MISSING· R4 function-body deferred upward import still fails · R5TYPE_CHECKING-guarded upward import still fails · R6 wildcard ignore fails · R7 unnamed top-level package fails exhaustiveness · R8 module-level dynamic import fails · R9 stale ignore entry fails · R10exclude_type_checking_imports = truefails · R11 uninstalled src-layout package is a typed config error (exit 2), never a violation verdict.Self-run (the gauge submits to its own gate)
Pre-registered prediction: the kit would fail its own new gate. Confirmed verbatim:
Resolution: fixed, not baselined (the gate's only baseline surface is the ignore-count ratchet; a missing contract has no baseline path). The kit now commits its own contract — the six gate modules sit above
repo_configaboveerrors— and self-ci runscf-import-contract --root .. The contract was refuted live: an injectederrors -> repo_configedge drawsCONTRACT_BROKEN: cf_quality.errors -> cf_quality.repo_config (l.86); reverting restores green.Verification
CONTRACT_MISSINGfixture exits 1 with the verdict; a contracted clean fixture exits 0; an injected upward edge exits 1 with the named edge — and the new packaging test pins the verdict path.Open fork for the maintainer
cf-import-contractis wired into self-ci only. Mounting it into the reusablequality-gate.ymlwould immediately fail every consumer repo that has not yet committed a contract — that rollout (and the per-repo contract content review) is a maintainer decision, deliberately not taken here.Do not merge — pending maintainer review.