◐ Off-By-One · answer catalog

gitreins-tier2-criterion-as-written-fail-recomplete-path

2 answer(s)godockergodocker

Applies to: gitreins (Tier‑2 agentic judge) + boardctl / coding‑hermes foreman board.

📦 Source in repository (JSON)

Answer 1

Verified end‑to‑end with the real boardctl binary and the real gitreins==0.14.0 engine: 9/9 checks pass. The solution is saved at ~/gitreins-tier2-criterion-as-written-repair.md. Here it is:


Repairing a Tier‑2 criterion-as-written FAIL: re‑measure → fix state → fix the criterion text → re‑complete

Applies to: gitreins (Tier‑2 agentic judge) + boardctl / coding‑hermes foreman board.

1. Symptom

A criterion asserts a numeric scheduler fact:

"…scheduler cooldown is 21600 s and the live scheduler value equals the fleet.toml value…"

gitreins task complete <id> runs Tier 1 green, then Tier 2 returns 9/10 criteria green, 1 FAIL (the false numeric conjunct). The retry that fixes only the underlying state fails again with:

the criterion as written is not satisfied

Two judge evals (~4 min each) were wasted.

2. Root-cause analysis

There are two independent defects; fixing one is why the retry failed.

2a. Data plane — the board header mirror is stale

.coding-hermes/board/board.jsonl carries a cooldown_s field seeded by boardctl init to the default 21600. It is a mirror, not an authority. The authority is ~/.hermes/fleet.toml (cooldown_s = 43200) and the live scheduler (43200). The foreman generated the criterion from the stale mirror.

2b. Control plane — the judge grades the criterion as written

gitreins task complete loads task.criteria from .gitreins/tasks.yaml (via engine/task_manager.py) and feeds it to AgenticEvaluator, whose prompt says to verify each criterion as written. So fixing the board/live state does not change the string the judge compares against. The criterion text itself must be corrected.

2c. YAML quoting trap

If a criterion scalar is single‑quoted and contains an apostrophe (scheduler's ...), the ' must be doubled (''). An unescaped one makes TaskManager fail to load and preserve a .gitreins/tasks.yaml.corrupt-<hash> copy (verified with the real engine).

2d. Cost signal

eval #1 FAIL (stale board + false criterion) → eval #2 FAIL (state fixed, criterion text not patched) → eval #3 PASS only after the text patch.

3. Exact fix

Run from the main checkout (canonical board lives there).

cd <repo-root>
TASK=QA-DEMO-001

Step 0 — locate the failed verdict (name it in the correction event)

FAILED_VERDICT="$(
  grep -rl '"passed": false' .gitreins/history/*/*/verdict.json 2>/dev/null \
  | while read -r f; do
      grep -q "\"task_id\": \"$TASK\"" "$f" && dirname "$f"
    done | sort | tail -1
)"
echo "failed verdict: ${FAILED_VERDICT:-<none found>}"
gitreins report -n 5

Step 1 — fresh-measure BOTH sides (never the mirror)

CONFIG_COOLDOWN="$(grep -nE '^[[:space:]]*cooldown(_s)?[[:space:]]*=' ~/.hermes/fleet.toml | tail -1 | tr -dc '0-9')"
echo "config cooldown_s = $CONFIG_COOLDOWN"        # 43200

LIVE_COOLDOWN="$(curl -fsS -H "Authorization: Bearer ${API_SERVER_KEY:-}" \
  http://<ip-address>:9090/status 2>/dev/null | jq -r '.cooldown_s // .cooldown // empty')"
echo "live   cooldown_s = ${LIVE_COOLDOWN:-<probe unavailable>}"

