@sun-asterisk/sungen 3.3.0-alpha.4 → 3.3.0-alpha.6
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/next.d.ts +3 -2
- package/dist/cli/commands/next.d.ts.map +1 -1
- package/dist/cli/commands/next.js +7 -1
- package/dist/cli/commands/next.js.map +1 -1
- package/dist/cli/commands/triage.d.ts +2 -1
- package/dist/cli/commands/triage.d.ts.map +1 -1
- package/dist/cli/commands/triage.js +42 -2
- package/dist/cli/commands/triage.js.map +1 -1
- package/dist/decisions/decision-schema.d.ts +2 -1
- package/dist/decisions/decision-schema.d.ts.map +1 -1
- package/dist/decisions/decision-schema.js +2 -1
- package/dist/decisions/decision-schema.js.map +1 -1
- package/dist/graph/manual-triage-decisions.d.ts +19 -0
- package/dist/graph/manual-triage-decisions.d.ts.map +1 -0
- package/dist/graph/manual-triage-decisions.js +53 -0
- package/dist/graph/manual-triage-decisions.js.map +1 -0
- package/dist/graph/nodes/test-case-nodes.d.ts +3 -0
- package/dist/graph/nodes/test-case-nodes.d.ts.map +1 -1
- package/dist/graph/nodes/test-case-nodes.js +18 -1
- package/dist/graph/nodes/test-case-nodes.js.map +1 -1
- package/dist/graph/test-case-results.d.ts +1 -0
- package/dist/graph/test-case-results.d.ts.map +1 -1
- package/dist/graph/test-case-results.js +27 -3
- package/dist/graph/test-case-results.js.map +1 -1
- package/dist/graph/test-case-satisfied.d.ts +5 -4
- package/dist/graph/test-case-satisfied.d.ts.map +1 -1
- package/dist/graph/test-case-satisfied.js +4 -0
- package/dist/graph/test-case-satisfied.js.map +1 -1
- package/dist/graph/test-case-state-shape.d.ts +7 -1
- package/dist/graph/test-case-state-shape.d.ts.map +1 -1
- package/dist/graph/unit-results-freshness.d.ts +29 -0
- package/dist/graph/unit-results-freshness.d.ts.map +1 -0
- package/dist/graph/unit-results-freshness.js +213 -0
- package/dist/graph/unit-results-freshness.js.map +1 -0
- package/dist/harness/audit.d.ts +3 -2
- package/dist/harness/audit.d.ts.map +1 -1
- package/dist/harness/audit.js +4 -17
- package/dist/harness/audit.js.map +1 -1
- package/dist/harness/defect-draft.d.ts.map +1 -1
- package/dist/harness/defect-draft.js +12 -2
- package/dist/harness/defect-draft.js.map +1 -1
- package/dist/harness/defects.d.ts +14 -0
- package/dist/harness/defects.d.ts.map +1 -1
- package/dist/harness/defects.js +14 -10
- package/dist/harness/defects.js.map +1 -1
- package/dist/harness/flow-check.d.ts +9 -0
- package/dist/harness/flow-check.d.ts.map +1 -1
- package/dist/harness/flow-check.js +11 -0
- package/dist/harness/flow-check.js.map +1 -1
- package/dist/harness/gates/audit-gate-verdict.d.ts.map +1 -1
- package/dist/harness/gates/audit-gate-verdict.js +2 -1
- package/dist/harness/gates/audit-gate-verdict.js.map +1 -1
- package/dist/harness/gates/audit-input-hash.d.ts +2 -0
- package/dist/harness/gates/audit-input-hash.d.ts.map +1 -1
- package/dist/harness/gates/audit-input-hash.js +30 -2
- package/dist/harness/gates/audit-input-hash.js.map +1 -1
- package/dist/harness/gates/audit-report-verification.js +1 -1
- package/dist/harness/gates/audit-report-verification.js.map +1 -1
- package/dist/harness/gates/delivery-checklist.d.ts +3 -0
- package/dist/harness/gates/delivery-checklist.d.ts.map +1 -1
- package/dist/harness/gates/delivery-checklist.js +28 -3
- package/dist/harness/gates/delivery-checklist.js.map +1 -1
- package/dist/harness/gates/in-run-decision-check.d.ts.map +1 -1
- package/dist/harness/gates/in-run-decision-check.js +14 -1
- package/dist/harness/gates/in-run-decision-check.js.map +1 -1
- package/dist/harness/next-step.d.ts +3 -0
- package/dist/harness/next-step.d.ts.map +1 -1
- package/dist/harness/next-step.js +7 -0
- package/dist/harness/next-step.js.map +1 -1
- package/dist/harness/triage-manual-classification.d.ts +32 -0
- package/dist/harness/triage-manual-classification.d.ts.map +1 -0
- package/dist/harness/triage-manual-classification.js +56 -0
- package/dist/harness/triage-manual-classification.js.map +1 -0
- package/dist/harness/unit-paths.d.ts +8 -0
- package/dist/harness/unit-paths.d.ts.map +1 -1
- package/dist/harness/unit-paths.js +23 -1
- package/dist/harness/unit-paths.js.map +1 -1
- package/dist/hooks/guard-tty-spoof-rule.d.ts +2 -1
- package/dist/hooks/guard-tty-spoof-rule.d.ts.map +1 -1
- package/dist/hooks/guard-tty-spoof-rule.js +3 -2
- package/dist/hooks/guard-tty-spoof-rule.js.map +1 -1
- package/package.json +3 -3
- package/src/cli/commands/next.ts +9 -3
- package/src/cli/commands/triage.ts +38 -2
- package/src/decisions/decision-schema.ts +2 -1
- package/src/graph/manual-triage-decisions.ts +55 -0
- package/src/graph/nodes/test-case-nodes.ts +19 -1
- package/src/graph/test-case-results.ts +25 -4
- package/src/graph/test-case-satisfied.ts +7 -4
- package/src/graph/test-case-state-shape.ts +7 -2
- package/src/graph/unit-results-freshness.ts +165 -0
- package/src/harness/audit.ts +4 -13
- package/src/harness/defect-draft.ts +13 -2
- package/src/harness/defects.ts +22 -9
- package/src/harness/flow-check.ts +11 -0
- package/src/harness/gates/audit-gate-verdict.ts +2 -1
- package/src/harness/gates/audit-input-hash.ts +27 -3
- package/src/harness/gates/audit-report-verification.ts +2 -2
- package/src/harness/gates/delivery-checklist.ts +26 -4
- package/src/harness/gates/in-run-decision-check.ts +15 -1
- package/src/harness/next-step.ts +7 -0
- package/src/harness/triage-manual-classification.ts +76 -0
- package/src/harness/unit-paths.ts +19 -0
- package/src/hooks/guard-tty-spoof-rule.ts +5 -3
|
@@ -59,6 +59,12 @@ exports.hasInRunKindDecisions = hasInRunKindDecisions;
|
|
|
59
59
|
* file does not vouch for — absent, uncommitted, different, introduced beyond the commits the walk
|
|
60
60
|
* reads (#924) or at a shallow clone's boundary — is judged as any other; in the last two cases an
|
|
61
61
|
* in-run finding says why.
|
|
62
|
+
*
|
|
63
|
+
* A manual triage classification that claims a human typed it (`triage-classification` with
|
|
64
|
+
* `humanConfirmed`, #928) is a claim the entry makes about itself — a script can write one. What backs
|
|
65
|
+
* it is review, so it is in-run while git HEAD does not hold it (and, with no git, always: nothing
|
|
66
|
+
* shows it was reviewed). Judged by HEAD membership only: it is always recorded after the run it
|
|
67
|
+
* judges, so its commit time says nothing.
|
|
62
68
|
*/
|
|
63
69
|
const fs = __importStar(require("fs"));
|
|
64
70
|
const path = __importStar(require("path"));
|
|
@@ -94,6 +100,8 @@ function runStartOf(resultsPath) {
|
|
|
94
100
|
}
|
|
95
101
|
}
|
|
96
102
|
const isInRunKind = (kind) => exports.IN_RUN_KINDS.includes(kind);
|
|
103
|
+
/** A triage classification claiming a human typed it (#928): reviewed only once git HEAD holds it. */
|
|
104
|
+
const claimsHumanTriage = (e) => e.kind === 'triage-classification' && e.payload?.humanConfirmed === true;
|
|
97
105
|
/** In-run decisions among `scopes`, judged against one run start (null: HEAD membership only) and the committed set. */
|
|
98
106
|
function inRunDecisions(projectRoot, scopes, runStart, committed) {
|
|
99
107
|
const out = [];
|
|
@@ -101,12 +109,17 @@ function inRunDecisions(projectRoot, scopes, runStart, committed) {
|
|
|
101
109
|
const migratedReview = committed ? (0, migrated_waiver_review_1.migratedWaiverReview)(projectRoot) : null;
|
|
102
110
|
for (const scope of scopes) {
|
|
103
111
|
for (const e of (0, read_decisions_1.readDecisions)(projectRoot, scope).entries) {
|
|
112
|
+
const rel = path.relative(projectRoot, e.path).split(path.sep).join('/');
|
|
113
|
+
if (claimsHumanTriage(e) && !committed?.has(rel)) {
|
|
114
|
+
out.push({ id: e.id, kind: e.kind, path: rel, visibility: e.visibility, why: [committed
|
|
115
|
+
? 'a classification by hand that claims a human typed it, not in git HEAD (never committed through review)'
|
|
116
|
+
: 'a classification by hand that claims a human typed it, and no git to show it was committed through review'] });
|
|
117
|
+
}
|
|
104
118
|
if (!isInRunKind(e.kind))
|
|
105
119
|
continue;
|
|
106
120
|
// A migrated waiver re-confirmed on a terminal is replaced: the new decision is judged on its own.
|
|
107
121
|
if (e.superseded_by && e.source?.where === 'migrated')
|
|
108
122
|
continue;
|
|
109
|
-
const rel = path.relative(projectRoot, e.path).split(path.sep).join('/');
|
|
110
123
|
const why = [];
|
|
111
124
|
const migrated = migratedReview?.reviewOf(scope, e) ?? null;
|
|
112
125
|
if (migrated?.at) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"in-run-decision-check.js","sourceRoot":"","sources":["../../../src/harness/gates/in-run-decision-check.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"in-run-decision-check.js","sourceRoot":"","sources":["../../../src/harness/gates/in-run-decision-check.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,6 +2,7 @@ import type { HeadSnapshot } from '../contract/contract-git';
|
|
|
2
2
|
import { EvaluateOptions } from '../graph/evaluate-graph';
|
|
3
3
|
import type { TestCaseState } from '../graph/test-case-state-shape';
|
|
4
4
|
import { AuditNotCounted, ContractSummary } from '../graph/unit-state-facts';
|
|
5
|
+
import { ResultsFreshness } from '../graph/unit-results-freshness';
|
|
5
6
|
export interface NextStep {
|
|
6
7
|
/** The command to run, as the operator would type it. */
|
|
7
8
|
command: string;
|
|
@@ -65,6 +66,8 @@ export interface UnitState {
|
|
|
65
66
|
contract?: ContractSummary;
|
|
66
67
|
/** Per-test-case state (DOS-11.1); absent when not read from disk or unreadable. */
|
|
67
68
|
testCases?: TestCaseState[];
|
|
69
|
+
/** Which run the results come from and what they are older than (#942); absent without results. Advisory, decides no step. */
|
|
70
|
+
results?: ResultsFreshness;
|
|
68
71
|
steps: NextStep[];
|
|
69
72
|
}
|
|
70
73
|
export interface ReadUnitStateOptions {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"next-step.d.ts","sourceRoot":"","sources":["../../src/harness/next-step.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAiB,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAEzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAKpE,OAAO,EAAE,eAAe,EAAiB,eAAe,EAAuC,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"next-step.d.ts","sourceRoot":"","sources":["../../src/harness/next-step.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAiB,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAEzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAKpE,OAAO,EAAE,eAAe,EAAiB,eAAe,EAAuC,MAAM,2BAA2B,CAAC;AACjI,OAAO,EAAE,gBAAgB,EAAoB,MAAM,iCAAiC,CAAC;AAErF,MAAM,WAAW,QAAQ;IACvB,yDAAyD;IACzD,OAAO,EAAE,MAAM,CAAC;IAChB,0EAA0E;IAC1E,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;;OAMG;IACH,IAAI,EAAE,IAAI,GAAG,SAAS,GAAG,UAAU,GAAG,QAAQ,CAAC;IAC/C,kGAAkG;IAClG,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC;IACnC,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8DAA8D;IAC9D,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,QAAQ,GAAG,MAAM,GAAG,KAAK,CAAC;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,OAAO,CAAC;IACtB,WAAW,EAAE,OAAO,CAAC;IACrB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,OAAO,CAAC;IACtB,uBAAuB,EAAE,OAAO,CAAC;IACjC,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,EAAE,OAAO,CAAC;IACpB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,OAAO,CAAC;IACxB,KAAK,CAAC,EAAE;QACN,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;QACnB,QAAQ,EAAE,MAAM,EAAE,CAAC;QACnB,uBAAuB,CAAC,EAAE,MAAM,CAAC;KAClC,CAAC;IACF;;;;;OAKG;IACH,WAAW,CAAC,EAAE;QAAE,eAAe,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAA;KAAE,CAAC;IAClE,wGAAwG;IACxG,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,0GAA0G;IAC1G,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,oFAAoF;IACpF,SAAS,CAAC,EAAE,aAAa,EAAE,CAAC;IAC5B,8HAA8H;IAC9H,OAAO,CAAC,EAAE,gBAAgB,CAAC;IAC3B,KAAK,EAAE,QAAQ,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,oBAAoB;IACnC,sEAAsE;IACtE,WAAW,CAAC,EAAE,YAAY,CAAC;CAC5B;AAID;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,CAAC,MAAM,CAAC,EAAE,IAAI,GAAE,oBAAyB,GAC3G,SAAS,CA6DX;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,GAAE,eAAoB,GAAG,QAAQ,EAAE,CAEhF"}
|
|
@@ -60,6 +60,7 @@ const core_node_catalog_1 = require("../graph/nodes/core-node-catalog");
|
|
|
60
60
|
const test_case_states_1 = require("../graph/test-case-states");
|
|
61
61
|
const unit_artifact_probes_1 = require("../graph/unit-artifact-probes");
|
|
62
62
|
const unit_state_facts_1 = require("../graph/unit-state-facts");
|
|
63
|
+
const unit_results_freshness_1 = require("../graph/unit-results-freshness");
|
|
63
64
|
const exists = (p) => fs.existsSync(p);
|
|
64
65
|
/**
|
|
65
66
|
* Everything the resolver needs, read from disk. `unit` is the catalog id
|
|
@@ -124,6 +125,12 @@ function readUnitState(projectRoot, unitDir, unit, kind, opts = {}) {
|
|
|
124
125
|
.map((tc) => (contract?.cases.has(tc.id) ? { ...tc, contract: contract.cases.get(tc.id) } : tc));
|
|
125
126
|
}
|
|
126
127
|
catch { /* an unparseable .feature — the unit-level nodes still answer, as before test-case state */ }
|
|
128
|
+
try {
|
|
129
|
+
const results = (0, unit_results_freshness_1.resultsFreshness)(projectRoot, unitDir, unit);
|
|
130
|
+
if (results)
|
|
131
|
+
state.results = results;
|
|
132
|
+
}
|
|
133
|
+
catch { /* advisory: results that cannot be dated add nothing */ }
|
|
127
134
|
state.steps = deriveSteps(state);
|
|
128
135
|
return state;
|
|
129
136
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"next-step.js","sourceRoot":"","sources":["../../src/harness/next-step.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"next-step.js","sourceRoot":"","sources":["../../src/harness/next-step.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `sungen triage --screen <u> --case <id> --as product|test-defect|environment --reason "…"` (#928):
|
|
3
|
+
* classify one red by hand — the one `sungen triage` left unclassified, or one it got wrong — as a
|
|
4
|
+
* `triage-classification` decision in the unit's `decisions/` (read back by graph/manual-triage-decisions.ts).
|
|
5
|
+
*
|
|
6
|
+
* validated first the class, a reason, a case that exists, is red and is not exempt — before any
|
|
7
|
+
* prompt, so a human never confirms something that is then rejected
|
|
8
|
+
* product stricter than any automatic class (it leads to a QA decision): anyone may record it
|
|
9
|
+
* test-defect, can hide a product defect behind "fix the test" / "re-run": a human types a
|
|
10
|
+
* environment confirmation on a terminal (DOS-13.4); without one it is refused, nothing is
|
|
11
|
+
* written and the exact command is printed. Only after the typed phrase does the
|
|
12
|
+
* entry carry `humanConfirmed: true`, and a reader ignores one without it
|
|
13
|
+
* bound evidence_hash = the case's red as it reads now; a re-run voids the classification
|
|
14
|
+
*/
|
|
15
|
+
import type { AppendResult } from '../decisions/append-decision';
|
|
16
|
+
import { ConfirmationIO } from '../decisions/human-confirmation';
|
|
17
|
+
import { ManualTriageClass } from '../graph/manual-triage-decisions';
|
|
18
|
+
export interface ClassifyRedCaseInput {
|
|
19
|
+
projectRoot: string;
|
|
20
|
+
unit: string;
|
|
21
|
+
caseId: string;
|
|
22
|
+
/** product | test-defect | environment */
|
|
23
|
+
as: string;
|
|
24
|
+
reason?: string;
|
|
25
|
+
io: ConfirmationIO;
|
|
26
|
+
}
|
|
27
|
+
export interface ClassifyRedCaseResult extends AppendResult {
|
|
28
|
+
caseId: string;
|
|
29
|
+
class: ManualTriageClass;
|
|
30
|
+
}
|
|
31
|
+
export declare function classifyRedCase(i: ClassifyRedCaseInput): Promise<ClassifyRedCaseResult>;
|
|
32
|
+
//# sourceMappingURL=triage-manual-classification.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"triage-manual-classification.d.ts","sourceRoot":"","sources":["../../src/harness/triage-manual-classification.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAIjE,OAAO,EAAE,cAAc,EAA2C,MAAM,iCAAiC,CAAC;AAE1G,OAAO,EAAuC,iBAAiB,EAAwB,MAAM,kCAAkC,CAAC;AAKhI,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,0CAA0C;IAC1C,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,EAAE,EAAE,cAAc,CAAC;CACpB;AAED,MAAM,WAAW,qBAAsB,SAAQ,YAAY;IAAG,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,iBAAiB,CAAA;CAAE;AAExG,wBAAsB,eAAe,CAAC,CAAC,EAAE,oBAAoB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAsC7F"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.classifyRedCase = classifyRedCase;
|
|
4
|
+
const append_decision_1 = require("../decisions/append-decision");
|
|
5
|
+
const decision_source_1 = require("../decisions/decision-source");
|
|
6
|
+
const human_confirmation_1 = require("../decisions/human-confirmation");
|
|
7
|
+
const resolve_unit_1 = require("../cli/resolve-unit");
|
|
8
|
+
const manual_triage_decisions_1 = require("../graph/manual-triage-decisions");
|
|
9
|
+
const test_case_satisfied_1 = require("../graph/test-case-satisfied");
|
|
10
|
+
const test_case_states_1 = require("../graph/test-case-states");
|
|
11
|
+
const unit_gate_overrides_1 = require("./gates/unit-gate-overrides");
|
|
12
|
+
async function classifyRedCase(i) {
|
|
13
|
+
const cls = Object.prototype.hasOwnProperty.call(manual_triage_decisions_1.MANUAL_TRIAGE_AS, i.as) ? manual_triage_decisions_1.MANUAL_TRIAGE_AS[i.as] : undefined;
|
|
14
|
+
if (!cls)
|
|
15
|
+
throw new Error(`--as must be one of ${Object.keys(manual_triage_decisions_1.MANUAL_TRIAGE_AS).join(', ')} (got "${i.as}")`);
|
|
16
|
+
const reason = (i.reason ?? '').trim();
|
|
17
|
+
if (!reason)
|
|
18
|
+
throw new Error('--reason is required: a classification records WHY this red is what you say it is');
|
|
19
|
+
const found = (0, resolve_unit_1.resolveUnitCandidates)(i.unit, i.projectRoot);
|
|
20
|
+
if (found.length !== 1)
|
|
21
|
+
throw new Error(`"${i.unit}" does not name exactly one unit under qa/screens, qa/flows or qa/api`);
|
|
22
|
+
const unitDir = found[0].dir;
|
|
23
|
+
const scope = (0, unit_gate_overrides_1.unitScopeOfDir)(i.projectRoot, unitDir);
|
|
24
|
+
if (!scope)
|
|
25
|
+
throw new Error(`"${i.unit}" has no decisions scope`);
|
|
26
|
+
const cases = (0, test_case_states_1.testCaseStates)(i.projectRoot, i.unit, { unitDir, auditFindings: [] });
|
|
27
|
+
const tc = cases.find((c) => c.id === i.caseId);
|
|
28
|
+
if (!tc) {
|
|
29
|
+
const known = cases.slice(0, 8).map((c) => c.id).join(', ');
|
|
30
|
+
throw new Error(`no test case "${i.caseId}" in ${i.unit}${known ? ` — known: ${known}${cases.length > 8 ? ', …' : ''}` : ''}`);
|
|
31
|
+
}
|
|
32
|
+
if (tc.result !== 'failed' || !tc.evidence)
|
|
33
|
+
throw new Error(`${tc.id} has no red result to classify (result: ${tc.result})`);
|
|
34
|
+
const exempt = (0, test_case_satisfied_1.exemptionOf)(tc);
|
|
35
|
+
if (exempt)
|
|
36
|
+
throw new Error(`${tc.id} is exempt (${exempt}) — it needs no classification`);
|
|
37
|
+
const human = manual_triage_decisions_1.HUMAN_ONLY_TRIAGE.has(cls);
|
|
38
|
+
if (human) {
|
|
39
|
+
await (0, human_confirmation_1.requireTypedConfirmation)({
|
|
40
|
+
what: `classify the red ${tc.id} of ${i.unit} as ${cls}`,
|
|
41
|
+
phrase: tc.id,
|
|
42
|
+
command: (0, human_confirmation_1.formatCommand)(['sungen', 'triage', found[0].kind === 'api' ? '--api' : '--screen', i.unit, '--case', tc.id, '--as', i.as, '--reason', reason]),
|
|
43
|
+
summary: [`case: ${tc.title}`, `automatic triage: ${tc.triage ? `${tc.triage.class} (${tc.triage.ruleId})` : 'none current'}`, `reason: ${reason}`,
|
|
44
|
+
`recorded by: ${(0, decision_source_1.gitUserName)(i.projectRoot)}`, `as ${cls} it leaves the product-defect path — confirm only after reading its evidence (trace, error)`,
|
|
45
|
+
'it stops counting as soon as the case runs again'],
|
|
46
|
+
io: i.io,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
const draft = {
|
|
50
|
+
kind: manual_triage_decisions_1.TRIAGE_DECISION_KIND, anchors: [], text: `${tc.id} is ${cls}: ${tc.title}`, reason,
|
|
51
|
+
payload: { case: tc.id, class: cls, ...(human ? { humanConfirmed: true } : {}) },
|
|
52
|
+
source: { by: (0, decision_source_1.gitUserName)(i.projectRoot), where: 'cli' }, valid_from: new Date().toISOString(), visibility: 'internal', evidence_hash: tc.evidence,
|
|
53
|
+
};
|
|
54
|
+
return { ...(0, append_decision_1.appendDecision)(i.projectRoot, scope, draft), caseId: tc.id, class: cls };
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=triage-manual-classification.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"triage-manual-classification.js","sourceRoot":"","sources":["../../src/harness/triage-manual-classification.ts"],"names":[],"mappings":"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"}
|
|
@@ -16,4 +16,12 @@ export declare const reportSlug: (unit: string) => string;
|
|
|
16
16
|
* behaviour (and error text) they already have.
|
|
17
17
|
*/
|
|
18
18
|
export declare function featureFilesFor(unitDir: string, unit: string): string[];
|
|
19
|
+
/**
|
|
20
|
+
* The catalog-resolution id for a unit dir (relative to qa/): screen · flows/<flow> · api/<area> ·
|
|
21
|
+
* api/flows/<flow>; a bare screen is its basename. Shared by the audit, which scores and links the
|
|
22
|
+
* unit under this id, and its inputsHash, which reads what the audit reads under the same id.
|
|
23
|
+
*/
|
|
24
|
+
export declare function catalogIdFromScreenDir(screenDir: string): string;
|
|
25
|
+
/** A screen unit — the only kind a sibling flow credits (TQ-4); flows and api units are not. */
|
|
26
|
+
export declare const isScreenUnitId: (catalogId: string) => boolean;
|
|
19
27
|
//# sourceMappingURL=unit-paths.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"unit-paths.d.ts","sourceRoot":"","sources":["../../src/harness/unit-paths.ts"],"names":[],"mappings":"AAcA,eAAO,MAAM,eAAe,GAAI,MAAM,MAAM,KAAG,MAA6B,CAAC;AAC7E,eAAO,MAAM,UAAU,GAAI,MAAM,MAAM,KAAG,MAAsC,CAAC;AAEjF;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAkBvE"}
|
|
1
|
+
{"version":3,"file":"unit-paths.d.ts","sourceRoot":"","sources":["../../src/harness/unit-paths.ts"],"names":[],"mappings":"AAcA,eAAO,MAAM,eAAe,GAAI,MAAM,MAAM,KAAG,MAA6B,CAAC;AAC7E,eAAO,MAAM,UAAU,GAAI,MAAM,MAAM,KAAG,MAAsC,CAAC;AAEjF;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAkBvE;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAShE;AAED,gGAAgG;AAChG,eAAO,MAAM,cAAc,GAAI,WAAW,MAAM,KAAG,OAA4C,CAAC"}
|
|
@@ -33,8 +33,9 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.reportSlug = exports.featureBasename = void 0;
|
|
36
|
+
exports.isScreenUnitId = exports.reportSlug = exports.featureBasename = void 0;
|
|
37
37
|
exports.featureFilesFor = featureFilesFor;
|
|
38
|
+
exports.catalogIdFromScreenDir = catalogIdFromScreenDir;
|
|
38
39
|
/**
|
|
39
40
|
* Unit-path helpers (api-flow fix). A unit id may be a bare name (`orders`, `login`) or a nested
|
|
40
41
|
* api-flow id (`flows/<flow>`). Three derivations the harness/CLI need:
|
|
@@ -86,4 +87,25 @@ function featureFilesFor(unitDir, unit) {
|
|
|
86
87
|
...names.filter((n) => n !== canonicalName),
|
|
87
88
|
].map((n) => path.join(dir, n));
|
|
88
89
|
}
|
|
90
|
+
/**
|
|
91
|
+
* The catalog-resolution id for a unit dir (relative to qa/): screen · flows/<flow> · api/<area> ·
|
|
92
|
+
* api/flows/<flow>; a bare screen is its basename. Shared by the audit, which scores and links the
|
|
93
|
+
* unit under this id, and its inputsHash, which reads what the audit reads under the same id.
|
|
94
|
+
*/
|
|
95
|
+
function catalogIdFromScreenDir(screenDir) {
|
|
96
|
+
const parts = screenDir.split(path.sep);
|
|
97
|
+
const qa = parts.lastIndexOf('qa');
|
|
98
|
+
if (qa >= 0) {
|
|
99
|
+
if (parts[qa + 1] === 'api' && parts[qa + 2] === 'flows' && parts[qa + 3])
|
|
100
|
+
return `api/flows/${parts[qa + 3]}`;
|
|
101
|
+
if (parts[qa + 1] === 'api' && parts[qa + 2])
|
|
102
|
+
return `api/${parts[qa + 2]}`;
|
|
103
|
+
if (parts[qa + 1] === 'flows' && parts[qa + 2])
|
|
104
|
+
return `flows/${parts[qa + 2]}`;
|
|
105
|
+
}
|
|
106
|
+
return path.basename(screenDir);
|
|
107
|
+
}
|
|
108
|
+
/** A screen unit — the only kind a sibling flow credits (TQ-4); flows and api units are not. */
|
|
109
|
+
const isScreenUnitId = (catalogId) => !/^(flows|api)\//.test(catalogId);
|
|
110
|
+
exports.isScreenUnitId = isScreenUnitId;
|
|
89
111
|
//# sourceMappingURL=unit-paths.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"unit-paths.js","sourceRoot":"","sources":["../../src/harness/unit-paths.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,0CAkBC;
|
|
1
|
+
{"version":3,"file":"unit-paths.js","sourceRoot":"","sources":["../../src/harness/unit-paths.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,0CAkBC;AAOD,wDASC;AAlED;;;;;;;;;;GAUG;AACH,uCAAyB;AACzB,2CAA6B;AAEtB,MAAM,eAAe,GAAG,CAAC,IAAY,EAAU,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;AAAhE,QAAA,eAAe,mBAAiD;AACtE,MAAM,UAAU,GAAG,CAAC,IAAY,EAAU,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;AAApE,QAAA,UAAU,cAA0D;AAEjF;;;;;;;;;;;;;;GAcG;AACH,SAAgB,eAAe,CAAC,OAAe,EAAE,IAAY;IAC3D,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;IAC3C,MAAM,aAAa,GAAG,GAAG,IAAA,uBAAe,EAAC,IAAI,CAAC,UAAU,CAAC;IACzD,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC;IAEhD,IAAI,KAAe,CAAC;IACpB,IAAI,CAAC;QACH,KAAK,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC;IACpE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,4DAA4D;IAClF,CAAC;IACD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,CAAC,SAAS,CAAC,CAAC;IAE3C,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;IAC/C,OAAO;QACL,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,aAAa,CAAC;QAC3C,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,aAAa,CAAC;KAC5C,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC;AAClC,CAAC;AAED;;;;GAIG;AACH,SAAgB,sBAAsB,CAAC,SAAiB;IACtD,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACxC,MAAM,EAAE,GAAG,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IACnC,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC;QACZ,IAAI,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,KAAK,KAAK,IAAI,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,KAAK,OAAO,IAAI,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC;YAAE,OAAO,aAAa,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC;QAC/G,IAAI,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,KAAK,KAAK,IAAI,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC;YAAE,OAAO,OAAO,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC;QAC5E,IAAI,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,KAAK,OAAO,IAAI,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC;YAAE,OAAO,SAAS,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC;IAClF,CAAC;IACD,OAAO,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;AAClC,CAAC;AAED,gGAAgG;AACzF,MAAM,cAAc,GAAG,CAAC,SAAiB,EAAW,EAAE,CAAC,CAAC,gBAAgB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;AAAnF,QAAA,cAAc,kBAAqE"}
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* Guard rule `tty-spoof` (phase 06, M5): ASK before an agent runs a human-confirmed sungen command
|
|
3
3
|
* through a terminal emulator.
|
|
4
4
|
*
|
|
5
|
-
* `contract approve`, `journey --waive`, `journey --decide
|
|
5
|
+
* `contract approve`, `journey --waive`, `journey --decide`, `journey --signoff` and `triage --as test-defect|environment`
|
|
6
|
+
* (#928: a red classified away from the product-defect path) refuse without a TTY (DOS-13.4), and
|
|
6
7
|
* an agent's Bash has none — unless it borrows one: `script`, `expect`, `unbuffer`, or a pty from
|
|
7
8
|
* Python/Perl/zsh. Then the prompt sees a "terminal" and a piped phrase reads as a human typing it.
|
|
8
9
|
* This rule spots the pair in the raw command (quotes included, so `bash -c "…"` and `expect -c '…'`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"guard-tty-spoof-rule.d.ts","sourceRoot":"","sources":["../../src/hooks/guard-tty-spoof-rule.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"guard-tty-spoof-rule.d.ts","sourceRoot":"","sources":["../../src/hooks/guard-tty-spoof-rule.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAK/C,eAAO,MAAM,QAAQ,EAAE,SActB,CAAC"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ttySpoof = void 0;
|
|
4
4
|
const TTY_EMULATOR = /\b(?:script|unbuffer|expect|pty|zpty|openpty|forkpty|ptyprocess)\b/i;
|
|
5
|
-
const HUMAN_GATED = /\bsungen\b[\s\S]*?\b(?:contract['",\s]+approve\b|journey\b[\s\S]*?--(?:waive|decide|signoff)\b)/i;
|
|
5
|
+
const HUMAN_GATED = /\bsungen\b[\s\S]*?\b(?:contract['",\s]+approve\b|journey\b[\s\S]*?--(?:waive|decide|signoff)\b|triage\b[\s\S]*?--as[\s='",]+(?:test-defect|environment)\b)/i;
|
|
6
6
|
exports.ttySpoof = {
|
|
7
7
|
id: 'tty-spoof',
|
|
8
8
|
onError: 'no-op',
|
|
@@ -12,7 +12,8 @@ exports.ttySpoof = {
|
|
|
12
12
|
return null;
|
|
13
13
|
return {
|
|
14
14
|
status: 'ask',
|
|
15
|
-
askReason: 'sungen: this runs a human-confirmed sungen action (`contract approve` / `journey --waive|--decide|--signoff`
|
|
15
|
+
askReason: 'sungen: this runs a human-confirmed sungen action (`contract approve` / `journey --waive|--decide|--signoff` / '
|
|
16
|
+
+ '`triage --as test-defect|environment`) through a terminal '
|
|
16
17
|
+ 'emulator (script / expect / unbuffer / pty). Those confirmations exist so a PERSON at a terminal decides — allow only if you '
|
|
17
18
|
+ 'are that person and asked for exactly this; otherwise run the command yourself in your own terminal.',
|
|
18
19
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"guard-tty-spoof-rule.js","sourceRoot":"","sources":["../../src/hooks/guard-tty-spoof-rule.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"guard-tty-spoof-rule.js","sourceRoot":"","sources":["../../src/hooks/guard-tty-spoof-rule.ts"],"names":[],"mappings":";;;AAeA,MAAM,YAAY,GAAG,qEAAqE,CAAC;AAC3F,MAAM,WAAW,GAAG,6JAA6J,CAAC;AAErK,QAAA,QAAQ,GAAc;IACjC,EAAE,EAAE,WAAW;IACf,OAAO,EAAE,OAAO;IAChB,KAAK,CAAC,IAAI;QACR,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC;QAChE,IAAI,CAAC,GAAG,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC;YAAE,OAAO,IAAI,CAAC;QAC3E,OAAO;YACL,MAAM,EAAE,KAAK;YACb,SAAS,EAAE,iHAAiH;kBACxH,4DAA4D;kBAC5D,+HAA+H;kBAC/H,sGAAsG;SAC3G,CAAC;IACJ,CAAC;CACF,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sun-asterisk/sungen",
|
|
3
|
-
"version": "3.3.0-alpha.
|
|
3
|
+
"version": "3.3.0-alpha.6",
|
|
4
4
|
"description": "Deterministic E2E Test Compiler - Gherkin + Selectors → Playwright tests",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"types": "src/index.ts",
|
|
@@ -40,8 +40,8 @@
|
|
|
40
40
|
"@babel/types": "^7.28.5",
|
|
41
41
|
"@cucumber/gherkin": "^37.0.0",
|
|
42
42
|
"@cucumber/messages": "^31.0.0",
|
|
43
|
-
"@sungen/driver-data-factory": "3.3.0-alpha.
|
|
44
|
-
"@sungen/driver-ui": "3.3.0-alpha.
|
|
43
|
+
"@sungen/driver-data-factory": "3.3.0-alpha.6",
|
|
44
|
+
"@sungen/driver-ui": "3.3.0-alpha.6",
|
|
45
45
|
"chalk": "^5.6.2",
|
|
46
46
|
"commander": "^14.0.2",
|
|
47
47
|
"dotenv": "^17.2.3",
|
package/src/cli/commands/next.ts
CHANGED
|
@@ -9,8 +9,9 @@
|
|
|
9
9
|
* sungen next <unit> --case <id> one test case: its state, and its steps after the blocking
|
|
10
10
|
* unit steps before them (OES-10.3)
|
|
11
11
|
* --json the same, machine-readable; steps add nodeId, granularity,
|
|
12
|
-
* testCase?, gates?;
|
|
13
|
-
* payload alone (#660, #836)
|
|
12
|
+
* testCase?, gates?; the unit adds `results` (#942); every older
|
|
13
|
+
* field is kept. stdout is the payload alone (#660, #836)
|
|
14
|
+
* The text names the run the results come from and warns when they are older than the suite (#942).
|
|
14
15
|
*/
|
|
15
16
|
import { Command } from 'commander';
|
|
16
17
|
import * as fs from 'fs';
|
|
@@ -18,6 +19,7 @@ import * as path from 'path';
|
|
|
18
19
|
import { CLI_GIT_TIMEOUT_MS, headSnapshot } from '../../contract/contract-git';
|
|
19
20
|
import { testCaseStatusLabel } from '../../graph/test-case-satisfied';
|
|
20
21
|
import { testCaseStates } from '../../graph/test-case-states';
|
|
22
|
+
import { describeResultsRun } from '../../graph/unit-results-freshness';
|
|
21
23
|
import { deriveSteps, NextStep, readUnitState, UnitState } from '../../harness/next-step';
|
|
22
24
|
import { resolveUnit, resolveUnitCandidates, AmbiguousUnitError } from '../resolve-unit';
|
|
23
25
|
import { withProjectInvocation } from '../project-sungen-invocation';
|
|
@@ -60,6 +62,10 @@ function render(s: UnitState, cwd: string): void {
|
|
|
60
62
|
L(` state: ${held.length ? held.join(' · ') : '(nothing yet)'}`);
|
|
61
63
|
if (s.audit) L(` audit: ${s.audit.overall}/10 [${s.audit.gateStatus}] · ${s.audit.findings.length} finding(s)`);
|
|
62
64
|
else if (s.auditNotCounted) L(` audit: ${s.auditNotCounted.state === 'stale' ? 'stale' : 'not trusted'} — ${s.auditNotCounted.reason}`);
|
|
65
|
+
if (s.results) {
|
|
66
|
+
L(` results: ${s.results.files.length} file${s.results.files.length === 1 ? '' : 's'}, ${describeResultsRun(s.results)}`);
|
|
67
|
+
if (s.results.warning) L(` ⚠ ${s.results.warning}`);
|
|
68
|
+
}
|
|
63
69
|
L('');
|
|
64
70
|
renderSteps(s.steps, cwd);
|
|
65
71
|
L('');
|
|
@@ -86,7 +92,7 @@ function caseView(s: UnitState, cwd: string, unitDir: string, id: string, json:
|
|
|
86
92
|
L('');
|
|
87
93
|
L(` test case: ${tc.title}${tc.unstableId ? ' (no VP-id — this id changes when the title does)' : ''}`);
|
|
88
94
|
L(` where: ${tc.file}#L${tc.lines[0]}-L${tc.lines[1]}`);
|
|
89
|
-
L(` status: ${testCaseStatusLabel(tc)} · result ${tc.result}${tc.mocked && tc.result === 'passed' ? ' (mocked)' : ''}${tc.stale ? ` · stale: ${tc.stale}` : ''}${tc.triage ? ` · triage ${tc.triage.class}` : ''}`);
|
|
95
|
+
L(` status: ${testCaseStatusLabel(tc)} · result ${tc.result}${tc.mocked && tc.result === 'passed' ? ' (mocked)' : ''}${tc.stale ? ` · stale: ${tc.stale}` : ''}${tc.triage ? ` · triage ${tc.triage.class}${tc.triage.ruleId === 'manual' ? ` (by hand, ${tc.triage.decision})` : ''}` : ''}`);
|
|
90
96
|
if (tc.contract) L(` contract: ${tc.contract.class}${tc.contract.uncommitted ? ' · lock entry not committed' : ''}${tc.contract.lowIdentity ? ' · low target identity' : ''}`);
|
|
91
97
|
if (tc.anchors.length) L(` anchors: ${tc.anchors.map((a) => `${a.anchor} (${a.change})`).join(', ')}`);
|
|
92
98
|
for (const f of tc.findings) L(` finding: ${f.code} [${f.confidence.label} ${f.confidence.score}] ${f.citation}`);
|
|
@@ -1,15 +1,43 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import { resolveUnit } from '../resolve-unit';
|
|
4
|
+
import { processIO } from '../../decisions/human-confirmation';
|
|
5
|
+
import { manualClassifications } from '../../graph/manual-triage-decisions';
|
|
6
|
+
import { testCaseStates } from '../../graph/test-case-states';
|
|
4
7
|
import { triageUnit, writeTriageReport } from '../../harness/triage-unit';
|
|
8
|
+
import { classifyRedCase } from '../../harness/triage-manual-classification';
|
|
5
9
|
import type { TriageClass } from '../../harness/triage';
|
|
6
10
|
|
|
7
11
|
/** Display order: what needs a decision first, green last. */
|
|
8
12
|
const ORDER: TriageClass[] = ['product-candidate', 'unclassified', 'test-defect', 'environment', 'blocked-capability', 'known-defect', 'flaky'];
|
|
9
13
|
|
|
14
|
+
interface TriageOptions { screen?: string; api?: string; area?: string; json?: boolean; case?: string; as?: string; reason?: string }
|
|
15
|
+
|
|
16
|
+
/** The classifications by hand in force for the unit (they win over the automatic class of the same red). */
|
|
17
|
+
function printManual(cwd: string, unitDir: string, name: string): void {
|
|
18
|
+
const inForce = testCaseStates(cwd, name, { unitDir, auditFindings: [] }).filter((c) => c.triage?.ruleId === 'manual');
|
|
19
|
+
if (!inForce.length) return;
|
|
20
|
+
const why = new Map(manualClassifications(cwd, unitDir).map((m) => [m.decision, m]));
|
|
21
|
+
console.log(`\n classified by hand (${inForce.length}) — these win over the automatic class`);
|
|
22
|
+
for (const c of inForce) {
|
|
23
|
+
const m = why.get(c.triage!.decision ?? '');
|
|
24
|
+
console.log(` • ${c.id} → ${c.triage!.class}${m ? ` (${m.decision}, by ${m.by}): ${m.reason}` : ''}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** `--case <id> --as <class> --reason "…"`: record one classification by hand (harness/triage-manual-classification.ts). */
|
|
29
|
+
async function classifyByHand(cwd: string, name: string, o: TriageOptions): Promise<void> {
|
|
30
|
+
if (!o.case || !o.as) throw new Error(o.as ? '--as needs --case <id>' : '--case needs --as product|test-defect|environment and --reason "…"');
|
|
31
|
+
const r = await classifyRedCase({ projectRoot: cwd, unit: name, caseId: o.case, as: o.as, reason: o.reason, io: processIO() });
|
|
32
|
+
if (o.json) { console.log(JSON.stringify({ status: r.status, caseId: r.caseId, class: r.class, path: path.relative(cwd, r.path), entry: r.entry }, null, 2)); return; }
|
|
33
|
+
console.log(`\n${r.status === 'created' ? '✓ Recorded' : '= Already recorded'} ${r.entry.id}: ${r.caseId} → ${r.class} (classified by hand) → ${path.relative(cwd, r.path)}`);
|
|
34
|
+
console.log(' It holds until the case runs again. Commit it through review with the run it judged.\n');
|
|
35
|
+
}
|
|
36
|
+
|
|
10
37
|
/**
|
|
11
38
|
* `sungen triage` — classify every red or flaky scenario of the latest run (see harness/triage.ts).
|
|
12
|
-
* Reports, never gates: always exits 0 once the unit is found.
|
|
39
|
+
* Reports, never gates: always exits 0 once the unit is found. With `--case --as` it records one
|
|
40
|
+
* classification by hand instead (exit 1 when refused or invalid).
|
|
13
41
|
*/
|
|
14
42
|
export function registerTriageCommand(program: Command): void {
|
|
15
43
|
program
|
|
@@ -18,14 +46,18 @@ export function registerTriageCommand(program: Command): void {
|
|
|
18
46
|
.option('-s, --screen <name>', 'Screen or flow name')
|
|
19
47
|
.option('--api <name>', 'API-first area or api flow (qa/api/<name>)')
|
|
20
48
|
.option('--area <name>', 'Alias of --api')
|
|
49
|
+
.option('--case <id>', 'Classify one red test case by hand (its VP-id) — with --as and --reason')
|
|
50
|
+
.option('--as <class>', 'product (anyone) | test-defect | environment (a human types a confirmation on a TTY)')
|
|
51
|
+
.option('--reason <text>', 'Why the red is that class (mandatory with --as)')
|
|
21
52
|
.option('--json', 'Output the raw JSON report')
|
|
22
|
-
.action((o:
|
|
53
|
+
.action(async (o: TriageOptions) => {
|
|
23
54
|
try {
|
|
24
55
|
const name = o.screen || o.api || o.area;
|
|
25
56
|
if (!name) throw new Error('Provide --screen <name> (or --api <area>)');
|
|
26
57
|
const cwd = process.cwd();
|
|
27
58
|
const unit = resolveUnit(name, cwd);
|
|
28
59
|
if (!unit) throw new Error(`Not found: qa/screens/${name}, qa/flows/${name}, or qa/api/${name}`);
|
|
60
|
+
if (o.case || o.as) { await classifyByHand(cwd, name, o); return; }
|
|
29
61
|
|
|
30
62
|
const report = triageUnit(cwd, unit);
|
|
31
63
|
if (report.resultFiles.length === 0) {
|
|
@@ -48,6 +80,10 @@ export function registerTriageCommand(program: Command): void {
|
|
|
48
80
|
L(` ${r.ruleId}: ${r.evidence}`);
|
|
49
81
|
}
|
|
50
82
|
}
|
|
83
|
+
printManual(cwd, unit.dir, name);
|
|
84
|
+
if (report.rows.some((r) => r.class === 'unclassified')) {
|
|
85
|
+
L(`\n Unclassified: read the evidence (trace, error), then classify by hand:\n sungen triage --${unit.kind === 'api' ? 'api' : 'screen'} ${name} --case <id> --as product|test-defect|environment --reason "…"`);
|
|
86
|
+
}
|
|
51
87
|
L(`\n Report: ${path.relative(cwd, saved)}\n`);
|
|
52
88
|
} catch (error) {
|
|
53
89
|
console.error('Error:', error instanceof Error ? error.message : error);
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
* checked when read — an unknown key (a stored `superseded_by`, say) makes the entry invalid, because
|
|
9
9
|
* a field outside the id's content could carry meaning the id does not vouch for.
|
|
10
10
|
*/
|
|
11
|
-
|
|
11
|
+
/** `triage-classification`: a red classified by hand, `sungen triage --case --as` (#928, graph/manual-triage-decisions.ts). */
|
|
12
|
+
export const DECISION_KINDS = ['waiver', 'gate-override', 'contract-approval', 'viewpoint-acceptance', 'clarification', 'triage-classification'] as const;
|
|
12
13
|
export type DecisionKind = typeof DECISION_KINDS[number];
|
|
13
14
|
export type DecisionVisibility = 'internal' | 'client';
|
|
14
15
|
export type JsonValue = string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue };
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Manual triage classifications (#928) — a red `sungen triage` could not classify, or classified
|
|
3
|
+
* wrongly, judged by hand with `sungen triage --screen <u> --case <id> --as <class> --reason "…"`
|
|
4
|
+
* (harness/triage-manual-classification.ts writes them). Each is a `triage-classification` decision
|
|
5
|
+
* in the unit's `decisions/`, read here for the test-case state (test-case-results.ts):
|
|
6
|
+
*
|
|
7
|
+
* payload { case: <id>, class: product-candidate | test-defect | environment, humanConfirmed? }
|
|
8
|
+
* evidence_hash the hash of the red final attempts it judged (TestCaseState.evidence): a re-run
|
|
9
|
+
* makes a new red, and the classification no longer applies to it
|
|
10
|
+
* in force verifies (read-decisions), not superseded, bound to today's red — and, for
|
|
11
|
+
* test-defect / environment, typed by a human on a terminal (`humanConfirmed`, only
|
|
12
|
+
* ever set after the typed confirmation): those classes can hide a product defect,
|
|
13
|
+
* so an entry without it is ignored (fail closed). product-candidate is stricter than
|
|
14
|
+
* any automatic class, so anyone may record it.
|
|
15
|
+
* The latest classification in force wins over the automatic one.
|
|
16
|
+
*/
|
|
17
|
+
import { readDecisions } from '../decisions/read-decisions';
|
|
18
|
+
import { unitScopeOfDir } from '../harness/gates/unit-gate-overrides';
|
|
19
|
+
|
|
20
|
+
export const TRIAGE_DECISION_KIND = 'triage-classification' as const;
|
|
21
|
+
export type ManualTriageClass = 'product-candidate' | 'test-defect' | 'environment';
|
|
22
|
+
/** `--as` word → the triage class it records. */
|
|
23
|
+
export const MANUAL_TRIAGE_AS: Readonly<Record<string, ManualTriageClass>> = { product: 'product-candidate', 'test-defect': 'test-defect', environment: 'environment' };
|
|
24
|
+
/** Classes that can hide a product defect: a human types a confirmation on a terminal. */
|
|
25
|
+
export const HUMAN_ONLY_TRIAGE: ReadonlySet<ManualTriageClass> = new Set(['test-defect', 'environment']);
|
|
26
|
+
|
|
27
|
+
export interface ManualClassification {
|
|
28
|
+
decision: string;
|
|
29
|
+
caseId: string;
|
|
30
|
+
class: ManualTriageClass;
|
|
31
|
+
evidence: string;
|
|
32
|
+
reason: string;
|
|
33
|
+
by: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const isClass = (v: unknown): v is ManualTriageClass => Object.values(MANUAL_TRIAGE_AS).includes(v as ManualTriageClass);
|
|
37
|
+
|
|
38
|
+
/** The unit's classifications that can be in force, oldest first (read-decisions orders by time). */
|
|
39
|
+
export function manualClassifications(projectRoot: string, unitDir: string): ManualClassification[] {
|
|
40
|
+
const scope = unitScopeOfDir(projectRoot, unitDir);
|
|
41
|
+
if (!scope) return [];
|
|
42
|
+
return readDecisions(projectRoot, scope, { kind: TRIAGE_DECISION_KIND }).entries.flatMap((e) => {
|
|
43
|
+
const p = e.payload ?? {};
|
|
44
|
+
if (e.superseded_by || typeof p.case !== 'string' || !isClass(p.class) || typeof e.evidence_hash !== 'string') return [];
|
|
45
|
+
if (HUMAN_ONLY_TRIAGE.has(p.class) && p.humanConfirmed !== true) return [];
|
|
46
|
+
return [{ decision: e.id, caseId: p.case, class: p.class, evidence: e.evidence_hash, reason: e.reason, by: e.source.by }];
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** The latest classification of this case bound to this red, if any. */
|
|
51
|
+
export function manualClassificationFor(list: ManualClassification[], caseId: string, evidence: string | undefined): ManualClassification | undefined {
|
|
52
|
+
if (!evidence) return undefined;
|
|
53
|
+
for (let i = list.length - 1; i >= 0; i--) if (list[i].caseId === caseId && list[i].evidence === evidence) return list[i];
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
@@ -8,6 +8,9 @@
|
|
|
8
8
|
* tc.rerun-stale changed after the recorded run (run manifest) do run-test --change-only
|
|
9
9
|
* tc.triage red, not classified by a current triage do sungen triage
|
|
10
10
|
* tc.product-candidate triage found a product-defect candidate decide sungen defect draft
|
|
11
|
+
* tc.rerun-environment a red the environment caused (#928) decide run-test --ids, once a human confirms it is up
|
|
12
|
+
* tc.fix-test a red the test caused (#928) do run-test --ids (auto-fix)
|
|
13
|
+
* The class is `sungen triage`'s, or a manual classification in force (`sungen triage --case --as`).
|
|
11
14
|
* They sit after unit.run (which cuts a unit that never ran) and before delivery. tc.triage takes
|
|
12
15
|
* the reds the cases account for; unit.triage keeps the rest (unattributedReds), so a red no case
|
|
13
16
|
* explains — an @cases row, a renamed scenario, a nested describe title — still gets a triage step.
|
|
@@ -23,6 +26,9 @@ export const listed = (cases: TestCaseState[]): string => `${cases.slice(0, 4).m
|
|
|
23
26
|
export const idsArg = (cases: TestCaseState[]): string =>
|
|
24
27
|
(cases.length <= MAX_IDS && cases.every((c) => !c.unstableId) ? ` --ids ${cases.map((c) => c.id).join(',')}` : '');
|
|
25
28
|
const flag = (s: UnitState): string => (s.kind === 'api' ? '--api' : '--screen');
|
|
29
|
+
/** Up to three cases with the triage rule that judged each. */
|
|
30
|
+
const withRule = (cases: TestCaseState[]): string =>
|
|
31
|
+
`${cases.slice(0, 3).map((c) => `${c.id}: ${c.triage?.ruleId}`).join('; ')}${cases.length > 3 ? '; …' : ''}`;
|
|
26
32
|
|
|
27
33
|
function caseNode(id: string, need: TestCaseNeed, rank: number, title: string, actor: TestCaseNodeDecl['actor'],
|
|
28
34
|
step: (s: UnitState, cases: TestCaseState[], single: boolean) => StepCore, produces: string[]): TestCaseNodeDecl {
|
|
@@ -50,7 +56,7 @@ export const TEST_CASE_NODES: readonly TestCaseNodeDecl[] = [
|
|
|
50
56
|
return {
|
|
51
57
|
kind: 'do',
|
|
52
58
|
command: `sungen triage ${flag(s)} ${s.unit}`,
|
|
53
|
-
because: `${cases.length} test case(s) failed in the latest run and are not classified yet${left ? ` (${left} left unclassified by the last triage — read its evidence and classify by hand)` : ''} — classify each (known defect, test defect, environment, product candidate) before exporting or extending: an unexplained red in the deliverable is a finding nobody has judged`,
|
|
59
|
+
because: `${cases.length} test case(s) failed in the latest run and are not classified yet${left ? ` (${left} left unclassified by the last triage — read its evidence and classify by hand: \`sungen triage ${flag(s)} ${s.unit} --case <id> --as product|test-defect|environment --reason "…"\`; test-defect and environment are a human's call, on a terminal)` : ''} — classify each (known defect, test defect, environment, product candidate) before exporting or extending: an unexplained red in the deliverable is a finding nobody has judged`,
|
|
54
60
|
};
|
|
55
61
|
}, ['.sungen/triage/<unit>.json']),
|
|
56
62
|
caseNode('tc.product-candidate', 'product-candidate', 134, 'Decide on the product-defect candidates', 'human', (s, cases, single) => ({
|
|
@@ -58,6 +64,18 @@ export const TEST_CASE_NODES: readonly TestCaseNodeDecl[] = [
|
|
|
58
64
|
command: `sungen defect draft ${flag(s)} ${s.unit}${single ? ` --scenario ${cases[0].id}` : ''}`,
|
|
59
65
|
because: `${cases.length} red test case(s) look like a product defect (${listed(cases)}) — the app answered and the answer differs from the oracle. A QA decides whether it is a defect to record; an agent presents it and does not draft it on its own`,
|
|
60
66
|
}), ['qa/defects/DEF-<n>-<slug>.md']),
|
|
67
|
+
// decide, not do: an agent re-running into an environment that is still down gets the same red and
|
|
68
|
+
// the same step again — a loop with no stop. A human confirms the environment first (#928 review W3).
|
|
69
|
+
caseNode('tc.rerun-environment', 'rerun-environment', 139, 'Re-run the cases the environment failed, once it is up', 'human', (s, cases) => ({
|
|
70
|
+
kind: 'decide',
|
|
71
|
+
command: `/sungen:run-test ${s.unit}${idsArg(cases)}`,
|
|
72
|
+
because: `${cases.length} red test case(s) failed on the environment, not the app (${withRule(cases)}) — the run never reached a usable app, so their results say nothing about it yet. A human confirms the app, base URL, network, auth state or device is up, then they are re-run; an agent presents this and does not re-run on its own — into an environment that is still down, the same red comes back`,
|
|
73
|
+
}), ['specs/generated/<unit>/<unit>-test-result.json']),
|
|
74
|
+
caseNode('tc.fix-test', 'fix-test', 139.5, 'Fix the tests triaged as test defects', 'ai', (s, cases) => ({
|
|
75
|
+
kind: 'do',
|
|
76
|
+
command: `/sungen:run-test ${s.unit}${idsArg(cases)}`,
|
|
77
|
+
because: `${cases.length} red test case(s) are test defects (${withRule(cases)}) — the test, not the app, failed: fix its selector, test data or step (run-test's auto-fix reads the triage evidence), then re-run them; never weaken an assertion to make one pass`,
|
|
78
|
+
}), ['selectors/<unit>.yaml', 'specs/generated/<unit>/<unit>-test-result.json']),
|
|
61
79
|
];
|
|
62
80
|
|
|
63
81
|
/** Red final attempts the test cases account for (a case read without the count stands for one). */
|