Skip to content

ground gc-toolkit bead-hosts in core (assigned-work wake reason) + wake-reason-aware teardown (tk-z130v.3)#213

Merged
zook-bot merged 4 commits into
mainfrom
polecat/tk-z130v.3
Jul 23, 2026
Merged

ground gc-toolkit bead-hosts in core (assigned-work wake reason) + wake-reason-aware teardown (tk-z130v.3)#213
zook-bot merged 4 commits into
mainfrom
polecat/tk-z130v.3

Conversation

@zook-bot

Copy link
Copy Markdown
Contributor

Summary

Ground gc-toolkit bead-hosts in core (assigned-work wake reason) + wake-reason-aware teardown

Parent decision: tk-z130v (read its notes — the A/B evidence and the accepted trade
are there). Repo: zookanalytics/gc-toolkit (INTERNAL fork; standard bead→PR→Codex→
refinery flow — open a PR, do NOT self-merge). This IS an implementation bead.

Goal (one sentence)

A gc-toolkit bead-host must survive an INVOLUNTARY config-drift drain by being brought
back automatically by the reconciler with its conversation intact — achieved by making
the host the assignee of its hosted bead (the assigned-work wake reason survives the
drained state), NOT by any fork exemption.

Proven facts you are building on (do not re-derive; verify only where noted)

  • gascity core revives a stopped session that is the assignee of a bead with
    awake-demand. A/B proven on this city: assignee set ⇒ revived ~20s, no operator
    action; assignee cleared ⇒ stayed drained. (Mechanism: compute_awake_set.go
    assigned-work loop has no Drained gate; workBeadHasAwakeDemand = in_progress, or
    open+Ready.)
  • Assignment MUST use the session name (s-<id>), not the session id — passing a
    session id to gc bd update <tk-bead> --assignee flips the resolver to the HQ store
    and fails "no issue found". s-<id> is also what the awake-set matches on.
  • A grounded host does NOT idle-sleep and cannot be stopped by plain drain-ack while
    the assignment + awake-demand hold (revives ~20s). Teardown must clear the wake reason
    FIRST.

The change (core of it)

The dual-link already written on host bring-up is the natural home. In
tools/gc-bead-host.sh:

  1. cmd_link (bring-up) — in addition to the existing reverse/forward/lineage
    writes, set the work bead's assignee to the session name ($name, already
    resolved in that function). This is the grounding. Keep it idempotent (re-linking the
    same session must not thrash). Note the existing convention (see gc-helm.sh header
    comment: "a child bead's assignee is its OWNING session — the session_name"), so
    assignee=session_name is already an established meaning here — confirm the host bead
    is consistent with it.

  2. cmd_unlink (teardown / re-bind) — clear the work bead's assignee (alongside
    the existing metadata clears) so the host can actually be stopped. Without this, a
    re-bind or cleanup leaves the old session grounded and un-drainable.

  3. gc-helm.sh takeaway --release — ALREADY clears assignee (--assignee=) as its
    closing step (verified). Confirm it still does after your change and that release =
    ungrounded-then-stoppable. If any other operator-facing "done with this bead" path
    exists, it must also clear the wake reason before drain.

The prompt/model change (do not skip — the old guidance is now wrong)

