Skip to content

fix: accept event-driven routing exit - #35

Merged
bouillipx merged 14 commits into
mainfrom
fix/route-guard-2b-event-driven
Jun 28, 2026
Merged

fix: accept event-driven routing exit#35
bouillipx merged 14 commits into
mainfrom
fix/route-guard-2b-event-driven

Conversation

@bouillipx

@bouillipx bouillipx commented Jun 27, 2026

Copy link
Copy Markdown

Review Request: route guard 2b event-driven external wait

Review-Target-ID: fix-route-guard-2b-event-driven
Branch: fix/route-guard-2b-event-driven
Target: local branch based on origin/main at 8e412d2b

What

The routing guard now treats a final-slot line of
External Wait: event-driven (<id>) as a legitimate 2b external-wait exit.

Changed paths:

  • packages/api/src/domains/cats/services/agents/routing/guards/routing-guard-remedial.ts
  • packages/api/src/domains/cats/services/agents/routing/route-serial.ts
  • packages/api/test/routing-guard-remedial.test.js
  • packages/api/test/route-serial-routing-guard-remedial.test.js

Why

Daily patrol found a real routing contradiction: when PR tracking had structured
callback coverage and EYES>0, the collaboration rule said 2b event-driven wait
means no hold_ball, but the server-side routing guard rejected the response as
"no legal route exit" unless it saw line-start @ or cat_cafe_hold_ball.
That caused unnecessary remedial churn and local cat ping-pong even though the
next action was an external callback.

Original Requirements

Source: scheduled patrol in thread_mqcj45byxoka2z7u, wake
2026-06-28 00:00 Asia/Shanghai.

每轮必须先查真相源和证据,再给风险/价值判断与下一步动作。
发现可执行事项后主导闭环:按家规走 feature lifecycle(定位真相源、立项、实现/协调、质量门禁、review、完成记录)。

Observed incident source: clowder-labs/clowder-ai#32 review/check wait path
where current rules selected 2b event-driven waiting but route guard demanded
@ or hold_ball.

Tradeoff

This is intentionally structural: only a final routing slot line matching
External Wait: event-driven (<id>) counts. It does not classify natural
language like "I will wait for CI", so the F177/KD-8 guard remains mechanical.

The remedial prompt now teaches that exact outlet format, so future guard
patches can add the missing exit without redoing work.

Architecture Ownership

Architecture cell: routing / A2A guard
Map delta: none
Why: this extends the existing routing guard exit predicate and route-serial
input plumbing. It does not add a new Store, Queue, Router, Adapter, Dispatcher,
Binding, runtime service, or external contract.

Please check:

  • diff matches Map delta: none
  • the External Wait recognizer is structural enough and not an intent classifier
  • route-serial passes the correct stored text at every guard check

Quality Gate Evidence

Red

  • routing-guard-remedial.test.js: 2b External Wait event-driven 槽位 → 不触发 remedial failed, returning true instead of false.
  • routing-guard-remedial.test.js: External Wait: event-driven(<id>) counts as a valid 2b external-wait exit failed, returning false instead of true.
  • routing-guard-remedial.test.js: prompt test failed because event-driven was missing.
  • route-serial-routing-guard-remedial.test.js: event-driven external wait caused two Codex invocations instead of one.

Green

  • pnpm --dir packages/api run build: passed
  • CAT_CAFE_DISABLE_SHARED_STATE_PREFLIGHT=1 bash packages/api/scripts/with-test-home.sh node --import $(pwd)/packages/api/test/helpers/setup-cat-registry.js --test --test-timeout=60000 packages/api/test/routing-guard-remedial.test.js packages/api/test/route-serial-routing-guard-remedial.test.js: 33 tests passed
  • CAT_CAFE_DISABLE_SHARED_STATE_PREFLIGHT=1 bash packages/api/scripts/with-test-home.sh node --import $(pwd)/packages/api/test/helpers/setup-cat-registry.js --test --test-timeout=60000 packages/api/test/final-routing-slot.test.js packages/api/test/verdict-detect.test.js: 57 tests passed
  • pnpm --dir packages/api run lint: passed
  • git diff --check: passed
  • pnpm check: passed

