@sun-asterisk/sungen 3.3.0-alpha.2 → 3.3.0-alpha.3
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/dist/cli/commands/contract.d.ts +3 -0
- package/dist/cli/commands/contract.d.ts.map +1 -1
- package/dist/cli/commands/contract.js +34 -4
- package/dist/cli/commands/contract.js.map +1 -1
- package/dist/contract/accept-opaque-steps.d.ts +3 -0
- package/dist/contract/accept-opaque-steps.d.ts.map +1 -0
- package/dist/contract/accept-opaque-steps.js +114 -0
- package/dist/contract/accept-opaque-steps.js.map +1 -0
- package/dist/contract/approve-contract.d.ts +8 -1
- package/dist/contract/approve-contract.d.ts.map +1 -1
- package/dist/contract/approve-contract.js +6 -0
- package/dist/contract/approve-contract.js.map +1 -1
- package/dist/contract/build-contract-verdict.js +2 -2
- package/dist/contract/build-contract-verdict.js.map +1 -1
- package/dist/contract/build-trace-conformance.d.ts +5 -1
- package/dist/contract/build-trace-conformance.d.ts.map +1 -1
- package/dist/contract/build-trace-conformance.js +9 -4
- package/dist/contract/build-trace-conformance.js.map +1 -1
- package/dist/contract/classify-contract-change.d.ts +3 -2
- package/dist/contract/classify-contract-change.d.ts.map +1 -1
- package/dist/contract/classify-contract-change.js.map +1 -1
- package/dist/contract/classify-trace-conformance.d.ts +18 -3
- package/dist/contract/classify-trace-conformance.d.ts.map +1 -1
- package/dist/contract/classify-trace-conformance.js +64 -17
- package/dist/contract/classify-trace-conformance.js.map +1 -1
- package/dist/contract/contract-schema.d.ts +21 -0
- package/dist/contract/contract-schema.d.ts.map +1 -1
- package/dist/contract/contract-schema.js +25 -3
- package/dist/contract/contract-schema.js.map +1 -1
- package/dist/contract/contracts-lock-file.d.ts.map +1 -1
- package/dist/contract/contracts-lock-file.js +1 -0
- package/dist/contract/contracts-lock-file.js.map +1 -1
- package/dist/contract/project-contract-blockers.d.ts.map +1 -1
- package/dist/contract/project-contract-blockers.js +4 -2
- package/dist/contract/project-contract-blockers.js.map +1 -1
- package/dist/contract/record-contract-approval.d.ts +16 -1
- package/dist/contract/record-contract-approval.d.ts.map +1 -1
- package/dist/contract/record-contract-approval.js +29 -1
- package/dist/contract/record-contract-approval.js.map +1 -1
- package/dist/contract/render-free-gherkin.d.ts +2 -0
- package/dist/contract/render-free-gherkin.d.ts.map +1 -1
- package/dist/contract/render-free-gherkin.js +6 -0
- package/dist/contract/render-free-gherkin.js.map +1 -1
- package/dist/contract/trace/collect-trace-evidence.d.ts +8 -3
- package/dist/contract/trace/collect-trace-evidence.d.ts.map +1 -1
- package/dist/contract/trace/collect-trace-evidence.js +65 -34
- package/dist/contract/trace/collect-trace-evidence.js.map +1 -1
- package/dist/contract/trace/read-file-once.d.ts +20 -0
- package/dist/contract/trace/read-file-once.d.ts.map +1 -0
- package/dist/contract/trace/read-file-once.js +135 -0
- package/dist/contract/trace/read-file-once.js.map +1 -0
- package/dist/contract/trace/select-final-attempt.d.ts +2 -2
- package/dist/contract/trace/select-final-attempt.d.ts.map +1 -1
- package/dist/contract/trace/select-final-attempt.js +3 -3
- package/dist/contract/trace/select-final-attempt.js.map +1 -1
- package/dist/contract/trace/trace-file-reader.d.ts +7 -1
- package/dist/contract/trace/trace-file-reader.d.ts.map +1 -1
- package/dist/contract/trace/trace-file-reader.js +17 -41
- package/dist/contract/trace/trace-file-reader.js.map +1 -1
- package/dist/contract/trace/trace-run-record.d.ts +2 -2
- package/dist/contract/trace/trace-run-record.d.ts.map +1 -1
- package/dist/contract/trace/trace-run-record.js +3 -3
- package/dist/contract/trace/trace-run-record.js.map +1 -1
- package/dist/contract/verify-approval-decisions.d.ts.map +1 -1
- package/dist/contract/verify-approval-decisions.js +9 -4
- package/dist/contract/verify-approval-decisions.js.map +1 -1
- package/package.json +3 -3
- package/src/cli/commands/contract.ts +35 -4
- package/src/contract/accept-opaque-steps.ts +114 -0
- package/src/contract/approve-contract.ts +10 -3
- package/src/contract/build-contract-verdict.ts +2 -2
- package/src/contract/build-trace-conformance.ts +8 -3
- package/src/contract/classify-contract-change.ts +3 -3
- package/src/contract/classify-trace-conformance.ts +76 -22
- package/src/contract/contract-schema.ts +31 -2
- package/src/contract/contracts-lock-file.ts +1 -0
- package/src/contract/project-contract-blockers.ts +4 -2
- package/src/contract/record-contract-approval.ts +41 -1
- package/src/contract/render-free-gherkin.ts +5 -0
- package/src/contract/trace/collect-trace-evidence.ts +66 -29
- package/src/contract/trace/read-file-once.ts +95 -0
- package/src/contract/trace/select-final-attempt.ts +3 -3
- package/src/contract/trace/trace-file-reader.ts +17 -6
- package/src/contract/trace/trace-run-record.ts +3 -3
- package/src/contract/verify-approval-decisions.ts +9 -5
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* a part without one is refused. Every JSONL line must parse — a line that does not is never skipped.
|
|
13
13
|
*/
|
|
14
14
|
import { createHash } from 'crypto';
|
|
15
|
-
import
|
|
15
|
+
import { FileRead, readFileOnce } from './read-file-once';
|
|
16
16
|
import { openZip, ZipArchive } from './trace-zip-reader';
|
|
17
17
|
import { SUPPORTED_TRACE_VERSIONS, traceZipLimits, TraceRejected, TraceZipLimits } from './trace-zip-limits';
|
|
18
18
|
|
|
@@ -124,10 +124,21 @@ export function readTraceBuffer(buf: Buffer, label: string, limits: TraceZipLimi
|
|
|
124
124
|
};
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
-
/**
|
|
127
|
+
/**
|
|
128
|
+
* A trace opened once (read-file-once.ts) → parsed: the size was checked on the descriptor BEFORE it
|
|
129
|
+
* was read into memory. `missing` / `outside` are the caller's to report; here they throw.
|
|
130
|
+
*/
|
|
131
|
+
export function traceFromRead(read: FileRead, label: string, limits: TraceZipLimits = traceZipLimits()): ParsedTrace {
|
|
132
|
+
switch (read.status) {
|
|
133
|
+
case 'read': return readTraceBuffer(read.bytes, read.real, limits);
|
|
134
|
+
case 'too-large': throw new TraceRejected('zip.too-large', `${read.size} B > ${limits.maxZipBytes}`);
|
|
135
|
+
case 'not-a-file': throw new TraceRejected('zip.malformed', `${label} is not a regular file`);
|
|
136
|
+
case 'missing': throw new TraceRejected('zip.malformed', `${label} does not exist`);
|
|
137
|
+
default: throw new TraceRejected('zip.malformed', `${label} was not read: ${read.why}`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** Read a trace from a trusted path (a fixture, a test): a regular file, opened once. */
|
|
128
142
|
export function readTraceFile(file: string, limits: TraceZipLimits = traceZipLimits()): ParsedTrace {
|
|
129
|
-
|
|
130
|
-
if (!st.isFile()) throw new TraceRejected('zip.malformed', `${file} is not a regular file`);
|
|
131
|
-
if (st.size > limits.maxZipBytes) throw new TraceRejected('zip.too-large', `${st.size} B > ${limits.maxZipBytes}`);
|
|
132
|
-
return readTraceBuffer(fs.readFileSync(file), file, limits);
|
|
143
|
+
return traceFromRead(readFileOnce(file, limits.maxZipBytes), file, limits);
|
|
133
144
|
}
|
|
@@ -60,10 +60,10 @@ function attemptOf(raw: unknown, i: number): RunRecordAttempt {
|
|
|
60
60
|
return { testId, file, title, retry: retry as number, status, startTime, duration, trace: trace as RunRecordAttempt['trace'] };
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
/** Read and check one record; throws an Error naming what is wrong. */
|
|
64
|
-
export function readRunRecord(file: string): RunRecord {
|
|
63
|
+
/** Read and check one record (`text`: its bytes, already read once by the caller); throws an Error naming what is wrong. */
|
|
64
|
+
export function readRunRecord(file: string, text?: string): RunRecord {
|
|
65
65
|
let raw: unknown;
|
|
66
|
-
try { raw = JSON.parse(fs.readFileSync(file, 'utf8')); } catch (e) { throw new Error(`not readable JSON (${(e as Error).message.split('\n')[0]})`); }
|
|
66
|
+
try { raw = JSON.parse(text ?? fs.readFileSync(file, 'utf8')); } catch (e) { throw new Error(`not readable JSON (${(e as Error).message.split('\n')[0]})`); }
|
|
67
67
|
if (!isObj(raw)) throw new Error('not an object');
|
|
68
68
|
const extra = Object.keys(raw).find((k) => !TOP.has(k));
|
|
69
69
|
if (extra) throw new Error(`unknown key "${extra}"`);
|
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
* Every approved lock entry must be vouched for by its decision (DOS-13.2, DOS-14.4, review M1):
|
|
3
3
|
* `qa/<kind>/<unit>/decisions/<decisionId>.yaml`, COMMITTED in git HEAD (outside git: on disk),
|
|
4
4
|
* intact (schema, id = content, entry_hash = entry), of kind `contract-approval`, whose payload lists
|
|
5
|
-
* this scenario at this hash
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* this scenario at this hash — with exactly the opaque steps the entry says QA accepted (#923) — and
|
|
6
|
+
* whose `valid_from` is the entry's `approvedAt`. A lock line copied from elsewhere, re-pointed at
|
|
7
|
+
* another decision, backdated, or given an acceptance nobody recorded fails here — `unverified`,
|
|
8
|
+
* reported under contract.lock-uncommitted (hard).
|
|
8
9
|
*
|
|
9
10
|
* Only entries that are otherwise identical to HEAD are checked: an entry already added, changed or
|
|
10
11
|
* tampered is a hard finding on its own.
|
|
@@ -14,6 +15,7 @@ import { computeDecisionId, computeEntryHash } from '../decisions/decision-ident
|
|
|
14
15
|
import { DecisionEntry, storedEntryErrors } from '../decisions/decision-schema';
|
|
15
16
|
import { safeYamlParse } from '../utils/safe-yaml-parse';
|
|
16
17
|
import { HeadSnapshot, headFileText, posixRel, readTextOrNull } from './contract-git';
|
|
18
|
+
import { sameJson } from './contract-hash';
|
|
17
19
|
import { ContractLock, isHumanApproved, LockEntry } from './contract-schema';
|
|
18
20
|
import type { UncommittedEntry } from './lock-vs-git-head';
|
|
19
21
|
|
|
@@ -38,8 +40,10 @@ function loadDecision(projectRoot: string, unitDir: string, id: string, snap: He
|
|
|
38
40
|
|
|
39
41
|
function mismatch(scenario: string, lockEntry: LockEntry, d: DecisionEntry): string | null {
|
|
40
42
|
if (d.kind !== 'contract-approval') return `decision ${d.id} is a ${d.kind}, not a contract-approval`;
|
|
41
|
-
const listed = Array.isArray(d.payload?.scenarios) ? d.payload!.scenarios as Array<{ id?: unknown; hash?: unknown }> : [];
|
|
42
|
-
|
|
43
|
+
const listed = Array.isArray(d.payload?.scenarios) ? d.payload!.scenarios as Array<{ id?: unknown; hash?: unknown; acceptedOpaque?: unknown }> : [];
|
|
44
|
+
const item = listed.find((s) => s && s.id === scenario && s.hash === lockEntry.hash);
|
|
45
|
+
if (!item) return `decision ${d.id} does not approve ${scenario} at hash ${lockEntry.hash}`;
|
|
46
|
+
if (!sameJson(item.acceptedOpaque ?? [], lockEntry.acceptedOpaque ?? [])) return `decision ${d.id} does not accept the opaque step(s) the lock entry of ${scenario} lists (acceptedOpaque)`;
|
|
43
47
|
if (Date.parse(lockEntry.approvedAt) !== Date.parse(d.valid_from)) return `approvedAt ${lockEntry.approvedAt} is not decision ${d.id}'s valid_from ${d.valid_from}`;
|
|
44
48
|
return null;
|
|
45
49
|
}
|