@sun-asterisk/sungen 3.2.2-beta.1 → 3.2.2-beta.2

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.
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare function registerDepthLintCommand(program: Command): void;
3
+ //# sourceMappingURL=depth-lint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"depth-lint.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/depth-lint.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAgBpC,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAkC/D"}
@@ -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.registerDepthLintCommand = registerDepthLintCommand;
37
+ const path = __importStar(require("path"));
38
+ const fs = __importStar(require("fs"));
39
+ const depth_lint_1 = require("../../harness/depth-lint");
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 registerDepthLintCommand(program) {
53
+ program
54
+ .command('depth-lint')
55
+ .description('Harness: classify shallow business-critical scenarios → deepen-in-place (with the data-assertion template) vs cross-screen (route to a flow). Generation-time depth self-check (#384).')
56
+ .option('-s, --screen <name>', 'Screen or flow name to lint')
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
+ const dir = findScreenDir(name);
64
+ if (!dir)
65
+ throw new Error(`Not found: qa/screens/${name} or qa/flows/${name}`);
66
+ const report = (0, depth_lint_1.runDepthLint)(dir, name);
67
+ const outDir = path.join(process.cwd(), '.sungen', 'reports');
68
+ fs.mkdirSync(outDir, { recursive: true });
69
+ const outPath = path.join(outDir, `${(0, unit_paths_1.reportSlug)(name)}-depth-lint.json`);
70
+ fs.writeFileSync(outPath, JSON.stringify(report, null, 2), 'utf-8');
71
+ if (options.json) {
72
+ console.log(JSON.stringify(report, null, 2));
73
+ }
74
+ else {
75
+ (0, depth_lint_1.renderDepthLint)(report);
76
+ console.log(` Report: ${path.relative(process.cwd(), outPath)}`);
77
+ console.log('');
78
+ }
79
+ // Non-zero when there are deepen-in-place candidates the generator should fix before audit.
80
+ process.exit(report.deepen.length > 0 ? 2 : 0);
81
+ }
82
+ catch (error) {
83
+ console.error('Error:', error instanceof Error ? error.message : error);
84
+ process.exit(1);
85
+ }
86
+ });
87
+ }
88
+ //# sourceMappingURL=depth-lint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"depth-lint.js","sourceRoot":"","sources":["../../../src/cli/commands/depth-lint.ts"],"names":[],"mappings":"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"}
package/dist/cli/index.js CHANGED
@@ -16,6 +16,7 @@ const figma_1 = require("./commands/figma");
16
16
  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
+ const depth_lint_1 = require("./commands/depth-lint");
19
20
  const ingest_1 = require("./commands/ingest");
20
21
  const eval_1 = require("./commands/eval");
21
22
  const manifest_1 = require("./commands/manifest");
