Skip to content

Commit fd08f34

Browse files
committed
Publish verified versioned machine resources from canonical Docs
1 parent 91fe084 commit fd08f34

9 files changed

Lines changed: 73 additions & 7 deletions

File tree

‎.gitignore‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ docs/.vitepress/cache
1818
.stack-specification
1919
.stack-docs
2020
/public/docs-source.json
21+
/public/machine/
2122
docs/**/*.md
2223

2324
# Editor directories and files

‎README.md‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,17 @@ Review command behavior, then run `STACK_CLI_BIN=/absolute/path/to/the/verified/
6969

7070
The required CI baseline compares the pin against GitHub's latest stable release and resolved tag commit, executes documentation commands using both the pinned source and the attested published Linux archive, and verifies that an unsupported flag fails the documentation smoke. Offline documentation builds check locale version consistency without contacting GitHub. The read-only `Release freshness` workflow checks daily and on manual dispatch, so a CLI-only release cannot remain silently stale until someone edits this repository. A failed run requires a synchronization PR and a verified Web deployment; this workflow does not auto-merge or auto-publish. GitHub scheduled runs may be delayed, and GitHub Actions notification preferences govern failure notifications.
7171

72+
## Machine resources
73+
74+
The pinned Docs bundle also supplies `/machine/index.json` and immutable versioned
75+
grammar/schema/diagnostic/example manifests. Both llms indexes link to discovery
76+
and the [canonical distribution policy](https://github.com/stack-sh/docs/blob/main/MACHINE_RESOURCES.md).
77+
Generated machine JSON is not hand-edited here. The output gate verifies every
78+
deployed machine file against the provider manifest and checks the discovery
79+
digest. Static headers allow public cross-origin reads, revalidate discovery, and
80+
cache version 1.0.0 artifacts immutably. Keep old versioned files when updating the
81+
Docs pin; publisher CI forbids rewriting them.
82+
7283
## Coding agent skill
7384

7485
Install the focused diagram skill with `npx skills add stack-sh/cli` in the target project. It provides instructions, not a CLI binary. See the [coding agent guide](https://stack-diagram.com/docs/guide/coding-agents) for the usage workflow. Docs generates both the skill distributed by CLI and the workflow displayed here from one source. Web verifies and executes the same Docs bundle's skill commands alongside documentation examples against the published CLI.

‎docs/public/llms.txt‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ These Markdown pages are the public user-facing reference. Examples marked as `s
2424

2525
## Reference
2626

