v6.8 Part 3: progressive floor escalation + heartbeat floor-status (#77, #78)#18
Merged
Merged
Conversation
…floor-status Two binding mechanisms for hermes-jarvis#77 + NousResearch#78. Both close SOUL-discipline gaps the 2026-06-10 v6.7 validation chain surfaced: workers don't reach for x_fast_justified when fast (NousResearch#77), and workers heartbeat forever past elapsed floor without re-calling complete (NousResearch#78). The paired SOUL updates ship in hermes-jarvis. Closes hermes-jarvis#77 Closes hermes-jarvis#78 ## Layer 1 — progressive RuntimeFloorViolation messaging RuntimeFloorViolation now carries prior_floor_rejections (count of past completion_blocked_v6_7_gates events with a RuntimeFloorViolation for this task) and floor_elapses_at (unix ts when the floor will pass). message() branches on prior_floor_rejections: - 0: standard "Either keep working ... or set x_fast_justified" with explicit seconds_remaining. - ≥1: escalated "REJECTED FOR THE Nth TIME — same inputs, same result. Bare retries will keep being rejected. You must choose ONE of: (A) Wait Xs ... (B) Opt out NOW with metadata=...". _v6_7_run_completion_gates seeds prior_floor_rejections from a new helper _v6_7_count_prior_floor_rejections that counts past gate events with a substring match on the JSON payload (fast on the bounded per-task event count). ## Layer 2 — heartbeat surfaces floor_status New v6_7_heartbeat_floor_status helper queries the latest completion_blocked_v6_7_gates event with RuntimeFloorViolation, computes floor_elapses_at from the task's started_at + role floor, and returns: - elapses_at: unix ts when the floor passes - seconds_remaining: signed delta (negative = passed) - retry_complete_now: convenience boolean - floor_seconds: the role's floor for clarity _handle_heartbeat in tools/kanban_tools.py threads this through — when there's pending floor rejection, the heartbeat response includes a floor_status object the worker can branch on. When no rejection has happened, returns None and the heartbeat response is unchanged. Cheap path so the heartbeat hot loop isn't slowed. ## Why both layers Per the v6.3 "enforcement, not text" lesson (hermes-jarvis case study chain): SOUL text alone doesn't bind. Layer 1 makes the gate's own error message progressively more directive so the worker literally cannot keep retrying the same call without consequences. Layer 2 gives the worker the runtime data it needs to actually choose between waiting and opt-ing out. ## Tests 22 new tests: TestProgressiveFloorEscalation (6) — first vs second rejection message shapes, third advances counter, default includes wait time, floor_elapses_at encoded correctly, build role gets escalation too. TestProgressiveFloorIntegration (2) — end-to-end via complete_task: second attempt with same input sees escalated message; count helper returns correct number after each rejection. TestHeartbeatFloorStatus (4) — includes elapses_at + seconds_remaining + retry_complete_now + floor_seconds; before floor elapsed (positive remaining); returns None when no rejection (cheap path); after floor elapsed signals retry_complete_now=True. 119/119 in test_kanban_completion_gates.py pass. 215/215 across full v6.7+v6.8 + adjacent regression set, zero failures. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…tics, kill dead loop, rename helper Independent review of v6.8 Part 3 flagged three BROKEN issues + a handful of polish items. All addressed. ## #1: LIKE pattern false-positive on summary_preview The substring match ``payload LIKE '%RuntimeFloorViolation%'`` would false-positive on a worker's summary_preview that mentioned the class name in prose (e.g. "Acknowledged the prior RuntimeFloorViolation; now fixing..."). Counter would inflate incorrectly. Fix: anchor the LIKE to the JSON ``"kind":`` field exactly: ``payload LIKE '%"kind": "RuntimeFloorViolation"%'``. Applied to both _v6_7_count_prior_floor_rejections and the _v6_7_heartbeat_floor_status lookup. ## #2: started_at reclaim semantics undocumented verify_runtime_floor reads tasks.started_at which is set ONCE on first claim (COALESCE in claim_task) and NOT updated on reclaim. So a second-attempt worker may pass the floor by elapsed lifetime even if its actual attempt was fast. Floor was designed against first-attempt fabrication; reclaim usually means the chain has been at it for a while already. Fix: added a "Reclaim semantics" paragraph to the docstring of verify_runtime_floor noting the anchor + the design rationale. Future-fix would switch to task_runs.started_at for the active run if this becomes a real problem. ## #3: dead code + unnecessary deferred import in _v6_7_heartbeat_floor_status Old code had a no-op for-loop iterating violations to "find" data that was immediately overwritten from the tasks row, plus a local ``from hermes_cli.kanban_completion_gates import ROLE_RUNTIME_FLOORS_SECONDS`` despite the module already being imported at top. Fix: - Rewrote helper to do a single existence-check on the event (changed SELECT payload → SELECT 1 since we don't parse it anymore). - Removed the dead for-loop. - Hoisted ROLE_RUNTIME_FLOORS_SECONDS to the top-level import block. - Renamed function to _v6_7_heartbeat_floor_status (leading underscore for v6.7-internal convention) and kept the old name as an alias so existing callers keep working. ## Other polish - Escalation message: changed "REJECTED FOR THE 2-th TIME" to "REJECTED #2" — same machine-readable count, no grammar awkwardness. - Added 4 self-review gap-fill tests: - summary_preview with class name doesn't inflate counter - multi-violation event counts once (not twice) - count on unknown task returns zero - escalated message clamps negative seconds_remaining to "Wait 0s" 123/123 in test_kanban_completion_gates.py pass. 219/219 across full v6.7+v6.8 + adjacent regression set, zero failures. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two binding mechanisms for the worker-discipline gaps the 2026-06-10 v6.7 validation chain surfaced. Both close hermes-jarvis#77 (workers don't reach for x_fast_justified when fast) and hermes-jarvis#78 (workers heartbeat forever past elapsed floor).
Paired with hermes-jarvis PR (link to be added) that updates Tony/Tchalla/Vision/Friday/Shuri SOULs with the decision tree.
Layer 1 — progressive RuntimeFloorViolation messaging
`RuntimeFloorViolation` now carries `prior_floor_rejections` and `floor_elapses_at`. `message()` branches on the rejection count:
`_v6_7_run_completion_gates` seeds the count from a new helper `_v6_7_count_prior_floor_rejections` (substring match on JSON payload — fast on bounded per-task event count).
Layer 2 — heartbeat surfaces floor_status
New `v6_7_heartbeat_floor_status` helper. When a task has a pending floor rejection, returns:
`_handle_heartbeat` threads this through — the heartbeat response now includes a `floor_status` object the worker can branch on. Returns None on tasks with no rejection (cheap path).
Why both layers
Per v6.3 "enforcement, not text": SOUL alone doesn't bind. Layer 1 makes the gate's own message progressively directive. Layer 2 gives the worker runtime data to choose between waiting and opt-ing out.
Test plan
🤖 Generated with Claude Code