revert(tests): drop the local-to-CI smoke parity assertion — CI is not a gate
Reverts the bidirectional half of the smoke parity pin added earlier today,
and records why at the assertion site so it is not "completed" again.
I read the suite list's comment ("so the local-first pre-push gate equals the
CI gate") as promising equality, made the assertion symmetric, and reported
tests/test_pack_draft_serve_boundary.py as drift because it sits in the local
gate and not in .github/workflows/smoke.yml.
That was wrong, and AGENTS.md says so in a line I had already read: GitHub
Actions are billing-locked and produce dead signals, never chase them. The
Forgejo host (e2-micro) cannot run workflows either. CI here is run MANUALLY
in-worktree — usually faster anyway on UMA hardware with real RAM — which is
what scripts/ci/local-ci.sh now front-ends. smoke.yml is secondary
observability, not a gate.
So the local suite is the SOURCE and is free to be broader. Asserting the
reverse converts a file nobody executes into a maintenance obligation, and one
agents cannot discharge at that — pushing workflow changes needs an OAuth scope
the push credential lacks. The three "owed" lines were never owed.
The surviving direction (CI subset-of local) still protects something real: if
smoke.yml ever names a path the local gate lacks, the local gate is narrower
than a published claim about it.
Arc-close brief updated to match: the "three files owed to smoke.yml" open item
is replaced by a recorded non-finding, so the next reader does not re-derive it.
Its shipped-work table now lists what actually landed in its place — the local
CI runner, the lane-roster fix, and the proposal-sink test-isolation fix.
[Verification]: 21 passed across test_cli_test_suites, test_lane_sha_verifier,
test_adr_index; ruff clean on the changed test.
NON-CANONICAL: Python 3.12.11, not the pinned 3.12.13 (unavailable for
linux-x86_64). Hash-pinned tests reproduce committed values regardless.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FduW6Krm3PPQv3P5iwBYtx
This commit is contained in:
parent
b318501c55
commit
50fa287d17
2 changed files with 39 additions and 42 deletions
|
|
@ -15,7 +15,9 @@
|
|||
| Coercion derives its whitelist from the enum instead of restating it | `workbench/api.py::_coerce_grounding_source` | same file, parametrized over the registration |
|
||||
| Ledger absence policy declared, not passed (ADR-0263 rule 5) | `core/ratified_ledger.py::CAPABILITY_LEDGERS` | `tests/test_ratified_ledger_bridge.py::TestCapabilityManifest` |
|
||||
| Deductive suite in the pre-push gate | `scripts/hooks/pre-push` | measured 285 tests / 29s vs smoke 216 / 62s |
|
||||
| Smoke↔CI parity assertion made bidirectional | `tests/test_cli_test_suites.py` | mutation-checked; carries a dated `PENDING_IN_CI` list — see §7 |
|
||||
| One-command local CI runner, fail-closed on interpreter | `scripts/ci/local-ci.sh` | `gate` tier verified: 236 + 10 + 285 |
|
||||
| Lane roster red on clean `main` fixed | `tests/test_lane_sha_verifier.py` | two arc lanes were never registered |
|
||||
| Test writing proposals into the live in-repo sink | `tests/test_derived_close_proposals.py` | plus a vacuous `or True` assertion removed |
|
||||
| Correction-binding lockstep pinned on the deduction path | `tests/test_prior_surface_deduction_binding.py` (6) | mutation-checked |
|
||||
| Ratification ceremony + `core proposal-queue ratify` | `teaching/ratification.py`, `core/cli_proposal_queue.py` | `tests/test_ratification_ceremony.py` (14) |
|
||||
| Domain-keyed ADR index | `docs/adr/INDEX-by-domain.md` | `tests/test_adr_index.py` (5) |
|
||||
|
|
@ -99,19 +101,24 @@ latter produces a renderer with nothing to render.
|
|||
| `hash_surface` on `TurnEvent`, or not | undecided | same |
|
||||
| Arena queue entry + ledger reseal after ratification | deferred by design (bridge rule 1) | `RatificationReceipt.pending_stages` |
|
||||
| Forgejo zero-length-object claim | unverified from this environment | assessment doc, preamble |
|
||||
| **Three files owed to `smoke.yml`** | blocked (push credential lacks `workflow` OAuth scope) | `tests/test_cli_test_suites.py::PENDING_IN_CI` |
|
||||
**A non-finding, recorded so it is not re-found.** Mid-arc this brief listed
|
||||
"three files owed to `smoke.yml`" as blocked work: `test_pack_draft_serve_boundary.py`
|
||||
plus the two new provenance files sit in the local gate and not in the CI yaml,
|
||||
which a newly-bidirectional parity assertion flagged as drift.
|
||||
|
||||
**On that last row — the one thing this arc found and could not finish.** The
|
||||
CI smoke gate is narrower than the local one. `test_pack_draft_serve_boundary.py`
|
||||
(ADR-0253 dual-pack boundary, INV-33) has been in the local pre-push gate and
|
||||
absent from `smoke.yml`, and the parity pin only ever checked the *other*
|
||||
direction, so it went unseen. The two new deduction-provenance files belong
|
||||
there for the same reason. The three-line edit was authored and rejected at push
|
||||
(`refusing to allow an OAuth App to create or update workflow smoke.yml without
|
||||
workflow scope`), so it is recorded as a named, dated exception rather than
|
||||
silently dropped: the assertion still fires on any *new* divergence, and a
|
||||
second guard fires once the three land so the list cannot rot. Anyone pushing
|
||||
with `workflow` scope closes it by adding three lines and emptying the tuple.
|
||||
It is not drift, and the assertion was wrong to exist. Per `AGENTS.md`, GitHub
|
||||
Actions are billing-locked dead signals, the Forgejo host cannot run workflows
|
||||
either, and CI is run **manually in-worktree** — which is faster anyway on UMA
|
||||
hardware with real RAM. `smoke.yml` is secondary observability, not a gate. The
|
||||
local suite is the source and is free to be broader; asserting the reverse turns
|
||||
a file nobody executes into a maintenance obligation that agents cannot even
|
||||
discharge (pushing workflow changes needs an OAuth scope the push credential
|
||||
lacks).
|
||||
|
||||
Reverted the same day, with the reasoning left at the assertion site so the
|
||||
"completion" is not attempted again. The surviving direction — CI ⊆ local —
|
||||
still protects something real: if `smoke.yml` ever names a path the local gate
|
||||
lacks, the local gate is narrower than a published claim about it.
|
||||
|
||||
## 8. Gates and their state at close
|
||||
|
||||
|
|
|
|||
|
|
@ -80,36 +80,26 @@ def test_cli_smoke_suite_covers_ci_smoke_gate() -> None:
|
|||
f"core/cli_test.py TEST_SUITES['smoke']: {sorted(missing_locally)}"
|
||||
)
|
||||
|
||||
# The other direction, which this pin did not check until 2026-07-25 and
|
||||
# which had in fact drifted: tests/test_pack_draft_serve_boundary.py sat in
|
||||
# the local gate and not in smoke.yml. The list's own comment promises the
|
||||
# two are *equal* ("so the local-first pre-push gate equals the CI gate
|
||||
# rather than silently narrowing it"), and a one-directional assertion can
|
||||
# only ever hold half of that. A file only the local gate runs is a file
|
||||
# whose regression is invisible to anyone reading a green CI badge.
|
||||
# DELIBERATELY ONE-DIRECTIONAL — do not "complete" this by also asserting
|
||||
# local_paths <= ci_paths.
|
||||
#
|
||||
# PENDING_IN_CI is a named, dated exception list, not a suppression. These
|
||||
# three are already in the local gate and belong in smoke.yml; the edit was
|
||||
# authored and could not be pushed from the authoring session, whose
|
||||
# credential lacked the `workflow` OAuth scope. Anyone with that scope
|
||||
# closes this by adding the three lines and emptying this tuple. The
|
||||
# assertion below still fires on any NEW divergence, which is the
|
||||
# protection that was missing entirely before.
|
||||
PENDING_IN_CI = (
|
||||
"tests/test_pack_draft_serve_boundary.py",
|
||||
"tests/test_prior_surface_deduction_binding.py",
|
||||
"tests/test_workbench_deduction_provenance.py",
|
||||
)
|
||||
missing_in_ci = local_paths - ci_paths - set(PENDING_IN_CI)
|
||||
assert not missing_in_ci, (
|
||||
"CI smoke gate is narrower than the CLI smoke suite; add to "
|
||||
f".github/workflows/smoke.yml: {sorted(missing_in_ci)}"
|
||||
)
|
||||
stale_exceptions = set(PENDING_IN_CI) & ci_paths
|
||||
assert not stale_exceptions, (
|
||||
"these are in smoke.yml now — drop them from PENDING_IN_CI: "
|
||||
f"{sorted(stale_exceptions)}"
|
||||
)
|
||||
# A bidirectional version was added on 2026-07-25 and reverted the same day.
|
||||
# It read the list's comment ("so the local-first pre-push gate equals the
|
||||
# CI gate") as promising equality, and flagged
|
||||
# tests/test_pack_draft_serve_boundary.py as drift because it sits in the
|
||||
# local gate and not in smoke.yml. That is not drift. Per AGENTS.md, GitHub
|
||||
# Actions are billing-locked and produce dead signals, the Forgejo host
|
||||
# cannot run workflows either, and CI is run manually in-worktree
|
||||
# (scripts/ci/local-ci.sh). smoke.yml is secondary observability, not a
|
||||
# gate.
|
||||
#
|
||||
# So the local suite is the SOURCE, and it is free to be broader. Asserting
|
||||
# the reverse turns a file nobody executes into a maintenance obligation —
|
||||
# one that agents cannot even discharge, since pushing workflow changes
|
||||
# needs an OAuth scope the push credential lacks. The direction checked
|
||||
# above is the one that still protects something: if smoke.yml ever names a
|
||||
# path the local gate lacks, the local gate is genuinely narrower than a
|
||||
# published claim about it.
|
||||
|
||||
|
||||
def test_core_test_suite_expands_to_expected_pytest_paths(monkeypatch) -> None:
|
||||
|
|
|
|||
Loading…
Reference in a new issue