@sun-asterisk/sungen 3.3.0-alpha.7 → 3.3.0-alpha.8

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.
Files changed (96) hide show
  1. package/dist/cli/case-triage-hint.d.ts +14 -0
  2. package/dist/cli/case-triage-hint.d.ts.map +1 -0
  3. package/dist/cli/case-triage-hint.js +23 -0
  4. package/dist/cli/case-triage-hint.js.map +1 -0
  5. package/dist/cli/commands/contract-derive-command.d.ts.map +1 -1
  6. package/dist/cli/commands/contract-derive-command.js +2 -1
  7. package/dist/cli/commands/contract-derive-command.js.map +1 -1
  8. package/dist/cli/commands/contract.d.ts.map +1 -1
  9. package/dist/cli/commands/contract.js +5 -2
  10. package/dist/cli/commands/contract.js.map +1 -1
  11. package/dist/cli/commands/delivery.d.ts.map +1 -1
  12. package/dist/cli/commands/delivery.js +29 -13
  13. package/dist/cli/commands/delivery.js.map +1 -1
  14. package/dist/cli/commands/gate.d.ts.map +1 -1
  15. package/dist/cli/commands/gate.js +2 -1
  16. package/dist/cli/commands/gate.js.map +1 -1
  17. package/dist/cli/commands/generate.d.ts.map +1 -1
  18. package/dist/cli/commands/generate.js +12 -2
  19. package/dist/cli/commands/generate.js.map +1 -1
  20. package/dist/cli/commands/journey.d.ts.map +1 -1
  21. package/dist/cli/commands/journey.js +4 -2
  22. package/dist/cli/commands/journey.js.map +1 -1
  23. package/dist/cli/commands/next.d.ts +4 -2
  24. package/dist/cli/commands/next.d.ts.map +1 -1
  25. package/dist/cli/commands/next.js +22 -6
  26. package/dist/cli/commands/next.js.map +1 -1
  27. package/dist/cli/commands/triage.d.ts.map +1 -1
  28. package/dist/cli/commands/triage.js +5 -3
  29. package/dist/cli/commands/triage.js.map +1 -1
  30. package/dist/cli/project-sungen-invocation.d.ts +6 -0
  31. package/dist/cli/project-sungen-invocation.d.ts.map +1 -1
  32. package/dist/cli/project-sungen-invocation.js +19 -0
  33. package/dist/cli/project-sungen-invocation.js.map +1 -1
  34. package/dist/cli/run-command-hints.d.ts +9 -0
  35. package/dist/cli/run-command-hints.d.ts.map +1 -0
  36. package/dist/cli/run-command-hints.js +84 -0
  37. package/dist/cli/run-command-hints.js.map +1 -0
  38. package/dist/cli/waiting-for-you-block.d.ts +9 -6
  39. package/dist/cli/waiting-for-you-block.d.ts.map +1 -1
  40. package/dist/cli/waiting-for-you-block.js +9 -6
  41. package/dist/cli/waiting-for-you-block.js.map +1 -1
  42. package/dist/exporters/csv-exporter.d.ts +11 -2
  43. package/dist/exporters/csv-exporter.d.ts.map +1 -1
  44. package/dist/exporters/csv-exporter.js +8 -4
  45. package/dist/exporters/csv-exporter.js.map +1 -1
  46. package/dist/exporters/matrix/export.d.ts +6 -1
  47. package/dist/exporters/matrix/export.d.ts.map +1 -1
  48. package/dist/exporters/matrix/export.js +11 -4
  49. package/dist/exporters/matrix/export.js.map +1 -1
  50. package/dist/exporters/xlsx-exporter.d.ts +3 -2
  51. package/dist/exporters/xlsx-exporter.d.ts.map +1 -1
  52. package/dist/exporters/xlsx-exporter.js +4 -4
  53. package/dist/exporters/xlsx-exporter.js.map +1 -1
  54. package/dist/graph/evaluate-graph.d.ts +3 -1
  55. package/dist/graph/evaluate-graph.d.ts.map +1 -1
  56. package/dist/graph/evaluate-graph.js +4 -2
  57. package/dist/graph/evaluate-graph.js.map +1 -1
  58. package/dist/graph/unit-artifact-probes.d.ts.map +1 -1
  59. package/dist/graph/unit-artifact-probes.js +3 -1
  60. package/dist/graph/unit-artifact-probes.js.map +1 -1
  61. package/dist/harness/gates/audit-report-verification.d.ts +2 -0
  62. package/dist/harness/gates/audit-report-verification.d.ts.map +1 -1
  63. package/dist/harness/gates/audit-report-verification.js +8 -3
  64. package/dist/harness/gates/audit-report-verification.js.map +1 -1
  65. package/dist/harness/next-step.d.ts +2 -0
  66. package/dist/harness/next-step.d.ts.map +1 -1
  67. package/dist/harness/next-step.js +2 -0
  68. package/dist/harness/next-step.js.map +1 -1
  69. package/dist/hooks/hand-back-core.d.ts +4 -1
  70. package/dist/hooks/hand-back-core.d.ts.map +1 -1
  71. package/dist/hooks/hand-back-core.js +7 -3
  72. package/dist/hooks/hand-back-core.js.map +1 -1
  73. package/dist/hooks/session-start-unit-state-hook.js +1 -1
  74. package/dist/hooks/session-start-unit-state-hook.js.map +1 -1
  75. package/package.json +3 -3
  76. package/src/cli/case-triage-hint.ts +31 -0
  77. package/src/cli/commands/contract-derive-command.ts +2 -1
  78. package/src/cli/commands/contract.ts +5 -2
  79. package/src/cli/commands/delivery.ts +29 -13
  80. package/src/cli/commands/gate.ts +2 -1
  81. package/src/cli/commands/generate.ts +11 -2
  82. package/src/cli/commands/journey.ts +4 -2
  83. package/src/cli/commands/next.ts +25 -9
  84. package/src/cli/commands/triage.ts +5 -3
  85. package/src/cli/project-sungen-invocation.ts +20 -0
  86. package/src/cli/run-command-hints.ts +47 -0
  87. package/src/cli/waiting-for-you-block.ts +14 -10
  88. package/src/exporters/csv-exporter.ts +12 -4
  89. package/src/exporters/matrix/export.ts +11 -4
  90. package/src/exporters/xlsx-exporter.ts +5 -4
  91. package/src/graph/evaluate-graph.ts +5 -3
  92. package/src/graph/unit-artifact-probes.ts +3 -1
  93. package/src/harness/gates/audit-report-verification.ts +7 -2
  94. package/src/harness/next-step.ts +3 -0
  95. package/src/hooks/hand-back-core.ts +8 -4
  96. package/src/hooks/session-start-unit-state-hook.ts +1 -1
