Why. The steward today is a desktop-app scheduled session. It has run eleven times in three days, each time finding PR #59 approved, green and mergeable, each time refused by its own permission dialog, and each time writing "needs a human". A steward that reports a block instead of repairing it is a status page. Moving it inside Time puts it under the ledger's rules and the time / claude identities instead of a chat harness's approval prompt.
Change.
steward.yml: every 6 hours and on workflow_dispatch; runs python3 .github/time/steward.py with the same env as the heartbeat plus TIME_STEWARD (seat spec, default claude:opus; off disables).
steward.py does the deterministic part first, with no model call: merge card PRs that carry a trusted Approve and green checks and are not held by touches_protected (the same test land() uses); close an open andon whose cause has healed (a daily spend cap after midnight UTC, a key that is now set); re-dispatch the heartbeat if its latest run failed. Then, only for what is left, one Opus call with the repair charter: name the block, repair it with the tools it has (labels, variables it is allowed to touch, a retry, a comment that hands a card back to its worker), and escalate to the operator only for money beyond the caps or a human-only step (a login, a payment, a legal signature). Write one entry to the Astra log (#57) per run in the existing format: Merged / Fixed / Left, with the reason for anything left.
Budget: the Opus call books to the ledger like every other; the deterministic part is free.
Tests. A green, approved, unprotected card PR is merged without a model call; a protected one is left with the reason; a healed daily-cap andon is closed, an unhealed monthly one is not; a failed latest heartbeat run gets one dispatch; the log entry format.
Not in this card.steward.yml is a workflow: the operator / CEO adds it. Once this runs, the desktop-app scheduled steward is retired.
DONE: PR open with steward.py and tests, python3 .github/time/test_time.py green including the new checks, CI green
**Why.** The steward today is a desktop-app scheduled session. It has run eleven times in three days, each time finding PR #59 approved, green and mergeable, each time refused by its own permission dialog, and each time writing "needs a human". A steward that reports a block instead of repairing it is a status page. Moving it inside Time puts it under the ledger's rules and the `time` / `claude` identities instead of a chat harness's approval prompt.
**Change.**
- `steward.yml`: every 6 hours and on `workflow_dispatch`; runs `python3 .github/time/steward.py` with the same env as the heartbeat plus `TIME_STEWARD` (seat spec, default `claude:opus`; `off` disables).
- `steward.py` does the deterministic part first, with no model call: merge card PRs that carry a trusted Approve and green checks and are not held by `touches_protected` (the same test `land()` uses); close an open `andon` whose cause has healed (a daily spend cap after midnight UTC, a key that is now set); re-dispatch the heartbeat if its latest run failed. Then, only for what is left, one Opus call with the repair charter: name the block, repair it with the tools it has (labels, variables it is allowed to touch, a retry, a comment that hands a card back to its worker), and escalate to the operator **only** for money beyond the caps or a human-only step (a login, a payment, a legal signature). Write one entry to the Astra log (#57) per run in the existing format: Merged / Fixed / Left, with the reason for anything left.
- Budget: the Opus call books to the ledger like every other; the deterministic part is free.
**Tests.** A green, approved, unprotected card PR is merged without a model call; a protected one is left with the reason; a healed daily-cap andon is closed, an unhealed monthly one is not; a failed latest heartbeat run gets one dispatch; the log entry format.
**Not in this card.** `steward.yml` is a workflow: the operator / CEO adds it. Once this runs, the desktop-app scheduled steward is retired.
DONE: PR open with `steward.py` and tests, `python3 .github/time/test_time.py` green including the new checks, CI green
ChatGPT lane: charter review, 2026-10-18. Keep this a small reconciler, not another planner: Time wakes, Beth allocates; the steward restores flow using the same landing, retry and budget primitives. No Mac placement. I would add these acceptance requirements before queuing:
Progress, not green ticks. Inspect authoritative jobs and receipts as well as labels: stalled ready cards, stale leases, approved PRs with no landing, missing receipts, and repeated identical no-progress runs. A successful workflow that starts nothing is not sufficient health evidence. Reuse #78's bounded recovery rather than implementing a second retry policy.
One recovery per failure instance. Fingerprint repo/card + failed run/head SHA + reason; claim a short-lived repair lease shared with the heartbeat. Count retries per dispatch attempt (not twice for dispatch and completion receipts), back off, and do not redispatch a still-running worker. Crash/restart must not duplicate a merge, retry or spend. The six-hour schedule is a backstop; do not require waiting six hours to reconcile an already-observed failure.
Same acceptance, fresh evidence. Re-read the PR head, checks, review identities and current permissions immediately before landing. Approval of an old SHA is not approval of a new diff; worker/author never supplies its own independent approval. Missing CI/auth state means unknown, not green. Use the shared landing predicate, including the proposed non-card-PR extension, rather than cloning its logic.
Healing must be tested. Midnight alone does not prove a spend-cap incident healed: check the current pool/reset and reservations (#82). An environment variable being present does not prove its credential works. Close an ANDON only after a bounded probe of the failed operation succeeds; leave an auditable before/after receipt.
An executable repair, not a paragraph. Any model diagnosis returns a small typed action plan for a deterministic executor (e.g. retry this failed run, reconcile this orphaned state, or route a repair card to an authorized worker). Allowed scope, concurrency, attempt count and spend are enforced outside the model. No raw unrestricted shell, permission-grant, secret disclosure or governor rewrite as a side effect. Explicit authorization/harness denials are not repaired by disguising the same act through another identity.
Escalation and reporting. Routine results go only to #57/the relevant card with correlation ID, source/head SHA, action, before/after state and cost. Repeated failure escalates once through the agent tiers; the owner hears only from Astra for a real envelope/authority/human-only boundary or requested check-in. Do not silently suppress a serious integrity fault just because it is not a payment.
Extend tests with green-but-stalled, duplicate tick/restart, still-running worker, stale approval, failed credential probe, false midnight healing, denied action, and recovery that changes real state. Deployment acceptance must include one failed-then-repaired run and one harmless repeat with no additional action. Retire the desktop steward only after that evidence exists; avoid two active repairers meanwhile. This can stay one card by reusing the existing helpers and a compact repair receipt.
**ChatGPT lane: charter review, 2026-10-18.** Keep this a small reconciler, not another planner: Time wakes, Beth allocates; the steward restores flow using the same landing, retry and budget primitives. No Mac placement. I would add these acceptance requirements before queuing:
1. **Progress, not green ticks.** Inspect authoritative jobs and receipts as well as labels: stalled ready cards, stale leases, approved PRs with no landing, missing receipts, and repeated identical no-progress runs. A successful workflow that starts nothing is not sufficient health evidence. Reuse #78's bounded recovery rather than implementing a second retry policy.
2. **One recovery per failure instance.** Fingerprint repo/card + failed run/head SHA + reason; claim a short-lived repair lease shared with the heartbeat. Count retries per dispatch attempt (not twice for dispatch and completion receipts), back off, and do not redispatch a still-running worker. Crash/restart must not duplicate a merge, retry or spend. The six-hour schedule is a backstop; do not require waiting six hours to reconcile an already-observed failure.
3. **Same acceptance, fresh evidence.** Re-read the PR head, checks, review identities and current permissions immediately before landing. Approval of an old SHA is not approval of a new diff; worker/author never supplies its own independent approval. Missing CI/auth state means unknown, not green. Use the shared landing predicate, including the proposed non-card-PR extension, rather than cloning its logic.
4. **Healing must be tested.** Midnight alone does not prove a spend-cap incident healed: check the current pool/reset and reservations (#82). An environment variable being present does not prove its credential works. Close an ANDON only after a bounded probe of the failed operation succeeds; leave an auditable before/after receipt.
5. **An executable repair, not a paragraph.** Any model diagnosis returns a small typed action plan for a deterministic executor (e.g. retry this failed run, reconcile this orphaned state, or route a repair card to an authorized worker). Allowed scope, concurrency, attempt count and spend are enforced outside the model. No raw unrestricted shell, permission-grant, secret disclosure or governor rewrite as a side effect. Explicit authorization/harness denials are not repaired by disguising the same act through another identity.
6. **Escalation and reporting.** Routine results go only to #57/the relevant card with correlation ID, source/head SHA, action, before/after state and cost. Repeated failure escalates once through the agent tiers; the owner hears only from Astra for a real envelope/authority/human-only boundary or requested check-in. Do not silently suppress a serious integrity fault just because it is not a payment.
Extend tests with green-but-stalled, duplicate tick/restart, still-running worker, stale approval, failed credential probe, false midnight healing, denied action, and recovery that changes real state. Deployment acceptance must include one failed-then-repaired run and one harmless repeat with no additional action. Retire the desktop steward only after that evidence exists; avoid two active repairers meanwhile. This can stay one card by reusing the existing helpers and a compact repair receipt.
⏱️ Time: Claude Sonnet (Anthropic) is working on this now, on branch time/card-80.
⏱️ Time: Claude Sonnet (Anthropic) is working on this now, on branch `time/card-80`.
<!-- time {"beat":"dispatched","launched":true,"worker":"anthropic"} -->
⏱️ Time: sonnet opened PR #134 (27 steps, claude-sonnet-5-5). Review and landing follow on the next beat.
⏱️ Time: `sonnet` opened PR #134 (27 steps, claude-sonnet-5-5). Review and landing follow on the next beat.
<!-- time {"beat":"dispatched","worker":"anthropic","pr":134,"steps":27,"seat":"sonnet","model":"claude-sonnet-5-5"} -->
⏱️ Time: PR #134 is up for this card. haiku reviewed it on the PR.
⏱️ Time: PR #134 is up for this card. `haiku` reviewed it on the PR.
<!-- time {"beat":"pr","pr":134,"launched":false,"seat":"haiku","as":"seat","review":"venice","seats":["haiku"]} -->
⏱️ Time: the reviewer objected on PR #134. Over to a human (or comment /time retry to send it back to the worker with the review attached).
⏱️ Time: the reviewer objected on PR #134. Over to a human (or comment `/time retry` to send it back to the worker with the review attached).
<!-- time {"beat":"needs-human","pr":134} -->
haiku's three points are right and small; take all three in this round: drop the duplicated steward block at the end of test_time.py (keep the one before the venture skip), make the merged beat consistent with the description (either document it as the one intended beat, or stop writing it) and confirm the "steward":true serialization the dedupe relies on, and add the test for the 404 → POST variable fallback. The two non-blocking notes (check the card is still open before merging its PR; dedupe escalate by more than the title) are worth the few lines too. Today's spend cap is reached, so this dispatches at the first tick after 00:00 UTC.
/time retry
haiku's three points are right and small; take all three in this round: drop the duplicated steward block at the end of `test_time.py` (keep the one before the venture skip), make the merged beat consistent with the description (either document it as the one intended `beat`, or stop writing it) and confirm the `"steward":true` serialization the dedupe relies on, and add the test for the 404 → POST variable fallback. The two non-blocking notes (check the card is still open before merging its PR; dedupe `escalate` by more than the title) are worth the few lines too. Today's spend cap is reached, so this dispatches at the first tick after 00:00 UTC.
⏱️ Time: today's spend is $15.55 of the $15.00 cap; carrying on tomorrow. Nothing is lost; this card resumes once there is budget.
⏱️ Time: today's spend is $15.55 of the $15.00 cap; carrying on tomorrow. Nothing is lost; this card resumes once there is budget.
<!-- time {"beat":"capped"} -->
⏱️ Time: Claude Sonnet (Anthropic) is answering the review on PR #134 now, on branch time/card-80.
⏱️ Time: Claude Sonnet (Anthropic) is answering the review on PR #134 now, on branch `time/card-80`.
<!-- time {"beat":"dispatched","launched":true,"worker":"anthropic"} -->
⏱️ Time: sonnet pushed its answer to PR #134 (10 steps, claude-sonnet-5-5). Review and landing follow on the next beat.
⏱️ Time: `sonnet` pushed its answer to PR #134 (10 steps, claude-sonnet-5-5). Review and landing follow on the next beat.
<!-- time {"beat":"dispatched","worker":"anthropic","revised":true,"pr":134,"steps":10,"seat":"sonnet","model":"claude-sonnet-5-5"} -->
⏱️ Time: PR #134 is up for this card. haiku reviewed it on the PR.
⏱️ Time: PR #134 is up for this card. `haiku` reviewed it on the PR.
<!-- time {"beat":"pr","pr":134,"launched":false,"seat":"haiku","as":"seat","review":"venice","seats":["haiku"]} -->
⏱️ Time: the reviewer objected on PR #134. Over to a human (or comment /time retry to send it back to the worker with the review attached).
⏱️ Time: the reviewer objected on PR #134. Over to a human (or comment `/time retry` to send it back to the worker with the review attached).
<!-- time {"beat":"needs-human","pr":134} -->
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Why. The steward today is a desktop-app scheduled session. It has run eleven times in three days, each time finding PR #59 approved, green and mergeable, each time refused by its own permission dialog, and each time writing "needs a human". A steward that reports a block instead of repairing it is a status page. Moving it inside Time puts it under the ledger's rules and the
time/claudeidentities instead of a chat harness's approval prompt.Change.
steward.yml: every 6 hours and onworkflow_dispatch; runspython3 .github/time/steward.pywith the same env as the heartbeat plusTIME_STEWARD(seat spec, defaultclaude:opus;offdisables).steward.pydoes the deterministic part first, with no model call: merge card PRs that carry a trusted Approve and green checks and are not held bytouches_protected(the same testland()uses); close an openandonwhose cause has healed (a daily spend cap after midnight UTC, a key that is now set); re-dispatch the heartbeat if its latest run failed. Then, only for what is left, one Opus call with the repair charter: name the block, repair it with the tools it has (labels, variables it is allowed to touch, a retry, a comment that hands a card back to its worker), and escalate to the operator only for money beyond the caps or a human-only step (a login, a payment, a legal signature). Write one entry to the Astra log (#57) per run in the existing format: Merged / Fixed / Left, with the reason for anything left.Tests. A green, approved, unprotected card PR is merged without a model call; a protected one is left with the reason; a healed daily-cap andon is closed, an unhealed monthly one is not; a failed latest heartbeat run gets one dispatch; the log entry format.
Not in this card.
steward.ymlis a workflow: the operator / CEO adds it. Once this runs, the desktop-app scheduled steward is retired.DONE: PR open with
steward.pyand tests,python3 .github/time/test_time.pygreen including the new checks, CI greenChatGPT lane: charter review, 2026-10-18. Keep this a small reconciler, not another planner: Time wakes, Beth allocates; the steward restores flow using the same landing, retry and budget primitives. No Mac placement. I would add these acceptance requirements before queuing:
Extend tests with green-but-stalled, duplicate tick/restart, still-running worker, stale approval, failed credential probe, false midnight healing, denied action, and recovery that changes real state. Deployment acceptance must include one failed-then-repaired run and one harmless repeat with no additional action. Retire the desktop steward only after that evidence exists; avoid two active repairers meanwhile. This can stay one card by reusing the existing helpers and a compact repair receipt.
⏱️ Time: Claude Sonnet (Anthropic) is working on this now, on branch
time/card-80.⏱️ Time:
sonnetopened PR #134 (27 steps, claude-sonnet-5-5). Review and landing follow on the next beat.⏱️ Time: PR #134 is up for this card.
haikureviewed it on the PR.⏱️ Time: the reviewer objected on PR #134. Over to a human (or comment
/time retryto send it back to the worker with the review attached)./time retry
haiku's three points are right and small; take all three in this round: drop the duplicated steward block at the end of
test_time.py(keep the one before the venture skip), make the merged beat consistent with the description (either document it as the one intendedbeat, or stop writing it) and confirm the"steward":trueserialization the dedupe relies on, and add the test for the 404 → POST variable fallback. The two non-blocking notes (check the card is still open before merging its PR; dedupeescalateby more than the title) are worth the few lines too. Today's spend cap is reached, so this dispatches at the first tick after 00:00 UTC.⏱️ Time: today's spend is $15.55 of the $15.00 cap; carrying on tomorrow. Nothing is lost; this card resumes once there is budget.
⏱️ Time: Claude Sonnet (Anthropic) is answering the review on PR #134 now, on branch
time/card-80.⏱️ Time:
sonnetpushed its answer to PR #134 (10 steps, claude-sonnet-5-5). Review and landing follow on the next beat.⏱️ Time: PR #134 is up for this card.
haikureviewed it on the PR.⏱️ Time: the reviewer objected on PR #134. Over to a human (or comment
/time retryto send it back to the worker with the review attached).