From 2c261b2a449b8ca7ad923ae01e20f46f7a7206a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matheus=20Vila=C3=A7a=20de=20Jesus?= Date: Sun, 25 Oct 2026 20:55:04 -0300 Subject: [PATCH 1/3] chore: start #653 From 9bafc1aff9949228a6964df83630edadc9bde579 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matheus=20Vila=C3=A7a=20de=20Jesus?= Date: Sun, 25 Oct 2026 21:04:42 -0300 Subject: [PATCH 2/3] test: generated-paths.sh must accept every path the manifest writes release-meta.ts no longer lists the generated files in a comment; the manifest exports TREE_OUTPUTS and a test checks it against is_generated. Fail before: dropping MIGRATION.md from the list fails the test; pass after. Closes #653 --- packages/ui/scripts/build-manifest.ts | 15 ++++++++++++++- packages/ui/tests/issue-workflow.test.ts | 13 +++++++++++++ scripts/release-meta.ts | 7 ++++--- 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/packages/ui/scripts/build-manifest.ts b/packages/ui/scripts/build-manifest.ts index 7460ff72..9ecc8274 100644 --- a/packages/ui/scripts/build-manifest.ts +++ b/packages/ui/scripts/build-manifest.ts @@ -23,7 +23,7 @@ * `Program` over the entry files and reported with its signature and JSDoc summary. */ import { existsSync, mkdirSync, writeFileSync } from "node:fs"; -import { dirname, join, resolve } from "node:path"; +import { dirname, join, relative, resolve } from "node:path"; import { loadMigrations, migrationMarkdown } from "../src/cli/migrations.ts"; import { buildAtlasFile } from "./atlas-markdown.ts"; import { buildReleaseFile } from "./changelog-markdown.ts"; @@ -61,6 +61,19 @@ const SKILL_API = resolve(ROOT, "../../.agents/skills/codeatlas-ui/API.md"); const MIGRATION_MD = resolve(ROOT, "MIGRATION.md"); const SKILL_DEMOS = resolve(ROOT, "../../.agents/skills/codeatlas-ui/DEMOS.md"); +/** + * What a manifest run writes into the git tree, as repo-relative paths (directories end in `/`). The land gate + * (`scripts/generated-paths.sh`, the one list of generated files) must accept every one of them, or a rebase + * would take a side of a file the manifest rebuilds; `tests/issue-workflow.test.ts` checks it (#653). + */ +export const TREE_OUTPUTS = [ + relative(resolve(ROOT, "../.."), SKILL_API), + relative(resolve(ROOT, "../.."), SKILL_DEMOS), + relative(resolve(ROOT, "../.."), MIGRATION_MD), + "site/public/api/v1/", + "site/public/gallery/", +]; + if (import.meta.main) { const manifest = buildManifest(); const tokens = buildTokens(); diff --git a/packages/ui/tests/issue-workflow.test.ts b/packages/ui/tests/issue-workflow.test.ts index d3f76c39..da4e470e 100644 --- a/packages/ui/tests/issue-workflow.test.ts +++ b/packages/ui/tests/issue-workflow.test.ts @@ -167,6 +167,19 @@ describe("issue-land.sh", () => { "sh", path, ]).exitCode === 0; + // One list (#653): every path a manifest run writes into the tree is accepted by `is_generated`, the list the land + // reads, so a new generated output cannot be left out of it (and `release.ts` stages whatever the manifest wrote). + it("accepts every path the manifest writes into the tree (#653)", async () => { + const { TREE_OUTPUTS } = await import("../scripts/build-manifest.ts"); + for (const out of TREE_OUTPUTS) { + // a directory stands for the files under it: probe a file the manifest really writes there + const probe = out.endsWith("/") + ? `${out}${out.includes("gallery") ? "001.md" : "catalog.json"}` + : out; + expect(generated(probe), probe).toBe(true); + } + }); + it("rebuilds generated files and never picks a side of hand-written ones (#241)", () => { for (const p of [ "bun.lock", diff --git a/scripts/release-meta.ts b/scripts/release-meta.ts index 7c01cc30..678efee4 100644 --- a/scripts/release-meta.ts +++ b/scripts/release-meta.ts @@ -7,9 +7,10 @@ export const PREFIX = "@codeatlasdev/ui@"; /** * Commands `bun run release` runs to turn pending changesets into the release commit. The bump - * stamps the new version into the generated catalog (API.md, DEMOS.md, site/public/api/v1: - * "Needs: @codeatlasdev/ui@"), so they are regenerated before `git add -A`; otherwise - * main keeps the previous stamp until an unrelated PR rebuilds them. + * stamps the new version into the generated catalog ("Needs: @codeatlasdev/ui@"), so the + * manifest is rebuilt before `git add -A`; otherwise main keeps the previous stamp until an unrelated PR + * rebuilds it. Which files that covers is `TREE_OUTPUTS` (packages/ui/scripts/build-manifest.ts), checked + * against `scripts/generated-paths.sh`, the one list of generated files. */ export function versionSteps(): string[][] { return [ From e46adbd8b2da462637ce8dd1a21a9567a7753fb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matheus=20Vila=C3=A7a=20de=20Jesus?= Date: Sun, 25 Oct 2026 21:59:37 -0300 Subject: [PATCH 3/3] refactor: TREE_OUTPUTS and its test derive the API paths from agent-guide --- packages/ui/scripts/build-manifest.ts | 2 +- packages/ui/tests/issue-workflow.test.ts | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/ui/scripts/build-manifest.ts b/packages/ui/scripts/build-manifest.ts index 9ecc8274..0b14de0f 100644 --- a/packages/ui/scripts/build-manifest.ts +++ b/packages/ui/scripts/build-manifest.ts @@ -70,7 +70,7 @@ export const TREE_OUTPUTS = [ relative(resolve(ROOT, "../.."), SKILL_API), relative(resolve(ROOT, "../.."), SKILL_DEMOS), relative(resolve(ROOT, "../.."), MIGRATION_MD), - "site/public/api/v1/", + `site/public/${API_DIR}/`, "site/public/gallery/", ]; diff --git a/packages/ui/tests/issue-workflow.test.ts b/packages/ui/tests/issue-workflow.test.ts index da4e470e..5a74b5a6 100644 --- a/packages/ui/tests/issue-workflow.test.ts +++ b/packages/ui/tests/issue-workflow.test.ts @@ -20,6 +20,7 @@ import { removeWorktree, worktrees, } from "../../../scripts/issue-lib.ts"; +import { API_PATHS } from "../src/cli/agent-guide.ts"; // Real data from #87/#88: a first-time contributor opened PRs from a fork before `issue:start` ran. const fork = (number: number, headRefName: string): PrRef => ({ @@ -174,7 +175,7 @@ describe("issue-land.sh", () => { for (const out of TREE_OUTPUTS) { // a directory stands for the files under it: probe a file the manifest really writes there const probe = out.endsWith("/") - ? `${out}${out.includes("gallery") ? "001.md" : "catalog.json"}` + ? `${out}${out.includes("gallery") ? "001.md" : API_PATHS.catalog}` : out; expect(generated(probe), probe).toBe(true); }