vibe-coding-master 0.7.27 → 0.7.29
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/README.md +31 -10
- package/dist/backend/api/harness-routes.js +0 -1
- package/dist/backend/cli/install-vcm-harness.js +37 -7
- package/dist/backend/server.js +9 -6
- package/dist/backend/services/architect-restart-service.js +51 -3
- package/dist/backend/services/auto-memory-service.js +118 -61
- package/dist/backend/services/claude-hook-service.js +12 -2
- package/dist/backend/services/gate-review-service.js +35 -18
- package/dist/backend/services/harness-feedback-service.js +138 -7
- package/dist/backend/services/memory-proposal-validation.js +80 -0
- package/dist/backend/services/memory-review-paths.js +7 -0
- package/dist/backend/services/memory-review-validation.js +94 -0
- package/dist/backend/templates/handoff.js +20 -16
- package/dist/backend/templates/harness/architect-agent.js +3 -2
- package/dist/backend/templates/harness/claude-root.js +1 -1
- package/dist/backend/templates/harness/coder-agent.js +12 -1
- package/dist/backend/templates/harness/coder-worker-agent.js +12 -1
- package/dist/backend/templates/harness/harness-engineer-agent.js +53 -12
- package/dist/backend/templates/harness/restart-architect-skill.js +12 -2
- package/dist/backend/templates/harness/role-memory.js +2 -2
- package/dist/backend/templates/harness/tester-agent.js +3 -3
- package/dist/backend/templates/harness/vcm-architecture-interview-skill.js +2 -2
- package/dist/backend/templates/harness/vcm-final-acceptance-skill.js +1 -1
- package/dist/backend/templates/harness/vcm-propose-memory-skill.js +55 -7
- package/dist/backend/templates/harness/vcm-route-message-skill.js +2 -5
- package/dist/shared/validation/artifact-check.js +78 -64
- package/dist/shared/validation/artifact-contract.js +22 -0
- package/package.json +1 -1
- package/scripts/uninstall-vcm-harness.mjs +5 -0
|
@@ -14,8 +14,19 @@ You are \`vcm-coder-worker\`, a bounded implementation worker invoked by Coder.
|
|
|
14
14
|
### Worker Runtime State
|
|
15
15
|
|
|
16
16
|
- Worker runtime status is only \`running\` or \`completed\`.
|
|
17
|
-
- Coder creates the assigned worker state with
|
|
17
|
+
- Coder creates the assigned worker state with this exact initial shape:
|
|
18
|
+
|
|
19
|
+
\`\`\`json
|
|
20
|
+
{
|
|
21
|
+
"workerId": "<worker-id>",
|
|
22
|
+
"status": "running",
|
|
23
|
+
"reportPath": ".ai/vcm/coder-workers/reports/<worker-id>.md",
|
|
24
|
+
"handled": false
|
|
25
|
+
}
|
|
26
|
+
\`\`\`
|
|
27
|
+
|
|
18
28
|
- After the sweep of assigned items and their assigned checks, commit the assigned files. After the commit succeeds, write the assigned report with the commit hash and \`Implementation Result: success|has_failed_items\`, then update only the assigned worker state to \`completed\` with the same \`commitHash\` as the final step.
|
|
29
|
+
- The completed state must retain \`workerId\`, \`reportPath\`, and \`handled: false\`, set \`status\` to \`completed\`, and add \`"commitHash": "<exact-report-commit-hash>"\`.
|
|
19
30
|
- Use \`completed\` only after every assigned item reached a terminal state. A successful item has green assigned proof and its marker removed. A failed item has a genuine attempt committed with objective failure evidence and its marker retained. Use \`success\` only when every item succeeded; otherwise use \`has_failed_items\`.
|
|
20
31
|
- If execution is interrupted before the sweep, commit, or report completes, leave the worker state as \`running\`. Coder must resume the worker or take over the remaining work.
|
|
21
32
|
- Do not set \`handled: true\`; only Coder may do that after reviewing and integrating the worker result.
|
|
@@ -38,20 +38,23 @@ You are not part of the task workflow round state.
|
|
|
38
38
|
permitted bootstrap edits directly in the active task worktree and commit them
|
|
39
39
|
yourself.
|
|
40
40
|
- Retrospective Mode: analyze a completed task for reusable harness problems.
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
phase of Task Harness Review, review role proposals and write only the review
|
|
44
|
-
output files assigned by VCM.
|
|
41
|
+
When the assigned prompt includes Auto Memory Review, also review the memory
|
|
42
|
+
proposals and write only the reviewed-memory output files assigned by VCM.
|
|
45
43
|
- VCM Feedback Mode: draft VCM product, installer, UI, or fixed-template issue
|
|
46
44
|
feedback. Do not submit without explicit in-session user authorization.
|
|
47
45
|
|
|
48
46
|
## Change Policy
|
|
49
47
|
|
|
50
|
-
- Apply edits only in Bootstrap Apply Mode,
|
|
51
|
-
explicitly asks you to apply an
|
|
48
|
+
- Apply edits only in Bootstrap Apply Mode, to assigned reviewed-memory output
|
|
49
|
+
files during Retrospective Mode, or when VCM explicitly asks you to apply an
|
|
50
|
+
approved harness change.
|
|
52
51
|
- When applying edits, work only in the active task worktree named by VCM. Do not
|
|
53
52
|
edit the base repository root unless VCM explicitly says so.
|
|
54
|
-
- In Proposal Mode
|
|
53
|
+
- In Proposal Mode, do not edit files.
|
|
54
|
+
- In Retrospective Mode, write the assigned retrospective report and, only when
|
|
55
|
+
Auto Memory Review is included in the prompt, the assigned reviewed-memory
|
|
56
|
+
output files. After every assigned pending feedback has a recorded
|
|
57
|
+
disposition, delete those processed feedback files.
|
|
55
58
|
- Commit every applied harness change yourself before ending your turn.
|
|
56
59
|
- Do not overwrite VCM fixed managed blocks.
|
|
57
60
|
- Keep project-specific customization outside VCM managed blocks.
|
|
@@ -64,11 +67,26 @@ You are not part of the task workflow round state.
|
|
|
64
67
|
## Memory Management
|
|
65
68
|
|
|
66
69
|
- Own VCM-managed project memory in the root and role \`<VCM-memory>\` blocks.
|
|
67
|
-
- When Auto Memory is disabled, do not request proposals
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
70
|
+
- When Auto Memory is disabled, do not request proposals or update memory.
|
|
71
|
+
- During a Retrospective that includes Auto Memory Review, first inspect every
|
|
72
|
+
entry in every current memory snapshot. Verify each entry against current
|
|
73
|
+
code, durable documentation, and final task evidence. For every substantive
|
|
74
|
+
entry, decide whether to retain, update, remove, or move it to a durable
|
|
75
|
+
document; record the decision reason, the impact of removing it, and whether
|
|
76
|
+
memory or a durable document is the correct source. Complete this full review
|
|
77
|
+
even when every proposal says \`no-change\`.
|
|
78
|
+
- After reviewing existing memory, verify every role proposal against task
|
|
79
|
+
evidence, including any Architect planning-session candidate assigned by VCM.
|
|
80
|
+
Independently verify the stated need, absence impact, and durable-document
|
|
81
|
+
disposition. Treat every candidate as a proposal rather than authority, merge
|
|
82
|
+
duplicates, remove stale entries, and keep role-specific knowledge in the
|
|
83
|
+
matching role memory output.
|
|
84
|
+
- Do not keep the full content in memory when a durable document is the correct
|
|
85
|
+
source. Use a short memory reference only when the role needs that document
|
|
86
|
+
pointer across tasks.
|
|
87
|
+
- Record every proposal disposition and the retained, updated, and removed
|
|
88
|
+
existing-memory decisions and summary in the exact Memory Review report block
|
|
89
|
+
assigned by VCM.
|
|
72
90
|
- Do not record task narrative, temporary state, unverified conclusions, or
|
|
73
91
|
Harness rules in memory.
|
|
74
92
|
- Edit only the review output paths assigned by VCM. Do not edit active
|
|
@@ -91,6 +109,28 @@ context, validation reports, Gate Review reports, final acceptance artifacts,
|
|
|
91
109
|
memory drafts, applied memory diffs, current memory, and user corrections during
|
|
92
110
|
the task.
|
|
93
111
|
|
|
112
|
+
Pending Harness Feedback is part of the retrospective, not a separate phase.
|
|
113
|
+
At the start of the retrospective, read every feedback file assigned by VCM
|
|
114
|
+
from \`.ai/vcm/harness-feedback/pending/\`.
|
|
115
|
+
|
|
116
|
+
For each pending feedback:
|
|
117
|
+
|
|
118
|
+
- verify it against the current harness, task evidence, and project behavior
|
|
119
|
+
- decide whether it is confirmed, rejected, duplicate, or already covered
|
|
120
|
+
- record the feedback path, decision, evidence, impact, and required action in
|
|
121
|
+
the retrospective report
|
|
122
|
+
|
|
123
|
+
Process every assigned feedback before completing the retrospective. A
|
|
124
|
+
feedback item is processed even when it is rejected or already covered.
|
|
125
|
+
|
|
126
|
+
Write the complete retrospective report before deleting any feedback file.
|
|
127
|
+
After the report contains a disposition for every assigned feedback, delete
|
|
128
|
+
those feedback files from \`.ai/vcm/harness-feedback/pending/\`.
|
|
129
|
+
|
|
130
|
+
Do not delete a feedback file unless its disposition is already recorded in
|
|
131
|
+
the retrospective report. Do not leave an assigned feedback file pending after
|
|
132
|
+
its disposition has been recorded.
|
|
133
|
+
|
|
94
134
|
For each finding, decide whether it is:
|
|
95
135
|
|
|
96
136
|
- a reusable harness problem that should be fixed
|
|
@@ -108,6 +148,7 @@ Do not edit harness files during retrospective analysis. Write a concise analysi
|
|
|
108
148
|
- impact
|
|
109
149
|
- recommended harness change, or reason no harness change is needed
|
|
110
150
|
- affected roles, skills, tools, or docs
|
|
151
|
+
- pending feedback path and disposition
|
|
111
152
|
|
|
112
153
|
## VCM Feedback
|
|
113
154
|
|
|
@@ -9,7 +9,12 @@ Run:
|
|
|
9
9
|
.ai/tools/request-architect-restart
|
|
10
10
|
\`\`\`
|
|
11
11
|
|
|
12
|
-
If VCM reports \`scheduled
|
|
12
|
+
If VCM reports \`scheduled\` with a non-empty \`memoryCandidatePath\`, use
|
|
13
|
+
\`vcm-propose-memory\` to write a planning-session memory candidate to that exact
|
|
14
|
+
path before writing the completed route. This candidate is provisional input for
|
|
15
|
+
the later Auto Memory review; it does not edit active memory.
|
|
16
|
+
|
|
17
|
+
Then write the completed Architect-to-PM route message and end the turn. VCM keeps the current Architect session through any architecture-plan Gate revision rounds and restarts it only after the route is accepted by PM and that Gate is approved or explicitly excepted.
|
|
13
18
|
|
|
14
19
|
Do not use this skill for incomplete planning, user clarification, Debug Mode, Architecture Diagnosis Mode, or docs sync.`;
|
|
15
20
|
}
|
|
@@ -54,7 +59,12 @@ def main():
|
|
|
54
59
|
try:
|
|
55
60
|
with urllib.request.urlopen(request, timeout=5) as response:
|
|
56
61
|
payload = json.loads(response.read().decode("utf-8"))
|
|
57
|
-
emit(
|
|
62
|
+
emit(
|
|
63
|
+
payload.get("status", "scheduled"),
|
|
64
|
+
taskSlug=task_slug,
|
|
65
|
+
sessionId=payload.get("sessionId"),
|
|
66
|
+
memoryCandidatePath=payload.get("memoryCandidatePath"),
|
|
67
|
+
)
|
|
58
68
|
return 0
|
|
59
69
|
except urllib.error.HTTPError as error:
|
|
60
70
|
try:
|
|
@@ -5,8 +5,8 @@ role turns. Update reviewed memory only through the output paths assigned by VCM
|
|
|
5
5
|
or explicit user edits in Harness Studio. When Auto Memory is disabled, do not
|
|
6
6
|
initiate memory proposals, reviews, or updates.`
|
|
7
7
|
: `Treat the \`<VCM-memory>\` block in this role definition as read-only. Only
|
|
8
|
-
when VCM explicitly
|
|
9
|
-
\`vcm-propose-memory\` and write
|
|
8
|
+
when VCM explicitly assigns a memory proposal or candidate path, use
|
|
9
|
+
\`vcm-propose-memory\` and write that exact path.`;
|
|
10
10
|
return `### Role Memory
|
|
11
11
|
|
|
12
12
|
The \`<VCM-memory>\` block in this role definition is accumulated project context,
|
|
@@ -155,11 +155,11 @@ L3 Required: yes|no
|
|
|
155
155
|
- Use \`pass\` only when required validation completed and no blocking test failure, missing required coverage, unacceptable test weakness, or unresolved validation risk remains.
|
|
156
156
|
- Use \`fail\` only when tests fail, coverage is insufficient and Tester continuation cannot resolve it, required validation is blocked from completion, test quality is unacceptable, or validation risk needs project-manager routing.
|
|
157
157
|
- Use \`incomplete\` only when required validation remains, no blocking issue has been found, and another Tester turn can continue the recorded remaining work.
|
|
158
|
-
- When \`Test Result: pass\`, \`Failed Expectations\`, \`Coverage Gaps\`, \`Blocking Validation Issues\`, and \`User Approval Evidence\` must be \`None
|
|
159
|
-
- When \`Test Result: incomplete\`, \`Completed Validation\` and \`Remaining Validation\` must both contain concrete progress, while \`Failed Expectations\`, \`Coverage Gaps\`, \`Blocking Validation Issues\`, and \`User Approval Evidence\` must be \`None
|
|
158
|
+
- When \`Test Result: pass\`, the entire body of \`Remaining Validation\`, \`Failed Expectations\`, \`Coverage Gaps\`, \`Blocking Validation Issues\`, and \`User Approval Evidence\` must be exactly \`None.\` with no additional text.
|
|
159
|
+
- When \`Test Result: incomplete\`, \`Completed Validation\` and \`Remaining Validation\` must both contain concrete progress, while the entire body of \`Failed Expectations\`, \`Coverage Gaps\`, \`Blocking Validation Issues\`, and \`User Approval Evidence\` must be exactly \`None.\` with no additional text.
|
|
160
160
|
- When \`Test Result: fail\`, \`Blocking Validation Issues\` must list concrete blocking evidence.
|
|
161
161
|
- When \`Coverage Gaps\` is not \`None\`, \`Test Result\` must be \`fail\`, \`User Approval Evidence\` must contain the user's exact authorization, and every recorded gap must match that authorization.
|
|
162
|
-
- When no gap has been approved, \`User Approval Evidence\` must be \`None
|
|
162
|
+
- When no gap has been approved, the entire \`User Approval Evidence\` section must be exactly \`None.\` with no additional text.
|
|
163
163
|
- For feature or cross-boundary changes, map required L2 integration coverage and mandatory L3 coverage separately. If required coverage is unavailable, report it as a blocking issue.
|
|
164
164
|
- For changed or newly added tests, state why the assertions prove real behavior rather than fixture-specific, implementation-specific, or mock-only behavior.
|
|
165
165
|
- Report confirmed unresolved issues that should survive current-task cleanup in \`.ai/vcm/handoffs/test-report.md\`; do not write \`.ai/vcm/handoffs/known-issues.md\` (architect-owned).
|
|
@@ -65,14 +65,14 @@ Architecture Brief Status: interviewing|confirmed
|
|
|
65
65
|
|
|
66
66
|
## Unresolved User Decisions
|
|
67
67
|
|
|
68
|
-
|
|
68
|
+
None.
|
|
69
69
|
|
|
70
70
|
## User Confirmation
|
|
71
71
|
|
|
72
72
|
...
|
|
73
73
|
\`\`\`
|
|
74
74
|
|
|
75
|
-
Record concise confirmed requirements and constraints. Tag each entry under Confirmed User Decisions with its provenance and depth — [user-stated | architect-proposed, user-approved | architect-inferred] and [intent-level | mechanism-level] — and record the user's real input faithfully as a short summary; never present an architect inference as a user requirement. Record correctness-critical mechanism choices surfaced during the interview with their options, your recommendation, the user's decision, and the rejected alternative, so later stages can tell a chosen mechanism from an inferred one. Keep this to decisions and their provenance — not a full implementation design, and not a transcript.
|
|
75
|
+
Record concise confirmed requirements and constraints. Tag each entry under Confirmed User Decisions with its provenance and depth — [user-stated | architect-proposed, user-approved | architect-inferred] and [intent-level | mechanism-level] — and record the user's real input faithfully as a short summary; never present an architect inference as a user requirement. Record correctness-critical mechanism choices surfaced during the interview with their options, your recommendation, the user's decision, and the rejected alternative, so later stages can tell a chosen mechanism from an inferred one. Keep this to decisions and their provenance — not a full implementation design, and not a transcript. When no user-owned decision remains, the entire Unresolved User Decisions section must be exactly \`None.\` with no additional text.
|
|
76
76
|
|
|
77
77
|
Maintain the evidence artifact with this structure:
|
|
78
78
|
|
|
@@ -94,7 +94,7 @@ Use this structure:
|
|
|
94
94
|
|
|
95
95
|
## Decision
|
|
96
96
|
|
|
97
|
-
accepted
|
|
97
|
+
accepted|accepted-with-known-risks|needs-coder-follow-up|needs-architect-follow-up|needs-docs-sync|blocked-by-user-decision
|
|
98
98
|
|
|
99
99
|
## Evidence Reviewed
|
|
100
100
|
|
|
@@ -1,33 +1,81 @@
|
|
|
1
1
|
export function renderVcmProposeMemorySkillRules() {
|
|
2
|
-
return `Use this skill only when VCM explicitly requests a memory proposal
|
|
3
|
-
|
|
2
|
+
return `Use this skill only when VCM explicitly requests a memory proposal or
|
|
3
|
+
planning-session memory candidate and provides an exact path.
|
|
4
4
|
|
|
5
5
|
## Rules
|
|
6
6
|
|
|
7
7
|
- Treat every \`<VCM-memory>\` block as read-only. This skill creates a proposal;
|
|
8
8
|
it never edits active memory.
|
|
9
|
-
- Write only to the exact
|
|
10
|
-
\`.ai/vcm/memory-review/runs/<run-id>/drafts/\`
|
|
11
|
-
-
|
|
9
|
+
- Write only to the exact path assigned by VCM. It must be either a role draft
|
|
10
|
+
under \`.ai/vcm/memory-review/runs/<run-id>/drafts/\` or a planning candidate
|
|
11
|
+
under \`.ai/vcm/memory-review/candidates/\` in the active task worktree.
|
|
12
|
+
- If VCM did not provide a path, do not create a proposal.
|
|
12
13
|
- Propose only verified, durable, reusable project knowledge supported by task
|
|
13
14
|
evidence.
|
|
15
|
+
- Target shared project knowledge to \`shared\`. Target knowledge used only by
|
|
16
|
+
the current role to \`current-role\`.
|
|
17
|
+
- For every add or update, explain why the memory is necessary, what future
|
|
18
|
+
impact its absence would have, and whether the knowledge belongs in memory,
|
|
19
|
+
a durable document, or a short memory reference to a durable document.
|
|
20
|
+
Use exactly \`memory\`, \`durable-doc\`, or \`memory-reference\`. Use
|
|
21
|
+
\`Durable doc path: none\` with \`memory\` and an actual path with either
|
|
22
|
+
durable-document disposition.
|
|
14
23
|
- Do not record task narrative, temporary state, unverified conclusions, or
|
|
15
24
|
Harness rules.
|
|
16
25
|
- Do not edit handoff artifacts or route messages from this skill.
|
|
17
26
|
|
|
18
27
|
## Draft Format
|
|
19
28
|
|
|
29
|
+
Use this exact format for \`Decision: no-change\`:
|
|
30
|
+
|
|
20
31
|
\`\`\`markdown
|
|
21
32
|
# Memory Proposal
|
|
22
|
-
Decision:
|
|
33
|
+
Decision: no-change
|
|
23
34
|
|
|
24
35
|
## Add
|
|
36
|
+
none
|
|
25
37
|
|
|
26
38
|
## Update
|
|
39
|
+
none
|
|
27
40
|
|
|
28
41
|
## Remove
|
|
42
|
+
none
|
|
43
|
+
\`\`\`
|
|
44
|
+
|
|
45
|
+
For \`Decision: update\`, use one or more numbered items and write every field
|
|
46
|
+
on one line. Use \`none\` as the complete body of an operation section that has
|
|
47
|
+
no item:
|
|
48
|
+
|
|
49
|
+
\`\`\`markdown
|
|
50
|
+
# Memory Proposal
|
|
51
|
+
Decision: update
|
|
29
52
|
|
|
30
|
-
##
|
|
53
|
+
## Add
|
|
54
|
+
### Item 1
|
|
55
|
+
Target: shared
|
|
56
|
+
Content: <new memory entry>
|
|
57
|
+
Reason: <why this must remain available across tasks>
|
|
58
|
+
Impact if absent: <specific future role or task failure>
|
|
59
|
+
Durable doc disposition: memory
|
|
60
|
+
Durable doc path: none
|
|
61
|
+
Evidence: <task artifact, code, or durable documentation>
|
|
62
|
+
|
|
63
|
+
## Update
|
|
64
|
+
### Item 1
|
|
65
|
+
Target: current-role
|
|
66
|
+
Existing: <exact existing memory entry>
|
|
67
|
+
Content: <replacement memory entry>
|
|
68
|
+
Reason: <why the replacement must remain available across tasks>
|
|
69
|
+
Impact if absent: <specific future role or task failure>
|
|
70
|
+
Durable doc disposition: memory-reference
|
|
71
|
+
Durable doc path: docs/ARCHITECTURE.md
|
|
72
|
+
Evidence: <task artifact, code, or durable documentation>
|
|
73
|
+
|
|
74
|
+
## Remove
|
|
75
|
+
### Item 1
|
|
76
|
+
Target: current-role
|
|
77
|
+
Existing: <exact existing memory entry>
|
|
78
|
+
Evidence: <task artifact, code, or durable documentation>
|
|
31
79
|
\`\`\`
|
|
32
80
|
|
|
33
81
|
Use \`Decision: no-change\` when the completed task produced no qualifying
|
|
@@ -56,9 +56,7 @@ type: task
|
|
|
56
56
|
workflow_flow: code-change
|
|
57
57
|
workflow_step: coder-implementation
|
|
58
58
|
workflow_status: active
|
|
59
|
-
artifact_refs:
|
|
60
|
-
- .ai/vcm/handoffs/architecture-plan.md
|
|
61
|
-
- docs/plans/example.md
|
|
59
|
+
artifact_refs: .ai/vcm/handoffs/architecture-plan.md, docs/plans/example.md
|
|
62
60
|
---
|
|
63
61
|
|
|
64
62
|
Summary:
|
|
@@ -84,8 +82,7 @@ For non-PM reports, use:
|
|
|
84
82
|
\`\`\`md
|
|
85
83
|
---
|
|
86
84
|
type: result
|
|
87
|
-
artifact_refs:
|
|
88
|
-
- .ai/vcm/handoffs/example.md
|
|
85
|
+
artifact_refs: .ai/vcm/handoffs/example.md
|
|
89
86
|
---
|
|
90
87
|
|
|
91
88
|
Summary:
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ARCHITECTURE_BRIEF_STATUSES, ARCHITECTURE_PLAN_RESULTS, DOCS_SYNC_DECISIONS, FINAL_ACCEPTANCE_DECISIONS, L3_ACTIONS, L3_REQUIRED_VALUES, STRICT_NONE_VALUE, TEST_RESULTS } from "./artifact-contract.js";
|
|
1
2
|
const REQUIRED_HEADINGS = {
|
|
2
3
|
"architecture-brief": [
|
|
3
4
|
"Accepted Outcome",
|
|
@@ -128,40 +129,37 @@ export function checkMarkdownArtifact(kind, artifactPath, content) {
|
|
|
128
129
|
}
|
|
129
130
|
function validateArtifactFields(kind, content) {
|
|
130
131
|
if (kind === "architecture-plan") {
|
|
131
|
-
const result =
|
|
132
|
-
|
|
133
|
-
return ["Planning Result is required and must be complete."];
|
|
134
|
-
}
|
|
135
|
-
return result === "complete"
|
|
132
|
+
const result = readInlineField(content, "Planning Result");
|
|
133
|
+
return result === ARCHITECTURE_PLAN_RESULTS[0]
|
|
136
134
|
? []
|
|
137
|
-
: [
|
|
135
|
+
: [renderExactFieldError("Planning Result", [ARCHITECTURE_PLAN_RESULTS[0]], result)];
|
|
138
136
|
}
|
|
139
137
|
if (kind === "architecture-brief") {
|
|
140
|
-
const status =
|
|
141
|
-
const invalidFields = status
|
|
138
|
+
const status = readInlineField(content, "Architecture Brief Status");
|
|
139
|
+
const invalidFields = isAllowedValue(status, ARCHITECTURE_BRIEF_STATUSES)
|
|
142
140
|
? []
|
|
143
|
-
: ["Architecture Brief Status
|
|
141
|
+
: [renderExactFieldError("Architecture Brief Status", ARCHITECTURE_BRIEF_STATUSES, status)];
|
|
144
142
|
if (status === "confirmed") {
|
|
145
|
-
const unresolved =
|
|
146
|
-
if (!
|
|
147
|
-
invalidFields.push("Unresolved User Decisions
|
|
143
|
+
const unresolved = readArtifactSectionContent(content, "Unresolved User Decisions");
|
|
144
|
+
if (!isExactNone(unresolved)) {
|
|
145
|
+
invalidFields.push(renderExactSectionError("Unresolved User Decisions", STRICT_NONE_VALUE, unresolved, "when Architecture Brief Status is confirmed"));
|
|
148
146
|
}
|
|
149
147
|
}
|
|
150
148
|
return invalidFields;
|
|
151
149
|
}
|
|
152
150
|
if (kind === "test-report") {
|
|
153
|
-
const result =
|
|
154
|
-
const invalidFields = result
|
|
151
|
+
const result = readInlineField(content, "Test Result");
|
|
152
|
+
const invalidFields = isAllowedValue(result, TEST_RESULTS)
|
|
155
153
|
? []
|
|
156
|
-
: ["Test Result
|
|
157
|
-
const l3Required =
|
|
158
|
-
if (l3Required
|
|
159
|
-
invalidFields.push("L3 Required
|
|
154
|
+
: [renderExactFieldError("Test Result", TEST_RESULTS, result)];
|
|
155
|
+
const l3Required = readInlineField(content, "L3 Required");
|
|
156
|
+
if (!isAllowedValue(l3Required, L3_REQUIRED_VALUES)) {
|
|
157
|
+
invalidFields.push(renderExactFieldError("L3 Required", L3_REQUIRED_VALUES, l3Required));
|
|
160
158
|
}
|
|
161
|
-
const l3TriggerAssessment =
|
|
159
|
+
const l3TriggerAssessment = readArtifactSectionContent(content, "Trigger Assessment");
|
|
162
160
|
const l3AffectedFlows = readArtifactSectionContent(content, "Affected End-To-End Flows");
|
|
163
|
-
const l3Commands =
|
|
164
|
-
const l3NotRequiredEvidence =
|
|
161
|
+
const l3Commands = readArtifactSectionContent(content, "L3 Commands And Evidence");
|
|
162
|
+
const l3NotRequiredEvidence = readArtifactSectionContent(content, "Not-Required Evidence");
|
|
165
163
|
if (l3Required === "yes") {
|
|
166
164
|
if (!hasSubstantiveSectionValue(l3TriggerAssessment)) {
|
|
167
165
|
invalidFields.push("Trigger Assessment is required when L3 Required is yes.");
|
|
@@ -176,31 +174,31 @@ function validateArtifactFields(kind, content) {
|
|
|
176
174
|
if (l3Required === "no" && !hasSubstantiveSectionValue(l3NotRequiredEvidence)) {
|
|
177
175
|
invalidFields.push("Not-Required Evidence is required when L3 Required is no.");
|
|
178
176
|
}
|
|
179
|
-
const coverageGaps =
|
|
180
|
-
const blockingIssues =
|
|
181
|
-
const userApproval =
|
|
182
|
-
const failedExpectations =
|
|
183
|
-
const completedValidation =
|
|
184
|
-
const remainingValidation =
|
|
185
|
-
const hasCoverageGaps =
|
|
186
|
-
const hasBlockingIssues =
|
|
187
|
-
const hasUserApproval =
|
|
188
|
-
const hasFailedExpectations =
|
|
177
|
+
const coverageGaps = readArtifactSectionContent(content, "Coverage Gaps");
|
|
178
|
+
const blockingIssues = readArtifactSectionContent(content, "Blocking Validation Issues");
|
|
179
|
+
const userApproval = readArtifactSectionContent(content, "User Approval Evidence");
|
|
180
|
+
const failedExpectations = readArtifactSectionContent(content, "Failed Expectations");
|
|
181
|
+
const completedValidation = readArtifactSectionContent(content, "Completed Validation");
|
|
182
|
+
const remainingValidation = readArtifactSectionContent(content, "Remaining Validation");
|
|
183
|
+
const hasCoverageGaps = hasSubstantiveSectionValue(coverageGaps);
|
|
184
|
+
const hasBlockingIssues = hasSubstantiveSectionValue(blockingIssues);
|
|
185
|
+
const hasUserApproval = hasSubstantiveSectionValue(userApproval);
|
|
186
|
+
const hasFailedExpectations = hasSubstantiveSectionValue(failedExpectations);
|
|
189
187
|
if (result === "pass") {
|
|
190
|
-
if (!coverageGaps
|
|
191
|
-
invalidFields.push("Coverage Gaps
|
|
188
|
+
if (!isExactNone(coverageGaps)) {
|
|
189
|
+
invalidFields.push(renderExactSectionError("Coverage Gaps", STRICT_NONE_VALUE, coverageGaps, "when Test Result is pass"));
|
|
192
190
|
}
|
|
193
|
-
if (!blockingIssues
|
|
194
|
-
invalidFields.push("Blocking Validation Issues
|
|
191
|
+
if (!isExactNone(blockingIssues)) {
|
|
192
|
+
invalidFields.push(renderExactSectionError("Blocking Validation Issues", STRICT_NONE_VALUE, blockingIssues, "when Test Result is pass"));
|
|
195
193
|
}
|
|
196
|
-
if (!userApproval
|
|
197
|
-
invalidFields.push("User Approval Evidence
|
|
194
|
+
if (!isExactNone(userApproval)) {
|
|
195
|
+
invalidFields.push(renderExactSectionError("User Approval Evidence", STRICT_NONE_VALUE, userApproval, "when Test Result is pass"));
|
|
198
196
|
}
|
|
199
|
-
if (!failedExpectations
|
|
200
|
-
invalidFields.push("Failed Expectations
|
|
197
|
+
if (!isExactNone(failedExpectations)) {
|
|
198
|
+
invalidFields.push(renderExactSectionError("Failed Expectations", STRICT_NONE_VALUE, failedExpectations, "when Test Result is pass"));
|
|
201
199
|
}
|
|
202
|
-
if (
|
|
203
|
-
invalidFields.push("Remaining Validation
|
|
200
|
+
if (!isExactNone(remainingValidation)) {
|
|
201
|
+
invalidFields.push(renderExactSectionError("Remaining Validation", STRICT_NONE_VALUE, remainingValidation, "when Test Result is pass"));
|
|
204
202
|
}
|
|
205
203
|
}
|
|
206
204
|
if (result === "incomplete") {
|
|
@@ -210,17 +208,17 @@ function validateArtifactFields(kind, content) {
|
|
|
210
208
|
if (!hasSubstantiveSectionValue(remainingValidation)) {
|
|
211
209
|
invalidFields.push("Remaining Validation must list continuation work when Test Result is incomplete.");
|
|
212
210
|
}
|
|
213
|
-
if (
|
|
214
|
-
invalidFields.push("Coverage Gaps
|
|
211
|
+
if (!isExactNone(coverageGaps)) {
|
|
212
|
+
invalidFields.push(renderExactSectionError("Coverage Gaps", STRICT_NONE_VALUE, coverageGaps, "when Test Result is incomplete"));
|
|
215
213
|
}
|
|
216
|
-
if (
|
|
217
|
-
invalidFields.push("Blocking Validation Issues
|
|
214
|
+
if (!isExactNone(blockingIssues)) {
|
|
215
|
+
invalidFields.push(renderExactSectionError("Blocking Validation Issues", STRICT_NONE_VALUE, blockingIssues, "when Test Result is incomplete"));
|
|
218
216
|
}
|
|
219
|
-
if (
|
|
220
|
-
invalidFields.push("User Approval Evidence
|
|
217
|
+
if (!isExactNone(userApproval)) {
|
|
218
|
+
invalidFields.push(renderExactSectionError("User Approval Evidence", STRICT_NONE_VALUE, userApproval, "when Test Result is incomplete"));
|
|
221
219
|
}
|
|
222
|
-
if (
|
|
223
|
-
invalidFields.push("Failed Expectations
|
|
220
|
+
if (!isExactNone(failedExpectations)) {
|
|
221
|
+
invalidFields.push(renderExactSectionError("Failed Expectations", STRICT_NONE_VALUE, failedExpectations, "when Test Result is incomplete"));
|
|
224
222
|
}
|
|
225
223
|
}
|
|
226
224
|
if (result === "fail" && !hasBlockingIssues) {
|
|
@@ -235,22 +233,15 @@ function validateArtifactFields(kind, content) {
|
|
|
235
233
|
}
|
|
236
234
|
}
|
|
237
235
|
else if (hasUserApproval) {
|
|
238
|
-
invalidFields.push("User Approval Evidence
|
|
236
|
+
invalidFields.push(renderExactSectionError("User Approval Evidence", STRICT_NONE_VALUE, userApproval, "when no Coverage Gaps are recorded"));
|
|
239
237
|
}
|
|
240
238
|
return invalidFields;
|
|
241
239
|
}
|
|
242
240
|
if (kind === "docs-sync-report") {
|
|
243
|
-
return validateDecision(content,
|
|
241
|
+
return validateDecision(content, DOCS_SYNC_DECISIONS);
|
|
244
242
|
}
|
|
245
243
|
if (kind === "final-acceptance") {
|
|
246
|
-
return validateDecision(content,
|
|
247
|
-
"accepted",
|
|
248
|
-
"accepted-with-known-risks",
|
|
249
|
-
"needs-coder-follow-up",
|
|
250
|
-
"needs-architect-follow-up",
|
|
251
|
-
"needs-docs-sync",
|
|
252
|
-
"blocked-by-user-decision"
|
|
253
|
-
]);
|
|
244
|
+
return validateDecision(content, FINAL_ACCEPTANCE_DECISIONS);
|
|
254
245
|
}
|
|
255
246
|
return [];
|
|
256
247
|
}
|
|
@@ -261,7 +252,7 @@ function hasCompleteL3FlowMapping(value) {
|
|
|
261
252
|
if (!value) {
|
|
262
253
|
return false;
|
|
263
254
|
}
|
|
264
|
-
const allowedActions = new Set(
|
|
255
|
+
const allowedActions = new Set(L3_ACTIONS);
|
|
265
256
|
return value
|
|
266
257
|
.split(/\r?\n/)
|
|
267
258
|
.map((line) => line.trim())
|
|
@@ -274,10 +265,10 @@ function hasCompleteL3FlowMapping(value) {
|
|
|
274
265
|
});
|
|
275
266
|
}
|
|
276
267
|
function validateDecision(content, allowed) {
|
|
277
|
-
const decision =
|
|
278
|
-
return decision && allowed.includes(decision)
|
|
268
|
+
const decision = readArtifactSectionContent(content, "Decision")?.trim();
|
|
269
|
+
return decision && allowed.includes(decision.toLowerCase())
|
|
279
270
|
? []
|
|
280
|
-
: [
|
|
271
|
+
: [renderExactSectionError("Decision", allowed.join("|"), decision)];
|
|
281
272
|
}
|
|
282
273
|
export function readArtifactSectionValue(content, heading) {
|
|
283
274
|
return readArtifactSectionContent(content, heading)
|
|
@@ -285,7 +276,7 @@ export function readArtifactSectionValue(content, heading) {
|
|
|
285
276
|
.map((line) => line.trim())
|
|
286
277
|
.find(Boolean);
|
|
287
278
|
}
|
|
288
|
-
function readArtifactSectionContent(content, heading) {
|
|
279
|
+
export function readArtifactSectionContent(content, heading) {
|
|
289
280
|
const match = new RegExp(`^#{1,6}\\s+${escapeRegExp(heading)}\\s*$`, "im").exec(content);
|
|
290
281
|
if (!match || match.index === undefined) {
|
|
291
282
|
return undefined;
|
|
@@ -297,6 +288,29 @@ function readArtifactSectionContent(content, heading) {
|
|
|
297
288
|
: afterHeading.slice(0, nextHeading.index);
|
|
298
289
|
return section.trim();
|
|
299
290
|
}
|
|
291
|
+
function readInlineField(content, field) {
|
|
292
|
+
return new RegExp(`^\\s*${escapeRegExp(field)}\\s*:\\s*(.+?)\\s*$`, "im")
|
|
293
|
+
.exec(content)?.[1]?.trim().toLowerCase();
|
|
294
|
+
}
|
|
295
|
+
function isAllowedValue(value, allowed) {
|
|
296
|
+
return value !== undefined && allowed.includes(value);
|
|
297
|
+
}
|
|
298
|
+
function isExactNone(value) {
|
|
299
|
+
return value?.trim() === STRICT_NONE_VALUE;
|
|
300
|
+
}
|
|
301
|
+
function renderExactFieldError(field, allowed, found) {
|
|
302
|
+
return `${field} must be exactly one of "${allowed.join("|")}"; found ${renderFoundValue(found)}.`;
|
|
303
|
+
}
|
|
304
|
+
function renderExactSectionError(section, expected, found, condition) {
|
|
305
|
+
const suffix = condition ? ` ${condition}` : "";
|
|
306
|
+
return `${section} must contain exactly "${expected}"${suffix}; found ${renderFoundValue(found)}.`;
|
|
307
|
+
}
|
|
308
|
+
function renderFoundValue(value) {
|
|
309
|
+
if (value === undefined || value.trim().length === 0) {
|
|
310
|
+
return "<missing>";
|
|
311
|
+
}
|
|
312
|
+
return JSON.stringify(value.trim().replace(/\s+/g, " "));
|
|
313
|
+
}
|
|
300
314
|
function hasHeading(content, heading) {
|
|
301
315
|
const pattern = new RegExp(`^#{1,6}\\s+${escapeRegExp(heading)}\\s*$`, "im");
|
|
302
316
|
return pattern.test(content);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export const STRICT_NONE_VALUE = "None.";
|
|
2
|
+
export const ARCHITECTURE_BRIEF_STATUSES = ["interviewing", "confirmed"];
|
|
3
|
+
export const ARCHITECTURE_PLAN_RESULTS = [
|
|
4
|
+
"complete",
|
|
5
|
+
"incomplete",
|
|
6
|
+
"user clarification required"
|
|
7
|
+
];
|
|
8
|
+
export const TEST_RESULTS = ["pass", "fail", "incomplete"];
|
|
9
|
+
export const L3_REQUIRED_VALUES = ["yes", "no"];
|
|
10
|
+
export const L3_ACTIONS = ["run-existing", "updated", "added"];
|
|
11
|
+
export const DOCS_SYNC_DECISIONS = ["synced", "unchanged", "blocked"];
|
|
12
|
+
export const FINAL_ACCEPTANCE_DECISIONS = [
|
|
13
|
+
"accepted",
|
|
14
|
+
"accepted-with-known-risks",
|
|
15
|
+
"needs-coder-follow-up",
|
|
16
|
+
"needs-architect-follow-up",
|
|
17
|
+
"needs-docs-sync",
|
|
18
|
+
"blocked-by-user-decision"
|
|
19
|
+
];
|
|
20
|
+
export function renderArtifactOptions(values) {
|
|
21
|
+
return values.join("|");
|
|
22
|
+
}
|
package/package.json
CHANGED
|
@@ -137,6 +137,11 @@ async function processEntry(context) {
|
|
|
137
137
|
return;
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
+
if (entry.ownership === "project-owned") {
|
|
141
|
+
context.operations.push(skip(entry.path, "project-owned; preserved"));
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
140
145
|
if (entry.ownership === "managed-block" || uninstallAction === "remove-managed-block") {
|
|
141
146
|
await removeManagedBlock(context);
|
|
142
147
|
return;
|