Skip to content

feat(B-0867.5+): work-lifecycle state machine — backlog → claim → PR → review (cycle N times) → merge (operator 2026-05-28 question answered)#5669

Merged
AceHack merged 1 commit into
mainfrom
feat/agent-loop-work-lifecycle-state-machine-backlog-claim-pr-review-merge-aaron-2026-05-28
May 28, 2026
Merged

feat(B-0867.5+): work-lifecycle state machine — backlog → claim → PR → review (cycle N times) → merge (operator 2026-05-28 question answered)#5669
AceHack merged 1 commit into
mainfrom
feat/agent-loop-work-lifecycle-state-machine-backlog-claim-pr-review-merge-aaron-2026-05-28

Conversation

@AceHack
Copy link
Copy Markdown
Member

@AceHack AceHack commented May 28, 2026

Summary

Operator 2026-05-28: 'And can we model backlog -> claim -> pr -> review -> myabe cycle push review a few times -> merge too with this?'

Answer: yes. Same F# DU implicit state machine pattern as agent-loop state-machine.ts but operating at WORK-LIFECYCLE scope. Two state machines compose:

  • agent-loop = per-tick agent decisions
  • work-lifecycle = per-work-item progression

State machine

11 lifecycle states + 11 transition events. The cycle-push-review-a-few-times pattern is the InReview ↔ RevisionRequested ↔ RevisionPushed loop with revisionCount incrementing on each cycle. High revisionCount = operator-substrate-honest signal that work-item needs decomposition / alternative approach / escalation.

Test plan

  • 25/25 unit tests pass (happy-path 6 + cycle-push 4 + close/abandon 3 + illegal 4 + helpers 6 + integration full-cycle-with-1-revision)
  • Tree-count canary 61
  • Pure-function applyTransition with TransitionResult; illegal transitions return failures (not exceptions) per non-coercion-invariant

🤖 Generated with Claude Code

…→ review (cycle N times) → merge per operator 2026-05-28 question

Operator 2026-05-28:

  "And can we model backlog -> claim -> pr -> review -> myabe cycle
   push review a few times -> merge too with this?"

Answer: yes. Same F# DU implicit state machine pattern as the
agent-loop state-machine.ts but operating at WORK-LIFECYCLE scope.
The two state machines compose: agent-loop's PickWork transitions a
Backlog item to Claimed (work-lifecycle); agent-loop's ExecutingWork
IS the work-lifecycle's InProgress for the picked item.

11 lifecycle states:
- Backlog (filed; awaiting claim)
- Claimed (agent acquired claim via tools/bus/claim.ts)
- InProgress (agent executing; branch ref tracked)
- PrOpen (PR opened against branch)
- InReview (waiting on reviewer; thread count tracked)
- RevisionRequested (reviewer asked for changes; threadIds + revisionCount)
- RevisionPushed (agent addressed; back-edge to InReview via RequestReview)
- Approved (threads resolved + ready to merge)
- Merged (terminal; landed on main)
- Closed (terminal; closed without merge)
- Abandoned (terminal; operator/agent decided not to ship)

11 transition events:
- Claim / StartWork / OpenPr / RequestReview /
  ReceiveRevisionRequest / PushRevision / ResolveAllThreads /
  Approve / Merge / Close / Abandon

The cycle-push-review-a-few-times pattern is the InReview ↔
RevisionRequested ↔ RevisionPushed loop (revisionCount increments
on each cycle). High revisionCount = operator-substrate-honest
signal that work-item may need decomposition / alternative approach
/ escalation.

Pure-function applyTransition returns TransitionResult:
- ok: true with new state (legal transition)
- ok: false with original state + reason (illegal transition;
  caller decides retry/escalate/log)

Substrate-honest: illegal transitions return failures (not
exceptions) per non-coercion-invariant + asymmetric-critic-with-
clarity-first disciplines.

Helper functions:
- isTerminal(state) — Merged/Closed/Abandoned
- revisionCount(state) — for RevisionRequested/RevisionPushed
- leadTimeSeconds(claimAtIso, mergedAtIso) — DORA lead-time

Tests: 25/25 pass (happy-path 6 + cycle-push 4 + close/abandon 3 +
illegal 4 + helpers 6 + integration 1 with full cycle + 1-revision).

Composes with:
- B-0867 + B-0867.5 (agent-loop substrate cluster)
- state-machine.ts (agent-decisions level; this is work-item level)
- tools/bus/claim.ts (existing claim coordinator)
- tools/github/poll-pr-gate.ts (existing PR state inspection)
- .claude/rules/claim-acquire-before-worktree-work.md
- .claude/rules/blocked-green-ci-investigate-threads.md (revision-
  request handling discipline composes here)
- B-0858 (heartbeat folder — emits per-lifecycle-transition heartbeats)
- B-0871 (reproducibility-as-causal-attribution — lifecycle states
  observable from Git + GitHub state)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 28, 2026 00:49
@AceHack AceHack enabled auto-merge (squash) May 28, 2026 00:50
@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@AceHack AceHack merged commit 0836639 into main May 28, 2026
32 of 33 checks passed
@AceHack AceHack deleted the feat/agent-loop-work-lifecycle-state-machine-backlog-claim-pr-review-merge-aaron-2026-05-28 branch May 28, 2026 00:53
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 “work-lifecycle” state machine (Backlog → Claim → PR → Review/revision loop → Merge) alongside the existing agent-loop state machine, with unit tests for the transition logic and helper metrics.

