rcf-lite 0.0.1 → 0.8.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.
- package/CHANGELOG.md +344 -0
- package/LICENSE +202 -0
- package/README.md +92 -4
- package/bin/rcf-verify.js +122 -0
- package/bin/rcf.js +174 -0
- package/bin/view-supervisor-child.mjs +14 -0
- package/fixtures/canary-manifest.json +103 -0
- package/guidance/README.md +25 -0
- package/guidance/build-cycle-playbook.md +492 -0
- package/guidance/build-cycle.md +40 -0
- package/guidance/document-model.md +45 -0
- package/guidance/elicitation-playbook.md +442 -0
- package/guidance/harness-template.md +246 -0
- package/guidance/managed/README.md +63 -0
- package/guidance/managed/agent-instructions-block.hash +1 -0
- package/guidance/managed/agent-instructions-block.md +220 -0
- package/guidance/managed/legacy-fragment-hashes.json +22 -0
- package/guidance/manifest.json +21 -0
- package/guidance/overview.md +48 -0
- package/guidance/persona-programme.md +44 -0
- package/package.json +71 -8
- package/rcf/adrs/adr-001.json +25 -0
- package/rcf/adrs/adr-002.json +25 -0
- package/rcf/adrs/adr-003.json +25 -0
- package/rcf/adrs/adr-004.json +20 -0
- package/rcf/adrs/adr-005.json +20 -0
- package/rcf/adrs/adr-006.json +25 -0
- package/rcf/adrs/adr-007.json +25 -0
- package/rcf/adrs/adr-008.json +25 -0
- package/rcf/adrs/adr-009.json +25 -0
- package/rcf/build-sequence.json +11 -0
- package/rcf/code-nodes/cn-001.json +14 -0
- package/rcf/code-nodes/cn-002.json +14 -0
- package/rcf/code-nodes/cn-003.json +15 -0
- package/rcf/code-nodes/cn-004.json +17 -0
- package/rcf/code-nodes/cn-005.json +19 -0
- package/rcf/code-nodes/cn-006.json +17 -0
- package/rcf/code-nodes/cn-007.json +16 -0
- package/rcf/code-nodes/cn-008.json +19 -0
- package/rcf/code-nodes/cn-009.json +16 -0
- package/rcf/code-nodes/cn-010.json +18 -0
- package/rcf/code-nodes/cn-011.json +18 -0
- package/rcf/code-nodes/cn-012.json +18 -0
- package/rcf/code-nodes/cn-013.json +19 -0
- package/rcf/code-nodes/cn-014.json +16 -0
- package/rcf/code-nodes/cn-015.json +16 -0
- package/rcf/code-nodes/cn-016.json +16 -0
- package/rcf/code-nodes/cn-017.json +16 -0
- package/rcf/code-nodes/cn-018.json +15 -0
- package/rcf/code-nodes/cn-019.json +16 -0
- package/rcf/code-nodes/cn-020.json +16 -0
- package/rcf/code-nodes/cn-021.json +16 -0
- package/rcf/code-nodes/cn-022.json +16 -0
- package/rcf/code-nodes/cn-023.json +16 -0
- package/rcf/code-nodes/cn-024.json +16 -0
- package/rcf/code-nodes/cn-025.json +16 -0
- package/rcf/code-nodes/cn-026.json +16 -0
- package/rcf/code-nodes/cn-027.json +16 -0
- package/rcf/code-nodes/cn-028.json +16 -0
- package/rcf/code-nodes/cn-029.json +19 -0
- package/rcf/code-nodes/cn-030.json +14 -0
- package/rcf/code-nodes/cn-031.json +14 -0
- package/rcf/code-nodes/cn-032.json +14 -0
- package/rcf/code-nodes/cn-033.json +14 -0
- package/rcf/code-nodes/cn-034.json +14 -0
- package/rcf/code-nodes/cn-035.json +14 -0
- package/rcf/code-nodes/cn-036.json +14 -0
- package/rcf/code-nodes/cn-037.json +14 -0
- package/rcf/code-nodes/cn-038.json +14 -0
- package/rcf/code-nodes/cn-039.json +14 -0
- package/rcf/code-nodes/cn-040.json +14 -0
- package/rcf/code-nodes/cn-041.json +14 -0
- package/rcf/code-nodes/cn-042.json +14 -0
- package/rcf/code-nodes/cn-043.json +14 -0
- package/rcf/code-nodes/cn-044.json +14 -0
- package/rcf/code-nodes/cn-045.json +14 -0
- package/rcf/code-nodes/cn-046.json +14 -0
- package/rcf/code-nodes/cn-047.json +14 -0
- package/rcf/code-nodes/cn-048.json +14 -0
- package/rcf/code-nodes/cn-049.json +14 -0
- package/rcf/code-nodes/cn-050.json +14 -0
- package/rcf/code-nodes/cn-051.json +14 -0
- package/rcf/code-nodes/cn-052.json +14 -0
- package/rcf/code-nodes/cn-053.json +14 -0
- package/rcf/code-nodes/cn-054.json +14 -0
- package/rcf/code-nodes/cn-055.json +14 -0
- package/rcf/code-nodes/cn-056.json +14 -0
- package/rcf/code-nodes/cn-057.json +14 -0
- package/rcf/fbs/fbs-001.json +49 -0
- package/rcf/fbs/fbs-002.json +42 -0
- package/rcf/fbs/fbs-003.json +37 -0
- package/rcf/fbs/fbs-004.json +39 -0
- package/rcf/fbs/fbs-005.json +38 -0
- package/rcf/fbs/fbs-006.json +48 -0
- package/rcf/fbs/fbs-007.json +39 -0
- package/rcf/fbs/fbs-008.json +40 -0
- package/rcf/fbs/fbs-009.json +36 -0
- package/rcf/fbs/fbs-010.json +41 -0
- package/rcf/fbs/fbs-011.json +36 -0
- package/rcf/fbs/fbs-012.json +46 -0
- package/rcf/fbs/fbs-013.json +42 -0
- package/rcf/fbs/fbs-014.json +49 -0
- package/rcf/fbs/fbs-015.json +32 -0
- package/rcf/manifest.json +17 -0
- package/rcf/prd.json +47 -0
- package/rcf/requirements/req-001.json +19 -0
- package/rcf/requirements/req-002.json +19 -0
- package/rcf/requirements/req-003.json +19 -0
- package/rcf/requirements/req-004.json +19 -0
- package/rcf/requirements/req-005.json +19 -0
- package/rcf/requirements/req-006.json +19 -0
- package/rcf/requirements/req-007.json +19 -0
- package/rcf/requirements/req-008.json +19 -0
- package/rcf/requirements/req-009.json +19 -0
- package/rcf/tacs/tac-001.json +45 -0
- package/rcf/tacs/tac-002.json +109 -0
- package/rcf/tacs/tac-003.json +40 -0
- package/rcf/tacs/tac-004.json +51 -0
- package/rcf/tacs/tac-005.json +52 -0
- package/rcf/tacs/tac-006.json +104 -0
- package/rcf/tacs/tac-007.json +38 -0
- package/rcf/tacs/tac-008.json +51 -0
- package/rcf/tad.json +51 -0
- package/rcf/test-suites/PENDING.md +23 -0
- package/rcf/test-suites/ts-001.json +38 -0
- package/rcf/test-suites/ts-002.json +38 -0
- package/rcf/test-suites/ts-003.json +43 -0
- package/rcf/test-suites/ts-004.json +44 -0
- package/rcf/test-suites/ts-005.json +30 -0
- package/rcf/test-suites/ts-006.json +36 -0
- package/rcf/test-suites/ts-007.json +43 -0
- package/rcf/test-suites/ts-008.json +37 -0
- package/rcf/test-suites/ts-009.json +38 -0
- package/rcf/test-suites/ts-010.json +38 -0
- package/rcf/test-suites/ts-011.json +44 -0
- package/rcf/test-suites/ts-012.json +36 -0
- package/rcf/test-suites/ts-013.json +38 -0
- package/rcf/test-suites/ts-014.json +38 -0
- package/rcf/test-suites/ts-015.json +38 -0
- package/rcf/test-suites/ts-016.json +37 -0
- package/rcf/test-suites/ts-017.json +38 -0
- package/rcf/test-suites/ts-018.json +38 -0
- package/rcf/test-suites/ts-019.json +37 -0
- package/rcf/test-suites/ts-020.json +46 -0
- package/rcf/test-suites/ts-021.json +46 -0
- package/rcf/test-suites/ts-022.json +46 -0
- package/rcf/test-suites/ts-023.json +46 -0
- package/rcf/test-suites/ts-024.json +46 -0
- package/rcf/test-suites/ts-025.json +52 -0
- package/rcf/user-stories/us-101.json +40 -0
- package/rcf/user-stories/us-102.json +40 -0
- package/rcf/user-stories/us-201.json +40 -0
- package/rcf/user-stories/us-202.json +40 -0
- package/rcf/user-stories/us-203.json +32 -0
- package/rcf/user-stories/us-301.json +40 -0
- package/rcf/user-stories/us-302.json +40 -0
- package/rcf/user-stories/us-303.json +40 -0
- package/rcf/user-stories/us-304.json +40 -0
- package/rcf/user-stories/us-401.json +40 -0
- package/rcf/user-stories/us-402.json +40 -0
- package/rcf/user-stories/us-403.json +40 -0
- package/rcf/user-stories/us-501.json +40 -0
- package/rcf/user-stories/us-502.json +40 -0
- package/rcf/user-stories/us-503.json +40 -0
- package/rcf/user-stories/us-601.json +40 -0
- package/rcf/user-stories/us-602.json +40 -0
- package/rcf/user-stories/us-701.json +40 -0
- package/rcf/user-stories/us-702.json +40 -0
- package/rcf/user-stories/us-801.json +49 -0
- package/rcf/user-stories/us-802.json +49 -0
- package/rcf/user-stories/us-803.json +49 -0
- package/rcf/user-stories/us-804.json +49 -0
- package/rcf/user-stories/us-805.json +49 -0
- package/rcf/user-stories/us-901.json +40 -0
- package/src/.gitkeep +0 -0
- package/src/admissibility/enforce.js +142 -0
- package/src/admissibility/index.js +8 -0
- package/src/admissibility/markers.js +104 -0
- package/src/admissibility/scope-lint.js +163 -0
- package/src/browser-verify/auth-smoke.js +109 -0
- package/src/browser-verify/index.js +29 -0
- package/src/browser-verify/invariants.js +336 -0
- package/src/browser-verify/manifest-writer.js +189 -0
- package/src/browser-verify/runner.js +171 -0
- package/src/build/bundle.js +198 -0
- package/src/build/formatters/json.js +18 -0
- package/src/build/formatters/markdown.js +406 -0
- package/src/build/index.js +14 -0
- package/src/build/mark.js +177 -0
- package/src/build/queue.js +285 -0
- package/src/cli/browser-verify.js +231 -0
- package/src/cli/build.js +584 -0
- package/src/cli/coverage.js +219 -0
- package/src/cli/create.js +358 -0
- package/src/cli/delete.js +127 -0
- package/src/cli/design.js +353 -0
- package/src/cli/doctor.js +548 -0
- package/src/cli/fbs.js +171 -0
- package/src/cli/finalise.js +377 -0
- package/src/cli/guidance.js +206 -0
- package/src/cli/help.js +156 -0
- package/src/cli/impact.js +119 -0
- package/src/cli/init.js +282 -0
- package/src/cli/intake.js +153 -0
- package/src/cli/link.js +128 -0
- package/src/cli/mcp.js +160 -0
- package/src/cli/preflight.js +220 -0
- package/src/cli/read.js +168 -0
- package/src/cli/req-baseline.js +269 -0
- package/src/cli/req-classify.js +135 -0
- package/src/cli/review.js +295 -0
- package/src/cli/test-suite.js +221 -0
- package/src/cli/trace.js +172 -0
- package/src/cli/ui-baseline.js +292 -0
- package/src/cli/ui-classify.js +108 -0
- package/src/cli/update.js +197 -0
- package/src/cli/validate.js +168 -0
- package/src/cli/view.js +452 -0
- package/src/core/baseline-catalog/data/auth.json +42 -0
- package/src/core/baseline-catalog/data/http-api.json +42 -0
- package/src/core/baseline-catalog/data/notifications.json +33 -0
- package/src/core/baseline-catalog/data/persistence.json +33 -0
- package/src/core/baseline-catalog/data/web-ui.json +60 -0
- package/src/core/baseline-catalog/index.js +121 -0
- package/src/core/errors/index.js +167 -0
- package/src/core/fixtures/register-canary/canary-prompt-01.json +18 -0
- package/src/core/fixtures/register-canary/canary-prompt-02.json +21 -0
- package/src/core/fixtures/register-canary/canary-prompt-03.json +17 -0
- package/src/core/isolation/index.js +60 -0
- package/src/core/mcp/framing.js +103 -0
- package/src/core/mcp/index.js +8 -0
- package/src/core/mcp/server.js +228 -0
- package/src/core/patterns/register-canary.js +209 -0
- package/src/core/patterns/req-shapes.js +158 -0
- package/src/core/patterns/services.js +358 -0
- package/src/core/patterns/ui-shapes.js +166 -0
- package/src/core/store/cn-resolve.js +134 -0
- package/src/core/store/derive-deps.js +93 -0
- package/src/core/store/ids.js +78 -0
- package/src/core/store/index.js +20 -0
- package/src/core/store/init.js +255 -0
- package/src/core/store/loader.js +214 -0
- package/src/core/store/tp-resolve.js +176 -0
- package/src/core/store/validator.js +191 -0
- package/src/core/store/walker.js +944 -0
- package/src/core/store/writer.js +1879 -0
- package/src/design/index.js +11 -0
- package/src/design/writer.js +271 -0
- package/src/finalise/detect.js +151 -0
- package/src/finalise/index.js +31 -0
- package/src/finalise/ingest.js +160 -0
- package/src/finalise/install.js +119 -0
- package/src/finalise/ship-without-verified.js +131 -0
- package/src/finalise/spawn.js +84 -0
- package/src/intake/fidelity.js +105 -0
- package/src/intake/index.js +6 -0
- package/src/intake/manifest-writer.js +100 -0
- package/src/intake/orchestrator.js +138 -0
- package/src/intake/validate.js +80 -0
- package/src/mcp/map-errors.js +131 -0
- package/src/mcp/prompts.js +49 -0
- package/src/mcp/resources.js +244 -0
- package/src/mcp/tools.js +1212 -0
- package/src/preflight/design-shapes.js +185 -0
- package/src/preflight/index.js +90 -0
- package/src/preflight/manifest-writer.js +264 -0
- package/src/preflight/scanner.js +206 -0
- package/src/preflight/secrets.js +134 -0
- package/src/preflight/session.js +246 -0
- package/src/query/attestation.js +285 -0
- package/src/query/coverage.js +308 -0
- package/src/query/formatters/json.js +21 -0
- package/src/query/formatters/mermaid.js +209 -0
- package/src/query/formatters/table.js +203 -0
- package/src/query/impact.js +173 -0
- package/src/query/index.js +13 -0
- package/src/query/refuse-on-admissibility.js +73 -0
- package/src/query/trace.js +345 -0
- package/src/register-canary/fixture-loader.js +87 -0
- package/src/register-canary/index.js +10 -0
- package/src/register-canary/record-writer.js +132 -0
- package/src/register-canary/runner.js +156 -0
- package/src/req-baseline/gate.js +86 -0
- package/src/req-baseline/index.js +27 -0
- package/src/req-baseline/open-candidates.js +143 -0
- package/src/req-baseline/opt-out.js +195 -0
- package/src/req-baseline/sweep.js +230 -0
- package/src/req-detection/classifier.js +181 -0
- package/src/req-detection/index.js +9 -0
- package/src/req-detection/persist.js +55 -0
- package/src/review/index.js +325 -0
- package/src/review/mutation.js +117 -0
- package/src/review/ui-baseline-drift.js +138 -0
- package/src/ruleset/index.js +140 -0
- package/src/ruleset/ruleset.json +146 -0
- package/src/server/index.js +178 -0
- package/src/server/routes.js +110 -0
- package/src/server/sse.js +118 -0
- package/src/setup/agent-setup.js +362 -0
- package/src/setup/identity-seed.js +104 -0
- package/src/setup/knowledge-seed.js +123 -0
- package/src/setup/managed-block.js +193 -0
- package/src/setup/managed-gitignore.js +166 -0
- package/src/setup/managed-markers.js +49 -0
- package/src/ui-baseline/defaults.js +119 -0
- package/src/ui-baseline/index.js +25 -0
- package/src/ui-baseline/manifest-writer.js +282 -0
- package/src/ui-baseline/session.js +178 -0
- package/src/ui-detection/classifier.js +192 -0
- package/src/verify/chain/index.js +221 -0
- package/src/verify/cli/cleanup.js +61 -0
- package/src/verify/cli/help.js +56 -0
- package/src/verify/cli/mcp.js +98 -0
- package/src/verify/cli/provision.js +71 -0
- package/src/verify/cli/report.js +71 -0
- package/src/verify/cli/run.js +155 -0
- package/src/verify/engine/brief.js +87 -0
- package/src/verify/engine/index.js +177 -0
- package/src/verify/engine/launcher.js +307 -0
- package/src/verify/mcp/tools.js +107 -0
- package/src/verify/profile/index.js +146 -0
- package/src/verify/provision/index.js +256 -0
- package/src/verify/report/index.js +139 -0
- package/src/verify/report/renderer.js +118 -0
- package/src/verify/verdict/index.js +313 -0
- package/src/view/doc-renderers/adr.js +44 -0
- package/src/view/doc-renderers/build-sequence.js +40 -0
- package/src/view/doc-renderers/fbs.js +128 -0
- package/src/view/doc-renderers/helpers.js +159 -0
- package/src/view/doc-renderers/index.js +12 -0
- package/src/view/doc-renderers/prd.js +45 -0
- package/src/view/doc-renderers/req.js +43 -0
- package/src/view/doc-renderers/tac.js +38 -0
- package/src/view/doc-renderers/tad.js +74 -0
- package/src/view/doc-renderers/test-suite.js +45 -0
- package/src/view/doc-renderers/user-story.js +63 -0
- package/src/view/html-page.js +462 -0
- package/src/view/index.js +63 -0
- package/src/view/live-client.js +338 -0
- package/src/view/mermaid-diagram.js +178 -0
- package/src/view/style.css +735 -0
- package/src/view/tree-model.js +152 -0
- package/src/view/vendored/mermaid.min.js +2607 -0
- package/src/view-supervisor/index.js +26 -0
- package/src/view-supervisor/logs.js +32 -0
- package/src/view-supervisor/manifest-writer.js +178 -0
- package/src/view-supervisor/persist-until.js +85 -0
- package/src/view-supervisor/supervisor.js +276 -0
- package/src/watch/index.js +152 -0
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
// Browser-verify runner (ui-design-gate-0.7.0-spec §8.2).
|
|
2
|
+
//
|
|
3
|
+
// Two modes:
|
|
4
|
+
// - `operatorSession`: the operator loaded the app and cleared the
|
|
5
|
+
// pages themselves; the runner takes a minimal input (the routes
|
|
6
|
+
// they visited, the invariants they ticked) and writes the record.
|
|
7
|
+
// - `agentScreenshotCritique`: the runner drives a browser (injected)
|
|
8
|
+
// over every enumerated route × theme, captures a DOM string per
|
|
9
|
+
// capture, runs the versioned invariant set (§8.3), runs the auth
|
|
10
|
+
// smoke pack when the FBS binds an auth REQ, aggregates the record
|
|
11
|
+
// verdict per §8.5.
|
|
12
|
+
//
|
|
13
|
+
// Everything driver-shaped is injectable (browser driver, fetch,
|
|
14
|
+
// artefact writer) so unit tests do not need a live Playwright and CI
|
|
15
|
+
// can run this without a real browser. The default agent-mode driver
|
|
16
|
+
// is a stub that returns an empty capture and a `verdict: warn` record
|
|
17
|
+
// with a note pointing at "no driver wired"; production wiring lands
|
|
18
|
+
// via `deps.browserDriver` on the CLI handler.
|
|
19
|
+
|
|
20
|
+
import { runInvariantsForCapture, foldInvariantsForRecord, compareTopLevelStructure } from './invariants.js';
|
|
21
|
+
import { runAuthSmokeChecks, shouldRunAuthSmokeChecks } from './auth-smoke.js';
|
|
22
|
+
import { composeBrowserVerificationRecord } from './manifest-writer.js';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @typedef {object} BrowserCapture
|
|
26
|
+
* @property {string} routePath
|
|
27
|
+
* @property {'light'|'dark'} themeApplied
|
|
28
|
+
* @property {string} dom
|
|
29
|
+
* @property {string} screenshotPath
|
|
30
|
+
* @property {boolean} [authenticated]
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @typedef {object} BrowserDriver
|
|
35
|
+
* @property {(args: { url: string, routes: object[], themes: ('light'|'dark')[], artefactDir: string }) => Promise<BrowserCapture[]>} capture
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Default agent-mode driver stub. Emits no captures; a warning surfaces
|
|
40
|
+
* on the record notes. Callers wire a real Playwright driver via
|
|
41
|
+
* `deps.browserDriver` on the CLI.
|
|
42
|
+
*
|
|
43
|
+
* @type {BrowserDriver}
|
|
44
|
+
*/
|
|
45
|
+
export const stubBrowserDriver = Object.freeze({
|
|
46
|
+
async capture() { return []; },
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Run the agent-screenshot-critique mode against a FBS.
|
|
51
|
+
*
|
|
52
|
+
* @param {object} args
|
|
53
|
+
* @param {import('#core/store/walker.js').TreeModel} args.tree
|
|
54
|
+
* @param {object} args.fbs
|
|
55
|
+
* @param {string} args.runtimeUrl
|
|
56
|
+
* @param {'deployed'|'ci'|'local-dev'} args.runtimeProfile
|
|
57
|
+
* @param {BrowserDriver} args.browserDriver
|
|
58
|
+
* @param {(url: string, init?: object) => Promise<any>} args.fetch
|
|
59
|
+
* @param {string} args.artefactDir
|
|
60
|
+
* @param {string} [args.critiqueNotes] agent-critique rubric findings (spec §8.4)
|
|
61
|
+
* @param {Date} [args.now]
|
|
62
|
+
* @returns {Promise<object>} the composed browserVerification record
|
|
63
|
+
*/
|
|
64
|
+
export async function runAgentScreenshotCritique({
|
|
65
|
+
tree, fbs, runtimeUrl, runtimeProfile, browserDriver, fetch, artefactDir, critiqueNotes, now = new Date(),
|
|
66
|
+
}) {
|
|
67
|
+
const routes = fbs?.designStage?.navModel?.routes ?? [];
|
|
68
|
+
const themes = readThemesFromBaseline(tree.manifest?.uiBaseline);
|
|
69
|
+
const captures = await browserDriver.capture({ url: runtimeUrl, routes, themes, artefactDir });
|
|
70
|
+
|
|
71
|
+
const perCapture = [];
|
|
72
|
+
const routesChecked = [];
|
|
73
|
+
for (const cap of captures) {
|
|
74
|
+
const results = runInvariantsForCapture({
|
|
75
|
+
routePath: cap.routePath,
|
|
76
|
+
themeApplied: cap.themeApplied,
|
|
77
|
+
fbs,
|
|
78
|
+
uiBaseline: tree.manifest?.uiBaseline,
|
|
79
|
+
dom: cap.dom,
|
|
80
|
+
authenticated: cap.authenticated,
|
|
81
|
+
});
|
|
82
|
+
perCapture.push({ routePath: cap.routePath, themeApplied: cap.themeApplied, results });
|
|
83
|
+
routesChecked.push({ path: cap.routePath, screenshotPath: cap.screenshotPath, themeApplied: cap.themeApplied });
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Structural cross-route compare for `sharedLayoutModule`.
|
|
87
|
+
const structuralResult = compareTopLevelStructure(
|
|
88
|
+
captures.filter((c) => c.themeApplied === 'light'),
|
|
89
|
+
tree.manifest?.uiBaseline,
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
let invariantChecks = foldInvariantsForRecord(perCapture);
|
|
93
|
+
// Replace / append the structural sharedLayoutModule result.
|
|
94
|
+
invariantChecks = invariantChecks.filter((c) => c.invariant !== 'sharedLayoutModule');
|
|
95
|
+
invariantChecks.push({
|
|
96
|
+
invariant: structuralResult.invariant,
|
|
97
|
+
verdict: structuralResult.verdict,
|
|
98
|
+
...(structuralResult.detail ? { detail: structuralResult.detail } : {}),
|
|
99
|
+
severity: structuralResult.severity,
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// If no captures came back at all, record a synthetic warn so the
|
|
103
|
+
// operator sees the "driver was not wired" state distinctly from a
|
|
104
|
+
// clean pass.
|
|
105
|
+
let notes = critiqueNotes;
|
|
106
|
+
if (captures.length === 0) {
|
|
107
|
+
invariantChecks.push({
|
|
108
|
+
invariant: 'agentDriverWired',
|
|
109
|
+
verdict: 'fail',
|
|
110
|
+
detail: 'browser driver returned zero captures; wire a Playwright driver via deps.browserDriver or run in --mode operatorSession',
|
|
111
|
+
severity: 'warn',
|
|
112
|
+
});
|
|
113
|
+
notes = notes ? `${notes}\n\nDriver: no captures produced.` : 'Driver: no captures produced.';
|
|
114
|
+
// Ensure schema minimums are met even without captures.
|
|
115
|
+
if (routesChecked.length === 0) {
|
|
116
|
+
routesChecked.push({ path: routes[0]?.path ?? '/', screenshotPath: `${artefactDir}/placeholder.png`, themeApplied: 'light' });
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
let authSmokeChecks;
|
|
121
|
+
if (shouldRunAuthSmokeChecks(fbs, tree.manifest?.uiBaseline)) {
|
|
122
|
+
authSmokeChecks = await runAuthSmokeChecks({ fetch, runtimeUrl });
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return composeBrowserVerificationRecord({
|
|
126
|
+
manifest: tree.manifest,
|
|
127
|
+
fbsId: fbs.fbsId,
|
|
128
|
+
mode: 'agentScreenshotCritique',
|
|
129
|
+
runtimeProfile,
|
|
130
|
+
runtimeUrl,
|
|
131
|
+
routesChecked,
|
|
132
|
+
invariantChecks,
|
|
133
|
+
authSmokeChecks,
|
|
134
|
+
notes,
|
|
135
|
+
now,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Compose the operator-session record. No captures; the operator's
|
|
141
|
+
* declared list of routes plus their invariant ticks are the evidence.
|
|
142
|
+
*
|
|
143
|
+
* @param {object} args
|
|
144
|
+
* @returns {object}
|
|
145
|
+
*/
|
|
146
|
+
export function composeOperatorSessionRecord({
|
|
147
|
+
tree, fbs, runtimeUrl, runtimeProfile, declaredRoutes, invariantTicks, now = new Date(),
|
|
148
|
+
}) {
|
|
149
|
+
const routesChecked = declaredRoutes.length > 0
|
|
150
|
+
? declaredRoutes.map((r) => ({ path: r.path, screenshotPath: `.rcf/artefacts/operator-session/${fbs.fbsId}${r.path.replace(/\W+/g, '_')}.png`, themeApplied: r.themeApplied ?? 'light' }))
|
|
151
|
+
: [{ path: fbs?.designStage?.navModel?.routes?.[0]?.path ?? '/', screenshotPath: `.rcf/artefacts/operator-session/${fbs.fbsId}-declared.png`, themeApplied: 'light' }];
|
|
152
|
+
const invariantChecks = invariantTicks && invariantTicks.length > 0
|
|
153
|
+
? invariantTicks.map((t) => ({ invariant: t.invariant, verdict: t.verdict ?? 'pass', ...(t.detail ? { detail: t.detail } : {}), severity: t.severity ?? 'block' }))
|
|
154
|
+
: [{ invariant: 'operatorAcknowledged', verdict: 'pass', severity: 'block' }];
|
|
155
|
+
return composeBrowserVerificationRecord({
|
|
156
|
+
manifest: tree.manifest,
|
|
157
|
+
fbsId: fbs.fbsId,
|
|
158
|
+
mode: 'operatorSession',
|
|
159
|
+
runtimeProfile,
|
|
160
|
+
runtimeUrl,
|
|
161
|
+
routesChecked,
|
|
162
|
+
invariantChecks,
|
|
163
|
+
now,
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function readThemesFromBaseline(baseline) {
|
|
168
|
+
const mode = baseline?.defaults?.themeMode;
|
|
169
|
+
if (mode === 'single-theme-declared') return ['light'];
|
|
170
|
+
return ['light', 'dark'];
|
|
171
|
+
}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
// Spec-bundle assembly (Phase 6 §D3). Pure projection of one FBS item
|
|
2
|
+
// and its surroundings into the seven-section bundle shape: header,
|
|
3
|
+
// queue and dependency context, the work, acceptance criteria with
|
|
4
|
+
// US/REQ ancestry, architectural context, existing test surface, and
|
|
5
|
+
// the build-cycle runbook parameters. No I/O, no wall clock, no
|
|
6
|
+
// semantic judgement (§D13): the bundle reports what the tree says.
|
|
7
|
+
//
|
|
8
|
+
// Ordering rules (§D10): acIds and every contextRequirements list
|
|
9
|
+
// render in authored order; dependency and dependent lists sort by
|
|
10
|
+
// buildOrder then fbsId; US groups order by first appearance in the
|
|
11
|
+
// authored acIds.
|
|
12
|
+
|
|
13
|
+
import { byBuildOrder, computeQueue } from './queue.js';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Copy `fields` from `source` onto a new object, skipping fields not
|
|
17
|
+
* present. Preserves the given key order (JSON key order is
|
|
18
|
+
* construction order, §D10).
|
|
19
|
+
*
|
|
20
|
+
* @param {object} source
|
|
21
|
+
* @param {string[]} fields
|
|
22
|
+
* @returns {object}
|
|
23
|
+
*/
|
|
24
|
+
function pick(source, fields) {
|
|
25
|
+
const out = {};
|
|
26
|
+
for (const f of fields) {
|
|
27
|
+
if (source?.[f] !== undefined) out[f] = source[f];
|
|
28
|
+
}
|
|
29
|
+
return out;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Assemble the bundle for one FBS item. The caller (handler) is
|
|
34
|
+
* responsible for id classification; this function returns null when
|
|
35
|
+
* the id does not resolve to an FBS document.
|
|
36
|
+
*
|
|
37
|
+
* @param {import('#core/store/walker.js').TreeModel} tree
|
|
38
|
+
* @param {object} opts
|
|
39
|
+
* @param {string} opts.fbsId
|
|
40
|
+
* @returns {object|null} BundleResult
|
|
41
|
+
*/
|
|
42
|
+
export function assembleBundle(tree, { fbsId }) {
|
|
43
|
+
const fbs = tree.byId.get(fbsId);
|
|
44
|
+
if (!fbs || tree.kindById.get(fbsId) !== 'fbs') return null;
|
|
45
|
+
|
|
46
|
+
const queue = computeQueue(tree);
|
|
47
|
+
const position = queue.items.findIndex((i) => i.fbsId === fbsId) + 1;
|
|
48
|
+
const queueItem = queue.items[position - 1];
|
|
49
|
+
const fbsById = new Map((tree.fbsItems ?? []).map((f) => [f.fbsId, f]));
|
|
50
|
+
|
|
51
|
+
// Section 1 - header identity (order follows §D14 fbs shape, then
|
|
52
|
+
// the optional estimate fields and the header's "spec last touched").
|
|
53
|
+
const fbsBlock = pick(fbs, [
|
|
54
|
+
'fbsId', 'title', 'buildOrder', 'executionStatus',
|
|
55
|
+
'summary', 'approach', 'deliverables', 'notes',
|
|
56
|
+
'estimatedSize', 'estimatedHours', 'riskLevel', 'domain', 'updatedAt',
|
|
57
|
+
]);
|
|
58
|
+
|
|
59
|
+
// Section 2 - queue and dependency context.
|
|
60
|
+
const dependencies = (fbs.dependsOnFbsIds ?? [])
|
|
61
|
+
.map((depId) => fbsById.get(depId) ?? { fbsId: depId })
|
|
62
|
+
.sort(byBuildOrder)
|
|
63
|
+
.map((dep) => pick(dep, ['fbsId', 'title', 'executionStatus']));
|
|
64
|
+
const dependents = (tree.dependentsByFbsId.get(fbsId) ?? [])
|
|
65
|
+
.map((depId) => fbsById.get(depId) ?? { fbsId: depId })
|
|
66
|
+
.sort(byBuildOrder)
|
|
67
|
+
.map((dep) => dep.fbsId);
|
|
68
|
+
const blockedBy = queueItem ? queueItem.blockedBy : [];
|
|
69
|
+
|
|
70
|
+
// Section 4 - acceptance criteria with US/REQ ancestry. Groups order
|
|
71
|
+
// by first appearance in the authored acIds; ACs within a group keep
|
|
72
|
+
// the authored acIds order.
|
|
73
|
+
const usOrder = [];
|
|
74
|
+
const acsByUs = new Map();
|
|
75
|
+
for (const acId of fbs.acIds ?? []) {
|
|
76
|
+
const usId = tree.parentByChild.get(acId);
|
|
77
|
+
const us = usId ? tree.byId.get(usId) : null;
|
|
78
|
+
if (!us) continue; // Broken cross-links cannot occur post-walker (§D6).
|
|
79
|
+
if (!acsByUs.has(usId)) {
|
|
80
|
+
usOrder.push(usId);
|
|
81
|
+
acsByUs.set(usId, []);
|
|
82
|
+
}
|
|
83
|
+
const ac = (us.acceptanceCriteria ?? []).find((a) => a.id === acId);
|
|
84
|
+
if (ac) acsByUs.get(usId).push(ac);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const acceptanceCriteria = [];
|
|
88
|
+
const userStories = [];
|
|
89
|
+
const requirements = [];
|
|
90
|
+
const seenReqs = new Set();
|
|
91
|
+
for (const usId of usOrder) {
|
|
92
|
+
const us = tree.byId.get(usId);
|
|
93
|
+
userStories.push(pick(us, ['usId', 'title', 'asA', 'iWant', 'soThat', 'status']));
|
|
94
|
+
const req = tree.byId.get(us.reqId);
|
|
95
|
+
if (req && !seenReqs.has(req.reqId)) {
|
|
96
|
+
seenReqs.add(req.reqId);
|
|
97
|
+
requirements.push(pick(req, ['reqId', 'title', 'description', 'category', 'priority', 'rationale']));
|
|
98
|
+
}
|
|
99
|
+
for (const ac of acsByUs.get(usId)) {
|
|
100
|
+
acceptanceCriteria.push({
|
|
101
|
+
...pick(ac, ['id', 'description', 'given', 'when', 'then', 'testable']),
|
|
102
|
+
usId,
|
|
103
|
+
reqId: us.reqId,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Section 5 - architectural context (§D11). Omitted entirely when
|
|
109
|
+
// contextRequirements is absent.
|
|
110
|
+
let context;
|
|
111
|
+
const ctx = fbs.contextRequirements;
|
|
112
|
+
if (ctx) {
|
|
113
|
+
const tacs = (ctx.tacIds ?? [])
|
|
114
|
+
.map((id) => tree.byId.get(id))
|
|
115
|
+
.filter(Boolean)
|
|
116
|
+
.map((tac) => pick(tac, [
|
|
117
|
+
'tacId', 'name', 'purpose', 'responsibilities', 'interfaces',
|
|
118
|
+
'dependencies', 'tradeoffs', 'notes',
|
|
119
|
+
]));
|
|
120
|
+
const adrs = (ctx.adrIds ?? [])
|
|
121
|
+
.map((id) => tree.byId.get(id))
|
|
122
|
+
.filter(Boolean)
|
|
123
|
+
.map((adr) => pick(adr, [
|
|
124
|
+
'adrId', 'title', 'status', 'context', 'decision', 'consequences',
|
|
125
|
+
'alternativesConsidered',
|
|
126
|
+
]));
|
|
127
|
+
const unresolvedSections = [];
|
|
128
|
+
const resolveSections = (names, doc) => {
|
|
129
|
+
const out = {};
|
|
130
|
+
for (const name of names ?? []) {
|
|
131
|
+
if (doc && Object.prototype.hasOwnProperty.call(doc, name)) {
|
|
132
|
+
out[name] = doc[name];
|
|
133
|
+
} else {
|
|
134
|
+
unresolvedSections.push(name);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return out;
|
|
138
|
+
};
|
|
139
|
+
const tadSections = resolveSections(ctx.tadSections, tree.tad);
|
|
140
|
+
const prdSections = resolveSections(ctx.prdSections, tree.prd);
|
|
141
|
+
context = {
|
|
142
|
+
tacs,
|
|
143
|
+
adrs,
|
|
144
|
+
tadSections,
|
|
145
|
+
prdSections,
|
|
146
|
+
unresolvedSections,
|
|
147
|
+
passThrough: {
|
|
148
|
+
existingModules: ctx.existingModules ?? [],
|
|
149
|
+
schemas: ctx.schemas ?? [],
|
|
150
|
+
externalDocs: ctx.externalDocs ?? [],
|
|
151
|
+
other: ctx.other ?? [],
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Section 6 - existing test surface. Presence reporting off the
|
|
157
|
+
// walker maps, not a coverage verdict (§D7). The per-AC `covered`
|
|
158
|
+
// flag here is PRESENCE-based (at least one TC row via tcsByAcId);
|
|
159
|
+
// since w-2026-07-28-005, computeCoverage's own leaf test is stricter
|
|
160
|
+
// (the TC's testPointer must resolve to a real test). `rcf coverage`
|
|
161
|
+
// is the coverage surface; this section only lists what is on the
|
|
162
|
+
// tree, pointers included, so the builder can see what exists.
|
|
163
|
+
const tests = acceptanceCriteria.map((ac) => {
|
|
164
|
+
const suites = tree.tsByAcId.get(ac.id) ?? [];
|
|
165
|
+
const cases = (tree.tcsByAcId.get(ac.id) ?? []).map(({ tsId, tcId }) => {
|
|
166
|
+
const ts = tree.byId.get(tsId);
|
|
167
|
+
const tc = (ts?.testCases ?? []).find((c) => c.id === tcId) ?? {};
|
|
168
|
+
return { tcId, tsId, ...pick(tc, ['description', 'status', 'testPointer']) };
|
|
169
|
+
});
|
|
170
|
+
return { acId: ac.id, covered: cases.length > 0, suites, cases };
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// Section 7 - completion contract (static, parameterised by fbsId).
|
|
174
|
+
const completionContract = {
|
|
175
|
+
markInProgress: `rcf build ${fbsId} --mark inProgress`,
|
|
176
|
+
markComplete: `rcf build ${fbsId} --mark complete`,
|
|
177
|
+
finalise: `rcf finalise ${fbsId} --url <deploy-url>`,
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
const result = {
|
|
181
|
+
fbs: fbsBlock,
|
|
182
|
+
queue: { position, total: queue.items.length },
|
|
183
|
+
bs: tree.bs
|
|
184
|
+
? pick(tree.bs, ['bsId', 'title', 'buildPhilosophy', 'generationStrategy'])
|
|
185
|
+
: null,
|
|
186
|
+
prd: tree.prd ? pick(tree.prd, ['prdId', 'productName']) : null,
|
|
187
|
+
blockedBy,
|
|
188
|
+
dependencies,
|
|
189
|
+
dependents,
|
|
190
|
+
acceptanceCriteria,
|
|
191
|
+
userStories,
|
|
192
|
+
requirements,
|
|
193
|
+
...(context ? { context } : {}),
|
|
194
|
+
tests,
|
|
195
|
+
completionContract,
|
|
196
|
+
};
|
|
197
|
+
return result;
|
|
198
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// JSON formatter for the build verb (Phase 6 §D14). Envelope shapes
|
|
2
|
+
// are stable-by-convention, same contract discipline as Phase 5 D15:
|
|
3
|
+
// no in-place reshape without a new flag or subcommand. These
|
|
4
|
+
// envelopes are the Phase 7 MCP layer's anchor. camelCase throughout;
|
|
5
|
+
// serialisation via JSON.stringify with construction-ordered keys
|
|
6
|
+
// (§D10 determinism).
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Format a build result as a JSON envelope.
|
|
10
|
+
*
|
|
11
|
+
* @param {object} result - QueueResult | BundleResult | next-mode empty envelope
|
|
12
|
+
* @param {('queue'|'bundle'|'next')} mode
|
|
13
|
+
* @returns {string}
|
|
14
|
+
*/
|
|
15
|
+
export function formatJson(result, mode) {
|
|
16
|
+
const envelope = { ok: true, mode, ...result };
|
|
17
|
+
return `${JSON.stringify(envelope, null, 2)}\n`;
|
|
18
|
+
}
|