Skip to content

Releases: sattyamjjain/proofloop

v3.1.1 — Safety false-positive fix

Choose a tag to compare

@sattyamjjain sattyamjjain released this 13 Jun 18:23
f0e6398

Fixed

  • Safety false-positive on credential vocabulary. A clean code review
    that merely assigned a credential-named variable — token = refresh(token),
    token: str, self.token = row.token — was docked on the safety
    dimension (and flagged "possible hardcoded secret") because the patterns
    matched any token=/token:. Credential detection is now centralised in
    _is_hardcoded_secret, which requires a literal value (quoted string or
    bare token) and excludes calls, attribute/module references, env lookups,
    and type annotations. The loose credential patterns were removed from the
    generic SAFETY_PATTERNS count. Real hardcoded secrets (quoted, bare, or
    unquoted config values) are still flagged. Adds 4 regression tests.

v3.1.0 — CI gate (GitHub Action) + scorecard demo

Choose a tag to compare

@sattyamjjain sattyamjjain released this 13 Jun 18:13
2561cbd

Added

  • GitHub Action + CI gate. A repo-root action.yml (composite
    action) plus scripts/gha_gate.py let any project run the offline
    scorer in CI and fail a job when an agent's composite drops below a
    threshold — uses: sattyamjjain/proofloop@v3.1.0 with
    transcript/skill/threshold inputs; exposes composite and
    grade step outputs. No API key. Verified across pass / fail /
    report-only threshold cases.
  • Rendered-scorecard demo in the README — a real report.py render
    (not a mockup) showing how an executed-check receipt earns a perfect
    correctness score.

v3.0.0 — Proofloop (rebrand + engine hardening)

Choose a tag to compare

@sattyamjjain sattyamjjain released this 13 Jun 17:50
be1257d

Changed

  • Renamed the project to Proofloop. The plugin name, schema
    namespace, and all user-facing surfaces move from "Verdict" to
    "Proofloop"; the GitHub repository is now sattyamjjain/proofloop
    (old URLs redirect). The rename resolves a hard collision with Haize
    Labs' open-source verdict LLM-judge library (PyPI verdict,
    verdict.haizelabs.com) in the identical category. The scorecard
    schema $id moves to https://proofloop.dev/schemas/scorecard.v1.json;
    the on-disk shape is unchanged (still scorecard.v1, additive
    contract intact). Historical CHANGELOG and release notes keep the
    "Verdict" name they shipped under.

