feat(goal): make get_goal's default view a summary instead of the whole catalog - #9973
Conversation
…le catalog Every get_goal read returned the entire bounded evidence catalog -- up to 100 entries with 240-byte previews -- plus the Goal snapshot, which carries the evidence checkpoint verbatim: up to 32 claims of up to 2,000 characters each, every one of which is already in the catalog as a `goal_checkpoint` entry with the same uuid and its own preview. A Goal that had been running for a while paid all of that on every read. In one session the prompt grew from 44k to 277k tokens over 27 rounds; in another the model read the Goal 32 times in 34 minutes. get_goal now takes `view: 'summary' | 'full'`, default summary. The summary collapses the checkpoint's claims to a count (the catalog entries carry their previews), keeps full previews for checkpoint entries and for this turn's entries -- the compacted proof and the records a proposal cites next -- and caps previews from earlier turns at 80 bytes, cut on a code point. Every uuid is present in both views and remains valid for update_goal, which validates references against the runtime's own catalog, never against what the model was shown. `full` returns the payload exactly as before. On a steady-state fixture (32 maximal claims, a 100-entry catalog, a 16-turn lineage) the read drops from 105,317 bytes to 27,225 bytes; the test pins a 36,000-byte ceiling and a >=3x ratio on that fixture. Mutation probes: defaulting to `full` fails 3 tests; removing either preview exemption fails the summary test; breaking the `full` switch fails 2; leaving the claims uncollapsed fails 2; restored suite 39/39.
|
✅ Qwen Triage finished — CI landed green on ✅ Qwen Triage 已完成 —— |
|
@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,首轮处理将由下一次定时扫描执行(通常几分钟内)。移除 |
|
Thanks for the PR! Template looks good ✓ Problem: observed, not theoretical. The PR cites measured sessions (a 27-round run where the prompt grew from 44k to 277k tokens dominated by Direction: aligned. Read-cost of the Goal evidence catalog is squarely within the ongoing Goal efficiency work, and the reference ecosystem is investing in the same area (long-goal check-in backoff, redundant-normalization removal for long conversations). No direct CHANGELOG reference to a slim read, but the area is clearly relevant. Size: core path touched ( Approach: the scope feels right. The projection lives exactly where the read payload is shaped ( Risk: no elevated risk signals — no high-risk paths matched, and the payload shape has no parsing consumers outside the model surface. Moving on to code review. 🔍 中文说明感谢贡献! 模板完整 ✓ 问题:已观测到,不是理论问题。PR 给出了实测 session(27 轮 session 的 prompt 从 44k 涨到 277k token,主要是 方向:对齐。Goal 证据目录的读取成本完全属于正在进行的 Goal 效率工作,参考生态也在同一方向投入(长 goal 签退避、长对话去除冗余归一化)。CHANGELOG 无直接对应"精简读取"的条目,但该领域明显相关。 规模:触及核心路径( 方案:范围合理。投影恰好放在读取载荷成形的位置( 风险:无升级风险信号——未命中高风险路径,且该载荷形状在模型可见面之外没有任何解析消费者。 进入代码审查 🔍 — Qwen Code · qwen3.8-max Reviewed at |
Code reviewRead the diff against the base at What I verified in the code:
Non-blocking, for later:
Everything else in the diff is necessary for the stated goal — no drive-by changes. Test evidenceThis is an unattended triage run, so no PR code was built or executed here; the evidence below is the PR's own CI on the reviewed commit, fetched via API. At fetch time the Linux unit suite — the gate that runs the new Final CI results for
One row per check name (latest run); skipped checks omitted; failures sort first. / 每个检查名一行(取最新一次运行),省略 skipped,失败项排在最前。 The PR's central claim — the default read shrinks ~3.87× while every uuid stays citable and 中文说明代码审查:先独立推演再看 diff——我的方案(只在读取投影里去重 checkpoint claims、按码点截断旧轮次 preview、给引用路径保留完整 preview、留一个回到旧载荷的逃生口)与该 PR 完全一致,因此审查重点是正确性。已核实: 测试证据:本次为无人值守 triage,未构建或执行任何 PR 代码,以上为通过 API 获取的 PR 自身 CI。抓取时 Linux 单测(运行新增断言的关键门禁)仍在执行,finalize 工作流会在 CI 落定后更新表格。macOS/Windows/集成测试的跳过是仓库设计使然(仅在合并队列运行),不是失败。该 PR 的核心主张(默认读取缩小约 3.87 倍、所有 uuid 仍可引用、full 视图保持逐字节一致)正是新增单测断言的内容,绿色 CI 即可证实;作者自述的变异检验与 400 测试本地运行属作者声明,由 CI 独立执行。载荷无 UI 界面,真实场景 TUI 测试不适用。 — Qwen Code · qwen3.8-max Reviewed at |
|
Confidence: 4/5 — clean, minimal, and well-pinned by tests; the only nit is the duplicated byte-capping helper, which is non-blocking. Stepping back: this is the third PR in a series I can see being absorbed well (#9834 and #9835 already merged), and it earns its place the same way — a measured cost (reads of 100 KB+ on a mature Goal) removed with a read-side projection that touches nothing stored. The approach matches what I would have proposed before reading the diff, and the safety question this design lives or dies on — can a slim read break a later The diff carries nothing extra: two files, one projection function, one parameter, tests that assert the actual payload rather than the plumbing. In six months this reads as obvious code. The one thing I'd genuinely flag for the future is the two copies of the code-point byte-capping loop — fine today, worth unifying if a third appears. Approval is deferred until CI lands green on 中文说明总结:这是同系列中第三个 PR(#9834、#9835 已合入),同样站得住——用只读侧投影消除了成熟 Goal 上每次 100 KB+ 的读取成本,且不动任何存储。方案与我读 diff 前的独立设想一致;该设计的生死问题(精简读取会不会破坏后续 — Qwen Code · qwen3.8-max Reviewed at |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
LGTM, looks ready to ship — CI landed green after the review. ✅
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Reviewed — no blockers. Suggestions are inline.
中文说明
已审查——无阻断问题。 建议见行内评论。
— qwen3.8-max via Qwen Code /review (v0.22.0)
| /** Cut `value` to at most `limit` UTF-8 bytes without splitting a code point. */ | ||
| function capPreviewBytes(value: string, limit: number): string { |
There was a problem hiding this comment.
[Suggestion] The code-point-safe UTF-8 byte-cap is now implemented twice: this new capPreviewBytes(value, limit) is a verbatim copy of the existing private capPreviewBytes in goal-evidence.ts:1073 (fixed at CATALOG_PREVIEW_BYTE_LIMIT = 240), with only the limit parameterised. The byte-bound guarantee is load-bearing in this module — per the comments in goal-evidence.ts it is what actually holds the preview bound after a real CJK bug — and goal-tools.ts already imports from ./goal-evidence.js, so reuse costs nothing.
The cost is future divergence: any change to truncation semantics — appending a truncation marker as capCheckpointContent in the same file already does, or fixing a boundary edge — must then be applied in two files; updating only one silently diverges catalog-build previews (write path, 240-byte cap) from summary-view previews (read path, 80-byte cap) for the same data, while both copies pass their own tests.
// goal-evidence.ts — generalise the existing helper:
export function capPreviewBytes(value: string, limit: number): string { ... }
// its existing call sites pass CATALOG_PREVIEW_BYTE_LIMIT
// goal-tools.ts — delete this copy and call the shared one
// in summarizeCatalog: capPreviewBytes(entry.preview, SUMMARY_PREVIEW_BYTE_LIMIT)中文说明
码点安全的 UTF-8 字节截断现在有了两份实现:这个新的 capPreviewBytes(value, limit) 与 goal-evidence.ts:1073 中已有的私有 capPreviewBytes(固定为 CATALOG_PREVIEW_BYTE_LIMIT = 240)逐字相同,只是把上限参数化了。该字节边界保证在本模块中是承重的——按 goal-evidence.ts 的注释,在一次真实的 CJK bug 之后,它是真正守住 preview 边界的实现——而 goal-tools.ts 已经从 ./goal-evidence.js 导入符号,复用零成本。
代价是未来的分叉:任何对截断语义的修改——比如追加截断标记(同文件的 capCheckpointContent 已经这么做)、或修复某个边界情况——都必须同时改两处;只改其中一份,目录构建期(写路径,240 字节上限)与摘要视图(读路径,80 字节上限)对同一份数据的 preview 就会悄悄不一致,而两份副本各自的测试都还能通过。
修复方式见上方英文代码块:把 goal-evidence.ts 的助手函数参数化并导出,删除此处的副本,在 summarizeCatalog 中调用共享版本。
— qwen3.8-max via Qwen Code /review (v0.22.0)
| GetGoalTool.Name, | ||
| ToolDisplayNames.GET_GOAL, | ||
| 'Read the current Goal identity, objective, evidence cursor, and bounded evidence-reference catalog for this permitted Goal turn. Outside a permitted Goal turn it reports "active": false together with "lastGoal", a scalar summary (goalId, revision, status, turnCount, activeTimeMs, tokensUsed, and lastReason when one was recorded) of the session\'s most recent Goal, so a Goal that has already stopped can still be inspected. It never returns uncited transcript history or changes Goal state. Use the result silently; do not narrate or acknowledge the retrieval to the user.', | ||
| 'Read the current Goal identity, objective, evidence cursor, and bounded evidence-reference catalog for this permitted Goal turn. The default "summary" view keeps every read small: checkpoint claims are reported as a count (each claim is already an evidenceCatalog entry with its own preview), entries from this turn and checkpoint entries keep full previews, and entries from earlier turns carry previews shortened to 80 bytes. Every entry uuid is present in both views and is valid for update_goal; request view "full" only when a shortened preview is not enough to decide what to cite. Outside a permitted Goal turn it reports "active": false together with "lastGoal", a scalar summary (goalId, revision, status, turnCount, activeTimeMs, tokensUsed, and lastReason when one was recorded) of the session\'s most recent Goal, so a Goal that has already stopped can still be inspected. It never returns uncited transcript history or changes Goal state. Use the result silently; do not narrate or acknowledge the retrieval to the user.', |
There was a problem hiding this comment.
[Suggestion] The 80-byte summary cap is hardcoded as the literal "80 bytes" here and again as "80-byte previews" in the view parameter description (around line 148), instead of interpolating SUMMARY_PREVIEW_BYTE_LIMIT (defined in this file around line 52). Sibling code in this directory interpolates limits into LLM-facing prose — goal-checkpoint-verifier.ts puts ${GOAL_CHECKPOINT_CLAIM_MAX_BYTES} straight into its system prompt.
If SUMMARY_PREVIEW_BYTE_LIMIT is later tuned — its own comment frames it as a heuristic — the code honours the new value but both descriptions keep claiming 80 bytes. The model uses that number to decide whether a shortened preview suffices or whether to spend a view: "full" read, so it would reason about the payload with a wrong contract: requesting expensive full reads it was told to avoid, or trusting previews as longer than they are. The schema test only asserts stringContaining('summary (default)') and cannot catch the drift.
`…previews shortened to ${SUMMARY_PREVIEW_BYTE_LIMIT} bytes…`
`${SUMMARY_PREVIEW_BYTE_LIMIT}-byte previews for earlier turns`中文说明
80 字节的摘要上限在这里被硬编码为字面量 "80 bytes",并且在 view 参数描述中(约第 148 行)再次写作 "80-byte previews",而没有插值本文件约第 52 行定义的 SUMMARY_PREVIEW_BYTE_LIMIT。同目录的兄弟代码会把上限插值进面向模型的文案——goal-checkpoint-verifier.ts 就把 ${GOAL_CHECKPOINT_CLAIM_MAX_BYTES} 直接写进了系统提示词。
如果以后调整 SUMMARY_PREVIEW_BYTE_LIMIT(它自己的注释就把它描述为一个经验值),代码会采用新值,但两处描述仍声称 80 字节。模型用这个数字来判断缩短后的 preview 是否够用、是否需要花一次 view: "full" 读取,于是它会带着错误的契约来推理载荷:要么去请求被告知应避免的昂贵完整读取,要么把 preview 当作比实际更长来信任。schema 测试只断言 stringContaining('summary (default)'),无法发现这种漂移。
修复方式见上方英文代码块:把两处字符串改为模板字面量,插值 ${SUMMARY_PREVIEW_BYTE_LIMIT}。
— qwen3.8-max via Qwen Code /review (v0.22.0)
| return { | ||
| ...structuredClone(catalog), | ||
| entries, |
There was a problem hiding this comment.
[Suggestion] The summary path deep-clones exactly the data it then throws away: this structuredClone(catalog) is spread and then entries is replaced wholesale with the mapped shallow copies above — only lineageTurnIds/truncated survive the clone — and summarizeSnapshot (around line 524) likewise deep-clones the snapshot including all checkpoint claims before collapsing them to claimCount. A measurement probe at the documented caps (32 claims × 2,000 chars, 100 catalog entries) shows both views clone byte-for-byte the same volume: summary {clonedBytes: 105083, outputBytes: 27285} vs full {clonedBytes: 105083, outputBytes: 105093}.
So every default get_goal against a mid-running Goal allocates and immediately garbage-collects ~105 KB of transient object graphs — strictly more CPU/GC work per call than view: 'full', on precisely the data this PR exists to stop paying for. Bounded per call, so this is an efficiency issue rather than a correctness one.
Strip the dropped data before cloning instead of after, e.g.:
// summarizeCatalog:
const { entries: _entries, ...catalogRest } = catalog;
return {
...structuredClone(catalogRest),
entries,
...(shortenedPreviews > 0 ? { shortenedPreviews } : {}),
};
// summarizeSnapshot: build the collapsed { ..., evidenceCheckpoint: { ...rest, claimCount } }
// shape from the originals first, then clone that smaller shape.中文说明
摘要路径深克隆了它随后丢弃的数据:这里先展开 structuredClone(catalog),然后又用上面映射出的浅拷贝整体替换 entries——克隆体里只有 lineageTurnIds/truncated 存活;summarizeSnapshot(约第 524 行)同样先深克隆包含全部 checkpoint claims 的快照,再把它们折叠成 claimCount。在文档所述上限(32 条 claim × 2,000 字符、100 条目录条目)下的实测探针显示,两种视图克隆的字节量完全相同:摘要 {clonedBytes: 105083, outputBytes: 27285} 对比完整 {clonedBytes: 105083, outputBytes: 105093}。
于是运行中的 Goal 每被默认读取一次 get_goal,就会分配并立即回收约 105 KB 的临时对象图——每次调用比 view: 'full' 做更多的 CPU/GC 工作,而这些数据正是本 PR 要停止重复付费的部分。单次开销有上限,所以这是效率问题而非正确性问题。
修复方式是在克隆之前先剥离将被丢弃的数据,示例见上方英文代码块。
— qwen3.8-max via Qwen Code /review (v0.22.0)
| expect(entries.find((entry) => entry.uuid === 'earlier-1')?.preview).toBe( | ||
| '证'.repeat(26), | ||
| ); | ||
| expect(payload.evidenceCatalog.shortenedPreviews).toBe(60); |
There was a problem hiding this comment.
[Suggestion] The summary fixture gives every earlier-turn entry a 240-byte preview, so the branch where an earlier-turn preview is already within the 80-byte cap — kept unchanged and not counted in shortenedPreviews — is never exercised. A mutation check confirmed the gap: changing if (preview !== entry.preview) shortenedPreviews += 1; to an unconditional shortenedPreviews += 1; leaves the whole suite green (39/39), and the byte-identical pass-through guarantee for short earlier-turn previews is pinned by no assertion. A regression inflating shortenedPreviews would tell the model that a view: "full" read reveals more than it actually would — undermining the read-full decision the field exists to inform.
Add one earlier-turn entry whose preview is already under the cap, keep the count at 60, and pin the pass-through:
// in the checkpointedCatalog fixture:
{ uuid: 'earlier-short', provenance: 'tool_result', turnId: 'earlier-turn-0',
preview: '12 tests passed', proofKind: 'external_fact' },
// in the summary test — count unchanged:
expect(payload.evidenceCatalog.shortenedPreviews).toBe(60);
// plus an assertion that the short preview is returned byte-identicalThe probe verified the new assertion earns its place: the same mutant fails with AssertionError: expected 61 to be 60 once that entry exists, and the un-mutated code stays green with it.
中文说明
摘要视图的 fixture 给每一条更早轮次的条目都设置了 240 字节的 preview,因此「更早轮次的 preview 已经在 80 字节上限之内——保持原样且不记入 shortenedPreviews」这个分支从未被执行。变异检验确认了这个缺口:把 if (preview !== entry.preview) shortenedPreviews += 1; 改成无条件的 shortenedPreviews += 1;,整个测试套件仍然全绿(39/39);短 preview 逐字节原样返回的保证也没有任何断言锁定。一个夸大 shortenedPreviews 的回归会告诉模型 view: "full" 读取能揭示比实际更多的内容——破坏了这个字段本要支撑的「是否值得完整读取」的决策。
修复方式:在 checkpointedCatalog fixture 中增加一条 preview 已低于上限的更早轮次条目(示例见上方英文代码块),计数保持 60,并断言该短 preview 原样返回。探针已验证该断言有效:加入该条目后,同一变异体以 AssertionError: expected 61 to be 60 失败,未变异代码保持全绿。
— qwen3.8-max via Qwen Code /review (v0.22.0)
|
🤖 Addressed the latest review feedback (round 1/100). What changed, and what I pushed back on: · 已处理最新评审反馈(第 1/100 轮)。改动内容与我反驳保留之处如下: Round summary — PR #9973 (review round 2)All four suggestions from the automated review are accepted and implemented in Findings and dispositions
Mutation probeThe new assertion's witness was verified by mutation: changing Conflict notesNone — no merge was performed ( Verification
No settings source changed, so no schema regeneration. The touched behavior is 中文说明轮次总结 — PR #9973(审查第 2 轮)自动审查提出的四条建议全部接受,并在一个提交中实现。没有拒绝、推迟或升级任何发现。 发现与处理
变异探针新断言的见证能力已通过变异验证:将 冲突说明无——未执行合并( 验证
未改动任何设置源,因此无需重新生成 schema。受影响行为由 core 单元测试覆盖,而非捆绑 CLI 或集成测试框架,因此无需运行集成测试。 🦷 Gate advisory — this round's changed tests all pass on the pre-round tree (machine-measured, not agent-authored). Expected for a refactor or coverage addition; if this round was meant to FIX a defect, that defect did not reproduce. · 本轮改动的测试在轮前树上全部通过(门自动测量,非 agent 文本)。对重构或补充覆盖属正常;若本轮意在修复缺陷,则该缺陷未能复现。 Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。 Re-review when you have a moment. After round 100 this bot stops and leaves the PR for a human. · 有空请复审;第 100 轮后本 bot 停止并将 PR 交给人工。 🧠 Handled by Qwen Code · model/模型 |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
No blocking issues. LGTM! ✅
Not explored to full depth (tool budget reached): "agent 1a": none — no check was cut short..
Deferred under the convergence posture (round 2, not a blocker) — recorded, not requested in this round:
packages/core/src/goals/goal-tools.test.ts:525 — [probe] summary-view tests never pin truncated or lineageTurnIds contents; a mutation dropping truncated from summarizeCatalog survives 403/403 tests
中文说明
无阻断问题。LGTM!✅
未探索到全部深度(达到工具调用预算):"agent 1a":none — no check was cut short.。
收敛姿态下延后(第 2 轮,非阻断)——已记录,本轮不要求修改:共 1 条(原文未翻译,列表见上方英文部分)。
— qwen3.8-max via Qwen Code /review (v0.22.0)
|
Released in v0.22.2. |
What this PR does
Gives
get_goalaviewparameter —'summary'(default) or'full'— and makes the default read a summary instead of the whole evidence catalog. The summary collapses the Goal snapshot's checkpoint claims to a count (every claim is already agoal_checkpointcatalog entry with the same uuid and its own preview), keeps full 240-byte previews for checkpoint entries and for this turn's entries — the compacted proof of everything earlier and the records a proposal cites next — and shortens previews from earlier turns to 80 bytes, cut on a code point so multi-byte text is never split. The payload names its view and reports how many previews were shortened.view: 'full'returns the payload exactly as before. The tool description tells the model when to ask for the full view.Every entry uuid is present in both views and remains valid for
update_goal: that tool validatesevidenceRefsagainst a fresh runtime catalog, never against what the model was shown, which is what makes a slim read safe. Nothing about what the catalog stores changes — only what a read returns.Why it's needed
Every
get_goalread returned up to 100 catalog entries with 240-byte previews plus the Goal snapshot, which carries the evidence checkpoint verbatim — up to 32 claims of up to 2,000 characters each, all duplicated as catalog previews. A Goal that had been running for a while paid all of that on every read. In the 27-round session analyzed for this series the prompt grew from 44k to 277k tokens, dominated by these reads; in the second session the model calledget_goal32 times in 34 minutes (8.6M tokens in total). On a steady-state fixture — 32 maximal claims, a catalog at its 100-entry cap, a 16-turn lineage — one read drops from 105,317 bytes to 27,225 bytes (3.87×). The catalog is the right design; re-sending all of it on every read was the cost problem.Reviewer Test Plan
How to verify
cd packages/core && npx vitest run src/goals/— 400 tests, 16 files. New ingoal-tools.test.ts: theviewschema pin;collapses checkpoint claims and shortens earlier previews in the summary view(claim text absent, checkpoint collapsed to{checkpointId, createdAt, claimCount}, uuid list identical to the full catalog, 240-byte previews kept only for checkpoint and current-turn entries, a 240-byte CJK preview cut to exactly 26 characters / 78 bytes,shortenedPreviews: 60);returns the whole checkpoint and catalog in the full view(byte-for-byte today's payload);keeps a steady-state summary read under a fixed byte ceiling(full > 100,000 bytes, summary ≤ 36,000, ratio ≥ 3).fullfails 3 tests; removing the current-turn exemption fails the summary test; removing the checkpoint exemption fails the summary test; breaking thefullswitch fails 2 tests; leaving the claims uncollapsed fails 2 tests. Restored suite 39/39 in the file.npx tsc --noEmitinpackages/core: 0 errors. prettier and eslint clean on both changed files.returns only the bounded worker view for the captured permit) changes only by the newview: 'summary'key — its single entry is current-turn, so its preview is untouched.Evidence (Before & After)
N/A (tool payload only; no UI). Payload size on the steady-state fixture: before 105,317 bytes, after 27,225 bytes.
Tested on
Environment (optional)
N/A (unit tests only).
Risk & Scope
view: 'full'; that is one extra call in the rare case, versus the full catalog on every call today. The 80-byte cap and the two exemptions are the whole policy and live in one function.packages/clitests that quote the continuation prompt were not re-run (they do not read this payload). Overlaps textually with fix(goal): cite this turn's delivered output instead of refusing over it #9880 (auto-cite) and feat(goal): stop autonomous continuation at a token budget the user re-arms #9891 (tokenBudgetin the unpermitted summary) ingoal-tools.ts; both edits are in different functions, so the merge is mechanical.get_goalwith no arguments still succeeds; the payload gains aviewkey and, in summary,evidenceCatalog.shortenedPreviewsand a collapsedsnapshot.goal.evidenceCheckpoint.Linked Issues
Series context: #9834 (B2, merged prompt convergence), #9835 (C2, catalog byte budget), #9880 (G1, current-turn auto-cite).
中文说明
这个 PR 做了什么
为
get_goal增加view参数——'summary'(默认)或'full'——并让默认读取返回摘要而不是整本证据目录。摘要把 Goal 快照里的 checkpoint claims 折叠为数量(每条 claim 本来就是一条goal_checkpoint目录条目,uuid 相同、自带 preview),对 checkpoint 条目和本轮条目保留完整的 240 字节 preview——它们分别是此前一切的压缩证明和提案接下来要引用的记录——对更早轮次的条目把 preview 缩短到 80 字节,并按码点切割,绝不拆开多字节字符。载荷标明自己的视图并报告缩短了多少条 preview。view: 'full'原样返回此前的载荷。工具描述告诉模型何时该请求完整视图。两种视图中每个条目的 uuid 都存在,且对
update_goal仍然有效:该工具是对 runtime 新取的目录校验evidenceRefs,而不是对模型看到的内容校验——这正是精简读取安全的原因。目录存储的内容完全不变——只改变一次读取返回什么。为什么需要
每次
get_goal读取都会返回多达 100 条带 240 字节 preview 的目录条目,外加 Goal 快照——它原样携带证据 checkpoint:最多 32 条、每条最长 2,000 字符的 claim,而这些全部已在目录里以 preview 形式重复。运行了一段时间的 Goal 每次读取都要付这全部代价。本系列分析的 27 轮 session 里 prompt 从 44k 涨到 277k token,主要就是这些读取;第二个 session 里模型 34 分钟内调用了 32 次get_goal(总计 860 万 token)。在一个稳态 fixture 上——32 条最长 claim、100 条满载目录、16 轮 lineage——一次读取从 105,317 字节降到 27,225 字节(3.87 倍)。目录本身的设计是对的;每次读取都重发全部内容才是成本问题。评审验证计划
如何验证
cd packages/core && npx vitest run src/goals/——400 个测试,16 个文件。goal-tools.test.ts新增:viewschema 固定;collapses checkpoint claims and shortens earlier previews in the summary view(claim 文本不出现、checkpoint 折叠为{checkpointId, createdAt, claimCount}、uuid 列表与完整目录一致、只有 checkpoint 与本轮条目保留 240 字节 preview、一条 240 字节的中文 preview 被精确切到 26 个字符 / 78 字节、shortenedPreviews: 60);returns the whole checkpoint and catalog in the full view(与此前载荷逐字节一致);keeps a steady-state summary read under a fixed byte ceiling(完整 > 100,000 字节,摘要 ≤ 36,000,比值 ≥ 3)。full挂 3 个测试;去掉本轮豁免挂摘要测试;去掉 checkpoint 豁免挂摘要测试;破坏full开关挂 2 个;不折叠 claims 挂 2 个。还原后该文件 39/39。packages/core的npx tsc --noEmit:0 错误。两个改动文件 prettier 与 eslint 干净。returns only the bounded worker view for the captured permit)只多了新的view: 'summary'键——它唯一的条目属于本轮,preview 不变。证据(前后对比)
N/A(仅工具载荷,无 UI)。稳态 fixture 上的载荷大小:改前 105,317 字节,改后 27,225 字节。
已测试平台
Linux ✅;macOS / Windows⚠️ (由 CI 覆盖)。
风险与范围
view: 'full'再读一次;那是罕见情况下多一次调用,而今天是每次调用都发整本目录。80 字节上限和两项豁免就是全部策略,集中在一个函数里。packages/cli中引用续跑提示词的测试未重跑(它们不读取该载荷)。与 fix(goal): cite this turn's delivered output instead of refusing over it #9880(自动引用)和 feat(goal): stop autonomous continuation at a token budget the user re-arms #9891(未许可摘要里的tokenBudget)在goal-tools.ts上有文本重叠;改动位于不同函数,合并是机械性的。get_goal仍然成功;载荷新增view键,摘要视图下新增evidenceCatalog.shortenedPreviews与折叠后的snapshot.goal.evidenceCheckpoint。关联 Issue
系列上下文:#9834(B2,已合入的提示词收敛)、#9835(C2,目录字节预算)、#9880(G1,本轮自动引用)。