diff --git a/.github/scripts/architecture/README.md b/.github/scripts/architecture/README.md index 3eb2916309f..39c172f314b 100644 --- a/.github/scripts/architecture/README.md +++ b/.github/scripts/architecture/README.md @@ -93,3 +93,50 @@ without git history; tests pin each ceiling to its current count. Removing debt the ceiling, while any increase requires an explicit reviewed ceiling change. Missing budget files fail in every checkout. Unit mutations prove math, sorting, root calls, and budget growth fail; tests and comments are excluded through the TypeScript syntax tree. + +## Pending site layout + +The owner-decided plan is in docs/site-architecture.md. Moves, sibling tiers, explicit denies, +atomic edit groups, the decided loader design and a compact path/class inventory live beside it. +`site-architecture.mts` shares the ordinary check's live graph. `status: "planned"` findings always warn with a fix command and Actions annotation; there is no deadline or warning ceiling. +`status: "enforced"` makes every plan finding fail; the owner flips it when S4 ends. Strict `--accept` always fails on plan findings. Existing rules and scanner failures still fail. +The architecture baseline is unchanged. + +Tests are leaf consumers (`tests: "any-tier"`) but remain in file SCCs. Production +cannot import tests, fixtures or helpers. New helpers use `.test-support.mts`. +Only env.d.ts remains at the source root; generated declarations sit beside their +ignored modules in prepared. Lazy and type imports count. Lateral edges are forbidden. + +The edits envelope contains `edits` with stable ids and `changes` that partition +those ids in S3 order. New modules declare imports. Minimality removes each atomic +change group; a removal must fail. Sequence replays S3 at current paths and S4 with +unmoved files in synthetic legacy. The doc explains the loader proof limitation. + +Acceptance reads declarations from tracked files only, so ignored generated files never change the result. From a clean +checkout with installed dependencies: + +```sh +export PATH=$HOME/.nvm/versions/node/v22.23.2/bin:$PATH +pnpm install --frozen-lockfile +node .github/scripts/architecture/import-declarations.mts --prefix site/ --out output/plan7/imports.json --check-against-scanner +node .github/scripts/architecture/site-architecture.mts --write +node .github/scripts/architecture/site-architecture.mts --accept +node .github/scripts/architecture/site-architecture.mts --identity +node .github/scripts/architecture/site-architecture.mts --sequence +node .github/scripts/architecture/site-architecture.mts --minimality +pnpm check:architecture +``` + +Each move PR deletes applied map entries and updates asset/test identities, +remaining edits, tables, references and wiring. It runs +`node .github/scripts/architecture/site-architecture.mts --references --old ` +after moves; zero live occurrences are required. Historical and plan pointers are +reported separately. S3 PRs remove applied groups. L6 removes null deletion entries. +`--references` prints full line-numbered evidence on demand; do not commit that output. `--write` regenerates all marked tables +and references. Ordinary additions while planned do not turn plan warnings into +application refactors. Strict acceptance, sequence and minimality remain blocking +proof commands. See the plan for the contributor workflow and S3 behavior gates. + +When a warning names your file, update its mapping or semantic edits, run `--write`, +then `--accept`. Keep application behavior unchanged; a planned warning never blocks +`pnpm check:architecture`. diff --git a/.github/scripts/architecture/graph.mts b/.github/scripts/architecture/graph.mts index da81932196d..d13b2266484 100644 --- a/.github/scripts/architecture/graph.mts +++ b/.github/scripts/architecture/graph.mts @@ -28,7 +28,7 @@ export interface ImportEdge { readonly symbols: readonly string[]; readonly typeOnly: boolean; } -export interface ImportGraph { readonly files: ReadonlyMap; readonly edges: readonly ImportEdge[] } +export interface ImportGraph { readonly files: ReadonlyMap; readonly edges: readonly ImportEdge[]; readonly resolution?: { readonly tsconfigPath: string; readonly baseUrl: string | undefined; readonly modules: readonly CruisedModule[] } } /** One module of dependency-cruiser's JSON result, validated. */ interface CruisedDependency { readonly module: string; readonly resolved: string; readonly coreModule: boolean; readonly couldNotResolve: boolean } @@ -235,5 +235,5 @@ export async function buildImportGraph(checkout: string, options: BuildOptions): + unresolved.slice(0, 5).map(item => ` ${item.from}: ${item.specifier} (${item.reason})`).join('\n') + '\nFix the package exports or its tsup entries, or teach .github/scripts/architecture/workspaces.mts the new form.'); } - return { files, edges: edges.filter(edge => files.has(edge.to)) }; + return { files, edges: edges.filter(edge => files.has(edge.to)), resolution: { tsconfigPath, baseUrl, modules } }; } diff --git a/.github/scripts/architecture/import-declarations.mts b/.github/scripts/architecture/import-declarations.mts new file mode 100644 index 00000000000..1ad02c3bed1 --- /dev/null +++ b/.github/scripts/architecture/import-declarations.mts @@ -0,0 +1,177 @@ +/** Declaration inventory layered on the production cruise and resolver; no independent resolution policy. + * Computed imports remain unresolved. Astro CSS and new URL assets are recorded although the scanner + * cannot see them; markup component references use their frontmatter imports, as in the scanner. */ +import { execFileSync, spawnSync } from 'node:child_process'; +import { mkdirSync, readFileSync, realpathSync, writeFileSync } from 'node:fs'; +import { dirname, extname, posix, resolve } from 'node:path'; +import { isBuiltin } from 'node:module'; +import { pathToFileURL } from 'node:url'; +import ts from 'typescript'; +import { astroScriptBlocks } from './astro-imports.mts'; +import { buildImportGraph, importTarget, readWorkspaces, trackedStandIn, type ImportGraph } from './graph.mts'; +import { createResolver } from './resolver.mts'; +import { byText, isTestPath, zoneOf } from './zones.mts'; + +export const KINDS = ['value', 'type', 'lazy', 'asset'] as const; +export type Kind = typeof KINDS[number]; +export interface Declaration { + readonly from: string; readonly line: number; readonly specifier: string; readonly to: string | null; + readonly kind: Kind; readonly symbols: readonly string[]; readonly form: string; + readonly sideEffectOnly: boolean; readonly test: boolean; readonly unresolved?: boolean; +} +interface Parsed extends Omit { readonly computed?: boolean } +export const codeTarget = (path: string): boolean => /\.(?:[cm]?[jt]s|tsx|jsx|astro)(?:[?#].*)?$/u.test(path); +const lineAt = (source: string, offset: number): number => source.slice(0, offset).split('\n').length; + +/** Parse declarations without resolving them; offsets in Astro always refer to the original source. */ +export function parseDeclarations(source: string, file: string): Parsed[] { + const found: Parsed[] = []; + const parse = (text: string, offset: number): void => { + const tree = ts.createSourceFile(file, text, ts.ScriptTarget.Latest, true, file.endsWith('.tsx') ? ts.ScriptKind.TSX : ts.ScriptKind.TS); + const add = (node: ts.Node, specifier: string, kind: Kind, symbols: string[], form: string, sideEffectOnly = false, computed = false): void => { + found.push({ line: lineAt(source, offset + node.getStart(tree)), specifier, kind, symbols, form, sideEffectOnly, ...(computed ? { computed: true } : {}) }); + }; + const visit = (node: ts.Node): void => { + if ((ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) && node.moduleSpecifier && ts.isStringLiteralLike(node.moduleSpecifier)) { + const clause = ts.isImportDeclaration(node) ? node.importClause : undefined; + const bindings = clause?.namedBindings ?? (ts.isExportDeclaration(node) ? node.exportClause : undefined); + const symbols: string[] = clause?.name ? ['default'] : []; + if (bindings && (ts.isNamespaceImport(bindings) || ts.isNamespaceExport(bindings))) symbols.push('*'); + else if (bindings) for (const element of bindings.elements) symbols.push((element.propertyName ?? element.name).text); + if (ts.isExportDeclaration(node) && !node.exportClause) symbols.push('*'); + const inlineType = bindings && (ts.isNamedImports(bindings) || ts.isNamedExports(bindings)) && bindings.elements.length > 0 + && bindings.elements.every(element => element.isTypeOnly) && !clause?.name; + const type = clause?.isTypeOnly || (ts.isExportDeclaration(node) && node.isTypeOnly) || inlineType; + const side = ts.isImportDeclaration(node) && !clause; + add(node, node.moduleSpecifier.text, type ? 'type' : 'value', symbols, + side ? 'side-effect' : ts.isImportDeclaration(node) ? 'import' : node.exportClause ? 'export-from' : 'export-star', side); + } else if (ts.isImportTypeNode(node) && ts.isLiteralTypeNode(node.argument) && ts.isStringLiteralLike(node.argument.literal)) { + add(node, node.argument.literal.text, 'type', [node.qualifier?.getText(tree).split('.')[0] ?? '*'], 'import-type-node'); + } else if (ts.isImportEqualsDeclaration(node) && ts.isExternalModuleReference(node.moduleReference) + && node.moduleReference.expression && ts.isStringLiteralLike(node.moduleReference.expression)) { + add(node, node.moduleReference.expression.text, node.isTypeOnly ? 'type' : 'value', ['*'], 'import'); + } else if (ts.isCallExpression(node) && node.expression.kind === ts.SyntaxKind.ImportKeyword && node.arguments[0]) { + const arg = node.arguments[0]; + add(node, ts.isStringLiteralLike(arg) ? arg.text : arg.getText(tree), 'lazy', ['(dynamic)'], 'dynamic-import', false, !ts.isStringLiteralLike(arg)); + } else if (ts.isNewExpression(node) && node.expression.getText(tree) === 'URL' && node.arguments?.length === 2 + && node.arguments[1]?.getText(tree) === 'import.meta.url' && node.arguments[0] && ts.isStringLiteralLike(node.arguments[0])) { + add(node, node.arguments[0].text, 'value', [], 'new URL(.., import.meta.url)'); + } + ts.forEachChild(node, visit); + }; + visit(tree); + }; + if (file.endsWith('.css')) { + const uncommented = source.replace(/\/\*[\s\S]*?\*\//gu, comment => comment.replace(/[^\r\n]/gu, ' ')); + for (const match of uncommented.matchAll(/@import\s+(?:url\(\s*(?:["']([^"']+)["']|([^\s)]+))\s*\)|["']([^"']+)["'])/gu)) + found.push({ line: lineAt(source, match.index ?? 0), specifier: match[1] ?? match[2] ?? match[3] ?? '', kind: 'asset', symbols: [], form: 'side-effect', sideEffectOnly: true }); + } else if (!file.endsWith('.astro')) parse(source, 0); + else { + // Reuse the scanner's eligibility and extraction, then recover each block's real source offset. + const frontmatter = /^\s*---\r?\n([\s\S]*?)\r?\n---/u.exec(source); + if (frontmatter?.[1] !== undefined) parse(astroScriptBlocks(frontmatter[0])[0] ?? '', frontmatter[0].indexOf('\n', frontmatter[0].indexOf('---')) + 1); + for (const script of source.matchAll(/]*)>([\s\S]*?)<\/script>/gu)) { + const block = astroScriptBlocks(script[0])[0]; + if (block === undefined) continue; + const attributes = script[1] ?? ''; + const src = /\bsrc\s*=\s*["']([^"']+)["']/u.exec(attributes); + const offset = (script.index ?? 0) + (src ? script[0].indexOf(src[0]) : script[0].indexOf('>') + 1); + parse(block, offset); + } + for (const style of source.matchAll(/]*)>([\s\S]*?)<\/style>/gu)) { + const attributes = style[1] ?? '', body = style[2] ?? '', offset = (style.index ?? 0) + style[0].indexOf('>') + 1; + const src = /\bsrc\s*=\s*["']([^"']+)["']/u.exec(attributes); + if (src?.[1]) found.push({ line: lineAt(source, style.index ?? 0), specifier: src[1], kind: 'asset', symbols: [], form: 'side-effect', sideEffectOnly: true }); + const uncommented = body.replace(/\/\*[\s\S]*?\*\//gu, comment => comment.replace(/[^\r\n]/gu, ' ')); + for (const match of uncommented.matchAll(/@import\s+(?:url\(\s*(?:["']([^"']+)["']|([^\s)]+))\s*\)|["']([^"']+)["'])/gu)) { + found.push({ line: lineAt(source, offset + (match.index ?? 0)), specifier: match[1] ?? match[2] ?? match[3] ?? '', kind: 'asset', symbols: [], form: 'side-effect', sideEffectOnly: true }); + } + } + } + return found; +} + +export interface InventoryOptions { readonly prefix: string; readonly checkAgainstScanner?: boolean; readonly graph?: ImportGraph } +const pairKey = (from: string, to: string) => `${from}\n${to}`; +export async function readImportDeclarations(checkout: string, options: InventoryOptions) { + const root = realpathSync(checkout), files = execFileSync('git', ['ls-files', '-z'], { cwd: root, encoding: 'utf8', maxBuffer: 256 * 1024 * 1024 }).split('\0').filter(Boolean).sort(), tracked = new Set(files), workspaces = readWorkspaces(root, tracked); + const selected = files.filter(file => file.startsWith(options.prefix) && /\.(mts|ts|tsx|mjs|astro|css)$/u.test(file)); + const graph = options.graph ?? await buildImportGraph(root, { details: false }); + const resolveImport = createResolver(root, graph.resolution?.tsconfigPath ?? resolve(root, 'tsconfig.json'), graph.resolution?.baseUrl); + const dependencies = new Map(graph.resolution?.modules.map(module => [module.source, module.dependencies]) ?? []); + const parsedFiles = new Map(selected.map(from => [from, parseDeclarations(readFileSync(resolve(root, from), 'utf8'), from)])); + const relativePath = (from: string, specifier: string) => specifier.startsWith('.') ? posix.normalize(posix.join(posix.dirname(from), specifier.replace(/[?#].*$/u, ''))) : undefined; + const candidates = [...new Set([...parsedFiles].flatMap(([from, entries]) => entries.map(entry => relativePath(from, entry.specifier)).filter((path): path is string => path !== undefined)))]; + const ignoredResult = spawnSync('git', ['check-ignore', '-z', '--stdin'], { cwd: root, input: candidates.join('\0'), encoding: 'utf8', maxBuffer: 64 * 1024 * 1024 }); + if (ignoredResult.error) throw ignoredResult.error; + if (ignoredResult.status !== 0 && ignoredResult.status !== 1) throw new Error(`git check-ignore failed: ${ignoredResult.stderr}`); + const ignored = new Set(ignoredResult.stdout.split('\0').filter(Boolean)); + const declarations: Declaration[] = []; + for (const [from, entries] of parsedFiles) for (const parsed of entries) { + const { computed, ...entry } = parsed; + const problems: { from: string; specifier: string; reason: string }[] = []; + const dependency = dependencies.get(from)?.find(item => item.module === parsed.specifier); + const resolved = dependency && !dependency.couldNotResolve ? dependency.resolved : resolveImport(from, parsed.specifier); + const relative = relativePath(from, parsed.specifier); + // Resolve ignored generated inputs by their declared address, never by their presence on disk. + const declared = relative ? trackedStandIn(relative, tracked) ?? (ignored.has(relative) ? relative : undefined) : undefined; + let to = computed ? undefined : declared ?? importTarget(from, parsed.specifier, resolved, workspaces, tracked, problems); + const bare = !/^(\.|\/|#)/u.test(parsed.specifier); + if (!computed && to === undefined && !problems.length && (isBuiltin(parsed.specifier) || parsed.specifier.startsWith('astro:') || (bare && resolved !== undefined))) { + to = parsed.specifier.startsWith('@') ? parsed.specifier.split('/').slice(0, 2).join('/') : parsed.specifier.split('/')[0]; + if (isBuiltin(parsed.specifier) || parsed.specifier.startsWith('astro:')) to = parsed.specifier; + } + const target = to && tracked.has(to) ? to : resolved ?? parsed.specifier; + const kind = !computed && extname(target.replace(/[?#].*$/u, '')) && !codeTarget(target) ? 'asset' : entry.kind; + declarations.push({ ...entry, from, to: to ?? null, kind, test: isTestPath(from), ...(to === undefined ? { unresolved: true } : {}) }); + } + declarations.sort((a, b) => byText(a.from, b.from) || a.line - b.line || byText(a.specifier, b.specifier) || byText(a.form, b.form)); + const pairs = new Map; declarations: number }>(); + for (const entry of declarations) { + if (entry.to === null) continue; + const key = pairKey(entry.from, entry.to), pair = pairs.get(key) ?? { from: entry.from, to: entry.to, kinds: { value: 0, type: 0, lazy: 0, asset: 0 }, declarations: 0 }; + pair.kinds[entry.kind]++; pair.declarations++; pairs.set(key, pair); + } + const folderPairs = new Map; filePairs: number; declarations: number }>(); + for (const pair of pairs.values()) { + const from = zoneOf(pair.from), to = tracked.has(pair.to) ? zoneOf(pair.to) : pair.to, key = pairKey(from, to); + const entry = folderPairs.get(key) ?? { from, to, kinds: { value: 0, type: 0, lazy: 0, asset: 0 }, filePairs: 0, declarations: 0 }; + for (const kind of KINDS) if (pair.kinds[kind]) entry.kinds[kind]++; + entry.filePairs++; entry.declarations += pair.declarations; folderPairs.set(key, entry); + } + const scanner = { checked: false, missing: [] as string[], extra: [] as string[], exceptions: [] as { pair: string; reason: string }[], unexplained: 0 }; + if (options.checkAgainstScanner) { + const expected = new Set(graph.edges.filter(edge => selected.includes(edge.from) && tracked.has(edge.to)).map(edge => pairKey(edge.from, edge.to))); + const actual = new Set([...pairs.values()].filter(pair => tracked.has(pair.to)).map(pair => pairKey(pair.from, pair.to))); + scanner.checked = true; + scanner.missing = [...expected].filter(key => !actual.has(key)).sort(byText); + for (const key of [...actual].filter(key => !expected.has(key)).sort(byText)) { + const hits = declarations.filter(entry => entry.to !== null && pairKey(entry.from, entry.to) === key); + if (hits.every(entry => entry.form === 'new URL(.., import.meta.url)' || ((entry.from.endsWith('.astro') || entry.from.endsWith('.css')) && entry.kind === 'asset'))) + scanner.exceptions.push({ pair: key, reason: 'Production scanner does not read new URL or stylesheet imports' }); + else scanner.extra.push(key); + } + scanner.unexplained = scanner.missing.length + scanner.extra.length; + } + return { base: execFileSync('git', ['rev-parse', 'HEAD'], { cwd: root, encoding: 'utf8' }).trim(), declarations, + pairs: [...pairs.values()].sort((a, b) => byText(a.from, b.from) || byText(a.to, b.to)), + summary: { files: selected, declarations: declarations.length, filePairs: pairs.size, unresolved: declarations.filter(entry => entry.unresolved).length, + folderPairs: [...folderPairs.values()].sort((a, b) => byText(a.from, b.from) || byText(a.to, b.to)), scanner } }; +} + +if (process.argv[1] && import.meta.url === pathToFileURL(resolve(process.argv[1])).href) { + try { + const args = process.argv.slice(2); let prefix = 'site/', out: string | undefined, checkAgainstScanner = false; + for (let i = 0; i < args.length; i++) { + if (args[i] === '--prefix' && args[i + 1]) prefix = args[++i]!; + else if (args[i] === '--out' && args[i + 1]) out = args[++i]!; + else if (args[i] === '--check-against-scanner') checkAgainstScanner = true; + else throw new Error(`Unknown or incomplete argument: ${args[i]}`); + } + const result = await readImportDeclarations(process.cwd(), { prefix, checkAgainstScanner }); + const json = `${JSON.stringify(result, null, 2)}\n`; + if (out) { mkdirSync(dirname(out), { recursive: true }); writeFileSync(out, json); } else console.log(json); + console.error(`${result.summary.declarations} declarations; ${result.summary.filePairs} file pairs; ${result.summary.scanner.unexplained} unexplained scanner mismatches`); + if (result.summary.scanner.unexplained) process.exitCode = 1; + } catch (error) { console.error(error); process.exitCode = 1; } +} diff --git a/.github/scripts/architecture/import-declarations.test.mts b/.github/scripts/architecture/import-declarations.test.mts new file mode 100644 index 00000000000..aa090900bd3 --- /dev/null +++ b/.github/scripts/architecture/import-declarations.test.mts @@ -0,0 +1,74 @@ +/** Declaration syntax, real Astro source lines and production-resolution parity in a tiny repository. */ +import assert from 'node:assert/strict'; +import { execFileSync } from 'node:child_process'; +import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import { dirname, join } from 'node:path'; +import { test } from 'node:test'; +import { parseDeclarations, readImportDeclarations } from './import-declarations.mts'; + +test('each syntax keeps declaration kind, imported symbols and side-effect status', () => { + const parsed = parseDeclarations([ + "import Default, { type A, B as Local } from './a.mts';", "import { type A, type B } from './a.mts';", + "import type * as Types from './a.mts';", "export type { A } from './a.mts';", "export * from './a.mts';", + "export { B } from './a.mts';", "import './style.css';", "const lazy = import(`./a.mts`);", + "type X = typeof import('./a.mts');", "type Y = import('./a.mts').A;", "new URL('./pic.png', import.meta.url);", + 'import(name);', "import type Q = require('./a.mts');", + ].join('\n'), 'site/a.test.mts'); + assert.deepEqual(parsed.map(entry => entry.kind), ['value', 'type', 'type', 'type', 'value', 'value', 'value', 'lazy', 'type', 'type', 'value', 'lazy', 'type']); + assert.deepEqual(parsed[0]?.symbols, ['default', 'A', 'B']); + assert.equal(parsed[6]?.sideEffectOnly, true); + assert.equal(parsed[11]?.computed, true); + assert.deepEqual(parsed.map(entry => entry.line), Array.from({ length: 13 }, (_, i) => i + 1)); + assert.deepEqual(parsed.map(entry => entry.form), ['import', 'import', 'import', 'export-from', 'export-star', 'export-from', 'side-effect', 'dynamic-import', 'import-type-node', 'import-type-node', 'new URL(.., import.meta.url)', 'dynamic-import', 'import']); +}); + +test('Astro blocks, src and CSS have original lines; inline scripts match production exclusions', () => { + const parsed = parseDeclarations([ + '---', "import A from './A.astro';", '---', '', '', + '', '', '', + ].join('\n'), 'site/Page.astro'); + assert.deepEqual(parsed.map(entry => [entry.specifier, entry.line]), [['./A.astro', 2], ['./x.mts', 6], ['./entry.mts', 8], ['./style.css', 11]]); +}); + +test('Astro leading whitespace and CSS URL imports retain lines and ignore comments', () => { + const parsed = parseDeclarations('\n\n---\nimport "./a.mts";\n---\n', 'site/Page.astro'); + assert.deepEqual(parsed.map(entry => [entry.specifier, entry.line]), [['./a.mts', 4], ['./outer.css', 6], ['./inner.css', 8]]); +}); + +test('production resolution retains duplicates, assets, tests, generated stand-ins and zero scanner mismatches', async () => { + const root = mkdtempSync(join(tmpdir(), 'import-declarations-')); + const files = { + 'package.json': '{"name":"fixture","type":"module"}', + 'tsconfig.json': '{"compilerOptions":{"module":"ESNext","moduleResolution":"Bundler","noEmit":true}}', + '.gitignore': 'site/generated.mts\nnode_modules/\n', + 'node_modules/tiny/package.json': '{"name":"tiny"}', 'node_modules/tiny/style.css': 'body {}', + 'site/a.mts': "import { x } from './b.mts';\nimport type { X } from './b.mts';\nimport './data.json';\nimport './generated.mts';\nimport 'node:fs';\nimport './missing.mts';\nimport('node:os');\nimport 'tiny/style.css';\n", + 'site/b.mts': 'export const x = 1; export interface X {}', + 'site/generated.d.mts': 'export {};', 'site/generated.mts': 'export {};', 'site/data.json': '{}', + 'site/a.test.mts': "import './b.mts';", 'site/Empty.astro': '