@@ -98,9 +98,13 @@ function attachCliHandBack(argv, cwd) {
98
98
  }
99
99
  // --- the hook-shaped core -----------------------------------------------------------------------
100
100
  const MARK = { do: '→', blocked: '⛔', optional: '○', decide: '⚖' };
101
- /** `sungen next` rendered compactly for an agent's context; commands as `root` runs sungen. */
101
+ /**
102
+ * `sungen next` rendered compactly for an agent's context; commands — the step's own and those quoted in
103
+ * its reason (#966) — as `root` runs sungen, the way `sungen next` prints them.
104
+ */
102
105
  function formatSteps(steps, max = 4, root) {
103
- return steps.slice(0, max).map((st) => ` ${MARK[st.kind]} ${root ? (0, project_sungen_invocation_1.withProjectInvocation)(st.command, root) : st.command}${st.kind === 'decide' ? ' (a QA decision — ask, do not run)' : ''}\n ${st.because}`);
106
+ const asRun = (text) => (root ? (0, project_sungen_invocation_1.withProjectInvocationInText)(text, root) : text);
107
+ return steps.slice(0, max).map((st) => ` ${MARK[st.kind]} ${asRun(st.command)}${st.kind === 'decide' ? ' (a QA decision — ask, do not run)' : ''}\n ${asRun(st.because)}`);
104
108
  }
105
109
  /** The unit a session is on (a named one when it exists, else the nearest) and its derived state. */
106
110
  function locateUnitState(ctx, preferred) {
@@ -118,7 +122,7 @@ function run(input, ctx) {
118
122
  return {
119
123
  status: 'context',
120
124
  output: [`${handBackLine(found.unit.name, (0, project_sungen_invocation_1.sungenInvocation)(found.root))} — derived from what the project holds now:`, ...formatSteps(found.state.steps, 4, found.root),
121
- ...(0, waiting_for_you_block_1.waitingForYouLines)(found.state.humanActions, found.root, found.state.humanActionsHeldBy)].join('\n'),
125
+ ...(0, waiting_for_you_block_1.waitingForYouLines)(found.state.humanActions, found.root, found.state.humanActionsHeldBy, found.state.humanActionsHeldByCommand)].join('\n'),
122
126
  };
123
127
  }
124
128
  //# sourceMappingURL=hand-back-core.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"hand-back-core.js","sourceRoot":"","sources":["../../src/hooks/hand-back-core.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"hand-back-core.js","sourceRoot":"","sources":["../../src/hooks/hand-back-core.ts"],"names":[],"mappings":"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"}
@@ -50,7 +50,7 @@ exports.sessionStartUnitStateHook = {
50
50
  state.audit ? `Audit: ${state.audit.overall}/10 [${state.audit.gateStatus}] · ${state.audit.findings.length} finding(s).` : '',
51
51
  `Next (\`${sungen} next ${unit.name}\`, derived from the project state):`,
52
52
  ...(0, hand_back_core_1.formatSteps)(state.steps, 4, root),
53
- ...(0, waiting_for_you_block_1.waitingForYouLines)(state.humanActions, root, state.humanActionsHeldBy),
53
+ ...(0, waiting_for_you_block_1.waitingForYouLines)(state.humanActions, root, state.humanActionsHeldBy, state.humanActionsHeldByCommand),
54
54
  input.source === 'compact' ? 'Context was compacted: re-read the unit\'s files before acting on them.' : '',
55
55
  others > 0 ? `Working on another unit? Ask \`${sungen} next <unit>\` (or \`${sungen} next\` for all).` : '',
56
56
  ];
