Skip to content

Audit the tach contract, and pin every port's flattened status axis to its owning enum - #800

Merged
xmap merged 4 commits into
mainfrom
fix/tach-audit-port-status-pins
Sep 11, 2026
Merged

Audit the tach contract, and pin every port's flattened status axis to its owning enum#800
xmap merged 4 commits into
mainfrom
fix/tach-audit-port-status-pins

Conversation

@xmap

@xmap xmap commented Sep 11, 2026

Copy link
Copy Markdown
Owner

Split out of `port-procedure-hold-claims`, which mixed this audit with an
unrelated Procedure hold-claim thread. This half stands on its own; the
other lands separately.

What the audit found

A tach.toml stress-test surfaced 16 real cross-BC dependencies with zero
declared edges (e.g. `run -> enclosure`, `run -> supply`), all going
through Protocol interfaces that live in the shared `cora.infrastructure.ports`
namespace rather than in either BC. Two shapes were on the table: move each
port into its owning BC so the edge becomes visible to tach ("B", tight), or
leave tach as a pure naming firewall and treat the coupling as a separate,
deliberately un-enforced map ("D", loose).

D is what ships here. A lookup port's consumer depends on a stable
question ("is this enclosure permitted?"); the implementation answering
it is swappable wiring. Merging the two into one file, as B would, means a
wiring change (swap who implements a port) forces a change to the
consumer's declared dependencies even though nothing in the consumer moved.
`BeamAvailabilityLookup`'s own docstring already committed to this
separation on purpose.

So this PR:

  1. Shrinks the contract itself. `test_tach_edges_are_used` now fails
    on any `depends_on` entry no tracked source file actually takes up.
    Caught 10 dead permissions immediately (161 -> 151 edges), including a
    whole module, `cora.equipment.ports`, that was declared and empty.
    Mutation-verified: a broad `cora.run` declared redundantly alongside
    `cora.run.aggregates` is correctly flagged.
  2. Rewrites the header to state what the file constrains (imports,
    not the true dependency graph) instead of a sentence that invited
    reading it as a complete map.
  3. Closes a real bug class the audit turned up along the way: 8 lookup
    ports flatten a typed `StrEnum` status/kind/direction field to a bare
    `str`, so 10+ consumers in other BCs re-type the value by hand against
    string literals nothing enforces. Safe today only by polarity accident
    (every comparison happens to be "match means proceed"), not by
    construction. Every remaining one is now a `Literal` alias pinned to
    its owning enum via a fitness test, extended twice: once to register
    every declared alias against its StrEnum, and once more to range over
    port fields directly (not just the aliases that happen to exist),
    which is what caught the two the first pass missed
    (`FacilityLookupResult.kind`, all three fields on
    `PermitLookupResult`). Mutation-verified at each step.

Verification

  • `uv run pyright`: 0 errors
  • `uv run tach check`: passes
  • `uv run pytest tests/unit tests/architecture -n 4`: 51,759 passed, 651 skipped
  • Each new/widened fitness test mutation-tested (reverted pin, drifted
    value set, removed recorded exception - each caught)

🤖 Generated with Claude Code

xmap and others added 4 commits September 11, 2026 14:14
…re than it checks

An audit of `tach.toml` after four months. Two findings acted on here; the
third is a design question left open on purpose.

IT ONLY EVER GREW. 15 declared edges at introduction, 161 today, across 46
commits. Exactly one commit ever removed an edge (b6c8e0a), and that was
a hand-written refactor deleting a documented "Pattern C exception" by making
the code stop needing it. Nothing in the process noticed the other 145.

Ten of those edges were taken up by no source file at all. Five clustered on
`cora.equipment.ports`, which is a docstring and an empty `__all__`: its one
export was hoisted to `cora.shared.ports` when the rule-of-three fired, and
the module declaration outlived the contents, still carrying three outbound
deps with two other modules declaring edges into it. The rest were three
unused `cora.shared` entries and two parent-to-own-adapters edges nothing
imports. All ten are removed, and `test_tach_edges_are_used` now fails on an
entry no tracked source file takes up, so the next one is caught in the
commit that strands it rather than four months later.

