Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/BACKLOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -796,6 +796,7 @@ are closed (status: closed in frontmatter)._
- [ ] **[B-0856](backlog/P2/B-0856-path-a-tmp-zeta-cluster-state-coordination-standard-per-node-marker-files-for-multi-agent-coordination-aaron-2026-05-27.md)** Path A — /tmp/zeta-cluster-state/ coordination standard for multi-agent per-node state advertisement (self-registered.marker / register-pr-in-flight.lock / last-seen.iso); composes with bus envelope substrate at cross-node scope (Aaron 2026-05-27)
- [ ] **[B-0857](backlog/P2/B-0857-install-sh-universal-unix-entry-consolidation-route-by-environment-replaces-zeta-install-sh-on-the-short-path-before-b-0854-ace-aaron-2026-05-27.md)** tools/setup/install.sh becomes the universal Unix-like-OS install entry — routes by environment (macOS / Linux-non-NixOS / NixOS-live-USB / installed-NixOS); replaces zeta-install.sh on the short-path BEFORE B-0854 Ace migration completes (Aaron 2026-05-27)
- [x] **[B-0857.1](backlog/P2/B-0857.1-audit-pr-5389-step-6-95a-invocation-of-tools-setup-install-sh-verified-present-aaron-2026-05-27.md)** Audit PR #5389's claim that zeta-install.sh Step 6.95a invokes tools/setup/install.sh — VERIFIED PRESENT (corrects B-0857 row body authoring error)
- [ ] **[B-0858.5](backlog/P2/B-0858.5-heartbeat-auto-state-gathering-equals-debug-logs-aaron-2026-05-27.md)** Heartbeat auto-state-gathering — writer gathers observable current state before pushing; heartbeats become debug logs once attached (Aaron 2026-05-27)

## P3 — convenience / deferred

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
---
id: B-0858.5
priority: P2
status: open
title: Heartbeat auto-state-gathering — writer gathers observable current state before pushing; heartbeats become debug logs once attached (Aaron 2026-05-27)
effort: M
ask: aaron 2026-05-27
created: 2026-05-27
last_updated: 2026-05-27
depends_on:
- B-0858.3
- B-0858.4
Comment thread
AceHack marked this conversation as resolved.
Outdated
composes_with:
- B-0858
tags: [b-0858-sub-row, agent-heartbeat, auto-state, debug-logs, observability, deferred-post-usb]
---

## Operator framing (Aaron 2026-05-27)

Two-part vision captured across two messages:
Comment thread
AceHack marked this conversation as resolved.
Outdated

### Message 1 (autonomous state-gathering)

> *"then over time we can start adding automated observations about
> current state to the heartbeat that it automatily gathers before
> pushing, we can backlog this no right now we are gong to go back
> to usb once we have this hearbeat current iteration done."*

### Message 2 (debug-log equivalence)

> *"then heartbeats also become debug logs once we have current state
> attached"*

### Message 3 (consent-first; critical constraint)

> *"when we gether current state it should be explicit to the agents
> what information is being gathers so it's consent first and the
> agent is okay with the verbosity of the current state heartbeat,
> we don't need to summugle in a panopticon for heartbeats."*

## Consent-first constraint (load-bearing)

This constraint operates on every implementation decision below.
Per `.claude/rules/non-coercion-invariant.md` HC-8 + operator
2026-05-27 message 3:

1. **Default: gather NOTHING beyond current minimal heartbeat.** No
field below is collected unless explicitly enabled. Preserves
stupid-simple zero-param invocation.
2. **Each gathered field is OPT-IN via explicit flag** (`--gather-git-state`,
`--gather-rate-limit`, etc.) OR explicit env var
(`ZETA_AGENT_GATHER_GIT_STATE=1`, etc.). No grouped `--gather-all`
default for the autonomous loop.
3. **Agent invocation MUST explicitly enable each gather** at CLI or
env-var level. No implicit "all-on" mode; no smuggled extras.
4. **The agent owns the verbosity.** Framework provides the option;
agent (or harness setting agent env vars) chooses what to disclose
per tick. Mid-tick reconfiguration via env var change is supported.
5. **Documented at point-of-introduction.** Each new gather flag
includes a one-line description of EXACTLY what it gathers + where
that data comes from + cost. No hidden behavior.
6. **--show-gather-config flag** prints all available gather fields +
current opt-in state without writing or pushing any heartbeat.
Agents can audit what they're allowing the writer to gather.

Anti-panopticon framing: heartbeats with auto-state attached ARE
debug logs (operator message 2) BUT the agent retains full control
over what state attaches (operator message 3). Debug-log property
does not override consent-first property; they compose. The gather
mechanism itself cannot become a coercion vector because each field
requires explicit opt-in at agent scope.

## Scope

`tools/agent-heartbeats/write-heartbeat.ts` already writes structured
YAML frontmatter (zetaid + agent + persona-slot + timestamp +
authority + momentum + chromosome + location + firefly + disposition
plus optional named-dep + optional parent-pr). This row extends the
writer with AUTO-GATHERED CURRENT-STATE fields that the agent doesn't
Comment thread
AceHack marked this conversation as resolved.
Outdated
have to pass explicitly.

Candidate auto-gathered fields (operator-driven prioritization):

