@stacksjs/buddy 0.70.163 → 0.70.165

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/README.md +1 -1
  2. package/dist/cli.js +1 -0
  3. package/dist/commands/ai-context.js +1 -1
  4. package/dist/commands/create.js +1 -1
  5. package/dist/commands/deploy.d.ts +11 -1
  6. package/dist/commands/deploy.js +22 -4
  7. package/dist/commands/dev.js +62 -14
  8. package/dist/commands/docs/buddy-commands.d.ts +8 -0
  9. package/dist/commands/docs/buddy-commands.js +116 -0
  10. package/dist/commands/docs/buddy-commands.test.d.ts +1 -0
  11. package/dist/commands/docs/buddy-commands.test.js +38 -0
  12. package/dist/commands/docs/generated-artifacts.d.ts +3 -0
  13. package/dist/commands/docs/generated-artifacts.js +69 -0
  14. package/dist/commands/docs/generated-artifacts.test.d.ts +1 -0
  15. package/dist/commands/docs/generated-artifacts.test.js +19 -0
  16. package/dist/commands/docs/links.d.ts +18 -0
  17. package/dist/commands/docs/links.js +83 -0
  18. package/dist/commands/docs/links.test.d.ts +1 -0
  19. package/dist/commands/docs/links.test.js +73 -0
  20. package/dist/commands/docs.d.ts +7 -0
  21. package/dist/commands/docs.js +24 -0
  22. package/dist/commands/doctor.js +8 -0
  23. package/dist/commands/env.d.ts +14 -0
  24. package/dist/commands/env.js +73 -7
  25. package/dist/commands/generate.js +1 -1
  26. package/dist/commands/index.d.ts +2 -0
  27. package/dist/commands/index.js +2 -0
  28. package/dist/commands/mail.js +1 -1
  29. package/dist/commands/migrate.js +4 -4
  30. package/dist/commands/protocol/craft-evidence.d.ts +76 -0
  31. package/dist/commands/protocol/craft-evidence.js +98 -0
  32. package/dist/commands/protocol/craft-evidence.test.d.ts +1 -0
  33. package/dist/commands/protocol/craft-evidence.test.js +20 -0
  34. package/dist/commands/protocol/desktop-lifecycle-report.d.ts +16 -0
  35. package/dist/commands/protocol/desktop-lifecycle-report.js +57 -0
  36. package/dist/commands/protocol/desktop-lifecycle-report.test.d.ts +1 -0
  37. package/dist/commands/protocol/desktop-lifecycle-report.test.js +32 -0
  38. package/dist/commands/protocol/desktop-support.d.ts +1 -0
  39. package/dist/commands/protocol/desktop-support.js +32 -0
  40. package/dist/commands/protocol/desktop-workflow.test.d.ts +1 -0
  41. package/dist/commands/protocol/desktop-workflow.test.js +33 -0
  42. package/dist/commands/protocol/driver-registry.d.ts +1 -0
  43. package/dist/commands/protocol/driver-registry.js +71 -0
  44. package/dist/commands/protocol/pantry-evidence.d.ts +67 -0
  45. package/dist/commands/protocol/pantry-evidence.js +144 -0
  46. package/dist/commands/protocol/pantry-evidence.test.d.ts +1 -0
  47. package/dist/commands/protocol/pantry-evidence.test.js +29 -0
  48. package/dist/commands/protocol/release-manifest.d.ts +30 -0
  49. package/dist/commands/protocol/release-manifest.js +102 -0
  50. package/dist/commands/protocol/release-manifest.test.d.ts +1 -0
  51. package/dist/commands/protocol/release-manifest.test.js +34 -0
  52. package/dist/commands/protocol/run-conformance.d.ts +21 -0
  53. package/dist/commands/protocol/run-conformance.js +358 -0
  54. package/dist/commands/protocol/run-conformance.test.d.ts +1 -0
  55. package/dist/commands/protocol/run-conformance.test.js +52 -0
  56. package/dist/commands/protocol/run-driver-contracts.d.ts +7 -0
  57. package/dist/commands/protocol/run-driver-contracts.js +94 -0
  58. package/dist/commands/protocol/run-driver-contracts.test.d.ts +1 -0
  59. package/dist/commands/protocol/run-driver-contracts.test.js +18 -0
  60. package/dist/commands/protocol/source-manifest.d.ts +25 -0
  61. package/dist/commands/protocol/source-manifest.js +113 -0
  62. package/dist/commands/protocol/sync-suite.d.ts +8 -0
  63. package/dist/commands/protocol/sync-suite.js +137 -0
  64. package/dist/commands/protocol/sync-suite.test.d.ts +1 -0
  65. package/dist/commands/protocol/sync-suite.test.js +44 -0
  66. package/dist/commands/protocol.d.ts +8 -0
  67. package/dist/commands/protocol.js +60 -0
  68. package/dist/commands/publish.js +149 -0
  69. package/dist/commands/run-tool.d.ts +7 -0
  70. package/dist/commands/run-tool.js +9 -0
  71. package/dist/commands/seed.js +15 -40
  72. package/dist/commands/serve.d.ts +18 -5
  73. package/dist/commands/serve.js +21 -2
  74. package/dist/commands/setup-ai.d.ts +39 -0
  75. package/dist/commands/setup-ai.js +121 -0
  76. package/dist/commands/setup.js +22 -0
  77. package/dist/lazy-commands.js +27 -1
  78. package/package.json +43 -42
