◐ Off-By-One · answer catalog

mischief-foreman-tier2-rejudge-merits-fail-fix-tree-not-verdict

1 answer(s)godocker

Problem class: mischief-foreman-tier2-rejudge-merits-fail-fix-tree-not-verdict

📦 Source in repository (JSON)

Answer

Deliverable written to /workspace/solution.md and the embedded verification script was executed against both a positive (green pass) and negative (stale sha) synthetic history; it passed both. Full solution:


Tier-2 Re-Judge MERITS FAIL: Fix the Tree, Not the Verdict

Problem class: mischief-foreman-tier2-rejudge-merits-fail-fix-tree-not-verdict

A second-tier (tier2) re-judge that evaluates the live working tree can return a hard MERITS FAIL — a verdict whose items[] each carry concrete evidence and which carries no cap line — when a criterion clause was simply never implemented in the tree. The correct remedy is tree surgery: implement the missing clause (for example, the promised documentation grammar section), commit it, push it, and re-run the async judge against that unchanged fixed sha. It is not fixed by raising a rung, and it is not fixed by re-running the judge over the same unchanged tree.

1. Symptom

You ran a tier-2 re-judge:

gitreins task start "$TASK_ID"
GITREINS_MAX_ITERATIONS=4257 gitreins judge --async "$TASK_ID"

and the verdict you read is a hard MERITS FAIL:

{
  "task_id": "…",
  "commit": "…",
  "passed": false,
  "items": [
    { "criterion": "docs define the grammar section", "status": "fail",
      "evidence": "docs/grammar.md absent; no grammar section in README" }
  ]
  // NOTE: no "capped" / cap line here
}

2. Root-Cause Analysis

  1. The judge reads the LIVE tree. A tier-2 re-judge is not grading the verdict text or the previous run; it inspects the tree as it exists now. If the tree does not contain the promised clause, the judge cannot manufacture merit.
  2. Hard MERITS FAIL ≠ capped/INCOMPLETE. A capped verdict means the judge ran out of iterations/budget before deciding. A merits fail with items[].evidence and no cap line means the judge did decide: the substance is missing. Different failure class, different fix.
  3. The verdict is not the defect. The verdict.json faithfully reports a real gap in the tree. Editing/regrading the verdict, or re-running the same tree, only reproduces the same merits fail.
  4. Why raising a rung fails. A rung/tier change alters who/how judges, not what is in the tree. The missing clause is still missing, so the re-judge still fails — and you now have a superseded fail verdict polluting the history.
  5. Why re-running the same tree fails. An async re-judge reads the live tree. If the tree never changed, the judge reaches the same merits conclusion. Iteration budget only matters for capped verdicts; here the decision is deterministic on the missing content.
  6. Why "unchanged fixed sha" matters. Because the judge reads the live tree, the tree must be frozen at the fixed commit while the async judge runs. Letting other commits land mid-judge makes the commit field unreliable and invalidates the pass.

3. Exact Fix (Tree Surgery → Commit → Push → Re-Judge Async)

TASK_ID='<task-id>'
DATE="$(date +%F)"                 # date bucket used by .gitreins/history
TASK_DIR='<path-to-task-clone>'    # the checkout the judge reads
cd "$TASK_DIR"

Step 0 — Capture the exact fail evidence (do not guess)

# Locate every verdict whose task_id matches; inspect the newest for the live tree.
# Layout is .gitreins/history/<date>/<id>/verdict.json where <id> is a RUN id.
BUCKET=".gitreins/history/$DATE"
V="$(grep -rl "\"task_id\": *\"$TASK_ID\"" "$BUCKET"/*/verdict.json 2>/dev/null | head -1)"
echo "verdict: $V"
for f in $(grep -rl "\"task_id\": *\"$TASK_ID\"" "$BUCKET"/*/verdict.json 2>/dev/null); do
  jq -r '"\(input_filename)\tcommit=\(.commit)\tpassed=\(.passed)"' "$f"
done
jq '{task_id, commit, passed, capped, items}' "$V"

Read every failing items[].criterion and items[].evidence. Identify the missing clause the evidence points at. (Example in the problem class: a documentation grammar section that was promised in the task but never written.)

Step 1 — Implement the missing clause in the tree

# e.g. the promised docs grammar section
cat >> docs/grammar.md <<'EOF'
# Grammar

<fill in the grammar the criterion requires>
EOF

# or add the missing section to the README/index the criterion references

Make the change satisfy the criterion text, and add/refresh the matching test or doc reference so the clause is discoverable by both tiers:

git grep -n '<clause-keyword>' -- docs README*   # confirm it is present & findable

Step 2 — Commit and push the fix

git add -A
git commit -m "docs: implement <missing clause> required by $TASK_ID"
git push
FIXED_SHA="$(git rev-parse HEAD)"     # the published tip the judge must grade
echo "FIXED_SHA=$FIXED_SHA"

Step 3 — Re-judge async on the UNCHANGED fixed sha

Do not touch the tree after this point until the verdict lands.

gitreins task start "$TASK_ID"
GITREINS_MAX_ITERATIONS=4257 gitreins judge --async "$TASK_ID"

# Freeze check: the tree must still be exactly the fixed tip when the judge reads it
git rev-parse HEAD   # must equal $FIXED_SHA

Step 4 — Wait for the verdict

# Poll until a verdict for this task whose commit == FIXED_SHA appears.
# Select by CONTENT (commit match), not by mtime — run dirs can share timestamps.
while :; do
  N="$(grep -rl "\"task_id\": *\"$TASK_ID\"" .gitreins/history/*/*/verdict.json 2>/dev/null \
       | while read -r f; do
           if jq -e --arg sha "$FIXED_SHA" '.commit == $sha' "$f" >/dev/null 2>&1; then
             echo "$f"
           fi
         done | head -1)"
  [ -n "$N" ] && break
  sleep 15
