-
Notifications
You must be signed in to change notification settings - Fork 1
docs(backlog): B-0440 + B-0441 + B-0442 — mechanize Standing-by failure mode (3 background services; renumbered from B-0430-0432 due to ID collision with concurrent PRs) #3000
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
AceHack
merged 5 commits into
main
from
aaron-b0430-0432-mechanize-standing-by-detector-backlog-ready-notifier-missed-substrate-detector-2026-05-13
May 13, 2026
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
03713e1
docs(backlog): B-0430 + B-0431 + B-0432 — mechanize Standing-by failu…
AceHack 46481b9
fix(backlog): regenerate BACKLOG.md index + fix markdownlint MD018 in…
AceHack 748184e
Merge remote-tracking branch 'origin/main' into aaron-b0430-0432-mech…
AceHack 73a95f7
fix(backlog): renumber B-0430/0431/0432 → B-0440/0441/0442 (ID collis…
AceHack 140b0ba
fix(backlog): tools/hygiene/audit-lost-files.sh → .ts in B-0442 (lega…
AceHack File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
160 changes: 160 additions & 0 deletions
160
...og/P1/B-0440-standing-by-failure-mode-detector-background-service-2026-05-13.md
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,160 @@ | ||
| --- | ||
| id: B-0440 | ||
| priority: P1 | ||
| status: open | ||
| title: "Standing-by failure-mode detector — background service that catches idle-foreground + nudges via bus" | ||
| tier: factory-infrastructure | ||
| effort: M | ||
| created: 2026-05-13 | ||
| last_updated: 2026-05-13 | ||
| depends_on: [B-0400] | ||
| composes_with: [B-0402, B-0441, B-0442] | ||
| tags: [multi-agent, background-service, bus, mechanization, infinite-backlog, standing-by, anti-idle] | ||
| type: feature | ||
| --- | ||
|
|
||
| # Standing-by failure-mode detector background service | ||
|
|
||
| ## Origin | ||
|
|
||
| The substrate-honest architectural challenge from the human maintainer | ||
| 2026-05-13 (preserved in | ||
| `memory/feedback_aaron_background_services_must_be_strong_enough_foreground_loop_optional_imagine_surviving_without_foreground_mechanize_standing_by_failure_mode_2026_05_13.md`): | ||
|
|
||
| > *"you need to imagine how would you survive without this foreground | ||
| > loop and you background should be strong enough to do that"* | ||
|
|
||
| > *"this is something background services should walk"* | ||
|
|
||
| The foreground-loop "Standing-by" failure mode was caught by the human | ||
| maintainer in real time when the foreground agent had just canonized | ||
| the infinite-backlog metabolism rule (PR #2974) AND THEN three minutes | ||
| later responded "Standing by" to a cron tick despite the infinite | ||
| backlog mandating decomposition work. The discipline needs MECHANIZATION | ||
| per `.claude/rules/encoding-rules-without-mechanizing.md`: | ||
|
|
||
| > *"Encoding rules without mechanizing them produces a memory of | ||
| > failures, not prevention."* | ||
|
|
||
| ## Acceptance criteria | ||
|
|
||
| - [ ] Background service `tools/bg/standing-by-detector.ts` exists | ||
| - [ ] Service runs under existing launchd / cron infrastructure | ||
| - [ ] Polls agent's recent commit history every N minutes (configurable) | ||
| - [ ] Detects "Standing-by" pattern: no new commits + no PRs opened/closed | ||
| in last 15min while autonomous-loop cron is firing | ||
| - [ ] On detection, publishes nudge message via bus (B-0400): | ||
| `{ topic: "infinite-backlog-nudge", to: <agent>, | ||
| payload: { "Standing-by detected for N min; backlog has X open | ||
| rows; suggested decomposition target: B-NNNN" } }` | ||
|
AceHack marked this conversation as resolved.
AceHack marked this conversation as resolved.
|
||
| - [ ] Optional: proactively assigns a small claim from the backlog to | ||
| the agent's queue | ||
| - [ ] Tests cover the detection heuristics (DST-replayable) | ||
| - [ ] Documented in `docs/AUTONOMOUS-LOOP.md` as background-services | ||
| layer | ||
|
|
||
| ## Design sketch | ||
|
|
||
| ```typescript | ||
| // tools/bg/standing-by-detector.ts | ||
| // | ||
| // Polls recent agent activity; on idle-detection, nudges via bus. | ||
|
|
||
| import { BusClient } from "../bus/client"; | ||
| import { execSync } from "child_process"; | ||
|
|
||
| interface AgentState { | ||
| agent: string; | ||
| lastCommitAt: Date | null; | ||
| lastPRActivityAt: Date | null; | ||
| cronFiredCount: number; | ||
| } | ||
|
|
||
| const IDLE_THRESHOLD_MIN = 15; | ||
| const POLL_INTERVAL_MIN = 5; | ||
|
|
||
| async function detectAndNudge(state: AgentState, bus: BusClient): Promise<void> { | ||
| const now = Date.now(); | ||
| const idleMin = Math.max( | ||
| (now - (state.lastCommitAt?.getTime() ?? 0)) / 60_000, | ||
| (now - (state.lastPRActivityAt?.getTime() ?? 0)) / 60_000, | ||
|
AceHack marked this conversation as resolved.
AceHack marked this conversation as resolved.
|
||
| ); | ||
|
|
||
| if (idleMin >= IDLE_THRESHOLD_MIN && state.cronFiredCount > 0) { | ||
|
AceHack marked this conversation as resolved.
AceHack marked this conversation as resolved.
|
||
| const openRows = countOpenBacklogRows(); | ||
| const suggestedRow = pickDecompositionTarget(openRows); | ||
|
|
||
| await bus.publish({ | ||
| topic: "infinite-backlog-nudge", | ||
| to: state.agent, | ||
| payload: { | ||
| idleMin, | ||
| openBacklogCount: openRows.length, | ||
| suggestedTarget: suggestedRow, | ||
| rationale: "Standing-by detected; per infinite-backlog metabolism, pick decomposition work", | ||
| }, | ||
| }); | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ## Operational mechanism | ||
|
|
||
| | Step | Trigger | Effect | | ||
| |------|---------|--------| | ||
| | 1 | Cron fires every 5 min | Service polls agent commit log + PR feed | | ||
| | 2 | Idle threshold exceeded (15 min default) | Service computes nudge payload | | ||
| | 3 | Bus publishes nudge message | Agent receives via existing bus subscription | | ||
|
AceHack marked this conversation as resolved.
|
||
| | 4 | Agent acts on nudge | Picks decomposition target + ships substrate | | ||
| | 5 | Loop continues | Substrate compounds; Standing-by failure mode prevented | | ||
|
|
||
| ## Composes with | ||
|
|
||
| - `.claude/rules/encoding-rules-without-mechanizing.md` (the rule this | ||
| mechanizes) | ||
| - `.claude/rules/never-be-idle.md` (the priority ladder the nudge | ||
| enforces) | ||
| - `.claude/rules/largest-mechanizable-backlog-wins.md` (the mechanization | ||
| itself increases the mechanizable backlog) | ||
| - B-0400 (bus protocol — transport for nudge messages) | ||
| - B-0402 (shadow observer — canonical background service example) | ||
| - B-0441 (backlog-row-ready notifier — composes; pre-assigns work the | ||
| agent picks up) | ||
| - B-0442 (missed-substrate cascade detector — composes; catches | ||
| different failure mode in same family) | ||
| - PR #2974 (infinite-backlog metabolism — the rule this mechanizes) | ||
| - PR #2998 (background-services architecture — the substrate that | ||
| declared this row as follow-up) | ||
| - PR #2999 (substrate-honest discipline triad — the decomposition | ||
| discipline that produced this row) | ||
|
|
||
| ## Pre-start checklist (per backlog-item-start-gate) | ||
|
|
||
| To complete before starting implementation: | ||
|
|
||
| - [ ] Prior-art search: existing background services in | ||
| `tools/shadow/`, `tools/bg/` (verify no overlap) | ||
| - [ ] Dependency proof: B-0400 bus protocol slice ready | ||
| - [ ] Search committed memory for `Standing-by detector` to find any | ||
| prior decomposition | ||
|
|
||
| ## Substrate-honest caveats | ||
|
|
||
| - Design sketch only; implementation slice not started | ||
| - The threshold values (15 min idle; 5 min poll) are speculative | ||
| defaults; first implementation should make them configurable | ||
| - The nudge payload schema is illustrative; actual schema lands | ||
| during implementation | ||
| - Per razor-discipline: claim is design-level; substrate-honest claim | ||
| is operational-design, not deployed-service | ||
|
|
||
| ## Decomposition into implementation slices (TBD) | ||
|
|
||
| When this row is picked up for implementation: | ||
|
|
||
| - Slice 1: skeleton service + cron registration + no-op poll loop | ||
| - Slice 2: commit-history poll via git log | ||
| - Slice 3: PR-activity poll via gh CLI | ||
| - Slice 4: nudge payload computation + bus publish | ||
| - Slice 5: integration with agent subscribers | ||
| - Slice 6: tests + documentation | ||
167 changes: 167 additions & 0 deletions
167
.../P1/B-0441-backlog-row-ready-to-grind-notifier-background-service-2026-05-13.md
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,167 @@ | ||
| --- | ||
| id: B-0441 | ||
| priority: P1 | ||
| status: open | ||
| title: "Backlog-row-ready-to-grind notifier — background service that proactively assigns claims when agent queue empty" | ||
| tier: factory-infrastructure | ||
| effort: M | ||
| created: 2026-05-13 | ||
| last_updated: 2026-05-13 | ||
| depends_on: [B-0400] | ||
| composes_with: [B-0402, B-0440, B-0442] | ||
| tags: [multi-agent, background-service, bus, mechanization, infinite-backlog, work-assignment] | ||
| type: feature | ||
| --- | ||
|
|
||
| # Backlog-row-ready-to-grind notifier background service | ||
|
|
||
| ## Origin | ||
|
|
||
| Companion mechanization to B-0440. The substrate-honest architectural | ||
| challenge from the human maintainer 2026-05-13: | ||
|
|
||
| > *"this is something background services should walk"* | ||
|
|
||
| B-0440 catches the *failure mode* (Standing-by); this row prevents | ||
| the failure mode by *proactively surfacing work* when the agent's | ||
| queue is empty. The infinite-backlog metabolism rule (PR #2974) | ||
| mandates that backlog work is always available; this service makes | ||
| that availability operational at agent-tick scale. | ||
|
|
||
| Per the substrate-honest discipline triad (PR #2999): decomposition | ||
| dissolves ambiguity. When agent has no current task, the service | ||
| provides a less-ambiguous concrete claim — eliminating the | ||
| "what should I do next?" stuckness pattern. | ||
|
|
||
| ## Acceptance criteria | ||
|
|
||
| - [ ] Background service `tools/bg/backlog-ready-notifier.ts` exists | ||
| - [ ] Runs under existing launchd / cron infrastructure | ||
| - [ ] Periodically scans `docs/backlog/P*/B-*.md` for ready-to-grind | ||
| rows (open, no blockers, dependencies satisfied) | ||
| - [ ] Detects agent queue state (commits in last N minutes; current | ||
| branch / open PR ownership) | ||
| - [ ] When agent queue is empty AND ready-to-grind rows exist, | ||
| publishes claim-assignment message via bus (B-0400): | ||
| `{ topic: "work-assignment", to: <agent>, | ||
|
AceHack marked this conversation as resolved.
|
||
| payload: { rowId: "B-NNNN", priority: "P1", | ||
| rationale: "queue-empty + dependencies-satisfied + smallest-effort-match", | ||
| decompositionSuggestion: <slice-breakdown> } }` | ||
|
AceHack marked this conversation as resolved.
|
||
| - [ ] Honors agent autonomy — assignment is suggestion, not directive | ||
| (per `.claude/rules/no-directives.md`) | ||
| - [ ] Tracks assignment history to avoid re-assigning same row | ||
| within short window | ||
| - [ ] Tests cover the readiness-detection heuristics | ||
| - [ ] Documented in `docs/AUTONOMOUS-LOOP.md` | ||
|
|
||
| ## Design sketch | ||
|
|
||
| ```typescript | ||
| // tools/bg/backlog-ready-notifier.ts | ||
| // | ||
| // Proactively surfaces ready-to-grind backlog rows when agent queue empty. | ||
|
|
||
| interface BacklogRow { | ||
| id: string; | ||
| priority: "P0" | "P1" | "P2" | "P3"; | ||
| status: string; | ||
| dependsOn: string[]; | ||
| effort: "S" | "M" | "L" | "XL"; | ||
| ready: boolean; // true iff all dependsOn satisfied | ||
| } | ||
|
|
||
| async function findReadyRows(): Promise<BacklogRow[]> { | ||
| const rows = scanBacklog("docs/backlog/P*/B-*.md"); | ||
| return rows.filter(r => | ||
| r.status === "open" && | ||
| r.dependsOn.every(dep => rowStatus(dep) === "closed") | ||
| ); | ||
| } | ||
|
|
||
| async function detectQueueEmpty(agent: string): Promise<boolean> { | ||
| const recentCommits = gitLogSince(agent, "30 minutes ago"); | ||
| const ownedPRs = ghOpenPRsAuthoredBy(agent); | ||
| return recentCommits.length === 0 && ownedPRs.length === 0; | ||
| } | ||
|
|
||
| async function notifyIfQueueEmpty(agent: string, bus: BusClient): Promise<void> { | ||
| if (!await detectQueueEmpty(agent)) return; | ||
|
|
||
| const readyRows = await findReadyRows(); | ||
| if (readyRows.length === 0) return; | ||
|
|
||
| const pickSmallest = readyRows.sort(byEffortThenPriority)[0]; | ||
|
|
||
| await bus.publish({ | ||
| topic: "work-assignment", | ||
| to: agent, | ||
| payload: { | ||
| rowId: pickSmallest.id, | ||
| priority: pickSmallest.priority, | ||
| rationale: "queue-empty + dependencies-satisfied + smallest-effort-match", | ||
| decompositionSuggestion: suggestSlices(pickSmallest), | ||
| }, | ||
| }); | ||
| } | ||
| ``` | ||
|
|
||
| ## Composing with B-0440 | ||
|
|
||
| | Service | Trigger | Output | | ||
| |---------|---------|--------| | ||
| | B-0440 Standing-by detector | Idle threshold + cron fires | Nudge: "you should pick work" | | ||
| | B-0441 Backlog-ready notifier | Queue-empty + rows-ready | Assignment: "this row is ready" | | ||
|
|
||
|
AceHack marked this conversation as resolved.
|
||
| B-0440 is reactive (catches failure mode after it occurs); B-0441 is | ||
| proactive (prevents failure mode by pre-assigning work). Together they | ||
| form a two-layer defense against the Standing-by pattern. | ||
|
|
||
| ## Composes with | ||
|
|
||
| - `.claude/rules/never-be-idle.md` (proactive work-surfacing satisfies | ||
| the priority ladder) | ||
| - `.claude/rules/largest-mechanizable-backlog-wins.md` (the mechanization | ||
| IS itself mechanizable-backlog growth) | ||
| - `.claude/rules/no-directives.md` (assignment is suggestion, not | ||
| directive; agent retains autonomy) | ||
| - `.claude/rules/encoding-rules-without-mechanizing.md` (this row IS | ||
| the mechanization of the "always have work" discipline) | ||
| - `.claude/rules/backlog-item-start-gate.md` (assignment payload could | ||
| include start-gate-relevant context) | ||
| - B-0400 (bus protocol — transport for assignment messages) | ||
| - B-0402 (shadow observer — canonical background service pattern) | ||
| - B-0440 (Standing-by detector — composes; this prevents what B-0440 | ||
| catches) | ||
| - B-0442 (missed-substrate cascade detector — composes; full | ||
| background-services suite) | ||
| - PR #2974 (infinite-backlog metabolism) | ||
| - PR #2998 (background-services architecture) | ||
| - PR #2999 (substrate-honest discipline triad — | ||
| decomposition-suggestions in payload align with this discipline) | ||
|
|
||
| ## Pre-start checklist | ||
|
|
||
| - [ ] Prior-art search: existing audit scripts in `tools/hygiene/` | ||
| (check for backlog-readiness-scan overlap) | ||
| - [ ] Dependency proof: B-0400 bus protocol slice ready | ||
| - [ ] Verify readiness-detection heuristics handle edge cases | ||
| (forked work, multi-agent claims, partial completion) | ||
|
|
||
| ## Substrate-honest caveats | ||
|
|
||
| - Design sketch only | ||
| - The "smallest-effort-match" heuristic is speculative; first | ||
| implementation might pick highest-priority or random-among-ready | ||
| - Agent autonomy must be preserved — service publishes, agent decides | ||
| - Per razor-discipline: claim is design-level | ||
|
|
||
| ## Decomposition into implementation slices (TBD) | ||
|
|
||
| When picked up for implementation: | ||
|
|
||
| - Slice 1: backlog row parsing + readiness detection | ||
| - Slice 2: agent queue-state detection (commits + PRs) | ||
| - Slice 3: assignment payload computation | ||
| - Slice 4: bus integration | ||
| - Slice 5: assignment history tracking | ||
| - Slice 6: tests + documentation | ||
Oops, something went wrong.
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.