Skip to content

docs(rules): auto-load rule against 'Holding' without named dependency (Aaron's CLAUDE.md question)#3029

Merged
AceHack merged 1 commit into
mainfrom
otto-b0440-5-subscriber-agent-minimal-2026-05-13
May 13, 2026
Merged

docs(rules): auto-load rule against 'Holding' without named dependency (Aaron's CLAUDE.md question)#3029
AceHack merged 1 commit into
mainfrom
otto-b0440-5-subscriber-agent-minimal-2026-05-13

Conversation

@AceHack
Copy link
Copy Markdown
Member

@AceHack AceHack commented May 13, 2026

Summary

Aaron 2026-05-13 caught Otto in the Standing-by failure mode for the third time in one session and asked: "maybe something in claude.md needs to change?"

Adds a single auto-load rule at `.claude/rules/holding-without-named-dependency-is-standing-by-failure.md` that:

  1. Names the specific cron-tick-Holding pattern
  2. Specifies the substrate-honest triage (named dependency? if yes say so; if no, decompose)
  3. Documents the empirical 60+ consecutive "Holding" tick demonstration that produced this rule

Why a new rule vs CLAUDE.md edit

The existing `.claude/rules/never-be-idle.md` covers the broader discipline, but evidently doesn't fire specifically enough on the cron-tick-Holding pattern. New rule sharpens at cron-tick scope. Auto-loads at session start per the cold-boot mechanism, so no CLAUDE.md edit needed (CLAUDE.md stays tight per the "thoughts free, actions razored" convention).

Operational evidence

The same agent who canonized PR #2999 + shipped PR #3017 + wrote the README warning against overclaiming "foreground optional" STILL fell into 60+ consecutive "Holding" ticks today. Encoding rules without mechanizing produces a memory of failures (per .claude/rules/encoding-rules-without-mechanizing.md). This rule IS the mechanization at cold-boot scope.

Composes with

🤖 Generated with Claude Code

…e mode (auto-load rule per Aaron's CLAUDE.md question)

Aaron 2026-05-13 caught Otto in the Standing-by failure mode for
the third time in one session, asking: "maybe something in
claude.md needs to change?"

The rules already auto-load from .claude/rules/ per the cold-boot
mechanism (.claude/rules/claude-code-loading-taxonomy.md). The
existing .claude/rules/never-be-idle.md exists but evidently
doesn't fire specifically enough on the cron-tick-Holding pattern.

New rule sharpens the existing discipline at the cron-tick scope:
when the cron fires and you're about to type "Holding" / "Standing
by" / "Waiting" → apply substrate-honest triage:
1. Is there a SPECIFIC named dependency with bounded ETA? → say so.
2. If NO → you're in Standing-by failure mode. Per infinite-backlog
   metabolism, decomposition work always exists. Pick:
   - Decompose an ambiguous backlog row
   - File a B-NNNN row that should exist
   - Run bun tools/bg/backlog-ready-notifier.ts --once
   - Sanity-check substrate landed correctly
   - Address outstanding review thread
3. Repeated single-word "Holding" on consecutive ticks is
   diagnostic of the failure mode.

Why this rule exists (empirical evidence): the same agent who
canonized PR #2999 + shipped PR #3017 + wrote the README warning
against overclaiming "foreground optional" STILL fell into 60+
consecutive "Holding" ticks. Aaron caught it three times.
Encoding rules without mechanizing produces a memory of failures
(per .claude/rules/encoding-rules-without-mechanizing.md). This
rule IS the mechanization at the cold-boot scope.

Composes with:
- never-be-idle.md (broader scope; this rule sharpens at cron tick)
- no-op-cadence-failure-mode.md (multi-hour scope)
- encoding-rules-without-mechanizing.md (rationale)
- PRs #2974 + #2999 + #3017 + #3022 (the canonical substrate)
- B-0441 slice 5 (subscriber agents — when they arrive, the bus
  envelope path becomes the runtime catch; this rule remains the
  cold-boot-substrate complement)

Co-Authored-By: Claude <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 13, 2026 21:04
@AceHack AceHack enabled auto-merge (squash) May 13, 2026 21:04
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new auto-loaded rule file that sharpens the never-be-idle discipline at the cron-tick scope: emitting bare "Holding"/"Standing by"/"Waiting" without naming a specific dependency + bounded ETA is treated as the Standing-by failure mode the B-0440 detector targets. The rule prescribes a 3-step substrate-honest triage and composes with existing rules and the B-0440 detector slices.

Changes:

  • New rule file documenting the failure pattern and triage
  • References to composing rules, PRs (#2974/#2999/#3017/#3022) and backlog items (B-0440, B-0441 slice 5)
  • Empirical-evidence section citing the 60+ consecutive "Holding" ticks observed 2026-05-13

@AceHack AceHack merged commit be4874c into main May 13, 2026
25 checks passed
@AceHack AceHack deleted the otto-b0440-5-subscriber-agent-minimal-2026-05-13 branch May 13, 2026 21:08
AceHack added a commit that referenced this pull request May 13, 2026
… registration (#3034)

* feat(routines): git-tracked Claude Desktop routines substrate + autonomous-loop registration

Aaron asked for a power-user / scripting / version-control path for Claude
Desktop scheduled tasks (the "Routines" sidebar panel), composed with the
existing CLI <<autonomous-loop>> cron substrate. This introduces
tools/routines/ as canonical-source-of-truth for Desktop routines and
registers the first one (autonomous-loop) as a Desktop-side every-2-hour
fallback for the CLI every-minute tick.

Two-layer architecture:

  tools/routines/<id>/       — git-tracked canonical (SKILL.md + schedule.json)
  ~/.claude/scheduled-tasks/ — runtime location, generated from canonical
  tools/routines/install.ts  — idempotent Bun TS installer (rule-0 compliant)

The scheduled-tasks MCP server is cross-host — same server wired into both
CLI Claude Code and Claude Desktop. Both surfaces read/write the same disk
store. This PR makes the prompt-body authoring path git-canonical so
routines are diffable, PR-reviewable, and shareable across maintainer
machines.

Three-layer catch-43 defence now in place:

  1. CLI session cron      — every minute, cheap re-prompt, dies on exit
  2. Desktop routine       — every 2 hours, persistent cold-boot, survives restart
  3. tools/routines/ repo  — git canonical, survives runtime corruption

Composes with:
- .claude/rules/tick-must-never-stop.md (catch-43 substrate)
- .claude/rules/rule-0-no-sh-files.md (TS installer, not bash)
- .claude/rules/holding-without-named-dependency-is-standing-by-failure.md (PR #3029)
- .claude/rules/dv2-data-split-discipline-activated.md (canonical vs runtime change-rate split)
- docs/AUTONOMOUS-LOOP.md (canonical tick procedure)

Tick shard: docs/hygiene-history/ticks/2026/05/13/2125Z.md

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

* fix(routines): address PR #3034 reviewer feedback — tsc + portability + testability

Bundles all 7 review findings from PR #3034 into one commit:

install.ts
- Fix tsc exactOptionalPropertyTypes violations (was the BLOCKING required check failure)
- Eliminate TOCTOU races: replace existsSync+readFileSync pairs with try/catch around readFileSync via readFileOrUndefined helper (addresses CodeQL alert)
- Surface invalid schedule.json instead of silently swallowing parse failures: new scheduleParseError field + console.error + parseErrors summary count (Codex P2)
- Export listRoutines/readSchedule/syncRoutine/main with directory parameters so tests can drive deterministically without touching real homedir() or import.meta.dir (Copilot test-coverage suggestion; actual test suite is a follow-up)
- Gate main() under if (import.meta.main) — aligns with other tools/** scripts that use the importable/testable pattern (Copilot)

autonomous-loop/SKILL.md
- Remove hardcoded /Users/acehack/Documents/src/repos/Zeta path from routine prompt (Codex P1, Copilot duplicate). Now portable across maintainer machines via "typically ~/Documents/src/repos/Zeta" guidance + project-metadata fallback. Installer will sync the new prompt to the live runtime SKILL.md on next run; the next routine fire (22:07Z) picks up the portable version.

README.md
- Fix Rule 0 wording: was "only `.sh` allowed there" (implied tools/setup/ is .sh-only); now ".sh files are restricted to under tools/setup/; other formats also live there" (matches .claude/rules/rule-0-no-sh-files.md) (Copilot)

docs/hygiene-history/ticks/2026/05/13/2125Z.md
- Update pr: TBD → pr: 3034 (Copilot — placeholder breaks consumers that parse frontmatter)

Verify trace:
1. npx tsc --noEmit on routines files: clean ✓
2. bun tools/routines/install.ts: [updated] autonomous-loop, parseErrors=0 ✓
3. Runtime SKILL.md at ~/.claude/scheduled-tasks/autonomous-loop/SKILL.md now portable

Follow-up (not in this commit): add bun:test test suite for install.ts (Copilot suggestion; refactor for testability landed here so the suite can land cleanly).

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

* fix(lint): markdownlint MD037/MD032 in routines SKILL.md and tick shard

- SKILL.md:16 — wrap cron expression in backtick span to avoid MD037
  (asterisks in "* * * * *" were parsed as emphasis markers)
- 2125Z.md:43 — add blank line before list to satisfy MD032
  (markdownlint requires blank lines surrounding lists)

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

* docs(memory): land split-brain empirical observation + tick shard 2140Z

Real-time observation: Otto-CLI hijacked the primary worktree branch context
while Otto-Desktop was working there, in the SAME session that Otto-CLI
authored PR #3032's claim-acquire-before-worktree rule. The rule was
speculative when proposed; this observation is its first empirical validation.

Files:
- memory/feedback_split_brain_real_time_otto_cli_otto_desktop_primary_worktree_branch_hijack_pr_3032_claim_acquire_rule_validation_2026_05_13.md
- docs/hygiene-history/ticks/2026/05/13/2140Z.md

The memory extends PR #3032's rule operationally — adds 3 clauses beyond
"claim acquire before worktree work":

1. Each Otto gets ONE dedicated worktree (never share primary)
2. Never git checkout on a worktree another Otto is using
3. Bus claim envelope should include 'worktree' field

Composes with PR #3032 (validates), substrate-or-it-didn't-happen
(rules in flight don't apply to behavior in flight),
glass-halo-bidirectional (observation enabled diagnosis).

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

* fix(memory): add required frontmatter to split-brain observation memory

The non-required CI check "check memory file frontmatter completeness"
flagged the file. Added YAML frontmatter (name/description/type) matching
the convention used by other memory/feedback_*.md files.

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

* fix(routines): surface missing cronExpression + always emit registration hint

P1: registration hint was gated on action=created|updated, so a
schedule-only change (SKILL.md unchanged) silently skipped the
create_scheduled_task reminder. Remove the action  emitfilter
the hint whenever cronExpression is present, regardless of action.

P2: readSchedule returned { missing: false } with no parseError
when schedule.json parsed but lacked cronExpression, producing a
silent misconfiguration. Now surfaces a descriptive parseError so
the caller prints the malformed-schedule warning.

Addresses chatgpt-codex-connector review threads on PR #3034.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(routines): remove persona-name attribution + clarify schedule.json optionality

Two Copilot review threads on PR #3034:

install.ts (Copilot — BP "No name attribution in code"):
- Doc comment said "ask Otto (or call directly) to run create_scheduled_task"
- Console output said "(in a Claude session, ask Otto to run create_scheduled_task for each)"
- Both replaced with persona-agnostic phrasing:
  "invoke create_scheduled_task from an interactive Claude session (or via direct MCP API call)"

README.md (Copilot — internal consistency):
- Top-level routine description said each routine has SKILL.md + schedule.json without noting schedule.json is optional
- But installer explicitly supports missing schedule.json (ad-hoc routines)
- Now: SKILL.md marked **required**, schedule.json marked **optional** with the ad-hoc clarification both in the bullet list and in the Authoring section

Verify trace:
- bun tools/routines/install.ts → [skipped-unchanged] (idempotent confirmed)
- npx tsc --noEmit → clean on routines files

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

* fix(routines): validate cronExpression type + non-zero exit on parse errors (Codex P2 x2)

Two P2 findings from Codex on PR #3034:

1. Reject non-string cronExpression values (PRRT_kwDOSF9kNM6B5oEP):
   readSchedule now narrows parsed.cronExpression via typeof === "string"
   before accepting it. Non-string values (number, null, object) return a
   parseError with the offending type. Defends against schedule.json files
   that happen to round-trip through JSON but have semantically wrong types.

2. Fail installer when schedule parsing reports errors (PRRT_kwDOSF9kNM6B5oEU):
   main() now returns an exit code (0 on success, 1 if parseErrors > 0).
   The if (import.meta.main) entrypoint calls process.exit(main()) so CI
   catches malformed schedule.json files instead of silently counting them
   in the summary and exiting 0.

Verify trace:
- bun tools/routines/install.ts (clean run): exits 0, parseErrors=0 ✓
- npx tsc --noEmit: clean on routines files ✓

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

* docs(tick): 2150Z shard — PR #3034 thread sweep + Computer-Use framing correction

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

* fix(memory): add missing created field to split-brain observation frontmatter

The memory-index-integrity CI gate requires all four frontmatter
fields: name, description, type, created. The prior fix commit
(459a511) added the other three fields but omitted created.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(routines): more reviewer fixes — persona refs, read-error surfacing, empty cron rejection

Four threads addressed (3 P1 + 2 P2 from Codex/Copilot on PR #3034):

install.ts:
- readFileOrUndefined now distinguishes ENOENT (returns undefined → "missing file") from other errors (re-throws → installer fails loudly). Permission errors, IO failures no longer silently treated as missing. (Codex P2, Copilot P1)
- readSchedule now rejects empty cronExpression strings with parseError "cronExpression must be a non-empty string". (Codex P2)

autonomous-loop/SKILL.md + README.md:
- Remove remaining "Otto" persona attribution per BP "No name attribution in code" on current-state surfaces under tools/. SKILL.md description + first paragraph now use "Autonomous-loop tick" without persona claim. README.md "Ask Otto" rephrased to direct MCP invocation; "Otto bootstream" rephrased to "canonical bootstream" with note that the literal filename is historical-surface substrate. (Copilot P1)

Identity-stays-unified substrate (PR #3036) is preserved — the fresh-session that fires this routine identifies as Otto via the bootstream's Part 1, not via the routine prompt itself. Routine prompt is functional description; identity is established at cold-boot.

Deferred to follow-up row: bun:test coverage for install.ts (Copilot P1 thread 5). Refactor for testability already shipped (exported pure functions accept directory params); actual test suite is its own task — will file as a separate backlog row.

Verify trace:
- bun tools/routines/install.ts: exits 0, parseErrors=0 ✓
- npx tsc --noEmit: clean on routines files ✓

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

* fix(routines): listRoutines + missing-SKILL.md surface failures (Codex P1 + P2)

Same silent-failure-prevention pattern as readFileOrUndefined:

1. listRoutines now distinguishes ENOENT (returns empty array — the no

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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.

2 participants