The test enumerates git-TRACKED files, and that is load-bearing rather than
stylistic: an hour before it was written, the architecture suite reported
51,665 passing with `EXPECTED_SCHEMA_VERSION` stale, because the pin's own
guard reads tracked files and the new migration had not been staged. A check
that cannot see a file passes by not looking.

Attribution is most-specific-wins, so a module declaring both `cora.run` and
`cora.run.aggregates` is not credited for the broad entry by an import the
narrow one already covers. That case is pinned by mutation, along with the
plain one.

THE HEADER CLAIMED MORE THAN THE FILE CHECKS. It said BCs depend on "any
sibling aggregate kernels they integrate with", which reads as a map of what
depends on what. It is a record of which module may NAME which, and naming is
one of several ways a dependency arises:

  - `cora.operation` reads `EnclosureLookup` and cannot start a Procedure
    without Enclosure's permit data, yet declares no edge to
    `cora.enclosure`. The Protocol sits in `cora.infrastructure.ports`, so
    both sides name only `cora.infrastructure`, which every module does.
    Twenty lookup adapters inside BCs bind to a protocol in that shared
    namespace, so this is a category and not one case.
  - An event subscription names its producer with a string.
  - `tests` is excluded outright.

None of that is a defect. The port indirection is deliberate and buys real
isolation. The defect was one sentence inviting the file to be read as
something it is not, which it was, by me, an hour before the audit. The
header now says what it constrains and lists what it does not, and names the
open question rather than answering it: whether those ports should move into
their owning BC, trading the isolation for a dependency graph that matches
reality. That needs the other nineteen measured, not the one anecdote.

Verified by mutation, both directions: adding a permission nothing imports
fails the test, and so does a broad edge shadowed by a narrower one already
covering every import. `tach check` itself caught me over-deleting during the
prune, dropping `cora.infrastructure` from `cora.equipment.adapters` where
the audit had only flagged `cora.shared`; restored before it went anywhere.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A cross-BC lookup port cannot import the owning BC's StrEnum, because
the ports package holds depends_on = [] and that neutrality is what
lets several BCs answer the same question. So a status crossed the
port surface as a bare str, and consumers in other BCs partitioned on
it by writing the value out by hand. Ten such comparisons exist today.

All ten happen to fail closed, so a drifted value stops work loudly
rather than admitting it. That is the polarity they were written in,
not a property anything enforced: the first gate written as
`if status == "Revoked": raise` would fail open, silently.

Typing the field as a Literal alias keeps the port neutral (Literal is
stdlib, not a BC type) while pinning the exact value set, so a
comparison against a value the enum lacks is a type error at the call
site. Adapters produce a validated member's .value, which narrows to
exactly the alias, so the existing runtime validation is preserved and
no cast is needed.

The alias is a hand-written mirror, so one fitness test pins it to the
enum and a second enumerates aliases from source, failing on any that
nothing registers. Verified by mutation: drifting the alias, adding an
unregistered alias, and comparing against an outside value each fail.

A weaker check asking only whether a literal belongs to SOME StrEnum
was rejected: "Active" is a member of 14 enums here, so it would admit
a ClearanceStatus value at an enclosure gate. It would also have
passed the permit_status="Denied" fixture this pin caught on its first
application, since "Denied" is a real RatificationStatus value. That
fixture described a state the adapter can never produce and is now
"NotPermitted".

This is the first of 13 flattened status fields; the pattern here is
the template for the rest.
…enums

Commit 87797f830cb did this for the enclosure port's two status axes.
The same gap existed on twelve more fields across asset, assembly,
family, capability, language_model, dataset_distribution, clearance,
clearance_template, credential, facility, and supply lookups: each
crossed its port as a bare str because cora.infrastructure.ports holds
depends_on = [] and cannot import the owning BC's StrEnum, so a
consumer in another BC partitioned on the value by writing it out by
hand with nothing pinning the set it could take.

