Skip to content

make-hera-plan-living: status-trust + plan-mutation verbs + DAG-authoritative docs (BREAKING hera_send)#795

Merged
anutron merged 14 commits into
masterfrom
argus/feat-hera-living-plan
Jun 25, 2026
Merged

make-hera-plan-living: status-trust + plan-mutation verbs + DAG-authoritative docs (BREAKING hera_send)#795
anutron merged 14 commits into
masterfrom
argus/feat-hera-living-plan

Conversation

@anutron

@anutron anutron commented Jun 21, 2026

Copy link
Copy Markdown
Collaborator

Makes the hera role-status + plan-DAG a continuously-reconciled, living source of truth — the fix for why the plan-DAG goes stale in practice (dogfooding feedback). Built in 3 phases on one branch.

⚠️ BREAKING + deploy step

  • Breaking: worker/freelance hera_send now requires a status arg. Existing coordination flows must pass status. (Repo policy: breaking changes are fine, single user.)
  • DB migration (existing ~/.argus/data.sql only): the new failed status widens a SQLite CHECK that can't be ALTERed. Before deploy, daemon stopped, run script/migrate-hera-failed-status.sh (backs up data.sql, rebuilds hera_role_status preserving rows). Fresh DBs need nothing.

Phase A — status trust (the "dead DAG" fix)

Worker hera_send applies a required status synchronously, so a finishing worker actually advances the gater (previously the done-signal lived in prose). New failed role-status (rail red ✕). Gater re-arms on blocker recovery + one-time "unblocked" notice.

Phase B — create-only → living

New coordinator verbs hera_plan_node_update / hera_unblock / hera_plan_node_cancel (MCP surface 12→15). Cancelled nodes render grey ✕ and stop gating; dependents proceed.

Phase C — docs/authority

hera SKILL states the plan-DAG is the source of truth with a live coordinator binding (harness TaskCreate defers). README/gotchas updated; required hera_send status + verbs + failed status documented.

Quality

  • ralph-review (2 parallel fresh-context reviewers, whole change): CLEAN — 0 auto-fix, HIGH regression confidence (report in .claude/reviews/2026-06-21/).
  • spec-audit (change-scoped): all 9 behaviors delta-backed, all 8 delta requirements implemented+tested.
  • golangci-lint v2.11.4 --new-from-rev → 0 issues; build + full test surface green; openspec validate make-hera-plan-living --strict valid. Rebased clean onto current master (feature-only, 39 files).

Parked follow-ups (non-blocking)

  1. Reviewer's /doitright suggestion: drop the redundant hera_role_status.status CHECK (Go already validates the enum) so future fresh DBs never hit the migration trap — left for author sign-off, not applied.
  2. Reopen-via-send doesn't clear ready_to_close (rail can briefly show review-✓ over a reopened worker) — ~5-line follow-up.

🤖 Generated with Claude Code

anutron and others added 14 commits June 24, 2026 21:15
Living plan-DAG: required synchronous send-status (+failed role-status), gater re-arm + recovery notice, plan-mutation verbs (update/unblock/cancel), DAG-authoritative skill. Adopt + observed-reopen + wedge-notice cut per devil's-advocate. 3 phases, each its own PR.
…se, rail red ✕)

- schema.go: widen hera_role_status.status CHECK to include 'failed'
- db/hera.go: add HeraStatusFailed constant; refactor shared
  rollHeraWorkerToReviewInner (bool stampReadyToClose param); new
  RollHeraWorkerFailed (in_review, no ready_to_close stamp)
- mcp/hera.go: toolHeraStatus accepts 'failed' (error names all 5 values);
  HeraStore interface adds RollHeraWorkerFailed; worker reporting 'failed'
  routes to RollHeraWorkerFailed (D2)
- widget/rolestatusicon.go: RoleStatusInputs.Failed field + '✕' red case in
  RoleStatusIcon, precedence below NeedsInput above Done
- tui/hera/rail.go: roleStatusInputs sets Failed from HeraStatusFailed
- Tests: hera_failed_test.go (db + mcp), widget + rail tests updated/extended

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Extract applyRoleStatus helper (DRY): hera_send and hera_status now share
  one path for status validation, upsert, meta mirror, and worker task roll
  (done→in_review+ready_to_close, failed→in_review/no-ready_to_close)