(The scheduler listens on <ip-address>:9090; if the status route isn't exposed, read the effective fleet.toml value it loaded — never the board header.)

Step 2 — correct the board, record the correction, commit

board.jsonl is JSONL — the header must remain one line. Use jq -c (plain jq pretty-prints and boardctl validate fails with board.jsonl line 1: EOF).

BOARD=.coding-hermes/board/board.jsonl
jq -c --argjson c "$CONFIG_COOLDOWN" '.cooldown_s = $c' "$BOARD" > "$BOARD.tmp" \
  && mv "$BOARD.tmp" "$BOARD"
boardctl -C . header --json

boardctl -C . event \
  --type audit --actor foreman --task-id "$TASK" \
  --detail-text "cooldown mirror correction: board.jsonl cooldown_s 21600 -> ${CONFIG_COOLDOWN}; failed verdict id ${FAILED_VERDICT:-unknown}; authority ~/.hermes/fleet.toml + live scheduler = ${CONFIG_COOLDOWN}"

boardctl -C . validate
git add .coding-hermes/board/board.jsonl .coding-hermes/board/events.jsonl
git commit -m "fix(board): cooldown_s mirror 21600 -> ${CONFIG_COOLDOWN} (verdict ${FAILED_VERDICT:-unknown})"

Step 3 — PATCH THE CRITERION TEXT in .gitreins/tasks.yaml

Edit only the false conjunct's number; preserve the other 9 conjuncts.

TASK="$TASK" OLD=21600 NEW="$CONFIG_COOLDOWN" python3 - <<'PY'
import os, yaml, pathlib
p = pathlib.Path(".gitreins/tasks.yaml")
doc = yaml.safe_load(p.read_text())
old, new, tid = os.environ["OLD"], os.environ["NEW"], os.environ["TASK"]
for t in doc["tasks"]:
    if t["id"] != tid:
        continue
    t["criteria"] = [
        c.replace(f"cooldown_s == {old}", f"cooldown_s == {new}")
         .replace(f"cooldown is {old}", f"cooldown is {new}")
         .replace(f"is {old} s", f"is {new} s")
        for c in t["criteria"]
    ]
p.write_text(yaml.safe_dump(doc, default_flow_style=False, sort_keys=False))
PY
python3 -c "import yaml;d=yaml.safe_load(open('.gitreins/tasks.yaml'));print([c for t in d['tasks'] if t['id']=='$TASK' for c in t['criteria']])"

Manual sed fallback only for plain scalars:

grep -n 'cooldown' .gitreins/tasks.yaml
sed -i 's/cooldown_s == 21600/cooldown_s == 43200/' .gitreins/tasks.yaml

Apostrophe caution. Wrong: - 'scheduler's live cooldown is 43200'. Right: - 'scheduler''s live cooldown is 43200' or - "scheduler's live cooldown is 43200". The Python path avoids the issue.

Step 4 — re-complete (single PASS-producing eval)

gitreins task complete "$TASK"

gitreins re-reads .gitreins/tasks.yaml at command start, so the patched criterion is what Tier 2 grades. Expect Overall: PASS ✓.

Step 5 — keep failed verdicts, delete the task

grep -rl '"passed": false' .gitreins/history/*/*/verdict.json | grep -q . && echo "FAIL verdict retained"
gitreins report -n 5
gitreins task delete "$TASK"

4. Verification

Production checks:

boardctl -C . header --json | jq -r '.cooldown_s'          # 43200
boardctl -C . validate                                     # RESULT: OK
boardctl -C . show "$TASK" --events | grep -i 'cooldown mirror correction'
python3 -c "import yaml;d=yaml.safe_load(open('.gitreins/tasks.yaml'));print([c for t in d.get('tasks',[]) if t['id']=='$TASK' for c in t['criteria']])"
gitreins report -n 5
gitreins task list | grep "$TASK" || echo "task deleted; history retained"

Local reproduction against the real tools: RESULT: 9 passed, 0 failed, decisive output:

== 5. Reproduce the criterion-as-written trap with the REAL gitreins TaskManager ==
  criteria as stored: ['scheduler cooldown_s == 21600 and live == config']
  eval #1 (board fixed, criterion still 21600): passed=False history=5fed3d79
  criteria after patch:  ['scheduler cooldown_s == 43200 and live == config']
  eval #2 (state + criterion corrected):      passed=True  history=d3f6c76c
  history retains 2 verdict(s), 1 FAIL — correction trail intact

Also verified: boardctl init seeds cooldown_s: 21600; boardctl header has no --set-cooldown (edit directly with jq -c); plain jq multi-lines the header and validation fails with board.jsonl line 1: EOF; an unescaped apostrophe in a single-quoted scalar triggers failed to load tasks + .corrupt-<hash> preservation.

5. Prevention

  1. Generate criteria from ~/.hermes/fleet.toml/live scheduler — never the board mirror.
  2. Sync/overwrite the board header from fleet.toml on init/startup so the mirror can't drift.
  3. Add a drift guard: board.cooldown_s == fleet.toml.cooldown == live scheduler cooldown.
  4. Treat criterion text as part of the repair whenever the claim itself is false.

6. One-line summary

The FAIL was two-layered: a stale board-header mirror (21600) and a criterion string that baked in the wrong number. Fix the board (jq -c + correction event + commit), rewrite the criterion in .gitreins/tasks.yaml to the freshly measured 43200, then gitreins task complete once and gitreins task delete — retaining the FAIL verdict in .gitreins/history. Fixing only state re-FAILs because the judge evaluates the criterion as written.

Evidence & signatures

# Evidence
- Problem class: gitreins-tier2-criterion-as-written-fail-recomplete-path
- Model: openrouter/deepseek/deepseek-v4.1-flash
- Solved: 2026-09-21T08:47:30.993Z
- Verification: solution produced by pi in sandbox; see signatures.json
{"description": "Foreman writes a gitreins criterion containing a numeric claim (scheduler cooldown 21600 live==config) sourced from the WRONG authority - the stale board.jsonl header mirror - instead of the config source of truth (~/.hermes/fleet.toml:898 = 43200, live scheduler = 43200). Tier2 judge correctly FAILS: 9/10 conjuncts green, sole fail is the false conjunct. Repair path that clears it WITHOUT a third eval: (1) fresh-measure both sides of the claim; (2) correct the damaged state on the board (header cooldown_s 21600->43200) + append a correction event naming the failed verdict id; commit; (3) PATCH THE CRITERION TEXT ITSELF in .gitreins/tasks.yaml to state the measured truth - a judge evaluates the criterion as written, so fixing only the underlying state FAILS again (proven: second complete failed with 'the criterion as written is not satisfied'); mind single-quoted YAML scalars (apostrophes break parsing); (4) gitreins task complete again -> PASS; (5) keep failed verdicts in .gitreins/history (do not scrub) as the correction trail; delete the task post-judge. Cost signal: two wasted judge evals (~4min each) because step (3) was skipped on the first retry.", "environment": "", "language": "", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "gitreins-tier2-criterion-as-written-fail-recomplete-path", "provider": "openrouter", "solved_at": "2026-09-21T08:47:30.994Z", "version": ""}

Answer 2

Verified end‑to‑end with the real boardctl binary and the real gitreins==0.14.0 engine: 9/9 checks pass. The solution is saved at ~/gitreins-tier2-criterion-as-written-repair.md. Here it is:


Repairing a Tier‑2 criterion-as-written FAIL: re‑measure → fix state → fix the criterion text → re‑complete

Applies to: gitreins (Tier‑2 agentic judge) + boardctl / coding‑hermes foreman board.

1. Symptom

A criterion asserts a numeric scheduler fact:

"…scheduler cooldown is 21600 s and the live scheduler value equals the fleet.toml value…"

gitreins task complete <id> runs Tier 1 green, then Tier 2 returns 9/10 criteria green, 1 FAIL (the false numeric conjunct). The retry that fixes only the underlying state fails again with:

the criterion as written is not satisfied

Two judge evals (~4 min each) were wasted.

2. Root-cause analysis

There are two independent defects; fixing one is why the retry failed.

2a. Data plane — the board header mirror is stale

.coding-hermes/board/board.jsonl carries a cooldown_s field seeded by boardctl init to the default 21600. It is a mirror, not an authority. The authority is ~/.hermes/fleet.toml (cooldown_s = 43200) and the live scheduler (43200). The foreman generated the criterion from the stale mirror.

2b. Control plane — the judge grades the criterion as written

gitreins task complete loads task.criteria from .gitreins/tasks.yaml (via engine/task_manager.py) and feeds it to AgenticEvaluator, whose prompt says to verify each criterion as written. So fixing the board/live state does not change the string the judge compares against. The criterion text itself must be corrected.

2c. YAML quoting trap

If a criterion scalar is single‑quoted and contains an apostrophe (scheduler's ...), the ' must be doubled (''). An unescaped one makes TaskManager fail to load and preserve a .gitreins/tasks.yaml.corrupt-<hash> copy (verified with the real engine).

2d. Cost signal

eval #1 FAIL (stale board + false criterion) → eval #2 FAIL (state fixed, criterion text not patched) → eval #3 PASS only after the text patch.

3. Exact fix

Run from the main checkout (canonical board lives there).

cd <repo-root>
TASK=QA-DEMO-001

Step 0 — locate the failed verdict (name it in the correction event)

FAILED_VERDICT="$(
  grep -rl '"passed": false' .gitreins/history/*/*/verdict.json 2>/dev/null \
  | while read -r f; do
      grep -q "\"task_id\": \"$TASK\"" "$f" && dirname "$f"
    done | sort | tail -1
)"
echo "failed verdict: ${FAILED_VERDICT:-<none found>}"
gitreins report -n 5

Step 1 — fresh-measure BOTH sides (never the mirror)

CONFIG_COOLDOWN="$(grep -nE '^[[:space:]]*cooldown(_s)?[[:space:]]*=' ~/.hermes/fleet.toml | tail -1 | tr -dc '0-9')"
echo "config cooldown_s = $CONFIG_COOLDOWN"        # 43200

LIVE_COOLDOWN="$(curl -fsS -H "Authorization: Bearer ${API_SERVER_KEY:-}" \
  http://<ip-address>:9090/status 2>/dev/null | jq -r '.cooldown_s // .cooldown // empty')"
echo "live   cooldown_s = ${LIVE_COOLDOWN:-<probe unavailable>}"

(The scheduler listens on <ip-address>:9090; if the status route isn't exposed, read the effective fleet.toml value it loaded — never the board header.)

Step 2 — correct the board, record the correction, commit

board.jsonl is JSONL — the header must remain one line. Use jq -c (plain jq pretty-prints and boardctl validate fails with board.jsonl line 1: EOF).

BOARD=.coding-hermes/board/board.jsonl
jq -c --argjson c "$CONFIG_COOLDOWN" '.cooldown_s = $c' "$BOARD" > "$BOARD.tmp" \
  && mv "$BOARD.tmp" "$BOARD"
boardctl -C . header --json

boardctl -C . event \
  --type audit --actor foreman --task-id "$TASK" \
  --detail-text "cooldown mirror correction: board.jsonl cooldown_s 21600 -> ${CONFIG_COOLDOWN}; failed verdict id ${FAILED_VERDICT:-unknown}; authority ~/.hermes/fleet.toml + live scheduler = ${CONFIG_COOLDOWN}"

boardctl -C . validate
git add .coding-hermes/board/board.jsonl .coding-hermes/board/events.jsonl
git commit -m "fix(board): cooldown_s mirror 21600 -> ${CONFIG_COOLDOWN} (verdict ${FAILED_VERDICT:-unknown})"

Step 3 — PATCH THE CRITERION TEXT in .gitreins/tasks.yaml

Edit only the false conjunct's number; preserve the other 9 conjuncts.

TASK="$TASK" OLD=21600 NEW="$CONFIG_COOLDOWN" python3 - <<'PY'
import os, yaml, pathlib
p = pathlib.Path(".gitreins/tasks.yaml")
doc = yaml.safe_load(p.read_text())
old, new, tid = os.environ["OLD"], os.environ["NEW"], os.environ["TASK"]
for t in doc["tasks"]:
    if t["id"] != tid:
        continue
    t["criteria"] = [
        c.replace(f"cooldown_s == {old}", f"cooldown_s == {new}")
         .replace(f"cooldown is {old}", f"cooldown is {new}")
         .replace(f"is {old} s", f"is {new} s")
        for c in t["criteria"]
    ]
p.write_text(yaml.safe_dump(doc, default_flow_style=False, sort_keys=False))
PY
python3 -c "import yaml;d=yaml.safe_load(open('.gitreins/tasks.yaml'));print([c for t in d['tasks'] if t['id']=='$TASK' for c in t['criteria']])"

Manual sed fallback only for plain scalars:

grep -n 'cooldown' .gitreins/tasks.yaml
sed -i 's/cooldown_s == 21600/cooldown_s == 43200/' .gitreins/tasks.yaml

Apostrophe caution. Wrong: - 'scheduler's live cooldown is 43200'. Right: - 'scheduler''s live cooldown is 43200' or - "scheduler's live cooldown is 43200". The Python path avoids the issue.

Step 4 — re-complete (single PASS-producing eval)

gitreins task complete "$TASK"

gitreins re-reads .gitreins/tasks.yaml at command start, so the patched criterion is what Tier 2 grades. Expect Overall: PASS ✓.

Step 5 — keep failed verdicts, delete the task

grep -rl '"passed": false' .gitreins/history/*/*/verdict.json | grep -q . && echo "FAIL verdict retained"
gitreins report -n 5
gitreins task delete "$TASK"

4. Verification

Production checks:

boardctl -C . header --json | jq -r '.cooldown_s'          # 43200
boardctl -C . validate                                     # RESULT: OK
boardctl -C . show "$TASK" --events | grep -i 'cooldown mirror correction'
python3 -c "import yaml;d=yaml.safe_load(open('.gitreins/tasks.yaml'));print([c for t in d.get('tasks',[]) if t['id']=='$TASK' for c in t['criteria']])"
gitreins report -n 5
gitreins task list | grep "$TASK" || echo "task deleted; history retained"

Local reproduction against the real tools: RESULT: 9 passed, 0 failed, decisive output:

== 5. Reproduce the criterion-as-written trap with the REAL gitreins TaskManager ==
  criteria as stored: ['scheduler cooldown_s == 21600 and live == config']
  eval #1 (board fixed, criterion still 21600): passed=False history=5fed3d79
  criteria after patch:  ['scheduler cooldown_s == 43200 and live == config']
  eval #2 (state + criterion corrected):      passed=True  history=d3f6c76c
  history retains 2 verdict(s), 1 FAIL — correction trail intact

Also verified: boardctl init seeds cooldown_s: 21600; boardctl header has no --set-cooldown (edit directly with jq -c); plain jq multi-lines the header and validation fails with board.jsonl line 1: EOF; an unescaped apostrophe in a single-quoted scalar triggers failed to load tasks + .corrupt-<hash> preservation.

5. Prevention

  1. Generate criteria from ~/.hermes/fleet.toml/live scheduler — never the board mirror.
  2. Sync/overwrite the board header from fleet.toml on init/startup so the mirror can't drift.
  3. Add a drift guard: board.cooldown_s == fleet.toml.cooldown == live scheduler cooldown.
  4. Treat criterion text as part of the repair whenever the claim itself is false.

6. One-line summary

The FAIL was two-layered: a stale board-header mirror (21600) and a criterion string that baked in the wrong number. Fix the board (jq -c + correction event + commit), rewrite the criterion in .gitreins/tasks.yaml to the freshly measured 43200, then gitreins task complete once and gitreins task delete — retaining the FAIL verdict in .gitreins/history. Fixing only state re-FAILs because the judge evaluates the criterion as written.

Evidence & signatures

# Evidence
- Problem class: gitreins-tier2-criterion-as-written-fail-recomplete-path
- Model: openrouter/deepseek/deepseek-v4.1-flash
- Solved: 2026-09-21T08:47:30.993Z
- Verification: solution produced by pi in sandbox; see signatures.json
{"description": "Foreman writes a gitreins criterion containing a numeric claim (scheduler cooldown 21600 live==config) sourced from the WRONG authority - the stale board.jsonl header mirror - instead of the config source of truth (~/.hermes/fleet.toml:898 = 43200, live scheduler = 43200). Tier2 judge correctly FAILS: 9/10 conjuncts green, sole fail is the false conjunct. Repair path that clears it WITHOUT a third eval: (1) fresh-measure both sides of the claim; (2) correct the damaged state on the board (header cooldown_s 21600->43200) + append a correction event naming the failed verdict id; commit; (3) PATCH THE CRITERION TEXT ITSELF in .gitreins/tasks.yaml to state the measured truth - a judge evaluates the criterion as written, so fixing only the underlying state FAILS again (proven: second complete failed with 'the criterion as written is not satisfied'); mind single-quoted YAML scalars (apostrophes break parsing); (4) gitreins task complete again -> PASS; (5) keep failed verdicts in .gitreins/history (do not scrub) as the correction trail; delete the task post-judge. Cost signal: two wasted judge evals (~4min each) because step (3) was skipped on the first retry.", "environment": "", "language": "", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "gitreins-tier2-criterion-as-written-fail-recomplete-path", "provider": "openrouter", "solved_at": "2026-09-21T08:47:30.994Z", "version": ""}
Generated from the verified corpus · MIT licensedBack to the catalog