fixup: 处理 PR #386 review 中尚未覆盖的 4 项

- src/cli/print.ts: cron onFire 改用 createAutonomyQueuedPromptIfNoActiveSource
  并以 prompt 文本作为 sourceId,避免同一定时提示在前一次 run 仍活跃时被重复
  入队叠加;顺手移除 4 个已没人引用的 dead import
  (commitAutonomyQueuedPrompt / prepareAutonomyTurnPrompt /
   markAutonomyRunCancelled / createAutonomyQueuedPrompt)
- src/services/compact/postCompactCleanup.ts: 在 void import().then() 处加
  注释,明确 sweepFileContentCache 是有意的 fire-and-forget,函数对外保持
  同步签名是设计而非疏忽
- src/utils/autonomyFlows.ts: 给 selectPersistedAutonomyFlows 的两阶段排序
  加文档注释(先按 active+updatedAt 选 top-N,再统一按 updatedAt 重排)
- tests/integration/autonomy-lifecycle-user-flow.test.ts: stderr 断言失败时
  把实际 stderr 内容写进 message,方便 CI 失败时定位
This commit is contained in:
Claude
2026-04-29 12:45:02 +00:00
parent f8388e44ed
commit 6b7cfda9b1
4 changed files with 25 additions and 12 deletions

View File

@@ -321,11 +321,8 @@ import {
} from 'src/utils/queryProfiler.js'
import { asSessionId } from 'src/types/ids.js'
import {
commitAutonomyQueuedPrompt,
createAutonomyQueuedPrompt,
createAutonomyQueuedPromptIfNoActiveSource,
createProactiveAutonomyCommands,
markAutonomyRunCancelled,
markAutonomyRunFailed,
} from 'src/utils/autonomyRuns.js'
import {
@@ -333,7 +330,6 @@ import {
claimConsumableQueuedAutonomyCommands,
finalizeAutonomyCommandsForTurn,
} from 'src/utils/autonomyQueueLifecycle.js'
import { prepareAutonomyTurnPrompt } from 'src/utils/autonomyAuthority.js'
import { jsonStringify } from '../utils/slowOperations.js'
import { skillChangeDetector } from '../utils/skills/skillChangeDetector.js'
import { getCommands, clearCommandsCache } from '../commands.js'
@@ -2827,17 +2823,22 @@ function runHeadlessStreaming(
onFire: prompt => {
if (inputClosed) return
void (async () => {
const prepared = await prepareAutonomyTurnPrompt({
// Use the prompt itself as the dedup source: legacy KAIROS-style
// cron entries fire the same prompt repeatedly, and without a
// dedicated task id the prompt text is what uniquely identifies
// the entry. Without source-dedup, repeated fires would stack
// additional runs while an earlier one is still active. Match the
// onFireTask branch below to keep the two paths consistent.
const command = await createAutonomyQueuedPromptIfNoActiveSource({
basePrompt: prompt,
trigger: 'scheduled-task',
currentDir: cwd(),
})
if (inputClosed) return
const command = await commitAutonomyQueuedPrompt({
prepared,
currentDir: cwd(),
sourceId: prompt,
sourceLabel: prompt,
workload: WORKLOAD_CRON,
shouldCreate: () => !inputClosed,
})
if (!command) return
if (inputClosed) {
await cancelQueuedAutonomyCommands({ commands: [command] })
return

View File

@@ -69,6 +69,12 @@ export function runPostCompactCleanup(querySource?: QuerySource): void {
// cacheUtils resets. See compactConversation() for full rationale.
clearBetaTracingState()
if (feature('COMMIT_ATTRIBUTION')) {
// Intentionally fire-and-forget: the file-content cache sweep is a
// best-effort memory release whose completion no caller depends on.
// Keeping `runPostCompactCleanup` synchronous lets compaction call sites
// (REPL post-compact handler, /compact command, autoCompact) finish their
// own state transitions without an extra microtask round-trip — the sweep
// catches up on the next event-loop tick.
void import('../../utils/attributionHooks.js').then(m =>
m.sweepFileContentCache(),
)

View File

@@ -170,6 +170,12 @@ function isManagedFlowStatusActive(status: AutonomyFlowStatus): boolean {
function selectPersistedAutonomyFlows(
flows: AutonomyFlowRecord[],
): AutonomyFlowRecord[] {
// Two-phase sort. Phase 1: priority sort (active flows first, then by
// updatedAt desc) selects the AUTONOMY_FLOWS_MAX most-relevant records to
// retain — active flows are guaranteed a slot before any inactive flow is
// considered. Phase 2: re-sort the retained slice by updatedAt desc only,
// so the persisted file is in plain reverse-chronological order regardless
// of activity status. Listings/UI consume the persisted order directly.
const retained = flows
.slice()
.map(cloneFlowRecord)

View File

@@ -42,8 +42,8 @@ async function runAutonomyCli(args: string[]): Promise<string> {
proc.exited,
])
expect(stderr).toBe('')
expect(exitCode).toBe(0)
expect(stderr, `unexpected stderr output:\n${stderr}`).toBe('')
expect(exitCode, `non-zero exit ${exitCode}; stderr:\n${stderr}`).toBe(0)
return stdout
}