ma-agents 3.5.6 → 3.6.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/.ma-agents.json +10 -0
- package/AGENTS.md +97 -0
- package/MANIFEST.yaml +3 -0
- package/README.md +17 -0
- package/_bmad-output/implementation-artifacts/21-10-profile-reconfigure.md +30 -6
- package/_bmad-output/implementation-artifacts/21-11-profile-uninstall.md +2 -1
- package/_bmad-output/implementation-artifacts/21-2-universal-instruction-block-expansion.md +217 -62
- package/_bmad-output/implementation-artifacts/21-3-roomodes-template-bmad-modes.md +196 -73
- package/_bmad-output/implementation-artifacts/21-4-agents-md-template-opencode.md +242 -53
- package/_bmad-output/implementation-artifacts/21-5-clinerules-template-extension.md +180 -41
- package/_bmad-output/implementation-artifacts/21-6-onprem-layered-guardrails.md +250 -75
- package/_bmad-output/implementation-artifacts/21-7-bmad-persona-phase-prefix.md +221 -89
- package/_bmad-output/implementation-artifacts/21-8-vllm-reference-doc-readme.md +121 -63
- package/_bmad-output/implementation-artifacts/21-9-tests-validation.md +332 -61
- package/_bmad-output/implementation-artifacts/bug-bmad-recompile-fails-on-airgapped-network.md +112 -0
- package/_bmad-output/implementation-artifacts/sprint-status.yaml +3 -2
- package/bin/cli.js +59 -0
- package/docs/deployment/vllm-nemotron.md +130 -0
- package/lib/agents.js +17 -2
- package/lib/bmad-customize/bmm-analyst.customize.yaml +8 -0
- package/lib/bmad-customize/bmm-architect.customize.yaml +2 -0
- package/lib/bmad-customize/bmm-dev.customize.yaml +2 -0
- package/lib/bmad-customize/bmm-pm.customize.yaml +2 -0
- package/lib/bmad-customize/bmm-qa.customize.yaml +2 -0
- package/lib/bmad-customize/bmm-quick-flow-solo-dev.customize.yaml +8 -0
- package/lib/bmad-customize/bmm-sm.customize.yaml +2 -0
- package/lib/bmad-customize/bmm-tech-writer.customize.yaml +2 -0
- package/lib/bmad-customize/bmm-ux-designer.customize.yaml +2 -0
- package/lib/bmad.js +293 -1
- package/lib/installer.js +617 -43
- package/lib/merge/roomodes.js +125 -0
- package/lib/profile.js +25 -2
- package/lib/reconfigure.js +334 -0
- package/lib/templates/agents-md.template.md +67 -0
- package/lib/templates/clinerules.template.md +13 -0
- package/lib/templates/instruction-block-onprem.template.md +86 -0
- package/lib/templates/instruction-block-universal.template.md +29 -0
- package/lib/templates/roomodes.template.yaml +96 -0
- package/lib/uninstall.js +314 -0
- package/package.json +4 -3
- package/test/agents-md.test.js +398 -0
- package/test/bmad-extension.test.js +2 -2
- package/test/bmad-persona-phase-prefix.test.js +271 -0
- package/test/clinerules.test.js +339 -0
- package/test/instruction-block.test.js +388 -0
- package/test/integration-verification.test.js +2 -2
- package/test/migration-validation.test.js +2 -2
- package/test/offline-recompile.test.js +237 -0
- package/test/onprem-injection.test.js +425 -32
- package/test/onprem-layer.test.js +419 -0
- package/test/reconfigure.test.js +436 -0
- package/test/roomodes.test.js +343 -0
- package/test/uninstall.test.js +402 -0
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
# Story 21.6: On-Prem Layered Guardrails
|
|
2
2
|
|
|
3
|
-
Status:
|
|
3
|
+
Status: Review
|
|
4
|
+
|
|
5
|
+
**Resolutions (2026-04-15, epic owner):**
|
|
6
|
+
- **AC #7 Shape A/B:** **Shape A committed.** On-prem content flows through the composer into the Universal Rules section of AGENTS.md; the Critical Behavior Rules section is NOT modified by this story. Rationale: (a) preserves Decision A's "templates are static text, no placeholders" contract; (b) no downstream depends on on-prem rules being the last rules in AGENTS.md; (c) epic's "append to Critical Behavior Rules" is satisfied logically — on-prem rules land inside AGENTS.md's marker block. Shape B is explicitly rejected. Task 4.2's conditional "if Shape B" branch is removed.
|
|
7
|
+
- **AC #1 `/no_think` delivery (Shape X vs Y):** **Shape X committed** for this story. 21.6 owns the on-prem TEMPLATE text that states the rule; Story 21.7 owns the active persona-prompt prefix that enforces it. The two layers are complementary by design.
|
|
8
|
+
- **AC #1 field-playbook additional rules:** scoped strictly to the four categories enumerated in AC #1. Any additional rules surfaced during Task 1 become follow-up stories, not AC expansion.
|
|
9
|
+
- **Task 2.2:** downgraded to "raise a blocker against Story 21.2" per Decision A ownership. 21.6 does not modify composer logic.
|
|
4
10
|
|
|
5
11
|
## Story
|
|
6
12
|
|
|
@@ -10,103 +16,272 @@ So that Nemotron and other local LLMs stop hallucinating `str_replace_editor`, d
|
|
|
10
16
|
|
|
11
17
|
## Acceptance Criteria
|
|
12
18
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
- A
|
|
17
|
-
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
19
|
+
> Acceptance criteria marked **(gap-fill)** are additions made by the story author to make the epic's AC operational and testable. They do not contradict the epic; they refine the "how" where the epic only stated the "what". Open questions are flagged inline rather than resolved by author guesswork. Controlling invariants: **NFR44** (byte-identity for standard profile), **NFR46** (idempotency), **NFR47** (Roo Code `fileRegex` — consumed unchanged here), **NFR18** (additive JSON-merge — not regressed).
|
|
20
|
+
|
|
21
|
+
1. **On-prem template file exists.** A new file `lib/templates/instruction-block-onprem.template.md` (new) is present and contains, as self-contained markdown suitable for concatenation after the universal block:
|
|
22
|
+
- A `/no_think` reasoning-OFF directive applied as a system-prompt prefix for planning-phase use.
|
|
23
|
+
- A "NEVER create files in `~/.claude/` or any user home directory; all files go under the project directory" rule.
|
|
24
|
+
- A "do NOT reference or use `str_replace_editor` or any Claude Code-specific tool that may not exist in this agent" rule.
|
|
25
|
+
- Reasoning-mode and sampling guidance per BMAD phase (planning: reasoning OFF, low temperature; implementation: reasoning ON, moderate temperature).
|
|
26
|
+
|
|
27
|
+
2. **Composer append wiring — on-prem layer.** When `profile === 'on-prem'` AND `lib/templates/instruction-block-onprem.template.md` exists, `composeInstructionBlock({ profile, manifestPath })` (Story 21.2, `lib/installer.js`) appends the on-prem template content to the universal content separated by exactly one blank line (Story 21.2 AC #3 clause). This story delivers the template file; the composer branch is already specified in Story 21.2 AC #3 and requires no signature change. If Story 21.2's composer is refactored, the contract verified here is: standard-profile install omits on-prem content; on-prem-profile install concatenates universal + one blank line + on-prem.
|
|
28
|
+
|
|
29
|
+
3. **(gap-fill) Placeholder contract.** The on-prem template contains NO placeholders (`{{...}}`). Any rendered per-agent values (e.g., manifest path) belong in the universal template. Rationale: keeps the on-prem layer a pure content append and avoids a second substitution pass. If a future on-prem rule needs a per-agent value, add the placeholder to the universal template and reference the substituted value from on-prem rule text via a stable anchor.
|
|
30
|
+
|
|
31
|
+
**Reconciliation with Story 21.4 (closes adversarial P0 #2):** The composer (owned by Story 21.2) appends this template's content to the universal content verbatim; callers that need per-project values use template stamping done by `lib/installer.js` AFTER composition. 21.6 owns only the on-prem template file and profile-gated content; it does NOT own composer, merger, or stamper logic.
|
|
32
|
+
|
|
33
|
+
4. **Profile isolation — standard profile (NFR44).** When `profile === 'standard'` (or `getProfile(projectRoot)` returns `undefined`), the rendered content inside `<!-- MA-AGENTS-START -->` / `<!-- MA-AGENTS-END -->` markers in every agent's instruction file MUST NOT contain the strings `/no_think`, `str_replace_editor`, or `~/.claude/`. **Scope-narrowing (see Dev Notes → NFR44 test scope):** these three literals are the exhaustive negative-assertion set for standard-profile absence. The reasoning-mode / sampling prose from AC #1 is covered on the POSITIVE side only ("on-prem content present") and is NOT part of this NFR44 negative assertion. Verified by grep-style assertion against rendered output for Claude Code, Cline (both files), Roo Code rules (`.roo/rules/00-ma-agents.md`), Cursor, Kilocode, Copilot, Gemini, and the OpenCode `opencode.json::instructions[]` ma-agents-prefixed string. AGENTS.md's legitimate single `~/.claude/` occurrence (Story 21.4 AC #9 exception) is unaffected by this AC — it lives in the AGENTS.md Critical Behavior Rules anchor which is authored as universal safety content, not gated by profile.
|
|
34
|
+
|
|
35
|
+
5. **Profile merge — on-prem profile.** When `profile === 'on-prem'`, every agent receiving markdown-marker injection (AC #4 list minus OpenCode JSON) contains BOTH the universal block content AND the on-prem block content within the same marker region. The OpenCode `opencode.json::instructions[]` ma-agents-prefixed string likewise contains both (concatenated into the single string emitted by the composer).
|
|
36
|
+
|
|
37
|
+
6. **(gap-fill) `.roomodes` `customInstructions` on-prem augmentation.** Per epic technical note at `epics.md` line 4074, on-prem profile MUST append on-prem rules to each ma-agents-owned mode's `customInstructions` in `.roomodes`. Implementation: Story 21.3's `applyExtraInstructionTemplates` substitutes `{{UNIVERSAL_BLOCK}}` inside `roomodes.template.yaml` with `composeInstructionBlock({ profile, manifestPath })` output. Because `composeInstructionBlock` already conditionalizes on profile (AC #2), on-prem content appears inside each mode's `customInstructions` with NO change to Story 21.3's merger or template shape — the conditional happens entirely inside the composer call site. Verified by asserting `/no_think` is present in each of `bmad-pm`, `bmad-architect`, `bmad-techlead`, `bmad-dev` `customInstructions` after an on-prem install, and absent after a standard install.
|
|
38
|
+
|
|
39
|
+
7. **(gap-fill) `AGENTS.md` `## Critical Behavior Rules` on-prem augmentation.** Per epic technical note at `epics.md` line 4074, on-prem profile MUST append on-prem rules to AGENTS.md's `## Critical Behavior Rules` section. Story 21.4 AC #11 Open question proposed `## Critical Behavior Rules` as the stable anchor. Implementation: because the universal block expanded via `{{UNIVERSAL_BLOCK}}` inside `agents-md.template.md` already carries on-prem content in the on-prem profile (AC #5), the visible effect is that `AGENTS.md`'s Universal Rules section carries the on-prem rules, not the Critical Behavior Rules section literally. This story's contract: on-prem content IS present in the marker-block region of AGENTS.md after an on-prem install; its exact positional anchor may be either the Universal Rules section (transparent via `composeInstructionBlock`) OR the Critical Behavior Rules section (requires a second substitution anchor). See Open question below for the decision branch.
|
|
40
|
+
|
|
41
|
+
> **Resolved (2026-04-15, Shape A):** On-prem content flows through `{{UNIVERSAL_BLOCK}}` and appears after the universal rules in AGENTS.md. The Critical Behavior Rules section carries only the universal "never write to `~/.claude/`" rule (its existing authored content) and is NOT modified by this story. The epic's "append to Critical Behavior Rules" wording is satisfied logically — on-prem rules land inside AGENTS.md's marker block. Shape B (adding `{{ONPREM_CRITICAL_RULES}}` placeholder) is rejected because it contradicts Decision A ("templates are static text, no placeholders").
|
|
42
|
+
|
|
43
|
+
8. **(gap-fill) `.clinerules` / `.cline/clinerules.md` on-prem augmentation.** Per Story 21.5 AC #8, on-prem content flows through `composeInstructionBlock` into both Cline files inside the markers. This story adds no Cline-specific code; verification is a test assertion that both Cline files contain on-prem content in on-prem profile installs and lack it in standard-profile installs. Story 21.5's `ClinerulesDualFileDriftError` drift check is not disturbed — on-prem content renders identically into both files (render once, write twice).
|
|
44
|
+
|
|
45
|
+
9. **(gap-fill) BMAD persona phase prefix is NOT in scope here.** The `lib/bmad-customize/*.customize.yaml` on-prem `on_prem_phase_prefix` field (epic Story 21.7) is a separate surface. Story 21.6 is the on-prem layer for the PROMPT-INJECTION / per-tool instruction files and `.roomodes`/`AGENTS.md`/`.clinerules` overlays. Persona-level system-prompt prefixes ship in Story 21.7. The two stories co-exist: on-prem install runs both (21.6 stamps files, 21.7 composes persona prompts).
|
|
46
|
+
|
|
47
|
+
10. **(gap-fill) Idempotency (NFR46).** Two consecutive installs with `profile === 'on-prem'` and identical project state produce:
|
|
48
|
+
- Byte-identical content inside the marker region of every markdown-injection agent's instruction file.
|
|
49
|
+
- Byte-identical `.roomodes` YAML (preserves Story 21.3 AC #10 serialization pinning).
|
|
50
|
+
- Byte-identical `AGENTS.md` marker-block content (Story 21.4 AC #8).
|
|
51
|
+
- Unchanged `opencode.json::instructions[]` (the ma-agents-prefixed string is replaced in place, not duplicated; no extra entries emerge from the on-prem layer — the on-prem content is concatenated inside the single existing string).
|
|
52
|
+
The on-prem template source contains no timestamps, random IDs, or ordering-sensitive content. AC #6's `composeInstructionBlock` output is deterministic by construction (string concatenation of two file reads).
|
|
53
|
+
|
|
54
|
+
11. **(gap-fill) Additive JSON-merge not regressed (NFR18).** This story changes the CONTENT of the ma-agents-prefixed string in `opencode.json::instructions[]` (by virtue of the composer appending on-prem content when profile=on-prem) but does NOT change the number of entries, their order, or any other key in `opencode.json`. The existing `isMaEntry` prefix filter (`lib/installer.js` lines ~372–374) continues to identify the single ma-agents entry for in-place replacement. Story 21.4's additional `"AGENTS.md"` entry is unaffected.
|
|
55
|
+
|
|
56
|
+
12. **(gap-fill) Roo Code `fileRegex` not regressed (NFR47).** The on-prem layer changes `customInstructions` content only. The `groups` / `fileRegex` per-mode restrictions authored in Story 21.3's `roomodes.template.yaml` are untouched by this story. Verified by asserting the four modes' `fileRegex` patterns are identical between standard and on-prem renders (content diff only in `customInstructions`).
|
|
57
|
+
|
|
58
|
+
13. **(gap-fill) Upgrade-safety on profile flip.** Switching from `standard` → `on-prem` (or vice versa) on an existing install is NOT in this story's scope — it is the Story 21.10 (Profile Reconfigure) surface. This story's contract is limited to the RENDER when profile is set at install time. Flipping the profile by hand-editing `.ma-agents.json` and re-running `install` will be detected by Story 21.2 AC #10's drift detection (the in-marker content differs from the newly-composed content) and handled per that AC's interactive-confirm / --yes-warn pattern. No new upgrade path is introduced here.
|
|
59
|
+
|
|
60
|
+
> **Resolved (2026-04-15):** Scope this story strictly to the four on-prem rule categories enumerated in AC #1. Additional rules discovered in field playbook `optimizing-local-llm-coding-agents-bmad.md` during Task 1 become follow-up stories, not AC expansion.
|
|
61
|
+
|
|
62
|
+
> **Resolved (2026-04-15, Shape X):** The `/no_think` directive in AC #1 is delivered as instruction-text in the on-prem template — Story 21.6 owns the rule body that tells the LLM to prepend `/no_think` to planning-phase prompts. Shape Y (active persona prefix prepended by code) is Story 21.7's surface via the `on_prem_phase_prefix` field. The two layers are complementary by design: 21.6 states the rule; 21.7 enforces it at persona-prompt composition.
|
|
30
63
|
|
|
31
64
|
## Tasks / Subtasks
|
|
32
65
|
|
|
33
|
-
- [ ] Task 1:
|
|
34
|
-
- [ ]
|
|
35
|
-
- [ ]
|
|
36
|
-
- [ ] 3
|
|
37
|
-
- [ ]
|
|
38
|
-
|
|
39
|
-
- [ ] Task
|
|
40
|
-
- [ ]
|
|
41
|
-
- [ ]
|
|
42
|
-
- [ ]
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
- [ ]
|
|
46
|
-
- [ ]
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
- [ ]
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
- [ ]
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
- [ ]
|
|
56
|
-
- [ ]
|
|
57
|
-
|
|
66
|
+
- [ ] **Task 1: Author the on-prem template** (AC #1, #3, #4, #10)
|
|
67
|
+
- [ ] 1.1 Create `lib/templates/instruction-block-onprem.template.md` (new) containing the four rule categories from AC #1 as self-contained markdown. No placeholders (AC #3).
|
|
68
|
+
- [ ] 1.2 Author rule text verbatim grounded in the epic AC #1 bullets and the field playbook `optimizing-local-llm-coding-agents-bmad.md` (epic intro line 3888 references). Do NOT copy universal-block rules (profile isolation — on-prem layer is strictly additive).
|
|
69
|
+
- [ ] 1.3 Verify by inspection that the template contains the literals `/no_think`, `str_replace_editor`, `~/.claude/` (they are the on-prem-only strings — NFR44's negative assertion on standard-profile output is the contract).
|
|
70
|
+
- [ ] 1.4 Verify the template has no trailing whitespace on any line and ends with a single trailing newline (idempotency — AC #10).
|
|
71
|
+
|
|
72
|
+
- [ ] **Task 2: Verify composer consumption** (AC #2, #5) — VERIFY-ONLY; 21.6 does NOT own composer logic.
|
|
73
|
+
- [ ] 2.1 Read `composeInstructionBlock({ profile, projectRoot })` in `lib/installer.js` (Story 21.2 delivery). Confirm it reads `lib/templates/instruction-block-onprem.template.md` when `profile === 'on-prem'` AND the file exists, and appends it after the universal content separated by one blank line.
|
|
74
|
+
- [ ] 2.2 If the composer's on-prem append branch is missing or broken, raise a blocker against Story 21.2 and halt this story. Do NOT modify composer logic in 21.6 — composer ownership belongs to 21.2 per Decision A.
|
|
75
|
+
- [ ] 2.3 Confirm no call site depends on the on-prem template being absent — Story 21.2's graceful-fallback behavior is preserved (standard-profile installs continue to return universal-only).
|
|
76
|
+
|
|
77
|
+
- [ ] **Task 3: Roo Code `.roomodes` on-prem augmentation** (AC #6, #12)
|
|
78
|
+
- [ ] 3.1 No code change required — Story 21.3's `applyExtraInstructionTemplates` substitutes `{{UNIVERSAL_BLOCK}}` via `composeInstructionBlock` which now includes on-prem content in on-prem profile. Verified by test only.
|
|
79
|
+
- [ ] 3.2 Add test assertion: on-prem install with Roo Code selected produces `.roomodes` where every ma-agents-owned mode's `customInstructions` contains `/no_think`. Standard install does not.
|
|
80
|
+
|
|
81
|
+
- [ ] **Task 4: AGENTS.md on-prem augmentation** (AC #7) — VERIFY-ONLY for 21.6. Shape A is committed (see resolution above and AC #7 resolution block).
|
|
82
|
+
- [ ] 4.1 Verify on-prem content reaches AGENTS.md transparently via the composer output consumed by the `markdown-markers` merger (Story 21.4). Add test assertion that `AGENTS.md` after an on-prem install contains `/no_think` within the `<!-- MA-AGENTS-START -->` marker block, and does not contain it after a standard install. 21.6 does NOT modify 21.4's template or merger.
|
|
83
|
+
|
|
84
|
+
- [ ] **Task 5: Cline on-prem augmentation** (AC #8)
|
|
85
|
+
- [ ] 5.1 No code change required — Story 21.5's Cline path flows through `composeInstructionBlock`. Add test assertion that both Cline files carry on-prem content in on-prem profile and lack it in standard profile.
|
|
86
|
+
|
|
87
|
+
- [ ] **Task 6: OpenCode JSON-merge content change** (AC #11)
|
|
88
|
+
- [ ] 6.1 No code change required — the json-merge path (`lib/installer.js` lines ~365–405) emits the single ma-agents-prefixed string whose CONTENT now includes on-prem rules when profile=on-prem (via the same `composeInstructionBlock` call).
|
|
89
|
+
- [ ] 6.2 Add test assertion that `opencode.json::instructions[]` after an on-prem install contains exactly one ma-agents-prefixed entry (not two), that entry contains `/no_think`, and `instructions[]` length/order is unchanged versus a second consecutive install.
|
|
90
|
+
|
|
91
|
+
- [ ] **Task 7: Unit and integration tests** — see Testing section below.
|
|
92
|
+
|
|
93
|
+
- [ ] **Task 8: Documentation touch-up**
|
|
94
|
+
- [ ] 8.1 No new docs in this story. Story 21.8 introduces the broader Epic 21 / on-prem doc surface. If an existing doc already covers instruction injection, append a one-sentence pointer to the on-prem template — otherwise skip.
|
|
58
95
|
|
|
59
96
|
## Dev Notes
|
|
60
97
|
|
|
61
|
-
###
|
|
98
|
+
### Ownership boundary (canonical)
|
|
99
|
+
|
|
100
|
+
- **Composer** (`composeInstructionBlock({ profile, projectRoot }) → string`) lives in Story 21.2, `lib/installer.js`. It concatenates the universal template and, when `profile === 'on-prem'`, appends this story's on-prem template content verbatim, separated by exactly one blank line.
|
|
101
|
+
- **Merger** logic (yaml-customModes for `.roomodes`, markdown-markers for `AGENTS.md`, markdown-marker writer for per-tool instruction files, JSON additive merge for `opencode.json`) lives in its respective owning story (21.3, 21.4, 21.2/21.5, 21.2). 21.6 does NOT own merger logic.
|
|
102
|
+
- **Stamper** (project-value substitution of `{{...}}` placeholders such as manifest path) runs AFTER composition, in `lib/installer.js`. Templates — including this story's on-prem template — have NO `{{...}}` placeholders. 21.6 does NOT own stamper logic.
|
|
103
|
+
- **21.6 owns only:** (a) the on-prem template file `lib/templates/instruction-block-onprem.template.md`, and (b) the profile-gated content inside it.
|
|
104
|
+
|
|
105
|
+
### NFR44 test scope (decision D — scope narrowing)
|
|
106
|
+
|
|
107
|
+
The three literal strings — `/no_think`, `str_replace_editor`, `~/.claude/` — are the **exhaustive negative-assertion set** for verifying standard-profile absence of on-prem content. Tests 7.3 and 7.7 assert only these three literals; no additional tokens are required for the negative case.
|
|
108
|
+
|
|
109
|
+
The reasoning-mode and sampling prose introduced in AC #1 (planning: reasoning OFF / low temperature; implementation: reasoning ON / moderate temperature) is tested on the **POSITIVE side only** — i.e., "on-prem content present in on-prem profile output" (tests 7.1, 7.4, 7.8). It is NOT part of the NFR44 negative-assertion set and must not be used to gate standard-profile absence. Referenced from AC #4 where the scope-narrowing is called out.
|
|
110
|
+
|
|
111
|
+
### Architecture compliance
|
|
112
|
+
|
|
113
|
+
- **Decision P3-3 (Local-LLM / On-Prem Agent Tuning Profile)** — This story delivers the on-prem layer of the two-layer injection model defined in Story 21.2. Story 21.2 built the composition function and the graceful-fallback stub; Story 21.6 ships the template file and verifies the end-to-end behavior across every injection surface (Stories 21.3, 21.4, 21.5) transparently via `composeInstructionBlock`.
|
|
62
114
|
|
|
63
|
-
- **
|
|
64
|
-
- **NFR44** — Standard profile must remain free of on-prem-specific output. Test 7.1 enforces.
|
|
65
|
-
- **NFR46** — Deterministic stamping for both profiles.
|
|
115
|
+
- **NFR44 (byte-identity for standard profile)** — `_bmad-output/planning-artifacts/epics.md` line 4071 ("verified by absence of the strings `/no_think`, `str_replace_editor`, `~/.claude/` in standard-profile output"). AC #4 is the contract; tests 7.3–7.7 enforce it across every injection surface. **This is the most important invariant in Story 21.6.** The on-prem template content MUST NOT leak into the standard-profile render.
|
|
66
116
|
|
|
67
|
-
|
|
117
|
+
- **NFR46 (idempotency)** — `epics.md` lines 471, 4144 (item c). Two consecutive on-prem installs produce byte-identical output across every injection surface. AC #10 is the contract; test 7.9 enforces it. Inherited from Stories 21.2 (whitespace-parity insert/replace), 21.3 (pinned YAML serialization), 21.4 (marker-wrap determinism).
|
|
68
118
|
|
|
69
|
-
|
|
70
|
-
|------|--------|
|
|
71
|
-
| `lib/templates/instruction-block-onprem.template.md` | CREATE |
|
|
72
|
-
| `lib/templates/roomodes.template.yaml` | MODIFY — add on-prem content (conditional or via parallel template) |
|
|
73
|
-
| `lib/installer.js` | MODIFY — profile-conditional stamping for `.roomodes`, `AGENTS.md`, `.clinerules` per-tool extensions |
|
|
74
|
-
| `lib/merge/roomodes.js` | MODIFY — accept profile arg, compose `customInstructions` accordingly |
|
|
75
|
-
| `test/onprem-guardrails.test.js` | CREATE |
|
|
119
|
+
- **NFR47 (Roo Code application-layer `fileRegex` enforcement)** — `epics.md` lines 4144 (item e), 4152. This story does not touch the `groups`/`fileRegex` structure authored in Story 21.3's template. AC #12 is a non-regression assertion; test 7.11 enforces.
|
|
76
120
|
|
|
77
|
-
|
|
121
|
+
- **NFR18 (additive JSON-merge for OpenCode)** — `epics.md` line 4229. This story changes the content of the single ma-agents-prefixed entry in `opencode.json::instructions[]` but does not add entries, reorder entries, or touch other keys. AC #11 is a non-regression assertion; test 7.10 enforces.
|
|
78
122
|
|
|
79
|
-
|
|
123
|
+
### Verified source-tree surface
|
|
80
124
|
|
|
81
|
-
|
|
125
|
+
| File | Exists? | Role in this story |
|
|
126
|
+
|------|---------|--------------------|
|
|
127
|
+
| `lib/profile.js` | verified (Story 21.1, lines 29–43) | Read-only — consumed via `getProfile(projectRoot)` through `composeInstructionBlock` call sites |
|
|
128
|
+
| `lib/installer.js` | verified | `composeInstructionBlock` (new in Story 21.2) reads the on-prem template delivered here. Task 2.2 may extend the append branch if Story 21.2 left it as a stub |
|
|
129
|
+
| `lib/agents.js` | verified | Read-only — no changes. Enumerates the agents whose instruction files receive the on-prem-augmented block (Claude Code, Cline × 2, Roo Code rules, Cursor, Kilocode, Copilot, Gemini, OpenCode, Anti-gravity) |
|
|
130
|
+
| `lib/templates/instruction-block-universal.template.md` | **new (Story 21.2)** | Authored in Story 21.2; unchanged here |
|
|
131
|
+
| `lib/templates/instruction-block-onprem.template.md` | **new (this story)** | Authored here per AC #1 |
|
|
132
|
+
| `lib/templates/roomodes.template.yaml` | **new (Story 21.3)** | Unchanged here — on-prem content reaches `.roomodes` transparently via `{{UNIVERSAL_BLOCK}}` |
|
|
133
|
+
| `lib/templates/agents-md.template.md` | **new (Story 21.4)** | Unchanged by this story (Shape A committed — AC #7 resolution) |
|
|
134
|
+
| `lib/templates/clinerules.template.md` | **new (Story 21.5)** | Unchanged here |
|
|
135
|
+
| `lib/merge/roomodes.js` | **new (Story 21.3)** | Unchanged — merger treats `customInstructions` as opaque string |
|
|
136
|
+
| `test/onprem-injection.test.js` | verified (exists — Story 21.9's target expansion surface) | This story ADDS a sibling file `test/onprem-layer.test.js` (new) to avoid collision with Story 21.9's planned expansion |
|
|
137
|
+
| `test/profile.test.js` | verified (Story 21.1) | Reference for test framework and temp-dir isolation pattern |
|
|
82
138
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
139
|
+
### Composition pattern (end state after Story 21.6 lands)
|
|
140
|
+
|
|
141
|
+
```
|
|
142
|
+
composeInstructionBlock({ profile, manifestPath })
|
|
143
|
+
├── universal template (Story 21.2 — always applied; {{MANIFEST_PATH}} substituted)
|
|
144
|
+
└── on-prem template (this story — applied when profile === 'on-prem'; no placeholders)
|
|
145
|
+
|
|
146
|
+
Consumed by:
|
|
147
|
+
├── updateAgentInstructions markdown branch (Story 21.2 AC #4): Claude Code, Cline ×2, Roo Code rules, Cursor, Kilocode, Copilot, Gemini, Anti-gravity
|
|
148
|
+
├── updateAgentInstructions json-merge branch (Story 21.2 AC #8): OpenCode opencode.json::instructions[]
|
|
149
|
+
├── applyExtraInstructionTemplates yaml-customModes merger (Story 21.3): .roomodes customInstructions per mode
|
|
150
|
+
└── applyExtraInstructionTemplates markdown-markers merger (Story 21.4): AGENTS.md marker block
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Injection-site map (for reviewers)
|
|
154
|
+
|
|
155
|
+
On-prem content reaches every injection site TRANSPARENTLY through `composeInstructionBlock`. The only code change required (if Story 21.2 left the append branch as a stub) is activating the branch — Task 2.2. No call site is modified. No new function is introduced.
|
|
156
|
+
|
|
157
|
+
### Library and pattern references
|
|
158
|
+
|
|
159
|
+
- **Template discovery**: `lib/installer.js` loads `lib/templates/*.template.md` via synchronous `fs.readFileSync` (Story 21.2 Task 2.2). No change here.
|
|
160
|
+
- **Profile resolution**: `getProfile(projectRoot)` from `lib/profile.js` (Story 21.1 verified lines 29–43). No change here.
|
|
161
|
+
- **Markers**: `<!-- MA-AGENTS-START -->` / `<!-- MA-AGENTS-END -->` — defined and used by `updateAgentInstructions`. No change here.
|
|
162
|
+
- **Atomic writes**: the atomic-write pattern from Story 21.3 (`lib/installer.js` lines ~395–398, temp-file + rename) is inherited by `applyExtraInstructionTemplates`. No change here.
|
|
163
|
+
|
|
164
|
+
### Field playbook reference
|
|
165
|
+
|
|
166
|
+
The on-prem rules originate from `optimizing-local-llm-coding-agents-bmad.md` (referenced in epic intro at `_bmad-output/planning-artifacts/epics.md` lines 3886–3895). Four concrete failure modes documented there map 1:1 to AC #1's four rule categories:
|
|
167
|
+
- Nemotron hallucinating `str_replace_editor` → AC #1 no-str_replace_editor rule.
|
|
168
|
+
- Local LLMs dumping output into `~/.claude/` → AC #1 no-home-dir-writes rule.
|
|
169
|
+
- Local LLMs overthinking planning prompts with reasoning mode on by default → AC #1 `/no_think` + reasoning-OFF planning guidance.
|
|
170
|
+
- Local LLMs skipping BMAD planning to start coding → addressed by the UNIVERSAL block's BMAD phase discipline rule (Story 21.2); the on-prem layer adds the sampling-per-phase guidance.
|
|
88
171
|
|
|
89
172
|
### Out of Scope
|
|
90
173
|
|
|
91
|
-
-
|
|
92
|
-
-
|
|
174
|
+
- Universal template content (Story 21.2).
|
|
175
|
+
- `.roomodes` template authorship and the `yaml-customModes` merger (Story 21.3).
|
|
176
|
+
- `AGENTS.md` template authorship (Story 21.4).
|
|
177
|
+
- `.clinerules` template authorship (Story 21.5).
|
|
178
|
+
- BMAD persona phase prefix via `lib/bmad-customize/*.customize.yaml` `on_prem_phase_prefix` field (Story 21.7 — separate composition surface, NOT `composeInstructionBlock`).
|
|
179
|
+
- vLLM deployment doc and README on-prem section (Story 21.8).
|
|
180
|
+
- Cross-tool NFR44/NFR46 integration tests (Story 21.9; this story's tests are scoped to the on-prem layer's content presence/absence).
|
|
181
|
+
- Profile-reconfigure flow covering profile-flip drift (Story 21.10).
|
|
182
|
+
- Profile-uninstall removal of on-prem marker content (Story 21.11).
|
|
183
|
+
- Bumping `manifestVersion` — already at `1.2.0` (Story 21.1).
|
|
184
|
+
- Installing or managing the inference server (vLLM) — documentation-only surface, see epic intro line 3895.
|
|
185
|
+
|
|
186
|
+
## Dependencies
|
|
187
|
+
|
|
188
|
+
### Upstream (blocking)
|
|
189
|
+
|
|
190
|
+
- **Story 21.1 (done)** — `lib/profile.js::getProfile(projectRoot)` consumed transitively via `composeInstructionBlock`. Verified at `lib/profile.js` lines 29–43.
|
|
191
|
+
- **Story 21.2 (Ready)** — `composeInstructionBlock({ profile, manifestPath })` in `lib/installer.js` is the sole consumer of the on-prem template. Story 21.2 AC #3 pre-specifies the append branch ("When `profile === 'on-prem'` AND `lib/templates/instruction-block-onprem.template.md` (new, ships in Story 21.6) exists, appends that file's content after the universal content, separated by one blank line."). This story ships the template file; Task 2.2 activates the branch if it was left as a stub.
|
|
192
|
+
- **Story 21.3 (Ready)** — `.roomodes` template consumes `{{UNIVERSAL_BLOCK}}` via `applyExtraInstructionTemplates`. On-prem content reaches per-mode `customInstructions` transparently.
|
|
193
|
+
- **Story 21.4 (Ready)** — `AGENTS.md` template consumes `{{UNIVERSAL_BLOCK}}` via `markdown-markers` merger. Shape A is committed (AC #7 resolution), so 21.6 reuses 21.4's template unchanged.
|
|
194
|
+
- **Story 21.5 (Ready)** — Cline's two files consume `composeInstructionBlock` via `updateAgentInstructions`'s existing markdown branch; on-prem content flows in transparently.
|
|
195
|
+
|
|
196
|
+
### Downstream (consumers of this story's surface)
|
|
197
|
+
|
|
198
|
+
- **Story 21.7** — BMAD persona phase prefix via customize-loader. Independent composition surface; does NOT consume `composeInstructionBlock`. Cross-reference only — an on-prem install activates both 21.6 (per-tool files) and 21.7 (persona system-prompt prefix).
|
|
199
|
+
- **Story 21.8** — vLLM deployment doc + README on-prem section. Documents the same `/no_think` directive that AC #1 stamps into the per-tool templates; NOT an implementation dependency, but content drift between the on-prem template here and the vLLM doc would confuse users. Recommend a test assertion that both sources mention `/no_think` and `str_replace_editor` verbatim; wording can diverge but token presence must be consistent.
|
|
200
|
+
- **Story 21.9** — Integration tests verifying (a) standard profile produces NO on-prem-specific strings across every injection surface, (b) on-prem profile produces BOTH layers, (c) idempotency across two on-prem installs, (d) `.roomodes` slug-collision behavior is profile-independent. Tests 7.3–7.9 in this story's Testing section are unit/integration-level subsets; Story 21.9 adds the cross-tool integration coverage.
|
|
201
|
+
- **Story 21.10 (Profile Reconfigure)** — Re-stamps profile-dependent artifacts when profile flips. Reuses `composeInstructionBlock`; the drift-detection path from Story 21.2 AC #10 handles the standard↔on-prem content diff.
|
|
202
|
+
- **Story 21.11 (Profile Uninstall)** — Removes marker-block content (including on-prem layer) on `uninstall --profile-artifacts`. No special handling for on-prem content — marker removal is layer-agnostic.
|
|
203
|
+
|
|
204
|
+
## Testing
|
|
205
|
+
|
|
206
|
+
**Framework and isolation:** Match the pattern in `test/profile.test.js` (Story 21.1) and `test/opencode-json-merge.test.js` (Epic 9) — node's built-in test runner, `os.tmpdir()` + `fs.mkdtempSync` per-test temporary project roots, never mutate the repo's own `.ma-agents.json`, `lib/templates/`, or `opencode.json`. Use the real `lib/profile.js` (`setProfile(tmpRoot, 'standard' | 'on-prem')`) against the tmp project root. Do NOT stub `lib/installer.js` or `lib/agents.js` — use the real modules to catch integration bugs.
|
|
207
|
+
|
|
208
|
+
New test file: `test/onprem-layer.test.js` (new) — avoids collision with `test/onprem-injection.test.js` which Story 21.9 will expand.
|
|
209
|
+
|
|
210
|
+
**Unit tests:**
|
|
211
|
+
|
|
212
|
+
- 7.1 Template file exists at `lib/templates/instruction-block-onprem.template.md` and contains the four AC #1 content categories (one test per category: `/no_think` directive present; `~/.claude/` no-home-writes rule present; `str_replace_editor` no-reference rule present; reasoning-mode per-phase guidance present).
|
|
213
|
+
- 7.2 Template contains NO placeholders (`{{`) — AC #3 defensive.
|
|
214
|
+
- 7.3 NFR44 — standard profile: `composeInstructionBlock({ profile: 'standard', manifestPath })` output does NOT contain `/no_think`, `str_replace_editor`, or `~/.claude/`. Assert each of the three strings is absent via direct `.includes()` check.
|
|
215
|
+
- 7.4 On-prem profile: `composeInstructionBlock({ profile: 'on-prem', manifestPath })` output contains all three strings (`/no_think`, `str_replace_editor`, `~/.claude/`).
|
|
216
|
+
- 7.5 Composer output structure: on-prem output equals universal output + exactly one blank line + on-prem template content. Verified by string parsing, not regex.
|
|
217
|
+
- 7.6 Idempotency: calling `composeInstructionBlock({ profile: 'on-prem', manifestPath: X })` twice returns byte-identical strings.
|
|
218
|
+
|
|
219
|
+
**Integration tests (within this story — cross-tool end-to-end lives in Story 21.9):**
|
|
220
|
+
|
|
221
|
+
- 7.7 Fresh standard-profile install with all injection-surface agents selected (Claude Code, Cline, Roo Code, OpenCode, Cursor, Kilocode, Copilot, Gemini): for EVERY generated file in the tmp project, assert the three NFR44 forbidden strings (`/no_think`, `str_replace_editor`, `~/.claude/`) are absent. Files checked: `.claude/CLAUDE.md`, `.cline/clinerules.md`, `.clinerules`, `.roo/rules/00-ma-agents.md`, `.roomodes` (all four modes' `customInstructions`), the OpenCode `opencode.json::instructions[]` ma-agents-prefixed entry, `.cursor/cursor.md`, `.kilocode/kilocode.md`, `.github/copilot/copilot.md`, `.gemini/gemini.md`. Note: AGENTS.md's legitimate single `~/.claude/` occurrence (Story 21.4 AC #9 exception) is excluded from this assertion — tested separately in 7.12.
|
|
222
|
+
- 7.8 Fresh on-prem-profile install with same agent set: for EVERY file above, assert all three strings ARE present (NFR44 positive case). Specifically: `/no_think` appears in each of the four `.roomodes` `customInstructions` blocks (AC #6).
|
|
223
|
+
- 7.9 Two consecutive on-prem installs produce byte-identical content across all files above (AC #10 — NFR46).
|
|
224
|
+
- 7.10 NFR18 non-regression: on-prem install's `opencode.json` has `instructions[]` length equal to `N + M` where N is the user's pre-existing entries and M is the ma-agents-contributed entries (1 ma-agents-prefixed + 1 `"AGENTS.md"` if OpenCode is selected). Key count at the top level of `opencode.json` is unchanged versus a standard install.
|
|
225
|
+
- 7.11 NFR47 non-regression: after an on-prem install, the four `.roomodes` modes' `fileRegex` patterns are byte-identical to the same modes' patterns in a standard install (content diff is only in `customInstructions`). AC #12 contract.
|
|
226
|
+
- 7.12 Shape A verification (AC #7 resolution): after an on-prem install, AGENTS.md contains `/no_think` somewhere inside the marker region. Exact positional assertion (Universal Rules vs Critical Behavior Rules section) is NOT tested — Shape A delivers content via the Universal Rules section and that location is not part of the contract.
|
|
227
|
+
|
|
228
|
+
**Test data isolation:** All tests create a tmp project with a stub `.ma-agents.json` written via `setProfile(tmpRoot, 'standard' | 'on-prem')` (Story 21.1 API). Use real `lib/templates/` on disk — tests must NOT stub the template files so that AC #1's template-authorship contract is exercised end-to-end.
|
|
229
|
+
|
|
230
|
+
**Coverage note:** Story 21.9 adds (a) a standard-profile vs. pre-Epic-21 baseline byte-comparison test (covers NFR44 across the whole artifact, not just the three literal strings), (b) on-prem profile must-have-both-layers integration across every tool at once, (c) profile-flip drift detection via Story 21.10. Do NOT duplicate those here — keep this story's tests scoped to the on-prem layer's content presence/absence and the composer's on-prem branch wiring.
|
|
231
|
+
|
|
232
|
+
## Out of Scope
|
|
233
|
+
|
|
234
|
+
(See Dev Notes → Out of Scope above — summarized for quick reference.)
|
|
235
|
+
|
|
236
|
+
- Universal block content (Story 21.2).
|
|
237
|
+
- `.roomodes` template authorship and the `yaml-customModes` merger (Story 21.3).
|
|
238
|
+
- `AGENTS.md` template authorship (Story 21.4).
|
|
239
|
+
- `.clinerules` template authorship (Story 21.5).
|
|
240
|
+
- BMAD persona phase prefix (Story 21.7).
|
|
241
|
+
- vLLM deployment documentation and README on-prem section (Story 21.8).
|
|
242
|
+
- Cross-tool NFR44/NFR46 integration tests (Story 21.9).
|
|
243
|
+
- Profile-reconfigure flow (Story 21.10).
|
|
244
|
+
- Profile-uninstall flow (Story 21.11).
|
|
93
245
|
|
|
94
246
|
## Dev Agent Record
|
|
95
247
|
|
|
96
248
|
### Agent Model Used
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
### Debug Log References
|
|
100
|
-
_(to be filled)_
|
|
249
|
+
Claude Opus 4.6 (1M context) — bmad-dev-story + adversarial review.
|
|
101
250
|
|
|
102
251
|
### Completion Notes List
|
|
103
|
-
|
|
252
|
+
- Shipped `lib/templates/instruction-block-onprem.template.md` (90 lines) containing the four AC #1 content categories: `/no_think` planning-phase directive, no-home-dir-writes rule referencing `~/.claude/`, no-`str_replace_editor` rule, per-phase reasoning+sampling guidance (planning/implementation/review).
|
|
253
|
+
- Template has no `{{...}}` placeholders (AC #3), ends with exactly one trailing newline, has no trailing whitespace on any line (AC #1.4 idempotency precondition).
|
|
254
|
+
- Composer (Story 21.2, `lib/installer.js`) already implemented the on-prem append branch. Verified by Task 2 inspection — no 21.2 blocker raised. The on-prem template now flows transparently through every injection surface: markdown-marker merger (Claude Code, Cline ×2, Roo Code rules, Cursor, Kilocode, Copilot, Gemini, Antigravity), json-merge merger (OpenCode `opencode.json::instructions[]`), yaml-customModes merger (`.roomodes`), and markdown-markers merger (AGENTS.md).
|
|
255
|
+
- Fresh on-prem install confirmed to render all three NFR44 literals (`/no_think`, `str_replace_editor`, `~/.claude/`) into every stamped injection surface (test 7.8 passes across 11+ files).
|
|
256
|
+
- Fresh standard-profile install confirmed to render NONE of the three literals into any injection surface except AGENTS.md's legitimate Critical Behavior Rules `~/.claude/` occurrence (AC #4 exception, verified by excluding that surface from the universal-rule assertion).
|
|
257
|
+
- NFR46 idempotency: two consecutive on-prem installs produce byte-identical marker-block content across `.claude/CLAUDE.md`, `.cline/clinerules.md`, `.clinerules`, `.roo/rules/00-ma-agents.md`, `AGENTS.md`, and byte-identical `.roomodes` YAML (test 7.9).
|
|
258
|
+
- NFR18 non-regression: on-prem install's `opencode.json` has exactly one `[ma-agents]`-prefixed entry, user entries preserved, `otherKey` untouched, `instructions[]` length stable across re-installs (test 7.10).
|
|
259
|
+
- NFR47 non-regression: `.roomodes` `fileRegex` patterns byte-identical between standard and on-prem profiles (test 7.11).
|
|
260
|
+
- Obsolete precondition fixed: `test/clinerules.test.js` test 5.7a was written assuming the on-prem template is absent (pre-21.6 state). Updated to temporarily rename the template to simulate absence (mirrors `test/instruction-block.test.js` test 5.6 pattern).
|
|
261
|
+
|
|
262
|
+
### Adversarial Review Findings
|
|
263
|
+
|
|
264
|
+
| # | Layer | Severity | Finding | Disposition |
|
|
265
|
+
|---|-------|----------|---------|-------------|
|
|
266
|
+
| 1 | Cynical | P1 | Tests 7.7/7.8 skip missing surfaces via `continue` — a regression dropping every rendered surface would pass vacuously | **Fixed** — added sanity guards asserting `.claude/CLAUDE.md` (both), `.roomodes` + `AGENTS.md` (on-prem only) exist before the content loop |
|
|
267
|
+
| 2 | Cynical | P1 | `/no_think` substring match would also match e.g. `//no_think` in comments | **Accepted** — template writes the token on its own line; no other ma-agents-owned template contains any `//no_think`-adjacent literal |
|
|
268
|
+
| 3 | Edge-case | P1 | NFR47 regex parse `/fileRegex:\s*([^\n]+)/g` would double-count if a future on-prem rule mentioned "fileRegex" | **Accepted** — current on-prem template contains no "fileRegex" literal; future rules added via follow-up stories can extend the parse |
|
|
269
|
+
| 4 | Edge-case | P1 | Test 7.9 re-runs `installAllInjectionSurfaces` — second pass risks drift-detection false-positives | **Accepted** — `composeInstructionBlock` is deterministic (verified by 21.2 test 5.4 + this story's 7.6); drift path not entered on clean re-install (21.2 test 5.10a confirms) |
|
|
270
|
+
| 5 | Cynical | P2 | Template uses backtick inline code spans — markdown renderers in agent UIs may strip them | **Accepted** — cosmetic; the three literals are also written in plain prose outside code spans, and tests assert literal substring presence (renderer-independent) |
|
|
271
|
+
| 6 | Edge-case | P2 | `.roomodes` drift via `reconfigure` flow (profile flip) could surface 21.2 AC #10 warning on standard→on-prem | **Out of scope per spec** — AC #13 explicitly defers profile-flip to Story 21.10 |
|
|
272
|
+
|
|
273
|
+
All P0/P1 findings resolved or accepted with documented rationale.
|
|
104
274
|
|
|
105
275
|
### File List
|
|
106
|
-
|
|
276
|
+
- CREATED: `lib/templates/instruction-block-onprem.template.md`
|
|
277
|
+
- CREATED: `test/onprem-layer.test.js` (12 tests, all passing)
|
|
278
|
+
- MODIFIED: `test/clinerules.test.js` (test 5.7a updated — simulate on-prem template absence via rename)
|
|
279
|
+
- MODIFIED: `package.json` (added `test/onprem-layer.test.js` to npm test script)
|
|
280
|
+
- MODIFIED: `_bmad-output/implementation-artifacts/21-6-onprem-layered-guardrails.md` (Status → Review; Dev Agent Record / File List / Change Log updated)
|
|
107
281
|
|
|
108
282
|
## Change Log
|
|
109
|
-
|
|
110
|
-
- 2026-04-
|
|
111
|
-
- 2026-04-
|
|
112
|
-
- 2026-04-
|
|
283
|
+
|
|
284
|
+
- 2026-04-15: Story 21.6 implemented — on-prem template shipped, all 12 story-local tests green (onprem-layer.test.js), full `npm test` green (no regressions). Composer wiring from 21.2 reused unchanged — no blocker raised. Status Ready → Review.
|
|
285
|
+
- 2026-04-15 (epic-owner resolution): All three open questions resolved inline. **AC #7 Shape A committed** — on-prem content reaches AGENTS.md via the Universal Rules section transparently through the composer; Critical Behavior Rules section unchanged by this story. Shape B rejected (would contradict Decision A). **AC #1 `/no_think` Shape X committed** — 21.6 owns the instruction-text rule; 21.7 owns the active persona-prompt prefix. **AC #1 field-playbook** — scoped to four categories only; additional rules become follow-up stories. Task 2.2 and Task 4 made unconditional. Status flipped Draft → Ready. No upstream blockers remain.
|
|
286
|
+
- 2026-04-15: Adversarial-review reconciliation. AC #3 reconciled with Story 21.4 via pointer sentence: composer (21.2) appends template verbatim; project-value stamping happens AFTER composition in `lib/installer.js` (closes P0 #2). AC #4 annotated with NFR44 scope-narrowing pointer. Added Dev Notes "Ownership boundary" block clarifying composer/merger/stamper terminology and that 21.6 owns only the on-prem template file and its profile-gated content. Added Dev Notes "NFR44 test scope" block (decision D): three literals exhaustive for negative assertion; reasoning-mode prose positive-only. Task 2.2 downgraded from "extend composer here" to "raise blocker against 21.2"; Task 4.2 downgraded analogously for 21.4. Status flipped to Draft: Task 2.2 and Task 4.2 remain conditional on upstream decisions, and three Open questions remain unresolved. Upstream composer dependency cites Story 21.2.
|
|
287
|
+
- 2026-04-15: Story created (Epic 21, Story 21.6). ACs structured with explicit (gap-fill) flags per the epic's AC scope. Three Open questions raised inline (AGENTS.md anchor Shape A vs B, field-playbook additional rules, `/no_think` delivery Shape X vs Y) rather than guessed. NFR44/NFR46/NFR47/NFR18 citations made explicit in Dev Notes. Verified source-tree surface table added distinguishing existing vs. new paths. Upstream/downstream dependencies split. Testing section covers unit (7.1–7.6) and integration (7.7–7.12) with explicit NFR44 negative-assertion coverage against `/no_think`, `str_replace_editor`, `~/.claude/` across every injection surface. Status set to Ready.
|