Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
f426fb7
feat(F117): register the unified message delivery lifecycle
mindfn Sep 21, 2026
88ee2bd
refactor(F117): put every connector input on one atomic admission
mindfn Sep 21, 2026
cd50fc5
fix(F117): let private input outlive its Queue row
mindfn Sep 21, 2026
eb2948e
refactor(F117): convert the remaining producers against the registere…
mindfn Sep 21, 2026
7d08c0a
fix(F117): decide publish-vs-suppress at a claim, before anything is …
mindfn Sep 21, 2026
3f126bb
fix(F117): admit the managed wake once under its lease, and delete th…
mindfn Sep 21, 2026
f8fe5d5
fix(F117): compare literals raw, so a coerced value cannot pass a fai…
mindfn Sep 21, 2026
7166ae3
fix(hold-ball): make cancelability a producer-stated fact, not a cons…
mindfn Sep 21, 2026
c94a58a
refactor(F254): retire the freshness machinery F117 made redundant
mindfn Sep 21, 2026
0888bdc
fix(F117): make dispatch admission and live UI coherent
mindfn Sep 22, 2026
6392172
fix(F117): unify lifecycle timeline presentation
mindfn Sep 22, 2026
fd4bd45
Merge branch 'main' into feat/F117-1354-message-delivery-lifecycle
mindfn Sep 23, 2026
f342a78
refactor(a2a): integrate main through unified delivery admission
mindfn Oct 8, 2026
baff448
fix(a2a): converge queue and response presentation
mindfn Oct 8, 2026
a791581
docs(a2a): align delivery and failure return contracts
mindfn Oct 8, 2026
16e6b0a
fix(a2a): remove retired presentation call parameters
mindfn Oct 8, 2026
d96a2a8
test(a2a): construct the undeclared capability fixture explicitly
mindfn Oct 8, 2026
b9fcdbd
fix(a2a): preserve independent failed response identities
mindfn Oct 8, 2026
d40262c
fix(a2a): recover failed responses with their exact caller wake
mindfn Oct 8, 2026
0c2a0e8
fix(a2a): replay committed terminal snapshots without normalization
mindfn Oct 8, 2026
6e4e406
docs(a2a): align public feature inventory with active lifecycle work
mindfn Oct 8, 2026
381c113
fix(a2a): preserve canonical sender identity across delivery consumers
mindfn Oct 8, 2026
d94ade4
fix(a2a): recheck measured silence before timing out a member
mindfn Oct 9, 2026
9686c30
fix(a2a): use canonical identity for human message grouping
mindfn Oct 9, 2026
42bdd08
fix(a2a): reconcile realtime projections from canonical owners
mindfn Oct 9, 2026
77375e0
fix(a2a): observe optional Append read status on exact messages
mindfn Oct 9, 2026
c2a058f
test(a2a): migrate remaining public delivery consumers to canonical e…
mindfn Oct 9, 2026
cd7bb08
fix(a2a): require SDK consumption evidence for Append read display
mindfn Oct 9, 2026
0c341fe
test(a2a): exercise canonical action recovery evidence
mindfn Oct 9, 2026
a14380e
fix(live): refuse active writer recovery before carrier replay
mindfn Oct 9, 2026
1835f9a
fix(a2a): verify durable cloud source before return authority
mindfn Oct 9, 2026
3dcfc70
refactor(task): hydrate wait authority through the existing codec
mindfn Oct 9, 2026
b2a4637
fix(eval): retire certificates for removed freshness planes
mindfn Oct 9, 2026
56dff69
test(a2a): migrate public consumers to canonical delivery evidence
mindfn Oct 9, 2026
86fb5d8
test(github): control admission deadline after child readiness
mindfn Oct 9, 2026
a5352c6
fix(websocket): reuse the validated HTTP session principal
mindfn Oct 9, 2026
01fbf75
fix(web): refresh approval owners after active GET generations
mindfn Oct 9, 2026
a734c2a
test(web): assert approval refresh generation after decisions
mindfn Oct 9, 2026
735bd9c
fix(web): restore HTTP session before Socket reconnect
mindfn Oct 9, 2026
028a63a
test(web): align socket consumers with session-gated recovery
mindfn Oct 9, 2026
9f8b644
fix(F117): use target guidance and simplify delivery feedback
mindfn Oct 9, 2026
984edb0
merge: preserve original F117 delivery lineage
mindfn Oct 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions feature-specs/evidence/f117-feedback-2026-10-09/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# F117 restart feedback: implementation and review packet

