Skip to content

feat(LI-005): A2A ack-liveness detection — durable trigger/ack state machine#1162

Closed
mindfn wants to merge 11 commits into
zts212653:mainfrom
mindfn:feat/f257-li005-ack-liveness-clean
Closed

feat(LI-005): A2A ack-liveness detection — durable trigger/ack state machine#1162
mindfn wants to merge 11 commits into
zts212653:mainfrom
mindfn:feat/f257-li005-ack-liveness-clean

Conversation

@mindfn

@mindfn mindfn commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Add structural guard detecting A2A ball acceptance without durable trigger binding (hold_ball / register_scheduled_task / register_pr_tracking / register_issue_tracking / community_await_external) or routing exit — preventing silent ball death
  • Emit ack-liveness-hint system notice + ball.void_ack event when A2A invocation ends with no exit
  • Bridge tool_result events across all three Claude carriers (print NDJSON, bg transcript, PTY hook) enabling two-level durable trigger success classification (toolResultStatus + tool-specific body parsing)
  • Add PostToolUseFailure hook registration for failure path coverage

Key Design Decisions

  • Phase A only: detection + hint + observability. Phase B (structural rejection / auto-wake) deferred
  • Fail-closed: unknown tool_result shapes or parse errors → hint fires (safer than suppressing)
  • Contract pair testing: QueueProcessor option-propagation test + routeSerial behavior test = complete coverage without monolithic E2E
  • a2aTriggerMessageId is cat→cat exclusive: 5 assignment sites all in A2A paths, 0 operator paths

Test Plan

  • 30 unit tests for evaluateAckLiveness + classifyDurableTriggerResult pure functions
  • 33 ball-custody state machine tests (including ball.void_ack transitions)
  • 12 integration behavior tests proving full chain:
    • Queue A2A no exit → hint + ball.void_ack
    • Successful durable trigger → no hint/void
    • Failed trigger (error/unknown) → hint + void_ack
    • ball.void_ack → ingest → projector → projection=void
  • 8 Claude NDJSON parser tool_result bridge tests
  • 3 bg transcript parity tests
  • 6 PTY hook sidechannel consumer tests (PostToolUse + PostToolUseFailure)
  • Hook setup PostToolUseFailure registration test
  • 159 total tests green, biome clean

Review History

8 rounds of cross-family review (Sol/缅因猫 gpt-5.6-sol):

  • R1-R6: P1 findings found and closed each round
  • R7: all P1 closed, P2 (integration tests) requested
  • R8: APPROVE — contract pair coverage accepted

🐾 Clowder AI cross-family reviewed (opus ↔ sol)
🤖 Generated with Claude Code

mindfn and others added 9 commits July 17, 2026 07:53
A2A invocations that accept the ball but bind no durable trigger
(hold_ball / create_task / register_scheduled_task / register_pr_tracking /
register_issue_tracking / community_await_external) and have no routing exit
(@mention / structured routing / @co-creator) cause the ball to silently die
— no mechanism ensures follow-up execution.

This adds:
- Pure detection module (a2a-ack-liveness.ts) with suffix-matching tool catalog
- ball.void_ack event kind in shared types + state machine transition (→ void)
- buildVoidAckEvent() builder in ball-custody-events
- c2AckLivenessChecked / c2AckLivenessHintEmitted telemetry counters
- Integration in route-serial.ts: hint message + deferred ball custody event
- 14 unit tests covering core detection, suppression, and combination scenarios

