@sun-asterisk/sungen 3.2.2-beta.5 → 3.2.2-beta.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/journey.d.ts +3 -0
- package/dist/cli/commands/journey.d.ts.map +1 -0
- package/dist/cli/commands/journey.js +88 -0
- package/dist/cli/commands/journey.js.map +1 -0
- package/dist/cli/index.js +2 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/harness/journey.d.ts +21 -0
- package/dist/harness/journey.d.ts.map +1 -0
- package/dist/harness/journey.js +176 -0
- package/dist/harness/journey.js.map +1 -0
- package/package.json +3 -3
- package/src/cli/commands/journey.ts +51 -0
- package/src/cli/index.ts +2 -0
- package/src/harness/journey.ts +152 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"journey.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/journey.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAgBpC,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAkC7D"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.registerJourneyCommand = registerJourneyCommand;
|
|
37
|
+
const path = __importStar(require("path"));
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
39
|
+
const journey_1 = require("../../harness/journey");
|
|
40
|
+
const unit_paths_1 = require("../../harness/unit-paths");
|
|
41
|
+
function findScreenDir(name) {
|
|
42
|
+
const candidates = [
|
|
43
|
+
path.join(process.cwd(), 'qa', 'screens', name),
|
|
44
|
+
path.join(process.cwd(), 'qa', 'flows', name),
|
|
45
|
+
path.join(process.cwd(), 'qa', 'api', name),
|
|
46
|
+
];
|
|
47
|
+
for (const c of candidates)
|
|
48
|
+
if (fs.existsSync(c))
|
|
49
|
+
return c;
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
function registerJourneyCommand(program) {
|
|
53
|
+
program
|
|
54
|
+
.command('journey')
|
|
55
|
+
.description('Durable "you are here" board (#381): obligations + what-to-review + next, synthesised read-only from the audit report + ledger already on disk.')
|
|
56
|
+
.option('-s, --screen <name>', 'Screen / flow / api unit name')
|
|
57
|
+
.option('--json', 'Output the raw JSON report')
|
|
58
|
+
.action((options) => {
|
|
59
|
+
try {
|
|
60
|
+
const name = options.screen;
|
|
61
|
+
if (!name)
|
|
62
|
+
throw new Error('Provide --screen <name>');
|
|
63
|
+
if (!findScreenDir(name))
|
|
64
|
+
throw new Error(`Not found: qa/screens/${name}, qa/flows/${name}, or qa/api/${name}`);
|
|
65
|
+
const report = (0, journey_1.runJourney)(process.cwd(), name);
|
|
66
|
+
const outDir = path.join(process.cwd(), '.sungen', 'journey');
|
|
67
|
+
fs.mkdirSync(outDir, { recursive: true });
|
|
68
|
+
const slug = (0, unit_paths_1.reportSlug)(name);
|
|
69
|
+
fs.writeFileSync(path.join(outDir, `${slug}.json`), JSON.stringify(report, null, 2), 'utf-8');
|
|
70
|
+
const board = (0, journey_1.renderJourneyBoard)(report);
|
|
71
|
+
fs.writeFileSync(path.join(outDir, `${slug}.board.md`), board, 'utf-8');
|
|
72
|
+
if (options.json) {
|
|
73
|
+
console.log(JSON.stringify(report, null, 2));
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
console.log('');
|
|
77
|
+
console.log(board);
|
|
78
|
+
console.log(` Board: ${path.relative(process.cwd(), path.join(outDir, `${slug}.board.md`))}`);
|
|
79
|
+
console.log('');
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
console.error('Error:', error instanceof Error ? error.message : error);
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=journey.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"journey.js","sourceRoot":"","sources":["../../../src/cli/commands/journey.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/index.js
CHANGED
|
@@ -17,6 +17,7 @@ const add_flow_1 = require("./commands/add-flow");
|
|
|
17
17
|
const dashboard_1 = require("./commands/dashboard");
|
|
18
18
|
const audit_1 = require("./commands/audit");
|
|
19
19
|
const depth_lint_1 = require("./commands/depth-lint");
|
|
20
|
+
const journey_1 = require("./commands/journey");
|
|
20
21
|
const ingest_1 = require("./commands/ingest");
|
|
21
22
|
const eval_1 = require("./commands/eval");
|
|
22
23
|
const manifest_1 = require("./commands/manifest");
|
|
@@ -55,6 +56,7 @@ async function main() {
|
|
|
55
56
|
(0, dashboard_1.registerDashboardCommand)(program);
|
|
56
57
|
(0, audit_1.registerAuditCommand)(program);
|
|
57
58
|
(0, depth_lint_1.registerDepthLintCommand)(program);
|
|
59
|
+
(0, journey_1.registerJourneyCommand)(program);
|
|
58
60
|
(0, manifest_1.registerManifestCommand)(program);
|
|
59
61
|
(0, ledger_1.registerLedgerCommand)(program);
|
|
60
62
|
(0, feedback_1.registerFeedbackCommand)(program);
|
package/dist/cli/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export type ObStatus = 'satisfied' | 'needs-work' | 'pending';
|
|
2
|
+
export interface Obligation {
|
|
3
|
+
id: string;
|
|
4
|
+
title: string;
|
|
5
|
+
status: ObStatus;
|
|
6
|
+
detail: string;
|
|
7
|
+
}
|
|
8
|
+
export interface JourneyReport {
|
|
9
|
+
unit: string;
|
|
10
|
+
generatedFrom: string[];
|
|
11
|
+
youAreHere: string;
|
|
12
|
+
phasesDone: string[];
|
|
13
|
+
gateStatus: string | null;
|
|
14
|
+
score: number | null;
|
|
15
|
+
obligations: Obligation[];
|
|
16
|
+
needsYou: string[];
|
|
17
|
+
nextSuggested: string;
|
|
18
|
+
}
|
|
19
|
+
export declare function runJourney(projectRoot: string, unit: string): JourneyReport;
|
|
20
|
+
export declare function renderJourneyBoard(r: JourneyReport): string;
|
|
21
|
+
//# sourceMappingURL=journey.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"journey.d.ts","sourceRoot":"","sources":["../../src/harness/journey.ts"],"names":[],"mappings":"AAiBA,MAAM,MAAM,QAAQ,GAAG,WAAW,GAAG,YAAY,GAAG,SAAS,CAAC;AAE9D,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,QAAQ,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;CACvB;AAuBD,wBAAgB,UAAU,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,aAAa,CAsE3E;AAID,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,aAAa,GAAG,MAAM,CAkB3D"}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.runJourney = runJourney;
|
|
37
|
+
exports.renderJourneyBoard = renderJourneyBoard;
|
|
38
|
+
/**
|
|
39
|
+
* Journey board (epic #381, story S1) — the durable, read-only "you are here" view.
|
|
40
|
+
*
|
|
41
|
+
* Reconstructs the QA's position from artifacts ALREADY on disk (no recomputation, no context
|
|
42
|
+
* needed): the audit report (`.sungen/reports/<slug>-audit.json`) supplies the obligation status
|
|
43
|
+
* via its calibration axes + gate + findings; the ledger (`.sungen/ledger/<slug>.jsonl`) supplies
|
|
44
|
+
* the phase history ("you are here"). The output answers the three QA questions — what's next /
|
|
45
|
+
* what to review / what's doubtful — and persists `.sungen/journey/<slug>.{json,board.md}`.
|
|
46
|
+
*
|
|
47
|
+
* S1 is READ-ONLY (a synthesis over existing data). The writable obligation lifecycle
|
|
48
|
+
* (waive / reconcile / auto-close) is S2; gate-bound predicates + inter-phase gates are S3.
|
|
49
|
+
* Pure-deterministic, no AI.
|
|
50
|
+
*/
|
|
51
|
+
const fs = __importStar(require("fs"));
|
|
52
|
+
const path = __importStar(require("path"));
|
|
53
|
+
const unit_paths_1 = require("./unit-paths");
|
|
54
|
+
function readJSON(p) {
|
|
55
|
+
try {
|
|
56
|
+
return fs.existsSync(p) ? JSON.parse(fs.readFileSync(p, 'utf-8')) : null;
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function readLedgerPhases(p) {
|
|
63
|
+
if (!fs.existsSync(p))
|
|
64
|
+
return [];
|
|
65
|
+
const out = [];
|
|
66
|
+
for (const line of fs.readFileSync(p, 'utf-8').split('\n')) {
|
|
67
|
+
if (!line.trim())
|
|
68
|
+
continue;
|
|
69
|
+
try {
|
|
70
|
+
const d = JSON.parse(line);
|
|
71
|
+
if (d.step)
|
|
72
|
+
out.push(String(d.step));
|
|
73
|
+
}
|
|
74
|
+
catch { /* skip */ }
|
|
75
|
+
}
|
|
76
|
+
return out;
|
|
77
|
+
}
|
|
78
|
+
/** Findings that ask for HUMAN judgment (the review-queue seed), vs pure machine-repair targets. */
|
|
79
|
+
function isHumanFinding(f) {
|
|
80
|
+
return /@manual|MANUAL-|DEPTH-DEFERRED|UNSOURCEABLE|CAPABILITY-SUGGESTION|judgment|oracle|review/i.test(f);
|
|
81
|
+
}
|
|
82
|
+
const SAT = 0.8; // axis at/above this = satisfied (below = needs-work)
|
|
83
|
+
function runJourney(projectRoot, unit) {
|
|
84
|
+
const slug = (0, unit_paths_1.reportSlug)(unit);
|
|
85
|
+
const audit = readJSON(path.join(projectRoot, '.sungen', 'reports', `${slug}-audit.json`));
|
|
86
|
+
const phases = readLedgerPhases(path.join(projectRoot, '.sungen', 'ledger', `${slug}.jsonl`));
|
|
87
|
+
const from = [];
|
|
88
|
+
if (audit)
|
|
89
|
+
from.push('audit');
|
|
90
|
+
if (phases.length)
|
|
91
|
+
from.push('ledger');
|
|
92
|
+
const youAreHere = phases.length ? phases[phases.length - 1] : 'not started';
|
|
93
|
+
const ran = (p) => phases.some((s) => s === p || s.startsWith(p));
|
|
94
|
+
const obligations = [];
|
|
95
|
+
const needsYou = [];
|
|
96
|
+
if (!audit) {
|
|
97
|
+
// Nothing measured yet — the journey hasn't really begun.
|
|
98
|
+
obligations.push({ id: 'OB-create', title: 'Generate test cases', status: 'pending', detail: 'No audit report yet — run /sungen:create-test.' });
|
|
99
|
+
return {
|
|
100
|
+
unit, generatedFrom: from, youAreHere, phasesDone: phases, gateStatus: null, score: null,
|
|
101
|
+
obligations, needsYou, nextSuggested: 'Run `/sungen:create-test ' + unit + '` to begin.',
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
const ax = (audit.calibration && audit.calibration.axes) || {};
|
|
105
|
+
const depthThreshold = (audit.depth && audit.depth.threshold) || SAT;
|
|
106
|
+
const ob = (id, title, val, thr, detail) => ({
|
|
107
|
+
id, title,
|
|
108
|
+
status: val === undefined ? 'pending' : val >= thr ? 'satisfied' : 'needs-work',
|
|
109
|
+
detail: val === undefined ? detail : `${Math.round(val * 100)}% (need ${Math.round(thr * 100)}%) — ${detail}`,
|
|
110
|
+
});
|
|
111
|
+
obligations.push(ob('OB-spec', 'Spec readiness', ax.specFR, 1, 'FR/sections sufficient to design from'));
|
|
112
|
+
obligations.push(ob('OB-coverage', 'Viewpoint coverage', ax.coverage, SAT, 'critical themes covered'));
|
|
113
|
+
obligations.push(ob('OB-depth', 'Assertion depth', ax.businessDepth, depthThreshold, 'business-critical scenarios assert data'));
|
|
114
|
+
obligations.push(ob('OB-trace', 'Traceability', ax.taxonomy ?? ax.traceability, SAT, 'scenarios trace to viewpoints'));
|
|
115
|
+
// Automation obligation: automatable @manual still pending a driver = needs-work.
|
|
116
|
+
const autoPending = audit.automatableManual && audit.automatableManual.automatable > 0;
|
|
117
|
+
obligations.push({
|
|
118
|
+
id: 'OB-automation', title: 'Automation coverage',
|
|
119
|
+
status: autoPending ? 'needs-work' : 'satisfied',
|
|
120
|
+
detail: autoPending ? `${audit.automatableManual.automatable} @manual scenario(s) a driver could automate` : 'no capability-pending automatable cases',
|
|
121
|
+
});
|
|
122
|
+
// Human sign-off: always pending until S5 explicit signoff; carries the review-queue count.
|
|
123
|
+
const manualCount = (audit.manualOracle && audit.manualOracle.total) || 0;
|
|
124
|
+
obligations.push({
|
|
125
|
+
id: 'OB-signoff', title: 'Human sign-off', status: 'pending',
|
|
126
|
+
detail: `${needsYou.length} item(s) queued for your review (single sign-off at the end)`,
|
|
127
|
+
});
|
|
128
|
+
// Needs-you: human-judgment findings (the review-queue seed), with their anchors (titles) intact.
|
|
129
|
+
for (const f of (audit.findings || []))
|
|
130
|
+
if (isHumanFinding(f))
|
|
131
|
+
needsYou.push(f);
|
|
132
|
+
// Re-stamp the signoff detail now that needsYou is filled.
|
|
133
|
+
const signoff = obligations.find((o) => o.id === 'OB-signoff');
|
|
134
|
+
if (signoff)
|
|
135
|
+
signoff.detail = `${needsYou.length} item(s) queued for your review · ${manualCount} @manual scenario(s)`;
|
|
136
|
+
// Next suggested = first non-satisfied obligation; if all satisfied but run-test not done → run.
|
|
137
|
+
const firstGap = obligations.find((o) => o.status !== 'satisfied' && o.id !== 'OB-signoff');
|
|
138
|
+
let nextSuggested;
|
|
139
|
+
if (firstGap)
|
|
140
|
+
nextSuggested = `Repair "${firstGap.title}" (${firstGap.detail}).`;
|
|
141
|
+
else if (!ran('run'))
|
|
142
|
+
nextSuggested = `Quality satisfied — run \`/sungen:run-test ${unit}\`.`;
|
|
143
|
+
else
|
|
144
|
+
nextSuggested = `All obligations satisfied — review the ${needsYou.length} queued item(s), then sign off & deliver.`;
|
|
145
|
+
return {
|
|
146
|
+
unit, generatedFrom: from, youAreHere, phasesDone: phases,
|
|
147
|
+
gateStatus: audit.gateStatus ?? null, score: (audit.score && audit.score.overall) ?? null,
|
|
148
|
+
obligations, needsYou, nextSuggested,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
const ICON = { satisfied: '✅', 'needs-work': '⚠️ ', pending: '⏳' };
|
|
152
|
+
function renderJourneyBoard(r) {
|
|
153
|
+
const L = [];
|
|
154
|
+
L.push(`# Journey — ${r.unit}`);
|
|
155
|
+
L.push('');
|
|
156
|
+
L.push(`📍 **You are here:** ${r.youAreHere}` + (r.phasesDone.length ? ` (phases: ${r.phasesDone.join(' → ')})` : ''));
|
|
157
|
+
if (r.gateStatus)
|
|
158
|
+
L.push(` gate: **${r.gateStatus}** · score: ${r.score ?? '—'}/10`);
|
|
159
|
+
L.push('');
|
|
160
|
+
L.push('## What must still be true');
|
|
161
|
+
for (const o of r.obligations)
|
|
162
|
+
L.push(`- ${ICON[o.status]} **${o.title}** — ${o.detail}`);
|
|
163
|
+
L.push('');
|
|
164
|
+
L.push(`## 🔎 Needs your judgment (${r.needsYou.length})`);
|
|
165
|
+
if (r.needsYou.length)
|
|
166
|
+
for (const f of r.needsYou.slice(0, 20))
|
|
167
|
+
L.push(`- ${f}`);
|
|
168
|
+
else
|
|
169
|
+
L.push('- (nothing queued)');
|
|
170
|
+
L.push('');
|
|
171
|
+
L.push(`## → Next`);
|
|
172
|
+
L.push(`${r.nextSuggested}`);
|
|
173
|
+
L.push('');
|
|
174
|
+
return L.join('\n');
|
|
175
|
+
}
|
|
176
|
+
//# sourceMappingURL=journey.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"journey.js","sourceRoot":"","sources":["../../src/harness/journey.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sun-asterisk/sungen",
|
|
3
|
-
"version": "3.2.2-beta.
|
|
3
|
+
"version": "3.2.2-beta.6",
|
|
4
4
|
"description": "Deterministic E2E Test Compiler - Gherkin + Selectors → Playwright tests",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"types": "src/index.ts",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"copy-templates": "mkdir -p dist/generators/test-generator/adapters/playwright/templates/steps && mkdir -p dist/generators/test-generator/templates && mkdir -p dist/orchestrator/templates && mkdir -p dist/dashboard/templates && cp -r src/generators/test-generator/adapters/playwright/templates/*.hbs dist/generators/test-generator/adapters/playwright/templates/ 2>/dev/null || true && cp -r src/generators/test-generator/adapters/playwright/templates/steps dist/generators/test-generator/adapters/playwright/templates/ && mkdir -p dist/generators/test-generator/adapters/appium/templates/steps && cp -r src/generators/test-generator/adapters/appium/templates/*.hbs dist/generators/test-generator/adapters/appium/templates/ 2>/dev/null || true && cp -r src/generators/test-generator/adapters/appium/templates/steps dist/generators/test-generator/adapters/appium/templates/ && cp src/generators/test-generator/templates/*.hbs dist/generators/test-generator/templates/ 2>/dev/null || true && cp -r src/orchestrator/templates/* dist/orchestrator/templates/ && cp src/dashboard/templates/index.html dist/dashboard/templates/index.html && mkdir -p dist/harness/catalog && cp src/harness/catalog/*.yaml dist/harness/catalog/",
|
|
13
13
|
"build:dashboard": "cd ../../dashboard && npm install --silent && npm run build && cd - && cp ../../dashboard/dist/index.html src/dashboard/templates/index.html",
|
|
14
14
|
"dev": "tsx src/cli/index.ts",
|
|
15
|
-
"test": "tsx tests/golden/run.ts && tsx tests/audit/run.ts && tsx tests/ingest/run.ts && tsx tests/eval/run.ts && tsx tests/exporter/run.ts && tsx tests/exporter/feature-parser-category.run.ts && tsx tests/exporter/api-detail-sheet.run.ts && tsx tests/api-runtime/base-path-url-join.run.ts && tsx tests/capabilities/run.ts && tsx tests/openapi/run.ts && tsx tests/packaging/run.ts",
|
|
15
|
+
"test": "tsx tests/golden/run.ts && tsx tests/audit/run.ts && tsx tests/ingest/run.ts && tsx tests/eval/run.ts && tsx tests/exporter/run.ts && tsx tests/exporter/feature-parser-category.run.ts && tsx tests/exporter/api-detail-sheet.run.ts && tsx tests/api-runtime/base-path-url-join.run.ts && tsx tests/capabilities/run.ts && tsx tests/openapi/run.ts && tsx tests/packaging/run.ts && tsx tests/journey/run.ts",
|
|
16
16
|
"test:update": "tsx tests/golden/run.ts --update && tsx tests/audit/run.ts --update && tsx tests/ingest/run.ts --update",
|
|
17
17
|
"prepublishOnly": "npm run build:dashboard && npm run build"
|
|
18
18
|
},
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"node": ">=18.0.0"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@sungen/driver-ui": "3.2.2-beta.
|
|
36
|
+
"@sungen/driver-ui": "3.2.2-beta.6",
|
|
37
37
|
"@anthropic-ai/sdk": "^0.71.0",
|
|
38
38
|
"@babel/parser": "^7.28.5",
|
|
39
39
|
"@babel/traverse": "^7.28.5",
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as fs from 'fs';
|
|
4
|
+
import { runJourney, renderJourneyBoard } from '../../harness/journey';
|
|
5
|
+
import { reportSlug } from '../../harness/unit-paths';
|
|
6
|
+
|
|
7
|
+
function findScreenDir(name: string): string | null {
|
|
8
|
+
const candidates = [
|
|
9
|
+
path.join(process.cwd(), 'qa', 'screens', name),
|
|
10
|
+
path.join(process.cwd(), 'qa', 'flows', name),
|
|
11
|
+
path.join(process.cwd(), 'qa', 'api', name),
|
|
12
|
+
];
|
|
13
|
+
for (const c of candidates) if (fs.existsSync(c)) return c;
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function registerJourneyCommand(program: Command): void {
|
|
18
|
+
program
|
|
19
|
+
.command('journey')
|
|
20
|
+
.description('Durable "you are here" board (#381): obligations + what-to-review + next, synthesised read-only from the audit report + ledger already on disk.')
|
|
21
|
+
.option('-s, --screen <name>', 'Screen / flow / api unit name')
|
|
22
|
+
.option('--json', 'Output the raw JSON report')
|
|
23
|
+
.action((options) => {
|
|
24
|
+
try {
|
|
25
|
+
const name = options.screen;
|
|
26
|
+
if (!name) throw new Error('Provide --screen <name>');
|
|
27
|
+
if (!findScreenDir(name)) throw new Error(`Not found: qa/screens/${name}, qa/flows/${name}, or qa/api/${name}`);
|
|
28
|
+
|
|
29
|
+
const report = runJourney(process.cwd(), name);
|
|
30
|
+
|
|
31
|
+
const outDir = path.join(process.cwd(), '.sungen', 'journey');
|
|
32
|
+
fs.mkdirSync(outDir, { recursive: true });
|
|
33
|
+
const slug = reportSlug(name);
|
|
34
|
+
fs.writeFileSync(path.join(outDir, `${slug}.json`), JSON.stringify(report, null, 2), 'utf-8');
|
|
35
|
+
const board = renderJourneyBoard(report);
|
|
36
|
+
fs.writeFileSync(path.join(outDir, `${slug}.board.md`), board, 'utf-8');
|
|
37
|
+
|
|
38
|
+
if (options.json) {
|
|
39
|
+
console.log(JSON.stringify(report, null, 2));
|
|
40
|
+
} else {
|
|
41
|
+
console.log('');
|
|
42
|
+
console.log(board);
|
|
43
|
+
console.log(` Board: ${path.relative(process.cwd(), path.join(outDir, `${slug}.board.md`))}`);
|
|
44
|
+
console.log('');
|
|
45
|
+
}
|
|
46
|
+
} catch (error) {
|
|
47
|
+
console.error('Error:', error instanceof Error ? error.message : error);
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
}
|
package/src/cli/index.ts
CHANGED
|
@@ -16,6 +16,7 @@ import { registerAddFlowCommand } from './commands/add-flow';
|
|
|
16
16
|
import { registerDashboardCommand } from './commands/dashboard';
|
|
17
17
|
import { registerAuditCommand } from './commands/audit';
|
|
18
18
|
import { registerDepthLintCommand } from './commands/depth-lint';
|
|
19
|
+
import { registerJourneyCommand } from './commands/journey';
|
|
19
20
|
import { registerIngestCommand } from './commands/ingest';
|
|
20
21
|
import { registerEvalCommand } from './commands/eval';
|
|
21
22
|
import { registerManifestCommand } from './commands/manifest';
|
|
@@ -59,6 +60,7 @@ async function main() {
|
|
|
59
60
|
registerDashboardCommand(program);
|
|
60
61
|
registerAuditCommand(program);
|
|
61
62
|
registerDepthLintCommand(program);
|
|
63
|
+
registerJourneyCommand(program);
|
|
62
64
|
registerManifestCommand(program);
|
|
63
65
|
registerLedgerCommand(program);
|
|
64
66
|
registerFeedbackCommand(program);
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Journey board (epic #381, story S1) — the durable, read-only "you are here" view.
|
|
3
|
+
*
|
|
4
|
+
* Reconstructs the QA's position from artifacts ALREADY on disk (no recomputation, no context
|
|
5
|
+
* needed): the audit report (`.sungen/reports/<slug>-audit.json`) supplies the obligation status
|
|
6
|
+
* via its calibration axes + gate + findings; the ledger (`.sungen/ledger/<slug>.jsonl`) supplies
|
|
7
|
+
* the phase history ("you are here"). The output answers the three QA questions — what's next /
|
|
8
|
+
* what to review / what's doubtful — and persists `.sungen/journey/<slug>.{json,board.md}`.
|
|
9
|
+
*
|
|
10
|
+
* S1 is READ-ONLY (a synthesis over existing data). The writable obligation lifecycle
|
|
11
|
+
* (waive / reconcile / auto-close) is S2; gate-bound predicates + inter-phase gates are S3.
|
|
12
|
+
* Pure-deterministic, no AI.
|
|
13
|
+
*/
|
|
14
|
+
import * as fs from 'fs';
|
|
15
|
+
import * as path from 'path';
|
|
16
|
+
import { reportSlug } from './unit-paths';
|
|
17
|
+
|
|
18
|
+
export type ObStatus = 'satisfied' | 'needs-work' | 'pending';
|
|
19
|
+
|
|
20
|
+
export interface Obligation {
|
|
21
|
+
id: string;
|
|
22
|
+
title: string;
|
|
23
|
+
status: ObStatus;
|
|
24
|
+
detail: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface JourneyReport {
|
|
28
|
+
unit: string;
|
|
29
|
+
generatedFrom: string[]; // which artifacts were available
|
|
30
|
+
youAreHere: string; // last ledger phase, or 'not started'
|
|
31
|
+
phasesDone: string[];
|
|
32
|
+
gateStatus: string | null;
|
|
33
|
+
score: number | null;
|
|
34
|
+
obligations: Obligation[];
|
|
35
|
+
needsYou: string[]; // located findings / human-judgment items (the review queue seed)
|
|
36
|
+
nextSuggested: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function readJSON(p: string): any {
|
|
40
|
+
try { return fs.existsSync(p) ? JSON.parse(fs.readFileSync(p, 'utf-8')) : null; } catch { return null; }
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function readLedgerPhases(p: string): string[] {
|
|
44
|
+
if (!fs.existsSync(p)) return [];
|
|
45
|
+
const out: string[] = [];
|
|
46
|
+
for (const line of fs.readFileSync(p, 'utf-8').split('\n')) {
|
|
47
|
+
if (!line.trim()) continue;
|
|
48
|
+
try { const d = JSON.parse(line); if (d.step) out.push(String(d.step)); } catch { /* skip */ }
|
|
49
|
+
}
|
|
50
|
+
return out;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Findings that ask for HUMAN judgment (the review-queue seed), vs pure machine-repair targets. */
|
|
54
|
+
function isHumanFinding(f: string): boolean {
|
|
55
|
+
return /@manual|MANUAL-|DEPTH-DEFERRED|UNSOURCEABLE|CAPABILITY-SUGGESTION|judgment|oracle|review/i.test(f);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const SAT = 0.8; // axis at/above this = satisfied (below = needs-work)
|
|
59
|
+
|
|
60
|
+
export function runJourney(projectRoot: string, unit: string): JourneyReport {
|
|
61
|
+
const slug = reportSlug(unit);
|
|
62
|
+
const audit = readJSON(path.join(projectRoot, '.sungen', 'reports', `${slug}-audit.json`));
|
|
63
|
+
const phases = readLedgerPhases(path.join(projectRoot, '.sungen', 'ledger', `${slug}.jsonl`));
|
|
64
|
+
|
|
65
|
+
const from: string[] = [];
|
|
66
|
+
if (audit) from.push('audit');
|
|
67
|
+
if (phases.length) from.push('ledger');
|
|
68
|
+
|
|
69
|
+
const youAreHere = phases.length ? phases[phases.length - 1] : 'not started';
|
|
70
|
+
const ran = (p: string) => phases.some((s) => s === p || s.startsWith(p));
|
|
71
|
+
|
|
72
|
+
const obligations: Obligation[] = [];
|
|
73
|
+
const needsYou: string[] = [];
|
|
74
|
+
|
|
75
|
+
if (!audit) {
|
|
76
|
+
// Nothing measured yet — the journey hasn't really begun.
|
|
77
|
+
obligations.push({ id: 'OB-create', title: 'Generate test cases', status: 'pending', detail: 'No audit report yet — run /sungen:create-test.' });
|
|
78
|
+
return {
|
|
79
|
+
unit, generatedFrom: from, youAreHere, phasesDone: phases, gateStatus: null, score: null,
|
|
80
|
+
obligations, needsYou, nextSuggested: 'Run `/sungen:create-test ' + unit + '` to begin.',
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const ax = (audit.calibration && audit.calibration.axes) || {};
|
|
85
|
+
const depthThreshold = (audit.depth && audit.depth.threshold) || SAT;
|
|
86
|
+
const ob = (id: string, title: string, val: number | undefined, thr: number, detail: string): Obligation => ({
|
|
87
|
+
id, title,
|
|
88
|
+
status: val === undefined ? 'pending' : val >= thr ? 'satisfied' : 'needs-work',
|
|
89
|
+
detail: val === undefined ? detail : `${Math.round(val * 100)}% (need ${Math.round(thr * 100)}%) — ${detail}`,
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
obligations.push(ob('OB-spec', 'Spec readiness', ax.specFR, 1, 'FR/sections sufficient to design from'));
|
|
93
|
+
obligations.push(ob('OB-coverage', 'Viewpoint coverage', ax.coverage, SAT, 'critical themes covered'));
|
|
94
|
+
obligations.push(ob('OB-depth', 'Assertion depth', ax.businessDepth, depthThreshold, 'business-critical scenarios assert data'));
|
|
95
|
+
obligations.push(ob('OB-trace', 'Traceability', ax.taxonomy ?? ax.traceability, SAT, 'scenarios trace to viewpoints'));
|
|
96
|
+
|
|
97
|
+
// Automation obligation: automatable @manual still pending a driver = needs-work.
|
|
98
|
+
const autoPending = audit.automatableManual && audit.automatableManual.automatable > 0;
|
|
99
|
+
obligations.push({
|
|
100
|
+
id: 'OB-automation', title: 'Automation coverage',
|
|
101
|
+
status: autoPending ? 'needs-work' : 'satisfied',
|
|
102
|
+
detail: autoPending ? `${audit.automatableManual.automatable} @manual scenario(s) a driver could automate` : 'no capability-pending automatable cases',
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
// Human sign-off: always pending until S5 explicit signoff; carries the review-queue count.
|
|
106
|
+
const manualCount = (audit.manualOracle && audit.manualOracle.total) || 0;
|
|
107
|
+
obligations.push({
|
|
108
|
+
id: 'OB-signoff', title: 'Human sign-off', status: 'pending',
|
|
109
|
+
detail: `${needsYou.length} item(s) queued for your review (single sign-off at the end)`,
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// Needs-you: human-judgment findings (the review-queue seed), with their anchors (titles) intact.
|
|
113
|
+
for (const f of (audit.findings || [])) if (isHumanFinding(f)) needsYou.push(f);
|
|
114
|
+
// Re-stamp the signoff detail now that needsYou is filled.
|
|
115
|
+
const signoff = obligations.find((o) => o.id === 'OB-signoff');
|
|
116
|
+
if (signoff) signoff.detail = `${needsYou.length} item(s) queued for your review · ${manualCount} @manual scenario(s)`;
|
|
117
|
+
|
|
118
|
+
// Next suggested = first non-satisfied obligation; if all satisfied but run-test not done → run.
|
|
119
|
+
const firstGap = obligations.find((o) => o.status !== 'satisfied' && o.id !== 'OB-signoff');
|
|
120
|
+
let nextSuggested: string;
|
|
121
|
+
if (firstGap) nextSuggested = `Repair "${firstGap.title}" (${firstGap.detail}).`;
|
|
122
|
+
else if (!ran('run')) nextSuggested = `Quality satisfied — run \`/sungen:run-test ${unit}\`.`;
|
|
123
|
+
else nextSuggested = `All obligations satisfied — review the ${needsYou.length} queued item(s), then sign off & deliver.`;
|
|
124
|
+
|
|
125
|
+
return {
|
|
126
|
+
unit, generatedFrom: from, youAreHere, phasesDone: phases,
|
|
127
|
+
gateStatus: audit.gateStatus ?? null, score: (audit.score && audit.score.overall) ?? null,
|
|
128
|
+
obligations, needsYou, nextSuggested,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const ICON: Record<ObStatus, string> = { satisfied: '✅', 'needs-work': '⚠️ ', pending: '⏳' };
|
|
133
|
+
|
|
134
|
+
export function renderJourneyBoard(r: JourneyReport): string {
|
|
135
|
+
const L: string[] = [];
|
|
136
|
+
L.push(`# Journey — ${r.unit}`);
|
|
137
|
+
L.push('');
|
|
138
|
+
L.push(`📍 **You are here:** ${r.youAreHere}` + (r.phasesDone.length ? ` (phases: ${r.phasesDone.join(' → ')})` : ''));
|
|
139
|
+
if (r.gateStatus) L.push(` gate: **${r.gateStatus}** · score: ${r.score ?? '—'}/10`);
|
|
140
|
+
L.push('');
|
|
141
|
+
L.push('## What must still be true');
|
|
142
|
+
for (const o of r.obligations) L.push(`- ${ICON[o.status]} **${o.title}** — ${o.detail}`);
|
|
143
|
+
L.push('');
|
|
144
|
+
L.push(`## 🔎 Needs your judgment (${r.needsYou.length})`);
|
|
145
|
+
if (r.needsYou.length) for (const f of r.needsYou.slice(0, 20)) L.push(`- ${f}`);
|
|
146
|
+
else L.push('- (nothing queued)');
|
|
147
|
+
L.push('');
|
|
148
|
+
L.push(`## → Next`);
|
|
149
|
+
L.push(`${r.nextSuggested}`);
|
|
150
|
+
L.push('');
|
|
151
|
+
return L.join('\n');
|
|
152
|
+
}
|