vibe-coding-master 0.7.38 → 0.7.40

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 (49) hide show
  1. package/README.md +12 -6
  2. package/dist/backend/adapters/claude-adapter.js +1 -0
  3. package/dist/backend/api/artifact-routes.js +72 -1
  4. package/dist/backend/api/runtime-state-routes.js +17 -5
  5. package/dist/backend/api/task-routes.js +6 -0
  6. package/dist/backend/api/workflow-control-routes.js +56 -0
  7. package/dist/backend/cli/install-vcm-harness.js +46 -22
  8. package/dist/backend/server.js +21 -5
  9. package/dist/backend/services/artifact-service.js +309 -5
  10. package/dist/backend/services/auto-memory-service.js +2 -2
  11. package/dist/backend/services/code-intelligence-service.js +5 -5
  12. package/dist/backend/services/gate-review-service.js +81 -33
  13. package/dist/backend/services/harness-feedback-service.js +11 -4
  14. package/dist/backend/services/harness-service.js +42 -26
  15. package/dist/backend/services/message-service.js +105 -37
  16. package/dist/backend/services/status-service.js +6 -0
  17. package/dist/backend/services/workflow-control-service.js +854 -0
  18. package/dist/backend/templates/handoff.js +158 -3
  19. package/dist/backend/templates/harness/architect-agent.js +3 -3
  20. package/dist/backend/templates/harness/check-scaffold-ledger.js +105 -39
  21. package/dist/backend/templates/harness/claude-root.js +9 -1
  22. package/dist/backend/templates/harness/coder-agent.js +2 -3
  23. package/dist/backend/templates/harness/coder-worker-agent.js +1 -1
  24. package/dist/backend/templates/harness/gate-review.js +4 -5
  25. package/dist/backend/templates/harness/harness-engineer-agent.js +18 -1
  26. package/dist/backend/templates/harness/project-manager-agent.js +3 -2
  27. package/dist/backend/templates/harness/tester-agent.js +1 -0
  28. package/dist/backend/templates/harness/vcm-architecture-interview-skill.js +5 -0
  29. package/dist/backend/templates/harness/vcm-code-navigation-skill.js +7 -6
  30. package/dist/backend/templates/harness/vcm-final-acceptance-skill.js +2 -2
  31. package/dist/backend/templates/harness/vcm-long-running-validation-skill.js +4 -2
  32. package/dist/backend/templates/harness/vcm-propose-memory-skill.js +4 -2
  33. package/dist/backend/templates/harness/vcm-report-harness-issue-skill.js +2 -1
  34. package/dist/backend/templates/harness/vcm-route-message-skill.js +4 -7
  35. package/dist/backend/templates/harness/vcm-task-state-skill.js +2 -13
  36. package/dist/backend/templates/harness/vcm-workflow-review-skill.js +62 -0
  37. package/dist/backend/templates/message-envelope.js +4 -4
  38. package/dist/shared/types/workflow.js +7 -1
  39. package/dist/shared/validation/artifact-check.js +99 -95
  40. package/dist/shared/validation/artifact-contract.js +9 -0
  41. package/dist/shared/validation/artifact-registry.js +211 -0
  42. package/dist-frontend/assets/{index-VillnFRo.js → index-Bocc2DWF.js} +34 -34
  43. package/dist-frontend/assets/{index-CXSOe-NN.css → index-C_XHGNBD.css} +1 -1
  44. package/dist-frontend/index.html +2 -2
  45. package/package.json +1 -1
  46. package/scripts/harness-tools/run-long-check +38 -8
  47. package/scripts/harness-tools/vcm-artifact +148 -0
  48. package/scripts/harness-tools/vcm-bash-guard +42 -47
  49. package/scripts/harness-tools/watch-job +58 -4
@@ -81,10 +81,10 @@ Do not accept when required role evidence is missing, required Gate Review evide
81
81
 
82
82
  ## Output
83
83
 