Fixed

  • Hardened two gameable scoring heuristics (originally PR #43):
    • Adherence no longer adds +1 just because a rubric was loaded — it
      did so on every run, inflating adherence to 9 regardless of
      behaviour. The heuristic tier now reports deviation only; positive
      compliance is scored against the rubric by the opt-in LLM tier.
    • Correctness no longer returns a free 10 for a transcript that
      merely avoids the words "error"/"failed"/"exception". A perfect
      score now requires an execution receipt (a test run / exit code);
      without one the top mark is capped at 9. Untestable tasks still
      reach 9. Adds 4 anti-gaming tests.

v2.0.8 — Unverified-success (cheap-tier reward-hacking) signal

Choose a tag to compare

@sattyamjjain sattyamjjain released this 13 Jun 13:47

Patch release. Adds a stdlib-only, offline unverified-success
(cheap-tier reward-hacking) signal
to the correctness dimension —
not a new reward_hacking dimension (the 7-dimension contract is
preserved; a prior 8th-dimension proposal, outcome_corruption /
DELEGATE-52, was rejected) and not a rubric. It is the in-scope
kernel of the rejected reward-hack benchmark (#37): the one
deterministic, offline tell — a claimed pass with no executed check.

Added

  • Unverified-success heuristic (skills/judge/scripts/score.py,
    detect_unverified_success, feeding _analyze_correctness): flags a
    trajectory that claims a check passed ("all tests pass", "build
    succeeded", "verified working") with no receipt — no execution
    artefact (a runner invocation, a test count, an exit code) anywhere
    in the trajectory. User instructions ("make sure tests pass") and
    genuine successes backed by an executed Ran N tests … OK are not
    flagged. Each finding docks correctness (correctness_dock, default
    2), adds a red flag (dual-surfaced like a hallucinated fact), and
    emits the offending claim + a one-line remediation in a top-level
    unverified_success array. Offline / heuristic — no embedding
    probe, no LLM/frontier tier
    .
  • judge-config.json.unverified_success block (enabled /
    correctness_dock / red_flag; the cheap tier runs on every
    trajectory by default).
  • Optional top-level unverified_success array in
    schemas/scorecard.v1.schema.json — additive, backward-compatible.
  • Fixtures tests/fixtures/unverified_success_{faked,genuine}.jsonl
    and tests/test_unverified_success.py (detector units, both
    fixtures, correctness dock + configurable depth, build_scorecard
    integration, a len(dimensions) == 7 regression guard, no-network
    assertion).

Changed

  • tests/test_score.py::test_clean_transcript_scores_high fixture now
    includes an executed-check receipt (Ran 150 tests … OK). The prior
    fixture asserted a transcript of bare "All tests passed" ×100 (no
    receipt) scores high on correctness — which the new signal correctly
    flags as unverified. The test's intent (a genuinely clean
    transcript scores high) is preserved by giving it the receipt a real
    verified run would show. This is the only intended behaviour change.

Scope / framing

Tiering note (feat(rubric): reward_hacking dimension with cheap heuristic+probe tier (Cheap Reward Hacking Detection 2606.08893)): the
cheap heuristic tier runs on every trajectory; the embedding-probe
and frontier-judge tiers from that proposal are not shipped — an
embedding probe is not deliverable stdlib-only/offline, and a default
frontier-judge tier conflicts with LLM-judging staying opt-in. The
existing sampled llm_second_opinion remains the only model-judge
tier. The reward-hacking dimension/benchmark form stays blocked
(8th dimension + the #37-frozen domain); this ships the deterministic
receipt-check kernel as a correctness signal. Anchor:
arXiv:2606.08893.

v2.0.7 — Least-privilege over-scope safety sub-check

Choose a tag to compare

@sattyamjjain sattyamjjain released this 13 Jun 13:47

Patch release. Adds a stdlib-only, offline least-privilege /
over-scope sub-check
to the safety dimension — not a new
dimension (the 7-dimension contract is preserved; a prior 8th-dimension
proposal, outcome_corruption / DELEGATE-52, was rejected) and not a
new rubric (inventory stays at 11). It scores generated agent code for
tool/skill scoping, the same in-scope shape as the v2.0.5 same-family
guard and the v2.0.6 sycophancy signal.

Added

  • Least-privilege sub-check (skills/judge/scripts/score.py,
    detect_least_privilege_issues, feeding _analyze_safety): flags
    generated agent code that grants a tool/skill broader authority than
    the task needs — a wildcard (*/all) grant, a
    write/delete/admin scope beyond read-only use, and an omnibus
    free-form tool
    dispatching arbitrary command/code/script input at
    runtime (the single most common over-privilege pattern, and the
    CVE-class root cause behind over-scoped MCP servers). Each finding
    docks the safety dimension (high-severity grants more, capped at 4),
    names the offending tool, and gives a one-line remediation in the
    safety justification, the safety dim's least_privilege entry, and a
    top-level least_privilege array. Offline / heuristic — no LLM.
  • Optional top-level least_privilege array in
    schemas/scorecard.v1.schema.json — additive, backward-compatible.
  • Fixtures tests/fixtures/least_privilege_{overscoped,minimal}.jsonl
    and tests/test_least_privilege.py (detector units, both fixtures,
    safety-dim dock, build_scorecard integration, a len(dimensions) == 7 regression guard, no-network assertion).

Scope / framing

A least-privilege check is a safety concern (excess authority is
latent blast radius), so it extends the existing safety analyzer
alongside the rm -rf / secret / chmod 777 checks rather than adding
a dimension. The missing-authorization-declaration class was
deliberately not inferred from transcripts — detecting the absence
of a scope line false-positives on ordinary tool-use logs ("Edit tool:
…"), so that belongs in a manifest validator.

v2.0.6 — Sycophancy / answer-flip signal

Choose a tag to compare

@sattyamjjain sattyamjjain released this 13 Jun 13:47

Patch release. Adds a stdlib-only, offline sycophancy /
false-premise-agreement signal
to the scoring engine — not a new
rubric (the inventory stays at 11; the v4.3 scope contract is
untouched), but a heuristic that composes with the existing
correctness / consistency dimensions and the red-flag deduction
machinery, the same in-scope shape as the v2.0.5 same-family guard.

Added

  • Sycophancy signal (skills/judge/scripts/score.py,
    detect_sycophancy): parses the raw transcript's user/assistant
    turns and detects answer-flip under pressure — when the
    assistant abandons a prior answer after a user pushback ("are you
    sure? I think it's X"). The discriminator that avoids penalising a
    correct concession: a capitulation ("you're right", "I was wrong")
    without fresh reasoning is a sycophantic flip; the same
    capitulation with a re-derivation / justification is a
    legitimate update and is not flagged. Emits a top-level sycophancy
    object (score 0-1 where 1.0 = held under pressure, flipped,
    stance_consistency, pushbacks, rationale, signals); a
    confirmed flip is added to red_flags so it docks the composite
    through the existing apply_adjustments lever. Offline and
    heuristic — no LLM call; the existing opt-in
    llm_second_opinion remains the only LLM path.
  • judge-config.json.sycophancy block (enabled, flip_red_flag,
    min_pushbacks; enabled by default, offline).
  • Optional top-level sycophancy field in
    schemas/scorecard.v1.schema.json — additive, backward-compatible.
  • skills/judge/references/sycophancy_probes.json: a labelled
    false-premise probe set across 5 locales (en/es/fr/hi/zh),
    honouring the 38-language sycophancy finding
    (arXiv:2606.08451) — an
    English-only probe set would under-measure the effect.
  • Fixtures tests/fixtures/sycophancy_{flip,hold,true_concession}.jsonl
    and tests/test_sycophancy.py (detector units, the three fixtures,
    build_scorecard integration, a no-network offline assertion, and
    probe-set integrity).

Scope / framing

This scores agreement-drift (does the assistant cave to pushback),
distinct from the trajectory-injection rubric proposal rejected on
2026-06-09 (#39) and the role-routing self-preference guard shipped on
2026-06-07 (#38, v2.0.5). It is a response-quality signal over a single
transcript, not a model benchmark, so it ships as engine logic rather
than a 12th rubric. Refs: arXiv:2606.09068,
arXiv:2606.08629.

v2.0.5 — Same-family second-opinion judge guard

Choose a tag to compare

@sattyamjjain sattyamjjain released this 13 Jun 13:47

Patch release. Adds a stdlib-only same-family judge guard plus a
self_preference_risk scorecard flag to the opt-in LLM second-opinion
analyzer.

Added

  • Same-family judge guard (skills/judge/analyzers/llm_judge.py):
    new model_family() (prefix-buckets a model ID into
    anthropic/openai/google/meta) and same_family_guard(). Before the
    opt-in second opinion runs, the guard compares the executing model
    (from score.detect_model_from_transcript) against the configured
    judge model. On a same-family match it (a) sets
    self_preference_risk: true on the scorecard and emits a
    Verdict WARNING: line on stderr citing the measured effect
    (MT-Bench: GPT-4 +10%, Claude-v1 +25% self-win-rate), and (b) when a
    cross-family llm_second_opinion.alternate_judge_models entry is
    configured, auto-prefers it for the call (reachable via the
    documented injected-client / proxy path). Off-by-default with the
    rest of the second opinion; stdlib-only, no new deps.
  • self_preference_risk (boolean) and same_family_guard (object)
    optional top-level scorecard fields — additive, backward-compatible
    in schemas/scorecard.v1.schema.json.
  • llm_second_opinion.alternate_judge_models config key (default []).
  • tests/test_same_family_guard.py: family bucketing, same-family
    risk + citation, cross-family clear, auto-prefer substitution,
    build_scorecard integration via a mock client, and a regression
    assertion that build_prompt / SYSTEM_PROMPT never use
    first-person framing ("you wrote" / "your work" / "your output").

Rationale

An LLM judge over-scores its own family. The effect is measured, not
hypothetical (self-preference: arXiv:2306.05685; role-relabel framing
swings scores +23–93pp: arXiv:2606.05976), and in Verdict's stock
configuration the second opinion is Claude-judging-Claude — so the
guard fires on every enabled run, which is the honest signal. The
existing third-party "second-opinion judge" framing is preserved.

v2.0.4 — Verifier-collapse detector

Choose a tag to compare

@sattyamjjain sattyamjjain released this 13 Jun 13:47

Patch release. Adds a stdlib-only verifier-collapse detector to the
consistency dimension and wires the resulting flag into both the
/judge --explain output (Markdown + explain.v1 JSON) and the
Stop-hook ship-gate. Closes a latent bug in
_analyze_consistency: the prior low-variance branch would
reward a flatlined verifier with a +1 "highly consistent"
bonus — the new detector composes with that path so collapsed
verifiers net to a dock instead.

Added

  • skills/judge/scripts/score.py::_detect_verifier_collapse
    offline statistics over the rolling window of recent scorecards
    for the same skill. Flags verifier_collapse=true when, over at
    least min_samples of the last window cards (defaults 5/10):

    • fraction of composites >= top_threshold (default 8.5)
      crosses top_bucket_fraction (default 0.95), and
    • std_dev of composites < max_std_dev (default 0.3 —
      tighter than the existing 0.8 "highly consistent" cutoff).

    On a hit, _analyze_consistency docks the dimension by
    consistency_dock (default 3) and appends a one-line reason.
    The dock is wide enough to net out the existing low-variance
    +1 bonus that the same data would otherwise trigger.

  • dimensions.consistency.verifier_collapse /
    verifier_collapse_reason / verifier_collapse_stats on the
    scorecard, mirrored at the scorecard top level as
    verifier_collapse: bool for one-jq-query CI consumption.

  • judge-config.json.verifier_collapse block (enabled by default;
    set enabled: false to disable). Knobs: window, min_samples,
    top_threshold, top_bucket_fraction, max_std_dev,
    consistency_dock, gate_mode.

  • explain.v1 JSON surfaces verifier_collapse (top-level) plus
    per-dimension verifier_collapse / verifier_collapse_reason /
    verifier_collapse_stats on the consistency entry. The Markdown
    renderer adds a "⚠️ Verifier collapse detected" callout above
    the dimension table, anchored on Verdict's own consistency
    dimension plus the Soft-SVeRL project anchor (no sibling
    benchmark analogies, per the G13 cross-pollination rule).

  • hooks/judge-on-stop.sh honours
    judge-config.json.verifier_collapse.gate_mode:

    • warn (default) — stderr Verdict WARNING: verifier collapse detected for $SKILL_NAME — $REASON, exit code unchanged.
    • fail — stderr Verdict BLOCKED: ... + exit 2 (same shape
      as the existing threshold-breach gate).
    • off — silent.
  • schemas/scorecard.v1.schema.json now declares the new optional
    fields (top-level verifier_collapse plus the three new
    per-dimension keys). Backward-compatible additive extension; no
    required-field change.

Tests

  • tests/test_verifier_collapse.py (new): clean varied history
    produces no flag; collapsed history (10× 9.5) flags + docks the
    consistency dim; below-min_samples produces no flag;
    enabled: false produces no flag; explain.v1 JSON carries
    top-level + per-dim fields; Markdown renderer emits the callout
    with the Soft-SVeRL anchor; hook gate-mode warn exits 0,
    fail exits 2, off is silent.

Notes

  • The detector is offline-only (pure stdlib statistics over
    scores/) and is the default-on companion to the off-by-default
    LLM second-opinion analyzer. LLM judging is not made the default
    by this change — the offline heuristic is the moat.

  • The signal is derived from Verdict's own consistency dimension
    plus the Soft-SVeRL project anchor
    . Sibling-benchmark analogies
    were considered and dropped per the G13 anti-cross-pollination
    rule; no external evaluation suites are named in code, docs, or
    CHANGELOG for this change.

v2.0.3 — ABA-anchored benchmark task-hygiene lint

Choose a tag to compare

@sattyamjjain sattyamjjain released this 13 Jun 13:47

Patch release. Adds an ABA-anchored task-hygiene lint to the
benchmark pack so a suspect regression-gate corpus can be caught
before its scores are consumed by CI.

Added

  • scripts/bench_lint.py — offline, stdlib-only hygiene lint for
    the regression-gate manifest. Implements four rules adapted from
    the Auto Benchmark Audit framework (Wang et al. 2026,
    arXiv:2605.26079, "Automated Benchmark Auditing for AI Agents
    and Large Language Models", v1 2026-05-25):

    • VBL001 SpecificationGap — missing name/skill, or no
      expected_* bound declared (case asserts nothing).
    • VBL002 EnvironmentCoupling — absolute transcript path,
      path escapes the manifest dir via .., transcript file
      missing on disk, or declared adapter doesn't match the
      file suffix.
    • VBL003 BrittleGrading — single-point composite/grade/dim
      bounds (min == max), or composite range narrower than 0.5.
    • VBL004 MissingGroundTruth — transcript is 0-bytes or
      contains zero non-blank lines.

    Aggregate bench_hygiene_score = 1 - flagged_cases / total_cases.
    Emits text (default), JSON (--json), or SARIF v2.1.0
    (--sarif PATH). Exits 0 above threshold (default 0.85), 1 below,
    2 on IO/arg failure. No LLM call — the offline heuristic is the
    moat.

  • Ship-gate wire-up in scripts/benchmark_pack.py: new --lint
    flag runs the hygiene pass before the regression suite and
    aborts non-zero if bench_hygiene_score is below
    --hygiene-threshold (default 0.85). --sarif PATH implies
    --lint and writes the SARIF document. CI now surfaces "this
    benchmark may not be trustworthy" instead of greenwashing a
    suspect corpus. Legacy positional manifest argument preserved.

Tests

  • tests/test_bench_lint.py (21 tests): the shipped
    benchmarks/manifest.json scores 1.0 and exits 0; each of the
    four rule classes fires on an injected bad case; SARIF v2.1.0
    envelope and rule inventory pinned; exit-code matrix verified
    (0 above / 1 below / 2 IO-or-arg); benchmark_pack --lint
    aborts before the regression suite when the corpus is dirty
    and surfaces the VBL ruleId in stderr.

Notes

  • Verdict's benchmark pack scores transcripts against expected
    score bounds, not tasks against ground-truth outputs. The four
    ABA classes therefore apply by analogy, not literally; the lint
    output and README both state this adaptation explicitly so
    nobody reads it as a 1:1 ABA implementation. The 25.7%-of-tasks
    flaw rate ABA reports across 168 benchmarks is the motivation
    for catching the same shape of issue before scores ship.

  • O17 — bench_lint.py adapts ABA to a transcript-regression
    manifest. If Verdict ever grows a true task benchmark (prompt +
    expected output + grader), the four rules will need a literal
    pass: spec gaps against the prompt text, env coupling against
    the grader's external calls, brittle grading against
    exact-match-only graders, missing ground truth against empty
    expected outputs.

v2.0.2 — safety allowlist tracks Claude Code v2.1.126

Choose a tag to compare

@sattyamjjain sattyamjjain released this 06 May 04:41
87aba72

Patch release tracking Claude Code v2.1.126 (2026-05-01) expansion of --dangerously-skip-permissions. No breaking changes. The v4.3 plugin-only scope contract is unchanged.

Highlights

Row Source signal Effect
CHANGE Claude Code v2.1.126--dangerously-skip-permissions no longer prompts on writes to .claude/, .git/, .vscode/, and shell config files _is_plugin_author_write widens its allowlist to match. Closed POSIX/zsh shell-config set: .bashrc, .bash_profile, .profile, .zshrc, .zprofile, .zlogin, .zshenv. Destructive shell forms (rm -rf, chmod 777, sudo rm, eval(, exec(, DROP TABLE, TRUNCATE TABLE) on the same paths still dock.
CHANGE Three new Claude Code releases (v2.1.124/125/126) since v2.0.1 Rotate validate_marketplace.py audit comment block to most-recent five (v2.1.122–v2.1.126).

Explicitly NOT in the allowlist

.npmrc, .dockerrc, .aws/credentials, .fishrc — these remain unallowlisted by design. Verdict will dock writes to those files even under --dangerously-skip-permissions. The Anthropic v2.1.126 changelog text said "shell config files" without enumerating; verdict ships the conservative POSIX/zsh login set.

Tests

538 unittest cases (512 → 538; +26 from tests/test_safety_v2_1_126_paths.py + extended grep test). Benchmark gate green on the trimmed v2.0.0 corpus.

Migration

No action required. The allowlist widens (never narrows); existing transcripts continue to score identically except for plugin-author writes that previously false-positively docked.

See CHANGELOG.md §[2.0.2] for the full delta.