diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 99791af..ed722c7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,14 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout + # Full history: check_metrics_fresh.py verifies that METRICS.yaml's + # ``generator_git_sha`` is an ancestor of HEAD; a shallow checkout + # (default depth 1) cannot walk ancestry on the PR merge ref, so the + # gate fails with "not an ancestor of HEAD" even when METRICS is in + # sync. uses: actions/checkout@v5 + with: + fetch-depth: 0 - name: Set up uv uses: astral-sh/setup-uv@v8.1.0 @@ -111,8 +118,12 @@ jobs: - name: Synthetic-surface classification gate # Every retained fallback/mock/placeholder/stub occurrence in tracked # project-owned files must be classified as degraded output, template - # variable, type stub, negative control, or UI empty state. - run: cd .. && uv run --directory cogant python ../tools/audit_synthetic_surfaces.py --strict + # variable, type stub, negative control, or UI empty state. --strict + # additionally validates generated public artifacts (output/manuscript, + # figure sidecars), which only exist after a pipeline run — that lane + # belongs to tools/release_gate.py on a rendered worktree, not a clean + # CI checkout, so CI enforces the classification layer only. + run: cd .. && uv run --directory cogant python ../tools/audit_synthetic_surfaces.py - name: Manuscript figure-renderer provenance gate # Registered manuscript figures must point to importable renderers and @@ -148,7 +159,12 @@ jobs: run: MYPYPATH=py uv run mypy --package cogant --strict --no-warn-unused-configs test: - name: Test (py${{ matrix.python-version }}) + # No explicit ``name:``: matrix context in ``jobs..name`` only + # expands for jobs that actually RUN — while the job is skipped (e.g. + # ``needs: lint`` failing) GitHub renders the raw ``${{ matrix.* }}`` + # expression literally in the UI. Dropping the name lets GitHub apply + # its default matrix naming (``test (3.11)`` …), which renders the + # expanded python version in both skipped and executed states. runs-on: ubuntu-latest needs: lint strategy: @@ -232,6 +248,15 @@ jobs: - name: Checkout uses: actions/checkout@v5 + # audit_release_integrity --check-wheel-reproducibility rebuilds the + # wheel via ``uv build``; without this step the job dies with + # FileNotFoundError: 'uv' (latent defect: the job is ``needs: lint`` + # and lint never reached green until now). + - name: Set up uv + uses: astral-sh/setup-uv@v8.1.0 + with: + enable-cache: true + - name: Set up Python uses: actions/setup-python@v6 with: @@ -270,8 +295,12 @@ jobs: PY - name: Release integrity and wheel reproducibility + # PYTHONPATH points at the clean target install so the OpenAPI + # contract check imports cogant from the wheel under test; without + # it the audit failed with "No module named 'cogant'" (latent + # defect: this job is needs: lint and lint never reached green). run: | - python ../tools/audit_release_integrity.py \ + PYTHONPATH=/tmp/cogant-wheel-install python ../tools/audit_release_integrity.py \ --wheel /tmp/cogant-wheelhouse/cogant-*.whl \ --require-wheel \ --check-wheel-reproducibility diff --git a/AGENTS.md b/AGENTS.md index 25eaa39..733fcde 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -10,7 +10,7 @@ | [`scripts/`](scripts/) | Thin orchestrators (`z_generate_manuscript_variables.py`, which also refreshes `output/figures/`). | | [`src/`](src/), [`tests/`](tests/), [`pyproject.toml`](pyproject.toml) | Tiny parent-template compatibility shell so `docxology/template` project discovery sees top-level source/tests when linked under `projects/working/cogant/`. The real package remains nested under `cogant/`. | | [`run_all.py`](run_all.py), [`run_all.sh`](run_all.sh), [`run_all.example.json`](run_all.example.json) | Configurable batch run: `translate` + GNN exports + `render` + `viz` + `validate` per target. Targets are either `path` (under inner `cogant/`) or `git_url` (shallow clone to `//_git_source/`). If `run_all.json` is absent, built-in defaults cover the three `examples/control_positive/*` fixtures plus two small Pallets repos; the shipped `run_all.json` is the larger corpus config. The shipped and built-in configs set `output_root: "cogant/output"`; `run_all.example.json` uses `output`. Each target writes under `//` (with `data/`, `figures/`, `site/`, `reports/`, `gnn_package/`, `analysis/`, `exports/`, `roundtrip/`). Stderr + optional `--log`: per-target banner, per-step wall time / exit status, batch `summary` in `/run_manifest.json`, cross-target `summary.md`/`summary.json`, and `/dashboard/` when `steps.batch_dashboard` is enabled. | -| [`output/`](output/) | Generated manuscript outputs — `data/manuscript_variables.json`, `output/manuscript/` injected copy, and copied figures (all disposable and regeneratable). Batch runs write per-target directories under the configured `//`; the shipped `run_all.json` uses `cogant/output`, while `run_all.example.json` uses project-root `output`. | +| `output/` | Generated manuscript outputs — `data/manuscript_variables.json`, `output/manuscript/` injected copy, and copied figures (all disposable and regeneratable). Batch runs write per-target directories under the configured `//`; the shipped `run_all.json` uses `cogant/output`, while `run_all.example.json` uses project-root `output`. | | [`PROMOTION.md`](PROMOTION.md) | Checklist for exposing this working sidecar tree to the parent template render location. | ## Location matrix diff --git a/cogant/.pre-commit-config.yaml b/cogant/.pre-commit-config.yaml index 68a8168..43c5197 100644 --- a/cogant/.pre-commit-config.yaml +++ b/cogant/.pre-commit-config.yaml @@ -16,15 +16,18 @@ repos: # CI ratchet (.github/workflows/ci.yml :: Lint (ruff)). Auxiliary # scripts under tools/, scripts/, manuscript/, etc. are intentionally # not policed by pre-commit yet — see CONTRIBUTING.md "Lint scope". - # NOTE: pre-commit is invoked from the repo root by both - # ``pre-commit run --config cogant/.pre-commit-config.yaml`` (CI) and - # ``pre-commit install`` from inside ``cogant/`` (local), so we - # accept either prefix. + # NOTE: pre-commit always matches ``files`` against paths relative to + # the git repo root (COGANT/), regardless of the cwd the command was + # invoked from, so the ``cogant/`` prefix is required. A bare + # ``tests/`` alternative would also sweep the repo-root ``tests/`` + # tree (tools audit tests) which is NOT covered by the CI ruff + # ratchet and formats under a different ruff config — on CI that made + # this hook rewrite 24 files and fail every run. - id: ruff args: [--fix] - files: ^(cogant/)?(py/cogant/|tests/) + files: ^cogant/(py/cogant|tests)/ - id: ruff-format - files: ^(cogant/)?(py/cogant/|tests/) + files: ^cogant/(py/cogant|tests)/ - repo: https://github.com/pre-commit/pre-commit-hooks rev: v5.0.0 diff --git a/cogant/benchmarks/results/suite_20260818.md b/cogant/benchmarks/results/suite_20260818.md index 909bb2e..03c6b68 100644 --- a/cogant/benchmarks/results/suite_20260818.md +++ b/cogant/benchmarks/results/suite_20260818.md @@ -95,4 +95,3 @@ - `OBSERVATION`: 2 **GNN shapes:** A=[2 x 3], B=[3 x 3 x 11], C=[2], D=[3] - diff --git a/cogant/evaluation/METRICS.yaml b/cogant/evaluation/METRICS.yaml index 807a5ec..ed77319 100644 --- a/cogant/evaluation/METRICS.yaml +++ b/cogant/evaluation/METRICS.yaml @@ -3,8 +3,8 @@ # DO NOT EDIT BY HAND. Run: cd cogant && uv run python ../tools/regenerate_metrics.py schema_version: '1.0' -generated_at: '2026-09-08T22:48:12.814800Z' -generator_git_sha: c14e1d9887cff323702329b5b9d2995d015346e5 +generated_at: '2026-09-10T17:46:31.510967Z' +generator_git_sha: f78e364d753b3899ef7975955c9e384b724ea4bc cogant_version: 0.6.0 package: name: cogant @@ -517,7 +517,7 @@ literature: source: cogant/docs/evaluation/LITERATURE.md bibliography_entries: 104 benchmark: - suite_runtime_s: 608.32 + suite_runtime_s: 871.82 shipped_fixture_count: 12 benchmark_suite_file: suite_20260818.md benchmark_python_version: 3.12.13 diff --git a/cogant/tests/property/test_correctness_laws.py b/cogant/tests/property/test_correctness_laws.py index 2190c17..b1c617d 100644 --- a/cogant/tests/property/test_correctness_laws.py +++ b/cogant/tests/property/test_correctness_laws.py @@ -234,10 +234,7 @@ def test_law6_transition_normalization( A=[[1.0]], D=[1.0], B=[ - [ - [1.0 if row == col else 0.0 for _ in range(n_actions)] - for col in range(n_states) - ] + [[1.0 if row == col else 0.0 for _ in range(n_actions)] for col in range(n_states)] for row in range(n_states) ], ) diff --git a/scripts/code_quality/batch_cogsec_improve.py b/scripts/code_quality/batch_cogsec_improve.py index 4d80398..d345c7b 100644 --- a/scripts/code_quality/batch_cogsec_improve.py +++ b/scripts/code_quality/batch_cogsec_improve.py @@ -11,6 +11,7 @@ import re from pathlib import Path + def improve_file(path: Path) -> dict: source = path.read_text() lines = source.splitlines(keepends=True) diff --git a/scripts/z_generate_manuscript_variables.py b/scripts/z_generate_manuscript_variables.py index df422b2..0f099c4 100644 --- a/scripts/z_generate_manuscript_variables.py +++ b/scripts/z_generate_manuscript_variables.py @@ -108,7 +108,6 @@ def get_logger(name: str) -> logging.Logger: substitute_text, ) - logger = get_logger(__name__) MANUSCRIPT_DIR = COGANT_STAGING_ROOT / "manuscript" diff --git a/tests/test_manuscript_figures.py b/tests/test_manuscript_figures.py index 9d2fa70..cc92103 100644 --- a/tests/test_manuscript_figures.py +++ b/tests/test_manuscript_figures.py @@ -161,6 +161,7 @@ def test_copy_manuscript_figures_writes_template_figure_registry(tmp_path: Path) "role": "test", "sha256": registry["figures"][0]["sha256"], "source_artifact": "", + "alt_text": "", } ] diff --git a/tools/audit_synthetic_surfaces.py b/tools/audit_synthetic_surfaces.py index 937f3c5..2cd3b7f 100644 --- a/tools/audit_synthetic_surfaces.py +++ b/tools/audit_synthetic_surfaces.py @@ -154,7 +154,7 @@ def format(self) -> str: justification="maximum-entropy priors and structural defaults are documented degraded-output values", ), AllowRule( - path=r"(^\.github/workflows/.*\.ya?ml$|^README\.md$|^TODO\.md$|^cogant/(\.gitignore|AGENTS\.md|CHANGELOG\.md|CONTRIBUTING\.md|Makefile|README\.md|pyproject\.toml)$|^cogant/docs/|^cogant/evaluation/|^cogant/examples/|^cogant/parsers/|^cogant/specs/|^cogant/rust/|^manuscript/|^scripts/|^tools/)", + path=r"(^\.github/workflows/.*\.ya?ml$|^README\.md$|^TODO\.md$|^ISA\.md$|^REVIEW_LOG_[0-9-]+\.md$|^cogant/(\.gitignore|AGENTS\.md|CHANGELOG\.md|CONTRIBUTING\.md|Makefile|README\.md|pyproject\.toml)$|^cogant/docs/|^cogant/evaluation/|^cogant/examples/|^cogant/parsers/|^cogant/specs/|^cogant/rust/|^manuscript/|^scripts/|^tools/)", terms=("fallback", "mock", "stub"), category="degraded_output", justification="project documentation, examples, and release notes describe audited degradation or compatibility behavior", diff --git a/tools/figures/copier.py b/tools/figures/copier.py index cb78b7f..2183981 100644 --- a/tools/figures/copier.py +++ b/tools/figures/copier.py @@ -313,6 +313,10 @@ def _write_template_figure_registry( "generated_by": record.get("renderer", ""), "source_artifact": record.get("source_artifact", ""), "sha256": record.get("sha256", ""), + # The template HTML renderer refuses figures without + # accessibility alt text (``require_record_alt``), so + # every registry record carries the registered alt text. + "alt_text": record.get("alt_text", ""), } ) registry = {