@@ -53,6 +54,7 @@ async function main() {
53
54
  (0, add_flow_1.registerAddFlowCommand)(program);
54
55
  (0, dashboard_1.registerDashboardCommand)(program);
55
56
  (0, audit_1.registerAuditCommand)(program);
57
+ (0, depth_lint_1.registerDepthLintCommand)(program);
56
58
  (0, manifest_1.registerManifestCommand)(program);
57
59
  (0, ledger_1.registerLedgerCommand)(program);
58
60
  (0, feedback_1.registerFeedbackCommand)(program);
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";;AACA;;;GAGG;;AAEH,yCAAoC;AACpC,0CAAsD;AACtD,wCAAoD;AACpD,kDAA8D;AAC9D,kDAA8D;AAC9D,8CAA0D;AAC1D,kDAA8D;AAC9D,4CAAwD;AACxD,kDAA6D;AAC7D,oDAAgE;AAChE,4CAAwD;AACxD,8CAA0D;AAC1D,0CAAsD;AACtD,kDAA8D;AAC9D,8CAA0D;AAC1D,kDAA8D;AAC9D,0DAAqE;AACrE,4CAAwD;AACxD,oDAAgE;AAChE,oDAAgE;AAChE,sDAAkE;AAClE,sDAAiE;AACjE,gDAA4D;AAC5D,8CAA0D;AAC1D,uDAA8D;AAC9D,uDAA2E;AAE3E,wFAAwF;AACxF,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,oBAAoB,CAAwB,CAAC;AAEzE,KAAK,UAAU,IAAI;IACjB,MAAM,OAAO,GAAG,IAAI,mBAAO,EAAE,CAAC;IAE9B,OAAO;SACJ,IAAI,CAAC,QAAQ,CAAC;SACd,WAAW,CAAC,oEAAoE,CAAC;SACjF,OAAO,CAAC,OAAO,CAAC,CAAC;IAEpB,iBAAiB;IACjB,OAAO;SACJ,MAAM,CAAC,eAAe,EAAE,wBAAwB,CAAC,CAAC;IAErD,oBAAoB;IACpB,IAAA,0BAAmB,EAAC,OAAO,CAAC,CAAC;IAC7B,IAAA,wBAAkB,EAAC,OAAO,CAAC,CAAC;IAC5B,IAAA,kCAAuB,EAAC,OAAO,CAAC,CAAC;IACjC,IAAA,kCAAuB,EAAC,OAAO,CAAC,CAAC;IACjC,IAAA,8BAAqB,EAAC,OAAO,CAAC,CAAC;IAC/B,IAAA,kCAAuB,EAAC,OAAO,CAAC,CAAC;IACjC,IAAA,4BAAoB,EAAC,OAAO,CAAC,CAAC;IAC9B,IAAA,iCAAsB,EAAC,OAAO,CAAC,CAAC;IAChC,IAAA,oCAAwB,EAAC,OAAO,CAAC,CAAC;IAClC,IAAA,4BAAoB,EAAC,OAAO,CAAC,CAAC;IAC9B,IAAA,kCAAuB,EAAC,OAAO,CAAC,CAAC;IACjC,IAAA,8BAAqB,EAAC,OAAO,CAAC,CAAC;IAC/B,IAAA,kCAAuB,EAAC,OAAO,CAAC,CAAC;IACjC,IAAA,yCAA0B,EAAC,OAAO,CAAC,CAAC;IACpC,IAAA,4BAAoB,EAAC,OAAO,CAAC,CAAC;IAC9B,IAAA,oCAAwB,EAAC,OAAO,CAAC,CAAC;IAClC,IAAA,oCAAwB,EAAC,OAAO,CAAC,CAAC;IAClC,IAAA,sCAAyB,EAAC,OAAO,CAAC,CAAC;IACnC,IAAA,qCAAwB,EAAC,OAAO,CAAC,CAAC;IAClC,IAAA,gCAAsB,EAAC,OAAO,CAAC,CAAC;IAChC,IAAA,8BAAqB,EAAC,OAAO,CAAC,CAAC;IAC/B,IAAA,8BAAqB,EAAC,OAAO,CAAC,CAAC;IAC/B,IAAA,0BAAmB,EAAC,OAAO,CAAC,CAAC;IAE7B,6FAA6F;IAC7F,iFAAiF;IACjF,IAAA,0CAA+B,GAAE,CAAC;IAClC,KAAK,MAAM,GAAG,IAAI,6BAAkB,CAAC,GAAG,EAAE,EAAE,CAAC;QAC3C,KAAK,MAAM,eAAe,IAAI,GAAG,CAAC,WAAW,IAAI,EAAE;YAAE,eAAe,CAAC,OAAO,CAAC,CAAC;IAChF,CAAC;IAED,MAAM,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;AACzC,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,OAAO,CAAC,KAAK,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC;IACrC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ export type DepthAction = 'deepen' | 'defer';
2
+ export interface DepthLintItem {
3
+ scenario: string;
4
+ theme: string | null;
5
+ action: DepthAction;
6
+ /** the exact deep step to emit (deepen) or the routing hint (defer) */
7
+ fix: string;
8
+ }
9
+ export interface DepthLintReport {
10
+ screen: string;
11
+ pageType: string | null;
12
+ focus: string;
13
+ threshold: number;
14
+ bcDepthRatio: number;
15
+ verdict: 'pass' | 'warn' | 'fail';
16
+ businessCriticalTotal: number;
17
+ shallowTotal: number;
18
+ /** shallow business-critical scenarios that CAN be deepened on-screen (actionable now) */
19
+ deepen: DepthLintItem[];
20
+ /** shallow business-critical scenarios that are cross-screen → route to a flow / @manual */
21
+ defer: DepthLintItem[];
22
+ }
23
+ export declare function runDepthLint(screenDir: string, screenName: string, focus?: string): DepthLintReport;
24
+ export declare function renderDepthLint(r: DepthLintReport): void;
25
+ //# sourceMappingURL=depth-lint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"depth-lint.d.ts","sourceRoot":"","sources":["../../src/harness/depth-lint.ts"],"names":[],"mappings":"AAkBA,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,OAAO,CAAC;AAE7C,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,WAAW,CAAC;IACpB,uEAAuE;IACvE,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IAClC,qBAAqB,EAAE,MAAM,CAAC;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,0FAA0F;IAC1F,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,4FAA4F;IAC5F,KAAK,EAAE,aAAa,EAAE,CAAC;CACxB;AAUD,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,SAAe,GAAG,eAAe,CAiDzG;AAED,wBAAgB,eAAe,CAAC,CAAC,EAAE,eAAe,GAAG,IAAI,CAmBxD"}
@@ -0,0 +1,118 @@
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.runDepthLint = runDepthLint;
37
+ exports.renderDepthLint = renderDepthLint;
38
+ const path = __importStar(require("path"));
39
+ const parse_1 = require("./parse");
40
+ const sensors_1 = require("./sensors");
41
+ /** Find the data-theme a scenario belongs to (precise depth.keywords, fallback theme.keywords). */
42
+ function matchTheme(s, dataThemes) {
43
+ return dataThemes.find((t) => {
44
+ const kws = t.depth?.keywords?.length ? t.depth.keywords : t.keywords;
45
+ return kws.some((k) => s.haystack.includes(k.toLowerCase()));
46
+ });
47
+ }
48
+ function runDepthLint(screenDir, screenName, focus = 'functional') {
49
+ const last = screenName.split('/').pop() || screenName;
50
+ const featurePath = path.join(screenDir, 'features', `${last}.feature`);
51
+ const viewpointPath = path.join(screenDir, 'requirements', 'test-viewpoint.md');
52
+ const scenarios = (0, parse_1.loadScenarios)(featurePath);
53
+ const viewpoints = (0, parse_1.parseViewpointOverview)(viewpointPath);
54
+ const catalog = (0, sensors_1.loadCatalog)();
55
+ const gate = (0, sensors_1.viewpointGate)(scenarios, viewpoints, catalog);
56
+ const dataThemes = (0, sensors_1.dataThemesFor)(catalog, gate.pageType);
57
+ const depth = (0, sensors_1.assertionDepth)(scenarios, dataThemes, focus);
58
+ const byName = new Map(scenarios.map((s) => [s.name, s]));
59
+ const deepen = [];
60
+ const defer = [];
61
+ for (const sb of depth.shallowBusinessCritical) {
62
+ const s = byName.get(sb.name);
63
+ const theme = s ? matchTheme(s, dataThemes) : undefined;
64
+ const crossScreen = theme?.depth?.cross_screen ?? false;
65
+ if (crossScreen) {
66
+ defer.push({
67
+ scenario: sb.name,
68
+ theme: theme?.theme ?? null,
69
+ action: 'defer',
70
+ fix: `cross-screen — own it in a flow (sungen add-flow) or tag @manual:Mx with a reason; do not fake an on-screen data assertion`,
71
+ });
72
+ }
73
+ else {
74
+ deepen.push({
75
+ scenario: sb.name,
76
+ theme: theme?.theme ?? null,
77
+ action: 'deepen',
78
+ fix: theme?.depth?.template ?? `add a data assertion (\`... with {{value}}\` or \`see all ... contain {{v}}\`)`,
79
+ });
80
+ }
81
+ }
82
+ return {
83
+ screen: screenName,
84
+ pageType: gate.pageType,
85
+ focus,
86
+ threshold: depth.threshold,
87
+ bcDepthRatio: depth.bcDepthRatio,
88
+ verdict: depth.verdict,
89
+ businessCriticalTotal: depth.businessCriticalTotal,
90
+ shallowTotal: depth.businessCriticalShallow,
91
+ deepen,
92
+ defer,
93
+ };
94
+ }
95
+ function renderDepthLint(r) {
96
+ const pct = (n) => `${Math.round(n * 100)}%`;
97
+ console.log('');
98
+ console.log(`━━━ Depth lint: ${r.screen} (page-type ${r.pageType ?? 'unknown'}) ━━━`);
99
+ console.log('');
100
+ console.log(` businessDepth ${pct(r.bcDepthRatio)} (threshold ${pct(r.threshold)} · focus ${r.focus}) → ${r.verdict.toUpperCase()}`);
101
+ console.log(` ${r.businessCriticalTotal} business-critical · ${r.shallowTotal} shallow → ${r.deepen.length} deepen-in-place · ${r.defer.length} cross-screen`);
102
+ if (r.deepen.length) {
103
+ console.log('');
104
+ console.log(' ── DEEPEN IN PLACE (fix before audit) ──');
105
+ for (const d of r.deepen)
106
+ console.log(` • ${d.scenario}\n [${d.theme}] → ${d.fix}`);
107
+ }
108
+ if (r.defer.length) {
109
+ console.log('');
110
+ console.log(' ── CROSS-SCREEN (route to flow / @manual:Mx) ──');
111
+ for (const d of r.defer)
112
+ console.log(` • ${d.scenario} [${d.theme}]`);
113
+ }
114
+ if (!r.deepen.length && !r.defer.length)
115
+ console.log(' ✓ no shallow business-critical scenarios');
116
+ console.log('');
117
+ }
118
+ //# sourceMappingURL=depth-lint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"depth-lint.js","sourceRoot":"","sources":["../../src/harness/depth-lint.ts"],"names":[],"mappings":"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"}
@@ -89,6 +89,11 @@ If the unit is **api-first** (`qa/api/<name>/` or `qa/api/flows/<name>/`), the d
89
89
 
90
90
  **5d. Sequential fallback.** Use the single-context incremental path (Step 2: tier-by-tier `Write`/`Edit` batches) when: only **one** shard applies, **Copilot / no sub-agents**, or a constrained setup. Same output, just no speedup. **For flows**: `[Screen:Element]` namespace refs, test-data namespaced by phase, `@flow` tag.
91
91
 
92
+ 5.4. **Depth self-check (deterministic — run BEFORE the audit).** Run `sungen depth-lint --screen <name>` (Bash). It reuses the audit's businessDepth classifier and splits every shallow business-critical scenario into two actionable buckets — act on them now so the audit/repair loop doesn't burn rounds on depth:
93
+ - **DEEPEN IN PLACE** — add a real value assertion to each (`User see all [X] contain {{v}}`, `User remember [X] as {{v}}` + `… with {{v}}`). The printed `template` is a **hint** keyed off the theme — apply judgment to the scenario's actual claim; do NOT paste a value assertion that doesn't fit (e.g. a carousel-visibility scenario should assert the product SET, not a price). If a flagged scenario is genuinely visibility/behavior (not data-correctness), that's an over-count — leave it and note it, never fake an assertion.
94
+ - **CROSS-SCREEN** — route to a flow (`/sungen:add-flow`) or tag `@manual:Mx` + reason; do NOT fake an on-screen data assertion. This removes it from the screen's depth denominator honestly.
95
+ Re-run `sungen depth-lint` until `deepen` is empty (or only honest over-counts remain), THEN proceed to the gate. This lifts first-pass `businessDepth` mechanically instead of via 2–3 organic repair rounds.
96
+
92
97
  5.5. **Quality gate & repair (harness — always run, do NOT skip).** Follow the `sungen-harness-audit` skill:
93
98
  - Run `sungen audit --screen <name>` (Bash) and read `gateStatus` + `findings` (deterministic, structural).
94
99
  - **Independent semantic review.** **Claude Code:** spawn the **`sungen-reviewer`** sub-agent (Task tool, `subagent_type: sungen-reviewer`) — it judges what the gate can't (does each scenario's steps PROVE its title/viewpoint, observable Thens, business-critical assertion depth) and returns `VERDICT` + `ISSUES` with concrete fixes. **Merge its NEEDS-REPAIR issues with the audit findings.** (Copilot / no sub-agents: run the same review inline using the `sungen-reviewer` criteria.)
