From 9d8f5c3edd3ebbf459a7d0303be78afd8be68b46 Mon Sep 17 00:00:00 2001 From: glennmichael123 Date: Sat, 25 Jul 2026 01:38:12 +0800 Subject: [PATCH] refactor(buddy): move protocol + docs governance commands from userland into core The protocol-conformance and docs-freshness tooling lived under app/Commands/{protocol,docs} (userland), but these are framework-repo core commands, not app code. Move them into storage/framework/core/buddy/src/commands/ and register them through the core lazy-command loader + barrel, like every other built-in command. - git-mv the 27 tooling + test files, preserving history - Protocol.ts/Docs.ts become named-export core registrars (protocol()/docs()) - 22 protocol:* / docs:* entries added to lazy-commands.ts; commands barrel updated - fix repo-root path depth (import.meta.dir) + cross-package imports for the new location; update CI workflow + generated-doc paths - app/Commands.ts drops the protocol/docs registrations (keeps the inspire example) - fix 6 latent strict-null type errors the looser app/ tsconfig had hidden (regex-group / buffer-index access) so the tooling passes framework typecheck Verified: framework typecheck clean in the moved files, lint clean, 48/48 moved tests pass, buddy protocol:* / docs:* wire + run correctly. protocol-conformance CI stays red on the pre-existing #2179 governance-evidence drift (orphaned bf1245e pin), unrelated to this move. --- .github/workflows/ci.yml | 4 +- .github/workflows/desktop-lifecycle.yml | 10 ++--- app/Commands.ts | 2 - docs/guide/buddy/commands.md | 2 +- .../framework/core/buddy/src/commands/docs.ts | 18 ++++---- .../src/commands}/docs/buddy-commands.test.ts | 2 +- .../src/commands}/docs/buddy-commands.ts | 8 ++-- .../docs/generated-artifacts.test.ts | 0 .../src/commands}/docs/generated-artifacts.ts | 8 ++-- .../buddy/src/commands}/docs/links.test.ts | 0 .../core/buddy/src/commands}/docs/links.ts | 4 +- .../core/buddy/src/commands/index.ts | 2 + .../core/buddy/src/commands/protocol.ts | 43 +++++++++---------- .../commands}/protocol/craft-evidence.test.ts | 0 .../src/commands}/protocol/craft-evidence.ts | 4 +- .../protocol/desktop-lifecycle-report.test.ts | 0 .../protocol/desktop-lifecycle-report.ts | 0 .../src/commands}/protocol/desktop-support.ts | 4 +- .../protocol/desktop-workflow.test.ts | 2 +- .../src/commands}/protocol/driver-registry.ts | 6 +-- .../protocol/pantry-evidence.test.ts | 0 .../src/commands}/protocol/pantry-evidence.ts | 4 +- .../protocol/release-manifest.test.ts | 0 .../commands}/protocol/release-manifest.ts | 7 +-- .../protocol/run-conformance.test.ts | 0 .../src/commands}/protocol/run-conformance.ts | 28 ++++++------ .../protocol/run-driver-contracts.test.ts | 2 +- .../protocol/run-driver-contracts.ts | 6 +-- .../src/commands}/protocol/source-manifest.ts | 8 ++-- .../src/commands}/protocol/sync-suite.test.ts | 4 +- .../src/commands}/protocol/sync-suite.ts | 4 +- .../core/buddy/src/commands}/run-tool.ts | 0 .../framework/core/buddy/src/lazy-commands.ts | 28 ++++++++++++ 33 files changed, 121 insertions(+), 89 deletions(-) rename app/Commands/Docs.ts => storage/framework/core/buddy/src/commands/docs.ts (84%) rename {app/Commands => storage/framework/core/buddy/src/commands}/docs/buddy-commands.test.ts (94%) rename {app/Commands => storage/framework/core/buddy/src/commands}/docs/buddy-commands.ts (94%) rename {app/Commands => storage/framework/core/buddy/src/commands}/docs/generated-artifacts.test.ts (100%) rename {app/Commands => storage/framework/core/buddy/src/commands}/docs/generated-artifacts.ts (90%) rename {app/Commands => storage/framework/core/buddy/src/commands}/docs/links.test.ts (100%) rename {app/Commands => storage/framework/core/buddy/src/commands}/docs/links.ts (97%) rename app/Commands/Protocol.ts => storage/framework/core/buddy/src/commands/protocol.ts (88%) rename {app/Commands => storage/framework/core/buddy/src/commands}/protocol/craft-evidence.test.ts (100%) rename {app/Commands => storage/framework/core/buddy/src/commands}/protocol/craft-evidence.ts (96%) rename {app/Commands => storage/framework/core/buddy/src/commands}/protocol/desktop-lifecycle-report.test.ts (100%) rename {app/Commands => storage/framework/core/buddy/src/commands}/protocol/desktop-lifecycle-report.ts (100%) rename {app/Commands => storage/framework/core/buddy/src/commands}/protocol/desktop-support.ts (91%) rename {app/Commands => storage/framework/core/buddy/src/commands}/protocol/desktop-workflow.test.ts (97%) rename {app/Commands => storage/framework/core/buddy/src/commands}/protocol/driver-registry.ts (93%) rename {app/Commands => storage/framework/core/buddy/src/commands}/protocol/pantry-evidence.test.ts (100%) rename {app/Commands => storage/framework/core/buddy/src/commands}/protocol/pantry-evidence.ts (96%) rename {app/Commands => storage/framework/core/buddy/src/commands}/protocol/release-manifest.test.ts (100%) rename {app/Commands => storage/framework/core/buddy/src/commands}/protocol/release-manifest.ts (95%) rename {app/Commands => storage/framework/core/buddy/src/commands}/protocol/run-conformance.test.ts (100%) rename {app/Commands => storage/framework/core/buddy/src/commands}/protocol/run-conformance.ts (96%) rename {app/Commands => storage/framework/core/buddy/src/commands}/protocol/run-driver-contracts.test.ts (92%) rename {app/Commands => storage/framework/core/buddy/src/commands}/protocol/run-driver-contracts.ts (95%) rename {app/Commands => storage/framework/core/buddy/src/commands}/protocol/source-manifest.ts (94%) rename {app/Commands => storage/framework/core/buddy/src/commands}/protocol/sync-suite.test.ts (94%) rename {app/Commands => storage/framework/core/buddy/src/commands}/protocol/sync-suite.ts (97%) rename {app/Commands => storage/framework/core/buddy/src/commands}/run-tool.ts (100%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2d33a668b9..b43297e7ce 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -174,8 +174,8 @@ jobs: env: PROTOCOL_REPORT_DIR: .github/protocol/reports run: | - bun test ./app/Commands/protocol/run-conformance.test.ts - bun test ./app/Commands/protocol/craft-evidence.test.ts ./app/Commands/protocol/desktop-lifecycle-report.test.ts + bun test ./storage/framework/core/buddy/src/commands/protocol/run-conformance.test.ts + bun test ./storage/framework/core/buddy/src/commands/protocol/craft-evidence.test.ts ./storage/framework/core/buddy/src/commands/protocol/desktop-lifecycle-report.test.ts bun test ./.github/scripts/deploy/resolve-target.test.ts bun run protocol:drivers:test bun run protocol:conformance diff --git a/.github/workflows/desktop-lifecycle.yml b/.github/workflows/desktop-lifecycle.yml index 639fc735d3..a6c3c1af95 100644 --- a/.github/workflows/desktop-lifecycle.yml +++ b/.github/workflows/desktop-lifecycle.yml @@ -5,16 +5,16 @@ on: branches: [main] paths: - storage/framework/core/desktop/** - - app/Commands/protocol/craft-evidence.ts - - app/Commands/protocol/desktop-lifecycle-report.ts + - storage/framework/core/buddy/src/commands/protocol/craft-evidence.ts + - storage/framework/core/buddy/src/commands/protocol/desktop-lifecycle-report.ts - .github/protocol/evidence/craft.json - .github/workflows/desktop-lifecycle.yml pull_request: branches: [main] paths: - storage/framework/core/desktop/** - - app/Commands/protocol/craft-evidence.ts - - app/Commands/protocol/desktop-lifecycle-report.ts + - storage/framework/core/buddy/src/commands/protocol/craft-evidence.ts + - storage/framework/core/buddy/src/commands/protocol/desktop-lifecycle-report.ts - .github/protocol/evidence/craft.json - .github/workflows/desktop-lifecycle.yml workflow_dispatch: @@ -123,7 +123,7 @@ jobs: - name: Validate and attest lifecycle report run: >- - bun app/Commands/protocol/desktop-lifecycle-report.ts + bun storage/framework/core/buddy/src/commands/protocol/desktop-lifecycle-report.ts --report .craft-contract/artifacts/native-lifecycle/${{ matrix.name }}/report.json --output .github/protocol/reports/desktop/${{ matrix.name }}/attestation.json --revision "$CRAFT_SOURCE_REVISION" diff --git a/app/Commands.ts b/app/Commands.ts index 2739b8e5e9..1d76c105a1 100644 --- a/app/Commands.ts +++ b/app/Commands.ts @@ -28,6 +28,4 @@ export type CommandRegistry = Record */ export default { 'inspire': 'Inspire', - 'protocol': 'Protocol', - 'docs': 'Docs', } satisfies CommandRegistry diff --git a/docs/guide/buddy/commands.md b/docs/guide/buddy/commands.md index 338fa7f722..f3cfe5d3d5 100644 --- a/docs/guide/buddy/commands.md +++ b/docs/guide/buddy/commands.md @@ -2,7 +2,7 @@ title: Buddy Command Reference description: Generated reference for every Buddy command, argument, option, alias, default, and example. --- - + # Buddy Command Reference diff --git a/app/Commands/Docs.ts b/storage/framework/core/buddy/src/commands/docs.ts similarity index 84% rename from app/Commands/Docs.ts rename to storage/framework/core/buddy/src/commands/docs.ts index 5ce304d3c2..66b21250f9 100644 --- a/app/Commands/Docs.ts +++ b/storage/framework/core/buddy/src/commands/docs.ts @@ -5,42 +5,42 @@ import { run as runLinks } from './docs/links' import { runTool } from './run-tool' /** - * Repo-scoped `buddy docs:*` commands wrapping the documentation-freshness - * tooling under `app/Commands/docs/`: the generated buddy command reference, + * Framework-repo `buddy docs:*` commands wrapping the documentation-freshness + * tooling under `commands/docs/`: the generated buddy command reference, * generated API artifacts (OpenAPI + types), and internal link checking. */ -export default function (cli: CLI): void { - cli +export function docs(buddy: CLI): void { + buddy .command('docs:buddy', 'Regenerate the buddy command reference doc') .action(async () => { await runTool(runBuddyDocs, '--write') }) - cli + buddy .command('docs:buddy:check', 'Verify the buddy command reference doc is current') .action(async () => { await runTool(runBuddyDocs, '--check') }) - cli + buddy .command('docs:artifacts', 'Regenerate the generated API artifacts (OpenAPI + types)') .action(async () => { await runTool(runArtifacts, '--write') }) - cli + buddy .command('docs:artifacts:check', 'Verify the generated API artifacts are current') .action(async () => { await runTool(runArtifacts, '--check') }) - cli + buddy .command('docs:links', 'Report internal documentation links') .action(async () => { await runTool(runLinks) }) - cli + buddy .command('docs:links:check', 'Verify internal documentation links resolve') .action(async () => { await runTool(runLinks, '--check') diff --git a/app/Commands/docs/buddy-commands.test.ts b/storage/framework/core/buddy/src/commands/docs/buddy-commands.test.ts similarity index 94% rename from app/Commands/docs/buddy-commands.test.ts rename to storage/framework/core/buddy/src/commands/docs/buddy-commands.test.ts index 82fd5da392..1630ec03d3 100644 --- a/app/Commands/docs/buddy-commands.test.ts +++ b/storage/framework/core/buddy/src/commands/docs/buddy-commands.test.ts @@ -1,4 +1,4 @@ -import type { BuddyCommandInventoryEntry } from '../../../storage/framework/core/buddy/src/commands/list' +import type { BuddyCommandInventoryEntry } from '../list' import { describe, expect, it } from 'bun:test' import { renderBuddyCommandReference } from './buddy-commands' diff --git a/app/Commands/docs/buddy-commands.ts b/storage/framework/core/buddy/src/commands/docs/buddy-commands.ts similarity index 94% rename from app/Commands/docs/buddy-commands.ts rename to storage/framework/core/buddy/src/commands/docs/buddy-commands.ts index a82acb28d3..8f2d7401e0 100644 --- a/app/Commands/docs/buddy-commands.ts +++ b/storage/framework/core/buddy/src/commands/docs/buddy-commands.ts @@ -1,4 +1,4 @@ -import type { BuddyCommandInventoryEntry, BuddyCommandInventoryOption } from '../../../storage/framework/core/buddy/src/commands/list' +import type { BuddyCommandInventoryEntry, BuddyCommandInventoryOption } from '../list' import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs' import { dirname, resolve } from 'node:path' @@ -7,7 +7,7 @@ interface BuddyInventory { total: number } -const root = resolve(import.meta.dir, '../../..') +const root = resolve(import.meta.dir, '../../../../../../..') const outputPath = resolve(root, 'docs/guide/buddy/commands.md') const cliPath = resolve(root, 'storage/framework/core/buddy/src/cli.ts') @@ -89,7 +89,7 @@ export function renderBuddyCommandReference(inventory: BuddyInventory): string { 'title: Buddy Command Reference', 'description: Generated reference for every Buddy command, argument, option, alias, default, and example.', '---', - '', + '', '', '# Buddy Command Reference', '', @@ -128,7 +128,7 @@ export function loadBuddyInventory(): BuddyInventory { export async function run(): Promise { const mode = process.argv.includes('--write') ? 'write' : process.argv.includes('--check') ? 'check' : null if (!mode) { - console.error('usage: bun app/Commands/docs/buddy-commands.ts --write | --check') + console.error('usage: bun storage/framework/core/buddy/src/commands/docs/buddy-commands.ts --write | --check') process.exit(2) } diff --git a/app/Commands/docs/generated-artifacts.test.ts b/storage/framework/core/buddy/src/commands/docs/generated-artifacts.test.ts similarity index 100% rename from app/Commands/docs/generated-artifacts.test.ts rename to storage/framework/core/buddy/src/commands/docs/generated-artifacts.test.ts diff --git a/app/Commands/docs/generated-artifacts.ts b/storage/framework/core/buddy/src/commands/docs/generated-artifacts.ts similarity index 90% rename from app/Commands/docs/generated-artifacts.ts rename to storage/framework/core/buddy/src/commands/docs/generated-artifacts.ts index cc799ff2a5..47eba8ac60 100644 --- a/app/Commands/docs/generated-artifacts.ts +++ b/storage/framework/core/buddy/src/commands/docs/generated-artifacts.ts @@ -1,9 +1,9 @@ import { readFileSync, writeFileSync } from 'node:fs' import { resolve } from 'node:path' -import { generateOpenApi } from '../../../storage/framework/core/api/src/generate-openapi' -import { type OpenApiDocument, renderOpenApiTypes } from '../../../storage/framework/core/api/src/generate-types' +import { generateOpenApi } from '../../../../api/src/generate-openapi' +import { type OpenApiDocument, renderOpenApiTypes } from '../../../../api/src/generate-types' -const root = resolve(import.meta.dir, '../../..') +const root = resolve(import.meta.dir, '../../../../../../..') const openApiPath = resolve(root, 'storage/framework/api/openapi.json') const apiTypesPath = resolve(root, 'storage/framework/api/api-types.ts') @@ -56,7 +56,7 @@ export async function run(): Promise { if (process.argv.includes('--write')) await write() else if (process.argv.includes('--check')) await check() else { - console.error('usage: bun app/Commands/docs/generated-artifacts.ts --write | --check') + console.error('usage: bun storage/framework/core/buddy/src/commands/docs/generated-artifacts.ts --write | --check') process.exit(2) } } diff --git a/app/Commands/docs/links.test.ts b/storage/framework/core/buddy/src/commands/docs/links.test.ts similarity index 100% rename from app/Commands/docs/links.test.ts rename to storage/framework/core/buddy/src/commands/docs/links.test.ts diff --git a/app/Commands/docs/links.ts b/storage/framework/core/buddy/src/commands/docs/links.ts similarity index 97% rename from app/Commands/docs/links.ts rename to storage/framework/core/buddy/src/commands/docs/links.ts index c3d6836ece..cec91e67f0 100644 --- a/app/Commands/docs/links.ts +++ b/storage/framework/core/buddy/src/commands/docs/links.ts @@ -7,13 +7,13 @@ * can reject a broken cross-reference. External links, mail/tel, and same-page * anchors are intentionally left alone. * - * Usage: `bun app/Commands/docs/links.ts [--check]` + * Usage: `bun storage/framework/core/buddy/src/commands/docs/links.ts [--check]` */ import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs' import { dirname, join, relative, resolve } from 'node:path' -const root = resolve(import.meta.dir, '../../..') +const root = resolve(import.meta.dir, '../../../../../../..') const docsDir = resolve(root, 'docs') export interface BrokenDocLink { diff --git a/storage/framework/core/buddy/src/commands/index.ts b/storage/framework/core/buddy/src/commands/index.ts index 748002b556..5b68bad897 100644 --- a/storage/framework/core/buddy/src/commands/index.ts +++ b/storage/framework/core/buddy/src/commands/index.ts @@ -13,6 +13,7 @@ export * from './create' export * from './deploy' export * from './dev' export * from './dns' +export * from './docs' export * from './doctor' export * from './domains' export * from './email' @@ -34,6 +35,7 @@ export * from './phone' export * from './ports' export * from './prepublish' export * from './projects' +export * from './protocol' export * from './publish' export * from './queue' export * from './release' diff --git a/app/Commands/Protocol.ts b/storage/framework/core/buddy/src/commands/protocol.ts similarity index 88% rename from app/Commands/Protocol.ts rename to storage/framework/core/buddy/src/commands/protocol.ts index caaccf19db..f7e50a354c 100644 --- a/app/Commands/Protocol.ts +++ b/storage/framework/core/buddy/src/commands/protocol.ts @@ -11,107 +11,106 @@ import { run as runSync } from './protocol/sync-suite' import { runTool } from './run-tool' /** - * Repo-scoped `buddy protocol:*` commands. The governance tooling itself lives - * beside this file under `app/Commands/protocol/`; the protocol suite + evidence - * DATA stays under `.github/protocol/`. These wrap the tooling so it is - * discoverable via the CLI (`buddy protocol:conformance`) instead of a bare - * `bun app/Commands/protocol/...`. + * Framework-repo `buddy protocol:*` commands. The governance tooling itself + * lives beside this file under `commands/protocol/`; the protocol suite + + * evidence DATA stays under `.github/protocol/`. These wrap the tooling so it + * is discoverable via the CLI (`buddy protocol:conformance`). */ -export default function (cli: CLI): void { - cli +export function protocol(buddy: CLI): void { + buddy .command('protocol:conformance', 'Generate the Stacks protocol conformance report') .action(async () => { await runConformance() }) - cli + buddy .command('protocol:sync', 'Sync the vendored protocol suite from stacksjs/rfcs') .option('--source ', 'Path to a local rfcs checkout', { default: undefined }) .action(async (options: { source?: string }) => { await runTool(runSync, '--write', ...(options.source ? ['--source', options.source] : [])) }) - cli + buddy .command('protocol:check', 'Verify the vendored protocol suite is pinned + internally consistent') .action(async () => { await runTool(runSync, '--check') }) - cli + buddy .command('protocol:manifest', 'Write the protocol source manifest') .option('--revision ', 'Source revision to pin (default HEAD)', { default: undefined }) .action(async (options: { revision?: string }) => { await runTool(runManifest, '--write', ...(options.revision ? ['--revision', options.revision] : [])) }) - cli + buddy .command('protocol:manifest:check', 'Verify the protocol source manifest is current') .action(async () => { await runTool(runManifest, '--check') }) - cli + buddy .command('protocol:release', 'Write the protocol release manifest') .option('--tag ', 'Release tag', { default: undefined }) .action(async (options: { tag?: string }) => { await runTool(runRelease, '--write', ...(options.tag ? ['--tag', options.tag] : [])) }) - cli + buddy .command('protocol:release:check', 'Verify the protocol release manifest is current') .action(async () => { await runTool(runRelease, '--check') }) - cli + buddy .command('protocol:drivers', 'Write the driver capability registry evidence') .action(async () => { await runTool(runDrivers, '--write') }) - cli + buddy .command('protocol:drivers:check', 'Verify the driver capability registry evidence') .action(async () => { await runTool(runDrivers, '--check') }) - cli + buddy .command('protocol:drivers:test', 'Run the driver contract suite') .action(async () => { await runTool(runDriverContracts) }) - cli + buddy .command('protocol:desktop', 'Write the desktop support matrix evidence') .action(async () => { await runTool(runDesktop, '--write') }) - cli + buddy .command('protocol:desktop:check', 'Verify the desktop support matrix evidence') .action(async () => { await runTool(runDesktop, '--check') }) - cli + buddy .command('protocol:pantry', 'Write the pantry evidence') .action(async () => { await runTool(runPantry, '--write') }) - cli + buddy .command('protocol:pantry:check', 'Verify the pantry evidence') .action(async () => { await runTool(runPantry, '--check') }) - cli + buddy .command('protocol:craft', 'Write the Craft evidence') .action(async () => { await runTool(runCraft, '--write') }) - cli + buddy .command('protocol:craft:check', 'Verify the Craft evidence') .action(async () => { await runTool(runCraft, '--check') diff --git a/app/Commands/protocol/craft-evidence.test.ts b/storage/framework/core/buddy/src/commands/protocol/craft-evidence.test.ts similarity index 100% rename from app/Commands/protocol/craft-evidence.test.ts rename to storage/framework/core/buddy/src/commands/protocol/craft-evidence.test.ts diff --git a/app/Commands/protocol/craft-evidence.ts b/storage/framework/core/buddy/src/commands/protocol/craft-evidence.ts similarity index 96% rename from app/Commands/protocol/craft-evidence.ts rename to storage/framework/core/buddy/src/commands/protocol/craft-evidence.ts index e7519da16f..abf744bbbd 100644 --- a/app/Commands/protocol/craft-evidence.ts +++ b/storage/framework/core/buddy/src/commands/protocol/craft-evidence.ts @@ -1,7 +1,7 @@ import { existsSync, readFileSync, writeFileSync } from 'node:fs' import { resolve } from 'node:path' -const root = resolve(import.meta.dir, '../../..') +const root = resolve(import.meta.dir, '../../../../../../..') const outputPath = resolve(root, '.github/protocol/evidence/craft.json') export const craftEvidence = { @@ -87,7 +87,7 @@ export async function run(): Promise { console.log(`Craft evidence pins ${craftEvidence.source.tag} at ${craftEvidence.source.revision}`) } else { - console.error('usage: bun app/Commands/protocol/craft-evidence.ts --write | --check') + console.error('usage: bun storage/framework/core/buddy/src/commands/protocol/craft-evidence.ts --write | --check') process.exit(2) } } diff --git a/app/Commands/protocol/desktop-lifecycle-report.test.ts b/storage/framework/core/buddy/src/commands/protocol/desktop-lifecycle-report.test.ts similarity index 100% rename from app/Commands/protocol/desktop-lifecycle-report.test.ts rename to storage/framework/core/buddy/src/commands/protocol/desktop-lifecycle-report.test.ts diff --git a/app/Commands/protocol/desktop-lifecycle-report.ts b/storage/framework/core/buddy/src/commands/protocol/desktop-lifecycle-report.ts similarity index 100% rename from app/Commands/protocol/desktop-lifecycle-report.ts rename to storage/framework/core/buddy/src/commands/protocol/desktop-lifecycle-report.ts diff --git a/app/Commands/protocol/desktop-support.ts b/storage/framework/core/buddy/src/commands/protocol/desktop-support.ts similarity index 91% rename from app/Commands/protocol/desktop-support.ts rename to storage/framework/core/buddy/src/commands/protocol/desktop-support.ts index 476ed4a11d..d8ab33a74a 100644 --- a/app/Commands/protocol/desktop-support.ts +++ b/storage/framework/core/buddy/src/commands/protocol/desktop-support.ts @@ -1,8 +1,8 @@ import { existsSync, readFileSync, writeFileSync } from 'node:fs' import { resolve } from 'node:path' -import { desktopSupportMatrix } from '../../../storage/framework/core/desktop/src/support' +import { desktopSupportMatrix } from '../../../../desktop/src/support' -const root = resolve(import.meta.dir, '../../..') +const root = resolve(import.meta.dir, '../../../../../../..') const outputPath = resolve(root, '.github/protocol/evidence/desktop-support.json') const document = { schemaVersion: '1.0.0', diff --git a/app/Commands/protocol/desktop-workflow.test.ts b/storage/framework/core/buddy/src/commands/protocol/desktop-workflow.test.ts similarity index 97% rename from app/Commands/protocol/desktop-workflow.test.ts rename to storage/framework/core/buddy/src/commands/protocol/desktop-workflow.test.ts index 5f167a6869..c2034e83ae 100644 --- a/app/Commands/protocol/desktop-workflow.test.ts +++ b/storage/framework/core/buddy/src/commands/protocol/desktop-workflow.test.ts @@ -2,7 +2,7 @@ import { describe, expect, it } from 'bun:test' import { readdirSync, readFileSync } from 'node:fs' import { resolve } from 'node:path' -const root = resolve(import.meta.dir, '../../..') +const root = resolve(import.meta.dir, '../../../../../../..') const workflow = readFileSync(resolve(root, '.github/workflows/desktop-lifecycle.yml'), 'utf8') const workflowDirectory = resolve(root, '.github/workflows') const repositoryWorkflows = readdirSync(workflowDirectory) diff --git a/app/Commands/protocol/driver-registry.ts b/storage/framework/core/buddy/src/commands/protocol/driver-registry.ts similarity index 93% rename from app/Commands/protocol/driver-registry.ts rename to storage/framework/core/buddy/src/commands/protocol/driver-registry.ts index 6e15ff0e1a..796d43c769 100644 --- a/app/Commands/protocol/driver-registry.ts +++ b/storage/framework/core/buddy/src/commands/protocol/driver-registry.ts @@ -1,8 +1,8 @@ import { existsSync, readFileSync, writeFileSync } from 'node:fs' import { resolve } from 'node:path' -import { capabilityRegistry } from '../../../storage/framework/core/config/src/capabilities' +import { capabilityRegistry } from '../../../../config/src/capabilities' -const root = resolve(import.meta.dir, '../../..') +const root = resolve(import.meta.dir, '../../../../../../..') const outputPath = resolve(root, '.github/protocol/evidence/drivers.json') function validate(): string[] { @@ -64,7 +64,7 @@ export async function run(): Promise { console.log(`Driver registry is current (${capabilityRegistry.length} records)`) } else { - console.error('usage: bun app/Commands/protocol/driver-registry.ts --write | --check') + console.error('usage: bun storage/framework/core/buddy/src/commands/protocol/driver-registry.ts --write | --check') process.exit(2) } } diff --git a/app/Commands/protocol/pantry-evidence.test.ts b/storage/framework/core/buddy/src/commands/protocol/pantry-evidence.test.ts similarity index 100% rename from app/Commands/protocol/pantry-evidence.test.ts rename to storage/framework/core/buddy/src/commands/protocol/pantry-evidence.test.ts diff --git a/app/Commands/protocol/pantry-evidence.ts b/storage/framework/core/buddy/src/commands/protocol/pantry-evidence.ts similarity index 96% rename from app/Commands/protocol/pantry-evidence.ts rename to storage/framework/core/buddy/src/commands/protocol/pantry-evidence.ts index 56d2b2f381..8650ab366a 100644 --- a/app/Commands/protocol/pantry-evidence.ts +++ b/storage/framework/core/buddy/src/commands/protocol/pantry-evidence.ts @@ -1,7 +1,7 @@ import { existsSync, readFileSync, writeFileSync } from 'node:fs' import { resolve } from 'node:path' -const root = resolve(import.meta.dir, '../../..') +const root = resolve(import.meta.dir, '../../../../../../..') const outputPath = resolve(root, '.github/protocol/evidence/pantry.json') export const pantryEvidence = { @@ -126,7 +126,7 @@ export async function run(): Promise { } else if (process.argv.includes('--check')) check() else { - console.error('usage: bun app/Commands/protocol/pantry-evidence.ts --write | --check') + console.error('usage: bun storage/framework/core/buddy/src/commands/protocol/pantry-evidence.ts --write | --check') process.exit(2) } } diff --git a/app/Commands/protocol/release-manifest.test.ts b/storage/framework/core/buddy/src/commands/protocol/release-manifest.test.ts similarity index 100% rename from app/Commands/protocol/release-manifest.test.ts rename to storage/framework/core/buddy/src/commands/protocol/release-manifest.test.ts diff --git a/app/Commands/protocol/release-manifest.ts b/storage/framework/core/buddy/src/commands/protocol/release-manifest.ts similarity index 95% rename from app/Commands/protocol/release-manifest.ts rename to storage/framework/core/buddy/src/commands/protocol/release-manifest.ts index 96532f93d9..c060f11511 100644 --- a/app/Commands/protocol/release-manifest.ts +++ b/storage/framework/core/buddy/src/commands/protocol/release-manifest.ts @@ -31,7 +31,7 @@ export interface ReleaseManifest { } } -const root = resolve(import.meta.dir, '../../..') +const root = resolve(import.meta.dir, '../../../../../../..') const outputPath = resolve(root, '.github/protocol/evidence/release-manifest.json') function runGit(...arguments_: string[]): Buffer { @@ -79,7 +79,8 @@ export function buildReleaseManifest(tag: string): ReleaseManifest { const treeListing = runGit('ls-tree', '-r', '-l', sourceRevision).toString() const paths = treeListing.trim().split('\n').filter(Boolean).flatMap((line): string[] => { const match = line.match(/^\d+\s+blob\s+[0-9a-f]+\s+\d+\t(.+)$/) - return match ? [match[1]] : [] + const path = match?.[1] + return path != null ? [path] : [] }) const packages = paths .filter(path => path === 'package.json' || path.endsWith('/package.json')) @@ -132,7 +133,7 @@ export function renderReleaseManifest(manifest: ReleaseManifest): string { export async function run(): Promise { const mode = process.argv.includes('--write') ? 'write' : process.argv.includes('--check') ? 'check' : null if (!mode) { - console.error('usage: bun app/Commands/protocol/release-manifest.ts --write --tag | --check') + console.error('usage: bun storage/framework/core/buddy/src/commands/protocol/release-manifest.ts --write --tag | --check') process.exit(2) } diff --git a/app/Commands/protocol/run-conformance.test.ts b/storage/framework/core/buddy/src/commands/protocol/run-conformance.test.ts similarity index 100% rename from app/Commands/protocol/run-conformance.test.ts rename to storage/framework/core/buddy/src/commands/protocol/run-conformance.test.ts diff --git a/app/Commands/protocol/run-conformance.ts b/storage/framework/core/buddy/src/commands/protocol/run-conformance.ts similarity index 96% rename from app/Commands/protocol/run-conformance.ts rename to storage/framework/core/buddy/src/commands/protocol/run-conformance.ts index f09f538037..cf46a06051 100644 --- a/app/Commands/protocol/run-conformance.ts +++ b/storage/framework/core/buddy/src/commands/protocol/run-conformance.ts @@ -4,14 +4,14 @@ import { arch, platform } from 'node:os' import { resolve } from 'node:path' import Ajv2020 from 'ajv/dist/2020.js' import addFormats from 'ajv-formats' -import { Action } from '../../../storage/framework/core/actions/src/index' -import { assertCapabilityAvailable, capabilityRegistry } from '../../../storage/framework/core/config/src/capabilities' -import { decryptValue, encryptValue, generateKeypair } from '../../../storage/framework/core/env/src/crypto' -import { escapeHtml } from '../../../storage/framework/core/error-handling/src/error-page-template' -import { appPath, defaultsAppPath } from '../../../storage/framework/core/path/src/index' -import { createValidationErrorResponse } from '../../../storage/framework/core/router/src/error-handler' -import { timingSafeEqualString } from '../../../storage/framework/core/security/src/hash' -import { object, string } from '../../../storage/framework/core/validation/src/index' +import { Action } from '../../../../actions/src/index' +import { assertCapabilityAvailable, capabilityRegistry } from '../../../../config/src/capabilities' +import { decryptValue, encryptValue, generateKeypair } from '../../../../env/src/crypto' +import { escapeHtml } from '../../../../error-handling/src/error-page-template' +import { appPath, defaultsAppPath } from '../../../../path/src/index' +import { createValidationErrorResponse } from '../../../../router/src/error-handler' +import { timingSafeEqualString } from '../../../../security/src/hash' +import { object, string } from '../../../../validation/src/index' type ResultStatus = 'pass' | 'fail' | 'skipped' | 'unsupported' | 'exception' | 'experimental' @@ -38,7 +38,7 @@ interface Result { /** Executable-check evidence keyed by requirement id, merged into the report. */ type Evidence = Map> -const root = resolve(import.meta.dir, '../../..') +const root = resolve(import.meta.dir, '../../../../../../..') const suiteRoot = resolve(root, '.github/protocol/suite/1.0-draft') function git(...arguments_: string[]): string { @@ -55,7 +55,9 @@ function exactSourceDigest(): string { function tamper(ciphertext: string): string { const prefix = 'encrypted:' const payload = Buffer.from(ciphertext.slice(prefix.length), 'base64') - payload[payload.length - 1] ^= 1 + const last = payload.length - 1 + if (last >= 0) + payload[last] = (payload[last] ?? 0) ^ 1 return `${prefix}${payload.toString('base64')}` } @@ -271,7 +273,7 @@ export async function executeQueryEvidence(revision: string): Promise // degrades this one check to skipped instead of crashing the whole adapter; // CI and a healthy checkout resolve it and run the check for real. try { - const { sql } = await import('../../../storage/framework/core/database/src/types') + const { sql } = await import('../../../../database/src/types') const attack = '\' OR 1=1 --' const query = sql`SELECT * FROM users WHERE email = ${attack}` as unknown as { sql: string, parameters: unknown[] } const parameterized = query.sql === 'SELECT * FROM users WHERE email = ?' @@ -303,8 +305,8 @@ export async function executeDatabaseEvidence(revision: string): Promise[0]) + const { createQueryBuilder, setConfig } = await import('../../../../query-builder/src/index') + setConfig({ dialect: 'sqlite', database: ':memory:' } as unknown as Parameters[0]) const db = createQueryBuilder() as any // The query builder reuses one global in-memory connection, so a prior run in // the same process leaves the table behind. Drop first for idempotency. diff --git a/app/Commands/protocol/run-driver-contracts.test.ts b/storage/framework/core/buddy/src/commands/protocol/run-driver-contracts.test.ts similarity index 92% rename from app/Commands/protocol/run-driver-contracts.test.ts rename to storage/framework/core/buddy/src/commands/protocol/run-driver-contracts.test.ts index 9957aa2050..a21d54cc78 100644 --- a/app/Commands/protocol/run-driver-contracts.test.ts +++ b/storage/framework/core/buddy/src/commands/protocol/run-driver-contracts.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'bun:test' -import type { CapabilityDriver } from '../../../storage/framework/core/config/src/capabilities' +import type { CapabilityDriver } from '../../../../config/src/capabilities' import { planDriverContracts } from './run-driver-contracts' describe('driver contract matrix planning', () => { diff --git a/app/Commands/protocol/run-driver-contracts.ts b/storage/framework/core/buddy/src/commands/protocol/run-driver-contracts.ts similarity index 95% rename from app/Commands/protocol/run-driver-contracts.ts rename to storage/framework/core/buddy/src/commands/protocol/run-driver-contracts.ts index 283b646dee..68294d3bdf 100644 --- a/app/Commands/protocol/run-driver-contracts.ts +++ b/storage/framework/core/buddy/src/commands/protocol/run-driver-contracts.ts @@ -2,10 +2,10 @@ import { Database } from 'bun:sqlite' import { mkdirSync, writeFileSync } from 'node:fs' import { arch, platform } from 'node:os' import { resolve } from 'node:path' -import type { CapabilityDriver } from '../../../storage/framework/core/config/src/capabilities' -import { capabilityRegistry } from '../../../storage/framework/core/config/src/capabilities' +import type { CapabilityDriver } from '../../../../config/src/capabilities' +import { capabilityRegistry } from '../../../../config/src/capabilities' -const root = resolve(import.meta.dir, '../../..') +const root = resolve(import.meta.dir, '../../../../../../..') const reportRoot = resolve(process.env.PROTOCOL_REPORT_DIR || resolve(root, '.github/protocol/reports')) export function planDriverContracts(drivers: readonly CapabilityDriver[]): { diff --git a/app/Commands/protocol/source-manifest.ts b/storage/framework/core/buddy/src/commands/protocol/source-manifest.ts similarity index 94% rename from app/Commands/protocol/source-manifest.ts rename to storage/framework/core/buddy/src/commands/protocol/source-manifest.ts index 20b6ae3cb4..fa1ae87ed3 100644 --- a/app/Commands/protocol/source-manifest.ts +++ b/storage/framework/core/buddy/src/commands/protocol/source-manifest.ts @@ -27,7 +27,7 @@ interface SourceManifest { protocolSuite: { version: string, rfcsRevision: string } } -const root = resolve(import.meta.dir, '../../..') +const root = resolve(import.meta.dir, '../../../../../../..') const outputPath = resolve(root, '.github/protocol/evidence/source-manifest.json') function runGit(...arguments_: string[]): Buffer { @@ -77,6 +77,8 @@ export function buildSourceManifest(requestedRevision: string): SourceManifest { if (!match) continue const bytes = Number(match[1]) const path = match[2] + if (path == null) + continue paths.push(path) totalBytes += bytes addCount(categories, category(path), bytes) @@ -123,7 +125,7 @@ function render(manifest: SourceManifest): string { export async function run(): Promise { const mode = process.argv.includes('--write') ? 'write' : process.argv.includes('--check') ? 'check' : null if (!mode) { - console.error('usage: bun app/Commands/protocol/source-manifest.ts --write [--revision ] | --check') + console.error('usage: bun storage/framework/core/buddy/src/commands/protocol/source-manifest.ts --write [--revision ] | --check') process.exit(2) } @@ -141,7 +143,7 @@ export async function run(): Promise { const current = JSON.parse(readFileSync(outputPath, 'utf8')) as SourceManifest const expected = render(buildSourceManifest(current.sourceRevision)) if (readFileSync(outputPath, 'utf8') !== expected) - throw new Error(`source manifest is stale or modified; run bun app/Commands/protocol/source-manifest.ts --write --revision ${current.sourceRevision}`) + throw new Error(`source manifest is stale or modified; run bun storage/framework/core/buddy/src/commands/protocol/source-manifest.ts --write --revision ${current.sourceRevision}`) console.log(`Source manifest matches ${current.sourceRevision}`) } } diff --git a/app/Commands/protocol/sync-suite.test.ts b/storage/framework/core/buddy/src/commands/protocol/sync-suite.test.ts similarity index 94% rename from app/Commands/protocol/sync-suite.test.ts rename to storage/framework/core/buddy/src/commands/protocol/sync-suite.test.ts index 63c4ecf6e6..495ecb9f85 100644 --- a/app/Commands/protocol/sync-suite.test.ts +++ b/storage/framework/core/buddy/src/commands/protocol/sync-suite.test.ts @@ -21,7 +21,7 @@ describe('protocol requirement-id uniqueness (stacksjs/stacks#2050)', () => { }) it('the vendored catalog.json has unique requirement ids', () => { - const catalog = JSON.parse(readFileSync(resolve(import.meta.dir, '../../../.github/protocol/suite/1.0-draft/catalog.json'), 'utf8')) as { + const catalog = JSON.parse(readFileSync(resolve(import.meta.dir, '../../../../../../../.github/protocol/suite/1.0-draft/catalog.json'), 'utf8')) as { requirements: Array<{ id: string }> } const ids = catalog.requirements.map(requirement => requirement.id) @@ -56,7 +56,7 @@ describe('protocol fixture-corpus integrity (stacksjs/stacks#2051)', () => { }) it('the vendored fixture corpus has unique ids and no dangling requirement refs', () => { - const base = resolve(import.meta.dir, '../../../.github/protocol/suite/1.0-draft') + const base = resolve(import.meta.dir, '../../../../../../../.github/protocol/suite/1.0-draft') const corpus = JSON.parse(readFileSync(resolve(base, 'fixtures/conformance.json'), 'utf8')) as { fixtures: Array<{ id: string, requirements?: string[] }> } diff --git a/app/Commands/protocol/sync-suite.ts b/storage/framework/core/buddy/src/commands/protocol/sync-suite.ts similarity index 97% rename from app/Commands/protocol/sync-suite.ts rename to storage/framework/core/buddy/src/commands/protocol/sync-suite.ts index 42f19f8fb1..86f50713eb 100644 --- a/app/Commands/protocol/sync-suite.ts +++ b/storage/framework/core/buddy/src/commands/protocol/sync-suite.ts @@ -9,7 +9,7 @@ interface SuiteLock { files: Record } -const root = resolve(import.meta.dir, '../../..') +const root = resolve(import.meta.dir, '../../../../../../..') const suiteRoot = resolve(root, '.github/protocol/suite/1.0-draft') const lockPath = resolve(root, '.github/protocol/suite.lock.json') @@ -200,7 +200,7 @@ export async function run(): Promise { checkFixtures() } else { - console.error('usage: bun app/Commands/protocol/sync-suite.ts --write [--source ../rfcs] | --check') + console.error('usage: bun storage/framework/core/buddy/src/commands/protocol/sync-suite.ts --write [--source ../rfcs] | --check') process.exit(2) } } diff --git a/app/Commands/run-tool.ts b/storage/framework/core/buddy/src/commands/run-tool.ts similarity index 100% rename from app/Commands/run-tool.ts rename to storage/framework/core/buddy/src/commands/run-tool.ts diff --git a/storage/framework/core/buddy/src/lazy-commands.ts b/storage/framework/core/buddy/src/lazy-commands.ts index 49beaea89e..d78e1a648b 100644 --- a/storage/framework/core/buddy/src/lazy-commands.ts +++ b/storage/framework/core/buddy/src/lazy-commands.ts @@ -135,6 +135,34 @@ const commandRegistry: Record = { 'undeploy': { path: './commands/cloud.ts', exportName: 'cloud' }, // Alias for cloud:remove 'upgrade': { path: './commands/upgrade.ts', exportName: 'upgrade' }, 'version': { path: './commands/version.ts', exportName: 'version' }, + + // Protocol governance (framework-repo tooling; registrar in commands/protocol.ts, + // tooling under commands/protocol/, evidence DATA under .github/protocol/). + 'protocol:conformance': { path: './commands/protocol.ts', exportName: 'protocol' }, + 'protocol:sync': { path: './commands/protocol.ts', exportName: 'protocol' }, + 'protocol:check': { path: './commands/protocol.ts', exportName: 'protocol' }, + 'protocol:manifest': { path: './commands/protocol.ts', exportName: 'protocol' }, + 'protocol:manifest:check': { path: './commands/protocol.ts', exportName: 'protocol' }, + 'protocol:release': { path: './commands/protocol.ts', exportName: 'protocol' }, + 'protocol:release:check': { path: './commands/protocol.ts', exportName: 'protocol' }, + 'protocol:drivers': { path: './commands/protocol.ts', exportName: 'protocol' }, + 'protocol:drivers:check': { path: './commands/protocol.ts', exportName: 'protocol' }, + 'protocol:drivers:test': { path: './commands/protocol.ts', exportName: 'protocol' }, + 'protocol:desktop': { path: './commands/protocol.ts', exportName: 'protocol' }, + 'protocol:desktop:check': { path: './commands/protocol.ts', exportName: 'protocol' }, + 'protocol:pantry': { path: './commands/protocol.ts', exportName: 'protocol' }, + 'protocol:pantry:check': { path: './commands/protocol.ts', exportName: 'protocol' }, + 'protocol:craft': { path: './commands/protocol.ts', exportName: 'protocol' }, + 'protocol:craft:check': { path: './commands/protocol.ts', exportName: 'protocol' }, + + // Docs freshness (framework-repo tooling; registrar in commands/docs.ts, + // tooling under commands/docs/). + 'docs:buddy': { path: './commands/docs.ts', exportName: 'docs' }, + 'docs:buddy:check': { path: './commands/docs.ts', exportName: 'docs' }, + 'docs:artifacts': { path: './commands/docs.ts', exportName: 'docs' }, + 'docs:artifacts:check': { path: './commands/docs.ts', exportName: 'docs' }, + 'docs:links': { path: './commands/docs.ts', exportName: 'docs' }, + 'docs:links:check': { path: './commands/docs.ts', exportName: 'docs' }, } // Commands that are commonly used together (for batch loading optimization)