@qulib/core 0.7.0 → 0.9.0

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 (116) hide show
  1. package/README.md +30 -5
  2. package/bin/qulib.js +2 -3
  3. package/dist/__tests__/fixtures/api-fixture-repo/app/api/orders/route.d.ts +7 -0
  4. package/dist/__tests__/fixtures/api-fixture-repo/app/api/orders/route.d.ts.map +1 -0
  5. package/dist/__tests__/fixtures/api-fixture-repo/app/api/orders/route.js +7 -0
  6. package/dist/__tests__/fixtures/api-fixture-repo/app/api/users/route.d.ts +10 -0
  7. package/dist/__tests__/fixtures/api-fixture-repo/app/api/users/route.d.ts.map +1 -0
  8. package/dist/__tests__/fixtures/api-fixture-repo/app/api/users/route.js +9 -0
  9. package/dist/__tests__/fixtures/api-fixture-repo/pages/api/health.d.ts +9 -0
  10. package/dist/__tests__/fixtures/api-fixture-repo/pages/api/health.d.ts.map +1 -0
  11. package/dist/__tests__/fixtures/api-fixture-repo/pages/api/health.js +10 -0
  12. package/dist/__tests__/playwright-available.d.ts +32 -0
  13. package/dist/__tests__/playwright-available.d.ts.map +1 -0
  14. package/dist/__tests__/playwright-available.js +35 -0
  15. package/dist/adapters/api-adapter.d.ts +26 -0
  16. package/dist/adapters/api-adapter.d.ts.map +1 -1
  17. package/dist/adapters/api-adapter.js +156 -2
  18. package/dist/adapters/ci-results-adapter.d.ts +67 -0
  19. package/dist/adapters/ci-results-adapter.d.ts.map +1 -0
  20. package/dist/adapters/ci-results-adapter.js +143 -0
  21. package/dist/adapters/cypress-e2e-adapter.d.ts.map +1 -1
  22. package/dist/adapters/cypress-e2e-adapter.js +25 -2
  23. package/dist/adapters/playwright-adapter.d.ts.map +1 -1
  24. package/dist/adapters/playwright-adapter.js +94 -2
  25. package/dist/adapters/pr-metadata-adapter.d.ts +75 -0
  26. package/dist/adapters/pr-metadata-adapter.d.ts.map +1 -0
  27. package/dist/adapters/pr-metadata-adapter.js +146 -0
  28. package/dist/adapters/validate-specs.d.ts +55 -0
  29. package/dist/adapters/validate-specs.d.ts.map +1 -0
  30. package/dist/adapters/validate-specs.js +67 -0
  31. package/dist/baseline/baseline.d.ts +54 -0
  32. package/dist/baseline/baseline.d.ts.map +1 -0
  33. package/dist/baseline/baseline.js +252 -0
  34. package/dist/baseline/baseline.schema.d.ts +233 -0
  35. package/dist/baseline/baseline.schema.d.ts.map +1 -0
  36. package/dist/baseline/baseline.schema.js +59 -0
  37. package/dist/cli/confidence-run.d.ts +16 -0
  38. package/dist/cli/confidence-run.d.ts.map +1 -0
  39. package/dist/cli/confidence-run.js +158 -0
  40. package/dist/cli/index.d.ts +11 -1
  41. package/dist/cli/index.d.ts.map +1 -1
  42. package/dist/cli/index.js +80 -4
  43. package/dist/cli/scaffold-run.d.ts +86 -0
  44. package/dist/cli/scaffold-run.d.ts.map +1 -0
  45. package/dist/cli/scaffold-run.js +232 -0
  46. package/dist/cli/score-automation-run.d.ts +25 -0
  47. package/dist/cli/score-automation-run.d.ts.map +1 -0
  48. package/dist/cli/score-automation-run.js +123 -0
  49. package/dist/examples/notquality-dogfood/fixture.d.ts +166 -0
  50. package/dist/examples/notquality-dogfood/fixture.d.ts.map +1 -0
  51. package/dist/examples/notquality-dogfood/fixture.js +174 -0
  52. package/dist/examples/notquality-dogfood/run.d.ts +34 -0
  53. package/dist/examples/notquality-dogfood/run.d.ts.map +1 -0
  54. package/dist/examples/notquality-dogfood/run.js +139 -0
  55. package/dist/index.d.ts +18 -1
  56. package/dist/index.d.ts.map +1 -1
  57. package/dist/index.js +13 -0
  58. package/dist/recipes/a11y.d.ts +36 -0
  59. package/dist/recipes/a11y.d.ts.map +1 -0
  60. package/dist/recipes/a11y.js +118 -0
  61. package/dist/recipes/auth.d.ts +38 -0
  62. package/dist/recipes/auth.d.ts.map +1 -0
  63. package/dist/recipes/auth.js +156 -0
  64. package/dist/recipes/index.d.ts +26 -0
  65. package/dist/recipes/index.d.ts.map +1 -0
  66. package/dist/recipes/index.js +41 -0
  67. package/dist/recipes/nav.d.ts +34 -0
  68. package/dist/recipes/nav.d.ts.map +1 -0
  69. package/dist/recipes/nav.js +128 -0
  70. package/dist/recipes/seed.d.ts +34 -0
  71. package/dist/recipes/seed.d.ts.map +1 -0
  72. package/dist/recipes/seed.js +87 -0
  73. package/dist/scaffold-tests.d.ts +21 -0
  74. package/dist/scaffold-tests.d.ts.map +1 -1
  75. package/dist/scaffold-tests.js +12 -2
  76. package/dist/schemas/automation-maturity.schema.d.ts +8 -8
  77. package/dist/schemas/automation-maturity.schema.d.ts.map +1 -1
  78. package/dist/schemas/automation-maturity.schema.js +1 -0
  79. package/dist/schemas/confidence.schema.d.ts +526 -0
  80. package/dist/schemas/confidence.schema.d.ts.map +1 -0
  81. package/dist/schemas/confidence.schema.js +161 -0
  82. package/dist/schemas/gap-analysis.schema.d.ts +8 -8
  83. package/dist/schemas/gap-analysis.schema.js +1 -1
  84. package/dist/schemas/index.d.ts +3 -0
  85. package/dist/schemas/index.d.ts.map +1 -1
  86. package/dist/schemas/index.js +3 -0
  87. package/dist/schemas/public-surface.schema.d.ts +5 -5
  88. package/dist/schemas/recipe.schema.d.ts +66 -0
  89. package/dist/schemas/recipe.schema.d.ts.map +1 -0
  90. package/dist/schemas/recipe.schema.js +45 -0
  91. package/dist/schemas/repo-analysis.schema.d.ts +7 -7
  92. package/dist/schemas/views.schema.d.ts +234 -0
  93. package/dist/schemas/views.schema.d.ts.map +1 -0
  94. package/dist/schemas/views.schema.js +82 -0
  95. package/dist/tools/repo/api-surface.d.ts +59 -0
  96. package/dist/tools/repo/api-surface.d.ts.map +1 -0
  97. package/dist/tools/repo/api-surface.js +414 -0
  98. package/dist/tools/scoring/api-coverage.d.ts +74 -0
  99. package/dist/tools/scoring/api-coverage.d.ts.map +1 -0
  100. package/dist/tools/scoring/api-coverage.js +158 -0
  101. package/dist/tools/scoring/automation-maturity.d.ts +11 -1
  102. package/dist/tools/scoring/automation-maturity.d.ts.map +1 -1
  103. package/dist/tools/scoring/automation-maturity.js +43 -9
  104. package/dist/tools/scoring/confidence-from-qulib.d.ts +34 -0
  105. package/dist/tools/scoring/confidence-from-qulib.d.ts.map +1 -0
  106. package/dist/tools/scoring/confidence-from-qulib.js +206 -0
  107. package/dist/tools/scoring/confidence-views.d.ts +40 -0
  108. package/dist/tools/scoring/confidence-views.d.ts.map +1 -0
  109. package/dist/tools/scoring/confidence-views.js +163 -0
  110. package/dist/tools/scoring/confidence.d.ts +32 -0
  111. package/dist/tools/scoring/confidence.d.ts.map +1 -0
  112. package/dist/tools/scoring/confidence.js +180 -0
  113. package/dist/tools/scoring/levels.d.ts +15 -0
  114. package/dist/tools/scoring/levels.d.ts.map +1 -0
  115. package/dist/tools/scoring/levels.js +21 -0
  116. package/package.json +15 -7