agents/bead-host/prompt.template.md currently tells hosts to gc runtime drain-ack
"between visits" for token economy. For a GROUNDED host that is a no-op-with-restart
(revives ~20s). Update the model to ONE coherent story and make the prompt match it:

  • DEFAULT (recommend): warm-while-live. A grounded host stays warm as long as its
    bead has awake-demand; it is put down by ENDING the work — close the bead, or park it
    (defer/block so it is no longer ready), or takeaway --release. Replace the
    "drain-ack between visits" instruction accordingly: intentional teardown clears the
    wake reason (release / close / park) FIRST, then drains.
  • ALTERNATIVE (only if you verify it): hold-to-sleep. Keep the assignment but use a
    bounded gc session suspend/held_until to sleep between visits — the hold beats
    every wake reason (spike claim, compute_awake_set.go "Hold suppression — overrides
    everything"). BEFORE proposing this, VERIFY EMPIRICALLY that a config-drift occurring
    DURING the hold still results in eventual revival (not permanent death). This
    interaction is UNVERIFIED. If you cannot verify it cleanly, do NOT adopt it — ship the
    warm-while-live model.

Guardrails to verify (must not regress)

  • Assignee=session_name must not make the host bead look like pool/handback work.
    Confirm scale_check, keeper prime-sweep (assignee-based), witness reconcile, and any
    pool router do NOT pick up a host bead just because it now has an assignee. (The
    child-bead convention suggests safe, but a HOST bead is often a decision/open bead —
    check it isn't treated as an unassigned handback or a routable root.)
  • Migration / live hosts. After deploy, existing live bead-hosts are still
    ungrounded. Provide a one-time backfill (set assignee=session_name on every live host
    bead from its hosts_bead link) OR document that they ground on next open. This
    matters: the exemption drop (tk-z130v.4) is gated on all live hosts being grounded,
    and THIS conversation's host (tk-z130v) is one of them.
  • Idempotency / cross-rig. up runs cross-rig with BEADS_DIR pinned; the assignee
    write must land in the bead's own rig ledger (same pinning as the existing link
    writes).

Tests / verification (TDD where practical)

  • Offline-testable: link sets assignee=session_name on the work bead; unlink clears
    it; both idempotent. (gc-bead-host.sh already has offline-testable link/unlink — extend
    those.)
  • Live smoke on a THROWAWAY host (mirror tk-z130v's A/B; do not touch real hosts): open
    → assignee set → drain/idle → auto-revive; release/unlink → assignee cleared → drain
    sticks. Tear the throwaway down cleanly.
  • Do NOT drift real city config to test the involuntary path; the assigned-work grounding
    is the tested invariant and the idle-revive A/B already stands in for it.

Deliverable

A PR on zookanalytics/gc-toolkit implementing the above, with the prompt/model updated
to the shipped sleep story, guardrails verified, and a backfill (or a documented
ground-on-next-open) for live hosts. Report the PR number back on this bead. Do NOT open
any upstream (gastownhall) PR. Do NOT touch the gascity fork or gc-8yr6px — that is
tk-z130v.4, deliberately separate and blocked on this.

Implementation notes

Implemented (branch polecat/tk-z130v.3 @ ca40c4d, target main). Grounding + warm-while-live teardown.

CORE (tools/gc-bead-host.sh): link sets work-bead assignee=session_name — the assigned-work wake
reason compute_awake_set revives across an INVOLUNTARY drain (no Drained gate); unlink clears it
(ungrounds → stoppable); new backfill grounds already-linked hosts city-wide, rig-pinned per bead.
New/resumed hosts ground automatically via up→link. gc-helm takeaway --release already clears
assignee — verified + noted it ungrounds.

MODEL (agents/bead-host/{prompt.template.md,agent.toml}): warm-while-live — a grounded host stays
warm while its bead has awake-demand; teardown clears the wake reason (close/park/release) FIRST,
then drains. hold-to-sleep NOT adopted (unverified; a polecat must not drift live config).

GUARDRAILS (verified, must-not-regress): scale-check/keeper/pool-router/refinery/reconcile never act
on a session-name assignee (they key on gc.routed_to/branch/merge_result/own-identity; grounding also
fails --unassigned). The witness recover-orphaned-beads scan newly sees grounded host beads, but
drained/asleep already classify not-orphaned (revival scenario safe); closed the dead-host edge
safe-by-code (cf #206/#210): recover-orphaned-beads drops beads grounded to their own host
(assignee==host_session_name).

TESTS: bead-host-binding-fixture.sh grounding/unground/idempotent/backfill (31/31 live);
host-bead-skip.test.sh (5/5, extracts the witness filter verbatim). bash -n + shellcheck clean.
Rebased onto origin/main incl. #205 — quiesce is orthogonal (keys gc.step_ref=mol-polecat-work.*,
never host beads).

DEFERRED (operator; needs a live LLM session — a polecat must not spawn/reset live sessions): live
smoke on a throwaway host (open→assignee set→drain→auto-revive; release/unlink→cleared→drain sticks).
Parent tk-z130v already A/B-proved revival; this ships the metadata contract that triggers it.

Scope: gc-toolkit only (no upstream/gascity; tk-z130v.4 separate). PR opens via the refinery pre-open
codex gate — PR number to be recorded then.
Rework tk-5bygy landed on polecat/tk-z130v.3 at 358e0b7; re-review tk-knb1t dispatched (one-anchor-per-PR, tk-ynz4b).
Rework tk-v369i landed on polecat/tk-z130v.3 at 9fd8e47; re-review tk-wc3gf dispatched (one-anchor-per-PR, tk-ynz4b).
Rework tk-0gnq9 landed on polecat/tk-z130v.3 at cc62091 (rebased onto origin/main 4b09bd5 — resolved the assets/scripts/gc-helm.sh cmd_clear conflict with landed tk-xypcy #211, preserving the codex signoff fixes); re-review tk-q6zwf dispatched (one-anchor-per-PR, tk-ynz4b). Gates at cc62091: bash -n clean; shellcheck clean at warning+ (info-only SC2015 x41 on the A && ok || bad assertion idiom, SC2016 x4).

Refinery handoff

  • Issue: tk-z130v.3 (task, P1)
  • Source branch: polecat/tk-z130v.3
  • Target: main
  • Codex signed off pre-open at cc620913; PR opened codex-green.

zook-bot added 4 commits July 22, 2026 21:17
…live teardown (tk-z130v.3)

Ground gc-toolkit bead-hosts so an involuntary config-drift drain no longer
kills them. gc-bead-host.sh 'link' sets the work bead's assignee to the host's
session NAME — the assigned-work wake reason the reconciler honors across drains
(compute_awake_set has no Drained gate) — and 'unlink' / gc-helm 'takeaway
--release' clear it so a host can still be stopped. New 'backfill' grounds hosts
linked before this shipped; new/resumed hosts ground automatically via 'up'.

- tools/gc-bead-host.sh: link grounds, unlink ungrounds, + backfill migration verb
- agents/bead-host/{prompt.template.md,agent.toml}: warm-while-live model — a
  grounded host stays warm while its bead has awake-demand; teardown clears the
  wake reason (close/park/release) FIRST, then drains
- assets/scripts/gc-helm.sh: note that --release's assignee-clear also ungrounds
- formulas/mol-witness-patrol.toml: recover-orphaned-beads skips grounded host
  beads (assignee==host_session_name) — safe-by-code, not judgment (cf #206/#210)
- tests: bead-host-binding-fixture.sh grounding assertions; host-bead-skip.test.sh

Guardrails verified (scale-check/keeper/pool/refinery/reconcile key on
gc.routed_to/branch/merge_result/own-identity, never a session-name assignee).
…v.3)

Pre-open signoff (tk-50mw4) found that ungrounding clobbers a real owner:
cmd_unlink always sent `--assignee=` and cmd_backfill always overwrote the
assignee with the host name, so tearing down (or backfilling) a host stripped
any newer non-host assignment and stranded that owner's work.

Guard both writes on the witness filter's contract (host-bead-skip.test.sh):
assignee == host_session_name => grounded (ours to clear); != => a real
assignee (keep).

- cmd_unlink: snapshot host_session_name + the current assignee before the
  metadata clear; clear the assignee only while it still equals the host's
  session name, else preserve it (and log).
- cmd_backfill: read the current assignee (rig-pinned, same --db as the
  ground write) and skip+log a bead a non-host owner already holds.
- fixture: add host_session_name != assignee preservation coverage for both
  unlink and backfill.

Addresses the sole P1 from the pre-open codex review of polecat/tk-z130v.3.
…he forward cache is absent (tk-z130v.3)

Codex pre-open signoff (review bead tk-knb1t) requested changes: cmd_unlink
decided whether to clear the grounding assignee from the optional forward
cache host_session_name ALONE. That cache may be absent (a partial link, a
manually cleared cache, the perf-cache-only case), and on that no-forward-cache
path unlink cleared host_session/hosts_bead but left assignee=<session_name>
behind — so the grounded host kept its assigned-work wake reason and revived
~20s after every drain. Repro on the reviewed commit: before/after assignee
both s-<session>, host_session and hosts_bead empty.

Derive the grounding-owner name from the authoritative sources instead: the
reverse link (each bound session bead's session_name, matched as the clear loop
walks the candidates) and the explicit session arg (the caller names the exact
host being torn down); the forward cache, when present, is now just one more
signal. Clear the assignee iff the current assignee equals a grounding-owner
name — a newer NON-host assignee (a real agent/operator that took the bead
after grounding) matches none and is still preserved, so we never strand a
live owner's work.

Add a no-forward-cache unlink assertion to bead-host-binding-fixture.sh: with
the forward cache cleared but the reverse link intact and the work bead
grounded going in, unlink must clear the grounding assignee (derived via the
bound session's session_name). Precondition + post-unlink assertion both added.
…fix (tk-0gnq9)

rig_db_for_bead's trailing `[ -n "$path" ] && [ -d ... ] && printf` chain
leaked exit 1 as the function's status when a hosted bead's id-prefix was
absent from `gc rig list`. Under `set -euo pipefail`, cmd_backfill's
`db="$(rig_db_for_bead "$work")"` then aborted the whole one-time grounding
pass on the FIRST unresolvable host — stranding every later live host (the
migration gates the tk-z130v.4 exemption drop).

Honor the documented "empty if unresolved" contract: emit the path via an
`if` so the function returns 0 on the unresolved path (caller falls back to
the ambient bd context). Add a hermetic missing-prefix fixture proving
backfill grounds a later host despite an earlier unresolvable one and
exits 0.

Addresses pre-open codex signoff (review tk-wc3gf) on branch polecat/tk-z130v.3.
@zook-bot

Copy link
Copy Markdown
Contributor Author

Codex signoff (pre-open, comment-only — not an approval):

Code Review: tk-q6zwf

Scope: pre-open branch review, origin/main...cc620913c282c7b537528825123a0cb2a20661a1
Branch: polecat/tk-z130v.3
Intent: ground gc-toolkit bead-hosts by assigning the hosted bead to the host session name, add wake-reason-aware teardown/backfill paths, update bead-host guidance, and prevent witness/pool/refinery recovery from misclassifying grounded hosts.

Review team: local correctness/testing/project-standards/reliability/security pass. Multi-agent reviewers were not spawned because this session's sub-agent tool requires an explicit delegation request.

Findings

No blocking findings.

Coverage

Reviewed tools/gc-bead-host.sh, tools/bead-host-binding-fixture.sh, assets/scripts/host-bead-skip.test.sh, assets/scripts/gc-helm.sh, formulas/mol-witness-patrol.toml, and agents/bead-host/* at cc620913c282c7b537528825123a0cb2a20661a1. Checked pool/refinery guardrails: worker pool queries require unassigned+routed demand, bead-host has work_query = [], and refinery handles only work assigned to $GC_AGENT with branch metadata. Confirmed a live hosted bead already carries the expected host_session_name shape.

Validation: bash -n passed for the changed shell scripts checked; assets/scripts/host-bead-skip.test.sh passed 5/5; tools/bead-host-binding-fixture.sh passed 40/40. shellcheck is not installed in this environment, so I could not rerun it locally.

Residual risk: the live LLM smoke/conversation-fidelity check remains operator-deferred as documented on the anchor; this review covered the shell metadata contract and hermetic regression surface.

Verdict: Ready to merge.

Actionable findings: none.

@zook-bot
zook-bot merged commit 141510c into main Jul 23, 2026
@zook-bot
zook-bot deleted the polecat/tk-z130v.3 branch July 23, 2026 01:06
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