Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
d2bbf75
fix(matcher): scorecard findings unreachable due to language gate
claude May 23, 2026
7cc2667
chore(baseline): regenerate .hypatia-baseline.json against current tree
claude May 23, 2026
e929621
feat(rules): consume secret-scanning and code-scanning alert APIs
claude May 23, 2026
74173ee
test(soundness): manifest-driven regression gate for scanner rules
claude May 23, 2026
12f2890
feat(outcomes): closed-loop verification metric + recipe_health task
claude May 23, 2026
5e895b5
feat(outcomes): canonical record_outcome_for_fix entry + mix task wra…
claude May 23, 2026
6d40240
test(soundness): escript packaging gate + fix latent extension gaps
claude May 23, 2026
97b299f
feat(dispatch): auto-quarantine recipes with low verification rate
claude May 23, 2026
3569d47
docs(soundness): document why other rule families don't drop in cleanly
claude May 23, 2026
d0ddd2f
feat(telemetry): instrument decision points + central event registry
claude May 24, 2026
3b4379e
feat(watcher): live-monitoring aggregator GenServer + ETS counters
claude May 24, 2026
30ced35
feat(web): /api/* operational endpoints backed by the Watcher
claude May 24, 2026
708a19b
feat(cli): mix hypatia.watch — live terminal dashboard
claude May 24, 2026
5b15430
feat(watcher): SSE event stream + recipe/quarantine drill-downs
claude May 24, 2026
a0075db
feat(web): single-page operational dashboard at /
claude May 24, 2026
adc2863
feat(web): Prometheus /metrics endpoint
claude May 24, 2026
3750086
feat(alerts): threshold rules + pluggable sinks + /api/alerts endpoint
claude May 24, 2026
a1eb6b2
feat(watcher): 5-min snapshot persistence to verisim-data/metrics/
claude May 24, 2026
fc4f5d0
feat(anomaly): statistical detector + anomaly_detected alert rule
claude May 24, 2026
699bf06
docs(roadmap): reflect PR #309 work (M7/M8/M11/M12 done) + plan M13-M15
claude May 24, 2026
01fce4d
feat(cli): native SARIF 2.1.0 output + retire inline JS converter (M13)
claude May 24, 2026
5fab9d4
feat(web): bearer-token auth on /api/* (M15a)
claude May 24, 2026
fe159c6
feat(watcher): persist state across restart (M15b)
claude May 24, 2026
5710910
feat(anomaly): ESN drift as an independent alert source (M15c)
claude May 24, 2026
de504de
feat(alerts): cross-host federation via Peer sink + ingest endpoint (…
claude May 24, 2026
a69fcbb
Merge branch 'main' into claude/charming-hamilton-2nLPc - resolve con…
hyperpolymath May 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 13 additions & 8 deletions .claude/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,25 +171,30 @@ verisim-data (git-backed flat files) is the canonical data store. VCL queries ex
- 3 safety systems: rate limiter, quarantine, batch rollback
- VCL integrated: built-in parser, file executor, query cache, cross-repo analytics

### Remaining Work (M7: Production Operations)
### Remaining Work (M7+: Production Operations)

**Critical:**
- Create PAT with repo scope for automated cross-repo dispatch
- Write real fix scripts for the 310 null-fix-script dispatch entries
- Push committed fixes to remotes across repos
- ~~Create PAT with repo scope for automated cross-repo dispatch~~ (DONE 2026-05-24: `HYPATIA_DISPATCH_PAT` provisioned + verified — 19 `hypatia-security-alert` events landed in gitbot-fleet from first manual sweep, all completing 17-26s)
- ~~Resolve "310 null-fix-script dispatch entries"~~ (DONE 2026-05-24, PR #309 commit `d2bbf75`: root cause was matcher language-gate, not missing scripts — all 22 scorecard fix scripts already existed on disk)
- Push committed fixes to remotes across repos (PAT now allows it; dispatch-runner side needs to call `mix hypatia.record_outcome` to populate the verification metric — `mix` task delivered in PR #309 commit `5e895b5`)

**Important:**
- Deploy verisim-api server (enables native graph/vector/temporal modalities)
- 5 new RSR compliance rules cover structural compliance (banned languages, SCM locations, required files, Containerfile naming) — distinct from PA rule recipes
- ~~Generate summaries for NULL-summary repos in verisim-data~~ (DONE 2026-03-07: 295 summaries auto-generated)
- ~~Historical trend tracking across scan cycles~~ (DONE 2026-04-22: `lib/historical_trends.ex` + VCL.Query integration)
- ~~Historical trend tracking across scan cycles~~ (DONE 2026-04-22: `lib/historical_trends.ex` + VCL.Query integration; PR #309 adds 5-min snapshot persistence to `data/verisim/metrics/`)
- ~~VCL federation executor — multi-store~~ (DONE 2026-04-22: `lib/vcl/remote_executor.ex`; `FROM FEDERATION REMOTE IN [...]`)
- ~~Live watcher / supervision interface~~ (DONE 2026-05-24, PR #309: 10 commits across 3 phases — telemetry → Watcher GenServer → HTTP API + SSE + HTML dashboard + Prometheus + alerts + 5-min persistence + statistical anomaly detection)
- ~~Closed-loop quality~~ (DONE 2026-05-24, PR #309: soundness gates (in-process + escript-packaging) + closed-loop verification metric + auto-quarantine in FleetDispatcher)

**Planned:**
- GraphQL API as live HTTP endpoint
- SARIF output for IDE integration
**Planned (M13-M15):**
- M13: SARIF output for IDE integration
- M14: GraphQL API as live HTTP endpoint
- M15: Bearer-token auth on `/api/*` + persistent Watcher state across restart + cross-host alert federation + ESN tight integration
- Nx/EXLA backend for the neural layer if/when reservoir sizes outgrow pure Elixir
- Cross-organization federation with VCL drift policies
- Neural rebalancer Strategy B (adversarial perturbation) + C (real failure corpus from panic-attack history) (M9 in progress)
- Ada TUI Elixir supervision wiring (M10 in progress)

### Known Gaps

Expand Down
139 changes: 21 additions & 118 deletions .github/workflows/hypatia-scan.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,27 @@ jobs:
run: |
echo "Scanning repository: ${{ github.repository }}"

# Run scanner (exits non-zero when findings exist — suppress to continue)
HYPATIA_FORMAT=json "$HOME/hypatia/hypatia-cli.sh" scan . --exit-zero > hypatia-findings.json || true
# Run scanner (exits non-zero when findings exist — suppress to continue).
# Emits BOTH:
# hypatia-findings.json — native JSON for the artifact + PR comment + Phase 2 submission
# hypatia.sarif — native SARIF 2.1.0 (Hypatia.SARIF module since PR #309)
# replaces the inline Node.js converter that used to live here.
HYPATIA_FORMAT=json "$HOME/hypatia/hypatia-cli.sh" scan . --exit-zero > hypatia-findings.json || true
HYPATIA_FORMAT=sarif "$HOME/hypatia/hypatia-cli.sh" scan . --exit-zero > hypatia.sarif || true

# Validate SARIF (empty SARIF is intentional — clears stale alerts).
# Falls back to a minimal empty document if the scanner couldn't emit
# SARIF for any reason (stale escript without the SARIF module, etc.).
if ! jq -e 'has("version") and .version == "2.1.0"' hypatia.sarif >/dev/null 2>&1; then
echo "::warning::scanner did not produce a valid SARIF; emitting empty fallback"
cat > hypatia.sarif <<'JSON'
{
"$schema": "https://json.schemastore.org/sarif-2.1.0.json",
"version": "2.1.0",
"runs": [{"tool":{"driver":{"name":"Hypatia","informationUri":"https://github.com/hyperpolymath/hypatia","rules":[]}},"results":[]}]
}
JSON
fi

# Count findings
FINDING_COUNT=$(jq '. | length' hypatia-findings.json 2>/dev/null || echo 0)
Expand Down Expand Up @@ -113,122 +132,6 @@ jobs:
path: hypatia-findings.json
retention-days: 90

- name: Convert Hypatia findings to SARIF
# Always runs (no findings_count guard): an EMPTY SARIF run is
# valid and intentional — uploading it clears stale Hypatia
# alerts from the code-scanning page when a repo goes clean.
# The converter is dependency-free Node (Node ships on
# ubuntu-latest; no npm install — estate npm ban respected) and
# is hardened against the heterogeneous Hypatia JSON schema:
# most findings are {rule_module,severity,type,file,reason,
# action}; only some carry an integer `line`; `file` may be
# empty or absolute. See lib/hypatia/cli.ex (collect_findings).
run: |
cat > "$RUNNER_TEMP/hypatia-sarif.cjs" <<'CJS'
const fs = require('fs');
const path = require('path');
const crypto = require('crypto');

const ws = process.env.GITHUB_WORKSPACE || process.cwd();

let findings = [];
try {
const parsed = JSON.parse(fs.readFileSync('hypatia-findings.json', 'utf8'));
if (Array.isArray(parsed)) findings = parsed;
} catch (_) {
// Scanner unavailable / empty / malformed -> empty SARIF.
// Intentionally clears stale alerts rather than erroring.
findings = [];
}

// Mirrors Hypatia's own "github" annotation mapping
// (lib/hypatia/cli.ex output/2): critical|high -> error,
// medium -> warning, everything else -> note.
const levelFor = (sev) => {
switch (String(sev || '').toLowerCase()) {
case 'critical':
case 'high': return 'error';
case 'medium': return 'warning';
default: return 'note';
}
};

// SARIF artifactLocation.uri must be a repo-relative POSIX
// path. Hypatia may emit absolute paths (scanned under
// $GITHUB_WORKSPACE) or "" / "." for repo-level findings.
const relUri = (file) => {
if (!file) return '.';
let f = String(file);
if (path.isAbsolute(f)) {
const rel = path.relative(ws, f);
f = (rel && !rel.startsWith('..')) ? rel : path.basename(f);
}
f = f.replace(/\\/g, '/').replace(/^\.\//, '');
return f || '.';
};

const rules = new Map();
const results = findings.map((f) => {
const mod = String(f.rule_module || 'hypatia');
const type = String(f.type || 'finding');
const ruleId = `hypatia/${mod}/${type}`;
const level = levelFor(f.severity);
if (!rules.has(ruleId)) {
rules.set(ruleId, {
id: ruleId,
name: `${mod}.${type}`,
shortDescription: { text: `Hypatia ${mod}: ${type}` },
defaultConfiguration: { level }
});
}
const uri = relUri(f.file);
const msg = String(f.reason || f.type || 'Hypatia finding');
const startLine =
Number.isInteger(f.line) && f.line > 0 ? f.line : 1;
// Stable cross-run fingerprint for dedupe (no line, so a
// moved finding in the same file/rule stays one alert).
const fp = crypto
.createHash('sha256')
.update([ruleId, uri, type, msg].join('|'))
.digest('hex');
return {
ruleId,
level,
message: { text: msg },
locations: [
{
physicalLocation: {
artifactLocation: { uri },
region: { startLine }
}
}
],
partialFingerprints: { 'hypatiaFindingHash/v1': fp }
};
});

const sarif = {
$schema: 'https://json.schemastore.org/sarif-2.1.0.json',
version: '2.1.0',
runs: [
{
tool: {
driver: {
name: 'Hypatia',
informationUri: 'https://github.com/hyperpolymath/hypatia',
rules: Array.from(rules.values())
}
},
results
}
]
};

fs.writeFileSync('hypatia.sarif', JSON.stringify(sarif, null, 2));
console.log(`hypatia.sarif written: ${results.length} result(s).`);
CJS
node "$RUNNER_TEMP/hypatia-sarif.cjs"

- name: Upload SARIF to GitHub code scanning
# Fork PRs get a read-only GITHUB_TOKEN, so security-events:write
# is unavailable and upload-sarif cannot publish — skip there
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,9 @@ htmlcov/
/tmp/
*.tmp
*.bak

# Watcher runtime state (warm-restart persistence — operational, not source)
data/verisim/watcher/watcher.state.json
# asdf version manager
.tool-versions
verification/proofs/agda/*.agdai
Expand Down
69 changes: 61 additions & 8 deletions .machine_readable/6a2/STATE.a2ml
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,18 @@
[metadata]
project = "hypatia"
version = "0.1.0"
last-updated = "2026-05-21"
last-updated = "2026-05-24"
status = "active"

[project-context]
name = "hypatia"
completion-percentage = 87
completion-percentage = 91
phase = "implementation"
maturity = "alpha"
crg-grade = "C"
crg-grade-declared-on = "2026-04-25"
crg-grade-target = "B"
crg-grade-notes = "Promoted D→C 2026-04-25: mix test 0/528 failures (233 verisim_data excluded by design); take_supervised/1 pattern fixed 25 test isolation failures in reflexive/contract suites; evict_expired >= ttl fix; 24 per-subtree README.adoc files; 7 Zig FFI domain functions. B blockers: live GitHub PAT dispatch, fix-script coverage, neural B+C rebalancer strategies, Ada TUI supervision wiring."
crg-grade-notes = "Promoted D→C 2026-04-25: mix test 0/528 failures (233 verisim_data excluded by design); take_supervised/1 pattern fixed 25 test isolation failures in reflexive/contract suites; evict_expired >= ttl fix; 24 per-subtree README.adoc files; 7 Zig FFI domain functions. Closed 2026-05-24 (PR #309): live GitHub PAT dispatch operational, fix-script coverage gap resolved (root cause was matcher bug not missing scripts), watcher/supervision interface delivered end-to-end (telemetry → ETS aggregator → JSON/SSE/Prometheus/HTML/TUI → alerts/persistence/anomaly). B blockers remaining: neural B+C rebalancer strategies, Ada TUI supervision wiring."

[dogfooding-status]
# Populated 2026-04-18 against real on-disk integrations. Each entry must
Expand All @@ -36,16 +36,24 @@ milestones = [
{ id = "M4", title = "ProofStrategySelection (PS001–PS010)", status = "done" },
{ id = "M5", title = "ProofObligation recipe type (PO001–PO006)", status = "done" },
{ id = "M6", title = "LearningScheduler N5 — ProverRecommender retrain", status = "done" },
{ id = "M7", title = "GitHub PAT for live cross-repo dispatch", status = "in_progress" },
{ id = "M7", title = "GitHub PAT for live cross-repo dispatch", status = "done" },
{ id = "M8", title = "Write real fix scripts (310 null-fix-script entries)", status = "done" },
{ id = "M9", title = "Neural rebalancer Strategy B+C (adversarial perturbation + failure corpus)", status = "in_progress" },
{ id = "M10", title = "Ada TUI Elixir supervision wiring", status = "in_progress" },
{ id = "M11", title = "Watcher / supervision interface (telemetry → aggregator → API/SSE/Prometheus/TUI → alerts/persistence/anomaly)", status = "done" },
{ id = "M12", title = "Closed-loop quality (soundness gates, verification metric, auto-quarantine)", status = "done" },
{ id = "M13", title = "SARIF output for IDE integration", status = "planned" },
{ id = "M14", title = "GraphQL API as live HTTP endpoint", status = "planned" },
{ id = "M15", title = "Bearer-token auth + state persistence + federation for /api/*", status = "planned" },
]

[blockers-and-issues]
issues = [
{ id = "B1", description = "GitHub PAT not yet configured — automated cross-repo dispatch blocked", severity = "high" },
# B2 resolved 2026-04-26: 14 fix scripts written + 12 recipes updated (M8 done)
# B1 resolved 2026-05-24: HYPATIA_DISPATCH_PAT provisioned, verified end-to-end
# (19 hypatia-security-alert dispatches landed in gitbot-fleet on first sweep).
# B2 resolved 2026-04-26: 14 fix scripts written + 12 recipes updated (M8 done).
# B3 resolved 2026-05-24: matcher language-gate bug fixed (PR #309 commit d2bbf75) —
# was the underlying cause of the "310 null fix_script" symptom; scripts existed.
]

[known-gaps]
Expand All @@ -58,13 +66,58 @@ gaps = [

[critical-next-actions]
actions = [
"Configure GitHub PAT with repo scope for live dispatch (M7)",
# M8 DONE 2026-04-26: 14 scripts written, 12 recipes updated, RecipeGenerator extended
# M7 DONE 2026-05-24: HYPATIA_DISPATCH_PAT provisioned + verified.
# M8 DONE 2026-04-26: 14 scripts written, 12 recipes updated, RecipeGenerator extended.
# M11 DONE 2026-05-24 (PR #309): watcher/supervision interface, 3-phase delivery.
# M12 DONE 2026-05-24 (PR #309): soundness gates + closed-loop verification.
"Complete neural rebalancer Strategy B+C (M9)",
"Complete Ada TUI Elixir supervision wiring (M10)",
"SARIF output for IDE integration (M13)",
"GraphQL API as live HTTP endpoint (M14)",
"Bearer auth + state persistence + cross-host federation for /api/* (M15)",
]

[session-history]
# 2026-05-24: PR #309 — watcher/supervision interface + closed-loop quality. 19 commits.
# Bug fixes (4): recipe-matcher language-gate (`d2bbf75` — unblocked ~310
# scorecard dispatches; the "310 null fix_script" symptom's real cause was
# a matcher bug treating `"any"` sentinel as unmatched and `"yaml"` recipes
# as unreachable, not missing scripts), baseline regen (`7cc2667` — 71 stale
# → 35 fresh, 0 critical/0 high), secret + code scanning alert consumers
# (`e929621`), `@language_extensions` walker missing `.agda`/`.zig`/`.thy`/
# `.fst`/`.adb` (`6d40240` — caught by the new escript packaging soundness
# test on its first run; same PR #278 class).
# Soundness gates (3): in-process manifest test with 14 fixtures (`74173ee`),
# end-to-end escript-build test wired into e2e-elixir CI (`6d40240`), honest
# doc on why other rule families don't drop into the manifest pattern
# (`3569d47`).
# Closed-loop quality (3): verification_rate + recipe_health + `mix
# hypatia.recipe_health` (`12f2890`), `record_outcome_for_fix` + `mix
# hypatia.record_outcome` CLI wrapper for the bash dispatch-runner with
# non-zero exit on still_present (`5e895b5`), auto-quarantine in
# FleetDispatcher when verification rate < 0.30 over ≥5 verifiable outcomes
# (`97b299f`).
# Watcher Phase 1 (4): `Hypatia.Telemetry` event registry + emit helpers
# (`d0ddd2f`), `Hypatia.Watcher` GenServer + ETS rolling-window aggregator
# (`3b4379e`), `/api/status` + `/api/counts/:window` + `/api/recipes`
# loopback-only via `Hypatia.Web.ApiRouter` (`30ced35`), `mix hypatia.watch`
# terminal dashboard (`708a19b`).
# Watcher Phase 2 (3): SSE stream at `/api/events` + `/api/recipes/:id` +
# `/api/quarantine` drill-downs (`5b15430`), static HTML dashboard at `/`
# with vanilla JS + EventSource (`a0075db`), Prometheus `/metrics`
# exposition (`adc2863`).
# Watcher Phase 3 (3): threshold rules + pluggable sinks (Log/Webhook/File)
# + `/api/alerts` (`3750086`), 5-min snapshot persistence to
# data/verisim/metrics/YYYY-MM-DD.jsonl (`a1eb6b2`), statistical anomaly
# detector with 2σ baseline divergence + ESN drift corroboration
# (`fc4f5d0`).
# Plus housekeeping (this commit): ROADMAP + STATE updates marking M7/M8/M11/
# M12 done; v7.5 (watcher) + v7.6 (closed-loop quality) sections added; M13/
# M14/M15 added for the remaining roadmap items.
# PAT verification: 19 hypatia-security-alert events landed in gitbot-fleet
# from the first manual sweep, all completing 17-26s, confirming the closed
# loop is operational end-to-end for the first time in the session.
#
# 2026-05-21: Epic #273 — Hypatia architecture reconciliation & neurosymbolic activation.
# Four-gap landing (one session, owner-merge gated):
# Gap (1) UNFED neural organs — already on `main` via PR #275 (merged
Expand Down
Loading
Loading