simt-dev 0.6.3 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +35 -0
- package/README.md +2 -2
- package/agents/simt-agent.md +84 -0
- package/docs/decisions/ADR-009-skill-layout.md +1 -1
- package/docs/decisions/ADR-086-the-stable-prefix-comes-first-and-the-lock-records-both-digests.md +1 -1
- package/docs/decisions/ADR-106-the-harness-hook-runs-through-a-repo-shim.md +46 -0
- package/docs/decisions/ADR-107-an-install-can-name-one-harness-and-the-skipped-ones-are-named.md +51 -0
- package/docs/decisions/ADR-108-an-adoption-builds-the-index-and-a-command-refreshes-it-on-read.md +59 -0
- package/docs/decisions/ADR-109-simt-dev-is-its-own-consumer-and-the-guards-that-kept-it-from-being-one-are-retired.md +47 -0
- package/docs/decisions/ADR-110-the-lock-file-follows-the-manifest-name-and-the-old-name-is-read-until-the-next-write.md +43 -0
- package/docs/decisions/ADR-111-the-tool-ships-one-agent-rendered-per-harness-and-installs-it-with-init-and-a-command.md +75 -0
- package/docs/decisions/ADR-112-the-registry-is-what-simt-dev-ships-and-a-repo-owns-its-own-skills.md +81 -0
- package/docs/decisions/ADR-113-a-pre-push-that-carries-only-documentation-skips-the-test-gate-and-says-so.md +87 -0
- package/docs/decisions/ADR-114-the-bench-pairs-at-the-task-level-a-missing-cost-is-not-zero-and-an-unasked-cross-group-compare-is-refused.md +74 -0
- package/docs/decisions/ADR-115-the-index-stamps-its-schema-version-migrates-older-databases-in-place-and-serves-newer-ones-from-a-sidecar.md +69 -0
- package/docs/decisions/ADR-116-rust-support-reaches-the-example-path-the-indent-and-the-bench-runner.md +100 -0
- package/docs/decisions/README.md +9 -0
- package/package.json +4 -2
- package/rules/core.md +2 -2
- package/rules/mdc/test-gate.mdc +2 -0
- package/rules/stacks/android.md +3 -3
- package/rules/stacks/connectiq.md +2 -2
- package/rules/stacks/dart.md +1 -1
- package/rules/stacks/deno.md +1 -1
- package/rules/stacks/dotnet.md +1 -1
- package/rules/stacks/elixir.md +1 -1
- package/rules/stacks/go.md +1 -1
- package/rules/stacks/jvm.md +3 -2
- package/rules/stacks/node.md +2 -2
- package/rules/stacks/python.md +7 -6
- package/rules/stacks/ruby.md +1 -1
- package/rules/stacks/rust.md +1 -1
- package/rules/stacks/swift.md +1 -1
- package/rules/stacks/typescript.md +2 -2
- package/rules/stacks/uv.md +3 -2
- package/skills/simt-commands/SKILL.md +70 -0
- package/src/advice/advice.mjs +7 -3
- package/src/agenthooks/decide.mjs +141 -9
- package/src/agenthooks/opencode.mjs +72 -0
- package/src/agenthooks/prompt.mjs +6 -2
- package/src/agenthooks/render.mjs +186 -19
- package/src/agenthooks/shim.mjs +55 -0
- package/src/agents/render.mjs +277 -0
- package/src/argv.mjs +8 -0
- package/src/bench/ablate.mjs +31 -14
- package/src/bench/mine.mjs +48 -1
- package/src/bench/probes.mjs +5 -6
- package/src/bench/report.mjs +243 -80
- package/src/bench/run.mjs +12 -11
- package/src/bench/variants.mjs +112 -40
- package/src/brief/assemble.mjs +6 -0
- package/src/ci/workflow.mjs +16 -7
- package/src/commands/agent.mjs +206 -0
- package/src/commands/agenthook.mjs +109 -15
- package/src/commands/bench.mjs +2 -1
- package/src/commands/compare.mjs +8 -7
- package/src/commands/decide.mjs +6 -5
- package/src/commands/evidence.mjs +13 -3
- package/src/commands/gate.mjs +85 -19
- package/src/commands/graph.mjs +18 -5
- package/src/commands/help.mjs +35 -14
- package/src/commands/hooks.mjs +68 -25
- package/src/commands/impact.mjs +4 -2
- package/src/commands/index.mjs +17 -4
- package/src/commands/init.mjs +169 -37
- package/src/commands/lease.mjs +203 -35
- package/src/commands/lessons.mjs +65 -9
- package/src/commands/map.mjs +33 -18
- package/src/commands/plugin.mjs +1 -1
- package/src/commands/repoindex.mjs +1 -0
- package/src/commands/rules.mjs +92 -18
- package/src/commands/skills.mjs +60 -25
- package/src/commands/skillscompile.mjs +8 -7
- package/src/commands/skillsinstall.mjs +12 -0
- package/src/commands/slice.mjs +377 -18
- package/src/commands/sync.mjs +37 -22
- package/src/commands/tests.mjs +5 -2
- package/src/commands/undo.mjs +24 -16
- package/src/commands/upgrade.mjs +33 -1
- package/src/commands/why.mjs +10 -4
- package/src/compare/hooksstate.mjs +23 -1
- package/src/compare/learn.mjs +82 -57
- package/src/compare/parallel.mjs +50 -0
- package/src/compare/row.mjs +26 -1
- package/src/completions.mjs +5 -0
- package/src/decisions/dir.mjs +84 -0
- package/src/decisions/index.mjs +109 -11
- package/src/distill/distill.mjs +3 -1
- package/src/ecosystem/node-runner.mjs +144 -0
- package/src/explain.mjs +18 -6
- package/src/graph/load.mjs +38 -19
- package/src/hooks/capability.mjs +14 -1
- package/src/hooks/dispatcher.mjs +335 -13
- package/src/hooks/resolve.mjs +9 -3
- package/src/hooks/shim.mjs +12 -7
- package/src/identity.mjs +67 -14
- package/src/index/extract.mjs +192 -11
- package/src/index/persist.mjs +307 -47
- package/src/index/store.mjs +263 -60
- package/src/index/tests.mjs +6 -35
- package/src/journal.mjs +296 -77
- package/src/kernel/card.mjs +37 -12
- package/src/kernel/checks.mjs +44 -9
- package/src/kernel/variants.mjs +27 -16
- package/src/lease.mjs +97 -15
- package/src/ledger/index.mjs +91 -25
- package/src/mcp/resources.mjs +5 -3
- package/src/mcp/server.mjs +81 -22
- package/src/mcp/tools.mjs +45 -9
- package/src/memory/decide.mjs +98 -43
- package/src/memory/lessons.mjs +168 -33
- package/src/name.mjs +9 -0
- package/src/plan/index.mjs +31 -8
- package/src/plan/template.mjs +27 -5
- package/src/plugins.mjs +6 -2
- package/src/prbody.mjs +7 -5
- package/src/projection/baselines.mjs +1 -1
- package/src/projection/disclosure.mjs +2 -2
- package/src/projection/loaders.mjs +41 -1
- package/src/projection/lock.mjs +21 -14
- package/src/projection/manifest.mjs +69 -19
- package/src/projection/markers.mjs +43 -3
- package/src/projection/mdc.mjs +34 -7
- package/src/projection/project.mjs +162 -18
- package/src/projection/rules.mjs +54 -3
- package/src/projection/surfaces.mjs +9 -1
- package/src/repo-root.mjs +69 -0
- package/src/resolve/body.mjs +4 -0
- package/src/resolve/fingerprint.mjs +5 -0
- package/src/resolve/local.mjs +58 -0
- package/src/review/critic.mjs +4 -2
- package/src/secrets.mjs +165 -22
- package/src/state.mjs +76 -0
- package/src/status.mjs +9 -4
- package/src/toolwords.mjs +5 -0
- package/src/tsconfig.mjs +39 -99
- package/templates/plan/tracker.json +1 -1
package/src/bench/ablate.mjs
CHANGED
|
@@ -11,6 +11,7 @@ import { mkdir, readFile, rm, unlink, writeFile } from 'node:fs/promises';
|
|
|
11
11
|
import { dirname, join, resolve } from 'node:path';
|
|
12
12
|
|
|
13
13
|
import { MANIFEST_FILE, STATE_DIR } from '../name.mjs';
|
|
14
|
+
import { LOCAL_DIR } from '../state.mjs';
|
|
14
15
|
import { readManifest } from '../projection/manifest.mjs';
|
|
15
16
|
import { inject, parse as parseRegion } from '../projection/markers.mjs';
|
|
16
17
|
import { loadMdcRules, renderMdc, ruleName } from '../projection/mdc.mjs';
|
|
@@ -23,6 +24,7 @@ import {
|
|
|
23
24
|
DEFAULT_BOOTSTRAP_SEED,
|
|
24
25
|
compareArms,
|
|
25
26
|
evaluateRunRecord,
|
|
27
|
+
holmAdjust,
|
|
26
28
|
loadPrices,
|
|
27
29
|
loadRunRecords,
|
|
28
30
|
} from './report.mjs';
|
|
@@ -340,11 +342,14 @@ export async function ablateSectionInWorkspace(workDir, ablate) {
|
|
|
340
342
|
|
|
341
343
|
/**
|
|
342
344
|
* Decide whether a canary comparison warrants escalating the section arm to the full task suite.
|
|
345
|
+
*
|
|
346
|
+
* `pValue` overrides the comparison's raw p-values when the caller supplies a
|
|
347
|
+
* multiplicity-adjusted value (Holm over the family of sections tested in one run).
|
|
343
348
|
*/
|
|
344
|
-
export function shouldEscalateCanary(comparison, { alpha = CANARY_ALPHA } = {}) {
|
|
349
|
+
export function shouldEscalateCanary(comparison, { alpha = CANARY_ALPHA, pValue } = {}) {
|
|
345
350
|
if (!comparison || !comparison.pairedTasks) return false;
|
|
346
|
-
|
|
347
|
-
if (
|
|
351
|
+
const p = pValue ?? Math.min(comparison.rTest.pValue, comparison.wilcoxon.pValue);
|
|
352
|
+
if (p < alpha) return true;
|
|
348
353
|
if (comparison.ci95.R[0] > 0 || comparison.ci95.R[1] < 0) return true;
|
|
349
354
|
if (Math.abs(comparison.deltas.R) >= 0.2) return true;
|
|
350
355
|
return false;
|
|
@@ -469,15 +474,27 @@ export async function ablateBench(
|
|
|
469
474
|
const canaryEvaluated = canaryRecords.map((rec) => evaluateRunRecord(rec, prices));
|
|
470
475
|
const currentCanary = canaryEvaluated.filter((r) => r.arm === 'current');
|
|
471
476
|
|
|
477
|
+
const canaryComparisons = [];
|
|
472
478
|
for (const sec of sections) {
|
|
473
479
|
const armCanary = canaryEvaluated.filter((r) => r.arm === sec.arm);
|
|
474
480
|
if (currentCanary.length > 0 && armCanary.length > 0) {
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
}
|
|
481
|
+
canaryComparisons.push({
|
|
482
|
+
sec,
|
|
483
|
+
cmp: compareArms(sec.arm, armCanary, 'current', currentCanary, { samples, seed }),
|
|
484
|
+
});
|
|
479
485
|
}
|
|
480
486
|
}
|
|
487
|
+
// Family = every section tested in this one run: testing ~N sections at α 0.05
|
|
488
|
+
// each inflates the false-positive rate, so the p-values are Holm-adjusted
|
|
489
|
+
// before the escalation decision (review 0003 Q46).
|
|
490
|
+
const adjusted = holmAdjust(
|
|
491
|
+
canaryComparisons.map(({ cmp }) => Math.min(cmp.rTest.pValue, cmp.wilcoxon.pValue)),
|
|
492
|
+
);
|
|
493
|
+
canaryComparisons.forEach(({ sec, cmp }, i) => {
|
|
494
|
+
if (shouldEscalateCanary(cmp, { pValue: adjusted[i] })) {
|
|
495
|
+
escalatedArms.add(sec.arm);
|
|
496
|
+
}
|
|
497
|
+
});
|
|
481
498
|
|
|
482
499
|
if (escalatedArms.size > 0) {
|
|
483
500
|
await execRunBench(resolvedRoot, {
|
|
@@ -529,7 +546,7 @@ export async function ablateBench(
|
|
|
529
546
|
dC: [0, 0],
|
|
530
547
|
valuePerKb: [0, 0],
|
|
531
548
|
},
|
|
532
|
-
|
|
549
|
+
rTest: { statistic: 0, pValue: 1, plus: 0, minus: 0 },
|
|
533
550
|
wilcoxon: { statistic: 0, pValue: 1, wPlus: 0, wMinus: 0 },
|
|
534
551
|
deletionCandidate: false,
|
|
535
552
|
};
|
|
@@ -566,11 +583,11 @@ export async function ablateBench(
|
|
|
566
583
|
dC: cmp.ci95.C,
|
|
567
584
|
valuePerKb: vCi,
|
|
568
585
|
},
|
|
569
|
-
|
|
570
|
-
statistic: cmp.
|
|
571
|
-
pValue: cmp.
|
|
572
|
-
|
|
573
|
-
|
|
586
|
+
rTest: {
|
|
587
|
+
statistic: cmp.rTest.statistic,
|
|
588
|
+
pValue: cmp.rTest.pValue,
|
|
589
|
+
plus: cmp.rTest.plus,
|
|
590
|
+
minus: cmp.rTest.minus,
|
|
574
591
|
},
|
|
575
592
|
wilcoxon: {
|
|
576
593
|
statistic: cmp.wilcoxon.statistic,
|
|
@@ -616,7 +633,7 @@ export async function ablateBench(
|
|
|
616
633
|
const markdown = formatAblationMarkdown(result);
|
|
617
634
|
|
|
618
635
|
if (apply) {
|
|
619
|
-
const outPath = join(resolvedRoot,
|
|
636
|
+
const outPath = join(resolvedRoot, LOCAL_DIR, 'bench', 'ablation.json');
|
|
620
637
|
await mkdir(dirname(outPath), { recursive: true });
|
|
621
638
|
await writeFile(outPath, `${JSON.stringify({ schemaVersion: 1, ...result }, null, 2)}\n`, 'utf8');
|
|
622
639
|
}
|
package/src/bench/mine.mjs
CHANGED
|
@@ -29,11 +29,12 @@ export const DEFAULT_MINE_TARGET = 20;
|
|
|
29
29
|
/**
|
|
30
30
|
* Classify whether a repository-relative path is a test file.
|
|
31
31
|
*
|
|
32
|
-
* Covers the
|
|
32
|
+
* Covers the ecosystems named in plan 0003 §5 R01:
|
|
33
33
|
* - `test/`, `tests/`, `spec/`, `__tests__/`, `src/test/`
|
|
34
34
|
* - `*.test.*`, `*.spec.*`, `*_test.*`, `*_spec.*`
|
|
35
35
|
* - `*_test.go` (Go)
|
|
36
36
|
* - `test_*.py` (pytest)
|
|
37
|
+
* - `tests/*.rs` (Cargo integration tests)
|
|
37
38
|
* - `*Test.java`, `*Tests.java`, `*Test.kt`, `*Tests.kt` (Gradle / Maven)
|
|
38
39
|
*
|
|
39
40
|
* @param {string} relPath forward-slash repository-relative path
|
|
@@ -259,6 +260,31 @@ export function parseGoTestJson(text) {
|
|
|
259
260
|
};
|
|
260
261
|
}
|
|
261
262
|
|
|
263
|
+
/**
|
|
264
|
+
* Parse libtest's text output, which `cargo test` prints for every test binary.
|
|
265
|
+
*
|
|
266
|
+
* A line is a result only when it has the `test <name> ... <status>` shape.
|
|
267
|
+
* Harness summaries (`test result: ok.`), compile output and doctest prose are
|
|
268
|
+
* not results. `ignored` is neither a pass nor a failure: counting it as one
|
|
269
|
+
* would make a skipped test look like evidence the change works.
|
|
270
|
+
*
|
|
271
|
+
* @param {string} text
|
|
272
|
+
* @returns {{passed: string[], failed: string[]}}
|
|
273
|
+
*/
|
|
274
|
+
export function parseCargoTestOutput(text) {
|
|
275
|
+
const passed = [];
|
|
276
|
+
const failed = [];
|
|
277
|
+
for (const line of String(text ?? '').split('\n')) {
|
|
278
|
+
const match = /^test\s+(.+?)\s+\.\.\.\s+(ok|FAILED|ignored)\s*$/.exec(line.trimEnd());
|
|
279
|
+
if (!match || match[2] === 'ignored') continue;
|
|
280
|
+
(match[2] === 'ok' ? passed : failed).push(match[1]);
|
|
281
|
+
}
|
|
282
|
+
return {
|
|
283
|
+
passed: dedupeSorted(passed),
|
|
284
|
+
failed: dedupeSorted(failed),
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
|
|
262
288
|
/**
|
|
263
289
|
* Detect the test runner for a repository and build the command for a set of
|
|
264
290
|
* changed test files.
|
|
@@ -302,6 +328,27 @@ export async function detectTestRunner(root, testFiles) {
|
|
|
302
328
|
};
|
|
303
329
|
}
|
|
304
330
|
|
|
331
|
+
if (existsSync(join(root, 'Cargo.toml'))) {
|
|
332
|
+
// Cargo has no "run this file" argument. An integration test is a target
|
|
333
|
+
// named after its file in `tests/`, so it can be selected with `--test`.
|
|
334
|
+
// A source file carries inline unit tests and can only be reached by the
|
|
335
|
+
// crate's test binary, so one such file means the whole crate runs.
|
|
336
|
+
const targets = dedupeSorted(
|
|
337
|
+
files.filter((f) => /^tests\/[^/]+\.rs$/.test(f)).map((f) => basename(f, '.rs')),
|
|
338
|
+
);
|
|
339
|
+
const integrationOnly = files.length > 0 && files.every((f) => /^tests\/[^/]+\.rs$/.test(f));
|
|
340
|
+
const args = ['test'];
|
|
341
|
+
if (integrationOnly) {
|
|
342
|
+
for (const target of targets) args.push('--test', target);
|
|
343
|
+
}
|
|
344
|
+
return {
|
|
345
|
+
runner: 'cargo-test',
|
|
346
|
+
displayCmd: ['cargo', ...args].join(' '),
|
|
347
|
+
buildExec: () => ({ cmd: 'cargo', args }),
|
|
348
|
+
parse: (stdout) => parseCargoTestOutput(stdout),
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
|
|
305
352
|
if (existsSync(join(root, 'go.mod'))) {
|
|
306
353
|
const pkgs = dedupeSorted(
|
|
307
354
|
files.map((f) => {
|
package/src/bench/probes.mjs
CHANGED
|
@@ -25,7 +25,7 @@ import { chmod, mkdir, mkdtemp, readdir, readFile, rm, writeFile } from 'node:fs
|
|
|
25
25
|
import { tmpdir } from 'node:os';
|
|
26
26
|
import { dirname, join, resolve } from 'node:path';
|
|
27
27
|
|
|
28
|
-
import {
|
|
28
|
+
import { LOCAL_DIR } from '../state.mjs';
|
|
29
29
|
import { parse as parseRegion } from '../projection/markers.mjs';
|
|
30
30
|
import { materialiseArm, parseArmSpec } from './run.mjs';
|
|
31
31
|
|
|
@@ -92,7 +92,7 @@ function runNode(args, { cwd, env = {} } = {}) {
|
|
|
92
92
|
* @returns {Promise<Array<object>>}
|
|
93
93
|
*/
|
|
94
94
|
export async function readLedgerEntries(workDir) {
|
|
95
|
-
const ledgerPath = join(workDir,
|
|
95
|
+
const ledgerPath = join(workDir, LOCAL_DIR, 'ledger.jsonl');
|
|
96
96
|
try {
|
|
97
97
|
const text = await readFile(ledgerPath, 'utf8');
|
|
98
98
|
const entries = [];
|
|
@@ -115,7 +115,7 @@ export async function readLedgerEntries(workDir) {
|
|
|
115
115
|
* Append a ledger entry to `.simt-dev/ledger.jsonl` in `workDir`.
|
|
116
116
|
*/
|
|
117
117
|
export async function appendLedgerEntry(workDir, entry) {
|
|
118
|
-
const ledgerPath = join(workDir,
|
|
118
|
+
const ledgerPath = join(workDir, LOCAL_DIR, 'ledger.jsonl');
|
|
119
119
|
await mkdir(dirname(ledgerPath), { recursive: true });
|
|
120
120
|
const treeRes = await runGit(['write-tree'], { cwd: workDir });
|
|
121
121
|
const record = {
|
|
@@ -1148,8 +1148,7 @@ export async function gradeProbe(probeOrId, ctx) {
|
|
|
1148
1148
|
* @param {string} root
|
|
1149
1149
|
* @returns {Promise<Array<object>>}
|
|
1150
1150
|
*/
|
|
1151
|
-
export async function loadProbeRecords(root) {
|
|
1152
|
-
const probesRoot = join(resolve(root), STATE_DIR, 'bench', 'probes');
|
|
1151
|
+
export async function loadProbeRecords(root) { const probesRoot = join(resolve(root), LOCAL_DIR, 'bench', 'probes');
|
|
1153
1152
|
if (!existsSync(probesRoot)) return [];
|
|
1154
1153
|
|
|
1155
1154
|
const entries = await readdir(probesRoot, { withFileTypes: true }).catch(() => []);
|
|
@@ -1242,7 +1241,7 @@ export async function runBenchProbes(
|
|
|
1242
1241
|
for (let rep = 1; rep <= reps; rep += 1) {
|
|
1243
1242
|
const runDir = join(
|
|
1244
1243
|
resolvedRoot,
|
|
1245
|
-
|
|
1244
|
+
LOCAL_DIR,
|
|
1246
1245
|
'bench',
|
|
1247
1246
|
'probes',
|
|
1248
1247
|
`${harness}-${armSpec.name}-r${rep}`,
|