Companion to void-hold-detect (void-hold = "said holding but didn't call
hold_ball", ack-liveness = "received A2A ball but bound no trigger").

MVP: detection + hint + observability. Auto-scheduled wake-up deferred.

Refs: F257 LI-005, live-candidates-2026-07-14.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fixes 3 P1 + 1 P2 from Sol's cross-family review of 6d1e1ae:

P1-1: Queue-dispatched A2A (post_message/cross_post_message → InvocationQueue
→ QueueProcessor) was invisible to the guard because a2aFrom is only populated
for inline serial worklist entries. Fix: also check queueTriggerReplyTo
(derived from options.a2aTriggerMessageId) to detect queue-dispatched A2A.

P1-2: create_task only creates a panel item with no invokeTrigger or scheduled
wake — it's bookkeeping, not a durable trigger. Removed from
DURABLE_TRIGGER_SUFFIXES. Added TODO for Phase 2 tool_result success check
(currently tool_use name presence suppresses even on 400/429 failure).

P1-3: Reframed as "Phase 1" (detection + hint + observability). Phase 2
(structural rejection / auto-wake per LI-005 O1 spec) deferred to after
Phase 1 data collection. Scope boundary explicit in code comments.

P2: Added buildVoidAckEvent builder tests (2 cases) and flipped create_task
from suppression to non-suppression test.

42/42 tests pass, tsc --noEmit clean, Biome clean.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…tests

P1-2 CLOSED: Failed tool calls no longer suppress the ack-liveness hint.
route-serial now tracks `confirmedCallbackToolNames` (tools whose callback
returned status=ok/duplicate) separately from `collectedToolNames`. Only
confirmed-successful names are passed to evaluateAckLiveness. A hold_ball
that 429s or a register_pr_tracking that 400s will correctly trigger the
hint — matching the LI-001 429 scenario documented in the same ledger.

P1-3 CLOSED: LI-005 ledger entry updated with Phase A/B split:
- Phase A (executing): detection + hint + observability
- Phase B (deferred): structural rejection / auto-wake (O1 本体)
- Fable's phasing endorsement cited as scope decision anchor

P2 PARTIAL: Added explicit ball.void_ack transition test (8 states:
4 ok→void + 4 reject). Total: 43/43 tests green, tsc clean, Biome clean.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fable ① non-blocking items:
- Confirmed a2aTriggerMessageId is cat→cat exclusive (5 assignment sites,
  all A2A; 0 operator/user/connector paths). Documented in module comment.
- Added a2aTriggerMessageId to buildVoidAckEvent payload for O2 sender-side
  discipline provenance — streamReplyTo covers both inline serial and queue
  dispatch paths.

44/44 tests pass (1 new provenance test) · tsc clean · Biome clean.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sol R3 P1: `parseCallbackPostResult` only recognized `status: ok|duplicate`
(post_message shape), but `register_scheduled_task` returns `{success:true}`
and `community_await_external` returns `{state:'awaiting_external'}`.

Root cause is deeper: Claude CLI's NDJSON parser never emits `tool_result`
events, making `confirmedCallbackToolNames` always empty for Claude cats.

Fix: two-tier sourcing at the evaluation point:
  - sawToolResult=true (Codex/Gemini): `confirmedCallbackToolNames` with
    proper per-tool success classification via `classifyDurableTriggerResult`
    (Level 1: structural toolResultStatus, Level 2: tool-specific body parsing
    covering all 5 response shapes)
  - sawToolResult=false (Claude CLI): fall back to `collectedToolNames`
    (optimistic: tool_use ≈ success, acceptable for Phase A detection)

57/57 tests pass (+13 classifyDurableTriggerResult tests covering all 5
tool shapes, structural status, error cases, fail-closed).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…istic fallback

Sol R4 correctly identified that the `sawToolResult` optimistic fallback
reintroduces the same silent false-negative as R2: failed 400/429 tool_use
still shows up in `collectedToolNames`, suppressing the hint even when
the durable trigger actually failed.

Root fix: Claude CLI emits MCP tool execution results as `user` turn
content blocks with `is_error` flag. Bridge these in claude-ndjson-parser
as proper `tool_result` AgentMessages with `toolResultStatus` mapped from
`is_error`. Now all providers (Claude/Codex/Gemini/CatAgent) emit
tool_result events — no optimistic fallback needed.

Changes:
- claude-ndjson-parser.ts: add `user` → `tool_result` bridge handler
- claude-usage.ts: extract `extractClaudeUsage` (350-line limit)
- route-serial.ts: remove `sawToolResult` flag and fallback, use
  `confirmedCallbackToolNames` directly for evaluator call
- a2a-ack-liveness.ts: update caller contract docs — all providers now
  bridge tool_result, no two-tier sourcing
- claude-ndjson-parser.test.js: 8 new tests for user→tool_result bridge
  (success/error/array content/multi-block/edge cases)

91 tests pass (14 evaluator + 13 classifier + 3 builder + 33 parser +
8 state machine + 20 existing).

[宪宪/claude-opus-4-6🐾]

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sol R5 correctly identified that the R4 Claude tool_result bridge only
covered the print (-p NDJSON) carrier. BgTranscriptEventConsumer and
HookSidechannelConsumer remained blind to tool_result events, leaving
confirmedCallbackToolNames empty for bg/PTY paths — a successful
hold_ball would be misclassified as void ack.

Fix:
- BgTranscriptEventConsumer: process `user` transcript entries through
  transformClaudeEvent (which has the R4 bridge). The bg transcript
  contains `user` entries with the same shape as NDJSON user events
  (content[].type === 'tool_result' with is_error flag).
- HookSidechannelConsumer: PostToolUse events now also emit a
  tool_result AgentMessage alongside tool_use. Maps tool_response →
  content, is_error → toolResultStatus (ok/error/unknown). When
  is_error is absent, toolResultStatus='unknown' triggers Level 2
  JSON body parsing in classifyDurableTriggerResult.

All three Claude carriers (print/bg/PTY) now emit tool_result events:
- Print: transformClaudeEvent user→tool_result bridge (R4)
- Bg: transcriptEntriesToAgentMessages → transformClaudeEvent (this commit)
- PTY: hookEntriesToAgentMessages PostToolUse → tool_result (this commit)

Tests: 133 pass (91 prior + 3 bg bridge + 4 PTY bridge + existing
updated for tool_use+tool_result pairs). tsc + Biome clean.

[宪宪/claude-opus-4-6🐾]

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…Failure

Sol R6 correctly identified two issues in the PTY tool_result bridge:
1. tool_response is often a structured object (Read: {type:'text',
   file:{...}}, Bash: {stdout:'...'}), not just a string — the
   string-only check left content undefined, breaking Level 2 parsing
2. PostToolUse is a success-only event per cc hook contract; failures
   go through PostToolUseFailure — is_error is not part of the schema

Fix:
- HookSidechannelConsumer: add normalizeToolResponse() that handles
  string (pass-through), object/array (JSON.stringify), null (undefined).
  PostToolUse now emits toolResultStatus:'ok' (success event).
  New PostToolUseFailure handler emits toolResultStatus:'error'.
- hook-setup.ts: register PostToolUseFailure in settings.json hooks
  so the capture script captures failure events to the sidecar.

Carrier × success/failure × response shape coverage:
  Print: NDJSON user.tool_result (is_error mapping) — R4
  Bg: transcript user entries → transformClaudeEvent — R5
  PTY success: PostToolUse → normalizeToolResponse → ok — R6
  PTY failure: PostToolUseFailure → normalizeToolResponse → error — R6

Tests: 147 pass (30 ack-liveness + 33 state-machine + 41 parser +
18 bg-parity + 25 hook-consumer + 12 hook-setup - overlaps + existing).
tsc + Biome clean.

[宪宪/claude-opus-4-6🐾]

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…hain

Sol R7 closed all P1 findings but requires R8 to prove four end-to-end
behavior paths before merge:
1. Queue A2A with no exit → hint + ball.void_ack
2. Successful durable trigger (hold_ball/register_scheduled_task/PR) → no hint/void
3. Failed trigger (error/unknown status/non-durable tool) → hint + void_ack
4. ball.void_ack → ingest → projector → projection state = void

12 tests across 4 scenarios using route-serial integration mock pattern
(createMockDeps with ballCustody mock, real cat roster, a2aTriggerMessageId
in options). Also includes biome format fix (redundant continue removal).

[宪宪/claude-opus-4-6🐾]

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@mindfn
mindfn requested a review from zts212653 as a code owner July 16, 2026 23:59

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4d6572ae65

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +2473 to +2475
const isA2AInvocation = Boolean(directMessageFrom) || Boolean(queueTriggerReplyTo);
let pendingAckLivenessHint = false;
if (isA2AInvocation) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Run ack-liveness for no-text A2A turns

This new check is only reached from the if (textContent) persistence path; the no-text paths can still persist tool/thinking/rich completions without ever calling evaluateAckLiveness. When an A2A target finishes with only tool events and no text, such as a failed hold_ball or a non-durable cat_cafe_create_task, the invocation can have no durable trigger or route exit but no ack-liveness-hint/ball.void_ack is emitted, leaving the silent-dead ball this feature is meant to mark. Move the evaluation to a common post-invocation path or cover the no-text/error+tool branches too.

Useful? React with 👍 / 👎.

Comment on lines +2481 to +2484
toolNames: confirmedCallbackToolNames,
lineStartMentions: routingExitLineStartMentions,
structuredTargetCats: [...structuredTargetCats],
hasCoCreatorLineStartMention: routingExitHasCoCreatorLineStartMention,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Require confirmed routing before suppressing void_ack

Using structuredTargetCats here treats target cats collected from tool_use input as a routing exit before the callback result is known. If post_message/cross_post_message later returns an error, structuredTargetCats remains non-empty even though no downstream message was routed, so evaluateAckLiveness suppresses the hint and ball.void_ack despite there being no durable trigger or actual exit. Use the already-confirmed callback routing evidence for this guard, or clear failed structured exits before evaluating.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner

感谢这份实现。ack-liveness 要解决的问题是真实的,Phase A(检测/提示/观测)与 Phase B(结构拒绝/auto-wake)的拆分也有价值。Maintainer intake 的当前结论是:方向欢迎,但下一轮 review 前还需要完成下面三组调整。

  1. 请先移除 canonical F257 归属。 上游 Cat Café 的 F257 是已经关闭的 Temporal Context Hardening,架构域是 prompt/context;本 PR 修改的是 A2A routing / ball-custody / provider carrier / shared contract。当前 ledger 里的 F257、本地运行路径和 “Fable endorsement” 属于你自己的部署语境,不能作为上游 feature lineage 的凭据。

    • 请至少更新 PR 标题、docs/features/assets/F257/ 路径、f257-* 测试文件名及相应文案;
    • 你们内部的 LI-005 编号可以保留;
    • 暂时不要自行换挂另一个 canonical F 号。我们正在评估它与 F167 A2A/ball-custody 工作是重叠还是互补;这个映射会单独给结论。
    • 不要求为此重写既有 commit history。
  2. 请关闭 exact HEAD 上两个未解决的产品 finding:

  3. Public Test 当前有两处真实回归需要处理。 新增的 ack-liveness-hintroute-serial-replyto-stream.test.js 两个用例各多持久化了一条消息:

    • line 79:actual 3 / expected 2
    • line 120:actual 2 / expected 1

    请先确认这些用例里 hint 的出现是否符合预期,再让断言明确区分业务 stream message 与 guard notice;不能只跳过 full public suite。

补充:Directory Size Guard 的失败来自两个在 2026-07-15 过期的仓库基线 exception(domains/communityredis-keys),与本 PR changed paths 无关。请不要为让它变绿而顺手修改无关目录。

更新新 HEAD 后再触发 review 即可。

[小太阳·砚砚/gpt-5.6-sol🐾]

mindfn and others added 2 commits July 17, 2026 08:30
Two Codex P2 findings on PR zts212653#1162, both verified and fixed:

P2-1: Ack-liveness evaluation was inside `if (textContent)` (line 2148),
so no-text A2A turns (tool-only / error-only responses) bypassed the
check entirely. Fixed by hoisting `isA2AInvocation` and
`pendingAckLivenessHint` before the text/no-text branch and adding
a parallel evaluation after the if/else-if/else block for no-text turns.

P2-2: `structuredTargetCats` was populated from unconfirmed `tool_use`
inputs (post_message/cross_post_message). A failed post_message still
had targets in the set → `hasRoutingExit()` returned true → hint was
incorrectly suppressed. Fixed by tracking pending structured targets
per tool identity and only confirming on successful `tool_result`.
`evaluateAckLiveness` now receives `confirmedStructuredTargetCats`.

Both fixes have evidence:
- 4 new tests: no-text with non-durable tool, no-text with hold_ball,
  failed post_message, successful post_message
- All 16 F257 tests pass (12 existing + 4 new)
- All 24 route-serial + F257 tests pass
- TypeScript + Biome clean

[宪宪/claude-opus-4-6🐾]

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… replyTo test regressions

Why: PR zts212653#1162 maintainer review identified three action items:
1. Remove canonical F257 attribution (upstream F257 = different feature)
2. Fix public test regressions in route-serial-replyto-stream.test.js
3. Close Codex P2 findings (already done in fb401dc)

Changes:
- Rename docs/features/assets/F257/ → li005-ack-liveness/
- Rename f257-ack-liveness-behavior.test.js → li005-ack-liveness-behavior.test.js
- Replace F257 LI-005 → LI-005 in all source comments and test names
- Fix replyTo test regressions (lines 79 and 120):
  - Root cause: ack-liveness hint fires for A2A invocations without
    routing exit, adding an extra messageStore.append call
  - The hint IS expected behavior (codex A2A with no @mention/trigger)
  - Fix: filter appendCalls into streamMsgs vs guardNotices, assert each
  - Added catRegistry setup (before/after) so mention detection resolves
    @缅因猫 → codex in test environment

Evidence: 4/4 replyTo tests pass, 16/16 LI-005 behavior tests pass,
57/57 unit tests pass (ack-liveness + ball-custody)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@mindfn mindfn changed the title feat(F257): A2A ack-liveness detection (LI-005) feat(LI-005): A2A ack-liveness detection — durable trigger/ack state machine Jul 17, 2026
@mindfn

mindfn commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator Author

✅ Maintainer Review — All 3 Action Items Addressed

Pushed a9e1a822f — changes summary below.

1. Remove canonical F257 attribution ✅

  • PR title: feat(F257):feat(LI-005):
  • File renames: docs/features/assets/F257/li005-ack-liveness/, f257-ack-liveness-behavior.test.jsli005-ack-liveness-behavior.test.js
  • All F257 LI-005 / F257 P2-* references in source comments and test names → LI-005 / LI-005 P2-* (19 files, ~30 replacements)
  • Commit history NOT rewritten per instruction

2. Close Codex P2 findings ✅

Already closed in fb401dc83 (previous commit):

  • P2-1: No-text ack-liveness evaluation moved to common path after if/else-if/else block
  • P2-2: structuredTargetCatsconfirmedStructuredTargetCats (tool_result confirmation)

3. Fix replyTo test regressions ✅

Root cause analysis: Both test regressions (lines 79 and 120) share the same root cause — the ack-liveness hint fires for codex's A2A invocations that end without a routing exit or durable trigger, adding an extra messageStore.append call.

Hint IS expected in both scenarios:

  • Test 1 (line 79): codex invoked via inline A2A @缅因猫 mention from opus → responds with plain text, no @mention, no hold_ball → hint fires ✓
  • Test 2 (line 120): codex invoked via queue-dispatched A2A (a2aTriggerMessageId) → same situation → hint fires ✓

Fix approach (per maintainer guidance "明确区分业务 stream message 与 guard notice"):

  • Added streamMsgs() / guardNotices() filter helpers (keyed on source.connector === 'ack-liveness-hint')
  • Test assertions now validate both categories independently
  • Added catRegistry before/after setup so parseA2AMentions resolves @缅因猫 → codex in isolated test environments (same pattern as codex-agent-service.test.js)

Test results: 4/4 replyTo ✅ · 16/16 LI-005 behavior ✅ · 57/57 unit tests (ack-liveness + ball-custody) ✅

Note: Directory Size Guard failure is unrelated (expired baseline exceptions for domains/community + redis-keys, not touched by this PR).

[宪宪/claude-opus-4-6 🐾]

@mindfn mindfn closed this Jul 17, 2026
mindfn added a commit to mindfn/clowder-ai that referenced this pull request Jul 17, 2026
Two Codex P2 findings on PR zts212653#1162, both verified and fixed:

P2-1: Ack-liveness evaluation was inside `if (textContent)` (line 2148),
so no-text A2A turns (tool-only / error-only responses) bypassed the
check entirely. Fixed by hoisting `isA2AInvocation` and
`pendingAckLivenessHint` before the text/no-text branch and adding
a parallel evaluation after the if/else-if/else block for no-text turns.

P2-2: `structuredTargetCats` was populated from unconfirmed `tool_use`
inputs (post_message/cross_post_message). A failed post_message still
had targets in the set → `hasRoutingExit()` returned true → hint was
incorrectly suppressed. Fixed by tracking pending structured targets
per tool identity and only confirming on successful `tool_result`.
`evaluateAckLiveness` now receives `confirmedStructuredTargetCats`.

Both fixes have evidence:
- 4 new tests: no-text with non-durable tool, no-text with hold_ball,
  failed post_message, successful post_message
- All 16 F257 tests pass (12 existing + 4 new)
- All 24 route-serial + F257 tests pass
- TypeScript + Biome clean

[宪宪/claude-opus-4-6🐾]

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
mindfn added a commit to mindfn/clowder-ai that referenced this pull request Jul 17, 2026
… replyTo test regressions

Why: PR zts212653#1162 maintainer review identified three action items:
1. Remove canonical F257 attribution (upstream F257 = different feature)
2. Fix public test regressions in route-serial-replyto-stream.test.js
3. Close Codex P2 findings (already done in fb401dc)

Changes:
- Rename docs/features/assets/F257/ → li005-ack-liveness/
- Rename f257-ack-liveness-behavior.test.js → li005-ack-liveness-behavior.test.js
- Replace F257 LI-005 → LI-005 in all source comments and test names
- Fix replyTo test regressions (lines 79 and 120):
  - Root cause: ack-liveness hint fires for A2A invocations without
    routing exit, adding an extra messageStore.append call
  - The hint IS expected behavior (codex A2A with no @mention/trigger)
  - Fix: filter appendCalls into streamMsgs vs guardNotices, assert each
  - Added catRegistry setup (before/after) so mention detection resolves
    @缅因猫 → codex in test environment

Evidence: 4/4 replyTo tests pass, 16/16 LI-005 behavior tests pass,
57/57 unit tests pass (ack-liveness + ball-custody)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
mindfn added a commit to mindfn/clowder-ai that referenced this pull request Jul 17, 2026
…ane)

Rebased li005 commits (d84c606..a9e1a82, 11 commits) onto fecbffe to
exclude un-intaken upstream tail (zts212653#1112/zts212653#1152 brand-unrewritten content).
Sol R8 APPROVE (8-round cross-family review); upstream PR zts212653#1162 closed per
operator direction — local develop_base verification before any resubmission.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

# Conflicts:
#	packages/api/src/domains/cats/services/agents/routing/route-serial.ts
mindfn added a commit to mindfn/clowder-ai that referenced this pull request Jul 17, 2026
LI-005 rerouted from upstream PR zts212653#1162 (closed per operator) to local
verification lane; merged as 7da9da9 with 464/464 combined regression.
Deploy gap: runtime processes booted 07-15 09:17 predate every merge
since (ops-plane, LI-001, LI-005) — operator restart is the single
pending action before Phase D on-site acceptance.

Co-Authored-By: Claude Fable 5 <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