mirror of
https://github.com/mvschwarz/openrig.git
synced 2026-11-07 05:56:21 -10:00
Adds the deterministic maturity-vocab verbs the scope-and-versioning convention section 2 names but rig scope lacked, so agents stop hand-editing and drifting: - stage <target> <new-stage>: enum-validated (wip|provisional|established| canonical|superseded|retired); superseded requires --successor; retired warns. Surgical updateFrontmatter; findSlice/findMission (path/name) resolution. - verified <target> --against <source>: provenance mandatory (no bare timestamp); stamps verified: <today> against <source>, overwriting the prior line. - repair: EXTENDED to also conform mandatory frontmatter (mint+register a missing id via the sec.1 lazy parent adoption, add a stage from the sec.4 migration map / wip, add a verified placeholder), idempotent + non-clobbering -- per consolidate-don't-invent, NOT a parallel reconcile verb. - show: derives read-time trust from (stage x verified) and NEVER stores it -- an established/canonical with stale/absent/bare/placeholder verified is shown as effectively provisional. The 90-day window + bare/stale taxonomy MIRROR packages/daemon/src/domain/skill-audit.ts:34 (the daemon is not importable across the package boundary, so we mirror rather than cross-import). FR-3 (create writes id+stage+verified) already shipped by slice 33 -> locked with a regression test instead of re-implemented. Help teaches the enforcement rules. New lib/scope/trust.ts (13 unit tests) + 24 integration tests across stage/ verified/repair/show/create/help. cli tsc clean. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
89 lines
3.9 KiB
TypeScript
89 lines
3.9 KiB
TypeScript
// OPR.0.4.1.6 — read-time-trust derivation for rig scope (FR-5).
|
|
// Mirrors the scope-and-versioning §2 keystone: trust is DERIVED at read time
|
|
// from (stage x verified), never stored. 90-day freshness window mirrors
|
|
// packages/daemon/src/domain/skill-audit.ts:34.
|
|
|
|
import { describe, expect, it } from "vitest";
|
|
import {
|
|
FRESHNESS_WINDOW_DAYS,
|
|
deriveVerifiedTrust,
|
|
deriveEffectiveStage,
|
|
} from "../src/lib/scope/trust.js";
|
|
|
|
const NOW = new Date("2026-06-20T00:00:00Z");
|
|
|
|
function daysAgo(n: number): string {
|
|
const d = new Date(NOW.getTime() - n * 24 * 60 * 60 * 1000);
|
|
return d.toISOString().slice(0, 10);
|
|
}
|
|
|
|
describe("deriveVerifiedTrust (mirrors skill-audit checkStaleness, 90-day window)", () => {
|
|
it("pins the freshness window at 90 days", () => {
|
|
expect(FRESHNESS_WINDOW_DAYS).toBe(90);
|
|
});
|
|
|
|
it("verified within the window with a real source => verified", () => {
|
|
const r = deriveVerifiedTrust(`${daysAgo(19)} against runtime (npm+tag)`, NOW);
|
|
expect(r.status).toBe("verified");
|
|
expect(r.source).toBe("runtime (npm+tag)");
|
|
});
|
|
|
|
it("BOUNDARY: 89 days => verified (fresh)", () => {
|
|
expect(deriveVerifiedTrust(`${daysAgo(89)} against founder review`, NOW).status).toBe("verified");
|
|
});
|
|
|
|
it("BOUNDARY: 91 days => stale_verified", () => {
|
|
expect(deriveVerifiedTrust(`${daysAgo(91)} against founder review`, NOW).status).toBe("stale_verified");
|
|
});
|
|
|
|
it("BOUNDARY: unparseable/bare date => bare_verified", () => {
|
|
expect(deriveVerifiedTrust("sometime against the docs", NOW).status).toBe("bare_verified");
|
|
expect(deriveVerifiedTrust("2026-13-99 against the docs", NOW).status).toBe("bare_verified");
|
|
});
|
|
|
|
it("missing/empty verified => missing_verified", () => {
|
|
expect(deriveVerifiedTrust(undefined, NOW).status).toBe("missing_verified");
|
|
expect(deriveVerifiedTrust(" ", NOW).status).toBe("missing_verified");
|
|
});
|
|
|
|
it("a scaffold/backfill/unverified provenance is NOT a real verification (regardless of date)", () => {
|
|
expect(deriveVerifiedTrust(`${daysAgo(1)} against scaffold (rig scope create)`, NOW).status).toBe("unverified_provenance");
|
|
expect(deriveVerifiedTrust(`${daysAgo(1)} against backfill (rig scope repair)`, NOW).status).toBe("unverified_provenance");
|
|
expect(deriveVerifiedTrust(`${daysAgo(1)} against (unverified)`, NOW).status).toBe("unverified_provenance");
|
|
});
|
|
});
|
|
|
|
describe("deriveEffectiveStage (the §2 self-correcting downgrade)", () => {
|
|
it("established + fresh verified stays established", () => {
|
|
const r = deriveEffectiveStage("established", "verified");
|
|
expect(r.effectiveStage).toBe("established");
|
|
expect(r.downgraded).toBe(false);
|
|
});
|
|
|
|
it("canonical + fresh verified stays canonical", () => {
|
|
expect(deriveEffectiveStage("canonical", "verified").effectiveStage).toBe("canonical");
|
|
});
|
|
|
|
it("established + stale verified => effectively provisional", () => {
|
|
const r = deriveEffectiveStage("established", "stale_verified");
|
|
expect(r.effectiveStage).toBe("provisional");
|
|
expect(r.downgraded).toBe(true);
|
|
expect(r.reason).toMatch(/stale/);
|
|
});
|
|
|
|
it("established + missing verified => effectively provisional", () => {
|
|
expect(deriveEffectiveStage("established", "missing_verified").effectiveStage).toBe("provisional");
|
|
});
|
|
|
|
it("canonical + bare/unverified provenance => effectively provisional", () => {
|
|
expect(deriveEffectiveStage("canonical", "bare_verified").effectiveStage).toBe("provisional");
|
|
expect(deriveEffectiveStage("canonical", "unverified_provenance").effectiveStage).toBe("provisional");
|
|
});
|
|
|
|
it("wip/provisional are never downgraded (only established/canonical can coast)", () => {
|
|
expect(deriveEffectiveStage("wip", "missing_verified").downgraded).toBe(false);
|
|
expect(deriveEffectiveStage("provisional", "stale_verified").downgraded).toBe(false);
|
|
expect(deriveEffectiveStage("provisional", "stale_verified").effectiveStage).toBe("provisional");
|
|
});
|
|
});
|