Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
8f3f173
feat(core): let a workflow agent pin a directory and outlive the defa…
qqqys Aug 12, 2026
189ea1f
Merge branch 'main' into workflow/agent-working-dir
qqqys Aug 12, 2026
af057d6
fix(workflows): harden agent worktree pinning
qqqys Aug 12, 2026
c46bcdc
Merge branch 'main' into workflow/agent-working-dir
wenshao Aug 12, 2026
d6d197e
Merge branch 'main' into workflow/agent-working-dir
qwen-code-dev-bot Aug 12, 2026
b78fc69
fix(workflows): anchor worktree-pin containment at the main working tree
qwen-code-dev-bot Aug 12, 2026
dc40e92
Merge branch 'workflow/agent-working-dir' of https://github.com/qqqys…
qwen-code-dev-bot Aug 12, 2026
b989dc3
fix(workflows): refuse truncated main-tree anchors in worktree pinnin…
qwen-code-dev-bot Aug 12, 2026
8b49682
Merge branch 'main' into workflow/agent-working-dir
qwen-code-dev-bot Aug 12, 2026
1ef8777
fix(workflows): harden worktree pinning per round-3 review (#8972)
qwen-code-dev-bot Aug 12, 2026
6fd1953
fix(workflows): type worktree-pin test stub as nullable per service (…
qwen-code-dev-bot Aug 12, 2026
0c16d2d
Merge remote-tracking branch 'origin/main' into workflow/agent-workin…
qwen-code-dev-bot Aug 12, 2026
29b866c
Merge branch 'main' into workflow/agent-working-dir
qwen-code-dev-bot Aug 12, 2026
cdcb10a
fix(workflows): harden stallMs gate and test portability per round-4 …
qwen-code-dev-bot Aug 12, 2026
5158c81
test(webui): deflake same-session refresh transcript assertion (#8972)
qwen-code-dev-bot Aug 13, 2026
1fce8f9
Merge remote-tracking branch 'origin/main' into workflow/agent-workin…
qwen-code-dev-bot Aug 13, 2026
f11b7eb
docs: note the workflow workingDir opt is stricter than working_dir (…
qwen-code-dev-bot Aug 14, 2026
eaa6d6b
fix(core): sanitize workingDir echo in workflow refusal errors (#8972)
qwen-code-dev-bot Aug 14, 2026
49bfc9f
fix(core): name the subagent turn/time env caps in the workflow tool …
qwen-code-dev-bot Aug 16, 2026
0e365af
fix(core): preserve trailing CR in git worktree path answers (#8972)
qwen-code-dev-bot Aug 16, 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
28 changes: 28 additions & 0 deletions packages/core/src/agents/runtime/workflow-journal.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,34 @@ describe('canonicalizeAgentOpts', () => {
expect(c).toBe(JSON.stringify({ agentType: 'a1', model: 'm1' }));
});

// The same prompt run against two worktrees is two different questions.
// Projecting workingDir away would let a resume that changed only the
// directory replay the previous tree's answers as if they were this one's.
it('keeps workingDir, so a resume cannot hit across directories', () => {
const a = deriveAgentKey('', 'review it', {
workingDir: '.qwen/tmp/review-pr-1',
});
const b = deriveAgentKey('', 'review it', {
workingDir: '.qwen/tmp/review-pr-2',
});
expect(canonicalizeAgentOpts({ workingDir: 'wt' })).toBe(
JSON.stringify({ workingDir: 'wt' }),
);
expect(a).not.toBe(b);
// Symmetric HIT direction: the same workingDir must derive the SAME key,
// or resumeFromRunId silently misses the journal and re-spends every
// dispatch of a workingDir-using workflow.
expect(
deriveAgentKey('', 'review it', {
workingDir: '.qwen/tmp/review-pr-1',
}),
).toBe(
deriveAgentKey('', 'review it', {
workingDir: '.qwen/tmp/review-pr-1',
}),
);
});

it('sorts object keys deeply so reordered schemas hash the same', () => {
const a = canonicalizeAgentOpts({
schema: { type: 'object', properties: { b: 1, a: 2 } },
Expand Down
27 changes: 19 additions & 8 deletions packages/core/src/agents/runtime/workflow-journal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@
* key, and so on — so the cache naturally invalidates from the edit point.
*
* The `canonicalOpts` projection keeps only the dispatch-affecting opts
* (`schema`, `model`, `isolation`, `agentType`) with object keys sorted, so
* cosmetic opt differences (a re-ordered schema, a `label` change) don't
* bust the cache.
* (`schema`, `model`, `isolation`, `agentType`, `workingDir`) with object keys
* sorted, so cosmetic opt differences (a re-ordered schema, a `label` change)
* don't bust the cache.
Comment on lines 23 to +26

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Declined — no code change this round. Your own deterministic probe records that the behaviour is already gated: adding 'workingDir' to the canonicalizeAgentOpts projection is mutation-killed, and a whole-file revert is caught by workflow-journal.test.ts. Only the prose comment is ungated, and the finding explicitly states no code change is required for this PR. Gating the prose itself would mean a brittle assertion on comment text, so per the repo's simplicity rule we are not growing the diff for it. The residual drift risk (a future change dropping workingDir from the projection leaving this JSDoc stale) is acknowledged and recorded here; the projection assertion in workflow-journal.test.ts remains the behavioural gate.

中文说明

拒绝——本轮不做代码改动。您自己的确定性探针已确认行为本身有测试拦截:把 'workingDir' 加入 canonicalizeAgentOpts 投影会被变异杀死,整文件回退也被 workflow-journal.test.ts 拦截。只有这段文字注释没有被钉住,且该发现明确写明本 PR 无需改动代码。要钉住文字本身,只能靠对注释文本的脆弱断言,因此按本仓库的简洁性原则不为此扩大 diff。残余的漂移风险(未来改动把 workingDir 从投影中移除、这段 JSDoc 随之过期而无人提示)在此记录在案;workflow-journal.test.ts 中的投影断言仍是行为层面的拦截。

*
* Determinism requirement: workflow scripts are deterministic (`Date.now`
* / `Math.random` throw in the sandbox), so the sequence of `agent()`
Expand Down Expand Up @@ -66,14 +66,25 @@ export interface JournalReplay {

/**
* Project the dispatch-affecting opts into a stable canonical string. Only
* `schema` / `model` / `isolation` / `agentType` change what the dispatch
* does; `label` / `phase` / `stallMs` are cosmetic or operational and must
* NOT bust the cache. Object keys are sorted recursively so a re-serialized
* schema with reordered keys hashes the same.
* `schema` / `model` / `isolation` / `agentType` / `workingDir` change what
* the dispatch does; `label` / `phase` / `stallMs` are cosmetic or
* operational and must NOT bust the cache. Object keys are sorted recursively
* so a re-serialized schema with reordered keys hashes the same.
*
* `workingDir` is dispatch-affecting for the same reason it exists: the same
* prompt run against two different worktrees is two different questions. Were
* it projected away, a resume that changed only the directory would replay
* the previous tree's answers as if they were this one's.
*/
export function canonicalizeAgentOpts(opts: WorkflowAgentOpts): string {
const projected: Record<string, unknown> = {};
for (const k of ['schema', 'model', 'isolation', 'agentType'] as const) {
for (const k of [
'schema',
'model',
'isolation',
'agentType',
'workingDir',
] as const) {
Comment on lines +81 to +87

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

已修复:journal fileoverview 已把 workingDir 纳入 dispatch-affecting canonical options。验证证据:Prettier、ESLint、diff check 通过。

const v = opts[k];
if (v === undefined || typeof v === 'function') continue;
projected[k] = v;
Expand Down
Loading
Loading