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.
- package/README.md +12 -6
- package/dist/backend/adapters/claude-adapter.js +1 -0
- package/dist/backend/api/artifact-routes.js +72 -1
- package/dist/backend/api/runtime-state-routes.js +17 -5
- package/dist/backend/api/task-routes.js +6 -0
- package/dist/backend/api/workflow-control-routes.js +56 -0
- package/dist/backend/cli/install-vcm-harness.js +46 -22
- package/dist/backend/server.js +21 -5
- package/dist/backend/services/artifact-service.js +309 -5
- package/dist/backend/services/auto-memory-service.js +2 -2
- package/dist/backend/services/code-intelligence-service.js +5 -5
- package/dist/backend/services/gate-review-service.js +81 -33
- package/dist/backend/services/harness-feedback-service.js +11 -4
- package/dist/backend/services/harness-service.js +42 -26
- package/dist/backend/services/message-service.js +105 -37
- package/dist/backend/services/status-service.js +6 -0
- package/dist/backend/services/workflow-control-service.js +854 -0
- package/dist/backend/templates/handoff.js +158 -3
- package/dist/backend/templates/harness/architect-agent.js +3 -3
- package/dist/backend/templates/harness/check-scaffold-ledger.js +105 -39
- package/dist/backend/templates/harness/claude-root.js +9 -1
- package/dist/backend/templates/harness/coder-agent.js +2 -3
- package/dist/backend/templates/harness/coder-worker-agent.js +1 -1
- package/dist/backend/templates/harness/gate-review.js +4 -5
- package/dist/backend/templates/harness/harness-engineer-agent.js +18 -1
- package/dist/backend/templates/harness/project-manager-agent.js +3 -2
- package/dist/backend/templates/harness/tester-agent.js +1 -0
- package/dist/backend/templates/harness/vcm-architecture-interview-skill.js +5 -0
- package/dist/backend/templates/harness/vcm-code-navigation-skill.js +7 -6
- package/dist/backend/templates/harness/vcm-final-acceptance-skill.js +2 -2
- package/dist/backend/templates/harness/vcm-long-running-validation-skill.js +4 -2
- package/dist/backend/templates/harness/vcm-propose-memory-skill.js +4 -2
- package/dist/backend/templates/harness/vcm-report-harness-issue-skill.js +2 -1
- package/dist/backend/templates/harness/vcm-route-message-skill.js +4 -7
- package/dist/backend/templates/harness/vcm-task-state-skill.js +2 -13
- package/dist/backend/templates/harness/vcm-workflow-review-skill.js +62 -0
- package/dist/backend/templates/message-envelope.js +4 -4
- package/dist/shared/types/workflow.js +7 -1
- package/dist/shared/validation/artifact-check.js +99 -95
- package/dist/shared/validation/artifact-contract.js +9 -0
- package/dist/shared/validation/artifact-registry.js +211 -0
- package/dist-frontend/assets/{index-VillnFRo.js → index-Bocc2DWF.js} +34 -34
- package/dist-frontend/assets/{index-CXSOe-NN.css → index-C_XHGNBD.css} +1 -1
- package/dist-frontend/index.html +2 -2
- package/package.json +1 -1
- package/scripts/harness-tools/run-long-check +38 -8
- package/scripts/harness-tools/vcm-artifact +148 -0
- package/scripts/harness-tools/vcm-bash-guard +42 -47
- 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
|
|
84
|
+
Write a complete candidate outside \`.ai/vcm\`, then submit it:
|
|
85
85
|
|
|
86
86
|
\`\`\`text
|
|
87
|
-
.ai/vcm
|
|
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
|
-
-
|
|
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
|
-
-
|
|
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.
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
-
|
|
21
|
-
- If PM sends a VCM message after handling this,
|
|
22
|
-
- If a non-PM role sends a VCM message after handling this,
|
|
23
|
-
- After
|
|
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,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
|
-
|
|
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: [...
|
|
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: [...
|
|
26
|
+
missingHeadings: [...requiredHeadings],
|
|
110
27
|
invalidFields: [],
|
|
111
28
|
status: "empty"
|
|
112
29
|
};
|
|
113
30
|
}
|
|
114
|
-
const
|
|
115
|
-
const
|
|
116
|
-
const
|
|
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
|
|
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",
|