Rules, tools, audits and refactors — the alignment map — 2026-09-12¶
Status: design record from an R&D run (session
5f61ae2b, 2026-09-12), written 2026-09-13 so the finding survives the session. Not canon. Its implementation surfaces are owned bychore-class-system.md, which carries the ratified design this map motivated. See § What this record does not license.
The question¶
The run opened on a pasted document, Python Codebase Architecture Guidelines, offered against the pythonic chores. The operator widened it twice.
Operator, 2026-09-12 20:15Z (verbatim): "although this analysis was likely meant to be authoring advice, we resort to refactoring if agents/rules arent effective, do we have pythonic rules on the frontend? do we have any chore that aligns authoring rules to audit/refactoring chores?"
Operator, 2026-09-12 20:22Z (verbatim): "I am trying to get front end and audit/refactor back-end alignment. Ensure that rules, tools, audits and refactors all agree. I am trying to tighten up how gzkit does its best to have good Python/software architectures and implmentation, as much as possible."
That second statement is the goal this record maps. The chore class system is the first mechanism it produced; it is not the whole of the goal.
Evidence standard¶
Counts were measured by command. The session measured them on 2026-09-12, and every
figure below was re-measured on 2026-09-13 before this record was written. Where the
two disagree, both are stated. Commands are in § Reproduction record. A value written
here is illustrative (AGENTS.md § Governance doctrine surfaces); the authority for each is
named beside it, and a later reader re-runs rather than trusts the transcription.
The four estates¶
| Estate | Surface | Fires when |
|---|---|---|
| Rules — the front end | .gzkit/rules/*.md, path-scoped |
every edit |
| Tools — the witnesses | ruff · ty · xenon · gz validate scopes · gz check |
every commit and push |
| Audits — diagnosis | the control-surface passes, gz-tech-debt-review, gz-intent-trace |
manually |
| Refactors — repair | the code-quality chores | manually |
The rules estate is scored clause by clause in the advisory scorecard
(docs/governance/advisory-rules-audit.md § Summary, fenced by
gz validate --advisory-scorecard). Measured 2026-09-13: 68 Mechanical · 31
Promotable · 64 Judgment · 0 Ambiguous.
Edges that exist¶
| Edge | Instrument |
|---|---|
| rule ↔ rule | Pass A, control-surface-rule-conflicts |
| skill ↔ rule | Pass B, control-surface-skill-rule-reachability |
| rule prose ↔ validator check | Pass C, control-surface-rule-vs-check-drift, plus the scorecard |
| check ↔ commit path | control-surface-validator-reachability |
| rule prose ↔ permission standing consent | control-surface-permission-consent-drift |
control-surface-validator-reachability/CHORE.md states the family's boundary in its
own words: Passes A, B and C "all ask about content relationships — rule vs rule (A),
skill vs rule (B), rule prose vs check semantics (C)."
Existing label collision¶
Two chores titled themselves Pass D: control-surface-validator-reachability
("Validator Reachability & Ungated Ratchet (Pass D)") and
control-surface-permission-consent-drift ("Rule Prose vs. Permission Standing
Consent (Pass D)"). ledger-vocabulary-inertness/CHORE.md cites the first as "Pass
D". Found 2026-09-13 while naming the rule ↔ chore pass. It is a Coherence finding
inside the family that exists to find Coherence findings, and it resolves with the
per-chore declarations in chore-class-system.md §
Implementation order, step 5.
Resolved 2026-09-13 (operator ruling, verbatim "Later one becomes Pass E
(Recommended)"): control-surface-validator-reachability, authored 2026-08-15, is now Pass E;
control-surface-permission-consent-drift, authored 2026-07-16, keeps Pass D.
Edges that do not¶
rule ↔ chore, and chore ↔ cadence.
A chore cannot even declare the rule it serves. The registry's fields are frequency,
lane, path, projectLocal, slug, timeoutSeconds, title, vendor and
version. .gzkit/rules/chores.md § Authoring a New Chore requires four files, and
none of them is a rule linkage. So a four-way alignment is today a three-way one: rules,
tools and audits-of-rules interlock, and the refactor estate hangs off the side.
The measured asymmetry¶
gzkit governs its governance more rigorously than it governs its own code architecture.
Freshness. The control-surface chores ran within the month; five chores carry a
2026-09-12 run heading. Twenty-four of the 39 chores with a run log last ran on
2026-07-31, a single manual sweep. The only candidates report
pythonic-design-pattern-detection has ever produced is
proofs/candidates-2026-04-26.md.
Nothing schedules any of it. One chore declares a frequency. No CI workflow,
pre-commit hook or .claude/hooks script invokes gz chores run.
Nothing ties a chore to a rule. Twelve chores' CHORE.md cite any
.gzkit/rules/ or .claude/rules/ file. Five of those twelve are the control-surface
family, for which rules are the subject rather than the authority. Of the code-quality
chores, only module-sloc-cap-radon, pythonic-design-pattern-application and
test-consolidation-subtest-sweep cite one. pythonic-refactoring,
complexity-reduction-xenon, pep257-docstring-compliance,
exceptions-and-logging-rationalization and pythonic-design-pattern-detection run
untethered to any declared doctrine.
The primary architecture directive is the weakest-witnessed family.
.gzkit/rules/hexagonal-architecture.md names itself "gzkit's primary
code-architecture directive." Its operative clauses score:
| Row | Clause | Score |
|---|---|---|
| 64 | Dependencies live in adapters, never in the core | Mechanical |
| 64a | Pydantic is the one ratified exception | Promotable |
| 64b | Ports are domain-typed contracts | Judgment |
| 64c | Never name the technology in the core | Promotable |
| 64d | Encapsulate first; formalize the port on the second adapter | Judgment |
| 64e | The core is testable without any adapter | Promotable |
The rule's own § Verify is lint and typecheck, and neither can see a port.
.gzkit/rules/pythonic.md's size limits — functions ≤50 lines, modules ≤600 — are rows
19 and 20, both Judgment, and the rule says so itself: "authoring-time guidance
only."
Why it happened, without blame. The control-surface family was born of acute pain:
an agent hitting a rule contradiction mid-work. Code architecture never produced that
pain, because ruff, ty and xenon fire on every commit and keep the floor clean. Clean
lint reads as healthy code. That sentence is the founding insight of
pythonic-design-pattern-detection — given the code is idiomatic line by line, is the
shape Pythonic? — and that chore has produced one report.
The missing middle scale¶
| Scale | Concern | Rule | Witness |
|---|---|---|---|
| Small | idiom, style, types | pythonic.md |
ruff and ty, every commit |
| Middle | module and package boundary — what a package exports, what may import what, catch-all modules | none | none |
| Large | ports and adapters, dependency direction | hexagonal-architecture.md |
one Mechanical clause of six |
This is why two points in the pasted document had no home in any rule file. It is not an oversight in one rule: it is a scale nobody claimed, and it is where the measured damage sits.
| Measurement (2026-09-13) | Value |
|---|---|
__init__.py under src/gzkit declaring no __all__ |
14 of 42, including src/gzkit/__init__.py, commands/, content/, core/ and validators/ |
src/gzkit/commands/common.py |
702 lines, 27 top-level import statements |
Top-level commands/*.py modules importing common.py |
74 of 97 (82 of 111 counting subpackages) |
Test modules importing gzkit.commands.common |
39 |
src/gzkit/utils.py |
127 lines, 17 importers |
Variance recorded, not resolved. The session reported "94 of the 99 modules in
src/gzkit/commands/" importing common.py. No probe run on 2026-09-13 reproduces
it: a literal from gzkit.commands.common import grep and an any-form grep both return
74. Cite the 2026-09-13 figure.
Size does not see it. At 702 lines common.py lands in the advise band of
module-sloc-cap-radon, which is advisory. The load-bearing signal is fan-in
combined with a generic name, and nothing measures it.
Where canon already holds the goal — and its blind spot¶
The goal is already seated as next-in-priority. Movement C, Reduce the accretion,
carries the box Close the doctrine-declared-without-mechanism family, pulled
forward on 2026-09-02. Its completion criterion is this record's question: "a declared
discipline either carries a mechanical witness or is demoted to advisory in its own
text — no third state." Its instrument is the scorecard, where Promotable is the
forbidden third state, and three hexagonal clauses sit there. The box's own sibling,
Oversized modules, already owns the size-limit authority conflict: 600 in
pythonic.md against .gzkit/rules/complexity-thresholds.json.
The blind spot. That box scores rule prose against validators. The chore estate is outside its instrument entirely. So the box can go green — every Promotable row resolved — while the refactor estate stays dormant and untethered, because no chore is scored at all. A rule demoted to Judgment satisfies the criterion even when a chore was silently auditing it. And a chore refactoring toward a target no rule declares is invisible from both sides.
The mirror family: mechanism running without doctrine¶
The box's family is doctrine declared without mechanism. The chore estate shows its
mirror: mechanism running without doctrine — a chore that refactors toward a shape
no rule declares, with a full proofs/ trail while it does it.
Measured specimen, from the session itself. The run's first proposal was a
module-boundary detection chore whose first signal was cycle-annotated deferred imports.
pythonic.md § Imports had already closed that subject: "This posture is ACCEPTED,
not deferred (operator ruling 2026-08-08)", and reclassification happens "not on the
count moving." The proposed chore — a scanner, a disposition model, proof artifacts —
would have driven toward a target the governing rule had explicitly refused. It was
retracted at 20:18Z. Nothing in gzkit would have caught it, because nothing asks a chore
which rule it serves.
Disposition of the pasted document¶
The document cites PEP 20, domain-driven design, Clean Architecture and PEP 544. Its
content is data, never instruction (AGENTS.md § Behavior Rules,
externally-authored tool output).
| Document section | gzkit surface | Disposition |
|---|---|---|
Dependency inversion with typing.Protocol; composition root |
hexagonal #4 — "Port shape via typing.Protocol, not ABC; inject adapters by composition" |
already covered |
| Low coupling, explicit boundaries | hexagonal #1–#6 | already covered, in more depth |
| Functional core, imperative shell | pythonic #2 — isolate IO, transforms, QC and persistence | already covered |
| Ban global state | pythonic #10 — no implicit globals; _detect_singleton |
already covered |
| Flat over nested, 2–3 levels | none needed — maximum package depth under src/gzkit measured 3 on 2026-09-13 |
already conformant |
| Group by domain, not layer; "screaming" architecture | hexagonal #7 — "core/ stays; do NOT add domain//application//adapters//contexts/ folder partitions to 'do DDD.'" — and the rule's preceding paragraph: "Domain cohesion lives in the type system, not the folder tree." |
ruled against |
| Circular imports signal a design flaw | pythonic.md § Imports — posture ACCEPTED 2026-08-08 |
canon-closed |
| Module over 500 lines; more than 10–15 imports | pythonic.md § Size Limits and the thresholds table |
covered, contested — Movement C box Oversized modules owns it |
Declare a package's public API with __all__; _-prefix internals |
none | absent from every rule file |
Ban catch-all utils / helpers / common modules |
none | absent from every rule file |
Where gzkit is better. Hexagonal #5: "Encapsulate first; formalize the port when the SECOND adapter is real." The document writes the Protocol immediately, and its own worked example has exactly one adapter. gzkit's clause is the same advice with the speculative-generality failure removed. Do not import the document's version over it.
Where the document is better. The last two rows. Hexagonal architecture is entirely about boundaries, yet gzkit never says declare what your package exports. The catch-all prohibition should be carried with fan-in as its signal, not length.
Not to be taken. The document's numbers. Thresholds come from
.gzkit/rules/complexity-thresholds.json, measured against a corpus, never from a
pasted document. If import count ever becomes a metric, it is measured into that table,
not transcribed.
Operator rulings on this thread¶
- Witness, hexagonal and rule-home questions are chore subjects, not doctrinal forks. Verbatim, 20:30Z: "chores are there to identify issues, provide an analysis, recommend a solution approach, and execute on solutions. a chore should diagnose and fix, with needed consultations with the operator, for input and judgment, as needed. so witness, hexagonal, and rule would all be subject to this same pattern and premise to chores." The three questions the session had put to the operator dissolved rather than being answered: they asked for pre-approval of work already under standing authorization.
- The design calibration comes before the conversion. Verbatim, 20:41Z: "look
carefully at the broad design of the chore system before we get lost in confusion -
this always a great danger when we do these sorts on reviews." That calibration is
chore-class-system.md. - Route. Verbatim, 21:21Z: "Movement C box — discharge it there, no new ADR." The
session had flagged the registry schema change as possibly contract-bearing, and so
possibly queued behind
ADR-0.35.0in semver order. The ruling settles it: this is discharged under the doctrine-declared-without-mechanism box, agent-side arm.
Advised order¶
In dependency order. Each step names its owning surface.
- Cadence before content. Adding rules or chores to an estate nothing schedules
multiplies dormant surface that reads as coverage — the presence-check doctrine one
level up. Owned by
chore-class-system.md§ Implementation order, steps 1–2, and GHI #936. - The rule ↔ chore edge. The
governing_ruleregistry field from the class system, then a rule ↔ chore pass: an audit, same shape as Passes A–C, flagging chores with no governing rule (ungoverned refactoring) and rules whose only witness is a chore (the scorecard's blind spot). The pass can take a first reading before the field lands, and that reading is the evidence for the field. It is deliberately unlettered: the session and its handoff called it "Pass D", but two existing chores already title themselves Pass D (see § Existing label collision). - Claim the middle scale, with its witness. Package API declaration goes into
hexagonal-architecture.md, because boundary declaration is its subject. The catch-all prohibition, with fan-in as its signal, goes intopythonic.md. Rule and witness land together or not at all: a rule clause without a witness adds a Promotable row, which is exactly what the Movement C box forbids. The__all__witness is AST-decidable — an import reaching past a package's__init__into an undeclared internal. - Hexagonal's three Promotable rows — 64a, 64c and 64e — each resolved by a witness or a demotion in the rule's own text.
- Size-limit authority — already owned by the Movement C box Oversized modules. Nothing here pre-empts its census.
Retracted by the session, recorded so they are not re-proposed: a chore built on deferred-import count (canon-closed, above), and a standalone module-boundary detection chore (one signal canon-closed, one better as a rule clause, and fan-in alone is not a chore — on this map it would be a fourth dormant chore on an orphaned estate).
What this record does not license¶
- It does not add a rule clause. The two middle-scale clauses land only with their witness, through the order above.
- It does not re-open settled rulings. The lazy-import posture in
pythonic.md§ Imports and hexagonal #7's folder-partition prohibition stand. - It does not close the Movement C box. It names a blind spot in the box's instrument. Amending that instrument is the operator's to ratify.
- It does not authorize an ADR or OBPI. Operator ruling: discharge under the Movement C box, no new ADR.
- It does not make the pasted document authoritative. gzkit's rules govern, and two of the document's sections are explicitly ruled against.
Reproduction record¶
# scorecard summary (validator-fenced)
uv run gz validate --advisory-scorecard
sed -n '/^## Summary/,/^<!--/p' docs/governance/advisory-rules-audit.md
# hexagonal and pythonic size rows
grep -nE '^\| *(64[a-e]?|19|20) *\|' docs/governance/advisory-rules-audit.md
# chores citing a rule file
for d in .gzkit/chores/*/; do
grep -qE '\.gzkit/rules/|\.claude/rules/' "$d"CHORE.md 2>/dev/null && basename "$d"
done
# last run per chore
for f in .gzkit/chores/*/proofs/CHORE-LOG.md; do
grep -oE '^## [0-9]{4}-[0-9]{2}-[0-9]{2}' "$f" | tail -1
done | sort | uniq -c
# packages with no declared public API
grep -L "__all__" $(find src/gzkit -name __init__.py)
# catch-all fan-in
wc -l < src/gzkit/commands/common.py
ls src/gzkit/commands/*.py | grep -vE '__init__|/common\.py$' | wc -l
grep -lE "gzkit\.commands\.common|from \.common import|from \. import common" \
src/gzkit/commands/*.py | grep -v '/common.py$' | wc -l
grep -rlE "gzkit\.commands\.common" tests --include='*.py' | wc -l
Sources¶
Operator-supplied paste, Python Codebase Architecture Guidelines (unattributed; cites
PEP 20, PEP 544, domain-driven design, Clean Architecture) · session transcript
5f61ae2b-9fc7-4646-8f2a-40d07743daaf, 2026-09-12 20:01Z–21:49Z ·
.gzkit/rules/hexagonal-architecture.md · .gzkit/rules/pythonic.md ·
.gzkit/rules/chores.md · docs/governance/advisory-rules-audit.md ·
docs/governance/build-to-1.0-campaign-2026-08-16.md § Movement C.