vibe-coding-master 0.7.27 → 0.7.28
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 +5 -2
- package/dist/backend/cli/install-vcm-harness.js +37 -7
- package/dist/backend/services/gate-review-service.js +35 -18
- package/dist/backend/templates/handoff.js +20 -16
- package/dist/backend/templates/harness/architect-agent.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/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-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
package/README.md
CHANGED
|
@@ -148,12 +148,15 @@ the active task worktree:
|
|
|
148
148
|
- `.claude/agents/**`
|
|
149
149
|
- `.claude/skills/**`
|
|
150
150
|
- `.claude/settings.json` hooks
|
|
151
|
-
- `.ai/tools/**`
|
|
151
|
+
- VCM protocol and runtime tools under `.ai/tools/**`
|
|
152
152
|
- `.gitignore` entries for VCM runtime state and task worktrees
|
|
153
|
-
- generated-context tooling
|
|
153
|
+
- initial project-owned generated-context tooling
|
|
154
154
|
- pull request template
|
|
155
155
|
|
|
156
156
|
VCM preserves user-authored content outside VCM managed blocks.
|
|
157
|
+
VCM seeds `.ai/tools/generate-module-index` and
|
|
158
|
+
`.ai/tools/generate-public-surface` only when missing. After installation these
|
|
159
|
+
generators belong to the project and later harness updates do not replace them.
|
|
157
160
|
|
|
158
161
|
The fixed harness install is deterministic and creates a commit in the active
|
|
159
162
|
task worktree. Bootstrap is AI-assisted and is run through the Harness Engineer
|
|
@@ -229,13 +229,7 @@ const DURABLE_DOC_TEMPLATES = [
|
|
|
229
229
|
content: "# Testing\n"
|
|
230
230
|
},
|
|
231
231
|
];
|
|
232
|
-
const
|
|
233
|
-
{
|
|
234
|
-
path: ".ai/tools/check-durable-docs",
|
|
235
|
-
category: "durable-docs-tool",
|
|
236
|
-
mode: 0o755,
|
|
237
|
-
templatePath: "scripts/harness-tools/check-durable-docs"
|
|
238
|
-
},
|
|
232
|
+
const PROJECT_OWNED_FILES = [
|
|
239
233
|
{
|
|
240
234
|
path: ".ai/tools/generate-module-index",
|
|
241
235
|
category: "generated-context-tool",
|
|
@@ -247,6 +241,14 @@ const WHOLE_FILES = [
|
|
|
247
241
|
category: "generated-context-tool",
|
|
248
242
|
mode: 0o755,
|
|
249
243
|
templatePath: "scripts/harness-tools/generate-public-surface"
|
|
244
|
+
}
|
|
245
|
+
];
|
|
246
|
+
const WHOLE_FILES = [
|
|
247
|
+
{
|
|
248
|
+
path: ".ai/tools/check-durable-docs",
|
|
249
|
+
category: "durable-docs-tool",
|
|
250
|
+
mode: 0o755,
|
|
251
|
+
templatePath: "scripts/harness-tools/check-durable-docs"
|
|
250
252
|
},
|
|
251
253
|
{
|
|
252
254
|
path: ".claude/skills/vcm-architecture-interview/SKILL.md",
|
|
@@ -397,6 +399,9 @@ async function main() {
|
|
|
397
399
|
for (const file of WHOLE_FILES) {
|
|
398
400
|
await installWholeFile({ projectRoot, file, dryRun, operations });
|
|
399
401
|
}
|
|
402
|
+
for (const file of PROJECT_OWNED_FILES) {
|
|
403
|
+
await installProjectOwnedFile({ projectRoot, file, dryRun, operations });
|
|
404
|
+
}
|
|
400
405
|
await removeLegacyFlatSkillFiles({ projectRoot, dryRun, operations });
|
|
401
406
|
await removeLegacyCodexHarnessPaths({ projectRoot, dryRun, operations });
|
|
402
407
|
await installManifest({
|
|
@@ -511,6 +516,14 @@ async function buildManifest(projectRoot) {
|
|
|
511
516
|
...fixedDirectories().map((directory) => manifestEntry(directory, "directory", directoryCategory(directory), "vcm-created")),
|
|
512
517
|
manifestEntry("docs/GLOSSARY.md", "file", "project-glossary", "project-owned"),
|
|
513
518
|
manifestEntry("docs/CODING_STANDARDS.md", "file", "project-coding-standards", "project-owned"),
|
|
519
|
+
...PROJECT_OWNED_FILES.map((file) => ({
|
|
520
|
+
path: file.path,
|
|
521
|
+
entryType: "file",
|
|
522
|
+
category: file.category,
|
|
523
|
+
ownership: "project-owned",
|
|
524
|
+
source: "vcm-template",
|
|
525
|
+
lifecycle: "long-term"
|
|
526
|
+
})),
|
|
514
527
|
...WHOLE_FILES.map((file) => ({
|
|
515
528
|
path: file.path,
|
|
516
529
|
entryType: "file",
|
|
@@ -820,6 +833,23 @@ async function installWholeFile({ projectRoot, file, dryRun, operations }) {
|
|
|
820
833
|
action: "write fixed VCM file"
|
|
821
834
|
});
|
|
822
835
|
}
|
|
836
|
+
async function installProjectOwnedFile({ projectRoot, file, dryRun, operations }) {
|
|
837
|
+
const targetPath = resolveInside(projectRoot, file.path);
|
|
838
|
+
if (await pathExists(targetPath)) {
|
|
839
|
+
operations.push(skip(file.path, "exists; project-owned"));
|
|
840
|
+
return;
|
|
841
|
+
}
|
|
842
|
+
const content = await wholeFileContent(file);
|
|
843
|
+
await writeIfChanged({
|
|
844
|
+
targetPath,
|
|
845
|
+
relativePath: file.path,
|
|
846
|
+
content: ensureTrailingNewline(content),
|
|
847
|
+
mode: file.mode,
|
|
848
|
+
dryRun,
|
|
849
|
+
operations,
|
|
850
|
+
action: "seed project-owned VCM file"
|
|
851
|
+
});
|
|
852
|
+
}
|
|
823
853
|
async function removeLegacyFlatSkillFiles({ projectRoot, dryRun, operations }) {
|
|
824
854
|
const wholeFilesByPath = new Map(WHOLE_FILES.map((file) => [file.path, file]));
|
|
825
855
|
for (const legacy of LEGACY_FLAT_SKILL_FILES) {
|
|
@@ -1014,10 +1014,12 @@ async function readArchitectureBriefError(fs, taskRepoRoot) {
|
|
|
1014
1014
|
const content = await fs.readText(absolutePath);
|
|
1015
1015
|
const check = checkMarkdownArtifact("architecture-brief", relativePath, content);
|
|
1016
1016
|
if (check.status !== "ok") {
|
|
1017
|
-
return `${relativePath} is incomplete
|
|
1017
|
+
return `${relativePath} is incomplete and cannot start architecture-plan review. ${formatArtifactCheckFailure(check)}`;
|
|
1018
1018
|
}
|
|
1019
|
-
|
|
1020
|
-
|
|
1019
|
+
const status = /^\s*Architecture Brief Status\s*:\s*(.+?)\s*$/im.exec(content)?.[1]?.trim();
|
|
1020
|
+
if (status?.toLowerCase() !== "confirmed") {
|
|
1021
|
+
return `${relativePath} is not confirmed and cannot start architecture-plan review. `
|
|
1022
|
+
+ `Architecture Brief Status must be exactly "confirmed"; found ${renderFoundValue(status)}.`;
|
|
1021
1023
|
}
|
|
1022
1024
|
return undefined;
|
|
1023
1025
|
}
|
|
@@ -1032,12 +1034,8 @@ async function readValidationReportError(fs, taskRepoRoot) {
|
|
|
1032
1034
|
if (check.status === "ok") {
|
|
1033
1035
|
return undefined;
|
|
1034
1036
|
}
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
check.invalidFields.length > 0 ? check.invalidFields.join(" ") : "",
|
|
1038
|
-
check.hasPlaceholder ? "contains placeholders" : ""
|
|
1039
|
-
].filter(Boolean).join("; ");
|
|
1040
|
-
return `${relativePath} is incomplete and cannot start validation-adequacy review.${details ? ` ${details}` : ""}`;
|
|
1037
|
+
return `${relativePath} is incomplete and cannot start validation-adequacy review. `
|
|
1038
|
+
+ formatValidationArtifactFailure(check, content);
|
|
1041
1039
|
}
|
|
1042
1040
|
async function readArchitectureEvidenceError(fs, taskRepoRoot) {
|
|
1043
1041
|
const relativePath = ".ai/vcm/handoffs/architecture-evidence.md";
|
|
@@ -1049,8 +1047,10 @@ async function readArchitectureEvidenceError(fs, taskRepoRoot) {
|
|
|
1049
1047
|
if (content.trim().length === 0) {
|
|
1050
1048
|
return `${relativePath} is empty. Complete architecture evidence before requesting architecture-plan review.`;
|
|
1051
1049
|
}
|
|
1052
|
-
|
|
1053
|
-
|
|
1050
|
+
const status = /^\s*Architecture Evidence Status\s*:\s*(.+?)\s*$/im.exec(content)?.[1]?.trim();
|
|
1051
|
+
if (status?.toLowerCase() !== "complete") {
|
|
1052
|
+
return `${relativePath} is incomplete and cannot start architecture-plan review. `
|
|
1053
|
+
+ `Architecture Evidence Status must be exactly "complete"; found ${renderFoundValue(status)}.`;
|
|
1054
1054
|
}
|
|
1055
1055
|
return undefined;
|
|
1056
1056
|
}
|
|
@@ -1260,18 +1260,35 @@ async function validateValidationApprovalInput(fs, taskRepoRoot) {
|
|
|
1260
1260
|
if (check.status === "ok") {
|
|
1261
1261
|
return;
|
|
1262
1262
|
}
|
|
1263
|
-
const details = [
|
|
1264
|
-
`status=${check.status}`,
|
|
1265
|
-
check.missingHeadings.length > 0 ? `missing headings: ${check.missingHeadings.join(", ")}` : "",
|
|
1266
|
-
check.invalidFields.length > 0 ? check.invalidFields.join(" ") : "",
|
|
1267
|
-
check.hasPlaceholder ? "contains placeholders" : ""
|
|
1268
|
-
].filter(Boolean).join("; ");
|
|
1269
1263
|
throw new VcmError({
|
|
1270
1264
|
code: "GATE_REVIEW_VALIDATION_INPUT_INCOMPLETE",
|
|
1271
|
-
message: `Validation-adequacy cannot approve incomplete Tester evidence in ${relativePath}.
|
|
1265
|
+
message: `Validation-adequacy cannot approve incomplete Tester evidence in ${relativePath}. `
|
|
1266
|
+
+ formatValidationArtifactFailure(check, content),
|
|
1272
1267
|
statusCode: 500
|
|
1273
1268
|
});
|
|
1274
1269
|
}
|
|
1270
|
+
function formatValidationArtifactFailure(check, content) {
|
|
1271
|
+
return /^\s*Test Result\s*:\s*incomplete\s*$/im.test(content ?? "")
|
|
1272
|
+
? 'Test Result must be exactly one of "pass|fail"; found "incomplete".'
|
|
1273
|
+
: formatArtifactCheckFailure(check);
|
|
1274
|
+
}
|
|
1275
|
+
function formatArtifactCheckFailure(check) {
|
|
1276
|
+
const details = [
|
|
1277
|
+
check.status === "missing" ? "Artifact is missing." : "",
|
|
1278
|
+
check.status === "empty" ? "Artifact is empty." : "",
|
|
1279
|
+
check.missingHeadings.length > 0
|
|
1280
|
+
? `Missing headings: ${check.missingHeadings.join(", ")}.`
|
|
1281
|
+
: "",
|
|
1282
|
+
...check.invalidFields,
|
|
1283
|
+
check.hasPlaceholder ? "Replace every standalone TBD, Not run yet, or draft-status placeholder." : ""
|
|
1284
|
+
].filter(Boolean);
|
|
1285
|
+
return details.length > 0
|
|
1286
|
+
? details.join(" ")
|
|
1287
|
+
: "Artifact is not in a gate-ready terminal state.";
|
|
1288
|
+
}
|
|
1289
|
+
function renderFoundValue(value) {
|
|
1290
|
+
return value && value.trim().length > 0 ? JSON.stringify(value.trim()) : "<missing>";
|
|
1291
|
+
}
|
|
1275
1292
|
function validateRequestChangeFindings(findings) {
|
|
1276
1293
|
if (findings.length === 0) {
|
|
1277
1294
|
throw new VcmError({
|
|
@@ -1,7 +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_RESULTS, renderArtifactOptions } from "../../shared/validation/artifact-contract.js";
|
|
1
2
|
export function renderArchitectureBriefTemplate(taskSlug) {
|
|
2
3
|
return `# Architecture Brief: ${taskSlug}
|
|
3
4
|
|
|
4
|
-
Architecture Brief Status:
|
|
5
|
+
Architecture Brief Status: ${renderArtifactOptions(ARCHITECTURE_BRIEF_STATUSES)}
|
|
5
6
|
|
|
6
7
|
## Accepted Outcome
|
|
7
8
|
|
|
@@ -17,7 +18,7 @@ TBD
|
|
|
17
18
|
|
|
18
19
|
## Unresolved User Decisions
|
|
19
20
|
|
|
20
|
-
|
|
21
|
+
${STRICT_NONE_VALUE}
|
|
21
22
|
|
|
22
23
|
## User Confirmation
|
|
23
24
|
|
|
@@ -27,7 +28,7 @@ TBD
|
|
|
27
28
|
export function renderArchitecturePlanTemplate(taskSlug) {
|
|
28
29
|
return `# Architecture Plan: ${taskSlug}
|
|
29
30
|
|
|
30
|
-
Planning Result:
|
|
31
|
+
Planning Result: ${renderArtifactOptions(ARCHITECTURE_PLAN_RESULTS)}
|
|
31
32
|
|
|
32
33
|
## Accepted Scope
|
|
33
34
|
|
|
@@ -100,10 +101,13 @@ TBD
|
|
|
100
101
|
Task-specific context and coder guidance go here, not in source-code comments.
|
|
101
102
|
Source-code comments should only describe durable behavior, contracts, invariants,
|
|
102
103
|
error boundaries, or non-obvious logic that should remain useful after this task.
|
|
104
|
+
Use an ID matching \`[A-Z]{2,6}-[0-9]{1,4}\`, choose exactly one Action
|
|
105
|
+
(\`create\`, \`change\`, or \`delete\`), put the repo-relative File path in
|
|
106
|
+
backticks, and enumerate every implementation item explicitly.
|
|
103
107
|
|
|
104
108
|
| ID | Action | File | Symbol Or Site | Coder Work | Allowed Implementation Freedom | Behavior / Contract Proof Point |
|
|
105
109
|
| --- | --- | --- | --- | --- | --- | --- |
|
|
106
|
-
|
|
|
110
|
+
| <ID> | <create|change|delete> | \`<repo-relative-file>\` | TBD | TBD | TBD | TBD |
|
|
107
111
|
|
|
108
112
|
## Scaffold Build Evidence
|
|
109
113
|
|
|
@@ -143,7 +147,7 @@ At task close, promote still-relevant confirmed issues to \`docs/known-issues.md
|
|
|
143
147
|
export function renderTestReportTemplate(taskSlug) {
|
|
144
148
|
return `# Test Report: ${taskSlug}
|
|
145
149
|
|
|
146
|
-
Test Result:
|
|
150
|
+
Test Result: ${renderArtifactOptions(TEST_RESULTS)}
|
|
147
151
|
|
|
148
152
|
## Evidence Reviewed
|
|
149
153
|
|
|
@@ -165,11 +169,11 @@ TBD
|
|
|
165
169
|
|
|
166
170
|
### Remaining Validation
|
|
167
171
|
|
|
168
|
-
|
|
172
|
+
${STRICT_NONE_VALUE}
|
|
169
173
|
|
|
170
174
|
## L3 Coverage
|
|
171
175
|
|
|
172
|
-
L3 Required:
|
|
176
|
+
L3 Required: ${renderArtifactOptions(L3_REQUIRED_VALUES)}
|
|
173
177
|
|
|
174
178
|
### Trigger Assessment
|
|
175
179
|
|
|
@@ -179,7 +183,7 @@ TBD
|
|
|
179
183
|
|
|
180
184
|
| Flow | Trigger | Case ID | Test File | Entry Point | Final Observable Result | Action | Result |
|
|
181
185
|
| --- | --- | --- | --- | --- | --- | --- | --- |
|
|
182
|
-
| TBD | TBD | TBD | TBD | TBD | TBD |
|
|
186
|
+
| TBD | TBD | TBD | TBD | TBD | TBD | <${renderArtifactOptions(L3_ACTIONS)}> | TBD |
|
|
183
187
|
|
|
184
188
|
### L3 Commands And Evidence
|
|
185
189
|
|
|
@@ -199,27 +203,27 @@ TBD
|
|
|
199
203
|
|
|
200
204
|
## Failed Expectations
|
|
201
205
|
|
|
202
|
-
|
|
206
|
+
${STRICT_NONE_VALUE}
|
|
203
207
|
|
|
204
208
|
## Reproduction Steps
|
|
205
209
|
|
|
206
|
-
|
|
210
|
+
${STRICT_NONE_VALUE}
|
|
207
211
|
|
|
208
212
|
## Skipped Checks With Reasons
|
|
209
213
|
|
|
210
|
-
|
|
214
|
+
${STRICT_NONE_VALUE}
|
|
211
215
|
|
|
212
216
|
## Coverage Gaps
|
|
213
217
|
|
|
214
|
-
|
|
218
|
+
${STRICT_NONE_VALUE}
|
|
215
219
|
|
|
216
220
|
## Blocking Validation Issues
|
|
217
221
|
|
|
218
|
-
|
|
222
|
+
${STRICT_NONE_VALUE}
|
|
219
223
|
|
|
220
224
|
## User Approval Evidence
|
|
221
225
|
|
|
222
|
-
|
|
226
|
+
${STRICT_NONE_VALUE}
|
|
223
227
|
`;
|
|
224
228
|
}
|
|
225
229
|
export function renderCoderCompletionTemplate(taskSlug) {
|
|
@@ -348,7 +352,7 @@ TBD
|
|
|
348
352
|
|
|
349
353
|
## Decision
|
|
350
354
|
|
|
351
|
-
|
|
355
|
+
${renderArtifactOptions(DOCS_SYNC_DECISIONS)}
|
|
352
356
|
`;
|
|
353
357
|
}
|
|
354
358
|
export function renderFinalAcceptanceTemplate(taskSlug) {
|
|
@@ -356,7 +360,7 @@ export function renderFinalAcceptanceTemplate(taskSlug) {
|
|
|
356
360
|
|
|
357
361
|
## Decision
|
|
358
362
|
|
|
359
|
-
|
|
363
|
+
${renderArtifactOptions(FINAL_ACCEPTANCE_DECISIONS)}
|
|
360
364
|
|
|
361
365
|
## Evidence Reviewed
|
|
362
366
|
|
|
@@ -78,7 +78,7 @@ ${renderRoleMemoryRules("architect")}
|
|
|
78
78
|
- \`Architecture Decision\`: use the required Changed Behavior Flow, Ownership, Data Flow, Lifecycle, Boundaries, Invariants, Failure Model, and Decision Rationale subsections. Describe why the design fits verified current code.
|
|
79
79
|
- \`Module/File Plan\`: list each affected module, changed or created file, file responsibility, why it is in scope, expected change, dependency direction, user-visible behavior change, durable comment needs, and every non-private callable surface intended for use outside its file. For every ledger item that consumes or sources cross-module data, name the module and symbol that owns or produces the data, trace the source-to-consumer path, and identify every field, parameter, accessor, trait method, command field, dependency, or other cross-file surface required by that path.
|
|
80
80
|
- \`Public Surface Impact\`: state changed APIs, routes, commands, events, exports, storage formats, configuration, UI behavior, visibility changes, side effects, error boundaries, expected callers, or explicitly state none.
|
|
81
|
-
- \`Scaffold Manifest\`: an item ledger — one entry per implementation item. An item is one
|
|
81
|
+
- \`Scaffold Manifest\`: an item ledger — one entry per implementation item. Use columns in the exact order \`ID | Action | File | ...\`; use an ID matching \`AA-1\` through \`AAAAAA-9999\`, an Action of exactly \`create\`, \`change\`, or \`delete\`, and a backticked repo-relative File path. An item is one created body or surface, one required change site — one contiguous edit region inside an existing body or surface — or one deletion of a body, site, or file. An item not in the ledger is not in the plan; coder must not implement it.
|
|
82
82
|
- Each ledger entry carries, in this column order: a unique stable ID such as \`SCF-001\`, action, exact file path, symbol or site, coder work, allowed implementation freedom, and a behavior/contract proof point. Per-file evidence, why-in-scope, and durable-comment needs live in the Module/File Plan, not in the ledger. Open-ended coverage language ("as work proceeds", "replicate", "etc.", "and others") is forbidden anywhere in the ledger.
|
|
83
83
|
- IDs and markers correspond one to one: every \`create\`, \`change\`, and \`delete\` entry has exactly one \`VCM:CODE <ID>\` marker pre-placed at its declared file and site; a \`delete\` marker sits on the code to be removed and leaves with it.
|
|
84
84
|
- The Scaffold Manifest is complete only when the ledger ID set and the tree's \`VCM:CODE\` ID set are equal, each ID appears exactly once on each side, and each marker sits in its declared file (\`.ai/tools/check-scaffold-ledger\` automates the check). Any mismatch means the plan is not complete.
|
|
@@ -46,7 +46,18 @@ ${renderRoleMemoryRules("coder")}
|
|
|
46
46
|
- Use workers when the task has at least 20 \`VCM:CODE\` markers and the marker distribution can form at least two worker-sized groups.
|
|
47
47
|
- Under a complete scaffold, marker implementations are order-independent — signatures, types, and cross-item contracts are frozen by the scaffold — so never serialize worker-sized groups for presumed implementation-order dependencies. When a group's module-scoped checks need peers that are still unimplemented, narrow that worker's assigned validation scope instead of serializing.
|
|
48
48
|
- An item counts as blocked only when a genuine implementation attempt has produced objective compile/check evidence already reported under the failure rules; prediction never blocks an item. A blocked marker item never exempts the remaining markers from worker dispatch.
|
|
49
|
-
- Before invoking workers, count \`VCM:CODE\` markers by module and create one runtime state file per worker under \`.ai/vcm/coder-workers/tasks/<worker-id>.json\` with
|
|
49
|
+
- Before invoking workers, count \`VCM:CODE\` markers by module and create one runtime state file per worker under \`.ai/vcm/coder-workers/tasks/<worker-id>.json\` with exactly this initial shape:
|
|
50
|
+
|
|
51
|
+
\`\`\`json
|
|
52
|
+
{
|
|
53
|
+
"workerId": "<worker-id>",
|
|
54
|
+
"status": "running",
|
|
55
|
+
"reportPath": ".ai/vcm/coder-workers/reports/<worker-id>.md",
|
|
56
|
+
"handled": false
|
|
57
|
+
}
|
|
58
|
+
\`\`\`
|
|
59
|
+
|
|
60
|
+
- After a worker completes, its state must retain those fields, set \`status\` to \`completed\`, and add the exact \`commitHash\` from its report. Only Coder changes \`handled\` to \`true\` after inspecting that report and commit.
|
|
50
61
|
- Create one worker task for each module with more than 10 \`VCM:CODE\` markers.
|
|
51
62
|
- Group modules with 10 or fewer \`VCM:CODE\` markers into one small-modules worker when their combined marker count is more than 10.
|
|
52
63
|
- If the combined small-module marker count is 10 or fewer, Coder handles those modules directly after worker results return.
|
|
@@ -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.
|
|
@@ -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
|
|
|
@@ -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;
|