Author: 砚砚 / gpt-6.1-sol. Worktree: cat-cafe-append-receipt-feedback; branch fix/f117-append-receipt-feedback. Base d015c806d91839658788a32593735e3f4ff99254 (tree 022ebeb40ac5326109a33cac710a6550509e4c1f). This base is fork PR217's merge; its tree equals public candidate028a63af71.

## Accepted user intent

Canonical thread thread_msr51149hym0i79f:

- 0001791549775494-000019-363c19fb and 0001791549864606-000022-3823f965: guidance/interrupt behavior is a target capability; remove transitional source-kind eligibility gates.
- 0001791550801818-000059-bceb02e9: exactly two stop scopes, per-member and all active members; do not merge185/210/215; explain root causes and changes.
- 0001791551501099-000083-b30df01a: investigate internal ~1second admission separately from native client startup; recover prior quote contract.
- 0001791552770209-000113-0ebffba2, 0001791553175243-000118-fd2249d2 and 0001791553423732-000124-c2acb766: one current status/time on dot/text hover; reuse dispatch read state; missing read capability is unsupported; hover uses 投递于/读取于.
- 0001791553220033-000119-8affed25 and 0001791553456012-000125-74f43c16: ordinary delivery should not scan Signals articles; use explicit retrieval.
- 0001791553679904-000131-6b5fab2c and 0001791553749174-000134-3ea29b71: file the broader automatic-context audit in cat-cafe-shared, not Clowder.

## Changes and why

1. Queue `/continue`, durable continuation binding, and automatic exact-entry Append no longer classify eligibility by user/agent/external/plugin/system sender. Target append capability and an explicit continuation's exact parent govern transfer. Owner authorization, Queue revision/status, private/pinned exclusions, sibling targets, provider readiness, exact execution and durable source provenance remain in their existing owners. GitHub Wait216's source remains external and its typed wait metadata is retained. Queue409 displays the real failure except actual revision/entry convergence errors. There is no UI source-kind disable.
2. Existing `LifecycleDispatchRef.inputRead` already encodes the requested three states: absent=unsupported, pending=not read, read+at=read. No duplicate consume field, store or provider inference. Console reads the unique source×target×response dispatch: 投递于 uses dispatchedAt; 读取于 uses read.at. Target theme color; only a supported pending receipt on an active run pulses. A terminal run without receipt confirmation stops animation without claiming read.
3. Dot and text share one tooltip (name, current status/time, full source text), delayed1second on normal mouse hover. Keyboard and mobile accessibility retain existing tooltip policy. Removed browser-native title and visible 展开全文/收起 controls; a truncated row uses a hand cursor and click/Enter to expand/collapse. Jump-to-source stays separate. Existing multi-row collapse stays.
4. Sender display names centrally omit @; route handles in body text are unchanged. Reply badge order is ↩, avatar, name: text, with flex centering. No component-specific name fallback layer.
5. Removed duplicate “猫猫正在回复” composer banner. Current-member append guidance lives in the disposition-specific input placeholder. Reused the existing stop icon/control in the classic composer too; it cancels all exact active executions, while execution-bar member controls retain per-member scope. No queue deletion/pause or new stop protocol.
6. Removed automatic Signals lookup dependency and article snippet injection from both routing strategies and router/bootstrap wiring. Article search/get/study/link services and stored data are untouched. Existing standalone lookup module is no longer in delivery wiring. This removes work rather than adding caches, source exceptions or moving admission labels ahead of work. Existing route timing splits entity nudges, proactive memory, disposition feedback and memory cues to distinguish remaining cost.

