|
| 1 | +<!-- SPDX-License-Identifier: CC-BY-SA-4.0 --> |
| 2 | +<!-- Owner: Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk> --> |
| 3 | +<!-- SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk> --> |
| 4 | + |
| 5 | +# Hypatia findings — outstanding triage handoff (2026-06-24) |
| 6 | + |
| 7 | +> **Status: OPEN.** The Hypatia neurosymbolic gate is now active and reports |
| 8 | +> **37 pre-existing findings (5 critical, 7 high, 25 medium)** on ephapax. |
| 9 | +> They are **not** fixed — triage + fix is the work below. These are |
| 10 | +> pre-existing repo debt that a broken CI pin had been masking, **not** |
| 11 | +> introduced by the change that unmasked them. |
| 12 | +
|
| 13 | +## How this surfaced |
| 14 | + |
| 15 | +The `scan / Hypatia Neurosymbolic Analysis` gate used to die at "Prepare all |
| 16 | +required actions" because the three `hyperpolymath/standards` reusable |
| 17 | +workflows were pinned to `5a93d9d57cc0`, a ref that is **not** an ancestor of |
| 18 | +`standards` HEAD; that broken pin also transitively referenced a dead |
| 19 | +`actions/cache` SHA. PR #315 re-pinned to a published HEAD (later bumped to |
| 20 | +`d135b05…` in #316), which let the scan actually run — and report the 37 |
| 21 | +findings that were previously invisible. |
| 22 | + |
| 23 | +> **Two milestones are not one:** "the workflow now *runs*" and "the workflow |
| 24 | +> now *passes*" are different. Fixing the pin achieved the first; the second |
| 25 | +> is this document. |
| 26 | +
|
| 27 | +## Why this is handed off (cannot be run in the remote sandbox) |
| 28 | + |
| 29 | +The engine is an Elixir escript in `github.com/hyperpolymath/hypatia`. Building |
| 30 | +it needs `mix deps.get`, which fetches from **`repo.hex.pm`** — and the remote |
| 31 | +session's egress policy returns `403` for the Hex registry (npm/PyPI/crates/Go |
| 32 | +are allowed; Hex is not). So the engine must be run on a host where Hex is |
| 33 | +reachable (a local machine, or a CI runner), **or** the environment's network |
| 34 | +policy must be widened to allow `repo.hex.pm` + `builds.hex.pm`. |
| 35 | + |
| 36 | +## Task 1 — produce the exact findings |
| 37 | + |
| 38 | +```bash |
| 39 | +git clone https://github.com/hyperpolymath/hypatia.git ~/hypatia |
| 40 | +cd ~/hypatia |
| 41 | +mix local.hex --force && mix local.rebar --force |
| 42 | +mix deps.get |
| 43 | +mix escript.build |
| 44 | +# Run with NO GITHUB_TOKEN to match CI (CI had none, so the dependabot / |
| 45 | +# secret-scanning / code-scanning alert rules emit nothing — the 37 are |
| 46 | +# file-based rules): |
| 47 | +HYPATIA_FORMAT=json ./hypatia-cli.sh scan /path/to/ephapax > hypatia-findings.json |
| 48 | +jq '[.[].severity] | group_by(.) | map({(.[0]): length}) | add' hypatia-findings.json |
| 49 | +``` |
| 50 | + |
| 51 | +- Rules are pure functions in `lib/rules/*.ex`; `scan` runs `@all_rule_modules`. |
| 52 | +- With no token the 37 are file-based — most likely from `honest_completion`, |
| 53 | + `proof_obligation`, `disambiguation_rules`, `code_safety`, `root_hygiene`, |
| 54 | + `supply_chain`, `structural_drift`, `workflow_audit`, `workflow_hardening`, |
| 55 | + `cicd_rules`, `scorecard_compliance`. (A local token may surface MORE than |
| 56 | + 37 — run without one to reproduce the gate's set.) |
| 57 | +- Severities depend on the pinned `standards` reusable-workflow version |
| 58 | + (currently `d135b05…`); the 5/7/25 split was observed on the first |
| 59 | + unblocked run and may shift slightly with the engine version. |
| 60 | + |
| 61 | +## Task 2 — triage + fix |
| 62 | + |
| 63 | +1. Group by rule × severity. Fix order: **5 critical → 7 high → 25 medium**. |
| 64 | +2. Per finding: true positive → fix in code/docs; false positive / accepted → |
| 65 | + add a justified `rule:path` line to `.hypatia-ignore` (matched `grep -qxF`), |
| 66 | + or enter it in `.hypatia-baseline.json` (schema in `standards` |
| 67 | + `.machine_readable/hypatia-baseline.schema.json`). |
| 68 | +3. **Proof / claim findings** (`honest_completion`, `proof_obligation`): fix by |
| 69 | + making *claims* honest (docs/metadata), **never** by editing or force-closing |
| 70 | + the fenced legacy proofs. Per `CLAUDE.md`: do not touch |
| 71 | + `formal/Semantics.v` `Theorem preservation` (provably false; deliberately |
| 72 | + `Admitted`), `formal/Typing.v`, or `formal/Counterexample.v`. |
| 73 | +4. **`disambiguation_rules`** findings: ensure the ephapax-vs-AffineScript |
| 74 | + disambiguation markers are present per `CLAUDE.md`. |
| 75 | +5. Re-run the scan until `critical = 0` (ideally total within policy). Open a |
| 76 | + PR with a triage table: *finding → disposition → fix*. |
| 77 | + |
| 78 | +## Task 3 — make the gate non-opaque (in `hyperpolymath/standards`) |
| 79 | + |
| 80 | +The gate fails **opaquely**: in `standards`' |
| 81 | +`.github/workflows/hypatia-scan-reusable.yml` the scan runs under `bash -e`, |
| 82 | +and `hypatia-cli.sh scan` **exits 1 when findings exist**, so the step aborts |
| 83 | +*before* the (already-present) `upload-artifact` step and the "warn but don't |
| 84 | +fail" critical-check ever run. Net effect: a hard job failure emitting only a |
| 85 | +severity count, no detail. |
| 86 | + |
| 87 | +**Fix (in `standards`):** wrap the scan in `set +e` + capture the rc; **always** |
| 88 | +upload `hypatia-findings.json` (`if: always()`); write a findings table to |
| 89 | +`$GITHUB_STEP_SUMMARY`; enforce the gate in a final dedicated step. Pin |
| 90 | +`actions/upload-artifact` by SHA (same supply-chain lesson as PR #315). A full |
| 91 | +suggested patch was drafted in-session (not yet applied). |
| 92 | + |
| 93 | +## Guardrails (from `CLAUDE.md`) |
| 94 | + |
| 95 | +- This is `hyperpolymath/ephapax` (not AffineScript). Signed commits. |
| 96 | +- Code/config = `MPL-2.0`; prose (`.md`/`.adoc`) = `CC-BY-SA-4.0`. |
| 97 | +- Never touch the fenced legacy preservation proof or `Typing.v` / |
| 98 | + `Counterexample.v`. Fix proof/claim findings by aligning claims to reality. |
0 commit comments