Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 8 additions & 14 deletions scripts/docs-gate/index.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,6 @@ function sh(cmd) {
return execSync(cmd, { encoding: "utf8", stdio: ["ignore", "pipe", "inherit"] }).trim();
}

/**
* Like sh(), but for a lookup whose failure is EXPECTED and meaningless.
*
* `git show <base>:<path>` cannot succeed for a file the PR adds, and sh()
* inherits git's stderr on purpose, so a PR adding forty pages printed forty
* `fatal: path ... exists on disk, but not in ...` lines before the gate said
* anything - which reads as a crashed gate rather than as forty new files.
*/
function shQuiet(cmd) {
return execSync(cmd, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
}

function changedDocs() {
// Ratchet against the PR's target branch, not a hardcoded main — so a PR into
// release/** diffs against that release branch, not main's fork point.
Expand Down Expand Up @@ -157,14 +145,20 @@ function frontmatterOnly(files, base) {
for (const f of files) {
let before;
try {
// shQuiet: a file the PR ADDS has no base blob, and that failure is
// both expected and meaningless here - see the note on shQuiet.
// execFileSync with an argv, not a shell string: `f` is a path from git
// output, and interpolating it into a command line is a class of bug
// rather than a present one. No doc path in the repo carries a shell
// metacharacter today, and a failure here falls back to "treat as
// body-changed", which is the safe direction - so this removes the class
// rather than fixing a symptom.
//
// The `"ignore"` on stderr is what keeps the output readable: a file the
// PR ADDS has no base blob, so git prints `fatal: path ... exists on
// disk, but not in ...` for every one of them. That failure is expected
// and meaningless here - the catch below treats the file as body-changed,
// which is correct for a new file - but forty of those lines ahead of the
// gate's own output reads as a crashed gate rather than as forty new
// pages.
before = execFileSync("git", ["show", `${base}:${f}`], {
encoding: "utf8",
stdio: ["ignore", "pipe", "ignore"],
Expand Down
21 changes: 21 additions & 0 deletions scripts/test-frameworks.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -252,4 +252,25 @@ check("every registry slug is unique", () => {
assert.strictEqual(new Set(slugs).size, slugs.length);
});

// `display` is load-bearing in the same way `slug` is, and was pinned in
// neither place. FRAMEWORK_URL_PATH and FRAMEWORK_SLUG (SkillsCallout),
// FRAMEWORK_DISPLAY_TO_SLUG (utils/frameworks.ts) and the
// features/products/skills cross-check in verify-frameworks.cjs all key off it.
//
// A duplicate is easy to introduce - a copy-pasted entry whose display was not
// updated - and Object.fromEntries silently keeps only the last, so SkillsCallout
// would build the OTHER framework's agent-skills link and report the wrong
// framework to analytics. verify-frameworks stays green through all of it,
// because it only ever asks whether a display is IN the registry, never whether
// it appears once.
check("every registry display name is unique", () => {
const displays = registry.FRAMEWORKS.map((f) => f.display);
const dupes = displays.filter((d, i) => displays.indexOf(d) !== i);
assert.deepStrictEqual(
dupes,
[],
`display names must be unique - they are map keys. Duplicated: ${dupes.join(", ")}`,
);
});

console.log(`\n${passed} passed\n`);
Loading