27+
- [Machine resource discovery](https://stack-diagram.com/machine/index.json): Versioned grammar, schemas, diagnostics, example sources, implementation capabilities, and SHA-256 integrity.
28+
- [Machine resource policy](https://github.com/stack-sh/docs/blob/main/MACHINE_RESOURCES.md): Compatibility, immutable versions, caching, and a verified resource consumer example.
29+
2730
- [Diagnostics and limits](https://stack-diagram.com/docs/reference/diagnostics-and-limits.md): Stable diagnostic codes, source ranges, warnings, and bounded-input limits.
2831
- [Versioning and safety](https://stack-diagram.com/docs/reference/versioning-and-safety.md): Compatibility policy, trust boundaries, escaping, and package responsibilities.
2932

‎public/_headers‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Static asset response rules: https://developers.cloudflare.com/workers/static-assets/headers/
2+
/machine/*
3+
Access-Control-Allow-Origin: *
4+
X-Content-Type-Options: nosniff
5+
6+
/machine/index.json
7+
Cache-Control: public, max-age=0, must-revalidate
8+
9+
/machine/v1.0.0/*
10+
Cache-Control: public, max-age=31536000, immutable
11+
12+
/docs-source.json
13+
Cache-Control: public, max-age=0, must-revalidate

‎public/llms.txt‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ Stack source describes components, boundaries, relationships, themes, and layout
66

77
## Documentation
88

9+
- [Machine resource discovery](https://stack-diagram.com/machine/index.json): Versioned grammar, schemas, diagnostics, example sources, implementation capabilities, and SHA-256 integrity.
10+
- [Machine resource policy](https://github.com/stack-sh/docs/blob/main/MACHINE_RESOURCES.md): Compatibility, immutable versions, caching, and a verified resource consumer example.
11+
912
- [Coding agents](https://stack-diagram.com/docs/guide/coding-agents.md): Install the Stack diagram skill or copy a minimal instruction; generate, validate, and render with the published local CLI.
1013

1114
- [Stack documentation](https://stack-diagram.com/docs/llms.txt): Curated guides, complete language reference, diagnostics, limits, versioning, and safety information.

‎scripts/docs-source.json‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
22
"repository": "stack-sh/docs",
3-
"revision": "cd7ba0356de8a70d8932036b0e6ea37519577fd0",
4-
"manifestSha256": "4187e92ff990702d53586db14658dc15a3721c69d42501a4d5b4c615a8e31552"
3+
"revision": "ba212b68ae52ca08c5593ad3f82ce634e876a4ad",
4+
"manifestSha256": "dd79651f87ce53ae626b101bd57f0fcec7cc42879de2e3f0ded1e169e9097710"
55
}

‎scripts/docs-source.mjs‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export function validateManifest(bytes, lock) {
2626
for (const file of manifest.files) {
2727
assert.match(
2828
file.path,
29-
/^(?:site\/(?:[a-z0-9-]+\/)*[a-z0-9-]+\.md|guide\/agent-workflow\.md|skills\/stack-diagrams\/SKILL\.md)$/,
29+
/^(?:site\/(?:[a-z0-9-]+\/)*[a-z0-9-]+\.md|guide\/agent-workflow\.md|skills\/stack-diagrams\/SKILL\.md|machine\/(?:index\.json|v\d+\.\d+\.\d+\/[a-z0-9.-]+\.json))$/,
3030
)
3131
assert.ok(!seen.has(file.path), "Duplicate Docs path")
3232
seen.add(file.path)
@@ -83,7 +83,9 @@ export async function syncDocs(directory = root, lock = docsSource, fetchResourc
8383
? `docs/${file.slice(5)}`
8484
: file === "skills/stack-diagrams/SKILL.md"
8585
? ".stack-docs/SKILL.md"
86-
: null
86+
: file.startsWith("machine/")
87+
? `public/${file}`
88+
: null
8789
if (!relative) continue
8890
const target = path.join(directory, relative)
8991
await mkdir(path.dirname(target), { recursive: true })

‎scripts/docs-source.test.mjs‎

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,12 @@ function fixture(change = (manifest) => manifest) {
1010
const manifest = change({
1111
schemaVersion: "1.0",
1212
cli: { repository: "stack-sh/cli", revision: "b".repeat(40), version: "0.4.0" },
13-
files: ["site/index.md", "skills/stack-diagrams/SKILL.md"].map((file) => ({
13+
files: [
14+
"site/index.md",
15+
"skills/stack-diagrams/SKILL.md",
16+
"machine/index.json",
17+
"machine/v1.0.0/manifest.json",
18+
].map((file) => ({
1419
path: file,
1520
sha256: digest(source),
1621
})),
@@ -25,7 +30,7 @@ function fixture(change = (manifest) => manifest) {
2530
assert.ok(
2631
url.startsWith(`https://raw.githubusercontent.com/stack-sh/docs/${lock.revision}/generated/`),
2732
)
28-
return new Response(url.endsWith("manifest.json") ? bytes : source)
33+
return new Response(url.endsWith("/generated/manifest.json") ? bytes : source)
2934
}
3035
return { source, bytes, lock, fetchResource }
3136
}
@@ -37,6 +42,11 @@ test("retrieves verified immutable content and supplies only generated build inp
3742
await syncDocs(directory, f.lock, f.fetchResource)
3843
assert.equal(await readFile(path.join(directory, "docs/index.md"), "utf8"), f.source)
3944
assert.equal(await readFile(path.join(directory, ".stack-docs/SKILL.md"), "utf8"), f.source)
45+
assert.equal(await readFile(path.join(directory, "public/machine/index.json"), "utf8"), f.source)
46+
assert.equal(
47+
await readFile(path.join(directory, "public/machine/v1.0.0/manifest.json"), "utf8"),
48+
f.source,
49+
)
4050
assert.deepEqual(
4151
JSON.parse(await readFile(path.join(directory, "public/docs-source.json"), "utf8")),
4252
f.lock,
@@ -50,6 +60,14 @@ test("rejects mutable pins, traversal, duplicate paths, and unsupported manifest
5060
(m) => ({ ...m, schemaVersion: "2.0" }),
5161
(m) => ({ ...m, files: [...m.files, m.files[0]] }),
5262
(m) => ({ ...m, files: [{ path: "site/../../README.md", sha256: "a".repeat(64) }] }),
63+
(m) => ({
64+
...m,
65+
files: [...m.files, { path: "machine/v1.0.0/../../outside.json", sha256: "a".repeat(64) }],
66+
}),
67+
(m) => ({
68+
...m,
69+
files: [...m.files, { path: "machine/latest/manifest.json", sha256: "a".repeat(64) }],
70+
}),
5371
]) {
5472
const invalid = fixture(change)
5573
assert.throws(() => validateManifest(invalid.bytes, invalid.lock))
@@ -72,7 +90,7 @@ test("fails on missing or altered resources before writing build inputs", async
7290
syncDocs(
7391
directory,
7492
f.lock,
75-
async (url) => new Response(url.endsWith("manifest.json") ? f.bytes : "altered"),
93+
async (url) => new Response(url.endsWith("/generated/manifest.json") ? f.bytes : "altered"),
7694
),
7795
/content integrity/,
7896
)

‎scripts/validate-docs-output.mjs‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,25 @@ import path from "node:path"
33

44
import { validateDocumentationContract } from "./docs-contract.mjs"
55
import { documentationContract } from "./docs-validation.config.mjs"
6+
import { digest, readDocsManifest } from "./docs-source.mjs"
67

78
const outputRoot = path.resolve("dist/docs")
89
const siteOutputRoot = path.resolve("dist")
910
const docsRoot = path.resolve("docs")
11+
const sourceManifest = readDocsManifest()
12+
const machineFiles = sourceManifest.files.filter((file) => file.path.startsWith("machine/"))
13+
if (machineFiles.length < 4) throw new Error("Machine distribution is incomplete")
14+
for (const file of machineFiles) {
15+
const bytes = await readFile(path.join(siteOutputRoot, file.path))
16+
if (digest(bytes) !== file.sha256) throw new Error(`Built machine asset drift: ${file.path}`)
17+
}
18+
const machineIndex = JSON.parse(
19+
await readFile(path.join(siteOutputRoot, "machine/index.json"), "utf8"),
20+
)
21+
const manifestPath = new URL(machineIndex.current.url).pathname.slice(1)
22+
const machineManifest = await readFile(path.join(siteOutputRoot, manifestPath))
23+
if (digest(machineManifest) !== machineIndex.current.sha256)
24+
throw new Error("Machine discovery digest mismatch")
1025
const localePages = [
1126
["index.html", "en-US"],
1227
["ja/index.html", "ja-JP"],

0 commit comments

Comments
 (0)