You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
SD004 false positive: a2ml files in canonical location flagged as 'wrong location' #426
Hypatia's SD004 rule flags all .machine_readable/*.a2ml files as "6a2ml file outside canonical location -- must be in .machine_readable/6a2/".
Reproduces on every repo whose A2ML files live at .machine_readable/ directly. Recent example: gitbot-fleet PR #249 hypatia scan — 6 critical SD004 findings, all false positives, mirrored by 6 high CSA001 alerts.
Root cause
The rule enforces the retired.machine_readable/6a2/ subdir layout. Per the current standards CLAUDE.md and per hypatia's own CLAUDE.md:
"the canonical seven A2ML files (`STATE`, `META`, `ECOSYSTEM`, `AGENTIC`, `NEUROSYM`, `PLAYBOOK`, `ANCHOR`) live directly under `.machine_readable/`, per the `A2ML-REPO-TEMPLATE` in `hyperpolymath/standards`. (Earlier versions of this CLAUDE.md referenced a `.machine_readable/6scm/` subdir; that layout has been retired.)"
So the rule is enforcing the old layout that the standards explicitly retired.
Blast radius
Every estate repo with A2ML files at the canonical .machine_readable/ location gets 6 critical + ~6 high false-positive findings on every Hypatia scan. CSA002 aggregate counts get inflated as a result.
Fix direction
Update the SD004 rule definition to accept .machine_readable/<file>.a2ml as the canonical location (and reject the retired .machine_readable/6a2/<file>.a2ml, or leave both passing during a transition).
Related
gitbot-fleet#249 (where this was surfaced)
gitbot-fleet#254 (workflow-hygiene fix PR; SD004 listed as out-of-scope deferred to this issue)
This is blocked on the #485 canonical-layout decision, not a clean FP fix
Investigated the fix and it turns out to be a policy call in disguise. The premise here — "flat .machine_readable/*.a2ml is canonical, so SD004 is wrong" — is contradicted by hypatia's own rules, which uniformly encode the opposite (6a2/-canonical) convention:
Source
Stance on .machine_readable/STATE.a2ml (flat)
lib/rules/structural_drift.exSD004
CRITICAL — "must be in .machine_readable/6a2/"
SD001
"must be .a2ml in .machine_readable/6a2/"
SD007 stale-ref patterns
flat path is a stale reference → "should be .machine_readable/6a2/STATE.a2ml"
SD023 doc comment
"estate v2 convention puts STATE at .machine_readable/6a2/STATE.a2ml. Some repos retain a legacy top-level .machine_readable/STATE.a2ml."
hypatia's own live .machine_readable/
canonical files kept in 6a2/ (so SD004 does not self-FP)
"seven canonical A2ML files live directly under .machine_readable/ … that [subdir] layout has been retired."
So the estate genuinely holds two contradictory conventions, and hypatia's scanner encodes the 6a2/ side. SD004 only false-positives on flat-layout repos (gitbot-fleet et al.); it's correct for any repo following the 6a2/ convention. That makes this a concrete instance of #485 (audit, needs-owner: rsr-template ↔ standards divergence) — it can't be resolved correctly until the owner rules which layout is canonical.
Why I didn't just patch the rule
Flipping SD004 to accept flat-as-canonical would (a) take a side in the #485 dispute and (b) desync it from SD001/SD007/SD023 in the same file, which would then need reconciling too. That's an architectural decision, not an FP cleanup.
Resolution paths for the owner (pick one under #485)
Flat is canonical → in structural_drift.ex: drop Path.join([repo_path, ".machine_readable", a2ml_file]) from SD004's wrong_locations (keep repo-root flagged), update SD004's reason/target, and reconcile SD001's reason + SD007's @stale_reference_patterns + SD023's framing to match. Also retire the 6a2/ references in hypatia's own .claude/CLAUDE.md.
6a2/ is canonical → SD004 is already correct; the real fix is to move the flat-layout repos' files into .machine_readable/6a2/ and correct gitbot-fleet/.claude/CLAUDE.md invariant Review SCM files and security updates #1.
Interim, policy-neutral (if the noise needs muting before the decision): accept files anywhere under .machine_readable/ (flat or6a2/), flag only repo-root placement, and downgrade severity from :critical. Stops the 6-critical-per-scan bleed without declaring a winner.
Leaving open and linking to #485; this shouldn't be actioned until that decision lands.
Eliminates false positives for repos using canonical layout (e.g., gitbot-fleet#249 which had 6 critical SD004 + 6 high CSA001 false positives).
Root cause: rule was enforcing retired .machine_readable/6a2/ layout while standards explicitly retired that layout in favor of .machine_readable/ directly.
Activity
hyperpolymath commentedon Jun 20, 2026
This is blocked on the #485 canonical-layout decision, not a clean FP fix
Investigated the fix and it turns out to be a policy call in disguise. The premise here — "flat
.machine_readable/*.a2mlis canonical, so SD004 is wrong" — is contradicted by hypatia's own rules, which uniformly encode the opposite (6a2/-canonical) convention:.machine_readable/STATE.a2ml(flat)lib/rules/structural_drift.exSD004.machine_readable/6a2/".machine_readable/6a2/".machine_readable/6a2/STATE.a2ml".machine_readable/6a2/STATE.a2ml. Some repos retain a legacy top-level.machine_readable/STATE.a2ml.".machine_readable/6a2/(so SD004 does not self-FP)…versus the premise this issue is built on:
gitbot-fleet/.claude/CLAUDE.mdinvariant #1.machine_readable/… that [subdir] layout has been retired."So the estate genuinely holds two contradictory conventions, and hypatia's scanner encodes the
6a2/side. SD004 only false-positives on flat-layout repos (gitbot-fleet et al.); it's correct for any repo following the6a2/convention. That makes this a concrete instance of #485 (audit, needs-owner: rsr-template ↔ standards divergence) — it can't be resolved correctly until the owner rules which layout is canonical.Why I didn't just patch the rule
Flipping SD004 to accept flat-as-canonical would (a) take a side in the #485 dispute and (b) desync it from SD001/SD007/SD023 in the same file, which would then need reconciling too. That's an architectural decision, not an FP cleanup.
Resolution paths for the owner (pick one under #485)
structural_drift.ex: dropPath.join([repo_path, ".machine_readable", a2ml_file])from SD004'swrong_locations(keep repo-root flagged), update SD004'sreason/target, and reconcile SD001's reason + SD007's@stale_reference_patterns+ SD023's framing to match. Also retire the6a2/references in hypatia's own.claude/CLAUDE.md.6a2/is canonical → SD004 is already correct; the real fix is to move the flat-layout repos' files into.machine_readable/6a2/and correctgitbot-fleet/.claude/CLAUDE.mdinvariant Review SCM files and security updates #1..machine_readable/(flat or6a2/), flag only repo-root placement, and downgrade severity from:critical. Stops the 6-critical-per-scan bleed without declaring a winner.Leaving open and linking to #485; this shouldn't be actioned until that decision lands.
Generated by Claude Code
docs(status): session handover snapshot 2026-06-20
docs(status): session handover snapshot 2026-06-20 (#517)
fix(rules): update SD004 to accept canonical .machine_readable/ layout (
hyperpolymath commentedon Aug 12, 2026
RESOLVED via PR #701. Fixes applied to lib/rules/structural_drift.ex:
Added ANCHOR.a2ml to @a2ml_state_files (completes the canonical seven: STATE, META, ECOSYSTEM, AGENTIC, NEUROSYM, PLAYBOOK, ANCHOR)
Updated SD004 rule to accept canonical .machine_readable/ layout:
Eliminates false positives for repos using canonical layout (e.g., gitbot-fleet#249 which had 6 critical SD004 + 6 high CSA001 false positives).
Root cause: rule was enforcing retired .machine_readable/6a2/ layout while standards explicitly retired that layout in favor of .machine_readable/ directly.