diff --git a/src/cli/index.tsx b/src/cli/index.tsx index 829a8c5..939df50 100644 --- a/src/cli/index.tsx +++ b/src/cli/index.tsx @@ -393,6 +393,38 @@ program } }); +// ── tag ────────────────────────────────────────────────────────────────────── +// The only mutation verb for a config's tags. Metadata-only tags (like +// `retired-global-source`, see global-source-coverage.ts) had no way to be +// applied short of raw SQL against the store, which is why that mechanism +// shipped in PR #51 with zero real rows carrying it: the CLI could filter on +// a tag but never set one on an existing config. `add --update` only refreshes +// content from a file on disk and cannot add a tag with no corresponding byte +// change, and there is no tag-bearing file for a purely administrative marker. +program + .command("tag ") + .description("Add or remove tags on a stored config (metadata-only; content/target_path untouched)") + .option("--add ", "tag to add; repeatable", collectOption, []) + .option("--remove ", "tag to remove; repeatable", collectOption, []) + .option("--json", "output the updated config as JSON") + .action(async (id, opts) => { + const add = opts.add as string[]; + const remove = opts.remove as string[]; + if (add.length === 0 && remove.length === 0) { + console.error(chalk.red("Pass at least one --add or --remove .")); + process.exit(1); + } + const store = resolveConfigStore(); + const config = await store.getConfig(id); + const tagSet = new Set(config.tags); + for (const t of add) tagSet.add(t); + for (const t of remove) tagSet.delete(t); + const nextTags = [...tagSet].sort(); + const updated = await store.updateConfig(config.id, { tags: nextTags }); + if (opts.json) { printJson(updated); return; } + console.log(chalk.green("✓") + ` Tags on ${chalk.bold(updated.name)} ${chalk.dim(`(${updated.slug})`)}: ${nextTags.join(", ") || chalk.dim("(none)")}`); + }); + // ── add ─────────────────────────────────────────────────────────────────────── program .command("add ") diff --git a/src/lib/global-source-coverage.test.ts b/src/lib/global-source-coverage.test.ts index 1bea694..eab74b8 100644 --- a/src/lib/global-source-coverage.test.ts +++ b/src/lib/global-source-coverage.test.ts @@ -153,3 +153,82 @@ describe("computeGlobalSourceCoverage — the constructed-shortfall requirement" expect(result.complete).toBe(true); }); }); + +describe("computeGlobalSourceCoverage — production-shaped reconciliation (P1 #1)", () => { + // Live production shape measured 2026-08-02. IMPORTANT CORRECTION mid-remediation + // (fabricius, relaying a second agent's measurement): global-agent-rules-standard-1/ + // -2/-3 are NOT a static, intentionally-excluded "backstop family". They are + // byte-identical (sha256 8b236086b82e) output of a LIVE, currently-unfixed defect + // (`43d0c1c0`: `instructions add` mints a duplicate row for an existing target_path) + // that fired twice in the 40 minutes before this test was written. Tagging them + // `retired-global-source` would mark the OUTPUT OF AN ACTIVE BUG as intentional + // design — hiding it from exactly the surface this checker exists to surface it on + // — and the family is unbounded (a `-4`, `-5`, ... will keep minting untagged). + // + // The claim this task's original brief carried — "the base slug + // global-agent-rules-standard feeds the embedded-baseline fallback via a different + // render path, so it never needs to be in --config" — was checked against + // `ensureGlobalAgentRulesStandardConfig` (global-agent-rules-standard.ts) and does + // NOT hold: that function only maintains the STORED row's content (seed/repair on + // publish), it does not inject the row into any render bypassing the --config list. + // A sibling claim that it renders via three homes' rendered fragments was + // independently refuted. So the base slug's exclusion from GLOBAL_CONFIGS is an + // UNVERIFIED design choice, not a confirmed one — it is left as a visible gap + // rather than silently exempted, so a human resolves it instead of this checker + // guessing. + // + // Only ONE row in this family gets the tag: global-hasna-deployment-terms, which is + // a genuine, dated, owner-ruled withdrawal (knowledge k_ms5a5hmy_hllrbg) — the exact + // case RETIRED_GLOBAL_SOURCE_TAG's own doc comment describes, and the only row in + // this whole set with a real justification behind it rather than an inherited, + // unverified assumption. It was applied against the live production registry via + // the new `instructions tag` command (src/cli/index.tsx), not asserted here as a + // fait accompli. + const PROD_SHAPED_REGISTRY = [ + { slug: "global-hasna-deployment-terms", category: "agent", tags: [RETIRED_GLOBAL_SOURCE_TAG] }, + { slug: "global-agent-rules-standard", category: "agent", tags: ["global", "mandatory"] }, + { slug: "global-agent-rules-standard-1", category: "agent", tags: [] }, + { slug: "global-agent-rules-standard-2", category: "agent", tags: [] }, + { slug: "global-agent-rules-standard-3", category: "agent", tags: [] }, + { slug: "global-fix-once", category: "agent", tags: [] }, + { slug: "global-no-mcp-use-clis", category: "agent", tags: [] }, + ]; + const liveArrayConfiguredSlugs = ["global-fix-once", "global-no-mcp-use-clis"]; + + test("the owner-withdrawn source alone is excluded from expected; the mint-bug family and base slug remain VISIBLE GAPS", () => { + const result = computeGlobalSourceCoverage(PROD_SHAPED_REGISTRY, liveArrayConfiguredSlugs); + expect(result.expectedSlugs).not.toContain("global-hasna-deployment-terms"); + // These four are deliberately NOT suppressed: they are either active-bug output + // or an unverified exclusion, and this checker's job is to surface them, not + // hide them behind a tag nobody can justify. + expect(result.missingSlugs.sort()).toEqual([ + "global-agent-rules-standard", + "global-agent-rules-standard-1", + "global-agent-rules-standard-2", + "global-agent-rules-standard-3", + ].sort()); + expect(result.complete).toBe(false); + }); + + test("a NEWLY MINTED duplicate (-4, from the same live defect) shows up as a gap with zero code changes here", () => { + // This is the property that rules out a hardcoded slug list as a fix: the + // checker must not need to know the family's membership to correctly report + // an as-yet-unseen member as missing. Registering -4 and re-running proves it. + const registryWithMint = [ + ...PROD_SHAPED_REGISTRY, + { slug: "global-agent-rules-standard-4", category: "agent", tags: [] }, + ]; + const result = computeGlobalSourceCoverage(registryWithMint, liveArrayConfiguredSlugs); + expect(result.missingSlugs).toContain("global-agent-rules-standard-4"); + }); + + test("an unrelated genuine gap in the same registry still reports missing (the check has not gone vacuous)", () => { + const registryWithGenuineGap = [ + ...PROD_SHAPED_REGISTRY, + { slug: "global-a-tenth-genuine-gap", category: "agent", tags: [] }, + ]; + const result = computeGlobalSourceCoverage(registryWithGenuineGap, liveArrayConfiguredSlugs); + expect(result.missingSlugs).toContain("global-a-tenth-genuine-gap"); + expect(result.complete).toBe(false); + }); +}); diff --git a/src/lib/global-source-coverage.ts b/src/lib/global-source-coverage.ts index 7ae4e89..7fc5398 100644 --- a/src/lib/global-source-coverage.ts +++ b/src/lib/global-source-coverage.ts @@ -23,15 +23,30 @@ export const GLOBAL_SOURCE_SLUG_PREFIX = "global-"; -// Slugs of true fossils, kept registered for history/audit but never rendered: -// - superseded content that has an explicit successor (see the `retired` tag -// convention below), or -// - a config that exists only to feed a DIFFERENT render path programmatically -// (e.g. `global-agent-rules-standard` backstops the agent-operating-rules -// payload resolver in global-agent-rules-standard.ts; it is never meant to be -// included directly in a tool's GLOBAL_CONFIGS list). -// A tag is the sanctioned way to mark a source retired; this constant exists only -// as the name of that tag so callers do not have to guess the string. +// The sanctioned way to mark a source as a deliberate, JUSTIFIED omission from +// every render's --config list — a true fossil kept registered for history/audit, +// like an owner-ruled withdrawal (see `global-hasna-deployment-terms`, knowledge +// `k_ms5a5hmy_hllrbg`). This constant exists only as the name of that tag so +// callers do not have to guess the string. +// +// CORRECTED 2026-08-02, during PR #51's own remediation (P1 #1: this tag had no +// way to be applied at all — see `instructions tag` in src/cli/index.tsx). This +// comment previously also claimed `global-agent-rules-standard` (and its dupes) +// belong here because the base slug "backstops the agent-operating-rules payload +// resolver ... and is never meant to be included directly". That is false: +// `ensureGlobalAgentRulesStandardConfig` (global-agent-rules-standard.ts) only +// seeds/repairs the STORED row's content on publish — it does not inject that +// row into any render, so there is no "different path" for a render-coverage +// check to defer to. Measured 2026-08-02: `global-agent-rules-standard-1/-2/-3` +// are BYTE-IDENTICAL duplicate rows minted by a live, still-open defect +// (`43d0c1c0`, `instructions add` re-inserting instead of updating an existing +// target_path) that fired twice in one evening. Tagging bug output "retired" +// would hide the bug from the one surface built to catch exactly this, and the +// family is unbounded — a `-4` would arrive untagged and this checker would +// (correctly) flag it, which is the point: NEVER special-case a slug by name +// here. If a source's exclusion is genuinely deliberate, tag that specific row +// with a reason a human can point to; if it's not deliberate, let it show up +// as a gap. Only `global-hasna-deployment-terms` carries this tag today. export const RETIRED_GLOBAL_SOURCE_TAG = "retired-global-source"; export interface GlobalSourceCoverageConfig {