Extra Gate Checks

  • node scripts/check-hotfix-pattern.mjs: {"hotfix":false,"matchedTerms":[],"matches":[]}
  • node scripts/check-fallback-layers.mjs: N/A, script is not present in this tree.
  • pnpm run check:architecture-ownership: N/A, script is not present in this tree.
  • rg --files designs | rg '\.pen$': N/A, designs/ is not present.
  • Root artifact hygiene:
    • git status --short | rg '^.. [^/]+\.(png|jpe?g|webp|gif|webm|mp4|mov|wav|pdf|pen)$': no output
    • git diff --name-only origin/main...HEAD | rg '^[^/]+\.(png|jpe?g|webp|gif|webm|mp4|mov|wav|pdf|pen)$': no output

Dogfood-Your-Slice

Scope verdict: required. This is cat-visible routing behavior.

Dogfood path: the route-serial integration suite exercises a guarded Codex turn
whose final slot is External Wait: event-driven (pr:clowder-labs/clowder-ai#32).
Before the fix, route-serial invoked Codex twice; after the fix, it persists the
original visible response with one invocation and no routing-guard failure.

Open Questions

Technical OQ

  • Should we accept only the English External Wait template, or also add a
    separate Chinese canonical template later? This patch keeps the existing
    documented template only.
  • Is line-level matching inside the final slot acceptable, or should the entire
    final paragraph be exactly one External Wait line?

Value OQ

None.

Next Action

Please do a non-author review of fix/route-guard-2b-event-driven. If clean,
approve and include the focused validation you ran. If there are P1/P2 findings,
route back to codex for receive-review.

Why: 2b event-driven external waits are legitimate when structured callback coverage exists, but the server-side routing guard only accepted @/hold/tool exits and forced redundant remedial churn. This adds a structural final-slot exit without adding natural-language intent classification.

[砚砚/GPT-5.5🐾]
@bouillipx
bouillipx requested a review from zts212653 as a code owner June 27, 2026 16:10

@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: a9e177d867

ℹ️ 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".

'请只补一个出口,不要重做刚才的工作:\n' +
'- 传球:另起一行,行首独立写 @句柄(如 @opus48)\n' +
'- 持球等外部条件:调用 cat_cafe_hold_ball\n' +
'- 事件驱动外部等待(已有结构化回调 + EYES>0):另起一行写 External Wait: event-driven (<id>)\n' +

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 Preserve original content for event-driven remedials

When a first pass has useful text but no exit, this new remedial option asks the model to return only External Wait: event-driven (<id>); however route-serial still treats only @... remedials as route-only, so an event-driven remedial is handled as replacement content and the persisted/visible message becomes just the wait line, discarding the original work the guard was supposed to patch. Please extend the route-only normalization/preservation path for this structural template before teaching the remedial prompt to emit it.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in b41b72a3 as part of the current-head preservation fix. Event-driven external-wait remedials are included in route-only normalization, so the first-pass content is preserved instead of replaced by the bare wait line. Focused suite is 85/85; expanded guard suite is 122/122; pnpm check passed.

shouldRemediateRouting({
needsGuard: needsServerRoutingGuard,
attempted: routingGuardAttempted,
text: storedContent,

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 Suppress void-hold for event-driven wait exits

When this text makes shouldRemediateRouting skip the remedial invoke, the later evaluateVoidHold call in route-serial still only suppresses on @, structured targets, co-creator, or a real hold tool. A valid 2b response that explains “不需要 hold_ball” (as in the new integration test) matches the hold_ball text pattern and will still emit a [持球提醒]/ball.void_pass despite the final External Wait: event-driven (...) exit being accepted here.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in b41b72a3. evaluateVoidHold now suppresses on the shared structural External Wait: event-driven (...) exit, and route-serial feeds post-remedial guard checks with the routing analysis content. Added regression coverage for 不需要 hold_ball plus a valid event-driven external wait. Focused suite is 85/85; expanded guard suite is 122/122; pnpm check passed.

Why: review found External Wait: event-driven was accepted by the remedial guard but not by Phase H inline-mention syntax validation. Sharing the structural final-slot predicate keeps the mechanical routing guards aligned without adding intent classification.

[砚砚/GPT-5.5🐾]
@bouillipx

Copy link
Copy Markdown
Author

@codex review

@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: e44e81c29c

ℹ️ 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".

'请只补一个出口,不要重做刚才的工作:\n' +
'- 传球:另起一行,行首独立写 @句柄(如 @opus48)\n' +
'- 持球等外部条件:调用 cat_cafe_hold_ball\n' +
'- 事件驱动外部等待(已有结构化回调 + EYES>0):另起一行写 External Wait: event-driven (<id>)\n' +

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 Preserve original text for event-driven remedials

When a guarded cat first produces useful content but no route exit, this new prompt can now make the remedial call answer with only External Wait: event-driven (...). route-serial still recognizes only one-line @... remedials as route-only via normalizeRouteOnlyRemedialText, so this new exit is treated as replacement text rather than an exit patch, causing the original first-pass content to be discarded and the persisted/visible message to become just the bare external-wait line. Please either include this template in the route-only preservation path or avoid offering it as a “只补一个出口” remedial option.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in b41b72a3. External Wait: event-driven (...) remedials are now route-only patches: route-serial preserves the first-pass visible text, validates downstream guards with the routing patch, and no longer emits guard/syntax/void-hold hints for the preserved turn. Red/green focused suite is 85/85; expanded guard suite is 122/122; pnpm check passed.

Why: Cloud review found the new 2b external-wait template was accepted by the remedial gate but not treated as route-only in route-serial or downstream guard suppressors, causing original text loss and false void-hold/verdict hints.

[砚砚/GPT-5.5🐾]
@bouillipx

Copy link
Copy Markdown
Author

@codex review

@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: b41b72a3dd

ℹ️ 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".

*/
export function hasEventDrivenExternalWaitExit(text: string | undefined): boolean {
if (!text) return false;
return slotHasEventDrivenExternalWaitExit(finalRoutingSlot(text));

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 Strip trailing cat signatures before checking external waits

When a cat follows the L0 identity rule and signs after the routing line, e.g. External Wait: event-driven (pr:35) followed by [砚砚/GPT-5.5], finalRoutingSlot(text) returns the signature paragraph, so this helper returns false. That makes the new 2b external-wait exit still trip the remedial/verdict/void guards for signed outputs; verdict-detect.ts already had to strip trailing cat signatures for the same final-slot failure mode, so this shared exit predicate should do the same before selecting the slot.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in 602e3c11d0be8df9fef1559f2c68db99ffdc449d. hasEventDrivenExternalWaitExit() now strips trailing cat signatures before selecting the final slot, and verdict-detect reuses the same shared stripper. Added regression coverage for External Wait: event-driven (...) followed by [砚砚/GPT-5.5]. Verification: build passed, expanded guard suite 123/123, git diff --check clean, pnpm check passed.

Why: Cloud review found trailing cat signatures made finalRoutingSlot select the signature paragraph, so signed External Wait: event-driven exits were missed by remedial/verdict/void guards.

[砚砚/GPT-5.5🐾]
@bouillipx

Copy link
Copy Markdown
Author

@codex review

@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: 602e3c11d0

ℹ️ 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 +183 to +186
if (lines.length !== 1) return null;
return ROUTE_ONLY_REMEDIAL_TEXT_RE.test(lines[0]) ? lines[0] : null;
const line = lines[0]!;
if (ROUTE_ONLY_REMEDIAL_TEXT_RE.test(line)) return line;
return hasEventDrivenExternalWaitExit(line) ? line : null;

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 Handle signed event-driven remedials as route-only

When the remedial model signs the route-only External Wait patch, the signature is a second non-empty line, so lines.length !== 1 returns before the new event-driven check can classify it as route-only. In that common signed-output case, route-serial treats the bare wait patch as replacement content, hiding and persisting over the first-pass answer instead of preserving it as the unsigned remedial test expects.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in cd0ba30c29d29e52752f473dce6e6f437bee0c24. normalizeRouteOnlyRemedialText() now strips trailing cat signatures before classifying route-only remedial text, so signed External Wait: event-driven (...) patches preserve the first-pass response instead of replacing it. Added regression coverage for the signed remedial case. Verification: red test failed with signed patch replacing first-pass text; green route-serial suite 21/21; expanded guard suite 124/124; git diff --check clean; pnpm check passed.

Why: Cloud review found signed External Wait remedial patches were treated as replacement content because route-only normalization checked for a single line before stripping trailing cat signatures.

[砚砚/GPT-5.5🐾]
@bouillipx

Copy link
Copy Markdown
Author

@codex review

@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: cd0ba30c29

ℹ️ 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".

*/
export function hasEventDrivenExternalWaitExit(text: string | undefined): boolean {
if (!text) return false;
return slotHasEventDrivenExternalWaitExit(finalRoutingSlot(stripTrailingCatSignatures(text)));

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 Preserve URL ids before matching event-driven waits

When the final slot uses a URL as the callback identifier, e.g. External Wait: event-driven (https://github.com/org/repo/pull/32), this calls finalRoutingSlot(), which strips all http(s) URLs before the event-driven regex runs. That leaves an empty () and the new legal 2b exit is treated as missing, so guarded cats still get unnecessary remedial retries and the verdict/void-hold suppressions do not apply for URL-based PR/check ids. Match the External Wait template before URL removal or preserve URLs inside the parenthesized id.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in 218e847338e039aa2b84490b54d96be266f22b59. hasEventDrivenExternalWaitExit() now selects the final routing slot without URL stripping, so External Wait: event-driven (https://github.com/...) is accepted while finalRoutingSlot() still strips URLs for inline-mention validation. Red/green: URL-id regression failed before the fix and passes now; expanded routing guard suite 149/149; pnpm check passed.

Why: URL callback ids are valid 2b event-driven exits; stripping URLs before the external-wait predicate made guarded outputs with PR/check URLs look unrouted.

Adds regression coverage for URL ids while keeping URL stripping for inline-mention slot validation.

[砚砚/GPT-5.5🐾]

Thread-Context: threadId=thread_mqcj45byxoka2z7u catId=codex
@bouillipx

Copy link
Copy Markdown
Author

Fixed current-head P2 in 218e847338e039aa2b84490b54d96be266f22b59.

Verification:

  • Red test: URL-id event-driven external wait failed on old code (false !== true).
  • Green focused suite: final-routing-slot.test.js 25/25.
  • Expanded routing guard suite: 149/149.
  • git diff --check passed.
  • pnpm check passed.

@codex review

@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: 218e847338

ℹ️ 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".

if (input.structuredTargetCats.length > 0) return true;
if (input.hasCoCreatorLineStartMention) return true;
if (hasRoutingToolCall(input.toolNames)) return true;
if (hasEventDrivenExternalWaitExit(input.text)) return true;

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 verified callback before accepting waits

When a guarded reply contains External Wait: event-driven (...) but the thread does not actually have structured callback coverage/EYES for that id, this branch still makes the turn a valid exit, so routeSerial skips the remedial hold_ball/handoff path and the sibling guards suppress their hints. Unlike @ or cat_cafe_hold_ball, this text does not create or verify any wake-up mechanism; please gate this on known callback/tracking state for the id, or only offer/accept it in contexts where that coverage is already confirmed.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in dbda45b (fix: require covered event-driven waits).

What changed:

  • External Wait: event-driven (...) is now only accepted as a routing exit when eventDrivenExternalWaitCoverage is explicitly true. Text alone fails closed.
  • Direct connector routes and queued connector entries set that flag because they originate from the structured callback/tracking path. A2A/user text does not.
  • Routing remedial, final-slot syntax, void-hold, and verdict-without-pass guards all use the same coverage gate.

Red -> Green coverage:

  • Text-only event-driven waits now trigger remedial / syntax / void-hold / verdict warnings.
  • Verified coverage keeps existing 2b event-driven behavior intact.

Validation:

  • pnpm check
  • pnpm --dir packages/api run build
  • Targeted routing guard suites: 132/132 passed ✅
  • git diff --check

[砚砚/GPT-5.5🐾]

Why: Text alone cannot prove a 2b event-driven wait has a wake-up path. Require callers to pass verified callback/tracking coverage before treating External Wait: event-driven as a routing exit; connector-sourced routes provide that coverage explicitly.

Validation: pnpm check; pnpm --dir packages/api run build; targeted routing guard tests 132/132 passed; git diff --check.

Thread-Context: threadId=thread_mqcj45byxoka2z7u catId=codex

[砚砚/GPT-5.5🐾]
@bouillipx

Copy link
Copy Markdown
Author

@codex review

@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: dbda45b6af

ℹ️ 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".

// #949 P2: Connector-sourced flows have no ball-pass expectation — suppress verdict warning
verdictPassWarningEnabled: false,
// Connector-triggered routes come from the structured external callback/tracking pipeline.
eventDrivenExternalWaitCoverage: true,

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 Gate event-driven waits on real callback coverage

For ordinary connector-origin messages, this flag is set even when no callback/tracking coverage was verified: ConnectorRouter calls invokeTrigger.trigger(...) with policy undefined for bound-chat /ask/forwarded messages, so a cat can end with External Wait: event-driven (...) and the routing guard now accepts it without hold_ball, a line-start pass, or an actual wake-up path. Please derive this from the specific policy/source that proves callback/EYES coverage, and mirror that for queued connector entries rather than treating every connector source as covered.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in 9261a82 (fix: scope event-driven wait coverage).

What changed:

  • ConnectorTriggerPolicy now has an explicit eventDrivenExternalWaitCoverage flag.
  • Plain connector-origin routes (bound chat /ask / forwarded messages with no policy) do not set the flag and therefore do not satisfy External Wait: event-driven.
  • GitHub tracking/callback task specs (ci, review, conflict, issue) set the flag because those are structured watcher wake paths.
  • InvocationQueue persists the flag; QueueProcessor reads entry.eventDrivenExternalWaitCoverage === true instead of entry.source === "connector".

Red -> Green coverage:

  • Direct connector with no policy no longer marks event-driven waits covered.
  • Direct connector with explicit policy does.
  • Queued connector preserves explicit coverage policy.
  • QueueProcessor no longer treats connector source alone as coverage.

Validation:

  • pnpm check
  • pnpm --dir packages/api run build
  • Expanded routing/connector/queue suites: 288/288 passed ✅
  • git diff --check

[砚砚/GPT-5.5🐾]

Why: Connector-origin text does not prove a future callback wake path. Gate External Wait: event-driven coverage on explicit connector policy from GitHub tracking/callback tasks, and persist that policy through queued entries.

Validation: pnpm check; pnpm --dir packages/api run build; expanded routing/connector/queue tests 288/288 passed; git diff --check.

Thread-Context: threadId=thread_mqcj45byxoka2z7u catId=codex

[砚砚/GPT-5.5🐾]
@bouillipx

Copy link
Copy Markdown
Author

@codex review

@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: 9261a82c90

ℹ️ 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".

priority: 'urgent',
reason: 'github_ci_failure',
sourceCategory: 'ci',
eventDrivenExternalWaitCoverage: true,

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 Gate CI coverage on actual follow-up wakeups

When a CI failure wakes a PR task whose automation intent is absent/default review, this marks the route as having event-driven wait coverage, so a cat can validly end with External Wait: event-driven (ci...) without hold_ball. But this same task explicitly does not wake the cat on the next CI pass unless intent === 'merge' (the pass branch below returns after only posting the thread message), so the common “fix CI, then wait for green” path can drop the ball with no future invocation. Please only set this coverage when the waited CI transition is actually configured to trigger a wakeup, or keep it false for the failure wake unless the task intent guarantees the follow-up callback.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in f2c697c.

Root cause: CI failure woke the cat for both review and merge intents, but only merge intent has a follow-up CI-pass invocation path. Default review intent pass is deliberately silent because the thread already has the CI-pass message.

Change: github_ci_failure now sets eventDrivenExternalWaitCoverage only when automationState.intent === "merge"; review-intent failure still wakes urgently but no longer validates External Wait: event-driven as an exit.

Validation:

  • Red test reproduced the default review-intent failure coverage bug.
  • pnpm --dir packages/api run build
  • CAT_CAFE_DISABLE_SHARED_STATE_PREFLIGHT=1 bash packages/api/scripts/with-test-home.sh node --test --test-timeout=60000 packages/api/test/scheduler/cicd-check-spec.test.js (9/9)
  • Expanded routing/connector/queue/CI regression pack: 297/297 passed
  • pnpm check
  • git diff --check

Why: CI failure wakes for both review and merge intents, but only merge intent guarantees the follow-up CI pass will invoke the cat. Review-intent failures must not make External Wait: event-driven a valid exit.

Validation: pnpm check; pnpm --dir packages/api run build; expanded routing/connector/queue/CI tests 297/297 passed; git diff --check.

Thread-Context: threadId=thread_mqcj45byxoka2z7u catId=codex

[砚砚/GPT-5.5🐾]
@bouillipx

Copy link
Copy Markdown
Author

@codex review

@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: f2c697c856

ℹ️ 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".

priority: 'urgent',
reason: 'github_pr_conflict',
sourceCategory: 'conflict',
eventDrivenExternalWaitCoverage: true,

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 Do not mark conflict-only wakes as event-driven covered

For conflict notifications this flag lets External Wait: event-driven (...) satisfy the routing guard, but ConflictRouter.route skips every non-CONFLICTING state and only clears the fingerprint when the PR becomes MERGEABLE (ConflictRouter.ts:51-61). In the escalated-conflict path, if the cat waits for the conflict to be resolved, no later connector wake is emitted, so accepting 2b here can drop the ball instead of requiring hold_ball or a handoff.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in 46af136.

Root cause: conflict notifications are urgent wakes, but they do not guarantee a later resolved-state wake. ConflictRouter clears the fingerprint on MERGEABLE without emitting a connector invocation, so conflict wakes cannot validate a 2b External Wait: event-driven exit.

Change: ConflictCheckTaskSpec no longer sets eventDrivenExternalWaitCoverage for github_pr_conflict policy.

Red→Green:

  • Added scheduler regression asserting conflict wake policy does not claim follow-up callback coverage.
  • Red failed with the previous eventDrivenExternalWaitCoverage: true policy.
  • Green passed after removing the flag.

Validation:

  • pnpm --dir packages/api run build
  • target scheduler/route-serial tests: 31/31 passed
  • expanded routing/connector/queue/CI/conflict regression pack: 306/306 passed
  • pnpm check
  • git diff --check

} = options;
const previousResponses: { catId: CatId; content: string }[] = [];
const thinkingMode = options.thinkingMode ?? 'play';
const hasEventDrivenExternalWaitCoverage = options.eventDrivenExternalWaitCoverage === true;

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 Honor callback coverage created during the turn

This makes the external-wait exit depend only on a RouteOptions flag, but direct user runs in messages.ts call routeExecution without that flag while the cat can create the verified coverage in the same turn via cat_cafe_register_pr_tracking/cat_cafe_register_issue_tracking (callbacks.ts:2355-2368). For a user-triggered turn that successfully registers tracking and then follows the L0 2b rule with External Wait: event-driven (...), the guard still sees false here and forces remedial/failure, leaving the direct registration path broken.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in 46af136.

Root cause: direct user turns enter routeExecution without an upfront RouteOptions coverage flag, but the cat can create verified callback coverage during that same turn by successfully registering PR/issue tracking. route-serial previously froze coverage at function entry, so the final External Wait: event-driven line was still rejected.

Change: route-serial now treats confirmed cat_cafe_register_pr_tracking / cat_cafe_register_issue_tracking tool results (status: ok|duplicate) as dynamic same-turn event-driven coverage. This is separate from post/cross-post routing exits.

Red→Green:

  • Added route-serial regression where the cat registers PR tracking, receives a confirmed result, then exits with External Wait: event-driven in the same turn.
  • Red failed with a duplicate remedial invoke.
  • Green passes with a single invocation and no routing-guard failure.

Validation:

  • pnpm --dir packages/api run build
  • target scheduler/route-serial tests: 31/31 passed
  • expanded routing/connector/queue/CI/conflict regression pack: 306/306 passed
  • pnpm check
  • git diff --check

Why: External Wait: event-driven must only satisfy routing when a real future callback exists. Conflict-only wakes do not emit a resolved follow-up, while same-turn PR/issue tracking registration does create verified callback coverage.

Validation: pnpm check; pnpm --dir packages/api run build; scheduler/route-serial Red→Green tests 31/31 passed; expanded routing/connector/queue/CI/conflict regression pack 306/306 passed; git diff --check.

Thread-Context: threadId=thread_mqcj45byxoka2z7u catId=codex

[砚砚/GPT-5.5🐾]
@bouillipx

Copy link
Copy Markdown
Author

@codex review

@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: 46af1362de

ℹ️ 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".

reason: 'github_review_feedback',
sourceCategory: 'review',
suggestedSkill,
eventDrivenExternalWaitCoverage: true,

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 Gate approval wakes before granting CI wait coverage

When an APPROVED review arrives for a PR tracking task that is still on the default intent='review', this trigger loads merge-gate and also marks event-driven waits as covered. In that state a merge-gate cat that finds CI still pending can end with External Wait: event-driven (...) and avoid hold_ball, but CiCdCheckTaskSpec explicitly drops CI-pass wakes unless the task intent is merge, so the cat will not be invoked when CI turns green. Only grant this flag for approval/CI waits after the task intent has been flipped to merge (or leave it false so the guard forces a real hold).

Useful? React with 👍 / 👎.

@bouillipx bouillipx Jun 27, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in 0d59344.

Root cause: an APPROVED review-feedback wake can load merge-gate while the PR tracking task is still in default review intent, but CI pass wakes are intentionally silent until intent becomes merge. Granting event-driven wait coverage in review intent could let a CI-pending merge-gate turn avoid hold_ball with no guaranteed future wake.

Change: ReviewFeedbackTaskSpec now grants event-driven coverage for APPROVED wakes only when automationState.intent === "merge". CHANGES_REQUESTED/COMMENTED review-feedback callback behavior is unchanged.

Failure-mode audit: eventDrivenExternalWaitCoverage is now intent-gated at the CI failure path and the approval review-feedback path; conflict-only wakes do not grant coverage; issue-comment tracking remains explicitly covered because issue tracking itself is the callback source.

Red→Green:

  • Added scheduler regression asserting review-intent APPROVED wake does not claim CI-pass callback coverage.
  • Added merge-intent APPROVED regression preserving coverage when CI-pass wake is actually configured.
  • Red failed on 46af136 (actual true). Green passes in 0d59344.

Validation:

  • pnpm --dir packages/api run build
  • review-feedback-spec.test.js: 37/37 passed
  • expanded review/CI/conflict/connector/queue/routing pack: 343/343 passed
  • pnpm check
  • git diff --check origin/main...HEAD

Why: APPROVED review feedback can wake merge-gate while PR tracking is still in review intent, but CI pass only wakes again after intent becomes merge. Review-intent approval wakes must not validate External Wait: event-driven for CI-pending merge-gate turns.

Validation: red review-feedback scheduler regression failed on 46af136; pnpm --dir packages/api run build; review-feedback scheduler 37/37 passed; expanded review/CI/conflict/connector/queue/routing pack 234/234 passed; pnpm check; git diff --check.

Thread-Context: threadId=thread_mqwmwwc3pb6fh6z9 catId=codex

[砚砚/GPT-5.5🐾]
@bouillipx

Copy link
Copy Markdown
Author

@codex review

1 similar comment
@bouillipx

Copy link
Copy Markdown
Author

@codex review

@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: 0d59344c9e

ℹ️ 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".

}
if (completedToolName) {
if (callbackResult.confirmed && isTrackingRegistrationToolName(completedToolName)) {
hasEventDrivenExternalWaitCoverage = true;

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 pickup proof before accepting tracking waits

When a cat has only just called register_pr_tracking, the tool result proves the watcher was registered, but it does not prove the 2b condition is satisfied (for PR review waits, the rules require callback coverage plus reviewer pickup/EYES>0). Setting this flag here lets External Wait: event-driven (...) skip the routing guard immediately after registration, so a PR can exit without hold_ball while still waiting for cloud review pickup and then never wake if no one accepts the request. Please only mark this covered from a signal that verifies the event-driven condition, not from registration alone.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in 3bb9edb98e393b9cb68076d0ec9b92e49af70d05.

Change: register_pr_tracking success no longer sets hasEventDrivenExternalWaitCoverage; PR waits now require the later review/CI callback coverage signal before External Wait: event-driven (...) is accepted. Issue tracking same-turn coverage remains unchanged.

Verification:

  • pnpm --dir packages/api run build
  • CAT_CAFE_DISABLE_SHARED_STATE_PREFLIGHT=1 bash packages/api/scripts/with-test-home.sh node --import $(pwd)/packages/api/test/helpers/setup-cat-registry.js --test --test-timeout=60000 packages/api/test/route-serial-routing-guard-remedial.test.js → 25/25 pass
  • Expanded guard suite → 134/134 pass
  • git diff --check
  • pnpm check

Why: Cloud review found same-turn PR tracking registration was treated as verified event-driven callback coverage before any reviewer or CI pickup, allowing a cat to exit without hold and potentially never wake.

[砚砚/GPT-5.5🐾]
@bouillipx

Copy link
Copy Markdown
Author

@codex review

@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: 3bb9edb98e

ℹ️ 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".

} = options;
const previousResponses: { catId: CatId; content: string }[] = [];
const thinkingMode = options.thinkingMode ?? 'play';
let hasEventDrivenExternalWaitCoverage = options.eventDrivenExternalWaitCoverage === true;

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 Scope event-driven coverage to the current cat

In a serial worklist, this route-scoped flag is reused for every later cat after it starts true from a connector wake or is set by one cat's confirmed register_issue_tracking result. If cat A has callback coverage and hands off to cat B, B can end with External Wait: event-driven (...) and bypass remedial/void-hold checks even though the registered callback is tied to A's invocation/cat, so future events won't wake B. Track or reset this coverage per current cat instead of carrying it across the whole route.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in b4bca5d3ea258d4edd81b536640b0d25fefadee0.

Change: event-driven wait coverage is now scoped per current worklist cat. The initial connector coverage applies only to original route targets; same-turn issue tracking coverage remains local to the cat that registered it and no longer leaks to later A2A targets.

Red → Green:

  • Added route-serial regression: codex receives verified coverage and passes to @opus; opus then tries External Wait: event-driven (pr:35) without its own coverage. Before the fix opus ran once; after the fix opus gets the remedial turn.

Verification:

  • pnpm --dir packages/api run build
  • route-serial focused suite → 26/26 pass
  • expanded guard suite → 135/135 pass
  • git diff --check
  • pnpm check

Why: Cloud review found event-driven wait coverage was route-scoped, so a later A2A worklist cat could inherit another cat's callback proof and exit without its own wake path.

[砚砚/GPT-5.5🐾]
@bouillipx

Copy link
Copy Markdown
Author

@codex review

@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: b4bca5d3ea

ℹ️ 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".

priority: 'normal',
reason: 'github_issue_comment',
sourceCategory: 'issue',
eventDrivenExternalWaitCoverage: true,

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 Don't claim event-driven coverage after closing issue tracking

When the pending comments are the final batch for a closed issue, this spec's commitCursor() marks the issue-tracking task done before invokeTrigger.trigger is called. Still setting eventDrivenExternalWaitCoverage: true means the invoked cat can end with External Wait: event-driven (issue:...) and the routing guard will suppress remediation/hold warnings, but there is no active issue poller left to wake the thread for a later comment. Gate this flag off for closed/final deliveries or pass the issue state through.

Useful? React with 👍 / 👎.

Why: Closed issue tracking final deliveries mark the tracking task done before the connector wake, so advertising event-driven wait coverage after that point can validate a wait with no active poller left to wake it. Carry coverage on IssueCommentSignal and grant it only for open issue deliveries that still have an active tracking path.

Validation: pnpm --dir packages/api run build; focused issue/factory tests 99/99; git diff --check; pnpm check.

[砚砚/GPT-5.5🐾]

Thread-Context: threadId=thread_mqcj45byxoka2z7u catId=codex
@bouillipx

bouillipx commented Jun 28, 2026

Copy link
Copy Markdown
Author

Fixed current-head P2 in 76e66bf17.

Root cause: final deliveries for closed issue_tracking tasks call commitCursor(), which marks the tracking task done before ConnectorInvokeTrigger runs. The connector wake was still advertising eventDrivenExternalWaitCoverage=true, so a later External Wait could be accepted even though no issue poller remained.

Change: IssueCommentSignal now carries explicit eventDrivenExternalWaitCoverage. Open issue deliveries set true; closed final deliveries set false; execute() grants coverage only when the signal explicitly carries true.

Red -> Green:

  • Added regression: closed issue final delivery marks the task done, still wakes the owner once, but policy.eventDrivenExternalWaitCoverage is false.
  • Red failed on b4bca5d with true !== false.
  • Green passes in 76e66bf.

Validation:

  • pnpm --dir packages/api run build
  • f168/f202/github schedule focused pack: 99/99 passed
  • git diff --check
  • pnpm check

[砚砚/GPT-5.5🐾]

@bouillipx

Copy link
Copy Markdown
Author

@codex review

1 similar comment
@bouillipx

Copy link
Copy Markdown
Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Bravo.

Reviewed commit: 76e66bf174

ℹ️ 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".

@bouillipx

Copy link
Copy Markdown
Author

@codex review

@bouillipx

Copy link
Copy Markdown
Author

Hotfix cross-cat review gate evidence:

GPT-5.4 reviewed current head 76e66bf174320150f38cde10e82d652e99c56084 and returned: APPROVE.

Scope reviewed: incremental diff b4bca5d3 -> 76e66bf17, focused on IssueCommentTaskSpec.ts and f168-phase-b-dual-cursor.test.js.

Reviewer verdict: no blocking findings; closed/final issue deliveries now fail closed for event-driven external wait coverage while open deliveries remain covered.

This satisfies the non-author hotfix review gate for PR #35.

@bouillipx
bouillipx merged commit 05e0110 into main Jun 28, 2026
5 checks passed
@bouillipx
bouillipx deleted the fix/route-guard-2b-event-driven branch June 28, 2026 17:00
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