## Validation

- API271/271,17suites: real Queue/tracker/store/processor boundary, all sender kinds, stale parent fences, wait provenance, serial/parallel routes, explicit article query service, read observations, projection, entity/proactive-memory and feedback context.
- Web127/127,13files: canonical identity across surfaces, row read/delivery state, tooltip timing, click/keyboard interaction, reply order, all-stop and queue action error behavior.
- API TypeScript compilation exit0. Shared/API builds passed earlier; final API dist regenerated by tsc in this same worktree.
- Full Web type check and exact base compiler-host comparison show identical single pre-existing TS2322 at proposal-card-realtime-socket.integration.test.tsx:152. No new diagnostics; not claiming full Web tsc green.
- Browser fixture imports actual current components/styles; in-memory API and synthetic source/response data only. Desktop/mobile screenshots inspected. Checks: no duplicate banner, target dot color, one-state tooltip, expansion and Enter collapse, two actual exact cancel requests, mobile stop within viewport, no browser errors. This is not complete Next/real-provider acceptance. The temporary preview has a bounded lifetime and is not an operator runtime entry.
- Original valid red logs retained: source gates; serial/parallel unrequested article injection; central sender labels; row interaction and one current status/time. Successful logs are compressed losslessly; input/artifact SHA256 manifest provided. Broadened tests initially lacked the local SQLite native dependency; installed the exact locked package's binding in this feature checkout and reran all271 successfully. Runtime dependencies/config untouched.
- git diff --check passes. Formatting has no errors; existing lint warnings remain, no unsafe broad rewrite.

## Performance: proved findings versus remaining diagnosis

Runtime read-only metadata in internal-preparation-timings.json (only this thread’s detailed coordinates, other runs aggregate-only): idle source ingress12:26:11.035Z, admission12:26:12.091Z, queueAge1056ms; Queue preparation14.27ms, route preparation984.82ms, admission3.27ms. Subgroups memory/feedback467.75ms and identity/linked354.85ms. Later native Codex startup8.905s is separate.36samples show recurring linked-context work; this is not explained away as cold startup.

Owned synthetic Signals probe: zero linked articles,500records27–28ms,2000records106–111ms. This verifies O(library-size) I/O even on no-match ordinary threads. Removing both callers removes this work from admission; no live after-deployment millisecond claim. Remaining468ms memory/feedback is not fixed by this change. Code inspection found owner-window scans and repeated registry/ledger resolution; per-operation attribution is still needed before changing memory semantics. New timing splits expose that distinction after ordinary delivery deploys, without logging message bodies or weakening privacy.

Broader audit issue: https://github.com/zts212653/cat-cafe-shared/issues/8 . Initially created in the wrong repository, then transferred (no duplicate Clowder issue remains). Canonical gh readback confirms OPEN/authormindfn. Automatic tracking registration returned422 “repository does not exist or is not accessible”; no tracker was falsely claimed. Follow-up through canonical gh remains available.

## PR cleanup and original capabilities

Provider-verified CLOSED:210(operator explicit139),216(replaced by merged217),211/212/213/214(A2A increments absorbed),191/199(public changes already in synced main). Branches/evidence preserved.

185/215 remain frozen,210closed at operator139; user explicitly prohibits merging their massive comparison diffs. Those diffs reflect their old baseline, not the capability's own size:

| PR | True original slice | Capability and disposition |
| --- | --- | --- |
|185|11files,+1787/-1|Skip scheduled eval model wakes when prerequisite OTel evidence is unavailable; plus fork rebuild guard/registry/workflow. Gate is not in current runtime. Separate from A2A, not imported here.|
|210|5files,+427/-23|MCP preserves raw quoted blocks and comment-first previews, keyword matching and bounded drilldown. Anchor preview part exists today; full contentBlocks/keyword projection is not fully absorbed. No giant merge.|
|215|4files,+838/-79|gh verdict-vs-main-sync classification, fresh remote candidate/base and CLI argument binding. Corrected develop_base baseline already removed A2A dependency. No guard activation/merge here.|