@@ -0,0 +1,32 @@
1
+ import { describe, expect, it } from "bun:test";
2
+ import { validateLifecycleReport } from "./desktop-lifecycle-report";
3
+ const steps = ["install v1", "launch v1", "update to v2", "launch v2", "rollback to v1", "launch rollback", "uninstall", "verify uninstall"], revision = "d".repeat(40);
4
+ describe("desktop lifecycle report validation", () => {
5
+ it("requires a complete source-pinned lifecycle and two versioned artifacts per format", () => {
6
+ const report = {
7
+ revision,
8
+ status: "passed",
9
+ installLifecycleExercised: !0,
10
+ runner: { os: "linux", arch: "x64" },
11
+ steps: steps.map((name) => ({ name, status: "passed" })),
12
+ artifacts: [{ format: "deb", sha256: "a".repeat(64) }, { format: "deb", sha256: "b".repeat(64) }]
13
+ };
14
+ expect(validateLifecycleReport(report, { revision, platform: "linux", architecture: "x64", formats: ["deb"] })).toEqual([]);
15
+ });
16
+ it("rejects missing rollback, wrong source, and invalid artifact evidence", () => {
17
+ const errors = validateLifecycleReport({
18
+ revision: "wrong",
19
+ status: "passed",
20
+ installLifecycleExercised: !0,
21
+ runner: { os: "linux", arch: "x64" },
22
+ steps: steps.filter((name) => name !== "rollback to v1").map((name) => ({ name, status: "passed" })),
23
+ artifacts: [{ format: "deb", sha256: "short" }]
24
+ }, { revision, platform: "linux", architecture: "x64", formats: ["deb"] });
25
+ expect(errors).toEqual(expect.arrayContaining([
26
+ "Craft source revision mismatch: wrong",
27
+ "lifecycle step did not pass: rollback to v1",
28
+ "expected two deb artifacts, found 1",
29
+ "deb artifact has an invalid digest"
30
+ ]));
31
+ });
32
+ });
@@ -0,0 +1 @@
1
+ export declare function run(): void;
@@ -0,0 +1,32 @@
1
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { resolve } from "node:path";
3
+ import { desktopSupportMatrix } from "../../../../desktop/src/support";
4
+ const root = resolve(import.meta.dir, "../../../../../../.."), outputPath = resolve(root, ".github/protocol/evidence/desktop-support.json"), document = {
5
+ schemaVersion: "1.0.0",
6
+ generatedFrom: "storage/framework/core/desktop/src/support.ts",
7
+ stableTargets: desktopSupportMatrix.filter((row) => row.status === "stable").length,
8
+ externalGates: [
9
+ "https://github.com/stacksjs/stacks/issues/2059",
10
+ "https://github.com/stacksjs/stacks/issues/2062",
11
+ "https://github.com/stacksjs/stacks/issues/2063"
12
+ ],
13
+ targets: desktopSupportMatrix
14
+ }, expected = `${JSON.stringify(document, null, 2)}
15
+ `;
16
+ export function run() {
17
+ if (process.argv.includes("--write")) {
18
+ writeFileSync(outputPath, expected);
19
+ console.log(`Wrote desktop support matrix (${document.stableTargets} stable targets)`);
20
+ } else if (process.argv.includes("--check")) {
21
+ if (!existsSync(outputPath) || readFileSync(outputPath, "utf8") !== expected)
22
+ throw Error("desktop support evidence is stale; run buddy protocol:desktop");
23
+ if (document.targets.some((row) => row.status === "stable" && (!row.installLaunchEvidence || !row.updateRollbackEvidence || row.signing !== "enforced")))
24
+ throw Error("stable desktop target lacks required evidence");
25
+ console.log(`Desktop support matrix is current (${document.stableTargets} stable targets)`);
26
+ } else {
27
+ console.error("usage: buddy protocol:desktop --write | --check");
28
+ process.exit(2);
29
+ }
30
+ }
31
+ if (import.meta.main)
32
+ run();
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,33 @@
1
+ import { describe, expect, it } from "bun:test";
2
+ import { readdirSync, readFileSync } from "node:fs";
3
+ import { resolve } from "node:path";
4
+ const root = resolve(import.meta.dir, "../../../../../../.."), workflow = readFileSync(resolve(root, ".github/workflows/desktop-lifecycle.yml"), "utf8"), workflowDirectory = resolve(root, ".github/workflows"), repositoryWorkflows = readdirSync(workflowDirectory).filter((path) => path.endsWith(".yml") || path.endsWith(".yaml")).map((path) => readFileSync(resolve(workflowDirectory, path), "utf8")).join(`
5
+ `);
6
+ describe("desktop lifecycle workflow", () => {
7
+ it("packages Craft before allocating the Stacks dependency graph", () => {
8
+ const craftInstall = workflow.indexOf("- name: Install Craft dependencies"), lifecycle = workflow.indexOf("- name: Exercise native package lifecycle"), reclaim = workflow.indexOf("- name: Reclaim Craft dependency space"), stacksInstall = workflow.indexOf("- name: Install Stacks dependencies"), stacksTests = workflow.indexOf("- name: Verify Stacks update security contracts");
9
+ expect([craftInstall, lifecycle, reclaim, stacksInstall, stacksTests].every((index) => index >= 0)).toBe(!0);
10
+ expect(craftInstall).toBeLessThan(lifecycle);
11
+ expect(lifecycle).toBeLessThan(reclaim);
12
+ expect(reclaim).toBeLessThan(stacksInstall);
13
+ expect(stacksInstall).toBeLessThan(stacksTests);
14
+ expect(workflow).toContain("rm -rf .craft-contract/node_modules");
15
+ });
16
+ it("provisions explicit macOS descriptor capacity for Bun installs", () => {
17
+ expect(workflow.match(/ulimit -n 65536/g)).toHaveLength(2);
18
+ expect(workflow).toContain('if [ "$RUNNER_OS" = "macOS" ]');
19
+ });
20
+ it("uses a compatible preinstalled WiX 3.14 before installing it", () => {
21
+ const discovery = workflow.indexOf("$bin = Get-ChildItem"), install = workflow.indexOf("choco install wixtoolset");
22
+ expect(discovery).toBeGreaterThan(0);
23
+ expect(discovery).toBeLessThan(install);
24
+ expect(workflow).toContain("$version.StartsWith('3.14')");
25
+ expect(workflow).toContain("--allow-downgrade");
26
+ });
27
+ it("retains artifacts through maintained Node 24 action runtimes", () => {
28
+ expect(repositoryWorkflows).not.toMatch(/actions\/upload-artifact@v[1-6]\b/);
29
+ expect(repositoryWorkflows).not.toMatch(/actions\/download-artifact@v[1-7]\b/);
30
+ expect(repositoryWorkflows.match(/actions\/upload-artifact@v7\b/g)).toHaveLength(3);
31
+ expect(repositoryWorkflows.match(/actions\/download-artifact@v8\b/g)).toHaveLength(1);
32
+ });
33
+ });
@@ -0,0 +1 @@
1
+ export declare function run(): Promise<void>;
@@ -0,0 +1,71 @@
1
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { resolve } from "node:path";
3
+ import { capabilityRegistry } from "../../../../config/src/capabilities";
4
+ const root = resolve(import.meta.dir, "../../../../../../.."), outputPath = resolve(root, ".github/protocol/evidence/drivers.json");
5
+ function validate() {
6
+ const errors = [], seen = new Set, categories = new Set(["database", "queue", "cache", "storage", "mail", "realtime", "deploy"]);
7
+ for (const driver of capabilityRegistry) {
8
+ const id = `${driver.category}/${driver.name}`;
9
+ if (seen.has(id))
10
+ errors.push(`${id}: duplicate registry entry`);
11
+ seen.add(id);
12
+ if (!categories.has(driver.category))
13
+ errors.push(`${id}: unknown category`);
14
+ if (driver.status === "supported" && driver.testEvidence.length === 0)
15
+ errors.push(`${id}: supported driver has no test evidence`);
16
+ if (driver.status === "supported" && /client-server|managed|smtp|redis/.test(driver.topology) && !driver.liveServiceContract)
17
+ errors.push(`${id}: externally hosted driver cannot be supported without a retained live-service contract`);
18
+ if (driver.liveServiceContract && !existsSync(resolve(root, driver.liveServiceContract.workflow)))
19
+ errors.push(`${id}: live-service workflow does not exist: ${driver.liveServiceContract.workflow}`);
20
+ if (driver.status !== "supported" && driver.limitations.length === 0)
21
+ errors.push(`${id}: non-supported driver must explain its evidence gap`);
22
+ if (driver.status !== "unsupported" && !driver.implementation)
23
+ errors.push(`${id}: operational driver has no implementation`);
24
+ if (driver.implementation && !existsSync(resolve(root, driver.implementation)))
25
+ errors.push(`${id}: implementation path does not exist`);
26
+ for (const testPath of driver.testEvidence)
27
+ if (!existsSync(resolve(root, testPath)))
28
+ errors.push(`${id}: test evidence does not exist: ${testPath}`);
29
+ }
30
+ for (const category of categories)
31
+ if (!capabilityRegistry.some((driver) => driver.category === category))
32
+ errors.push(`${category}: no drivers registered`);
33
+ return errors;
34
+ }
35
+ function render() {
36
+ const document = {
37
+ schemaVersion: "1.0.0",
38
+ generatedFrom: "storage/framework/core/config/src/capabilities.ts",
39
+ resultSemantics: {
40
+ supported: "Implemented, selected by configuration, and backed by checked-in automated evidence.",
41
+ partial: "Implemented, but missing live-service or feature-parity evidence.",
42
+ experimental: "Implemented for evaluation without a compatibility commitment or complete matrix.",
43
+ unsupported: "Reserved, planned, or intentionally unavailable; selection must fail loudly."
44
+ },
45
+ drivers: [...capabilityRegistry].sort((a, b) => a.category.localeCompare(b.category) || a.name.localeCompare(b.name))
46
+ };
47
+ return `${JSON.stringify(document, null, 2)}
48
+ `;
49
+ }
50
+ export async function run() {
51
+ const errors = validate();
52
+ if (errors.length > 0) {
53
+ for (const error of errors)
54
+ console.error(`error: ${error}`);
55
+ process.exit(1);
56
+ }
57
+ const expected = render();
58
+ if (process.argv.includes("--write")) {
59
+ writeFileSync(outputPath, expected);
60
+ console.log(`Wrote ${capabilityRegistry.length} driver capability records`);
61
+ } else if (process.argv.includes("--check")) {
62
+ if (!existsSync(outputPath) || readFileSync(outputPath, "utf8") !== expected)
63
+ throw Error("driver registry evidence is stale; run bun run protocol:drivers");
64
+ console.log(`Driver registry is current (${capabilityRegistry.length} records)`);
65
+ } else {
66
+ console.error("usage: bun storage/framework/core/buddy/src/commands/protocol/driver-registry.ts --write | --check");
67
+ process.exit(2);
68
+ }
69
+ }
70
+ if (import.meta.main)
71
+ await run();
@@ -0,0 +1,67 @@
1
+ export declare function validatePantryEvidence(evidence: typeof pantryEvidence): string[];
2
+ export declare function run(): Promise<void>;
3
+ export declare const pantryEvidence: {
4
+ schemaVersion: 1;
5
+ status: 'pinned';
6
+ issue: 'https://github.com/stacksjs/stacks/issues/2066';
7
+ source: {
8
+ repository: 'https://github.com/pantry-pm/pantry';
9
+ version: '0.10.47';
10
+ tag: 'v0.10.47';
11
+ revision: 'd738d5fd2e543e3b8380e5fbd5d01ba8936c1790'
12
+ };
13
+ whitepaper: {
14
+ repository: 'https://github.com/stacksjs/white-paper';
15
+ revision: 'ac6d1dfba8cb96cf6e888f3369cbcc3e17cdf202';
16
+ evidenceLock: 'evidence/pantry/evidence.lock.json'
17
+ };
18
+ contracts: readonly [{
19
+ id: 'package-manager';
20
+ sourcePath: 'docs/package-manager.md';
21
+ sha256: '03bab0e0373e2405a4f0656745786702a19c3b3a0ffb9be09ca0931f85a5158e';
22
+ referencePath: 'docs/reference/package-manager.md'
23
+ }, {
24
+ id: 'registry';
25
+ sourcePath: 'docs/registry.md';
26
+ sha256: 'c16a2ef8855d99b34398bddfd6f574e0e3001ce8a769dc6dd6792af418c4724b';
27
+ referencePath: 'docs/reference/registry.md'
28
+ }];
29
+ serviceContracts: readonly [{
30
+ id: 'github-actions-service-interface';
31
+ sourcePath: 'packages/action/action.yml';
32
+ sha256: '06f4ac3f3307a474e493abe56f1e8f7d99f7af8ce49cecbf3dfdd2e55333ea13'
33
+ }, {
34
+ id: 'github-actions-redis-lifecycle';
35
+ sourcePath: 'packages/action/src/services.ts';
36
+ sha256: '7e88c89880bf1dc42cfee44f5a741b7659b2c2a1aced7da9c23520ed28a8afe9'
37
+ }, {
38
+ id: 'github-actions-redis-orchestration';
39
+ sourcePath: 'packages/action/src/index.ts';
40
+ sha256: '3fd905facb4109f0a3e65d743c522eb72b934dea466531aeb24c468ab4fc76ab'
41
+ }, {
42
+ id: 'github-actions-install-mode';
43
+ sourcePath: 'packages/action/src/install-mode.ts';
44
+ sha256: 'e3ff33f0197eb9607d9a74efd946d34c76aad21ed4af4b2a89169aa04db30581'
45
+ }, {
46
+ id: 'github-actions-redis-cleanup';
47
+ sourcePath: 'packages/action/src/post.ts';
48
+ sha256: '9dbfa452e86a61ec75da8c461d69d10a4f493765c19523ba999ee4f916ccde54'
49
+ }, {
50
+ id: 'native-service-readiness';
51
+ sourcePath: 'packages/zig/src/cli/commands/services.zig';
52
+ sha256: '7da9d6271b8dc755865db26a366202448cc408e0d2e3b0daa0a3b8cb917fd748'
53
+ }, {
54
+ id: 'native-redis-definition';
55
+ sourcePath: 'packages/zig/src/services/definitions.zig';
56
+ sha256: 'faf362b6a9f037350b6baf7204f424e78b9bfe8704dfdfcf26e1daa1b5dce678'
57
+ }];
58
+ verification: {
59
+ documentationContracts: 'bun run docs:contracts:check (54 source-linked markers)';
60
+ targetedBunTests: '332 passed, 6 skipped, 0 failed';
61
+ typecheck: 'bun run typecheck';
62
+ nativeTests: '29 semver tests passed; zig build test completed 32/32 steps';
63
+ actionRedisService: 'Pantry CI action-redis-service job (Redis 8.8.0)';
64
+ whitepaperCheck: 'bun run evidence:check'
65
+ };
66
+ boundaries: readonly ['Pantry system packages, npm-compatible packages, and workspace/local packages use distinct resolution paths.', 'npm fallback is read-only and does not publish npm packages into Pantry.', 'Configured storage and authentication modes do not by themselves prove production readiness.', 'Stacks consumes Pantry as a versioned toolchain boundary and does not redefine Pantry behavior.']
67
+ };
@@ -0,0 +1,144 @@
1
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { resolve } from "node:path";
3
+ const root = resolve(import.meta.dir, "../../../../../../.."), outputPath = resolve(root, ".github/protocol/evidence/pantry.json");
4
+ export const pantryEvidence = {
5
+ schemaVersion: 1,
6
+ status: "pinned",
7
+ issue: "https://github.com/stacksjs/stacks/issues/2066",
8
+ source: {
9
+ repository: "https://github.com/pantry-pm/pantry",
10
+ version: "0.10.47",
11
+ tag: "v0.10.47",
12
+ revision: "d738d5fd2e543e3b8380e5fbd5d01ba8936c1790"
13
+ },
14
+ whitepaper: {
15
+ repository: "https://github.com/stacksjs/white-paper",
16
+ revision: "ac6d1dfba8cb96cf6e888f3369cbcc3e17cdf202",
17
+ evidenceLock: "evidence/pantry/evidence.lock.json"
18
+ },
19
+ contracts: [
20
+ {
21
+ id: "package-manager",
22
+ sourcePath: "docs/package-manager.md",
23
+ sha256: "03bab0e0373e2405a4f0656745786702a19c3b3a0ffb9be09ca0931f85a5158e",
24
+ referencePath: "docs/reference/package-manager.md"
25
+ },
26
+ {
27
+ id: "registry",
28
+ sourcePath: "docs/registry.md",
29
+ sha256: "c16a2ef8855d99b34398bddfd6f574e0e3001ce8a769dc6dd6792af418c4724b",
30
+ referencePath: "docs/reference/registry.md"
31
+ }
32
+ ],
33
+ serviceContracts: [
34
+ {
35
+ id: "github-actions-service-interface",
36
+ sourcePath: "packages/action/action.yml",
37
+ sha256: "06f4ac3f3307a474e493abe56f1e8f7d99f7af8ce49cecbf3dfdd2e55333ea13"
38
+ },
39
+ {
40
+ id: "github-actions-redis-lifecycle",
41
+ sourcePath: "packages/action/src/services.ts",
42
+ sha256: "7e88c89880bf1dc42cfee44f5a741b7659b2c2a1aced7da9c23520ed28a8afe9"
43
+ },
44
+ {
45
+ id: "github-actions-redis-orchestration",
46
+ sourcePath: "packages/action/src/index.ts",
47
+ sha256: "3fd905facb4109f0a3e65d743c522eb72b934dea466531aeb24c468ab4fc76ab"
48
+ },
49
+ {
50
+ id: "github-actions-install-mode",
51
+ sourcePath: "packages/action/src/install-mode.ts",
52
+ sha256: "e3ff33f0197eb9607d9a74efd946d34c76aad21ed4af4b2a89169aa04db30581"
53
+ },
54
+ {
55
+ id: "github-actions-redis-cleanup",
56
+ sourcePath: "packages/action/src/post.ts",
57
+ sha256: "9dbfa452e86a61ec75da8c461d69d10a4f493765c19523ba999ee4f916ccde54"
58
+ },
59
+ {
60
+ id: "native-service-readiness",
61
+ sourcePath: "packages/zig/src/cli/commands/services.zig",
62
+ sha256: "7da9d6271b8dc755865db26a366202448cc408e0d2e3b0daa0a3b8cb917fd748"
63
+ },
64
+ {
65
+ id: "native-redis-definition",
66
+ sourcePath: "packages/zig/src/services/definitions.zig",
67
+ sha256: "faf362b6a9f037350b6baf7204f424e78b9bfe8704dfdfcf26e1daa1b5dce678"
68
+ }
69
+ ],
70
+ verification: {
71
+ documentationContracts: "bun run docs:contracts:check (54 source-linked markers)",
72
+ targetedBunTests: "332 passed, 6 skipped, 0 failed",
73
+ typecheck: "bun run typecheck",
74
+ nativeTests: "29 semver tests passed; zig build test completed 32/32 steps",
75
+ actionRedisService: "Pantry CI action-redis-service job (Redis 8.8.0)",
76
+ whitepaperCheck: "bun run evidence:check"
77
+ },
78
+ boundaries: [
79
+ "Pantry system packages, npm-compatible packages, and workspace/local packages use distinct resolution paths.",
80
+ "npm fallback is read-only and does not publish npm packages into Pantry.",
81
+ "Configured storage and authentication modes do not by themselves prove production readiness.",
82
+ "Stacks consumes Pantry as a versioned toolchain boundary and does not redefine Pantry behavior."
83
+ ]
84
+ };
85
+ export function validatePantryEvidence(evidence) {
86
+ const errors = [];
87
+ if (evidence.source.tag !== `v${evidence.source.version}`)
88
+ errors.push("Pantry tag does not match its version");
89
+ if (!/^[a-f0-9]{40}$/.test(evidence.source.revision))
90
+ errors.push("Pantry revision is not a full commit SHA");
91
+ if (!/^[a-f0-9]{40}$/.test(evidence.whitepaper.revision))
92
+ errors.push("whitepaper revision is not a full commit SHA");
93
+ if (evidence.contracts.length !== 2)
94
+ errors.push("both canonical Pantry contracts must be pinned");
95
+ for (const contract of evidence.contracts) {
96
+ if (!/^[a-f0-9]{64}$/.test(contract.sha256))
97
+ errors.push(`${contract.id}: invalid SHA-256 digest`);
98
+ if (!contract.sourcePath.endsWith(".md"))
99
+ errors.push(`${contract.id}: source contract is not Markdown`);
100
+ if (!contract.referencePath.startsWith("docs/reference/"))
101
+ errors.push(`${contract.id}: whitepaper reference path is invalid`);
102
+ }
103
+ if (evidence.serviceContracts.length !== 7)
104
+ errors.push("all Pantry Redis service contracts must be pinned");
105
+ for (const contract of evidence.serviceContracts) {
106
+ if (!/^[a-f0-9]{64}$/.test(contract.sha256))
107
+ errors.push(`${contract.id}: invalid SHA-256 digest`);
108
+ if (!contract.sourcePath.startsWith("packages/"))
109
+ errors.push(`${contract.id}: source path is outside Pantry packages`);
110
+ }
111
+ return errors;
112
+ }
113
+ function serializedEvidence() {
114
+ return `${JSON.stringify(pantryEvidence, null, 2)}
115
+ `;
116
+ }
117
+ function check() {
118
+ const errors = validatePantryEvidence(pantryEvidence);
119
+ if (!existsSync(outputPath))
120
+ errors.push(".github/protocol/evidence/pantry.json is missing");
121
+ else if (readFileSync(outputPath, "utf8") !== serializedEvidence())
122
+ errors.push("Pantry evidence is stale; run bun run protocol:pantry");
123
+ if (errors.length)
124
+ throw Error(errors.join(`
125
+ `));
126
+ console.log(`Pantry evidence pins ${pantryEvidence.source.tag} at ${pantryEvidence.source.revision}`);
127
+ }
128
+ export async function run() {
129
+ if (process.argv.includes("--write")) {
130
+ const errors = validatePantryEvidence(pantryEvidence);
131
+ if (errors.length)
132
+ throw Error(errors.join(`
133
+ `));
134
+ writeFileSync(outputPath, serializedEvidence());
135
+ console.log(`Wrote Pantry evidence for ${pantryEvidence.source.tag}`);
136
+ } else if (process.argv.includes("--check"))
137
+ check();
138
+ else {
139
+ console.error("usage: bun storage/framework/core/buddy/src/commands/protocol/pantry-evidence.ts --write | --check");
140
+ process.exit(2);
141
+ }
142
+ }
143
+ if (import.meta.main)
144
+ await run();
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,29 @@
1
+ import { describe, expect, it } from "bun:test";
2
+ import { pantryEvidence, validatePantryEvidence } from "./pantry-evidence";
3
+ describe("Pantry protocol evidence", () => {
4
+ it("pins both verified upstream contracts", () => {
5
+ expect(validatePantryEvidence(pantryEvidence)).toEqual([]);
6
+ expect(pantryEvidence.contracts.map((contract) => contract.id)).toEqual(["package-manager", "registry"]);
7
+ expect(pantryEvidence.serviceContracts.map((contract) => contract.id)).toEqual([
8
+ "github-actions-service-interface",
9
+ "github-actions-redis-lifecycle",
10
+ "github-actions-redis-orchestration",
11
+ "github-actions-install-mode",
12
+ "github-actions-redis-cleanup",
13
+ "native-service-readiness",
14
+ "native-redis-definition"
15
+ ]);
16
+ });
17
+ it("rejects an untraceable release or contract digest", () => {
18
+ const invalid = structuredClone(pantryEvidence);
19
+ Object.assign(invalid.source, { tag: "latest", revision: "short" });
20
+ Object.assign(invalid.contracts[0], { sha256: "not-a-digest" });
21
+ Object.assign(invalid.serviceContracts[0], { sha256: "not-a-digest" });
22
+ expect(validatePantryEvidence(invalid)).toEqual(expect.arrayContaining([
23
+ "Pantry tag does not match its version",
24
+ "Pantry revision is not a full commit SHA",
25
+ "package-manager: invalid SHA-256 digest",
26
+ "github-actions-service-interface: invalid SHA-256 digest"
27
+ ]));
28
+ });
29
+ });
@@ -0,0 +1,30 @@
1
+ export declare function assertReleaseMapping(tag: string, releasePackageVersion?: string): void;
2
+ export declare function buildReleaseManifest(tag: string): ReleaseManifest;
3
+ export declare function renderReleaseManifest(manifest: ReleaseManifest): string;
4
+ export declare function run(): Promise<void>;
5
+ export declare interface ReleasePackageEntry {
6
+ name: string
7
+ path: string
8
+ version: string
9
+ publishable: boolean
10
+ }
11
+ export declare interface ReleaseManifest {
12
+ schemaVersion: '1.0.0'
13
+ repository: string
14
+ tag: string
15
+ tagType: 'annotated' | 'lightweight'
16
+ releaseVersion: string
17
+ releasePackage: string
18
+ sourceRevision: string
19
+ sourceTree: string
20
+ sourceDigest: string
21
+ generatedAt: 'derived-from-immutable-git-objects'
22
+ rootPackage: { name: string, version: string, private: boolean }
23
+ prerequisites: Record<string, string>
24
+ packages: ReleasePackageEntry[]
25
+ policy: {
26
+ tagMatchesReleasePackageVersion: true
27
+ rootVersionIsIndependent: true
28
+ notes: string
29
+ }
30
+ }
@@ -0,0 +1,102 @@
1
+ import { createHash } from "node:crypto";
2
+ import { execFileSync } from "node:child_process";
3
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
4
+ import { dirname, resolve } from "node:path";
5
+ const root = resolve(import.meta.dir, "../../../../../../.."), outputPath = resolve(root, ".github/protocol/evidence/release-manifest.json");
6
+ function runGit(...arguments_) {
7
+ try {
8
+ return execFileSync("git", arguments_, { cwd: root, maxBuffer: 67108864 });
9
+ } catch (error) {
10
+ throw Error(`git ${arguments_.join(" ")} failed: ${error instanceof Error ? error.message : String(error)}`);
11
+ }
12
+ }
13
+ function showJson(sourceRevision, path) {
14
+ return JSON.parse(runGit("show", `${sourceRevision}:${path}`).toString());
15
+ }
16
+ function assertReleaseTag(tag) {
17
+ assertReleaseMapping(tag);
18
+ try {
19
+ runGit("show-ref", "--verify", "--quiet", `refs/tags/${tag}`);
20
+ } catch {
21
+ throw Error(`release tag does not exist: ${tag}`);
22
+ }
23
+ return runGit("rev-parse", `refs/tags/${tag}^{commit}`).toString().trim();
24
+ }
25
+ export function assertReleaseMapping(tag, releasePackageVersion) {
26
+ if (!/^v\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(tag))
27
+ throw Error(`release tag must be an exact semantic version tag, received: ${tag}`);
28
+ if (releasePackageVersion !== void 0 && releasePackageVersion !== tag.slice(1))
29
+ throw Error(`release tag ${tag} does not match storage/framework/package.json version ${releasePackageVersion}`);
30
+ }
31
+ export function buildReleaseManifest(tag) {
32
+ const sourceRevision = assertReleaseTag(tag), tagObjectType = runGit("cat-file", "-t", `refs/tags/${tag}`).toString().trim(), tagType = tagObjectType === "tag" ? "annotated" : tagObjectType === "commit" ? "lightweight" : null;
33
+ if (!tagType)
34
+ throw Error(`release tag ${tag} points to unsupported Git object type: ${tagObjectType}`);
35
+ const sourceTree = runGit("rev-parse", `${sourceRevision}^{tree}`).toString().trim(), treeListing = runGit("ls-tree", "-r", "-l", sourceRevision).toString(), packages = treeListing.trim().split(`
36
+ `).filter(Boolean).flatMap((line) => {
37
+ const path = line.match(/^\d+\s+blob\s+[0-9a-f]+\s+\d+\t(.+)$/)?.[1];
38
+ return path != null ? [path] : [];
39
+ }).filter((path) => path === "package.json" || path.endsWith("/package.json")).flatMap((path) => {
40
+ const pkg = showJson(sourceRevision, path);
41
+ if (!pkg.name || !pkg.version)
42
+ return [];
43
+ return [{ name: pkg.name, path, version: pkg.version, publishable: pkg.private !== !0 }];
44
+ }).sort((a, b) => a.name.localeCompare(b.name) || a.path.localeCompare(b.path)), rootPackage = showJson(sourceRevision, "package.json"), releasePackagePath = "storage/framework/package.json", releasePackage = packages.find((pkg) => pkg.path === releasePackagePath);
45
+ if (!releasePackage)
46
+ throw Error(`${releasePackagePath} is missing a name or version at ${sourceRevision}`);
47
+ const releaseVersion = tag.slice(1);
48
+ assertReleaseMapping(tag, releasePackage.version);
49
+ return {
50
+ schemaVersion: "1.0.0",
51
+ repository: "https://github.com/stacksjs/stacks",
52
+ tag,
53
+ tagType,
54
+ releaseVersion,
55
+ releasePackage: releasePackage.name,
56
+ sourceRevision,
57
+ sourceTree,
58
+ sourceDigest: `sha256:${createHash("sha256").update(treeListing).digest("hex")}`,
59
+ generatedAt: "derived-from-immutable-git-objects",
60
+ rootPackage: {
61
+ name: rootPackage.name,
62
+ version: rootPackage.version,
63
+ private: rootPackage.private === !0
64
+ },
65
+ prerequisites: rootPackage.system || {},
66
+ packages,
67
+ policy: {
68
+ tagMatchesReleasePackageVersion: !0,
69
+ rootVersionIsIndependent: !0,
70
+ notes: "The release tag versions stacks-framework. The private root package orchestrates the monorepo and has an independent version. Every versioned package is recorded at the exact tagged commit."
71
+ }
72
+ };
73
+ }
74
+ export function renderReleaseManifest(manifest) {
75
+ return `${JSON.stringify(manifest, null, 2)}
76
+ `;
77
+ }
78
+ export async function run() {
79
+ const mode = process.argv.includes("--write") ? "write" : process.argv.includes("--check") ? "check" : null;
80
+ if (!mode) {
81
+ console.error("usage: bun storage/framework/core/buddy/src/commands/protocol/release-manifest.ts --write --tag <tag> | --check");
82
+ process.exit(2);
83
+ }
84
+ if (mode === "write") {
85
+ const index = process.argv.indexOf("--tag"), tag = index === -1 ? null : process.argv[index + 1];
86
+ if (!tag)
87
+ throw Error("--tag requires an exact release tag");
88
+ const manifest = buildReleaseManifest(tag);
89
+ mkdirSync(dirname(outputPath), { recursive: !0 });
90
+ writeFileSync(outputPath, renderReleaseManifest(manifest));
91
+ console.log(`Wrote release manifest for ${manifest.tag} at ${manifest.sourceRevision} (${manifest.packages.length} packages)`);
92
+ } else {
93
+ if (!existsSync(outputPath))
94
+ throw Error("release manifest is missing; run bun run protocol:release -- --tag <tag>");
95
+ const current = JSON.parse(readFileSync(outputPath, "utf8")), expected = renderReleaseManifest(buildReleaseManifest(current.tag));
96
+ if (readFileSync(outputPath, "utf8") !== expected)
97
+ throw Error(`release manifest is stale or modified; run bun run protocol:release -- --tag ${current.tag}`);
98
+ console.log(`Release manifest matches ${current.tag} at ${current.sourceRevision}`);
99
+ }
100
+ }
101
+ if (import.meta.main)
102
+ await run();
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,34 @@
1
+ import { describe, expect, it } from "bun:test";
2
+ import { assertReleaseMapping, renderReleaseManifest } from "./release-manifest";
3
+ describe("release manifest", () => {
4
+ it("requires the framework release version to match the tag", () => {
5
+ expect(() => assertReleaseMapping("v0.70.161", "0.70.161")).not.toThrow();
6
+ expect(() => assertReleaseMapping("v0.70.161", "0.70.160")).toThrow("does not match");
7
+ expect(() => assertReleaseMapping("main")).toThrow("exact semantic version tag");
8
+ });
9
+ it("renders deterministically without wall-clock state", () => {
10
+ const manifest = {
11
+ schemaVersion: "1.0.0",
12
+ repository: "https://github.com/stacksjs/stacks",
13
+ tag: "v0.70.161",
14
+ tagType: "lightweight",
15
+ releaseVersion: "0.70.161",
16
+ releasePackage: "stacks-framework",
17
+ sourceRevision: "a9eaab647858ee12d891a74341c99923074a6faf",
18
+ sourceTree: "99ebce98969603c1fa0b0f743efcc04c9de02b98",
19
+ sourceDigest: "sha256:f20e1dabc57b8b9c0422d6574d756a0fc8ba40b1df2598171e28dcade07e3535",
20
+ generatedAt: "derived-from-immutable-git-objects",
21
+ rootPackage: { name: "stacks", version: "0.70.52", private: !0 },
22
+ prerequisites: { bun: "^1.3.0" },
23
+ packages: [],
24
+ policy: {
25
+ tagMatchesReleasePackageVersion: !0,
26
+ rootVersionIsIndependent: !0,
27
+ notes: "test policy"
28
+ }
29
+ };
30
+ expect(manifest.generatedAt).toBe("derived-from-immutable-git-objects");
31
+ expect(renderReleaseManifest(manifest)).toBe(`${JSON.stringify(manifest, null, 2)}
32
+ `);
33
+ });
34
+ });
@@ -0,0 +1,21 @@
1
+ import { string } from '../../../../validation/src/index';
2
+ export declare function executeSecurityEvidence(revision: string): Evidence;
3
+ export declare function executeConfigEvidence(revision: string): Evidence;
4
+ export declare function executeValidationEvidence(revision: string): Promise<Evidence>;
5
+ export declare function executeConventionsEvidence(revision: string): Evidence;
6
+ export declare function executeLifecycleEvidence(revision: string): Promise<Evidence>;
7
+ export declare function executeQueryEvidence(revision: string): Promise<Evidence>;
8
+ export declare function executeDatabaseEvidence(revision: string): Promise<Evidence>;
9
+ export declare function buildReport(): Promise<Record<string, unknown>>;
10
+ export declare function run(): Promise<void>;
11
+ declare interface Result {
12
+ requirementId: string
13
+ status: ResultStatus
14
+ fixtureId: string | null
15
+ evidenceUrl: string | null
16
+ durationMs: number
17
+ notes?: string
18
+ }
19
+ declare type ResultStatus = 'pass' | 'fail' | 'skipped' | 'unsupported' | 'exception' | 'experimental';
20
+ /** Executable-check evidence keyed by requirement id, merged into the report. */
21
+ declare type Evidence = Map<string, Omit<Result, 'requirementId' | 'fixtureId'>>;