fix(scanner): make SD022 path-drift rule precise + fix real doc/workflow drift - #545
Merged
Conversation
Point the scan wrapper at the standards reusable commit that sets GITHUB_TOKEN (HYPATIA_SCAN_PAT || GITHUB_TOKEN) in the scan step, so the Dependabot / code-scanning / secret-scanning alert checks stop emitting "GITHUB_TOKEN not set" warnings. The reusable at this pin also keeps the --exit-zero + always() artifact upload, so findings are published even when the severity gate fails. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V13sGvHnaeCY5A9fF2QsDm
…ow drift
SD022 ("stale `src/<dir>/` reference after a directory rename") was written
for a single-crate workspace and over-fired in this polyglot, multi-crate,
cross-repo tree: 20 hits, of which 17 were false positives. Hypatia self-scans,
so this also stops it re-introducing these FPs into its own results.
Rule fix (lib/rules/structural_drift.ex):
- A `src/<dir>/` reference is REAL when <dir> exists as `**/src/<dir>` ANYWHERE
in the tree (crate-relative `scripts/x/src/bin`, doc-relative `ffi/zig` ->
`src/connectors`, prefixed `cli/src/commands`), or when it carries a path
prefix whose leading segment is not a real top-level dir here (foreign-repo
citations like `vcl-ut/src/core`, `examples/nestjs/src/i18n`).
- Skip corpus/vendored docs (.audittraining/, test*/, fixtures, third_party/),
mirroring lib/hypatia/scanner_suppression.ex.
This clears 13 of the 20 structurally (FP_relative, FP_foreign, FP_corpus).
Genuine drift fixed at source (not suppressed):
- 0-AI-MANIFEST.a2ml, docs/EXPLAINME.adoc: the Rust workspace is at the repo
ROOT (adapters/ cli/ data/ fixer/ integration/), not `src/rust/`.
- src/ui/gossamer/README.adoc: canonical pattern file is `bridge.eph` at the
gossamer root, not `src/core/Bridge.eph`.
- docs/EXPLAINME.adoc: qualify foreign FFI-layer paths with their repo
(`gossamer/src/interface/...`, `burble/src/Burble/...`).
- docs/architecture/system-integration.md: make the example scan argument a
clearly-foreign placeholder path.
Real non-path finding fixed:
- .github/workflows/push-email-notify.yml: add `timeout-minutes: 5`
(workflow_audit/missing_timeout_minutes).
Result: self-scan 22 medium -> effectively 0 real (remaining: 1 PROSPECTIVE
ref to a planned cli/src/registry/ module in a design doc; git_state GS001/GS007
are working-tree/branch environmentals).
Labelled practice dataset (.audittraining/path-reference-drift/): the 22
findings with discriminants, trope, type, verdict, and recommended action,
plus TYPOLOGY.md (the discriminant set + typology + tropology). Parked in the
exempt audit-training corpus; portable to hermeneia.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V13sGvHnaeCY5A9fF2QsDm
hyperpolymath
marked this pull request as ready for review
June 26, 2026 20:28
Signed-off-by: Jonathan D.A. Jewell <6759885+hyperpolymath@users.noreply.github.com>
hyperpolymath
added a commit
that referenced
this pull request
Jun 26, 2026
…per (#551) ## What Removes the `print-findings` job from `.github/workflows/hypatia-scan.yml`, restoring the wrapper to its thin form (`hypatia:` job + `secrets: inherit`). ## Why That job was **triage scaffolding** — it downloaded the `hypatia-scan-findings` artifact and `cat`'d `hypatia-findings.json` to the job log so the self-scan findings could be enumerated without artifact download. Its own comment says *"TEMPORARY (triage scaffolding — remove before merge)"*, but it shipped to `main` with #545. It adds a redundant download-and-print job to every scan run. The findings are still fully available via the `hypatia-scan-findings` artifact uploaded by the reusable workflow (`if: always()`), so nothing is lost. No change to the scan itself or the `e2ef79ee` reusable pin. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- _Generated by [Claude Code](https://claude.ai/code/session_01V13sGvHnaeCY5A9fF2QsDm)_ Co-authored-by: Claude <noreply@anthropic.com>
50 tasks
hyperpolymath
pushed a commit
that referenced
this pull request
Jun 27, 2026
#545's rewrite of sd022_stale_path_after_rename/1 left a dangling `real_subdirs` after the binding was renamed to `real_basenames` (src_dir_index/1). `mix escript.build` fails with "undefined function real_subdirs/0", which breaks the Hypatia scan job estate-wide — the reusable workflow clones hypatia and builds the escript before scanning, so every consumer repo's scan dies at build. Fixes: - real_subdirs -> real_basenames in the final reject. - The lookbehind regex has one capture group, so the 2-group `[_, prefix, dir]` destructure could never match (FunctionClauseError after the compile error was resolved); reduced to `[_, dir]` and removed the now-unused real_path_reference?/4 + foreign_prefix?/2 (the negative lookbehind already excludes prefixed/foreign refs). - Guard also checks File.dir?(repo/src) so an empty `src/<dir>` directory still counts (git ls-files cannot see empty dirs). Verified locally: mix test test/structural_drift_test.exs -> 32 tests, 0 failures; module compiles via elixirc. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012JvDqQCW4CHxkEBAoJV5FU
hyperpolymath
added a commit
that referenced
this pull request
Jun 27, 2026
… scan (#553) ## Problem — `main` does not compile, so every repo's `scan` is red The Hypatia reusable workflow `git clone`s this repo and runs `mix escript.build` **before** scanning. As of **#545 (`7df53b1b`)**, `main` fails to build: ``` == Compilation error in file lib/rules/structural_drift.ex == ** (CompileError) undefined function real_subdirs/0 ``` So `scan / Hypatia Neurosymbolic Analysis` dies at *"Build Hypatia scanner"* on **every consumer repo in the estate** (surfaced while verifying #464 — the cache pin is fine, this is the real blocker). ## Root cause #545 rewrote `sd022_stale_path_after_rename/1` and renamed the binding `real_subdirs` → `real_basenames` (now produced by `src_dir_index/1`), but left a dangling `real_subdirs` reference in the final `Enum.reject`. Two further latent defects from the same half-finished refactor: 1. The regex uses a **negative lookbehind with one capture group**, but the pipeline destructured `[_, prefix, dir]` (two groups) and called `real_path_reference?/4` — a `FunctionClauseError` waiting to fire once the compile error was cleared. 2. `src_dir_index/1` switched to `git ls-files`, which **cannot see an empty `src/<dir>` directory** — so the early-return guard would wrongly short-circuit (this is exactly what the existing test `"flags docs referencing src/<dir>/…"` sets up). ## Fix (minimal, behaviour-preserving) - `real_subdirs` → `real_basenames` in the final reject. - Collapse the vestigial 2-group prefix path to `[_, dir]` and remove the now-unused `real_path_reference?/4` + `foreign_prefix?/2` (the lookbehind already excludes prefixed/foreign refs, so they were dead). - Guard now also checks `File.dir?(repo/src)` so an empty `src/<dir>` still counts. Net diff: **+4 / −25** in one file. ## Verification Ran the real suite locally (Elixir 1.14 / OTP 25): ``` mix test test/structural_drift_test.exs 32 tests, 0 failures ``` (All four `sd022_*` tests pass, plus `elixirc` compiles the module clean — reproduced the original `CompileError` first, confirmed it's gone after.) Once `main` compiles again, the estate-wide `scan` lane unblocks. Closes the scan-side of #464. --- _Drafted by Claude Code; please run `mix test` / `mix escript.build` in CI before merge._ --- _Generated by [Claude Code](https://claude.ai/code/session_012JvDqQCW4CHxkEBAoJV5FU)_ Co-authored-by: Claude <noreply@anthropic.com>
This was referenced Jun 27, 2026
hyperpolymath
added a commit
that referenced
this pull request
Jun 27, 2026
…che) (#558) ## Summary Bumps the Hypatia scan-reusable pin in `.github/workflows/hypatia-scan.yml` from `d7c2271` to `e9c8888` (current standards `main`). standards#441 (merged) source-pins the scanner build cache: it resolves hypatia's `main` tip with `git ls-remote` and folds the SHA into the cache key, so the clone + `mix escript.build` re-run when hypatia advances instead of restoring the first scanner ever cached **forever**. Until this bump, this repo's scans ran against that stale cached binary — so scanner fixes (e.g. the SD022 path-drift precision fix, #545) never actually took effect in this repo's CI. ## Effect Next scan rebuilds the scanner from current hypatia `main`, so CI reflects the live ruleset. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- _Generated by [Claude Code](https://claude.ai/code/session_017wGTeLwiBGJ5rETC3QT4Pm)_ Co-authored-by: Claude <noreply@anthropic.com>
4 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Triages the 22 medium findings that were the real
exit 1of Hypatia's own scan. Reproduced the exact CI result locally (22 medium / 0 high), classified every finding, and fixed at source — the rule where it was wrong, the docs where they were genuinely stale, the workflow where the finding was real.The 22, classified
src/rust/drift (Rust is at repo root) + 1 workflow missingtimeout-minutescli/src/registry/module referenced in a design docgit_state/GS007stale remote branch (not a tree defect)Source fixes
Rule —
lib/rules/structural_drift.ex(SD022): it was written for a single-crate workspace and only checked the repo-rootsrc/. Corrected discriminants:src/<dir>/reference is real when<dir>exists as**/src/<dir>anywhere in the tree (handlesscripts/x/src/bin,ffi/zig→src/connectors,cli/src/commands), or carries a prefix whose leading segment isn't a real top-level dir here (foreign citations:vcl-ut/src/core,examples/nestjs/src/i18n);.audittraining/,test*/, fixtures,third_party/), mirroringscanner_suppression.ex.This clears 13 of 20 SD022 FPs structurally — and stops Hypatia re-emitting them estate-wide.
Genuine drift (fixed, not suppressed):
0-AI-MANIFEST.a2ml,docs/EXPLAINME.adoc— Rust workspace is the repo root crates (adapters/ cli/ data/ fixer/ integration/), notsrc/rust/.src/ui/gossamer/README.adoc— canonical pattern file isbridge.ephat the gossamer root, notsrc/core/Bridge.eph.docs/EXPLAINME.adoc— qualify foreign FFI-layer paths with their repo (gossamer/…,burble/…).docs/architecture/system-integration.md— example scan arg is now a clearly-foreign placeholder.Real finding:
.github/workflows/push-email-notify.yml— addedtimeout-minutes: 5.Result
Self-scan 22 medium → effectively 0 real. Remaining after this PR: 1 PROSPECTIVE (planned module in a design doc) and
GS001/GS007(working-tree + branch environmentals that don't exist on a clean main).Bonus: labelled practice dataset
.audittraining/path-reference-drift/— the 22 findings as a labelled corpus (raw_text, structuraldiscriminants,trope,type,verdict,recommended_action) plusTYPOLOGY.md(the discriminant set + typology + tropology). Parked in the exempt audit-training corpus; portable tohermeneiaas a real practice dataset for path-reference-drift classification.🤖 Generated with Claude Code
Generated by Claude Code