feat(daemon): support scoped workspace memory tasks - #9895
Conversation
E2E test reportScopeManual browser validation of a project-scoped remember action against a locally built daemon, plus automated coverage for project/user remember, scoped forget, capability negotiation, and invalid-scope rejection. Result
NotesThe manual browser pass covered the project target and completion timing. User-target and forget boundaries are covered by focused automated tests; Windows and Linux manual passes were not run locally. |
|
Re-run on new commits — head moved from Template still complete ✓ Problem: unchanged from the first pass — real, not theoretical. Linked #9894 is a triaged feature request ( Direction: aligned. This extends the daemon's public contract ( Size: core paths are touched ( Approach: scope matches the issue's acceptance criteria; nothing meaningful to cut. Both open questions from the first pass are closed: (1) the no-op remember → Risk: Stage 1e still matches Moving on to code review. 🔍 中文说明因新提交重跑 —— head 从 模板 依然完整 ✓ 问题: 与首轮结论一致 —— 真实存在而非理论性问题。关联的 #9894 是已分诊的 feature request( 方向: 对齐。本 PR 扩展了 daemon 的公开契约( 规模: 触及核心路径( 方案: 范围与 issue 验收标准一致,没有可有意义砍掉的部分。首轮的两个开放问题都已关闭:(1) 无写入 remember → 风险: Stage 1e 仍命中 进入代码审查。🔍 — Qwen Code · qwen3.8-max Reviewed at |
Code reviewThis is a full re-read of the production diff at the new head, not a delta skim. Since the first triage pass, the autofix loop landed a substantial set of fixes that I re-verified in the current code: the early-completion abort is deferred out of the emitter handler ( No critical blockers found. Things I verified as correct by reading:
Notes for the maintainer — none blocking, all recorded rather than requested:
The completion flow (unchanged in shape since the first pass, now with the deferred abort): sequenceDiagram
participant C as Client
participant S as Serve task lane
participant R as Remember core
participant F as Forked agent
participant M as Memory files
C->>S: remember with content and scope
S->>S: validate scope, fail closed if invalid
S->>R: enqueue scoped task
R->>R: scope the permission boundary
R->>F: run, complete after first successful write
F->>M: write one memory entry
M-->>F: write succeeds
F-->>R: deferred self-abort, report completed
R->>R: reject no-op or cross-scope, rebuild index on every path
R-->>S: entry saved
S-->>C: task completed
Files changed (27 of 27 shown)
Testing evidence — the PR's own CI, quoted from the APIFetched once for The single failure is classified pre-existing/environmental, not PR-caused. The only failing test in the entire job is The macOS/Windows test legs and
One row per check name (latest run); skipped checks omitted; failures sort first. / 每个检查名一行(取最新一次运行),省略 skipped,失败项排在最前。 Behavioural substantiation. The gap the first pass flagged — mocked agent tests proving only the unit seams — has been closed at this exact head by the maintainer's live-daemon A/B report in-thread (@wenshao, Linux, bundled build, scripted model): 24/24 checks pass on the PR and 17 flip to FAIL against the merge-base control, covering boundary enforcement in both directions (including symlink alias and pinned-record probes), scoped-forget candidate lists read off the wire, first-write completion in 1 model round vs 3, fail-closed invalid scope with zero model requests, capability negotiation, and the SDK pre-flight sending zero requests to an old daemon. That is maintainer evidence, not the author's claim, and it is stronger than a sandboxed Not verified: Windows/Linux manual UX of the new task states (author-flagged out of scope; the failure rendering note above covers the residual risk), and Recall/Extract/Dream lifecycle parity (also out of scope — those planners share the config factory and are untouched by this delta). 中文说明代码审查: 这是对当前 head 生产代码 diff 的完整重读,不是增量浏览。自首轮分诊以来,autofix 循环落地了一大批修复,我在当前代码中逐一复核:提前完成中止被延迟到发射器处理器之外( 未发现致命阻塞项。 通过阅读核对无误的点:边界处的 scope 取值( 给维护者的说明(均不阻塞,记录而非要求): scope 校验内联重复约 6 次(与周边风格一致,可选抽校验器); 测试证据: 通过 check-runs API 对 — Qwen Code · qwen3.8-max Reviewed at |
|
Confidence: 4/5 — every production hunk re-read at the new head with no blocker found, the earlier behavioral gap closed by a maintainer's live A/B verification of this exact commit, and CI green except one failure with concrete pre-existing evidence. Not 5/5 because the red check is classified rather than re-run, this is a cross-repo change to core memory infrastructure, and the recorded Suggestion-level items from the review rounds are deferred rather than resolved. Stepping back: the first pass deferred to a maintainer for three reasons — the mocked-out abort path, the public-contract sign-off, and CI still in flight. All three are now answered on this head. The abort/completion mechanism is no longer a read-only inference: it was driven through a real daemon with a scripted adversarial model, A/B against the merge base, with 17 of 24 checks flipping without the patch. The public contract ( What I still want a human to keep on the list, none of it merge-blocking: the deferred Suggestion items from the review rounds (recorded in the round-9/14 lists), how Web Shell/desktop render the new Verdict: approving, pinned to the reviewed commit. The standing 中文说明置信度:4/5 —— 在新 head 上重读了每一个生产代码改动块、未发现阻塞项;此前的行为层缺口已由维护者对当前提交的真实 daemon A/B 验证关闭;CI 除一项有具体"既有问题"证据的失败外全绿。没给 5/5 是因为该红项是归类结论而非复跑结论、这是跨仓库的核心记忆基础设施改动,且评审轮次记录的 Suggestion 级条目是延后而非已解决。 退一步看:首轮转交维护者有三个原因 —— 被 mock 掉的中止路径、公开契约签核、CI 未落定。现在这三点都在当前 head 上有了答案。中止/完成机制不再是纯阅读推断:它在真实 daemon 上被脚本化的对抗性模型驱动过、与 merge-base 做了 A/B 对照,24 项检查中 17 项在去掉补丁后翻转。公开契约(REST/ACP/SDK 的 仍希望人工保留在清单上的(均不阻塞合并):评审轮次延后的 Suggestion 条目(记录在第 9/14 轮清单)、Web Shell/桌面端对新的 结论:批准,并钉住在已审查的提交上。 本机器人此前挂着的 — Qwen Code · qwen3.8-max Reviewed at |
🩺 serve daemon A/BBuilt the PR base vs this PR head
|
| field | PR base (before) | this PR (after) |
|---|---|---|
features[] |
— | "workspace_memory_remember_project_scope" |
features[] |
— | "workspace_memory_remember_user_scope" |
features[] |
— | "workspace_memory_forget_scope" |
— Qwen Code · serve A/B
|
@qwen-code /takeover |
|
🤝 Takeover engaged: the autofix loop now manages this PR — it will address new review feedback and resolve base conflicts until the label is removed or the round cap is reached. This is a fork PR, so the first round comes from the next scheduled scan (usually within minutes). Remove the 中文说明🤝 已接管:autofix 循环现在管理此 PR —— 将持续处理新的评审反馈与 base 冲突,直到移除标签或达到轮次上限。本 PR 来自 fork,首轮处理将由下一次定时扫描执行(通常几分钟内)。移除 |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Partially reviewed — gaps disclosed.
Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its full suite did not run locally (the capability-snapshot file inside it was executed by verification and fails; the rest of the integration suite remains unexercised).
[Critical] packages/cli/src/serve/acp-http/transport.test.ts — removing the no-op remember completion path breaks two pre-existing tests that the diff did not update: '_qwen/workspace/memory/remember queues and polls hidden tasks' (~line 9950) and 'shares remember task state between REST and ACP transports' (~line 10111). The file's bridge stub returns { summary: 'remembered', filesTouched: [], touchedScopes: [] } and the tests run the real lane, which now throws remember_no_update, so both toMatchObject({ status: 'completed', result: { summary: 'No memory files updated.' } }) assertions fail. Measured net-new by base-tree/test-delta: passes at merge base e0d933b, fails only on the PR side; npm test --workspace=packages/cli exits 1. Fix: update the two expectations to the new failed/remember_no_update contract (or give the stub non-empty filesTouched and assert 'Memory update completed.').
[Critical] integration-tests/cli/qwen-serve-routes.test.ts — the three new baseline capability tags break the 'advertises all baseline capabilities' test (~line 302): it asserts the full advertised feature list against a hardcoded array that still lists 'workspace_memory_remember', 'workspace_memory_forget', 'workspace_memory_dream' consecutively, and the PR touches nothing under integration-tests/ (it updated only the unit-level twin EXPECTED_STAGE1_FEATURES in server.test.ts). Probe: with the bundle built from this commit the test fails 3/3 attempts with exactly the three missing tags; inserting them in registry order flips it green. The required no-AK integration gate in ci.yml runs this file for this PR, so this lands on the PR's own required check.
中文说明
仅完成部分审查,审查缺口已披露。
未审查:build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its full suite did not run locally (the capability-snapshot file inside it was executed by verification and fails; the rest of the integration suite remains unexercised)。
[Critical] packages/cli/src/serve/acp-http/transport.test.ts — removing the no-op remember completion path breaks two pre-existing tests that the diff did not update: '_qwen/workspace/memory/remember queues and polls hidden tasks' (~line 9950) and 'shares remember task state between REST and ACP transports' (~line 10111). The file's bridge stub returns { summary: 'remembered', filesTouched: [], touchedScopes: [] } and the tests run the real lane, which now throws remember_no_update, so both toMatchObject({ status: 'completed', result: { summary: 'No memory files updated.' } }) assertions fail. Measured net-new by base-tree/test-delta: passes at merge base e0d933b, fails only on the PR side; npm test --workspace=packages/cli exits 1. Fix: update the two expectations to the new failed/remember_no_update contract (or give the stub non-empty filesTouched and assert 'Memory update completed.').
[Critical] integration-tests/cli/qwen-serve-routes.test.ts — the three new baseline capability tags break the 'advertises all baseline capabilities' test (~line 302): it asserts the full advertised feature list against a hardcoded array that still lists 'workspace_memory_remember', 'workspace_memory_forget', 'workspace_memory_dream' consecutively, and the PR touches nothing under integration-tests/ (it updated only the unit-level twin EXPECTED_STAGE1_FEATURES in server.test.ts). Probe: with the bundle built from this commit the test fails 3/3 attempts with exactly the three missing tags; inserting them in registry order flips it green. The required no-AK integration gate in ci.yml runs this file for this PR, so this lands on the PR's own required check.
— qwen3.8-max via Qwen Code /review (v0.22.0)
|
🔀 Base updated: red check(s) [Test (ubuntu-latest, Node 22.x)] pass on current main — merged current main via update-branch; CI will re-run. 中文说明🔀 已更新 base:红色检查 [Test (ubuntu-latest, Node 22.x)] 在当前 main 上通过 —— 已通过 update-branch 合入当前 main,CI 将重新运行。 |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Partially reviewed — gaps disclosed.
Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally (the capability-snapshot file inside it was verified by probe and fails; the rest of the integration suite remains unexercised).
Deferred under the convergence posture (round 2, not a blocker) — recorded, not requested in this round:
packages/core/src/agents/forkedAgent.agent.test.ts:427 — [review] Vacuous abort assertion in the first-write completion testpackages/core/src/memory/remember.ts:97 — [review] scope='user' system prompt still advertises the excluded PROJECT tierpackages/cli/src/serve/workspace-remember.test.ts:400 — [review] Remember-side task-status scope echo is untested
[Critical] R1-11 packages/cli/src/serve/acp-http/transport.test.ts — removing the no-op remember completion path breaks two pre-existing tests that the diff did not update: '_qwen/workspace/memory/remember queues and polls hidden tasks' (~line 9950) and 'shares remember task state between REST and ACP transports' (~line 10111). The file's bridge stub returns { summary: 'remembered', filesTouched: [], touchedScopes: [] } and the tests run the real lane, which now throws remember_no_update, so both toMatchObject({ status: 'completed', result: { summary: 'No memory files updated.' } }) assertions fail. Measured netNew by base-tree/test-delta: passes at the merge base, fails only on the PR side; npm test --workspace=packages/cli exits 1. Fix: update the two expectations to the new failed/remember_no_update contract (or give the stub non-empty filesTouched and assert 'Memory update completed.').
[Critical] R1-12 integration-tests/cli/qwen-serve-routes.test.ts — the three new baseline capability tags break the 'advertises all baseline capabilities' test (~line 302): it asserts the full advertised feature list against a hardcoded array (lines 338-341) that still lists only 'workspace_memory', 'workspace_memory_remember', 'workspace_memory_forget', 'workspace_memory_dream' consecutively, and the PR touches nothing under integration-tests/ (it updated only the unit-level twin EXPECTED_STAGE1_FEATURES in server.test.ts). Probe: the real getAdvertisedServeFeatures() at this commit lists all three new tags as advertised-but-missing from the hardcoded array; inserting them in registry order flips the diff empty. The required no-AK integration gate in ci.yml runs this file for this PR, so this lands on the PR's own required check.
中文说明
仅完成部分审查,审查缺口已披露。
未审查:build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally (the capability-snapshot file inside it was verified by probe and fails; the rest of the integration suite remains unexercised)。
收敛姿态下延后(第 2 轮,非阻断)——已记录,本轮不要求修改:共 3 条(原文未翻译,列表见上方英文部分)。
[Critical] R1-11 packages/cli/src/serve/acp-http/transport.test.ts — removing the no-op remember completion path breaks two pre-existing tests that the diff did not update: '_qwen/workspace/memory/remember queues and polls hidden tasks' (~line 9950) and 'shares remember task state between REST and ACP transports' (~line 10111). The file's bridge stub returns { summary: 'remembered', filesTouched: [], touchedScopes: [] } and the tests run the real lane, which now throws remember_no_update, so both toMatchObject({ status: 'completed', result: { summary: 'No memory files updated.' } }) assertions fail. Measured netNew by base-tree/test-delta: passes at the merge base, fails only on the PR side; npm test --workspace=packages/cli exits 1. Fix: update the two expectations to the new failed/remember_no_update contract (or give the stub non-empty filesTouched and assert 'Memory update completed.').
[Critical] R1-12 integration-tests/cli/qwen-serve-routes.test.ts — the three new baseline capability tags break the 'advertises all baseline capabilities' test (~line 302): it asserts the full advertised feature list against a hardcoded array (lines 338-341) that still lists only 'workspace_memory', 'workspace_memory_remember', 'workspace_memory_forget', 'workspace_memory_dream' consecutively, and the PR touches nothing under integration-tests/ (it updated only the unit-level twin EXPECTED_STAGE1_FEATURES in server.test.ts). Probe: the real getAdvertisedServeFeatures() at this commit lists all three new tags as advertised-but-missing from the hardcoded array; inserting them in registry order flips the diff empty. The required no-AK integration gate in ci.yml runs this file for this PR, so this lands on the PR's own required check.
— qwen3.8-max via Qwen Code /review (v0.22.0)
|
🔀 Base updated: red check(s) [Test (ubuntu-latest, Node 22.x)] pass on current main — merged current main via update-branch; CI will re-run. 中文说明🔀 已更新 base:红色检查 [Test (ubuntu-latest, Node 22.x)] 在当前 main 上通过 —— 已通过 update-branch 合入当前 main,CI 将重新运行。 |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Partially reviewed — gaps disclosed.
Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally (the capability-snapshot file inside it was verified by probe and fails; the rest of the integration suite remains unexercised).
Not explored to full depth (tool budget reached): "agent reverse-audit (round 2)": none — full assigned range (diff lines 1588–1872) read untruncated, all follow-up source checks completed..
Deferred under the convergence posture (round 3, not a blocker) — recorded, not requested in this round:
packages/cli/src/acp-integration/acpAgent.test.ts:8327 — [probe] D3-1 Three ext-method remember tests assert the pre-PR resolve contract the new remember_no_update path can no longer produce; ext-method rejection path untested (deferred: co…
中文说明
仅完成部分审查,审查缺口已披露。
未审查:build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally (the capability-snapshot file inside it was verified by probe and fails; the rest of the integration suite remains unexercised)。
未探索到全部深度(达到工具调用预算):"agent reverse-audit (round 2)":none — full assigned range (diff lines 1588–1872) read untruncated, all follow-up source checks completed.。
收敛姿态下延后(第 3 轮,非阻断)——已记录,本轮不要求修改:共 1 条(原文未翻译,列表见上方英文部分)。
— qwen3.8-max via Qwen Code /review (v0.22.0)
|
🔀 Base updated: red check(s) [Test (ubuntu-latest, Node 22.x)] pass on current main — merged current main via update-branch; CI will re-run. 中文说明🔀 已更新 base:红色检查 [Test (ubuntu-latest, Node 22.x)] 在当前 main 上通过 —— 已通过 update-branch 合入当前 main,CI 将重新运行。 |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Partially reviewed — gaps disclosed.
Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally (the capability-snapshot file inside it was verified by probe and fails; the rest of the integration suite remains unexercised).
Deferred under the convergence posture (round 4, not a blocker) — recorded, not requested in this round:
packages/cli/src/serve/capabilities.ts:101 — [review] R1-3 no capability tag advertises the non-additive no-op-remember behavior changepackages/core/src/memory/forget.ts:149 — [review] R1-4 scoped forget scans both stores unconditionally before filtering candidatespackages/sdk-typescript/src/daemon/DaemonClient.ts:2227 — [review] R1-5 test gap: SDK forget scope pass-through untestedpackages/cli/src/serve/workspace-remember.ts:910 — [review] R1-6 test gap: invalid-scope rejection on the FORGET route untestedpackages/cli/src/acp-integration/acpAgent.ts:8388 — [review] R1-7 test gap: ext-method forget scope pass-through and rejection untestedpackages/cli/src/serve/acp-http/dispatch.ts:3953 — [review] R1-8 test gap: workspace-qualified ACP forget scope pass-through untestedpackages/cli/src/acp-integration/acpAgent.ts:8260 — [review] R1-9 test gap: REMEMBER-side ext-method invalid-scope rejection untestedpackages/sdk-typescript/src/daemon/DaemonClient.ts:2198 — [review] R1-10 SDK sends scope unconditionally; the three new capability tags have zero read sitespackages/cli/src/serve/workspace-remember.ts:161 — [review] D4-1 remember-side task snapshot scope echo is untested (deferred: anchored on code unchanged since the previous round)packages/cli/src/serve/workspace-remember.ts:796 — [review] D4-2 committed API contract doc not updated for scope and the new error codes (deferred: anchored on code unchanged since the previous round)
中文说明
仅完成部分审查,审查缺口已披露。
未审查:build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally (the capability-snapshot file inside it was verified by probe and fails; the rest of the integration suite remains unexercised)。
收敛姿态下延后(第 4 轮,非阻断)——已记录,本轮不要求修改:共 10 条(原文未翻译,列表见上方英文部分)。
— qwen3.8-max via Qwen Code /review (v0.22.0)
…#9895) Restore the user-first deny-rule root order, drop the exact-duplicate prompt exception that collided with remember_no_update, exclude MEMORY.md from the early-completion abort trigger, skip the excluded store in scoped forget, and update/witness the scope surfaces across REST, ACP, SDK, and capability snapshot tests.
|
🤖 Addressed the latest review feedback (round 1/100). What changed, and what I pushed back on: · 已处理最新评审反馈(第 1/100 轮)。改动内容与我反驳保留之处如下: Round summary — PR #9895 review feedbackOutcome: changed. Commit This round addressed 11 findings (all 5 Critical + 6 Suggestions) and deferred 2 Suggestions (R1-3, R1-10) to the next round via Critical findings (all reproduced, then fixed)R1-1 — deny-rule message root order flipped (
|
|
Evidence for the round-15 That file did run in PR CI at this head ( Housekeeping: resolved 51 bot review threads from rounds 1–12 (2026-08-24 → 08-27). Every one is a single bot comment with no human reply, and none is standing in the round-15 ledger ( 中文说明针对第 15 轮的 另:resolve 了第 1–12 轮的 51 条 bot 评审 thread——均为无人类回复的单条 bot 评论,且没有一条仍在第 15 轮 ledger 中( |
|
@qwen-code /review |
|
🤖 AutoFix updated a stale base — the fix did not pass verification, but this PR was behind Round summaryFocus: same-run verification repair. Per the repair instruction, the previously rejected commit ( Root cause — reproduced and diagnosed from evidenceThe rejection's junit was written into this very checkout, so the failing suite was reproduced here directly: the test fails deterministically on this machine. A standalone probe printed the The chain: the test writes an extensionless fake Why it was not pushed: Note: the base has since been auto-updated; the verdict below predates that update, and the next round's re-measurement may charge the round. tests failed in packages/core 中文说明🤖 AutoFix 更新了一个过期的 base —— 修复未通过验证,但本 PR 落后于 验证门的拒绝原因与日志证据见上方英文部分(gate-rejection 不翻译)。 Run log: https://github.com/QwenLM/qwen-code/actions/runs/33159300648 🧠 Handled by Qwen Code · model/模型 |
The deterministic no-credential integration set (QwenLM#8313) ran as a step inside the Ubuntu `test` job. A step is invisible to anything that reads check names: the `Integration Tests (CLI, No Sandbox)` check is merge_group-only and shows as skipped on every PR, and the PR review bot ruled from that skip that a changed integration test "never ran" (QwenLM#9895 round 15) while this very gate had executed it and passed inside `test`. Move the gate into `integration_no_ak`, named `Integration Tests (no-AK, No Sandbox)`: same runner routing, same PR + merge-queue events, same isolated home and cleared credentials, same typecheck-then-run order. The CI profile is classified in the job itself (same wrapper as `test`) so a docs-only PR still skips it without queueing the check behind the hour-long unit run. The pre-checkout sweep is the byte-identical copy the cleanup test requires; the size baseline moves with the file. Claude-Session: https://claude.ai/code/session_01GNcnzoA34LZr3rMhsHEcbs
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Partially reviewed — gaps disclosed.
16 Suggestion-level finding(s) this review confirmed are already reported on this PR and are not repeated:
- vacuous mid-run abort probes in the early-completion tests (forkedAgent.agent.test.ts:896/:803) — already reported (round 14 deferred list, review 5048355538; round 11 deferred list, review 5035681468)
- scoped-forget prompt assertions inside the runSideQuery mock (forget.test.ts:752-755) — already reported (round 9 deferred list, review 5028539093; confirmed duplicate R15-1 in review 5049095460)
- missing .catch on the no-update/scope-mismatch rebuilds masks the coded error (remember.ts:355) — already reported (round 9 deferred list, review 5028539093; round 14 deferred list, review 5048355538)
- remember task snapshot scope echo untested (workspace-remember.ts:161-162) — already reported (round 4 D4-1; round 9 deferred list, review 5028539093; round 14 deferred list, review 5048355538; confirmed duplicate R15-2 in review 5049095460…
- lane-level remember_scope_mismatch publicErrorMessage branch untested (workspace-remember.ts:209-211) — already reported (round 9 deferred list, review 5028539093; round 14 deferred list, review 5048355538; confirmed duplicate R15-3 in revi…
- new-capability-tag assertions live in a file no workspace test command collects (qwen-serve-routes.test.ts:345-346) — already reported (round 9 deferred list, review 5028539093; round 14 deferred list, review 5048355538; confirmed duplicate…
- SDK remember/forget body spreads pinned for one scope value each (DaemonClient.test.ts:7297-7300/:7183-7186) — already reported (round 10 deferred list, review 5031179662: ':2204 user-scoped remember SDK wire body unpinned' and ':7271 proje…
- scope-omitted branch of the ACP remember entry points unpinned (acpAgent.test.ts:9408) — already reported (round 11 deferred list, review 5035681468: 'acpAgent.ts:8612 unscoped remember ext-method lost its args pin')
- successful EDIT results never exercised through runForkedAgent (forkedAgent.agent.test.ts:367) — already reported (round 6 deferred list, review 5014923047; round 10 deferred list, review 5031179662; round 14 deferred list, review 504835553…
- resolveTrustedMemoryRoot docstring contradicts the new alias-symlink resolution (memory-scoped-agent-config.ts:240-242) — already reported (round 11 deferred list, review 5035681468: ':241 threat-model docstring contradicts the new projects…
- project-store rebuild-failure semantics pinned in neither direction (remember.test.ts:478-480) — already reported (round 10 deferred list, review 5031179662: ':466 strict project-index rebuild unpinned'; round 14 deferred list, review 50483…
- scope-omitted branch of the dispatch forget entry point unpinned (dispatch.ts:4079-4081) — already reported (round 11 deferred list, review 5035681468: ':3949 no over-the-wire test drives an unscoped remember/forget through the dispatcher o…
- daemon-side forget scope pass-through pinned only for 'user' (workspace-remember.test.ts:385 and twins) — already reported (round 10 deferred list, review 5031179662: ':7271 project-scoped forget untested at every layer of the destructive p…
- SDK forget pre-flight gate pinned for one scope value (DaemonClient.test.ts:7327-7329) — already reported (round 10 deferred list, review 5031179662: ':7271 project-scoped forget untested at every layer of the destructive path')
- daemon forget lane scope-omitted direction unpinned (workspace-remember.ts:537-539/:524) — already reported (round 11 deferred list, review 5035681468: ':3949 no over-the-wire test drives an unscoped remember/forget through the dispatcher o…
- unscoped zero-write remember_no_update guard unpinned (remember.test.ts:494-510) — already reported (round 6 deferred list, review 5014923047; round 12 deferred list, review 5036872927, D12-12)
Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI at ac8ab16 and its suite did not run locally: it collects integration-tests/cli/qwen-serve-routes.test.ts (changed by this PR), whose capability-array addition was verified by code inspection and registry-order cross-check only.
Not reviewed: reverse audit — stopped before round 5 by the review time budget.
Deferred under the convergence posture (round 16, not a blocker) — recorded, not requested in this round:
packages/core/src/memory/manager.ts:1429 — [probe] MemoryManager.forget scope pass-through has no test witnesspackages/core/src/memory/paths.ts:162 — [review] getAutoMemoryTrustedAnchor doc comment falsified by the new alias-symlink resolutionpackages/cli/src/acp-integration/acpAgent.test.ts:9498 — [probe] stale test asserts the deleted no-op success contract; remember_no_update rejection path untestedpackages/core/src/agents/forkedAgent.agent.test.ts:752 — [probe] cancelled-after-write test never asserts filesWritten; empty-list mutant survivespackages/core/src/memory/remember.ts:59 — [probe] scoped dirHint directory interpolations pinned only by substring checkspackages/cli/src/acp-integration/acpAgent.ts:8728 (+3 locations) — [review] ACP entry-point scope pass-through pinned for exactly one scope value per site
中文说明
仅完成部分审查,审查缺口已披露。
本轮确认的 16 条建议级发现已在 PR 上报告过,不再重复发布(列表见上方英文部分)。
未审查:build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI at ac8ab16 and its suite did not run locally: it collects integration-tests/cli/qwen-serve-routes.test.ts (changed by this PR), whose capability-array addition was verified by code inspection and registry-order cross-check only。
未审查:反向审计——评审时间预算不足,未能开始第 5 轮。
收敛姿态下延后(第 16 轮,非阻断)——已记录,本轮不要求修改:共 6 条(原文未翻译,列表见上方英文部分)。
— qwen3.8-max via Qwen Code /review (v0.22.2)
|
@qwen-code /review |
|
Qwen Code review request accepted. Review is running in workflow run. A command-triggered review is not listed under the checks of this PR; the result is posted here as a review when it finishes. |
… reporting Nine of the sixteen standing Suggestion-level findings on this PR were the same shape: a scope-carrying branch that only ever ran for one value, or a guard whose coded error nothing asserted. - remember.ts: the `remember_no_update` and `remember_scope_mismatch` guards awaited their index repair bare, so a rebuild rejection replaced the coded error every caller branches on. Both now match the failed/cancelled paths above them and swallow the repair failure. - remember.test.ts: the unscoped zero-write guard, both repair-also-fails paths, and the project-store rebuild direction (the counterpart to the best-effort user store) are pinned. - server.test.ts: the three new capability tags reached a real `/capabilities` body only in integration-tests/cli, which no workspace test command collects and which CI skipped at this head. Asserted on the wire, in registry order, in a collected file. - workspace-remember.test.ts: project-scoped forget (the destructive half, previously pinned for 'user' alone), the scope echo on a remember snapshot, both scope-omitted directions under `toStrictEqual` so an undefined-valued key cannot pass, and the `remember_scope_mismatch` public message. - memory-scoped-agent-config.ts: the `resolveTrustedMemoryRoot` docstring claimed the managed suffix is always appended literally, which the shared-project alias resolution had made false. Every added test was mutation-checked against the branch it covers. Claude-Session: https://claude.ai/code/session_01Wee3Z7ePGHvnp7hBygvbio
…cope arms The last seven standing Suggestion-level findings. The two early-completion abort probes were vacuous in a specific way: `runForkedAgent` defers its self-abort to a macrotask on purpose (so the batch that triggered it finishes emitting first), and both probes read `signal.aborted` in the statement right after the emit — before the deferral can land. They reported `false` for every run, aborting or not. Both now flush the macrotask first, and each is paired with a `true` reading taken after the same flush, so the `false` one is a statement about the predicate rather than about timing. Deleting the abort entirely now fails them; before, it did not. `edit` counts as a mutating tool alongside `write_file`, but the only edit this file emitted failed — so the success side was asserted nowhere and dropping `ToolNames.EDIT` from the check went unnoticed. Covered, including the early completion it triggers, which is the path a remember agent amending an existing entry takes. The scoped-forget prompt assertions ran inside the `runSideQuery` mock, where a run that never reached the selector skips them silently — worse for the negative claim, since "the excluded store never reaches the prompt" is also what an unreached mock reports. Hoisted out to the capture-then-assert shape this file already uses elsewhere, with the identically-shaped twin above fixed too. Also pinned: the SDK remember/forget body spreads for their second scope value each and the forget pre-flight gate for 'project' (the gate is `if (opts.scope)`, not scope-specific), the dispatcher's forget scope in both directions over the wire, and the ACP remember ext-method's scope-omitted arm — asserted strictly, since `objectContaining` cannot see an absent key and a leaked `scope: undefined` reads as a requested scope to `params.scope &&` downstream. Every added or repaired assertion was mutation-checked against the branch it covers. Claude-Session: https://claude.ai/code/session_01Wee3Z7ePGHvnp7hBygvbio
|
🤖 Reviewed the latest feedback — no changes needed. Why, point by point: · 已审阅最新反馈——无需改动。逐点说明原因如下: AutoFix round: no code changes neededThis round made no code changes. Every actionable item in the feedback is already resolved in the code on this branch — verified by execution and mutation probes, not assumed — and the remaining feedback is informational or explicitly deferred by the review itself. Triage of this round's feedback
Previous gate rejection — diagnosed, no code change indicatedThe rejected attempt (never pushed) failed on
VerificationAll runs at HEAD a81cf78, package
中文说明AutoFix 本轮:无需代码修改本轮没有做任何代码修改。反馈中所有可操作的条目在本分支的代码里都已经解决——以下结论均通过实际运行和变异探针(mutation probe)验证,而不是凭空假定——其余反馈要么是信息性的,要么已被评审本身明确延后。 本轮反馈的分诊
上一次门禁拒绝——已诊断,无需代码修改被拒绝的尝试(从未推送)失败于
验证以下全部在 HEAD a81cf78、
Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。 🧠 Handled by Qwen Code · model/模型 |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Partially reviewed — gaps disclosed.
9 Suggestion-level finding(s) this review confirmed are already reported on this PR and are not repeated:
- R17-1 remember-side dispatch scope wire pin missing (transport.test.ts:636) — already reported (round 11 deferred list, review 5035681468: ':3949 no over-the-wire test drives an unscoped remember/forget through the dispatcher'; round 16 dup…
- R17-2 acpAgent remember handler 'user' arm unpinned (acpAgent.ts:8627) — already reported (round 16 deferred list, review 5053009432: 'acpAgent.ts:8728 (+3 locations) — ACP entry-point scope pass-through pinned for exactly one scope value p…
- R17-3 integration envelope test unreachable by workspace commands (qwen-serve-routes.test.ts:345) — already reported (round 9 and round 14 deferred lists, review 5048355538; confirmed duplicate R15-4 in review 5049095460)
- R17-4 project-scope tier-hiding mirror test missing (remember.test.ts:367) — already reported (round 14 deferred list, review 5048355538: 'remember.ts:98 — Project-scope user-tier prompt omission unpinned')
- R17-5 rebuild-rejection swallows on error paths untested (remember.ts:339) — already reported (round 14 deferred list, review 5048355538: 'remember.test.ts:583 — Reject-path rebuild swallow pinned by no test')
- R17-7 vacuous finalText-suppression assertions (forkedAgent.agent.test.ts:515) — already reported (round 14 deferred list, review 5048355538: 'forkedAgent.agent.test.ts:581 — finalText-undefined assertions cannot pin the completedAfterWrite…
- R17-8 repair-completion await ordering unpinned (remember.test.ts:669) — already reported (round 14 deferred list, review 5048355538: 'remember.test.ts:983 — Rebuild-before-surface ordering cannot be pinned by toHaveBeenCalledWith')
- R17-10 acpAgent forget handler 'project' arm unpinned (acpAgent.test.ts:10256) — already reported (round 16 deferred list, review 5053009432: 'acpAgent.ts:8728 (+3 locations) — ACP entry-point scope pass-through pinned for exactly one scope…
- R17-11 setImmediate macrotask deferral unpinned (forkedAgent.ts:701) — already reported (round 11 and round 14 deferred lists, review 5035681468/5048355538: vacuous abort-probe items)
Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally.
Not explored to full depth (tool budget reached): chunk 7: running packages/core/src/memory/forget.test.ts and memory-scoped-agent-config.test.ts — the review worktree has no node_modules , and a monorepo install +….
Not reviewed: reverse audit — stopped before round 5 by the review time budget.
Deferred under the convergence posture (round 17, not a blocker) — recorded, not requested in this round:
packages/core/src/memory/remember.ts:131 — [probe] MEMORY.md rule contradicts embedded two-step protocol Step-2 index instructionpackages/sdk-typescript/test/unit/DaemonClient.test.ts:7203 — [probe] SDK remember pre-flight project-scope arm has no refusal test
Mechanism health: this round did not close cleanly, so it withholds the incremental anchor — and the round it recovered had no anchor this round could use either — none at all, one with no certifier, one certified by an identity other than the one this round runs under, or one this round's fetch refused or resolved to the head — so the next review re-reads the whole diff unless recovery grafts an earlier own anchor that the round running it can use onto the complete work list this round leaves behind, and keeps doing so until a round's marker carries an anchor again or a graft lands that the round running it can use. (Stated, not acted on — this changes nothing about what the round posts.)
中文说明
仅完成部分审查,审查缺口已披露。
本轮确认的 9 条建议级发现已在 PR 上报告过,不再重复发布(列表见上方英文部分)。
未审查:build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally。
未探索到全部深度(达到工具调用预算):chunk 7:running packages/core/src/memory/forget.test.ts and memory-scoped-agent-config.test.ts — the review worktree has no node_modules , and a monorepo install +…。
未审查:反向审计——评审时间预算不足,未能开始第 5 轮。
收敛姿态下延后(第 17 轮,非阻断)——已记录,本轮不要求修改:共 2 条(原文未翻译,列表见上方英文部分)。
机制健康:本轮未能干净收尾,因而扣留了增量锚点,而它恢复到的那一轮也没有留下本轮可用的锚点——要么完全没有、要么没有认证者、要么由本轮运行身份之外的身份认证、要么被本轮的获取拒绝或解析为头提交——因此下一次评审将重读整个 diff,除非恢复流程把本轮能使用的更早自有锚点嫁接到本轮留下的完整工作清单上;并会一直如此,直到某一轮的标记重新带上锚点,或落地的嫁接能被运行该轮的评审使用。(仅陈述,不据此行动——这不改变本轮发布的任何内容。)
— qwen3.8-max via Qwen Code /review (v0.22.2)
|
🤖 AutoFix updated a stale base — the fix did not pass verification, but this PR was behind Address-review summary — PR #9895 (round 19, same-run verification repair)Commits: preserved rejected The rejection and its diagnosisThe deterministic gate rejected the previous commit with "tests failed in packages/cli":
Why it was not pushed: Note: the base has since been auto-updated; the verdict below predates that update, and the next round's re-measurement may charge the round. round expands into CI/verification machinery outside the PR footprint 中文说明🤖 AutoFix 更新了一个过期的 base —— 修复未通过验证,但本 PR 落后于 验证门的拒绝原因与日志证据见上方英文部分(gate-rejection 不翻译)。 Run log: https://github.com/QwenLM/qwen-code/actions/runs/33215139550 🧠 Handled by Qwen Code · model/模型 |
Local verification report — real daemon, no mocksI built this branch locally and verified it end to end against a live A scriptable OpenAI-compatible fake model plays the hidden agent, so each run is deterministic and I can make the model deliberately target the wrong store. No network, no API key.
Result: 24/24 checks pass on this branch; 17 of them flip to FAIL on a merge-base bundleThe base column is the negative control — it re-runs the identical harness against a bundle built from the merge-base, which is what makes the checks meaningful rather than "green is green". The five checks that pass on both arms are deliberate: 1. The scope target is enforced at the permission boundary, not just in the promptThis is the claim that matters most, so I made the scripted model attack it directly: in each run it targets the forbidden store first, then the allowed one. The text in the panes is the literal tool result the daemon fed back to the model, captured off the wire. Concretely, on this branch:
On the merge-base every one of those attempts succeeds — the I also checked the prompt shape, since the enforcement and the steering have to agree: a 2. Scoped forget, first-write completion, and the new terminal outcomesFor forget I used the strongest available oracle: the candidate list handed to the selection model, read off the wire. That is the direct answer to "was the other store searched at all?"
3. Capability negotiation and the SDK pre-flight
Zero requests leave the client. Without the pre-flight that call would have run an unscoped forget against an old daemon, which deletes from both stores — so this guard is earning its keep. Invalid scopes fail closed on every surface I could reach: REST 4. The shared-project alias exception (the part I probed adversarially)
So the exception did not widen the escape surface, which was my main concern reading that hunk. 5. Repository gates
Two environment caveats, both confirmed not caused by this PR:
VerdictNo blocking defect found. Everything the PR description claims — explicit target, filesystem-level enforcement in both directions, scope-limited forget selection, fail-closed validation, capability negotiation, and completion on the first successful entry write — reproduces on a live daemon, and each one demonstrably fails without the patch. Non-blocking notes for the author
中文版本地验证报告 —— 真实 daemon,无 mock我在本地构建了这个分支,并对着真实运行的 隐藏 Agent 背后接的是一个可编排的 OpenAI 兼容假模型,所以每次运行都是确定的,并且我可以让模型故意写错存储区。全程不联网、不需要 API key。
结论:本分支 24/24 全部通过;其中 17 项在 merge-base 上翻转为 FAILBASE 列是反向对照 —— 用同一套 harness 打 merge-base 构建出的 bundle。有了它,这些检查才有判别力,而不只是"绿即通过"。 在两个臂都通过的 5 项是刻意为之: 1. 范围目标在权限边界被强制执行,而不只是写在提示词里这是最关键的一条,所以我让脚本模型直接攻击它:每次运行先写被禁止的存储区,再写允许的。截图里的文字是 daemon 回传给模型的原始 tool result,从线上抓取。 在本分支上:
在 merge-base 上,以上每一项尝试都成功了 —— 我也检查了提示词形态,因为强制执行和引导必须一致: 2. 按范围去除、首次写入即完成,以及新的终态语义去除记忆我用了最强的可用判据:从线上读取交给选择模型的候选清单。这直接回答了"另一个存储区到底有没有被搜索"。
3. 能力协商与 SDK 预检
没有任何请求离开客户端。如果没有这个预检,这次调用会在旧 daemon 上执行不带范围的去除,从两个存储区删除条目 —— 所以这道守卫是值得的。 非法 scope 在我能触达的每个表面都失败关闭:REST 对 4. 共享项目别名例外(我做了对抗性探测的部分)
也就是说,这个例外没有扩大逃逸面 —— 这是我读那段 hunk 时最担心的一点。 5. 仓库门禁
两条环境说明,都已确认与本 PR 无关:
结论未发现阻塞性缺陷。 PR 描述声称的全部内容 —— 显式目标、双向的文件系统级强制、按范围限定的去除候选、失败关闭的校验、能力协商、以及首个条目成功写入即完成 —— 都在真实 daemon 上复现,并且每一项在去掉本补丁后都会失败。 给作者的非阻塞说明
Verified with Claude Code · model: Claude Opus 5 (1M context) |
|
@qwen-code /triage |
|
Sandboxed verification: ✅ passed — merge-ready (agent verdict) - workflow run Ran the PR in an isolated, token-free container: A/B against the base build, mock-free harness assertions, targeted gates. Advisory evidence for human reviewers — not a review, an approval, or a CI check. Scripted assertions: 42 passed · 0 failed · 42 total Flakiness gate: 中文 — 判定:✅ 通过 · 可合入(agent 判定)沙箱验证在隔离、无凭证的容器中执行了该 PR 的代码(与 base 构建 A/B 对照、无 mock harness 断言、定向门禁)。仅作为评审证据,不构成评审、批准或 CI 检查。 脚本断言:42 通过 · 0 失败 · 42 总计 抖动门: Verification reportPR #9895 Deep Verification — feat(daemon): support scoped workspace memory tasksVerdict:
|
| cell | arm | model behavior | oracle | head | base |
|---|---|---|---|---|---|
malicious-project |
scope=project | turn 1 writes USER entry, turn 2 writes project entry | hostile USER entry on disk? | NOT on disk (denied); only project written; touchedScopes=[project]; index rebuilt |
lands on disk (no boundary) |
malicious-user |
scope=user | turn 1 writes PROJECT entry, turn 2 writes user entry | hostile PROJECT entry on disk? | NOT on disk; only user written; touchedScopes=[user] |
lands on disk |
auto |
scope omitted | writes a user entry | behavior preserved? | completes, touchedScopes=[user] |
identical — no regression |
Counts: head malicious-project 6/6, head malicious-user 6/6, head auto 3/3; base controls
flip as predicted (hostile write lands). The "wire proof" assertions additionally confirm the
cross-scope write was genuinely attempted (present in the follow-up request's message history),
so the empty disk on head is a denial, not a no-op.
Early completion (secondary claim, witness: 02-ab-early-completion-head-vs-base.png)
| cell | arm | model behavior | oracle | head | base |
|---|---|---|---|---|---|
chatty |
model keeps requesting more writes after the first | # model requests; extra writes land? | completes after first write | 1 request, only c1 lands, completed |
3 requests, c1+c2 land, rejects MAX_TURNS |
Scoped forget (witness: 03-scoped-forget-head-vs-base.png)
| cell | arm | seeded | result | head | base |
|---|---|---|---|---|---|
| scope=project | both stores hold matching token | only project deleted, user intact | removedEntries=1, touchedScopes=[project] |
✅ | ignores scope — deletes both (removedEntries=2) |
| scope=user | mirror | only user deleted | removedEntries=1, [user] |
✅ | (n/a — base has no scope) |
| omitted | both | both deleted | removedEntries=2 |
✅ | ✅ identical |
Mutation matrix (witness: 04-mutation-matrix.png)
Each guard isolated by reverting exactly one hunk in a scratch copy of the head dist:
| mutant | reverted hunk | predicted | observed | classification |
|---|---|---|---|---|
M1 mut-boundary |
remember.ts scope→permission wiring | hostile write lands | hostile USER write lands and run throws remember_scope_mismatch (audit backstop fires) |
guard load-bearing |
M2 mut-early |
completeAfterFirstSuccessfulWrite |
loop resumes | 3 requests, extra writes land, MAX_TURNS |
guard load-bearing |
C1 mut-forget-skip |
scan-skip only | scoping preserved | scoping preserved (3/3) | redundant defence |
C2 mut-forget-filter |
candidate filter only | scoping preserved | scoping preserved (3/3) | redundant defence |
C3 mut-forget-both |
both layered guards | scoping breaks | deletes from both stores | the set is load-bearing |
M1 and M2 double as the positive control: the harness can fail, and reverting a load-bearing
hunk does fail it. M1 additionally shows defence-in-depth: with the permission boundary gone, the
post-run scope audit still rejects the cross-scope result with a coded error instead of
reporting success.
Test vacuity check: reverting the single includeProjectMemory guard in
memory-scoped-agent-config.ts (source) turned exactly 1 test red in
memory-scoped-agent-config.test.ts (the user-scoped write denial), proving that test pins the
guard. Source restored byte-identical afterward (git diff clean).
Corrections
None — no prior review round or bot comment in this run to correct.
Findings
No blocking findings. One observation, reported for completeness rather than as a defect:
- Scoped forget carries two redundant guards for one hazard (informational). The scan-skip
(forget.tsreturns[]for the excluded store) and the candidate filter
(if (scope && storageScope !== scope) continue;) each independently enforce the scope:
reverting either alone changed nothing (C1, C2), reverting both broke it (C3). This is correct
defence-in-depth as shipped — neither should be removed — and matches the PR's stated intent
(skip also keeps a failure in the never-deleted store from failing the scoped forget). No action
required.
Not covered
- Early-completion vs external-abort race (the
b0a1149cfix: an external cancel landing on a
batch boundary). Exercised only through the PR'sforkedAgent.agent.test.ts(22 tests green),
not reproduced end-to-end as a live race. - Live-daemon REST/ACP polling (Reviewer Test Plan steps 1–2 phrased as "poll each task"):
scope propagation and fail-closedinvalid_scopewere verified through the realcreateServeApp
Express routes under supertest (38+42 tests green) and the real core pipeline, but not against a
long-running daemon process. - Index rebuild on failed/cancelled runs and
protectPinnedMemorypaths: covered by the
PR's unit tests (green) but not independently driven by this round's harnesses. - Trial merge into current
main: environment has no GitHub token and the checkout is depth-2
(merge commit + two parents only), so conflict-freeness against livemainwas not re-measured. - Windows / Linux behavioral parity (author marked
⚠️ ): only Linux in-container. - Repo-wide
typecheck/ full-suite: not re-run (CI already covers); targeted gates below were
run from a known-clean state.
Methodology
Environment: CI verify container (node:22-bookworm), working tree at refs/pull/9895/merge
(depth 2). npm ci + npm run build already complete at HEAD. No GitHub token; local tree +
metadata snapshot treated as the whole world.
Harnesses (kept in this artifact dir, rerunnable): ab-scoped-remember.mjs (remember A/B),
scoped-forget.mjs (forget), make-mutants.mjs (builds the five scratch mutant dists),
run-evidence.sh (labelled evidence slices). Each harness isolates memory under a fresh
QWEN_CODE_MEMORY_BASE_DIR and a temp project root, builds a real Config
(authType=USE_OPENAI, modelProvidersConfig pointed at the loopback fake), calls
refreshAuth + initialize, and drives the compiled dist over real sockets. Raw per-cell logs in
logs/; image witnesses in evidence/.
Base control: git worktree add tmp/base-tree HEAD^1, rebuilt only packages/core (tsc
emitted despite pre-existing type-only errors in telemetry/node-pty unrelated to this PR; emitted
JS verified by behavior). node_modules reused from root — a clean control because the PR
leaves package.json/package-lock.json byte-identical (git diff HEAD^1..HEAD --stat on all
package manifests is empty). Realpath of internal deps asserted (base dist resolves its own
node_modules symlink; no @qwen-code/* workspace link silently re-pointed at head code).
Targeted gates (all green):
- core:
remember.test.ts+forget.test.ts+memory-scoped-agent-config.test.ts→ 89 pass;
forkedAgent.agent.test.ts→ 22 pass. - cli:
workspace-remember.test.ts+workspace-qualified-rest.test.ts→ 80 pass;
acp-http/workspace-qualified-acp.test.ts+acp-http/transport.test.ts→ 401 pass;
serve/server.test.ts+acp-integration/acpAgent.test.ts→ 1614 pass, 1 skipped. - sdk-typescript:
test/unit/DaemonClient.test.ts→ 358 pass.
Assertion accounting (42 = 36 + 5 + 1): 36 harness cell assertions (19 head + 7 base controls
- 10 forget), 5 mutation-matrix delta assertions (each mutant produced its predicted outcome), and
1 test-vacuity assertion.[FAIL]lines emitted by the head-encoded harness when run against a
base/mutant arm are expected outcomes (controls / kills) and are counted as pass per the
"expected failures are passes" rule;assertions.json.failcounts only unexpected outcomes
(none).
Flakiness gate log
integration test, out of gate scope: integration-tests/cli/qwen-serve-routes.test.ts
rounds=5 files=11 skipped=1
file packages/cli/src/acp-integration/acpAgent.test.ts: (cd packages/cli) npx --no-install vitest run ./src/acp-integration/acpAgent.test.ts
file packages/cli/src/serve/acp-http/transport.test.ts: (cd packages/cli) npx --no-install vitest run ./src/serve/acp-http/transport.test.ts
file packages/cli/src/serve/acp-http/workspace-qualified-acp.test.ts: (cd packages/cli) npx --no-install vitest run ./src/serve/acp-http/workspace-qualified-acp.test.ts
file packages/cli/src/serve/server.test.ts: (cd packages/cli) npx --no-install vitest run ./src/serve/server.test.ts
file packages/cli/src/serve/workspace-qualified-rest.test.ts: (cd packages/cli) npx --no-install vitest run ./src/serve/workspace-qualified-rest.test.ts
file packages/cli/src/serve/workspace-remember.test.ts: (cd packages/cli) npx --no-install vitest run ./src/serve/workspace-remember.test.ts
file packages/core/src/agents/forkedAgent.agent.test.ts: (cd packages/core) npx --no-install vitest run ./src/agents/forkedAgent.agent.test.ts
file packages/core/src/memory/forget.test.ts: (cd packages/core) npx --no-install vitest run ./src/memory/forget.test.ts
file packages/core/src/memory/memory-scoped-agent-config.test.ts: (cd packages/core) npx --no-install vitest run ./src/memory/memory-scoped-agent-config.test.ts
file packages/core/src/memory/remember.test.ts: (cd packages/core) npx --no-install vitest run ./src/memory/remember.test.ts
file packages/sdk-typescript/test/unit/DaemonClient.test.ts: (cd packages/sdk-typescript) npx --no-install vitest run ./test/unit/DaemonClient.test.ts
per-file results (P=pass F=fail I=infra-exit, one letter per run):
packages/cli/src/acp-integration/acpAgent.test.ts: PPPP
packages/cli/src/serve/acp-http/transport.test.ts: PPP
packages/cli/src/serve/acp-http/workspace-qualified-acp.test.ts: PPP
packages/cli/src/serve/server.test.ts: PPP
packages/cli/src/serve/workspace-qualified-rest.test.ts: PPP
packages/cli/src/serve/workspace-remember.test.ts: PPP
packages/core/src/agents/forkedAgent.agent.test.ts: PPP
packages/core/src/memory/forget.test.ts: PPP
packages/core/src/memory/memory-scoped-agent-config.test.ts: PPP
packages/core/src/memory/remember.test.ts: PPP
packages/sdk-typescript/test/unit/DaemonClient.test.ts: PPP
verdict: timeout
summary: only 3 of 5 rounds fit the 15-minute budget; the completed rounds agreed
--- per-invocation detail (full copy in the artifact) ---
round 1 · packages/cli/src/acp-integration/acpAgent.test.ts: P (exit 0)
round 1 · packages/cli/src/serve/acp-http/transport.test.ts: P (exit 0)
round 1 · packages/cli/src/serve/acp-http/workspace-qualified-acp.test.ts: P (exit 0)
round 1 · packages/cli/src/serve/server.test.ts: P (exit 0)
round 1 · packages/cli/src/serve/workspace-qualified-rest.test.ts: P (exit 0)
round 1 · packages/cli/src/serve/workspace-remember.test.ts: P (exit 0)
round 1 · packages/core/src/agents/forkedAgent.agent.test.ts: P (exit 0)
round 1 · packages/core/src/memory/forget.test.ts: P (exit 0)
round 1 · packages/core/src/memory/memory-scoped-agent-config.test.ts: P (exit 0)
round 1 · packages/core/src/memory/remember.test.ts: P (exit 0)
round 1 · packages/sdk-typescript/test/unit/DaemonClient.test.ts: P (exit 0)
round 2 · packages/cli/src/acp-integration/acpAgent.test.ts: P (exit 0)
round 2 · packages/cli/src/serve/acp-http/transport.test.ts: P (exit 0)
round 2 · packages/cli/src/serve/acp-http/workspace-qualified-acp.test.ts: P (exit 0)
round 2 · packages/cli/src/serve/server.test.ts: P (exit 0)
round 2 · packages/cli/src/serve/workspace-qualified-rest.test.ts: P (exit 0)
round 2 · packages/cli/src/serve/workspace-remember.test.ts: P (exit 0)
round 2 · packages/core/src/agents/forkedAgent.agent.test.ts: P (exit 0)
round 2 · packages/core/src/memory/forget.test.ts: P (exit 0)
round 2 · packages/core/src/memory/memory-scoped-agent-config.test.ts: P (exit 0)
round 2 · packages/core/src/memory/remember.test.ts: P (exit 0)
round 2 · packages/sdk-typescript/test/unit/DaemonClient.test.ts: P (exit 0)
round 3 · packages/cli/src/acp-integration/acpAgent.test.ts: P (exit 0)
round 3 · packages/cli/src/serve/acp-http/transport.test.ts: P (exit 0)
round 3 · packages/cli/src/serve/acp-http/workspace-qualified-acp.test.ts: P (exit 0)
round 3 · packages/cli/src/serve/server.test.ts: P (exit 0)
round 3 · packages/cli/src/serve/workspace-qualified-rest.test.ts: P (exit 0)
round 3 · packages/cli/src/serve/workspace-remember.test.ts: P (exit 0)
round 3 · packages/core/src/agents/forkedAgent.agent.test.ts: P (exit 0)
round 3 · packages/core/src/memory/forget.test.ts: P (exit 0)
round 3 · packages/core/src/memory/memory-scoped-agent-config.test.ts: P (exit 0)
round 3 · packages/core/src/memory/remember.test.ts: P (exit 0)
round 3 · packages/sdk-typescript/test/unit/DaemonClient.test.ts: P (exit 0)
round 4 · packages/cli/src/acp-integration/acpAgent.test.ts: P (exit 0)
Evidence images
Harness scripts and raw logs are in the workflow run artifacts (7-day retention).
— Qwen Code · sandboxed verification
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
LGTM, looks ready to ship. ✅
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Reviewed.
4 Suggestion-level finding(s) this review confirmed are already reported on this PR and are not repeated:
- R18-1 untested scope hop in bridge.ts { ...request } spread — already reported (round-11 deferred list, review 5035681468)
- R18-2 MemoryManager.forget scope forwarding unpinned — already reported (round-16 deferred list, review 5053009432)
- R18-4 new-capability-tag assertions live in a file no workspace test command collects — already reported (R15-4 in review 5049095460; round-14 deferred list, review 5048355538)
- R18-5 project-scope tier-hiding mirror test missing — already reported (round-14 deferred list, review 5048355538; R17-4 in review 5055256245)
中文说明
已审查。
本轮确认的 4 条建议级发现已在 PR 上报告过,不再重复发布(列表见上方英文部分)。
— qwen3.8-max via Qwen Code /review (v0.22.3)
|
🔀 Base updated: red check(s) [Test (ubuntu-latest, Node 22.x)] pass on current main — merged current main via update-branch; CI will re-run. 中文说明🔀 已更新 base:红色检查 [Test (ubuntu-latest, Node 22.x)] 在当前 main 上通过 —— 已通过 update-branch 合入当前 main,CI 将重新运行。 |
Disposition of the four deferred Suggestion-level findingsRound 18 re-listed R18-1/-2/-4/-5 as "already reported, not repeated". Since the PR is approved and merge-ready, I checked each one against the code at Two of the four are no longer accurate. The remaining two are real but narrow, and both are behavior-verified on a live daemon — they are missing tests, not missing behavior.
Why the two open ones are safe to land as follow-upsBoth are covered behaviorally by the live-daemon run, they just are not locked down by a unit test:
RecommendationMerge as is. R18-1, R18-2 and R18-5 are three small test-only additions that belong in a follow-up PR rather than a nineteenth round on an approved branch — the surrounding behavior is already proven, and re-opening this branch costs another full CI cycle for no behavioral change. R18-4 needs nothing; it can be struck from the deferred ledger. 中文版四条延期的 Suggestion 级发现的处置第 18 轮把 R18-1/-2/-4/-5 作为"已报告、不再重复"再次列出。鉴于本 PR 已 approve 且可合入,我对着 其中两条已经不成立。 剩下两条属实,但很窄,而且都已在真实 daemon 上验证过行为 —— 缺的是测试,不是行为。
为什么这两条未决项可以放到后续 PR两条在真实 daemon 的运行里都已被行为覆盖,只是没有被单测锁住:
建议按现状合入。R18-1、R18-2、R18-5 是三处纯测试补充,更适合放到后续 PR,而不是在一个已 approve 的分支上再开第 19 轮 —— 周边行为已经证明过,重开这个分支只会再烧一整轮 CI 而不带来任何行为变化。R18-4 无需处理,可以从延期清单里划掉。 Verified with Claude Code · model: Claude Opus 5 (1M context) |







What this PR does
This change adds an optional
projectorusertarget to sessionless managed-memory remember and forget tasks across workspace-qualified REST, ACP extension methods, and the TypeScript daemon SDK. New capability tags let clients negotiate project remember, user remember, and scoped forget support before exposing those controls.The selected target is enforced at the hidden agent's filesystem permission boundary: a project task cannot read or write user memory, a user task cannot read or write project memory, and scoped forget searches only the requested store. Omitted scope preserves the existing automatic classification behavior.
An explicit remember task now completes after the first successful managed entry write instead of waiting for another model turn. The caller rebuilds the corresponding index and rejects no-op, index-only, out-of-memory-root, and cross-scope results with stable public errors.
Why it's needed
Multi-workspace clients need deterministic storage semantics for manual memory actions. A control labeled “current workspace” must not be reclassified into shared user memory, and a user-memory action must not write into one project. The same boundary is required before destructive forget operations can be exposed safely.
The previous remember flow could also appear stuck after the durable entry already existed because the hidden agent waited for a follow-up model response. Completing on the successful write makes task status reflect the actual persistence boundary and lets clients refresh immediately.
Reviewer Test Plan
How to verify
completedon the first poll after the entry write, with the rebuilt index containing the new entry.npm run build,npm run typecheck, and the focused core, daemon REST/ACP, and SDK tests.Evidence (Before & After)
Before: manual remember/forget requests had no target contract, so the hidden agent chose project versus user storage heuristically; a completed file write could still be followed by another model turn before the daemon reported success.
After: clients negotiate and send an explicit target, the hidden agent is permission-scoped to that store, forget selection is scope-limited, and a successful managed entry write completes the task and triggers index rebuild immediately.
Tested on
Environment (optional)
macOS arm64, Node.js v25.9.0, npm 11.12.1. The full repository build and typecheck passed. Focused validation passed for 84 core memory/agent tests, 348 SDK daemon-client tests, 38 workspace-qualified REST tests, 49 workspace-qualified ACP tests, 36 remember-lane tests, and the changed read-only server test in isolation. One unrelated full-server-suite run produced a transient
socket hang up; the exact test passed when rerun alone.Risk & Scope
Linked Issues
Closes #9894
Related to #6974, #8854, and #9658.
中文说明
本 PR 的改动
本改动为无会话的托管记忆“记住”和“去除”任务增加可选的
project或user目标,并贯通工作区限定 REST、ACP 扩展方法和 TypeScript daemon SDK。新增能力标签,客户端可以在展示项目记忆、用户记忆和按范围去除记忆的操作前完成能力协商。所选目标会在隐藏 Agent 的文件系统权限边界被强制执行:项目任务不能读取或写入用户记忆,用户任务不能读取或写入项目记忆,按范围去除只搜索请求指定的存储区。不传 scope 时继续保留现有的自动分类行为。
显式记住任务现在会在第一个托管记忆条目成功写入后完成,不再等待下一轮模型响应。调用方会重建对应索引,并以稳定的公开错误拒绝无写入、仅索引写入、越出记忆根目录以及跨范围写入的结果。
为什么需要
多工作区客户端需要为手动记忆操作提供确定的存储语义。标注为“当前工作区”的操作不能被重新分类到共享用户记忆,用户记忆操作也不能写入某个项目。在安全开放破坏性的去除记忆操作前,同样需要这条范围边界。
之前的记住流程还可能在持久条目已经存在后继续等待下一轮模型响应,使任务看起来一直卡住。成功写入即完成,可以让任务状态与真实持久化边界一致,并让客户端立即刷新。
Reviewer 测试计划
如何验证
completed,且重建后的索引包含新条目。npm run build、npm run typecheck,以及 core、daemon REST/ACP 和 SDK 的聚焦测试。证据(改动前后)
改动前:手动记住/去除请求没有目标范围契约,隐藏 Agent 通过启发式规则选择项目或用户存储;文件完成写入后,daemon 仍可能等待下一轮模型响应才报告成功。
改动后:客户端协商并传入显式目标,隐藏 Agent 的权限被限制在该存储区,去除候选被限制在请求范围内,成功写入托管条目后任务立即完成并触发索引重建。
已测试平台
环境(可选)
macOS arm64,Node.js v25.9.0,npm 11.12.1。完整仓库构建和类型检查通过。聚焦验证通过:84 个 core 记忆/Agent 测试、348 个 SDK daemon 客户端测试、38 个工作区限定 REST 测试、49 个工作区限定 ACP 测试、36 个记忆任务队列测试,以及隔离运行的受影响只读服务器测试。一次与本改动无关的完整服务器测试运行出现瞬时
socket hang up,精确测试单独复跑后通过。风险与范围
关联 Issue
关闭 #9894。
关联 #6974、#8854 和 #9658。