- hera_send gains optional status param; REQUIRED for worker/freelance senders:
  missing status returns a hard error naming all five valid values and sends
  nothing; status is applied synchronously before message send (D1); soft-fail
  on apply error never blocks the send; coordinator status remains optional

- Update hera_send tool description and schema to document the requirement

- Update all existing worker hera_send call sites in tests to include status

- New hera_send_status_test.go: 7 tests covering worker-no-status rejection,
  freelance rejection, status=working applied synchronously, status=done rolls
  task to in_review+ready_to_close, status=failed rolls to in_review without
  ready_to_close, coordinator no-status succeeds, delivery-independent apply

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
…notice

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
…gater cancelled-is-non-gating

- schema.go: add cancelled_at TEXT + idx_hera_roles_cancelled to hera_roles; add
  idempotent ALTER TABLE hera_roles ADD COLUMN cancelled_at TEXT migration
  (same pattern as nuked_at/base_branch) so pre-existing DBs migrate automatically
- hera.go: add CancelledAt *time.Time to HeraRole struct; update scanHeraRole to
  scan 11 columns (cancelled_at last); update all five SELECT column lists on
  hera_roles to include cancelled_at (ListHeraRoles, ListHeraRolesByKind,
  heraRoleByID, heraRoleByActiveName)
- hera_plan.go: ListHeraPlannedNodes adds AND cancelled_at IS NULL; add
  RemoveHeraBlock (idempotent DELETE), UpdateHeraPlannedNode (prompt + optional
  project, preserves project on empty string), CancelHeraPlannedNode (COALESCE
  idempotent cancelled_at stamp); uxlog via slog on each mutation
- heragater.go: blockerOutcome checks CancelledAt != nil FIRST, returning
  blockerDone — a cancelled planned blocker is treated as satisfied so its
  dependents can materialize; placed before the never-bound/working inference
  to prevent a cancelled never-bound node from blocking dependents forever

Tests: 11 new db tests (RemoveHeraBlock x3, UpdateHeraPlannedNode x2,
CancelHeraPlannedNode x4, migration round-trip); 3 new gater tests
(CancelledNodeNeverMaterializes, DependentOfCancelledBlockerBecomesReady,
CancelledBlockerAmongMultiple); migration test extended with cancelled_at column

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
… MCP verbs

Three coordinator-only plan-mutation verbs (D5, make-hera-plan-living):

- hera_plan_node_update: edits prompt/project on a planned node; rejects
  materialized nodes (HeraRoleHasBinding guard); requires at least one of
  prompt or project.
- hera_unblock: drops a hera_blocks edge; idempotent on missing edges.
- hera_plan_node_cancel: stamps cancelled_at; excludes from gater; keeps
  role visible for plan rendering; rejects materialized nodes.

All three are guarded by heroPlanCoordinatorGuard (non-coordinator rejected).
Adds HeraRoleHasBinding, UpdateHeraPlannedNode, CancelHeraPlannedNode, and
RemoveHeraBlock to the HeraStore interface. Updates heraToolDefs (12→15) and
the tools/list assertion. 11 new tests cover all scenarios from the spec.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
UpdateHeraPlannedNode built a static SET clause for the project!='' branch
that always included prompt=?, overwriting the stored prompt with empty when
the caller only supplied a project.  Now the SET clause is built dynamically:
prompt=? and argus_project=? are appended only when the respective input is
non-empty, so each field is preserved independently.

New db-level tests assert:
- project-only update preserves existing prompt (regression case)
- both-fields update sets both

Strengthened MCP-level TestHeraPlanNodeUpdate_EditsProject to assert the
prompt survived the project-only edit.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Add StateCancelled to planview.State enum with grey ✕ glyph (ColorDimmed)
  and distinct style from StateFailed (red ✕, bold). Updates all three switch
  statements (Glyph/Label/style) and the groupCounts legend list.
- Add Cancelled bool to RoleView; set from role.CancelledAt != nil in
  buildRoleView alongside the existing Planned discriminator.
- planNodeState: check Cancelled before Planned so cancelled nodes render
  StateCancelled (grey ✕) even when they were never materialized.
- planNodeIcon: return nil for StateCancelled (State overlay handles rendering).
- README: document hera_plan, hera_plan_node, hera_block, hera_plan_node_update,
  hera_unblock, hera_plan_node_cancel in the Reference MCP tools table.
- Tests: render_test.go covers glyph/label/style distinctness + chip draw +
  groupCounts inclusion. plan_test.go covers cancelled projection to
  StateCancelled, node list retention (not dropped), Cancelled-wins-Planned
  priority, and nil Icon for cancelled nodes.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
