Skip to content

fix: classify settled spawned TTL cleanup correctly - #469

Merged
pedronauck merged 2 commits into
mainfrom
linear-eng-147
Aug 25, 2026
Merged

pedronauck merged 2 commits into
mainfrom
linear-eng-147

Conversation

@pedronauck

@pedronauck pedronauck commented Aug 25, 2026 •

Copy link
Copy Markdown
Member

Summary

Closes ENG-147.

This change makes spawned-session TTL cleanup state-aware. A child whose prompt has already settled is now reaped as a clean completed session, while a prompt that is still in flight at the stop transition remains a timeout failure.

The existing cleanup lifecycle remains intact: lease release, process teardown, spawn.ttl_expired, spawn.reaped, session stop hooks, durable stop origin, and creator wake delivery continue to use their existing paths and exact-once success semantics.

Root cause

The spawn reaper previously mapped every ttl_expired candidate to session.CauseTimeout. That cleanup reason was therefore indistinguishable from an active prompt timeout.

The session lifecycle then projected the stop as:

  • StopTimeout
  • a timeout failure payload
  • a transcript.MarkerPromptTimeout
  • a failed parent wake/badge

even when the child had already received a durable done or end_turn event and had no active prompt.

Design

  • Added an internal CauseSpawnTTLExpired request and Manager.StopWithSpawnTTL operation.
  • The concrete session manager resolves that request under the session lifecycle lock, at the same transition that changes the session to stopping.
    • Active prompt setup/turn state becomes CauseTimeout.
    • Settled prompt state becomes CauseCompleted.
    • A prompt admitted after the lifecycle lock is rejected by the stopping state instead of racing into a clean classification.
  • The clean path preserves spawn_reaper:ttl_expired as the durable stop detail.
  • Transport-only session adapters that do not expose the atomic operation conservatively retain the historical timeout classification; they cannot prove that a prompt stayed settled through the stop transition.
  • Hook/payload helpers were split into spawn_reaper_hooks.go so production files remain below the repository's 500-line cap.

Changed surfaces

  • Runtime daemon spawn reaper classification and atomic TTL stop dispatch.
  • Session stop-cause resolution and clean stop-detail persistence.
  • Lifecycle projections covering failure absence, timeout-marker absence, stopped notifier, and parent wake badge.
  • Daemon reaper tests for settled, in-flight, atomic-adapter, and conservative-fallback paths.
  • Session stop-reason and lifecycle contract tests.
  • skills/compozy/references/runtime-operations.md now documents the settled-versus-in-flight TTL behavior.
  • QA tracker:
    • added RT-spawn-ttl-cleanup
    • reset the affected RT-session-spawn-wake scenario
    • recorded focused evidence and explicit provider-backed blocked-verify instructions in docs/qa/reports/2026-08-24-eng-147-ttl-cleanup.md

Lifecycle and exactly-once behavior

The change does not reorder or duplicate the existing reaper sequence:

  1. dispatch the reason hook;
  2. release child run leases with spawn_reaper:ttl_expired;
  3. stop the child through the session lifecycle;
  4. dispatch spawn.reaped;
  5. let the existing session finalization path emit one stopped notification and one creator wake.

The clean branch only changes the stop classification. It does not suppress process teardown, lease release, hooks, or creator wake. Existing failure-path ordering and error reporting are intentionally unchanged because durable retry/idempotency semantics would be a separate design.

Tests and focused verification

Passing focused checks:

  • go test -race ./internal/daemon -run 'TestSpawnReaper' -count=1 — 9 tests passed.
  • go test -race ./internal/session -run 'Test(ClassifyStopReason|SpawnTTLStopCauseUsesPromptStateAtLifecycleTransition|CompletedSpawnReapKeepsCleanTerminalProjection|PromptActivitySupervisorTimeoutCancelsThenStopsSession)$' -count=1 — 22 tests passed.
  • go run github.com/golangci/golangci-lint/v2/cmd/[email protected] run --new-from-rev origin/main --timeout 10m --concurrency 8 ./internal/daemon/... ./internal/session/... — 0 new issues.
  • git diff --check — passed.
  • Compozy test-shape checks for internal/daemon/spawn_reaper_test.go and internal/session/stop_reason_test.go — passed.

make gate was attempted as the focused repository gate. It reached the scoped Go packages but is currently blocked by 56 pre-existing lint findings elsewhere in internal/daemon/... and internal/session/... (goconst/gocritic baseline findings); no new-code findings remain. Per the ENG-147 request, make gate-full, make verify, and other full-monorepo gates were not run.

A provider-backed CLI/HTTP/UDS QA walk is recorded as blocked-verify because this review environment has no isolated ACP provider. The automated lifecycle evidence is committed, with exact human walk instructions in the QA report.

Risks and trade-offs

  • The clean TTL path is available only when the concrete session manager can make the atomic decision. Unknown prompt state in a transport-only adapter remains a timeout for safety.
  • The stop-origin detail is now retained for clean completion, which improves public diagnosis without changing the stop reason enum.
  • No schema, migration, config, provider, or native tool contract changes are required.

Compozy Impact Audit

  • Native tools: no compozy__* tool IDs, toolsets, descriptors, schemas, digests, risk flags, capability gates, or CLI/API fallbacks changed; checked the session-spawn and session-status projection surfaces.
  • Extensibility and hooks: no extension registry or config lifecycle changes; existing spawn.ttl_expired, spawn.reaped, session stop hooks, lease release, and creator wake hooks remain wired exactly once on the success path; the official runtime-operations skill reference was updated.
  • Workspace data isolation: no new datum or scope was introduced; stop cause/detail remain session-scoped, and parent wake resolution continues through the existing child lineage/workspace path without cross-workspace reads.
  • Official Compozy skill: updated skills/compozy/references/runtime-operations.md with the state-aware TTL cleanup rule.

Review resolution

The required GPT-5.6-Sol high-reasoning review identified a possible snapshot race and an unsafe adapter fallback. Both are addressed by the atomic StopWithSpawnTTL lifecycle operation and conservative timeout fallback. The review also examined lease/hook behavior on partial failures; that ordering is unchanged from the base branch and is intentionally outside this classification fix.

Summary by CodeRabbit

  • Bug Fixes
    • Improved spawn session expiration handling by distinguishing completed sessions from sessions interrupted during an active prompt or turn.
    • Preserved clean completion details and terminal status for sessions that finish before expiration.
    • Added lifecycle notifications for session stops and reaping events.
    • Improved cleanup and lease release behavior after expired sessions.

@vercel

vercel Bot commented Aug 25, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
compozy-site Ready Ready Preview Aug 25, 2026 2:13am

Request Review

@coderabbitai

coderabbitai Bot commented Aug 25, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 2b30cc9d-7692-4c11-8641-343ad23c60d7

📥 Commits

Reviewing files that changed from the base of the PR and between b386cd5 and 1fc4020.

⛔ Files ignored due to path filters (4)
  • docs/qa/reports/2026-08-24-eng-147-ttl-cleanup.md is excluded by !**/*.md
  • docs/qa/scenarios/RT-session-spawn-wake.md is excluded by !**/*.md
  • docs/qa/scenarios/RT-spawn-ttl-cleanup.md is excluded by !**/*.md
  • skills/compozy/references/runtime-operations.md is excluded by !**/*.md
📒 Files selected for processing (8)
  • internal/daemon/spawn_reaper.go
  • internal/daemon/spawn_reaper_hooks.go
  • internal/daemon/spawn_reaper_test.go
  • internal/session/manager_lifecycle_contract_test.go
  • internal/session/session_stop_state.go
  • internal/session/stop_cause.go
  • internal/session/stop_reason.go
  • internal/session/stop_reason_test.go

Included review availability: Your plan provides up to 8 included reviews per hour; 1 remains after this review.


Walkthrough

The spawn reaper now uses atomic TTL stopping when supported. The session manager classifies settled children as completed and prompting children as timed out. The daemon adds lifecycle hook payload construction and dispatch for parent stops, TTL expiry, and reaping.

Changes

Spawn TTL reaping

Layer / File(s) Summary
Session TTL stop contract
internal/session/stop_cause.go, internal/session/stop_reason.go, internal/session/session_stop_state.go, internal/session/*_test.go
The session manager adds CauseSpawnTTLExpired and StopWithSpawnTTL. It classifies the stop while holding the lifecycle lock and preserves the TTL detail for completed sessions.
Daemon reaper integration
internal/daemon/spawn_reaper.go, internal/daemon/spawn_reaper_hooks.go
The reaper uses atomic TTL stopping when available and keeps the timeout fallback. Lifecycle payloads include session metadata, lineage, permissions, state, network, soul, reasons, and errors.
Reaper behavior validation
internal/daemon/spawn_reaper_test.go
Tests cover settled and prompting children, fallback stopping, lease release, stop causes, and lifecycle hooks.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Merge Risk: ⚪ Minimal · up to 1fc40

The change is merge-ready after normal checks and review; no actionable merge-blocking risk remains.

Sequence Diagram(s)

sequenceDiagram
  participant Reaper as spawn reaper
  participant Manager as session.Manager
  participant Hooks as spawn lifecycle hooks
  Reaper->>Manager: StopWithSpawnTTL(session ID, spawn_reaper:ttl_expired)
  Manager-->>Reaper: completed or timeout classification
  Reaper->>Hooks: dispatch TTL-expiry payload
  Hooks-->>Reaper: dispatch result
  Reaper->>Hooks: dispatch reaped payload
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 14 functions across 8 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: correcting classification for settled spawned-session TTL cleanup.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch linear-eng-147

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@greptile-apps

greptile-apps Bot commented Aug 25, 2026 •

Copy link
Copy Markdown

Greptile Summary

This PR makes spawned-session TTL cleanup classify settled prompts as completed and active prompts as timed out.

  • Adds an atomic session-manager stop operation that resolves prompt state under the lifecycle lock.
  • Preserves the TTL cleanup origin in completed-session metadata and terminal projections.
  • Adds daemon and session lifecycle coverage and updates runtime guidance and QA tracking.

Confidence Score: 4/5

The PR is not ready to merge until the required full repository gate passes.

The runtime changes have focused coverage, but the current QA report explicitly confirms that the required full-monorepo verification remains unrun.

Files Needing Attention: docs/qa/reports/2026-08-24-eng-147-ttl-cleanup.md

Important Files Changed

Filename Overview
internal/daemon/spawn_reaper.go Dispatches TTL cleanup through the atomic manager operation when available and retains the conservative timeout fallback for adapters.
internal/session/session_stop_state.go Resolves the internal TTL cause under the session lock from current prompt setup and turn state.
internal/session/stop_reason.go Adds the atomic TTL-stop entry point and preserves details for completed stops.
docs/qa/reports/2026-08-24-eng-147-ttl-cleanup.md Records focused automated coverage and blocked provider-backed verification.

Sequence Diagram

sequenceDiagram
    participant R as Spawn reaper
    participant M as Session manager
    participant S as Session lifecycle
    participant P as Parent wake
    R->>M: StopWithSpawnTTL(child, ttl detail)
    M->>S: Lock and begin stop transition
    alt Prompt is active
        S-->>M: CauseTimeout
        M-->>P: Failed timeout wake
    else Prompt is settled
        S-->>M: CauseCompleted
        M-->>P: Clean stopped wake
    end
Loading

Reviews (2): Last reviewed commit: "fix: format spawn reaper regression test" | Re-trigger Greptile


## Final Status

- **Exit gate (focused automated suite):** pass — `go test -race ./internal/daemon -run 'TestSpawnReaper' -count=1` (9 passed); `go test -race ./internal/session -run 'Test(ClassifyStopReason|SpawnTTLStopCauseUsesPromptStateAtLifecycleTransition|CompletedSpawnReapKeepsCleanTerminalProjection|PromptActivitySupervisorTimeoutCancelsThenStopsSession)$' -count=1` (22 passed); new-code golangci-lint (`--new-from-rev origin/main`) reported 0 issues. Full-monorepo gates were not run per the ENG-147 request.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Full repository gate remains outstanding

This report declares the runtime workstream ready while stating that the required make gate-full verification was not run, leaving integration failures outside the focused packages undetected before merge.

Context Used: CLAUDE.md (source)

@pedronauck
pedronauck merged commit f77378f into main Aug 25, 2026
31 of 33 checks passed
@pedronauck
pedronauck deleted the linear-eng-147 branch August 25, 2026 02:49
This was referenced Aug 25, 2026
This was referenced Sep 1, 2026
This was referenced Sep 27, 2026

This branch was successfully deployed

1 active deployment
Preview — 1fc4020e Deployed Aug 25, 2026 by vercel[bot]
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.

1 participant