CORE served the affirmative of propositions the user denied. Measured live on
main @ 536d6e55 with realizer_grounded_authority=True:
"evidence does not support truth" -> 'Evidence is verified: what supports truth.'
"evidence supports truth" -> 'Evidence is verified: what supports truth.'
Byte-identical.
Phase 4 (#136) pinned "render_semantic has no negated parameter" as a defect.
Sizing the exposure before acting on it -- the step §7 requires -- showed the
pin was correct and INCOMPLETE. The defect is two drops in series, and the
first is in the graph:
intent.negated parsed from the user's words, always has been
GraphNode NO FIELD FOR IT <-- first drop
ground_graph rebuilds nodes field-by-field; nothing to carry
depth enrichment rebuilds nodes field-by-field; nothing to carry
plan_articulation cannot carry what the node does not hold
realize_semantic never read step.negated
render_semantic no parameter for it <-- second drop
Five separate constructors, each silently defaulting to the affirmative.
Why every gate was green
------------------------
On the default config the ungrounded realizer emits "...", _is_useful_surface
rejects it, and the runtime echo wins the resolver -- and the echo contains the
user's own "does not". The truth path was correct BY ACCIDENT. ADR-0088 Phase B
grounds the graph first, which is exactly when the realizer's surface becomes
useful enough to win. So the defect sat behind a shipped flag, invisible to
every property-of-one-surface test, because nothing compared a denial to its
assertion.
Delivered
---------
- GraphNode.negated, threaded intent -> graph -> ground -> enrich -> step ->
surface. Serialized ONLY when True, so every pre-existing as_dict and every
trace_hash folded from one stays byte-identical -- which is why no lane pin
moves.
- Clause grammar delegated to its one owner. Four of the eight intent
"templates" were never frames; they were plain clauses. They now call
render_step. Writing a second negation implementation in semantic_templates
was the tempting fix and is rejected: Phase 2A spent a unit giving every
linguistic fact one owner, and that design rebuilds the disease one level up.
- Frames that keep a finite verb (VERIFICATION, PROCEDURE, COMPARISON) get an
explicit negated form. RECALL is a speech act with no proposition to deny, so
it falls back to the clause path rather than drop the denial (ADR-0261 §5.1).
Measured
--------
delegation on affirmatives 192/192 byte-identical
serving realizer, all corpora 85/347 -> 109/347 (+24 = the denials)
feature-bearing bucket 49/214 -> 73/214
CONTROL (nothing droppable) 33/33 -> 33/33 unchanged
multi-node (clause joining) 3/100 -> 3/100 unchanged, out of scope
lane pins 11/11 byte-identical, none edited
The control staying 33/33 and multi-node staying 3/100 is the evidence that
this moved the denials and nothing else.
The exhaustive control earned its keep immediately
--------------------------------------------------
It found FIVE more intents serving a denial as its own assertion --
TRANSITIVE_QUERY, FRAME_TRANSFER, NARRATIVE, EXAMPLE, DEDUCTION -- because an
intent with no frame fell back to the UNKNOWN *template* (which cannot say
"not") rather than the UNKNOWN *clause* (which can). The default is now the
capable path, so the next intent added inherits correctness. Found by the
control, not by inspection.
Mutation -- every link reverted individually
--------------------------------------------
baseline 19 pass
graph_from_intent drops intent.negated 8 FAIL
plan_articulation drops node.negated 6 FAIL
ground_graph drops it on rebuild 2 FAIL
realize_semantic stops passing step.negated 6 FAIL
unframed intents fall back to the template 1 FAIL
pipeline depth-enrichment drops it 1 FAIL
The last row was GREEN on the first run -- my fix there was unguarded. That is
what added the structural invariant: every GraphNode(...) built on the serving
path must NAME `negated` or be recorded in an allowlist with a reason. The
defect was five constructors; a per-site test must be written per site, and a
site added without one is invisible. Only recognition/connector.py is exempt
(an EpistemicNode has no polarity to carry).
Four Phase 4 pins are revised, not relaxed -- #136's M2 mutation ("render_semantic
GAINS a negated parameter -> FAIL") has now happened for real, and forced the
deliberate revision it was built to force.
Registered in `smoke` in the same PR that creates it, per the #136 finding that
an unregistered pin runs nowhere.
Still unexpressed, deliberately: quantifier, tense, aspect. NO PRODUCER sets
them anywhere on the serving path, so threading them would be machinery with no
caller. render_step already handles all three the moment a producer exists.
[Verification]: in-worktree on CPython 3.12.13 with `uv sync --locked` --
smoke 641 (was 621), deductive 503, lane pins 11/11 with no pin edited.
373 lines
16 KiB
Python
373 lines
16 KiB
Python
"""Curated pytest and ruff CLI command handlers."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import subprocess
|
|
import sys
|
|
from collections.abc import Sequence
|
|
from pathlib import Path
|
|
from typing import Protocol
|
|
|
|
|
|
TEST_SUITES: dict[str, tuple[str, ...]] = {
|
|
"fast": (
|
|
"tests/test_cli_test_suites.py",
|
|
"tests/test_cli_runner_contract.py",
|
|
"tests/test_runtime_config.py",
|
|
"tests/test_core_semantic_seed_pack.py",
|
|
"tests/test_intent_proposition_graph.py",
|
|
"tests/test_articulation_realizer_v2.py",
|
|
"tests/test_reviewed_teaching_loop.py",
|
|
"tests/test_cognitive_eval_harness.py",
|
|
),
|
|
"smoke": (
|
|
"tests/test_chat_runtime.py",
|
|
"tests/test_achat.py",
|
|
"tests/test_runtime_config.py",
|
|
"tests/test_cognitive_turn_pipeline.py",
|
|
"tests/test_audit_ledger_r7.py",
|
|
"tests/test_architectural_invariants.py",
|
|
"tests/test_cli_runner_contract.py",
|
|
# Audio sensorium lane — part of the smoke.yml PR gate (compiler,
|
|
# CRDT merge, eval gates, pack manifest, mount, teachers; ~3s).
|
|
# Listed explicitly so the local-first pre-push gate (AGENTS.md
|
|
# protocol) equals the CI gate rather than silently narrowing it.
|
|
"tests/test_audio_compiler.py",
|
|
"tests/test_audio_crdt_merge.py",
|
|
"tests/test_audio_eval_gates.py",
|
|
"tests/test_audio_pack_manifest.py",
|
|
"tests/test_audio_sensorium_mount.py",
|
|
"tests/test_audio_teachers.py",
|
|
# ADR-0043 — identity falsifiability: ratified identity packs must
|
|
# produce distinct, directionally-correct articulations, with a
|
|
# pack-invariant grounding/refusal floor and zero fabrication. Lives
|
|
# only under ``full`` historically, so a divergence regression cleared
|
|
# the PR gate and surfaced only post-merge. Promoted into smoke so
|
|
# the falsifiability claim blocks-on-regression rather than
|
|
# detect-after-merge.
|
|
"tests/test_pack_measurements_phase2.py",
|
|
# ADR-0253 dual-pack boundary — draft he/grc trees must not be serve imports.
|
|
"tests/test_pack_draft_serve_boundary.py",
|
|
# Register axis (ADR-0069/0071/0077) — the e2e tests here are the
|
|
# falsifiable contract that terse/convivial registers actually differ
|
|
# from neutral on pipeline-served surfaces. Red on main outside every
|
|
# gate for 2026-07-20..24 (the register-axis serving regression,
|
|
# docs/research/lane-drift-investigation-2026-07-24.md) because this
|
|
# file lived only under `full`; the lane-shas job was the sole gate
|
|
# that runs the demo lanes this axis shows up in, and its failures
|
|
# were conflated with an unrelated flake. Promoted so this axis can
|
|
# never silently regress again.
|
|
"tests/test_register_substantive_consumption.py",
|
|
# Deduction serving is LIVE (ADR-0256 ratified the flag ON), so its
|
|
# cross-stack provenance contract is a serving-path contract, not a
|
|
# capability-under-development one. Workbench's live chat route builds
|
|
# a bare ChatRuntime(), which means these composers decide Workbench
|
|
# turns too — and while their labels were unregistered the API coerced
|
|
# them to "none" and recorded a proved answer as ungrounded. ~3s.
|
|
"tests/test_workbench_deduction_provenance.py",
|
|
# Correction binding anchors to hash_surface, in lockstep with every
|
|
# substantive override of the served surface. Cheap, and the axis is
|
|
# invisible until it breaks silently.
|
|
"tests/test_prior_surface_deduction_binding.py",
|
|
# CORE must not serve the affirmative of a proposition the user denied.
|
|
# This was live behind `realizer_grounded_authority`: "evidence does not
|
|
# support truth" and "evidence supports truth" served byte-identical
|
|
# surfaces, because the graph had no slot for a denial. It is a truth
|
|
# defect on a serving path, so it belongs on the pre-push gate, not
|
|
# under `full` — and this file is registered in the same PR that
|
|
# creates it, per the #136 finding that an unregistered pin runs
|
|
# nowhere. ~6s, mostly two real pipeline turns per denial pair.
|
|
"tests/test_negation_survives_articulation.py",
|
|
# ADR governance. An ADR that governs a default-ON flag records a
|
|
# decision already in force, so leaving it "Proposed" makes the
|
|
# governance record assert something false about the running system —
|
|
# which is what happened to ADR-0256 while deduction serving was live.
|
|
# Filesystem + regex only, ~1.2s, and it is the record for a serving
|
|
# path, so it belongs on the pre-push gate rather than under `full`.
|
|
# test_adr_index.py rides along: it landed in #113 registered in NO
|
|
# curated suite, which is the same silent-red shape it exists to catch.
|
|
"tests/test_adr_status_governance.py",
|
|
"tests/test_adr_index.py",
|
|
# Volume honesty (ADR-0264 R9). The Wilson floor assumes independent
|
|
# trials; a deterministic pipeline replaying one case N times supplies
|
|
# one trial's evidence, not N. This pins the measured exposure in the
|
|
# ratified deduction ledger (21 of 25 bands short on distinct evidence)
|
|
# in BOTH directions, and forces any new licensed capability to register
|
|
# an audit source. It gates a live, flag-ON serving path, so it belongs
|
|
# on the pre-push gate. ~1.5s.
|
|
"tests/test_volume_honesty.py",
|
|
# ADR-0264 R1-R4/R8 — taught curriculum NEGATIVES. Before this, a row
|
|
# authored to refute compiled to a premise ASSERTING the atom, and the
|
|
# independent oracle ignored polarity too, so gold agreed and wrong=0
|
|
# stayed green. Corpus-level epistemology is unfalsifiable by every
|
|
# other gate here, so this one belongs on the pre-push gate. ~0.4s.
|
|
"tests/test_curriculum_polarity.py",
|
|
),
|
|
"runtime": (
|
|
"tests/test_chat_runtime.py",
|
|
"tests/test_achat.py",
|
|
"tests/test_runtime_config.py",
|
|
"tests/test_session_coherence.py",
|
|
),
|
|
"cognition": (
|
|
"tests/test_intent_proposition_graph.py",
|
|
"tests/test_cognitive_turn_pipeline.py",
|
|
"tests/test_audit_ledger_r7.py",
|
|
"tests/test_articulation_realizer_v2.py",
|
|
"tests/test_semantic_realizer_integration.py",
|
|
"tests/test_cognitive_eval_harness.py",
|
|
"tests/test_deterministic_hash.py",
|
|
"tests/test_morphology_irregular.py",
|
|
"tests/test_realizer_quantifier_agreement.py",
|
|
"tests/test_benchmarks_profiler.py",
|
|
"tests/test_compose_relations.py",
|
|
"tests/test_replay_vs_llm_benchmark.py",
|
|
),
|
|
"teaching": (
|
|
"tests/test_reviewed_teaching_loop.py",
|
|
"tests/test_pipeline_teaching_integration.py",
|
|
"tests/test_epistemic_invariants.py",
|
|
"tests/test_adr_0172_w2_decomposer.py",
|
|
"tests/test_adr_0172_w5_inference_proposal.py",
|
|
"tests/test_math_frame_ratification.py",
|
|
"tests/test_math_composition_ratification.py",
|
|
"tests/test_teaching_coverage_cli.py",
|
|
"tests/test_proposal_queue.py",
|
|
# The ratification ceremony is the only path that turns a reviewed
|
|
# proposal into a routable ratified chain — i.e. the one mechanism that
|
|
# can move the curriculum-volume constraint. It landed in #113 in NO
|
|
# curated suite, so its 14 tests ran only under `full`.
|
|
"tests/test_ratification_ceremony.py",
|
|
),
|
|
"packs": (
|
|
"tests/test_pack_draft_serve_boundary.py",
|
|
"tests/test_core_semantic_seed_pack.py",
|
|
"tests/test_adr_0127_pack_ratification.py",
|
|
"tests/test_frame_registry_load.py",
|
|
"tests/test_composition_registry_load.py",
|
|
"tests/test_composition_consult_in_injector.py",
|
|
"tests/test_consumption_case_0050_hazard_pin.py",
|
|
"tests/test_consumption_empty_registry_no_op.py",
|
|
"tests/test_consumption_partition.py",
|
|
"tests/test_matcher_extension_currency_per_unit.py",
|
|
"tests/test_matcher_extension_case_0050_hazard_pin.py",
|
|
"tests/test_matcher_extension_end_to_end_admission.py",
|
|
"tests/test_me2_cross_sentence_subject.py",
|
|
"tests/test_me2_case_0019_admits.py",
|
|
"tests/test_me3_additive_composition.py",
|
|
"tests/test_me4_subtractive_composition.py",
|
|
"tests/test_me5_all_categories_integration.py",
|
|
"tests/test_rat1_end_to_end_admission.py",
|
|
"tests/test_wave_a_multiplicative_aggregation_injector.py",
|
|
),
|
|
"algebra": (
|
|
"tests/test_stage2_physics_hardening.py",
|
|
"tests/test_geometric_convergence_checklist.py",
|
|
"tests/test_versor_closure.py",
|
|
"tests/test_holonomy.py",
|
|
"tests/test_holonomy_resonance.py",
|
|
"tests/test_energy.py",
|
|
"tests/test_motor.py",
|
|
"tests/test_null_cone.py",
|
|
"tests/test_vault_recall.py",
|
|
"tests/test_vault_recall_vectorised.py",
|
|
"tests/test_vault_recall_rust_parity.py",
|
|
"tests/test_cga_inner_rust_parity.py",
|
|
"tests/test_geometric_product_rust_parity.py",
|
|
"tests/test_versor_condition_rust_parity.py",
|
|
"tests/test_versor_apply_rust_parity.py",
|
|
),
|
|
"sensorium": (
|
|
"tests/test_sensorium_compiler_delta.py",
|
|
"tests/test_audio_compiler.py",
|
|
"tests/test_audio_crdt_merge.py",
|
|
"tests/test_audio_eval_gates.py",
|
|
"tests/test_audio_pack_manifest.py",
|
|
"tests/test_audio_sensorium_mount.py",
|
|
"tests/test_vision_compiler.py",
|
|
"tests/test_event_vision_compiler.py",
|
|
"tests/test_vision_crdt_merge.py",
|
|
"tests/test_vision_eval_gates.py",
|
|
"tests/test_vision_sensorium_mount.py",
|
|
"tests/test_sensorimotor_contract.py",
|
|
"tests/test_sensorimotor_pack_manifest.py",
|
|
"tests/test_observation_frame_contract.py",
|
|
"tests/test_observation_frame_harness.py",
|
|
"tests/test_environment_falsification.py",
|
|
"tests/test_environment_falsification_eval_cli.py",
|
|
"tests/test_witness_log_importer.py",
|
|
"tests/test_tabletop_lab_protocol.py",
|
|
"tests/test_sensorium_eval_cli.py",
|
|
"tests/test_efferent_gate.py",
|
|
),
|
|
"pulse": (
|
|
"tests/test_pulse_integration.py",
|
|
"tests/test_graph_diffusion.py",
|
|
),
|
|
"formation": ("tests/formation",),
|
|
"proof": ("tests/test_proof_properties.py",),
|
|
# ADR-0024 chain suites (Phases 2-6). Each phase has its own contract
|
|
# tests so reviewers can run them independently; ``adr-0024`` runs the full
|
|
# chain end-to-end.
|
|
"refusal": ("tests/test_refusal_contract.py",),
|
|
"margin": ("tests/test_margin_admissibility.py",),
|
|
"rotor": ("tests/test_rotor_admissibility.py",),
|
|
"inner-loop": (
|
|
"tests/test_inner_loop_admissibility.py",
|
|
"tests/test_inner_loop_phase2.py",
|
|
"tests/test_inner_loop_phase3.py",
|
|
"tests/test_inner_loop_phase4.py",
|
|
),
|
|
"phase5": ("tests/test_phase5_corpus.py",),
|
|
"phase6": ("tests/test_phase6_demo.py",),
|
|
"adr-0024": (
|
|
"tests/test_refusal_contract.py",
|
|
"tests/test_margin_admissibility.py",
|
|
"tests/test_rotor_admissibility.py",
|
|
"tests/test_inner_loop_admissibility.py",
|
|
"tests/test_inner_loop_phase2.py",
|
|
"tests/test_inner_loop_phase3.py",
|
|
"tests/test_inner_loop_phase4.py",
|
|
"tests/test_phase5_corpus.py",
|
|
"tests/test_phase6_demo.py",
|
|
),
|
|
# ADR-0126 P6 — measurement harness for the GSM8K candidate-graph parser
|
|
# exit criterion. ``wrong == 0`` is a hard gate (Obligation #4: refuse
|
|
# rather than confabulate).
|
|
"math": ("tests/test_adr_0126_train_sample_runner.py",),
|
|
"deductive": (
|
|
"tests/test_deductive_logic_entail.py",
|
|
"tests/test_deduction_serve_lane.py",
|
|
"tests/test_deduction_serve_license.py",
|
|
"tests/test_categorical_decider.py",
|
|
"tests/test_english_argument_reader.py",
|
|
"tests/test_member_argument_reader.py",
|
|
"tests/test_cond_member_argument_reader.py",
|
|
"tests/test_verb_argument_reader.py",
|
|
"tests/test_exist_argument_reader.py",
|
|
"tests/test_deduction_serve_e2e.py",
|
|
"tests/test_curriculum_serve.py",
|
|
"tests/test_curriculum_practice.py",
|
|
"tests/test_curriculum_polarity.py",
|
|
"tests/test_ledger_reseal.py",
|
|
"tests/test_ratified_ledger_bridge.py",
|
|
"tests/test_vocab_trigger_instrument.py",
|
|
"tests/test_grammar_roundtrip.py",
|
|
"tests/test_lexicon_single_source.py",
|
|
# Phase 4 — which realizer serves, and what the other one's score
|
|
# means. Registered here deliberately: this file exists because a lane
|
|
# reported 117/117 for a function nothing calls, and a pin that no
|
|
# curated suite runs is the same defect one level up. Pure in-process
|
|
# lane replay, ~0.4s.
|
|
"tests/test_phase4_realizer_resolution.py",
|
|
# The grammar arc's agreement oracle and the tail-preservation
|
|
# invariant. It lived ONLY in the `cognition` suite, which is not on
|
|
# the AGENTS.md pre-push gate (smoke + deductive) — so every pin
|
|
# Phases 3 and 4 added to it, including the invariant that covers all
|
|
# twelve inflection branches, ran in no gate at all. Exactly the
|
|
# silent-red shape called out for test_adr_index.py above, and the
|
|
# reason smoke stayed at 621 across two PRs that added 13 tests.
|
|
# ~0.4s; it belongs with the other grammar pins.
|
|
"tests/test_realizer_quantifier_agreement.py",
|
|
),
|
|
"full": ("tests/",),
|
|
}
|
|
|
|
|
|
class CommandRunner(Protocol):
|
|
def __call__(
|
|
self,
|
|
*args: str,
|
|
check: bool = False,
|
|
cwd: Path | None = None,
|
|
env: dict[str, str] | None = None,
|
|
) -> int: ...
|
|
|
|
|
|
def run_command(
|
|
*args: str,
|
|
check: bool = False,
|
|
cwd: Path | None = None,
|
|
env: dict[str, str] | None = None,
|
|
) -> int:
|
|
completed = subprocess.run(args, check=check, text=True, cwd=cwd, env=env)
|
|
return int(completed.returncode)
|
|
|
|
|
|
def pytest_args_for_suite(suite: str, extra_args: Sequence[str]) -> list[str]:
|
|
paths = TEST_SUITES[suite]
|
|
forwarded = list(extra_args)
|
|
if forwarded and forwarded[0] == "--":
|
|
forwarded = forwarded[1:]
|
|
return [*paths, *forwarded]
|
|
|
|
|
|
def xdist_available() -> bool:
|
|
"""Return True iff ``pytest-xdist`` is importable."""
|
|
try:
|
|
import xdist # noqa: F401
|
|
except ImportError:
|
|
return False
|
|
return True
|
|
|
|
|
|
def maybe_inject_xdist(forwarded: list[str], suite: str | None) -> list[str]:
|
|
"""Inject ``-n auto`` for suites large enough to benefit from parallelism."""
|
|
if not xdist_available():
|
|
return forwarded
|
|
# Honour explicit operator override.
|
|
if any(a.startswith("-n") or a == "--dist" for a in forwarded):
|
|
return forwarded
|
|
if suite == "full":
|
|
return ["-n", "auto", *forwarded]
|
|
return forwarded
|
|
|
|
|
|
def cmd_test(
|
|
args: argparse.Namespace,
|
|
*,
|
|
run: CommandRunner = run_command,
|
|
python_executable: str = sys.executable,
|
|
) -> int:
|
|
"""Run pytest through curated suite aliases or direct passthrough args."""
|
|
default_args = ["-q", "--tb=short"]
|
|
if args.list_suites:
|
|
for name in sorted(TEST_SUITES):
|
|
print(name)
|
|
return 0
|
|
if args.suite:
|
|
forwarded = pytest_args_for_suite(args.suite, args.args or default_args)
|
|
else:
|
|
forwarded = list(args.args or default_args)
|
|
if forwarded and forwarded[0] == "--":
|
|
forwarded = forwarded[1:]
|
|
forwarded = maybe_inject_xdist(forwarded, args.suite)
|
|
return run(python_executable, "-m", "pytest", *forwarded)
|
|
|
|
|
|
def cmd_check(
|
|
args: argparse.Namespace,
|
|
*,
|
|
run: CommandRunner = run_command,
|
|
python_executable: str = sys.executable,
|
|
) -> int:
|
|
"""Run ruff over selected project paths."""
|
|
targets = args.paths or [
|
|
"algebra",
|
|
"alignment",
|
|
"chat",
|
|
"core",
|
|
"field",
|
|
"generate",
|
|
"ingest",
|
|
"packs",
|
|
"morphology",
|
|
"persona",
|
|
"sensorium",
|
|
"session",
|
|
"vault",
|
|
"vocab",
|
|
"tests",
|
|
]
|
|
return run(python_executable, "-m", "ruff", "check", *targets)
|