… plan-DAG

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
…sting DBs

Rebuilds hera_role_status with the widened CHECK ('failed' added), preserving rows. Fresh DBs are already correct via schema.go CREATE; this is only for in-place upgrades of an existing ~/.argus/data.sql (SQLite can't ALTER a CHECK).
…#795 rebase)

DRN's archive pass rewrote the Status-icon precedence requirement (4-tier -> 6-tier + BUG-003 spinner-from-real-activity + new scenarios). Re-layer the failed red ✕ addition onto DRN's version instead of clobbering it with the stale 4-tier text.
@anutron anutron force-pushed the argus/feat-hera-living-plan branch from beefdaf to 91026d2 Compare June 25, 2026 04:19
@github-actions

Copy link
Copy Markdown

Merging this branch changes the coverage (3 decrease, 3 increase)

Impacted Packages Coverage Δ 🤖
github.com/drn/argus/internal/db 90.11% (-0.02%) 👎
github.com/drn/argus/internal/heragater 87.34% (-0.31%) 👎
github.com/drn/argus/internal/mcp 88.51% (-0.60%) 👎
github.com/drn/argus/internal/tui/hera 92.15% (+0.01%) 👍
github.com/drn/argus/internal/tui/planview 93.72% (+0.17%) 👍
github.com/drn/argus/internal/tui/widget 95.48% (+0.01%) 👍

Coverage by file

Changed files (no unit tests)

Changed File Coverage Δ Total Covered Missed 🤖
github.com/drn/argus/internal/db/hera.go 89.19% (+0.10%) 444 (+4) 396 (+4) 48 👍
github.com/drn/argus/internal/db/hera_plan.go 84.00% (+0.78%) 175 (+32) 147 (+28) 28 (+4) 👍
github.com/drn/argus/internal/db/schema.go 78.57% (+0.31%) 70 (+1) 55 (+1) 15 👍
github.com/drn/argus/internal/heragater/heragater.go 87.34% (-0.31%) 229 (+59) 200 (+51) 29 (+8) 👎
github.com/drn/argus/internal/mcp/hera.go 82.00% (-0.10%) 439 (+20) 360 (+16) 79 (+4) 👎
github.com/drn/argus/internal/mcp/hera_plan.go 80.74% (-0.79%) 244 (+87) 197 (+69) 47 (+18) 👎
github.com/drn/argus/internal/mcp/server.go 95.42% (+0.02%) 677 (+3) 646 (+3) 31 👍
github.com/drn/argus/internal/tui/hera/model.go 97.06% (+0.01%) 272 (+1) 264 (+1) 8 👍
github.com/drn/argus/internal/tui/hera/plan.go 94.23% (+0.23%) 52 (+2) 49 (+2) 3 👍
github.com/drn/argus/internal/tui/hera/rail.go 91.54% (ø) 638 584 54
github.com/drn/argus/internal/tui/planview/planview.go 93.72% (+0.17%) 907 (+8) 850 (+9) 57 (-1) 👍
github.com/drn/argus/internal/tui/widget/rolestatusicon.go 100.00% (ø) 18 (+1) 18 (+1) 0

Please note that the "Total", "Covered", and "Missed" counts above refer to code statements instead of lines of code. The value in brackets refers to the test coverage of that file in the old version of the code.

Changed unit test files

  • github.com/drn/argus/internal/db/hera_failed_test.go
  • github.com/drn/argus/internal/db/hera_plan_test.go
  • github.com/drn/argus/internal/db/hera_test.go
  • github.com/drn/argus/internal/heragater/heragater_test.go
  • github.com/drn/argus/internal/mcp/hera_failed_test.go
  • github.com/drn/argus/internal/mcp/hera_plan_test.go
  • github.com/drn/argus/internal/mcp/hera_send_status_test.go
  • github.com/drn/argus/internal/mcp/hera_test.go
  • github.com/drn/argus/internal/tui/hera/plan_test.go
  • github.com/drn/argus/internal/tui/hera/rail_test.go
  • github.com/drn/argus/internal/tui/planview/render_test.go
  • github.com/drn/argus/internal/tui/widget/rolestatusicon_test.go

@anutron anutron merged commit 2e83609 into master Jun 25, 2026
1 check passed
@anutron anutron deleted the argus/feat-hera-living-plan branch June 25, 2026 06:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant