feat(skills): add public-skill strip and mirror controls

This commit is contained in:
v-openrig-build committed 2026-07-31 20:03:26 +00:00
1 parent d11dda32dc
commit 151774d276
13 files changed
+3318 -13

No files matched your search

+1
View File
@@ -6,6 +6,7 @@
"type": "module",
"scripts": {
"build": "tsc && node scripts/copy-workflow-specs.mjs",
"gen:control-plane-json": "node scripts/gen-control-plane-json.mjs",
"test": "vitest run",
"test:watch": "vitest",
"dev": "tsx watch src/index.ts"
@@ -0,0 +1,320 @@
#!/usr/bin/env node
import { createHash } from "node:crypto";
import {
existsSync,
lstatSync,
mkdirSync,
readFileSync,
readdirSync,
writeFileSync,
} from "node:fs";
import { dirname, join, relative, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { parse } from "yaml";
const EDGE_NAMES = ["canonical", "plugin", "spec"];
const CATEGORIES = new Set(["core", "pm", "pods", "process", null]);
const REQUIRED_MEMBERSHIP_CATEGORIES = [
"clean",
"ship_after_fix",
"ship_misses_add",
"sanitize_borderlines_ship",
];
const REQUIRED_DENYLIST_ARRAYS = [
"path_prefixes",
"seat_and_rig_patterns",
"host_patterns",
"charged_terms",
"frontmatter_drop_keys",
"internal_path_globs",
"allowed_context_substrings",
];
export async function generateControlPlaneJson({
repoRoot,
membershipPath,
denylistPath,
layoutPath,
outputDir,
}) {
const membership = readYaml(membershipPath);
const denylist = readYaml(denylistPath);
const layoutConfig = readYaml(layoutPath);
validateMembership(membership, membershipPath);
validateDenylist(denylist, denylistPath);
const layout = await extractSkillEdgeLayout({
repoRoot,
sourcePath: layoutPath,
config: layoutConfig,
});
const digests = buildEdgeDigests({ repoRoot, layout });
mkdirSync(outputDir, { recursive: true });
writeJson(join(outputDir, "product-public-skills.generated.json"), membership);
writeJson(join(outputDir, "internal-tokens.generated.json"), denylist);
writeJson(join(outputDir, "skill-edge-layout.generated.json"), layout);
writeJson(join(outputDir, "skill-edge-digests.generated.json"), digests);
}
export async function extractSkillEdgeLayout({
repoRoot,
config,
sourcePath = "skill-edge-layout.yaml",
}) {
validateLayout(config, sourcePath);
const skills = new Map();
for (const [edge, edgeConfig] of Object.entries(config.edges).sort()) {
const edgeRoot = join(repoRoot, edgeConfig.path);
for (const skillFile of walkFiles(edgeRoot).filter((path) =>
path.endsWith("SKILL.md"),
)) {
const rel = relative(edgeRoot, skillFile).replaceAll("\\", "/");
const parts = rel.split("/");
const skill =
edgeConfig.layout === "flat" ? parts[0] : parts.at(-2);
const category =
edgeConfig.layout === "flat" ? null : parts.length >= 3 ? parts[0] : null;
const current = skills.get(skill) ?? { edges: [], category };
if (
category !== null &&
current.category !== null &&
current.category !== category
) {
throw new Error(
`${sourcePath}: ${skill} has conflicting categories ${current.category} and ${category}`,
);
}
current.category ??= category;
current.edges.push(edge);
skills.set(skill, current);
}
}
for (const [skill, override] of Object.entries(
config.forward_overrides ?? {},
).sort()) {
validateOverride(skill, override, config.edges, sourcePath);
skills.set(skill, {
edges: [...new Set(override.edges)].sort(),
category: override.category ?? null,
});
}
return {
version: config.version,
owner: config.owner,
edges: sortObject(config.edges),
skills: Object.fromEntries(
[...skills.entries()]
.sort(([left], [right]) => left.localeCompare(right))
.map(([skill, entry]) => [
skill,
{
edges: [...new Set(entry.edges)].sort(),
category: entry.category ?? null,
},
]),
),
};
}
function buildEdgeDigests({ repoRoot, layout }) {
return {
version: 1,
edges: Object.fromEntries(
Object.entries(layout.edges)
.sort(([left], [right]) => left.localeCompare(right))
.map(([edge, config]) => {
const root = join(repoRoot, config.path);
return [
edge,
Object.fromEntries(
walkFiles(root).map((path) => [
relative(root, path).replaceAll("\\", "/"),
sha256(readFileSync(path)),
]),
),
];
}),
),
};
}
function validateMembership(value, sourcePath) {
if (!isObject(value?.product_public)) {
invalid(sourcePath, "product_public must be an object");
}
for (const category of REQUIRED_MEMBERSHIP_CATEGORIES) {
if (!isStringArray(value.product_public[category])) {
invalid(sourcePath, `product_public.${category} must be an array`);
}
}
if (!isStringArray(value.vendored_ship_with_provenance)) {
invalid(sourcePath, "vendored_ship_with_provenance must be an array");
}
if (!isObject(value.not_public)) {
invalid(sourcePath, "not_public must be an object");
}
for (const [category, skills] of Object.entries(value.not_public)) {
if (!isStringArray(skills)) {
invalid(sourcePath, `not_public.${category} must be an array`);
}
}
if (!isStringArray(value.pending_author_public)) {
invalid(sourcePath, "pending_author_public must be an array");
}
}
function validateDenylist(value, sourcePath) {
if (!isObject(value)) invalid(sourcePath, "denylist must be an object");
for (const field of REQUIRED_DENYLIST_ARRAYS) {
if (Object.hasOwn(value, field) && !isStringArray(value[field])) {
invalid(sourcePath, `${field} must be an array`);
}
}
for (const field of REQUIRED_DENYLIST_ARRAYS) {
if (!isStringArray(value[field])) {
invalid(sourcePath, `${field} must be an array`);
}
}
if (
!isObject(value.section_fence) ||
typeof value.section_fence.begin !== "string" ||
typeof value.section_fence.end !== "string"
) {
invalid(sourcePath, "section_fence.begin and section_fence.end are required");
}
}
function validateLayout(value, sourcePath) {
if (!isObject(value?.edges)) invalid(sourcePath, "edges must be an object");
const edgeNames = Object.keys(value.edges).sort();
if (
edgeNames.length !== EDGE_NAMES.length ||
edgeNames.some((edge, index) => edge !== EDGE_NAMES[index])
) {
invalid(sourcePath, "edges must contain exactly canonical, plugin, and spec");
}
for (const edge of EDGE_NAMES) {
const config = value.edges[edge];
if (
!isObject(config) ||
typeof config.path !== "string" ||
!["categorized", "mirror-of-spec", "flat"].includes(config.layout)
) {
invalid(sourcePath, `${edge} edge path/layout is invalid`);
}
}
if (value.extract_from_committed_trees !== true) {
invalid(sourcePath, "extract_from_committed_trees must be true");
}
if (!isObject(value.forward_overrides)) {
invalid(sourcePath, "forward_overrides must be an object");
}
for (const [skill, override] of Object.entries(value.forward_overrides)) {
validateOverride(skill, override, value.edges, sourcePath);
}
}
function validateOverride(skill, override, edges, sourcePath) {
if (
!isObject(override) ||
!isStringArray(override.edges) ||
override.edges.some(
(edge) =>
!Object.hasOwn(edges, edge) || !["plugin", "spec"].includes(edge),
)
) {
invalid(sourcePath, `${skill} edges are invalid`);
}
if (!CATEGORIES.has(override.category ?? null)) {
invalid(sourcePath, `${skill} category is invalid`);
}
}
function readYaml(path) {
try {
return parse(readFileSync(path, "utf8"));
} catch (error) {
throw new Error(`${path}: ${error.message}`);
}
}
function writeJson(path, value) {
writeFileSync(path, `${JSON.stringify(value, null, 2)}\n`);
}
function walkFiles(root) {
const stat = lstatSync(root, { throwIfNoEntry: false });
if (!stat) return [];
if (stat.isSymbolicLink()) {
throw new Error(`${root}: symlink entries are not allowed`);
}
if (stat.isFile()) return [root];
if (!stat.isDirectory()) {
throw new Error(`${root}: unsupported filesystem entry`);
}
return readdirSync(root)
.sort()
.flatMap((entry) => walkFiles(join(root, entry)));
}
function sha256(bytes) {
return createHash("sha256").update(bytes).digest("hex");
}
function sortObject(value) {
return Object.fromEntries(
Object.entries(value).sort(([left], [right]) =>
left.localeCompare(right),
),
);
}
function isObject(value) {
return value !== null && typeof value === "object" && !Array.isArray(value);
}
function isStringArray(value) {
return Array.isArray(value) && value.every((item) => typeof item === "string");
}
function invalid(path, reason) {
throw new Error(`${path}: ${reason}`);
}
function cliOptions(argv) {
const values = {};
for (let index = 0; index < argv.length; index += 2) {
const key = argv[index];
const value = argv[index + 1];
if (!key?.startsWith("--") || value === undefined) {
throw new Error(`Invalid argument near ${key ?? "<end>"}`);
}
values[key.slice(2)] = resolve(value);
}
const required = ["repo-root", "membership", "denylist", "layout", "output"];
for (const key of required) {
if (!values[key]) throw new Error(`--${key} is required`);
}
return {
repoRoot: values["repo-root"],
membershipPath: values.membership,
denylistPath: values.denylist,
layoutPath: values.layout,
outputDir: values.output,
};
}
const scriptPath = fileURLToPath(import.meta.url);
if (resolve(process.argv[1]) === scriptPath) {
try {
await generateControlPlaneJson(cliOptions(process.argv.slice(2)));
} catch (error) {
process.stderr.write(`${error.message}\n`);
process.exitCode = 1;
}
}
@@ -0,0 +1,417 @@
import { createHash } from "node:crypto";
import {
existsSync,
mkdirSync,
mkdtempSync,
readFileSync,
rmSync,
symlinkSync,
writeFileSync,
} from "node:fs";
import { tmpdir } from "node:os";
import { dirname, join, resolve } from "node:path";
import { pathToFileURL } from "node:url";
import { afterEach, describe, expect, it } from "vitest";
const roots: string[] = [];
const REPO_ROOT = resolve(import.meta.dirname, "../../..");
afterEach(() => {
for (const root of roots.splice(0)) {
rmSync(root, { recursive: true, force: true });
}
});
describe("control-plane JSON generator", () => {
it("exposes the daemon-workspace generation command without a root YAML dependency", () => {
const daemonPackage = JSON.parse(
readFileSync(join(REPO_ROOT, "packages/daemon/package.json"), "utf8"),
);
const rootPackage = JSON.parse(
readFileSync(join(REPO_ROOT, "package.json"), "utf8"),
);
expect(daemonPackage.scripts["gen:control-plane-json"]).toBe(
"node scripts/gen-control-plane-json.mjs",
);
expect(daemonPackage.dependencies.yaml).toBeTruthy();
expect(rootPackage.dependencies?.yaml).toBeUndefined();
expect(rootPackage.devDependencies?.yaml).toBeUndefined();
});
it("parses canon YAML and emits deterministic JSON plus edge digests", async () => {
const generator = await loadGenerator();
const root = tempRoot();
const conventions = join(root, "conventions");
const output = join(root, "scripts");
const membershipPath = join(conventions, "product-public-skills.yaml");
const denylistPath = join(conventions, "internal-tokens.yaml");
const layoutPath = join(conventions, "skill-edge-layout.yaml");
write(
membershipPath,
[
"version: 0",
"provisional: true",
"owner: pm-lead@example",
"product_public:",
" clean: [alpha]",
" ship_after_fix: [beta]",
" ship_misses_add: []",
" sanitize_borderlines_ship: []",
"vendored_ship_with_provenance: []",
"not_public:",
" reclass_host_only: [private]",
"pending_author_public: [future]",
"",
].join("\n"),
);
write(
denylistPath,
[
"version: 1",
"path_prefixes: [openrig-work/]",
"seat_and_rig_patterns: ['operator-agent@']",
"host_patterns: [mm2-]",
"charged_terms: [founder]",
"frontmatter_drop_keys: [source_evidence]",
"internal_path_globs: ['*.internal.*', '**/internal/**', '*-internal/**']",
"section_fence:",
" begin: '<!-- internal:begin -->'",
" end: '<!-- internal:end -->'",
"allowed_context_substrings: [do not ship]",
"",
].join("\n"),
);
write(layoutPath, layoutYaml());
seedEdges(root);
await generator.generateControlPlaneJson({
repoRoot: root,
membershipPath,
denylistPath,
layoutPath,
outputDir: output,
});
const first = readGenerated(output);
await generator.generateControlPlaneJson({
repoRoot: root,
membershipPath,
denylistPath,
layoutPath,
outputDir: output,
});
const second = readGenerated(output);
expect(second).toEqual(first);
expect(first.membership.product_public.clean).toEqual(["alpha"]);
expect(first.membership.not_public.reclass_host_only).toEqual(["private"]);
expect(first.denylist.section_fence.begin).toBe("<!-- internal:begin -->");
expect(first.layout.skills.alpha).toEqual({
edges: ["canonical", "plugin", "spec"],
category: "core",
});
expect(first.layout.skills.pluginOnly).toEqual({
edges: ["plugin"],
category: null,
});
expect(first.digests.edges.plugin["alpha/SKILL.md"]).toBe(
sha256("# Plugin alpha\n"),
);
});
it("exact-tree extraction ignores illustrative layout and applies only forward_overrides", async () => {
const generator = await loadGenerator();
const root = tempRoot();
seedEdges(root);
const config = {
version: 0,
owner: "skills-architect@example",
edges: {
spec: {
path: "packages/daemon/specs/agents/shared/skills",
layout: "categorized",
},
canonical: {
path: "skills/_canonical",
layout: "mirror-of-spec",
},
plugin: {
path: "packages/daemon/assets/plugins/openrig-core/skills",
layout: "flat",
},
},
extract_from_committed_trees: true,
forward_overrides: {
future: { edges: ["spec", "plugin"], category: "process" },
},
reference_layout_da101b29: {
spec_categorized: { pm: ["alpha"], core: ["stale-only"] },
plugin_flat: ["stale-only"],
},
};
const layout = await generator.extractSkillEdgeLayout({
repoRoot: root,
config,
});
expect(layout.skills.alpha).toEqual({
edges: ["canonical", "plugin", "spec"],
category: "core",
});
expect(layout.skills["stale-only"]).toBeUndefined();
expect(layout.skills.future).toEqual({
edges: ["plugin", "spec"],
category: "process",
});
});
it("rejects malformed forward overrides with the source path and reason", async () => {
const generator = await loadGenerator();
const root = tempRoot();
seedEdges(root);
await expect(
generator.extractSkillEdgeLayout({
repoRoot: root,
sourcePath: "/canon/conventions/skill-edge-layout.yaml",
config: {
version: 0,
edges: edgeConfig(),
extract_from_committed_trees: true,
forward_overrides: {
broken: { edges: ["spec"], category: "not-a-category" },
},
},
}),
).rejects.toThrow(
/skill-edge-layout\.yaml.*broken.*category|broken.*category.*skill-edge-layout\.yaml/i,
);
});
it("fails closed on malformed membership and denylist schemas with source paths", async () => {
const generator = await loadGenerator();
const root = tempRoot();
const conventions = join(root, "conventions");
const output = join(root, "scripts");
const membershipPath = join(conventions, "product-public-skills.yaml");
const denylistPath = join(conventions, "internal-tokens.yaml");
const layoutPath = join(conventions, "skill-edge-layout.yaml");
write(layoutPath, layoutYaml());
seedEdges(root);
write(membershipPath, "version: 0\nowner: pm\n");
write(
denylistPath,
"version: 1\npath_prefixes: []\nseat_and_rig_patterns: []\nhost_patterns: []\ncharged_terms: []\nfrontmatter_drop_keys: []\ninternal_path_globs: []\nsection_fence: {begin: a, end: b}\nallowed_context_substrings: []\n",
);
await expect(
generator.generateControlPlaneJson({
repoRoot: root,
membershipPath,
denylistPath,
layoutPath,
outputDir: output,
}),
).rejects.toThrow(/product-public-skills\.yaml.*product_public|product_public.*product-public-skills\.yaml/i);
write(
membershipPath,
"version: 0\nproduct_public: {clean: [], ship_after_fix: [], ship_misses_add: [], sanitize_borderlines_ship: []}\nvendored_ship_with_provenance: []\nnot_public: {}\npending_author_public: []\n",
);
write(denylistPath, "version: 1\ncharged_terms: not-an-array\n");
await expect(
generator.generateControlPlaneJson({
repoRoot: root,
membershipPath,
denylistPath,
layoutPath,
outputDir: output,
}),
).rejects.toThrow(/internal-tokens\.yaml.*charged_terms|charged_terms.*internal-tokens\.yaml/i);
});
it("rejects edge file symlinks before digesting outside-root bytes", async () => {
const generator = await loadGenerator();
const root = tempRoot();
const input = seedGeneratorInput(root);
seedEdges(root);
const outside = join(root, "outside-file.txt");
write(outside, "outside edge bytes\n");
const link = join(
root,
"packages/daemon/assets/plugins/openrig-core/skills/alpha/references/linked.txt",
);
mkdirSync(dirname(link), { recursive: true });
symlinkSync(outside, link, "file");
await expect(
generator.generateControlPlaneJson(input),
).rejects.toThrow(/symlink.*linked\.txt|linked\.txt.*symlink/i);
expect(
existsSync(join(input.outputDir, "skill-edge-digests.generated.json")),
).toBe(false);
});
it("rejects edge directory symlinks before traversal or digesting outside-root bytes", async () => {
const generator = await loadGenerator();
const root = tempRoot();
const input = seedGeneratorInput(root);
seedEdges(root);
const outside = join(root, "outside-directory");
write(join(outside, "secret.txt"), "outside directory bytes\n");
const link = join(
root,
"skills/_canonical/core/alpha/references/linked-directory",
);
mkdirSync(dirname(link), { recursive: true });
symlinkSync(outside, link, "dir");
await expect(
generator.generateControlPlaneJson(input),
).rejects.toThrow(/symlink.*linked-directory|linked-directory.*symlink/i);
expect(
existsSync(join(input.outputDir, "skill-edge-digests.generated.json")),
).toBe(false);
});
});
async function loadGenerator(): Promise<Record<string, any>> {
const url = pathToFileURL(
join(REPO_ROOT, "packages/daemon/scripts/gen-control-plane-json.mjs"),
).href;
const loaded = await import(url).catch(() => null);
expect(loaded, "daemon control-plane JSON generator must exist").not.toBeNull();
expect(typeof loaded?.generateControlPlaneJson).toBe("function");
expect(typeof loaded?.extractSkillEdgeLayout).toBe("function");
return loaded as Record<string, any>;
}
function tempRoot(): string {
const root = mkdtempSync(join(tmpdir(), "openrig-control-json-red-"));
roots.push(root);
return root;
}
function seedEdges(root: string): void {
write(
join(
root,
"packages/daemon/specs/agents/shared/skills/core/alpha/SKILL.md",
),
"# Spec alpha\n",
);
write(
join(root, "skills/_canonical/core/alpha/SKILL.md"),
"# Spec alpha\n",
);
write(
join(
root,
"packages/daemon/assets/plugins/openrig-core/skills/alpha/SKILL.md",
),
"# Plugin alpha\n",
);
write(
join(
root,
"packages/daemon/assets/plugins/openrig-core/skills/pluginOnly/SKILL.md",
),
"# Plugin only\n",
);
}
function seedGeneratorInput(root: string): {
repoRoot: string;
membershipPath: string;
denylistPath: string;
layoutPath: string;
outputDir: string;
} {
const conventions = join(root, "conventions");
const membershipPath = join(conventions, "product-public-skills.yaml");
const denylistPath = join(conventions, "internal-tokens.yaml");
const layoutPath = join(conventions, "skill-edge-layout.yaml");
write(
membershipPath,
"version: 0\nproduct_public: {clean: [alpha], ship_after_fix: [], ship_misses_add: [], sanitize_borderlines_ship: []}\nvendored_ship_with_provenance: []\nnot_public: {}\npending_author_public: []\n",
);
write(
denylistPath,
"version: 1\npath_prefixes: []\nseat_and_rig_patterns: []\nhost_patterns: []\ncharged_terms: []\nfrontmatter_drop_keys: []\ninternal_path_globs: []\nsection_fence: {begin: '<!-- internal:begin -->', end: '<!-- internal:end -->'}\nallowed_context_substrings: []\n",
);
write(layoutPath, layoutYaml());
return {
repoRoot: root,
membershipPath,
denylistPath,
layoutPath,
outputDir: join(root, "scripts"),
};
}
function layoutYaml(): string {
return [
"version: 0",
"owner: skills-architect@example",
"edges:",
" spec:",
" path: packages/daemon/specs/agents/shared/skills",
" layout: categorized",
" canonical:",
" path: skills/_canonical",
" layout: mirror-of-spec",
" plugin:",
" path: packages/daemon/assets/plugins/openrig-core/skills",
" layout: flat",
"extract_from_committed_trees: true",
"forward_overrides: {}",
"reference_layout_da101b29:",
" spec_categorized:",
" pm: [alpha]",
" core: [stale-only]",
" plugin_flat: [stale-only]",
"",
].join("\n");
}
function edgeConfig(): Record<string, unknown> {
return {
spec: {
path: "packages/daemon/specs/agents/shared/skills",
layout: "categorized",
},
canonical: {
path: "skills/_canonical",
layout: "mirror-of-spec",
},
plugin: {
path: "packages/daemon/assets/plugins/openrig-core/skills",
layout: "flat",
},
};
}
function readGenerated(output: string): Record<string, any> {
return {
membership: readJson(join(output, "product-public-skills.generated.json")),
denylist: readJson(join(output, "internal-tokens.generated.json")),
layout: readJson(join(output, "skill-edge-layout.generated.json")),
digests: readJson(join(output, "skill-edge-digests.generated.json")),
};
}
function readJson(path: string): any {
return JSON.parse(readFileSync(path, "utf8"));
}
function write(path: string, content: string): void {
mkdirSync(dirname(path), { recursive: true });
writeFileSync(path, content);
}
function sha256(value: string): string {
return createHash("sha256").update(value).digest("hex");
}
+176
View File
@@ -0,0 +1,176 @@
#!/usr/bin/env node
import { execFileSync } from "node:child_process";
import { existsSync, readFileSync } from "node:fs";
import { resolve } from "node:path";
import {
buildInternalLeakMessage,
scanInternalLeaks,
} from "./internal-leak-scanner.mjs";
export function main(argv = process.argv.slice(2)) {
const options = parseArguments(argv);
const rules = readRules(options.rules);
const files = selectFiles(options);
const findings = files.flatMap(({ path, bytes }) =>
scanInternalLeaks({ path, bytes, rules }),
);
if (findings.length > 0) {
process.stderr.write(`${buildInternalLeakMessage(findings)}\n`);
return 1;
}
return 0;
}
function parseArguments(argv) {
const values = {};
for (let index = 0; index < argv.length; index += 2) {
const key = argv[index];
const value = argv[index + 1];
if (!key?.startsWith("--") || value === undefined) {
throw new Error(`Invalid argument near ${key ?? "<end>"}`);
}
values[key.slice(2)] = value;
}
if (!values.repo || !values.rules || !values.mode) {
throw new Error("--repo, --rules, and --mode are required");
}
if (!["full", "staged", "range"].includes(values.mode)) {
throw new Error(`Unknown mode: ${values.mode}`);
}
if (values.mode === "range" && (!values.from || !values.to)) {
throw new Error("--from and --to are required for range mode");
}
return {
...values,
repo: resolve(values.repo),
rules: resolve(values.rules),
};
}
function readRules(path) {
if (!existsSync(path)) throw new Error(`Rules file not found: ${path}`);
try {
const rules = JSON.parse(readFileSync(path, "utf8"));
validateRules(rules, path);
return rules;
} catch (error) {
if (error.message.startsWith(`Invalid rules file ${path}:`)) throw error;
throw new Error(`Invalid rules file ${path}: ${error.message}`);
}
}
function selectFiles(options) {
if (options.mode === "full") {
return splitNul(git(options.repo, ["ls-files", "-z"]))
.filter((path) => existsSync(resolve(options.repo, path)))
.map((path) => ({
path,
bytes: readFileSync(resolve(options.repo, path)),
}));
}
if (options.mode === "staged") {
return splitNul(
git(options.repo, [
"diff",
"--cached",
"--name-only",
"--diff-filter=ACMR",
"-z",
]),
).map((path) => ({
path,
bytes: git(options.repo, ["show", `:${path}`]),
}));
}
const commits = git(options.repo, [
"rev-list",
"--reverse",
`${options.from}..${options.to}`,
])
.toString("utf8")
.trim()
.split("\n")
.filter(Boolean);
return commits.flatMap((commit) =>
changedPathsAtCommit(options.repo, commit).map((path) => ({
path,
bytes: git(options.repo, ["show", `${commit}:${path}`]),
})),
);
}
function changedPathsAtCommit(repo, commit) {
const ancestry = git(repo, ["rev-list", "--parents", "-n", "1", commit])
.toString("utf8")
.trim()
.split(/\s+/);
const parents = ancestry.slice(1);
if (parents.length === 0) {
return splitNul(git(repo, ["ls-tree", "-r", "--name-only", "-z", commit]));
}
return [
...new Set(
parents.flatMap((parent) =>
splitNul(
git(repo, [
"diff",
"--name-only",
"--diff-filter=ACMR",
"-z",
parent,
commit,
]),
),
),
),
].sort();
}
function validateRules(rules, path) {
if (rules === null || typeof rules !== "object" || Array.isArray(rules)) {
throw new Error(`Invalid rules file ${path}: rules must be an object`);
}
for (const field of [
"path_prefixes",
"seat_and_rig_patterns",
"host_patterns",
"charged_terms",
"internal_path_globs",
"allowed_context_substrings",
]) {
if (
!Array.isArray(rules[field]) ||
rules[field].length === 0 ||
rules[field].some((value) => typeof value !== "string" || value === "")
) {
throw new Error(
`Invalid rules file ${path}: ${field} must be a nonempty string array`,
);
}
}
}
function git(repo, args) {
return execFileSync("git", args, { cwd: repo });
}
function splitNul(bytes) {
return bytes
.toString("utf8")
.split("\0")
.filter(Boolean)
.sort();
}
if (import.meta.url === `file://${resolve(process.argv[1])}`) {
try {
process.exitCode = main();
} catch (error) {
process.stderr.write(`${error.message}\n`);
process.exitCode = 1;
}
}
+244
View File
@@ -0,0 +1,244 @@
import test from "node:test";
import assert from "node:assert/strict";
import { execFileSync, spawnSync } from "node:child_process";
import {
existsSync,
mkdirSync,
mkdtempSync,
readFileSync,
rmSync,
writeFileSync,
} from "node:fs";
import { dirname, join, resolve } from "node:path";
import { tmpdir } from "node:os";
const GUARD = resolve("scripts/check-internal-leak-guard.mjs");
test("full mode scans tracked files and fails loudly on a planted leak", () => {
requireGuard();
withRepo((repo, rules) => {
write(join(repo, "README.md"), "founder-only detail\n");
git(repo, "add", "README.md");
git(repo, "commit", "-m", "tracked leak");
const result = runGuard(repo, rules, "--mode", "full");
assert.notEqual(result.status, 0);
assert.match(result.stderr, /README\.md/);
assert.match(result.stderr, /founder/);
assert.match(result.stderr, /line 1/i);
assert.match(result.stderr, /sidecar|fence|genericize|host-only/i);
});
});
test("full mode passes a clean tracked tree and ignores untracked files", () => {
requireGuard();
withRepo((repo, rules) => {
write(join(repo, "README.md"), "public detail\n");
git(repo, "add", "README.md");
git(repo, "commit", "-m", "clean");
write(join(repo, "untracked.md"), "founder\n");
const result = runGuard(repo, rules, "--mode", "full");
assert.equal(result.status, 0, result.stderr);
});
});
test("staged mode reads index bytes rather than the worktree", () => {
requireGuard();
withRepo((repo, rules) => {
write(join(repo, "README.md"), "clean\n");
git(repo, "add", "README.md");
git(repo, "commit", "-m", "base");
write(join(repo, "README.md"), "operator-agent@kernel\n");
git(repo, "add", "README.md");
write(join(repo, "README.md"), "clean worktree replacement\n");
const result = runGuard(repo, rules, "--mode", "staged");
assert.notEqual(result.status, 0);
assert.match(result.stderr, /operator-agent@/);
assert.doesNotMatch(result.stderr, /clean worktree replacement/);
});
});
test("staged mode handles rename and deletion without reading missing worktree paths", () => {
requireGuard();
withRepo((repo, rules) => {
write(join(repo, "old.md"), "clean\n");
write(join(repo, "delete.md"), "clean\n");
git(repo, "add", ".");
git(repo, "commit", "-m", "base");
git(repo, "mv", "old.md", "renamed.md");
git(repo, "rm", "delete.md");
const result = runGuard(repo, rules, "--mode", "staged");
assert.equal(result.status, 0, result.stderr);
});
});
test("range mode scans exact committed bytes between from and to", () => {
requireGuard();
withRepo((repo, rules) => {
write(join(repo, "README.md"), "clean\n");
git(repo, "add", "README.md");
git(repo, "commit", "-m", "base");
const from = git(repo, "rev-parse", "HEAD").trim();
write(join(repo, "skills", "public", "SKILL.md"), "mm2-secret\n");
git(repo, "add", ".");
git(repo, "commit", "-m", "leak");
const to = git(repo, "rev-parse", "HEAD").trim();
write(join(repo, "README.md"), "clean uncommitted replacement\n");
const result = runGuard(
repo,
rules,
"--mode",
"range",
"--from",
from,
"--to",
to,
);
assert.notEqual(result.status, 0);
assert.match(result.stderr, /skills\/public\/SKILL\.md/);
assert.match(result.stderr, /mm2-/);
});
});
test("range mode rejects a leak from an intermediate pushed commit even when cleaned at to", () => {
requireGuard();
withRepo((repo, rules) => {
write(join(repo, "README.md"), "clean\n");
git(repo, "add", "README.md");
git(repo, "commit", "-m", "base");
const from = git(repo, "rev-parse", "HEAD").trim();
write(join(repo, "README.md"), "founder-only intermediate\n");
git(repo, "add", "README.md");
git(repo, "commit", "-m", "intermediate leak");
write(join(repo, "README.md"), "clean again\n");
git(repo, "add", "README.md");
git(repo, "commit", "-m", "clean tip");
const to = git(repo, "rev-parse", "HEAD").trim();
const result = runGuard(
repo,
rules,
"--mode",
"range",
"--from",
from,
"--to",
to,
);
assert.notEqual(result.status, 0);
assert.match(result.stderr, /README\.md/);
assert.match(result.stderr, /founder/);
});
});
test("each explicit mode refuses a missing generated rules file", () => {
requireGuard();
withRepo((repo) => {
write(join(repo, "README.md"), "clean\n");
git(repo, "add", "README.md");
git(repo, "commit", "-m", "base");
const head = git(repo, "rev-parse", "HEAD").trim();
const missing = join(repo, "missing-rules.json");
for (const args of [
["--mode", "full"],
["--mode", "staged"],
["--mode", "range", "--from", head, "--to", head],
]) {
const result = runGuard(repo, missing, ...args);
assert.notEqual(result.status, 0);
assert.match(result.stderr, /missing-rules\.json|rules/i);
}
});
});
test("each explicit mode refuses an empty generated rules object", () => {
requireGuard();
withRepo((repo) => {
write(join(repo, "README.md"), "clean\n");
git(repo, "add", "README.md");
git(repo, "commit", "-m", "base");
const head = git(repo, "rev-parse", "HEAD").trim();
const empty = join(repo, "empty-rules.json");
writeFileSync(empty, "{}");
for (const args of [
["--mode", "full"],
["--mode", "staged"],
["--mode", "range", "--from", head, "--to", head],
]) {
const result = runGuard(repo, empty, ...args);
assert.notEqual(result.status, 0);
assert.match(result.stderr, /rules|path_prefixes|invalid/i);
}
});
});
test("Plain B keeps guard enforcement out of root test:repo while fixture tests remain discovered", () => {
const pkg = JSON.parse(readFileSync("package.json", "utf8"));
const command = pkg.scripts["test:repo"];
assert.match(command, /node --test scripts\/\*\.test\.mjs/);
assert.doesNotMatch(command, /node scripts\/check-internal-leak-guard\.mjs/);
});
function withRepo(run) {
const root = mkdtempSync(join(tmpdir(), "openrig-guard-red-"));
const repo = join(root, "repo");
const rules = join(root, "rules.json");
mkdirSync(repo, { recursive: true });
writeFileSync(rules, JSON.stringify(fixtureRules()));
git(repo, "init");
git(repo, "config", "user.email", "[email protected]");
git(repo, "config", "user.name", "QA");
try {
run(repo, rules);
} finally {
rmSync(root, { recursive: true, force: true });
}
}
function requireGuard() {
assert.equal(
existsSync(GUARD),
true,
"scripts/check-internal-leak-guard.mjs must exist",
);
}
function runGuard(repo, rules, ...args) {
return spawnSync(
process.execPath,
[GUARD, "--repo", repo, "--rules", rules, ...args],
{ cwd: repo, encoding: "utf8" },
);
}
function git(repo, ...args) {
return execFileSync("git", args, { cwd: repo, encoding: "utf8" });
}
function write(path, content) {
mkdirSync(dirname(path), { recursive: true });
writeFileSync(path, content);
}
function fixtureRules() {
return {
path_prefixes: ["openrig-work/"],
seat_and_rig_patterns: ["operator-agent@"],
host_patterns: ["mm2-"],
charged_terms: ["founder"],
internal_path_globs: ["*.internal.*", "**/internal/**", "*-internal/**"],
allowed_context_substrings: ["do not ship"],
};
}
+74
View File
@@ -0,0 +1,74 @@
export const INTERNAL_LEAK_REMEDY =
"Move the detail to an internal sidecar, fence it, genericize it, or keep the whole file host-only.";
export function scanInternalLeaks({ path, bytes, rules }) {
const normalizedPath = path.replaceAll("\\", "/");
const pathRule = (rules.internal_path_globs ?? []).find((glob) =>
matchesInternalPath(normalizedPath, glob),
);
if (pathRule) {
return [
{
file: normalizedPath,
token: pathRule,
line: 1,
remedy: INTERNAL_LEAK_REMEDY,
kind: "path",
},
];
}
const tokens = [
...(rules.path_prefixes ?? []),
...(rules.seat_and_rig_patterns ?? []),
...(rules.host_patterns ?? []),
...(rules.charged_terms ?? []),
];
const allowed = rules.allowed_context_substrings ?? [];
const findings = [];
const lines = Buffer.from(bytes).toString("utf8").split("\n");
for (const [index, line] of lines.entries()) {
const lowerLine = line.toLowerCase();
if (allowed.some((text) => lowerLine.includes(text.toLowerCase()))) {
continue;
}
for (const token of tokens) {
if (lowerLine.includes(token.toLowerCase())) {
findings.push({
file: normalizedPath,
token,
line: index + 1,
remedy: INTERNAL_LEAK_REMEDY,
kind: "content",
});
}
}
}
return findings;
}
export function buildInternalLeakMessage(findings) {
return [
"Internal content leak detected:",
...findings.map(
({ file, token, line }) => ` ${file}: line ${line}: ${token}`,
),
`Remedy: ${INTERNAL_LEAK_REMEDY}`,
].join("\n");
}
function matchesInternalPath(path, glob) {
const parts = path.split("/");
if (glob === "*.internal.*") {
return parts.some((part) => part.includes(".internal."));
}
if (glob === "**/internal/**") {
return parts.includes("internal");
}
if (glob === "*-internal/**") {
return parts.some((part) => part.endsWith("-internal"));
}
return false;
}
+121
View File
@@ -0,0 +1,121 @@
import test from "node:test";
import assert from "node:assert/strict";
test("scanner blocks internal paths before reading content", async () => {
const scanner = await loadScanner();
const findings = scanner.scanInternalLeaks({
path: "skills/openrig-user/internal/host.md",
bytes: Buffer.from("clean text\n"),
rules: fixtureRules(),
});
assert.deepEqual(findings, [
{
file: "skills/openrig-user/internal/host.md",
token: "**/internal/**",
line: 1,
remedy: scanner.INTERNAL_LEAK_REMEDY,
kind: "path",
},
]);
});
test("scanner reports case-insensitive text tokens with file, token, line, and remedy", async () => {
const scanner = await loadScanner();
const findings = scanner.scanInternalLeaks({
path: "skills/public/SKILL.md",
bytes: Buffer.from("# Public\nContact OPERATOR-AGENT@kernel.\n"),
rules: fixtureRules(),
});
assert.equal(findings.length, 1);
assert.deepEqual(findings[0], {
file: "skills/public/SKILL.md",
token: "operator-agent@",
line: 2,
remedy: scanner.INTERNAL_LEAK_REMEDY,
kind: "content",
});
assert.match(scanner.buildInternalLeakMessage(findings), /skills\/public\/SKILL\.md/);
assert.match(scanner.buildInternalLeakMessage(findings), /operator-agent@/);
assert.match(scanner.buildInternalLeakMessage(findings), /line 2/i);
assert.match(scanner.buildInternalLeakMessage(findings), /sidecar|fence|genericize|host-only/i);
});
test("scanner honors an allowed-context substring on the same line only", async () => {
const scanner = await loadScanner();
const findings = scanner.scanInternalLeaks({
path: "skills/public/SKILL.md",
bytes: Buffer.from(
[
"Do not ship operator-agent@ examples.",
"A later operator-agent@ reference is still a leak.",
"",
].join("\n"),
),
rules: fixtureRules(),
});
assert.equal(findings.length, 1);
assert.equal(findings[0].line, 2);
});
test("scanner finds tokens in NUL-containing bytes and does not crash on clean binary bytes", async () => {
const scanner = await loadScanner();
const planted = scanner.scanInternalLeaks({
path: "skills/public/fixture.bin",
bytes: Buffer.concat([
Buffer.from([0, 1, 2]),
Buffer.from("mm2-secret"),
Buffer.from([0, 255]),
]),
rules: fixtureRules(),
});
assert.ok(planted.some((finding) => finding.token === "mm2-"));
assert.doesNotThrow(() =>
scanner.scanInternalLeaks({
path: "skills/public/clean.bin",
bytes: Buffer.from([0, 1, 2, 3, 255]),
rules: fixtureRules(),
}),
);
});
test("scanner returns findings in deterministic path/token/line order", async () => {
const scanner = await loadScanner();
const findings = scanner.scanInternalLeaks({
path: "skills/public/SKILL.md",
bytes: Buffer.from("founder\noperator-agent@kernel\nfounder\n"),
rules: fixtureRules(),
});
assert.deepEqual(
findings.map(({ token, line }) => ({ token, line })),
[
{ token: "founder", line: 1 },
{ token: "operator-agent@", line: 2 },
{ token: "founder", line: 3 },
],
);
});
async function loadScanner() {
const moduleUrl = new URL("./internal-leak-scanner.mjs", import.meta.url);
const scanner = await import(moduleUrl).catch(() => null);
assert.ok(scanner, "scripts/internal-leak-scanner.mjs must exist");
assert.equal(typeof scanner.scanInternalLeaks, "function");
assert.equal(typeof scanner.buildInternalLeakMessage, "function");
return scanner;
}
function fixtureRules() {
return {
path_prefixes: ["openrig-work/"],
seat_and_rig_patterns: ["operator-agent@"],
host_patterns: ["mm2-"],
charged_terms: ["founder"],
internal_path_globs: ["*.internal.*", "**/internal/**", "*-internal/**"],
allowed_context_substrings: ["do not ship"],
};
}
+67
View File
@@ -0,0 +1,67 @@
{
"version": 1,
"path_prefixes": [
"openrig-work/",
"openrig-internal/",
"/Users/wrandom/",
"~/code/substrate/",
"code/substrate/shared-docs/",
".openrig/"
],
"seat_and_rig_patterns": [
"@skill-library",
"@kernel",
"@openrig-pm",
"@openrig-studio",
"@openrig-comms",
"@v-openrig-build",
"operator-agent@",
"pm-lead@",
"skills-architect@",
"skills-analyst@",
"openrig-velocity",
"redo-driver",
"dev1-guard",
"dev-driver3"
],
"host_patterns": [
"mm2-",
"ovh1-",
"linkpix",
"vm-claude-test"
],
"charged_terms": [
"dogfood",
"founder",
"founder-owned host",
"one open trust domain",
"default-open",
"bomb on this host",
"the unforgivable sin"
],
"frontmatter_drop_keys": [
"source_evidence",
"curation_note",
"content_curator",
"transfer_test",
"transfer_test_notes",
"naming_note",
"last_verified",
"structure"
],
"internal_path_globs": [
"*.internal.*",
"**/internal/**",
"*-internal/**"
],
"section_fence": {
"begin": "<!-- internal:begin -->",
"end": "<!-- internal:end -->"
},
"allowed_context_substrings": [
"deprecated",
"defunct",
"do not ship",
"internal-only (example)"
]
}
+585 -11
View File
@@ -1,6 +1,22 @@
import { execFileSync } from "node:child_process";
import { existsSync, mkdirSync } from "node:fs";
import { resolve } from "node:path";
import { createHash } from "node:crypto";
import {
copyFileSync,
existsSync,
mkdirSync,
mkdtempSync,
readFileSync,
readdirSync,
rmSync,
lstatSync,
writeFileSync,
} from "node:fs";
import { tmpdir } from "node:os";
import { basename, dirname, join, relative, resolve } from "node:path";
import {
buildInternalLeakMessage,
scanInternalLeaks,
} from "./internal-leak-scanner.mjs";
// Mirror canonical skills from packages/daemon/specs/agents/shared/skills/
// to <repo-root>/skills/_canonical/. Hand-authored files at <repo-root>/skills/
@@ -102,33 +118,591 @@ export function checkModeAbsolute(sourceDir, targetDir, exec = execFileSync) {
return { stale: changes.length > 0, changes, output };
}
const SHIP_CATEGORIES = [
"clean",
"ship_after_fix",
"ship_misses_add",
"sanitize_borderlines_ship",
];
export function shipSetFromMembership(membership) {
const productPublic = membership?.product_public ?? {};
const excluded = new Set([
...Object.values(membership?.not_public ?? {}).flat(),
...(membership?.pending_author_public ?? []),
]);
return [
...SHIP_CATEGORIES.flatMap((category) => productPublic[category] ?? []),
...(membership?.vendored_ship_with_provenance ?? []),
]
.filter((skill) => !excluded.has(skill))
.filter((skill, index, all) => all.indexOf(skill) === index)
.sort();
}
export async function stagePublicSkills({
canonRoot,
stagingRoot,
membership,
rules,
}) {
rmSync(stagingRoot, { recursive: true, force: true });
mkdirSync(stagingRoot, { recursive: true });
for (const skill of shipSetFromMembership(membership)) {
const sourceRoot = join(canonRoot, skill);
if (!existsSync(sourceRoot) || isInternalPath(skill, rules)) continue;
for (const sourcePath of walkFiles(sourceRoot)) {
const skillRelative = relative(sourceRoot, sourcePath).replaceAll("\\", "/");
const publicPath = `${skill}/${skillRelative}`;
if (isExcludedPath(skillRelative)) continue;
if (isInternalPath(publicPath, rules)) continue;
let bytes = readFileSync(sourcePath);
if (basename(sourcePath) === "SKILL.md") {
const transformed = stripPublicSkill(
bytes.toString("utf8"),
publicPath,
rules,
);
bytes = Buffer.from(transformed);
} else if (/\.mdx?$/i.test(sourcePath)) {
bytes = Buffer.from(
stripInternalFences(
bytes.toString("utf8"),
publicPath,
rules.section_fence,
),
);
}
const findings = scanInternalLeaks({ path: publicPath, bytes, rules });
if (findings.length > 0) {
throw new Error(buildInternalLeakMessage(findings));
}
const targetPath = join(stagingRoot, skillRelative === "." ? skill : publicPath);
mkdirSync(dirname(targetPath), { recursive: true });
writeFileSync(targetPath, bytes);
}
}
}
export async function checkGeneratedEdges({
repoRoot = process.cwd(),
layout,
digests,
}) {
validateGeneratedControls(layout, digests);
const changes = [];
for (const [edge, edgeConfig] of Object.entries(layout.edges).sort()) {
const edgeRoot = join(repoRoot, edgeConfig.path);
const expected = digests?.edges?.[edge] ?? {};
const edgeFiles = walkFiles(edgeRoot);
const actual = Object.fromEntries(
edgeFiles.map((path) => [
relative(edgeRoot, path).replaceAll("\\", "/"),
sha256(readFileSync(path)),
]),
);
for (const path of Object.keys(expected).sort()) {
if (!(path in actual)) {
changes.push({ edge, path, reason: "missing" });
} else if (actual[path] !== expected[path]) {
changes.push({ edge, path, reason: "digest" });
}
}
for (const path of Object.keys(actual).sort()) {
if (!(path in expected)) {
changes.push({ edge, path, reason: "unexpected" });
}
}
const actualSkills = new Map(
edgeFiles
.filter((path) => basename(path) === "SKILL.md")
.map((path) => {
const rel = relative(edgeRoot, path).replaceAll("\\", "/");
const parts = rel.split("/");
const flat = edgeConfig.layout === "flat";
return [
flat ? parts[0] : parts.at(-2),
{ path: rel, category: flat ? null : parts[0] },
];
}),
);
for (const [skill, actualEntry] of actualSkills) {
const expectedEntry = layout.skills?.[skill];
if (!expectedEntry?.edges?.includes(edge)) {
changes.push({
edge,
path: actualEntry.path,
reason: "layout-unexpected",
});
} else if (
actualEntry.category !== null &&
expectedEntry.category !== actualEntry.category
) {
changes.push({
edge,
path: actualEntry.path,
reason: "layout-category",
});
}
}
for (const [skill, expectedEntry] of Object.entries(
layout.skills ?? {},
).sort()) {
if (expectedEntry.edges.includes(edge) && !actualSkills.has(skill)) {
changes.push({
edge,
path: skill,
reason: "layout-missing",
});
}
}
}
return { stale: changes.length > 0, changes };
}
export async function regeneratePublicSkills({
canonRoot,
repoRoot,
membership,
rules,
layout,
exec = execFileSync,
}) {
validateAuthoringLayout(layout);
const shipping = shipSetFromMembership(membership);
for (const skill of shipping) {
if (!existsSync(join(canonRoot, skill))) {
throw new Error(`Missing shipping skill in canon: ${skill}`);
}
if (!layout.skills[skill]?.edges?.length) {
throw new Error(`Missing shipping skill in edge layout: ${skill}`);
}
}
const temporaryRoot = mkdtempSync(join(tmpdir(), "openrig-public-skills-"));
try {
const stagingRoot = join(temporaryRoot, "staging");
await stagePublicSkills({
canonRoot,
stagingRoot,
membership,
rules,
});
const changes = [];
for (const [edge, edgeConfig] of Object.entries(layout.edges).sort()) {
const projectedRoot = join(temporaryRoot, `edge-${edge}`);
mkdirSync(projectedRoot, { recursive: true });
for (const skill of shipping) {
const skillLayout = layout.skills[skill];
if (!skillLayout.edges.includes(edge)) continue;
const category =
edgeConfig.layout === "flat" ? null : skillLayout.category;
if (edgeConfig.layout !== "flat" && !category) {
throw new Error(`Missing category for shipping skill ${skill} on ${edge}`);
}
const destination = category
? join(projectedRoot, category, skill)
: join(projectedRoot, skill);
copyTree(join(stagingRoot, skill), destination);
}
const edgeRoot = join(repoRoot, edgeConfig.path);
mkdirSync(edgeRoot, { recursive: true });
const output = runRsyncAbsolute(projectedRoot, edgeRoot, exec);
for (const line of parseChanges(output)) {
changes.push({
edge,
path: rsyncChangePath(line),
reason: line.startsWith("*deleting ") ? "delete" : "write",
});
}
}
return { changes };
} finally {
rmSync(temporaryRoot, { recursive: true, force: true });
}
}
export async function authoringApplyMode({
generateControlPlaneJson = defaultGenerateControlPlaneJson,
readAuthoringInputs = defaultReadAuthoringInputs,
regeneratePublicSkills: regenerate = regeneratePublicSkills,
} = {}) {
await generateControlPlaneJson();
const inputs = readAuthoringInputs();
const result = await regenerate(inputs);
if (result.changes.length > 0) {
await generateControlPlaneJson();
}
return result;
}
export function applyMode(exec = execFileSync) {
ensureTargetExists();
return runRsync({ dryRun: false }, exec);
}
export function main(argv = process.argv.slice(2)) {
export async function main(argv = process.argv.slice(2), dependencies = {}) {
const isCheck = argv.includes("--check");
if (isCheck) {
const { stale, changes } = checkMode();
const verify = dependencies.checkGeneratedEdges ?? checkGeneratedEdges;
const inputs =
dependencies.checkInputs ??
(dependencies.checkGeneratedEdges ? {} : readGeneratedCheckInputs());
const { stale, changes } = await verify(inputs);
if (stale) {
console.error(buildStaleMessage(changes));
console.error(buildGeneratedStaleMessage(changes));
process.exitCode = 1;
}
return;
}
const output = applyMode();
const changes = parseChanges(output);
const apply = dependencies.authoringApplyMode ?? authoringApplyMode;
const log = dependencies.log ?? console.log;
const { changes } = await apply();
if (changes.length === 0) {
console.log("Skills mirror already in sync; no changes.");
log("Public skill edges already in sync; no changes.");
} else {
console.log(`Skills mirror updated. ${changes.length} change(s):`);
for (const c of changes) console.log(` ${c}`);
log(`Public skill edge regeneration measured ${changes.length} change(s):`);
for (const { edge, path, reason } of changes) {
log(` ${edge}: ${path} (${reason})`);
}
}
}
if (import.meta.url === `file://${resolve(process.argv[1])}`) {
main();
await main();
}
function readGeneratedCheckInputs() {
const repoRoot = process.cwd();
return {
repoRoot,
layout: readJson(join(repoRoot, "scripts/skill-edge-layout.generated.json")),
digests: readJson(join(repoRoot, "scripts/skill-edge-digests.generated.json")),
};
}
function readJson(path) {
return JSON.parse(readFileSync(path, "utf8"));
}
function defaultGenerateControlPlaneJson() {
const repoRoot = process.cwd();
const required = {
membership: process.env.OPENRIG_PRODUCT_PUBLIC_SKILLS_YAML,
denylist: process.env.OPENRIG_INTERNAL_TOKENS_YAML,
layout: process.env.OPENRIG_SKILL_EDGE_LAYOUT_YAML,
};
for (const [name, path] of Object.entries(required)) {
if (!path) {
throw new Error(
`Authoring apply requires the ${name} authority path environment variable`,
);
}
}
execFileSync(
process.execPath,
[
join(repoRoot, "packages/daemon/scripts/gen-control-plane-json.mjs"),
"--repo-root",
repoRoot,
"--membership",
required.membership,
"--denylist",
required.denylist,
"--layout",
required.layout,
"--output",
join(repoRoot, "scripts"),
],
{ cwd: repoRoot, stdio: "inherit" },
);
}
function defaultReadAuthoringInputs() {
const repoRoot = process.cwd();
const canonRoot = process.env.OPENRIG_SKILL_CANON_ROOT;
if (!canonRoot) {
throw new Error(
"Authoring apply requires OPENRIG_SKILL_CANON_ROOT; real apply is founder-gated",
);
}
return {
repoRoot,
canonRoot: resolve(canonRoot),
membership: readJson(
join(repoRoot, "scripts/product-public-skills.generated.json"),
),
rules: readJson(join(repoRoot, "scripts/internal-tokens.generated.json")),
layout: readJson(
join(repoRoot, "scripts/skill-edge-layout.generated.json"),
),
};
}
function buildGeneratedStaleMessage(changes) {
return [
"Generated skill edges are stale. Regenerate the control-plane manifests.",
...changes.map(
({ edge, path, reason }) => ` ${edge}: ${path} (${reason})`,
),
].join("\n");
}
function stripPublicSkill(content, path, rules) {
const withoutFences = stripInternalFences(content, path, rules.section_fence);
const lines = withoutFences.split("\n");
if (lines[0] !== "---") return withoutFences;
const end = lines.indexOf("---", 1);
if (end === -1) return withoutFences;
const frontmatter = lines.slice(1, end);
const kept = sanitizeFrontmatterEntries(frontmatter, 0, rules);
return ["---", ...kept, "---", ...lines.slice(end + 1)].join("\n");
}
function sanitizeFrontmatterEntries(lines, indent, rules) {
const kept = [];
for (let index = 0; index < lines.length; ) {
const match = frontmatterEntry(lines[index]);
if (!match || match.indent !== indent) {
kept.push(lines[index]);
index += 1;
continue;
}
let next = index + 1;
while (next < lines.length) {
const candidate = frontmatterEntry(lines[next]);
if (candidate && candidate.indent <= indent) break;
next += 1;
}
const children = lines.slice(index + 1, next);
const configured =
match.key === "distribution_scope" ||
(rules.frontmatter_drop_keys ?? []).includes(match.key);
if (configured) {
index = next;
continue;
}
const childIndent = firstEntryIndent(children);
if (match.value === "" && childIndent !== null) {
const sanitized = sanitizeFrontmatterEntries(children, childIndent, rules);
if (sanitized.some((line) => line.trim() !== "")) {
kept.push(lines[index], ...sanitized);
}
} else {
const entry = [lines[index], ...children];
if (!containsInternalValue(entry.join("\n"), rules)) kept.push(...entry);
}
index = next;
}
return kept;
}
function frontmatterEntry(line) {
const match = /^(\s*)([A-Za-z0-9_-]+):(?:\s*(.*))?$/.exec(line);
return match
? { indent: match[1].length, key: match[2], value: match[3] ?? "" }
: null;
}
function firstEntryIndent(lines) {
for (const line of lines) {
const entry = frontmatterEntry(line);
if (entry) return entry.indent;
}
return null;
}
function stripInternalFences(content, path, fence) {
if (!fence?.begin || !fence?.end) return content;
const lines = content.split("\n");
const kept = [];
let openedAt = null;
for (const [index, line] of lines.entries()) {
if (line.includes(fence.begin)) {
if (openedAt !== null) {
throw new Error(`${path}: unmatched internal fence at line ${index + 1}`);
}
openedAt = index + 1;
continue;
}
if (line.includes(fence.end)) {
if (openedAt === null) {
throw new Error(`${path}: unmatched internal fence at line ${index + 1}`);
}
openedAt = null;
continue;
}
if (openedAt === null) kept.push(line);
}
if (openedAt !== null) {
throw new Error(`${path}: unmatched internal fence at line ${openedAt}`);
}
return kept.join("\n");
}
function containsInternalValue(value, rules) {
const lower = value.toLowerCase();
return [
...(rules.path_prefixes ?? []),
...(rules.seat_and_rig_patterns ?? []),
...(rules.host_patterns ?? []),
...(rules.charged_terms ?? []),
].some((token) => lower.includes(token.toLowerCase()));
}
function isInternalPath(path, rules) {
const normalized = path.replaceAll("\\", "/");
const parts = normalized.split("/");
return (rules.internal_path_globs ?? []).some((glob) => {
if (glob === "*.internal.*") {
return parts.some((part) => part.includes(".internal."));
}
if (glob === "**/internal/**") {
return parts.includes("internal");
}
if (glob === "*-internal/**") {
return parts.some((part) => part.endsWith("-internal"));
}
return false;
});
}
function isExcludedPath(path) {
const normalized = path.replaceAll("\\", "/");
const parts = normalized.split("/");
const file = parts.at(-1);
return EXCLUDES.some((pattern) => {
if (pattern === "feedback.md") return file === pattern;
if (pattern === "evals/") return parts.includes("evals");
if (pattern === ".DS_Store") return file === pattern;
if (pattern === "*.local.md") return file.endsWith(".local.md");
return false;
});
}
function walkFiles(root) {
const stat = lstatSync(root, { throwIfNoEntry: false });
if (!stat) return [];
if (stat.isSymbolicLink()) {
throw new Error(`${root}: symlink entries are not allowed`);
}
if (stat.isFile()) return [root];
if (!stat.isDirectory()) {
throw new Error(`${root}: unsupported filesystem entry`);
}
return readdirSync(root)
.sort()
.flatMap((entry) => walkFiles(join(root, entry)));
}
function copyTree(sourceRoot, targetRoot) {
for (const sourcePath of walkFiles(sourceRoot)) {
const targetPath = join(targetRoot, relative(sourceRoot, sourcePath));
mkdirSync(dirname(targetPath), { recursive: true });
copyFileSync(sourcePath, targetPath);
}
}
function runRsyncAbsolute(sourceRoot, targetRoot, exec) {
return exec(
"rsync",
[
"-a",
"--delete",
"--delete-excluded",
"--itemize-changes",
...EXCLUDES.map((pattern) => `--exclude=${pattern}`),
`${sourceRoot}/`,
`${targetRoot}/`,
],
{
encoding: "utf8",
stdio: ["ignore", "pipe", "inherit"],
},
);
}
function rsyncChangePath(line) {
if (line.startsWith("*deleting ")) return line.slice("*deleting ".length);
const separator = line.indexOf(" ");
return separator === -1 ? line : line.slice(separator + 1).trim();
}
function validateGeneratedControls(layout, digests) {
validateAuthoringLayout(layout);
if (!isRecord(digests?.edges) || Object.keys(digests.edges).length === 0) {
throw new Error("Digest control must contain edge inventories");
}
for (const edge of Object.keys(layout.edges)) {
if (!isRecord(digests.edges[edge])) {
throw new Error(`Digest control is missing edge ${edge}`);
}
}
}
function validateAuthoringLayout(layout) {
if (!isRecord(layout?.edges) || Object.keys(layout.edges).length === 0) {
throw new Error("Layout control must contain edges");
}
const edgeNames = Object.keys(layout.edges).sort();
const requiredEdges = ["canonical", "plugin", "spec"];
if (
edgeNames.length !== requiredEdges.length ||
edgeNames.some((edge, index) => edge !== requiredEdges[index])
) {
throw new Error(
"Layout control must contain exactly canonical, plugin, and spec edges",
);
}
if (!isRecord(layout.skills) || Object.keys(layout.skills).length === 0) {
throw new Error("Layout control must contain skills");
}
for (const [edge, config] of Object.entries(layout.edges)) {
if (
!isRecord(config) ||
typeof config.path !== "string" ||
config.path.length === 0 ||
!["categorized", "mirror-of-spec", "flat"].includes(config.layout)
) {
throw new Error(`Layout control has invalid edge ${edge}`);
}
}
for (const [skill, config] of Object.entries(layout.skills)) {
if (
!isRecord(config) ||
!Array.isArray(config.edges) ||
config.edges.length === 0 ||
config.edges.some((edge) => !Object.hasOwn(layout.edges, edge))
) {
throw new Error(`Layout control has invalid skill ${skill}`);
}
}
}
function isRecord(value) {
return value !== null && typeof value === "object" && !Array.isArray(value);
}
function sha256(bytes) {
return createHash("sha256").update(bytes).digest("hex");
}
+870 -2
View File
@@ -1,8 +1,20 @@
import test from "node:test";
import assert from "node:assert/strict";
import { execFileSync } from "node:child_process";
import { existsSync, readdirSync, statSync } from "node:fs";
import { join } from "node:path";
import {
existsSync,
mkdirSync,
mkdtempSync,
readFileSync,
readdirSync,
rmSync,
statSync,
symlinkSync,
writeFileSync,
} from "node:fs";
import { createHash } from "node:crypto";
import { dirname, join } from "node:path";
import { tmpdir } from "node:os";
import {
SOURCE_DIR,
TARGET_DIR,
@@ -10,6 +22,7 @@ import {
parseChanges,
buildStaleMessage,
checkMode,
checkModeAbsolute,
} from "./mirror-skills.mjs";
test("parseChanges extracts file-change and deletion lines from itemize-changes output", () => {
@@ -120,8 +133,863 @@ test("excluded patterns are absent in the mirror target", () => {
);
});
test("stagePublicSkills applies membership, path, frontmatter, and fence transforms", async () => {
const mirror = await import("./mirror-skills.mjs");
assert.equal(
typeof mirror.stagePublicSkills,
"function",
"stagePublicSkills must be exported",
);
const root = mkdtempSync(join(tmpdir(), "openrig-mirror-red-"));
try {
const canon = join(root, "canon");
const staging = join(root, "staging");
write(
join(canon, "public-skill", "SKILL.md"),
[
"---",
"name: public-skill",
"description: public",
"distribution_scope: product-bound",
"source_evidence: openrig-work/private.md",
"curation_note: remove-me",
"content_curator: reviewer",
"transfer_test: pending",
"naming_note: historical",
"last_verified: 2026-07-01",
"private_path: openrig-work/host-only.md",
"internal_owner: operator-agent@kernel",
"metadata:",
" openrig:",
" stage: factory-approved",
" sibling_skills:",
" - public-sibling",
"---",
"",
"Visible.",
"<!-- internal:begin -->",
"operator-agent@kernel",
"<!-- internal:end -->",
"Still visible.",
"",
].join("\n"),
);
write(join(canon, "public-skill", "notes.internal.md"), "secret\n");
write(join(canon, "public-skill", "internal", "host.md"), "secret\n");
write(join(canon, "private-skill", "SKILL.md"), "# private\n");
write(join(canon, "operator-internal", "SKILL.md"), "# whole internal\n");
await mirror.stagePublicSkills({
canonRoot: canon,
stagingRoot: staging,
membership: membershipFixture({
clean: ["public-skill", "operator-internal"],
}),
rules: fixtureRules(),
});
const shipped = readFileSync(
join(staging, "public-skill", "SKILL.md"),
"utf8",
);
assert.match(shipped, /name: public-skill/);
assert.match(shipped, /metadata:/);
assert.match(shipped, /stage: factory-approved/);
assert.match(shipped, /public-sibling/);
assert.match(shipped, /Visible\./);
assert.match(shipped, /Still visible\./);
assert.doesNotMatch(shipped, /distribution_scope/);
assert.doesNotMatch(shipped, /source_evidence/);
assert.doesNotMatch(shipped, /curation_note/);
assert.doesNotMatch(shipped, /content_curator/);
assert.doesNotMatch(shipped, /transfer_test/);
assert.doesNotMatch(shipped, /naming_note/);
assert.doesNotMatch(shipped, /last_verified/);
assert.doesNotMatch(shipped, /private_path/);
assert.doesNotMatch(shipped, /internal_owner/);
assert.doesNotMatch(shipped, /operator-agent@kernel/);
assert.equal(existsSync(join(staging, "public-skill", "notes.internal.md")), false);
assert.equal(existsSync(join(staging, "public-skill", "internal")), false);
assert.equal(existsSync(join(staging, "private-skill")), false);
assert.equal(existsSync(join(staging, "operator-internal")), false);
} finally {
rmSync(root, { recursive: true, force: true });
}
});
test("stagePublicSkills applies existing EXCLUDES before scanning or copying", async () => {
const mirror = await import("./mirror-skills.mjs");
const root = mkdtempSync(join(tmpdir(), "openrig-mirror-excludes-red-"));
try {
const canon = join(root, "canon");
const staging = join(root, "staging");
write(join(canon, "alpha", "SKILL.md"), "# Public alpha\n");
for (const path of [
"feedback.md",
"evals/case.md",
".DS_Store",
"notes.local.md",
]) {
write(join(canon, "alpha", path), "founder-only excluded fixture\n");
}
await mirror.stagePublicSkills({
canonRoot: canon,
stagingRoot: staging,
membership: membershipFixture({ clean: ["alpha"] }),
rules: fixtureRules(),
});
assert.equal(
readFileSync(join(staging, "alpha", "SKILL.md"), "utf8"),
"# Public alpha\n",
);
for (const path of [
"feedback.md",
"evals",
".DS_Store",
"notes.local.md",
]) {
assert.equal(existsSync(join(staging, "alpha", path)), false);
}
} finally {
rmSync(root, { recursive: true, force: true });
}
});
test("stagePublicSkills recursively strips configured keys and internal values while preserving clean metadata", async () => {
const mirror = await import("./mirror-skills.mjs");
assert.equal(typeof mirror.stagePublicSkills, "function");
const root = mkdtempSync(join(tmpdir(), "openrig-mirror-nested-red-"));
try {
const canon = join(root, "canon");
const staging = join(root, "staging");
write(
join(canon, "public-skill", "SKILL.md"),
[
"---",
"name: public-skill",
"structure: public-sections",
"metadata:",
" openrig:",
" stage: factory-approved",
" distribution_scope: product-bound",
" source_evidence: |",
" private provenance",
" curation_note: remove-me",
" content_curator: reviewer",
" transfer_test: pending",
" transfer_test_notes: remove-me-too",
" naming_note: historical",
" last_verified: 2026-07-01",
" structure: openrig-work/private-layout.md",
" source_location: openrig-work/private-source.md",
" sibling_skills:",
" - public-sibling",
"---",
"",
"# Public",
"",
].join("\n"),
);
await mirror.stagePublicSkills({
canonRoot: canon,
stagingRoot: staging,
membership: membershipFixture({ clean: ["public-skill"] }),
rules: fixtureRules(),
});
const shipped = readFileSync(
join(staging, "public-skill", "SKILL.md"),
"utf8",
);
assert.match(shipped, /metadata:/);
assert.match(shipped, /stage: factory-approved/);
assert.match(shipped, /sibling_skills:/);
assert.match(shipped, /public-sibling/);
for (const key of [
"distribution_scope",
"source_evidence",
"curation_note",
"content_curator",
"transfer_test",
"transfer_test_notes",
"naming_note",
"last_verified",
"structure",
]) {
assert.doesNotMatch(shipped, new RegExp(`^\\s*${key}:`, "m"));
}
assert.doesNotMatch(shipped, /^\s*source_location:/m);
assert.doesNotMatch(shipped, /openrig-work\/private-(?:layout|source)/);
} finally {
rmSync(root, { recursive: true, force: true });
}
});
test("stagePublicSkills aborts an unmatched internal fence with file and line", async () => {
const mirror = await import("./mirror-skills.mjs");
assert.equal(typeof mirror.stagePublicSkills, "function");
const root = mkdtempSync(join(tmpdir(), "openrig-mirror-red-"));
try {
const canon = join(root, "canon");
write(
join(canon, "public-skill", "SKILL.md"),
"# Public\n\n<!-- internal:begin -->\nsecret\n",
);
await assert.rejects(
mirror.stagePublicSkills({
canonRoot: canon,
stagingRoot: join(root, "staging"),
membership: membershipFixture({ clean: ["public-skill"] }),
rules: fixtureRules(),
}),
(error) => {
assert.match(error.message, /public-skill\/SKILL\.md/);
assert.match(error.message, /line 3/i);
assert.match(error.message, /unbalanced|unmatched/i);
return true;
},
);
} finally {
rmSync(root, { recursive: true, force: true });
}
});
test("stagePublicSkills strips internal fences from non-SKILL Markdown without sanitizing ordinary content", async () => {
const mirror = await import("./mirror-skills.mjs");
const root = mkdtempSync(join(tmpdir(), "openrig-mirror-markdown-red-"));
try {
const canon = join(root, "canon");
const staging = join(root, "staging");
write(join(canon, "alpha", "SKILL.md"), "# Public skill\n");
for (const path of ["references/guide.md", "references/walkthrough.mdx"]) {
write(
join(canon, "alpha", path),
[
"---",
"distribution_scope: ordinary-reference-metadata",
"---",
"# Public before",
"ordinary reference content remains",
"<!-- internal:begin -->",
"private prose deliberately outside the denylist",
"<!-- internal:end -->",
"# Public after",
"",
].join("\n"),
);
}
await mirror.stagePublicSkills({
canonRoot: canon,
stagingRoot: staging,
membership: membershipFixture({ clean: ["alpha"] }),
rules: fixtureRules(),
});
for (const path of ["references/guide.md", "references/walkthrough.mdx"]) {
const shipped = readFileSync(join(staging, "alpha", path), "utf8");
assert.match(shipped, /distribution_scope: ordinary-reference-metadata/);
assert.match(shipped, /# Public before/);
assert.match(shipped, /ordinary reference content remains/);
assert.match(shipped, /# Public after/);
assert.doesNotMatch(shipped, /internal:(?:begin|end)/);
assert.doesNotMatch(shipped, /private prose deliberately/);
}
} finally {
rmSync(root, { recursive: true, force: true });
}
});
test("stagePublicSkills rejects an unmatched fence in non-SKILL Markdown with file and line", async () => {
const mirror = await import("./mirror-skills.mjs");
const root = mkdtempSync(join(tmpdir(), "openrig-mirror-markdown-red-"));
try {
const canon = join(root, "canon");
write(join(canon, "alpha", "SKILL.md"), "# Public skill\n");
write(
join(canon, "alpha", "references", "guide.mdx"),
"# Public\n\n<!-- internal:begin -->\nprivate prose\n",
);
await assert.rejects(
mirror.stagePublicSkills({
canonRoot: canon,
stagingRoot: join(root, "staging"),
membership: membershipFixture({ clean: ["alpha"] }),
rules: fixtureRules(),
}),
(error) => {
assert.match(error.message, /alpha\/references\/guide\.mdx/);
assert.match(error.message, /line 3/i);
assert.match(error.message, /unbalanced|unmatched/i);
return true;
},
);
} finally {
rmSync(root, { recursive: true, force: true });
}
});
test("stagePublicSkills rejects canon file symlinks before reading or staging outside bytes", async () => {
const mirror = await import("./mirror-skills.mjs");
const root = mkdtempSync(join(tmpdir(), "openrig-mirror-symlink-red-"));
try {
const canon = join(root, "canon");
const staging = join(root, "staging");
const outside = join(root, "outside-file.txt");
write(join(canon, "alpha", "SKILL.md"), "# Public skill\n");
write(outside, "outside bytes deliberately outside the denylist\n");
const link = join(canon, "alpha", "references", "linked.txt");
mkdirSync(dirname(link), { recursive: true });
symlinkSync(outside, link, "file");
await assert.rejects(
mirror.stagePublicSkills({
canonRoot: canon,
stagingRoot: staging,
membership: membershipFixture({ clean: ["alpha"] }),
rules: fixtureRules(),
}),
(error) => {
assert.match(error.message, /symlink/i);
assert.match(error.message, /alpha\/references\/linked\.txt/);
return true;
},
);
assert.equal(
existsSync(join(staging, "alpha", "references", "linked.txt")),
false,
);
} finally {
rmSync(root, { recursive: true, force: true });
}
});
test("stagePublicSkills rejects canon directory symlinks before recursion or staging outside bytes", async () => {
const mirror = await import("./mirror-skills.mjs");
const root = mkdtempSync(join(tmpdir(), "openrig-mirror-symlink-red-"));
try {
const canon = join(root, "canon");
const staging = join(root, "staging");
const outside = join(root, "outside-directory");
write(join(canon, "alpha", "SKILL.md"), "# Public skill\n");
write(join(outside, "secret.txt"), "outside directory bytes\n");
const link = join(canon, "alpha", "references", "linked-directory");
mkdirSync(dirname(link), { recursive: true });
symlinkSync(outside, link, "dir");
await assert.rejects(
mirror.stagePublicSkills({
canonRoot: canon,
stagingRoot: staging,
membership: membershipFixture({ clean: ["alpha"] }),
rules: fixtureRules(),
}),
(error) => {
assert.match(error.message, /symlink/i);
assert.match(error.message, /alpha\/references\/linked-directory/);
return true;
},
);
assert.equal(
existsSync(join(staging, "alpha", "references", "linked-directory")),
false,
);
} finally {
rmSync(root, { recursive: true, force: true });
}
});
test("stagePublicSkills is deterministic for the same clean input", async () => {
const mirror = await import("./mirror-skills.mjs");
assert.equal(typeof mirror.stagePublicSkills, "function");
const root = mkdtempSync(join(tmpdir(), "openrig-mirror-red-"));
try {
const canon = join(root, "canon");
write(join(canon, "public-skill", "SKILL.md"), "---\nname: public-skill\n---\n\n# Public\n");
write(join(canon, "public-skill", "references", "guide.md"), "# Guide\n");
const first = join(root, "first");
const second = join(root, "second");
const input = {
canonRoot: canon,
membership: membershipFixture({ clean: ["public-skill"] }),
rules: fixtureRules(),
};
await mirror.stagePublicSkills({ ...input, stagingRoot: first });
await mirror.stagePublicSkills({ ...input, stagingRoot: second });
assert.deepEqual(snapshotTree(first), snapshotTree(second));
} finally {
rmSync(root, { recursive: true, force: true });
}
});
test("shipSetFromMembership consumes exactly the five shipping categories", async () => {
const mirror = await import("./mirror-skills.mjs");
assert.equal(
typeof mirror.shipSetFromMembership,
"function",
"shipSetFromMembership must be exported",
);
const shipSet = mirror.shipSetFromMembership({
version: 0,
product_public: {
clean: ["clean", "duplicate"],
ship_after_fix: ["after", "duplicate"],
ship_misses_add: ["miss"],
sanitize_borderlines_ship: ["sanitize"],
},
vendored_ship_with_provenance: ["vendored"],
not_public: {
reclass_host_only: ["host-only"],
private_with_public_sibling_pending: ["private"],
},
pending_author_public: ["pending"],
});
assert.deepEqual(shipSet, [
"after",
"clean",
"duplicate",
"miss",
"sanitize",
"vendored",
]);
});
test("shipSetFromMembership subtracts not_public and pending overlaps", async () => {
const mirror = await import("./mirror-skills.mjs");
assert.equal(typeof mirror.shipSetFromMembership, "function");
const shipSet = mirror.shipSetFromMembership({
version: 0,
product_public: {
clean: ["blocked", "ok", "pending"],
ship_after_fix: [],
ship_misses_add: [],
sanitize_borderlines_ship: [],
},
vendored_ship_with_provenance: [],
not_public: {
reclass_host_only: ["blocked"],
},
pending_author_public: ["pending"],
});
assert.deepEqual(shipSet, ["ok"]);
});
test("checkGeneratedEdges detects layout and digest drift without invoking the leak scanner", async () => {
const mirror = await import("./mirror-skills.mjs");
assert.equal(
typeof mirror.checkGeneratedEdges,
"function",
"checkGeneratedEdges must be exported",
);
const root = mkdtempSync(join(tmpdir(), "openrig-digest-red-"));
try {
const specPath = "packages/daemon/specs/agents/shared/skills";
const canonicalPath = "skills/_canonical";
const pluginPath = "packages/daemon/assets/plugins/openrig-core/skills";
write(join(root, specPath, "core", "alpha", "SKILL.md"), "# Alpha\n");
write(join(root, specPath, "core", "alpha", "references", "guide.md"), "# Guide\n");
write(join(root, canonicalPath, "core", "alpha", "SKILL.md"), "# Alpha\n");
write(join(root, canonicalPath, "core", "alpha", "references", "guide.md"), "# Guide\n");
write(join(root, pluginPath, "alpha", "SKILL.md"), "# Alpha\n");
write(join(root, pluginPath, "alpha", "references", "guide.md"), "# Guide\n");
const layout = {
version: 0,
edges: {
spec: { path: specPath, layout: "categorized" },
canonical: { path: canonicalPath, layout: "mirror-of-spec" },
plugin: { path: pluginPath, layout: "flat" },
},
skills: {
alpha: { edges: ["spec", "canonical", "plugin"], category: "core" },
},
};
const digests = {
version: 1,
edges: {
spec: {
"core/alpha/SKILL.md": sha256("# Alpha\n"),
"core/alpha/references/guide.md": sha256("# Guide\n"),
},
canonical: {
"core/alpha/SKILL.md": sha256("# Alpha\n"),
"core/alpha/references/guide.md": sha256("# Guide\n"),
},
plugin: {
"alpha/SKILL.md": sha256("# Alpha\n"),
"alpha/references/guide.md": sha256("# Guide\n"),
},
},
};
let scannerCalls = 0;
const clean = await mirror.checkGeneratedEdges({
repoRoot: root,
layout,
digests,
scan: () => {
scannerCalls += 1;
throw new Error("provisional scanner must not run from mirror --check");
},
});
assert.deepEqual(clean, { stale: false, changes: [] });
assert.equal(scannerCalls, 0);
write(join(root, pluginPath, "alpha", "SKILL.md"), "# Changed\n");
const drift = await mirror.checkGeneratedEdges({ repoRoot: root, layout, digests });
assert.equal(drift.stale, true);
assert.ok(
drift.changes.some(
(change) =>
change.edge === "plugin" &&
change.path === "alpha/SKILL.md" &&
change.reason === "digest",
),
);
write(join(root, pluginPath, "alpha", "SKILL.md"), "# Alpha\n");
write(join(root, pluginPath, "alpha", "unexpected.txt"), "extra\n");
const added = await mirror.checkGeneratedEdges({ repoRoot: root, layout, digests });
assert.ok(
added.changes.some(
(change) =>
change.edge === "plugin" &&
change.path === "alpha/unexpected.txt" &&
change.reason === "unexpected",
),
);
rmSync(join(root, pluginPath, "alpha", "unexpected.txt"));
rmSync(join(root, pluginPath, "alpha", "references", "guide.md"));
const missing = await mirror.checkGeneratedEdges({ repoRoot: root, layout, digests });
assert.ok(
missing.changes.some(
(change) =>
change.edge === "plugin" &&
change.path === "alpha/references/guide.md" &&
change.reason === "missing",
),
);
write(join(root, pluginPath, "alpha", "guide.md"), "# Guide\n");
const relocated = await mirror.checkGeneratedEdges({ repoRoot: root, layout, digests });
assert.ok(
relocated.changes.some(
(change) =>
change.edge === "plugin" &&
change.path === "alpha/guide.md" &&
change.reason === "unexpected",
),
);
} finally {
rmSync(root, { recursive: true, force: true });
}
});
test("checkGeneratedEdges fails closed on empty or malformed control manifests", async () => {
const mirror = await import("./mirror-skills.mjs");
await assert.rejects(
mirror.checkGeneratedEdges({
repoRoot: "/unused",
layout: {},
digests: {},
}),
/layout|edges|control/i,
);
await assert.rejects(
mirror.checkGeneratedEdges({
repoRoot: "/unused",
layout: {
version: 0,
edges: {
spec: { path: "spec", layout: "categorized" },
},
skills: {},
},
digests: { version: 1, edges: {} },
}),
/digest|spec|control/i,
);
const root = mkdtempSync(join(tmpdir(), "openrig-partial-controls-red-"));
try {
write(join(root, "spec", "core", "alpha", "SKILL.md"), "# Alpha\n");
await assert.rejects(
mirror.checkGeneratedEdges({
repoRoot: root,
layout: {
version: 0,
edges: {
spec: { path: "spec", layout: "categorized" },
},
skills: {
alpha: { edges: ["spec"], category: "core" },
},
},
digests: {
version: 1,
edges: {
spec: {
"core/alpha/SKILL.md": sha256("# Alpha\n"),
},
},
},
}),
/canonical|plugin|spec|exactly three|control/i,
);
} finally {
rmSync(root, { recursive: true, force: true });
}
});
test("authoring regeneration stages canon and projects exact manifest layouts to all three edges", async () => {
const mirror = await import("./mirror-skills.mjs");
assert.equal(
typeof mirror.regeneratePublicSkills,
"function",
"regeneratePublicSkills must be exported",
);
const root = mkdtempSync(join(tmpdir(), "openrig-authoring-red-"));
try {
const canonRoot = join(root, "canon");
const repoRoot = join(root, "repo");
write(join(canonRoot, "alpha", "SKILL.md"), "# Alpha\n");
write(join(canonRoot, "alpha", "references", "guide.md"), "# Guide\n");
write(join(canonRoot, "alpha", "feedback.md"), "private cycle\n");
write(join(canonRoot, "alpha", "evals", "case.md"), "private eval\n");
const result = await mirror.regeneratePublicSkills({
canonRoot,
repoRoot,
membership: membershipFixture({ clean: ["alpha"] }),
rules: fixtureRules(),
layout: {
version: 0,
edges: {
spec: { path: "spec", layout: "categorized" },
canonical: { path: "canonical", layout: "mirror-of-spec" },
plugin: { path: "plugin", layout: "flat" },
},
skills: {
alpha: {
edges: ["canonical", "plugin", "spec"],
category: "core",
},
},
},
});
for (const path of [
"spec/core/alpha/SKILL.md",
"canonical/core/alpha/SKILL.md",
"plugin/alpha/SKILL.md",
"spec/core/alpha/references/guide.md",
"canonical/core/alpha/references/guide.md",
"plugin/alpha/references/guide.md",
]) {
assert.equal(readFileSync(join(repoRoot, path), "utf8").startsWith("#"), true);
}
assert.equal(existsSync(join(repoRoot, "spec/core/alpha/feedback.md")), false);
assert.equal(existsSync(join(repoRoot, "plugin/alpha/evals")), false);
assert.deepEqual(
[...new Set(result.changes.map(({ edge }) => edge))].sort(),
["canonical", "plugin", "spec"],
);
assert.ok(result.changes.every(({ path }) => !path.includes("feedback.md")));
await assert.rejects(
mirror.regeneratePublicSkills({
canonRoot,
repoRoot,
membership: membershipFixture({ clean: ["alpha", "missing"] }),
rules: fixtureRules(),
layout: {
version: 0,
edges: {
spec: { path: "spec", layout: "categorized" },
canonical: { path: "canonical", layout: "mirror-of-spec" },
plugin: { path: "plugin", layout: "flat" },
},
skills: {
alpha: { edges: ["spec"], category: "core" },
},
},
}),
/missing.*ship|ship.*missing|missing.*membership/i,
);
} finally {
rmSync(root, { recursive: true, force: true });
}
});
test("default apply refreshes controls after regeneration and enumerates every diff", async () => {
const mirror = await import("./mirror-skills.mjs");
assert.equal(
typeof mirror.authoringApplyMode,
"function",
"authoringApplyMode must be exported",
);
const order = [];
const result = await mirror.authoringApplyMode({
generateControlPlaneJson: async () => {
order.push("generate");
},
readAuthoringInputs: () => {
order.push("read");
return { fixture: true };
},
regeneratePublicSkills: async (inputs) => {
order.push(`regenerate:${inputs.fixture}`);
return {
changes: [
{ edge: "canonical", path: "core/alpha/SKILL.md", reason: "write" },
{ edge: "plugin", path: "alpha/SKILL.md", reason: "write" },
{ edge: "spec", path: "core/alpha/SKILL.md", reason: "write" },
],
};
},
});
assert.deepEqual(order, [
"generate",
"read",
"regenerate:true",
"generate",
]);
assert.equal(result.changes.length, 3);
let defaultApplyCalls = 0;
const messages = [];
await mirror.main([], {
authoringApplyMode: async () => {
defaultApplyCalls += 1;
return result;
},
log: (message) => messages.push(message),
});
assert.equal(defaultApplyCalls, 1);
for (const change of result.changes) {
assert.ok(
messages.some(
(message) =>
message.includes(change.edge) && message.includes(change.path),
),
);
}
});
test("mirror main --check uses layout/digests and never activates the provisional scanner", async () => {
const mirror = await import("./mirror-skills.mjs");
let checkCalls = 0;
let scannerCalls = 0;
await mirror.main(["--check"], {
checkGeneratedEdges: async () => {
checkCalls += 1;
return { stale: false, changes: [] };
},
scanInternalLeaks: () => {
scannerCalls += 1;
throw new Error("Plain B forbids scanner activation from mirror main");
},
});
assert.equal(checkCalls, 1, "main --check must call the layout/digest verifier");
assert.equal(scannerCalls, 0, "main --check must not call the provisional scanner");
});
test("checkModeAbsolute retains its one-source/one-target checksum contract", () => {
const calls = [];
const result = checkModeAbsolute("/source", "/target", (command, args) => {
calls.push({ command, args });
return "";
});
assert.equal(result.stale, false);
assert.equal(calls.length, 1);
assert.equal(calls[0].command, "rsync");
assert.ok(calls[0].args.includes("--checksum"));
assert.equal(calls[0].args.at(-2), "/source/");
assert.equal(calls[0].args.at(-1), "/target/");
});
// --- helpers (test-only) ---
function fixtureRules() {
return {
path_prefixes: ["openrig-work/"],
seat_and_rig_patterns: ["operator-agent@"],
host_patterns: ["mm2-"],
charged_terms: ["founder"],
frontmatter_drop_keys: [
"source_evidence",
"curation_note",
"content_curator",
"transfer_test",
"transfer_test_notes",
"naming_note",
"last_verified",
"structure",
],
internal_path_globs: ["*.internal.*", "**/internal/**", "*-internal/**"],
section_fence: {
begin: "<!-- internal:begin -->",
end: "<!-- internal:end -->",
},
allowed_context_substrings: ["do not ship"],
};
}
function membershipFixture({ clean = [] } = {}) {
return {
version: 0,
product_public: {
clean,
ship_after_fix: [],
ship_misses_add: [],
sanitize_borderlines_ship: [],
},
vendored_ship_with_provenance: [],
not_public: {},
pending_author_public: [],
};
}
function write(path, content) {
mkdirSync(join(path, ".."), { recursive: true });
writeFileSync(path, content);
}
function snapshotTree(root) {
const result = {};
walk(root, (path) => {
result[path.slice(root.length + 1)] = readFileSync(path);
});
return result;
}
function sha256(value) {
return createHash("sha256").update(value).digest("hex");
}
function walkSkillFiles(root) {
const out = [];
walk(root, (path) => {
@@ -0,0 +1,60 @@
{
"version": 0,
"provisional": true,
"owner": "pm-lead@openrig-pm",
"product_public": {
"clean": [
"mission-slice-sop",
"openrig-cmux",
"openrig-herdr"
],
"ship_after_fix": [
"openrig-user",
"forming-an-openrig-mental-model",
"queue-handoff",
"seat-continuity-and-handover",
"claude-compaction-restore",
"openrig-architect"
],
"ship_misses_add": [
"topology-mutation-and-seat-management",
"rig-lifecycle",
"session-compaction-and-restore"
],
"sanitize_borderlines_ship": [
"rig-bundles-and-shareable-artifacts",
"cross-host-rig-commands",
"openrig-upgrade",
"agent-starters",
"specification-system",
"attention-queue",
"human-in-the-loop",
"watchdog",
"session-source-fork"
]
},
"vendored_ship_with_provenance": [
"agent-browser",
"brainstorming",
"executing-plans",
"systematic-debugging",
"test-driven-development",
"using-superpowers",
"verification-before-completion",
"writing-plans",
"frontend-design",
"dogfood"
],
"not_public": {
"reclass_host_only": [
"openrig-operator"
],
"private_with_public_sibling_pending": [
"security-and-consequence-boundary-policy"
]
},
"pending_author_public": [
"openrig-operator",
"foot-guns"
]
}
+124
View File
@@ -0,0 +1,124 @@
{
"version": 1,
"edges": {
"canonical": {
"core/mission-slice-sop/SKILL.md": "d195ef466ddd09944ad4ddb073fa1c5e5f809c8167ac72d1a3b3277314321930",
"core/openrig-architect/SKILL.md": "fe038f4ec31e47ac1d5f40fd6d940f6ec291e0f57eab04ce9b2f4ae127289545",
"core/openrig-cmux/SKILL.md": "f54d9b0a0cf42fc9617ab26bb1a45d8b28b982091dbb4332d68b2ebda7d09208",
"core/openrig-herdr/SKILL.md": "9301cdff9734dfaffe400c11733168a82154814023e6e021550fe28b392ef9a8",
"core/openrig-installer/SKILL.md": "362bf919ff91dc083550e4fa2583ec349187d3ed9e0ec5443d49b704cb900e80",
"core/openrig-operator/SKILL.md": "69d5a61f708a69b7d28aeddcd4bda0fbaec26bd4cc9f5f808bf1db16b7d8cf84",
"core/openrig-user/SKILL.md": "a0af630b0126c870a4c9ebecb0f7df0058bae63c826a932a3b09d59f9516e5e1",
"core/openrig-user-settings/SKILL.md": "a9650e93a669d65cabcb61409f2812bdf4a27672ba8c077c6f94fa704656fe34",
"core/rig-lifecycle/SKILL.md": "dde7165d9c1718a154684326ea36d239b82b692b1eb8c97d65f2813a5eb82aa4",
"pm/backlog-capture/SKILL.md": "345c2b47fc85e48c15c0d42bbb48fb27dd3b0ceec36bf59dc6ed20daaea47a84",
"pm/context-builder/SKILL.md": "0134c9b0a9f196565109f7f51b6ec78d5c3e4b5982a0d7105a93b29952625b3c",
"pm/exec-summary/SKILL.md": "9c06b089b4dec325b1be12b261d6a1984627808915818259d63cbf83a5448352",
"pm/office-hours/SKILL.md": "c09b92957e709e43b28186883aca9e73218b412a69355051b76700efac88b4cf",
"pm/plan-review/SKILL.md": "bba9c1b1e53712c539ac716900b49c215c5f18ae99621e0e396d571106dcf6d9",
"pm/requirements-writer/SKILL.md": "01e84e3b1fd6eb37754e45d7ba72488458c427961ec1c35a4fba4c07adc8c80d",
"pm/ui-mockup/SKILL.md": "b527c72c73c926c3d2a61e1264279486737d429aa85d3624c4adc364c577afc8",
"pods/development-team/SKILL.md": "8bec17cadc8bd4f06ff3593bd802e4103f08124c118c41cb1eb066e58e71ebd7",
"pods/orchestration-team/SKILL.md": "08b87a6f73a1f163dcbefadc39adbf0c42d391b68a693e1de44ce08ac69fa312",
"pods/review-team/SKILL.md": "1f8ad4dc3960350c373faf77e3ea90d22970766b6aa634dbbfaa765feeafe063",
"process/agent-browser/LOCAL-INSIGHTS.md": "e793e30464517bfe0eee3485fb03206292338552cf7ccaadb1713e39bf21b043",
"process/agent-browser/OPENRIG.md": "d15123689824c934b51ba572bc7c4d995651e2f606e0e53697263f4ecde108c9",
"process/agent-browser/SKILL.md": "f2709bae71a8c60f6a4b80a6873560cdea9632000ed4b28d082dfae58801199b",
"process/brainstorming/OPENRIG.md": "608f4b913e482dc12a4d201ece1d265dff1d2bfc7d609c2f11f71cb06d5cfbbe",
"process/brainstorming/SKILL.md": "f65187f40745b2016332f426a44db5884687d71e53ea0883c5da992e6e66b802",
"process/dogfood/SKILL.md": "8addc5d22813193649e8286b354ae2282975d75f1dda6d5d4b33d6ace47f438b",
"process/dogfood/references/issue-taxonomy.md": "dee7e925f7fe001903de6cc14f9656bcda90d069bbf65a2273f22202efebba09",
"process/dogfood/templates/dogfood-report-template.md": "55eef9286e32e595dcfca30db76352e9f1d149a78b60b54644eb701072364f35",
"process/executing-plans/OPENRIG.md": "351f388447a01a27fbc35f08d278da635a922b54d7fded9859aaebbb0eb3eec1",
"process/executing-plans/SKILL.md": "c73eca0a0f7101b6923c87c8bb1a85c06a9714927e5b8d0b5cf0afbbd8ccc529",
"process/frontend-design/LICENSE.txt": "0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594",
"process/frontend-design/SKILL.md": "cec3b51520db90c76b044e9bb7645ba6676157dc7b60786a81a97d82dc9395b4",
"process/systematic-debugging/CREATION-LOG.md": "b482ef9a918fbfc6c369729e8160633ddfa2332466dd362ee73f1527c239ef8b",
"process/systematic-debugging/SKILL.md": "b2adbca870df5232f018a43f6715c7a97c3ddd9429aefa11f9248a859280c2c5",
"process/systematic-debugging/condition-based-waiting-example.ts": "40ae5ebe497fdf310200e43fe986552546d0a22837c0d39e855db1cfd33eb88e",
"process/systematic-debugging/condition-based-waiting.md": "e89fec8400d6cd50f43407cec9fab50976ba4d55d0ec2eb51c0bd68036b54c26",
"process/systematic-debugging/defense-in-depth.md": "1e175fb86fc357e58c6aebf5441e481e1b7868b4380c0456b63a17eefbd18ba7",
"process/systematic-debugging/find-polluter.sh": "6462747eae9b175ac145b78bcfaeab755654a75e32637f08eb633f065a9e1d7c",
"process/systematic-debugging/root-cause-tracing.md": "a81bee9448794d261b4aca8487c595d11ff4bd4e10a5f96bca1117dd32527050",
"process/systematic-debugging/test-academic.md": "fe2ba480d78ac0d686dc025f41c2a32a43d642bf533f91b0c6053a04d35d6486",
"process/systematic-debugging/test-pressure-1.md": "0b6a915db0054577819834c79be9eb614e97bddba10d73768e1fbe91cfed048a",
"process/systematic-debugging/test-pressure-2.md": "b2030aeffba07050e8ad573ddf87486457c4a016a786bb326235bebd856f2016",
"process/systematic-debugging/test-pressure-3.md": "96b50a52e2c7989c9cf20fb752c47c1e9a3a70dc362f8f7989f8f5b64dac7708",
"process/test-driven-development/SKILL.md": "4ad2e041879cb2708791de1997c9d8e491fef66f29773bb3bd7bc7412b239e8f",
"process/test-driven-development/testing-anti-patterns.md": "bde453bc258f06543987477c837939afaa774ea2acbd9f308d702fc452bc4283",
"process/using-superpowers/OPENRIG.md": "3af62c62fcab2ff962a5cdf398da3031c9bd4a8fbed6bd8ffc4bf52a0205b403",
"process/using-superpowers/SKILL.md": "df82c78ef649dee45f4b3fd3e99bcd48d716411230dbe593902b395abdaa1a6e",
"process/verification-before-completion/SKILL.md": "ecec3b8358b9c4aa95e9331bc68758c81b2ddd6a3f37046436925290eb206bb6",
"process/writing-plans/OPENRIG.md": "cf4d50311b02841938343bb1c25ae38bca24e489396acd52cc987f6b435e26ad",
"process/writing-plans/SKILL.md": "ab71f8b300a16a7d6eaf6e3c54c97019919199d519d34b4a149c2d8866fbd507"
},
"plugin": {
"agent-startup-and-context-ingestion/SKILL.md": "c1d759ff3ef7edf689706fc1976f1695d1af21421ee4bf4707622a4b28f15f08",
"claude-compaction-restore/SKILL.md": "58602493348d405af3501fe410289b28e12ba54efdebc4a98ce9d818a07aa71b",
"claude-compaction-restore/scripts/precompact-hook.mjs": "be1161c8528f81dcecc7b3f88c17622496e6d8b7c4f376347bf4eb3b37e4d311",
"claude-compaction-restore/scripts/restore-from-jsonl.mjs": "9de164582eed8270d8e257860d156fade3b1295ca952c23b10d3db8569dd964c",
"claude-compaction-restore/templates/compact-instruction.md": "682c778464517b831902ddc2678c055545899b4751a1d32391ce965d5eb7061d",
"claude-compaction-restore/templates/post-compact-restore-instruction.md": "10a5a7e5e052f3aa000a4209f386dc4eb90a3c2bba449ca685f124714f59e47a",
"forming-an-openrig-mental-model/SKILL.md": "ebadef30569ea07fd27e4cac87104a1ce02fe7a17838e4591c35a9a186e2e7f9",
"mission-slice-sop/SKILL.md": "d195ef466ddd09944ad4ddb073fa1c5e5f809c8167ac72d1a3b3277314321930",
"openrig-architect/SKILL.md": "fe038f4ec31e47ac1d5f40fd6d940f6ec291e0f57eab04ce9b2f4ae127289545",
"openrig-cmux/SKILL.md": "f54d9b0a0cf42fc9617ab26bb1a45d8b28b982091dbb4332d68b2ebda7d09208",
"openrig-herdr/SKILL.md": "9301cdff9734dfaffe400c11733168a82154814023e6e021550fe28b392ef9a8",
"openrig-operator/SKILL.md": "5fa5223914b7242455dec8c47f4c88b2c408257b5be60cd8bfb5f340a819d948",
"openrig-user/SKILL.md": "a0af630b0126c870a4c9ebecb0f7df0058bae63c826a932a3b09d59f9516e5e1",
"queue-handoff/SKILL.md": "d4d5d37fb88c885ea40eb2f06ccdde2584d4b471f04fa0f9ed3059cf5446c3ee",
"seat-continuity-and-handover/SKILL.md": "bd34b87a320cd43eb1bfaea341ce5a78b3178afe07dcbca7fd524066d61212fc"
},
"spec": {
"core/mission-slice-sop/SKILL.md": "d195ef466ddd09944ad4ddb073fa1c5e5f809c8167ac72d1a3b3277314321930",
"core/openrig-architect/SKILL.md": "fe038f4ec31e47ac1d5f40fd6d940f6ec291e0f57eab04ce9b2f4ae127289545",
"core/openrig-cmux/SKILL.md": "f54d9b0a0cf42fc9617ab26bb1a45d8b28b982091dbb4332d68b2ebda7d09208",
"core/openrig-herdr/SKILL.md": "9301cdff9734dfaffe400c11733168a82154814023e6e021550fe28b392ef9a8",
"core/openrig-installer/SKILL.md": "362bf919ff91dc083550e4fa2583ec349187d3ed9e0ec5443d49b704cb900e80",
"core/openrig-operator/SKILL.md": "69d5a61f708a69b7d28aeddcd4bda0fbaec26bd4cc9f5f808bf1db16b7d8cf84",
"core/openrig-user/SKILL.md": "a0af630b0126c870a4c9ebecb0f7df0058bae63c826a932a3b09d59f9516e5e1",
"core/openrig-user-settings/SKILL.md": "a9650e93a669d65cabcb61409f2812bdf4a27672ba8c077c6f94fa704656fe34",
"core/rig-lifecycle/SKILL.md": "dde7165d9c1718a154684326ea36d239b82b692b1eb8c97d65f2813a5eb82aa4",
"pm/backlog-capture/SKILL.md": "345c2b47fc85e48c15c0d42bbb48fb27dd3b0ceec36bf59dc6ed20daaea47a84",
"pm/context-builder/SKILL.md": "0134c9b0a9f196565109f7f51b6ec78d5c3e4b5982a0d7105a93b29952625b3c",
"pm/exec-summary/SKILL.md": "9c06b089b4dec325b1be12b261d6a1984627808915818259d63cbf83a5448352",
"pm/office-hours/SKILL.md": "c09b92957e709e43b28186883aca9e73218b412a69355051b76700efac88b4cf",
"pm/plan-review/SKILL.md": "bba9c1b1e53712c539ac716900b49c215c5f18ae99621e0e396d571106dcf6d9",
"pm/requirements-writer/SKILL.md": "01e84e3b1fd6eb37754e45d7ba72488458c427961ec1c35a4fba4c07adc8c80d",
"pm/ui-mockup/SKILL.md": "b527c72c73c926c3d2a61e1264279486737d429aa85d3624c4adc364c577afc8",
"pods/development-team/SKILL.md": "8bec17cadc8bd4f06ff3593bd802e4103f08124c118c41cb1eb066e58e71ebd7",
"pods/orchestration-team/SKILL.md": "08b87a6f73a1f163dcbefadc39adbf0c42d391b68a693e1de44ce08ac69fa312",
"pods/review-team/SKILL.md": "1f8ad4dc3960350c373faf77e3ea90d22970766b6aa634dbbfaa765feeafe063",
"process/agent-browser/LOCAL-INSIGHTS.md": "e793e30464517bfe0eee3485fb03206292338552cf7ccaadb1713e39bf21b043",
"process/agent-browser/OPENRIG.md": "d15123689824c934b51ba572bc7c4d995651e2f606e0e53697263f4ecde108c9",
"process/agent-browser/SKILL.md": "f2709bae71a8c60f6a4b80a6873560cdea9632000ed4b28d082dfae58801199b",
"process/brainstorming/OPENRIG.md": "608f4b913e482dc12a4d201ece1d265dff1d2bfc7d609c2f11f71cb06d5cfbbe",
"process/brainstorming/SKILL.md": "f65187f40745b2016332f426a44db5884687d71e53ea0883c5da992e6e66b802",
"process/dogfood/SKILL.md": "8addc5d22813193649e8286b354ae2282975d75f1dda6d5d4b33d6ace47f438b",
"process/dogfood/references/issue-taxonomy.md": "dee7e925f7fe001903de6cc14f9656bcda90d069bbf65a2273f22202efebba09",
"process/dogfood/templates/dogfood-report-template.md": "55eef9286e32e595dcfca30db76352e9f1d149a78b60b54644eb701072364f35",
"process/executing-plans/OPENRIG.md": "351f388447a01a27fbc35f08d278da635a922b54d7fded9859aaebbb0eb3eec1",
"process/executing-plans/SKILL.md": "c73eca0a0f7101b6923c87c8bb1a85c06a9714927e5b8d0b5cf0afbbd8ccc529",
"process/frontend-design/LICENSE.txt": "0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594",
"process/frontend-design/SKILL.md": "cec3b51520db90c76b044e9bb7645ba6676157dc7b60786a81a97d82dc9395b4",
"process/systematic-debugging/CREATION-LOG.md": "b482ef9a918fbfc6c369729e8160633ddfa2332466dd362ee73f1527c239ef8b",
"process/systematic-debugging/SKILL.md": "b2adbca870df5232f018a43f6715c7a97c3ddd9429aefa11f9248a859280c2c5",
"process/systematic-debugging/condition-based-waiting-example.ts": "40ae5ebe497fdf310200e43fe986552546d0a22837c0d39e855db1cfd33eb88e",
"process/systematic-debugging/condition-based-waiting.md": "e89fec8400d6cd50f43407cec9fab50976ba4d55d0ec2eb51c0bd68036b54c26",
"process/systematic-debugging/defense-in-depth.md": "1e175fb86fc357e58c6aebf5441e481e1b7868b4380c0456b63a17eefbd18ba7",
"process/systematic-debugging/find-polluter.sh": "6462747eae9b175ac145b78bcfaeab755654a75e32637f08eb633f065a9e1d7c",
"process/systematic-debugging/root-cause-tracing.md": "a81bee9448794d261b4aca8487c595d11ff4bd4e10a5f96bca1117dd32527050",
"process/systematic-debugging/test-academic.md": "fe2ba480d78ac0d686dc025f41c2a32a43d642bf533f91b0c6053a04d35d6486",
"process/systematic-debugging/test-pressure-1.md": "0b6a915db0054577819834c79be9eb614e97bddba10d73768e1fbe91cfed048a",
"process/systematic-debugging/test-pressure-2.md": "b2030aeffba07050e8ad573ddf87486457c4a016a786bb326235bebd856f2016",
"process/systematic-debugging/test-pressure-3.md": "96b50a52e2c7989c9cf20fb752c47c1e9a3a70dc362f8f7989f8f5b64dac7708",
"process/test-driven-development/SKILL.md": "4ad2e041879cb2708791de1997c9d8e491fef66f29773bb3bd7bc7412b239e8f",
"process/test-driven-development/testing-anti-patterns.md": "bde453bc258f06543987477c837939afaa774ea2acbd9f308d702fc452bc4283",
"process/using-superpowers/OPENRIG.md": "3af62c62fcab2ff962a5cdf398da3031c9bd4a8fbed6bd8ffc4bf52a0205b403",
"process/using-superpowers/SKILL.md": "df82c78ef649dee45f4b3fd3e99bcd48d716411230dbe593902b395abdaa1a6e",
"process/verification-before-completion/SKILL.md": "ecec3b8358b9c4aa95e9331bc68758c81b2ddd6a3f37046436925290eb206bb6",
"process/writing-plans/OPENRIG.md": "cf4d50311b02841938343bb1c25ae38bca24e489396acd52cc987f6b435e26ad",
"process/writing-plans/SKILL.md": "ab71f8b300a16a7d6eaf6e3c54c97019919199d519d34b4a149c2d8866fbd507"
}
}
}
+259
View File
@@ -0,0 +1,259 @@
{
"version": 0,
"owner": "skills-architect@skill-library",
"edges": {
"canonical": {
"path": "skills/_canonical",
"layout": "mirror-of-spec"
},
"plugin": {
"path": "packages/daemon/assets/plugins/openrig-core/skills",
"layout": "flat"
},
"spec": {
"path": "packages/daemon/specs/agents/shared/skills",
"layout": "categorized"
}
},
"skills": {
"agent-browser": {
"edges": [
"canonical",
"spec"
],
"category": "process"
},
"agent-startup-and-context-ingestion": {
"edges": [
"plugin"
],
"category": null
},
"backlog-capture": {
"edges": [
"canonical",
"spec"
],
"category": "pm"
},
"brainstorming": {
"edges": [
"canonical",
"spec"
],
"category": "process"
},
"claude-compaction-restore": {
"edges": [
"plugin"
],
"category": null
},
"context-builder": {
"edges": [
"canonical",
"spec"
],
"category": "pm"
},
"development-team": {
"edges": [
"canonical",
"spec"
],
"category": "pods"
},
"dogfood": {
"edges": [
"canonical",
"spec"
],
"category": "process"
},
"exec-summary": {
"edges": [
"canonical",
"spec"
],
"category": "pm"
},
"executing-plans": {
"edges": [
"canonical",
"spec"
],
"category": "process"
},
"forming-an-openrig-mental-model": {
"edges": [
"plugin"
],
"category": null
},
"frontend-design": {
"edges": [
"canonical",
"spec"
],
"category": "process"
},
"mission-slice-sop": {
"edges": [
"canonical",
"plugin",
"spec"
],
"category": "core"
},
"office-hours": {
"edges": [
"canonical",
"spec"
],
"category": "pm"
},
"openrig-architect": {
"edges": [
"canonical",
"plugin",
"spec"
],
"category": "core"
},
"openrig-cmux": {
"edges": [
"canonical",
"plugin",
"spec"
],
"category": "core"
},
"openrig-herdr": {
"edges": [
"canonical",
"plugin",
"spec"
],
"category": "core"
},
"openrig-installer": {
"edges": [
"canonical",
"spec"
],
"category": "core"
},
"openrig-operator": {
"edges": [
"canonical",
"plugin",
"spec"
],
"category": "core"
},
"openrig-user": {
"edges": [
"canonical",
"plugin",
"spec"
],
"category": "core"
},
"openrig-user-settings": {
"edges": [
"canonical",
"spec"
],
"category": "core"
},
"orchestration-team": {
"edges": [
"canonical",
"spec"
],
"category": "pods"
},
"plan-review": {
"edges": [
"canonical",
"spec"
],
"category": "pm"
},
"queue-handoff": {
"edges": [
"plugin"
],
"category": null
},
"requirements-writer": {
"edges": [
"canonical",
"spec"
],
"category": "pm"
},
"review-team": {
"edges": [
"canonical",
"spec"
],
"category": "pods"
},
"rig-lifecycle": {
"edges": [
"canonical",
"spec"
],
"category": "core"
},
"seat-continuity-and-handover": {
"edges": [
"plugin"
],
"category": null
},
"systematic-debugging": {
"edges": [
"canonical",
"spec"
],
"category": "process"
},
"test-driven-development": {
"edges": [
"canonical",
"spec"
],
"category": "process"
},
"ui-mockup": {
"edges": [
"canonical",
"spec"
],
"category": "pm"
},
"using-superpowers": {
"edges": [
"canonical",
"spec"
],
"category": "process"
},
"verification-before-completion": {
"edges": [
"canonical",
"spec"
],
"category": "process"
},
"writing-plans": {
"edges": [
"canonical",
"spec"
],
"category": "process"
}
}
}