Data Model: CI down-route of prose-only .py diffs

This mission is behavioral CI wiring, not persistent data. The "entities" are the in-memory values that flow through classification and routing.

Entity: FileVerdict

The classification of one changed .py file.

FieldTypeMeaning
pathstrrepo-relative path of the changed .py
is_prose_onlybooltrue only on proof of comment/docstring-only change
reasonstr (enum-ish)why not prose-only, for logging: code_diff, type_comment, noqa_pragma, doctest, parse_error, no_base, unknown

Invariants:

ast.dump(include_attributes=False) are equal, their # type:/# noqa/# pragma token sets are equal, and no changed docstring contains a >>> doctest prompt.

  • is_prose_only == True ⟹ base and head parse cleanly, their docstring-stripped
  • Any exception or unknown condition ⟹ is_prose_only == False (fail-closed).

Entity: PRProseVerdict (aggregate)

The all-or-nothing decision for the whole PR.

FieldTypeMeaning
prose_onlybooltrue only when every changed file is a proven prose-only .py OR an existing doc/corpus path
changed_pathslist[str]the raw changed-path set
reduced_pathslist[str]changed_paths minus proven prose-only .py (fed to select_modules)

Invariants (corrected per research R2):

down-routable if: keeps its existing gate ANDed with prose_only != 'true', so prose_only can only subtract an already-selected code lane and can never defeat the FR-004 unmatched catch-all. prose_only == True is NOT assumed to imply "no unmapped src present" (the original R2 claim, retracted).

that carries real code (the classifier proved each dropped .py prose-only).

battery / code shards and add the docs lane; it never removes an always-on lane.

  • Safety is structural in the wiring, not a classifier implication: every
  • prose_only == True ⟹ reduced_paths contains no src/.py or tests/.py
  • The verdict is monotonic-safe: it can only remove the module matrix / arch

Value transitions (control flow)

raw changed paths
   │
   ├─ ci-modules.yml: reduced_paths = drop(proven prose-only .py) → select_modules → module matrix (skipped when empty)
   │
   └─ ci-router.yml `changes` job: prose_only = aggregate verdict (git blobs base..head)
          ├─ architectural-heavy.if   &&= prose_only != true       (skip battery when prose-only)
          ├─ tests-<code>.if          &&= prose_only != true       (skip code shards when prose-only)
          └─ tests-docs.if            ||= prose_only == true        (positive docs/help-drift enablement)

Lane sets (the observable contract, pinned by the golden test)

PR shapemodule matrixarch batterycode shardstests-docsalways-on (ruff/terminology/…)
proven prose-only .py (only)✗✗✗✓✓
any real code / mixed / unprovable✓ (today)✓ (today)✓ (today)as today✓