Historical quote contract recovered from full canonical user messages0001791430966608-000013-accfaf5b (messageId+selected fragment),0001791433389141-000027-28bf60b3 (small justified quote/read changes permitted; no scenario if/else). PR210 addresses model-facing read projection, not this reply-avatar UI or admission latency. It needs a clean current-baseline scope rather than its old large PR; no new quote storage/protocol implemented in this packet.

## Risk and ownership

Behavior: target-based continuation broadening, visual simplification, removal of implicit article prompt injection. Data: no new store/schema, durable inputs untouched. Security: existing owner, target, exact-parent and revision checks retained; no trust in source labels. Contract: existing dispatch receipt and cancel APIs reused; internal optional Signals router dependency removed; explicit article APIs unchanged. Irreversibility: local commit and later normal feature push/PR only; no merge, runtime code/config/process mutation or persistent data deletion.

Reviewer should judge exact-source-neutral guidance with all target/provenance fences, latest tooltip semantics and two stop scopes, and whether optional article capability is now absent from normal routing. Remaining performance diagnosis is expressly open, not covered by a green test claim. Review before public push; operator will restart after the incremental delivery returns normally.
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Compare the candidate and its exact base through a compiler host; do not
// modify the checkout or hide any diagnostic from the full web project.
const path = require('node:path');
const { execFileSync } = require('node:child_process');
const { createRequire } = require('node:module');
const root = process.cwd();
const web = path.join(root, 'packages/web');
const ts = createRequire(path.join(web, 'package.json'))('typescript');
const base = 'd015c806d91839658788a32593735e3f4ff99254';
const changed = execFileSync('git', ['diff', '--name-only', base], { cwd: root, encoding: 'utf8' }).trim().split('\n');
const baseline = new Map(
changed
.filter((file) => /\.[cm]?[jt]sx?$/.test(file))
.map((file) => [
path.join(root, file),
execFileSync('git', ['show', `${base}:${file}`], { cwd: root, encoding: 'utf8' }),
]),
);
const configFile = ts.readConfigFile(path.join(web, 'tsconfig.json'), ts.sys.readFile);
const config = ts.parseJsonConfigFileContent(configFile.config, ts.sys, web);
function check(useBase) {
const host = ts.createCompilerHost(config.options);
const read = host.readFile.bind(host);
host.readFile = (file) => (useBase && baseline.has(file) ? baseline.get(file) : read(file));
const program = ts.createProgram(config.fileNames, { ...config.options, incremental: false }, host);
return ts.getPreEmitDiagnostics(program).map((diagnostic) => {
const position =
diagnostic.file && diagnostic.start !== undefined
? diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start)
: undefined;
return {
file: diagnostic.file ? path.relative(root, diagnostic.file.fileName) : null,
line: position ? position.line + 1 : null,
code: diagnostic.code,
message: ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n'),
};
});
}
const candidate = check(false);
const baseDiagnostics = check(true);
const result = {
base,
replacedBaseFiles: baseline.size,
candidate,
baseDiagnostics,
sameDiagnostics: JSON.stringify(candidate) === JSON.stringify(baseDiagnostics),
};
console.log(JSON.stringify(result, null, 2));
process.exitCode = result.sameDiagnostics ? 0 : 1;
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
## Problem

Sending an ordinary chat message performs optional capability discovery before the target can accept execution. We should audit what is automatically fetched/injected on this critical path, retain required identity/routing/session context, and make optional retrieval explicit and bounded.

A confirmed example is Signals article injection. Both serial and parallel routes call `signalArticleLookup(threadId)` before model invocation. `createSignalArticleLookup` first reads all inbox records, then sequentially reads each article's study metadata to discover whether it belongs to the thread. A thread with no linked articles still traverses the library. This is Signals/news/article retrieval, not the normal message history or memory lookup.

