@osovv/vv-opencode 0.21.0 → 0.24.1
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 +46 -10
- package/dist/index.d.ts +2 -0
- package/dist/index.js +9 -5
- package/dist/index.js.map +1 -1
- package/dist/lib/managed-agents.d.ts +3 -3
- package/dist/lib/managed-agents.js +14 -13
- package/dist/lib/managed-agents.js.map +1 -1
- package/dist/lib/model-roles.d.ts +3 -3
- package/dist/lib/model-roles.js +5 -4
- package/dist/lib/model-roles.js.map +1 -1
- package/dist/lib/opencode.js +100 -5
- package/dist/lib/opencode.js.map +1 -1
- package/dist/lib/vvoc-config.js +5 -4
- package/dist/lib/vvoc-config.js.map +1 -1
- package/dist/plugins/hashline-edit/autocorrect-replacement-lines.d.ts +1 -0
- package/dist/plugins/hashline-edit/autocorrect-replacement-lines.js +184 -0
- package/dist/plugins/hashline-edit/autocorrect-replacement-lines.js.map +1 -0
- package/dist/plugins/hashline-edit/constants.d.ts +4 -0
- package/dist/plugins/hashline-edit/constants.js +26 -0
- package/dist/plugins/hashline-edit/constants.js.map +1 -0
- package/dist/plugins/hashline-edit/edit-operation-primitives.d.ts +10 -0
- package/dist/plugins/hashline-edit/edit-operation-primitives.js +114 -0
- package/dist/plugins/hashline-edit/edit-operation-primitives.js.map +1 -0
- package/dist/plugins/hashline-edit/edit-operations.d.ts +7 -0
- package/dist/plugins/hashline-edit/edit-operations.js +168 -0
- package/dist/plugins/hashline-edit/edit-operations.js.map +1 -0
- package/dist/plugins/hashline-edit/edit-text-normalization.d.ts +6 -0
- package/dist/plugins/hashline-edit/edit-text-normalization.js +123 -0
- package/dist/plugins/hashline-edit/edit-text-normalization.js.map +1 -0
- package/dist/plugins/hashline-edit/file-text-canonicalization.d.ts +7 -0
- package/dist/plugins/hashline-edit/file-text-canonicalization.js +52 -0
- package/dist/plugins/hashline-edit/file-text-canonicalization.js.map +1 -0
- package/dist/plugins/hashline-edit/hash-computation.d.ts +3 -0
- package/dist/plugins/hashline-edit/hash-computation.js +34 -0
- package/dist/plugins/hashline-edit/hash-computation.js.map +1 -0
- package/dist/plugins/hashline-edit/index.d.ts +2 -0
- package/dist/plugins/hashline-edit/index.js +246 -0
- package/dist/plugins/hashline-edit/index.js.map +1 -0
- package/dist/plugins/hashline-edit/normalize-edits.d.ts +10 -0
- package/dist/plugins/hashline-edit/normalize-edits.js +82 -0
- package/dist/plugins/hashline-edit/normalize-edits.js.map +1 -0
- package/dist/plugins/hashline-edit/tool-description.d.ts +1 -0
- package/dist/plugins/hashline-edit/tool-description.js +35 -0
- package/dist/plugins/hashline-edit/tool-description.js.map +1 -0
- package/dist/plugins/hashline-edit/types.d.ts +17 -0
- package/dist/plugins/hashline-edit/types.js +19 -0
- package/dist/plugins/hashline-edit/types.js.map +1 -0
- package/dist/plugins/hashline-edit/validation.d.ts +20 -0
- package/dist/plugins/hashline-edit/validation.js +160 -0
- package/dist/plugins/hashline-edit/validation.js.map +1 -0
- package/dist/plugins/system-context-injection/index.js +65 -2
- package/dist/plugins/system-context-injection/index.js.map +1 -1
- package/dist/plugins/workflow/index.d.ts +2 -0
- package/dist/plugins/workflow/index.js +411 -0
- package/dist/plugins/workflow/index.js.map +1 -0
- package/dist/plugins/workflow/protocol.d.ts +33 -0
- package/dist/plugins/workflow/protocol.js +188 -0
- package/dist/plugins/workflow/protocol.js.map +1 -0
- package/dist/plugins/workflow/state.d.ts +79 -0
- package/dist/plugins/workflow/state.js +307 -0
- package/dist/plugins/workflow/state.js.map +1 -0
- package/dist/plugins/workflow/system-instruction.md +14 -0
- package/dist/plugins/workflow/tooling.d.ts +26 -0
- package/dist/plugins/workflow/tooling.js +161 -0
- package/dist/plugins/workflow/tooling.js.map +1 -0
- package/dist/plugins/workflow/transitions.d.ts +7 -0
- package/dist/plugins/workflow/transitions.js +102 -0
- package/dist/plugins/workflow/transitions.js.map +1 -0
- package/package.json +11 -3
- package/schemas/vvoc/v1.json +1 -1
- package/schemas/vvoc/v2.json +1 -1
- package/schemas/vvoc/v3.json +1 -1
- package/templates/agents/{code-reviewer.md → vv-code-reviewer.md} +12 -4
- package/templates/agents/{implementer.md → vv-implementer.md} +14 -8
- package/templates/agents/{spec-reviewer.md → vv-spec-reviewer.md} +13 -5
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
// FILE: src/plugins/workflow/protocol.ts
|
|
2
|
+
// VERSION: 0.1.2
|
|
3
|
+
// START_MODULE_CONTRACT
|
|
4
|
+
// PURPOSE: Define the tracked subagent result protocol, strict top-block parsing rules, and work-item header extraction.
|
|
5
|
+
// SCOPE: Tracked agent/status constants, status validation per agent, strict result-block parsing, protocol error reporting, and VVOC_WORK_ITEM_ID header parsing.
|
|
6
|
+
// DEPENDS: [none]
|
|
7
|
+
// LINKS: [M-WORKFLOW-PROTOCOL]
|
|
8
|
+
// ROLE: RUNTIME
|
|
9
|
+
// MAP_MODE: EXPORTS
|
|
10
|
+
// END_MODULE_CONTRACT
|
|
11
|
+
//
|
|
12
|
+
// START_MODULE_MAP
|
|
13
|
+
// TrackedAgentName - Tracked subagent names used by workflow enforcement.
|
|
14
|
+
// VVocStatus - Union of all supported protocol statuses.
|
|
15
|
+
// ParsedResultBlock - Parsed strict top-block fields from tracked subagent output.
|
|
16
|
+
// ProtocolErrorCode - Stable machine-readable protocol error codes.
|
|
17
|
+
// ProtocolError - Structured protocol error payload.
|
|
18
|
+
// ProtocolResult - Deterministic success/failure wrapper for parsing and validation operations.
|
|
19
|
+
// TRACKED_SUBAGENT_NAMES - Ordered tracked subagent names.
|
|
20
|
+
// ALLOWED_STATUSES - Allowed VVOC_STATUS values per tracked subagent.
|
|
21
|
+
// parseResultBlock - Parses strict top-block protocol fields from tracked subagent output.
|
|
22
|
+
// validateStatusForAgent - Validates VVOC_STATUS against tracked agent allowances.
|
|
23
|
+
// parseWorkItemHeader - Extracts and validates the top-line VVOC_WORK_ITEM_ID prompt header.
|
|
24
|
+
// END_MODULE_MAP
|
|
25
|
+
//
|
|
26
|
+
// START_CHANGE_SUMMARY
|
|
27
|
+
// LAST_CHANGE: [v0.1.2 - Rejected duplicate strict top-block protocol fields to keep parse outcomes deterministic.]
|
|
28
|
+
// LAST_CHANGE: [v0.1.1 - Enforced case-sensitive status validation and strict top-block line validation for protocol-only fields.]
|
|
29
|
+
// LAST_CHANGE: [v0.1.0 - Added workflow protocol grammar, strict top-block parser, per-agent status validation, and prompt-header extraction.]
|
|
30
|
+
// END_CHANGE_SUMMARY
|
|
31
|
+
export const TRACKED_SUBAGENT_NAMES = [
|
|
32
|
+
"vv-implementer",
|
|
33
|
+
"vv-spec-reviewer",
|
|
34
|
+
"vv-code-reviewer",
|
|
35
|
+
];
|
|
36
|
+
export const ALLOWED_STATUSES = {
|
|
37
|
+
"vv-implementer": ["DONE", "DONE_WITH_CONCERNS", "NEEDS_CONTEXT", "BLOCKED"],
|
|
38
|
+
"vv-spec-reviewer": ["PASS", "FAIL", "NEEDS_CONTEXT"],
|
|
39
|
+
"vv-code-reviewer": ["PASS", "FAIL", "NEEDS_CONTEXT"],
|
|
40
|
+
};
|
|
41
|
+
const WORK_ITEM_HEADER_RE = /^VVOC_WORK_ITEM_ID:\s*(wi-\d+)\s*$/;
|
|
42
|
+
function createProtocolError(code, message) {
|
|
43
|
+
return {
|
|
44
|
+
ok: false,
|
|
45
|
+
error: {
|
|
46
|
+
code,
|
|
47
|
+
message,
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function splitTopBlock(text) {
|
|
52
|
+
const lines = text.replace(/\r\n/g, "\n").split("\n");
|
|
53
|
+
const firstMeaningfulIndex = lines.findIndex((line) => line.trim().length > 0);
|
|
54
|
+
if (firstMeaningfulIndex < 0)
|
|
55
|
+
return [];
|
|
56
|
+
const block = [];
|
|
57
|
+
for (let index = firstMeaningfulIndex; index < lines.length; index += 1) {
|
|
58
|
+
const line = lines[index] ?? "";
|
|
59
|
+
if (line.trim().length === 0) {
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
block.push(line.trim());
|
|
63
|
+
}
|
|
64
|
+
return block;
|
|
65
|
+
}
|
|
66
|
+
function readTopBlockField(topBlockLines, field) {
|
|
67
|
+
const fieldPattern = new RegExp(`^${field}\\s*:\\s*(.+)$`);
|
|
68
|
+
for (const line of topBlockLines) {
|
|
69
|
+
const match = fieldPattern.exec(line);
|
|
70
|
+
if (!match) {
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
const value = match[1]?.trim() ?? "";
|
|
74
|
+
return value || undefined;
|
|
75
|
+
}
|
|
76
|
+
return undefined;
|
|
77
|
+
}
|
|
78
|
+
function validateTopBlockLines(topBlockLines) {
|
|
79
|
+
const allowedFields = new Set(["VVOC_WORK_ITEM_ID", "VVOC_STATUS", "VVOC_ROUTE"]);
|
|
80
|
+
const seenFields = new Set();
|
|
81
|
+
for (const line of topBlockLines) {
|
|
82
|
+
const match = /^([A-Z_]+)\s*:\s*(.+)$/.exec(line);
|
|
83
|
+
if (!match) {
|
|
84
|
+
return createProtocolError("UNEXPECTED_TOP_BLOCK_LINE", `UNEXPECTED_TOP_BLOCK_LINE: strict top block contains a non-protocol line \`${line}\``);
|
|
85
|
+
}
|
|
86
|
+
const field = match[1] ?? "";
|
|
87
|
+
if (!allowedFields.has(field)) {
|
|
88
|
+
return createProtocolError("UNEXPECTED_TOP_BLOCK_LINE", `UNEXPECTED_TOP_BLOCK_LINE: strict top block field \`${field}\` is not recognized`);
|
|
89
|
+
}
|
|
90
|
+
if (seenFields.has(field)) {
|
|
91
|
+
return createProtocolError("DUPLICATE_TOP_BLOCK_FIELD", `DUPLICATE_TOP_BLOCK_FIELD: strict top block repeats field \`${field}\``);
|
|
92
|
+
}
|
|
93
|
+
seenFields.add(field);
|
|
94
|
+
}
|
|
95
|
+
return { ok: true, value: true };
|
|
96
|
+
}
|
|
97
|
+
// START_CONTRACT: validateStatusForAgent
|
|
98
|
+
// PURPOSE: Validate that the provided VVOC_STATUS value is allowed for a tracked subagent.
|
|
99
|
+
// INPUTS: { agent: TrackedAgentName - tracked subagent name, status: string - candidate status value }
|
|
100
|
+
// OUTPUTS: { ProtocolResult<VVocStatus> - normalized status success or protocol validation error }
|
|
101
|
+
// SIDE_EFFECTS: [none]
|
|
102
|
+
// LINKS: [M-WORKFLOW-PROTOCOL]
|
|
103
|
+
// END_CONTRACT: validateStatusForAgent
|
|
104
|
+
export function validateStatusForAgent(agent, status) {
|
|
105
|
+
const normalized = status.trim();
|
|
106
|
+
const allowed = ALLOWED_STATUSES[agent];
|
|
107
|
+
const allKnownStatuses = new Set(Object.values(ALLOWED_STATUSES).flatMap((entries) => [...entries]));
|
|
108
|
+
if (!allKnownStatuses.has(normalized)) {
|
|
109
|
+
return createProtocolError("UNKNOWN_STATUS", `UNKNOWN_STATUS: ${normalized || "<empty>"} is not a recognized VVOC_STATUS`);
|
|
110
|
+
}
|
|
111
|
+
if (!allowed.some((entry) => entry === normalized)) {
|
|
112
|
+
return createProtocolError("STATUS_NOT_ALLOWED", `STATUS_NOT_ALLOWED: ${normalized} is not allowed for ${agent}`);
|
|
113
|
+
}
|
|
114
|
+
return {
|
|
115
|
+
ok: true,
|
|
116
|
+
value: normalized,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
// START_CONTRACT: parseResultBlock
|
|
120
|
+
// PURPOSE: Parse strict top-block workflow protocol fields from tracked subagent output.
|
|
121
|
+
// INPUTS: { agent: TrackedAgentName - tracked subagent source, output: string - subagent textual output, expectedWorkItemId?: string - optional ID to match against parsed output }
|
|
122
|
+
// OUTPUTS: { ProtocolResult<ParsedResultBlock> - parsed protocol fields or protocol error }
|
|
123
|
+
// SIDE_EFFECTS: [none]
|
|
124
|
+
// LINKS: [M-WORKFLOW-PROTOCOL]
|
|
125
|
+
// END_CONTRACT: parseResultBlock
|
|
126
|
+
// START_BLOCK_PARSE_RESULT
|
|
127
|
+
export function parseResultBlock(options) {
|
|
128
|
+
const topBlockLines = splitTopBlock(options.output);
|
|
129
|
+
const topBlockValidation = validateTopBlockLines(topBlockLines);
|
|
130
|
+
if (!topBlockValidation.ok) {
|
|
131
|
+
return topBlockValidation;
|
|
132
|
+
}
|
|
133
|
+
const workItemId = readTopBlockField(topBlockLines, "VVOC_WORK_ITEM_ID");
|
|
134
|
+
if (!workItemId) {
|
|
135
|
+
return createProtocolError("MISSING_WORK_ITEM_ID", "MISSING_WORK_ITEM_ID: strict top block must include VVOC_WORK_ITEM_ID");
|
|
136
|
+
}
|
|
137
|
+
if (!/^wi-\d+$/.test(workItemId)) {
|
|
138
|
+
return createProtocolError("MISSING_WORK_ITEM_ID", `MISSING_WORK_ITEM_ID: invalid work item id format ${workItemId}`);
|
|
139
|
+
}
|
|
140
|
+
if (options.expectedWorkItemId && options.expectedWorkItemId !== workItemId) {
|
|
141
|
+
return createProtocolError("WORK_ITEM_MISMATCH", `WORK_ITEM_MISMATCH: expected ${options.expectedWorkItemId} but parsed ${workItemId}`);
|
|
142
|
+
}
|
|
143
|
+
const rawStatus = readTopBlockField(topBlockLines, "VVOC_STATUS");
|
|
144
|
+
if (!rawStatus) {
|
|
145
|
+
return createProtocolError("MISSING_STATUS", "MISSING_STATUS: strict top block must include VVOC_STATUS");
|
|
146
|
+
}
|
|
147
|
+
const statusValidation = validateStatusForAgent(options.agent, rawStatus);
|
|
148
|
+
if (!statusValidation.ok) {
|
|
149
|
+
return statusValidation;
|
|
150
|
+
}
|
|
151
|
+
const route = readTopBlockField(topBlockLines, "VVOC_ROUTE");
|
|
152
|
+
if (options.agent === "vv-implementer" && !route) {
|
|
153
|
+
return createProtocolError("MISSING_ROUTE", "MISSING_ROUTE: vv-implementer output must include VVOC_ROUTE");
|
|
154
|
+
}
|
|
155
|
+
return {
|
|
156
|
+
ok: true,
|
|
157
|
+
value: {
|
|
158
|
+
agent: options.agent,
|
|
159
|
+
workItemId,
|
|
160
|
+
status: statusValidation.value,
|
|
161
|
+
...(route ? { route } : {}),
|
|
162
|
+
},
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
// END_BLOCK_PARSE_RESULT
|
|
166
|
+
// START_CONTRACT: parseWorkItemHeader
|
|
167
|
+
// PURPOSE: Extract VVOC_WORK_ITEM_ID from the first meaningful line of a tracked subagent prompt header.
|
|
168
|
+
// INPUTS: { promptText: string - full prompt text }
|
|
169
|
+
// OUTPUTS: { ProtocolResult<string> - parsed work item ID or protocol error }
|
|
170
|
+
// SIDE_EFFECTS: [none]
|
|
171
|
+
// LINKS: [M-WORKFLOW-PROTOCOL]
|
|
172
|
+
// END_CONTRACT: parseWorkItemHeader
|
|
173
|
+
export function parseWorkItemHeader(promptText) {
|
|
174
|
+
const lines = promptText.replace(/\r\n/g, "\n").split("\n");
|
|
175
|
+
const firstMeaningfulLine = lines.find((line) => line.trim().length > 0)?.trim();
|
|
176
|
+
if (!firstMeaningfulLine) {
|
|
177
|
+
return createProtocolError("MISSING_WORK_ITEM_HEADER", "MISSING_WORK_ITEM_HEADER: prompt must begin with VVOC_WORK_ITEM_ID header");
|
|
178
|
+
}
|
|
179
|
+
const match = WORK_ITEM_HEADER_RE.exec(firstMeaningfulLine);
|
|
180
|
+
if (!match) {
|
|
181
|
+
return createProtocolError("MALFORMED_WORK_ITEM_HEADER", `MALFORMED_WORK_ITEM_HEADER: first meaningful line must match \`VVOC_WORK_ITEM_ID: wi-<n>\` but received \`${firstMeaningfulLine}\``);
|
|
182
|
+
}
|
|
183
|
+
return {
|
|
184
|
+
ok: true,
|
|
185
|
+
value: match[1],
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
//# sourceMappingURL=protocol.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../../src/plugins/workflow/protocol.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,iBAAiB;AACjB,wBAAwB;AACxB,2HAA2H;AAC3H,qKAAqK;AACrK,oBAAoB;AACpB,iCAAiC;AACjC,kBAAkB;AAClB,sBAAsB;AACtB,sBAAsB;AACtB,EAAE;AACF,mBAAmB;AACnB,4EAA4E;AAC5E,2DAA2D;AAC3D,qFAAqF;AACrF,sEAAsE;AACtE,uDAAuD;AACvD,kGAAkG;AAClG,6DAA6D;AAC7D,wEAAwE;AACxE,6FAA6F;AAC7F,qFAAqF;AACrF,+FAA+F;AAC/F,iBAAiB;AACjB,EAAE;AACF,uBAAuB;AACvB,sHAAsH;AACtH,qIAAqI;AACrI,iJAAiJ;AACjJ,qBAAqB;AAErB,MAAM,CAAC,MAAM,sBAAsB,GAAG;IACpC,gBAAgB;IAChB,kBAAkB;IAClB,kBAAkB;CACV,CAAC;AAIX,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,gBAAgB,EAAE,CAAC,MAAM,EAAE,oBAAoB,EAAE,eAAe,EAAE,SAAS,CAAC;IAC5E,kBAAkB,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,eAAe,CAAC;IACrD,kBAAkB,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,eAAe,CAAC;CAC7C,CAAC;AA4CX,MAAM,mBAAmB,GAAG,oCAAoC,CAAC;AAEjE,SAAS,mBAAmB,CAAC,IAAuB,EAAE,OAAe;IACnE,OAAO;QACL,EAAE,EAAE,KAAK;QACT,KAAK,EAAE;YACL,IAAI;YACJ,OAAO;SACR;KACF,CAAC;AACJ,CAAC;AAED,SAAS,aAAa,CAAC,IAAY;IACjC,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACtD,MAAM,oBAAoB,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC/E,IAAI,oBAAoB,GAAG,CAAC;QAAE,OAAO,EAAE,CAAC;IAExC,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,IAAI,KAAK,GAAG,oBAAoB,EAAE,KAAK,GAAG,KAAK,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QACxE,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QAChC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC7B,MAAM;QACR,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IAC1B,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,iBAAiB,CAAC,aAAuB,EAAE,KAAa;IAC/D,MAAM,YAAY,GAAG,IAAI,MAAM,CAAC,IAAI,KAAK,gBAAgB,CAAC,CAAC;IAC3D,KAAK,MAAM,IAAI,IAAI,aAAa,EAAE,CAAC;QACjC,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,SAAS;QACX,CAAC;QAED,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;QACrC,OAAO,KAAK,IAAI,SAAS,CAAC;IAC5B,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,qBAAqB,CAAC,aAAuB;IACpD,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,CAAC,mBAAmB,EAAE,aAAa,EAAE,YAAY,CAAC,CAAC,CAAC;IAClF,MAAM,UAAU,GAAG,IAAI,GAAG,EAAU,CAAC;IAErC,KAAK,MAAM,IAAI,IAAI,aAAa,EAAE,CAAC;QACjC,MAAM,KAAK,GAAG,wBAAwB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClD,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,mBAAmB,CACxB,2BAA2B,EAC3B,8EAA8E,IAAI,IAAI,CACvF,CAAC;QACJ,CAAC;QAED,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAC7B,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YAC9B,OAAO,mBAAmB,CACxB,2BAA2B,EAC3B,uDAAuD,KAAK,sBAAsB,CACnF,CAAC;QACJ,CAAC;QAED,IAAI,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YAC1B,OAAO,mBAAmB,CACxB,2BAA2B,EAC3B,+DAA+D,KAAK,IAAI,CACzE,CAAC;QACJ,CAAC;QACD,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACxB,CAAC;IAED,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;AACnC,CAAC;AAED,yCAAyC;AACzC,6FAA6F;AAC7F,yGAAyG;AACzG,qGAAqG;AACrG,yBAAyB;AACzB,iCAAiC;AACjC,uCAAuC;AACvC,MAAM,UAAU,sBAAsB,CACpC,KAAuB,EACvB,MAAc;IAEd,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;IACjC,MAAM,OAAO,GAAG,gBAAgB,CAAC,KAAK,CAAsB,CAAC;IAC7D,MAAM,gBAAgB,GAAgB,IAAI,GAAG,CAC3C,MAAM,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CACnE,CAAC;IAEF,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC;QACtC,OAAO,mBAAmB,CACxB,gBAAgB,EAChB,mBAAmB,UAAU,IAAI,SAAS,kCAAkC,CAC7E,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,UAAU,CAAC,EAAE,CAAC;QACnD,OAAO,mBAAmB,CACxB,oBAAoB,EACpB,uBAAuB,UAAU,uBAAuB,KAAK,EAAE,CAChE,CAAC;IACJ,CAAC;IAED,OAAO;QACL,EAAE,EAAE,IAAI;QACR,KAAK,EAAE,UAAwB;KAChC,CAAC;AACJ,CAAC;AAED,mCAAmC;AACnC,2FAA2F;AAC3F,sLAAsL;AACtL,8FAA8F;AAC9F,yBAAyB;AACzB,iCAAiC;AACjC,iCAAiC;AACjC,2BAA2B;AAC3B,MAAM,UAAU,gBAAgB,CAAC,OAIhC;IACC,MAAM,aAAa,GAAG,aAAa,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACpD,MAAM,kBAAkB,GAAG,qBAAqB,CAAC,aAAa,CAAC,CAAC;IAChE,IAAI,CAAC,kBAAkB,CAAC,EAAE,EAAE,CAAC;QAC3B,OAAO,kBAAkB,CAAC;IAC5B,CAAC;IAED,MAAM,UAAU,GAAG,iBAAiB,CAAC,aAAa,EAAE,mBAAmB,CAAC,CAAC;IACzE,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO,mBAAmB,CACxB,sBAAsB,EACtB,uEAAuE,CACxE,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;QACjC,OAAO,mBAAmB,CACxB,sBAAsB,EACtB,qDAAqD,UAAU,EAAE,CAClE,CAAC;IACJ,CAAC;IAED,IAAI,OAAO,CAAC,kBAAkB,IAAI,OAAO,CAAC,kBAAkB,KAAK,UAAU,EAAE,CAAC;QAC5E,OAAO,mBAAmB,CACxB,oBAAoB,EACpB,gCAAgC,OAAO,CAAC,kBAAkB,eAAe,UAAU,EAAE,CACtF,CAAC;IACJ,CAAC;IAED,MAAM,SAAS,GAAG,iBAAiB,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC;IAClE,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,OAAO,mBAAmB,CACxB,gBAAgB,EAChB,2DAA2D,CAC5D,CAAC;IACJ,CAAC;IAED,MAAM,gBAAgB,GAAG,sBAAsB,CAAC,OAAO,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;IAC1E,IAAI,CAAC,gBAAgB,CAAC,EAAE,EAAE,CAAC;QACzB,OAAO,gBAAgB,CAAC;IAC1B,CAAC;IAED,MAAM,KAAK,GAAG,iBAAiB,CAAC,aAAa,EAAE,YAAY,CAAC,CAAC;IAC7D,IAAI,OAAO,CAAC,KAAK,KAAK,gBAAgB,IAAI,CAAC,KAAK,EAAE,CAAC;QACjD,OAAO,mBAAmB,CACxB,eAAe,EACf,8DAA8D,CAC/D,CAAC;IACJ,CAAC;IAED,OAAO;QACL,EAAE,EAAE,IAAI;QACR,KAAK,EAAE;YACL,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,UAAU;YACV,MAAM,EAAE,gBAAgB,CAAC,KAAK;YAC9B,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC5B;KACF,CAAC;AACJ,CAAC;AACD,yBAAyB;AAEzB,sCAAsC;AACtC,2GAA2G;AAC3G,sDAAsD;AACtD,gFAAgF;AAChF,yBAAyB;AACzB,iCAAiC;AACjC,oCAAoC;AACpC,MAAM,UAAU,mBAAmB,CAAC,UAAkB;IACpD,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC5D,MAAM,mBAAmB,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC;IAEjF,IAAI,CAAC,mBAAmB,EAAE,CAAC;QACzB,OAAO,mBAAmB,CACxB,0BAA0B,EAC1B,2EAA2E,CAC5E,CAAC;IACJ,CAAC;IAED,MAAM,KAAK,GAAG,mBAAmB,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;IAC5D,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,mBAAmB,CACxB,4BAA4B,EAC5B,6GAA6G,mBAAmB,IAAI,CACrI,CAAC;IACJ,CAAC;IAED,OAAO;QACL,EAAE,EAAE,IAAI;QACR,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;KAChB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { TrackedAgentName } from "./protocol.js";
|
|
2
|
+
export type WorkItemState = "open" | "awaiting_implementer" | "awaiting_spec_review" | "awaiting_code_review" | "needs_context" | "blocked" | "ready_to_close" | "closed";
|
|
3
|
+
export type WorkItemRecord = {
|
|
4
|
+
sessionId: string;
|
|
5
|
+
workItemId: string;
|
|
6
|
+
key: string;
|
|
7
|
+
title: string;
|
|
8
|
+
state: WorkItemState;
|
|
9
|
+
specReviewCount: number;
|
|
10
|
+
codeReviewCount: number;
|
|
11
|
+
createdAt: string;
|
|
12
|
+
updatedAt: string;
|
|
13
|
+
closedAt?: string;
|
|
14
|
+
};
|
|
15
|
+
export type OpenWorkItemResult = {
|
|
16
|
+
ok: true;
|
|
17
|
+
reused: boolean;
|
|
18
|
+
record: WorkItemRecord;
|
|
19
|
+
header: string;
|
|
20
|
+
} | {
|
|
21
|
+
ok: false;
|
|
22
|
+
errorCode: "WORK_ITEM_KEY_CONFLICT";
|
|
23
|
+
message: string;
|
|
24
|
+
existingWorkItemId: string;
|
|
25
|
+
};
|
|
26
|
+
export type CloseWorkItemResult = {
|
|
27
|
+
ok: true;
|
|
28
|
+
record: WorkItemRecord;
|
|
29
|
+
header: string;
|
|
30
|
+
} | {
|
|
31
|
+
ok: false;
|
|
32
|
+
errorCode: "WORK_ITEM_NOT_FOUND" | "WORK_ITEM_ALREADY_CLOSED";
|
|
33
|
+
message: string;
|
|
34
|
+
};
|
|
35
|
+
export type TransitionWorkItemStateResult = {
|
|
36
|
+
ok: true;
|
|
37
|
+
record: WorkItemRecord;
|
|
38
|
+
} | {
|
|
39
|
+
ok: false;
|
|
40
|
+
errorCode: "WORK_ITEM_NOT_FOUND" | "WORK_ITEM_ALREADY_CLOSED" | "INVALID_STATE_TRANSITION" | "MISSING_TRANSITION_ACTOR";
|
|
41
|
+
message: string;
|
|
42
|
+
};
|
|
43
|
+
export type WorkItemStore = {
|
|
44
|
+
openWorkItem: (input: {
|
|
45
|
+
sessionId: string;
|
|
46
|
+
key: string;
|
|
47
|
+
title: string;
|
|
48
|
+
}) => OpenWorkItemResult;
|
|
49
|
+
getWorkItem: (sessionId: string, workItemId: string) => WorkItemRecord | undefined;
|
|
50
|
+
listWorkItems: (sessionId: string, options?: {
|
|
51
|
+
includeClosed?: boolean;
|
|
52
|
+
}) => WorkItemRecord[];
|
|
53
|
+
closeWorkItem: (sessionId: string, workItemId: string) => CloseWorkItemResult;
|
|
54
|
+
transitionWorkItemState: (input: {
|
|
55
|
+
sessionId: string;
|
|
56
|
+
workItemId: string;
|
|
57
|
+
state: WorkItemState;
|
|
58
|
+
actor?: TrackedAgentName;
|
|
59
|
+
}) => TransitionWorkItemStateResult;
|
|
60
|
+
getReviewRound: (record: Pick<WorkItemRecord, "specReviewCount" | "codeReviewCount">) => number;
|
|
61
|
+
};
|
|
62
|
+
export declare function getReviewRound(record: Pick<WorkItemRecord, "specReviewCount" | "codeReviewCount">): number;
|
|
63
|
+
export declare function createWorkItemStore(): WorkItemStore;
|
|
64
|
+
export declare function openWorkItem(store: WorkItemStore, input: {
|
|
65
|
+
sessionId: string;
|
|
66
|
+
key: string;
|
|
67
|
+
title: string;
|
|
68
|
+
}): OpenWorkItemResult;
|
|
69
|
+
export declare function getWorkItem(store: WorkItemStore, sessionId: string, workItemId: string): WorkItemRecord | undefined;
|
|
70
|
+
export declare function listWorkItems(store: WorkItemStore, sessionId: string, options?: {
|
|
71
|
+
includeClosed?: boolean;
|
|
72
|
+
}): WorkItemRecord[];
|
|
73
|
+
export declare function closeWorkItem(store: WorkItemStore, sessionId: string, workItemId: string): CloseWorkItemResult;
|
|
74
|
+
export declare function transitionWorkItemState(store: WorkItemStore, input: {
|
|
75
|
+
sessionId: string;
|
|
76
|
+
workItemId: string;
|
|
77
|
+
state: WorkItemState;
|
|
78
|
+
actor?: TrackedAgentName;
|
|
79
|
+
}): TransitionWorkItemStateResult;
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
// FILE: src/plugins/workflow/state.ts
|
|
2
|
+
// VERSION: 0.1.2
|
|
3
|
+
// START_MODULE_CONTRACT
|
|
4
|
+
// PURPOSE: Manage in-memory workflow work-item state scoped by session with idempotent open semantics.
|
|
5
|
+
// SCOPE: Session-scoped work-item storage, id generation, idempotent open-by-key, state transitions, review counters, and close/list/get operations.
|
|
6
|
+
// DEPENDS: [src/plugins/workflow/protocol.ts]
|
|
7
|
+
// LINKS: [M-WORKFLOW-STATE]
|
|
8
|
+
// ROLE: RUNTIME
|
|
9
|
+
// MAP_MODE: EXPORTS
|
|
10
|
+
// END_MODULE_CONTRACT
|
|
11
|
+
//
|
|
12
|
+
// START_MODULE_MAP
|
|
13
|
+
// WorkItemState - Allowed lifecycle states for a tracked work item.
|
|
14
|
+
// WorkItemRecord - Canonical in-memory state record for a work item.
|
|
15
|
+
// WorkItemStore - Store interface exposing deterministic workflow state operations.
|
|
16
|
+
// OpenWorkItemResult - Structured open operation result with idempotency and conflict signaling.
|
|
17
|
+
// CloseWorkItemResult - Structured close operation result.
|
|
18
|
+
// TransitionWorkItemStateResult - Structured transition operation result.
|
|
19
|
+
// createWorkItemStore - Creates a new in-memory store instance.
|
|
20
|
+
// openWorkItem - Opens an item idempotently by (sessionId, key).
|
|
21
|
+
// getWorkItem - Fetches an item by session and work-item id.
|
|
22
|
+
// listWorkItems - Lists session items with optional closed inclusion.
|
|
23
|
+
// closeWorkItem - Closes an existing open item.
|
|
24
|
+
// transitionWorkItemState - Applies deterministic state transitions and increments review counters.
|
|
25
|
+
// getReviewRound - Computes review round as max(specReviewCount, codeReviewCount).
|
|
26
|
+
// END_MODULE_MAP
|
|
27
|
+
//
|
|
28
|
+
// START_CHANGE_SUMMARY
|
|
29
|
+
// LAST_CHANGE: [v0.1.2 - Required actor metadata for tracked transitions so reviewer counters cannot be bypassed by actor-less updates.]
|
|
30
|
+
// LAST_CHANGE: [v0.1.1 - Enforced deterministic transition invariants with sticky hard-stop states and optional closed-item listing support.]
|
|
31
|
+
// LAST_CHANGE: [v0.1.0 - Added session-scoped in-memory workflow state store with idempotent open, transitions, close/list/get, and review-round helpers.]
|
|
32
|
+
// END_CHANGE_SUMMARY
|
|
33
|
+
function createRecordLookupKey(sessionId, workItemId) {
|
|
34
|
+
return `${sessionId}::${workItemId}`;
|
|
35
|
+
}
|
|
36
|
+
function cloneRecord(record) {
|
|
37
|
+
return {
|
|
38
|
+
...record,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function createHeader(workItemId) {
|
|
42
|
+
return `VVOC_WORK_ITEM_ID: ${workItemId}`;
|
|
43
|
+
}
|
|
44
|
+
function toIsoNow() {
|
|
45
|
+
return new Date().toISOString();
|
|
46
|
+
}
|
|
47
|
+
function openWorkItemInStore(store, input) {
|
|
48
|
+
const sessionIndex = store.keyIndexBySession.get(input.sessionId) ??
|
|
49
|
+
(() => {
|
|
50
|
+
const index = new Map();
|
|
51
|
+
store.keyIndexBySession.set(input.sessionId, index);
|
|
52
|
+
return index;
|
|
53
|
+
})();
|
|
54
|
+
const existingId = sessionIndex.get(input.key);
|
|
55
|
+
if (existingId) {
|
|
56
|
+
const existing = store.records.get(createRecordLookupKey(input.sessionId, existingId));
|
|
57
|
+
if (!existing) {
|
|
58
|
+
sessionIndex.delete(input.key);
|
|
59
|
+
}
|
|
60
|
+
else if (existing.title !== input.title) {
|
|
61
|
+
return {
|
|
62
|
+
ok: false,
|
|
63
|
+
errorCode: "WORK_ITEM_KEY_CONFLICT",
|
|
64
|
+
message: `WORK_ITEM_KEY_CONFLICT: key ${input.key} is already associated with a different title`,
|
|
65
|
+
existingWorkItemId: existing.workItemId,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
return {
|
|
70
|
+
ok: true,
|
|
71
|
+
reused: true,
|
|
72
|
+
record: cloneRecord(existing),
|
|
73
|
+
header: createHeader(existing.workItemId),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
const workItemId = `wi-${store.nextId}`;
|
|
78
|
+
store.nextId += 1;
|
|
79
|
+
const now = toIsoNow();
|
|
80
|
+
const record = {
|
|
81
|
+
sessionId: input.sessionId,
|
|
82
|
+
workItemId,
|
|
83
|
+
key: input.key,
|
|
84
|
+
title: input.title,
|
|
85
|
+
state: "open",
|
|
86
|
+
specReviewCount: 0,
|
|
87
|
+
codeReviewCount: 0,
|
|
88
|
+
createdAt: now,
|
|
89
|
+
updatedAt: now,
|
|
90
|
+
};
|
|
91
|
+
sessionIndex.set(input.key, workItemId);
|
|
92
|
+
store.records.set(createRecordLookupKey(input.sessionId, workItemId), record);
|
|
93
|
+
return {
|
|
94
|
+
ok: true,
|
|
95
|
+
reused: false,
|
|
96
|
+
record: cloneRecord(record),
|
|
97
|
+
header: createHeader(workItemId),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
function getWorkItemInStore(store, sessionId, workItemId) {
|
|
101
|
+
const record = store.records.get(createRecordLookupKey(sessionId, workItemId));
|
|
102
|
+
return record ? cloneRecord(record) : undefined;
|
|
103
|
+
}
|
|
104
|
+
function isTransitionAllowed(fromState, toState) {
|
|
105
|
+
const allowed = {
|
|
106
|
+
open: ["awaiting_spec_review", "needs_context", "blocked"],
|
|
107
|
+
awaiting_implementer: ["awaiting_spec_review", "needs_context", "blocked"],
|
|
108
|
+
awaiting_spec_review: ["awaiting_code_review", "awaiting_implementer", "needs_context"],
|
|
109
|
+
awaiting_code_review: ["ready_to_close", "awaiting_implementer", "needs_context"],
|
|
110
|
+
ready_to_close: [],
|
|
111
|
+
needs_context: [],
|
|
112
|
+
blocked: [],
|
|
113
|
+
closed: [],
|
|
114
|
+
};
|
|
115
|
+
return allowed[fromState].includes(toState);
|
|
116
|
+
}
|
|
117
|
+
function getAllowedActorForState(state) {
|
|
118
|
+
if (state === "open" || state === "awaiting_implementer") {
|
|
119
|
+
return "vv-implementer";
|
|
120
|
+
}
|
|
121
|
+
if (state === "awaiting_spec_review") {
|
|
122
|
+
return "vv-spec-reviewer";
|
|
123
|
+
}
|
|
124
|
+
if (state === "awaiting_code_review") {
|
|
125
|
+
return "vv-code-reviewer";
|
|
126
|
+
}
|
|
127
|
+
return undefined;
|
|
128
|
+
}
|
|
129
|
+
function listWorkItemsInStore(store, sessionId, options = {}) {
|
|
130
|
+
const includeClosed = options.includeClosed === true;
|
|
131
|
+
return [...store.records.values()]
|
|
132
|
+
.filter((record) => record.sessionId === sessionId && (includeClosed || record.state !== "closed"))
|
|
133
|
+
.sort((left, right) => {
|
|
134
|
+
const leftNumber = Number(left.workItemId.slice("wi-".length));
|
|
135
|
+
const rightNumber = Number(right.workItemId.slice("wi-".length));
|
|
136
|
+
return leftNumber - rightNumber;
|
|
137
|
+
})
|
|
138
|
+
.map((record) => cloneRecord(record));
|
|
139
|
+
}
|
|
140
|
+
function closeWorkItemInStore(store, sessionId, workItemId) {
|
|
141
|
+
const lookupKey = createRecordLookupKey(sessionId, workItemId);
|
|
142
|
+
const existing = store.records.get(lookupKey);
|
|
143
|
+
if (!existing) {
|
|
144
|
+
return {
|
|
145
|
+
ok: false,
|
|
146
|
+
errorCode: "WORK_ITEM_NOT_FOUND",
|
|
147
|
+
message: `WORK_ITEM_NOT_FOUND: no work item ${workItemId} for session ${sessionId}`,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
if (existing.state === "closed") {
|
|
151
|
+
return {
|
|
152
|
+
ok: false,
|
|
153
|
+
errorCode: "WORK_ITEM_ALREADY_CLOSED",
|
|
154
|
+
message: `WORK_ITEM_ALREADY_CLOSED: ${workItemId} is already closed`,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
const now = toIsoNow();
|
|
158
|
+
const updated = {
|
|
159
|
+
...existing,
|
|
160
|
+
state: "closed",
|
|
161
|
+
closedAt: now,
|
|
162
|
+
updatedAt: now,
|
|
163
|
+
};
|
|
164
|
+
store.records.set(lookupKey, updated);
|
|
165
|
+
return {
|
|
166
|
+
ok: true,
|
|
167
|
+
record: cloneRecord(updated),
|
|
168
|
+
header: createHeader(workItemId),
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
function transitionWorkItemStateInStore(store, input) {
|
|
172
|
+
const lookupKey = createRecordLookupKey(input.sessionId, input.workItemId);
|
|
173
|
+
const existing = store.records.get(lookupKey);
|
|
174
|
+
if (!existing) {
|
|
175
|
+
return {
|
|
176
|
+
ok: false,
|
|
177
|
+
errorCode: "WORK_ITEM_NOT_FOUND",
|
|
178
|
+
message: `WORK_ITEM_NOT_FOUND: no work item ${input.workItemId} for session ${input.sessionId}`,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
if (existing.state === "closed") {
|
|
182
|
+
return {
|
|
183
|
+
ok: false,
|
|
184
|
+
errorCode: "WORK_ITEM_ALREADY_CLOSED",
|
|
185
|
+
message: `WORK_ITEM_ALREADY_CLOSED: ${input.workItemId} is already closed`,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
const allowedActor = getAllowedActorForState(existing.state);
|
|
189
|
+
if (allowedActor && !input.actor) {
|
|
190
|
+
return {
|
|
191
|
+
ok: false,
|
|
192
|
+
errorCode: "MISSING_TRANSITION_ACTOR",
|
|
193
|
+
message: `MISSING_TRANSITION_ACTOR: state ${existing.state} requires actor metadata`,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
if (input.actor && allowedActor && input.actor !== allowedActor) {
|
|
197
|
+
return {
|
|
198
|
+
ok: false,
|
|
199
|
+
errorCode: "INVALID_STATE_TRANSITION",
|
|
200
|
+
message: `INVALID_STATE_TRANSITION: actor ${input.actor} is not allowed for state ${existing.state}`,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
if (!isTransitionAllowed(existing.state, input.state)) {
|
|
204
|
+
return {
|
|
205
|
+
ok: false,
|
|
206
|
+
errorCode: "INVALID_STATE_TRANSITION",
|
|
207
|
+
message: `INVALID_STATE_TRANSITION: cannot transition from ${existing.state} to ${input.state}`,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
const updated = {
|
|
211
|
+
...existing,
|
|
212
|
+
state: input.state,
|
|
213
|
+
updatedAt: toIsoNow(),
|
|
214
|
+
specReviewCount: input.actor === "vv-spec-reviewer" ? existing.specReviewCount + 1 : existing.specReviewCount,
|
|
215
|
+
codeReviewCount: input.actor === "vv-code-reviewer" ? existing.codeReviewCount + 1 : existing.codeReviewCount,
|
|
216
|
+
};
|
|
217
|
+
store.records.set(lookupKey, updated);
|
|
218
|
+
return {
|
|
219
|
+
ok: true,
|
|
220
|
+
record: cloneRecord(updated),
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
// START_CONTRACT: getReviewRound
|
|
224
|
+
// PURPOSE: Compute deterministic review round from reviewer counters.
|
|
225
|
+
// INPUTS: { record: Pick<WorkItemRecord, "specReviewCount" | "codeReviewCount"> - review counters }
|
|
226
|
+
// OUTPUTS: { number - max(specReviewCount, codeReviewCount) }
|
|
227
|
+
// SIDE_EFFECTS: [none]
|
|
228
|
+
// LINKS: [M-WORKFLOW-STATE]
|
|
229
|
+
// END_CONTRACT: getReviewRound
|
|
230
|
+
export function getReviewRound(record) {
|
|
231
|
+
return Math.max(record.specReviewCount, record.codeReviewCount);
|
|
232
|
+
}
|
|
233
|
+
// START_CONTRACT: createWorkItemStore
|
|
234
|
+
// PURPOSE: Create a session-scoped in-memory work-item store with deterministic operation behavior.
|
|
235
|
+
// INPUTS: { none }
|
|
236
|
+
// OUTPUTS: { WorkItemStore - operation surface for open/get/list/close/transition operations }
|
|
237
|
+
// SIDE_EFFECTS: [none]
|
|
238
|
+
// LINKS: [M-WORKFLOW-STATE]
|
|
239
|
+
// END_CONTRACT: createWorkItemStore
|
|
240
|
+
export function createWorkItemStore() {
|
|
241
|
+
const data = {
|
|
242
|
+
nextId: 1,
|
|
243
|
+
records: new Map(),
|
|
244
|
+
keyIndexBySession: new Map(),
|
|
245
|
+
};
|
|
246
|
+
return {
|
|
247
|
+
openWorkItem: (input) => openWorkItemInStore(data, input),
|
|
248
|
+
getWorkItem: (sessionId, workItemId) => getWorkItemInStore(data, sessionId, workItemId),
|
|
249
|
+
listWorkItems: (sessionId, options) => listWorkItemsInStore(data, sessionId, options),
|
|
250
|
+
closeWorkItem: (sessionId, workItemId) => closeWorkItemInStore(data, sessionId, workItemId),
|
|
251
|
+
transitionWorkItemState: (input) => transitionWorkItemStateInStore(data, input),
|
|
252
|
+
getReviewRound,
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
// START_CONTRACT: openWorkItem
|
|
256
|
+
// PURPOSE: Open a work item idempotently by (sessionId, key) while enforcing key/title consistency.
|
|
257
|
+
// INPUTS: { store: WorkItemStoreData - backing store, input: { sessionId, key, title } - open parameters }
|
|
258
|
+
// OUTPUTS: { OpenWorkItemResult - success with record/header or WORK_ITEM_KEY_CONFLICT }
|
|
259
|
+
// SIDE_EFFECTS: [Mutates in-memory work-item store]
|
|
260
|
+
// LINKS: [M-WORKFLOW-STATE]
|
|
261
|
+
// END_CONTRACT: openWorkItem
|
|
262
|
+
export function openWorkItem(store, input) {
|
|
263
|
+
return store.openWorkItem(input);
|
|
264
|
+
}
|
|
265
|
+
// START_CONTRACT: getWorkItem
|
|
266
|
+
// PURPOSE: Return a work item by session and work-item id.
|
|
267
|
+
// INPUTS: { store: WorkItemStoreData - backing store, sessionId: string - session scope, workItemId: string - work item id }
|
|
268
|
+
// OUTPUTS: { WorkItemRecord | undefined - matching record when present }
|
|
269
|
+
// SIDE_EFFECTS: [none]
|
|
270
|
+
// LINKS: [M-WORKFLOW-STATE]
|
|
271
|
+
// END_CONTRACT: getWorkItem
|
|
272
|
+
export function getWorkItem(store, sessionId, workItemId) {
|
|
273
|
+
return store.getWorkItem(sessionId, workItemId);
|
|
274
|
+
}
|
|
275
|
+
// START_CONTRACT: listWorkItems
|
|
276
|
+
// PURPOSE: List session work items with optional closed-item inclusion.
|
|
277
|
+
// INPUTS: { store: WorkItemStoreData - backing store, sessionId: string - session scope, options?: { includeClosed?: boolean } - list behavior options }
|
|
278
|
+
// OUTPUTS: { WorkItemRecord[] - records sorted by numeric work-item id }
|
|
279
|
+
// SIDE_EFFECTS: [none]
|
|
280
|
+
// LINKS: [M-WORKFLOW-STATE]
|
|
281
|
+
// END_CONTRACT: listWorkItems
|
|
282
|
+
export function listWorkItems(store, sessionId, options) {
|
|
283
|
+
return store.listWorkItems(sessionId, options);
|
|
284
|
+
}
|
|
285
|
+
// START_CONTRACT: closeWorkItem
|
|
286
|
+
// PURPOSE: Close an existing open work item and stamp closedAt.
|
|
287
|
+
// INPUTS: { store: WorkItemStoreData - backing store, sessionId: string - session scope, workItemId: string - target id }
|
|
288
|
+
// OUTPUTS: { CloseWorkItemResult - success with updated record/header or failure code }
|
|
289
|
+
// SIDE_EFFECTS: [Mutates in-memory work-item store]
|
|
290
|
+
// LINKS: [M-WORKFLOW-STATE]
|
|
291
|
+
// END_CONTRACT: closeWorkItem
|
|
292
|
+
export function closeWorkItem(store, sessionId, workItemId) {
|
|
293
|
+
return store.closeWorkItem(sessionId, workItemId);
|
|
294
|
+
}
|
|
295
|
+
// START_CONTRACT: transitionWorkItemState
|
|
296
|
+
// PURPOSE: Update work-item state deterministically and maintain reviewer counters.
|
|
297
|
+
// INPUTS: { store: WorkItemStoreData - backing store, input: { sessionId, workItemId, state, actor? } - transition payload }
|
|
298
|
+
// OUTPUTS: { TransitionWorkItemStateResult - success with updated record or failure code }
|
|
299
|
+
// SIDE_EFFECTS: [Mutates in-memory work-item store]
|
|
300
|
+
// LINKS: [M-WORKFLOW-STATE]
|
|
301
|
+
// END_CONTRACT: transitionWorkItemState
|
|
302
|
+
// START_BLOCK_TRANSITION_STATE
|
|
303
|
+
export function transitionWorkItemState(store, input) {
|
|
304
|
+
return store.transitionWorkItemState(input);
|
|
305
|
+
}
|
|
306
|
+
// END_BLOCK_TRANSITION_STATE
|
|
307
|
+
//# sourceMappingURL=state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../../src/plugins/workflow/state.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<workflow_protocol>
|
|
2
|
+
Workflow tracking is active for vv-managed review loops.
|
|
3
|
+
|
|
4
|
+
For tracked subagents (`vv-implementer`, `vv-spec-reviewer`, `vv-code-reviewer`):
|
|
5
|
+
|
|
6
|
+
1. Open work items first with `work_item_open`.
|
|
7
|
+
2. Reuse the returned `VVOC_WORK_ITEM_ID`.
|
|
8
|
+
3. Put that header as the first line in tracked subagent prompts.
|
|
9
|
+
4. Treat `NEEDS_CONTEXT` as a hard stop.
|
|
10
|
+
5. Use `work_item_list` to inspect workflow state before retrying.
|
|
11
|
+
6. Avoid free-form review loops without explicit work-item identity.
|
|
12
|
+
|
|
13
|
+
Use `work_item_close` explicitly when a work item is complete.
|
|
14
|
+
</workflow_protocol>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type WorkItemStore } from "./state.js";
|
|
2
|
+
export type WorkflowToolContext = {
|
|
3
|
+
sessionId: string;
|
|
4
|
+
};
|
|
5
|
+
export type WorkflowToolDefinition<TArgs, TResult> = {
|
|
6
|
+
name: string;
|
|
7
|
+
description: string;
|
|
8
|
+
execute: (args: TArgs, context: WorkflowToolContext) => TResult;
|
|
9
|
+
};
|
|
10
|
+
type OpenInputItem = {
|
|
11
|
+
key: string;
|
|
12
|
+
title: string;
|
|
13
|
+
};
|
|
14
|
+
type OpenArgs = {
|
|
15
|
+
items: OpenInputItem[];
|
|
16
|
+
};
|
|
17
|
+
type ListArgs = {
|
|
18
|
+
includeClosed?: boolean;
|
|
19
|
+
};
|
|
20
|
+
type CloseArgs = {
|
|
21
|
+
workItemId: string;
|
|
22
|
+
};
|
|
23
|
+
export declare function createWorkItemOpenTool(store: WorkItemStore): WorkflowToolDefinition<OpenArgs, Record<string, unknown>>;
|
|
24
|
+
export declare function createWorkItemListTool(store: WorkItemStore): WorkflowToolDefinition<ListArgs, Record<string, unknown>>;
|
|
25
|
+
export declare function createWorkItemCloseTool(store: WorkItemStore): WorkflowToolDefinition<CloseArgs, Record<string, unknown>>;
|
|
26
|
+
export {};
|