@captain_z/zsk 1.8.2 → 1.8.4
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/dist/bin.js +4 -0
- package/dist/bin.js.map +1 -1
- package/dist/commands/add-flow.d.ts +3 -7
- package/dist/commands/add-flow.js +7 -59
- package/dist/commands/add-flow.js.map +1 -1
- package/dist/commands/add.js +25 -104
- package/dist/commands/add.js.map +1 -1
- package/dist/commands/check.js +184 -8
- package/dist/commands/check.js.map +1 -1
- package/dist/commands/gate.d.ts +2 -0
- package/dist/commands/gate.js +2 -0
- package/dist/commands/gate.js.map +1 -1
- package/dist/core/prepare-sync.d.ts +2 -31
- package/dist/core/prepare-sync.js +119 -136
- package/dist/core/prepare-sync.js.map +1 -1
- package/dist/core/profile-bundle-installation.d.ts +55 -0
- package/dist/core/profile-bundle-installation.js +170 -0
- package/dist/core/profile-bundle-installation.js.map +1 -0
- package/dist/core/source-snapshot-adapters.d.ts +59 -0
- package/dist/core/source-snapshot-adapters.js +82 -0
- package/dist/core/source-snapshot-adapters.js.map +1 -0
- package/dist/core/staffing-plan.d.ts +7 -1
- package/dist/core/staffing-plan.js +186 -29
- package/dist/core/staffing-plan.js.map +1 -1
- package/dist/core/stage-clarity-verification.d.ts +31 -0
- package/dist/core/stage-clarity-verification.js +313 -0
- package/dist/core/stage-clarity-verification.js.map +1 -0
- package/dist/core/stage-quality-artifacts.d.ts +15 -0
- package/dist/core/stage-quality-artifacts.js +421 -0
- package/dist/core/stage-quality-artifacts.js.map +1 -0
- package/dist/core/stage-quality-contracts.d.ts +86 -0
- package/dist/core/stage-quality-contracts.js +2 -0
- package/dist/core/stage-quality-contracts.js.map +1 -0
- package/dist/core/stage-quality-criteria.d.ts +9 -0
- package/dist/core/stage-quality-criteria.js +323 -0
- package/dist/core/stage-quality-criteria.js.map +1 -0
- package/dist/core/stage-quality-rendering.d.ts +13 -0
- package/dist/core/stage-quality-rendering.js +122 -0
- package/dist/core/stage-quality-rendering.js.map +1 -0
- package/dist/core/stage-quality.d.ts +5 -52
- package/dist/core/stage-quality.js +40 -432
- package/dist/core/stage-quality.js.map +1 -1
- package/dist/core/template-registry.js +6 -0
- package/dist/core/template-registry.js.map +1 -1
- package/package.json +2 -2
- package/templates/module/frontend-module/design.md +25 -3
- package/templates/module/frontend-module/proposal.md +8 -0
- package/templates/module/frontend-module/spec.md +16 -0
- package/templates/module/frontend-module/tasks.md +23 -7
- package/templates/project-init/.zsk/config.yaml +33 -0
- package/templates/project-init/.zsk/docs/PROJECT-CONFIG.md +43 -0
- package/templates/project-init/.zsk/docs/SYSTEM-SPEC.md +23 -1
- package/templates/project-init/.zsk/raws/index.md +19 -0
- package/templates/project-init/.zsk/raws/prepare/design/index.md +18 -0
- package/templates/project-init/.zsk/raws/prepare/index.md +33 -0
- package/templates/project-init/.zsk/raws/prepare/ux/index.md +19 -0
- package/templates/project-init/.zsk/roles.yaml +11 -11
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
import { resolveSourceSnapshot, } from "./config.js";
|
|
3
|
+
import { inferSourceOrigin } from "./origin-detection.js";
|
|
4
|
+
export async function createSourceSnapshotContext(target, config, entry, opts, previousSnapshotHashFor) {
|
|
5
|
+
const source = entry.source;
|
|
6
|
+
const origin = inferSourceOrigin(source);
|
|
7
|
+
const snapshot = resolveSourceSnapshot(config, entry);
|
|
8
|
+
const snapshotPath = resolve(target, snapshot);
|
|
9
|
+
const previousSnapshotHash = await previousSnapshotHashFor(snapshotPath);
|
|
10
|
+
const base = {
|
|
11
|
+
envelopeVersion: 1,
|
|
12
|
+
sourceKey: entry.id,
|
|
13
|
+
sourcePath: entry.path,
|
|
14
|
+
rawLane: entry.rawLane,
|
|
15
|
+
provider: origin.provider,
|
|
16
|
+
origin: origin.ref,
|
|
17
|
+
snapshot,
|
|
18
|
+
previousSnapshotHash,
|
|
19
|
+
};
|
|
20
|
+
return {
|
|
21
|
+
target,
|
|
22
|
+
config,
|
|
23
|
+
entry,
|
|
24
|
+
source,
|
|
25
|
+
origin,
|
|
26
|
+
opts,
|
|
27
|
+
snapshot,
|
|
28
|
+
snapshotPath,
|
|
29
|
+
previousSnapshotHash,
|
|
30
|
+
base,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export async function acquireSourceSnapshot(context, handlers) {
|
|
34
|
+
if (context.opts.dryRun)
|
|
35
|
+
return handlers.dryRun(context);
|
|
36
|
+
if (context.origin.method === "local")
|
|
37
|
+
return handlers.local(context);
|
|
38
|
+
const providerAdapter = selectSourceSnapshotProviderAdapter(context.source, context.origin);
|
|
39
|
+
if (providerAdapter) {
|
|
40
|
+
const result = await handlers.provider(context, providerAdapter);
|
|
41
|
+
if (result)
|
|
42
|
+
return result;
|
|
43
|
+
}
|
|
44
|
+
if (context.origin.method === "repository")
|
|
45
|
+
return handlers.repository(context);
|
|
46
|
+
if (context.origin.method === "url")
|
|
47
|
+
return handlers.url(context);
|
|
48
|
+
return handlers.providerManaged(context);
|
|
49
|
+
}
|
|
50
|
+
export function selectSourceSnapshotProviderAdapter(source, origin) {
|
|
51
|
+
const keys = [
|
|
52
|
+
origin.provider,
|
|
53
|
+
origin.kind,
|
|
54
|
+
source.origin?.provider,
|
|
55
|
+
source.origin?.kind,
|
|
56
|
+
source.kind,
|
|
57
|
+
source.type,
|
|
58
|
+
].map((value) => normalizeAdapterKey(typeof value === "string" ? value : undefined));
|
|
59
|
+
if (keys.some((value) => value.includes("confluence")))
|
|
60
|
+
return "confluence";
|
|
61
|
+
if (keys.some((value) => value.includes("jira")))
|
|
62
|
+
return "jira";
|
|
63
|
+
if (keys.some((value) => value.includes("gitlab")))
|
|
64
|
+
return "gitlab";
|
|
65
|
+
if (keys.some((value) => ["figma", "modao", "mastergo", "design", "design-asset", "design-source"].includes(value))) {
|
|
66
|
+
return "design-source";
|
|
67
|
+
}
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
export function chooseSourceSnapshotStrategy(method) {
|
|
71
|
+
if (method === "local")
|
|
72
|
+
return "local-copy-structured-markdown";
|
|
73
|
+
if (method === "repository")
|
|
74
|
+
return "repository-metadata-only";
|
|
75
|
+
if (method === "url")
|
|
76
|
+
return "playwright-auth-or-direct-fetch";
|
|
77
|
+
return "confirm-acquisition-method";
|
|
78
|
+
}
|
|
79
|
+
function normalizeAdapterKey(value) {
|
|
80
|
+
return value?.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") ?? "";
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=source-snapshot-adapters.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source-snapshot-adapters.js","sourceRoot":"","sources":["../../src/core/source-snapshot-adapters.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { type ProjectConfig } from "./config.js";
|
|
2
|
-
import { type GateAssessment } from "./stage-quality.js";
|
|
2
|
+
import { type GateAssessment, type ReviewGateMode } from "./stage-quality.js";
|
|
3
3
|
export type StaffingPlanOptions = {
|
|
4
4
|
stage?: string;
|
|
5
5
|
skill?: string;
|
|
6
6
|
surface?: string;
|
|
7
7
|
runId?: string;
|
|
8
8
|
module?: string;
|
|
9
|
+
issue?: string;
|
|
10
|
+
reviewTarget?: string;
|
|
9
11
|
gateThreshold?: number;
|
|
10
12
|
acceptRisk?: string;
|
|
11
13
|
};
|
|
@@ -118,6 +120,8 @@ export type StaffingStageGate = {
|
|
|
118
120
|
runId: string;
|
|
119
121
|
stage: string;
|
|
120
122
|
module?: string;
|
|
123
|
+
reviewTarget?: string;
|
|
124
|
+
reviewMode?: ReviewGateMode;
|
|
121
125
|
status: GateAssessment["status"];
|
|
122
126
|
decision: GateAssessment["decision"];
|
|
123
127
|
score: number;
|
|
@@ -133,6 +137,8 @@ export type StaffingPlan = {
|
|
|
133
137
|
stage: string;
|
|
134
138
|
skill: string;
|
|
135
139
|
module?: string;
|
|
140
|
+
reviewTarget?: string;
|
|
141
|
+
reviewMode?: ReviewGateMode;
|
|
136
142
|
surface: string;
|
|
137
143
|
stageGate?: StaffingStageGate;
|
|
138
144
|
orchestration: StaffingOrchestration;
|
|
@@ -3,7 +3,7 @@ import { dirname, join, resolve } from "node:path";
|
|
|
3
3
|
import YAML from "yaml";
|
|
4
4
|
import { flattenProjectSources } from "./config.js";
|
|
5
5
|
import { inferSourceOrigin } from "./origin-detection.js";
|
|
6
|
-
import { gateThresholdForStage, writeGateAssessment } from "./stage-quality.js";
|
|
6
|
+
import { gateThresholdForStage, normalizeReviewTargetPath, reviewModeForReviewTarget, writeGateAssessment, } from "./stage-quality.js";
|
|
7
7
|
import { getWorkspacePath } from "./workspace-layout.js";
|
|
8
8
|
const DEFAULT_PACKET_TIMEOUT_POLICY = {
|
|
9
9
|
heartbeatIntervalMs: 60_000,
|
|
@@ -11,6 +11,20 @@ const DEFAULT_PACKET_TIMEOUT_POLICY = {
|
|
|
11
11
|
maxAttempts: 2,
|
|
12
12
|
onTimeout: "fallback-to-leader-sequential",
|
|
13
13
|
};
|
|
14
|
+
const PREPROPOSAL_ROLE_NAMES = [
|
|
15
|
+
"product-owner",
|
|
16
|
+
"business-analyst",
|
|
17
|
+
"planner",
|
|
18
|
+
"ux-specialist",
|
|
19
|
+
"design-specialist",
|
|
20
|
+
"frontend-engineer",
|
|
21
|
+
"architect",
|
|
22
|
+
"qa-engineer",
|
|
23
|
+
"researcher",
|
|
24
|
+
"verifier",
|
|
25
|
+
"lead-integrator",
|
|
26
|
+
];
|
|
27
|
+
const PREPROPOSAL_REVIEW_MODES = new Set(["preproposal", "raw-source"]);
|
|
14
28
|
const BUILTIN_ROLE_POOL = {
|
|
15
29
|
roles: Object.fromEntries([
|
|
16
30
|
rolePoolEntry("lead-integrator", {
|
|
@@ -29,21 +43,21 @@ const BUILTIN_ROLE_POOL = {
|
|
|
29
43
|
}),
|
|
30
44
|
rolePoolEntry("product-owner", {
|
|
31
45
|
subagentType: "analyst",
|
|
32
|
-
stages: ["prepare", "proposal", "spec", "review", "verify", "acceptance"],
|
|
46
|
+
stages: ["prepare", "preproposal", "proposal", "spec", "review", "verify", "acceptance"],
|
|
33
47
|
lanes: ["product", "pm", "requirement", "requirements", "srs", "prd"],
|
|
34
48
|
activation: "when product or requirement evidence is configured or in scope",
|
|
35
49
|
reason: "interpret product scope, requirement semantics, priorities, and acceptance gaps",
|
|
36
50
|
}),
|
|
37
51
|
rolePoolEntry("business-analyst", {
|
|
38
52
|
subagentType: "analyst",
|
|
39
|
-
stages: ["proposal", "spec"],
|
|
53
|
+
stages: ["preproposal", "proposal", "spec"],
|
|
40
54
|
lanes: ["business", "domain", "process", "rules"],
|
|
41
55
|
activation: "when domain process or business-rule evidence is configured or in scope",
|
|
42
56
|
reason: "map domain process, terminology, business rules, and edge cases",
|
|
43
57
|
}),
|
|
44
58
|
rolePoolEntry("architect", {
|
|
45
59
|
subagentType: "architect",
|
|
46
|
-
stages: ["proposal", "spec", "design", "review"],
|
|
60
|
+
stages: ["preproposal", "proposal", "spec", "design", "review"],
|
|
47
61
|
activation: "when architecture, API, system-boundary, or dependency decisions are in scope",
|
|
48
62
|
reason: "review system boundaries, module relationships, contracts, and technical risks",
|
|
49
63
|
}),
|
|
@@ -56,28 +70,28 @@ const BUILTIN_ROLE_POOL = {
|
|
|
56
70
|
}),
|
|
57
71
|
rolePoolEntry("frontend-engineer", {
|
|
58
72
|
subagentType: "executor",
|
|
59
|
-
stages: ["design"],
|
|
73
|
+
stages: ["preproposal", "design"],
|
|
60
74
|
lanes: ["frontend", "web", "client", "mobile"],
|
|
61
75
|
activation: "when frontend implementation, routing, state, or UI integration is in scope",
|
|
62
76
|
reason: "map UI implementation, routing, state, and component integration risk",
|
|
63
77
|
}),
|
|
64
78
|
rolePoolEntry("design-specialist", {
|
|
65
79
|
subagentType: "designer",
|
|
66
|
-
stages: ["prepare", "
|
|
80
|
+
stages: ["prepare", "preproposal"],
|
|
67
81
|
lanes: ["design", "ui", "visual"],
|
|
68
82
|
activation: "when design, UI, visual, prototype, or asset sources are configured",
|
|
69
83
|
reason: "inspect design assets, screens, visual states, and design-source gaps",
|
|
70
84
|
}),
|
|
71
85
|
rolePoolEntry("ux-specialist", {
|
|
72
86
|
subagentType: "designer",
|
|
73
|
-
stages: ["prepare"],
|
|
87
|
+
stages: ["prepare", "preproposal"],
|
|
74
88
|
lanes: ["ux", "ue", "user-experience", "research"],
|
|
75
89
|
activation: "when UX, UE, user-flow, or research sources are configured",
|
|
76
90
|
reason: "inspect user flows, interaction constraints, and usability ambiguity",
|
|
77
91
|
}),
|
|
78
92
|
rolePoolEntry("qa-engineer", {
|
|
79
93
|
subagentType: "test-engineer",
|
|
80
|
-
stages: ["prepare", "spec", "task", "coding", "review", "verify"],
|
|
94
|
+
stages: ["prepare", "preproposal", "spec", "task", "coding", "fix", "review", "verify"],
|
|
81
95
|
lanes: ["qa", "test", "testing", "quality"],
|
|
82
96
|
activation: "when QA, test, acceptance, or quality evidence is configured or in scope",
|
|
83
97
|
reason: "connect requirements to acceptance scenarios, test coverage, and verification evidence",
|
|
@@ -104,19 +118,19 @@ const BUILTIN_ROLE_POOL = {
|
|
|
104
118
|
}),
|
|
105
119
|
rolePoolEntry("researcher", {
|
|
106
120
|
subagentType: "researcher",
|
|
107
|
-
stages: ["prepare"],
|
|
121
|
+
stages: ["prepare", "preproposal"],
|
|
108
122
|
activation: "optional when current official or external evidence is required",
|
|
109
123
|
reason: "collect official/current external references only when configured local sources are insufficient",
|
|
110
124
|
}),
|
|
111
125
|
rolePoolEntry("planner", {
|
|
112
126
|
subagentType: "planner",
|
|
113
|
-
stages: ["task"],
|
|
127
|
+
stages: ["preproposal", "task"],
|
|
114
128
|
activation: "when task sequencing, dependencies, and risk flags are in scope",
|
|
115
129
|
reason: "sequence implementation tasks, dependencies, ownership, and evidence hooks",
|
|
116
130
|
}),
|
|
117
131
|
rolePoolEntry("executor", {
|
|
118
132
|
subagentType: "executor",
|
|
119
|
-
stages: ["task", "coding"],
|
|
133
|
+
stages: ["task", "coding", "fix"],
|
|
120
134
|
activation: "when implementation ownership, write scope, or scoped code changes are in scope",
|
|
121
135
|
reason: "implement or estimate scoped code ownership and write boundaries",
|
|
122
136
|
}),
|
|
@@ -159,9 +173,13 @@ const ROLE_CONTRACTS = {
|
|
|
159
173
|
outputs: [
|
|
160
174
|
".zsk/evidence/{scope}/{run}/integration-summary.md",
|
|
161
175
|
".zsk/raws/manifest.json when prepare snapshots change",
|
|
176
|
+
".zsk/raws/manifest.json and .zsk/raws/index.md when preproposal raw resources change",
|
|
177
|
+
".zsk/raws/prepare/product/{topic}/intake-clarity.md when running preproposal",
|
|
178
|
+
".zsk/evidence/preproposal/{run}/readiness-review.md when running preproposal",
|
|
162
179
|
],
|
|
163
180
|
evidenceRequired: [
|
|
164
181
|
"integrated lane status with pass/blocker/waived reason for each role",
|
|
182
|
+
"source-discoverable questions answered before user clarification when running preproposal",
|
|
165
183
|
"confirmation that shared/global artifacts were updated only after lane validation",
|
|
166
184
|
],
|
|
167
185
|
forbiddenDecisions: [
|
|
@@ -173,15 +191,23 @@ const ROLE_CONTRACTS = {
|
|
|
173
191
|
"product-owner": {
|
|
174
192
|
owns: ["business goals, product scope, priorities, requirement semantics, and acceptance meaning"],
|
|
175
193
|
doesNotOwn: ["backend implementation details", "technical feasibility signoff without architect/engineer evidence"],
|
|
176
|
-
outputs: [
|
|
177
|
-
|
|
194
|
+
outputs: [
|
|
195
|
+
".zsk/evidence/{scope}/{run}/product-source-report.md",
|
|
196
|
+
".zsk/raws/prepare/product/{topic}/intake-clarity.md when running preproposal",
|
|
197
|
+
".zsk/raws/prepare/product/{topic}/product-brief.md when running preproposal",
|
|
198
|
+
".zsk/evidence/preproposal/{run}/checkpoint-product-review.md when running preproposal",
|
|
199
|
+
],
|
|
200
|
+
evidenceRequired: ["requirement/source IDs or paths behind each product claim", "accepted clarifications, inferred assumptions, and one blocking question when running preproposal", "open requirement gaps and acceptance risks", "product checkpoint verdict before roadmap/decomposition when running preproposal"],
|
|
178
201
|
forbiddenDecisions: ["Do not rewrite technical design or implementation ownership.", "Do not turn weak/inferred evidence into accepted requirements."],
|
|
179
202
|
stopCondition: "Product facts, gaps, and acceptance risks are explicit and handed to lead-integrator.",
|
|
180
203
|
},
|
|
181
204
|
"business-analyst": {
|
|
182
205
|
owns: ["domain process, terminology, business rules, and edge cases"],
|
|
183
206
|
doesNotOwn: ["UI aesthetics", "implementation architecture"],
|
|
184
|
-
outputs: [
|
|
207
|
+
outputs: [
|
|
208
|
+
".zsk/evidence/{scope}/{run}/business-rule-map.md",
|
|
209
|
+
".zsk/raws/prepare/product/{topic}/business-context.md when running preproposal",
|
|
210
|
+
],
|
|
185
211
|
evidenceRequired: ["business rule source references", "ambiguity list for unresolved process or terminology conflicts"],
|
|
186
212
|
forbiddenDecisions: ["Do not choose product priority or implementation strategy.", "Do not resolve contradictory business rules silently."],
|
|
187
213
|
stopCondition: "Rules, edge cases, and unresolved ambiguities are documented for lead-integrator.",
|
|
@@ -189,7 +215,11 @@ const ROLE_CONTRACTS = {
|
|
|
189
215
|
architect: {
|
|
190
216
|
owns: ["system boundaries, module relationships, data flow, integration risks, and API/dependency boundaries"],
|
|
191
217
|
doesNotOwn: ["product priority decisions", "business acceptance signoff"],
|
|
192
|
-
outputs: [
|
|
218
|
+
outputs: [
|
|
219
|
+
".zsk/evidence/{scope}/{run}/architecture-review.md",
|
|
220
|
+
".zsk/raws/prepare/product/{topic}/roadmap.md when running preproposal",
|
|
221
|
+
".zsk/evidence/preproposal/{run}/checkpoint-roadmap-review.md when running preproposal",
|
|
222
|
+
],
|
|
193
223
|
evidenceRequired: ["referenced files, modules, APIs, or diagrams behind architecture claims", "risk map with owner and verification hook"],
|
|
194
224
|
forbiddenDecisions: ["Do not override product acceptance or source truth.", "Do not widen implementation scope without lead-integrator handoff."],
|
|
195
225
|
stopCondition: "Architecture risks and boundary decisions are ready for integration or recorded as blockers.",
|
|
@@ -203,26 +233,36 @@ const ROLE_CONTRACTS = {
|
|
|
203
233
|
stopCondition: "Backend facts and gaps are reported with source references and no shared artifact edits pending.",
|
|
204
234
|
},
|
|
205
235
|
"frontend-engineer": {
|
|
206
|
-
owns: ["UI implementation, routing, state, component integration, and frontend framework constraints"],
|
|
236
|
+
owns: ["UI implementation, routing, state, page/module-to-code component mapping, component integration, and frontend framework constraints"],
|
|
207
237
|
doesNotOwn: ["source-of-truth product decisions", "backend/API correctness signoff"],
|
|
208
238
|
outputs: [".zsk/evidence/{scope}/{run}/frontend-impact-report.md"],
|
|
209
|
-
evidenceRequired: ["component/route/state file references", "UI implementation risks and validation hooks"],
|
|
239
|
+
evidenceRequired: ["component/route/state file references", "page/module-to-code mapping coverage", "UI implementation risks and validation hooks"],
|
|
210
240
|
forbiddenDecisions: ["Do not reinterpret acceptance criteria without product-owner handoff.", "Do not approve API behavior without backend evidence."],
|
|
211
241
|
stopCondition: "Frontend impact and validation needs are clear for lead-integrator.",
|
|
212
242
|
},
|
|
213
243
|
"design-specialist": {
|
|
214
|
-
owns: ["design assets, visual constraints, screen/state inventory, and design snapshot validation"],
|
|
244
|
+
owns: ["design assets, visual constraints, screen/state inventory, provider source maps, and design snapshot validation"],
|
|
215
245
|
doesNotOwn: ["backend/API correctness", "final product scope acceptance"],
|
|
216
|
-
outputs: [
|
|
217
|
-
|
|
246
|
+
outputs: [
|
|
247
|
+
".zsk/evidence/{scope}/{run}/design-asset-report.md",
|
|
248
|
+
".zsk/raws/prepare/design/{topic}/design-source-needs.md when running preproposal",
|
|
249
|
+
".zsk/raws/prepare/design/{topic}/design-source-map.md when provider-backed design sources are used",
|
|
250
|
+
],
|
|
251
|
+
evidenceRequired: ["design/prototype source references", "screen/state map, provider source coverage, and missing asset list"],
|
|
218
252
|
forbiddenDecisions: ["Do not infer implementation truth from design assets.", "Do not store auth state or exported private assets outside declared scope."],
|
|
219
253
|
stopCondition: "Design assets are mapped or blocked with provider/auth evidence.",
|
|
220
254
|
},
|
|
221
255
|
"ux-specialist": {
|
|
222
|
-
owns: ["user flows, interaction constraints, usability risks, and UX source validation"],
|
|
256
|
+
owns: ["user flows, interaction handoff, interaction constraints, usability risks, and UX source validation"],
|
|
223
257
|
doesNotOwn: ["backend/API correctness", "business priority decisions"],
|
|
224
|
-
outputs: [
|
|
225
|
-
|
|
258
|
+
outputs: [
|
|
259
|
+
".zsk/evidence/{scope}/{run}/ux-source-report.md",
|
|
260
|
+
".zsk/raws/prepare/ux/{topic}/ux-readiness.md when running preproposal",
|
|
261
|
+
".zsk/raws/prepare/ux/{topic}/interaction-handoff.md when UI, UX, or design-source interaction is triggered",
|
|
262
|
+
".zsk/evidence/preproposal/{run}/checkpoint-ux-review.md when running preproposal",
|
|
263
|
+
".zsk/evidence/preproposal/{run}/checkpoint-interaction-review.md when interaction handoff is triggered",
|
|
264
|
+
],
|
|
265
|
+
evidenceRequired: ["flow/source references", "AI brief draft status", "page/module interaction detail coverage", "interaction/state/a11y coverage", "usability risks and unresolved interaction questions"],
|
|
226
266
|
forbiddenDecisions: ["Do not approve product scope or technical design alone.", "Do not resolve UX/product conflicts silently."],
|
|
227
267
|
stopCondition: "UX facts, questions, and risks are ready for lead integration.",
|
|
228
268
|
},
|
|
@@ -237,8 +277,11 @@ const ROLE_CONTRACTS = {
|
|
|
237
277
|
"qa-engineer": {
|
|
238
278
|
owns: ["test cases, regression matrix, acceptance scenarios, Playwright/runtime evidence, and test gaps"],
|
|
239
279
|
doesNotOwn: ["business signoff", "implementation design ownership"],
|
|
240
|
-
outputs: [
|
|
241
|
-
|
|
280
|
+
outputs: [
|
|
281
|
+
".zsk/evidence/{scope}/{run}/qa-evidence-report.md",
|
|
282
|
+
".zsk/raws/prepare/product/{topic}/scenario-seeds.md when running preproposal",
|
|
283
|
+
],
|
|
284
|
+
evidenceRequired: ["test command/scenario evidence", "acceptance coverage and missing-test gaps", "scenario seeds and formal-test-case deferral when running preproposal"],
|
|
242
285
|
forbiddenDecisions: ["Do not treat unrun tests as passing.", "Do not accept partial snapshots as complete evidence."],
|
|
243
286
|
stopCondition: "Coverage status is pass/fail/blocked with concrete evidence and residual risk.",
|
|
244
287
|
},
|
|
@@ -428,11 +471,11 @@ function seedFromRolePool(role, entries, overrides = {}) {
|
|
|
428
471
|
reason: overrides.reason ?? role.reason ?? `configured ${role.role} role`,
|
|
429
472
|
entries,
|
|
430
473
|
activation: overrides.activation ?? role.activation,
|
|
431
|
-
extraInputs: role.extraInputs,
|
|
474
|
+
extraInputs: unique([...(role.extraInputs ?? []), ...(overrides.extraInputs ?? [])]),
|
|
432
475
|
writeScope: unique([...(role.writeScope ?? []), ...(overrides.writeScope ?? [])]),
|
|
433
476
|
outputs: unique([...(role.outputs ?? []), ...(overrides.outputs ?? [])]),
|
|
434
477
|
evidenceRequired: unique([...(role.evidenceRequired ?? []), ...(overrides.evidenceRequired ?? [])]),
|
|
435
|
-
contract: role.contract,
|
|
478
|
+
contract: mergeContractOverrides(role.contract, overrides.contract),
|
|
436
479
|
};
|
|
437
480
|
}
|
|
438
481
|
function roleMatchesStageOrSkill(role, stage, skill) {
|
|
@@ -461,6 +504,8 @@ function summarizeStageGate(assessment, assessmentPath, markdownPath, waiverPath
|
|
|
461
504
|
runId: assessment.runId,
|
|
462
505
|
stage: assessment.stage,
|
|
463
506
|
...(assessment.module ? { module: assessment.module } : {}),
|
|
507
|
+
...(assessment.reviewTarget ? { reviewTarget: assessment.reviewTarget } : {}),
|
|
508
|
+
...(assessment.reviewMode ? { reviewMode: assessment.reviewMode } : {}),
|
|
464
509
|
status: assessment.status,
|
|
465
510
|
decision: assessment.decision,
|
|
466
511
|
score: assessment.score,
|
|
@@ -490,6 +535,8 @@ export async function writeStaffingPlan(target, config, opts = {}) {
|
|
|
490
535
|
const gateBundle = await writeGateAssessment(target, config, {
|
|
491
536
|
stage: bundle.plan.stage,
|
|
492
537
|
module: opts.module,
|
|
538
|
+
issue: opts.issue,
|
|
539
|
+
reviewTarget: bundle.plan.reviewTarget,
|
|
493
540
|
threshold: opts.gateThreshold ?? gateThresholdForStage(config, bundle.plan.stage),
|
|
494
541
|
acceptRisk: opts.acceptRisk,
|
|
495
542
|
});
|
|
@@ -515,9 +562,11 @@ export function buildStaffingPlan(target, config, opts = {}, rolePool = BUILTIN_
|
|
|
515
562
|
const stage = normalizeStage(opts.stage ?? "prepare");
|
|
516
563
|
const skill = opts.skill ?? stage;
|
|
517
564
|
const surface = opts.surface ?? "auto";
|
|
565
|
+
const reviewTarget = stage === "review" ? normalizeReviewTargetPath(target, opts.reviewTarget) : undefined;
|
|
566
|
+
const reviewMode = reviewModeForReviewTarget(reviewTarget);
|
|
518
567
|
const timeoutPolicy = packetTimeoutPolicyFromConfig(config);
|
|
519
568
|
const entries = flattenProjectSources(config.sources);
|
|
520
|
-
const seeds = seedRoles(stage, skill, entries, rolePool);
|
|
569
|
+
const seeds = seedRoles(stage, skill, entries, rolePool, reviewMode, opts.module);
|
|
521
570
|
const orchestration = buildOrchestrationPlan(surface, stage, entries, seeds, config);
|
|
522
571
|
const roles = seeds.map((seed) => buildRole(seed, stage, skill, orchestration, config));
|
|
523
572
|
const dir = resolve(target, getWorkspacePath(config, "evidenceRoot"), "dispatch", runId);
|
|
@@ -542,6 +591,8 @@ export function buildStaffingPlan(target, config, opts = {}, rolePool = BUILTIN_
|
|
|
542
591
|
stage,
|
|
543
592
|
skill,
|
|
544
593
|
...(opts.module ? { module: opts.module } : {}),
|
|
594
|
+
...(reviewTarget ? { reviewTarget } : {}),
|
|
595
|
+
...(reviewMode ? { reviewMode } : {}),
|
|
545
596
|
surface,
|
|
546
597
|
orchestration,
|
|
547
598
|
uncertaintyPolicy: [
|
|
@@ -616,7 +667,7 @@ function markPacketStale(packet, now) {
|
|
|
616
667
|
blocker: "No heartbeat was recorded before the packet deadline; route this lane through leader-sequential fallback or re-emit with a new packet.",
|
|
617
668
|
};
|
|
618
669
|
}
|
|
619
|
-
function seedRoles(stage, skill, entries, rolePool) {
|
|
670
|
+
function seedRoles(stage, skill, entries, rolePool, reviewMode, module) {
|
|
620
671
|
const seeds = new Map();
|
|
621
672
|
const poolRoles = Object.values(rolePool.roles);
|
|
622
673
|
for (const role of poolRoles) {
|
|
@@ -661,6 +712,109 @@ function seedRoles(stage, skill, entries, rolePool) {
|
|
|
661
712
|
}
|
|
662
713
|
}
|
|
663
714
|
}
|
|
715
|
+
if (stage === "preproposal" || skill === "preproposal") {
|
|
716
|
+
const topic = module ?? "{topic}";
|
|
717
|
+
const moduleInputs = module
|
|
718
|
+
? [
|
|
719
|
+
`.zsk/modules/${module}/module.yaml`,
|
|
720
|
+
`.zsk/modules/${module}/CONTEXT.md`,
|
|
721
|
+
`.zsk/modules/${module}/proposal.md when present`,
|
|
722
|
+
`.zsk/modules/${module}/spec.md when present`,
|
|
723
|
+
`.zsk/modules/${module}/design.md when present`,
|
|
724
|
+
`.zsk/modules/${module}/tasks.md when present`,
|
|
725
|
+
]
|
|
726
|
+
: [];
|
|
727
|
+
for (const roleName of PREPROPOSAL_ROLE_NAMES) {
|
|
728
|
+
const role = rolePool.roles[roleName];
|
|
729
|
+
if (!role)
|
|
730
|
+
continue;
|
|
731
|
+
addSeed(seeds, seedFromRolePool(role, [], {
|
|
732
|
+
activation: "required for preproposal product/roadmap/UX/readiness checkpoint coverage",
|
|
733
|
+
reason: module
|
|
734
|
+
? role.reason || `produce and review module-scoped preproposal raw resources for ${module} before formal proposal`
|
|
735
|
+
: role.reason || "produce and review preproposal raw resources before formal proposal",
|
|
736
|
+
writeScope: [
|
|
737
|
+
".zsk/raws/prepare/product/**",
|
|
738
|
+
".zsk/raws/prepare/ux/**",
|
|
739
|
+
".zsk/raws/prepare/design/**",
|
|
740
|
+
".zsk/evidence/preproposal/**",
|
|
741
|
+
".zsk/raws/manifest.json",
|
|
742
|
+
".zsk/raws/index.md",
|
|
743
|
+
...role.writeScope,
|
|
744
|
+
],
|
|
745
|
+
outputs: [
|
|
746
|
+
`.zsk/raws/prepare/product/${topic}/intake-clarity.md`,
|
|
747
|
+
`.zsk/raws/prepare/product/${topic}/product-brief.md`,
|
|
748
|
+
`.zsk/raws/prepare/product/${topic}/roadmap.md`,
|
|
749
|
+
`.zsk/raws/prepare/ux/${topic}/ux-readiness.md`,
|
|
750
|
+
`.zsk/raws/prepare/ux/${topic}/interaction-handoff.md when UI/UX/design-source interaction is triggered`,
|
|
751
|
+
`.zsk/raws/prepare/design/${topic}/design-source-needs.md`,
|
|
752
|
+
`.zsk/raws/prepare/design/${topic}/design-source-map.md when provider-backed design sources are used`,
|
|
753
|
+
".zsk/evidence/preproposal/{run}/checkpoint-*.md",
|
|
754
|
+
".zsk/evidence/preproposal/{run}/readiness-review.md",
|
|
755
|
+
...role.outputs,
|
|
756
|
+
],
|
|
757
|
+
evidenceRequired: [
|
|
758
|
+
...(module ? [
|
|
759
|
+
`module target is fixed to ${module}; module docs and raw sources are challenged before widening scope`,
|
|
760
|
+
`interaction handoff and design-source map use ${module} as the default raw topic unless a narrower page/topic is justified`,
|
|
761
|
+
] : []),
|
|
762
|
+
"source-discoverable questions answered before user clarification",
|
|
763
|
+
"accepted clarifications, inferred assumptions, and one blocking question are separated",
|
|
764
|
+
"product checkpoint passes before roadmap/decomposition",
|
|
765
|
+
"roadmap/decomposition checkpoint passes before UX/design-readiness",
|
|
766
|
+
"AI brief drafts and candidate code component mapping exist for source-backed page/module interaction details before asking humans to fill gaps",
|
|
767
|
+
"interaction handoff separates sourced provider facts from accepted decisions, assumptions, and missing page/module interaction details when triggered",
|
|
768
|
+
"UX/design-readiness checkpoint passes before readiness handoff",
|
|
769
|
+
"final readiness review passes or blocks with owner, missing input, impact, and next action",
|
|
770
|
+
...role.evidenceRequired,
|
|
771
|
+
],
|
|
772
|
+
...(moduleInputs.length > 0 ? { extraInputs: moduleInputs } : {}),
|
|
773
|
+
contract: {
|
|
774
|
+
owns: module
|
|
775
|
+
? [`preproposal lane facts, source gaps, checkpoint evidence, and readiness handoff for module ${module}`]
|
|
776
|
+
: ["preproposal lane facts, source gaps, checkpoint evidence, and readiness handoff for this role"],
|
|
777
|
+
doesNotOwn: ["formal module proposal/spec/design/task artifacts", "formal test cases", "implementation edits"],
|
|
778
|
+
forbiddenDecisions: ["Do not skip checkpoint order.", "Do not treat scenario seeds as formal post-design test cases."],
|
|
779
|
+
stopCondition: "Preproposal lane output is reviewable, or blockers are recorded for the checkpoint owner.",
|
|
780
|
+
},
|
|
781
|
+
}));
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
if (stage === "review" && reviewMode && PREPROPOSAL_REVIEW_MODES.has(reviewMode)) {
|
|
785
|
+
for (const roleName of PREPROPOSAL_ROLE_NAMES) {
|
|
786
|
+
const role = rolePool.roles[roleName];
|
|
787
|
+
if (!role)
|
|
788
|
+
continue;
|
|
789
|
+
addSeed(seeds, seedFromRolePool(role, [], {
|
|
790
|
+
activation: `required for ${reviewMode} review target checkpoint coverage`,
|
|
791
|
+
reason: role.reason || `review ${reviewMode} target for product, roadmap, UX, source, and readiness gaps before formal proposal`,
|
|
792
|
+
writeScope: [
|
|
793
|
+
".zsk/evidence/review/**",
|
|
794
|
+
".zsk/issues/**",
|
|
795
|
+
...role.writeScope,
|
|
796
|
+
],
|
|
797
|
+
outputs: [
|
|
798
|
+
".zsk/evidence/review/{run}/target-review.md",
|
|
799
|
+
".zsk/evidence/review/{run}/checkpoint-findings.md",
|
|
800
|
+
...role.outputs,
|
|
801
|
+
],
|
|
802
|
+
evidenceRequired: [
|
|
803
|
+
"explicit review-target path and review mode",
|
|
804
|
+
"product, roadmap/decomposition, UX/design-readiness, assumption, risk, source, and blocker checks where relevant",
|
|
805
|
+
"findings grouped by role with pass/blocker/waived result and owner",
|
|
806
|
+
"confirmation that missing module spec/design/tasks/smoke are not blockers for raw/preproposal target review",
|
|
807
|
+
...role.evidenceRequired,
|
|
808
|
+
],
|
|
809
|
+
contract: {
|
|
810
|
+
owns: ["role-specific findings for the explicit raw/preproposal review target"],
|
|
811
|
+
doesNotOwn: ["rewriting raw resources during review", "formal module proposal/spec/design/task production", "implementation edits"],
|
|
812
|
+
forbiddenDecisions: ["Do not require module spec/design/tasks/smoke for raw/preproposal target review.", "Do not silently pass missing product/UX/readiness evidence."],
|
|
813
|
+
stopCondition: "Target findings are reviewable, or blockers identify owner, missing input, impact, and next action.",
|
|
814
|
+
},
|
|
815
|
+
}));
|
|
816
|
+
}
|
|
817
|
+
}
|
|
664
818
|
return [...seeds.values()];
|
|
665
819
|
}
|
|
666
820
|
function addSeed(seeds, seed) {
|
|
@@ -697,6 +851,7 @@ function buildOrchestrationPlan(surface, stage, entries, seeds, config) {
|
|
|
697
851
|
const remoteCount = entries.filter((entry) => inferSourceOrigin(entry.source).remote).length;
|
|
698
852
|
const activeSeedCount = seeds.filter((seed) => seedIsActive(seed, stage)).length;
|
|
699
853
|
const stageParallelNeed = (policy.strictReview && ["review", "verify"].includes(stage)) ||
|
|
854
|
+
(stage === "preproposal" && activeSeedCount > 2) ||
|
|
700
855
|
(["prepare", "design"].includes(stage) && activeSeedCount > 2);
|
|
701
856
|
const hasIndependentLanes = lanes.size > 1 ||
|
|
702
857
|
remoteCount > 0 ||
|
|
@@ -1022,6 +1177,8 @@ function renderStaffingPlanMarkdown(plan, timeoutPolicy) {
|
|
|
1022
1177
|
`- Stage: \`${plan.stage}\``,
|
|
1023
1178
|
`- Skill: \`${plan.skill}\``,
|
|
1024
1179
|
...(plan.module ? [`- Module: \`${plan.module}\``] : []),
|
|
1180
|
+
...(plan.reviewTarget ? [`- Review target: \`${plan.reviewTarget}\``] : []),
|
|
1181
|
+
...(plan.reviewMode ? [`- Review mode: \`${plan.reviewMode}\``] : []),
|
|
1025
1182
|
`- Surface: \`${plan.surface}\``,
|
|
1026
1183
|
`- Roles: ${plan.roles.length}`,
|
|
1027
1184
|
`- Active emit lanes: ${plan.emitPackets.length}`,
|