Retype each field to a Literal alias that mirrors its owning enum's
full value set (never a query-filtered subset), matching the enclosure
precedent: Literal is stdlib, so the alias keeps the port neutral while
turning a comparison against a value the enum lacks into a type error
at the call site. The existing test_port_status_literals_match_owning_enums
fitness test's REGISTRY grows from two entries to fourteen; its
companion test keeps every declared alias in that registry.

Retyping surfaced one test fixture that could not type-check under the
new alias: test_record_witnessed_run_decider_properties.py generated
clearance_status via st.text(...).filter(lambda s: s != "Active"),
which samples arbitrary strings outside the enum's value set. Replaced
with st.sampled_from() over the enum's non-Active members, preserving
the test's intent while satisfying the narrower type.
…ught

The registry check asked whether every declared `<Name>Value` alias is
pinned to a StrEnum, which is blind to a field that never got an alias
declared for it in the first place. That blindness is not hypothetical:
it is exactly how `FacilityLookupResult.kind` and all three fields on
`PermitLookupResult` (direction, status, abi_tier_floor) were missed
when the other lookup ports were converted from bare `str` in the two
prior commits. The new test walks every port class's fields directly
via AST instead of trusting the alias set to be complete, so a field
that skips the alias step entirely can no longer hide.

Both misses are now pinned: `FacilityKindValue = Literal["Site", "Area"]`
against `FacilityKind`, and `DirectionValue` / `PermitStatusValue` /
`AbiTierValue` against `Direction` / `PermitStatus` / `AbiTier`. A real
exception (Supply.kind, which has no owning StrEnum yet) is recorded in
`UNPINNED_AXIS_FIELDS` with its reason, so an absence of a pin is now a
written claim rather than a silent gap.

Mutation-verified: reverting a pinned field to bare str, drifting
FacilityKindValue's value set from FacilityKind's, and removing the
recorded exception are each caught.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@xmap
xmap enabled auto-merge September 11, 2026 19:31
@github-actions

Copy link
Copy Markdown

Coverage report

Click to see where and how coverage changed

FileStatementsMissingCoverageCoverage
(new stmts)
Lines missing
  apps/api/src/cora/agent/adapters
  postgres_language_model_lookup.py
  apps/api/src/cora/equipment/adapters
  postgres_assembly_lookup.py 54
  postgres_family_lookup.py 53
  apps/api/src/cora/infrastructure/adapters
  in_memory_assembly_lookup.py
  in_memory_clearance_template_lookup.py
  in_memory_credential_lookup.py
  in_memory_enclosure_lookup.py
  in_memory_facility_lookup.py
  in_memory_family_lookup.py
  apps/api/src/cora/infrastructure/ports
  assembly_lookup.py
  asset_lookup.py
  capability_lookup.py
  clearance_lookup.py
  clearance_template_lookup.py
  credential_lookup.py
  dataset_distribution_lookup.py
  enclosure_lookup.py
  facility_lookup.py
  family_lookup.py
  language_model_lookup.py
  supply_lookup.py
  apps/api/src/cora/infrastructure/ports/federation
  permit_lookup.py
  apps/api/src/cora/recipe/adapters
  postgres_capability_lookup.py
  apps/api/src/cora/safety/adapters
  postgres_clearance_lookup.py
  apps/api/src/cora/supply/adapters
  postgres_supply_lookup.py
Project Total  

The report is truncated to 25 files out of 32. To see the full report, please visit the workflow summary page.

This report was generated by python-coverage-comment-action

@xmap
xmap merged commit 3119d14 into main Sep 11, 2026
19 checks passed
@xmap
xmap deleted the fix/tach-audit-port-status-pins branch September 11, 2026 19:40
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