@@ -1 +1 @@
1
- {"version":3,"file":"session-start-unit-state-hook.js","sourceRoot":"","sources":["../../src/hooks/session-start-unit-state-hook.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"session-start-unit-state-hook.js","sourceRoot":"","sources":["../../src/hooks/session-start-unit-state-hook.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.3.0-alpha.7",
3
+ "version": "3.3.0-alpha.8",
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.7",
44
- "@sungen/driver-ui": "3.3.0-alpha.7",
43
+ "@sungen/driver-data-factory": "3.3.0-alpha.8",
44
+ "@sungen/driver-ui": "3.3.0-alpha.8",
45
45
  "chalk": "^5.6.2",
46
46
  "commander": "^14.0.2",
47
47
  "dotenv": "^17.2.3",
@@ -0,0 +1,31 @@
1
+ /**
2
+ * `sungen next <unit> --case <id>` for a red that needs triage (#966): how to classify it, as commands.
3
+ * When a blocking unit step (a stale audit, say) holds the frontier, the case's own triage step is not
4
+ * offered — the view said "needs triage" and named nothing to run. Now it lists, after the steps:
5
+ *
6
+ * held by a step above sungen triage (every red of the run) · /sungen:run-test --ids <id> (re-run, which
7
+ * triages its own run) · the by-hand `--case <id> --as …` form
8
+ * triage step offered the by-hand form only (the step above already names `sungen triage`)
9
+ * Commands as this project runs sungen (#939, #963).
10
+ */
11
+ import type { TestCaseState } from '../graph/test-case-state-shape';
12
+ import type { NextStep, UnitState } from '../harness/next-step';
13
+ import { withProjectInvocation } from './project-sungen-invocation';
14
+
15
+ export function caseTriageLines(s: UnitState, tc: TestCaseState, steps: readonly NextStep[], cwd: string): string[] {
16
+ const flag = s.kind === 'api' ? '--api' : '--screen';
17
+ const byHand = [
18
+ ` → ${withProjectInvocation(`sungen triage ${flag} ${s.unit} --case ${tc.id} --as product|test-defect|environment --reason "…"`, cwd)}`,
19
+ " by hand — test-defect and environment are a human's call, typed on a terminal; product anyone may record",
20
+ ];
21
+ if (steps.some((st) => st.nodeId === 'tc.triage')) return [' or classify it by hand:', ...byHand];
22
+ const one = steps.length === 1;
23
+ return [
24
+ steps.length ? ` then, once the step${one ? '' : 's'} above clear${one ? 's' : ''}, classify this red:` : ' classify this red:',
25
+ ` → ${withProjectInvocation(`sungen triage ${flag} ${s.unit}`, cwd)}`,
26
+ ' classifies every red of the latest run',
27
+ ` → /sungen:run-test ${s.unit}${tc.unstableId ? '' : ` --ids ${tc.id}`}`,
28
+ ' or re-run it first — run-test triages its own run',
29
+ ...byHand,
30
+ ];
31
+ }
@@ -11,6 +11,7 @@ import * as path from 'path';
11
11
  import { deriveFreeFeature, DeriveResult, nothingDerived } from '../../contract/derive-free-feature';
12
12
  import { registeredTraceResolver } from '../../contract/resolve-unit-trace-identities';
13
13
  import { resolveContractUnit } from '../../contract/contract-units';
14
+ import { withProjectInvocation } from '../project-sungen-invocation';
14
15
 
15
16
  function print(r: DeriveResult, unit: string): void {
16
17
  const L = console.log;
@@ -28,7 +29,7 @@ function print(r: DeriveResult, unit: string): void {
28
29
  L(' Move the value into test-data (or an env overlay) and use it from there, then derive again.');
29
30
  } else if (r.written) {
30
31
  L(` ✓ Wrote ${rel}. Review the steps (they ARE the contract), then QA approves on a terminal:`);
31
- L(` sungen contract approve --screen ${unit}`);
32
+ L(` ${withProjectInvocation(`sungen contract approve --screen ${unit}`, process.cwd())}`);
32
33
  L(' After approval, re-run the free tests (trace: "on") — a trace older than the contract proves nothing.');
33
34
  } else if (r.scenarios.length) {
34
35
  L(` = ${rel} is already up to date.`);
@@ -28,6 +28,7 @@ import { resolveContractUnit } from '../../contract/contract-units';
28
28
  import { secretScanHits } from '../../contract/field-semantic-redaction';
29
29
  import { resolveUnitTraceIdentities } from '../../contract/resolve-unit-trace-identities';
30
30
  import { registerDeriveCommand } from './contract-derive-command';
31
+ import { withProjectInvocationInText } from '../project-sungen-invocation';
31
32
 
32
33
  const MARK: Record<string, string> = {
33
34
  match: '✓', strengthened: '↑', proposed: '+', 'hash-changed': '⚖', 'oracle-weakened': '✗', 'target-changed': '✗', 'assertion-lost': '✗',
@@ -44,7 +45,8 @@ function generateCommand(unit: { name: string; dir: string }): string {
44
45
  }
45
46
 
46
47
  function printVerdict(v: ContractVerdict, reportFile: string, generate: string): void {
47
- const L = console.log;
48
+ // The approve / --accept-opaque / generate lines as this project runs sungen (#963).
49
+ const L = (line: string): void => console.log(withProjectInvocationInText(line, process.cwd()));
48
50
  const head = v.verdict === 'pass' ? '✓ PASS' : v.verdict === 'review'
49
51
  ? '⚖ REVIEW — changed, not weaker. Exit 0 here, but review blocks `sungen delivery` until QA re-approves' : '✗ FAIL';
50
52
  L('');
@@ -124,7 +126,8 @@ export function registerContractCommand(program: Command): void {
124
126
  console.log(`\n✓ Approved ${r.approved.length} scenario(s)${r.removed.length ? `, removed ${r.removed.length}` : ''} → ${path.relative(process.cwd(), r.lockPath)} (decision ${r.decisionId})`);
125
127
  console.log(' Commit the lock and the decision through review: until they are in git HEAD, `sungen contract check` reports contract.lock-uncommitted.\n');
126
128
  } catch (error) {
127
- console.error('Error:', error instanceof Error ? error.message : error);
129
+ // A refusal names the command a human runs instead: as this project runs sungen (#963).
130
+ console.error('Error:', error instanceof Error ? withProjectInvocationInText(error.message, process.cwd()) : error);
128
131
  process.exit(1);
129
132
  }
130
133
  });
@@ -17,6 +17,7 @@ import { mergeFeatureAndSpec } from '../../exporters/scenario-merger';
17
17
  import {
18
18
  buildTestCaseRows,
19
19
  buildSummary,
20
+ deliverableBasename,
20
21
  renderCsv,
21
22
  writeCsv,
22
23
  } from '../../exporters/csv-exporter';
@@ -47,6 +48,10 @@ import { DeliveryDecisionGate } from '../../harness/gates/delivery-decision-gate
47
48
  import { runDeliveryDecisionStep } from '../../harness/gates/delivery-decision-step';
48
49
  import { deliverableGateOverrides, InRunAcceptance } from '../../harness/gates/gate-override-report';
49
50
  import { ChecklistUnit, renderChecklist, unitChecklist } from '../../harness/gates/delivery-checklist';
51
+ import { withProjectInvocationInText } from '../project-sungen-invocation';
52
+ import { freeRunCommand, freeRunHasResults, generatedSubdir, plainRunCommand } from '../run-command-hints';
53
+ import { isHandWritten } from '../../harness/parse';
54
+ import { FREE_RUN_DIR } from '../../contract/trace/trace-run-record';
50
55
 
51
56
  const COLOR = {
52
57
  reset: '\x1b[0m',
@@ -186,8 +191,7 @@ function qaDir(cwd: string, target: DeliveryTarget): string {
186
191
  // dashboard snapshot builder loads the identical contract (shared + unit merge).
187
192
 
188
193
  function generatedDir(cwd: string, target: DeliveryTarget): string {
189
- const sub = target.kind === 'flow' ? path.join('flows', target.screen) : target.kind === 'api' ? path.join('api', target.screen) : target.screen;
190
- return path.join(cwd, 'specs', 'generated', sub);
194
+ return path.join(cwd, generatedSubdir(target.kind, target.screen));
191
195
  }
192
196
 
193
197
  // ----------------------------------------------------------------------------
@@ -250,6 +254,9 @@ export function runPreflight(cwd: string, target: DeliveryTarget): PreflightChec
250
254
  const selectorsFile = path.join(base, 'selectors', `${target.featureBaseName}.yaml`);
251
255
  const specFile = path.join(genBase, `${target.featureBaseName}.spec.ts`);
252
256
  const resultsFile = resolveResultsPath(cwd, target);
257
+ // A derived `*.free.feature` is implemented by hand-written tests (DOS-14.8): no selectors, never compiled,
258
+ // run with the project's free-test config into its own outputDir — so its hints are that run's (#964).
259
+ const free = isHandWritten([], featureFile);
253
260
 
254
261
  const featureOk = checkFeatureReal(featureFile);
255
262
  // test-data is only REQUIRED when the feature actually references a
@@ -260,9 +267,9 @@ export function runPreflight(cwd: string, target: DeliveryTarget): PreflightChec
260
267
  ? checkTestDataHasVars(testDataFile)
261
268
  : true;
262
269
  // API units have no DOM → no selectors; the catalog (apis.yaml) is the contract. N/A, not missing.
263
- const selectorsOk = target.kind === 'api' ? true : checkSelectorsHasEntries(selectorsFile, target.featureBaseName);
264
- const specOk = fs.existsSync(specFile);
265
- const resultsOk = resultsFile !== null;
270
+ const selectorsOk = target.kind === 'api' || free ? true : checkSelectorsHasEntries(selectorsFile, target.featureBaseName);
271
+ const specOk = free || fs.existsSync(specFile);
272
+ const resultsOk = free ? freeRunHasResults(cwd) : resultsFile !== null;
266
273
 
267
274
  const label = target.isFlow ? `flow/${target.featureBaseName}` : target.featureBaseName;
268
275
  const missing: string[] = [];
@@ -284,13 +291,15 @@ export function runPreflight(cwd: string, target: DeliveryTarget): PreflightChec
284
291
  missing.push(`compiled .spec.ts missing (optional — needed to run): ${path.relative(cwd, specFile)}`);
285
292
  suggestions.push(`sungen generate --${target.kind === 'flow' ? 'flow' : target.kind === 'api' ? 'api' : 'screen'} ${target.screen}`);
286
293
  }
287
- if (!resultsOk) {
294
+ if (!resultsOk && free) {
295
+ missing.push(`free-test results missing (optional): ${FREE_RUN_DIR}/`);
296
+ suggestions.push(freeRunCommand(cwd));
297
+ } else if (!resultsOk) {
288
298
  const env = process.env.SUNGEN_ENV;
289
299
  const envSuffix = env ? `.${env}` : '';
290
300
  missing.push(`test-result${envSuffix}.json missing (optional)`);
291
301
  const genRel = path.relative(cwd, genBase);
292
- const envPrefix = env ? `SUNGEN_ENV=${env} ` : '';
293
- suggestions.push(`${envPrefix}PLAYWRIGHT_JSON_OUTPUT_NAME=${genRel}/${target.featureBaseName}-test-result.json npx playwright test ${genRel}/${target.featureBaseName}.spec.ts`);
302
+ suggestions.push(plainRunCommand(genRel, target.featureBaseName, env));
294
303
  }
295
304
 
296
305
  return {
@@ -748,11 +757,13 @@ async function exportMatrixTarget(
748
757
  // counted, so this line, the workbook's totals and the summary table all agree (#891). A file
749
758
  // that cannot be read is reported where it matters — when it would be overwritten.
750
759
  const format = opts.format ?? 'xlsx';
760
+ // A draft (DRAFT banner) is written to its own `…-testcases.DRAFT.<ext>`, never onto the official file (#965).
761
+ const draft = model.manifest.approvalState === 'draft';
751
762
  let prior: PriorManualResults | null = null;
752
763
  let priorError: Error | null = null;
753
764
  try { prior = await readPriorManualResults(deliverableXlsxPath(cwd, paths.unit)); }
754
- // A CSV-only export overwrites no workbook, so an unreadable one only means "nothing to carry".
755
- catch (e) { if (format !== 'csv') priorError = e as Error; }
765
+ // A CSV-only export or a draft overwrites no official workbook, so an unreadable one only means "nothing to carry".
766
+ catch (e) { if (format !== 'csv' && !draft) priorError = e as Error; }
756
767
  applyManualCarry(model, prior);
757
768
  model.gateOverrides = deliverableGateOverrides(decisions?.overridesFor(qaDir(cwd, target)) ?? [], hasReview(model.findings), decisions?.acceptedInRun);
758
769
 
@@ -802,6 +813,9 @@ async function exportMatrixTarget(
802
813
  const failed = model.items.reduce((a, i) => a + i.resultCounts.failed, 0);
803
814
  const written = [xlsxPath, csvPath].filter((p): p is string => !!p).map((p) => path.relative(cwd, p));
804
815
  for (const rel of written) log(` ${COLOR.green}→ ${rel}${COLOR.reset}`);
816
+ if (draft) {
817
+ log(` ${COLOR.yellow}draft only — not a deliverable:${COLOR.reset} it carries the DRAFT banner, and the official ${deliverableBasename(paths.unit)} file was neither written nor changed.`);
818
+ }
805
819
  if (manualCarry) printManualCarry(manualCarry);
806
820
  return {
807
821
  label: paths.label,
@@ -929,7 +943,8 @@ async function printDeliveryChecklist(cwd: string, targets: DeliveryTarget[], ac
929
943
  const rows: Array<{ label: string; boxes: Awaited<ReturnType<typeof unitChecklist>> }> = [];
930
944
  for (const u of units.values()) rows.push({ label: u.label, boxes: await unitChecklist(cwd, u, accepted) });
931
945
  log('');
932
- for (const line of renderChecklist(rows)) log(line);
946
+ // Each box's fix command as this project runs sungen (#963).
947
+ for (const line of renderChecklist(rows)) log(withProjectInvocationInText(line, cwd));
933
948
  }
934
949
 
935
950
  // ----------------------------------------------------------------------------
@@ -948,7 +963,7 @@ export function registerDeliveryCommand(program: Command): void {
948
963
  .option('--full', 'Alias of --legacy')
949
964
  .option('--check', 'Matrix gates only — validate the delivery map, write nothing')
950
965
  .option('--approve [ids]', 'Approve proposed groups (all, or a comma-separated id list) + stamp fingerprints')
951
- .option('--preview', 'Render the matrix despite review-required findings (DRAFT watermark)')
966
+ .option('--preview', 'Render the matrix despite review-required findings — a DRAFT (watermarked) written to <unit>-testcases.DRAFT.*, never onto the official file')
952
967
  .option('--format <format>', 'Matrix output format: xlsx (default) | csv | both', 'xlsx')
953
968
  .option('--accept-in-run', 'Deliver despite gate-overrides/waivers/approvals recorded during the run (decision.in-run) — a human confirms on a terminal')
954
969
  .action(async (names: string[], options: {
@@ -1042,7 +1057,8 @@ export function registerDeliveryCommand(program: Command): void {
1042
1057
  }
1043
1058
  if (options.approve === undefined) {
1044
1059
  const gate = await runDeliveryDecisionStep({
1045
- projectRoot: cwd, acceptInRun: !!options.acceptInRun, io: processIO(), log,
1060
+ // The accept-in-run command and its refusal as this project runs sungen (#963).
1061
+ projectRoot: cwd, acceptInRun: !!options.acceptInRun, io: processIO(), log: (line) => log(withProjectInvocationInText(line, cwd)),
1046
1062
  // --preview / --check soften only the matrix export; --legacy has no draft mode, so it always needs one.
1047
1063
  requireTrustedAudit: !!(options.legacy || options.full) || (!options.preview && !options.check),
1048
1064
  refs: toExport.map((t) => ({ label: t.isFlow ? `flow/${t.featureBaseName}` : t.featureBaseName, unitName: t.screen, unitDir: qaDir(cwd, t), resultsPath: resolveResultsPath(cwd, t) })),
@@ -3,6 +3,7 @@ import * as path from 'path';
3
3
  import * as fs from 'fs';
4
4
  import { runGate, renderGate, GatePhase } from '../../harness/journey';
5
5
  import { findUnitDir } from '../resolve-unit';
6
+ import { withProjectInvocationInText } from '../project-sungen-invocation';
6
7
 
7
8
 
8
9
  const PHASES: GatePhase[] = ['create', 'run', 'deliver'];
@@ -24,7 +25,7 @@ export function registerGateCommand(program: Command): void {
24
25
 
25
26
  const verdict = runGate(process.cwd(), name, phase);
26
27
  if (options.json) console.log(JSON.stringify(verdict, null, 2));
27
- else console.log(renderGate(verdict));
28
+ else console.log(withProjectInvocationInText(renderGate(verdict), process.cwd())); // waive / sign-off hints as the project runs sungen (#963)
28
29
 
29
30
  // Exit 2 on HALT — usable in CI / the orchestration loop to block the next phase.
30
31
  process.exit(verdict.status === 'halt' ? 2 : 0);
@@ -8,6 +8,7 @@ import { capabilityRegistry } from '../../capabilities/registry';
8
8
  import { discoverAndRegisterCapabilities } from '../../capabilities/discover';
9
9
  import { readCapabilities, writeCapabilities, driverMeta, loadDriverCatalog } from '../../harness/capability';
10
10
  import { isHandWritten } from '../../harness/parse';
11
+ import { generatedSubdir, plainRunCommand } from '../run-command-hints';
11
12
 
12
13
  /**
13
14
  * Find .feature files recursively in a directory
@@ -257,8 +258,16 @@ export function registerGenerateCommand(program: Command): void {
257
258
 
258
259
  // Never the interactive `--ui` (#939): an agent cannot drive it, and run-test is what records the results.
259
260
  const unit = screenName || flowName || apiName;
260
- const nextStep = platform === 'mobile' ? 'npm run test:mobile' : unit ? `/sungen:run-test ${unit} (or the plain run: npx playwright test)` : 'npx playwright test';
261
- console.log(`Next step: ${nextStep}\n`);
261
+ const nextStep = platform === 'mobile' ? 'npm run test:mobile' : unit ? `/sungen:run-test ${unit}` : 'npx playwright test';
262
+ console.log(`Next step: ${nextStep}`);
263
+ // The plain run writes each feature's results where `sungen next` and `sungen delivery` read them (#964) —
264
+ // a bare `npx playwright test` wrote them to the config's default file, which neither reads.
265
+ if (platform !== 'mobile' && unit) {
266
+ const genRel = generatedSubdir(apiName ? 'api' : flowName ? 'flow' : 'screen', unit);
267
+ console.log(' or the plain run — it writes the results file `sungen next` and `sungen delivery` read:');
268
+ for (const f of featureFiles) console.log(` ${plainRunCommand(genRel, path.basename(f, '.feature'))}`);
269
+ }
270
+ console.log('');
262
271
  } catch (error) {
263
272
  console.error('Error:', error instanceof Error ? error.message : error);
264
273
  process.exit(1);
@@ -5,6 +5,7 @@ import { reportSlug } from '../../harness/unit-paths';
5
5
  import { findUnitDir } from '../resolve-unit';
6
6
  import { answerPendingMigration, decideWithConfirmation, DECIDE_KINDS, signoffWithConfirmation, waiveWithConfirmation } from '../../decisions/confirmed-decision-commands';
7
7
  import { processIO } from '../../decisions/human-confirmation';
8
+ import { withProjectInvocationInText } from '../project-sungen-invocation';
8
9
 
9
10
  const collect = (value: string, previous: string[]): string[] => [...previous, value];
10
11
 
@@ -77,12 +78,13 @@ export function registerJourneyCommand(program: Command): void {
77
78
  console.log(JSON.stringify(report, null, 2));
78
79
  } else {
79
80
  console.log('');
80
- console.log(board);
81
+ console.log(withProjectInvocationInText(board, process.cwd()));
81
82
  console.log(` Board: ${written ? path.relative(process.cwd(), written) : '(not written — read-only checkout)'}`);
82
83
  console.log('');
83
84
  }
84
85
  } catch (error) {
85
- console.error('Error:', error instanceof Error ? error.message : error);
86
+ // A refusal names the command a human runs instead: as this project runs sungen (#963).
87
+ console.error('Error:', error instanceof Error ? withProjectInvocationInText(error.message, process.cwd()) : error);
86
88
  process.exit(1);
87
89
  }
88
90
  });
@@ -10,8 +10,10 @@
10
10
  * unit steps before them (OES-10.3)
11
11
  * --json the same, machine-readable; steps add nodeId, granularity,
12
12
  * testCase?, gates?; the unit adds `results` (#942) and
13
- * `humanActions: [{command, reason, kind}]` (#954); every older
14
- * field is kept. stdout is the payload alone (#660, #836)
13
+ * `humanActions: [{command, reason, kind, rendered}]` (#954) —
14
+ * `command` canonical, `rendered` as this project runs it (#963),
15
+ * and `humanActionsHeldBy[Command]` when a blocking step holds them (#966);
16
+ * every older field is kept. stdout is the payload alone (#660, #836)
15
17
  * The text names the run the results come from and warns when they are older than the suite (#942),
16
18
  * and ends with "Waiting for you": every pending human-only command, with why (#954).
17
19
  */
@@ -24,8 +26,10 @@ import { testCaseStates } from '../../graph/test-case-states';
24
26
  import { describeResultsRun } from '../../graph/unit-results-freshness';
25
27
  import { deriveSteps, NextStep, readUnitState, UnitState } from '../../harness/next-step';
26
28
  import { resolveUnit, resolveUnitCandidates, AmbiguousUnitError } from '../resolve-unit';
27
- import { withProjectInvocation } from '../project-sungen-invocation';
29
+ import { withProjectInvocation, withProjectInvocationInText } from '../project-sungen-invocation';
28
30
  import { waitingForYouLines } from '../waiting-for-you-block';
31
+ import { caseTriageLines } from '../case-triage-hint';
32
+ import { testCaseNeed } from '../../graph/test-case-satisfied';
29
33
 
30
34
  /** Every unit the project holds, so `sungen next` with no argument still answers. */
31
35
  function allUnits(cwd: string): Array<{ name: string; kind: UnitState['kind']; dir: string }> {
@@ -41,15 +45,24 @@ function allUnits(cwd: string): Array<{ name: string; kind: UnitState['kind']; d
41
45
  return out;
42
46
  }
43
47
 
44
- /** Steps as text, each `sungen …` command as this project runs it (#939); `--json` keeps them canonical. */
48
+ /** Steps as text, each `sungen …` command — the reason's too (#963) — as this project runs it (#939); `--json` keeps them canonical. */
45
49
  function renderSteps(steps: NextStep[], cwd: string): void {
46
50
  for (const st of steps) {
47
51
  const mark = st.kind === 'blocked' ? '⛔' : st.kind === 'optional' ? '○' : st.kind === 'decide' ? '⚖' : '→';
48
- console.log(` ${mark} ${withProjectInvocation(st.command, cwd)}${st.kind === 'decide' ? ' (a QA decision — ask, do not run)' : ''}`);
49
- console.log(` ${st.because}`);
52
+ console.log(` ${mark} ${withProjectInvocationInText(st.command, cwd)}${st.kind === 'decide' ? ' (a QA decision — ask, do not run)' : ''}`);
53
+ console.log(` ${withProjectInvocationInText(st.because, cwd)}`);
50
54
  }
51
55
  }
52
56
 
57
+ /**
58
+ * `--json`: each human action keeps its canonical `command` and adds `rendered` — the line a human runs in
59
+ * this project (`npx sungen …` with a local install; an instruction unchanged), as the text shows it (#963).
60
+ */
61
+ function withRenderedHumanActions(s: UnitState, cwd: string): UnitState {
62
+ if (!s.humanActions) return s;
63
+ return { ...s, humanActions: s.humanActions.map((a) => ({ ...a, rendered: a.instruction ? a.command : withProjectInvocation(a.command, cwd) })) };
64
+ }
65
+
53
66
  function render(s: UnitState, cwd: string): void {
54
67
  const L = console.log;
55
68
  L('');
@@ -64,7 +77,7 @@ function render(s: UnitState, cwd: string): void {
64
77
  ].filter(Boolean);
65
78
  L(` state: ${held.length ? held.join(' · ') : '(nothing yet)'}`);
66
79
  if (s.audit) L(` audit: ${s.audit.overall}/10 [${s.audit.gateStatus}] · ${s.audit.findings.length} finding(s)`);
67
- else if (s.auditNotCounted) L(` audit: ${s.auditNotCounted.state === 'stale' ? 'stale' : 'not trusted'} — ${s.auditNotCounted.reason}`);
80
+ else if (s.auditNotCounted) L(` audit: ${s.auditNotCounted.state === 'stale' ? 'stale' : 'not trusted'} — ${withProjectInvocationInText(s.auditNotCounted.reason, cwd)}`);
68
81
  if (s.results) {
69
82
  L(` results: ${s.results.files.length} file${s.results.files.length === 1 ? '' : 's'}, ${describeResultsRun(s.results)}`);
70
83
  if (s.results.warning) L(` ⚠ ${s.results.warning}`);
@@ -72,7 +85,7 @@ function render(s: UnitState, cwd: string): void {
72
85
  L('');
73
86
  renderSteps(s.steps, cwd);
74
87
  L('');
75
- const waiting = waitingForYouLines(s.humanActions, cwd, s.humanActionsHeldBy);
88
+ const waiting = waitingForYouLines(s.humanActions, cwd, s.humanActionsHeldBy, s.humanActionsHeldByCommand);
76
89
  if (waiting.length) { for (const line of waiting) L(line); L(''); }
77
90
  }
78
91
 
@@ -104,6 +117,8 @@ function caseView(s: UnitState, cwd: string, unitDir: string, id: string, json:
104
117
  L('');
105
118
  if (steps.length) renderSteps(steps, cwd);
106
119
  else L(` ✓ nothing to do for this test case — ${testCaseStatusLabel(tc)}`);
120
+ // "needs triage" names how to classify the red, even while a unit step holds its own step back (#966).
121
+ if (testCaseNeed(tc) === 'triage') for (const line of caseTriageLines(s, tc, steps, cwd)) L(line);
107
122
  L('');
108
123
  }
109
124
 
@@ -141,7 +156,8 @@ export function registerNextCommand(program: Command): void {
141
156
  });
142
157
  }
143
158
  if (o.json) {
144
- console.log(JSON.stringify(states.length === 1 ? states[0] : states, null, 2));
159
+ const out = states.map((s) => withRenderedHumanActions(s, cwd));
160
+ console.log(JSON.stringify(out.length === 1 ? out[0] : out, null, 2));
145
161
  return;
146
162
  }
147
163
  for (const s of states) render(s, cwd);
@@ -1,6 +1,7 @@
1
1
  import { Command } from 'commander';
2
2
  import * as path from 'path';
3
3
  import { resolveUnit } from '../resolve-unit';
4
+ import { withProjectInvocation, withProjectInvocationInText } from '../project-sungen-invocation';
4
5
  import { processIO } from '../../decisions/human-confirmation';
5
6
  import { manualClassifications } from '../../graph/manual-triage-decisions';
6
7
  import { testCaseStates } from '../../graph/test-case-states';
@@ -47,7 +48,7 @@ export function registerTriageCommand(program: Command): void {
47
48
  .option('--api <name>', 'API-first area or api flow (qa/api/<name>)')
48
49
  .option('--area <name>', 'Alias of --api')
49
50
  .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('--as <class>', 'product (anyone may record it) | test-defect | environment (test-defect and environment: a human types a confirmation on a TTY)')
51
52
  .option('--reason <text>', 'Why the red is that class (mandatory with --as)')
52
53
  .option('--json', 'Output the raw JSON report')
53
54
  .action(async (o: TriageOptions) => {
@@ -82,11 +83,12 @@ export function registerTriageCommand(program: Command): void {
82
83
  }
83
84
  printManual(cwd, unit.dir, name);
84
85
  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
+ L(`\n Unclassified: read the evidence (trace, error), then classify by hand:\n ${withProjectInvocation(`sungen triage --${unit.kind === 'api' ? 'api' : 'screen'} ${name} --case <id> --as product|test-defect|environment --reason "…"`, cwd)}`);
86
87
  }
87
88
  L(`\n Report: ${path.relative(cwd, saved)}\n`);
88
89
  } catch (error) {
89
- console.error('Error:', error instanceof Error ? error.message : error);
90
+ // A refusal names the command a human runs instead: as this project runs sungen (#963).
91
+ console.error('Error:', error instanceof Error ? withProjectInvocationInText(error.message, process.cwd()) : error);
90
92
  process.exitCode = 1;
91
93
  }
92
94
  });
@@ -8,6 +8,9 @@
8
8
  *
9
9
  * sungenInvocation(root) 'npx sungen' when <root>/node_modules/@sun-asterisk/sungen exists, else 'sungen'
10
10
  * withProjectInvocation(cmd, root) a canonical `sungen …` command as this project runs it
11
+ * withProjectInvocationInText(text, root)
12
+ * every `sungen …` command inside printed text (a refusal, a hint, a
13
+ * step's reason) as this project runs it (#963); prose mentions stay
11
14
  * updateCommand(root, version?) `<invocation> update` plus `--alpha` / `--beta` (`--tag <id>` for another prerelease)
12
15
  *
13
16
  * Only the project root itself is looked at — where `npm i` puts a QA project's install — so a
@@ -27,6 +30,23 @@ export function withProjectInvocation(command: string, projectRoot: string): str
27
30
  return inv !== 'sungen' && /^sungen\b/.test(command) ? `${inv}${command.slice('sungen'.length)}` : command;
28
31
  }
29
32
 
33
+ /**
34
+ * A `sungen` that starts a command inside text: at the start of a line (after indentation), after a
35
+ * backtick or `(`, after ": ", "run " or "then " — and followed by a subcommand or an option. A prose
36
+ * mention ("written by sungen 3.3.0", "an older sungen", "sungen: …") is none of these.
37
+ */
38
+ const COMMAND_IN_TEXT = /(^[ \t]*|[`(]|:[ \t]+|\b(?:[Rr]un|then)[ \t]+)sungen(?= [a-z-])/gm;
39
+
40
+ /**
41
+ * Printed text (a refusal, a hint, a step's reason) with every `sungen …` command in it as this project
42
+ * runs sungen (#963): the human-only commands a QA copies from a refusal must run the project's own
43
+ * install, not an older global one. Text for machines (`--json`) stays canonical; this is for the screen.
44
+ */
45
+ export function withProjectInvocationInText(text: string, projectRoot: string): string {
46
+ const inv = sungenInvocation(projectRoot);
47
+ return inv === 'sungen' ? text : text.replace(COMMAND_IN_TEXT, (_m, before: string) => `${before}${inv}`);
48
+ }
49
+
30
50
  /** The `sungen update` flag that keeps the channel of `version` (its prerelease id); '' for stable. */
31
51
  export function updateChannelFlag(version: string = getPackageVersion()): string {
32
52
  const id = version.match(/^\d+\.\d+\.\d+-([0-9A-Za-z-]+)/)?.[1];
@@ -0,0 +1,47 @@
1
+ /**
2
+ * The run commands sungen suggests, each writing its results where the reader that asks for them
3
+ * looks (#964). A hint whose results nobody reads leaves a passing run invisible: the plain
4
+ * `npx playwright test` wrote `test-result.json` at the project root, which `sungen next` never reads.
5
+ *
6
+ * generatedSubdir(kind, unit) `specs/generated/<unit>` · `…/flows/<flow>` · `…/api/<area>` — where
7
+ * `generate` writes a unit's specs and `next` / `delivery` read its results
8
+ * plainRunCommand(genRel, feature) one compiled feature's spec with the JSON reporter pointed at
9
+ * `<genRel>/<feature>-test-result.json` (graph/unit-artifact-probes RESULT_FILE)
10
+ * freeRunCommand(cwd) hand-written @free / impl:code tests: the project's free-test config, which
11
+ * writes into its own outputDir `test-results-free/` (#941)
12
+ * freeRunHasResults(cwd) that run left its results (or a sungen run record) there
13
+ */
14
+ import * as fs from 'fs';
15
+ import * as path from 'path';
16
+ import { FREE_RUN_DIR } from '../contract/trace/trace-run-record';
17
+
18
+ export type RunUnitKind = 'screen' | 'flow' | 'api';
19
+
20
+ export function generatedSubdir(kind: RunUnitKind, unit: string): string {
21
+ return path.posix.join('specs', 'generated', ...(kind === 'flow' ? ['flows'] : kind === 'api' ? ['api'] : []), unit);
22
+ }
23
+
24
+ /** `[SUNGEN_ENV=<env> ]PLAYWRIGHT_JSON_OUTPUT_NAME=<genRel>/<feature>-test-result.json npx playwright test <genRel>/<feature>.spec.ts` */
25
+ export function plainRunCommand(genRel: string, featureBaseName: string, env?: string): string {
26
+ return `${env ? `SUNGEN_ENV=${env} ` : ''}PLAYWRIGHT_JSON_OUTPUT_NAME=${genRel}/${featureBaseName}-test-result.json npx playwright test ${genRel}/${featureBaseName}.spec.ts`;
27
+ }
28
+
29
+ const FREE_CONFIG = /^playwright\.free\.config\.(?:ts|mts|cts|js|mjs|cjs)$/;
30
+
31
+ /** The free run: `npx playwright test -c <playwright.free.config.*>` when the project has one, else what that config holds. */
32
+ export function freeRunCommand(cwd: string): string {
33
+ let config: string | undefined;
34
+ try { config = fs.readdirSync(cwd).find((f) => FREE_CONFIG.test(f)); } catch { /* no project dir: the generic form */ }
35
+ return config
36
+ ? `npx playwright test -c ${config}`
37
+ : `run the hand-written tests with the project's free-test config (outputDir '${FREE_RUN_DIR}', trace: 'on', the sungen run-record reporter — "Free tests" in the sungen docs)`;
38
+ }
39
+
40
+ /** The free run left its JSON results or a sungen run record in `test-results-free/`. */
41
+ export function freeRunHasResults(cwd: string): boolean {
42
+ try {
43
+ return fs.readdirSync(path.join(cwd, FREE_RUN_DIR)).some((f) => f === 'results.json' || /^sungen-run-record.*\.json$/.test(f));
44
+ } catch {
45
+ return false; // never run
46
+ }
47
+ }