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.
Files changed (29) hide show
  1. package/README.md +31 -10
  2. package/dist/backend/api/harness-routes.js +0 -1
  3. package/dist/backend/cli/install-vcm-harness.js +37 -7
  4. package/dist/backend/server.js +9 -6
  5. package/dist/backend/services/architect-restart-service.js +51 -3
  6. package/dist/backend/services/auto-memory-service.js +118 -61
  7. package/dist/backend/services/claude-hook-service.js +12 -2
  8. package/dist/backend/services/gate-review-service.js +35 -18
  9. package/dist/backend/services/harness-feedback-service.js +138 -7
  10. package/dist/backend/services/memory-proposal-validation.js +80 -0
  11. package/dist/backend/services/memory-review-paths.js +7 -0
  12. package/dist/backend/services/memory-review-validation.js +94 -0
  13. package/dist/backend/templates/handoff.js +20 -16
  14. package/dist/backend/templates/harness/architect-agent.js +3 -2
  15. package/dist/backend/templates/harness/claude-root.js +1 -1
  16. package/dist/backend/templates/harness/coder-agent.js +12 -1
  17. package/dist/backend/templates/harness/coder-worker-agent.js +12 -1
  18. package/dist/backend/templates/harness/harness-engineer-agent.js +53 -12
  19. package/dist/backend/templates/harness/restart-architect-skill.js +12 -2
  20. package/dist/backend/templates/harness/role-memory.js +2 -2
  21. package/dist/backend/templates/harness/tester-agent.js +3 -3
  22. package/dist/backend/templates/harness/vcm-architecture-interview-skill.js +2 -2
  23. package/dist/backend/templates/harness/vcm-final-acceptance-skill.js +1 -1
  24. package/dist/backend/templates/harness/vcm-propose-memory-skill.js +55 -7
  25. package/dist/backend/templates/harness/vcm-route-message-skill.js +2 -5
  26. package/dist/shared/validation/artifact-check.js +78 -64
  27. package/dist/shared/validation/artifact-contract.js +22 -0
  28. package/package.json +1 -1
  29. 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 \`status: running\` and assigns its state path and report path.
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
- Do not edit harness or memory files.
42
- - Memory Review Mode: when Auto Memory is enabled and VCM starts the memory
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, Memory Review Mode, or when VCM
51
- explicitly asks you to apply an approved harness change.
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 and Retrospective Mode, do not edit files.
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, start Memory Review
68
- Mode, or update memory.
69
- - During VCM-assigned Memory Review, verify every role proposal against task
70
- evidence, merge duplicates, remove stale entries, and keep role-specific
71
- knowledge in the matching role memory output.
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\`, 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.
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(payload.get("status", "scheduled"), taskSlug=task_slug, sessionId=payload.get("sessionId"))
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 requests a proposal during Task Harness Review, use
9
- \`vcm-propose-memory\` and write the exact assigned draft path.`;
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. Use \`None\` under Unresolved User Decisions only when no user-owned decision remains.
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 | accepted-with-known-risks | needs-coder-follow-up | needs-architect-follow-up | needs-docs-sync | blocked-by-user-decision
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 during Task
3
- Harness Review and provides an exact draft path.
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 draft path assigned by VCM. The path must be under
10
- \`.ai/vcm/memory-review/runs/<run-id>/drafts/\` in the active task worktree.
11
- - If VCM did not provide a draft path, do not create a proposal.
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: update | no-change
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
- ## Evidence
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 = /^\s*Planning Result\s*:\s*(.+?)\s*$/im.exec(content)?.[1]?.trim().toLowerCase();
132
- if (!result) {
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
- : [`Planning Result must be complete; received "${result}".`];
135
+ : [renderExactFieldError("Planning Result", [ARCHITECTURE_PLAN_RESULTS[0]], result)];
138
136
  }
139
137
  if (kind === "architecture-brief") {
140
- const status = /^\s*Architecture Brief Status\s*:\s*(\S+)\s*$/im.exec(content)?.[1]?.toLowerCase();
141
- const invalidFields = status === "interviewing" || status === "confirmed"
138
+ const status = readInlineField(content, "Architecture Brief Status");
139
+ const invalidFields = isAllowedValue(status, ARCHITECTURE_BRIEF_STATUSES)
142
140
  ? []
143
- : ["Architecture Brief Status must be interviewing or confirmed."];
141
+ : [renderExactFieldError("Architecture Brief Status", ARCHITECTURE_BRIEF_STATUSES, status)];
144
142
  if (status === "confirmed") {
145
- const unresolved = readArtifactSectionValue(content, "Unresolved User Decisions");
146
- if (!unresolved || !/^none\.?$/i.test(unresolved)) {
147
- invalidFields.push("Unresolved User Decisions must be None when Architecture Brief Status is confirmed.");
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 = /^\s*Test Result\s*:\s*(\S+)\s*$/im.exec(content)?.[1]?.toLowerCase();
154
- const invalidFields = result === "pass" || result === "fail" || result === "incomplete"
151
+ const result = readInlineField(content, "Test Result");
152
+ const invalidFields = isAllowedValue(result, TEST_RESULTS)
155
153
  ? []
156
- : ["Test Result must be pass, fail, or incomplete."];
157
- const l3Required = /^\s*L3 Required\s*:\s*(\S+)\s*$/im.exec(content)?.[1]?.toLowerCase();
158
- if (l3Required !== "yes" && l3Required !== "no") {
159
- invalidFields.push("L3 Required must be yes or no.");
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 = readArtifactSectionValue(content, "Trigger Assessment");
159
+ const l3TriggerAssessment = readArtifactSectionContent(content, "Trigger Assessment");
162
160
  const l3AffectedFlows = readArtifactSectionContent(content, "Affected End-To-End Flows");
163
- const l3Commands = readArtifactSectionValue(content, "L3 Commands And Evidence");
164
- const l3NotRequiredEvidence = readArtifactSectionValue(content, "Not-Required Evidence");
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 = readArtifactSectionValue(content, "Coverage Gaps");
180
- const blockingIssues = readArtifactSectionValue(content, "Blocking Validation Issues");
181
- const userApproval = readArtifactSectionValue(content, "User Approval Evidence");
182
- const failedExpectations = readArtifactSectionValue(content, "Failed Expectations");
183
- const completedValidation = readArtifactSectionValue(content, "Completed Validation");
184
- const remainingValidation = readArtifactSectionValue(content, "Remaining Validation");
185
- const hasCoverageGaps = Boolean(coverageGaps && !/^none\.?$/i.test(coverageGaps));
186
- const hasBlockingIssues = Boolean(blockingIssues && !/^none\.?$/i.test(blockingIssues));
187
- const hasUserApproval = Boolean(userApproval && !/^none\.?$/i.test(userApproval));
188
- const hasFailedExpectations = Boolean(failedExpectations && !/^none\.?$/i.test(failedExpectations));
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 || hasCoverageGaps) {
191
- invalidFields.push("Coverage Gaps must be None when Test Result is pass.");
188
+ if (!isExactNone(coverageGaps)) {
189
+ invalidFields.push(renderExactSectionError("Coverage Gaps", STRICT_NONE_VALUE, coverageGaps, "when Test Result is pass"));
192
190
  }
193
- if (!blockingIssues || hasBlockingIssues) {
194
- invalidFields.push("Blocking Validation Issues must be None when Test Result is pass.");
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 || hasUserApproval) {
197
- invalidFields.push("User Approval Evidence must be None when Test Result is pass.");
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 || hasFailedExpectations) {
200
- invalidFields.push("Failed Expectations must be None when Test Result is pass.");
197
+ if (!isExactNone(failedExpectations)) {
198
+ invalidFields.push(renderExactSectionError("Failed Expectations", STRICT_NONE_VALUE, failedExpectations, "when Test Result is pass"));
201
199
  }
202
- if (hasSubstantiveSectionValue(remainingValidation)) {
203
- invalidFields.push("Remaining Validation must be None when Test Result is pass.");
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 (hasCoverageGaps) {
214
- invalidFields.push("Coverage Gaps must be None when Test Result is incomplete.");
211
+ if (!isExactNone(coverageGaps)) {
212
+ invalidFields.push(renderExactSectionError("Coverage Gaps", STRICT_NONE_VALUE, coverageGaps, "when Test Result is incomplete"));
215
213
  }
216
- if (hasBlockingIssues) {
217
- invalidFields.push("Blocking Validation Issues must be None when Test Result is incomplete.");
214
+ if (!isExactNone(blockingIssues)) {
215
+ invalidFields.push(renderExactSectionError("Blocking Validation Issues", STRICT_NONE_VALUE, blockingIssues, "when Test Result is incomplete"));
218
216
  }
219
- if (hasUserApproval) {
220
- invalidFields.push("User Approval Evidence must be None when Test Result is incomplete.");
217
+ if (!isExactNone(userApproval)) {
218
+ invalidFields.push(renderExactSectionError("User Approval Evidence", STRICT_NONE_VALUE, userApproval, "when Test Result is incomplete"));
221
219
  }
222
- if (hasFailedExpectations) {
223
- invalidFields.push("Failed Expectations must be None when Test Result is incomplete.");
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 must be None when no Coverage Gaps are recorded.");
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, ["synced", "unchanged", "blocked"]);
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(["run-existing", "updated", "added"]);
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 = readArtifactSectionValue(content, "Decision")?.toLowerCase();
278
- return decision && allowed.includes(decision)
268
+ const decision = readArtifactSectionContent(content, "Decision")?.trim();
269
+ return decision && allowed.includes(decision.toLowerCase())
279
270
  ? []
280
- : [`Decision must be one of: ${allowed.join(", ")}.`];
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibe-coding-master",
3
- "version": "0.7.27",
3
+ "version": "0.7.29",
4
4
  "description": "Local GUI session cockpit for Claude Code role sessions.",
5
5
  "type": "module",
6
6
  "files": [
@@ -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;