docs(fleet): add operator configuration guide
All checks were successful
ci/woodpecker/pr/ci Pipeline was successful

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Jarvis
2026-07-16 09:27:43 -05:00
parent 9745bc3f29
commit 0aee2c0981
25 changed files with 768 additions and 77 deletions

View File

@@ -0,0 +1,131 @@
import { readFile, readdir } from 'node:fs/promises';
import { dirname, extname, join, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { describe, expect, it } from 'vitest';
import { parseRosterV2, validateRosterV2Semantics } from './roster-v2.js';
const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..', '..');
const repositoryRoot = resolve(packageRoot, '..', '..');
const fleetDocs = join(repositoryRoot, 'docs', 'fleet');
const frameworkFleet = join(packageRoot, 'framework', 'fleet');
const REQUIRED_FLEET_PAGES = [
'README.md',
'concepts/desired-vs-observed-state.md',
'concepts/identity-class-runtime.md',
'concepts/role-authority-and-leases.md',
'concepts/generated-env-launch-chain.md',
'reference/roster-v2.schema.json',
'reference/roster-v2-fields.md',
'reference/cli.md',
'reference/role-classes.md',
'reference/lifecycle-transitions.md',
'reference/status-and-drift.md',
'how-to/create-update-delete-agent.md',
'how-to/start-stop-restart.md',
'how-to/configure-tess-interaction.md',
'how-to/configure-ultron-validator.md',
'how-to/customize-roles.md',
'operations/reconcile-and-recover.md',
'operations/env-quarantine.md',
'operations/systemd-tmux-troubleshooting.md',
'operations/backup-restore.md',
'operations/upgrade-assets.md',
'migration/v1-to-v2.md',
'migration/example-profile-disposition.md',
'migration/legacy-class-aliases.md',
] as const;
async function markdownFiles(root: string): Promise<string[]> {
const entries = await readdir(root, { withFileTypes: true });
const paths = await Promise.all(
entries.map(async (entry): Promise<string[]> => {
const path = join(root, entry.name);
if (entry.isDirectory()) return markdownFiles(path);
return extname(entry.name) === '.md' ? [path] : [];
}),
);
return paths.flat().sort();
}
function localMarkdownTargets(source: string): string[] {
return [...source.matchAll(/\[[^\]]*\]\(([^)]+)\)/g)]
.map((match): string => match[1] ?? '')
.filter(
(target): boolean =>
target !== '' &&
!target.startsWith('#') &&
!target.startsWith('http://') &&
!target.startsWith('https://') &&
!target.startsWith('mailto:'),
)
.map((target): string => decodeURIComponent(target.split('#', 1)[0] ?? ''));
}
function fencedCodeBlocks(source: string): string[] {
return [...source.matchAll(/```[^\n]*\n(.*?)```/gs)].map((match): string => match[1] ?? '');
}
describe('fleet operator documentation', (): void => {
it('ships every accepted information-architecture page', async (): Promise<void> => {
await expect(
Promise.all(REQUIRED_FLEET_PAGES.map((path) => readFile(join(fleetDocs, path), 'utf8'))),
).resolves.toHaveLength(REQUIRED_FLEET_PAGES.length);
});
it('resolves every local Markdown link in the fleet book and sitemap', async (): Promise<void> => {
const files = [...(await markdownFiles(fleetDocs)), join(repositoryRoot, 'docs', 'SITEMAP.md')];
const missing: string[] = [];
for (const file of files) {
const source = await readFile(file, 'utf8');
for (const target of localMarkdownTargets(source)) {
try {
await readFile(resolve(dirname(file), target), 'utf8');
} catch {
missing.push(`${file.slice(repositoryRoot.length + 1)} -> ${target}`);
}
}
}
expect(missing).toEqual([]);
});
it('validates the canonical documentation example through the production compiler and resolver', async (): Promise<void> => {
const source = await readFile(join(fleetDocs, 'examples', 'roster-v2.yaml'), 'utf8');
const roster = parseRosterV2(source, 'yaml');
const validated = await validateRosterV2Semantics(roster, {
rolesDir: join(frameworkFleet, 'roles'),
overrideDir: join(fleetDocs, 'examples', 'roles.local'),
});
expect(validated.generation).toBe(1);
expect(validated.agents.map((agent) => agent.canonicalClass)).toEqual([
'code',
'interaction',
'validator',
]);
});
it('keeps every fenced fleet example free of sensitive values, arbitrary commands, and product-hardcoded identities', async (): Promise<void> => {
const violations: string[] = [];
for (const file of await markdownFiles(fleetDocs)) {
const source = await readFile(file, 'utf8');
for (const [index, block] of fencedCodeBlocks(source).entries()) {
if (/(?:secret|token|password|credential|MOSAIC_AGENT_COMMAND)/i.test(block)) {
violations.push(`${file.slice(repositoryRoot.length + 1)}#block-${index + 1}: sensitive`);
}
if (/\b(?:Tess|Ultron)\b/.test(block)) {
violations.push(`${file.slice(repositoryRoot.length + 1)}#block-${index + 1}: identity`);
}
}
}
const rosterSource = await readFile(join(fleetDocs, 'examples', 'roster-v2.yaml'), 'utf8');
if (/(?:secret|token|password|credential|MOSAIC_AGENT_COMMAND)/i.test(rosterSource)) {
violations.push('docs/fleet/examples/roster-v2.yaml: sensitive');
}
if (/\b(?:Tess|Ultron)\b/.test(rosterSource)) {
violations.push('docs/fleet/examples/roster-v2.yaml: identity');
}
expect(violations).toEqual([]);
});
});