specrails-core 5.5.0 → 5.5.1
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.
|
@@ -3,6 +3,6 @@ export interface ExitHonestyFinding {
|
|
|
3
3
|
failures: number | null;
|
|
4
4
|
sample: string;
|
|
5
5
|
}
|
|
6
|
-
/** A failure count
|
|
6
|
+
/** A failure count in a runner summary, or a failure-marker line, despite exit 0. */
|
|
7
7
|
export declare function exitCodeContradiction(exitCode: number | null, output: string): ExitHonestyFinding | null;
|
|
8
8
|
export declare function exitHonestyReason(findings: ReadonlyArray<ExitHonestyFinding>): string;
|
|
@@ -1,25 +1,75 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
|
|
1
|
+
import { stripVTControlCharacters } from 'node:util';
|
|
2
|
+
// Counts belong to runner summaries, never arbitrary prose or test names.
|
|
3
|
+
// In particular, TAP's "# Subtest: ... non-404 failures" is not a count.
|
|
4
|
+
const FAILURE_LINE = /^\s*(?:FAIL(?:ED)?\b(?:[:\s]|$)|✗|✖|×\s|not ok\b)/;
|
|
5
|
+
const ERROR_LINE = /^\s*(?:AssertionError\b|(?:Type|Reference|Syntax|Range)Error:)/;
|
|
6
|
+
const FAILED_COUNT = /^(\d+)\s+(?:failed|failures?|failing)$/i;
|
|
7
|
+
const SUMMARY_LABEL = /^(?:(?:[\w -]*tests?|test files|test suites|suites)\s*:\s*|(?:tests?|test files|test suites|suites)\s+)(?=\d)/i;
|
|
8
|
+
const SUMMARY_ITEM = /\d+\s+(?:passed|passing|failed|failures?|failing|skipped|pending|todo|cancelled|total|tests?)/gi;
|
|
9
|
+
const TAP_FAIL = /^# fail (\d+)\s*$/;
|
|
10
|
+
const TAP_EXPECTED_FAILURE = /^not ok\b.*\s#\s*(?:TODO|SKIP)\b/i;
|
|
11
|
+
/** Parse the entire summary grammar before interpreting any number as a count. */
|
|
12
|
+
function failureCounts(line) {
|
|
13
|
+
const tap = TAP_FAIL.exec(line);
|
|
14
|
+
if (tap)
|
|
15
|
+
return [Number(tap[1])];
|
|
16
|
+
// Common runner suffixes: Vitest's "(5)" and Mocha's "(12ms)".
|
|
17
|
+
const body = line.replace(SUMMARY_LABEL, '').replace(/\s+\(\d+(?:\.\d+)?(?:ms|s)?\)$/, '');
|
|
18
|
+
const items = [...body.matchAll(SUMMARY_ITEM)];
|
|
19
|
+
if (!items.length || items[0].index !== 0)
|
|
20
|
+
return [];
|
|
21
|
+
let end = 0;
|
|
22
|
+
const counts = [];
|
|
23
|
+
for (const item of items) {
|
|
24
|
+
if (end && !/^(?:\s*[,|]\s*|\s+)$/.test(body.slice(end, item.index)))
|
|
25
|
+
return [];
|
|
26
|
+
const failed = FAILED_COUNT.exec(item[0]);
|
|
27
|
+
if (failed)
|
|
28
|
+
counts.push(Number(failed[1]));
|
|
29
|
+
end = item.index + item[0].length;
|
|
30
|
+
}
|
|
31
|
+
return end === body.length ? counts : [];
|
|
32
|
+
}
|
|
33
|
+
function completeTail(output) {
|
|
34
|
+
const start = Math.max(0, output.length - 16_000);
|
|
35
|
+
if (start === 0 || output[start - 1] === '\n')
|
|
36
|
+
return output.slice(start);
|
|
37
|
+
const newline = output.indexOf('\n', start);
|
|
38
|
+
return newline === -1 ? '' : output.slice(newline + 1);
|
|
39
|
+
}
|
|
40
|
+
/** A failure count in a runner summary, or a failure-marker line, despite exit 0. */
|
|
6
41
|
export function exitCodeContradiction(exitCode, output) {
|
|
7
42
|
if (exitCode !== 0)
|
|
8
43
|
return null;
|
|
9
|
-
const
|
|
44
|
+
const clean = stripVTControlCharacters(output);
|
|
45
|
+
// Do not turn the middle of a truncated test name into a summary or marker.
|
|
46
|
+
const tail = completeTail(clean);
|
|
10
47
|
let failures = null;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
48
|
+
let countSample = '';
|
|
49
|
+
let marker = '';
|
|
50
|
+
let error = '';
|
|
51
|
+
let zeroSummary = false;
|
|
52
|
+
for (const raw of tail.split('\n')) {
|
|
53
|
+
const line = raw.trim();
|
|
54
|
+
if (!marker && FAILURE_LINE.test(line) && !TAP_EXPECTED_FAILURE.test(line))
|
|
55
|
+
marker = line;
|
|
56
|
+
if (!error && ERROR_LINE.test(line))
|
|
57
|
+
error = line;
|
|
58
|
+
for (const n of failureCounts(line)) {
|
|
59
|
+
if (n === 0)
|
|
60
|
+
zeroSummary = true;
|
|
61
|
+
if (Number.isFinite(n) && n > 0 && n > (failures ?? 0)) {
|
|
62
|
+
failures = n;
|
|
63
|
+
countSample = line;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
15
66
|
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
if (
|
|
67
|
+
// A green summary may accompany expected exception diagnostics, but cannot
|
|
68
|
+
// erase an explicit failed test or another suite's positive failure count.
|
|
69
|
+
const sample = marker || countSample || (!zeroSummary ? error : '');
|
|
70
|
+
if (!sample)
|
|
20
71
|
return null;
|
|
21
|
-
|
|
22
|
-
return { command: '', failures, sample: sample.trim() };
|
|
72
|
+
return { command: '', failures, sample: sample.slice(0, 160) };
|
|
23
73
|
}
|
|
24
74
|
export function exitHonestyReason(findings) {
|
|
25
75
|
const parts = findings.map(item => `\`${item.command}\` exited 0 but its output reports ${item.failures !== null ? `${item.failures} failing test${item.failures === 1 ? '' : 's'}` : 'a failure'} (${JSON.stringify(item.sample)})`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"exit-code-honesty.js","sourceRoot":"","sources":["../../../src/agent-runtime/compact/exit-code-honesty.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"exit-code-honesty.js","sourceRoot":"","sources":["../../../src/agent-runtime/compact/exit-code-honesty.ts"],"names":[],"mappings":"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"}
|
package/docs/agent-runtime.md
CHANGED
|
@@ -118,6 +118,14 @@ Run and resume emit JSON lines: `workflow-event` (the durable ledger events), `a
|
|
|
118
118
|
|
|
119
119
|
`runtime api` returns `{type:"runtime-api",apiVersion:1,coreVersion:"..."}` without invoking providers. Hosts can send a JSON configuration through stdin to `runtime validate --stdin` (maximum 2 MiB), avoiding temporary files and platform-specific shell quoting. It is mutually exclusive with `--config`. Use `runtime status --context <file> --compact` for process/UI integration: it retains the run and trace identities, phase status and visits, `pendingApproval`, `pendingQuestion`, usage, the completion verdict and the acceptance summary while omitting accumulated outputs, history and frozen context. Omit `--compact` for full inspection.
|
|
120
120
|
|
|
121
|
+
## Verification output and correction limits
|
|
122
|
+
|
|
123
|
+
The `exit-code-honesty` guardrail catches test harnesses that print failures but exit with code 0. It recognizes complete count summaries (including TAP `# fail N`, Jest/Vitest test totals and Mocha-style counts) and explicit failure markers. Passing test names such as `rethrows non-404 failures` are descriptions, not failure counts. ANSI colors and Windows line endings are normalized; a partial line at the diagnostic tail boundary is discarded. TAP TODO/SKIP results are not treated as ordinary failed tests. A green suite does not erase another suite's explicit failure.
|
|
124
|
+
|
|
125
|
+
This is a conservative text diagnostic over the last 16,000 characters of captured output, not a universal parser for every test framework. Test commands must still propagate real failures through their exit status. When the guardrail rejects output, the log includes the matching line so the reported failure can be checked against the runner's results.
|
|
126
|
+
|
|
127
|
+
Verification failures return to the fixer. `limits.maxAttempts` bounds fixer invocations (default: 3), after which Core blocks with `Implementation correction limit reached`. An explicit resume grants a fresh budget. If the fixer repeatedly reports no defect, inspect the quoted verification evidence before resuming; rerunning an unchanged command cannot correct a diagnostic false positive.
|
|
128
|
+
|
|
121
129
|
## Questions and approvals
|
|
122
130
|
|
|
123
131
|
The graph pauses through LangGraph interrupts; the host resumes it with the matching answer.
|
package/docs/ci-cd.md
CHANGED
|
@@ -4,10 +4,11 @@ Core's release workflow publishes the **same npm tarball that passed CI**. It do
|
|
|
4
4
|
|
|
5
5
|
## Quality gates
|
|
6
6
|
|
|
7
|
-
`CI` runs for
|
|
7
|
+
`CI` runs for pushes to `main`, pull requests to `main`, and manual dispatches. Feature branches are checked through their PR, avoiding two complete matrix runs for every update. The post-merge main push still runs the entire release gate. It has read-only repository permissions, cancels superseded runs on the same branch/PR, and has bounded job timeouts.
|
|
8
8
|
|
|
9
9
|
- Typecheck/build on the exact supported Node minimum, **20.19.0**.
|
|
10
10
|
- Full Vitest and release-guard tests on **Linux, macOS and Windows**, with **Node 20.19.0, 22 and 24**.
|
|
11
|
+
- Windows distributes the two slow runtime integration suites across three jobs by collected test locations; a fourth job runs every other test file. Parameterized cases stay together. The partition runner checks its selected inventory against Vitest before running, failing if any assigned test is missing or extra. Linux/macOS and coverage run the complete suite without partitioning.
|
|
11
12
|
- Coverage on Node 24 with the existing configured thresholds (not lowered).
|
|
12
13
|
- A checksum-verified actionlint binary validates workflow syntax, expressions and action inputs (shellcheck is not included).
|
|
13
14
|
- A checksum-verified Gitleaks binary scans Git history with redacted output.
|
|
@@ -19,13 +20,15 @@ Local checks, after installing dependencies:
|
|
|
19
20
|
|
|
20
21
|
```sh
|
|
21
22
|
npm run ci # typecheck, guard regressions, coverage, packaged-consumer smoke
|
|
22
|
-
npm run test:scripts # hermetic release regressions
|
|
23
|
+
npm run test:scripts # hermetic release and CI partition regressions
|
|
23
24
|
npm run check:package # build and consumer smoke; prints its temporary artifact directory
|
|
24
25
|
# To choose where the verified tarball is kept:
|
|
25
26
|
node scripts/verify-package.mjs /absolute/path/to/temporary-package-output
|
|
26
27
|
```
|
|
27
28
|
|
|
28
|
-
|
|
29
|
+
To reproduce a Windows test partition locally, run `node scripts/ci-tests.mjs runtime-1` (or `runtime-2`, `runtime-3`, `general`). Add `--list-only` to a runtime partition to verify its inventory without executing tests.
|
|
30
|
+
|
|
31
|
+
The package check reuses the npm dependency cache (including the cache populated by `npm ci` and setup-node) with `--prefer-offline`; it may download missing production dependencies from npm. The temporary consumer, user configuration and credential isolation remain in place. Its generated output directory should be outside the checkout. Release-guard unit tests are hermetic and do not access npm or GitHub.
|
|
29
32
|
|
|
30
33
|
## Automatic release
|
|
31
34
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "specrails-core",
|
|
3
|
-
"version": "5.5.
|
|
3
|
+
"version": "5.5.1",
|
|
4
4
|
"description": "Provider-independent AI agent workflow system for Claude Code, Codex, Gemini CLI, and Kimi Code",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
"test:coverage": "npm run build && vitest run --coverage",
|
|
73
73
|
"dogfood": "npm run build && node bin/specrails-core.mjs init --yes",
|
|
74
74
|
"prepack": "npm run build",
|
|
75
|
-
"test:scripts": "node --test scripts/release-utils.test.mjs",
|
|
75
|
+
"test:scripts": "node --test scripts/release-utils.test.mjs scripts/ci-tests.test.mjs",
|
|
76
76
|
"check:package": "npm run build && node scripts/verify-package.mjs",
|
|
77
77
|
"ci": "npm run typecheck && npm run test:scripts && npm run test:coverage && npm run check:package"
|
|
78
78
|
},
|