test: close per-file coverage gaps opened by the message-machine refactor

Downstream packages lost the tests that exercised their agent-loop-facing
edges when the loop was rewritten. Restore 100% per-file coverage with
behavior tests through public seams: llm-retry config validation and
cancellation races, goal replay drift/staleness/teardown edges, plan-mode
disposed-flush, workspace-context empty-change commits, api-proxy
synchronous send failures, acp-snapshot spill-path extraction and refresh
write-back, ACP injection-triggered turns, cli-demo and tui inbox
lifecycle edges, and agent-loop retry/settlement/lifecycle branches.
The only source changes are narrowly-justified v8 ignore annotations on
invariant guards and one redundant-guard removal (workspace-context).
This commit is contained in:
_Kerman
2026-07-26 11:49:19 +08:00
parent 8432c68b6a
commit af3311e1f6
16 changed files with 940 additions and 5 deletions

View File

@@ -117,8 +117,7 @@ function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === 'object' && value !== null && !Array.isArray(value)
}
function workspaceInstructionChanges(source: unknown): WorkspaceInstructionChange[] {
if (!isWorkspaceContextSource(source)) return []
function workspaceInstructionChanges(source: { changes: unknown[] }): WorkspaceInstructionChange[] {
const changes: WorkspaceInstructionChange[] = []
for (const value of source.changes) {
if (!isRecord(value)) continue

View File

@@ -3260,6 +3260,29 @@ describe('workspace context pending state', () => {
expect(versions.has(agent.session)).toBe(false)
})
it('keeps an unrelated scope\'s version fast path when a step-close discard empties only its own scope', () => {
const agent = stubAgent('/')
const pending = new WeakMap<object, Map<string, PendingInstructionChange>>()
const versions: InstructionVersionCache = new WeakMap()
agent.session.append('step/start', { turn: 1, step: 1 })
commitPendingInstructionContexts(agent, [workspaceChangeContext('pkg', 'one')], pending)
versions.set(agent.session, new Map([
['pkg', {
path: join('pkg', 'AGENTS.md'), version: FsVersion('v1'), digest: 'one', trimmedDigest: 'one',
}],
['other', {
path: join('other', 'AGENTS.md'), version: FsVersion('v2'), digest: 'two', trimmedDigest: 'two',
}],
]))
const ended = agent.session.append('step/end', { turn: 1, step: 1 })
observeInstructionSessionEvent(agent.session, ended, pending, versions)
expect(pending.has(agent.session)).toBe(false)
expect(versions.get(agent.session)?.has('pkg')).toBe(false)
expect(versions.get(agent.session)?.has('other')).toBe(true)
})
it('rolls back only the exact current transition and releases empty session state', () => {
const agent = stubAgent('/')
const pending = new WeakMap<object, Map<string, PendingInstructionChange>>()
@@ -3270,6 +3293,13 @@ describe('workspace context pending state', () => {
expect(commitPendingInstructionContexts(agent, [{
content: [], source: { kind: 'plugin', plugin: 'workspace-context' },
}], pending)).toEqual([])
// A workspace-instructions source whose change list filters to nothing
// must not mint per-session pending state.
expect(commitPendingInstructionContexts(agent, [{
content: [],
source: { kind: 'workspace-instructions', changes: [] },
}], pending)).toEqual([])
expect(pending.has(agent.session)).toBe(false)
const committed = commitPendingInstructionContexts(agent, [
workspaceChangeContext('first', 'one'),