fix(janitor): require completion evidence before auto-closing on completed_at#2
Conversation
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughA new kanban janitor script scans Hermes SQLite task boards to classify tasks by completion evidence, staleness, and redundancy. It detects safe auto-close candidates, optionally applies decisions within a transaction with DB backup, generates JSON and Markdown reports, and discovers cron scheduler metadata. Tests validate evidence detection and integration with the main scan function. ChangesKanban Janitor Task Cleanup
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
🚨 CRITICAL Supply Chain Risk DetectedThis PR contains a pattern that has been used in real supply chain attacks. A maintainer must review the flagged code carefully before merging. 🚨 CRITICAL: Install-hook file added or modifiedThese files can execute code during package installation or interpreter startup. Files: Scanner only fires on high-signal indicators: .pth files, base64+exec/eval combos, subprocess with encoded commands, or install-hook files. Low-signal warnings were removed intentionally — if you're seeing this comment, the finding is worth inspecting. |
🔎 Lint report:
|
| Rule | Count |
|---|---|
invalid-argument-type |
3 |
First entries
run_agent.py:7736: [invalid-argument-type] invalid-argument-type: Argument to function `build_anthropic_client` is incorrect: Expected `str`, found `str | dict[Unknown | str, Unknown | str | dict[str, str]] | Any | ... omitted 3 union elements`
run_agent.py:14031: [invalid-argument-type] invalid-argument-type: Argument to function `_is_oauth_token` is incorrect: Expected `str`, found `str | dict[Unknown | str, Unknown | str | dict[str, str]] | Any | ... omitted 3 union elements`
run_agent.py:14034: [invalid-argument-type] invalid-argument-type: Argument to function `len` is incorrect: Expected `Sized`, found `(str & ~AlwaysFalsy) | (dict[Unknown | str, Unknown | str | dict[str, str]] & ~AlwaysFalsy) | (Any & ~AlwaysFalsy) | ... omitted 3 union elements`
✅ Fixed issues (3):
| Rule | Count |
|---|---|
invalid-argument-type |
3 |
First entries
run_agent.py:14031: [invalid-argument-type] invalid-argument-type: Argument to function `_is_oauth_token` is incorrect: Expected `str`, found `str | dict[Unknown, Unknown] | Any | ... omitted 3 union elements`
run_agent.py:14034: [invalid-argument-type] invalid-argument-type: Argument to function `len` is incorrect: Expected `Sized`, found `(str & ~AlwaysFalsy) | (dict[Unknown, Unknown] & ~AlwaysFalsy) | (Any & ~AlwaysFalsy) | ... omitted 3 union elements`
run_agent.py:7736: [invalid-argument-type] invalid-argument-type: Argument to function `build_anthropic_client` is incorrect: Expected `str`, found `str | dict[Unknown, Unknown] | Any | ... omitted 3 union elements`
Unchanged: 4352 pre-existing issues carried over.
Diagnostics are surfaced as warnings — this check never fails the build.
|
…leted_at
The kanban janitor is a SECOND phantom-done path. Its
explicit_completion_evidence() completed_at_present branch returned a
CLOSE decision purely because completed_at was set — with NO requirement
of a real result/summary/comment. build_scan() collected it and
apply_closes() raw-UPDATEd status='done', stamping a tautological
placeholder ("task has completed_at but non-terminal status") as the
task's only "evidence". A brief worker claim that stamps completed_at on
a fresh ready/triage/in_progress task got rubber-stamped to done.
Live proof: task t_5dbfc384 carries a janitor_completed event closing it
~13 min after creation on completed_at_present with result_len=0; it had
to be manually reopened and is now correctly in_review with a real
result.
This is the same pathology hermes-agent PR #1 fixed for complete_task()
(CompletionEvidenceError) — but the janitor bypasses complete_task
entirely with a raw UPDATE.
Changes (scripts/kanban_janitor.py):
- Add has_completion_evidence(task, comments): True iff non-empty result
OR any non-empty comment body. A bare completed_at is NOT evidence.
Mirrors kanban_db._has_completion_evidence (janitor has its own
dataclasses so it needs a local copy of the predicate).
- explicit_completion_evidence(): the completed_at_present close decision
is now gated on has_completion_evidence; evidence-free completed_at
tasks fall through (return None -> not closed).
- build_scan(): new phantom_completed_at anomaly category (capped 50)
for non-terminal, non-running tasks with completed_at but no evidence.
- render_markdown(): new "Phantom completed_at" triage section so
operators see the anomaly instead of it being silently rubber-stamped.
- apply_closes() unchanged — once evidence-free closes stop being
emitted, it naturally never closes them.
The janitor was previously an unversioned loose script at
~/.hermes/scripts/kanban_janitor.py; this commit brings it under version
control. Operator deploy step (Mac-gated): after merge, sync
~/.hermes/scripts/kanban_janitor.py from the repo copy.
Confidence: high
Scope-risk: moderate
Machine: orion-terminal
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
b22a106 to
fe490ef
Compare
Root cause
The kanban janitor is a second phantom-done path.
explicit_completion_evidence()(thecompleted_at_presentbranch) returned a CLOSE decision purely becausecompleted_atwas set on a non-terminal task — with no requirement of realresult/summary/comment evidence:build_scan()collected it andapply_closes()raw-UPDATEdstatus='done', stamping a tautological placeholder ("task has completed_at but non-terminal status") as the task's only "evidence". A freshready/triage/in_progresstask that merely hadcompleted_atstamped by a brief worker claim got rubber-stamped todone.Live proof —
t_5dbfc384Task
t_5dbfc384carries ajanitor_completedevent closing it ~13 min after creation:The
completedevent right before it hadresult_len: 0— no real evidence. It had to be manually reopened and is now correctlyin_reviewwith a substantive result.Why this is the 2nd path
This is the same pathology PR #1 fixed for
complete_task()(CompletionEvidenceError). But the janitor bypassescomplete_taskentirely with a rawUPDATE status='done', so the PR #1 guard never fired here. This PR is the complement: it closes the janitor path.Previously unversioned
The live janitor was a loose script at
~/.hermes/scripts/kanban_janitor.py, not in any git repo. This PR brings it under version control atscripts/kanban_janitor.py(verbatim copy of the live script as the starting point) and applies the fix to the repo copy.The fix (
scripts/kanban_janitor.py)has_completion_evidence(task, comments)— True iff non-emptyresult(after strip) OR any comment with a non-empty stripped body. A barecompleted_attimestamp is NOT evidence. Mirrorskanban_db._has_completion_evidence; the janitor uses its ownTask/Commentdataclasses so it needs a local copy of the predicate.explicit_completion_evidence()— thecompleted_at_presentclose decision is now gated onhas_completion_evidence(...). Evidence-freecompleted_attasks fall through (returnNone→ not closed). Comment-pattern / trusted-operator checks still run normally.build_scan()— newphantom_completed_atanomaly category (capped at 50): non-terminal, non-runningtasks withcompleted_atset but no evidence.render_markdown()— new "Phantom completed_at" triage section so operators see the anomaly instead of it being silently rubber-stamped.apply_closes()unchanged — onceexplicit_completion_evidencestops emitting evidence-free closes,apply_closesnaturally never closes them.Tests (
tests/scripts/test_kanban_janitor_completion_evidence.py)10 tests, all passing:
has_completion_evidence: result-only True, comment-only True, whitespace-only False, nothing False.explicit_completion_evidence: barecompleted_atno-evidence →None; with result →("completed_at_present", ...); with comment →("completed_at_present", ...); whitespace-only →None.build_scan-level: a fixture DB with a bare-completed_atno-evidence task → it appears inphantom_completed_atand NOT inclose_decisions; a real-evidence task → inclose_decisions, not flagged.Verification
python3 -m py_compile scripts/kanban_janitor.py→ OKpytest tests/scripts/test_kanban_janitor_completion_evidence.py→ 10 passed--apply) against a read-only snapshot of the live kanban → 0 evidence-freecompleted_at_presentcloses,applied: 0.t_5dbfc384-style phantom task → task flagged inphantom_completed_at, absent fromclose_decisions, rendered in the markdown triage section. Pre-fix it would have been auto-closed.Operator deploy step (Mac-gated)
After merge, sync the live janitor from the repo copy — deployment is an operator step, the live
~/.hermes/scripts/file was intentionally left untouched by this PR:🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Tests