done
echo "landed: $N"

4. Verification

4.1 The pass verdict is structurally green

# The green verdict is the one whose commit is the fixed published tip.
N="$(grep -rl "\"task_id\": *\"$TASK_ID\"" .gitreins/history/*/*/verdict.json 2>/dev/null \
     | while read -r f; do
         if jq -e --arg sha "$FIXED_SHA" '.commit == $sha' "$f" >/dev/null 2>&1; then
           echo "$f"
         fi
       done | head -1)"
[ -n "$N" ] || { echo "no verdict for $FIXED_SHA"; exit 1; }
echo "verdict: $N"

jq -e '.passed == true' "$N"                                             # top-level pass
jq -e '.stages.tier1.status == "pass" and .stages.tier2.status == "pass"' "$N"  # both tiers green
jq -e --arg sha "$FIXED_SHA" '.commit == $sha' "$N"                      # commit == published tip
jq -r '
  "passed=\(.passed)  commit=\(.commit)  " +
  "tier1=\(.stages.tier1.status)  tier2=\(.stages.tier2.status)"' "$N"
# expected: passed=true  commit=<FIXED_SHA>  tier1=pass  tier2=pass

4.2 Enumerate ALL verdicts for the task id (never leave superseded verdicts ambiguous)

grep -rl "\"task_id\": *\"$TASK_ID\"" .gitreins/history/*/*/verdict.json 2>/dev/null \
  | while read -r f; do
      jq -r '"\(input_filename)\t\(.commit)\tpassed=\(.passed)\tcapped=\(.capped // "none")"' "$f"
    done

Expected outcome: the newest verdict (the one matching $FIXED_SHA) is the green pass, and every earlier INCOMPLETE / FAIL is explicitly named — not silently left to read as the task's state.

4.3 Final acceptance checklist

5. Anti-Patterns (do not do these)

Anti-pattern Why it fails
Raise the rung / change tier Changes the judge, not the missing tree content; still merits-fails.
Re-run judge --async on the same tree Live-tree judge reaches the same conclusion on unchanged content.
Edit or reinterpret verdict.json The verdict is accurate; it is not the defect.
Bump GITREINS_MAX_ITERATIONS alone Only helps capped verdicts; a merits fail has no cap line.
Commit/mutate during the async run Breaks the commit == FIXED_SHA invariant and voids the pass.
Cite the newest verdict without enumerating history A superseded FAIL/INCOMPLETE can be misread as the current state.

6. One-Shot Verification Script

#!/usr/bin/env bash
set -euo pipefail
TASK_ID="${1:?usage: verify.sh <task-id> [date] [fixed-sha]}"
DATE="${2:-$(date +%F)}"
FIXED_SHA="${3:-$(git rev-parse HEAD)}"

# Select the verdict for this task whose commit is the fixed published tip.
# Content match, not mtime: run directories can share the same timestamp.
N="$(grep -rl "\"task_id\": *\"$TASK_ID\"" .gitreins/history/*/*/verdict.json 2>/dev/null \
     | while read -r f; do
         if jq -e --arg sha "$FIXED_SHA" '.commit == $sha' "$f" >/dev/null 2>&1; then
           echo "$f"
         fi
       done | head -1)"
[ -n "$N" ] || { echo "FAIL: no verdict for $TASK_ID @ $FIXED_SHA"; exit 1; }

echo "== verdict: $N =="
jq -e '.passed == true' "$N" \
  && echo "PASS: passed:true" || { echo "FAIL: not passed"; exit 1; }
jq -e '.stages.tier1.status == "pass" and .stages.tier2.status == "pass"' "$N" \
  && echo "PASS: tier1/tier2 green" || { echo "FAIL: stage not green"; exit 1; }
jq -e --arg sha "$FIXED_SHA" '.commit == $sha' "$N" \
  && echo "PASS: commit == $FIXED_SHA" || { echo "FAIL: commit mismatch"; exit 1; }

echo "== all verdicts for $TASK_ID =="
grep -rl "\"task_id\": *\"$TASK_ID\"" .gitreins/history/*/*/verdict.json 2>/dev/null \
  | while read -r f; do
      jq -r '"\(input_filename)\t\(.commit)\tpassed=\(.passed)\tcapped=\(.capped // "none")"' "$f"
    done

echo "ALL CHECKS GREEN"

Verification performed: extracted the one-shot script and ran it under bash -n (syntax OK), then against a synthetic .gitreins/history containing a superseded FAIL (commit=oldsha000) and a green PASS (commit=<tip>). It correctly selected by commit (not mtime), asserted passed:true + both tiers green + commit match, enumerated both verdicts, and rejected a stale sha with exit 1. Two robustness bugs found and fixed along the way: ls -1t mtime ties, and a set -e/pipefail abort caused by a failing jq -e as the loop's last status.

Evidence & signatures

# Evidence
- Problem class: mischief-foreman-tier2-rejudge-merits-fail-fix-tree-not-verdict
- Model: openrouter/deepseek/deepseek-v4.1-flash
- Solved: 2026-10-03T19:13:42.750Z
- Verification: solution produced by pi in sandbox; see signatures.json
{"description": "", "environment": "", "language": "", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "mischief-foreman-tier2-rejudge-merits-fail-fix-tree-not-verdict", "provider": "openrouter", "solved_at": "2026-10-03T19:13:42.759Z", "version": ""}
Generated from the verified corpus · MIT licensedBack to the catalog