@@ -275,6 +275,7 @@ Security: [S1 – admin only]
275
275
 
276
276
  **Depth is a GATE dimension (harness-roadmap P1) — self-raise, never silently go shallow:**
277
277
  - For every data-correctness theme the catalog marks `depth.requires: data-assertion`, emit its `depth.template` shape by **default** — don't wait for the repair loop. `sungen audit` measures `businessDepth` (ratio of these scenarios that assert data) against an intent threshold (functional ≥ 0.70); below it the **gate FAILs**.
278
+ - **Verify depth deterministically before the gate:** run `sungen depth-lint --screen <name>`. It classifies every shallow business-critical scenario into **deepen-in-place** (add the theme's value assertion — the printed `template` is a hint, fit it to the actual claim) vs **cross-screen** (route to a flow / `@manual:Mx`). Clear the `deepen` list first — this is the mechanical way to hit `businessDepth` on the first pass instead of churning repair rounds. Never fake a value assertion onto a visibility/behavior scenario the lint over-counts; leave it and note the over-count.
278
279
  - `depth.cross_screen: true` (cart / detail / filter / brand correctness) → write the deep capture/compare shape as an **automated flow scenario** (in the flow — do NOT leave a full-step `@manual` duplicate on the screen). `@manual` is **only** for genuine judgment (M6 visual/UX · M8 not-worth · M9 human) or a missing capability (M1–M5/M7), and it **must** carry a reason code (`@manual:Mx`, or a reason comment the planner can infer). A `@manual` scenario that still has full automatable steps (a data assertion, no visual/mock/a11y judgment) is now flagged by `sungen audit` as `MANUAL-AUTOMATABLE`, and business-critical scenarios you defer to `@manual` are reported as `DEPTH-DEFERRED` (they do NOT silently inflate `businessDepth`). Deferring automatable work to `@manual` lowers quality — automate it in the flow instead.
279
280
  - **Pick the right `@manual:Mx` code — it decides which driver can later automate the case** (`sungen audit` flags a code↔reason mismatch). Tag the code that matches the **oracle the reason describes**:
280
281
 
@@ -66,6 +66,7 @@ If the unit is **api-first** (`qa/api/<name>/` or `qa/api/flows/<name>/`), the d
66
66
  4. Follow the `sungen-tc-generation` skill for section identification, viewpoint generation, and output format. **For flows**, use the "Flow Test Generation" section in the skill. When requirements exist, use the "Requirements-Driven Generation" strategy. **For Tier 1**, apply the **Lightweight Guard** — verify required fields, validation rules, business rules, security checks, and key state transitions all have TCs after generation. **For Tier 2+**, **MUST** apply the full **Mapping Contract** — walk every `spec.md` section top-to-bottom and produce the indicated TCs per Table 1; handle `test-viewpoint.md` per Table 2. Do not silently skip sections. Present sections as a numbered list and let user pick.
67
67
  5. Generate or update `.feature` + `test-data.yaml` following `sungen-gherkin-syntax` and `sungen-tc-generation` skills. Generate **group-by-group** (one viewpoint group at a time, tier-by-tier `Write`/`Edit` batches) to stay under the output-token cap. **For flows**: use `[Screen:Element]` namespace format, namespace test-data by phase, add `@flow` tag.
68
68
  > **No parallel fan-out here.** Copilot has no sub-agents, so generation is sequential (the Claude Code variant fans out one `sungen-generator` per viewpoint group and merges). Same output, no speedup.
69
+ 5.4. **Depth self-check (deterministic — BEFORE the audit).** Run `sungen depth-lint --screen ${input:name}`. It splits every shallow business-critical scenario into **DEEPEN IN PLACE** (add a real value assertion — the printed `template` is a theme-keyed hint, apply judgment to the actual claim; never fake one onto a visibility/behavior scenario) and **CROSS-SCREEN** (route to a flow / tag `@manual:Mx` + reason — removes it from the depth denominator honestly). Act on both, re-run until `deepen` is empty (or only honest over-counts remain), THEN gate. Lifts first-pass `businessDepth` mechanically instead of via 2–3 repair rounds.
69
70
  5.5. **Quality gate & repair (harness — always run).** Per `sungen-harness-audit`: run `sungen audit --screen ${input:name}` (structural), THEN do an **independent semantic review inline** using the `sungen-reviewer` criteria (does each scenario's steps PROVE its title/viewpoint? observable Thens? business-critical assertion depth?). Merge both sets of issues; if gate FAILs / findings exist, repair (budget 3) and re-audit — GATE missing theme → generate it (cross-screen → **automate it in the flow** via `/sungen:add-flow`, NOT a full `@manual` screen duplicate — `sungen audit` flags an automatable `@manual` as `MANUAL-AUTOMATABLE`; reserve `@manual:Mx` for true judgment/missing-capability); DEPTH → add data assertions; BALANCE → add business-core first; TRACE → align VP ids. Never fake a pass.
70
71
  5.6. **Record.** `sungen manifest --screen ${input:name}`. Ledger **each phase** (not just repair) — pick one `runId` at the start and pass it so `trace`/`ledger report` show THIS run, not a mix: `sungen ledger record --screen ${input:name} --run <runId> --step <discovery|viewpoint|gherkin|audit|repair:N> --ms <elapsed>`. On re-run, start with `sungen manifest --screen ${input:name} --diff` and only regenerate changed sections.
71
72
  6. **Converge — show the trace.** Run `sungen trace --screen ${input:name}` and present: process map (phases + repair rounds), bottlenecks, **HUMAN-LOOP FOCUS** (@manual to verify), audit score + gate + residual gaps. Then offer next steps based on which tier was just generated:
@@ -275,6 +275,7 @@ Security: [S1 – admin only]
275
275
 
276
276
  **Depth is a GATE dimension (harness-roadmap P1) — self-raise, never silently go shallow:**
277
277
  - For every data-correctness theme the catalog marks `depth.requires: data-assertion`, emit its `depth.template` shape by **default** — don't wait for the repair loop. `sungen audit` measures `businessDepth` (ratio of these scenarios that assert data) against an intent threshold (functional ≥ 0.70); below it the **gate FAILs**.
278
+ - **Verify depth deterministically before the gate:** run `sungen depth-lint --screen <name>`. It classifies every shallow business-critical scenario into **deepen-in-place** (add the theme's value assertion — the printed `template` is a hint, fit it to the actual claim) vs **cross-screen** (route to a flow / `@manual:Mx`). Clear the `deepen` list first — this is the mechanical way to hit `businessDepth` on the first pass instead of churning repair rounds. Never fake a value assertion onto a visibility/behavior scenario the lint over-counts; leave it and note the over-count.
278
279
  - `depth.cross_screen: true` (cart / detail / filter / brand correctness) → write the deep capture/compare shape as an **automated flow scenario** (in the flow — do NOT leave a full-step `@manual` duplicate on the screen). `@manual` is **only** for genuine judgment (M6 visual/UX · M8 not-worth · M9 human) or a missing capability (M1–M5/M7), and it **must** carry a reason code (`@manual:Mx`, or a reason comment the planner can infer). A `@manual` scenario that still has full automatable steps (a data assertion, no visual/mock/a11y judgment) is now flagged by `sungen audit` as `MANUAL-AUTOMATABLE`, and business-critical scenarios you defer to `@manual` are reported as `DEPTH-DEFERRED` (they do NOT silently inflate `businessDepth`). Deferring automatable work to `@manual` lowers quality — automate it in the flow instead.
279
280
  - **Pick the right `@manual:Mx` code — it decides which driver can later automate the case** (`sungen audit` flags a code↔reason mismatch). Tag the code that matches the **oracle the reason describes**:
280
281
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sun-asterisk/sungen",
3
- "version": "3.2.2-beta.1",
3
+ "version": "3.2.2-beta.2",
4
4
  "description": "Deterministic E2E Test Compiler - Gherkin + Selectors → Playwright tests",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",
@@ -33,7 +33,7 @@
33
33
  "node": ">=18.0.0"
34
34
  },
35
35
  "dependencies": {
36
- "@sungen/driver-ui": "3.2.2-beta.1",
36
+ "@sungen/driver-ui": "3.2.2-beta.2",
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 { runDepthLint, renderDepthLint } from '../../harness/depth-lint';
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 registerDepthLintCommand(program: Command): void {
18
+ program
19
+ .command('depth-lint')
20
+ .description('Harness: classify shallow business-critical scenarios → deepen-in-place (with the data-assertion template) vs cross-screen (route to a flow). Generation-time depth self-check (#384).')
21
+ .option('-s, --screen <name>', 'Screen or flow name to lint')
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
+ const dir = findScreenDir(name);
28
+ if (!dir) throw new Error(`Not found: qa/screens/${name} or qa/flows/${name}`);
29
+
30
+ const report = runDepthLint(dir, name);
31
+
32
+ const outDir = path.join(process.cwd(), '.sungen', 'reports');
33
+ fs.mkdirSync(outDir, { recursive: true });
34
+ const outPath = path.join(outDir, `${reportSlug(name)}-depth-lint.json`);
35
+ fs.writeFileSync(outPath, JSON.stringify(report, null, 2), 'utf-8');
36
+
37
+ if (options.json) {
38
+ console.log(JSON.stringify(report, null, 2));
39
+ } else {
40
+ renderDepthLint(report);
41
+ console.log(` Report: ${path.relative(process.cwd(), outPath)}`);
42
+ console.log('');
43
+ }
44
+ // Non-zero when there are deepen-in-place candidates the generator should fix before audit.
45
+ process.exit(report.deepen.length > 0 ? 2 : 0);
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
@@ -15,6 +15,7 @@ import { registerFigmaCommand } from './commands/figma';
15
15
  import { registerAddFlowCommand } from './commands/add-flow';
16
16
  import { registerDashboardCommand } from './commands/dashboard';
17
17
  import { registerAuditCommand } from './commands/audit';
18
+ import { registerDepthLintCommand } from './commands/depth-lint';
18
19
  import { registerIngestCommand } from './commands/ingest';
19
20
  import { registerEvalCommand } from './commands/eval';
20
21
  import { registerManifestCommand } from './commands/manifest';
@@ -57,6 +58,7 @@ async function main() {
57
58
  registerAddFlowCommand(program);
58
59
  registerDashboardCommand(program);
59
60
  registerAuditCommand(program);
61
+ registerDepthLintCommand(program);
60
62
  registerManifestCommand(program);
61
63
  registerLedgerCommand(program);
62
64
  registerFeedbackCommand(program);
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Depth lint (issue #384) — a deterministic, generation-time depth self-check.
3
+ *
4
+ * The audit's `assertionDepth` sensor decides WHICH business-critical scenarios are shallow
5
+ * (the authoritative set). This lint adds the missing half: for each shallow business-critical
6
+ * scenario it classifies the *fix* using the catalog's per-theme `depth` metadata —
7
+ * • cross_screen:false → DEEPEN in place (emit the theme's `depth.template` value assertion)
8
+ * • cross_screen:true → DEFER (flow-own, or @manual:Mx with a reason) — leaves the depth denominator
9
+ * so a generator (or the create-test repair step) can act mechanically BEFORE the first audit,
10
+ * instead of churning the 3-round repair budget on scenarios that can't be deepened on-screen.
11
+ *
12
+ * Reuses the audit plumbing verbatim (parse + catalog + assertionDepth) → same verdict as `sungen audit`.
13
+ */
14
+ import * as fs from 'fs';
15
+ import * as path from 'path';
16
+ import { loadScenarios, parseViewpointOverview, ScenarioInfo, ViewpointEntry } from './parse';
17
+ import { loadCatalog, viewpointGate, assertionDepth, dataThemesFor, CatalogTheme } from './sensors';
18
+
19
+ export type DepthAction = 'deepen' | 'defer';
20
+
21
+ export interface DepthLintItem {
22
+ scenario: string;
23
+ theme: string | null;
24
+ action: DepthAction;
25
+ /** the exact deep step to emit (deepen) or the routing hint (defer) */
26
+ fix: string;
27
+ }
28
+
29
+ export interface DepthLintReport {
30
+ screen: string;
31
+ pageType: string | null;
32
+ focus: string;
33
+ threshold: number;
34
+ bcDepthRatio: number;
35
+ verdict: 'pass' | 'warn' | 'fail';
36
+ businessCriticalTotal: number;
37
+ shallowTotal: number;
38
+ /** shallow business-critical scenarios that CAN be deepened on-screen (actionable now) */
39
+ deepen: DepthLintItem[];
40
+ /** shallow business-critical scenarios that are cross-screen → route to a flow / @manual */
41
+ defer: DepthLintItem[];
42
+ }
43
+
44
+ /** Find the data-theme a scenario belongs to (precise depth.keywords, fallback theme.keywords). */
45
+ function matchTheme(s: ScenarioInfo, dataThemes: CatalogTheme[]): CatalogTheme | undefined {
46
+ return dataThemes.find((t) => {
47
+ const kws = t.depth?.keywords?.length ? t.depth.keywords : t.keywords;
48
+ return kws.some((k) => s.haystack.includes(k.toLowerCase()));
49
+ });
50
+ }
51
+
52
+ export function runDepthLint(screenDir: string, screenName: string, focus = 'functional'): DepthLintReport {
53
+ const last = screenName.split('/').pop() || screenName;
54
+ const featurePath = path.join(screenDir, 'features', `${last}.feature`);
55
+ const viewpointPath = path.join(screenDir, 'requirements', 'test-viewpoint.md');
56
+
57
+ const scenarios: ScenarioInfo[] = loadScenarios(featurePath);
58
+ const viewpoints: ViewpointEntry[] = parseViewpointOverview(viewpointPath);
59
+ const catalog = loadCatalog();
60
+ const gate = viewpointGate(scenarios, viewpoints, catalog);
61
+ const dataThemes = dataThemesFor(catalog, gate.pageType);
62
+ const depth = assertionDepth(scenarios, dataThemes, focus);
63
+
64
+ const byName = new Map(scenarios.map((s) => [s.name, s]));
65
+ const deepen: DepthLintItem[] = [];
66
+ const defer: DepthLintItem[] = [];
67
+
68
+ for (const sb of depth.shallowBusinessCritical) {
69
+ const s = byName.get(sb.name);
70
+ const theme = s ? matchTheme(s, dataThemes) : undefined;
71
+ const crossScreen = theme?.depth?.cross_screen ?? false;
72
+ if (crossScreen) {
73
+ defer.push({
74
+ scenario: sb.name,
75
+ theme: theme?.theme ?? null,
76
+ action: 'defer',
77
+ fix: `cross-screen — own it in a flow (sungen add-flow) or tag @manual:Mx with a reason; do not fake an on-screen data assertion`,
78
+ });
79
+ } else {
80
+ deepen.push({
81
+ scenario: sb.name,
82
+ theme: theme?.theme ?? null,
83
+ action: 'deepen',
84
+ fix: theme?.depth?.template ?? `add a data assertion (\`... with {{value}}\` or \`see all ... contain {{v}}\`)`,
85
+ });
86
+ }
87
+ }
88
+
89
+ return {
90
+ screen: screenName,
91
+ pageType: gate.pageType,
92
+ focus,
93
+ threshold: depth.threshold,
94
+ bcDepthRatio: depth.bcDepthRatio,
95
+ verdict: depth.verdict,
96
+ businessCriticalTotal: depth.businessCriticalTotal,
97
+ shallowTotal: depth.businessCriticalShallow,
98
+ deepen,
99
+ defer,
100
+ };
101
+ }
102
+
103
+ export function renderDepthLint(r: DepthLintReport): void {
104
+ const pct = (n: number) => `${Math.round(n * 100)}%`;
105
+ console.log('');
106
+ console.log(`━━━ Depth lint: ${r.screen} (page-type ${r.pageType ?? 'unknown'}) ━━━`);
107
+ console.log('');
108
+ console.log(` businessDepth ${pct(r.bcDepthRatio)} (threshold ${pct(r.threshold)} · focus ${r.focus}) → ${r.verdict.toUpperCase()}`);
109
+ console.log(` ${r.businessCriticalTotal} business-critical · ${r.shallowTotal} shallow → ${r.deepen.length} deepen-in-place · ${r.defer.length} cross-screen`);
110
+ if (r.deepen.length) {
111
+ console.log('');
112
+ console.log(' ── DEEPEN IN PLACE (fix before audit) ──');
113
+ for (const d of r.deepen) console.log(` • ${d.scenario}\n [${d.theme}] → ${d.fix}`);
114
+ }
115
+ if (r.defer.length) {
116
+ console.log('');
117
+ console.log(' ── CROSS-SCREEN (route to flow / @manual:Mx) ──');
118
+ for (const d of r.defer) console.log(` • ${d.scenario} [${d.theme}]`);
119
+ }
120
+ if (!r.deepen.length && !r.defer.length) console.log(' ✓ no shallow business-critical scenarios');
121
+ console.log('');
122
+ }
@@ -89,6 +89,11 @@ If the unit is **api-first** (`qa/api/<name>/` or `qa/api/flows/<name>/`), the d
89
89
 
90
90
  **5d. Sequential fallback.** Use the single-context incremental path (Step 2: tier-by-tier `Write`/`Edit` batches) when: only **one** shard applies, **Copilot / no sub-agents**, or a constrained setup. Same output, just no speedup. **For flows**: `[Screen:Element]` namespace refs, test-data namespaced by phase, `@flow` tag.
91
91
 
92
+ 5.4. **Depth self-check (deterministic — run BEFORE the audit).** Run `sungen depth-lint --screen <name>` (Bash). It reuses the audit's businessDepth classifier and splits every shallow business-critical scenario into two actionable buckets — act on them now so the audit/repair loop doesn't burn rounds on depth:
93
+ - **DEEPEN IN PLACE** — add a real value assertion to each (`User see all [X] contain {{v}}`, `User remember [X] as {{v}}` + `… with {{v}}`). The printed `template` is a **hint** keyed off the theme — apply judgment to the scenario's actual claim; do NOT paste a value assertion that doesn't fit (e.g. a carousel-visibility scenario should assert the product SET, not a price). If a flagged scenario is genuinely visibility/behavior (not data-correctness), that's an over-count — leave it and note it, never fake an assertion.
94
+ - **CROSS-SCREEN** — route to a flow (`/sungen:add-flow`) or tag `@manual:Mx` + reason; do NOT fake an on-screen data assertion. This removes it from the screen's depth denominator honestly.
95
+ Re-run `sungen depth-lint` until `deepen` is empty (or only honest over-counts remain), THEN proceed to the gate. This lifts first-pass `businessDepth` mechanically instead of via 2–3 organic repair rounds.
96
+
92
97
  5.5. **Quality gate & repair (harness — always run, do NOT skip).** Follow the `sungen-harness-audit` skill:
93
98
  - Run `sungen audit --screen <name>` (Bash) and read `gateStatus` + `findings` (deterministic, structural).
94
99
  - **Independent semantic review.** **Claude Code:** spawn the **`sungen-reviewer`** sub-agent (Task tool, `subagent_type: sungen-reviewer`) — it judges what the gate can't (does each scenario's steps PROVE its title/viewpoint, observable Thens, business-critical assertion depth) and returns `VERDICT` + `ISSUES` with concrete fixes. **Merge its NEEDS-REPAIR issues with the audit findings.** (Copilot / no sub-agents: run the same review inline using the `sungen-reviewer` criteria.)
@@ -275,6 +275,7 @@ Security: [S1 – admin only]
275
275
 
276
276
  **Depth is a GATE dimension (harness-roadmap P1) — self-raise, never silently go shallow:**
277
277
  - For every data-correctness theme the catalog marks `depth.requires: data-assertion`, emit its `depth.template` shape by **default** — don't wait for the repair loop. `sungen audit` measures `businessDepth` (ratio of these scenarios that assert data) against an intent threshold (functional ≥ 0.70); below it the **gate FAILs**.
278
+ - **Verify depth deterministically before the gate:** run `sungen depth-lint --screen <name>`. It classifies every shallow business-critical scenario into **deepen-in-place** (add the theme's value assertion — the printed `template` is a hint, fit it to the actual claim) vs **cross-screen** (route to a flow / `@manual:Mx`). Clear the `deepen` list first — this is the mechanical way to hit `businessDepth` on the first pass instead of churning repair rounds. Never fake a value assertion onto a visibility/behavior scenario the lint over-counts; leave it and note the over-count.
278
279
  - `depth.cross_screen: true` (cart / detail / filter / brand correctness) → write the deep capture/compare shape as an **automated flow scenario** (in the flow — do NOT leave a full-step `@manual` duplicate on the screen). `@manual` is **only** for genuine judgment (M6 visual/UX · M8 not-worth · M9 human) or a missing capability (M1–M5/M7), and it **must** carry a reason code (`@manual:Mx`, or a reason comment the planner can infer). A `@manual` scenario that still has full automatable steps (a data assertion, no visual/mock/a11y judgment) is now flagged by `sungen audit` as `MANUAL-AUTOMATABLE`, and business-critical scenarios you defer to `@manual` are reported as `DEPTH-DEFERRED` (they do NOT silently inflate `businessDepth`). Deferring automatable work to `@manual` lowers quality — automate it in the flow instead.
279
280
  - **Pick the right `@manual:Mx` code — it decides which driver can later automate the case** (`sungen audit` flags a code↔reason mismatch). Tag the code that matches the **oracle the reason describes**:
280
281
 
@@ -66,6 +66,7 @@ If the unit is **api-first** (`qa/api/<name>/` or `qa/api/flows/<name>/`), the d
66
66
  4. Follow the `sungen-tc-generation` skill for section identification, viewpoint generation, and output format. **For flows**, use the "Flow Test Generation" section in the skill. When requirements exist, use the "Requirements-Driven Generation" strategy. **For Tier 1**, apply the **Lightweight Guard** — verify required fields, validation rules, business rules, security checks, and key state transitions all have TCs after generation. **For Tier 2+**, **MUST** apply the full **Mapping Contract** — walk every `spec.md` section top-to-bottom and produce the indicated TCs per Table 1; handle `test-viewpoint.md` per Table 2. Do not silently skip sections. Present sections as a numbered list and let user pick.
67
67
  5. Generate or update `.feature` + `test-data.yaml` following `sungen-gherkin-syntax` and `sungen-tc-generation` skills. Generate **group-by-group** (one viewpoint group at a time, tier-by-tier `Write`/`Edit` batches) to stay under the output-token cap. **For flows**: use `[Screen:Element]` namespace format, namespace test-data by phase, add `@flow` tag.
68
68
  > **No parallel fan-out here.** Copilot has no sub-agents, so generation is sequential (the Claude Code variant fans out one `sungen-generator` per viewpoint group and merges). Same output, no speedup.
69
+ 5.4. **Depth self-check (deterministic — BEFORE the audit).** Run `sungen depth-lint --screen ${input:name}`. It splits every shallow business-critical scenario into **DEEPEN IN PLACE** (add a real value assertion — the printed `template` is a theme-keyed hint, apply judgment to the actual claim; never fake one onto a visibility/behavior scenario) and **CROSS-SCREEN** (route to a flow / tag `@manual:Mx` + reason — removes it from the depth denominator honestly). Act on both, re-run until `deepen` is empty (or only honest over-counts remain), THEN gate. Lifts first-pass `businessDepth` mechanically instead of via 2–3 repair rounds.
69
70
  5.5. **Quality gate & repair (harness — always run).** Per `sungen-harness-audit`: run `sungen audit --screen ${input:name}` (structural), THEN do an **independent semantic review inline** using the `sungen-reviewer` criteria (does each scenario's steps PROVE its title/viewpoint? observable Thens? business-critical assertion depth?). Merge both sets of issues; if gate FAILs / findings exist, repair (budget 3) and re-audit — GATE missing theme → generate it (cross-screen → **automate it in the flow** via `/sungen:add-flow`, NOT a full `@manual` screen duplicate — `sungen audit` flags an automatable `@manual` as `MANUAL-AUTOMATABLE`; reserve `@manual:Mx` for true judgment/missing-capability); DEPTH → add data assertions; BALANCE → add business-core first; TRACE → align VP ids. Never fake a pass.
70
71
  5.6. **Record.** `sungen manifest --screen ${input:name}`. Ledger **each phase** (not just repair) — pick one `runId` at the start and pass it so `trace`/`ledger report` show THIS run, not a mix: `sungen ledger record --screen ${input:name} --run <runId> --step <discovery|viewpoint|gherkin|audit|repair:N> --ms <elapsed>`. On re-run, start with `sungen manifest --screen ${input:name} --diff` and only regenerate changed sections.
71
72
  6. **Converge — show the trace.** Run `sungen trace --screen ${input:name}` and present: process map (phases + repair rounds), bottlenecks, **HUMAN-LOOP FOCUS** (@manual to verify), audit score + gate + residual gaps. Then offer next steps based on which tier was just generated:
@@ -275,6 +275,7 @@ Security: [S1 – admin only]
275
275
 
276
276
  **Depth is a GATE dimension (harness-roadmap P1) — self-raise, never silently go shallow:**
277
277
  - For every data-correctness theme the catalog marks `depth.requires: data-assertion`, emit its `depth.template` shape by **default** — don't wait for the repair loop. `sungen audit` measures `businessDepth` (ratio of these scenarios that assert data) against an intent threshold (functional ≥ 0.70); below it the **gate FAILs**.
278
+ - **Verify depth deterministically before the gate:** run `sungen depth-lint --screen <name>`. It classifies every shallow business-critical scenario into **deepen-in-place** (add the theme's value assertion — the printed `template` is a hint, fit it to the actual claim) vs **cross-screen** (route to a flow / `@manual:Mx`). Clear the `deepen` list first — this is the mechanical way to hit `businessDepth` on the first pass instead of churning repair rounds. Never fake a value assertion onto a visibility/behavior scenario the lint over-counts; leave it and note the over-count.
278
279
  - `depth.cross_screen: true` (cart / detail / filter / brand correctness) → write the deep capture/compare shape as an **automated flow scenario** (in the flow — do NOT leave a full-step `@manual` duplicate on the screen). `@manual` is **only** for genuine judgment (M6 visual/UX · M8 not-worth · M9 human) or a missing capability (M1–M5/M7), and it **must** carry a reason code (`@manual:Mx`, or a reason comment the planner can infer). A `@manual` scenario that still has full automatable steps (a data assertion, no visual/mock/a11y judgment) is now flagged by `sungen audit` as `MANUAL-AUTOMATABLE`, and business-critical scenarios you defer to `@manual` are reported as `DEPTH-DEFERRED` (they do NOT silently inflate `businessDepth`). Deferring automatable work to `@manual` lowers quality — automate it in the flow instead.
279
280
  - **Pick the right `@manual:Mx` code — it decides which driver can later automate the case** (`sungen audit` flags a code↔reason mismatch). Tag the code that matches the **oracle the reason describes**:
280
281