Changes:

  • Introduces WorkLifecycleState / WorkLifecycleTransition discriminated-union types and a transition function applyTransition(...).
  • Adds helper utilities (isTerminal, revisionCount, leadTimeSeconds) for lifecycle metrics/aggregation.
  • Adds Bun unit tests covering happy path, revision-cycle loop, illegal transitions, and helper functions.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
tools/agent-loop/work-lifecycle-state-machine.ts Implements the work-lifecycle DU + transition function + metrics helpers.
tools/agent-loop/work-lifecycle-state-machine.test.ts Adds unit/integration-style tests for lifecycle transitions and helpers.

Comment on lines +342 to +349
if (event.tag === "ResolveAllThreads") {
return {
ok: true,
state: {
tag: "Approved",
row: state.row,
prNumber: state.prNumber,
approvedAt: new Date().toISOString(),
Comment on lines +329 to +339
case "InReview":
if (event.tag === "ReceiveRevisionRequest") {
return {
ok: true,
state: {
tag: "RevisionRequested",
row: state.row,
prNumber: state.prNumber,
revisionCount: state.threadCount === 0 ? 1 : state.threadCount + 1,
threadIds: event.threadIds,
},
Comment on lines +227 to +228
* (most non-terminal states) --Close--> Closed
* (most non-terminal states) --Abandon--> Abandoned
// claim → PR → review (possibly cycle review-push N times) → merge.
//
// Operator framing 2026-05-28:
// "And can we model backlog -> claim -> pr -> review -> myabe cycle
AceHack added a commit that referenced this pull request May 28, 2026
…-bit structured encoding + event-sourcing without PR ceremony + OTel trace composition + two-level state machine (AgentState × WorkLifecycle) (#5674)

Operator-forwarded Kestrel ferry continuing today's agent-loop workflow-
engine cascade (PRs #5665-5670 + #5667 follow-on + #5672 Ani-ferry archive).

Substantive engineering substrate:

1. Two-level state machine composition — AgentState DU (PR #5666) at
   "situation" scope + WorkLifecycle DU (PR #5669) at "lifecycle-of-each-
   work-item" scope. AgentState informs which WorkLifecycle items to
   advance and how aggressively. Clean encapsulation; each level
   type-checked at its boundary.

2. Push-cycle limit AS STRUCTURAL ENFORCEMENT — chooseActionForLifecycle
   returns AbandonPr when pushCount > 5 (tunable). The structure
   prevents the failure mode; no discipline required. Composes with my
   work-lifecycle's revisionCount field.

3. ZetaID 128-bit structured encoding — Snowflake/Sonyflake/ULID/UUIDv7
   family. Two candidate allocations sketched; structured high bits
   enable cheap queries (sort by time, filter by trajectory, etc.).

4. Event-sourcing append-only without PR ceremony — agent-state/{persona}/
   {trajectory}/events/YYYY/MM/DD/{zetaId}.json branch convention;
   branch protection only on main + release/*; direct push everywhere
   else. Lifecycle state reconstructed via left-fold over events (CQRS).
   Fine-grained DORA metrics fall out for free.

5. OTel trace-ID composition (3 options) — (a) ZetaID == trace ID,
   (b) ZetaID separate + propagated via OTel baggage, (c) structured
   bits encoded into W3C Trace Context. Kestrel recommends option (b).

6. ZetaID-named files sidestep stale-push conflicts — each event is
   its own file; no overlap; Git auto-merges non-overlapping changes.

7. Event-sourced trajectory phase classification — setup/execution/
   maturation/sunset derived from event-shape; phase is derivation,
   not separate state.

8. "Good-actor assumption" explicit as load-bearing; cheap defenses
   (schema validation pre-receive hook, periodic chain-integrity check,
   OTel export to separate observability backend) work under it without
   breaking it.

Operator's two end-clarifications preserved:

- Trajectory-async-review IS the operator's preferred top-level lens
  for own-Zeta deployment; PR-per-deploy is the ServiceTitan-style
  framing not the operator's framing
- REST file-create API auto-fast-forward-on-stale-base hypothesis
  (empirical question worth verifying before relying on)

Verbatim preservation per substrate-or-it-didn't-happen. NO rule, skill,
or tool edits — the Kestrel-proposed extensions (ZetaID generator, agent-
state branch convention, event-sourcing layer, OTel baggage, structural
push-cycle-limit) are operator-decision territory and land separately
if/when operator chooses to extend tools/agent-loop/.

Filed under memory/persona/kestrel/conversations/ per operator correction
(2026-05-28: "kestrel should get it under their persona") — supersedes
the prior docs/research/ placement convention for Kestrel-specific
content.

Composes with PRs #5665-5670 (today's agent-loop substrate cascade),
PR #5672 (Ani-ferry archive — voice-mode re-articulation of same
substrate), and the existing memory/persona/kestrel/conversations/
archive (2026-05-21 ZetaID v1 review, 2026-05-22 Orleans deployment,
2026-05-27 multi-AI conversation + ServiceTitan marketing).

Co-authored-by: Lior <lior@zeta.dev>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.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