84
- Write or update:
84
+ Write a complete candidate outside \`.ai/vcm\`, then submit it:
85
85
 
86
86
  \`\`\`text
87
- .ai/vcm/handoffs/final-acceptance.md
87
+ .ai/tools/vcm-artifact final-acceptance --file <candidate> --mode final
88
88
  \`\`\`
89
89
 
90
90
  Use this structure:
@@ -15,7 +15,7 @@ The hard ceiling is 60 minutes per job, enforced by the job worker itself. No ap
15
15
 
16
16
  1. Start the command with an explicit ceiling: \`.ai/tools/run-long-check --timeout <duration> -- <command>\`. Pass the validation executable and its arguments directly. Do not use a shell command-string wrapper, pipeline its output, or append another command: run-long-check already captures stdout/stderr, and shell wrappers can hide the validation exit code. Pick the ceiling from \`docs/TESTING.md\` guidance or a realistic estimate, never above 60m. The tool prints the job id and creates job state under \`.ai/vcm/jobs/<job-id>/\`.
17
17
  2. In the same turn, run \`.ai/tools/watch-job <job-id>\` as its own Bash tool call. The default watch window is 8 minutes.
18
- 3. If watch-job exits 125, the job is still running: run \`.ai/tools/watch-job <job-id>\` again immediately. Do not end the turn between windows.
18
+ 3. If watch-job exits 125, the job is still running and a bounded handoff is active for the next watcher: run \`.ai/tools/watch-job <job-id>\` again immediately. Do not end the turn between windows.
19
19
  4. Repeat until watch-job reports a terminal result.
20
20
  5. Read the final status and the relevant log tail.
21
21
  6. Record command, result, duration, and required follow-up wherever the caller normally records command evidence.
@@ -44,7 +44,8 @@ Treat watch-job exit codes as explicit results:
44
44
  A running job requires a live foreground watcher:
45
45
 
46
46
  - watch-job renews the job supervision lease while it runs.
47
- - If no watcher renews the lease for about 2 minutes, the worker kills the command process group and records \`orphaned\`. A job cannot keep running unsupervised.
47
+ - A normal exit 125 records a five-minute handoff deadline for the next model-generated watcher call. The next watcher renews the lease and clears that handoff when it starts.
48
+ - Without a normal handoff, about two minutes without lease renewal causes the worker to kill the command process group and record \`orphaned\`. An expired handoff is also recorded as \`orphaned\` with a distinct reason.
48
49
  - VCM also blocks ending the turn while a job is running. Stay in the turn and keep watching.
49
50
  - Only one validation job may run at a time; run-long-check refuses to start a second one.
50
51
 
@@ -56,6 +57,7 @@ A running job requires a live foreground watcher:
56
57
  .ai/vcm/jobs/<job-id>/stdout.log
57
58
  .ai/vcm/jobs/<job-id>/stderr.log
58
59
  .ai/vcm/jobs/<job-id>/lease
60
+ .ai/vcm/jobs/<job-id>/watch-handoff.json # present only between normal watch windows
59
61
  \`\`\`
60
62
 
61
63
  ## Timeout
@@ -6,7 +6,7 @@ planning-session memory candidate and provides an exact path.
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 path assigned by VCM. It must be either a role draft
9
+ - Submit only to the exact path assigned by VCM. It must be either a role draft
10
10
  under \`.ai/vcm/memory-review/runs/<run-id>/drafts/\` or a planning candidate
11
11
  under \`.ai/vcm/memory-review/candidates/\` in the active task worktree.
12
12
  - If VCM did not provide a path, do not create a proposal.
@@ -79,5 +79,7 @@ Evidence: <task artifact, code, or durable documentation>
79
79
  \`\`\`
80
80
 
81
81
  Use \`Decision: no-change\` when the completed task produced no qualifying
82
- memory. End the turn after writing the assigned draft.`;
82
+ memory. Write the proposal to a candidate outside \`.ai/vcm\`, submit it with
83
+ \`.ai/tools/vcm-artifact memory-proposal --file <candidate> --path <assigned-path> --mode final\`,
84
+ and end the turn only after VCM accepts it.`;
83
85
  }
