- **Restore 82 completed tasks** from tasks/complete/ back to tasks/ top level (all <7 days old per the cleanup policy; premature bulk archive was fixed). - **Dashboard: fix scroll-reset on auto-refresh** — renderBoard rebuilds the board via innerHTML every 2s, destroying each column-body's scrollTop. Now snapshots column-body scrollTop + board.scrollLeft + view.scrollTop before rebuild and restores after (matched by PHASE_GROUPS index). - **Dashboard UI additions** (pre-existing unstaged work): approval section cards, transition buttons, inline artifact editor (textarea for writing missing SPEC/VERDICT/etc from the detail modal). - **Bind ornith as Implement model** — config.md: Model explicit to omlx/Ornith-1.0-35B-4bit-mlx, context window 32768. Interactive autopilot already used ornith via opencode default; now explicit. - **Fix cleanup stub** — automaton-cleanup.sh had a stale --project arg pointing at a pytest temp dir (test isolation leak). Rewired to point at ~/.automaton. - **Fix plist-isolation test** — test asserted host plist doesn't exist, but a real install creates it. Now snapshots mtime before run, asserts unchanged after (only a write during the test counts as bleed). - **New Playwright smoke test** (tests/test_dashboard_ui.py) — 2 tests: board renders tasks, column scroll survives auto-refresh tick. Verified the test fails without the scroll fix (scrollTop resets to 0). Skipped via importorskip when playwright is absent (main CI stays green). - **Clarify SI loop scope in README** — new-project onboarding section documents the framework-scoped self-improvement loop and options (leave/pause/create project loop). - **CHANGELOG** documents all changes including the known model-divergence gap (mde tasks marked complete but per-role model binding was never implemented).
30 lines
2.7 KiB
Markdown
30 lines
2.7 KiB
Markdown
# Bug Report: task-status-reason
|
|
|
|
## Summary
|
|
Audit of status_reason implementation against SPEC.md found minor issues: a dead-code branch in the status_reason message, and a pre-existing REFEREE state that's never produced.
|
|
|
|
## Bugs Found
|
|
|
|
### Bug 1: Dead message branch in status_reason for BLOCKED state
|
|
- **Severity**: Low
|
|
- **Location**: `automaton/dashboard/core/task.py:149`
|
|
- **Description**: The message `"Verdict is empty or could not be parsed"` has two scenarios:
|
|
1. Empty verdict → reachable (content is `""`, correctly returns this message)
|
|
2. Unparseable verdict → unreachable. When `parse_verdict_status()` returns `None`, the state machine in `determine_task_state()` doesn't classify the task as BLOCKED — it falls through to earlier state checks. So a task is never both BLOCKED and "could not be parsed".
|
|
- **Suggested Fix**: Change message to `"Verdict is empty"` to accurately reflect the only reachable case.
|
|
|
|
### Bug 2: REFEREE state never produced by state machine
|
|
- **Severity**: Low
|
|
- **Location**: `automaton/dashboard/core/task.py:174` (fallback `status_reason` line), and `task.py:226-272` (determine_task_state)
|
|
- **Description**: `TaskState.REFEREE` exists in the enum and in the sort order, but `determine_task_state` never returns it. When a VERDICT.md is present but unparseable, the state machine falls through to earlier artifact checks instead of assigning REFEREE. This means the fallback status reason `"In referee phase"` is unreachable, and tasks with ambiguous verdicts silently show as earlier states (e.g., RESEARCH if only SPEC.md exists alongside an unparseable VERDICT.md).
|
|
- **Pre-existing**: This predates the task and is not introduced by the implementation, but the status_reason property exposes it because the REFEREE branch is dead code.
|
|
- **Suggested Fix**: Add `if "VERDICT.md" in artifacts: return TaskState.REFEREE, artifacts` after the terminal-state checks in `determine_task_state()`, before the state machine fallthrough.
|
|
|
|
### Bug 3: REFEREE status reason is generic
|
|
- **Severity**: Low
|
|
- **Location**: `automaton/dashboard/core/task.py:175`
|
|
- **Description**: The fallthrough line `return f"In {self.state.value} phase"` would produce generic messages like `"In blocked phase"` or `"In done phase"` for states that are handled above it. This is actually dead code for all defined states since every `TaskState` value is covered by an explicit `if` branch. If a new state is added without adding a status_reason handler, it gets a generic message rather than failing loudly.
|
|
- **Suggested Fix**: Replace the fallthrough with a clear signal: either raise an error, or explicitly list the catch to alert developers when adding states.
|
|
|
|
## Score
|
|
+5 |