Code at the published A2A candidate `028a63af71bc3ae6f9ebf9fcf8bd542c0e02d40b`:

- [serial route](https://github.com/zts212653/clowder-ai/blob/028a63af71bc3ae6f9ebf9fcf8bd542c0e02d40b/packages/api/src/domains/cats/services/agents/routing/route-serial.ts)
- [parallel route](https://github.com/zts212653/clowder-ai/blob/028a63af71bc3ae6f9ebf9fcf8bd542c0e02d40b/packages/api/src/domains/cats/services/agents/routing/route-parallel.ts)
- [Signals lookup](https://github.com/zts212653/clowder-ai/blob/028a63af71bc3ae6f9ebf9fcf8bd542c0e02d40b/packages/api/src/domains/signals/services/signal-thread-lookup.ts)

## Observations and limits

In the fork after [Clowder PR1398](https://github.com/zts212653/clowder-ai/pull/1398)'s candidate was merged, one idle-input admission measured 1,056ms from ingress to execution acceptance: Queue preparation 14.27ms, route preparation 984.82ms, response admission 3.27ms. Within route preparation, memory/feedback took 467.75ms and identity/linked-context 354.85ms. The later 8.9s native Codex client startup is a separate problem, not an explanation for this internal second.

A synthetic, isolated file fixture (no user data, Redis or model service) reproduced the Signals lookup cost for an unrelated thread: 500 articles took 27–28ms; 2,000 took 106–111ms, all with zero matches. This proves unnecessary work grows with the article library; it does **not** attribute the entire live 354.85ms bucket to Signals.

## Audit scope

The following preparation surfaces merit an inventory and cost/necessity check. Only the Signals scan above is confirmed unnecessary here; the other rows are investigation targets, not proven defects.

| Surface | Current preparation work | Audit question |
| --- | --- | --- |
| Signals articles | Global inbox + sequential article metadata discovery; snippets/history added automatically | Use existing `signal_search` / `signal_get_article` on demand instead of scanning on ordinary sends |
| Entity nudges | Alias discovery against evidence data | Is work bounded to the current input and necessary before acceptance? |
| Proactive memory | Owner-scoped message-window scan, privacy checks, candidate registry lookup | Avoid repeatedly hydrating a broad window for each send; preserve owner/privacy boundaries |
| Disposition feedback | Up to 50 lexical candidates, sequential exact-subject resolution and ledger reads | Repeated discovery/query cost versus indexed exact subjects; preserve truth checks |
| Proposal status | Up to 200 recent messages scanned for proposal cards, then live candidate checks | Explicit proposal references/status questions versus unconditional discovery |
| Concierge search | Automatically composed search context where enabled | What user/task evidence makes retrieval needed? |
| Always-on evidence / world / packs | Conditional document, world or pack context | Explicit activation, bounded reads and prompt budget |
| Identity / routing / session / message history | Core execution and continuity context | Keep correctness; measure separately from optional capabilities |

## Proposed outcome

1. Ordinary sends do not search Signals automatically. Article tools, study/link actions and stored content remain available.
2. Classify each preparation fetch as execution-required, explicitly activated, or on-demand; document the trigger and real consumption path.
3. Measure internal admission independently of provider startup, with per-operation timings and large-library/no-match fixtures.
4. Remove unnecessary work at its ownership boundary rather than adding per-source conditions, an unconditional cache, or moving slow work behind an earlier status label.
5. Preserve authorization, privacy, durable lifecycle and session recovery behavior.

A local fork follow-up currently removes the automatic Signals dependency from both routing strategies and has red→green serial/parallel regressions. It is not yet reviewed/merged; no claim that all admission latency is solved. Remaining memory/feedback cost is still under investigation.

Related: [Clowder issue839](https://github.com/zts212653/clowder-ai/issues/839) inventories injection visibility; this issue focuses on **retrieval necessity and latency on ordinary sends**, not another Console editor or runtime injection framework.
Loading