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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions .claude/skills/correctness/scripts/checklist.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,7 @@ function extractLens(argv) {
// Group-scoped path. Sorted so the SAME group always resolves to the same file regardless of the
// order the caller listed its lenses — otherwise `a,b` and `b,a` would be two different runs.
const lensPath = (lens) =>
lens && lens.length
? `.claude/.correctness-review-${[...lens].sort().join('+')}.json`
: CHECKLIST_PATH;
lens?.length ? `.claude/.correctness-review-${[...lens].sort().join('+')}.json` : CHECKLIST_PATH;

const log = console.log;

Expand Down
4 changes: 1 addition & 3 deletions .cursor/skills/correctness/scripts/checklist.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,7 @@ function extractLens(argv) {
// Group-scoped path. Sorted so the SAME group always resolves to the same file regardless of the
// order the caller listed its lenses — otherwise `a,b` and `b,a` would be two different runs.
const lensPath = (lens) =>
lens && lens.length
? `.claude/.correctness-review-${[...lens].sort().join('+')}.json`
: CHECKLIST_PATH;
lens?.length ? `.claude/.correctness-review-${[...lens].sort().join('+')}.json` : CHECKLIST_PATH;

const log = console.log;

Expand Down
6 changes: 3 additions & 3 deletions .devkit/skills-manifest.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"devkitRef": "v0.49.1",
"generatedAt": "2026-08-07T09:41:05.307Z",
"devkitRef": "v0.50.0",
"generatedAt": "2026-08-07T11:47:22.452Z",
"targets": [
"claude",
"cursor"
Expand All @@ -17,7 +17,7 @@
"commit-guard/SKILL.md": "9a5310f972f70d625db1c4fd2c149c899eff081ee2ff11fc372d7450dbffb5ce",
"commit-guard/scripts/checklist.mjs": "dc57c1f1407a0fe7503ebe72e30e9167b29bdf1154ab44e68de82b6d558770e1",
"correctness/SKILL.md": "16fca786d4b2fa85c2588aee13d14f4cf6a71a30014cbcc24993f526f67770c7",
"correctness/scripts/checklist.mjs": "4ea70cf9257dbae589b8648b396fc3c12c5b306026ec455c80c1c438023c6e58",
"correctness/scripts/checklist.mjs": "adb9777dc7b3fdeb5e4dc31bc5e231c2f947b0bca235b0d04b54f81c3180c427",
"decisions/SKILL.md": "9bdad17d600fed8c0bc5be1bb6f04822ed03327635629b266e2b5e07cb92b498",
"dup-detection/SKILL.md": "74a4964795bbb27ce2bda0e13b91da00f04a01a989bcbf6149d10ff9c3688a4c",
"feature-critique/SKILL.md": "0a595267e6dab2fad6e571b3861da4964ccff934d50ac9efaf137a23d3a8efef",
Expand Down
2 changes: 2 additions & 0 deletions cli/__tests__/husky-block.test.mts
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,7 @@ describe('buildOverlayHook — fallow gate (overlay)', () => {
});

it('emits the fallow gate scoped by DEVKIT_SHIP_BASE_SHA', () => {
// biome-ignore lint/suspicious/noTemplateCurlyInString: shell ${VAR:-default}, not a JS template
expect(hook).toContain('[ -n "${DEVKIT_SHIP_BASE_SHA:-}" ]');
expect(hook).toContain('FALLOW_BASE_ARGS="--base $DEVKIT_SHIP_BASE_SHA"');
expect(hook).toContain('fallow audit $FALLOW_BASE_ARGS || exit 1');
Expand All @@ -437,6 +438,7 @@ describe('buildOverlayHook — fallow gate (overlay)', () => {
const stub = join(binDir, 'fallow');
writeFileSync(
stub,
// biome-ignore lint/suspicious/noTemplateCurlyInString: shell ${VAR:-default}, not a JS template
'#!/bin/sh\necho "FALLOW_ARGS:$*"\necho "GIT_DIR:${GIT_DIR:-unset} GIT_INDEX_FILE:${GIT_INDEX_FILE:-unset}"\n',
);
chmodSync(stub, 0o755);
Expand Down
15 changes: 15 additions & 0 deletions cli/__tests__/self-host.test.mts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,20 @@ describe('buildSelfHostHook', () => {
expect(hook).not.toContain('@norvalbv/devkit');
});

// The `--extra` above is only as hard as the script it names, and biome exits 0 when every
// diagnostic is warn-severity. A bare `biome check .` therefore PRINTS its findings into the gate
// log — indistinguishable from a real failure to the reader — and passes the commit anyway (a
// v0.50.0 ship shipped with six of them). `--error-on-warnings` is what makes the exit code match
// what the log shows. devkit's own root config turns `noConsole` (the one deliberately-advisory
// rule in biome/base.jsonc) off for the whole authored surface, so nothing advisory is caught here.
it('backs the lint extra with a biome invocation that exits non-zero on WARNINGS too', () => {
const pkg: { scripts?: Record<string, string> } = JSON.parse(
readFileSync(join(ROOT, 'package.json'), 'utf8'),
);
expect(SELF_HOST_EXTRAS).toContainEqual({ label: 'lint', cmd: 'bun run lint' });
expect(pkg.scripts?.lint).toContain('--error-on-warnings');
});

it('preserves the advisory fallow-audit gate INSIDE the block (never blocks, survives re-run)', () => {
const hook = buildSelfHostHook(HOOK_SEL, '', ROOT);
expect(hook).toContain(
Expand Down Expand Up @@ -144,6 +158,7 @@ describe('buildSelfHostHook', () => {
// which execs a binary and cannot see shell functions.
const binDir = mkdtempSync(join(tmpdir(), 'fallow-stub-'));
const stub = join(binDir, 'fallow');
// biome-ignore lint/suspicious/noTemplateCurlyInString: shell ${VAR:-default}, not a JS template
writeFileSync(stub, '#!/bin/sh\necho "FALLOW_ARGS:$*"\necho "GIT_DIR:${GIT_DIR:-unset}"\n');
chmodSync(stub, 0o755);
const script = `${DK_NO_GIT_ENV_HELPER}\n${fragment}`;
Expand Down
6 changes: 4 additions & 2 deletions cli/lib/husky/self-host.mts
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,10 @@ import {
} from './husky-block.mts';

// devkit's own structure-lint command (package.json `lint:structure` = `eslint cli gate-engine`)
// and its hard biome-lint gate (`lint` = `biome check .`). The hard-lint is folded into the
// deterministic orchestrator via `--extra` (any non-zero blocks); both run via real devDeps
// and its hard biome-lint gate (`lint` = `biome check --error-on-warnings .` — biome exits 0 when
// every diagnostic is warn-severity, so without that flag the gate PRINTS its findings into the log
// and passes the commit anyway). The hard-lint is folded into the deterministic orchestrator via
// `--extra` (any non-zero blocks); both run via real devDeps
// (eslint/biome), so toSelfHost leaves them untouched. Together with the advisory fallow fragment
// below, the self-host hook preserves every gate the pre-self-host hand hook ran AND adds review + dup/clone.
export const SELF_HOST_STRUCTURE_CMD = 'bun run lint:structure';
Expand Down
6 changes: 4 additions & 2 deletions dist/cli/lib/husky/self-host.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@ import { readJson } from "../fs-helpers.mjs";
import { markEnd } from "./husky.mjs";
import { buildFullHook, buildGuardBlock, extractGuardBlock, replaceGuardBlock, } from "./husky-block.mjs";
// devkit's own structure-lint command (package.json `lint:structure` = `eslint cli gate-engine`)
// and its hard biome-lint gate (`lint` = `biome check .`). The hard-lint is folded into the
// deterministic orchestrator via `--extra` (any non-zero blocks); both run via real devDeps
// and its hard biome-lint gate (`lint` = `biome check --error-on-warnings .` — biome exits 0 when
// every diagnostic is warn-severity, so without that flag the gate PRINTS its findings into the log
// and passes the commit anyway). The hard-lint is folded into the deterministic orchestrator via
// `--extra` (any non-zero blocks); both run via real devDeps
// (eslint/biome), so toSelfHost leaves them untouched. Together with the advisory fallow fragment
// below, the self-host hook preserves every gate the pre-self-host hand hook ran AND adds review + dup/clone.
export const SELF_HOST_STRUCTURE_CMD = 'bun run lint:structure';
Expand Down
2 changes: 1 addition & 1 deletion dist/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
"test:run": "vitest run",
"test:e2e": "vitest run -c vitest.e2e.config.mjs",
"playground": "bun scripts/playground.mts",
"lint": "biome check .",
"lint": "biome check --error-on-warnings .",
"lint:structure": "eslint cli gate-engine",
"benchmarks:check": "bun gate-engine/eval/cli.mts check",
"benchmarks:render": "bun gate-engine/eval/cli.mts render",
Expand Down
4 changes: 1 addition & 3 deletions dist/skills/correctness/scripts/checklist.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,7 @@ function extractLens(argv) {
// Group-scoped path. Sorted so the SAME group always resolves to the same file regardless of the
// order the caller listed its lenses — otherwise `a,b` and `b,a` would be two different runs.
const lensPath = (lens) =>
lens && lens.length
? `.claude/.correctness-review-${[...lens].sort().join('+')}.json`
: CHECKLIST_PATH;
lens?.length ? `.claude/.correctness-review-${[...lens].sort().join('+')}.json` : CHECKLIST_PATH;

const log = console.log;

Expand Down
1 change: 1 addition & 0 deletions docs/decisions/gate-opt-out-is-visible-and-detectable.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ created: 2026-08-05
**Scope:** gate-engine/deterministic/run.mts,cli/lib/doctor/guard-config-checks.mts,gate-engine/config.mts
**Category:** commit-gates
**Source:** collab · sc-search-index-drift
- 2026-08-07 — devkit's own hard biome gate now runs `biome check --error-on-warnings .`. This is the REVERSE of the failure mode the Target addresses: not a gate that silently opts out, but one that PRINTS its findings into the gate log and exits 0 anyway. biome's exit code counts only error-severity diagnostics, so six warn-severity findings (noTemplateCurlyInString x3, noNonNullAssertion x2, useOptionalChain) rendered in the v0.50.0 ship log at the visual weight of a block and shipped. The deterministic runner's trichotomy was working correctly — `--extra "lint=bun run lint"` is only ever as hard as the script it names, and the tool under it was not. Scoped to devkit's OWN lint script: the consumer emitter (cli/lib/install/package-json.mts) and the overlay biome gate (cli/lib/husky/husky-block.mts) keep a bare `biome check`, because biome/base.jsonc holds noConsole at "warn" DELIBERATELY for consumers while devkit's root config turns it off across its whole authored surface — the flag therefore costs devkit nothing and would cost a consumer every console.log. Regression-tested in cli/__tests__/self-host.test.mts against the repo's real package.json, since the strictness lives in a script string that no hook-text assertion can reach.
6 changes: 5 additions & 1 deletion gate-engine/coverage/__tests__/produce.test.mts
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,11 @@ describe('publishCoverage', () => {
mkdirSync(join(root, COVERAGE_DIR), { recursive: true });
writeFileSync(join(root, COVERAGE_FILE), '{"sibling.ts":{}}');
const dir = runDirWith(root, 'runA');
const aDifferentFileThanTheOneThereNow = snapshotArtifact(root)! - 5_000;
// The artifact was just written, so it HAS an mtime — asserted rather than assumed, because a
// null here would silently read as mtime 0 and make the -5s below meaningless.
const mtimeNow = snapshotArtifact(root);
if (mtimeNow === null) throw new Error('the artifact written above must have an mtime');
const aDifferentFileThanTheOneThereNow = mtimeNow - 5_000;

expect(publishCoverage(dir, root, aDifferentFileThanTheOneThereNow)).toBe(false);
expect(JSON.parse(readFileSync(join(root, COVERAGE_FILE), 'utf8'))).toEqual({
Expand Down
19 changes: 11 additions & 8 deletions gate-engine/decisions/__tests__/recall-scoring.test.mts
Original file line number Diff line number Diff line change
Expand Up @@ -118,18 +118,21 @@ describe('scoreCase — MULTI', () => {
});

describe('scoreCase — CURRENT_STATE', () => {
// Hoisted (not inlined into `cs()`) so a case can vary ONE field off the same baseline without
// reaching back through the optional `currentState` of a freshly built case.
const CURRENT_STATE: NonNullable<RecallCase['currentState']> = {
axis: 'hooks',
liveId: 'target:2026-06-17',
staleId: 'target:2026-06-08',
mustSurface: ['nothing executable to bridge'],
mustNotAssert: ['hooks FOLLOW the user'],
};
const cs = (): RecallCase => ({
id: 'c1',
q: 'q',
type: 'CURRENT_STATE',
gold: ['hooks'],
currentState: {
axis: 'hooks',
liveId: 'target:2026-06-17',
staleId: 'target:2026-06-08',
mustSurface: ['nothing executable to bridge'],
mustNotAssert: ['hooks FOLLOW the user'],
},
currentState: { ...CURRENT_STATE },
});

it('CSA needs all three: right axis, LIVE block, live content without an unqualified stale claim', () => {
Expand Down Expand Up @@ -252,7 +255,7 @@ describe('scoreCase — CURRENT_STATE', () => {
it('an EMPTY mustSurface cannot silently disable SFER (vacuous [].every)', () => {
// `[].every()` is true, which would claim the live content surfaced and switch staleness off.
// An empty list is evidence of nothing, so it must read as NOT surfaced.
const empty = { ...cs(), currentState: { ...cs().currentState!, mustSurface: [] } };
const empty = { ...cs(), currentState: { ...CURRENT_STATE, mustSurface: [] } };
const s = scoreCase(
empty,
env(
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
"test:run": "vitest run",
"test:e2e": "vitest run -c vitest.e2e.config.mjs",
"playground": "bun scripts/playground.mts",
"lint": "biome check .",
"lint": "biome check --error-on-warnings .",
"lint:structure": "eslint cli gate-engine",
"benchmarks:check": "bun gate-engine/eval/cli.mts check",
"benchmarks:render": "bun gate-engine/eval/cli.mts render",
Expand Down
4 changes: 1 addition & 3 deletions skills/correctness/scripts/checklist.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,7 @@ function extractLens(argv) {
// Group-scoped path. Sorted so the SAME group always resolves to the same file regardless of the
// order the caller listed its lenses — otherwise `a,b` and `b,a` would be two different runs.
const lensPath = (lens) =>
lens && lens.length
? `.claude/.correctness-review-${[...lens].sort().join('+')}.json`
: CHECKLIST_PATH;
lens?.length ? `.claude/.correctness-review-${[...lens].sort().join('+')}.json` : CHECKLIST_PATH;

const log = console.log;

Expand Down
Loading