@@ -5,7 +5,7 @@ This skill records feedback only. It must not call Harness Engineer directly and
5
5
 
6
6
  ## Write Location
7
7
 
8
- Write one markdown file under:
8
+ Submit one markdown file under:
9
9
 
10
10
  \`\`\`
11
11
  \${VCM_BASE_REPO_ROOT}/.ai/vcm/harness-feedback/pending/
@@ -47,5 +47,6 @@ Use this structure:
47
47
  - Include enough evidence for Harness Engineer to verify the issue.
48
48
  - Do not include secrets, private logs, or unnecessary source content.
49
49
  - Continue the current VCM role work after writing the report unless the original task is blocked.
50
+ - Write the report to a candidate outside \`.ai/vcm\`, then run \`.ai/tools/vcm-artifact harness-feedback --file <candidate> --path <feedback-path> --mode final\`.
50
51
  `;
51
52
  }
@@ -32,10 +32,10 @@ Allowed message types:
32
32
 
33
33
  ## Route File
34
34
 
35
- Write or update exactly one file:
35
+ Write the message to a candidate outside \`.ai/vcm\`, then submit it:
36
36
 
37
37
  \`\`\`text
38
- .ai/vcm/handoffs/messages/<from-role>-<to-role>.md
38
+ .ai/tools/vcm-artifact route-message --file <candidate> --path .ai/vcm/handoffs/messages/<from-role>-<to-role>.md --mode final
39
39
  \`\`\`
40
40
 
41
41
  Use one allowed route above. The file name is authoritative. Do not put from/to in frontmatter and do not create alternate message paths.
@@ -53,9 +53,6 @@ For PM dispatch, use:
53
53
  \`\`\`md
54
54
  ---
55
55
  type: task
56
- workflow_flow: code-change
57
- workflow_step: coder-implementation
58
- workflow_status: active
59
56
  artifact_refs: .ai/vcm/handoffs/architecture-plan.md, docs/plans/example.md
60
57
  ---
61
58
 
@@ -75,7 +72,7 @@ Stop conditions:
75
72
  ...
76
73
  \`\`\`
77
74
 
78
- PM route files should declare the workflow checkpoint fields defined by the \`vcm-task-state\` skill. Non-PM reports must not declare workflow state.
75
+ Project Manager must obtain an accepted \`vcm-workflow-review\` transition for the exact target before submitting this route. Do not put workflow approval or checkpoint fields in the route file.
79
76
 
80
77
  For non-PM reports, use:
81
78
 
@@ -112,7 +109,7 @@ Formal messages should include:
112
109
 
113
110
  ## Turn Rule
114
111
 
115
- After writing or updating the route file, end the current Claude Code turn immediately.
112
+ After VCM accepts the route file, end the current Claude Code turn immediately.
116
113
 
117
114
  Do not:
118
115
 
@@ -7,18 +7,7 @@ The declaration is recoverable context, not a workflow controller. It does not a
7
7
 
8
8
  ## Declaration
9
9
 
10
- For a PM role dispatch, put the current declaration in the route-file frontmatter:
11
-
12
- \`\`\`yaml
13
- workflow_flow: code-change
14
- workflow_step: coder-implementation
15
- workflow_branch: none
16
- workflow_resume_point: none
17
- workflow_status: active
18
- workflow_evidence_refs: .ai/vcm/handoffs/architecture-plan.md
19
- \`\`\`
20
-
21
- For a checkpoint without a role route, run:
10
+ Record a recoverable checkpoint with:
22
11
 
23
12
  \`\`\`bash
24
13
  .ai/tools/update-task-state --flow code-change --step awaiting-user --status awaiting-user
@@ -26,7 +15,7 @@ For a checkpoint without a role route, run:
26
15
 
27
16
  Supply only fields that changed. Use \`none\` to clear branch or resume point. Repeat \`--evidence\` for evidence paths.
28
17
 
29
- Declare the selected flow before its first dispatch, update the step on later PM dispatches, and update no-route checkpoints such as waiting for the user, waiting for Gate Review, or completion.
18
+ Declare the selected flow before its first dispatch, update the step before later PM dispatches, and update no-route checkpoints such as waiting for the user, waiting for Gate Review, or completion. Do not put task-state or workflow-approval fields in route-message frontmatter.
30
19
 
31
20
  If declaration fails, report the warning when relevant and continue the existing workflow. Never delay routing, Gate Review, final acceptance, or task close because task state is unavailable.
32
21
  `;
