rcf-lite 0.7.1 → 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.
- package/CHANGELOG.md +97 -0
- package/bin/rcf.js +6 -0
- package/fixtures/canary-manifest.json +9 -9
- package/guidance/harness-template.md +11 -0
- package/guidance/managed/agent-instructions-block.hash +1 -1
- package/guidance/managed/agent-instructions-block.md +11 -0
- package/package.json +5 -3
- package/rcf/adrs/adr-010.json +30 -0
- package/rcf/code-nodes/cn-058.json +18 -0
- package/rcf/code-nodes/cn-059.json +14 -0
- package/rcf/code-nodes/cn-060.json +14 -0
- package/rcf/code-nodes/cn-061.json +14 -0
- package/rcf/code-nodes/cn-062.json +14 -0
- package/rcf/code-nodes/cn-063.json +15 -0
- package/rcf/code-nodes/cn-064.json +15 -0
- package/rcf/code-nodes/cn-065.json +16 -0
- package/rcf/code-nodes/cn-066.json +14 -0
- package/rcf/code-nodes/cn-067.json +15 -0
- package/rcf/code-nodes/cn-068.json +15 -0
- package/rcf/code-nodes/cn-069.json +16 -0
- package/rcf/fbs/fbs-016.json +39 -0
- package/rcf/fbs/fbs-017.json +40 -0
- package/rcf/fbs/fbs-018.json +34 -0
- package/rcf/fbs/fbs-019.json +33 -0
- package/rcf/requirements/req-010.json +20 -0
- package/rcf/test-suites/ts-026.json +54 -0
- package/rcf/test-suites/ts-027.json +115 -0
- package/rcf/test-suites/ts-028.json +46 -0
- package/rcf/test-suites/ts-029.json +46 -0
- package/rcf/user-stories/us-1001.json +56 -0
- package/rcf/user-stories/us-1002.json +96 -0
- package/rcf/user-stories/us-1003.json +48 -0
- package/rcf/user-stories/us-1004.json +48 -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/blueprint/apply.js +464 -0
- package/src/blueprint/conflicts.js +351 -0
- package/src/blueprint/diff.js +82 -0
- package/src/blueprint/index.js +12 -0
- package/src/blueprint/list.js +21 -0
- package/src/blueprint/loader.js +163 -0
- package/src/blueprint/manifest-writer.js +49 -0
- package/src/blueprint/namespace.js +145 -0
- package/src/blueprint/remove.js +105 -0
- package/src/blueprint/resolutions.js +83 -0
- package/src/blueprint/standards.js +148 -0
- package/src/blueprint/supersede.js +318 -0
- package/src/browser-verify/invariants.js +33 -6
- package/src/build/bundle.js +34 -11
- package/src/build/standards-selector.js +52 -0
- package/src/cli/blueprint.js +325 -0
- package/src/cli/create.js +49 -1
- package/src/cli/help.js +8 -0
- package/src/cli/init.js +20 -5
- package/src/cli/read.js +7 -1
- package/src/cli/standards.js +127 -0
- package/src/cli/test-suite.js +7 -2
- package/src/core/store/ids.js +168 -18
- package/src/core/store/loader.js +31 -17
- package/src/core/store/walker.js +62 -4
- package/src/core/store/writer.js +41 -11
- package/src/deployment/index.js +13 -0
- package/src/deployment/placeholder-detector.js +113 -0
- package/src/finalise/detect.js +51 -29
- package/src/finalise/index.js +16 -2
- package/src/finalise/ingest.js +41 -0
- package/src/mcp/tools.js +10 -2
- package/src/query/formatters/table.js +7 -10
- package/src/query/index.js +4 -0
- package/src/query/refuse-on-admissibility.js +73 -0
- package/src/query/trace.js +45 -4
- package/src/ruleset/index.js +140 -0
- package/src/ruleset/ruleset.json +146 -0
- package/src/verify/chain/index.js +31 -0
- package/src/verify/verdict/index.js +67 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"fbsId": "FBS-018",
|
|
3
|
+
"prdId": "PRD-001",
|
|
4
|
+
"bsId": "BS-001",
|
|
5
|
+
"buildOrder": 18,
|
|
6
|
+
"executionStatus": "notStarted",
|
|
7
|
+
"title": "Standards ingestion: rcf standards add + reference-by-default",
|
|
8
|
+
"summary": "Introduce a `rcf standards <verb>` verb family (`add`, `list`) that registers standards packs on the project manifest. Reference-by-default when the source lives inside the project root; copy into `rcf/standards/<slug>/` only when the source lives outside. Manifest registration is authoritative; every add writes a `manifest.standards[]` entry per the 0.4.4 `standardsPackRecord` schema.",
|
|
9
|
+
"approach": "One dispatcher, one applier. src/cli/standards.js parses `add|list`; src/blueprint/standards.js resolves whether the source is inside or outside the project root (via node:path.relative + startsWith), copies when needed, and writes the manifest entry. Copy path is fs.cp recursive with a per-run overwrite guard (idempotent re-adds refuse to re-copy unless `--refresh` is passed; Phase 1 keeps to add-only + idempotent read, `refresh` verb deferred to Phase 3).",
|
|
10
|
+
"deliverables": [
|
|
11
|
+
"src/cli/standards.js: parseArgs + HELP + main() for add/list",
|
|
12
|
+
"src/blueprint/standards.js: registerStandardsPack(root, opts) — in-root vs out-of-root detection + optional copy + manifest.standards[] write",
|
|
13
|
+
"bin/rcf.js: `standards: standardsMain` on SUBCOMMANDS",
|
|
14
|
+
"src/cli/help.js: `standards <verb>` on the TOP_LEVEL block",
|
|
15
|
+
"test/blueprint/standards.test.js: in-root reference / out-of-root copy / idempotency cases keyed to AC-1003-1..4"
|
|
16
|
+
],
|
|
17
|
+
"acIds": [
|
|
18
|
+
"AC-1003-1",
|
|
19
|
+
"AC-1003-2",
|
|
20
|
+
"AC-1003-3",
|
|
21
|
+
"AC-1003-4"
|
|
22
|
+
],
|
|
23
|
+
"contextRequirements": {
|
|
24
|
+
"tacIds": [],
|
|
25
|
+
"adrIds": []
|
|
26
|
+
},
|
|
27
|
+
"dependsOnFbsIds": [],
|
|
28
|
+
"domain": "cli",
|
|
29
|
+
"estimatedSize": "small",
|
|
30
|
+
"riskLevel": "low",
|
|
31
|
+
"notes": "Reference-by-default is ratified decision 2 (design-brief.md v2). Copy path targets rcf/standards/<slug>/ (ratified decision 3).",
|
|
32
|
+
"createdAt": "2026-08-18T22:56:02.353Z",
|
|
33
|
+
"updatedAt": "2026-08-18T22:56:02.353Z"
|
|
34
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"fbsId": "FBS-019",
|
|
3
|
+
"prdId": "PRD-001",
|
|
4
|
+
"bsId": "BS-001",
|
|
5
|
+
"buildOrder": 19,
|
|
6
|
+
"executionStatus": "notStarted",
|
|
7
|
+
"title": "Selective retrieval at bundle assembly (contextRequirements.standardIds)",
|
|
8
|
+
"summary": "Extend `src/build/bundle.js#assembleBundle` so its `context` section reads `contextRequirements.standardIds`. When the FBS carries an operator-authored `standardIds` array, that array is honoured verbatim. Otherwise a deterministic tag-scoring selector reads the FBS work text (title + summary + approach + notes + AC descriptions) against `manifest.standards[]` (tags + slug + summary) and returns the subset of slugs whose tags occur in the work text. The bundle's context.standardIds carries the selection; the standards' summaries render into the standards payload alongside TACs and ADRs.",
|
|
9
|
+
"approach": "The selector is a pure function in src/build/standards-selector.js — no wall clock, no random source, no network. Scoring is deterministic tag-hit counting with lowercase-normalised substring match; ties break by manifest order (standards[] is authored). The bundle assembler prefers operator-authored `contextRequirements.standardIds` when present (design principle 5 override path) and calls the selector otherwise. Empty selection is legitimate and never blocks. Phase 1 keeps the selector local and heuristic; the design brief allows a small model call, but adding an LLM dependency to a pure projection is out of scope for the mechanism landing — the heuristic satisfies the ACs and the seam is where a smarter selector plugs in.",
|
|
10
|
+
"deliverables": [
|
|
11
|
+
"src/build/standards-selector.js: selectStandards(fbs, standards) — deterministic tag-hit scoring returning the ordered subset of slugs",
|
|
12
|
+
"src/build/bundle.js: contextRequirements.standardIds hosts operator override or selector output; standards payload rendered into context alongside tacs/adrs",
|
|
13
|
+
"test/build/standards-selector.test.js: deterministic-selection cases keyed to AC-1004-1, AC-1004-3, AC-1004-4",
|
|
14
|
+
"test/build/bundle-standards.test.js: bundle-shape cases (default + override) keyed to AC-1004-1, AC-1004-2"
|
|
15
|
+
],
|
|
16
|
+
"acIds": [
|
|
17
|
+
"AC-1004-1",
|
|
18
|
+
"AC-1004-2",
|
|
19
|
+
"AC-1004-3",
|
|
20
|
+
"AC-1004-4"
|
|
21
|
+
],
|
|
22
|
+
"contextRequirements": {
|
|
23
|
+
"tacIds": [],
|
|
24
|
+
"adrIds": []
|
|
25
|
+
},
|
|
26
|
+
"dependsOnFbsIds": ["FBS-018"],
|
|
27
|
+
"domain": "build",
|
|
28
|
+
"estimatedSize": "small",
|
|
29
|
+
"riskLevel": "low",
|
|
30
|
+
"notes": "Ratified decision 4 (agentic at bundle-assembly time, operator override supported). Heuristic-tag selector satisfies the deterministic ACs; a smarter selector plugs into the same seam without touching the bundle assembler.",
|
|
31
|
+
"createdAt": "2026-08-18T22:56:02.353Z",
|
|
32
|
+
"updatedAt": "2026-08-18T22:56:02.353Z"
|
|
33
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"createdAt": "2026-08-18T22:54:27.031Z",
|
|
3
|
+
"updatedAt": "2026-08-18T22:57:45.660Z",
|
|
4
|
+
"description": "The framework provides a Blueprint mechanism the operator uses to compose blueprint packages (specifications + assets, not code) onto a project tree at any point in its life. Composition is many blueprints per project. Blueprint-contributed docs are namespaced by the blueprint's slug so multiple blueprints cannot collide on ids. Standards packs register at rcf init (or later via rcf standards add), reference-by-default, copied to rcf/standards/<slug>/ only when the source lives outside the project root. Manifest registration is authoritative. Selective retrieval populates fbs.contextRequirements.standardIds at bundle-assembly time by analysing the FBS work text against the standards tag vocabulary; operator override is supported by authoring the array on the FBS. scope:global ADR contributions surface as blocking conflicts at rcf blueprint add time.",
|
|
5
|
+
"reqId": "REQ-010",
|
|
6
|
+
"prdId": "PRD-001",
|
|
7
|
+
"title": "Blueprint library mechanism (composition, standards, selective retrieval)",
|
|
8
|
+
"category": "functional",
|
|
9
|
+
"domain": "cli",
|
|
10
|
+
"priority": "must",
|
|
11
|
+
"version": "0.1.0",
|
|
12
|
+
"status": "draft",
|
|
13
|
+
"shapeClassification": {
|
|
14
|
+
"shapes": [
|
|
15
|
+
"none"
|
|
16
|
+
],
|
|
17
|
+
"reason": "keyword-scan",
|
|
18
|
+
"classifiedAt": "2026-08-18T22:54:27.048Z"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "TS-026",
|
|
3
|
+
"usId": "US-1001",
|
|
4
|
+
"title": "Blueprint verb family",
|
|
5
|
+
"purpose": "Cover rcf blueprint add/list/remove verb family behaviour and the manifest.blueprints[] write path.",
|
|
6
|
+
"testLevel": "unit",
|
|
7
|
+
"acIds": [
|
|
8
|
+
"AC-1001-1",
|
|
9
|
+
"AC-1001-2",
|
|
10
|
+
"AC-1001-3",
|
|
11
|
+
"AC-1001-4",
|
|
12
|
+
"AC-1001-5"
|
|
13
|
+
],
|
|
14
|
+
"testCases": [
|
|
15
|
+
{
|
|
16
|
+
"id": "TC-026-add-writes-manifest",
|
|
17
|
+
"acId": "AC-1001-1",
|
|
18
|
+
"description": "add on a clean project writes manifest.blueprints[] and copies namespaced contributions",
|
|
19
|
+
"testPointer": "test/blueprint/apply.test.js::applyBlueprint: writes an entry to manifest.blueprints[] and copies namespaced contributions (AC-1001-1)",
|
|
20
|
+
"status": "pending"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"id": "TC-026-list-in-apply-order",
|
|
24
|
+
"acId": "AC-1001-2",
|
|
25
|
+
"description": "list prints applied blueprints in appliedAt order",
|
|
26
|
+
"testPointer": "test/blueprint/list-remove.test.js::listBlueprints: two applied blueprints render in appliedAt order (AC-1001-2)",
|
|
27
|
+
"status": "pending"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"id": "TC-026-remove-refuses-on-referring",
|
|
31
|
+
"acId": "AC-1001-3",
|
|
32
|
+
"description": "remove refuses when a project-authored doc references a contribution id",
|
|
33
|
+
"testPointer": "test/blueprint/list-remove.test.js::removeBlueprint: refuses when a project-authored doc references a contribution id (AC-1001-3)",
|
|
34
|
+
"status": "pending"
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"id": "TC-026-add-idempotent-reapply",
|
|
38
|
+
"acId": "AC-1001-4",
|
|
39
|
+
"description": "re-apply of same slug+version is a no-op",
|
|
40
|
+
"testPointer": "test/blueprint/apply.test.js::applyBlueprint: idempotent re-apply of the same slug + version returns alreadyApplied and does not rewrite the manifest (AC-1001-4)",
|
|
41
|
+
"status": "pending"
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"id": "TC-026-help-advertises-blueprint",
|
|
45
|
+
"acId": "AC-1001-5",
|
|
46
|
+
"description": "rcf --help advertises blueprint as a top-level command",
|
|
47
|
+
"testPointer": "test/blueprint/cli.test.js::rcf --help advertises `blueprint` and `standards` as top-level commands (AC-1001-5)",
|
|
48
|
+
"status": "pending"
|
|
49
|
+
}
|
|
50
|
+
],
|
|
51
|
+
"status": "draft",
|
|
52
|
+
"createdAt": "2026-08-19T00:00:00Z",
|
|
53
|
+
"updatedAt": "2026-08-19T00:00:00Z"
|
|
54
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "TS-027",
|
|
3
|
+
"usId": "US-1002",
|
|
4
|
+
"title": "Namespacing + scope:global ADR conflict surfacing",
|
|
5
|
+
"purpose": "Cover blueprint-contributed id namespacing and the scope:global ADR conflict-detection path.",
|
|
6
|
+
"testLevel": "unit",
|
|
7
|
+
"acIds": [
|
|
8
|
+
"AC-1002-1",
|
|
9
|
+
"AC-1002-2",
|
|
10
|
+
"AC-1002-3",
|
|
11
|
+
"AC-1002-4",
|
|
12
|
+
"AC-1002-5",
|
|
13
|
+
"AC-1002-6",
|
|
14
|
+
"AC-1002-7",
|
|
15
|
+
"AC-1002-8",
|
|
16
|
+
"AC-1002-9",
|
|
17
|
+
"AC-1002-10"
|
|
18
|
+
],
|
|
19
|
+
"testCases": [
|
|
20
|
+
{
|
|
21
|
+
"id": "TC-027-prefix-namespacing",
|
|
22
|
+
"acId": "AC-1002-1",
|
|
23
|
+
"description": "prefix-family ids gain a kebab-slug prefix on stamp",
|
|
24
|
+
"testPointer": "test/blueprint/namespace.test.js::stampId: bare prefix-family id gains the slug as PREFIX",
|
|
25
|
+
"status": "pending"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"id": "TC-027-suffix-namespacing",
|
|
29
|
+
"acId": "AC-1002-1",
|
|
30
|
+
"description": "suffix-family ids gain a kebab-slug suffix on stamp",
|
|
31
|
+
"testPointer": "test/blueprint/namespace.test.js::stampId: bare suffix-family id gains the slug as SUFFIX",
|
|
32
|
+
"status": "pending"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"id": "TC-027-global-adr-conflict-surfaced",
|
|
36
|
+
"acId": "AC-1002-2",
|
|
37
|
+
"description": "add refuses on scope:global ADR topic collision",
|
|
38
|
+
"testPointer": "test/blueprint/apply.test.js::applyBlueprint: scope:global ADR conflict across two applied blueprints refuses the second add (AC-1002-2)",
|
|
39
|
+
"status": "pending"
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"id": "TC-027-conflict-report-shape",
|
|
43
|
+
"acId": "AC-1002-3",
|
|
44
|
+
"description": "conflict report leads with per-side title + decision headers and names four honest resolution paths with actual blueprint slugs substituted (Phase 3.5 reshape)",
|
|
45
|
+
"testPointer": "test/blueprint/conflicts.test.js::renderConflictReport: leads with per-side headers (topic in parens) and names four honest resolution paths (Phase 3.5)",
|
|
46
|
+
"status": "pending"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"id": "TC-027-nonglobal-namespaced-no-conflict",
|
|
50
|
+
"acId": "AC-1002-4",
|
|
51
|
+
"description": "namespaced non-global TACs do not conflict",
|
|
52
|
+
"testPointer": "test/blueprint/conflicts.test.js::detectGlobalAdrConflicts: namespaced TACs never conflict (AC-1002-4)",
|
|
53
|
+
"status": "pending"
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"id": "TC-027-shipped-blueprints-option-3-executable",
|
|
57
|
+
"acId": "AC-1002-5",
|
|
58
|
+
"description": "shipped SPA + REST option-3 verbatim-from-refused-state money probe: fresh scaffold; add spa; add rest (refused with the reshaped message); follow option 3 EXACTLY as printed (`rcf blueprint supersede errorEnvelope --incoming <rest>` and `rcf blueprint supersede authModel --incoming <rest>`) with ZERO prep; then re-run `rcf blueprint add <rest>` and verify co-residence + honour",
|
|
59
|
+
"testPointer": "test/blueprint/cli.test.js::shipped SPA + REST: option 3 executes VERBATIM from the refused-add state (rev-3 P1-1, Baz ruling on the conflict-pair supersede precondition)",
|
|
60
|
+
"status": "pending"
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"id": "TC-027-add-reason-persists",
|
|
64
|
+
"acId": "AC-1002-6",
|
|
65
|
+
"description": "rcf blueprint add --reason attaches the ruling text to every resolutions[] record on that add",
|
|
66
|
+
"testPointer": "test/blueprint/cli.test.js::rcf blueprint add --reason: reason lands on every resolution record (rev-2 P1-2)",
|
|
67
|
+
"status": "pending"
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"id": "TC-027-add-reason-whitespace-refused",
|
|
71
|
+
"acId": "AC-1002-6",
|
|
72
|
+
"description": "whitespace-only --reason on add refused at the writer edge (exit 2, no record written)",
|
|
73
|
+
"testPointer": "test/blueprint/cli.test.js::rcf blueprint add --reason: whitespace-only reason is refused at the writer edge (rev-2 P1-2)",
|
|
74
|
+
"status": "pending"
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"id": "TC-027-add-resolve-dedupes-with-warn",
|
|
78
|
+
"acId": "AC-1002-7",
|
|
79
|
+
"description": "duplicate --resolve for the same topic keeps the first declaration, drops the rest, and warns on stderr",
|
|
80
|
+
"testPointer": "test/blueprint/cli.test.js::rcf blueprint add --resolve: duplicate topic dedupes with a stderr warning; only one record persists (rev-2 P3-b)",
|
|
81
|
+
"status": "pending"
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"id": "TC-027-add-resolve-error-no-double-prefix",
|
|
85
|
+
"acId": "AC-1002-7",
|
|
86
|
+
"description": "--resolve validation error carries the CLI's single '[error] blueprint add: ' prefix — the underlying rcfError message MUST NOT prepend its own 'blueprint add: '",
|
|
87
|
+
"testPointer": "test/blueprint/cli.test.js::rcf blueprint add --resolve: validation error carries no doubled prefix (rev-2 P3-a)",
|
|
88
|
+
"status": "pending"
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
"id": "TC-027-conflict-enrichment-symmetric",
|
|
92
|
+
"acId": "AC-1002-8",
|
|
93
|
+
"description": "conflict rendering + --json shape carry title + one-sentence decision on BOTH sides (incoming read from disk, existing read from tree)",
|
|
94
|
+
"testPointer": "test/blueprint/cli.test.js::conflict enrichment is symmetric: incoming side carries title + decision read off the blueprint contribution file (rev-2 nit)",
|
|
95
|
+
"status": "pending"
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"id": "TC-027-topic-free-label-camelcase-verbatim",
|
|
99
|
+
"acId": "AC-1002-9",
|
|
100
|
+
"description": "ADR-010 chain-level decision on record: shipped camelCase topics are accepted verbatim on manifest.resolutions[].topic; the ADR id tail is kebab-ified; no rcfError names 'kebab slug' on any non-whitespace topic",
|
|
101
|
+
"testPointer": "test/blueprint/cli.test.js::shipped SPA + REST: option 3 executes VERBATIM from the refused-add state (rev-3 P1-1, Baz ruling on the conflict-pair supersede precondition)",
|
|
102
|
+
"status": "pending"
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"id": "TC-027-supersede-no-double-prefix",
|
|
106
|
+
"acId": "AC-1002-10",
|
|
107
|
+
"description": "every error path in supersede.js exits at the CLI with exactly one `[error] blueprint supersede: ` prefix; the doubled `blueprint supersede: blueprint supersede:` string never appears",
|
|
108
|
+
"testPointer": "test/blueprint/cli.test.js::rcf blueprint supersede: every rcfError message lands with exactly one `[error] blueprint supersede:` prefix (rev-3 mechanical P1)",
|
|
109
|
+
"status": "pending"
|
|
110
|
+
}
|
|
111
|
+
],
|
|
112
|
+
"status": "draft",
|
|
113
|
+
"createdAt": "2026-08-19T00:00:00Z",
|
|
114
|
+
"updatedAt": "2026-08-19T00:00:00Z"
|
|
115
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "TS-028",
|
|
3
|
+
"usId": "US-1003",
|
|
4
|
+
"title": "Standards ingestion",
|
|
5
|
+
"purpose": "Cover rcf standards add reference-by-default and out-of-root copy paths.",
|
|
6
|
+
"testLevel": "unit",
|
|
7
|
+
"acIds": [
|
|
8
|
+
"AC-1003-1",
|
|
9
|
+
"AC-1003-2",
|
|
10
|
+
"AC-1003-3",
|
|
11
|
+
"AC-1003-4"
|
|
12
|
+
],
|
|
13
|
+
"testCases": [
|
|
14
|
+
{
|
|
15
|
+
"id": "TC-028-add-writes-manifest-entry",
|
|
16
|
+
"acId": "AC-1003-1",
|
|
17
|
+
"description": "add writes a validated standards[] manifest entry",
|
|
18
|
+
"testPointer": "test/blueprint/standards.test.js::registerStandardsPack: in-repo source is referenced in place (AC-1003-1, AC-1003-2)",
|
|
19
|
+
"status": "pending"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"id": "TC-028-inrepo-referenced-in-place",
|
|
23
|
+
"acId": "AC-1003-2",
|
|
24
|
+
"description": "in-repo source is referenced in place, no copy written",
|
|
25
|
+
"testPointer": "test/blueprint/standards.test.js::registerStandardsPack: in-repo source is referenced in place (AC-1003-1, AC-1003-2)",
|
|
26
|
+
"status": "pending"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"id": "TC-028-outofroot-copied-in",
|
|
30
|
+
"acId": "AC-1003-3",
|
|
31
|
+
"description": "out-of-root source copied into rcf/standards/<slug>/",
|
|
32
|
+
"testPointer": "test/blueprint/standards.test.js::registerStandardsPack: out-of-root source is copied into rcf/standards/<slug>/ (AC-1003-3)",
|
|
33
|
+
"status": "pending"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"id": "TC-028-manifest-validates",
|
|
37
|
+
"acId": "AC-1003-4",
|
|
38
|
+
"description": "manifest with standards[] entries validates against the shipped schema",
|
|
39
|
+
"testPointer": "test/blueprint/standards.test.js::registerStandardsPack: in-repo source is referenced in place (AC-1003-1, AC-1003-2)",
|
|
40
|
+
"status": "pending"
|
|
41
|
+
}
|
|
42
|
+
],
|
|
43
|
+
"status": "draft",
|
|
44
|
+
"createdAt": "2026-08-19T00:00:00Z",
|
|
45
|
+
"updatedAt": "2026-08-19T00:00:00Z"
|
|
46
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "TS-029",
|
|
3
|
+
"usId": "US-1004",
|
|
4
|
+
"title": "Selective retrieval at bundle assembly",
|
|
5
|
+
"purpose": "Cover the agentic tag-scoring selector and its operator-override path at bundle assembly.",
|
|
6
|
+
"testLevel": "unit",
|
|
7
|
+
"acIds": [
|
|
8
|
+
"AC-1004-1",
|
|
9
|
+
"AC-1004-2",
|
|
10
|
+
"AC-1004-3",
|
|
11
|
+
"AC-1004-4"
|
|
12
|
+
],
|
|
13
|
+
"testCases": [
|
|
14
|
+
{
|
|
15
|
+
"id": "TC-029-agentic-selection",
|
|
16
|
+
"acId": "AC-1004-1",
|
|
17
|
+
"description": "bundle carries agentically-selected standardIds",
|
|
18
|
+
"testPointer": "test/build/bundle-standards.test.js::bundle: agentic selection picks matching standards from manifest.standards[] (AC-1004-1)",
|
|
19
|
+
"status": "pending"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"id": "TC-029-operator-override",
|
|
23
|
+
"acId": "AC-1004-2",
|
|
24
|
+
"description": "operator-authored standardIds override the selector",
|
|
25
|
+
"testPointer": "test/build/bundle-standards.test.js::bundle: operator-authored contextRequirements.standardIds overrides the agentic selection (AC-1004-2)",
|
|
26
|
+
"status": "pending"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"id": "TC-029-empty-never-blocks",
|
|
30
|
+
"acId": "AC-1004-3",
|
|
31
|
+
"description": "empty selection is legitimate; bundle assembles without blocking",
|
|
32
|
+
"testPointer": "test/build/bundle-standards.test.js::bundle: empty selection is legitimate and never blocks (AC-1004-3)",
|
|
33
|
+
"status": "pending"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"id": "TC-029-deterministic",
|
|
37
|
+
"acId": "AC-1004-4",
|
|
38
|
+
"description": "selection is stable per input across repeat invocations",
|
|
39
|
+
"testPointer": "test/build/standards-selector.test.js::selectStandards: selection is deterministic across repeat invocations (AC-1004-4)",
|
|
40
|
+
"status": "pending"
|
|
41
|
+
}
|
|
42
|
+
],
|
|
43
|
+
"status": "draft",
|
|
44
|
+
"createdAt": "2026-08-19T00:00:00Z",
|
|
45
|
+
"updatedAt": "2026-08-19T00:00:00Z"
|
|
46
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"usId": "US-1001",
|
|
3
|
+
"prdId": "PRD-001",
|
|
4
|
+
"reqId": "REQ-010",
|
|
5
|
+
"version": "0.1.0",
|
|
6
|
+
"status": "draft",
|
|
7
|
+
"title": "Blueprint verb family: add, list, remove, upgrade",
|
|
8
|
+
"description": "A `rcf blueprint` verb family provides the primary operator seat for composing blueprints onto a project. Every verb runs against the current tree, is idempotent where the semantics allow, and writes to the manifest's `blueprints[]` registry so the applied set is greppable and mechanical.",
|
|
9
|
+
"asA": "operator on an RCF project",
|
|
10
|
+
"iWant": "to add, list, remove and upgrade blueprints against my project tree from a single `rcf blueprint` verb family",
|
|
11
|
+
"soThat": "composition is a first-class action instead of a one-shot init flag, and I can layer several blueprints across the life of a project without hand-editing the manifest",
|
|
12
|
+
"acceptanceCriteria": [
|
|
13
|
+
{
|
|
14
|
+
"id": "AC-1001-1",
|
|
15
|
+
"description": "`rcf blueprint add <slug>` on a project with no blueprints applied writes an entry into `manifest.blueprints[]` and prints the applied slug and version",
|
|
16
|
+
"given": "a validated project tree with no existing `blueprints[]` entries and a resolvable blueprint slug",
|
|
17
|
+
"when": "`rcf blueprint add <slug>` runs",
|
|
18
|
+
"then": "the manifest gains one `blueprints[]` entry (slug, version, appliedAt, source, and any contributions[] the blueprint wrote), and stdout names the slug and applied version",
|
|
19
|
+
"testable": true
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"id": "AC-1001-2",
|
|
23
|
+
"description": "`rcf blueprint list` prints every applied blueprint (slug, version, appliedAt) in apply order",
|
|
24
|
+
"given": "a project with one or more blueprints already applied",
|
|
25
|
+
"when": "`rcf blueprint list` runs",
|
|
26
|
+
"then": "every applied blueprint prints, one per line, ordered by `appliedAt`, with slug, version and apply timestamp columns",
|
|
27
|
+
"testable": true
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"id": "AC-1001-3",
|
|
31
|
+
"description": "`rcf blueprint remove <slug>` removes the manifest entry and refuses when any project-authored doc references a contributed id",
|
|
32
|
+
"given": "a project with the blueprint applied and no project-authored doc referencing any of the contributions[] ids",
|
|
33
|
+
"when": "`rcf blueprint remove <slug>` runs",
|
|
34
|
+
"then": "the `blueprints[]` entry is dropped from the manifest; when a project-authored doc DOES reference a contribution, the verb exits non-zero and prints the referencing id + path without mutating the manifest",
|
|
35
|
+
"testable": true
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"id": "AC-1001-4",
|
|
39
|
+
"description": "`rcf blueprint add <slug>` is idempotent: repeating on an already-applied clean slug is a no-op with a friendly message",
|
|
40
|
+
"given": "a project with `<slug>` already in `blueprints[]`",
|
|
41
|
+
"when": "`rcf blueprint add <slug>` runs again with no conflicts to surface",
|
|
42
|
+
"then": "the manifest is not rewritten, exit code is 0, and stdout names the slug as already applied at the applied version",
|
|
43
|
+
"testable": true
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"id": "AC-1001-5",
|
|
47
|
+
"description": "`rcf --help` lists `blueprint` alongside the other top-level commands",
|
|
48
|
+
"given": "the unified `rcf` bin",
|
|
49
|
+
"when": "`rcf --help` runs",
|
|
50
|
+
"then": "the top-level usage advertises `blueprint <verb>` as an available command",
|
|
51
|
+
"testable": true
|
|
52
|
+
}
|
|
53
|
+
],
|
|
54
|
+
"createdAt": "2026-08-18T22:54:33.194Z",
|
|
55
|
+
"updatedAt": "2026-08-18T22:54:33.194Z"
|
|
56
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
{
|
|
2
|
+
"usId": "US-1002",
|
|
3
|
+
"prdId": "PRD-001",
|
|
4
|
+
"reqId": "REQ-010",
|
|
5
|
+
"version": "0.1.0",
|
|
6
|
+
"status": "draft",
|
|
7
|
+
"title": "Composition, namespaced ids and scope:global ADR conflict surfacing",
|
|
8
|
+
"description": "Multiple blueprints may be applied to a single project. Blueprint-contributed docs are namespaced by the blueprint's slug so ids cannot collide across blueprints. A blueprint's ADR contributions may be tagged `scope: global`; two applied blueprints each contributing a `scope: global` ADR are a genuine conflict and `rcf blueprint add` surfaces both entries and refuses to write. Conflict-presentation ergonomics are prototype-unknown #1 (design brief); this US covers the first pass.",
|
|
9
|
+
"asA": "operator on an RCF project",
|
|
10
|
+
"iWant": "blueprint contributions to compose without id collisions, and for `scope: global` ADR conflicts to be surfaced with both sides visible before anything is written",
|
|
11
|
+
"soThat": "composition never silently merges incompatible whole-project decisions",
|
|
12
|
+
"acceptanceCriteria": [
|
|
13
|
+
{
|
|
14
|
+
"id": "AC-1002-1",
|
|
15
|
+
"description": "Blueprint-contributed doc ids are namespaced with the blueprint's slug so composition cannot collide on ids at the id layer",
|
|
16
|
+
"given": "two blueprints `spa` and `rest` that both contribute a REQ, a US and an ADR",
|
|
17
|
+
"when": "both are applied to the same project via `rcf blueprint add`",
|
|
18
|
+
"then": "every contributed id carries the blueprint slug as its prefix (REQ/US: `spa-REQ-001`, `rest-REQ-001`) or as its kebab-suffix (ADR: `ADR-004-spa-theme`, `ADR-005-rest-versioning`); no two contributed ids across the two blueprints match",
|
|
19
|
+
"testable": true
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"id": "AC-1002-2",
|
|
23
|
+
"description": "`rcf blueprint add <slug>` refuses when the new blueprint contributes a `scope: global` ADR on a topic already contributed by an applied blueprint",
|
|
24
|
+
"given": "a project with blueprint `alpha` already applied contributing an ADR tagged `scope: global` on topic `versioning`",
|
|
25
|
+
"when": "`rcf blueprint add beta` runs and blueprint `beta` also contributes a `scope: global` ADR on `versioning`",
|
|
26
|
+
"then": "the verb exits non-zero, prints both ADR ids and their file paths, and the manifest is unchanged",
|
|
27
|
+
"testable": true
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"id": "AC-1002-3",
|
|
31
|
+
"description": "Conflict output leads with per-side headers (ADR title + one-sentence decision) and names four honest resolution paths with actual blueprint slugs filled in (Phase 3.5 reshape)",
|
|
32
|
+
"given": "the conflict case above",
|
|
33
|
+
"when": "the verb prints the conflict",
|
|
34
|
+
"then": "the output opens with `conflict on topic (<topic>)` and one line per side carrying `blueprint <slug>: <title> — <decision>` (or `<id> at <path>` when the tree did not carry the ADR body), a `refs:` footer with both ids + paths, and four honest resolutions numbered 1-4: adopt-incoming (`rcf blueprint remove <existing-slug>`), keep-existing (do not add the incoming), supersede via a project-level ADR (`rcf blueprint supersede <topic>`), and declare-on-add (`rcf blueprint add <source> --resolve <topic>=project:<ADR-id>`) — all with the actual conflicting blueprint slugs already substituted, no `<slug>` placeholders",
|
|
35
|
+
"testable": true
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"id": "AC-1002-4",
|
|
39
|
+
"description": "Non-global TACs, ADRs, REQs, USs and FBSes are namespaced and cannot conflict",
|
|
40
|
+
"given": "two blueprints each contributing a TAC on the same topic without `scope: global`",
|
|
41
|
+
"when": "both are applied",
|
|
42
|
+
"then": "both TACs co-exist under distinct namespaced ids; no conflict is surfaced",
|
|
43
|
+
"testable": true
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"id": "AC-1002-5",
|
|
47
|
+
"description": "The supersession record's two sides are the CONFLICT PAIR — one currently-applied blueprint's scope:global ADR on the topic PLUS one incoming (would-be-applied) blueprint's scope:global ADR on the same topic — and the `rcf blueprint supersede` verb accepts the incoming blueprint as the second side. Operator ruling (Baz, w-2026-08-19-008 round 3): option 3 as printed in the reshaped conflict message MUST be executable EXACTLY as printed from the refused-add state, with zero prep. Mechanism (builder judgement against the chain): the reshaped conflict message names option 3 as `rcf blueprint supersede <topic> --incoming <source>`, carrying the very same `<source>` argument the refused add was invoked with; `supersede` loads the incoming blueprint from `--incoming <source>`, finds its scope:global ADR on `<topic>`, stamps its id into the incoming blueprint's namespace, and uses `{slug, adrId}` for the incoming blueprint as the second side of `manifest.resolutions[].supersedes[]`. The scaffolded project ADR file lists both blueprint ADR ids (applied + incoming stamped) under `relatedAdrs[]`; after supersede returns, re-running `rcf blueprint add <source>` succeeds because the detector honours the freshly-recorded resolution. `--incoming <source>` is required when applied count on the topic is < 2 and is silently accepted (informational) when applied count is already >= 2.",
|
|
48
|
+
"given": "a fresh project with ZERO applied blueprints and the shipped SPA + REST blueprints on disk (both carry scope:global ADRs on camelCase topics `errorEnvelope` and `authModel`)",
|
|
49
|
+
"when": "the operator runs the flow verbatim as printed: `rcf blueprint add <spa>`; `rcf blueprint add <rest>` (refused with the reshaped four-honest-paths message); then follows option 3 EXACTLY as printed: `rcf blueprint supersede errorEnvelope --incoming <rest>` and `rcf blueprint supersede authModel --incoming <rest>`; then re-runs `rcf blueprint add <rest>`",
|
|
50
|
+
"then": "each supersede exits 0, scaffolds a project ADR at `rcf/adrs/adr-NNN-<kebab-topic>.json` (topic string verbatim on `manifest.resolutions[].topic`; ADR id tail kebab-ified per ADR-010), and appends a `manifest.resolutions[]` record listing both blueprint ADR ids in `supersedes[]`; the subsequent `rcf blueprint add <rest>` exits 0 (detector honours the resolution) and both blueprint ADR files co-reside on disk with the project supersedes-ADR alongside them",
|
|
51
|
+
"testable": true
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"id": "AC-1002-6",
|
|
55
|
+
"description": "`rcf blueprint add --reason <text>` attaches the operator's ruling text to every `manifest.resolutions[]` record minted on that add. Whitespace-only reasons are refused at the writer edge (schema does not enforce a minimum floor on the field itself; the writer's floor is emptiness, not length).",
|
|
56
|
+
"given": "an incoming blueprint whose apply would collide on one or more topics, resolved via one or more --resolve declarations",
|
|
57
|
+
"when": "the operator adds `--reason \"...\"` alongside the --resolve flags",
|
|
58
|
+
"then": "every newly-minted resolutions[] record carries the reason string verbatim; a whitespace-only --reason exits 2 with an error naming the topic and no record is written",
|
|
59
|
+
"testable": true
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"id": "AC-1002-7",
|
|
63
|
+
"description": "Duplicate `--resolve` declarations for the same topic within a single `rcf blueprint add` are deduped: the first declaration is kept, subsequent declarations on the same topic are dropped, and a `[warn]` line names each dropped topic on stderr. The add still exits 0 on success; the manifest carries exactly one resolutions[] record per topic.",
|
|
64
|
+
"given": "an incoming blueprint whose apply would collide on topic X",
|
|
65
|
+
"when": "the operator passes `--resolve X=project:ADR-A --resolve X=project:ADR-B` on the same command",
|
|
66
|
+
"then": "the manifest carries exactly one resolutions[] record on topic X with `resolvedByAdrId: ADR-A` (first-wins), and stderr carries a warn line naming X as a duplicate --resolve topic",
|
|
67
|
+
"testable": true
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"id": "AC-1002-8",
|
|
71
|
+
"description": "Conflict enrichment is symmetric: the CLI's conflict report and `--json` output both carry `title` + one-sentence `decision` on BOTH sides of a globalAdrTopic conflict — the existing side read from the walker's tree, the incoming side read from the blueprint's ADR file on disk. Round-1 only enriched the existing side, which read asymmetrically against shipped blueprints.",
|
|
72
|
+
"given": "an incoming blueprint that would collide with an applied blueprint on a scope:global ADR topic, both ADRs carrying a title and a decision",
|
|
73
|
+
"when": "`rcf blueprint add <source>` refuses with the reshaped message (with or without --json)",
|
|
74
|
+
"then": "both `incoming` and `existing` sides render `blueprint <slug>: <title> — <decision>` in the terminal output, and `conflicts[].incoming` and `conflicts[].existing` both carry `title` and `decision` in the --json output",
|
|
75
|
+
"testable": true
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"id": "AC-1002-9",
|
|
79
|
+
"description": "Chain-level decision (ADR-010): the blueprint conflict-topic vocabulary is a FREE LABEL — a lookup key used by the detector (exact-match between an incoming ADR's topic and each applied ADR's topic) and stored verbatim on `manifest.resolutions[].topic`. No character-class constraint beyond the manifest schema's `minLength: 1` plus a writer-edge whitespace-only refuse. The shipped SPA + REST blueprints declare their scope:global ADR topics in camelCase (authModel, errorEnvelope, clientRouting, theming, clientState, apiVersioning, logging) and that vocabulary is canonical. Kebab-ification is confined to the projection into the ADR-id slug tail (ADR grammar requires kebab), never at the vocabulary edge.",
|
|
80
|
+
"given": "the shipped SPA + REST blueprint packs on disk (topics: authModel, errorEnvelope, ...)",
|
|
81
|
+
"when": "supersede and add --resolve run on any of those topics as printed",
|
|
82
|
+
"then": "the writer accepts the topic verbatim; the manifest.resolutions[].topic value byte-matches the applied ADR's topic; the scaffolded project ADR id carries a kebab-cased tail derived from the topic (`authModel` -> `ADR-NNN-auth-model`); no rcfError references 'kebab slug' or 'valid kebab' on any non-whitespace topic string",
|
|
83
|
+
"testable": true
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"id": "AC-1002-10",
|
|
87
|
+
"description": "Error-message prefix discipline on supersede: rcfError messages returned from `src/blueprint/supersede.js` MUST NOT prepend their own `blueprint supersede: ` prefix — the CLI edge already writes `[error] blueprint supersede: ` on every rcfError from the supersede verb. Round-2 caught the same bug on the `add` path; round-3 catches it on the supersede path (six sites: supersede.js:54, :70, :85, :107, :145, :160).",
|
|
88
|
+
"given": "any error path in `src/blueprint/supersede.js` (missing topic, --reason whitespace-only, sub-2 applied ADRs, missing prd/tad, overwrite guard tripped, ADR write ioFailure)",
|
|
89
|
+
"when": "`rcf blueprint supersede ...` runs into that error path at the CLI",
|
|
90
|
+
"then": "the printed stderr line matches `^\\[error\\] blueprint supersede: <message>$` with exactly one `blueprint supersede: ` prefix; the string `blueprint supersede: blueprint supersede:` does not appear anywhere in the output",
|
|
91
|
+
"testable": true
|
|
92
|
+
}
|
|
93
|
+
],
|
|
94
|
+
"createdAt": "2026-08-18T22:54:33.194Z",
|
|
95
|
+
"updatedAt": "2026-08-19T14:30:00.000Z"
|
|
96
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"usId": "US-1003",
|
|
3
|
+
"prdId": "PRD-001",
|
|
4
|
+
"reqId": "REQ-010",
|
|
5
|
+
"version": "0.1.0",
|
|
6
|
+
"status": "draft",
|
|
7
|
+
"title": "Standards ingestion: reference by default, copy only when out of root",
|
|
8
|
+
"description": "Standards packs are per operator or organisation and register at `rcf init` (or later via `rcf standards add`). Manifest registration is authoritative. In-repo sources are referenced in place. Out-of-root sources are copied into `rcf/standards/<slug>/` so the tree remains portable. Provenance (personal vs corporate) is recorded per pack.",
|
|
9
|
+
"asA": "operator on an RCF project",
|
|
10
|
+
"iWant": "to register standards packs against my project tree, referencing in place when the source is already inside the repo and copying into `rcf/standards/<slug>/` only when the source lives outside the project root",
|
|
11
|
+
"soThat": "duplication inside the repo is not accepted, portability across machines is preserved for external sources, and the manifest is the single source of truth for what standards apply",
|
|
12
|
+
"acceptanceCriteria": [
|
|
13
|
+
{
|
|
14
|
+
"id": "AC-1003-1",
|
|
15
|
+
"description": "`rcf standards add <path>` writes a manifest entry with the pack's id, slug, sourcePath, tag vocabulary, testsProvidedBy and provenance",
|
|
16
|
+
"given": "a project with no standards registered and a valid standards folder anywhere on disk",
|
|
17
|
+
"when": "`rcf standards add <path> --slug wsd-naming --tags naming,conventions --tests-provided-by agent --provenance corporate` runs",
|
|
18
|
+
"then": "the manifest gains one `standards[]` entry with all named fields populated and validates against the schema",
|
|
19
|
+
"testable": true
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"id": "AC-1003-2",
|
|
23
|
+
"description": "In-repo sources are referenced in place; no copy is written",
|
|
24
|
+
"given": "a standards folder path inside the project root (e.g. `docs/standards/wsd-naming`)",
|
|
25
|
+
"when": "`rcf standards add` runs against it",
|
|
26
|
+
"then": "the `sourcePath` records the in-repo path, `copyPath` is absent, and no file is written under `rcf/standards/`",
|
|
27
|
+
"testable": true
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"id": "AC-1003-3",
|
|
31
|
+
"description": "Out-of-root sources are mirrored into `rcf/standards/<slug>/` and `copyPath` records the mirror",
|
|
32
|
+
"given": "a standards folder path outside the project root (an absolute path pointing outside the repo)",
|
|
33
|
+
"when": "`rcf standards add` runs against it",
|
|
34
|
+
"then": "the source is copied into `rcf/standards/<slug>/`, the manifest entry records the original `sourcePath` and the in-tree `copyPath`, and re-running the verb is idempotent (no double copy)",
|
|
35
|
+
"testable": true
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"id": "AC-1003-4",
|
|
39
|
+
"description": "The manifest.standards[] entry validates against the shipped schema (@stravica-ai/rcf-schemas standardsPackRecord)",
|
|
40
|
+
"given": "a manifest with one or more standards[] entries",
|
|
41
|
+
"when": "`rcf validate` runs",
|
|
42
|
+
"then": "the manifest validates without errors under the shipped schema",
|
|
43
|
+
"testable": true
|
|
44
|
+
}
|
|
45
|
+
],
|
|
46
|
+
"createdAt": "2026-08-18T22:54:33.194Z",
|
|
47
|
+
"updatedAt": "2026-08-18T22:54:33.194Z"
|
|
48
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"usId": "US-1004",
|
|
3
|
+
"prdId": "PRD-001",
|
|
4
|
+
"reqId": "REQ-010",
|
|
5
|
+
"version": "0.1.0",
|
|
6
|
+
"status": "draft",
|
|
7
|
+
"title": "Selective retrieval at bundle assembly (contextRequirements.standardIds)",
|
|
8
|
+
"description": "The build-context bundle assembler runs a deterministic selector during FBS bundle assembly that reads the FBS work text plus the standards manifest (tags, titles, and short summaries) and returns the subset of standard slugs relevant to the FBS. The selected slugs populate `contextRequirements.standardIds` for that bundle only. Operator override is supported by authoring the array on the FBS.",
|
|
9
|
+
"asA": "operator on an RCF project with standards registered",
|
|
10
|
+
"iWant": "the bundle assembler to select relevant standards per FBS automatically, and to honour my override when I author the array on the FBS itself",
|
|
11
|
+
"soThat": "the default path does not require operator authoring and the override path exists when the agentic selection needs correcting",
|
|
12
|
+
"acceptanceCriteria": [
|
|
13
|
+
{
|
|
14
|
+
"id": "AC-1004-1",
|
|
15
|
+
"description": "The bundle for an FBS with no operator-authored standardIds carries the agentically-selected subset",
|
|
16
|
+
"given": "a project with two standards packs (`wsd-naming` tagged `naming`, `security-baseline` tagged `security`) and an FBS whose summary names `security`",
|
|
17
|
+
"when": "the bundle assembler runs for that FBS with no operator-authored `standardIds`",
|
|
18
|
+
"then": "the bundle's `context` section carries `standardIds` including `security-baseline` and the pack's rendered summary in the standards payload",
|
|
19
|
+
"testable": true
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"id": "AC-1004-2",
|
|
23
|
+
"description": "Operator-authored `contextRequirements.standardIds` on the FBS overrides the agentic selection",
|
|
24
|
+
"given": "the same project and FBS as AC-1004-1, but with `contextRequirements.standardIds: ['wsd-naming']` authored on the FBS",
|
|
25
|
+
"when": "the bundle assembler runs",
|
|
26
|
+
"then": "the bundle's `context.standardIds` matches the operator-authored value byte-for-byte; the agentic selection is not invoked",
|
|
27
|
+
"testable": true
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"id": "AC-1004-3",
|
|
31
|
+
"description": "The assembler never blocks on an empty selection",
|
|
32
|
+
"given": "a project with no standards registered, or an FBS whose work text matches no standard",
|
|
33
|
+
"when": "the bundle assembler runs",
|
|
34
|
+
"then": "the bundle assembles with `context.standardIds` empty (or absent) and no standards payload; the assembler does not error",
|
|
35
|
+
"testable": true
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"id": "AC-1004-4",
|
|
39
|
+
"description": "Selection is stable per input (deterministic tag-scoring; no wall-clock or random source)",
|
|
40
|
+
"given": "an FBS work text and a standards manifest, both fixed",
|
|
41
|
+
"when": "the selector is invoked twice",
|
|
42
|
+
"then": "the two returned selections are equal (same slugs, same order)",
|
|
43
|
+
"testable": true
|
|
44
|
+
}
|
|
45
|
+
],
|
|
46
|
+
"createdAt": "2026-08-18T22:54:33.194Z",
|
|
47
|
+
"updatedAt": "2026-08-18T22:54:33.194Z"
|
|
48
|
+
}
|