Skip to content

fix(reconcile): --verify no longer crashes JSON-encoding its result#272

Open
hyperpolymath wants to merge 7 commits into
mainfrom
fix/reconcile-verify-json-encode
Open

fix(reconcile): --verify no longer crashes JSON-encoding its result#272
hyperpolymath wants to merge 7 commits into
mainfrom
fix/reconcile-verify-json-encode

Conversation

@hyperpolymath
Copy link
Copy Markdown
Owner

Tiny follow-up to the merged Scorecard epic (#260). Surfaced by the live mix hypatia.reconcile hyperpolymath/modshells --verify run.

Bug

ScorecardReconciler.verify/2 returns {:ok, summary} | {:error, reason}; reconcile/3 returns a bare map. The mix task fed the raw result to Jason.encode!/2, which cannot encode a tuple — so --verify crashed on output. The verify logic ran correctly (recurrence-defects were computed); only the print failed.

Fix

Normalise verify to a bare map at the task boundary, matching reconcile/3's convention:

  • {:ok, summary}summary
  • {:error, reason}%{repo, verified: false, error: reason}

Task-wrapper only — no change to verify/2 or any tested code path.

Verification

  • --verify with token → clean JSON ({"recurrence_defects": [], "repo": "hyperpolymath/modshells"})
  • --verify without token (also previously crashed) → clean JSON ({"error": "GITHUB_TOKEN not set", ...})
  • Reconciler suite 10/10 green.

Refs #260 #263.

🤖 Generated with Claude Code

ScorecardReconciler.verify/2 returns {:ok, summary} | {:error, reason}
whereas reconcile/3 returns a bare map. The mix task fed the raw result
straight to Jason.encode!/2, which cannot encode a tuple, so
`mix hypatia.reconcile owner/repo --verify` crashed on output (the
verify logic itself ran correctly — recurrence_defects were computed,
just never printed). Surfaced by the live modshells reconcile run while
closing the Scorecard epic (hypatia#260).

Normalise verify to a bare map at the task boundary (matching
reconcile/3's convention): {:ok, summary} -> summary; {:error, reason}
-> %{repo, verified: false, error: reason}. Both the success and the
no-token error path now emit clean JSON. Task-wrapper only; no change
to verify/2 or any tested code path; reconciler suite green.

Refs #260 #263.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@hyperpolymath hyperpolymath enabled auto-merge (squash) May 17, 2026 05:50
@github-actions
Copy link
Copy Markdown

🔍 Hypatia Security Scan

Findings: 13 issues detected

Severity Count
🔴 Critical 2
🟠 High 3
🟡 Medium 8

⚠️ Action Required: Critical security issues found!

View findings
[
  {
    "reason": "Issue in quality.yml",
    "type": "missing_workflow",
    "file": "quality.yml",
    "action": "create",
    "rule_module": "workflow_audit",
    "severity": "high"
  },
  {
    "reason": "Issue in mirror.yml",
    "type": "missing_workflow",
    "file": "mirror.yml",
    "action": "create",
    "rule_module": "workflow_audit",
    "severity": "high"
  },
  {
    "reason": "Issue in security-policy.yml",
    "type": "missing_workflow",
    "file": "security-policy.yml",
    "action": "create",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Action hyperpolymath/standards/.github/workflows/governance-reusable.yml@main needs attention",
    "type": "unpinned_action",
    "file": "governance.yml",
    "action": "pin_sha",
    "rule_module": "workflow_audit",
    "severity": "high"
  },
  {
    "reason": "believe_me undermines formal verification (2 occurrences, CWE-704)",
    "type": "believe_me",
    "file": "/home/runner/work/hypatia/hypatia/src/abi/RuleEngine.idr",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "critical"
  },
  {
    "reason": "Nickel file missing SPDX-License-Identifier header (1 occurrences, CWE-1104)",
    "type": "ncl_missing_spdx",
    "file": "/home/runner/work/hypatia/hypatia/configs/config.ncl",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "medium"
  },
  {
    "reason": "unsafe block -- requires SAFETY comment (22 occurrences, CWE-676)",
    "type": "unsafe_block",
    "file": "/home/runner/work/hypatia/hypatia/clients/rust/hypatia-client/src/ffi.rs",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "medium"
  },
  {
    "reason": "as_ptr exposes raw pointer that may dangle or alias unsafely (10 occurrences, CWE-676)",
    "type": "as_ptr",
    "file": "/home/runner/work/hypatia/hypatia/clients/rust/hypatia-client/src/ffi.rs",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "medium"
  },
  {
    "reason": "expect() in hot path (1 occurrences, CWE-754)",
    "type": "expect_in_hot_path",
    "file": "/home/runner/work/hypatia/hypatia/adapters/src/codeberg.rs",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "medium"
  },
  {
    "reason": "expect() in hot path (1 occurrences, CWE-754)",
    "type": "expect_in_hot_path",
    "file": "/home/runner/work/hypatia/hypatia/adapters/src/radicle.rs",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "medium"
  }
]

Powered by Hypatia Neurosymbolic CI/CD Intelligence

hyperpolymath and others added 3 commits May 18, 2026 09:06
…egrity)

The taiki-e/install-action step installed 'just' unpinned, tripping the
governance R1 tooling-version-integrity rule (estate-canonical pin is
just@1.34.0, matching other estate workflows). Pre-existing on main;
fixed here so #272 can clear governance / Security policy checks.

The Language/anti-pattern (Python) red was a stale run against the
pre-escape governance-reusable; the bench scripts already carry both
.hypatia-ignore entries and inline hypatia:ignore pragmas, so it
resolves on re-run against current standards/main.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Eliminates the last org-policy Python carve-out: replaces
scripts/{check-bench-regression,update-bench-baselines}.py with a
standalone, zero-dependency Rust crate (scripts/bench-tools/, deliberately
outside the workspace so it never perturbs the main build / proof gates).

- Faithful 1:1 port: same criterion bencher parsing, same Markdown
  summary + ::error:: annotations, same exit codes (0/1/2), and a
  byte-identical baselines.json serializer (Python json.dumps indent=2,
  sort_keys=False semantics; insertion order + int tokens preserved).
  Behaviour verified across advisory / regression / no-regression /
  empty-input / usage-error cases.
- tests.yml + benchmarks/README.md rewired to 'cargo run --manifest-path'.
- Removed the now-moot exemptions: .hypatia-ignore lines,
  .hypatia-exemptions.md rows, and the stale .hypatia-baseline.json
  banned_language_file entries — so the carve-out can't become drift.

Unblocks #272's governance / Language anti-pattern by eliminating the
Python rather than suppressing it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Completes human-facing docs for the #272 governance fixes so nothing
is stale or undocumented:
- CHANGELOG.adoc [Unreleased]: bench Python→Rust port + just@1.34.0 pin.
- scripts/README.adoc: document the new scripts/bench-tools/ crate
  (previously an undocumented directory).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@@ -0,0 +1,331 @@
// SPDX-License-Identifier: PMPL-1.0-or-later
@github-actions
Copy link
Copy Markdown

🔍 Hypatia Security Scan

Findings: 14 issues detected

Severity Count
🔴 Critical 2
🟠 High 4
🟡 Medium 8

⚠️ Action Required: Critical security issues found!

View findings
[
  {
    "reason": "Issue in quality.yml",
    "type": "missing_workflow",
    "file": "quality.yml",
    "action": "create",
    "rule_module": "workflow_audit",
    "severity": "high"
  },
  {
    "reason": "Issue in mirror.yml",
    "type": "missing_workflow",
    "file": "mirror.yml",
    "action": "create",
    "rule_module": "workflow_audit",
    "severity": "high"
  },
  {
    "reason": "Issue in security-policy.yml",
    "type": "missing_workflow",
    "file": "security-policy.yml",
    "action": "create",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Action hyperpolymath/standards/.github/workflows/governance-reusable.yml@main needs attention",
    "type": "unpinned_action",
    "file": "governance.yml",
    "action": "pin_sha",
    "rule_module": "workflow_audit",
    "severity": "high"
  },
  {
    "reason": "believe_me undermines formal verification (2 occurrences, CWE-704)",
    "type": "believe_me",
    "file": "/home/runner/work/hypatia/hypatia/src/abi/RuleEngine.idr",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "critical"
  },
  {
    "reason": "Nickel file missing SPDX-License-Identifier header (1 occurrences, CWE-1104)",
    "type": "ncl_missing_spdx",
    "file": "/home/runner/work/hypatia/hypatia/configs/config.ncl",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "medium"
  },
  {
    "reason": "unsafe block -- requires SAFETY comment (22 occurrences, CWE-676)",
    "type": "unsafe_block",
    "file": "/home/runner/work/hypatia/hypatia/clients/rust/hypatia-client/src/ffi.rs",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "medium"
  },
  {
    "reason": "as_ptr exposes raw pointer that may dangle or alias unsafely (10 occurrences, CWE-676)",
    "type": "as_ptr",
    "file": "/home/runner/work/hypatia/hypatia/clients/rust/hypatia-client/src/ffi.rs",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "medium"
  },
  {
    "reason": "expect() in hot path (1 occurrences, CWE-754)",
    "type": "expect_in_hot_path",
    "file": "/home/runner/work/hypatia/hypatia/adapters/src/codeberg.rs",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "medium"
  },
  {
    "reason": "expect() in hot path (1 occurrences, CWE-754)",
    "type": "expect_in_hot_path",
    "file": "/home/runner/work/hypatia/hypatia/adapters/src/radicle.rs",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "medium"
  }
]

Powered by Hypatia Neurosymbolic CI/CD Intelligence

hyperpolymath and others added 3 commits May 18, 2026 20:48
…panic)

Resolves the Hypatia code-scanning alert introduced by PR #272's Python->Rust
port: lib.rs number() now propagates the (statically-unreachable, ASCII-only)
UTF-8 error via ? instead of unwrap(), mirroring the existing \u handler.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
scripts/bench-tools/src/lib.rs used char::from_u32(cp).unwrap_or('\u{fffd}').
unwrap_or is infallible (cannot panic), but Hypatia's
code_safety/unwrap_without_check rule matches the `unwrap` substring inside
`unwrap_or` and raised a file-level (line 1, zero-width) DoS-via-panic / CWE-754
error, blocking this PR. map_or('\u{fffd}', |c| c) is behaviour-identical and
carries no `unwrap` token. Root cause is the scanner rule, not this code —
tracked separately; this is the minimal unblock for #272.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants