diff --git a/tools/orion-ref-gate/index.test.ts b/tools/orion-ref-gate/index.test.ts index e04bc84e..f96f9ca2 100644 --- a/tools/orion-ref-gate/index.test.ts +++ b/tools/orion-ref-gate/index.test.ts @@ -12,6 +12,7 @@ import { findViolations, isCarveOut, lineHasToken, + REMEDIATION_DOC, runOnce, } from "./index.ts"; @@ -159,7 +160,16 @@ describe("runOnce", () => { expect(code).toBe(1); const e = errs.join("\n"); expect(e).toContain("docs/x.md:7"); - expect(e).toContain("skill://private-repo-boundary"); + expect(e).toContain("the managed service"); + expect(e).toContain(REMEDIATION_DOC); + }); + + test("the doc cited in the failure hint exists", async () => { + // Resolved from this file, not the cwd, so the assertion holds under any + // invocation. Without it the hint can silently die in a docs move — + // the defect this pointer was repaired for. + const abs = new URL(`../../${REMEDIATION_DOC}`, import.meta.url); + expect(await Bun.file(abs).exists()).toBe(true); }); test("returns 2 on a scan error (fail closed)", async () => { diff --git a/tools/orion-ref-gate/index.ts b/tools/orion-ref-gate/index.ts index 2d7d9350..2bcc2811 100644 --- a/tools/orion-ref-gate/index.ts +++ b/tools/orion-ref-gate/index.ts @@ -77,6 +77,20 @@ export const CARVEOUT_PATHS: readonly string[] = ["bun.lock"]; */ export const ALLOWLIST: Readonly> = {}; +/** + * The in-repo doc stating the boundary rule, cited in the failure hint. Named + * here so the test can assert the file actually exists: a bare string would + * let a docs move silently kill the pointer again — the exact defect this + * hint was repaired for. Wording tracks its "Never name or point at the + * private repo" bullet; update both together. This path also appears in + * `moon.yml` as an input of the `test` task — that declaration is what makes + * moon's cache re-run the assertion when the doc changes, so move both. Note + * a pull_request selects targets by project, so any PR not touching this + * project's own tree does not select this gate at all — including its leak + * scan — and relies on the main/nightly full sweep (see moon.yml). + */ +export const REMEDIATION_DOC = "docs/concepts/self-host-and-managed.md"; + /** One scanned line carrying the token. */ export interface Reference { readonly file: string; @@ -154,7 +168,9 @@ export async function runOnce(deps: Deps): Promise { deps.err(` ${v.file}:${v.line}: ${v.text.trim()}`); deps.err( "A public repo must not name, cite, or quote the private internal monorepo. " + - "Refer to it by architectural role instead (see skill://private-repo-boundary).", + 'Refer to it by architectural role instead — say "the managed service", or ' + + "describe the core capability directly so it need not be named. " + + `See ${REMEDIATION_DOC}.`, ); return 1; } diff --git a/tools/orion-ref-gate/moon.yml b/tools/orion-ref-gate/moon.yml index be557f57..5f998e7d 100644 --- a/tools/orion-ref-gate/moon.yml +++ b/tools/orion-ref-gate/moon.yml @@ -7,8 +7,9 @@ # lint/format are whole-repo tasks on the root project (/moon.yml), so this # leaf has no own bun.lock and never runs its own install. # -# Compass CI is a single moon-driven `CI` job (.github/workflows/ci.yml runs -# `moon run :ci`), so the `ci` aggregate below is swept automatically. +# Compass CI runs a per-concern matrix, not one job: tools/ci-matrix computes +# the targets and .github/workflows/ci.yml runs `moon run `, so this +# project's `ci` aggregate is swept only when the matrix selects it. layer: 'tool' language: 'typescript' tags: ['bun', 'ci-group.bun'] @@ -19,7 +20,37 @@ tasks: deps: ['install'] inputs: ['*.ts', 'tsconfig.json', '/tsconfig.base.json', 'package.json', '/bun.lock'] test: - inputs: ['*.ts', 'tsconfig.json', '/tsconfig.base.json', 'package.json', '/bun.lock'] + # `/docs/concepts/self-host-and-managed.md` is a real input: a test asserts + # the doc cited in the failure hint still exists, so the cache key must + # track it. Without it moon replays a green after the doc is deleted — + # the same silent-pointer-death this gate's hint was repaired for. Scoped + # to the one cited file, not `/docs/**`, so unrelated docs edits don't + # re-run the suite. Keep in sync with `REMEDIATION_DOC` in index.ts. + # + # That declaration governs moon's CACHE, not CI's task selection: on a + # pull_request, tools/ci-matrix selects targets via `moon query projects + # --affected`, which walks the project graph and never consults a + # cross-tree input (see the discriminator note in + # .github/workflows/ci.yml). KNOWN GAP, and it is not docs-specific: ANY + # PR that does not touch this project's own tree fails to select it, since + # the `check` scan's `/**/*` input is cross-tree the same way. A Go-only + # or docs-only PR that ADDS a private-repo reference is therefore not + # gated on that PR; the doc pointer going stale is the mild case. The + # backstop is ci.yml's unconditional push + nightly full sweep, which does + # run the scan to a real verdict. `dependsOn: ['root']` would close the + # PR-time gap — root's source is `.`, so every path marks it affected and + # `--downstream direct` would pull this project in — but for the same + # reason it would run the gate on EVERY PR, since moon edges are + # project-level rather than file-level. sql-migration-gate declines the + # same edge, though its gap is narrower (two root-level configs). + # Tracked as RIG-3381. + inputs: + - '*.ts' + - 'tsconfig.json' + - '/tsconfig.base.json' + - 'package.json' + - '/bun.lock' + - '/docs/concepts/self-host-and-managed.md' check: # The boundary gate: scan the whole tracked tree (via git grep) for any # reference to the private monorepo. Run from the workspace root so the