reflection-check 0.0.1

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 (133) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +55 -0
  3. package/dist/adapters/route-manifest.d.ts +3 -0
  4. package/dist/adapters/route-manifest.js +98 -0
  5. package/dist/adapters/route-manifest.js.map +1 -0
  6. package/dist/cli.d.ts +3 -0
  7. package/dist/cli.js +93 -0
  8. package/dist/cli.js.map +1 -0
  9. package/dist/commands/doctor.d.ts +4 -0
  10. package/dist/commands/doctor.js +5 -0
  11. package/dist/commands/doctor.js.map +1 -0
  12. package/dist/commands/gc.d.ts +8 -0
  13. package/dist/commands/gc.js +45 -0
  14. package/dist/commands/gc.js.map +1 -0
  15. package/dist/commands/review.d.ts +7 -0
  16. package/dist/commands/review.js +149 -0
  17. package/dist/commands/review.js.map +1 -0
  18. package/dist/commands/run.d.ts +10 -0
  19. package/dist/commands/run.js +168 -0
  20. package/dist/commands/run.js.map +1 -0
  21. package/dist/commands/update.d.ts +11 -0
  22. package/dist/commands/update.js +183 -0
  23. package/dist/commands/update.js.map +1 -0
  24. package/dist/contracts/browser/assertions.d.ts +34 -0
  25. package/dist/contracts/browser/assertions.js +87 -0
  26. package/dist/contracts/browser/assertions.js.map +1 -0
  27. package/dist/contracts/browser/browser-contract.d.ts +13 -0
  28. package/dist/contracts/browser/browser-contract.js +35 -0
  29. package/dist/contracts/browser/browser-contract.js.map +1 -0
  30. package/dist/contracts/browser/console-observer.d.ts +6 -0
  31. package/dist/contracts/browser/console-observer.js +14 -0
  32. package/dist/contracts/browser/console-observer.js.map +1 -0
  33. package/dist/contracts/browser/overflow-check.d.ts +6 -0
  34. package/dist/contracts/browser/overflow-check.js +15 -0
  35. package/dist/contracts/browser/overflow-check.js.map +1 -0
  36. package/dist/contracts/browser/route-runner.d.ts +21 -0
  37. package/dist/contracts/browser/route-runner.js +98 -0
  38. package/dist/contracts/browser/route-runner.js.map +1 -0
  39. package/dist/contracts/component/component-visual-contract.d.ts +30 -0
  40. package/dist/contracts/component/component-visual-contract.js +147 -0
  41. package/dist/contracts/component/component-visual-contract.js.map +1 -0
  42. package/dist/contracts/design/command-adapter.d.ts +17 -0
  43. package/dist/contracts/design/command-adapter.js +60 -0
  44. package/dist/contracts/design/command-adapter.js.map +1 -0
  45. package/dist/contracts/design/design-contract.d.ts +8 -0
  46. package/dist/contracts/design/design-contract.js +149 -0
  47. package/dist/contracts/design/design-contract.js.map +1 -0
  48. package/dist/contracts/visual/baseline-compare.d.ts +19 -0
  49. package/dist/contracts/visual/baseline-compare.js +94 -0
  50. package/dist/contracts/visual/baseline-compare.js.map +1 -0
  51. package/dist/contracts/visual/image-diff.d.ts +27 -0
  52. package/dist/contracts/visual/image-diff.js +58 -0
  53. package/dist/contracts/visual/image-diff.js.map +1 -0
  54. package/dist/contracts/visual/thresholds.d.ts +15 -0
  55. package/dist/contracts/visual/thresholds.js +11 -0
  56. package/dist/contracts/visual/thresholds.js.map +1 -0
  57. package/dist/contracts/visual/visual-contract.d.ts +11 -0
  58. package/dist/contracts/visual/visual-contract.js +32 -0
  59. package/dist/contracts/visual/visual-contract.js.map +1 -0
  60. package/dist/core/artifact-store.d.ts +18 -0
  61. package/dist/core/artifact-store.js +105 -0
  62. package/dist/core/artifact-store.js.map +1 -0
  63. package/dist/core/baseline-store.d.ts +18 -0
  64. package/dist/core/baseline-store.js +56 -0
  65. package/dist/core/baseline-store.js.map +1 -0
  66. package/dist/core/config.d.ts +129 -0
  67. package/dist/core/config.js +159 -0
  68. package/dist/core/config.js.map +1 -0
  69. package/dist/core/define-reflection.d.ts +2 -0
  70. package/dist/core/define-reflection.js +4 -0
  71. package/dist/core/define-reflection.js.map +1 -0
  72. package/dist/core/exit-codes.d.ts +7 -0
  73. package/dist/core/exit-codes.js +9 -0
  74. package/dist/core/exit-codes.js.map +1 -0
  75. package/dist/core/failure-classifier.d.ts +3 -0
  76. package/dist/core/failure-classifier.js +19 -0
  77. package/dist/core/failure-classifier.js.map +1 -0
  78. package/dist/core/gc.d.ts +19 -0
  79. package/dist/core/gc.js +161 -0
  80. package/dist/core/gc.js.map +1 -0
  81. package/dist/core/manifest.d.ts +23 -0
  82. package/dist/core/manifest.js +21 -0
  83. package/dist/core/manifest.js.map +1 -0
  84. package/dist/core/redaction.d.ts +3 -0
  85. package/dist/core/redaction.js +63 -0
  86. package/dist/core/redaction.js.map +1 -0
  87. package/dist/core/report-schema.d.ts +262 -0
  88. package/dist/core/report-schema.js +112 -0
  89. package/dist/core/report-schema.js.map +1 -0
  90. package/dist/core/report-writer.d.ts +4 -0
  91. package/dist/core/report-writer.js +77 -0
  92. package/dist/core/report-writer.js.map +1 -0
  93. package/dist/core/server-manager.d.ts +23 -0
  94. package/dist/core/server-manager.js +64 -0
  95. package/dist/core/server-manager.js.map +1 -0
  96. package/dist/core/target-ir.d.ts +64 -0
  97. package/dist/core/target-ir.js +85 -0
  98. package/dist/core/target-ir.js.map +1 -0
  99. package/dist/index.d.ts +2 -0
  100. package/dist/index.js +2 -0
  101. package/dist/index.js.map +1 -0
  102. package/dist/integrations/playwright/browser-manager.d.ts +2 -0
  103. package/dist/integrations/playwright/browser-manager.js +5 -0
  104. package/dist/integrations/playwright/browser-manager.js.map +1 -0
  105. package/dist/integrations/playwright/context-factory.d.ts +7 -0
  106. package/dist/integrations/playwright/context-factory.js +19 -0
  107. package/dist/integrations/playwright/context-factory.js.map +1 -0
  108. package/dist/integrations/playwright/trace-policy.d.ts +5 -0
  109. package/dist/integrations/playwright/trace-policy.js +7 -0
  110. package/dist/integrations/playwright/trace-policy.js.map +1 -0
  111. package/dist/integrations/storybook/index-json.d.ts +21 -0
  112. package/dist/integrations/storybook/index-json.js +44 -0
  113. package/dist/integrations/storybook/index-json.js.map +1 -0
  114. package/dist/integrations/storybook/server.d.ts +8 -0
  115. package/dist/integrations/storybook/server.js +23 -0
  116. package/dist/integrations/storybook/server.js.map +1 -0
  117. package/dist/integrations/storybook/story-url.d.ts +2 -0
  118. package/dist/integrations/storybook/story-url.js +13 -0
  119. package/dist/integrations/storybook/story-url.js.map +1 -0
  120. package/dist/utils/process.d.ts +9 -0
  121. package/dist/utils/process.js +69 -0
  122. package/dist/utils/process.js.map +1 -0
  123. package/docs/agent-workflows.md +146 -0
  124. package/docs/artifacts-and-gc.md +125 -0
  125. package/docs/browser-contract.md +98 -0
  126. package/docs/ci.md +44 -0
  127. package/docs/configuration.md +210 -0
  128. package/docs/getting-started.md +166 -0
  129. package/docs/plans/reflection-implementation-plan.md +898 -0
  130. package/docs/target-ir-and-adapters.md +111 -0
  131. package/docs/validation-process.md +172 -0
  132. package/docs/visual-contract.md +174 -0
  133. package/package.json +62 -0