@@ -0,0 +1,123 @@
1
+ /**
2
+ * `qulib score-automation` — score a local repo's test-automation maturity.
3
+ *
4
+ * Wraps `computeAutomationMaturity(repo)` (../tools/scoring/automation-maturity.js)
5
+ * as a first-class CLI surface. Until now the maturity score was only produced as a
6
+ * side-effect of `analyze --repo`; this exposes it standalone so an agent or CI can
7
+ * score a repo's automation directly, without a deployed URL to crawl.
8
+ *
9
+ * How a RepoAnalysis is obtained (smallest honest path, no duplicated logic):
10
+ * `scanRepo(repoPath)` (../tools/repo/scan.js) is a pure static scan — it infers
11
+ * routes, test files, test-id hygiene, CI presence and Cypress structure straight
12
+ * from the repo layout, with no browser/URL dependency. We reuse it (root CLAUDE.md:
13
+ * shared logic lives in core, never duplicate) and then call computeAutomationMaturity
14
+ * on its result so the printed report reflects a freshly-computed maturity object.
15
+ *
16
+ * Output honesty (root design principle — no false confidence):
17
+ * Per-dimension applicability (`applicable | not_applicable | unknown`) is surfaced
18
+ * verbatim. A `not_applicable` or `unknown` dimension reads as honest uncertainty
19
+ * with its reason/guidance — it is NEVER rendered as a "0/100" that looks like a
20
+ * real failing score. The overall score is normalized over applicable dimensions
21
+ * only (see computeAutomationMaturity), so absent capabilities don't drag it down.
22
+ *
23
+ * This file owns the `score-automation` subcommand end-to-end and is registered from
24
+ * cli/index.ts via `registerScoreAutomationCommand(program)`, so this command's build
25
+ * never edits index.ts (avoids collision with the parallel scaffold command).
26
+ */
27
+ import { resolve } from 'node:path';
28
+ import { existsSync, statSync } from 'node:fs';
29
+ import { scanRepo } from '../tools/repo/scan.js';
30
+ import { computeAutomationMaturity } from '../tools/scoring/automation-maturity.js';
31
+ /**
32
+ * Resolve `--repo` to an absolute path and assert it is an existing directory.
33
+ * Fails fast with a clear, actionable message rather than letting glob silently
34
+ * scan nothing and report a falsely-confident "everything is uncovered" score.
35
+ */
36
+ export function resolveRepoPath(repoOption, cwd = process.cwd()) {
37
+ if (!repoOption || !repoOption.trim()) {
38
+ throw new Error('score-automation requires --repo <path> pointing at a local repository to score.');
39
+ }
40
+ const abs = resolve(cwd, repoOption.trim());
41
+ if (!existsSync(abs)) {
42
+ throw new Error(`--repo path does not exist: ${abs}`);
43
+ }
44
+ if (!statSync(abs).isDirectory()) {
45
+ throw new Error(`--repo path is not a directory: ${abs}`);
46
+ }
47
+ return abs;
48
+ }
49
+ /** Single-letter glyph + word for a dimension's applicability, for the human report. */
50
+ function applicabilityTag(dim) {
51
+ const applicability = dim.applicability ?? 'applicable';
52
+ switch (applicability) {
53
+ case 'not_applicable':
54
+ return 'n/a';
55
+ case 'unknown':
56
+ return 'unknown';
57
+ default:
58
+ return 'applicable';
59
+ }
60
+ }
61
+ /**
62
+ * Render one dimension line. For applicable dimensions we show the score; for
63
+ * not_applicable / unknown we show the word and the reason — NOT a misleading "0".
64
+ */
65
+ function formatDimensionLine(dim) {
66
+ const applicability = dim.applicability ?? 'applicable';
67
+ const weightPct = `${Math.round(dim.weight * 100)}%`;
68
+ const head = ` - ${dim.dimension} [w=${weightPct}]`;
69
+ if (applicability === 'applicable') {
70
+ return `${head}: ${dim.score}/100`;
71
+ }
72
+ // Honest-uncertainty rendering: surface the label + reason, never a bare 0.
73
+ const reason = dim.reason ? ` — ${dim.reason}` : '';
74
+ return `${head}: ${applicabilityTag(dim)} (excluded from overall)${reason}`;
75
+ }
76
+ /** Build the human-readable report as a single string (kept pure so tests can assert on it). */
77
+ export function formatHumanReport(maturity) {
78
+ const lines = [];
79
+ lines.push(`[qulib] Automation maturity for ${maturity.repoPath}`);
80
+ lines.push(` overall: ${maturity.overallScore}/100 — ${maturity.label} (level ${maturity.level})`);
81
+ lines.push(' dimensions:');
82
+ for (const dim of maturity.dimensions) {
83
+ lines.push(formatDimensionLine(dim));
84
+ }
85
+ if (maturity.topRecommendations.length > 0) {
86
+ lines.push(' top recommendations:');
87
+ for (const rec of maturity.topRecommendations) {
88
+ lines.push(` • ${rec}`);
89
+ }
90
+ }
91
+ else {
92
+ lines.push(' top recommendations: none — applicable dimensions are at/above target.');
93
+ }
94
+ return lines.join('\n');
95
+ }
96
+ /**
97
+ * Core of the command, factored out of the action handler so node:test can drive it
98
+ * directly against a fixture repo without spawning a process.
99
+ *
100
+ * Reuses scanRepo (static repo intelligence) then computes maturity explicitly.
101
+ */
102
+ export async function runScoreAutomation(options, out = (line) => console.log(line)) {
103
+ const repoPath = resolveRepoPath(options.repo);
104
+ const repo = await scanRepo(repoPath);
105
+ const maturity = computeAutomationMaturity(repo);
106
+ if (options.json) {
107
+ out(JSON.stringify(maturity, null, 2));
108
+ }
109
+ else {
110
+ out(formatHumanReport(maturity));
111
+ }
112
+ return maturity;
113
+ }
114
+ export function registerScoreAutomationCommand(program) {
115
+ program
116
+ .command('score-automation')
117
+ .description("Score a local repo's test-automation maturity (overall + per-dimension, with honest applicability)")
118
+ .requiredOption('--repo <path>', 'Path to the local repository to score')
119
+ .option('--json', 'Emit the full AutomationMaturity object as JSON to stdout', false)
120
+ .action(async (options) => {
121
+ await runScoreAutomation({ repo: options.repo, json: Boolean(options.json) });
122
+ });
123
+ }
@@ -0,0 +1,166 @@
1
+ /**
2
+ * notquality DOGFOOD — real delivery signals fixture.
3
+ *
4
+ * P5 — qulib ingests notquality's own delivery signals → Release Confidence.
5
+ *
6
+ * PROVENANCE (all signals gathered 2026-06-04 via `gh` CLI against TapeshN/notquality):
7
+ *
8
+ * E2E run: gh run view 26931370208 -R TapeshN/notquality
9
+ * branch: notquality-app/prod-migrate
10
+ * sha: 5732ed5a37ba46c503d1319da83c5c6f4c8e5cb6
11
+ * workflow: E2E (playwright job, job ID 79451559555)
12
+ * completed: 2026-06-04T04:46:20Z (duration: ~6m4s)
13
+ * conclusion: success
14
+ * test files: 29 spec files on origin/main (git ls-tree -r origin/main)
15
+ * test cases: 194 total test() calls across 29 spec files
16
+ * fixme/skip: 26 test.fixme / test.skip markers
17
+ * active (non-fixme): 194 - 26 = 168 runnable tests
18
+ * all passed (CI conclusion: success)
19
+ *
20
+ * CI run: gh run view 26931370215 -R TapeshN/notquality
21
+ * branch: notquality-app/prod-migrate
22
+ * sha: 5732ed5a37ba46c503d1319da83c5c6f4c8e5cb6
23
+ * workflow: CI (validate job, job ID 79451559554)
24
+ * completed: 2026-06-04T04:46:20Z (duration: ~1m13s)
25
+ * conclusion: success
26
+ * steps: typecheck, lint, validate:bugs, prisma generate, build — all green
27
+ *
28
+ * PR: gh pr view 52 -R TapeshN/notquality --json number,url,reviewDecision,mergeable,statusCheckRollup
29
+ * title: "Run prisma migrate deploy before build on Vercel"
30
+ * branch: notquality-app/prod-migrate
31
+ * number: 52
32
+ * url: https://github.com/TapeshN/notquality/pull/52
33
+ * reviewDecision: "" (no review assigned — open PR, no blocking change requests)
34
+ * mergeable: MERGEABLE
35
+ * status checks: CI validate → SUCCESS, E2E playwright → SUCCESS, Vercel → SUCCESS
36
+ *
37
+ * Repo inventory (gh ls-tree -r origin/main, 2026-06-04):
38
+ * 29 spec files, 77 source .ts/.tsx files (excl. e2e), 19 route pages
39
+ * playwright.config.ts present (.github/workflows/e2e.yml present)
40
+ * CI: .github/workflows/ci.yml present (typecheck + lint + validate:bugs + build)
41
+ * auth: dual auth (iron-session playground + NextAuth platform)
42
+ * test-id hygiene: data-testid used in app/ + e2e/ (29 spec files use getByTestId)
43
+ * challenges: 1 seeded (legacy-bug-hunt-1); 16-card static list page (P5 truth-fix target)
44
+ *
45
+ * COLLECTION TIMESTAMP: 2026-06-04T09:00:00.000Z
46
+ *
47
+ * This fixture is intentionally FROZEN at this point in time. When real delivery
48
+ * signals are updated, create a NEW fixture version (fixture-v2.ts, etc.) and
49
+ * update the integration tests to use the latest. Never silently mutate this file —
50
+ * the provenance citation is load-bearing for the eval/audit trail.
51
+ */
52
+ export declare const FIXTURE_COLLECTION_TS = "2026-06-04T09:00:00.000Z";
53
+ /**
54
+ * Real CI run data from notquality E2E workflow (run #26931370208).
55
+ * Source: gh run view 26931370208 -R TapeshN/notquality
56
+ */
57
+ export declare const NOTQUALITY_E2E_RUN: {
58
+ /** ISO-8601 timestamp of run completion. */
59
+ readonly completedAt: "2026-06-04T04:46:20.000Z";
60
+ /** CI build step succeeded (typecheck, lint, validate:bugs, prisma generate, build). */
61
+ readonly buildPassed: true;
62
+ /**
63
+ * 168 runnable tests (194 total test() calls − 26 test.fixme/test.skip markers).
64
+ * All 168 passed in this run. Source: spec-file grep + run conclusion=success.
65
+ */
66
+ readonly testsPassed: 168;
67
+ readonly testsFailed: 0;
68
+ readonly testsErrored: 0;
69
+ /**
70
+ * 26 tests marked test.fixme or test.skip across 16 spec files.
71
+ * These are known quarantined defects (color-contrast a11y, label regression,
72
+ * EVT-001, duplicate challenge-title) — intentional, not infra failures.
73
+ */
74
+ readonly testsFlaky: 0;
75
+ readonly runUrl: "https://github.com/TapeshN/notquality/actions/runs/26931370208";
76
+ readonly workflowName: "E2E (playwright)";
77
+ };
78
+ /**
79
+ * Real CI validate-job data from notquality CI workflow (run #26931370215).
80
+ * Source: gh run view 26931370215 -R TapeshN/notquality
81
+ */
82
+ export declare const NOTQUALITY_CI_RUN: {
83
+ readonly completedAt: "2026-06-04T04:46:20.000Z";
84
+ readonly buildPassed: true;
85
+ readonly testsPassed: 0;
86
+ readonly testsFailed: 0;
87
+ readonly testsErrored: 0;
88
+ readonly runUrl: "https://github.com/TapeshN/notquality/actions/runs/26931370215";
89
+ readonly workflowName: "CI (validate)";
90
+ };
91
+ /**
92
+ * Real PR #52 metadata from notquality.
93
+ * Source: gh pr view 52 -R TapeshN/notquality --json number,url,reviewDecision,mergeable,statusCheckRollup
94
+ */
95
+ export declare const NOTQUALITY_PR_52: {
96
+ readonly number: 52;
97
+ readonly url: "https://github.com/TapeshN/notquality/pull/52";
98
+ readonly reviewDecision: null;
99
+ readonly mergeable: "MERGEABLE";
100
+ readonly statusCheckRollup: readonly [{
101
+ readonly state: "SUCCESS";
102
+ readonly name: "validate";
103
+ readonly targetUrl: "https://github.com/TapeshN/notquality/actions/runs/26931370215/job/79451559554";
104
+ }, {
105
+ readonly state: "SUCCESS";
106
+ readonly name: "playwright";
107
+ readonly targetUrl: "https://github.com/TapeshN/notquality/actions/runs/26931370208/job/79451559555";
108
+ }, {
109
+ readonly state: "SUCCESS";
110
+ readonly name: "Vercel";
111
+ readonly targetUrl: "https://vercel.com/tapeshnagarwal-7364s-projects/notquality-app/5mSLRhKKEdwvnqMoTY4XWxSePFYB";
112
+ }, {
113
+ readonly state: "SUCCESS";
114
+ readonly name: "Vercel Preview Comments";
115
+ readonly targetUrl: "https://vercel.com/github";
116
+ }];
117
+ readonly noPr: false;
118
+ };
119
+ /**
120
+ * Repo-level automation maturity facts (from static scan of origin/main, 2026-06-04).
121
+ * These facts drive the test-automation EvidenceItem used when qulib_score_automation
122
+ * is held (no live local scan in this fixture — see HELD note below).
123
+ *
124
+ * HELD: The live qulib_score_automation(repoPath) call requires the notquality
125
+ * repo to be available at an absolute path on the build machine and needs the full
126
+ * qulib CLI. The integration test uses these pre-scored facts instead of a live scan.
127
+ * The live scan is operator-gated (run `qulib score-automation <path>` locally).
128
+ */
129
+ export declare const NOTQUALITY_AUTOMATION_MATURITY: {
130
+ /**
131
+ * Estimated overall automation maturity score (0–100).
132
+ * Basis: Playwright present (framework-adoption ✓), 29 spec files covering 19 routes
133
+ * (test-coverage-breadth ~72%), e2e.yml + ci.yml present (ci-integration ✓),
134
+ * auth-test-coverage present (iron-session + NextAuth both tested ✓),
135
+ * data-testid used in spec files (test-id-hygiene present but not perfect — duplicate
136
+ * challenge-title test-id is a known defect, P5 truth-fix target), no component/unit
137
+ * tests yet (component-test-ratio = 0). Estimated L3 (60–79 range).
138
+ * Conservative estimate: 65/100.
139
+ */
140
+ readonly overallScore: 65;
141
+ readonly level: 3;
142
+ readonly label: "L3 — building maturity";
143
+ /**
144
+ * Key facts cited (no live scan was run; these are statically-derived):
145
+ * - framework: Playwright (playwright.config.ts present on origin/main)
146
+ * - specFiles: 29 (git ls-tree origin/main | grep "e2e/" | grep "spec.ts")
147
+ * - routePages: 19 (git ls-tree origin/main | grep "app/" | grep "page.tsx")
148
+ * - ciWorkflows: 2 (.github/workflows/ci.yml + e2e.yml)
149
+ * - authTests: yes (e2e/specs/auth/ — 3 spec files, 18 test() calls)
150
+ * - testIdHygiene: partial (data-testid used; duplicate challenge-title known defect)
151
+ * - componentTests: 0 (no vitest/jest unit tests in repo)
152
+ */
153
+ readonly topRecommendations: readonly ["Add vitest/jest unit tests for lib/ (scoring, API helpers) to raise component-test-ratio.", "Fix duplicate data-testid=\"challenge-title\" (breadcrumb vs h1) for unambiguous E2E selectors.", "Add dedicated challenges list + attempt E2E specs once fake list page is replaced with DB truth."];
154
+ readonly computedAt: "2026-06-04T09:00:00.000Z";
155
+ readonly repoPath: "TapeshN/notquality@5732ed5a (origin/main, 2026-06-04)";
156
+ };
157
+ /**
158
+ * Repository context for the ConfidenceSubject.
159
+ * ref: the commit SHA + branch context; tenantId: 'notquality' (multi-tenant from day one).
160
+ */
161
+ export declare const NOTQUALITY_SUBJECT: {
162
+ readonly kind: "release";
163
+ readonly ref: "TapeshN/notquality@5732ed5a (notquality-app/prod-migrate)";
164
+ readonly tenantId: "notquality";
165
+ };
166
+ //# sourceMappingURL=fixture.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fixture.d.ts","sourceRoot":"","sources":["../../../src/examples/notquality-dogfood/fixture.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AAEH,eAAO,MAAM,qBAAqB,6BAA6B,CAAC;AAEhE;;;GAGG;AACH,eAAO,MAAM,kBAAkB;IAC7B,4CAA4C;;IAE5C,wFAAwF;;IAExF;;;OAGG;;;;IAIH;;;;OAIG;;;;CAIK,CAAC;AAEX;;;GAGG;AACH,eAAO,MAAM,iBAAiB;;;;;;;;CAQpB,CAAC;AAEX;;;GAGG;AACH,eAAO,MAAM,gBAAgB;;;6BAGH,IAAI;;;;;;;;;;;;;;;;;;;;CAyBpB,CAAC;AAEX;;;;;;;;;GASG;AACH,eAAO,MAAM,8BAA8B;IACzC;;;;;;;;;OASG;;;;IAIH;;;;;;;;;OASG;;;;CAQK,CAAC;AAEX;;;GAGG;AACH,eAAO,MAAM,kBAAkB;;;;CAIrB,CAAC"}
@@ -0,0 +1,174 @@
1
+ /**
2
+ * notquality DOGFOOD — real delivery signals fixture.
3
+ *
4
+ * P5 — qulib ingests notquality's own delivery signals → Release Confidence.
5
+ *
6
+ * PROVENANCE (all signals gathered 2026-06-04 via `gh` CLI against TapeshN/notquality):
7
+ *
8
+ * E2E run: gh run view 26931370208 -R TapeshN/notquality
9
+ * branch: notquality-app/prod-migrate
10
+ * sha: 5732ed5a37ba46c503d1319da83c5c6f4c8e5cb6
11
+ * workflow: E2E (playwright job, job ID 79451559555)
12
+ * completed: 2026-06-04T04:46:20Z (duration: ~6m4s)
13
+ * conclusion: success
14
+ * test files: 29 spec files on origin/main (git ls-tree -r origin/main)
15
+ * test cases: 194 total test() calls across 29 spec files
16
+ * fixme/skip: 26 test.fixme / test.skip markers
17
+ * active (non-fixme): 194 - 26 = 168 runnable tests
18
+ * all passed (CI conclusion: success)
19
+ *
20
+ * CI run: gh run view 26931370215 -R TapeshN/notquality
21
+ * branch: notquality-app/prod-migrate
22
+ * sha: 5732ed5a37ba46c503d1319da83c5c6f4c8e5cb6
23
+ * workflow: CI (validate job, job ID 79451559554)
24
+ * completed: 2026-06-04T04:46:20Z (duration: ~1m13s)
25
+ * conclusion: success
26
+ * steps: typecheck, lint, validate:bugs, prisma generate, build — all green
27
+ *
28
+ * PR: gh pr view 52 -R TapeshN/notquality --json number,url,reviewDecision,mergeable,statusCheckRollup
29
+ * title: "Run prisma migrate deploy before build on Vercel"
30
+ * branch: notquality-app/prod-migrate
31
+ * number: 52
32
+ * url: https://github.com/TapeshN/notquality/pull/52
33
+ * reviewDecision: "" (no review assigned — open PR, no blocking change requests)
34
+ * mergeable: MERGEABLE
35
+ * status checks: CI validate → SUCCESS, E2E playwright → SUCCESS, Vercel → SUCCESS
36
+ *
37
+ * Repo inventory (gh ls-tree -r origin/main, 2026-06-04):
38
+ * 29 spec files, 77 source .ts/.tsx files (excl. e2e), 19 route pages
39
+ * playwright.config.ts present (.github/workflows/e2e.yml present)
40
+ * CI: .github/workflows/ci.yml present (typecheck + lint + validate:bugs + build)
41
+ * auth: dual auth (iron-session playground + NextAuth platform)
42
+ * test-id hygiene: data-testid used in app/ + e2e/ (29 spec files use getByTestId)
43
+ * challenges: 1 seeded (legacy-bug-hunt-1); 16-card static list page (P5 truth-fix target)
44
+ *
45
+ * COLLECTION TIMESTAMP: 2026-06-04T09:00:00.000Z
46
+ *
47
+ * This fixture is intentionally FROZEN at this point in time. When real delivery
48
+ * signals are updated, create a NEW fixture version (fixture-v2.ts, etc.) and
49
+ * update the integration tests to use the latest. Never silently mutate this file —
50
+ * the provenance citation is load-bearing for the eval/audit trail.
51
+ */
52
+ export const FIXTURE_COLLECTION_TS = '2026-06-04T09:00:00.000Z';
53
+ /**
54
+ * Real CI run data from notquality E2E workflow (run #26931370208).
55
+ * Source: gh run view 26931370208 -R TapeshN/notquality
56
+ */
57
+ export const NOTQUALITY_E2E_RUN = {
58
+ /** ISO-8601 timestamp of run completion. */
59
+ completedAt: '2026-06-04T04:46:20.000Z',
60
+ /** CI build step succeeded (typecheck, lint, validate:bugs, prisma generate, build). */
61
+ buildPassed: true,
62
+ /**
63
+ * 168 runnable tests (194 total test() calls − 26 test.fixme/test.skip markers).
64
+ * All 168 passed in this run. Source: spec-file grep + run conclusion=success.
65
+ */
66
+ testsPassed: 168,
67
+ testsFailed: 0,
68
+ testsErrored: 0,
69
+ /**
70
+ * 26 tests marked test.fixme or test.skip across 16 spec files.
71
+ * These are known quarantined defects (color-contrast a11y, label regression,
72
+ * EVT-001, duplicate challenge-title) — intentional, not infra failures.
73
+ */
74
+ testsFlaky: 0,
75
+ runUrl: 'https://github.com/TapeshN/notquality/actions/runs/26931370208',
76
+ workflowName: 'E2E (playwright)',
77
+ };
78
+ /**
79
+ * Real CI validate-job data from notquality CI workflow (run #26931370215).
80
+ * Source: gh run view 26931370215 -R TapeshN/notquality
81
+ */
82
+ export const NOTQUALITY_CI_RUN = {
83
+ completedAt: '2026-06-04T04:46:20.000Z',
84
+ buildPassed: true,
85
+ testsPassed: 0, // CI job runs lint/typecheck/validate:bugs — no Jest/Vitest unit tests yet
86
+ testsFailed: 0,
87
+ testsErrored: 0,
88
+ runUrl: 'https://github.com/TapeshN/notquality/actions/runs/26931370215',
89
+ workflowName: 'CI (validate)',
90
+ };
91
+ /**
92
+ * Real PR #52 metadata from notquality.
93
+ * Source: gh pr view 52 -R TapeshN/notquality --json number,url,reviewDecision,mergeable,statusCheckRollup
94
+ */
95
+ export const NOTQUALITY_PR_52 = {
96
+ number: 52,
97
+ url: 'https://github.com/TapeshN/notquality/pull/52',
98
+ reviewDecision: null, // no review assigned — open PR, no blocking changes_requested
99
+ mergeable: 'MERGEABLE',
100
+ statusCheckRollup: [
101
+ {
102
+ state: 'SUCCESS',
103
+ name: 'validate',
104
+ targetUrl: 'https://github.com/TapeshN/notquality/actions/runs/26931370215/job/79451559554',
105
+ },
106
+ {
107
+ state: 'SUCCESS',
108
+ name: 'playwright',
109
+ targetUrl: 'https://github.com/TapeshN/notquality/actions/runs/26931370208/job/79451559555',
110
+ },
111
+ {
112
+ state: 'SUCCESS',
113
+ name: 'Vercel',
114
+ targetUrl: 'https://vercel.com/tapeshnagarwal-7364s-projects/notquality-app/5mSLRhKKEdwvnqMoTY4XWxSePFYB',
115
+ },
116
+ {
117
+ state: 'SUCCESS',
118
+ name: 'Vercel Preview Comments',
119
+ targetUrl: 'https://vercel.com/github',
120
+ },
121
+ ],
122
+ noPr: false,
123
+ };
124
+ /**
125
+ * Repo-level automation maturity facts (from static scan of origin/main, 2026-06-04).
126
+ * These facts drive the test-automation EvidenceItem used when qulib_score_automation
127
+ * is held (no live local scan in this fixture — see HELD note below).
128
+ *
129
+ * HELD: The live qulib_score_automation(repoPath) call requires the notquality
130
+ * repo to be available at an absolute path on the build machine and needs the full
131
+ * qulib CLI. The integration test uses these pre-scored facts instead of a live scan.
132
+ * The live scan is operator-gated (run `qulib score-automation <path>` locally).
133
+ */
134
+ export const NOTQUALITY_AUTOMATION_MATURITY = {
135
+ /**
136
+ * Estimated overall automation maturity score (0–100).
137
+ * Basis: Playwright present (framework-adoption ✓), 29 spec files covering 19 routes
138
+ * (test-coverage-breadth ~72%), e2e.yml + ci.yml present (ci-integration ✓),
139
+ * auth-test-coverage present (iron-session + NextAuth both tested ✓),
140
+ * data-testid used in spec files (test-id-hygiene present but not perfect — duplicate
141
+ * challenge-title test-id is a known defect, P5 truth-fix target), no component/unit
142
+ * tests yet (component-test-ratio = 0). Estimated L3 (60–79 range).
143
+ * Conservative estimate: 65/100.
144
+ */
145
+ overallScore: 65,
146
+ level: 3,
147
+ label: 'L3 — building maturity',
148
+ /**
149
+ * Key facts cited (no live scan was run; these are statically-derived):
150
+ * - framework: Playwright (playwright.config.ts present on origin/main)
151
+ * - specFiles: 29 (git ls-tree origin/main | grep "e2e/" | grep "spec.ts")
152
+ * - routePages: 19 (git ls-tree origin/main | grep "app/" | grep "page.tsx")
153
+ * - ciWorkflows: 2 (.github/workflows/ci.yml + e2e.yml)
154
+ * - authTests: yes (e2e/specs/auth/ — 3 spec files, 18 test() calls)
155
+ * - testIdHygiene: partial (data-testid used; duplicate challenge-title known defect)
156
+ * - componentTests: 0 (no vitest/jest unit tests in repo)
157
+ */
158
+ topRecommendations: [
159
+ 'Add vitest/jest unit tests for lib/ (scoring, API helpers) to raise component-test-ratio.',
160
+ 'Fix duplicate data-testid="challenge-title" (breadcrumb vs h1) for unambiguous E2E selectors.',
161
+ 'Add dedicated challenges list + attempt E2E specs once fake list page is replaced with DB truth.',
162
+ ],
163
+ computedAt: FIXTURE_COLLECTION_TS,
164
+ repoPath: 'TapeshN/notquality@5732ed5a (origin/main, 2026-06-04)',
165
+ };
166
+ /**
167
+ * Repository context for the ConfidenceSubject.
168
+ * ref: the commit SHA + branch context; tenantId: 'notquality' (multi-tenant from day one).
169
+ */
170
+ export const NOTQUALITY_SUBJECT = {
171
+ kind: 'release',
172
+ ref: 'TapeshN/notquality@5732ed5a (notquality-app/prod-migrate)',
173
+ tenantId: 'notquality',
174
+ };
@@ -0,0 +1,34 @@
1
+ /**
2
+ * notquality DOGFOOD — runnable example.
3
+ *
4
+ * P5: qulib scoring a real app we own. This is the confidence-layer thesis
5
+ * proven on a live delivery pipeline: qulib ingests notquality's CI results,
6
+ * PR metadata, and automation maturity, then emits a real Release Confidence
7
+ * score + verdict.
8
+ *
9
+ * Run with:
10
+ * npx tsx packages/core/src/examples/notquality-dogfood/run.ts
11
+ *
12
+ * What it does:
13
+ * 1. Loads DATED real-sample fixture (provenance: gh CLI, 2026-06-04).
14
+ * 2. Maps CI run → ciResultsToEvidence (EvidenceItem, source='ci-results').
15
+ * 3. Maps PR metadata → prMetadataToEvidence (EvidenceItem, source='deploy-metadata').
16
+ * 4. Builds a test-automation EvidenceItem from the pre-scored maturity facts.
17
+ * 5. Calls computeReleaseConfidence → fused score + verdict.
18
+ * 6. Prints a structured JSON report to stdout.
19
+ *
20
+ * HELD signals (operator-gated, not run here):
21
+ * - qulib_score_automation live scan (requires local notquality checkout + qulib CLI).
22
+ * Instead this example uses the statically-derived NOTQUALITY_AUTOMATION_MATURITY
23
+ * from the fixture (documented, conservative, clearly labelled as pre-scored).
24
+ * - analyze_app deployed crawl (external HTTP + optional LLM cost).
25
+ * The live-app-quality and accessibility EvidenceItems are omitted here; the
26
+ * pipeline runs correctly with a subset of evidence (partial-evidence honesty).
27
+ *
28
+ * Output format: NDJSON line appended to stdout (one JSON object per run).
29
+ * The rubricVersion field versions the roll-up formula so it can evolve without
30
+ * silently changing meaning (P5 stopgap formula; retires when qulib's own aggregator
31
+ * ships in P3/P4 roadmap).
32
+ */
33
+ export {};
34
+ //# sourceMappingURL=run.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/examples/notquality-dogfood/run.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG"}
@@ -0,0 +1,139 @@
1
+ /**
2
+ * notquality DOGFOOD — runnable example.
3
+ *
4
+ * P5: qulib scoring a real app we own. This is the confidence-layer thesis
5
+ * proven on a live delivery pipeline: qulib ingests notquality's CI results,
6
+ * PR metadata, and automation maturity, then emits a real Release Confidence
7
+ * score + verdict.
8
+ *
9
+ * Run with:
10
+ * npx tsx packages/core/src/examples/notquality-dogfood/run.ts
11
+ *
12
+ * What it does:
13
+ * 1. Loads DATED real-sample fixture (provenance: gh CLI, 2026-06-04).
14
+ * 2. Maps CI run → ciResultsToEvidence (EvidenceItem, source='ci-results').
15
+ * 3. Maps PR metadata → prMetadataToEvidence (EvidenceItem, source='deploy-metadata').
16
+ * 4. Builds a test-automation EvidenceItem from the pre-scored maturity facts.
17
+ * 5. Calls computeReleaseConfidence → fused score + verdict.
18
+ * 6. Prints a structured JSON report to stdout.
19
+ *
20
+ * HELD signals (operator-gated, not run here):
21
+ * - qulib_score_automation live scan (requires local notquality checkout + qulib CLI).
22
+ * Instead this example uses the statically-derived NOTQUALITY_AUTOMATION_MATURITY
23
+ * from the fixture (documented, conservative, clearly labelled as pre-scored).
24
+ * - analyze_app deployed crawl (external HTTP + optional LLM cost).
25
+ * The live-app-quality and accessibility EvidenceItems are omitted here; the
26
+ * pipeline runs correctly with a subset of evidence (partial-evidence honesty).
27
+ *
28
+ * Output format: NDJSON line appended to stdout (one JSON object per run).
29
+ * The rubricVersion field versions the roll-up formula so it can evolve without
30
+ * silently changing meaning (P5 stopgap formula; retires when qulib's own aggregator
31
+ * ships in P3/P4 roadmap).
32
+ */
33
+ import { ciResultsToEvidence } from '../../adapters/ci-results-adapter.js';
34
+ import { prMetadataToEvidence } from '../../adapters/pr-metadata-adapter.js';
35
+ import { computeReleaseConfidence } from '../../tools/scoring/confidence.js';
36
+ import { FIXTURE_COLLECTION_TS, NOTQUALITY_E2E_RUN, NOTQUALITY_PR_52, NOTQUALITY_AUTOMATION_MATURITY, NOTQUALITY_SUBJECT, } from './fixture.js';
37
+ // ---------------------------------------------------------------------------
38
+ // Build the evidence bundle from real notquality delivery signals
39
+ // ---------------------------------------------------------------------------
40
+ /**
41
+ * E2E CI run → ci-results evidence item.
42
+ * Source: run #26931370208, E2E workflow, 2026-06-04.
43
+ */
44
+ const e2eEvidence = ciResultsToEvidence(NOTQUALITY_E2E_RUN, FIXTURE_COLLECTION_TS);
45
+ /**
46
+ * PR #52 metadata → deploy-metadata evidence item.
47
+ * Source: gh pr view 52 -R TapeshN/notquality, 2026-06-04.
48
+ */
49
+ const prEvidence = prMetadataToEvidence({
50
+ ...NOTQUALITY_PR_52,
51
+ statusCheckRollup: NOTQUALITY_PR_52.statusCheckRollup.map((c) => ({ ...c })),
52
+ }, FIXTURE_COLLECTION_TS);
53
+ /**
54
+ * Automation maturity → test-automation evidence item.
55
+ * Source: static scan of origin/main, 2026-06-04.
56
+ * NOTE: This is a pre-scored estimate. Live qulib_score_automation(repoPath)
57
+ * is the authoritative signal; this is the operator-held substitute.
58
+ */
59
+ const automationEvidence = {
60
+ source: 'test-automation',
61
+ score: NOTQUALITY_AUTOMATION_MATURITY.overallScore,
62
+ weight: 0.22,
63
+ applicability: 'applicable',
64
+ blocking: false,
65
+ evidence: [
66
+ `Automation maturity: ${NOTQUALITY_AUTOMATION_MATURITY.label} (score ${NOTQUALITY_AUTOMATION_MATURITY.overallScore})`,
67
+ `Source: static scan of ${NOTQUALITY_AUTOMATION_MATURITY.repoPath}`,
68
+ '29 E2E spec files, 168 runnable tests (26 fixme/skip quarantined), Playwright + CI wired.',
69
+ 'No vitest/jest unit tests yet (component-test-ratio = 0).',
70
+ ],
71
+ recommendations: NOTQUALITY_AUTOMATION_MATURITY.topRecommendations.slice(),
72
+ reason: undefined,
73
+ collectedAt: FIXTURE_COLLECTION_TS,
74
+ collector: {
75
+ tool: 'qulib_score_automation.pre-scored',
76
+ inputRef: NOTQUALITY_AUTOMATION_MATURITY.repoPath,
77
+ },
78
+ };
79
+ // ---------------------------------------------------------------------------
80
+ // Assemble ConfidenceInput and compute the score
81
+ // ---------------------------------------------------------------------------
82
+ const input = {
83
+ subject: NOTQUALITY_SUBJECT,
84
+ evidence: [e2eEvidence, prEvidence, automationEvidence],
85
+ policy: {
86
+ passThreshold: 80,
87
+ failThreshold: 30,
88
+ maxListLength: 5,
89
+ requiredSources: [],
90
+ // Default weights apply; explicit ci-results weight already on the EvidenceItem.
91
+ },
92
+ };
93
+ const result = computeReleaseConfidence(input);
94
+ // ---------------------------------------------------------------------------
95
+ // Emit the structured report
96
+ // ---------------------------------------------------------------------------
97
+ const report = {
98
+ rubricVersion: 'p5-dogfood-v1',
99
+ collectionTimestamp: FIXTURE_COLLECTION_TS,
100
+ tenantId: NOTQUALITY_SUBJECT.tenantId,
101
+ subject: NOTQUALITY_SUBJECT.ref,
102
+ sources: {
103
+ e2eRun: NOTQUALITY_E2E_RUN.runUrl,
104
+ ciRun: 'https://github.com/TapeshN/notquality/actions/runs/26931370215',
105
+ pr: NOTQUALITY_PR_52.url,
106
+ automationMaturity: `pre-scored from ${NOTQUALITY_AUTOMATION_MATURITY.repoPath}`,
107
+ },
108
+ heldSignals: [
109
+ 'qulib_score_automation live scan (operator-gated: requires local notquality checkout)',
110
+ 'analyze_app deployed crawl (operator-gated: external HTTP + optional LLM cost)',
111
+ ],
112
+ confidence: {
113
+ score: result.confidenceScore,
114
+ verdict: result.verdict,
115
+ level: result.level,
116
+ label: result.label,
117
+ },
118
+ contributions: result.contributions.map((c) => ({
119
+ source: c.source,
120
+ score: c.score,
121
+ effectiveWeight: c.effectiveWeight,
122
+ applicability: c.applicability,
123
+ })),
124
+ topRisks: result.topRisks,
125
+ recommendations: result.recommendedNextChecks,
126
+ honestyNotes: result.honestyNotes,
127
+ scoreFormula: result.scoreFormula,
128
+ };
129
+ // eslint-disable-next-line no-console
130
+ console.log(JSON.stringify(report, null, 2));
131
+ // Print a human-readable summary line on stderr for quick reading in CI.
132
+ const verdictEmoji = {
133
+ ship: 'SHIP',
134
+ caution: 'CAUTION',
135
+ hold: 'HOLD',
136
+ block: 'BLOCK',
137
+ };
138
+ const v = verdictEmoji[result.verdict] ?? result.verdict.toUpperCase();
139
+ process.stderr.write(`[notquality dogfood] confidence=${result.confidenceScore ?? 'null'} verdict=${v} (${result.label})\n`);