fix(team): surface unreadable bootstrap journal warnings
This commit is contained in:
parent
8ef89eefce
commit
d484f1d62d
2 changed files with 47 additions and 1 deletions
|
|
@ -363,7 +363,11 @@ async function readBootstrapLockMetadata(teamName: string): Promise<BootstrapLoc
|
||||||
}
|
}
|
||||||
|
|
||||||
async function readBootstrapJournalWarnings(teamName: string): Promise<string[] | undefined> {
|
async function readBootstrapJournalWarnings(teamName: string): Promise<string[] | undefined> {
|
||||||
return (await inspectBootstrapJournal(teamName)).warnings;
|
const inspection = await inspectBootstrapJournal(teamName);
|
||||||
|
const warnings = [inspection.issue, ...(inspection.warnings ?? [])].filter(
|
||||||
|
(item): item is string => typeof item === 'string' && item.trim().length > 0
|
||||||
|
);
|
||||||
|
return warnings.length > 0 ? warnings : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function inspectBootstrapJournal(teamName: string): Promise<BootstrapJournalInspection> {
|
async function inspectBootstrapJournal(teamName: string): Promise<BootstrapJournalInspection> {
|
||||||
|
|
@ -436,6 +440,13 @@ async function inspectBootstrapJournal(teamName: string): Promise<BootstrapJourn
|
||||||
})
|
})
|
||||||
.filter((item): item is string => Boolean(item));
|
.filter((item): item is string => Boolean(item));
|
||||||
|
|
||||||
|
if (lines.length > 0 && messages.length === 0) {
|
||||||
|
return {
|
||||||
|
issue:
|
||||||
|
'Persisted deterministic bootstrap journal is unreadable because bootstrap-journal.jsonl is invalid, truncated, or inaccessible.',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
warnings:
|
warnings:
|
||||||
messages.length > 0
|
messages.length > 0
|
||||||
|
|
|
||||||
|
|
@ -142,6 +142,41 @@ describe('TeamBootstrapStateReader', () => {
|
||||||
nowSpy.mockRestore();
|
nowSpy.mockRestore();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('surfaces unreadable bootstrap journal as a warning without breaking active recovery', async () => {
|
||||||
|
const killSpy = vi.spyOn(process, 'kill').mockImplementation(() => true as never);
|
||||||
|
|
||||||
|
hoisted.files.set('/mock/teams/demo/bootstrap-state.json', {
|
||||||
|
contents: JSON.stringify({
|
||||||
|
version: 1,
|
||||||
|
runId: 'run-123',
|
||||||
|
teamName: 'demo',
|
||||||
|
ownerPid: 4242,
|
||||||
|
startedAt: 1700000000000,
|
||||||
|
updatedAt: 1700000000500,
|
||||||
|
phase: 'spawning_members',
|
||||||
|
members: [{ name: 'alice', status: 'pending' }],
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
hoisted.files.set('/mock/teams/demo/bootstrap-journal.jsonl', {
|
||||||
|
contents: '{invalid-json',
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(readBootstrapRuntimeState('demo')).resolves.toMatchObject({
|
||||||
|
teamName: 'demo',
|
||||||
|
isAlive: false,
|
||||||
|
runId: 'run-123',
|
||||||
|
progress: {
|
||||||
|
state: 'assembling',
|
||||||
|
message: 'Spawning teammate runtimes (1)',
|
||||||
|
warnings: [
|
||||||
|
'Persisted deterministic bootstrap journal is unreadable because bootstrap-journal.jsonl is invalid, truncated, or inaccessible.',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
killSpy.mockRestore();
|
||||||
|
});
|
||||||
|
|
||||||
it('ignores terminal bootstrap-state for runtime recovery projection', async () => {
|
it('ignores terminal bootstrap-state for runtime recovery projection', async () => {
|
||||||
hoisted.files.set('/mock/teams/demo/bootstrap-state.json', {
|
hoisted.files.set('/mock/teams/demo/bootstrap-state.json', {
|
||||||
contents: JSON.stringify({
|
contents: JSON.stringify({
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue