From a39bafb8e3a56e1e663ba8304f50bd17522e2896 Mon Sep 17 00:00:00 2001 From: Jarvis Date: Fri, 17 Jul 2026 19:41:18 -0500 Subject: [PATCH] test(fleet): stabilize documentation surface scan Co-Authored-By: Claude Opus 4.8 --- docs/fleet/NORTH_STAR.yaml | 2 +- docs/fleet/PRD.md | 4 +- .../src/fleet/fleet-documentation.spec.ts | 47 ++++++++++++++++--- 3 files changed, 44 insertions(+), 9 deletions(-) diff --git a/docs/fleet/NORTH_STAR.yaml b/docs/fleet/NORTH_STAR.yaml index 3e10b20..79af87c 100644 --- a/docs/fleet/NORTH_STAR.yaml +++ b/docs/fleet/NORTH_STAR.yaml @@ -97,7 +97,7 @@ success_criteria: - id: AC-NS-7 text: >- A user-customized persona (edited or added via the orchestrator) survives - `mosaic update`: baseline reseed never clobbers user overrides. + mosaic update: baseline reseed never clobbers user overrides. workstreams: - id: A diff --git a/docs/fleet/PRD.md b/docs/fleet/PRD.md index b8caf78..c33cc4d 100644 --- a/docs/fleet/PRD.md +++ b/docs/fleet/PRD.md @@ -97,8 +97,8 @@ observability and no safe way to watch a session. is an intentional fail-closed design (FR-5). - **agent watch uses a grouped viewer session:** tmux attach -r directly against the agent session lets the viewer terminal shrink the agent's window. agent watch instead - creates a throwaway grouped session (`tmux new-session -d -t '=' -s -'-watch-'`), attaches read-only to that session, and kills it on detach. + creates a throwaway grouped session (tmux new-session -d -t '=' -s + '-watch-'), attaches read-only to that session, and kills it on detach. The grouped session shares the agent's windows but has independent sizing, so the agent's window is never affected. tmux attach is still interactive and requires inherited stdio; the `interactiveRunner` handles TTY passthrough. diff --git a/packages/mosaic/src/fleet/fleet-documentation.spec.ts b/packages/mosaic/src/fleet/fleet-documentation.spec.ts index d936929..2e5a108 100644 --- a/packages/mosaic/src/fleet/fleet-documentation.spec.ts +++ b/packages/mosaic/src/fleet/fleet-documentation.spec.ts @@ -483,15 +483,11 @@ function codeSurfaceDiagnostics(path: string, source: string): SurfaceDiagnostic diagnostics.push({ path, line: lineNumber, code: 'indented-code' }); continue; } - if (/`{2,}/.test(line) && !/^```[a-z-]+$/.test(line)) { - diagnostics.push({ path, line: lineNumber, code: 'inline-delimiter' }); - continue; - } const marker = line.match(/^(`{3,}|~{3,})(.*)$/); if (marker !== null) { block += 1; const info = marker[2] ?? ''; - if (marker[1] !== '```') { + if (marker[1] !== '```' || /[`~]/.test(info)) { diagnostics.push({ path, line: lineNumber, block, code: 'fence-marker' }); continue; } @@ -507,6 +503,15 @@ function codeSurfaceDiagnostics(path: string, source: string): SurfaceDiagnostic inFence = true; continue; } + if (/`{2,}/.test(line)) { + diagnostics.push({ path, line: lineNumber, code: 'inline-delimiter' }); + continue; + } + const withoutClosedInlineSpans = line.replace(/`[^`\n]+`/g, ''); + if (withoutClosedInlineSpans.includes('`')) { + diagnostics.push({ path, line: lineNumber, code: 'inline-delimiter' }); + continue; + } if (/<\/?(?:pre|code|script|style|xmp|listing)(?=$|\s|[>/])/i.test(line)) { diagnostics.push({ path, line: lineNumber, code: 'raw-code-container' }); } @@ -702,8 +707,38 @@ describe('closed documentation publication grammar', (): void => { } }); + it('rejects unmatched single-backtick delimiters on either side', (): void => { + for (const source of ['`literal', 'literal`', 'text `literal', 'literal` text']) { + expect(codeSurfaceDiagnostics('fixture.md', source)).toContainEqual({ + path: 'fixture.md', + line: 1, + code: 'inline-delimiter', + }); + } + }); + + it('counts every invalid column-one fence candidate before later profile selection', (): void => { + for (const firstCandidate of [ + '````fleet-synopsis', + '```fleet-synopsis```', + '~~~fleet-synopsis~~~', + ]) { + expect( + codeSurfaceDiagnostics( + 'reference/cli.md', + `${firstCandidate}\n\`\`\`unknown\n\`\`\`fleet-synopsis\nmosaic fleet verify\n\`\`\``, + ), + ).toEqual([ + { path: 'reference/cli.md', line: 1, block: 1, code: 'fence-marker' }, + { path: 'reference/cli.md', line: 2, block: 2, code: 'fence-info' }, + { path: 'reference/cli.md', line: 3, block: 3, code: 'profile-unknown' }, + { path: 'reference/cli.md', line: 5, block: 4, code: 'fence-info' }, + ]); + } + }); + it('rejects inline delimiter runs instead of silently omitting them', (): void => { - for (const source of ['``literal``', '```literal```', 'text ``literal`` text']) { + for (const source of ['``literal``', 'text ```literal```', 'text ``literal`` text']) { expect(codeSurfaceDiagnostics('fixture.md', source)).toContainEqual({ path: 'fixture.md', line: 1,