', + }; + try { + // A repository of its own with one empty commit: a shared clone of the checkout fails on CI's partial clones. + execFileSync('git', ['init', '--quiet'], { cwd: root }); + execFileSync('git', ['-c', 'user.name=fixture', '-c', 'user.email=fixture@example.invalid', '-c', 'commit.gpgsign=false', 'commit', '--quiet', '--allow-empty', '-m', 'fixture'], { cwd: root }); + for (const [file, source] of Object.entries(files)) { mkdirSync(dirname(join(root, file)), { recursive: true }); writeFileSync(join(root, file), source); } + execFileSync('git', ['add', '.'], { cwd: root }); + const result = await readImportDeclarations(root, { prefix: 'site/', checkAgainstScanner: true }); + assert.equal(result.summary.scanner.unexplained, 0); + assert.equal(result.declarations.find(entry => entry.specifier === './generated.mts')?.to, 'site/generated.d.mts'); + assert.equal(result.declarations.find(entry => entry.specifier === './data.json')?.kind, 'asset'); + assert.equal(result.declarations.find(entry => entry.specifier === './missing.mts')?.unresolved, true); + assert.equal(result.declarations.find(entry => entry.from.endsWith('.test.mts'))?.test, true); + assert.equal(result.pairs.find(pair => pair.from === 'site/a.mts' && pair.to === 'site/b.mts')?.declarations, 2); + assert.equal(result.declarations.find(entry => entry.specifier === 'tiny/style.css')?.to, 'tiny'); + assert.equal(result.declarations.find(entry => entry.specifier === 'tiny/style.css')?.kind, 'asset'); + assert.equal(result.summary.declarations, 9); + assert.ok(result.summary.files.includes('site/Empty.astro')); + } finally { rmSync(root, { recursive: true, force: true }); } +}); + +test('stylesheet imports retain asset kind and source line without reading commented imports', () => { + const imports = parseDeclarations('/* @import "ignored.css"; */\n@import "./marker.css";\n@import url(./panel.css);', 'site/site.css'); + assert.deepEqual(imports.map(entry => [entry.specifier, entry.kind, entry.line]), [['./marker.css', 'asset', 2], ['./panel.css', 'asset', 3]]); +}); diff --git a/.github/scripts/architecture/plan-proofs.mts b/.github/scripts/architecture/plan-proofs.mts new file mode 100644 index 00000000000..03feaa2e9d2 --- /dev/null +++ b/.github/scripts/architecture/plan-proofs.mts @@ -0,0 +1,68 @@ +/** Replay ordered semantic changes and folder moves, and reject unnecessary semantic groups. */ +import { project, editList, type ProjectionInput } from './projection.mts'; +import { isTestPath } from './zones.mts'; +function object(value: unknown): Record { + if (!value || typeof value !== 'object' || Array.isArray(value)) throw new TypeError('Expected object'); + return Object.fromEntries(Object.entries(value)); +} +function list(value: unknown): unknown[] { if (!Array.isArray(value)) throw new TypeError('Expected array'); return value; } +function text(value: unknown): string { if (typeof value !== 'string') throw new TypeError('Expected string'); return value; } +function changes(input: ProjectionInput) { + const data = object(input.edits); editList(data); + return list(data.changes).map(value => { const entry = object(value); return { id: text(entry.id), ids: list(entry.edits).map(text) }; }); +} +/** Removal is at the PR group boundary: imports, new modules and their users are one atomic change. */ +export function minimality(input: ProjectionInput) { + if (project(input).failed) throw new Error('Minimality requires a passing full projection'); + const edits = editList(input.edits).map(object); + const results = changes(input).map(change => { + try { return { change: change.id, necessary: project({ ...input, edits: edits.filter(edit => !change.ids.includes(text(edit.id))) }).failed }; } + catch { return { change: change.id, necessary: true }; } // A missing dependency is a projection failure too. + }); + return { failed: results.some(result => !result.necessary), results }; +} +export interface SequenceStep { phase: string; step: string; fileSccs: number; folderSccs: number; upward: number; lateral: number; failed: boolean } +/** S3 keeps old file locations; S4 holds every remaining file in one temporary top-tier owner. */ +export function sequence(input: ProjectionInput): { failed: boolean; steps: SequenceStep[] } { + const tiers = object(input.tiers), moves = object(input.moves), inventory = object(input.declarations); + const originals = [...new Set([...list(object(inventory.summary).files).map(text), ...list(tiers.assets ?? []).map(text), ...list(tiers.testFiles ?? []).map(text)])]; + const edits = editList(input.edits).map(object), added = new Set(edits.filter(edit => edit.op === 'add-file').map(edit => text(edit.path))); + const inverse = new Map(Object.entries(moves).filter((entry): entry is [string, string] => typeof entry[1] === 'string').map(([old, final]) => [final, old])); + const oldPath = (file: string) => added.has(file) ? `site/${file.slice(file.lastIndexOf('/') + 1)}` : inverse.get(file) ?? file; + const translate = (edit: Record) => Object.fromEntries(Object.entries(edit).map(([key, value]) => { + if (['from', 'to', 'newTo', 'path'].includes(key)) return [key, oldPath(text(value))]; + if (key === 'imports') return [key, list(value).map(value => { const imported = object(value); return { ...imported, to: oldPath(text(imported.to)) }; })]; + return [key, value]; + })); + const liveFolders = [...new Set(originals.filter(file => file.split('/').length > 2).map(file => file.slice(0, file.indexOf('/', 5))))]; + const finalTiers = list(tiers.tiers).map(object); + const s3Tiers = { ...tiers, forbid: [], lateral: [], tiers: [...new Set([...liveFolders, ...finalTiers.flatMap(tier => list(tier.folders).map(text))])].map(folder => ({ tier: 0, name: folder, folders: [folder] })), root: { allow: [...originals.filter(file => file.split('/').length === 2), ...[...added].map(oldPath)] } }; + const deleted = Object.fromEntries(Object.entries(moves).filter(([, value]) => value === null)); + const baseline = project({ declarations: input.declarations, moves: deleted, tiers: s3Tiers }); + const steps: SequenceStep[] = [], active: Record[] = []; + let previous = baseline; + const groups = changes(input); + for (const [index, change] of groups.entries()) { + active.push(...edits.filter(edit => change.ids.includes(text(edit.id))).map(translate)); + const result = project({ declarations: input.declarations, moves: deleted, tiers: s3Tiers, edits: active }), view = result.views.all!; + const failed = Object.entries(result.views).some(([name, current]) => current.fileSccs.length > previous.views[name]!.fileSccs.length + || current.folderSccs.some(cycle => !baseline.views[name]!.folderSccs.some(old => cycle.members.every(member => old.members.includes(member)))) + || (index === groups.length - 1 && current.fileSccs.length !== 0)); + steps.push({ phase: 'S3', step: change.id, fileSccs: view.fileSccs.length, folderSccs: view.folderSccs.length, upward: view.upward.length, lateral: view.lateral.length, failed }); + previous = result; + } + const final = project(input), finalFiles = [...new Set([...originals.map(file => moves[file] === undefined ? file : moves[file] === null ? '' : text(moves[file])), ...added])].filter(Boolean); + const tests = new Set(originals.filter(file => isTestPath(file) || list(tiers.testFiles ?? []).includes(file)).map(file => moves[file] === undefined ? file : moves[file] === null ? '' : text(moves[file])).filter(Boolean)); + const folderOrder = finalTiers.flatMap(tier => list(tier.folders).map(folder => ({ folder: text(folder), tier: Number(tier.tier) }))).sort((a, b) => a.tier - b.tier || a.folder.localeCompare(b.folder)); + const moved = new Set(), root = object(tiers.root), allow = list(root.allow).map(text); + for (const { folder } of folderOrder) { + moved.add(folder); + const locate = (file: string) => (allow.includes(file) && moved.size === folderOrder.length) || [...moved].some(folder => file.startsWith(`${folder}/`)) || !file.startsWith('site/') ? file : `site/legacy/${file.slice(5)}`; + const declarations = { declarations: final.edges.map(edge => ({ ...edge, from: locate(edge.from), to: locate(edge.to), test: tests.has(edge.from) })), summary: { files: finalFiles.map(locate) } }; + const result = project({ declarations, moves: {}, tiers: { ...tiers, assets: [], testFiles: [...tests].map(locate), + tiers: [...finalTiers, { tier: Math.max(...folderOrder.map(item => item.tier)) + 2, name: 'Temporary remaining files', folders: ['site/legacy'] }] } }); + const view = result.views.all!; + steps.push({ phase: 'S4', step: folder, fileSccs: view.fileSccs.length, folderSccs: view.folderSccs.length, upward: view.upward.length, lateral: view.lateral.length, failed: result.failed }); + } + return { failed: steps.some(step => step.failed), steps }; +} diff --git a/.github/scripts/architecture/plan-proofs.test.mts b/.github/scripts/architecture/plan-proofs.test.mts new file mode 100644 index 00000000000..c4842e1d13a --- /dev/null +++ b/.github/scripts/architecture/plan-proofs.test.mts @@ -0,0 +1,47 @@ +/** Mutations must invalidate ordered replay and group minimality, not only the final graph. */ +import assert from 'node:assert/strict'; +import { test } from 'node:test'; +import { sequence, minimality } from './plan-proofs.mts'; +import { readFileSync } from 'node:fs'; +import { trackedStandIn } from './graph.mts'; +const fixture = { + declarations: { declarations: [{ from: 'site/a.mts', to: 'site/b.mts', kind: 'value' }, { from: 'site/b.mts', to: 'site/a.mts', kind: 'type' }], summary: { files: ['site/a.mts', 'site/b.mts'] } }, + moves: { 'site/a.mts': 'site/high/a.mts', 'site/b.mts': 'site/low/b.mts' }, + tiers: { tests: 'any-tier', root: { allow: [] }, tiers: [{ tier: 0, name: 'Low', folders: ['site/low'] }, { tier: 1, name: 'High', folders: ['site/high'] }] }, + edits: { edits: [{ id: 'cut-cycle', op: 'remove-import', from: 'site/low/b.mts', to: 'site/high/a.mts', kind: 'type' }], changes: [{ id: 'cut', edits: ['cut-cycle'] }] }, +}; +test('old locations replay; S4 prefixes never import legacy from a moved lower folder', () => { + assert.equal(sequence(fixture).failed, false); + assert.equal(sequence(fixture).steps[0]!.fileSccs, 0); + const wrongOrder = { ...fixture, tiers: { ...fixture.tiers, tiers: [...fixture.tiers.tiers].reverse().map((tier, index) => ({ ...tier, tier: index })) } }; + assert.equal(sequence(wrongOrder).failed, true); +}); +test('new S3 cycles fail even if a later change cuts them', () => { + const edits = [ + { id: 'add-node', op: 'add-file', path: 'site/low/new.mts', imports: [] }, + { id: 'add-self-cycle', op: 'add-import', from: 'site/low/new.mts', to: 'site/low/new.mts', kind: 'value' }, + { id: 'remove-self-cycle', op: 'remove-import', from: 'site/low/new.mts', to: 'site/low/new.mts', kind: 'value' }, + ...fixture.edits.edits, + ]; + assert.equal(sequence({ ...fixture, edits: { edits, changes: [{ id: 'bad', edits: ['add-node', 'add-self-cycle'] }, { id: 'fix', edits: ['remove-self-cycle', 'cut-cycle'] }] } }).failed, true); +}); +test('a droppable group fails minimality; removing a required cut breaks acceptance', () => { + assert.equal(minimality(fixture).failed, false); + const redundant = { id: 'add-unused', op: 'add-file', path: 'site/low/unused.mts', imports: [] }; + assert.equal(minimality({ ...fixture, edits: { edits: [...fixture.edits.edits, redundant], changes: [...fixture.edits.changes, { id: 'unused', edits: ['add-unused'] }] } }).failed, true); +}); +test('generated module maps to its declaration in the same prepared folder', () => { + assert.equal(trackedStandIn('site/prepared/prepared-shell-icons.mjs', new Set(['site/prepared/prepared-shell-icons.d.mts'])), 'site/prepared/prepared-shell-icons.d.mts'); +}); +test('committed plan has seven ordered, necessary change groups', () => { + const data = JSON.parse(readFileSync('docs/site-architecture/edits.json', 'utf8')) as unknown; + assert.ok(data && typeof data === 'object' && 'changes' in data && Array.isArray(data.changes)); + assert.equal(data.changes.length, 7); +}); + +test('the last S3 prefix must eliminate pre-existing file cycles even without increasing them', () => { + const remaining = { ...fixture, edits: { edits: [{ id: 'noop', op: 'add-file', path: 'site/low/noop.mts', imports: [] }], changes: [{ id: 'noop', edits: ['noop'] }] } }; + const result = sequence(remaining); + assert.equal(result.steps.find(step => step.phase === 'S3')?.fileSccs, 1); + assert.equal(result.steps.find(step => step.phase === 'S3')?.failed, true); +}); diff --git a/.github/scripts/architecture/plan-references.mts b/.github/scripts/architecture/plan-references.mts new file mode 100644 index 00000000000..707959da126 --- /dev/null +++ b/.github/scripts/architecture/plan-references.mts @@ -0,0 +1,83 @@ +/** Inventory literal move references and depth-sensitive reads; dated records stay separate from live pointers. */ +import { spawnSync, execFileSync } from 'node:child_process'; +import { existsSync, readFileSync } from 'node:fs'; +import { dirname, posix, resolve } from 'node:path'; +import { byText, isTestPath } from './zones.mts'; +export interface Reference { old: string; file: string; line: number; form: string; classification: string; scope: 'live' | 'history' | 'plan'; text: string } +function classify(file: string, line: string): string { + if (file.endsWith('AGENTS.md') || file.endsWith('CLAUDE.md')) return 'AGENTS.md contract text'; + if (file.endsWith('.py')) return 'Python script'; + if (file.includes('ci-areas')) return 'CI routing'; + if (file.startsWith('.github/workflows/')) return 'workflow'; + if (/tsconfig|eslint|package\.json/u.test(file)) return 'tsconfig/eslint/package.json'; + if (/import\s*\([^'"`]/u.test(line)) return 'computed import'; + if (/\b(?:import|export)\b.*['"]/u.test(line)) return 'source import'; + if (file.endsWith('.md')) return /\]\(/u.test(line) ? 'docs link' : 'docs code-span'; + return 'generator/producer literal'; +} +/** A literal scan includes untracked draft files, unlike plain git grep; ignores binary evidence. */ +export function references(root: string, moves: Readonly>, only?: readonly string[]) { + const paths = [...new Set([...Object.keys(moves), ...(Object.keys(moves).some(path => path.startsWith('site/test/')) ? ['site/test/'] : [])])], occurrences: Reference[] = [], relativeReads: { file: string; destination: string; line: number; text: string }[] = []; + const patterns = [...new Set(paths.flatMap(old => { + if (old.endsWith('/')) return [old, './test/']; + const base = old.slice(old.lastIndexOf('/') + 1), without = old.replace(/\.(?:[cm]?[jt]s|astro|json|css)$/u, ''); + const stem = base.replace(/\.(?:[cm]?[jt]s|astro|json|css)$/u, ''); + return [old, without, old.slice(5), base, ...["'", '"', '`'].map(quote => `/${stem}${quote}`)]; + }))]; + const grep = only ? { status: 1, stdout: '', stderr: '', error: undefined } : spawnSync('git', ['grep', '-I', '-l', '-F', '-f', '-', '--'], { cwd: root, encoding: 'utf8', input: `${patterns.join('\n')}\n`, maxBuffer: 64 * 1024 * 1024 }); + if (grep.error) throw grep.error; + if (grep.status !== 0 && grep.status !== 1) throw new Error(`git grep failed: ${grep.stderr}`); + const untracked = only ? [] : execFileSync('git', ['ls-files', '--others', '--exclude-standard', '-z'], { cwd: root, encoding: 'utf8' }).split('\0').filter(Boolean); + const selected = only ?? [...new Set([...grep.stdout.split('\n').filter(Boolean), ...untracked, ...paths.filter(path => !path.endsWith('/') && isTestPath(path))])].sort(); + for (const file of selected) { + if (!existsSync(resolve(root, file))) continue; + if (file.startsWith('docs/site-architecture/') || /^\.github\/scripts\/architecture\/(?:site-architecture|projection|plan-proofs|plan-references|import-declarations)(?:\.test)?\.mts$/u.test(file)) continue; // The inventory cannot inventory its own serialization. + const bytes = readFileSync(resolve(root, file)); if (bytes.includes(0)) continue; + const content = bytes.toString('utf8'); + const candidates = paths.filter(old => { + if (old.endsWith('/')) return content.includes(old) || content.includes(old.slice(5)); + const base = old.slice(old.lastIndexOf('/') + 1), stem = base.replace(/\.(?:[cm]?[jt]s|astro|json|css)$/u, ''); + return content.includes(old) || content.includes(old.replace(/\.(?:[cm]?[jt]s|astro|json|css)$/u, '')) || content.includes(old.slice(5)) || content.includes(base) + || ["'", '"', '`'].some(quote => content.includes(`/${stem}${quote}`)); + }); + if (!candidates.length && !(isTestPath(file) && typeof moves[file] === 'string')) continue; + const lines = content.split('\n'); + const scope = file.startsWith('docs/site-architecture') || /^\.github\/scripts\/architecture\/(?:site-architecture|projection|plan-proofs|plan-references|import-declarations)(?:\.test)?\.mts$/u.test(file) ? 'plan' + : /(?:^untangle\/|\/history\/|\/archive\/|\/done\/|(?:^|\/)(?:reports|records)\/)/u.test(file) ? 'history' : 'live'; + let generated = false; + for (const [index, line] of lines.entries()) { + if (file === 'docs/site-architecture.md' && line.startsWith('`, end = ``; + if (result.split(start).length !== 2 || result.split(end).length !== 2) throw new Error(`Missing or duplicate ${name} markers`); + const first = result.indexOf(start) + start.length, last = result.indexOf(end); + if (last < first) throw new Error(`Reversed ${name} markers`); + result = `${result.slice(0, first)}\n${content}\n${result.slice(last)}`; + } + return result; +} + +/** Fail on stale generated content; exported to mutation-test the actual throw. */ +export function assertCurrent(document: string, next: string): void { + if (document !== next) throw new Error('Site architecture tables are stale; run node .github/scripts/architecture/site-architecture.mts --write'); +} +/** Planned findings warn without a deadline; enforced and strict acceptance findings throw. */ +export function planFinding(status: unknown, check: () => void, policy: { actions?: boolean } = {}): void { + if (status !== 'planned' && status !== 'enforced') throw new TypeError('status: expected planned or enforced'); + try { check(); } catch (error) { + if (status !== 'planned') throw error; + const message = `SITE_PLAN_WARNING: ${String(error)}. Fix: node .github/scripts/architecture/site-architecture.mts --write`; + console.warn(message); + if (policy.actions ?? process.env.GITHUB_ACTIONS === 'true') console.warn(`::warning file=docs/site-architecture.md::${message.replaceAll('%', '%25').replaceAll('\r', '%0D').replaceAll('\n', '%0A')}`); + } +} + +export async function checkSiteArchitecture(root: string, options: { readonly graph?: ImportGraph; readonly write?: boolean; readonly sequence?: boolean; readonly minimality?: boolean; readonly identity?: boolean; readonly accept?: boolean; readonly references?: boolean; readonly old?: readonly string[] } = {}): Promise { + const started = performance.now(); + const read = (name: string): unknown => JSON.parse(readFileSync(resolve(root, DATA, `${name}.json`), 'utf8')); + const moves = read('moves'), tiers = read('tiers'), edits = read('edits'); + if (options.references) { + const inventory = references(root, { ...record(moves), ...Object.fromEntries((options.old ?? []).map(old => [old, null])) }); + if (options.old?.length) { + const findings = liveReferences(inventory, options.old); console.log(JSON.stringify({ liveOldPaths: findings, folderAndGlobReview: coveringReferences(inventory, options.old) }, null, 2)); + if (findings.length) throw new Error(`${findings.length} live old-path references`); + } else console.log(JSON.stringify(inventory, null, 2)); + return; + } + const declarations = await readImportDeclarations(root, { prefix: 'site/', graph: options.graph, checkAgainstScanner: true }); + if (declarations.summary.scanner.unexplained) throw new Error('Site declarations disagree with the production scanner'); + const known = new Set(repositoryFiles(root)); + const tierData = record(tiers); + const validateInputs = () => { + // Ignored generated inputs may be absent before preparation; their proposed relocations require the documented generator edits. + for (const file of [...Object.keys(record(moves)), ...list(tierData.testFiles ?? []), ...list(tierData.assets ?? [])].map(text)) { + if (!known.has(file) && !/^site\/(?:prepared-[^/]+|moon-labels\.prepared\.json)$/u.test(file)) throw new Error(`Unknown plan input: ${file}`); + } + }; + const input = { declarations, moves, tiers, edits }; + if (options.identity) { + const roots = declarations.summary.files.filter(file => file.split('/').length === 2); + const result = project({ declarations, moves: {}, tiers: { ...tierData, root: { allow: roots } } }); + console.log(JSON.stringify(result.views, null, 2)); return; + } + if (options.sequence || options.minimality) { + validateInputs(); + const result = options.sequence ? sequence(input) : minimality(input); console.log(JSON.stringify(result, null, 2)); + if (result.failed) throw new Error('Plan proof failed'); return; + } + planFinding(options.accept ? 'enforced' : tierData.status, () => { + validateInputs(); + const tables = renderTables(input); + if (!Array.isArray(edits)) { + const proof = sequence(input); + tables.sequence = [row(['Phase', 'Step', 'File SCCs', 'Folder SCCs', 'Upward', 'Lateral', 'Result']), row(['---', '---', '---:', '---:', '---:', '---:', '---']), + ...proof.steps.map(step => row([step.phase, step.step, step.fileSccs, step.folderSccs, step.phase === 'S3' ? 'n/a' : step.upward, step.phase === 'S3' ? 'n/a' : step.lateral, step.failed ? 'FAIL' : 'pass']))].join('\n'); + if (proof.failed) throw new Error('Sequence proof failed; run node .github/scripts/architecture/site-architecture.mts --sequence'); + if (minimality(input).failed) throw new Error('Minimality proof failed; run node .github/scripts/architecture/site-architecture.mts --minimality'); + const inventory = compactReferences(references(root, record(moves))); + const stored = readFileSync(resolve(root, DATA, 'references.json'), 'utf8'); + if (!options.write) assertCurrent(stored, `${JSON.stringify(inventory, null, 2)}\n`); + const referenceData = record(inventory); + const counts = new Map(); + for (const value of list(referenceData.occurrences)) { const entry = record(value); const key = `${text(entry.scope)}: ${text(entry.classification)}`; counts.set(key, (counts.get(key) ?? 0) + 1); } + tables.references = [row(['Scope and class', 'Path/class pairs']), row(['---', '---:']), ...[...counts].sort().map(([key, count]) => row([key, count]))].join('\n'); + tables['relative-reads'] = [row(['Test with depth-sensitive reads', 'Destination']), row(['---', '---']), + ...list(referenceData.relativeReads).map(value => { const entry = record(value); return row([text(entry.file), text(entry.destination)]); })].join('\n'); + const refPath = resolve(root, DATA, 'references.json'), expected = `${JSON.stringify(inventory, null, 2)}\n`; + if (options.write) writeFileSync(refPath, expected); + } + const docPath = resolve(root, DOC), document = readFileSync(docPath, 'utf8'), next = updateTables(document, tables); + if (options.write) { + writeFileSync(docPath, next); + } else assertCurrent(document, next); + console.log(`SITE_PLAN_OK: all views clear; tables match (${((performance.now() - started) / 1000).toFixed(1)} s${options.graph ? ', shared graph' : ', including scan'}).`); + }); +} + +if (process.argv[1] && import.meta.url === pathToFileURL(resolve(process.argv[1])).href) { + try { + const args = process.argv.slice(2); + const modes = ['--write', '--sequence', '--minimality', '--identity', '--references', '--accept']; + const mode = args[0]; + if (mode && !modes.includes(mode)) throw new Error('Usage: site-architecture.mts [--accept|--write|--sequence|--minimality|--identity|--references [--old paths...]]'); + const old = mode === '--references' && args[1] === '--old' ? args.slice(2) : []; + if (args.length > 1 && !old.length) throw new Error('Unexpected arguments'); + await checkSiteArchitecture(resolve(import.meta.dirname, '../../..'), { write: mode === '--write', sequence: mode === '--sequence', + minimality: mode === '--minimality', identity: mode === '--identity', references: mode === '--references', old, accept: mode === '--accept' }); + } catch (error) { console.error(error); process.exitCode = 1; } +} diff --git a/.github/scripts/architecture/site-architecture.test.mts b/.github/scripts/architecture/site-architecture.test.mts new file mode 100644 index 00000000000..f3212c8b555 --- /dev/null +++ b/.github/scripts/architecture/site-architecture.test.mts @@ -0,0 +1,82 @@ +/** The plan's generated tables must expose connectivity failures and become stale on data changes. */ +import assert from 'node:assert/strict'; +import { test } from 'node:test'; +import { readFileSync } from 'node:fs'; +import { spawnSync } from 'node:child_process'; +import ts from 'typescript'; +import { renderTables, updateTables } from './site-architecture.mts'; +const input = { + declarations: { declarations: [{ from: 'site/a.mts', to: 'site/base/base.mts', kind: 'value' }], summary: { files: ['site/a.mts', 'site/base/base.mts'] } }, + moves: { 'site/a.mts': 'site/app/a.mts' }, + tiers: { tiers: [{ tier: 1, name: 'Foundation', folders: ['site/base'] }, { tier: 2, name: 'Application', folders: ['site/app'] }], root: { allow: [] } }, edits: [], +}; +test('renders counts, before/after and exact marked tables; changed data changes the document', () => { + const tables = renderTables(input); + assert.match(tables.folders!, /Application \| 1 \| 1/u); + assert.match(tables.numbers!, /\| all \| 0 \| 0 \| 1 \| 0 \| 0 \| 0 \| 0 \| 0 \|/u); + const document = Object.keys(tables).map(name => `\n`).join('\n'); + const written = updateTables(document, tables); + assert.notEqual(document, written); assert.equal(updateTables(written, tables), written); + assert.notEqual(updateTables(written, renderTables({ ...input, tiers: { ...input.tiers, + tiers: input.tiers.tiers.map(tier => ({ ...tier, name: `${tier.name} changed` })) } })), written); + assert.throws(() => updateTables('', tables), /markers/u); + assert.throws(() => updateTables(document + document, tables), /markers/u); +}); +test('refuses to render a plan with cycles or uncovered root files', () => { + assert.throws(() => renderTables({ ...input, moves: {} }), /fails projection/u); + assert.throws(() => renderTables({ ...input, edits: [{ op: 'add-import', from: 'site/base/base.mts', to: 'site/app/a.mts', kind: 'lazy', + change: 'Bad cycle', note: 'Mutation makes the base depend on application.' }] }), /fails projection/u); +}); + +test('the stale-table guard throws; equal content passes', async () => { + const { assertCurrent } = await import('./site-architecture.mts'); + assert.throws(() => assertCurrent('stale', 'current'), /stale/u); + assert.doesNotThrow(() => assertCurrent('current', 'current')); +}); +test('planned findings only warn, enforced findings throw, and invalid statuses fail', async () => { + const { planFinding } = await import('./site-architecture.mts'); + const failure = () => { throw new Error('plan changed'); }; + const warn = console.warn, messages: string[] = []; console.warn = value => { messages.push(String(value)); }; + try { + // Removing the warn-only branch makes this assertion fail on the real finding. + assert.doesNotThrow(() => planFinding('planned', failure, { actions: false })); + assert.equal(messages.length, 1); + assert.match(messages[0]!, /WARNING.*plan changed.*Fix: node .*--write/u); + assert.throws(() => planFinding('enforced', failure), /plan changed/u); + for (const status of ['unsigned', 'draft', undefined, null]) assert.throws(() => planFinding(status, failure), /status/u); + assert.doesNotThrow(() => planFinding('enforced', () => {})); + } finally { console.warn = warn; } +}); + +test('planned findings always emit Actions warnings, without a count ceiling', async () => { + const { planFinding } = await import('./site-architecture.mts'); + const warn = console.warn, messages: string[] = []; console.warn = value => { messages.push(String(value)); }; + try { + for (let i = 0; i < 3; i++) planFinding('planned', () => { throw new Error('changed%\nnext'); }, { actions: true }); + assert.equal(messages.length, 6); + assert.equal(messages.filter(message => message.startsWith('::warning file=docs/site-architecture.md::')).length, 3); + assert.match(messages[1]!, /changed%25%0Anext.*--write/u); + } finally { console.warn = warn; } +}); + +test('plan schema accepts only planned and enforced statuses', () => { + for (const status of ['planned', 'enforced']) assert.doesNotThrow(() => renderTables({ ...input, tiers: { ...input.tiers, status } })); + for (const status of ['draft', 'unsigned', null]) assert.throws(() => renderTables({ ...input, tiers: { ...input.tiers, status } }), /status/u); +}); + +test('deleting the planned warn-only rule turns the same finding assertion red', () => { + const source = readFileSync(new URL('./site-architecture.mts', import.meta.url), 'utf8'); + const start = source.indexOf('export function planFinding('), end = source.indexOf('\nexport async function checkSiteArchitecture', start); + assert.ok(start >= 0 && end > start); + const original = source.slice(start, end); + const mutant = original.replace("if (status !== 'planned') throw error;", 'throw error;'); + assert.notEqual(mutant, original); + const probe = (implementation: string) => { + const code = ts.transpileModule(implementation, { compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2022 } }).outputText; + return spawnSync(process.execPath, ['-e', `${code}\nrequire('node:assert/strict').doesNotThrow(() => planFinding('planned', () => { throw new Error('plan changed'); }, { actions: false }));`], { encoding: 'utf8' }); + }; + const passing = probe(original); assert.equal(passing.status, 0, passing.stderr); + assert.match(passing.stderr, /SITE_PLAN_WARNING.*plan changed.*--write/u); + const failing = probe(mutant); assert.equal(failing.status, 1, failing.stderr); + assert.match(failing.stderr, /AssertionError.*|Got unwanted exception/u); +}); diff --git a/.github/scripts/audits/check-documentation-links.mts b/.github/scripts/audits/check-documentation-links.mts index 16bb9abec63..94ff13c5eb4 100644 --- a/.github/scripts/audits/check-documentation-links.mts +++ b/.github/scripts/audits/check-documentation-links.mts @@ -196,7 +196,9 @@ function audit(snapshot: Snapshot): Audit { // Owner-designated documentation evidence beside the virtual telescope guide (Wave T). const projectionEvidence = new Set(['docs/fixtures/telescope-projection/europa-navigation.json', 'docs/fixtures/telescope-projection/europa-oracle.json', 'docs/fixtures/telescope-projection/europa-registration.json']); - for (const file of docs) if (!guides.has(file) && !illustrations.has(file) && !projectionEvidence.has(file)) errors.push({file, reason: 'docs/ accepts Markdown guides and illustrations under docs/images/; move code, fixtures and raw output to their owner'}); + // The site proposal is generated from these exact owner-requested inputs; arbitrary JSON stays forbidden. + const sitePlanInputs = new Set(['docs/site-architecture/moves.json', 'docs/site-architecture/tiers.json', 'docs/site-architecture/edits.json', 'docs/site-architecture/references.json', 'docs/site-architecture/loader-options.json']); + for (const file of docs) if (!guides.has(file) && !illustrations.has(file) && !projectionEvidence.has(file) && !sitePlanInputs.has(file)) errors.push({file, reason: 'docs/ accepts Markdown guides and illustrations under docs/images/; move code, fixtures and raw output to their owner'}); const index = 'docs/README.md'; if (!guides.has(index)) errors.push({file: index, reason: 'missing documentation index'}); const pending = guides.has(index) ? [index] : []; diff --git a/.github/scripts/audits/check-documentation-links.test.mts b/.github/scripts/audits/check-documentation-links.test.mts index 69f7c79de5c..42572858671 100644 --- a/.github/scripts/audits/check-documentation-links.test.mts +++ b/.github/scripts/audits/check-documentation-links.test.mts @@ -1,3 +1,4 @@ +/** Documentation links, placement and narrowly admitted executable plan inputs. */ import assert from 'node:assert/strict'; import {execFileSync, spawnSync} from 'node:child_process'; import {mkdirSync, mkdtempSync, rmSync, writeFileSync} from 'node:fs'; @@ -215,3 +216,9 @@ test('the three guide-owned telescope projection records are documentation evide f.write(`docs/fixtures/telescope-projection/europa-${name}.json`, '{}'); assert.deepEqual(f.check().errors, []); }); + +test('only named executable site-plan inputs are admitted beside the guide', t => { + const f = fixture(t); + for (const name of ['moves', 'tiers', 'edits', 'references', 'loader-options', 'unexpected']) f.write(`docs/site-architecture/${name}.json`, '{}'); + assert.deepEqual(f.check().errors.map(error => error.file), ['docs/site-architecture/unexpected.json']); +}); diff --git a/docs/README.md b/docs/README.md index 8d304a2764d..98337cab529 100644 --- a/docs/README.md +++ b/docs/README.md @@ -9,6 +9,7 @@ For a body's sources, processing, evidence and known problems, read its | Topic | Guide | | --- | --- | +| Proposed site folders, dependency tiers and move sequence | [Site architecture plan](site-architecture.md) | | Computed schema ownership across packages and architecture locks | [Prepared format ownership](prepared-format-ownership.md) | | PR feedback budget, check selection, failure recovery and deployment | [CI/CD maintenance](ci-cd.md) | | Galaxies, LMC image datasets and extragalactic datasets | [Galaxies and the nearby universe](galaxies/README.md) | diff --git a/docs/site-architecture.md b/docs/site-architecture.md new file mode 100644 index 00000000000..7dce23855cd --- /dev/null +++ b/docs/site-architecture.md @@ -0,0 +1,245 @@ +# Site architecture plan + +**Option 3 was decided by the owner on 2026-10-05: relocate queued loader functions to their owners.** The router starts at module evaluation exactly as today, and all loading uses the same single serialized queue chain. S2 changes only documentation and tools; it moves no application files. The projection proves connectivity, not runtime behavior or generated chunk bytes. This PR is ready for the owner to merge. + +[moves.json](site-architecture/moves.json) records destinations; [tiers.json](site-architecture/tiers.json) records layers, denies and planned/enforced status; [edits.json](site-architecture/edits.json) records the decided semantic changes; [references.json](site-architecture/references.json) is a compact path-and-class inventory. + +## Folder ownership + +A folder imports itself or strictly lower layers. Sibling folders, including L0 siblings, cannot import each other. Value, lazy, type, CSS and JSON edges all count. Tests are leaf consumers above production. There are no lateral allowances. For a legitimate new sibling edge, re-derive longest-path levels for the production DAG and regenerate all tables; a cycle requires an ownership change, not a tier exemption. + + +| Tier | Folder | Purpose | Final files | Incoming moves | +| ---: | --- | --- | ---: | --- | +| 0 | `browser/` | Browser input, DOM and serialized import queue | 17 | 15; `runtime-policy.mts`, `diagnostics-policy.mts`, `in-flight-loader.mts`, `next-frame.mts` | +| 0 | `model/` | Object identities, routes and held addresses | 8 | 7; `navigation/system-address.mts`, `root-object.mts`, `orbit-root.mts`, `planetary-system-members.mts` | +| 0 | `overview/` | Prepared spectral overview readers | 4 | 2 | +| 0 | `prepared/` | Prepared transports and generated inputs | 25 | 24; `prepared-catalogue.d.mts`, `prepared-navigation-markers.d.mts`, `prepared-object-path.mts`, `prepared-shell-icons.d.mts` | +| 0 | `source/` | Preserved input records and artwork | 60 | 0 | +| 0 | `vendor/` | Preserved third-party notices | 1 | 0 | +| 0 | `server-assets/` | Server and build prepared-asset origin | 2 | 2; `asset-origin.mts` | +| 0 | `contracts/` | Shared page and shell interfaces | 3 | 3; `object-shell-types.ts`, `object-page-contract.mts` | +| 1 | `directory/` | Startup reads and object catalogue directory | 13 | 12; `startup-world.mts`, `startup-requests.mts`, `object-entries.mts`, `object-directory.mts` | +| 1 | `minimap/` | Surface-map measurements and view formatting | 7 | 4 | +| 2 | `world/` | Shared framing, visibility and camera context | 54 | 53; `world-objects.mts`, `world-system-views.mts`, `context-availability.mts`, `context-datasets.mts` | +| 3 | `content/` | Card content, citations and metadata | 22 | 21; `dataset-content.mts`, `dataset-context.mts`, `object-text.mts`, `prepared-panel-content.mts` | +| 3 | `navigation/` | History, requests, flights and arrivals | 35 | 23; `prepared-arrival.mts`, `prepared-scene-ownership.mts`, `arrival-billboard.mts`, `prepared-world-navigation.mts` | +| 4 | `search/` | Catalogue search and result presentation | 17 | 6 | +| 4 | `selection/` | Committed selection and camera handovers | 6 | 6; `scene/scene-selection.mts`, `satellite-selection.mts`, `overview-selection.mts`, `showcase.mts` | +| 5 | `shell/` | Retained shell controls and panels | 32 | 25; `object-browser.mts`, `feature-browser.mts`, `destination-browser.mts`, `selection-presentation.mts` | +| 6 | `server/` | SSR readers, responses and host middleware | 32 | 19; `object-page-data.mts`, `first-view-transport.mts`, `object-entry.mts`, `world-places.mts` | +| 6 | `scene/` | Scene sessions, replacement and publication | 30 | 15; `object-adapter.mts`, `packaged-object-runtime.mts`, `startup-billboard.mts`, `initial-scene.mts` | +| 7 | `startup/` | Page boot helpers and router entry | 8 | 8; `shared-imports.mts`, `startup-boot.mts`, `startup-cover.mts`, `initial-shell-context.mts` | +| 7 | `build/` | Site preparation and packaging | 83 | 32; `prepare-body-moons.mts` | +| 8 | `layouts/` | Shared page frame and its styles | 7 | 5; `components/ObjectSwatchStyles.astro`, `site.css`, `object-shell.css`, `wordmark.css` | +| 9 | `components/` | Reusable Astro markup and SSR composition | 29 | 0 | +| 10 | `pages/` | Routes and object-page entries | 20 | 0 | +| 11 | `journeys/` | Cross-cutting journeys and support | 19 | 19 | +| 11 | `test/` | Retired test folder; no final occupants | 0 | 0 | + + +The table is the final layering. Directory owns startup requests, startup world reads, object entries, registry and world context plan: it is the lower world family. World owns framing, visibility, datasets and camera context. History and fragments belong to navigation. The model's held-address reader carries its WeakMap and registration/disposal state; history keeps its re-export. + +Startup is the page-boot root. Startup cover, initial shell context, error reporting and analytics are used only by ObjectLayout and belong there. Browser retains shared input/DOM primitives and the import queue. Server-assets owns the node-dependent asset-origin reader used by server and build; clients may not enter it. ObjectSwatchStyles moves beside its layout; ObjectPage stays in components. The layout module-script body stays in ObjectLayout: extraction is unnecessary. + +Server/search-response deliberately shares shell/selection-presentation and selection/scene-selection with the client. These operate on its supplied server document and selection values; they are shared presentation/selection logic, not browser entry points. This is an explicit permitted downward dependency, not a general server-to-client isolation claim. Client owners cannot import server, build or server-assets. Layouts/components/pages cannot import build. Only layouts and pages may enter startup. + +Registry types live in directory, navigation types in navigation and presenter types in scene. Their declared imports model implementation dependencies; adding an edge cannot establish necessity. + +## Contributor workflow + +There is no deadline. With `status: "planned"`, a plan finding is a WARNING with the fix command and a GitHub Actions annotation; it never fails `pnpm check:architecture`. The check reports the first plan finding. With `status: "enforced"`, every plan finding fails. The owner changes the status when S4 ends. Existing architecture rules and scanner failures always fail. Strict `--accept` always fails on plan findings. + +When a warning names your file, update its destination or semantic edits and run `node .github/scripts/architecture/site-architecture.mts --write`, then strict `--accept`. Do not change application behavior just to silence a plan warning. Every mapped path is checked for existence, including deleted mapped files; generated inputs are explicit exceptions. Routine checks rescan the compact inventory so stale references produce findings. + +## S3 atomic changes + +The changes array partitions stable edit ids into atomic groups. All seven groups are necessary by removal projection. **Minimality holds at group level only**: individual modelling imports can be removable. Groups currently commute; the listed order is a tested replay, not a claim that another order must fail. Added modules are placed at root during S3, where destination folders may not yet exist; S4 moves them with their owners. The L6 null moves are deletion preconditions. + + +| Tier | Change / PR | Edit ids | Required code change | +| ---: | --- | --- | --- | +| 0 | Extract held address reader | add-file-navigation-href, add-import-navigation-history-navigation-href, retarget-application-world-resources-navigation-href | Move navigationHref AND its owners WeakMap. Export a registration function for history; preserve deferred URL semantics. History registers and unregisters its held address reader in the same WeakMap; re-export the getter for existing navigation callers. Only lower world caller uses navigationHref; other callers retain history behavior. | +| 1 | Extract registry types | add-file-object-entry-types, retarget-object-directory-object-entry-types, add-import-objects-object-entry-types | Extract registry aliases from objects; directory must not import the registry that calls it. Use the extracted registry aliases without evaluating the full registry. Registry imports and re-exports its extracted aliases so clients can migrate without duplication. | +| 3 | Extract navigation contracts | add-file-navigation-types, retarget-prepared-arrival-navigation-types, retarget-navigation-history-navigation-types, add-import-prepared-world-navigation-navigation-types, add-import-navigation-request-navigation-types | Own WorldHandoff, NavigationHistory and NavigationIntent beside navigation. Import MountOptions, ObjectSceneLifecycle, ObjectWorldNavigation, SceneSubject and PageView; preserve erased aliases SceneView and SelectionTarget. Arrival reads WorldHandoff from the lower contract; navigation still owns flights. Extract NavigationHistory and NavigationIntent into navigation-types; the history transport must not import request orchestration. Flights import and re-export the extracted WorldHandoff contract. Request imports its extracted intent and history types and the plain selection shape; selection behavior is passed in. | +| 3 | Split the moon catalogue reader | add-file-moon-catalogue, retarget-object-children-moon-catalogue, remove-import-prepare-body-moons-moon-catalogues, add-import-prepare-body-moons-moon-catalogue | Split catalogue parsing, cached source catalogue and catalogueMoons into a runtime-safe reader; preparation keeps search-based ordering. Read catalogueMoons from the split reader; production must never import build preparation. Move the source catalogue import with its parser into the reader; preparation calls that reader. Use the single catalogue reader for preparation too; preserve the named-moon eligibility policy in preparation. | +| 3 | Pass selection into navigation requests | remove-import-navigation-request-scene-selection, add-import-navigation-request-scene-subject | Pass selectionTargetFromUrl explicitly through resolveNavigation and readNavigationSelection. Router uses the existing registry export, injects it into createSceneActivation, which forwards all three calls. Import erased SceneView/SelectionTarget aliases from navigation-types; never retain the selection import. Keep existing subject identity helpers in the lower world owner; pass selectionTargetFromUrl as a parameter. | +| 6 | Extract the frame presenter contract | add-file-scene-frame-presenter, retarget-scene-session-scene-frame-presenter, add-import-scene-world-scene-frame-presenter | Extract the structural presenter interface; a session must not import the world coordinator that consumes it. Session reads SceneFramePresenter from the lower contract, breaking the two-way type dependency. World coordinator imports and re-exports the structural presenter contract. | +| 0 | Relocate queued loader owners | add-file-scene-imports, add-file-world-imports, remove-queue-runtime, remove-shared-registry, remove-shared-world, retarget-adapter-scene-imports, retarget-router-world-imports, add-router-scene-imports, remove-directory-queue, retarget-boot-scene-imports, add-boot-world-imports | Decided by the owner: loader modules hold only import() calls through the existing queue. Own registry and packaged-runtime functions in scene; move the single last chain to lower browser/import-queue. Prove one queue chain, registration before the first scene call, the same entry-chunk modules and bytes, identical L1/L2/L3, L7 no worse and the real-iPad startup journey. Own the queued application-world import in world; no initializer inside the thunk. Move the runtime function out of the import-free browser queue implementation. Leave only importSceneRouter in startup/shared-imports. Move the world function to its owner. Adapter directly uses the same queued runtime function; preserve the frozen default adapter. Split router loader imports; preserve module-evaluation autostart. Import registry/runtime functions and register only the directory runtime loader using the existing object-directory import before first scene call. Directory accepts a typed runtime-loader registration; read the slot at scene invocation, not metadata construction. Missing registration fails explicitly. Body prestart and registry prestart reuse scene-owned promises. View prestart reuses the world-owned promise. | + + +## Decided loader design: option 3 + +Read sources: site/import-queue.mts:13–26, site/shared-imports.mts:1–7, site/startup-boot.mts:1–24, site/layouts/ObjectLayout.astro:162–178, site/object-directory.mts:55–72, site/object-adapter.mts:1–20 and site/scene/scene-router.mts:839–845. + +```text +layout → startup/shared-imports → queued router import (module autostart) + → startup/startup-boot → scene/scene-imports → registry/runtime + → world/world-imports → application world + all loaders → browser/import-queue +router → directory runtime-loader registration → first scene call +``` + +- Move queuedImport and its **single last chain** to browser/import-queue. Keep its pending sharing and rejection-reset semantics unchanged. It has no imports of scene/runtime modules. +- Scene/scene-imports owns importSceneRegistry and importPackagedObjectRuntime. World/world-imports owns importApplicationWorld. Each function still wraps only import(), using the same queuedImport. Startup/shared-imports keeps only importSceneRouter; startup-boot imports scene/world loaders directly. +- Router imports scene/world loader modules. Its existing object-directory import registers only the directory runtime loader before any scene call, then runs the existing module-evaluation autostart. The adapter directly imports scene/scene-imports; its frozen default remains intact. No factory or .then(start) is introduced. +- Directory exports a typed registration function. It reads the configured loader at scene invocation, never during objectFromEntry or registry construction. Missing registration fails clearly. Metadata callers in build, Astro SSR and server endpoints remain valid without a loader. System-host recursion, abort signals, retry and entry identity must remain identical. +- Root-level scene-imports/world-imports exist during S3; imports point at current paths until S4. No thunk awaits a queued function behind itself. The router's rejection remains a module-evaluation failure with the existing retry behavior and layout failure cleanup. + +S3 must prove one shared queue chain, registration before the first scene call, metadata construction without registration, registration/retry/abort behavior, and queue settlement/error cleanup by tests with mutations. Require the same entry-chunk module membership and bytes, identical L1/L2/L3 results, L7 performance no worse, and the real-iPad startup journey (including delayed-summary cold loads). The extracted loader modules hold only import() calls through the shared queue; the single last chain lives in the lower browser module. These are S3 acceptance requirements, not results established by the S2 graph projection. Reuse of functions does not prove unchanged bytes. No functionality may break anywhere; performance must stay the same or improve. L7 is a performance guard beside the behavior checks. + +## Options not taken + +Parameter injection with a changed router start changes the router's evaluation phase. An explicit lazy-boundary exception gives up the zero-cycle claim. Neither is part of the plan. + +## Other S3 risks + +- Moon catalogue split changes build/SSR chunk membership, checkObjectTree timing and client bytes from the source parsers. The single reader owns parsing/cache/catalogueMoons; preparation retains search ordering and eligibility. Compare outputs, errors, timing and chunks before S3. +- Selection is passed into navigation requests. Router and activation must forward the registry function on all three readNavigationSelection calls. Test the **default production URL path** and mutation-remove its wiring; passing a non-default callback that routes around the defect is insufficient. +- Type-owner extractions must preserve aliases and re-exports. The held-address extraction must preserve deferred reads and disposal in the same WeakMap. + +## Generated delivery and S4 gates + +Declarations use **git ls-files only**. Ignored outputs resolve to their declared paths, with tracked sibling .d.mts stand-ins for generated modules. The snapshot must match in a clean checkout, a restored checkout and CI. The four tracked declarations move beside their ignored modules. Restore object prepared inputs with pnpm setup:prepared; generate all ignored site inputs locally with pnpm build:prepare (including prepare:inputs, prepare:world-presentation, prepare:shell-assets and prepare:object-json) when running application/build proofs; neither is needed for plan acceptance. S2 publishes nothing. + +Update producers and readers together: site prepare-catalog, shell-icons/titles, moon-labels, world-presentation, facilities and feature-index; packages/bake navigation preparation and dataset billboards. Preserve published addresses. Move the four layout CSS files with their style imports. Also update ignores and declaration/template identities. + +S3 replays current locations with root-level additions. File SCCs cannot increase, folder SCCs must remain contained in the baseline, and the final S3 prefix must have zero file SCCs. S3 tier counts are n/a because current folders are not final layers. S4's synthetic top-tier legacy replay is a **corollary of the final DAG**, not an independent proof: moving folders in tier order cannot fail when the final projection passes. S4's real risks are references and executed-test identities. + + +| Phase | Step | File SCCs | Folder SCCs | Upward | Lateral | Result | +| --- | --- | ---: | ---: | ---: | ---: | --- | +| S3 | extract-held-address-reader | 4 | 2 | n/a | n/a | pass | +| S3 | extract-registry-types | 4 | 2 | n/a | n/a | pass | +| S3 | extract-navigation-contracts | 3 | 2 | n/a | n/a | pass | +| S3 | split-the-moon-catalogue-reader | 3 | 2 | n/a | n/a | pass | +| S3 | pass-selection-into-navigation-requests | 3 | 2 | n/a | n/a | pass | +| S3 | extract-the-frame-presenter-contract | 2 | 2 | n/a | n/a | pass | +| S3 | relocate-queued-loader-owners | 0 | 2 | n/a | n/a | pass | +| S4 | site/browser | 0 | 0 | 0 | 0 | pass | +| S4 | site/contracts | 0 | 0 | 0 | 0 | pass | +| S4 | site/model | 0 | 0 | 0 | 0 | pass | +| S4 | site/overview | 0 | 0 | 0 | 0 | pass | +| S4 | site/prepared | 0 | 0 | 0 | 0 | pass | +| S4 | site/server-assets | 0 | 0 | 0 | 0 | pass | +| S4 | site/source | 0 | 0 | 0 | 0 | pass | +| S4 | site/vendor | 0 | 0 | 0 | 0 | pass | +| S4 | site/directory | 0 | 0 | 0 | 0 | pass | +| S4 | site/minimap | 0 | 0 | 0 | 0 | pass | +| S4 | site/world | 0 | 0 | 0 | 0 | pass | +| S4 | site/content | 0 | 0 | 0 | 0 | pass | +| S4 | site/navigation | 0 | 0 | 0 | 0 | pass | +| S4 | site/search | 0 | 0 | 0 | 0 | pass | +| S4 | site/selection | 0 | 0 | 0 | 0 | pass | +| S4 | site/shell | 0 | 0 | 0 | 0 | pass | +| S4 | site/scene | 0 | 0 | 0 | 0 | pass | +| S4 | site/server | 0 | 0 | 0 | 0 | pass | +| S4 | site/build | 0 | 0 | 0 | 0 | pass | +| S4 | site/startup | 0 | 0 | 0 | 0 | pass | +| S4 | site/layouts | 0 | 0 | 0 | 0 | pass | +| S4 | site/components | 0 | 0 | 0 | 0 | pass | +| S4 | site/pages | 0 | 0 | 0 | 0 | pass | +| S4 | site/journeys | 0 | 0 | 0 | 0 | pass | +| S4 | site/test | 0 | 0 | 0 | 0 | pass | + + +The reference scan covers full/extensionless/relative file strings plus the retired site/test/ folder prefix, including site/test/** and site/test/* globs. It does not prove arbitrary computed paths or every possible folder spelling. Plan-internal inventories and plan-checker implementation files are excluded. Generated tables exclude themselves. Dated history stays separate from live references. Full line-numbered output exists only on demand with --references; it is not committed. + + +| Scope and class | Path/class pairs | +| --- | ---: | +| live: AGENTS.md contract text | 4 | +| live: CI routing | 1 | +| live: Python script | 2 | +| live: computed import | 4 | +| live: docs code-span | 21 | +| live: docs link | 26 | +| live: generator/producer literal | 106 | +| live: source import | 256 | +| live: tsconfig/eslint/package.json | 20 | +| live: workflow | 3 | +| plan: docs code-span | 1 | + + +Before **every moved-test PR**, capture the actual executed test-file list. After the move, require every old test's mapped destination in the executed list, with no lost identity. Check both local wiring and each affected CI command. Node 22 may exit zero for a no-match glob alongside valid files; an exit code alone is insufficient. + +Required reference updates include: + +- .github/workflows/audit.yml:180,182 source/facility globs; universe.yml:66 sparse checkout prefix; eslint.config.mts:167,175 test/helper exclusions. +- docs/sources-catalogue.md:117; src/objects/README.md:37; packages/bake/AGENTS.md:117,126 folder guidance and its stale prepare-body-overview path (actual caller is site/build/charts/charts.ts). +- labs/investigations/capture-galaxies.mts:10 fixture output; typescript ownership and nebula inbound prefix checks; site tsconfigs, lint selectors, preload, rendered routes, affected-test wiring, directory growth baseline and CI areas. +- Root/object/bake contracts and outside-site producers/imports. Inspect computed import/read producers during each move. Compare actual typechecked files so tests/helpers do not silently enter production lint/typecheck policy. + + +| Test with depth-sensitive reads | Destination | +| --- | --- | +| site/test/arrival-discovery.test.mts | site/build/prepare/arrival-discovery.test.mts | +| site/test/body-additions.test.mts | site/build/prepare/body-additions.test.mts | +| site/test/category-frames.test.mts | site/build/prepare/category-frames.test.mts | +| site/test/compact-spectrum.test.mts | site/overview/compact-spectrum.test.mts | +| site/test/dataset-response.test.mts | site/server/dataset-response.test.mts | +| site/test/folded-transit.test.mts | site/build/charts/folded-transit.test.mts | +| site/test/hosted-banks.test.mts | site/build/prepare/hosted-banks.test.mts | +| site/test/load-object-content.mts | site/build/content/load-object-content.test-support.mts | +| site/test/lonlat-slice-table.test.mts | site/build/prepare/lonlat-slice-table.test.mts | +| site/test/moon-labels.test.mts | site/world/moon-labels.test.mts | +| site/test/neutral-catalogue-color.test.mts | site/journeys/neutral-catalogue-color.test.mts | +| site/test/object-page-data.test.mts | site/server/object-page-data.test.mts | +| site/test/object-systems.test.mts | site/world/object-systems.test.mts | +| site/test/prepare-spatial-context.test.mts | site/build/prepare/prepare-spatial-context.test.mts | +| site/test/prepared-panel-content.test.mts | site/content/prepared-panel-content.test.mts | +| site/test/prepared-world-context.test.ts | site/world/prepared-world-context.test.ts | +| site/test/satellite-arrival-framing.test.mts | site/navigation/satellite-arrival-framing.test.mts | +| site/test/scene-contract.test.mts | site/world/scene-contract.test.mts | +| site/test/system-framing.test.mts | site/world/system-framing.test.mts | +| site/test/system-text.test.mts | site/build/prepare/system-text.test.mts | +| site/test/system-view-file.mts | site/world/system-view-file.test-support.mts | +| site/test/world-billboards.test.mts | site/journeys/world-billboards.test.mts | +| site/test/world-stars.test.mts | site/world/world-stars.test.mts | +| site/test/world-tables.test.mts | site/world/world-tables.test.mts | +| site/test/zoom-scope.test.mts | site/world/zoom-scope.test.mts | + + +Move PRs delete applied mappings/groups, retarget pending edits and regenerate tables. The --references --old gate fails live old-file references and also prints covering folder/glob references for review. During partial moves, old globs may still serve remaining tests: extend wiring for the moved tests and prove executed identities. Once the folder is retired, run --references --old site/test/ and require zero live folder/glob references. Both gates work after removing applied map entries. + +## Executable projection + + +| View | Before file SCCs | Before folder SCCs | Before upward | Before lateral | After file SCCs | After folder SCCs | After upward | After lateral | +| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | +| value | 0 | 2 | 30 | 10 | 0 | 0 | 0 | 0 | +| value+lazy | 2 | 2 | 32 | 11 | 0 | 0 | 0 | 0 | +| value+lazy+type | 4 | 2 | 43 | 24 | 0 | 0 | 0 | 0 | +| all | 4 | 2 | 43 | 26 | 0 | 0 | 0 | 0 | + +Unassigned files: before 0; after 0. No lateral allowances. +Identity uses the proposed numbers on existing folders and tier zero for loose root files. It applies no moves or edits. + + +Counts represent declarations and cyclic components. External package boundaries remain under the ordinary architecture rules. Acceptance scans live tracked declarations and live compact references; it requires no ignored snapshot. No independent review is claimed unless a completed cited report exists. + +Commands from a clean checkout: + +```sh +export PATH=$HOME/.nvm/versions/node/v22.23.2/bin:$PATH +pnpm install --frozen-lockfile +node --test .github/scripts/architecture/*.test.mts .github/scripts/audits/check-documentation-links.test.mts +node .github/scripts/architecture/site-architecture.mts --accept +node .github/scripts/architecture/site-architecture.mts --identity +node .github/scripts/architecture/site-architecture.mts --sequence +node .github/scripts/architecture/site-architecture.mts --minimality +node .github/scripts/architecture/site-architecture.mts --references > /tmp/site-architecture-references.json +pnpm check:architecture +# When changing plan data, regenerate first, then rerun acceptance: +node .github/scripts/architecture/site-architecture.mts --write +node .github/scripts/architecture/site-architecture.mts --accept +# Per-move gate: substitute that PR's actual old paths; fails before migration. +node .github/scripts/architecture/site-architecture.mts --references --old site/test/source-link.test.mts +``` + +## Owner decisions and remaining ownership + +- Option 3 and the final folder ownership, including page-boot helpers in startup, were decided by the owner. +- There is no deadline. The owner sets S4/S5 timing and changes planned to enforced when S4 ends. +- Branches touching scene-router, startup-boot and ObjectLayout are timed together so they do not collide. +- The real-iPad startup-proof device owner remains to be assigned. diff --git a/docs/site-architecture/edits.json b/docs/site-architecture/edits.json new file mode 100644 index 00000000000..ce6c53fb0e1 --- /dev/null +++ b/docs/site-architecture/edits.json @@ -0,0 +1,432 @@ +{ + "edits": [ + { + "op": "add-file", + "path": "site/model/navigation-href.mts", + "imports": [], + "change": "Extract held address reader", + "note": "Move navigationHref AND its owners WeakMap. Export a registration function for history; preserve deferred URL semantics.", + "id": "add-file-navigation-href" + }, + { + "op": "add-import", + "from": "site/navigation/navigation-history.mts", + "to": "site/model/navigation-href.mts", + "kind": "value", + "change": "Extract held address reader", + "note": "History registers and unregisters its held address reader in the same WeakMap; re-export the getter for existing navigation callers.", + "id": "add-import-navigation-history-navigation-href" + }, + { + "op": "retarget", + "from": "site/world/application-world-resources.mts", + "to": "site/navigation/navigation-history.mts", + "newTo": "site/model/navigation-href.mts", + "kind": "value", + "change": "Extract held address reader", + "note": "Only lower world caller uses navigationHref; other callers retain history behavior.", + "id": "retarget-application-world-resources-navigation-href", + "line": 18 + }, + { + "op": "add-file", + "path": "site/directory/object-entry-types.mts", + "note": "Extract registry aliases from objects; directory must not import the registry that calls it.", + "change": "Extract registry types", + "imports": [ + { + "to": "site/browser/browser-types.mts", + "kind": "type" + }, + { + "to": "packages/objects/src/index.ts", + "kind": "type" + } + ], + "id": "add-file-object-entry-types" + }, + { + "op": "retarget", + "from": "site/directory/object-directory.mts", + "to": "site/directory/objects.mts", + "note": "Use the extracted registry aliases without evaluating the full registry.", + "kind": "type", + "newTo": "site/directory/object-entry-types.mts", + "change": "Extract registry types", + "id": "retarget-object-directory-object-entry-types", + "line": 3 + }, + { + "op": "add-import", + "to": "site/directory/object-entry-types.mts", + "kind": "type", + "note": "Registry imports and re-exports its extracted aliases so clients can migrate without duplication.", + "from": "site/directory/objects.mts", + "change": "Extract registry types", + "id": "add-import-objects-object-entry-types" + }, + { + "op": "add-file", + "path": "site/navigation/navigation-types.mts", + "imports": [ + { + "to": "site/browser/browser-types.mts", + "kind": "type" + }, + { + "to": "packages/renderer/src/runtime/world-navigation-types.ts", + "kind": "type" + }, + { + "to": "packages/renderer/src/runtime/object-scene.ts", + "kind": "type" + }, + { + "to": "site/world/scene-subject.mts", + "kind": "type" + }, + { + "to": "site/world/navigation-scope.mts", + "kind": "type" + } + ], + "change": "Extract navigation contracts", + "note": "Own WorldHandoff, NavigationHistory and NavigationIntent beside navigation. Import MountOptions, ObjectSceneLifecycle, ObjectWorldNavigation, SceneSubject and PageView; preserve erased aliases SceneView and SelectionTarget.", + "id": "add-file-navigation-types" + }, + { + "op": "retarget", + "from": "site/navigation/prepared-arrival.mts", + "to": "site/navigation/prepared-world-navigation.mts", + "note": "Arrival reads WorldHandoff from the lower contract; navigation still owns flights.", + "kind": "type", + "newTo": "site/navigation/navigation-types.mts", + "change": "Extract navigation contracts", + "id": "retarget-prepared-arrival-navigation-types", + "line": 4 + }, + { + "op": "retarget", + "from": "site/navigation/navigation-history.mts", + "to": "site/navigation/navigation-request.mts", + "newTo": "site/navigation/navigation-types.mts", + "kind": "type", + "note": "Extract NavigationHistory and NavigationIntent into navigation-types; the history transport must not import request orchestration.", + "change": "Extract navigation contracts", + "id": "retarget-navigation-history-navigation-types", + "line": 4 + }, + { + "op": "add-import", + "to": "site/navigation/navigation-types.mts", + "kind": "type", + "note": "Flights import and re-export the extracted WorldHandoff contract.", + "from": "site/navigation/prepared-world-navigation.mts", + "change": "Extract navigation contracts", + "id": "add-import-prepared-world-navigation-navigation-types" + }, + { + "op": "add-import", + "to": "site/navigation/navigation-types.mts", + "kind": "type", + "note": "Request imports its extracted intent and history types and the plain selection shape; selection behavior is passed in.", + "from": "site/navigation/navigation-request.mts", + "change": "Extract navigation contracts", + "id": "add-import-navigation-request-navigation-types" + }, + { + "op": "add-file", + "path": "site/content/moon-catalogue.mts", + "note": "Split catalogue parsing, cached source catalogue and catalogueMoons into a runtime-safe reader; preparation keeps search-based ordering.", + "change": "Split the moon catalogue reader", + "imports": [ + { + "to": "site/source/moon-catalogues.json", + "kind": "asset" + }, + { + "to": "packages/objects/src/sources/index.ts", + "kind": "value" + } + ], + "id": "add-file-moon-catalogue" + }, + { + "op": "retarget", + "from": "site/content/object-children.mts", + "to": "site/build/prepare/prepare-body-moons.mts", + "note": "Read catalogueMoons from the split reader; production must never import build preparation.", + "newTo": "site/content/moon-catalogue.mts", + "change": "Split the moon catalogue reader", + "id": "retarget-object-children-moon-catalogue", + "line": 3 + }, + { + "op": "remove-import", + "from": "site/build/prepare/prepare-body-moons.mts", + "to": "site/source/moon-catalogues.json", + "note": "Move the source catalogue import with its parser into the reader; preparation calls that reader.", + "kind": "asset", + "change": "Split the moon catalogue reader", + "id": "remove-import-prepare-body-moons-moon-catalogues", + "line": 4 + }, + { + "op": "add-import", + "from": "site/build/prepare/prepare-body-moons.mts", + "to": "site/content/moon-catalogue.mts", + "kind": "value", + "note": "Use the single catalogue reader for preparation too; preserve the named-moon eligibility policy in preparation.", + "change": "Split the moon catalogue reader", + "id": "add-import-prepare-body-moons-moon-catalogue" + }, + { + "op": "remove-import", + "from": "site/navigation/navigation-request.mts", + "to": "site/selection/scene-selection.mts", + "note": "Pass selectionTargetFromUrl explicitly through resolveNavigation and readNavigationSelection. Router uses the existing registry export, injects it into createSceneActivation, which forwards all three calls. Import erased SceneView/SelectionTarget aliases from navigation-types; never retain the selection import.", + "change": "Pass selection into navigation requests", + "id": "remove-import-navigation-request-scene-selection", + "line": 11 + }, + { + "op": "add-import", + "to": "site/world/scene-subject.mts", + "kind": "value", + "note": "Keep existing subject identity helpers in the lower world owner; pass selectionTargetFromUrl as a parameter.", + "from": "site/navigation/navigation-request.mts", + "change": "Pass selection into navigation requests", + "id": "add-import-navigation-request-scene-subject" + }, + { + "op": "add-file", + "path": "site/scene/scene-frame-presenter.mts", + "note": "Extract the structural presenter interface; a session must not import the world coordinator that consumes it.", + "change": "Extract the frame presenter contract", + "imports": [ + { + "to": "site/world/application-world-context.mts", + "kind": "type" + }, + { + "to": "site/world/world-viewport.mts", + "kind": "type" + } + ], + "id": "add-file-scene-frame-presenter" + }, + { + "op": "retarget", + "from": "site/scene/scene-session.mts", + "to": "site/scene/scene-world.mts", + "note": "Session reads SceneFramePresenter from the lower contract, breaking the two-way type dependency.", + "kind": "type", + "newTo": "site/scene/scene-frame-presenter.mts", + "change": "Extract the frame presenter contract", + "id": "retarget-scene-session-scene-frame-presenter", + "line": 2 + }, + { + "op": "add-import", + "to": "site/scene/scene-frame-presenter.mts", + "kind": "type", + "note": "World coordinator imports and re-exports the structural presenter contract.", + "from": "site/scene/scene-world.mts", + "change": "Extract the frame presenter contract", + "id": "add-import-scene-world-scene-frame-presenter" + }, + { + "id": "add-file-scene-imports", + "op": "add-file", + "change": "Relocate queued loader owners", + "path": "site/scene/scene-imports.mts", + "imports": [ + { + "to": "site/browser/import-queue.mts", + "kind": "value" + }, + { + "to": "site/scene/scene-registry.mts", + "kind": "lazy" + }, + { + "to": "site/scene/packaged-object-runtime.mts", + "kind": "lazy" + } + ], + "note": "Decided by the owner: loader modules hold only import() calls through the existing queue. Own registry and packaged-runtime functions in scene; move the single last chain to lower browser/import-queue. Prove one queue chain, registration before the first scene call, the same entry-chunk modules and bytes, identical L1/L2/L3, L7 no worse and the real-iPad startup journey." + }, + { + "id": "add-file-world-imports", + "op": "add-file", + "change": "Relocate queued loader owners", + "path": "site/world/world-imports.mts", + "imports": [ + { + "to": "site/browser/import-queue.mts", + "kind": "value" + }, + { + "to": "site/world/application-world-context.mts", + "kind": "lazy" + } + ], + "note": "Own the queued application-world import in world; no initializer inside the thunk." + }, + { + "id": "remove-queue-runtime", + "op": "remove-import", + "change": "Relocate queued loader owners", + "to": "site/scene/packaged-object-runtime.mts", + "kind": "lazy", + "line": 26, + "note": "Move the runtime function out of the import-free browser queue implementation.", + "from": "site/browser/import-queue.mts" + }, + { + "id": "remove-shared-registry", + "op": "remove-import", + "change": "Relocate queued loader owners", + "to": "site/scene/scene-registry.mts", + "kind": "lazy", + "line": 5, + "note": "Leave only importSceneRouter in startup/shared-imports.", + "from": "site/startup/shared-imports.mts" + }, + { + "id": "remove-shared-world", + "op": "remove-import", + "change": "Relocate queued loader owners", + "to": "site/world/application-world-context.mts", + "kind": "lazy", + "line": 6, + "note": "Move the world function to its owner.", + "from": "site/startup/shared-imports.mts" + }, + { + "id": "retarget-adapter-scene-imports", + "op": "retarget", + "change": "Relocate queued loader owners", + "to": "site/browser/import-queue.mts", + "newTo": "site/scene/scene-imports.mts", + "line": 1, + "note": "Adapter directly uses the same queued runtime function; preserve the frozen default adapter.", + "from": "site/scene/object-adapter.mts" + }, + { + "id": "retarget-router-world-imports", + "op": "retarget", + "change": "Relocate queued loader owners", + "to": "site/startup/shared-imports.mts", + "newTo": "site/world/world-imports.mts", + "line": 2, + "note": "Split router loader imports; preserve module-evaluation autostart.", + "from": "site/scene/scene-router.mts" + }, + { + "id": "add-router-scene-imports", + "op": "add-import", + "change": "Relocate queued loader owners", + "to": "site/scene/scene-imports.mts", + "kind": "value", + "note": "Import registry/runtime functions and register only the directory runtime loader using the existing object-directory import before first scene call.", + "from": "site/scene/scene-router.mts" + }, + { + "id": "remove-directory-queue", + "op": "remove-import", + "change": "Relocate queued loader owners", + "to": "site/browser/import-queue.mts", + "line": 1, + "note": "Directory accepts a typed runtime-loader registration; read the slot at scene invocation, not metadata construction. Missing registration fails explicitly.", + "from": "site/directory/object-directory.mts" + }, + { + "id": "retarget-boot-scene-imports", + "op": "retarget", + "change": "Relocate queued loader owners", + "to": "site/startup/shared-imports.mts", + "newTo": "site/scene/scene-imports.mts", + "line": 1, + "note": "Body prestart and registry prestart reuse scene-owned promises.", + "from": "site/startup/startup-boot.mts" + }, + { + "id": "add-boot-world-imports", + "op": "add-import", + "change": "Relocate queued loader owners", + "to": "site/world/world-imports.mts", + "kind": "value", + "note": "View prestart reuses the world-owned promise.", + "from": "site/startup/startup-boot.mts" + } + ], + "changes": [ + { + "id": "extract-held-address-reader", + "edits": [ + "add-file-navigation-href", + "add-import-navigation-history-navigation-href", + "retarget-application-world-resources-navigation-href" + ] + }, + { + "id": "extract-registry-types", + "edits": [ + "add-file-object-entry-types", + "retarget-object-directory-object-entry-types", + "add-import-objects-object-entry-types" + ] + }, + { + "id": "extract-navigation-contracts", + "edits": [ + "add-file-navigation-types", + "retarget-prepared-arrival-navigation-types", + "retarget-navigation-history-navigation-types", + "add-import-prepared-world-navigation-navigation-types", + "add-import-navigation-request-navigation-types" + ] + }, + { + "id": "split-the-moon-catalogue-reader", + "edits": [ + "add-file-moon-catalogue", + "retarget-object-children-moon-catalogue", + "remove-import-prepare-body-moons-moon-catalogues", + "add-import-prepare-body-moons-moon-catalogue" + ] + }, + { + "id": "pass-selection-into-navigation-requests", + "edits": [ + "remove-import-navigation-request-scene-selection", + "add-import-navigation-request-scene-subject" + ] + }, + { + "id": "extract-the-frame-presenter-contract", + "edits": [ + "add-file-scene-frame-presenter", + "retarget-scene-session-scene-frame-presenter", + "add-import-scene-world-scene-frame-presenter" + ] + }, + { + "id": "relocate-queued-loader-owners", + "edits": [ + "add-file-scene-imports", + "add-file-world-imports", + "remove-queue-runtime", + "remove-shared-registry", + "remove-shared-world", + "retarget-adapter-scene-imports", + "retarget-router-world-imports", + "add-router-scene-imports", + "remove-directory-queue", + "retarget-boot-scene-imports", + "add-boot-world-imports" + ] + } + ] +} diff --git a/docs/site-architecture/moves.json b/docs/site-architecture/moves.json new file mode 100644 index 00000000000..7ac450013a6 --- /dev/null +++ b/docs/site-architecture/moves.json @@ -0,0 +1,310 @@ +{ + "site/runtime-policy.mts": "site/browser/runtime-policy.mts", + "site/diagnostics-policy.mts": "site/browser/diagnostics-policy.mts", + "site/in-flight-loader.mts": "site/browser/in-flight-loader.mts", + "site/next-frame.mts": "site/browser/next-frame.mts", + "site/narrow-layout.mts": "site/browser/narrow-layout.mts", + "site/object-shell-types.ts": "site/contracts/object-shell-types.ts", + "site/object-page-contract.mts": "site/contracts/object-page-contract.mts", + "site/navigation/system-address.mts": "site/model/system-address.mts", + "site/root-object.mts": "site/model/root-object.mts", + "site/orbit-root.mts": "site/model/orbit-root.mts", + "site/planetary-system-members.mts": "site/model/planetary-system-members.mts", + "site/object-classification-label.mts": "site/model/object-classification-label.mts", + "site/dataset-url.mts": "site/model/dataset-url.mts", + "site/startup-world.mts": "site/directory/startup-world.mts", + "site/startup-requests.mts": "site/directory/startup-requests.mts", + "site/object-entries.mts": "site/directory/object-entries.mts", + "site/object-directory.mts": "site/directory/object-directory.mts", + "site/objects.mts": "site/directory/objects.mts", + "site/world-context-plan.mts": "site/directory/world-context-plan.mts", + "site/world-objects.mts": "site/world/world-objects.mts", + "site/world-system-views.mts": "site/world/world-system-views.mts", + "site/context-availability.mts": "site/world/context-availability.mts", + "site/context-datasets.mts": "site/world/context-datasets.mts", + "site/stellar-extents.mts": "site/world/stellar-extents.mts", + "site/hosted-banks.mts": "site/world/hosted-banks.mts", + "site/dot-catalogues.mts": "site/world/dot-catalogues.mts", + "site/prepared-world-presentation.mts": "site/world/prepared-world-presentation.mts", + "site/application-world-context.mts": "site/world/application-world-context.mts", + "site/application-world-frames.mts": "site/world/application-world-frames.mts", + "site/application-world-resources.mts": "site/world/application-world-resources.mts", + "site/application-world-types.mts": "site/world/application-world-types.mts", + "site/application-world-visibility.mts": "site/world/application-world-visibility.mts", + "site/world-approach.mts": "site/world/world-approach.mts", + "site/world-camera.mts": "site/world/world-camera.mts", + "site/world-viewport.mts": "site/world/world-viewport.mts", + "site/world-preferences.mts": "site/world/world-preferences.mts", + "site/moon-orbit-policy.mts": "site/world/moon-orbit-policy.mts", + "site/catalogue-moon-labels.mts": "site/world/catalogue-moon-labels.mts", + "site/system-framing.mts": "site/world/system-framing.mts", + "site/system-framing-radii.mts": "site/world/system-framing-radii.mts", + "site/zoom-scope.mts": "site/world/zoom-scope.mts", + "site/zoom-carry.mts": "site/world/zoom-carry.mts", + "site/inside-view.mts": "site/world/inside-view.mts", + "site/object-systems.mts": "site/world/object-systems.mts", + "site/satellite-systems.mts": "site/world/satellite-systems.mts", + "site/scene/scene-subject.mts": "site/world/scene-subject.mts", + "site/navigation/navigation-scope.mts": "site/world/navigation-scope.mts", + "site/navigation/navigation-history.mts": "site/navigation/navigation-history.mts", + "site/navigation/navigation-fragments.mts": "site/navigation/navigation-fragments.mts", + "site/scene/scene-selection.mts": "site/selection/scene-selection.mts", + "site/satellite-selection.mts": "site/selection/satellite-selection.mts", + "site/overview-selection.mts": "site/selection/overview-selection.mts", + "site/showcase.mts": "site/selection/showcase.mts", + "site/object-adapter.mts": "site/scene/object-adapter.mts", + "site/packaged-object-runtime.mts": "site/scene/packaged-object-runtime.mts", + "site/startup-billboard.mts": "site/scene/startup-billboard.mts", + "site/initial-scene.mts": "site/scene/initial-scene.mts", + "site/prepared-arrival.mts": "site/navigation/prepared-arrival.mts", + "site/prepared-scene-ownership.mts": "site/navigation/prepared-scene-ownership.mts", + "site/arrival-billboard.mts": "site/navigation/arrival-billboard.mts", + "site/prepared-world-navigation.mts": "site/navigation/prepared-world-navigation.mts", + "site/view-url-runtime.mts": "site/navigation/view-url-runtime.mts", + "site/prepared-descriptor.mts": "site/navigation/prepared-descriptor.mts", + "site/import-queue.mts": "site/browser/import-queue.mts", + "site/shared-imports.mts": "site/startup/shared-imports.mts", + "site/startup-boot.mts": "site/startup/startup-boot.mts", + "site/object-browser.mts": "site/shell/object-browser.mts", + "site/feature-browser.mts": "site/shell/feature-browser.mts", + "site/destination-browser.mts": "site/shell/destination-browser.mts", + "site/selection-presentation.mts": "site/shell/selection-presentation.mts", + "site/information-card.mts": "site/shell/information-card.mts", + "site/chart-pixel-alignment.mts": "site/shell/chart-pixel-alignment.mts", + "site/dataset-picker.mts": "site/shell/dataset-picker.mts", + "site/tab-panels.mts": "site/shell/tab-panels.mts", + "site/layout-sections.mts": "site/shell/layout-sections.mts", + "site/view-readout.mts": "site/shell/view-readout.mts", + "site/native-view-forms.mts": "site/shell/native-view-forms.mts", + "site/source-link.mts": "site/browser/source-link.mts", + "site/dataset-content.mts": "site/content/dataset-content.mts", + "site/dataset-context.mts": "site/content/dataset-context.mts", + "site/object-text.mts": "site/content/object-text.mts", + "site/prepared-panel-content.mts": "site/content/prepared-panel-content.mts", + "site/volume-presentation.mts": "site/content/volume-presentation.mts", + "site/exploration-catalog.mts": "site/content/exploration-catalog.mts", + "site/source-documentation.mts": "site/content/source-documentation.mts", + "site/source-icons.mts": "site/content/source-icons.mts", + "site/sidebar-thumbnails.mts": "site/content/sidebar-thumbnails.mts", + "site/seo.mts": "site/content/seo.mts", + "site/seo-trail.mts": "site/content/seo-trail.mts", + "site/system-card.mts": "site/content/system-card.mts", + "site/object-children.mts": "site/content/object-children.mts", + "site/learn-more.mts": "site/content/learn-more.mts", + "site/native-input.mts": "site/browser/native-input.mts", + "site/startup-cover.mts": "site/startup/startup-cover.mts", + "site/initial-shell-context.mts": "site/startup/initial-shell-context.mts", + "site/error-report.mts": "site/startup/error-report.mts", + "site/web-analytics.mts": "site/startup/web-analytics.mts", + "site/asset-origin.mts": "site/server-assets/asset-origin.mts", + "site/object-page-data.mts": "site/server/object-page-data.mts", + "site/first-view-transport.mts": "site/server/first-view-transport.mts", + "site/object-entry.mts": "site/server/object-entry.mts", + "site/world-places.mts": "site/server/world-places.mts", + "site/dot-catalogue-data.mts": "site/server/dot-catalogue-data.mts", + "site/dataset-response.mts": "site/server/dataset-response.mts", + "site/built-pages.mts": "site/server/built-pages.mts", + "site/prepare-body-moons.mts": "site/build/prepare/prepare-body-moons.mts", + "site/components/ObjectSwatchStyles.astro": "site/layouts/ObjectSwatchStyles.astro", + "site/default-width-share.mts": "site/world/default-width-share.mts", + "site/diagnostic-recorder.mts": "site/shell/diagnostic-recorder.mts", + "site/env.d.ts": "site/env.d.ts", + "site/prepared-catalogue.d.mts": "site/prepared/prepared-catalogue.d.mts", + "site/prepared-navigation-markers.d.mts": "site/prepared/prepared-navigation-markers.d.mts", + "site/prepared-object-path.mts": "site/prepared/prepared-object-path.mts", + "site/prepared-shell-icons.d.mts": "site/prepared/prepared-shell-icons.d.mts", + "site/prepared-shell-titles.d.mts": "site/prepared/prepared-shell-titles.d.mts", + "site/social-images.mts": "site/server/social-images.mts", + "site/build/source-maps.test.mts": "site/build/source-maps.test.mts", + "site/navigation/evidence/star-click/one-click.jpg": "site/navigation/evidence/star-click/one-click.jpg", + "site/prepared-panel-content.shared-readers.test.mts": "site/content/prepared-panel-content.shared-readers.test.mts", + "site/test/application-world-frames.test.mts": "site/world/application-world-frames.test.mts", + "site/test/application-world-visibility.test.mts": "site/world/application-world-visibility.test.mts", + "site/test/arrival-billboard.test.mts": "site/navigation/arrival-billboard.test.mts", + "site/test/arrival-discovery.test.mts": "site/build/prepare/arrival-discovery.test.mts", + "site/test/asset-origin.test.mts": "site/server/asset-origin.test.mts", + "site/test/astro-component-imports.test.mts": "site/journeys/astro-component-imports.test.mts", + "site/test/authored-object-fixture.mts": "site/build/fixtures/authored-object-fixture.mts", + "site/test/body-additions.test.mts": "site/build/prepare/body-additions.test.mts", + "site/test/body-moons.test.mts": "site/build/prepare/body-moons.test.mts", + "site/test/browser-node-imports.test.mts": "site/journeys/browser-node-imports.test.mts", + "site/test/built-pages.test.mts": "site/server/built-pages.test.mts", + "site/test/camera-handover.test.mts": "site/scene/camera-handover.test.mts", + "site/test/catalogue-window.test.mts": "site/search/catalogue-window.test.mts", + "site/test/category-frames.test.mts": "site/build/prepare/category-frames.test.mts", + "site/test/chart-pixel-alignment.test.mts": "site/shell/chart-pixel-alignment.test.mts", + "site/test/charts.test.ts": "site/build/charts/charts.test.mts", + "site/test/check-preparation-inputs.test.mts": "site/build/prepare/check-preparation-inputs.test.mts", + "site/test/cloudflare-worker.test.mts": "site/journeys/cloudflare-worker.test.mts", + "site/test/compact-spectrum.test.mts": "site/overview/compact-spectrum.test.mts", + "site/test/context-lineage.test.mts": "site/journeys/context-lineage.test.mts", + "site/test/dataset-picker.test.mts": "site/shell/dataset-picker.test.mts", + "site/test/dataset-response.test.mts": "site/server/dataset-response.test.mts", + "site/test/dataset-url.test.mts": "site/model/dataset-url.test.mts", + "site/test/descriptor-roundtrip.test.mts": "site/build/prepare/descriptor-roundtrip.test.mts", + "site/test/destination-browser.test.mts": "site/shell/destination-browser.test.mts", + "site/test/diagnostic-accessor.test.mts": "site/journeys/diagnostic-accessor.test.mts", + "site/test/diagnostic-recorder.test.mts": "site/shell/diagnostic-recorder.test.mts", + "site/test/error-report.test.mts": "site/browser/error-report.test.mts", + "site/test/evidence/passive-minimaps/browser.json": "site/minimap/evidence/passive-minimaps/browser.json", + "site/test/evidence/passive-minimaps/earth.png": "site/minimap/evidence/passive-minimaps/earth.png", + "site/test/evidence/passive-minimaps/jupiter-sequence.png": "site/minimap/evidence/passive-minimaps/jupiter-sequence.png", + "site/test/first-view-transport.test.mts": "site/server/first-view-transport.test.mts", + "site/test/fixtures/anchor-table.mts": "site/world/fixtures/anchor-table.mts", + "site/test/fixtures/context-package.mts": "site/build/fixtures/context-package.mts", + "site/test/fixtures/galaxies/captures.json": "site/world/fixtures/galaxies/captures.json", + "site/test/fixtures/object-content-fixture.mts": "site/build/fixtures/object-content-fixture.mts", + "site/test/fixtures/objects.mts": "site/directory/fixtures/objects.mts", + "site/test/fixtures/prepared-schemas.mts": "site/world/fixtures/prepared-schemas.mts", + "site/test/fixtures/saturn-prepared.mts": "site/world/fixtures/saturn-prepared.mts", + "site/test/folded-transit.test.mts": "site/build/charts/folded-transit.test.mts", + "site/test/galactic-focus.test.mts": "site/search/galactic-focus.test.mts", + "site/test/hosted-banks.test.mts": "site/build/prepare/hosted-banks.test.mts", + "site/test/import-queue.test.mts": "site/startup/import-queue.test.mts", + "site/test/in-flight-loader.test.mts": "site/browser/in-flight-loader.test.mts", + "site/test/information-card-sections.test.mts": "site/shell/information-card-sections.test.mts", + "site/test/information-tab-styles.test.mts": "site/server/information-tab-styles.test.mts", + "site/test/initial-shell-context.test.mts": "site/browser/initial-shell-context.test.mts", + "site/test/inline-page-stylesheet.test.mts": "site/build/inline-page-stylesheet.test.mts", + "site/test/kept-load.test.mts": "site/server/kept-load.test.mts", + "site/test/list-distance.test.mts": "site/search/list-distance.test.mts", + "site/test/load-object-content.mts": "site/build/content/load-object-content.test-support.mts", + "site/test/lonlat-slice-table.test.mts": "site/build/prepare/lonlat-slice-table.test.mts", + "site/test/moon-labels.test.mts": "site/world/moon-labels.test.mts", + "site/test/navigation-content.test.mts": "site/navigation/navigation-content.test.mts", + "site/test/navigation-fragments.test.mts": "site/navigation/navigation-fragments.test.mts", + "site/test/navigation-history.test.mts": "site/navigation/navigation-history.test.mts", + "site/test/navigation-lifecycle.test.mts": "site/navigation/navigation-lifecycle.test.mts", + "site/test/navigation-ontology.test.mts": "site/world/navigation-ontology.test.mts", + "site/test/navigation-preparation.test.mts": "site/world/navigation-preparation.test.mts", + "site/test/navigation-readiness.test.mts": "site/navigation/navigation-readiness.test.mts", + "site/test/navigation-request.test.mts": "site/navigation/navigation-request.test.mts", + "site/test/navigation-router-context.mts": "site/scene/navigation-router-context.test-support.mts", + "site/test/navigation-scope.test.mts": "site/world/navigation-scope.test.mts", + "site/test/navigation-shell-content.test.mts": "site/navigation/navigation-shell-content.test.mts", + "site/test/navigation-styles.test.mts": "site/navigation/navigation-styles.test.mts", + "site/test/navigation-test-values.mts": "site/navigation/navigation-test-values.test-support.mts", + "site/test/navigation-timing.test.mts": "site/navigation/navigation-timing.test.mts", + "site/test/nebula-search.test.mts": "site/world/nebula-search.test.mts", + "site/test/neutral-catalogue-color.test.mts": "site/journeys/neutral-catalogue-color.test.mts", + "site/test/object-children.test.mts": "site/content/object-children.test.mts", + "site/test/object-control-binding.test.mts": "site/journeys/object-control-binding.test.mts", + "site/test/object-directory.test.mts": "site/directory/object-directory.test.mts", + "site/test/object-navigation.test.mts": "site/world/object-navigation.test.mts", + "site/test/object-package-contract.test.mts": "site/build/object-package-contract.test.mts", + "site/test/object-page-data.test.mts": "site/server/object-page-data.test.mts", + "site/test/object-result.test.mts": "site/search/object-result.test.mts", + "site/test/object-runtime-contract.test.mts": "site/journeys/object-runtime-contract.test.mts", + "site/test/object-search-rank.test.mts": "site/search/object-search-rank.test.mts", + "site/test/object-systems.test.mts": "site/world/object-systems.test.mts", + "site/test/object-text.test.mts": "site/content/object-text.test.mts", + "site/test/overview-selection.test.mts": "site/selection/overview-selection.test.mts", + "site/test/preparation-boundary.test.mts": "site/journeys/preparation-boundary.test.mts", + "site/test/prepare-authored.test.mts": "site/build/prepare/prepare-authored.test.mts", + "site/test/prepare-context-availability.test.mts": "site/build/prepare/prepare-context-availability.test.mts", + "site/test/prepare-object-json.test.mts": "site/build/prepare/prepare-object-json.test.mts", + "site/test/prepare-spatial-context.test.mts": "site/build/prepare/prepare-spatial-context.test.mts", + "site/test/prepare-text.test.mts": "site/build/prepare/prepare-text.test.mts", + "site/test/prepared-arrival.test.mts": "site/navigation/prepared-arrival.test.mts", + "site/test/prepared-body-billboards.test.mts": "site/world/prepared-body-billboards.test.mts", + "site/test/prepared-dataset-facts.test.mts": "site/build/content/prepared-dataset-facts.test.mts", + "site/test/prepared-dataset-labels.test.mts": "site/journeys/prepared-dataset-labels.test.mts", + "site/test/prepared-dataset-legends.test.mts": "site/build/content/prepared-dataset-legends.test.mts", + "site/test/prepared-descriptor.test.mts": "site/navigation/prepared-descriptor.test.mts", + "site/test/prepared-icons.test.mts": "site/build/prepare/prepared-icons.test.mts", + "site/test/prepared-motion-css.test.mts": "site/build/prepared-motion-css.test.mts", + "site/test/prepared-panel-content.test.mts": "site/content/prepared-panel-content.test.mts", + "site/test/prepared-presentation-bindings.test.mts": "site/contracts/prepared-presentation-bindings.test.mts", + "site/test/prepared-presentation-contract.test.mts": "site/journeys/prepared-presentation-contract.test.mts", + "site/test/prepared-world-context.test.ts": "site/world/prepared-world-context.test.ts", + "site/test/prepared-world-navigation.test.mts": "site/navigation/prepared-world-navigation.test.mts", + "site/test/preview.test.mts": "site/server/preview.test.mts", + "site/test/production-diagnostics.test.mts": "site/journeys/production-diagnostics.test.mts", + "site/test/recovery-commands.test.mts": "site/journeys/recovery-commands.test.mts", + "site/test/refresh-charts.test.mts": "site/build/prepare/refresh-charts.test.mts", + "site/test/rendered-page.test.mts": "site/journeys/rendered-page.test.mts", + "site/test/restore-inputs.test.mts": "site/build/restore-inputs.test.mts", + "site/test/run-script-chain.test.mts": "site/build/run-script-chain.test.mts", + "site/test/runtime-package.test.mts": "site/world/runtime-package.test.mts", + "site/test/runtime-policy.test.mts": "site/browser/runtime-policy.test.mts", + "site/test/satellite-arrival-framing.test.mts": "site/navigation/satellite-arrival-framing.test.mts", + "site/test/satellite-systems.test.mts": "site/world/satellite-systems.test.mts", + "site/test/scene-contract.test.mts": "site/world/scene-contract.test.mts", + "site/test/scene-datasets.test.mts": "site/scene/scene-datasets.test.mts", + "site/test/scene-frame.test.mts": "site/scene/scene-frame.test.mts", + "site/test/scene-publication.test.mts": "site/scene/scene-publication.test.mts", + "site/test/scene-session.test.mts": "site/scene/scene-session.test.mts", + "site/test/search-api.test.mts": "site/journeys/search-api.test.mts", + "site/test/search-response.test.mts": "site/server/search-response.test.mts", + "site/test/seo-discovery.mts": "site/journeys/seo-discovery.test-support.mts", + "site/test/seo-discovery.test.mts": "site/journeys/seo-discovery.test.mts", + "site/test/seo.test.mts": "site/content/seo.test.mts", + "site/test/setup-assets.test.mts": "site/build/prepare/setup-assets.test.mts", + "site/test/shell-selectors.test.mts": "site/search/shell-selectors.test.mts", + "site/test/shell-settings.test.mts": "site/shell/shell-settings.test.mts", + "site/test/shell-sheet.test.mts": "site/shell/shell-sheet.test.mts", + "site/test/showcase.test.mts": "site/selection/showcase.test.mts", + "site/test/source-evidence-values.mts": "site/overview/source-evidence-values.test-support.mts", + "site/test/source-link.test.mts": "site/browser/source-link.test.mts", + "site/test/startup-billboard.test.mts": "site/scene/startup-billboard.test.mts", + "site/test/startup-cover.test.mts": "site/browser/startup-cover.test.mts", + "site/test/startup-requests.test.mts": "site/directory/startup-requests.test.mts", + "site/test/stellar-extents.test.mts": "site/world/stellar-extents.test.mts", + "site/test/surface-label-loop.test.mts": "site/journeys/surface-label-loop.test.mts", + "site/test/surface-minimap.test.mts": "site/minimap/surface-minimap.test.mts", + "site/test/system-framing.test.mts": "site/world/system-framing.test.mts", + "site/test/system-text.test.mts": "site/build/prepare/system-text.test.mts", + "site/test/system-view-file.mts": "site/world/system-view-file.test-support.mts", + "site/test/view-readout.test.mts": "site/shell/view-readout.test.mts", + "site/test/view-url-runtime.test.mts": "site/navigation/view-url-runtime.test.mts", + "site/test/volume-presentation.test.mts": "site/content/volume-presentation.test.mts", + "site/test/web-analytics.test.mts": "site/browser/web-analytics.test.mts", + "site/test/world-billboards.test.mts": "site/journeys/world-billboards.test.mts", + "site/test/world-stars.test.mts": "site/world/world-stars.test.mts", + "site/test/world-tables.test.mts": "site/world/world-tables.test.mts", + "site/test/zoom-scope.test.mts": "site/world/zoom-scope.test.mts", + "site/moon-labels.prepared.json": "site/prepared/moon-labels.prepared.json", + "site/prepared-catalogue.mjs": "site/prepared/prepared-catalogue.mjs", + "site/prepared-context-bank-assets.json": "site/prepared/prepared-context-bank-assets.json", + "site/prepared-context-json.mts": "site/prepared/prepared-context-json.mts", + "site/prepared-context-objects.mts": "site/prepared/prepared-context-objects.mts", + "site/prepared-dataset-billboards.json": "site/prepared/prepared-dataset-billboards.json", + "site/prepared-dataset-volumes.json": "site/prepared/prepared-dataset-volumes.json", + "site/prepared-dot-catalogues.json": "site/prepared/prepared-dot-catalogues.json", + "site/prepared-facilities.json": "site/prepared/prepared-facilities.json", + "site/prepared-feature-index.json": "site/prepared/prepared-feature-index.json", + "site/prepared-fit-boxes.json": "site/prepared/prepared-fit-boxes.json", + "site/prepared-hosted-banks.json": "site/prepared/prepared-hosted-banks.json", + "site/prepared-navigation-markers.mjs": "site/prepared/prepared-navigation-markers.mjs", + "site/prepared-shell-icons.mjs": "site/prepared/prepared-shell-icons.mjs", + "site/prepared-shell-titles.mjs": "site/prepared/prepared-shell-titles.mjs", + "site/prepared-source-credits.json": "site/prepared/prepared-source-credits.json", + "site/prepared-sources.json": "site/prepared/prepared-sources.json", + "site/prepared-stellar-extents.json": "site/prepared/prepared-stellar-extents.json", + "site/prepared-world-presentation.json": "site/prepared/prepared-world-presentation.json", + "site/site.css": "site/layouts/site.css", + "site/object-shell.css": "site/layouts/object-shell.css", + "site/wordmark.css": "site/layouts/wordmark.css", + "site/object-navigation-marker.css": "site/layouts/object-navigation-marker.css", + "site/scientific-chart-order.mts": null, + "site/test/scientific-chart-order.test.mts": null, + "site/asset-origin.characterization.test.mts": "site/server-assets/asset-origin.characterization.test.mts", + "site/dataset-picker.characterization.test.mts": "site/shell/dataset-picker.characterization.test.mts", + "site/dataset-response.characterization.test.mts": "site/server/dataset-response.characterization.test.mts", + "site/destination-browser.characterization.test.mts": "site/shell/destination-browser.characterization.test.mts", + "site/diagnostic-recorder.characterization.test.mts": "site/shell/diagnostic-recorder.characterization.test.mts", + "site/feature-browser.characterization.test.mts": "site/shell/feature-browser.characterization.test.mts", + "site/object-adapter.characterization.test.mts": "site/scene/object-adapter.characterization.test.mts", + "site/object-browser.characterization.test.mts": "site/shell/object-browser.characterization.test.mts", + "site/object-children.characterization.test.mts": "site/content/object-children.characterization.test.mts", + "site/object-directory.characterization.test.mts": "site/directory/object-directory.characterization.test.mts", + "site/object-page-data.characterization.test.mts": "site/server/object-page-data.characterization.test.mts", + "site/packaged-object-runtime.characterization.test.mts": "site/scene/packaged-object-runtime.characterization.test.mts", + "site/prepare-body-moons.characterization.test.mts": "site/build/prepare/prepare-body-moons.characterization.test.mts", + "site/startup-billboard.characterization.test.mts": "site/scene/startup-billboard.characterization.test.mts", + "site/startup-boot.characterization.test.mts": "site/startup/startup-boot.characterization.test.mts", + "site/startup-requests.characterization.test.mts": "site/directory/startup-requests.characterization.test.mts", + "site/startup-world.characterization.test.mts": "site/directory/startup-world.characterization.test.mts", + "site/view-url-runtime.characterization.test.mts": "site/navigation/view-url-runtime.characterization.test.mts", + "site/test/fixtures/characterization-runtime.mts": "site/scene/fixtures/characterization-runtime.mts" +} diff --git a/docs/site-architecture/references.json b/docs/site-architecture/references.json new file mode 100644 index 00000000000..7fd581e74c2 --- /dev/null +++ b/docs/site-architecture/references.json @@ -0,0 +1,2326 @@ +{ + "occurrences": [ + { + "file": ".agents/skills/celestial-skill/references/implementation-map.md", + "classification": "docs code-span", + "scope": "live" + }, + { + "file": ".github/ci-areas.json", + "classification": "CI routing", + "scope": "live" + }, + { + "file": ".github/coverage-ratchet.json", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": ".github/coverage-ratchet.json", + "classification": "source import", + "scope": "live" + }, + { + "file": ".github/scripts/architecture/architecture.test.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": ".github/scripts/architecture/format-schema-exceptions.json", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": ".github/scripts/architecture/format-schema-ownership.test.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": ".github/scripts/architecture/graph.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": ".github/scripts/architecture/moved-format-ownership.test.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": ".github/scripts/architecture/nebula-inbound.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": ".github/scripts/architecture/nebula-inbound.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": ".github/scripts/architecture/nebula-inbound.test.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": ".github/scripts/audits/provenance-documentation-inventory.py", + "classification": "Python script", + "scope": "live" + }, + { + "file": ".github/scripts/audits/test_provenance_documentation_inventory.py", + "classification": "Python script", + "scope": "live" + }, + { + "file": ".github/scripts/build-compare/apply-moves.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": ".github/scripts/build-compare/apply-moves.test.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": ".github/scripts/build-compare/compare.test.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": ".github/scripts/checks/object-test-relocation.test.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": ".github/scripts/checks/package-boundaries.test.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": ".github/scripts/checks/typescript-ownership.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": ".github/scripts/ci/astroquery-tests.test.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": ".github/scripts/ci/check-ci.test.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": ".github/scripts/ci/classify-changes.test.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": ".github/scripts/ci/object-scope-gate.test.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": ".github/scripts/ci/prepare-typecheck.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": ".github/scripts/coverage/ratchet.test.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": ".github/scripts/coverage/report.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": ".github/scripts/tsconfig.tests.json", + "classification": "tsconfig/eslint/package.json", + "scope": "live" + }, + { + "file": ".github/workflows/audit.yml", + "classification": "workflow", + "scope": "live" + }, + { + "file": ".github/workflows/nightly.yml", + "classification": "workflow", + "scope": "live" + }, + { + "file": ".github/workflows/universe.yml", + "classification": "workflow", + "scope": "live" + }, + { + "file": ".gitignore", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "AGENTS.md", + "classification": "AGENTS.md contract text", + "scope": "live" + }, + { + "file": "CONTRIBUTING.md", + "classification": "docs link", + "scope": "live" + }, + { + "file": "astro.config.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "cloudflare/worker.ts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "docs/architecture/exploration-catalog.md", + "classification": "docs link", + "scope": "live" + }, + { + "file": "docs/flight-lifecycle.md", + "classification": "docs code-span", + "scope": "live" + }, + { + "file": "docs/galaxies/README.md", + "classification": "docs link", + "scope": "live" + }, + { + "file": "docs/moon-catalogues.md", + "classification": "docs code-span", + "scope": "live" + }, + { + "file": "docs/native-scroll-zoom.md", + "classification": "docs code-span", + "scope": "live" + }, + { + "file": "docs/navigation-identity.md", + "classification": "docs link", + "scope": "live" + }, + { + "file": "docs/navigation-identity.md", + "classification": "docs code-span", + "scope": "live" + }, + { + "file": "docs/object-discovery.md", + "classification": "docs code-span", + "scope": "live" + }, + { + "file": "docs/object-provenance.md", + "classification": "docs code-span", + "scope": "live" + }, + { + "file": "docs/page-navigation-transport.md", + "classification": "docs code-span", + "scope": "live" + }, + { + "file": "docs/performance/marker-declutter.md", + "classification": "docs code-span", + "scope": "live" + }, + { + "file": "docs/performance/startup-billboard.md", + "classification": "docs code-span", + "scope": "live" + }, + { + "file": "docs/performance/startup-gate.md", + "classification": "docs link", + "scope": "live" + }, + { + "file": "docs/performance/world-context-by-system.md", + "classification": "docs link", + "scope": "live" + }, + { + "file": "docs/prepared-navigation-ownership.md", + "classification": "docs code-span", + "scope": "live" + }, + { + "file": "docs/provenance/CONTRACT.md", + "classification": "docs code-span", + "scope": "live" + }, + { + "file": "docs/reader-text.md", + "classification": "docs link", + "scope": "live" + }, + { + "file": "docs/reader-text.md", + "classification": "docs code-span", + "scope": "live" + }, + { + "file": "docs/satellite-system-navigation.md", + "classification": "docs link", + "scope": "live" + }, + { + "file": "docs/seo.md", + "classification": "docs code-span", + "scope": "live" + }, + { + "file": "docs/site-architecture.md", + "classification": "docs code-span", + "scope": "plan" + }, + { + "file": "docs/sources-catalogue.md", + "classification": "docs code-span", + "scope": "live" + }, + { + "file": "eslint.config.mts", + "classification": "tsconfig/eslint/package.json", + "scope": "live" + }, + { + "file": "labs/experiments/native-scroll/tsconfig.json", + "classification": "tsconfig/eslint/package.json", + "scope": "live" + }, + { + "file": "labs/investigations/capture-galaxies.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "labs/nebula/packages/lab/src/adapters/css-earth-shell.css", + "classification": "source import", + "scope": "live" + }, + { + "file": "labs/performance/README.md", + "classification": "docs code-span", + "scope": "live" + }, + { + "file": "labs/performance/tsconfig.tests.json", + "classification": "tsconfig/eslint/package.json", + "scope": "live" + }, + { + "file": "netlify.toml", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "netlify/functions/report.ts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "package.json", + "classification": "tsconfig/eslint/package.json", + "scope": "live" + }, + { + "file": "packages/astronomy/tsconfig.tests.json", + "classification": "tsconfig/eslint/package.json", + "scope": "live" + }, + { + "file": "packages/bake/AGENTS.md", + "classification": "AGENTS.md contract text", + "scope": "live" + }, + { + "file": "packages/bake/README.md", + "classification": "docs code-span", + "scope": "live" + }, + { + "file": "packages/bake/src/delivery/asset-origin-scenes.ts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "packages/bake/src/delivery/public-runtime-assets.ts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "packages/bake/src/navigation/prepare-navigation.ts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "packages/bake/src/prepared-presentation/prepared-presentation-bindings.ts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "packages/bake/src/presentation/clean-leaves.test.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "packages/bake/src/presentation/clean-leaves.ts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "packages/bake/src/run-implemented-objects/index.ts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "packages/bake/src/run-implemented-objects/test-discovery.test.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "packages/bake/src/run-implemented-objects/test-discovery.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "packages/bake/src/runtime-source/runtime-source-graph.test.ts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "packages/bake/src/site-assets/prepare-dataset-billboards.ts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "packages/bake/src/site-assets/prepare-search-thumbnails.test.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "packages/bake/src/site-assets/prepare-search-thumbnails.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "packages/bake/src/site-assets/prepare-search-thumbnails.ts", + "classification": "computed import", + "scope": "live" + }, + { + "file": "packages/bake/src/sources/facility-artwork-refresh.ts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "packages/bake/src/world-context/plain-star-dots.ts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "packages/bake/src/world-context/summary.ts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "packages/bake/tsconfig.tests.json", + "classification": "tsconfig/eslint/package.json", + "scope": "live" + }, + { + "file": "packages/catalog/tsconfig.tests.json", + "classification": "tsconfig/eslint/package.json", + "scope": "live" + }, + { + "file": "packages/core/tsconfig.tests.json", + "classification": "tsconfig/eslint/package.json", + "scope": "live" + }, + { + "file": "packages/engine/tsconfig.tests.json", + "classification": "tsconfig/eslint/package.json", + "scope": "live" + }, + { + "file": "packages/fits/tsconfig.tests.json", + "classification": "tsconfig/eslint/package.json", + "scope": "live" + }, + { + "file": "packages/objects/AGENTS.md", + "classification": "AGENTS.md contract text", + "scope": "live" + }, + { + "file": "packages/objects/README.md", + "classification": "docs code-span", + "scope": "live" + }, + { + "file": "packages/objects/src/node/prepared-registry.ts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "packages/objects/src/prepared-data/source/format-reader-ledger.ts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "packages/objects/src/provenance/source-credits.ts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "packages/objects/src/registry/index.ts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "packages/objects/tsconfig.tests.json", + "classification": "tsconfig/eslint/package.json", + "scope": "live" + }, + { + "file": "packages/renderer/AGENTS.md", + "classification": "AGENTS.md contract text", + "scope": "live" + }, + { + "file": "packages/renderer/src/labels/surface-feature-labels.ts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "packages/renderer/src/navigation/camera-layout.ts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "packages/renderer/src/navigation/eye-relative-readers.test.ts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "packages/renderer/src/node/test-exports.test.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "packages/renderer/src/rendering/prepared-asset-origin.ts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "packages/renderer/src/universe/world-context/world-context-orbit-demand.test.ts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "packages/renderer/src/universe/world-context/world-context-planner.test.ts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "packages/renderer/tsconfig.tests.json", + "classification": "tsconfig/eslint/package.json", + "scope": "live" + }, + { + "file": "packages/spice/tsconfig.tests.json", + "classification": "tsconfig/eslint/package.json", + "scope": "live" + }, + { + "file": "packages/telescope-cli/src/query.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "packages/telescope-cli/src/sphere/sphere-lane.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "packages/telescope-cli/tsconfig.tests.json", + "classification": "tsconfig/eslint/package.json", + "scope": "live" + }, + { + "file": "packages/telescope/tsconfig.tests.json", + "classification": "tsconfig/eslint/package.json", + "scope": "live" + }, + { + "file": "packages/volume-viewer/tsconfig.tests.json", + "classification": "tsconfig/eslint/package.json", + "scope": "live" + }, + { + "file": "site/application-world-context.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/application-world-frames.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/application-world-resources.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/application-world-resources.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "site/application-world-types.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/application-world-visibility.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/asset-origin.characterization.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/build/bundle-cloudflare-worker.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "site/build/bundle-netlify-functions.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "site/build/content/prepare.ts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/build/function-project-files.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "site/build/object-package-contract.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/build/prepare/check-preparation-inputs.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/build/prepare/prepare-catalog.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/build/prepare/prepare-catalog.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "site/build/prepare/prepare-context-availability.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/build/prepare/prepare-facilities.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "site/build/prepare/prepare-feature-index.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/build/prepare/prepare-feature-index.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "site/build/prepare/prepare-interior-fills.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/build/prepare/prepare-moon-labels.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/build/prepare/prepare-moon-labels.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "site/build/prepare/prepare-object-discovery.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/build/prepare/prepare-object-json.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/build/prepare/prepare-shell-icons.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/build/prepare/prepare-shell-titles.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/build/prepare/prepare-sidebar-thumbnails.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/build/prepare/prepare-spatial-context.ts", + "classification": "computed import", + "scope": "live" + }, + { + "file": "site/build/prepare/prepare-text.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/build/prepare/prepare-text.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "site/build/prepare/prepare-volume-presentation.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/build/prepare/prepare-world-presentation.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/build/prepare/prepare-world-presentation.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "site/build/prepare/refresh-source-icons.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/build/prepare/refresh-source-icons.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "site/build/prepare/system-packages.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "site/build/prepare/system-packages.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/build/prepare/system-text.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/build/share-images.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/catalogue-moon-labels.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/components/ChildList.astro", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/components/ContextNavigationMarker.astro", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/components/CssEarthWordmark.astro", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/components/DatasetContextPanels.astro", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/components/DatasetDescription.astro", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/components/DatasetLegend.astro", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/components/DatasetList.astro", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/components/ObjectBreadcrumbs.astro", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/components/ObjectCardHeader.astro", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/components/ObjectFactsheet.astro", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/components/ObjectInformationPanel.astro", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/components/ObjectNavigationMarker.astro", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/components/ObjectPage.astro", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/components/ObjectPage.astro", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "site/components/ObjectShell.astro", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/components/ObjectSwatchStyles.astro", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "site/components/PreparedObjectPanel.astro", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/components/SequencePlayer.astro", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/components/SourcesLink.astro", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/components/SystemCard.astro", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/dataset-picker.characterization.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/dataset-picker.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/dataset-response.characterization.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/dataset-response.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/destination-browser.characterization.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/diagnostic-recorder.characterization.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/dot-catalogue-data.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/dot-catalogue-data.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "site/env.d.ts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/exploration-catalog.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/feature-browser.characterization.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/first-view-transport.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/hosted-banks.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/hosted-banks.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "site/import-queue.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/information-card.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/inside-view.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/layout-sections.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/layouts/ObjectLayout.astro", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/layouts/ObjectLayout.astro", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "site/navigation/navigation-content.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/navigation/navigation-fragments.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/navigation/navigation-history.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/navigation/navigation-request.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/navigation/navigation-scope.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/object-adapter.characterization.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/object-adapter.characterization.test.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "site/object-adapter.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/object-browser.characterization.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/object-browser.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/object-children.characterization.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/object-children.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/object-directory.characterization.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/object-directory.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/object-entries.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/object-entry.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/object-page-contract.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "site/object-page-data.characterization.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/object-page-data.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/object-systems.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/object-systems.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "site/object-text.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/objects.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/overview-selection.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/packaged-object-runtime.characterization.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/packaged-object-runtime.characterization.test.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "site/packaged-object-runtime.characterization.test.mts", + "classification": "computed import", + "scope": "live" + }, + { + "file": "site/packaged-object-runtime.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/pages/[id].astro", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/pages/catalogues/dots.bin.ts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/pages/index.astro", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/pages/native-input.css.ts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/pages/native-input.css.ts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "site/pages/navigation/[id].astro", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/pages/objects/[id]/asset-hashes/[group].json.ts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/pages/objects/[id]/datasets/[dataset].json.ts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/pages/objects/[id]/entry.json.ts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/pages/objects/[id]/entry.json.ts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "site/pages/objects/[id]/first-view.json.ts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/pages/objects/[id]/object.json.ts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/pages/robots.txt.ts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/pages/sitemap.xml.ts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/pages/world/anywhere.json.ts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/pages/world/context-assets/[id].json.ts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/pages/world/dots/[id].bin.ts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/pages/world/orbits/[id].bin.ts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/pages/world/places/[id].json.ts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/pages/world/places/[id].json.ts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "site/pages/world/system-views/[id].json.ts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/pages/world/system-views/[id].json.ts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "site/pages/world/systems/[id].json.ts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/pages/world/systems/[id].json.ts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "site/planetary-system-members.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/planetary-system-members.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "site/prepare-body-moons.characterization.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/prepare-body-moons.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/prepared-arrival.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/prepared-panel-content.shared-readers.test.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "site/prepared-world-navigation.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/prepared-world-navigation.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "site/prepared-world-presentation.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "site/prepared-world-presentation.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/prepared/prepared-world-context-node-source.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "site/root-object.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/satellite-selection.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/satellite-systems.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/scene/camera-handover.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/scene/scene-activation.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/scene/scene-datasets.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/scene/scene-feature.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/scene/scene-publication.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/scene/scene-registry.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/scene/scene-router.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/scene/scene-selection.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/scene/scene-session.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/scene/scene-subject.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/scene/scene-transition.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/scene/scene-transition.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "site/scene/scene-view.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/scene/scene-world.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/search/prepared-catalogue-index.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/search/prepared-catalogue-index.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "site/search/search-client.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/search/search-objects.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/selection-presentation.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/seo-trail.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/seo.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/server/feature-pin.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/server/load-prepared-scene.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/server/search-response.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/server/search-route.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/server/search-server.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/shared-imports.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/shell/object-shell-client.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/shell/object-shell-types.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/shell/shell-settings.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/shell/shell-sheet.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/site.css", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/social-images.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/source-documentation.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/startup-billboard.characterization.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/startup-billboard.characterization.test.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "site/startup-billboard.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/startup-boot.characterization.test.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "site/startup-boot.characterization.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/startup-boot.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/startup-requests.characterization.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/startup-requests.characterization.test.mts", + "classification": "computed import", + "scope": "live" + }, + { + "file": "site/startup-world.characterization.test.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "site/startup-world.characterization.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/startup-world.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/stellar-extents.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/system-card.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/system-framing-radii.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/system-framing.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/system-framing.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "site/tab-panels.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/application-world-frames.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/application-world-visibility.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/arrival-billboard.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/asset-origin.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/body-additions.test.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "site/test/body-moons.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/browser-node-imports.test.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "site/test/built-pages.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/camera-handover.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/category-frames.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/chart-pixel-alignment.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/compact-spectrum.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/dataset-picker.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/dataset-response.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/dataset-url.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/destination-browser.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/diagnostic-recorder.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/error-report.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/first-view-transport.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/fixtures/objects.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/fixtures/saturn-prepared.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/hosted-banks.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/import-queue.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/in-flight-loader.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/information-card-sections.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/initial-shell-context.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/moon-labels.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/navigation-fragments.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/navigation-fragments.test.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "site/test/navigation-history.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/navigation-ontology.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/navigation-preparation.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/navigation-preparation.test.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "site/test/navigation-request.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/navigation-router-context.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "site/test/navigation-scope.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/navigation-styles.test.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "site/test/navigation-test-values.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/navigation-timing.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/nebula-search.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/object-children.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/object-directory.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/object-navigation.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/object-package-contract.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/object-package-contract.test.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "site/test/object-page-data.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/object-page-data.test.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "site/test/object-systems.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/object-text.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/overview-selection.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/prepare-context-availability.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/prepared-arrival.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/prepared-body-billboards.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/prepared-dataset-facts.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/prepared-dataset-legends.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/prepared-descriptor.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/prepared-icons.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/prepared-icons.test.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "site/test/prepared-panel-content.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/prepared-presentation-bindings.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/prepared-presentation-bindings.test.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "site/test/prepared-world-context.test.ts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/prepared-world-context.test.ts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "site/test/prepared-world-navigation.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/restore-inputs.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/restore-inputs.test.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "site/test/runtime-package.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/runtime-policy.test.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "site/test/satellite-arrival-framing.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/satellite-systems.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/scene-contract.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/scene-publication.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/scene-session.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/scientific-chart-order.test.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "site/test/search-api.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/search-response.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/seo-discovery.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/seo.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/setup-assets.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/shell-settings.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/showcase.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/source-link.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/startup-billboard.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/startup-cover.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/startup-requests.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/stellar-extents.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/system-framing.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/system-text.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/system-view-file.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/view-readout.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/view-url-runtime.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/volume-presentation.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/web-analytics.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/world-stars.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/world-tables.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/test/zoom-scope.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/view-readout.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/view-url-runtime.characterization.test.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/view-url-runtime.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/volume-presentation.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/world-approach.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/world-context-plan.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/world-objects.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/world-objects.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "site/world-places.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/world-system-views.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "site/world-system-views.mts", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "site/zoom-scope.mts", + "classification": "source import", + "scope": "live" + }, + { + "file": "src/objects/README.md", + "classification": "docs code-span", + "scope": "live" + }, + { + "file": "src/objects/antares/README.md", + "classification": "docs code-span", + "scope": "live" + }, + { + "file": "src/objects/eps-indi-ba/README.md", + "classification": "docs link", + "scope": "live" + }, + { + "file": "src/objects/hd-189733-companion/README.md", + "classification": "docs link", + "scope": "live" + }, + { + "file": "src/objects/kelt-9b/README.md", + "classification": "docs link", + "scope": "live" + }, + { + "file": "src/objects/lmc-volume/investigations.json", + "classification": "generator/producer literal", + "scope": "live" + }, + { + "file": "src/objects/luhman-16/README.md", + "classification": "docs link", + "scope": "live" + }, + { + "file": "src/objects/nearby-universe-galaxies/README.md", + "classification": "docs link", + "scope": "live" + }, + { + "file": "src/objects/observable-universe-cmb/README.md", + "classification": "docs code-span", + "scope": "live" + }, + { + "file": "src/objects/observable-universe-cmb/README.md", + "classification": "docs link", + "scope": "live" + }, + { + "file": "src/objects/trappist-1/README.md", + "classification": "docs link", + "scope": "live" + }, + { + "file": "src/objects/trappist-1c/README.md", + "classification": "docs link", + "scope": "live" + }, + { + "file": "src/objects/trappist-1d/README.md", + "classification": "docs link", + "scope": "live" + }, + { + "file": "src/objects/trappist-1e/README.md", + "classification": "docs link", + "scope": "live" + }, + { + "file": "src/objects/trappist-1f/README.md", + "classification": "docs link", + "scope": "live" + }, + { + "file": "src/objects/trappist-1g/README.md", + "classification": "docs link", + "scope": "live" + }, + { + "file": "src/objects/trappist-1h/README.md", + "classification": "docs link", + "scope": "live" + }, + { + "file": "src/objects/wasp-103b/README.md", + "classification": "docs link", + "scope": "live" + }, + { + "file": "src/objects/wasp-121/README.md", + "classification": "docs link", + "scope": "live" + }, + { + "file": "src/objects/wasp-18/README.md", + "classification": "docs link", + "scope": "live" + }, + { + "file": "src/objects/wasp-76/README.md", + "classification": "docs link", + "scope": "live" + }, + { + "file": "src/objects/wasp-76b/README.md", + "classification": "docs link", + "scope": "live" + }, + { + "file": "src/tsconfig.tests.json", + "classification": "tsconfig/eslint/package.json", + "scope": "live" + }, + { + "file": "tsconfig.integration-tests.json", + "classification": "tsconfig/eslint/package.json", + "scope": "live" + }, + { + "file": "tsconfig.scripts.json", + "classification": "tsconfig/eslint/package.json", + "scope": "live" + } + ], + "relativeReads": [ + { + "file": "site/test/arrival-discovery.test.mts", + "destination": "site/build/prepare/arrival-discovery.test.mts" + }, + { + "file": "site/test/body-additions.test.mts", + "destination": "site/build/prepare/body-additions.test.mts" + }, + { + "file": "site/test/category-frames.test.mts", + "destination": "site/build/prepare/category-frames.test.mts" + }, + { + "file": "site/test/compact-spectrum.test.mts", + "destination": "site/overview/compact-spectrum.test.mts" + }, + { + "file": "site/test/dataset-response.test.mts", + "destination": "site/server/dataset-response.test.mts" + }, + { + "file": "site/test/folded-transit.test.mts", + "destination": "site/build/charts/folded-transit.test.mts" + }, + { + "file": "site/test/hosted-banks.test.mts", + "destination": "site/build/prepare/hosted-banks.test.mts" + }, + { + "file": "site/test/load-object-content.mts", + "destination": "site/build/content/load-object-content.test-support.mts" + }, + { + "file": "site/test/lonlat-slice-table.test.mts", + "destination": "site/build/prepare/lonlat-slice-table.test.mts" + }, + { + "file": "site/test/moon-labels.test.mts", + "destination": "site/world/moon-labels.test.mts" + }, + { + "file": "site/test/neutral-catalogue-color.test.mts", + "destination": "site/journeys/neutral-catalogue-color.test.mts" + }, + { + "file": "site/test/object-page-data.test.mts", + "destination": "site/server/object-page-data.test.mts" + }, + { + "file": "site/test/object-systems.test.mts", + "destination": "site/world/object-systems.test.mts" + }, + { + "file": "site/test/prepare-spatial-context.test.mts", + "destination": "site/build/prepare/prepare-spatial-context.test.mts" + }, + { + "file": "site/test/prepared-panel-content.test.mts", + "destination": "site/content/prepared-panel-content.test.mts" + }, + { + "file": "site/test/prepared-world-context.test.ts", + "destination": "site/world/prepared-world-context.test.ts" + }, + { + "file": "site/test/satellite-arrival-framing.test.mts", + "destination": "site/navigation/satellite-arrival-framing.test.mts" + }, + { + "file": "site/test/scene-contract.test.mts", + "destination": "site/world/scene-contract.test.mts" + }, + { + "file": "site/test/system-framing.test.mts", + "destination": "site/world/system-framing.test.mts" + }, + { + "file": "site/test/system-text.test.mts", + "destination": "site/build/prepare/system-text.test.mts" + }, + { + "file": "site/test/system-view-file.mts", + "destination": "site/world/system-view-file.test-support.mts" + }, + { + "file": "site/test/world-billboards.test.mts", + "destination": "site/journeys/world-billboards.test.mts" + }, + { + "file": "site/test/world-stars.test.mts", + "destination": "site/world/world-stars.test.mts" + }, + { + "file": "site/test/world-tables.test.mts", + "destination": "site/world/world-tables.test.mts" + }, + { + "file": "site/test/zoom-scope.test.mts", + "destination": "site/world/zoom-scope.test.mts" + } + ] +} diff --git a/docs/site-architecture/tiers.json b/docs/site-architecture/tiers.json new file mode 100644 index 00000000000..d8636782844 --- /dev/null +++ b/docs/site-architecture/tiers.json @@ -0,0 +1,519 @@ +{ + "tiers": [ + { + "tier": 0, + "name": "Browser input, DOM and serialized import queue", + "folders": [ + "site/browser" + ] + }, + { + "tier": 0, + "name": "Preserved input records and artwork", + "folders": [ + "site/source" + ] + }, + { + "tier": 0, + "name": "Preserved third-party notices", + "folders": [ + "site/vendor" + ] + }, + { + "tier": 0, + "name": "Shared page and shell interfaces", + "folders": [ + "site/contracts" + ] + }, + { + "tier": 0, + "name": "Prepared transports and generated inputs", + "folders": [ + "site/prepared" + ] + }, + { + "tier": 0, + "name": "Object identities, routes and held addresses", + "folders": [ + "site/model" + ] + }, + { + "tier": 1, + "name": "Surface-map measurements and view formatting", + "folders": [ + "site/minimap" + ] + }, + { + "tier": 2, + "name": "Shared framing, visibility and camera context", + "folders": [ + "site/world" + ] + }, + { + "tier": 0, + "name": "Prepared spectral overview readers", + "folders": [ + "site/overview" + ] + }, + { + "tier": 3, + "name": "Card content, citations and metadata", + "folders": [ + "site/content" + ] + }, + { + "tier": 4, + "name": "Catalogue search and result presentation", + "folders": [ + "site/search" + ] + }, + { + "tier": 3, + "name": "History, requests, flights and arrivals", + "folders": [ + "site/navigation" + ] + }, + { + "tier": 4, + "name": "Committed selection and camera handovers", + "folders": [ + "site/selection" + ] + }, + { + "tier": 5, + "name": "Retained shell controls and panels", + "folders": [ + "site/shell" + ] + }, + { + "tier": 6, + "name": "Scene sessions, replacement and publication", + "folders": [ + "site/scene" + ] + }, + { + "tier": 7, + "name": "Page boot helpers and router entry", + "folders": [ + "site/startup" + ] + }, + { + "tier": 6, + "name": "SSR readers, responses and host middleware", + "folders": [ + "site/server" + ] + }, + { + "tier": 8, + "name": "Shared page frame and its styles", + "folders": [ + "site/layouts" + ] + }, + { + "tier": 9, + "name": "Reusable Astro markup and SSR composition", + "folders": [ + "site/components" + ] + }, + { + "tier": 10, + "name": "Routes and object-page entries", + "folders": [ + "site/pages" + ] + }, + { + "tier": 7, + "name": "Site preparation and packaging", + "folders": [ + "site/build" + ] + }, + { + "tier": 11, + "name": "Retired test folder; no final occupants", + "folders": [ + "site/test" + ] + }, + { + "tier": 11, + "name": "Cross-cutting journeys and support", + "folders": [ + "site/journeys" + ] + }, + { + "tier": 1, + "name": "Startup reads and object catalogue directory", + "folders": [ + "site/directory" + ] + }, + { + "tier": 0, + "name": "Server and build prepared-asset origin", + "folders": [ + "site/server-assets" + ] + } + ], + "root": { + "allow": [ + "site/env.d.ts" + ], + "entries": [ + "site/env.d.ts" + ], + "tier": 11 + }, + "lateral": [], + "tests": "any-tier", + "testFiles": [ + "site/asset-origin.characterization.test.mts", + "site/build/source-maps.test.mts", + "site/dataset-picker.characterization.test.mts", + "site/dataset-response.characterization.test.mts", + "site/destination-browser.characterization.test.mts", + "site/diagnostic-recorder.characterization.test.mts", + "site/feature-browser.characterization.test.mts", + "site/navigation/evidence/star-click/one-click.jpg", + "site/object-adapter.characterization.test.mts", + "site/object-browser.characterization.test.mts", + "site/object-children.characterization.test.mts", + "site/object-directory.characterization.test.mts", + "site/object-page-data.characterization.test.mts", + "site/packaged-object-runtime.characterization.test.mts", + "site/prepare-body-moons.characterization.test.mts", + "site/prepared-panel-content.shared-readers.test.mts", + "site/startup-billboard.characterization.test.mts", + "site/startup-boot.characterization.test.mts", + "site/startup-requests.characterization.test.mts", + "site/startup-world.characterization.test.mts", + "site/test/application-world-frames.test.mts", + "site/test/application-world-visibility.test.mts", + "site/test/arrival-billboard.test.mts", + "site/test/arrival-discovery.test.mts", + "site/test/asset-origin.test.mts", + "site/test/astro-component-imports.test.mts", + "site/test/authored-object-fixture.mts", + "site/test/body-additions.test.mts", + "site/test/body-moons.test.mts", + "site/test/browser-node-imports.test.mts", + "site/test/built-pages.test.mts", + "site/test/camera-handover.test.mts", + "site/test/catalogue-window.test.mts", + "site/test/category-frames.test.mts", + "site/test/chart-pixel-alignment.test.mts", + "site/test/charts.test.ts", + "site/test/check-preparation-inputs.test.mts", + "site/test/cloudflare-worker.test.mts", + "site/test/compact-spectrum.test.mts", + "site/test/context-lineage.test.mts", + "site/test/dataset-picker.test.mts", + "site/test/dataset-response.test.mts", + "site/test/dataset-url.test.mts", + "site/test/descriptor-roundtrip.test.mts", + "site/test/destination-browser.test.mts", + "site/test/diagnostic-accessor.test.mts", + "site/test/diagnostic-recorder.test.mts", + "site/test/error-report.test.mts", + "site/test/evidence/passive-minimaps/browser.json", + "site/test/evidence/passive-minimaps/earth.png", + "site/test/evidence/passive-minimaps/jupiter-sequence.png", + "site/test/first-view-transport.test.mts", + "site/test/fixtures/anchor-table.mts", + "site/test/fixtures/characterization-runtime.mts", + "site/test/fixtures/context-package.mts", + "site/test/fixtures/galaxies/captures.json", + "site/test/fixtures/object-content-fixture.mts", + "site/test/fixtures/objects.mts", + "site/test/fixtures/prepared-schemas.mts", + "site/test/fixtures/saturn-prepared.mts", + "site/test/folded-transit.test.mts", + "site/test/galactic-focus.test.mts", + "site/test/hosted-banks.test.mts", + "site/test/import-queue.test.mts", + "site/test/in-flight-loader.test.mts", + "site/test/information-card-sections.test.mts", + "site/test/information-tab-styles.test.mts", + "site/test/initial-shell-context.test.mts", + "site/test/inline-page-stylesheet.test.mts", + "site/test/kept-load.test.mts", + "site/test/list-distance.test.mts", + "site/test/load-object-content.mts", + "site/test/lonlat-slice-table.test.mts", + "site/test/moon-labels.test.mts", + "site/test/navigation-content.test.mts", + "site/test/navigation-fragments.test.mts", + "site/test/navigation-history.test.mts", + "site/test/navigation-lifecycle.test.mts", + "site/test/navigation-ontology.test.mts", + "site/test/navigation-preparation.test.mts", + "site/test/navigation-readiness.test.mts", + "site/test/navigation-request.test.mts", + "site/test/navigation-router-context.mts", + "site/test/navigation-scope.test.mts", + "site/test/navigation-shell-content.test.mts", + "site/test/navigation-styles.test.mts", + "site/test/navigation-test-values.mts", + "site/test/navigation-timing.test.mts", + "site/test/nebula-search.test.mts", + "site/test/neutral-catalogue-color.test.mts", + "site/test/object-children.test.mts", + "site/test/object-control-binding.test.mts", + "site/test/object-directory.test.mts", + "site/test/object-navigation.test.mts", + "site/test/object-package-contract.test.mts", + "site/test/object-page-data.test.mts", + "site/test/object-result.test.mts", + "site/test/object-runtime-contract.test.mts", + "site/test/object-search-rank.test.mts", + "site/test/object-systems.test.mts", + "site/test/object-text.test.mts", + "site/test/overview-selection.test.mts", + "site/test/preparation-boundary.test.mts", + "site/test/prepare-authored.test.mts", + "site/test/prepare-context-availability.test.mts", + "site/test/prepare-object-json.test.mts", + "site/test/prepare-spatial-context.test.mts", + "site/test/prepare-text.test.mts", + "site/test/prepared-arrival.test.mts", + "site/test/prepared-body-billboards.test.mts", + "site/test/prepared-dataset-facts.test.mts", + "site/test/prepared-dataset-labels.test.mts", + "site/test/prepared-dataset-legends.test.mts", + "site/test/prepared-descriptor.test.mts", + "site/test/prepared-icons.test.mts", + "site/test/prepared-motion-css.test.mts", + "site/test/prepared-panel-content.test.mts", + "site/test/prepared-presentation-bindings.test.mts", + "site/test/prepared-presentation-contract.test.mts", + "site/test/prepared-world-context.test.ts", + "site/test/prepared-world-navigation.test.mts", + "site/test/preview.test.mts", + "site/test/production-diagnostics.test.mts", + "site/test/recovery-commands.test.mts", + "site/test/refresh-charts.test.mts", + "site/test/rendered-page.test.mts", + "site/test/restore-inputs.test.mts", + "site/test/run-script-chain.test.mts", + "site/test/runtime-package.test.mts", + "site/test/runtime-policy.test.mts", + "site/test/satellite-arrival-framing.test.mts", + "site/test/satellite-systems.test.mts", + "site/test/scene-contract.test.mts", + "site/test/scene-datasets.test.mts", + "site/test/scene-frame.test.mts", + "site/test/scene-publication.test.mts", + "site/test/scene-session.test.mts", + "site/test/scientific-chart-order.test.mts", + "site/test/search-api.test.mts", + "site/test/search-response.test.mts", + "site/test/seo-discovery.mts", + "site/test/seo-discovery.test.mts", + "site/test/seo.test.mts", + "site/test/setup-assets.test.mts", + "site/test/shell-selectors.test.mts", + "site/test/shell-settings.test.mts", + "site/test/shell-sheet.test.mts", + "site/test/showcase.test.mts", + "site/test/source-evidence-values.mts", + "site/test/source-link.test.mts", + "site/test/startup-billboard.test.mts", + "site/test/startup-cover.test.mts", + "site/test/startup-requests.test.mts", + "site/test/stellar-extents.test.mts", + "site/test/surface-label-loop.test.mts", + "site/test/surface-minimap.test.mts", + "site/test/system-framing.test.mts", + "site/test/system-text.test.mts", + "site/test/system-view-file.mts", + "site/test/view-readout.test.mts", + "site/test/view-url-runtime.test.mts", + "site/test/volume-presentation.test.mts", + "site/test/web-analytics.test.mts", + "site/test/world-billboards.test.mts", + "site/test/world-stars.test.mts", + "site/test/world-tables.test.mts", + "site/test/zoom-scope.test.mts", + "site/view-url-runtime.characterization.test.mts" + ], + "assets": [ + "site/layouts/cubic-sky.css", + "site/moon-labels.prepared.json", + "site/navigation/evidence/star-click/one-click.jpg", + "site/object-navigation-marker.css", + "site/object-shell.css", + "site/prepared-catalogue.mjs", + "site/prepared-context-bank-assets.json", + "site/prepared-context-json.mts", + "site/prepared-context-objects.mts", + "site/prepared-dataset-billboards.json", + "site/prepared-dataset-volumes.json", + "site/prepared-dot-catalogues.json", + "site/prepared-facilities.json", + "site/prepared-feature-index.json", + "site/prepared-fit-boxes.json", + "site/prepared-hosted-banks.json", + "site/prepared-navigation-markers.mjs", + "site/prepared-shell-icons.mjs", + "site/prepared-shell-titles.mjs", + "site/prepared-source-credits.json", + "site/prepared-sources.json", + "site/prepared-stellar-extents.json", + "site/prepared-world-presentation.json", + "site/shell/maps-shell.css", + "site/shell/settings-panel.css", + "site/shell/shell-layout.css", + "site/site.css", + "site/source/agency-logos.json", + "site/source/branding/particle-globe.svg", + "site/source/facilities/catalog.json", + "site/source/facilities/emblem-library.json", + "site/source/facilities/emblems/README.md", + "site/source/facilities/emblems/originals/cassini.png", + "site/source/facilities/emblems/originals/dawn.png", + "site/source/facilities/emblems/originals/galileo.png", + "site/source/facilities/emblems/originals/grail-prepared.webp", + "site/source/facilities/emblems/originals/hayabusa.jpg", + "site/source/facilities/emblems/originals/hayabusa2.png", + "site/source/facilities/emblems/originals/hubble-commons.png", + "site/source/facilities/emblems/originals/juno.svg", + "site/source/facilities/emblems/originals/lro-commons.png", + "site/source/facilities/emblems/originals/magellan.png", + "site/source/facilities/emblems/originals/mars-global-surveyor-jpl.jpg", + "site/source/facilities/emblems/originals/messenger.png", + "site/source/facilities/emblems/originals/mro.png", + "site/source/facilities/emblems/originals/near-shoemaker.png", + "site/source/facilities/emblems/originals/new-horizons.png", + "site/source/facilities/emblems/originals/odyssey.png", + "site/source/facilities/emblems/originals/osiris-rex.png", + "site/source/facilities/emblems/originals/rosetta-patch.png", + "site/source/facilities/emblems/originals/sdo.png", + "site/source/facilities/emblems/originals/suomi-npp-commons.png", + "site/source/facilities/emblems/originals/terra-commons.png", + "site/source/facilities/emblems/originals/viking-commons.jpg", + "site/source/facilities/emblems/originals/voyager-1.png", + "site/source/facilities/emblems/originals/voyager-2.png", + "site/source/facilities/emblems/source-records.json", + "site/source/facilities/photograph-records.json", + "site/source/facilities/render-library.json", + "site/source/icons/SOURCE.md", + "site/source/icons/codepen-mark-LICENSE.txt", + "site/source/icons/github-mark-LICENSE.txt", + "site/source/icons/symbol-half-black-circle.svg", + "site/source/icons/symbol-proportional-to.svg", + "site/source/icons/symbol-quarter-phase.svg", + "site/source/icons/symbol-reference-mark.svg", + "site/source/icons/symbol-sine-wave.svg", + "site/source/icons/symbol-square-crosshatch-fill.svg", + "site/source/jpl-small-body-mission-targets.json", + "site/source/major-moons.json", + "site/source/moon-catalogues.json", + "site/source/moon-horizons.json.gz", + "site/source/overview-facts.json", + "site/source/scientific-charts/planetary-context.json", + "site/source/source-icons.json", + "site/source/titles/title-factsheet.svg", + "site/source/titles/title-reflectance-spectrum.svg", + "site/source/titles/title-settings.svg", + "site/source/titles/title-sources-resources.svg", + "site/source/titles/title-surface-dataset.svg", + "site/source/titles/title-surface-photos.svg", + "site/source/titles/title-telescope-images.svg", + "site/source/titles/title-thermal-profile.svg", + "site/test/evidence/passive-minimaps/browser.json", + "site/test/evidence/passive-minimaps/earth.png", + "site/test/evidence/passive-minimaps/jupiter-sequence.png", + "site/test/fixtures/galaxies/captures.json", + "site/vendor/cesium-LICENSE.md", + "site/wordmark.css" + ], + "status": "planned", + "forbid": [ + { + "from": [ + "site/browser", + "site/source", + "site/vendor", + "site/contracts", + "site/prepared", + "site/model", + "site/minimap", + "site/world", + "site/overview", + "site/content", + "site/search", + "site/navigation", + "site/selection", + "site/shell", + "site/scene", + "site/startup", + "site/directory" + ], + "to": [ + "site/server", + "site/build", + "site/server-assets" + ], + "reason": "Client owners cannot import server responses or preparation." + }, + { + "from": [ + "site/browser", + "site/source", + "site/vendor", + "site/contracts", + "site/prepared", + "site/model", + "site/minimap", + "site/world", + "site/overview", + "site/content", + "site/search", + "site/navigation", + "site/selection", + "site/shell", + "site/scene", + "site/server", + "site/components", + "site/build", + "site/directory" + ], + "to": [ + "site/startup" + ], + "reason": "Only layout and page entries enter the startup composition root." + }, + { + "to": [ + "site/build" + ], + "reason": "Page composition cannot import preparation.", + "from": [ + "site/layouts", + "site/components", + "site/pages" + ] + } + ] +} diff --git a/packages/bake/AGENTS.md b/packages/bake/AGENTS.md index 772e0d0559e..3a096c7f0fb 100644 --- a/packages/bake/AGENTS.md +++ b/packages/bake/AGENTS.md @@ -178,7 +178,7 @@ cycle. Share anything else through `@cssearth/core` or another package. Prepared reflectance, temperature-pressure, phase and light curves, FITS gallery pictures) and their shared SVG style. It imports no topic. The recipe dispatcher is site-owned preparation in `site/build/charts/`; the spectrum reader and compact spectrum are in `site/overview/`, because - `site/prepare-body-overview.mts` uses them and the runtime may not import the bake. + `site/build/charts/charts.ts` uses them and the runtime may not import the bake. - `objects/content`: the shared dataset vocabulary, dataset steps and prepared legends, each dataset control's billboard color, and the legend labels a palette dataset derives from the stretch its report states. It imports no topic. The content preparer that reads factsheets and writes the payload is site-owned preparation in `site/build/content/`