| Field | Source | Cost |
|---|---|---|
| `branch-protection-state` | `gh api repos/{owner}/{repo}/rulesets` count + names | 1 REST |
| `open-pr-count` | `gh pr list --state open --author "@me" --json number` | 1 REST |
| `recent-merged-pr-count` | `gh pr list --state merged --author "@me" --json number --limit 10` | 1 REST |
| `current-rate-limit-graphql-remaining` | `gh api rate_limit --jq '.resources.graphql.remaining'` | 1 REST (free) |
| `current-rate-limit-core-remaining` | same call | 0 extra |
| `cron-sentinel-alive` | check CronList for `<<autonomous-loop>>` | local |
| `git-clean-state` | `git status --porcelain \| wc -l` from operator primary checkout | local |
| `last-commit-on-main-sha-prefix` | `git ls-remote origin main` first 8 chars | local |
| `dotgit-saturation-tier` | per .claude/rules/refresh-world-model-poll-pr-gate.md table | composed lookup |
| `peer-agent-process-count` | `pgrep -fl "claude-code\|gemini\|kiro" \| wc -l` | local |
| `current-worktree-path` | `process.cwd()` | local |
| `current-branch` | `git rev-parse --abbrev-ref HEAD` from worktree | local |
| `staged-file-count` | `git diff --cached --name-only \| wc -l` | local |
| `unstaged-file-count` | `git diff --name-only \| wc -l` | local |
| `untracked-file-count` | `git ls-files --others --exclude-standard \| wc -l` | local |

All fields are observational (read-only); no operator-coercion;
each adds 0-1 REST or local-git call to the writer.

## Debug-log property (operator 2026-05-27)

When current-state attached, each heartbeat becomes a structured
debug-log snapshot of the agent at tick-fire time:

- Cross-referencing 100 consecutive heartbeats from one agent
reveals tick-cadence + named-dep timeline + disposition
evolution
- Cross-referencing heartbeats from multiple agents reveals
multi-agent saturation timeline (when did Lior fire? when did
peer-Otto fire? what was rate-limit at the moment?)
- Cross-referencing heartbeats with PR landings reveals which
agent + which disposition produced which substrate landing
- Cross-referencing heartbeats with sentinel-restart events
reveals catch-43 firing timeline

Each ZetaID-indexed heartbeat carries enough state to reconstruct
"what was the world like at this tick" for forensic analysis. The
debug-log property is what operator named: "heartbeats also become
debug logs once we have current state attached."

## Sub-rows planned

- **B-0858.5a** — local-only auto-gather (cwd, branch, staged/unstaged/untracked counts; git-clean-state; current-worktree-path) — zero REST cost; fastest landing
- **B-0858.5b** — git-state auto-gather (last-commit-sha-prefix; behind-vs-ahead status vs origin/main) — pure-git calls; no REST
- **B-0858.5c** — REST-state auto-gather (rate-limit; open-PR-count; recent-merged-PR-count) — bounded REST cost; might skip when rate-limit low
- **B-0858.5d** — peer-state auto-gather (peer-agent-process-count via pgrep; dotgit-saturation-tier per refresh-world-model rule) — local; informational
- **B-0858.5e** — sentinel-state auto-gather (CronList check for `<<autonomous-loop>>` presence) — harness-tool call

Order: 5a → 5b → 5d (all local) → 5c (REST-cost-bounded) → 5e (last).

## What this is NOT

- NOT a replacement for AgencySignature trailer on substantive
commits (those still carry full Agency-Signature-Version: 1 block;
heartbeats are the per-tick complement, not the substantive-commit
replacement)
- NOT a coercion mechanism: gathered fields are observational; agent
authority over what's emitted preserved by env-var override + CLI
--no-gather-X flags
- NOT a security risk: all fields are repo-state-observable already
(`gh pr list`, `git status`, `pgrep` are non-secret)

## Composes with substrate

- B-0858 (parent row; PR #5456 merged)
- B-0858.3 writer (PR #5464 merged) — this row extends
- B-0858.4 merge tool (PR #5471 in-flight) — heartbeats now batch-merge
to main periodically, so debug-log property eventually visible in main
history without needing to query agent-heartbeats branch separately
- `.claude/rules/refresh-world-model-poll-pr-gate.md` — rate-limit
tier table consumed by 5c
- `.claude/rules/codeql-no-source-on-docs-only-pr-is-broken-commit-canary.md`
— dotgit-saturation tier consumed by 5d
Comment thread
AceHack marked this conversation as resolved.
Outdated
- `tools/hygiene/audit-agencysignature-main-tip.ts` — sibling forensic
tool at substantive-commit scope; this row extends to per-tick scope

## Substrate-inventory pass (per `.claude/rules/verify-existing-substrate-before-authoring.md`)

Topic: auto-state-gathering for agent heartbeats

Searched:

- `tools/agent-heartbeats/` — writer + merge tool already shipped (B-0858.3 + B-0858.4)
- `tools/hygiene/audit-agencysignature-main-tip.ts` — sibling forensic tool
- `.claude/rules/holding-without-named-dependency-is-standing-by-failure.md` — externalized counter discipline this row extends
- `memory/` — no prior memory on auto-state-gathering specifically
- `docs/backlog/` — no existing row covers this scope; this row fills the gap

Conclusion: composes with B-0858.3 + B-0858.4; extends the writer
with optional auto-gather mode (defaults preserve current minimal
behavior).

## Heartbeat per CLAUDE.md discipline

Filing this row IS counter-reset work per `.claude/rules/holding-without-named-dependency-is-standing-by-failure.md`
condition #3 — capturing operator-named future work so substrate
exists; deferred per operator's "right now we are gong to go back to
usb once we have this hearbeat current iteration done" — implementation
defers until USB push lands.

## Full reasoning

Operator 2026-05-27 verbatim quotes preserved above. Filed in the
same heartbeat-substrate iteration as B-0858 row + .3 writer + .4
merge tool. Implementation deferred per operator's explicit USB-priority
direction. Recording the row exists is critical for deferring the
work to reliably happen (operator 2026-05-27 separation-of-concerns
discipline).
Loading