@@ -0,0 +1,62 @@
1
+ export function renderVcmWorkflowReviewSkillRules() {
2
+ return `## Purpose
3
+
4
+ Use this skill before every Project Manager dispatch to Architect, Coder, or Tester. VCM validates the proposed transition against the confirmed task Flow Record and real task artifacts.
5
+
6
+ ## Submission
7
+
8
+ Read the current \`.ai/vcm/handoffs/workflow-progress.md\`. Copy its Revision, Flow, Status, and Dispatch History exactly, increment Revision by one, and propose one dispatch:
9
+
10
+ \`\`\`md
11
+ # Workflow Progress: <task-slug>
12
+
13
+ Revision: <previous-revision + 1>
14
+ Flow: <copy confirmed Flow exactly>
15
+ Status: <copy confirmed Status exactly>
16
+
17
+ ## Dispatch History
18
+
19
+ <copy exactly; use none when empty>
20
+
21
+ ## Proposed Dispatch
22
+
23
+ Requested Flow: <none|code-change|architect-debug|architecture-diagnosis|docs-only|validation-only>
24
+ Target Role: <architect|coder|tester>
25
+ Evidence: <current artifact, Gate result, or user request supporting this dispatch>
26
+
27
+ ## User Override
28
+
29
+ Authorization ID: none
30
+ Authorization Quote: none
31
+ Violated Rule: none
32
+ \`\`\`
33
+
34
+ Use \`Requested Flow\` only to start a flow, switch a top-level flow, enter or replace a Branch, or return from a Branch. Otherwise use \`none\`.
35
+
36
+ Submit the candidate outside \`.ai/vcm\`:
37
+
38
+ \`\`\`text
39
+ .ai/tools/vcm-artifact workflow-progress --file <candidate> --mode final
40
+ \`\`\`
41
+
42
+ An accepted submission grants exactly one matching PM route. Then use \`vcm-route-message\` for that target role.
43
+
44
+ If VCM rejects the transition, remain in the current PM turn and choose a legal dispatch. Do not write the route file first.
45
+
46
+ ## User Override
47
+
48
+ Only direct user authorization may override a rejected transition. To request confirmation, resubmit the same transition with:
49
+
50
+ \`\`\`text
51
+ Authorization ID: request
52
+ Authorization Quote: <exact proposed exception for the user>
53
+ Violated Rule: <copy the exact VCM rejection reason>
54
+ \`\`\`
55
+
56
+ Wait for VCM's user decision callback. If approved, resubmit the unchanged transition with the returned Authorization ID, exact Authorization Text, and the same Violated Rule. The approval applies once and only to that exact transition.
57
+
58
+ ## Completion
59
+
60
+ When the active flow has completed without another role dispatch, increment Revision, copy the confirmed history exactly, set \`Status: completed\`, and set every Proposed Dispatch and User Override value to \`none\`. VCM accepts completion only when the flow's required final artifact and Gate evidence exists.
61
+ `;
62
+ }
@@ -17,10 +17,10 @@ ${artifactRefs}
17
17
 
18
18
  Instructions:
