@vibecompass/vibecompass 0.1.0
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/LICENSE +21 -0
- package/README.md +329 -0
- package/package.json +42 -0
- package/src/cli.js +572 -0
- package/src/frontmatter.js +28 -0
- package/src/generators/agent-files/agents-md.js +13 -0
- package/src/generators/agent-files/claude-md.js +13 -0
- package/src/generators/agent-files/copilot-instructions.js +13 -0
- package/src/generators/agent-files/cursor-rules.js +13 -0
- package/src/generators/agent-files/index.js +147 -0
- package/src/generators/agent-files/markers.js +49 -0
- package/src/generators/agent-files/template.js +75 -0
- package/src/hash.js +29 -0
- package/src/index.js +15 -0
- package/src/init.js +227 -0
- package/src/manifest.js +75 -0
- package/src/project-memory.js +678 -0
- package/src/project-yaml.js +107 -0
- package/src/read-model.js +557 -0
- package/src/scaffold.js +404 -0
- package/src/session.js +553 -0
- package/src/setup.js +719 -0
- package/src/simple-yaml.js +227 -0
- package/src/workflow.js +120 -0
package/src/scaffold.js
ADDED
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
import { access, mkdir, writeFile } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { resolveWorkflowSettings } from './workflow.js';
|
|
4
|
+
|
|
5
|
+
export async function scaffoldInitFiles(options) {
|
|
6
|
+
const createdFiles = [];
|
|
7
|
+
const skippedFiles = [];
|
|
8
|
+
|
|
9
|
+
if (!options.workflow) {
|
|
10
|
+
return {
|
|
11
|
+
contextFilePath: null,
|
|
12
|
+
createdFiles,
|
|
13
|
+
skippedFiles,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
await mkdir(path.join(options.rootDir, 'architecture'), { recursive: true });
|
|
18
|
+
await mkdir(path.join(options.rootDir, 'decisions'), { recursive: true });
|
|
19
|
+
await mkdir(path.join(options.rootDir, 'sessions'), { recursive: true });
|
|
20
|
+
|
|
21
|
+
const contextFilePath = path.join(options.rootDir, 'context.md');
|
|
22
|
+
// context.md is a derived artifact owned by the package; regenerate it on rerun.
|
|
23
|
+
await writeFile(contextFilePath, generateContextMarkdown(options), 'utf8');
|
|
24
|
+
createdFiles.push(contextFilePath);
|
|
25
|
+
|
|
26
|
+
const workflowFiles = [
|
|
27
|
+
{
|
|
28
|
+
path: path.join(options.rootDir, 'architecture', 'README.md'),
|
|
29
|
+
content: generateArchitectureGuide(options.projectConfig),
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
path: path.join(options.rootDir, 'decisions', 'README.md'),
|
|
33
|
+
content: generateDecisionsGuide(options.projectConfig),
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
path: path.join(options.rootDir, 'sessions', 'README.md'),
|
|
37
|
+
content: generateSessionsGuide(options.projectConfig),
|
|
38
|
+
},
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
for (const file of workflowFiles) {
|
|
42
|
+
const outcome = await writeIfMissing(file.path, file.content);
|
|
43
|
+
if (outcome.created) {
|
|
44
|
+
createdFiles.push(file.path);
|
|
45
|
+
} else {
|
|
46
|
+
skippedFiles.push(file.path);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const bootstrapFiles = [];
|
|
51
|
+
if (options.claude) {
|
|
52
|
+
bootstrapFiles.push({
|
|
53
|
+
path: path.join(options.toolingRootDir, 'CLAUDE.md'),
|
|
54
|
+
content: generateClaudeTemplate(options),
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (options.agents) {
|
|
59
|
+
bootstrapFiles.push({
|
|
60
|
+
path: path.join(options.toolingRootDir, 'AGENTS.md'),
|
|
61
|
+
content: generateAgentsTemplate(options),
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
for (const file of bootstrapFiles) {
|
|
66
|
+
const outcome = await writeIfMissing(file.path, file.content);
|
|
67
|
+
if (outcome.created) {
|
|
68
|
+
createdFiles.push(file.path);
|
|
69
|
+
} else {
|
|
70
|
+
skippedFiles.push(file.path);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
contextFilePath,
|
|
76
|
+
createdFiles,
|
|
77
|
+
skippedFiles,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function writeIfMissing(filePath, content) {
|
|
82
|
+
if (await fileExists(filePath)) {
|
|
83
|
+
return { created: false };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
await mkdir(path.dirname(filePath), { recursive: true });
|
|
87
|
+
await writeFile(filePath, content, 'utf8');
|
|
88
|
+
return { created: true };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async function fileExists(filePath) {
|
|
92
|
+
try {
|
|
93
|
+
await access(filePath);
|
|
94
|
+
return true;
|
|
95
|
+
} catch {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function generateContextMarkdown(options) {
|
|
101
|
+
const projectName = options.projectConfig.name;
|
|
102
|
+
const rootRelativePath = options.rootRelativePath;
|
|
103
|
+
const workflow = resolveWorkflowSettings(options.projectConfig);
|
|
104
|
+
const repos = options.projectConfig.repos
|
|
105
|
+
.map((repo) => `- \`${repo.id}\` → ${repo.remote}`)
|
|
106
|
+
.join('\n');
|
|
107
|
+
|
|
108
|
+
return `# Project Context — ${projectName}
|
|
109
|
+
|
|
110
|
+
This workspace uses VibeCompass project memory rooted at \`${rootRelativePath}\`.
|
|
111
|
+
|
|
112
|
+
## Canonical files
|
|
113
|
+
- \`${rootRelativePath}/project.yaml\` — machine-oriented project metadata
|
|
114
|
+
- \`${rootRelativePath}/architecture/\` — canonical architecture docs
|
|
115
|
+
- \`${rootRelativePath}/decisions/\` — canonical decision log
|
|
116
|
+
- \`${rootRelativePath}/sessions/\` — finalized session notes
|
|
117
|
+
|
|
118
|
+
## Derived and scratch files
|
|
119
|
+
- \`${rootRelativePath}/context.md\` — generated AI-facing workflow context
|
|
120
|
+
- \`${rootRelativePath}/state/manifest.json\` — machine-owned local state; do not hand-edit
|
|
121
|
+
- \`${rootRelativePath}/sessions/wip.md\` — active builder scratchpad during a session
|
|
122
|
+
- \`${rootRelativePath}/sessions/handoff.md\` — builder/reviewer relay during a session
|
|
123
|
+
|
|
124
|
+
## Repos in scope
|
|
125
|
+
${repos}
|
|
126
|
+
|
|
127
|
+
## Session roles
|
|
128
|
+
| Trigger | Role | Owns session lifecycle? |
|
|
129
|
+
|---|---|---|
|
|
130
|
+
| "start session" | Builder | Yes — opens the session, keeps scratch files current, and writes the final session note |
|
|
131
|
+
| "join as reviewer" | Reviewer | No — reviews the builder's work, appends findings, and updates handoff guidance |
|
|
132
|
+
|
|
133
|
+
## Session prompt commands
|
|
134
|
+
- \`start session\` — builder role trigger
|
|
135
|
+
- \`join as reviewer\` — reviewer role trigger
|
|
136
|
+
- \`review handoff\` — reviewer reads the latest finalized note, \`wip.md\`, \`handoff.md\`, and relevant diffs before appending findings
|
|
137
|
+
- \`address review\` — builder reads reviewer feedback in \`wip.md\` / \`handoff.md\`, responds inline, applies accepted changes, and refreshes the builder handoff
|
|
138
|
+
|
|
139
|
+
These are prompt commands for agent behavior, not \`vibecompass\` CLI subcommands.
|
|
140
|
+
Reviewer handback is explicit: the reviewer ends the pass by updating \`wip.md\` + \`handoff.md\` and then stopping. Builder close-out uses \`vibecompass close-session\`, which follows the workflow defaults recorded in \`${rootRelativePath}/project.yaml\`.
|
|
141
|
+
|
|
142
|
+
## Workflow defaults
|
|
143
|
+
${renderWorkflowDefaults(workflow)}
|
|
144
|
+
|
|
145
|
+
## Session startup
|
|
146
|
+
1. Read \`${rootRelativePath}/project.yaml\`.
|
|
147
|
+
2. Read the latest finalized session note in \`${rootRelativePath}/sessions/\`.
|
|
148
|
+
3. If present, read \`${rootRelativePath}/sessions/wip.md\`.
|
|
149
|
+
4. If present, read \`${rootRelativePath}/sessions/handoff.md\`.
|
|
150
|
+
5. Read the relevant docs under \`${rootRelativePath}/architecture/\` and \`${rootRelativePath}/decisions/\`.
|
|
151
|
+
|
|
152
|
+
## Builder workflow
|
|
153
|
+
At session start, prefer running \`vibecompass start-session --working-on "..." \`.
|
|
154
|
+
If you manage files manually, create \`${rootRelativePath}/sessions/wip.md\` if it does not exist:
|
|
155
|
+
|
|
156
|
+
\`\`\`md
|
|
157
|
+
# WIP — YYYY-MM-DD (session N)
|
|
158
|
+
|
|
159
|
+
## Working on
|
|
160
|
+
|
|
161
|
+
## Log
|
|
162
|
+
|
|
163
|
+
## Reviewer input needed
|
|
164
|
+
|
|
165
|
+
## Review log
|
|
166
|
+
\`\`\`
|
|
167
|
+
|
|
168
|
+
Also create \`${rootRelativePath}/sessions/handoff.md\` if it does not exist:
|
|
169
|
+
|
|
170
|
+
\`\`\`md
|
|
171
|
+
# Handoff — YYYY-MM-DD (session N)
|
|
172
|
+
|
|
173
|
+
## Builder → Reviewer
|
|
174
|
+
|
|
175
|
+
### What changed
|
|
176
|
+
|
|
177
|
+
### What needs review
|
|
178
|
+
|
|
179
|
+
### What's next
|
|
180
|
+
|
|
181
|
+
## Reviewer → Builder
|
|
182
|
+
|
|
183
|
+
### Findings summary
|
|
184
|
+
|
|
185
|
+
### Recommended next step
|
|
186
|
+
\`\`\`
|
|
187
|
+
|
|
188
|
+
During the session:
|
|
189
|
+
- append short summaries to \`wip.md\` after meaningful exchanges
|
|
190
|
+
- keep \`handoff.md\` current after substantive work blocks
|
|
191
|
+
- use \`address review\` when reviewer feedback lands so the builder resolves it from the latest \`wip.md\` / \`handoff.md\`
|
|
192
|
+
- stay in builder role through close-out; resolve or explicitly defer reviewer feedback before running \`vibecompass close-session\`
|
|
193
|
+
- record architectural decisions in \`${rootRelativePath}/decisions/\` before implementing them
|
|
194
|
+
|
|
195
|
+
At session close:
|
|
196
|
+
- prefer running \`vibecompass close-session --title "..." --completed "..." --model "..." --next-step "..."\`
|
|
197
|
+
- follow the stored close-session defaults from \`${rootRelativePath}/project.yaml\`
|
|
198
|
+
- finalize \`wip.md\` into \`${rootRelativePath}/sessions/YYYY-MM-DD-N-title.md\`
|
|
199
|
+
- delete \`wip.md\` and \`handoff.md\`
|
|
200
|
+
- refresh any affected architecture/decision docs
|
|
201
|
+
|
|
202
|
+
## Reviewer workflow
|
|
203
|
+
- use \`review handoff\` when you want the reviewer to run the next review pass
|
|
204
|
+
- read the latest finalized session note, then \`wip.md\`, then \`handoff.md\`
|
|
205
|
+
- inspect the relevant code/docs diffs
|
|
206
|
+
- append findings under \`## Review log\` in \`wip.md\`
|
|
207
|
+
- write a concise baton-pass summary into \`handoff.md\`
|
|
208
|
+
- end the review pass after those file updates; there is no separate reviewer-exit step
|
|
209
|
+
|
|
210
|
+
## Tooling note
|
|
211
|
+
\`CLAUDE.md\` and \`AGENTS.md\` are developer-owned starter files. If they exist,
|
|
212
|
+
they should point back to this context file and should not be overwritten
|
|
213
|
+
automatically by package commands.
|
|
214
|
+
`;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function generateArchitectureGuide(projectConfig) {
|
|
218
|
+
return `# Architecture
|
|
219
|
+
|
|
220
|
+
This directory holds canonical architecture docs for ${projectConfig.name}.
|
|
221
|
+
|
|
222
|
+
## Recommended layout
|
|
223
|
+
|
|
224
|
+
\`\`\`text
|
|
225
|
+
architecture/<domain-slug>/<feature-slug>/<component>.md
|
|
226
|
+
\`\`\`
|
|
227
|
+
|
|
228
|
+
## Required frontmatter
|
|
229
|
+
- \`domain\`
|
|
230
|
+
- \`feature\`
|
|
231
|
+
- \`component\`
|
|
232
|
+
- \`status\`
|
|
233
|
+
- optional \`repo\` or \`repos\`
|
|
234
|
+
|
|
235
|
+
## Recommended sections
|
|
236
|
+
- \`## Description\`
|
|
237
|
+
- \`## Details\`
|
|
238
|
+
- \`## Next steps\`
|
|
239
|
+
- \`## Involved files\`
|
|
240
|
+
|
|
241
|
+
Each component doc is canonical. This README is only a convenience guide.
|
|
242
|
+
`;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function generateDecisionsGuide(projectConfig) {
|
|
246
|
+
return `# Decisions
|
|
247
|
+
|
|
248
|
+
Append-only decision log for ${projectConfig.name}.
|
|
249
|
+
|
|
250
|
+
## Conventions
|
|
251
|
+
- store canonical decision entries in domain-grouped files such as \`cross-cutting.md\`
|
|
252
|
+
- never edit or delete prior decision entries
|
|
253
|
+
- use headings in the form \`### D-<number> — Title\`
|
|
254
|
+
- include \`**Timestamp:**\`, \`**Decision:**\`, and \`**Rationale:**\`
|
|
255
|
+
|
|
256
|
+
## Example
|
|
257
|
+
|
|
258
|
+
\`\`\`md
|
|
259
|
+
### D-001 — Example decision
|
|
260
|
+
**Timestamp:** YYYY-MM-DD HH:MM TZ
|
|
261
|
+
**Decision:** What was decided.
|
|
262
|
+
**Rationale:** Why this was the correct tradeoff.
|
|
263
|
+
\`\`\`
|
|
264
|
+
|
|
265
|
+
This README is guidance only; canonical decision content lives in the domain files.
|
|
266
|
+
`;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function generateSessionsGuide(projectConfig) {
|
|
270
|
+
return `# Sessions
|
|
271
|
+
|
|
272
|
+
Finalized session notes for ${projectConfig.name} live here.
|
|
273
|
+
|
|
274
|
+
## Finalized session note format
|
|
275
|
+
- recommended filename: \`YYYY-MM-DD-N-title.md\`
|
|
276
|
+
- recommended H1: \`# Session — YYYY-MM-DD-N — Title\`
|
|
277
|
+
|
|
278
|
+
## Recommended sections
|
|
279
|
+
- \`## What we worked on\`
|
|
280
|
+
- \`## Completed\`
|
|
281
|
+
- \`## Decisions made\`
|
|
282
|
+
- \`## Models used\`
|
|
283
|
+
- \`## Blockers / open questions\`
|
|
284
|
+
- \`## Next session should start with\`
|
|
285
|
+
|
|
286
|
+
## Active-session scratch files
|
|
287
|
+
- \`wip.md\` — builder scratchpad during an active session
|
|
288
|
+
- \`handoff.md\` — reviewer/builder baton-pass during an active session
|
|
289
|
+
|
|
290
|
+
Those scratch files are session-scoped working artifacts, not finalized history.
|
|
291
|
+
`;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function generateClaudeTemplate(options) {
|
|
295
|
+
const workflow = resolveWorkflowSettings(options.projectConfig);
|
|
296
|
+
|
|
297
|
+
return `# ${options.projectConfig.name} Workspace
|
|
298
|
+
|
|
299
|
+
Read \`${options.contextRelativeToToolingRoot}\` before doing substantive work.
|
|
300
|
+
|
|
301
|
+
## Session roles
|
|
302
|
+
|
|
303
|
+
| Trigger | Role | Owns session lifecycle? |
|
|
304
|
+
|---|---|---|
|
|
305
|
+
| "start session" | Builder | Yes |
|
|
306
|
+
| "join as reviewer" | Reviewer | No |
|
|
307
|
+
|
|
308
|
+
## Prompt commands
|
|
309
|
+
|
|
310
|
+
- \`start session\` — builder role trigger
|
|
311
|
+
- \`join as reviewer\` — reviewer role trigger
|
|
312
|
+
- \`review handoff\` — reviewer reads the latest finalized note, \`wip.md\`, \`handoff.md\`, and relevant diffs before appending findings
|
|
313
|
+
- \`address review\` — builder reads reviewer feedback in \`wip.md\` / \`handoff.md\`, responds inline, applies accepted changes, and refreshes the builder handoff
|
|
314
|
+
|
|
315
|
+
These are prompt commands for agent behavior, not \`vibecompass\` CLI subcommands.
|
|
316
|
+
Reviewer handback is explicit: the reviewer ends the pass by updating \`${options.rootRelativePath}/sessions/wip.md\` + \`${options.rootRelativePath}/sessions/handoff.md\` and then stopping. Builder close-out uses \`vibecompass close-session\`, which follows the workflow defaults recorded in \`${options.rootRelativePath}/project.yaml\`.
|
|
317
|
+
|
|
318
|
+
## Current session
|
|
319
|
+
|
|
320
|
+
**Update this block at the start and end of every session.**
|
|
321
|
+
|
|
322
|
+
\`\`\`
|
|
323
|
+
Date: not started
|
|
324
|
+
Working on: Session not started yet.
|
|
325
|
+
Last thing completed: Project memory bootstrap completed.
|
|
326
|
+
Blockers: None recorded.
|
|
327
|
+
Next session should: Read \`${options.contextRelativeToToolingRoot}\`, then open the first builder or reviewer session.
|
|
328
|
+
\`\`\`
|
|
329
|
+
|
|
330
|
+
## Startup
|
|
331
|
+
- Read \`${options.contextRelativeToToolingRoot}\`
|
|
332
|
+
- Follow the builder/reviewer protocol defined there
|
|
333
|
+
- Use \`vibecompass start-session\` and \`vibecompass close-session\` to manage the active-session files when possible
|
|
334
|
+
- Keep \`${options.rootRelativePath}/sessions/wip.md\` and \`${options.rootRelativePath}/sessions/handoff.md\` current during an active session
|
|
335
|
+
- Workflow defaults:
|
|
336
|
+
${renderWorkflowDefaults(workflow)}
|
|
337
|
+
`;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function generateAgentsTemplate(options) {
|
|
341
|
+
const workflow = resolveWorkflowSettings(options.projectConfig);
|
|
342
|
+
|
|
343
|
+
return `# ${options.projectConfig.name} Workspace
|
|
344
|
+
|
|
345
|
+
This workspace uses VibeCompass project memory rooted at \`${options.rootRelativePath}\`.
|
|
346
|
+
|
|
347
|
+
## Session roles
|
|
348
|
+
|
|
349
|
+
| Trigger | Role | Owns session lifecycle? |
|
|
350
|
+
|---|---|---|
|
|
351
|
+
| "start session" | Builder | Yes — opens and closes the session lifecycle |
|
|
352
|
+
| "join as reviewer" | Reviewer | No — reviews the builder's work and updates the handoff |
|
|
353
|
+
|
|
354
|
+
## Prompt commands
|
|
355
|
+
|
|
356
|
+
- \`start session\` — builder role trigger
|
|
357
|
+
- \`join as reviewer\` — reviewer role trigger
|
|
358
|
+
- \`review handoff\` — reviewer reads the latest finalized note, \`wip.md\`, \`handoff.md\`, and relevant diffs before appending findings
|
|
359
|
+
- \`address review\` — builder reads reviewer feedback in \`wip.md\` / \`handoff.md\`, responds inline, applies accepted changes, and refreshes the builder handoff
|
|
360
|
+
|
|
361
|
+
These are prompt commands for agent behavior, not \`vibecompass\` CLI subcommands.
|
|
362
|
+
Reviewer handback is explicit: the reviewer ends the pass by updating \`${options.rootRelativePath}/sessions/wip.md\` + \`${options.rootRelativePath}/sessions/handoff.md\` and then stopping. Builder close-out uses \`vibecompass close-session\`, which follows the workflow defaults recorded in \`${options.rootRelativePath}/project.yaml\`.
|
|
363
|
+
|
|
364
|
+
## Startup
|
|
365
|
+
Before doing substantive work:
|
|
366
|
+
1. Read \`${options.contextRelativeToToolingRoot}\`.
|
|
367
|
+
2. Read the latest finalized session note under \`${options.rootRelativePath}/sessions/\`.
|
|
368
|
+
3. If present, read \`${options.rootRelativePath}/sessions/wip.md\`.
|
|
369
|
+
4. If present, read \`${options.rootRelativePath}/sessions/handoff.md\`.
|
|
370
|
+
5. Read the relevant docs under \`${options.rootRelativePath}/architecture/\` and \`${options.rootRelativePath}/decisions/\`.
|
|
371
|
+
|
|
372
|
+
## Working rule
|
|
373
|
+
Treat \`${options.contextRelativeToToolingRoot}\` as the local workflow source of truth for the builder/reviewer protocol and session scratch-file structure. When the package provides \`start-session\` / \`close-session\`, prefer those commands over hand-editing boilerplate.
|
|
374
|
+
- Workflow defaults:
|
|
375
|
+
${renderWorkflowDefaults(workflow)}
|
|
376
|
+
`;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
function renderWorkflowDefaults(workflow) {
|
|
380
|
+
const lines = [
|
|
381
|
+
`- reviewer handback: ${describeReviewerHandback(workflow)}`,
|
|
382
|
+
'- close-session: refresh any relevant architecture docs before finalizing the session',
|
|
383
|
+
'- close-session: refresh any relevant decision files before finalizing the session',
|
|
384
|
+
];
|
|
385
|
+
|
|
386
|
+
if (workflow.closeSession.gitPublish) {
|
|
387
|
+
lines.push(
|
|
388
|
+
`- close-session: include a Git publish step after finalization using remote \`${workflow.closeSession.gitRemote}\``,
|
|
389
|
+
`- close-session: use commit message format \`${workflow.closeSession.commitTemplate}\` for VibeCompass-templated commits`,
|
|
390
|
+
);
|
|
391
|
+
} else {
|
|
392
|
+
lines.push('- close-session: no Git publish step is included by default');
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
return lines.join('\n');
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
function describeReviewerHandback(workflow) {
|
|
399
|
+
if (workflow.reviewerHandback === 'handoff-file') {
|
|
400
|
+
return 'update sessions/wip.md and sessions/handoff.md, then stop the review pass';
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
return workflow.reviewerHandback;
|
|
404
|
+
}
|