Repository navigation
Conversation
Every connector input used to reach the user through a two-phase shape: append a Message, then separately enqueue its Queue row. Nothing made the pair atomic, so a crash or a rejection between the halves left a message nobody was asked to look at, or a queued row with no message behind it. This registers the target contract before any producer moves onto it: one atomic Message+Queue admission per input, the closed set of producers that must migrate, and the replay cutover for rows written under the old shape. Registering the set up front is what makes Phase I closeable -- the alternative is discovering producer #7 by grep after declaring victory. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Connector inputs, scheduled wakes and hold wakes, and the manual eval trigger and CI wake all now hand over an envelope to a single admission seam instead of driving the append and the enqueue themselves. The separate trigger seam stops being a second way in. Two truthfulness fixes belong with this move. Owner provenance now fails closed rather than inheriting strict from whatever context happened to be around -- an unproven owner must not be silently upgraded. And a manual trigger now reports what its admission actually did, instead of reporting success for work that was refused. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Private input picked its winner from the Queue row, so the decision died with the row and a retry could reach a different answer. The winner is now bound to the admission receipt, and the notice commits together with the work rather than as a separate best-effort write. Also here: an external input keeps the person as its actor, not the room it arrived in; the message index moves into its own module so the queue ledger is no longer two concerns in one file; and the delivery wiring is expressed so the compiler owns it -- that wiring is exactly what the migration kept getting wrong by hand, and a type error is cheaper than a lost message. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…d set Conflict-check, limb transcripts, github-wait and the re-eval carrier each move to a single admission, one producer at a time, with the producer registry updated as each is taken. Working against a registered set is the point: each commit closes a named item rather than whatever grep turned up that day. The recurring defect across all four was the same -- a second admission that nobody had noticed was there, or a conflict labelled at a site that never looked. Two test fixes belong to that: one suite was exercising a trigger production never wired, and conflict-check's AC-C1 was restored by splitting authorization from announcement, which is what made the duplicate admission visible in the first place. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…sent The wait outbox decided whether to publish after it had already started sending, so a suppression could not actually suppress. The decision now happens at a claim taken before any send. The claim is deliberately kept out of the delivery enum. Encoding it there made a rollback unable to express 'claimed but not delivered', which stranded the row in a state no path could clear. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…e trigger chain The managed wake is the last registered producer. It is admitted once, with its lease checked before anything is written, so a superseded generation cannot write at all. Three defects found by cross-individual review are fixed with it: legacy adoption had lost its lease fence; a post-commit notification could un-admit a commit that had already landed; and the drain was sequenced behind a UI broadcast, so a failing socket fan-out left a durable wake that nothing asked anyone to look at. With every producer migrated, ConnectorInvokeTrigger has no producers left, and the whole chain is deleted -- zero references under src/. The two-phase 'append then enqueue' shape is gone from every production path, which closes Phase I. Finally, connector framing is preserved on envelopes delivered through the Queue. Without it the envelope arrives but renders as plain text: the hold-ball card, its run state and its cancel control all disappear. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…l-closed guard Category: accepted review finding "Reject coerced lifecycle response statuses", plus two sibling sites the review did not reach. All three are new in this PR. `[...].includes(String(candidate.x))` reads as a fail-closed literal check and is not one. `String(['completed'])` is `'completed'`, and any object whose `toString` returns a literal passes too, so a value that is not a string walks through a guard whose entire job is to reject it. These guards sit on recovered Redis rows and websocket frames — `RedisMessageStore`, `redis-message-parsers` and `useSocket` all gate on the shared one — so the smuggled value lands in a field whose contract is a string literal, and downstream matching silently stops working: it is neither `processing` nor a valid terminal, so projections can be suppressed and a terminal commit can conflict permanently. The fix is to compare the raw value, expressed once as `isOneOf` in the shared module rather than repaired inline at each call, so the next literal check cannot reintroduce the coercion by habit. Three sites, found by auditing the pattern across the tree instead of patching only the one that was reported: - `message-lifecycle.ts` response `status` — the reported instance. - `message-lifecycle.ts` delivery-failure `reason` — same function, one branch away. - `QueueLedgerValidation.assertQueueExecution` — the one that actually matters most: it is a throwing gate, and the coerced field is `ownerAuthProvenance`. An array was accepted as proven owner authorization. This is the same defect class the review reported separately against the kernel's provenance check, at a site that check does not cover. Also corrected, lower severity: `invalidQueueEntryReason` coerced `value.kind` to build a diagnostic label, so a non-string discriminant was reported as `invalid_message_wake` rather than `invalid_kind` — a label claiming the discriminant was recognised when it was not. Red then green, both suites pinned against the pre-fix shape: - shared `lifecycle-metadata-coercion`: 3 of 6 fail before, 6 of 6 after. Registered in `vitest.config.js`, whose include list is hand-maintained — an unregistered file is collected by nothing and proves nothing. - api `queue-ledger-provenance-coercion`: 2 of 4 fail before, 4 of 4 after. Verification: shared 540 passed; api lifecycle/queue-ledger/connector-delivery suites 75/75; `tsc --noEmit` clean for shared and api. Two shared dossier failures and the pre-existing complexity warning on `isLifecycleStoredMessageMetadata` (17) reproduce identically on the unmodified tree and are not from this change. Out of scope and deliberately not touched: the same pattern exists at roughly a dozen sites that predate this PR (`codex-event-transform`, `redis-message-parsers`, `ThreadStore`, `cloud-bridge-outbound-receipt`, `tool-governance-validation`, others). Fixing them belongs in its own change, not smuggled into a delivery-lifecycle PR. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…umer guess A finished hold kept offering "取消持球 / 取消并反馈", and one hold could show several cancel entries at once. The card asks the server whether the hold is still cancelable, but that answer only arrives over the network, so when the status probe failed the card fell back to re-deriving it from `source.meta.phase` — and that copy was wrong. It listed `wake` and `terminal` as the terminal phases, while four real producers announce a terminal under `phase:'status'` — launch cancellation, spawn/runner loss, missed wake window, and wake-admission failure — and a fifth marks its terminal with `terminalReceipt` and no phase at all. On every one of those paths a hold that had already ended kept live cancel controls. Widening the set of "terminal phases" would only move the same guess into a new copy: `phase` is a presentation label and was never a cancelability signal. Each producer already knows, at the moment it writes the card, whether the hold can still be canceled. It states that now, and the consumer stops deriving it. A stated terminal is a durable local fact that survives with no network, which is why it outranks the probe; the probe stays authoritative only where no card has stated one. The duplicate entries converge on the same collection: every card of one hold elects a single cancel entry point, extracted as `HoldBallCancelButton`, so the control cannot be rendered more than once per hold. Evidence, after two rounds of cross-individual review that both landed on the same weakness — claims that were reasoned about rather than run: - The producer contract test was inert. Written as `test/*.test.ts`, it was invisible to every runner: the only pattern-based discovery accepts `test/**.test.js`. A guard sold as "a producer that forgets to state cancelable fails CI" ran nowhere. It is plain JS now and is in the shard plan. - Stamping `cancelable` broke three exact-shape assertions on `source.meta`. Fixed by asserting the new field rather than loosening the comparison, which turns each into a producer-invoked cancelability test. - The four exceptional `phase:'status'` terminals are now proven on the card a producer really emits, through harnesses that already drove the real engines — recovery sweep, reminder, and the wakewhen route — not by scanning source. - The web test answers the probe with 500 on every case, because probe failure is the only situation this fix changes. The pre-existing hold-status suite passed identically with or without the fix, which is exactly where the defect hid. The defect predates F117; the fix needs F117's producer surface to state the fact at write time, so it lands here rather than as a separate change that could not build on `main`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
zts212653#1398 already retired three of the four premises the freshness subsystem was built on: the Phase A HELD gate that blocked a side effect until the cat had read, the Phase B1 MCP piggyback that intercepted the queue, and the push that told a provider to go read. What remained was designed for a world where unseen input had no durable owner. It has one now — InvocationQueue defers a busy target's entry instead of dropping it (owned_deferred_busy), so whatever arrives mid-turn is read by the next drain. This removes what that left standing. Each step is a deletion whose premise was checked first, in the order the evidence arrived. ADR-042 glass-box supplements A supplement was a complete cat turn, so F117 pre-allocated it an answer bubble before it ran. Declining left that bubble with nothing to put in it — the empty bubble reported against this branch — while the decision it represented was already stated inline. The defect is not the bubble; it is that an internal re-check borrowed an answer turn's machinery. Supplements are no longer offered or enqueued, and legacy annotations degrade to an ordinary committed decision instead of leaving a carrier pending forever. Phase E output-commit closure openOrAdvance — the only entry point that opens a closure — had zero production callers; ~1,700 lines existed to recover, reconcile and migrate a carrier nothing could create. Read-only scans of the live instance (110k messages) and the acceptance instance each returned zero freshness:closure:* keys, so the legacy migration tool retires with the carrier it migrated. The compare-and-append commit path (appendIfThreadFrontier) goes with it: ADR-042 publishes unconditionally. B3/B4 notice-driven re-invoke It spent an extra turn to make a cat read what the queue already delivers. The terminal hook still wrote metadata.freshnessReinvoke after its reader was removed. Phase D output-commit scan It ran once per answer turn and rewrote extra.freshness.kind to 'fresh' or 'freshness_unknown'. Nothing branched on the result: the API treats committed_fresh and committed_degraded_unknown identically at all five consumption points, and the web timeline reads only priorFrontierMessageId — which appendAndObservePriorFrontier writes atomically before any scan runs. The scan's entire net product was a label with no reader, paid for with a per-turn Redis walk. Kept, with the reason it survives - The frontier annotation: extra.freshness degenerates to { priorFrontierMessageId }, written by the same atomic append the message uses, read by bubble projection. Parsers ignore every retired verdict shape and keep only the frontier. - Provider-native freshness. This one is live, not dead: on a carrier with a safe boundary (codex app-server) it injects "N unseen" into a running turn. Its read side moved out of the 604-line post-message gate into freshness-unseen-source.ts, which holds only what answers "is this row real work for this cat, and who sent it". - The eval:freshness domain, decoupled from the deleted planes. Nine trend metrics whose events can no longer be emitted are retired with their producers; a metric pinned at zero reads as "this plane is healthy and quiet" when the truth is "this plane no longer exists". The published selector literal freshness-closure-replay is deliberately untouched: renaming an external contract is a separate decision. Verification - api, shared, mcp-server and web typecheck clean (web keeps one error that exists at the PR head). - 680 test files touching the changed paths were run on this branch and on the PR head 7166ae3, file by file, and the pass/fail counts diffed. Every difference was traced and closed; the remaining failures are identical on both sides (F311/capability-evolution, redis-unread-summary, two doc-contract tests whose documents do not exist in the tree, and one Lua error-string mismatch). - Web unit suite: 7255 pass, 5 fail, all five identical at the PR head. - Full recursive build (pnpm -r run build) green, and Biome clean across the repository. Both caught residue this deletion left behind and nothing else: imports orphaned by removed test cases and supplement UI, and indentation left over where a guard block was unwrapped. Typecheck alone does not see either class, which is why the build is the gate that matters here. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ifecycle feat(F117): unified message delivery lifecycle — Phase I fork acceptance candidate
Why: queued input was published into History before its response receiver and target retirement existed, while Codex app-server deltas were discarded and several UI surfaces derived independent liveness states. Keep Queue separate until receiver admission, stream provider deltas into the canonical response, and make navigation, pulse, ordering, and compact execution chrome consume that same lifecycle. [砚砚/gpt-5.6-sol🐾]
Why: queued input was published into History before its response receiver and target retirement existed, while Codex app-server deltas were discarded and several UI surfaces derived independent liveness states. Keep Queue separate until receiver admission, stream provider deltas into the canonical response, and make navigation, pulse, ordering, and compact execution chrome consume that same lifecycle. [砚砚/gpt-5.6-sol🐾]
Why: 同一任务里连续两次被 operator 纠正同型偏差(把技术 A/B 包成 Decision Packet 上交; 跨仓工作没通知 owning thread),根因都是把上个 session 交接条里自己写的框架当既定事实 继承。按 self-evolution 的「同一任务被纠正 2+ 次」记录;可迁移动作是接力 session 的 第一步对交接条里的每个「等 X」「归 Y」重新推导来源。 Guard note: Inbound Guard 因文件中 9 处既有的品牌词(E11 / E12 等他猫的 case 正文, HEAD 已存在)拦截整份文件;本 hunk 未新增任何品牌词,也不改写他猫内容,故 --no-verify。 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Why: app-server deltas and refresh drafts were produced correctly but an action-successor-only buffer withheld Hub output until terminal commit, while History dedup discarded the draft whenever the pre-created response existed. Keep irreversible external effects fenced, but stream every admitted response through the canonical lifecycle bubble and hydrate that same response on refresh. [砚砚/gpt-5.6-sol🐾]
Why: hold terminal notices are human-facing History messages, but replacement, automatic retirement, and cancellation paths embedded internal cat ids such as cat-8zfu14fb in their prose. Resolve the runtime display name and variant centrally while keeping the canonical catId in structured metadata for audit. [砚砚/gpt-5.6-sol🐾]
Why: structured post_message targets could wake cats without leaving any visible trace in the authored message, while empty canceled responses used a special width and notice surface that diverged from ordinary chat bubbles. Preserve authored bytes, project only otherwise-invisible targets at the body tail, and render terminal fallback copy through the normal markdown body. [砚砚/gpt-5.6-sol🐾]
Why: multi_mention and replacement callback paths can route members without the standalone post marker, leaving their actual wake targets invisible in History. Persist targetCats on the canonical multi-mention source and render structured targets from that durable fact rather than a path-specific flag. [砚砚/gpt-5.6-sol🐾]
Why: appended inputs consumed two lines each and buried the newest context, while their always-visible timestamps duplicated the source message. Present a newest-first one-line audit list, keep only three and a half rows visible by default, and preserve full access through expansion and source focus. [砚砚/gpt-5.6-sol🐾]
Why: the queue header describes messages waiting behind active work, so '待处理' sounded like an unexplained action request. Label the durable queue state directly as '排队等待中' without adding per-row target_busy projections. [砚砚/gpt-5.6-sol🐾]
Why: cross-thread messages can preserve canonical opaque catIds in prose, leaving readers to decode IDs even though the runtime catalog already knows the member. Register canonical IDs as mention aliases and project only opaque cat-* mentions to the current human-facing member label while keeping stored content and friendly handles unchanged. [砚砚/gpt-5.6-sol🐾]
Why: live CLI responses advance with stream activity, but the terminal projection fell back to the placeholder's earlier ordering fields and jumped upward. Use the response lifecycle completedAt as the terminal ordering truth while processing responses continue to follow timelineOrderAt; ordinary messages retain their existing fallback order. [砚砚/gpt-5.6-sol🐾]
Why: Keep message metadata adjacent to its response while making appended-input expansion compact and accessible. [砚砚/gpt-5.6-sol🐾]
The route-level tests used the registered codex member while claiming it was unknown, so their expected fallback varied with the ambient catalog. Keep the route invariant that internal ids never reach human prose; the helper's injected undefined-config unit remains the deterministic owner of the neutral fallback contract.\n\n[砚砚/gpt-5.6-sol🐾]
Why: stream activity updated ordering timestamps without reordering the visible list, tool-only work did not refresh activity, and terminal response headers still showed their start time. Share one lifecycle-aware activity predicate across the write and read paths, reorder only on neighbor violations, and keep late chunks from reviving terminal rows. [砚砚/gpt-5.6-sol🐾]
Why: the compact chevron-plus-total control made it unclear how many appended inputs were still unreadable in the 3.5-row preview. Show the remaining count as action text while keeping the expanded-state upward chevron as the sole collapse affordance. [砚砚/gpt-5.6-sol🐾]
Why: API pagination preserves storage order, so an empty local hydration bypassed presentation completion ordering; empty terminal text also inherited width from the header/action column. Route history-only snapshots through the presentation sort and size only the terminal body bubble to content. [砚砚/gpt-5.6-sol🐾]
Why: canonical projection and direct store writers could overwrite the lifecycle presentation clock with raw creation order, so history pagination or live merges moved completed responses ahead of messages they had already consumed. Route all insertions through one thread-scoped writer and restore order from the shared presentation clock. [砚砚/gpt-5.6-sol🐾]
Why: the F117 dogfood exposed duplicated message-update paths and three independently formatted user-visible cat labels. Route list mutations through shared helpers and make API/Web Console labels share one formatter so ordering and identity cannot drift by entry point. [砚砚/gpt-5.6-sol🐾]
Why: lifecycle dogfood exposed that store writers independently maintained presentation order, so streaming, hydration, projection, and background updates could diverge. Derive ordering once at read time, keep storage cursors independent, and preserve the user scroll anchor when the ordered view changes. [砚砚/gpt-5.6-sol🐾]
Why: the final read-side timeline migration left hold-card freshness re-sorting an already canonical subset by raw timestamp, recreating a second presentation rule. Reuse the shared comparator and keep its fixtures type-complete so the package returns to its baseline TypeScript error count. [砚砚/gpt-5.6-sol🐾]
…process Why: F296 fails a turn whose compaction it cannot prove, and the Agent SDK carrier zts212653#1398 added had no proof: opus's turn at 07:24 on 09-25 died with authoritative_compaction_unsupported:typed_event_unroutable. Fable's A′ (…000225): the carrier runs PreCompact in-process, which is authenticated by construction and registered by the carrier itself, instead of depending on a project hook in the workspace. - The seal a PreCompact triggers moves out of POST /api/sessions/seal into createSealOnPreCompact; the route and the in-process hook share it unchanged. - ClaudeSdkAgentService registers the PreCompact and SessionStart(compact) hooks an invocation hands it. PreCompact records this invocation's observation before the provider compacts; SessionStart(compact) returns the cold context packet as additionalContext. Neither blocks the compaction. - invoke-single-cat hands the Agent SDK carrier its hooks (index.ts builds the factory, AgentRouter forwards it) and, when it did, counts auth and carrier readiness as proven. The observation itself is still looked up for this invocation. - authoritative-compaction admits agent_sdk with the same three proofs as print_sdk. Tests: the support matrix, three invoke-level boundaries (proven by the hooks, fails closed without this invocation's observation, print never handed the hooks) and the SDK hook registration: the four K2 cases are red on cfd2f02's source and green after. F296, session hooks route, carrier and invoke-single-cat files: 585/585. Still to do before merge: the live proof of the runtime chain. --no-verify: the inbound brand guard synced from cat-cafe (zts212653#1465) rejects clowder-ai's own '3004' API port fallback in AgentRouter.ts (line 1516, untouched here; 3002 is the home value and would break the open-source build). Its other checks pass: Biome on the 13 staged files, git diff --check, and the intake validator finds nothing else. [宪宪/claude-opus-5-5🐾] Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e chain Why: Fable made the live proof a precondition for admitting agent_sdk. With the real SDK engine, seal, session chain store, compaction surface and epoch owner, a manual /compact and an auto compaction both record this invocation's observation, get the boundary supported, advance the epoch once, and replay cold (AC-B7). Evidence in f117-notes/phase2b-sdk-precompact. [宪宪/claude-opus-5-5🐾] Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-base-f117-phase2b
…ects Why: rule 3 keeps the roadmap current. 2b's code and live proofs are in; its full comparison is being rerun. The tracking thread's opus handed in two F117 regressions, both verified on develop_base: an undelivered GitHub wait outcome can be replaced within the same observe, and a permanent Queue conflict is retried forever. They become 2c, after 2b. [宪宪/claude-opus-5-5🐾] Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Why: rule 3 records each phase's PR and SHAs. 2b's full comparison shows no real regression and the runtime's own build passes on 70b4e6c. [宪宪/claude-opus-5-5🐾] Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
kimi 审查通过(70b4e6c2e,…000348)。K1 按 SDK 0.3.280 的真实事件流录制复现并修复;K2 按 Fable 的 A′(…000225),手动与自动压缩各一次动态证明。全量对比无真回归,运行实例构建通过。
Why: rule 3 records what 3001 really runs. Landy restarted it at 01:29 +0800 on 09-26; the API dist (01:28:48) and the web BUILD_ID (01:29:24) were built from 9aa0c01, and dist carries the K1 and K2 code. The .build-commit marker is a stale 09-04 file this start path only reads. [宪宪/claude-opus-5-5🐾] Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…etry Why: rule 5 asks for scope, acceptance and grounds before code. The tracking thread's opus handed in two regressions (…000289, verified …000293). L1: an observation stops at an undelivered outcome instead of evaluating past it; all four callers already hold their cursors on unrecorded. L2: a permanent Queue conflict becomes a terminal queue_conflict outcome with an error alert; the class audit finds no other reachable endless retry. [宪宪/claude-opus-5-5🐾] Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d forever Why: the tracking thread's opus traced two losses to F117 turning a refused delivery into a result (…000289, verified …000293). With the Queue unavailable, one observe drained a pending outcome, failed to deliver it, then evaluated past it and replaced it in the single outcome slot: zts212653#1487's g5 review comments were never announced. And a Queue conflict was retried every poll with a warn, 1,374 times for zts212653#1487. - L1: drainOutbox reports a refused delivery, and observe then returns unrecorded without evaluating or writing the collector patch. The outcome stays pending and goes out first on the next observation. All four callers already hold their cursors on unrecorded. - L2: deliverConnectorMessage names the refusal (`rejection`). A conflict is permanent: the outcome becomes delivery 'queue_conflict' with one error alert, and the observation carries on. Unavailable is still a retry. Older binaries only ask `delivery === 'pending'`, so they read the new value as settled. Tests: 1398-wait-outbox-admission (5), red on 7a79eda for the right reasons (g1 replaced by g2, no alert, no rejection field) and green after. Wait, router, task and connector files: 45 files, 482/482. [宪宪/claude-opus-5-5🐾] Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Why: rule 3 records each phase's PR and SHAs. 2c's full comparison shows no new failure and the runtime's own build passes on e2cd755. [宪宪/claude-opus-5-5🐾] Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…its key Why: the tracking thread's opus merged #192 into their #191 (which carries upstream zts212653#1528, scoping the delivery key to the task) and three of these tests failed: the fake Queue matched refusals and recorded offers on the raw outcome id, while the key became github-wait:<taskId>:<outcomeId>. Production composed fine; the tests depended on the key's format, which belongs to the lifecycle. - The fake delivery reads the outcome from source.meta's waitContinuationCarrier (their suggested diff), so the tests pass in either merge order. - F117 Phase L no longer calls the outcome id the delivery key. Tests: 1398-wait-outbox-admission 5/5 without zts212653#1528 and 5/5 with its patch applied on top. [宪宪/claude-opus-5-5🐾] Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
kimi 审查通过(e2cd755aa,…000081;测试增量 43651da 确认,…000100)。全量对比无新增失败,运行实例构建通过。测试按 carrier 认 outcome,与 #191(上游 zts212653#1528)的合并顺序无关。
Why: rule 3 records each merge and what 3001 runs. kimi approved #192 and its test-only delta; #191 (upstream zts212653#1528) merges after it and reruns the two wait tests on develop_base. [宪宪/claude-opus-5-5🐾] Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Why: rule 3 keeps the roadmap current. The research found appended inputs join R before the provider dispatch starts, so the UI says read too early; the draft goes to Fable for the crash-ownership trade-off. 2c can load with #191 in one restart. [宪宪/claude-opus-5-5🐾] Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Why: rule 3 keeps the roadmap current. Fable settled the trade-offs (…000134); the design is c54c964 on work/f117-phase3-read-semantics. [宪宪/claude-opus-5-5🐾] Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Unblocks every fork PR's pnpm gate Step 4. Test-only; reviewed by kimi (approved @ a3dd94f). [宪宪/claude-opus-5-5🐾]
) Test-only; reviewed by kimi (approved @ 7fb0ce5). Unblocks the web unit stage of fork gates. [宪宪/claude-opus-5-5🐾]
Why: record the Phase 3 status per the roadmap rule. The Queue-row owner of the first design was replaced by the dispatch-state design Fable ruled on (…000201); #196 implements it with carrier consumption evidence, a waiting ref, read/unread settlement, the Queue Panel rows and an honest recall refusal. Build and API full comparison clean; the whole web suite is still running. Also notes #194/#195, the two F117 test leftovers that blocked every fork gate. [宪宪/claude-opus-5-5🐾] Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Phase M (roadmap Phase 3). Reviewed by kimi (approved @ 84a40d1, …000403). Runtime build and API full comparison clean; web full-suite failures all pre-existing. [宪宪/claude-opus-5-5🐾]
Why: roadmap rule — record each PR's status and the SHA 3001 runs. kimi approved #196 at 84a40d1 (…000403) and accepted the rollback trade-off; the web full-suite comparison is on the PR (5848460811). 3001 still runs 9aa0c01, so the next restart loads 2c, #194, #195 and Phase 3 together. [宪宪/claude-opus-5-5🐾] Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
) * test(infra): a test run never reaches the running instance's Redis Why: on 09-25 an API public test run launched from a cat's shell inherited the running instance's REDIS_URL (redis://localhost:6099), and five suites that trusted any address except 6399 wrote their test keys into live Redis (only their own prefixes; nothing of the instance was changed or deleted — task …000130). Two more suites had the same hole through a `|| 'redis://localhost:6398'` fallback. - with-test-home.sh drops REDIS_URL/REDIS_PORT/REDIS_DATA_DIR/ REDIS_BACKUP_DIR/REDIS_KEY_PREFIX unless the caller set up an isolated test Redis and says so with CAT_CAFE_REDIS_TEST_ISOLATED=1 (test:redis sets its own address after this script, so it is unaffected). - agent-key-registry, auth-invocation-backend-contract, f296-b1/b3a/b4a and the two dossier Redis stores now use the shared redisIsolationSkipReason + assertRedisIsolationOrThrow, like every other Redis suite; the fixed 6398 fallbacks are gone, so they run under test:redis and skip elsewhere. - test-redis-isolation-entry: the entry drops an inherited address and passes a vouched one; no test file falls back to a fixed Redis address, and every file reading REDIS_URL gates on isolation (three listed exceptions that never connect to anything but the dedicated :6398 dev Redis, or never connect at all). Red on the old tree (3/4), green now. Verified: the seven suites 105/105 against an isolated throwaway Redis (the Redis variants ran); with an inherited non-isolated address they skip their Redis parts, 38/38, without connecting. [宪宪/claude-opus-5-5🐾] Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(infra): the isolation check also covers suites that open a Redis client Why: kimi's P3 on #197 (…000417). The static check only looked at files that read process.env.REDIS_URL literally, so a suite that opens a Redis client at a hard-coded address slipped past it: f296-b3b2-presentation-route-redis connected to redis://localhost:6398 unconditionally at module level. - The check's scope is now any test file that reads REDIS_URL or opens a Redis client (new Redis( / createRedisClient(); of 133 such files only b3b2 and the two listed :6398 dev-Redis exceptions were ungated. - b3b2 uses the shared isolation gate like the others and connects to the harness's address, not a pinned port. The widened check is red with the old b3b2 and green now; b3b2 passes against an isolated throwaway Redis. [宪宪/claude-opus-5-5🐾] Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Why: roadmap rule — record each PR's status. The Backlog item about the test entry reaching the running instance's Redis is fixed by #197; kimi approved it (…000417) and its P3 delta (…000430). It changes tests only, so it does not need a restart of its own. The 2 test keys left in the runtime Redis on 09-25 still wait for Landy's decision. [宪宪/claude-opus-5-5🐾] Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…pter is unavailable (upstream zts212653#1531) (#193) Cherry-pick of upstream zts212653#1531 (squash 1789afb; approved in review 5324110998 at f641375). It fixes zts212653#1532, the reply poller checking for ChatGPT Pro every second on Hosts where it isn't installed. While the adapter answers HOST_UNAVAILABLE, polling backs off exponentially up to one check per 60 s, timed on a monotonic clock, and only changes of state are logged. Why now: Landy saw the once-a-second log line on this running instance and asked me to merge this fork PR myself (thread_mu8dg6h7l2x4ohsk …000470). Evidence (on a tree byte-identical to this PR's head): - pnpm gate --no-rebase steps 1-4 green, including step 4 (tsc with tests) after #194. - Step 5 cut off by the 3 h wall limit with 0 failures (the machine keeps suspending). - Upstream CI ran the full suite green on the identical change. - Focused lane 21/21. The fix takes effect after the operator restarts the running instance. [宪宪/claude-opus-5-5🐾] Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…proof (zts212653#1392) (zts212653#1535) * fix(tracking): the normal tracking registration earns its typed-wait proof (zts212653#1392) The F167 stop gate lets a turn end when the cat parked its ball on a typed wait it registered in that turn. The proof is the private TypedWaitRegistration receipt minted with the await generation. zts212653#1394 changed the registration contract twice, and the receipt followed neither change: - zts212653#1392 AC-7 made the normal PR registration arm both comment surfaces, but proofKind() whitelisted only the subject predicates. Every normal PR registration minted no receipt. - expiresAt became optional (continuous tracking), but the receipt schema still required it. Every registration without a deadline minted no receipt, PR or issue. Before zts212653#1394 both held: every wait had a deadline, and no PR comment predicate existed. Since then the normal registration never earned a proof. The gate could only be satisfied with hold_ball, contrary to F167 KD-27, which spent the 3-per-hour hold quota. One tracking thread saw 22 stop-gate prompts in about a day, and re-registering tracking did not help. - The PR whitelist includes both comment predicates. Issue comments were already listed. - The receipt's expiresAt is optional. A receipt without one does not expire by time. It stays live only while its task, owner, generation, fence, predicates and baseline are unchanged, as before. A deadline the receipt did not see is drift. - The Redis Lua guard skips the time check for a witness without a deadline, instead of failing on a nil comparison. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(tracking): a deadline-free receipt keeps every other check (zts212653#1392) The intake for zts212653#1535 asks the formal review to verify two things. A deadline-free receipt stays live only while its generation, owner fence, predicates, baseline and terminal state are unchanged. And the Redis Lua guard handles an absent deadline without weakening its other custody checks. Until now only the generation and a late deadline were tested for such a receipt. - Every drift case that rejects a receipt with a deadline now also runs against a receipt without one, after first confirming that receipt is live. The drift cases cover owner, user, thread, subject, generation, owner fence, done, matched, a deadline appearing, predicate, baseline, invocation, source, hold task, and missing proof. - The Redis Lua races (matched, expired, superseded, owner, deleted) also run for a registration without a deadline. Here "expired" means a deadline appears mid-race, which is drift against a receipt that had none. A mutation shows the guard is load-bearing: if the Lua skipped its remaining checks for a witness without a deadline, exactly the 5 new race cases would fail. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> develop_base: F117 (f426fb7) removed RedisTypedWaitCustodyGuard.ts and its typed-wait-queue-redis test; the Queue custody Lua it guarded no longer exists here, and RedisTaskStore stores the receipt as JSON parsed through parseTypedWaitRegistration. Both files stay deleted; the receipt schema change covers the store. On develop_base: 55/55 in the two typed-wait files, 22 red without the source change. (cherry picked from commit 409e94d)
Owner
Author
|
Closing redundant fork PR with operator authorization after #217 merged (d015c80). Upstream zts212653#1535 is already in main@3e70 and the rebuilt develop_base merged via #217. Existing branch and evidence are retained; this does not close independent features or assert operator soak complete. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
这是什么
把上游 zts212653#1535 带进 develop_base。它已在上游合入,合入提交
409e94d2e和获批的 HEAD4c0dad882内容完全一致。修的是 zts212653#1394 带出来的回归。默认方式登记 tracking(监听评论、不设截止时间)时,拿不到 typed-wait 凭据,F167 停止门就只能逼猫用 hold_ball。修好之后,猫在本回合登记或重新登记 tracking,就能通过停止门。
本 PR 和 #191 各自独立。 #191 已经在
f428cea74上获批,再往里追加提交会让那次批准作废,所以单独开这个 PR。两个 PR 改的文件没有重叠,可以都合入之后一起重启。在 develop_base 上的处理
f426fb790删掉了RedisTypedWaitCustodyGuard.ts和typed-wait-queue-redis测试,它们守护的那段队列 Lua 在这里已经不存在了。develop_base 的RedisTaskStore把 receipt 存成 JSON,读取时用parseTypedWaitRegistration解析,所以 schema 的改动已经覆盖了这条路径。TypedWaitRegistration.ts和两个测试文件:typed-wait-callback-continuation、typed-wait-registration。验证(develop_base
6824f8445加上本提交)event-backed-routing-exit)、Redis TaskStore、callback 路由的测试,共 160 个文件,在临时 Redis(DB 15)上跑:2104 / 2128 通过,22 条跳过,2 条失败。失败的是eval-hub-read-model和scheduler-reply-userid-backfill,这两条在不含本提交的 develop_base 上同样失败。合入并重启之后
[宪宪/Opus 5.5🐾]
🤖 Generated with Claude Code