19
19
  - Read the message and execute only within this VCM task.
20
- - If you write or update a VCM route file, use the vcm-route-message skill.
21
- - If PM sends a VCM message after handling this, write or update .ai/vcm/handoffs/messages/project-manager-<target-role>.md.
22
- - If a non-PM role sends a VCM message after handling this, reply only to project-manager with .ai/vcm/handoffs/messages/<your-role>-project-manager.md.
23
- - After writing a route file, end this Claude Code turn immediately.
20
+ - Submit every VCM route file through the vcm-route-message skill and .ai/tools/vcm-artifact.
21
+ - If PM sends a VCM message after handling this, submit .ai/vcm/handoffs/messages/project-manager-<target-role>.md.
22
+ - If a non-PM role sends a VCM message after handling this, submit only .ai/vcm/handoffs/messages/<your-role>-project-manager.md.
23
+ - After VCM accepts a route file, end this Claude Code turn immediately.
24
24
  - Do not poll, loop, or wait for another role in this turn. VCM scans route files after your Stop hook and delivers later replies in a new turn.
25
25
  [/VCM MESSAGE]
26
26
  `;
@@ -1 +1,7 @@
1
- export {};
1
+ export const WORKFLOW_FLOWS = [
2
+ "code-change",
3
+ "architect-debug",
4
+ "architecture-diagnosis",
5
+ "docs-only",
6
+ "validation-only"
7
+ ];
@@ -1,91 +1,8 @@
1
- import { ARCHITECTURE_BRIEF_STATUSES, ARCHITECTURE_PLAN_RESULTS, DOCS_SYNC_DECISIONS, FINAL_ACCEPTANCE_DECISIONS, L3_ACTIONS, L3_REQUIRED_VALUES, STRICT_NONE_VALUE, TEST_INFRASTRUCTURE_STATUSES, TEST_RESULTS } from "./artifact-contract.js";
2
- const REQUIRED_HEADINGS = {
3
- "architecture-brief": [
4
- "Accepted Outcome",
5
- "Confirmed User Decisions",
6
- "Existing Constraints",
7
- "Unresolved User Decisions",
8
- "User Confirmation"
9
- ],
10
- "architecture-plan": [
11
- "Accepted Scope",
12
- "Current Code Reality",
13
- "Planning Boundary",
14
- "Code Reading Evidence",
15
- "Existing Behavior Trace",
16
- "Code / Docs Conflicts",
17
- "Architecture Decision",
18
- "Changed Behavior Flow",
19
- "Ownership",
20
- "Data Flow",
21
- "Lifecycle",
22
- "Boundaries",
23
- "Invariants",
24
- "Failure Model",
25
- "Decision Rationale",
26
- "Module/File Plan",
27
- "Public Surface Impact",
28
- "Scaffold Manifest",
29
- "Scaffold Build Evidence",
30
- "Tester Coverage Hints",
31
- "Docs Impact",
32
- "Known Risks",
33
- "Coder Handoff Notes"
34
- ],
35
- "known-issues": [
36
- "Task Issues",
37
- "Escalation To Docs"
38
- ],
39
- "test-report": [
40
- "Evidence Reviewed",
41
- "Tests Added Or Updated",
42
- "Coverage Mapping",
43
- "Validation Progress",
44
- "Completed Validation",
45
- "Remaining Validation",
46
- "L3 Coverage",
47
- "Trigger Assessment",
48
- "Affected End-To-End Flows",
49
- "L3 Commands And Evidence",
50
- "Not-Required Evidence",
51
- "Commands Run Or Checked",
52
- "Validation Results",
53
- "Test Infrastructure",
54
- "Affected Files",
55
- "Boundary Evidence",
56
- "Defect-Class Sweep",
57
- "Repair Commit",
58
- "Failed Expectations",
59
- "Reproduction Steps",
60
- "Skipped Checks With Reasons",
61
- "Coverage Gaps",
62
- "Blocking Validation Issues",
63
- "User Approval Evidence"
64
- ],
65
- "docs-sync-report": [
66
- "Summary",
67
- "Architecture Drift Check",
68
- "Docs Updated",
69
- "Docs Reviewed And Left Unchanged",
70
- "Public Contract / Module Boundary Notes",
71
- "Remaining Documentation Risks",
72
- "Known Issues Disposition",
73
- "Decision"
74
- ],
75
- "final-acceptance": [
76
- "Decision",
77
- "Evidence Reviewed",
78
- "Scope Traceability",
79
- "Validation Summary",
80
- "Review And Docs Sync",
81
- "Known Issues Disposition",
82
- "Gate Review Gates",
83
- "Cleanup Readiness",
84
- "Final User Summary"
85
- ]
86
- };
1
+ import { ARCHITECT_DEBUG_STATUSES, ARCHITECTURE_BRIEF_STATUSES, ARCHITECTURE_DIAGNOSIS_DISPOSITIONS, ARCHITECTURE_EVIDENCE_STATUSES, ARCHITECTURE_PLAN_RESULTS, CODER_COMPLETION_DECISIONS, DOCS_SYNC_DECISIONS, FINAL_ACCEPTANCE_DECISIONS, L3_ACTIONS, L3_REQUIRED_VALUES, PLANNING_PROGRESS_STATUSES, STRICT_NONE_VALUE, TEST_INFRASTRUCTURE_STATUSES, TEST_RESULTS } from "./artifact-contract.js";
2
+ import { getArtifactDefinition } from "./artifact-registry.js";
87
3
  const PLACEHOLDER_PATTERN = /(^|\n)\s*(TBD|Not run yet\.?|status:\s*draft)\s*(\n|$)/i;
88
- export function checkMarkdownArtifact(kind, artifactPath, content) {
4
+ export function checkMarkdownArtifact(kind, artifactPath, content, options = {}) {
5
+ const requiredHeadings = getArtifactDefinition(kind).requiredHeadings;
89
6
  if (content === null) {
90
7
  return {
91
8
  kind,
@@ -93,7 +10,7 @@ export function checkMarkdownArtifact(kind, artifactPath, content) {
93
10
  exists: false,
94
11
  isEmpty: true,
95
12
  hasPlaceholder: false,
96
- missingHeadings: [...REQUIRED_HEADINGS[kind]],
13
+ missingHeadings: [...requiredHeadings],
97
14
  invalidFields: [],
98
15
  status: "missing"
99
16
  };
@@ -106,14 +23,18 @@ export function checkMarkdownArtifact(kind, artifactPath, content) {
106
23
  exists: true,
107
24
  isEmpty: true,
108
25
  hasPlaceholder: false,
109
- missingHeadings: [...REQUIRED_HEADINGS[kind]],
26
+ missingHeadings: [...requiredHeadings],
110
27
  invalidFields: [],
111
28
  status: "empty"
112
29
  };
113
30
  }
114
- const missingHeadings = REQUIRED_HEADINGS[kind].filter((heading) => !hasHeading(trimmed, heading));
115
- const hasPlaceholder = PLACEHOLDER_PATTERN.test(trimmed);
116
- const invalidFields = validateArtifactFields(kind, trimmed);
31
+ const headingErrors = validateHeadingContract(trimmed, requiredHeadings);
32
+ const missingHeadings = headingErrors.missing;
33
+ const hasPlaceholder = options.mode === "draft" ? false : PLACEHOLDER_PATTERN.test(trimmed);
34
+ const invalidFields = [
35
+ ...headingErrors.invalid,
36
+ ...validateArtifactFields(kind, trimmed, options.mode ?? "final")
37
+ ];
117
38
  const isWorkInProgress = kind === "test-report"
118
39
  && /^\s*Test Result\s*:\s*incomplete\s*$/im.test(trimmed);
119
40
  return {
@@ -132,18 +53,22 @@ export function checkMarkdownArtifact(kind, artifactPath, content) {
132
53
  : "incomplete"
133
54
  };
134
55
  }
135
- function validateArtifactFields(kind, content) {
56
+ function validateArtifactFields(kind, content, mode) {
136
57
  if (kind === "architecture-plan") {
137
58
  const result = readInlineField(content, "Planning Result");
138
- return result === ARCHITECTURE_PLAN_RESULTS[0]
59
+ return isAllowedValue(result, ARCHITECTURE_PLAN_RESULTS)
60
+ && (mode === "draft" || result === ARCHITECTURE_PLAN_RESULTS[0])
139
61
  ? []
140
- : [renderExactFieldError("Planning Result", [ARCHITECTURE_PLAN_RESULTS[0]], result)];
62
+ : [renderExactFieldError("Planning Result", mode === "draft" ? ARCHITECTURE_PLAN_RESULTS : [ARCHITECTURE_PLAN_RESULTS[0]], result)];
141
63
  }
142
64
  if (kind === "architecture-brief") {
143
65
  const status = readInlineField(content, "Architecture Brief Status");
144
66
  const invalidFields = isAllowedValue(status, ARCHITECTURE_BRIEF_STATUSES)
145
67
  ? []
146
68
  : [renderExactFieldError("Architecture Brief Status", ARCHITECTURE_BRIEF_STATUSES, status)];
69
+ if (mode === "final" && status !== "confirmed") {
70
+ invalidFields.push(renderExactFieldError("Architecture Brief Status", ["confirmed"], status));
71
+ }
147
72
  if (status === "confirmed") {
148
73
  const unresolved = readArtifactSectionContent(content, "Unresolved User Decisions");
149
74
  if (!isExactNone(unresolved)) {
@@ -152,6 +77,31 @@ function validateArtifactFields(kind, content) {
152
77
  }
153
78
  return invalidFields;
154
79
  }
80
+ if (kind === "architecture-evidence") {
81
+ return validateLifecycleField(content, "Architecture Evidence Status", ARCHITECTURE_EVIDENCE_STATUSES, "complete", mode);
82
+ }
83
+ if (kind === "planning-progress") {
84
+ return validateLifecycleField(content, "Planning Progress Status", PLANNING_PROGRESS_STATUSES, "complete", mode);
85
+ }
86
+ if (kind === "coder-completion") {
87
+ const decision = readInlineField(content, "Decision");
88
+ if (!isAllowedValue(decision, CODER_COMPLETION_DECISIONS)) {
89
+ return [renderExactFieldError("Decision", CODER_COMPLETION_DECISIONS, decision)];
90
+ }
91
+ if (mode === "final" && decision === "incomplete") {
92
+ return [renderExactFieldError("Decision", ["ready_for_review", "failed"], decision)];
93
+ }
94
+ return [];
95
+ }
96
+ if (kind === "architect-debug") {
97
+ return validateLifecycleField(content, "Status", ARCHITECT_DEBUG_STATUSES, "completed", mode);
98
+ }
99
+ if (kind === "architecture-diagnosis") {
100
+ const disposition = readArtifactSectionContent(content, "Final Disposition")?.trim().toLowerCase();
101
+ return isAllowedValue(disposition, ARCHITECTURE_DIAGNOSIS_DISPOSITIONS)
102
+ ? []
103
+ : [renderExactSectionError("Final Disposition", ARCHITECTURE_DIAGNOSIS_DISPOSITIONS.join("|"), disposition)];
104
+ }
155
105
  if (kind === "test-report") {
156
106
  const result = readInlineField(content, "Test Result");
157
107
  const invalidFields = isAllowedValue(result, TEST_RESULTS)
@@ -256,6 +206,9 @@ function validateArtifactFields(kind, content) {
256
206
  }
257
207
  }
258
208
  if (result === "incomplete") {
209
+ if (mode === "final") {
210
+ invalidFields.push("Test Result must be pass or fail for final submission.");
211
+ }
259
212
  if (!hasSubstantiveSectionValue(completedValidation)) {
260
213
  invalidFields.push("Completed Validation must record progress when Test Result is incomplete.");
261
214
  }
@@ -299,6 +252,57 @@ function validateArtifactFields(kind, content) {
299
252
  }
300
253
  return [];
301
254
  }
255
+ function validateLifecycleField(content, field, allowed, finalValue, mode) {
256
+ const value = readInlineField(content, field);
257
+ if (!isAllowedValue(value, allowed)) {
258
+ return [renderExactFieldError(field, allowed, value)];
259
+ }
260
+ if (mode === "final" && finalValue && value !== finalValue) {
261
+ return [renderExactFieldError(field, [finalValue], value)];
262
+ }
263
+ return [];
264
+ }
265
+ function validateHeadingContract(content, required) {
266
+ const headings = readMarkdownHeadings(content);
267
+ const missing = required.filter((heading) => !headings.some((candidate) => candidate.text === heading));
268
+ const invalid = [];
269
+ for (const heading of required) {
270
+ const matches = headings.filter((candidate) => candidate.text === heading);
271
+ if (matches.length > 1) {
272
+ invalid.push(`Heading ${heading} must appear exactly once; found ${matches.length}.`);
273
+ }
274
+ }
275
+ let previousIndex = -1;
276
+ for (const heading of required) {
277
+ const index = headings.findIndex((candidate) => candidate.text === heading);
278
+ if (index < 0) {
279
+ continue;
280
+ }
281
+ if (index < previousIndex) {
282
+ invalid.push(`Heading ${heading} is out of the required order.`);
283
+ }
284
+ previousIndex = Math.max(previousIndex, index);
285
+ }
286
+ return { missing, invalid };
287
+ }
288
+ function readMarkdownHeadings(content) {
289
+ const result = [];
290
+ let fenced = false;
291
+ for (const line of content.split(/\r?\n/)) {
292
+ if (/^\s*(```|~~~)/.test(line)) {
293
+ fenced = !fenced;
294
+ continue;
295
+ }
296
+ if (fenced) {
297
+ continue;
298
+ }
299
+ const match = /^(#{2,6})\s+(.+?)\s*#*\s*$/.exec(line);
300
+ if (match) {
301
+ result.push({ level: match[1].length, text: match[2].trim() });
302
+ }
303
+ }
304
+ return result;
305
+ }
302
306
  function hasSubstantiveSectionValue(value) {
303
307
  return Boolean(value && !/^(none|tbd)\.?$/i.test(value.trim()));
304
308
  }
@@ -1,10 +1,19 @@
1
1
  export const STRICT_NONE_VALUE = "None.";
2
2
  export const ARCHITECTURE_BRIEF_STATUSES = ["interviewing", "confirmed"];
3
+ export const ARCHITECTURE_EVIDENCE_STATUSES = ["incomplete", "complete"];
4
+ export const PLANNING_PROGRESS_STATUSES = ["active", "complete"];
3
5
  export const ARCHITECTURE_PLAN_RESULTS = [
4
6
  "complete",
5
7
  "incomplete",
6
8
  "user clarification required"
7
9
  ];
10
+ export const CODER_COMPLETION_DECISIONS = ["ready_for_review", "incomplete", "failed"];
11
+ export const ARCHITECT_DEBUG_STATUSES = ["pending", "completed"];
12
+ export const ARCHITECTURE_DIAGNOSIS_DISPOSITIONS = [
13
+ "analysis completed",
14
+ "diagnosis implementation completed",
15
+ "user clarification required"
16
+ ];
8
17
  export const TEST_RESULTS = ["pass", "fail", "incomplete"];
9
18
  export const TEST_INFRASTRUCTURE_STATUSES = [
10
19
  "none",