@@ -0,0 +1,85 @@
1
+ export function compileReflectionTargets(config) {
2
+ const targets = [];
3
+ const browser = config.contracts.browser;
4
+ if (browser && browser.enabled !== false) {
5
+ for (const route of browser.routes) {
6
+ targets.push({
7
+ id: route.id,
8
+ family: 'browser-route',
9
+ source: 'reflection-config',
10
+ runModes: ['smoke', 'full'],
11
+ blocking: browser.blocking ?? true,
12
+ route: {
13
+ path: route.path,
14
+ ...(route.name ? { name: route.name } : {}),
15
+ viewports: route.viewports,
16
+ expects: route.expects
17
+ },
18
+ browser: {
19
+ baseUrl: browser.baseUrl,
20
+ maskSelectors: browser.maskSelectors ?? []
21
+ }
22
+ });
23
+ }
24
+ for (const visualCase of browser.visualSmoke ?? []) {
25
+ targets.push({
26
+ id: visualCase.id,
27
+ family: 'route-visual',
28
+ source: 'reflection-config',
29
+ runModes: ['smoke', 'full'],
30
+ blocking: visualCase.blocking === true || visualCase.strict === true,
31
+ route: {
32
+ path: visualCase.route
33
+ },
34
+ visual: {
35
+ viewport: visualCase.viewport,
36
+ baseline: visualCase.baseline,
37
+ ...(visualCase.baselineRoot ? { baselineRoot: visualCase.baselineRoot } : {}),
38
+ ...(visualCase.threshold !== undefined ? { threshold: visualCase.threshold } : {})
39
+ }
40
+ });
41
+ }
42
+ }
43
+ const component = config.contracts.component;
44
+ if (component && component.enabled !== false) {
45
+ for (const componentCase of component.cases) {
46
+ targets.push({
47
+ id: componentCase.id,
48
+ family: 'component-visual',
49
+ source: 'reflection-config',
50
+ runModes: ['visual', 'full'],
51
+ blocking: componentCase.blocking === true || componentCase.strict === true,
52
+ story: {
53
+ storyId: componentCase.storyId,
54
+ statePolicy: componentCase.browserState ? 'browser-forced-with-stabilization' : 'story-controlled',
55
+ ...(componentCase.stateNote ? { stateNote: componentCase.stateNote } : {}),
56
+ ...(componentCase.browserState ? { browserState: componentCase.browserState } : {})
57
+ },
58
+ visual: {
59
+ viewport: componentCase.viewport ?? 'component',
60
+ baseline: componentCase.baseline,
61
+ ...(componentCase.baselineRoot ? { baselineRoot: componentCase.baselineRoot } : {}),
62
+ ...(componentCase.threshold !== undefined ? { threshold: componentCase.threshold } : {})
63
+ }
64
+ });
65
+ }
66
+ }
67
+ const design = config.contracts.design;
68
+ if (design && design.enabled !== false) {
69
+ for (const designCommand of design.commands) {
70
+ targets.push({
71
+ id: designCommand.id,
72
+ family: 'design-command',
73
+ source: 'reflection-config',
74
+ runModes: ['design', 'full'],
75
+ blocking: designCommand.blocking ?? true,
76
+ command: {
77
+ command: designCommand.command,
78
+ ...(designCommand.cwd ? { cwd: designCommand.cwd } : {})
79
+ }
80
+ });
81
+ }
82
+ }
83
+ return { project: config.project, targets };
84
+ }
85
+ //# sourceMappingURL=target-ir.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"target-ir.js","sourceRoot":"","sources":["../../src/core/target-ir.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export { defineReflection } from './core/define-reflection.js';
2
+ export type { ReflectionConfig, ReflectionConfigInput, RunMode } from './core/config.js';
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export { defineReflection } from './core/define-reflection.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { type Browser } from 'playwright';
2
+ export declare function launchBrowser(): Promise<Browser>;
@@ -0,0 +1,5 @@
1
+ import { chromium } from 'playwright';
2
+ export async function launchBrowser() {
3
+ return chromium.launch({ headless: true });
4
+ }
5
+ //# sourceMappingURL=browser-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-manager.js","sourceRoot":"","sources":["../../../src/integrations/playwright/browser-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAgB,MAAM,YAAY,CAAC;AAEpD,MAAM,CAAC,KAAK,UAAU,aAAa;IACjC,OAAO,QAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;AAC7C,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { Browser, BrowserContext } from 'playwright';
2
+ export type ViewportName = 'desktop' | 'mobile' | 'tablet' | 'component';
3
+ export declare function resolveViewport(viewport: string): {
4
+ width: number;
5
+ height: number;
6
+ };
7
+ export declare function createBrowserContext(browser: Browser, viewport: string): Promise<BrowserContext>;
@@ -0,0 +1,19 @@
1
+ const viewportPresets = {
2
+ desktop: { width: 1440, height: 900 },
3
+ mobile: { width: 390, height: 844 },
4
+ tablet: { width: 820, height: 1180 },
5
+ component: { width: 390, height: 220 }
6
+ };
7
+ export function resolveViewport(viewport) {
8
+ return viewportPresets[viewport] ?? viewportPresets.desktop;
9
+ }
10
+ export async function createBrowserContext(browser, viewport) {
11
+ return browser.newContext({
12
+ viewport: resolveViewport(viewport),
13
+ deviceScaleFactor: 1,
14
+ locale: 'en-US',
15
+ timezoneId: 'Europe/Stockholm',
16
+ colorScheme: 'light'
17
+ });
18
+ }
19
+ //# sourceMappingURL=context-factory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-factory.js","sourceRoot":"","sources":["../../../src/integrations/playwright/context-factory.ts"],"names":[],"mappings":"AAIA,MAAM,eAAe,GAA4D;IAC/E,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE;IACrC,MAAM,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE;IACnC,MAAM,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE;IACpC,SAAS,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE;CACvC,CAAC;AAEF,MAAM,UAAU,eAAe,CAAC,QAAgB;IAC9C,OAAO,eAAe,CAAC,QAAwB,CAAC,IAAI,eAAe,CAAC,OAAO,CAAC;AAC9E,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,OAAgB,EAAE,QAAgB;IAC3E,OAAO,OAAO,CAAC,UAAU,CAAC;QACxB,QAAQ,EAAE,eAAe,CAAC,QAAQ,CAAC;QACnC,iBAAiB,EAAE,CAAC;QACpB,MAAM,EAAE,OAAO;QACf,UAAU,EAAE,kBAAkB;QAC9B,WAAW,EAAE,OAAO;KACrB,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,5 @@
1
+ export type TracePolicy = {
2
+ trace: 'off' | 'retain-on-failure' | 'on';
3
+ video: 'off' | 'retain-on-failure' | 'on';
4
+ };
5
+ export declare function createTracePolicy(overrides?: Partial<TracePolicy>): TracePolicy;
@@ -0,0 +1,7 @@
1
+ export function createTracePolicy(overrides = {}) {
2
+ return {
3
+ trace: overrides.trace ?? 'off',
4
+ video: overrides.video ?? 'off'
5
+ };
6
+ }
7
+ //# sourceMappingURL=trace-policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trace-policy.js","sourceRoot":"","sources":["../../../src/integrations/playwright/trace-policy.ts"],"names":[],"mappings":"AAKA,MAAM,UAAU,iBAAiB,CAAC,YAAkC,EAAE;IACpE,OAAO;QACL,KAAK,EAAE,SAAS,CAAC,KAAK,IAAI,KAAK;QAC/B,KAAK,EAAE,SAAS,CAAC,KAAK,IAAI,KAAK;KAChC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,21 @@
1
+ import { z } from 'zod';
2
+ declare const StorybookEntrySchema: z.ZodObject<{
3
+ id: z.ZodString;
4
+ title: z.ZodString;
5
+ name: z.ZodString;
6
+ type: z.ZodString;
7
+ importPath: z.ZodOptional<z.ZodString>;
8
+ }, z.core.$strip>;
9
+ declare const StorybookIndexSchema: z.ZodObject<{
10
+ entries: z.ZodRecord<z.ZodString, z.ZodObject<{
11
+ id: z.ZodString;
12
+ title: z.ZodString;
13
+ name: z.ZodString;
14
+ type: z.ZodString;
15
+ importPath: z.ZodOptional<z.ZodString>;
16
+ }, z.core.$strip>>;
17
+ }, z.core.$strip>;
18
+ export type StorybookEntry = z.output<typeof StorybookEntrySchema>;
19
+ export type StorybookIndex = z.output<typeof StorybookIndexSchema>;
20
+ export declare function loadStorybookIndex(baseUrl: string): Promise<StorybookIndex>;
21
+ export {};
@@ -0,0 +1,44 @@
1
+ import { z } from 'zod';
2
+ const StorybookEntrySchema = z.object({
3
+ id: z.string().min(1),
4
+ title: z.string().min(1),
5
+ name: z.string().min(1),
6
+ type: z.string().min(1),
7
+ importPath: z.string().optional()
8
+ });
9
+ const StorybookIndexSchema = z.object({
10
+ entries: z.record(z.string(), StorybookEntrySchema)
11
+ });
12
+ export async function loadStorybookIndex(baseUrl) {
13
+ const indexUrl = new URL('index.json', normalizeBaseUrl(baseUrl));
14
+ let response;
15
+ try {
16
+ response = await fetch(indexUrl, { cache: 'no-store' });
17
+ }
18
+ catch (error) {
19
+ const message = error instanceof Error ? error.message : String(error);
20
+ throw new Error(`Reflection Storybook setup/config error: failed to load ${indexUrl.toString()}: ${message}`);
21
+ }
22
+ if (!response.ok) {
23
+ await response.body?.cancel();
24
+ throw new Error(`Reflection Storybook setup/config error: failed to load ${indexUrl.toString()}: HTTP ${response.status}`);
25
+ }
26
+ let payload;
27
+ try {
28
+ payload = await response.json();
29
+ }
30
+ catch (error) {
31
+ const message = error instanceof Error ? error.message : String(error);
32
+ throw new Error(`Reflection Storybook setup/config error: invalid JSON from ${indexUrl.toString()}: ${message}`);
33
+ }
34
+ const parsed = StorybookIndexSchema.safeParse(payload);
35
+ if (!parsed.success) {
36
+ const details = parsed.error.issues.map((issue) => `${issue.path.join('.') || 'index'}: ${issue.message}`).join('; ');
37
+ throw new Error(`Reflection Storybook setup/config error: invalid ${indexUrl.toString()}: ${details}`);
38
+ }
39
+ return parsed.data;
40
+ }
41
+ function normalizeBaseUrl(baseUrl) {
42
+ return baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`;
43
+ }
44
+ //# sourceMappingURL=index-json.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-json.js","sourceRoot":"","sources":["../../../src/integrations/storybook/index-json.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import { type ManagedServer, type ServerConfig, type StartManagedServerOptions } from '../../core/server-manager.js';
2
+ import { type StorybookIndex } from './index-json.js';
3
+ export type StorybookServer = {
4
+ baseUrl: string;
5
+ index: StorybookIndex;
6
+ server: ManagedServer;
7
+ };
8
+ export declare function startStorybookServer(config: ServerConfig, options?: StartManagedServerOptions): Promise<StorybookServer>;
@@ -0,0 +1,23 @@
1
+ import { startManagedServer } from '../../core/server-manager.js';
2
+ import { loadStorybookIndex } from './index-json.js';
3
+ export async function startStorybookServer(config, options = {}) {
4
+ const server = await startManagedServer({
5
+ ...config,
6
+ readyUrl: storybookIndexUrl(config.readyUrl)
7
+ }, options);
8
+ try {
9
+ return {
10
+ baseUrl: config.readyUrl,
11
+ index: await loadStorybookIndex(config.readyUrl),
12
+ server
13
+ };
14
+ }
15
+ catch (error) {
16
+ await server.stop();
17
+ throw error;
18
+ }
19
+ }
20
+ function storybookIndexUrl(baseUrl) {
21
+ return new URL('index.json', baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`).toString();
22
+ }
23
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../../src/integrations/storybook/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAyE,MAAM,8BAA8B,CAAC;AACzI,OAAO,EAAE,kBAAkB,EAAuB,MAAM,iBAAiB,CAAC;AAQ1E,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,MAAoB,EACpB,UAAqC,EAAE;IAEvC,MAAM,MAAM,GAAG,MAAM,kBAAkB,CACrC;QACE,GAAG,MAAM;QACT,QAAQ,EAAE,iBAAiB,CAAC,MAAM,CAAC,QAAQ,CAAC;KAC7C,EACD,OAAO,CACR,CAAC;IAEF,IAAI,CAAC;QACH,OAAO;YACL,OAAO,EAAE,MAAM,CAAC,QAAQ;YACxB,KAAK,EAAE,MAAM,kBAAkB,CAAC,MAAM,CAAC,QAAQ,CAAC;YAChD,MAAM;SACP,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;QACpB,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,iBAAiB,CAAC,OAAe;IACxC,OAAO,IAAI,GAAG,CAAC,YAAY,EAAE,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,GAAG,CAAC,CAAC,QAAQ,EAAE,CAAC;AAC3F,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { StorybookIndex } from './index-json.js';
2
+ export declare function resolveStoryUrl(index: StorybookIndex, baseUrl: string, storyId: string): string;
@@ -0,0 +1,13 @@
1
+ export function resolveStoryUrl(index, baseUrl, storyId) {
2
+ const entry = index.entries[storyId];
3
+ if (!entry || entry.type !== 'story') {
4
+ throw new Error(`Reflection Storybook setup/config error: storyId "${storyId}" was not found or is not a story in ${new URL('index.json', normalizeBaseUrl(baseUrl)).toString()}`);
5
+ }
6
+ const url = new URL('iframe.html', normalizeBaseUrl(baseUrl));
7
+ url.searchParams.set('id', entry.id);
8
+ return url.toString();
9
+ }
10
+ function normalizeBaseUrl(baseUrl) {
11
+ return baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`;
12
+ }
13
+ //# sourceMappingURL=story-url.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"story-url.js","sourceRoot":"","sources":["../../../src/integrations/storybook/story-url.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,eAAe,CAAC,KAAqB,EAAE,OAAe,EAAE,OAAe;IACrF,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IACrC,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QACrC,MAAM,IAAI,KAAK,CACb,qDAAqD,OAAO,wCAAwC,IAAI,GAAG,CACzG,YAAY,EACZ,gBAAgB,CAAC,OAAO,CAAC,CAC1B,CAAC,QAAQ,EAAE,EAAE,CACf,CAAC;IACJ,CAAC;IAED,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,aAAa,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAC;IAC9D,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;IACrC,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAC;AACxB,CAAC;AAED,SAAS,gBAAgB,CAAC,OAAe;IACvC,OAAO,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,GAAG,CAAC;AACzD,CAAC"}
@@ -0,0 +1,9 @@
1
+ export type ManagedProcessOptions = {
2
+ cwd?: string;
3
+ logPath?: string;
4
+ };
5
+ export type ManagedProcess = {
6
+ pid: number | undefined;
7
+ stop: () => Promise<void>;
8
+ };
9
+ export declare function spawnManagedProcess(command: string, options?: ManagedProcessOptions): Promise<ManagedProcess>;
@@ -0,0 +1,69 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { createWriteStream } from 'node:fs';
3
+ import { mkdir } from 'node:fs/promises';
4
+ import { dirname } from 'node:path';
5
+ import { createRedactionTransform } from '../core/redaction.js';
6
+ export async function spawnManagedProcess(command, options = {}) {
7
+ let logStream;
8
+ if (options.logPath) {
9
+ await mkdir(dirname(options.logPath), { recursive: true });
10
+ logStream = createWriteStream(options.logPath, { flags: 'a' });
11
+ }
12
+ const spawnOptions = {
13
+ detached: true,
14
+ shell: true,
15
+ stdio: 'pipe'
16
+ };
17
+ if (options.cwd) {
18
+ spawnOptions.cwd = options.cwd;
19
+ }
20
+ const child = spawn(command, spawnOptions);
21
+ child.stdin?.end();
22
+ if (logStream) {
23
+ child.stdout?.pipe(createRedactionTransform()).pipe(logStream, { end: false });
24
+ child.stderr?.pipe(createRedactionTransform()).pipe(logStream, { end: false });
25
+ }
26
+ return {
27
+ pid: child.pid,
28
+ stop: () => stopChildProcess(child, logStream)
29
+ };
30
+ }
31
+ async function stopChildProcess(child, logStream) {
32
+ if (child.exitCode !== null || child.signalCode !== null) {
33
+ await closeLogStream(logStream);
34
+ return;
35
+ }
36
+ await new Promise((resolve) => {
37
+ const timeout = setTimeout(() => {
38
+ if (child.exitCode === null && child.signalCode === null) {
39
+ killProcessGroup(child, 'SIGKILL');
40
+ }
41
+ }, 1_000);
42
+ child.once('close', () => {
43
+ clearTimeout(timeout);
44
+ resolve();
45
+ });
46
+ killProcessGroup(child, 'SIGTERM');
47
+ });
48
+ await closeLogStream(logStream);
49
+ }
50
+ function killProcessGroup(child, signal) {
51
+ if (!child.pid) {
52
+ return;
53
+ }
54
+ try {
55
+ process.kill(-child.pid, signal);
56
+ }
57
+ catch {
58
+ child.kill(signal);
59
+ }
60
+ }
61
+ async function closeLogStream(logStream) {
62
+ if (!logStream || logStream.closed || logStream.destroyed) {
63
+ return;
64
+ }
65
+ await new Promise((resolve) => {
66
+ logStream.end(resolve);
67
+ });
68
+ }
69
+ //# sourceMappingURL=process.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process.js","sourceRoot":"","sources":["../../src/utils/process.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,146 @@
1
+ # Agent workflows
2
+
3
+ Reflection is meant to give agents an evidence gate for rendered UI work. Agents should use it to observe and report, not to silently heal visual changes.
4
+
5
+ ## Core agent loop
6
+
7
+ Before claiming frontend work is complete:
8
+
9
+ ```bash
10
+ reflection doctor
11
+ reflection run --config reflection.config.ts --mode smoke
12
+ reflection review --json
13
+ ```
14
+
15
+ `reflection doctor` is currently a lightweight setup check. Treat `reflection run --config ...` as the command that exercises project contracts.
16
+
17
+ If the project uses a non-default config path or report root, pass it explicitly:
18
+
19
+ ```bash
20
+ reflection doctor
21
+ reflection run --config path/to/reflection.config.ts --mode smoke --report-dir .reflection
22
+ reflection review --report-dir .reflection --json
23
+ ```
24
+
25
+ ## How to interpret review JSON
26
+
27
+ Treat the review summary as the handoff contract:
28
+
29
+ - `status: "pass"` — validation passed.
30
+ - `status: "pass-with-review"` — blocking checks passed, but the agent must summarize review items and artifact paths.
31
+ - `status: "fail"` — blocking failures must be fixed before completion.
32
+ - Tool/configuration errors mean the validation did not complete; report the blocker and fix setup when in scope.
33
+
34
+ A good completion message includes:
35
+
36
+ - commands run;
37
+ - status;
38
+ - report path;
39
+ - blocking failures, if any;
40
+ - review items, if any;
41
+ - artifact paths for changed/failing UI;
42
+ - whether any baseline update was dry-run only or human-approved non-dry.
43
+
44
+ ## Baseline update policy for agents
45
+
46
+ Agents may propose a baseline update only with a dry run:
47
+
48
+ ```bash
49
+ reflection update --route <routeId> --from-run latest --dry-run
50
+ reflection update --case <routeVisualCaseId> --from-run latest --dry-run
51
+ ```
52
+
53
+ `reflection update` currently targets route-level `visualSmoke` baselines. Component visual baseline promotion is still a manual review/copy step.
54
+
55
+ Do not run non-dry `reflection update` unless the human explicitly approves that exact target.
56
+
57
+ Never run `reflection update` in CI.
58
+
59
+ After a human-approved non-dry update:
60
+
61
+ 1. Inspect the git diff.
62
+ 2. Report exactly which baseline files changed.
63
+ 3. Re-run `reflection run` and `reflection review --json` if the task requires proof that the new baseline now passes.
64
+
65
+ ## Handling `pass-with-review`
66
+
67
+ `pass-with-review` is useful. It means the functional/browser contract did not block, but some artifact needs human attention.
68
+
69
+ For visual diffs, include:
70
+
71
+ - check id;
72
+ - target route/story and viewport;
73
+ - expected image path;
74
+ - actual image path;
75
+ - diff image path;
76
+ - threshold metadata if present;
77
+ - whether the case is review-only or strict/blocking.
78
+
79
+ Do not hide review items behind "tests passed". The review item is the reason Reflection exists.
80
+
81
+ ## Suggested agent-file section
82
+
83
+ Repository agent files should include a short pointer, not a copy of this whole guide.
84
+
85
+ Supported files to update, in order of preference:
86
+
87
+ - existing `AGENTS.md`;
88
+ - existing `CLAUDE.md`;
89
+ - existing `.github/copilot-instructions.md`;
90
+ - existing `copilot-instructions.md`;
91
+ - create `AGENTS.md` only if none exists.
92
+
93
+ Suggested section:
94
+
95
+ ````md
96
+ ## Reflection validation
97
+
98
+ Use Reflection as the UI evidence gate before claiming frontend work is complete.
99
+
100
+ Run:
101
+
102
+ ```bash
103
+ reflection doctor
104
+ reflection run --config reflection.config.ts --mode smoke
105
+ reflection review --json
106
+ ```
107
+
108
+ Rules:
109
+
110
+ - Treat blocking failures as task blockers.
111
+ - Summarize review items with artifact paths for the human.
112
+ - Use `reflection update --route <routeId> --from-run latest --dry-run` only to propose intentional visual changes.
113
+ - Do not run non-dry `reflection update` unless the human explicitly approves it.
114
+ - Never run `reflection update` in CI.
115
+
116
+ Full protocol: `docs/validation-process.md`.
117
+ ````
118
+
119
+ ## CI workflow
120
+
121
+ CI should run Reflection and upload artifacts, but never update baselines:
122
+
123
+ ```bash
124
+ reflection doctor
125
+ reflection run --ci --config reflection.config.ts --mode smoke
126
+ reflection review --report-dir artifacts/reflection --json
127
+ ```
128
+
129
+ `reflection run --ci` writes to `artifacts/reflection` by default, so pass the same report root to `review`.
130
+
131
+ Upload the report root even on failure:
132
+
133
+ ```text
134
+ artifacts/reflection/**
135
+ ```
136
+
137
+ See `docs/ci.md` for current CI-specific defaults and exit codes.
138
+
139
+ ## Common mistakes
140
+
141
+ - Treating generated evidence screenshots as baselines.
142
+ - Running non-dry `reflection update` without human approval.
143
+ - Ignoring `pass-with-review` because the shell exit code is `0`.
144
+ - Deleting run artifacts before reporting the artifact paths.
145
+ - Copying long Reflection instructions into multiple agent files and letting them drift.
146
+ - Forgetting `--report-dir` when reviewing a CI or custom-root run.