@captain_z/zsk 1.8.3 → 1.8.5

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.
Files changed (143) hide show
  1. package/dist/bin.js +13 -0
  2. package/dist/bin.js.map +1 -1
  3. package/dist/commands/add-flow.d.ts +3 -7
  4. package/dist/commands/add-flow.js +7 -59
  5. package/dist/commands/add-flow.js.map +1 -1
  6. package/dist/commands/add.js +25 -104
  7. package/dist/commands/add.js.map +1 -1
  8. package/dist/commands/check.js +14 -575
  9. package/dist/commands/check.js.map +1 -1
  10. package/dist/commands/config.js +1 -1
  11. package/dist/commands/config.js.map +1 -1
  12. package/dist/commands/demo.d.ts +5 -0
  13. package/dist/commands/demo.js +70 -297
  14. package/dist/commands/demo.js.map +1 -1
  15. package/dist/commands/doctor.js +9 -4
  16. package/dist/commands/doctor.js.map +1 -1
  17. package/dist/commands/gate.d.ts +1 -0
  18. package/dist/commands/gate.js +8 -2
  19. package/dist/commands/gate.js.map +1 -1
  20. package/dist/commands/prepare.js +7 -1
  21. package/dist/commands/prepare.js.map +1 -1
  22. package/dist/commands/project-init.js +30 -8
  23. package/dist/commands/project-init.js.map +1 -1
  24. package/dist/core/config.d.ts +68 -0
  25. package/dist/core/config.js +198 -15
  26. package/dist/core/config.js.map +1 -1
  27. package/dist/core/demo-auth.d.ts +30 -0
  28. package/dist/core/demo-auth.js +213 -0
  29. package/dist/core/demo-auth.js.map +1 -0
  30. package/dist/core/demo-scenarios.d.ts +62 -0
  31. package/dist/core/demo-scenarios.js +276 -0
  32. package/dist/core/demo-scenarios.js.map +1 -0
  33. package/dist/core/demo-sources.d.ts +37 -0
  34. package/dist/core/demo-sources.js +198 -0
  35. package/dist/core/demo-sources.js.map +1 -0
  36. package/dist/core/mcp-registry-discovery.d.ts +16 -0
  37. package/dist/core/mcp-registry-discovery.js +187 -0
  38. package/dist/core/mcp-registry-discovery.js.map +1 -0
  39. package/dist/core/origin-detection.js +1 -1
  40. package/dist/core/origin-detection.js.map +1 -1
  41. package/dist/core/prepare-artifacts.d.ts +16 -0
  42. package/dist/core/prepare-artifacts.js +25 -0
  43. package/dist/core/prepare-artifacts.js.map +1 -0
  44. package/dist/core/prepare-auth-helper.d.ts +8 -0
  45. package/dist/core/prepare-auth-helper.js +32 -0
  46. package/dist/core/prepare-auth-helper.js.map +1 -0
  47. package/dist/core/prepare-materialization.d.ts +8 -0
  48. package/dist/core/prepare-materialization.js +26 -0
  49. package/dist/core/prepare-materialization.js.map +1 -0
  50. package/dist/core/prepare-migration.d.ts +6 -0
  51. package/dist/core/prepare-migration.js +57 -0
  52. package/dist/core/prepare-migration.js.map +1 -0
  53. package/dist/core/prepare-reporting.d.ts +5 -0
  54. package/dist/core/prepare-reporting.js +106 -0
  55. package/dist/core/prepare-reporting.js.map +1 -0
  56. package/dist/core/prepare-routing.d.ts +12 -0
  57. package/dist/core/prepare-routing.js +182 -0
  58. package/dist/core/prepare-routing.js.map +1 -0
  59. package/dist/core/prepare-sync.d.ts +13 -53
  60. package/dist/core/prepare-sync.js +878 -359
  61. package/dist/core/prepare-sync.js.map +1 -1
  62. package/dist/core/prepare-utils.d.ts +6 -0
  63. package/dist/core/prepare-utils.js +35 -0
  64. package/dist/core/prepare-utils.js.map +1 -0
  65. package/dist/core/profile-bundle-installation.d.ts +55 -0
  66. package/dist/core/profile-bundle-installation.js +170 -0
  67. package/dist/core/profile-bundle-installation.js.map +1 -0
  68. package/dist/core/provider-policy.d.ts +26 -0
  69. package/dist/core/provider-policy.js +180 -0
  70. package/dist/core/provider-policy.js.map +1 -0
  71. package/dist/core/provider-readiness.d.ts +39 -0
  72. package/dist/core/provider-readiness.js +78 -0
  73. package/dist/core/provider-readiness.js.map +1 -0
  74. package/dist/core/source-adapter-normalization.d.ts +31 -0
  75. package/dist/core/source-adapter-normalization.js +235 -0
  76. package/dist/core/source-adapter-normalization.js.map +1 -0
  77. package/dist/core/source-snapshot-adapters.d.ts +59 -0
  78. package/dist/core/source-snapshot-adapters.js +60 -0
  79. package/dist/core/source-snapshot-adapters.js.map +1 -0
  80. package/dist/core/staffing-plan.d.ts +1 -0
  81. package/dist/core/staffing-plan.js +113 -21
  82. package/dist/core/staffing-plan.js.map +1 -1
  83. package/dist/core/stage-clarity-verification.d.ts +31 -0
  84. package/dist/core/stage-clarity-verification.js +316 -0
  85. package/dist/core/stage-clarity-verification.js.map +1 -0
  86. package/dist/core/stage-output-quality.d.ts +3 -0
  87. package/dist/core/stage-output-quality.js +122 -0
  88. package/dist/core/stage-output-quality.js.map +1 -0
  89. package/dist/core/stage-quality-artifacts.d.ts +15 -0
  90. package/dist/core/stage-quality-artifacts.js +421 -0
  91. package/dist/core/stage-quality-artifacts.js.map +1 -0
  92. package/dist/core/stage-quality-contracts.d.ts +105 -0
  93. package/dist/core/stage-quality-contracts.js +2 -0
  94. package/dist/core/stage-quality-contracts.js.map +1 -0
  95. package/dist/core/stage-quality-criteria.d.ts +9 -0
  96. package/dist/core/stage-quality-criteria.js +286 -0
  97. package/dist/core/stage-quality-criteria.js.map +1 -0
  98. package/dist/core/stage-quality-rendering.d.ts +15 -0
  99. package/dist/core/stage-quality-rendering.js +240 -0
  100. package/dist/core/stage-quality-rendering.js.map +1 -0
  101. package/dist/core/stage-quality.d.ts +4 -59
  102. package/dist/core/stage-quality.js +54 -795
  103. package/dist/core/stage-quality.js.map +1 -1
  104. package/dist/core/template-registry.js +12 -15
  105. package/dist/core/template-registry.js.map +1 -1
  106. package/dist/core/workspace-conformance.d.ts +39 -0
  107. package/dist/core/workspace-conformance.js +603 -0
  108. package/dist/core/workspace-conformance.js.map +1 -0
  109. package/package.json +2 -2
  110. package/schemas/providers.schema.json +74 -0
  111. package/schemas/zsk-config.schema.json +417 -1
  112. package/templates/module/frontend-module/design.md +10 -0
  113. package/templates/module/frontend-module/proposal.md +8 -0
  114. package/templates/module/frontend-module/spec.md +7 -0
  115. package/templates/project-init/.zsk/README.md +48 -0
  116. package/templates/project-init/.zsk/config.yaml +37 -0
  117. package/templates/project-init/.zsk/docs/CONFIG-SCHEMA.md +127 -0
  118. package/templates/project-init/.zsk/docs/PROJECT-CONFIG.md +53 -5
  119. package/templates/project-init/.zsk/docs/README.md +10 -0
  120. package/templates/project-init/.zsk/docs/SECURITY.md +34 -0
  121. package/templates/project-init/.zsk/docs/SYSTEM-SPEC.md +39 -7
  122. package/templates/project-init/.zsk/evidence/README.md +15 -0
  123. package/templates/project-init/.zsk/issues/README.md +10 -0
  124. package/templates/project-init/.zsk/modules/README.md +19 -0
  125. package/templates/project-init/.zsk/modules/index.md +9 -5
  126. package/templates/project-init/.zsk/raws/README.md +34 -0
  127. package/templates/project-init/.zsk/roles.yaml +36 -105
  128. package/templates/project-init/.zsk/templates/config.examples.yaml +83 -0
  129. package/templates/project-init/.zsk/templates/issue-card.md +23 -0
  130. package/templates/project-init/.zsk/templates/module/README.md +13 -0
  131. package/templates/project-init/.zsk/templates/module/design.md +22 -0
  132. package/templates/project-init/.zsk/templates/module/module.yaml +15 -0
  133. package/templates/project-init/.zsk/templates/module/proposal.md +20 -0
  134. package/templates/project-init/.zsk/templates/module/spec.md +22 -0
  135. package/templates/project-init/.zsk/templates/module/tasks.md +16 -0
  136. package/templates/project-init/.zsk/raws/index.md +0 -18
  137. package/templates/project-init/.zsk/raws/manifest.json +0 -4
  138. package/templates/project-init/.zsk/raws/prepare/backend/index.md +0 -4
  139. package/templates/project-init/.zsk/raws/prepare/design/index.md +0 -3
  140. package/templates/project-init/.zsk/raws/prepare/index.md +0 -4
  141. package/templates/project-init/.zsk/raws/prepare/product/index.md +0 -4
  142. package/templates/project-init/.zsk/raws/prepare/qa/index.md +0 -4
  143. package/templates/project-init/.zsk/raws/prepare/ux/index.md +0 -3
@@ -0,0 +1,59 @@
1
+ import { type ProjectConfig, type SourceConfig, type SourceEntry } from "./config.js";
2
+ import { type OriginInference } from "./origin-detection.js";
3
+ import { type SourceSnapshotAdapterId } from "./source-adapter-normalization.js";
4
+ export type PrepareSyncOptions = {
5
+ source?: string;
6
+ all?: boolean;
7
+ allowNetwork?: boolean;
8
+ authState?: string;
9
+ authProfile?: string;
10
+ browser?: boolean;
11
+ dryRun?: boolean;
12
+ runId?: string;
13
+ };
14
+ export type PrepareSyncStatus = "materialized" | "materialized-partial" | "metadata-only" | "blocked-auth" | "source-gap" | "skipped" | "failed";
15
+ export type PrepareSyncResult = {
16
+ envelopeVersion?: 1;
17
+ sourceKey: string;
18
+ sourcePath: string;
19
+ rawLane?: string;
20
+ provider?: string;
21
+ adapter?: string;
22
+ strategy: string;
23
+ status: PrepareSyncStatus;
24
+ origin?: string;
25
+ resolvedOrigin?: string;
26
+ contentType?: string;
27
+ snapshot?: string;
28
+ snapshotHash?: string;
29
+ previousSnapshotHash?: string;
30
+ metadata?: Record<string, unknown>;
31
+ changed: boolean;
32
+ reason: string;
33
+ validation: Record<string, "pass" | "fail" | "skipped">;
34
+ };
35
+ export type PrepareSyncBase = Omit<PrepareSyncResult, "strategy" | "status" | "changed" | "reason" | "validation">;
36
+ export type SourceSnapshotProviderAdapter = SourceSnapshotAdapterId;
37
+ export type SourceSnapshotContext = {
38
+ target: string;
39
+ config: ProjectConfig;
40
+ entry: SourceEntry;
41
+ source: SourceConfig;
42
+ origin: OriginInference;
43
+ opts: PrepareSyncOptions;
44
+ snapshot: string;
45
+ snapshotPath: string;
46
+ previousSnapshotHash?: string;
47
+ base: PrepareSyncBase;
48
+ };
49
+ export type SourceSnapshotAdapterHandlers = {
50
+ dryRun: (context: SourceSnapshotContext) => Promise<PrepareSyncResult> | PrepareSyncResult;
51
+ local: (context: SourceSnapshotContext) => Promise<PrepareSyncResult>;
52
+ provider: (context: SourceSnapshotContext, adapter: SourceSnapshotProviderAdapter) => Promise<PrepareSyncResult | null>;
53
+ repository: (context: SourceSnapshotContext) => Promise<PrepareSyncResult>;
54
+ url: (context: SourceSnapshotContext) => Promise<PrepareSyncResult>;
55
+ providerManaged: (context: SourceSnapshotContext) => Promise<PrepareSyncResult> | PrepareSyncResult;
56
+ };
57
+ export declare function createSourceSnapshotContext(target: string, config: ProjectConfig, entry: SourceEntry, opts: PrepareSyncOptions, previousSnapshotHashFor: (snapshotPath: string) => Promise<string | undefined>): Promise<SourceSnapshotContext>;
58
+ export declare function acquireSourceSnapshot(context: SourceSnapshotContext, handlers: SourceSnapshotAdapterHandlers): Promise<PrepareSyncResult>;
59
+ export declare function chooseSourceSnapshotStrategy(method: string): string;
@@ -0,0 +1,60 @@
1
+ import { resolve } from "node:path";
2
+ import { resolveSourceSnapshot, } from "./config.js";
3
+ import { inferSourceOrigin } from "./origin-detection.js";
4
+ import { selectSourceSnapshotAdapter, } from "./source-adapter-normalization.js";
5
+ export async function createSourceSnapshotContext(target, config, entry, opts, previousSnapshotHashFor) {
6
+ const source = entry.source;
7
+ const origin = inferSourceOrigin(source);
8
+ const snapshot = resolveSourceSnapshot(config, entry);
9
+ const snapshotPath = resolve(target, snapshot);
10
+ const previousSnapshotHash = await previousSnapshotHashFor(snapshotPath);
11
+ const base = {
12
+ envelopeVersion: 1,
13
+ sourceKey: entry.id,
14
+ sourcePath: entry.path,
15
+ rawLane: entry.rawLane,
16
+ provider: origin.provider,
17
+ origin: origin.ref,
18
+ snapshot,
19
+ previousSnapshotHash,
20
+ };
21
+ return {
22
+ target,
23
+ config,
24
+ entry,
25
+ source,
26
+ origin,
27
+ opts,
28
+ snapshot,
29
+ snapshotPath,
30
+ previousSnapshotHash,
31
+ base,
32
+ };
33
+ }
34
+ export async function acquireSourceSnapshot(context, handlers) {
35
+ if (context.opts.dryRun)
36
+ return handlers.dryRun(context);
37
+ if (context.origin.method === "local")
38
+ return handlers.local(context);
39
+ const providerAdapter = selectSourceSnapshotAdapter(context.source, context.origin);
40
+ if (providerAdapter) {
41
+ const result = await handlers.provider(context, providerAdapter);
42
+ if (result)
43
+ return result;
44
+ }
45
+ if (context.origin.method === "repository")
46
+ return handlers.repository(context);
47
+ if (context.origin.method === "url")
48
+ return handlers.url(context);
49
+ return handlers.providerManaged(context);
50
+ }
51
+ export function chooseSourceSnapshotStrategy(method) {
52
+ if (method === "local")
53
+ return "local-copy-structured-markdown";
54
+ if (method === "repository")
55
+ return "repository-metadata-only";
56
+ if (method === "url")
57
+ return "playwright-auth-or-direct-fetch";
58
+ return "confirm-acquisition-method";
59
+ }
60
+ //# 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":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EACL,qBAAqB,GAItB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,iBAAiB,EAAwB,MAAM,uBAAuB,CAAC;AAChF,OAAO,EACL,2BAA2B,GAE5B,MAAM,mCAAmC,CAAC;AAwE3C,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAC/C,MAAc,EACd,MAAqB,EACrB,KAAkB,EAClB,IAAwB,EACxB,uBAA8E;IAE9E,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;IAC5B,MAAM,MAAM,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAC;IACzC,MAAM,QAAQ,GAAG,qBAAqB,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACtD,MAAM,YAAY,GAAG,OAAO,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAC/C,MAAM,oBAAoB,GAAG,MAAM,uBAAuB,CAAC,YAAY,CAAC,CAAC;IACzE,MAAM,IAAI,GAAoB;QAC5B,eAAe,EAAE,CAAC;QAClB,SAAS,EAAE,KAAK,CAAC,EAAE;QACnB,UAAU,EAAE,KAAK,CAAC,IAAI;QACtB,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,MAAM,EAAE,MAAM,CAAC,GAAG;QAClB,QAAQ;QACR,oBAAoB;KACrB,CAAC;IACF,OAAO;QACL,MAAM;QACN,MAAM;QACN,KAAK;QACL,MAAM;QACN,MAAM;QACN,IAAI;QACJ,QAAQ;QACR,YAAY;QACZ,oBAAoB;QACpB,IAAI;KACL,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,OAA8B,EAC9B,QAAuC;IAEvC,IAAI,OAAO,CAAC,IAAI,CAAC,MAAM;QAAE,OAAO,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACzD,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,KAAK,OAAO;QAAE,OAAO,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAEtE,MAAM,eAAe,GAAG,2BAA2B,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IACpF,IAAI,eAAe,EAAE,CAAC;QACpB,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC;QACjE,IAAI,MAAM;YAAE,OAAO,MAAM,CAAC;IAC5B,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,KAAK,YAAY;QAAE,OAAO,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;IAChF,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,KAAK,KAAK;QAAE,OAAO,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAClE,OAAO,QAAQ,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,UAAU,4BAA4B,CAAC,MAAc;IACzD,IAAI,MAAM,KAAK,OAAO;QAAE,OAAO,gCAAgC,CAAC;IAChE,IAAI,MAAM,KAAK,YAAY;QAAE,OAAO,0BAA0B,CAAC;IAC/D,IAAI,MAAM,KAAK,KAAK;QAAE,OAAO,iCAAiC,CAAC;IAC/D,OAAO,4BAA4B,CAAC;AACtC,CAAC"}
@@ -188,6 +188,7 @@ type RoleContract = {
188
188
  };
189
189
  type RolePoolRole = {
190
190
  role: string;
191
+ baseRole?: string;
191
192
  subagentType?: string;
192
193
  reason?: string;
193
194
  stages: string[];
@@ -17,6 +17,7 @@ const PREPROPOSAL_ROLE_NAMES = [
17
17
  "planner",
18
18
  "ux-specialist",
19
19
  "design-specialist",
20
+ "frontend-engineer",
20
21
  "architect",
21
22
  "qa-engineer",
22
23
  "researcher",
@@ -69,14 +70,14 @@ const BUILTIN_ROLE_POOL = {
69
70
  }),
70
71
  rolePoolEntry("frontend-engineer", {
71
72
  subagentType: "executor",
72
- stages: ["design"],
73
+ stages: ["preproposal", "design"],
73
74
  lanes: ["frontend", "web", "client", "mobile"],
74
75
  activation: "when frontend implementation, routing, state, or UI integration is in scope",
75
76
  reason: "map UI implementation, routing, state, and component integration risk",
76
77
  }),
77
78
  rolePoolEntry("design-specialist", {
78
79
  subagentType: "designer",
79
- stages: ["prepare", "preproposal", "design"],
80
+ stages: ["prepare", "preproposal"],
80
81
  lanes: ["design", "ui", "visual"],
81
82
  activation: "when design, UI, visual, prototype, or asset sources are configured",
82
83
  reason: "inspect design assets, screens, visual states, and design-source gaps",
@@ -173,10 +174,12 @@ const ROLE_CONTRACTS = {
173
174
  ".zsk/evidence/{scope}/{run}/integration-summary.md",
174
175
  ".zsk/raws/manifest.json when prepare snapshots change",
175
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",
176
178
  ".zsk/evidence/preproposal/{run}/readiness-review.md when running preproposal",
177
179
  ],
178
180
  evidenceRequired: [
179
181
  "integrated lane status with pass/blocker/waived reason for each role",
182
+ "source-discoverable questions answered before user clarification when running preproposal",
180
183
  "confirmation that shared/global artifacts were updated only after lane validation",
181
184
  ],
182
185
  forbiddenDecisions: [
@@ -190,10 +193,11 @@ const ROLE_CONTRACTS = {
190
193
  doesNotOwn: ["backend implementation details", "technical feasibility signoff without architect/engineer evidence"],
191
194
  outputs: [
192
195
  ".zsk/evidence/{scope}/{run}/product-source-report.md",
196
+ ".zsk/raws/prepare/product/{topic}/intake-clarity.md when running preproposal",
193
197
  ".zsk/raws/prepare/product/{topic}/product-brief.md when running preproposal",
194
198
  ".zsk/evidence/preproposal/{run}/checkpoint-product-review.md when running preproposal",
195
199
  ],
196
- evidenceRequired: ["requirement/source IDs or paths behind each product claim", "open requirement gaps and acceptance risks", "product checkpoint verdict before roadmap/decomposition when running preproposal"],
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"],
197
201
  forbiddenDecisions: ["Do not rewrite technical design or implementation ownership.", "Do not turn weak/inferred evidence into accepted requirements."],
198
202
  stopCondition: "Product facts, gaps, and acceptance risks are explicit and handed to lead-integrator.",
199
203
  },
@@ -229,33 +233,36 @@ const ROLE_CONTRACTS = {
229
233
  stopCondition: "Backend facts and gaps are reported with source references and no shared artifact edits pending.",
230
234
  },
231
235
  "frontend-engineer": {
232
- 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"],
233
237
  doesNotOwn: ["source-of-truth product decisions", "backend/API correctness signoff"],
234
238
  outputs: [".zsk/evidence/{scope}/{run}/frontend-impact-report.md"],
235
- 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"],
236
240
  forbiddenDecisions: ["Do not reinterpret acceptance criteria without product-owner handoff.", "Do not approve API behavior without backend evidence."],
237
241
  stopCondition: "Frontend impact and validation needs are clear for lead-integrator.",
238
242
  },
239
243
  "design-specialist": {
240
- 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"],
241
245
  doesNotOwn: ["backend/API correctness", "final product scope acceptance"],
242
246
  outputs: [
243
247
  ".zsk/evidence/{scope}/{run}/design-asset-report.md",
244
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",
245
250
  ],
246
- evidenceRequired: ["design/prototype source references", "screen/state map and missing asset list"],
251
+ evidenceRequired: ["design/prototype source references", "screen/state map, provider source coverage, and missing asset list"],
247
252
  forbiddenDecisions: ["Do not infer implementation truth from design assets.", "Do not store auth state or exported private assets outside declared scope."],
248
253
  stopCondition: "Design assets are mapped or blocked with provider/auth evidence.",
249
254
  },
250
255
  "ux-specialist": {
251
- 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"],
252
257
  doesNotOwn: ["backend/API correctness", "business priority decisions"],
253
258
  outputs: [
254
259
  ".zsk/evidence/{scope}/{run}/ux-source-report.md",
255
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",
256
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",
257
264
  ],
258
- evidenceRequired: ["flow/source references", "usability risks and unresolved interaction questions"],
265
+ evidenceRequired: ["flow/source references", "AI brief draft status", "page/module interaction detail coverage", "interaction/state/a11y coverage", "usability risks and unresolved interaction questions"],
259
266
  forbiddenDecisions: ["Do not approve product scope or technical design alone.", "Do not resolve UX/product conflicts silently."],
260
267
  stopCondition: "UX facts, questions, and risks are ready for lead integration.",
261
268
  },
@@ -378,7 +385,7 @@ async function readStaffingRolePool(target, config) {
378
385
  if (config.staffing?.roles) {
379
386
  pools.push(parseRolePool({ roles: config.staffing.roles }));
380
387
  }
381
- return mergeRolePools(...pools);
388
+ return resolveRoleInheritance(mergeRolePools(...pools));
382
389
  }
383
390
  function parseRolePool(value) {
384
391
  const rolesValue = isRecord(value) ? value.roles : undefined;
@@ -389,16 +396,24 @@ function parseRolePool(value) {
389
396
  if (!isRecord(config))
390
397
  continue;
391
398
  entries.push(rolePoolEntry(role, {
399
+ baseRole: stringValue(config.extends),
392
400
  subagentType: stringValue(config.subagentType),
393
401
  reason: stringValue(config.reason),
394
402
  stages: stringList(config.stages),
395
403
  skills: stringList(config.skills),
396
- lanes: stringList(config.lanes),
404
+ lanes: unique([
405
+ ...stringList(config.lanes),
406
+ ...stringList(config.resources),
407
+ ...stringList(config.artifacts),
408
+ ]),
397
409
  required: booleanValue(config.required),
398
410
  remoteSources: booleanValue(config.remoteSources),
399
411
  activation: stringValue(config.activation),
400
412
  extraInputs: stringList(config.inputs),
401
- writeScope: stringList(config.writeScope),
413
+ writeScope: unique([
414
+ ...stringList(config.writeScope),
415
+ ...stringList(config.writeScopes),
416
+ ]),
402
417
  outputs: stringList(config.outputs),
403
418
  evidenceRequired: stringList(config.evidenceRequired),
404
419
  contract: {
@@ -424,6 +439,7 @@ function mergeRolePools(...pools) {
424
439
  function mergeRolePoolRole(base, override) {
425
440
  return {
426
441
  role: base.role,
442
+ baseRole: override.baseRole ?? base.baseRole,
427
443
  subagentType: override.subagentType ?? base.subagentType,
428
444
  reason: override.reason ?? base.reason,
429
445
  stages: unique([...base.stages, ...override.stages]),
@@ -442,6 +458,7 @@ function mergeRolePoolRole(base, override) {
442
458
  function rolePoolEntry(role, config) {
443
459
  return [role, {
444
460
  role,
461
+ baseRole: config.baseRole,
445
462
  subagentType: config.subagentType,
446
463
  reason: config.reason,
447
464
  stages: normalizeList(config.stages ?? []),
@@ -457,6 +474,54 @@ function rolePoolEntry(role, config) {
457
474
  contract: config.contract,
458
475
  }];
459
476
  }
477
+ function resolveRoleInheritance(pool) {
478
+ const resolving = new Set();
479
+ const resolved = new Map();
480
+ const resolveRole = (name) => {
481
+ const cached = resolved.get(name);
482
+ if (cached)
483
+ return cached;
484
+ const role = pool.roles[name];
485
+ if (!role)
486
+ return undefined;
487
+ if (!role.baseRole) {
488
+ resolved.set(name, role);
489
+ return role;
490
+ }
491
+ if (resolving.has(name)) {
492
+ resolved.set(name, role);
493
+ return role;
494
+ }
495
+ resolving.add(name);
496
+ const base = resolveRole(role.baseRole);
497
+ resolving.delete(name);
498
+ const merged = base ? inheritRole(base, role) : role;
499
+ resolved.set(name, merged);
500
+ return merged;
501
+ };
502
+ for (const name of Object.keys(pool.roles))
503
+ resolveRole(name);
504
+ return { roles: Object.fromEntries(resolved) };
505
+ }
506
+ function inheritRole(base, role) {
507
+ return {
508
+ role: role.role,
509
+ baseRole: role.baseRole,
510
+ subagentType: role.subagentType ?? base.subagentType,
511
+ reason: role.reason ?? base.reason,
512
+ stages: unique([...base.stages, ...role.stages]),
513
+ skills: unique([...base.skills, ...role.skills]),
514
+ lanes: unique([...base.lanes, ...role.lanes]),
515
+ required: role.required,
516
+ remoteSources: base.remoteSources || role.remoteSources,
517
+ activation: role.activation ?? base.activation,
518
+ extraInputs: unique([...base.extraInputs, ...role.extraInputs]),
519
+ writeScope: unique([...base.writeScope, ...role.writeScope]),
520
+ outputs: unique([...base.outputs, ...role.outputs]),
521
+ evidenceRequired: unique([...base.evidenceRequired, ...role.evidenceRequired]),
522
+ contract: mergeContractOverrides(base.contract, role.contract),
523
+ };
524
+ }
460
525
  function seedFromRolePool(role, entries, overrides = {}) {
461
526
  return {
462
527
  role: role.role,
@@ -464,7 +529,7 @@ function seedFromRolePool(role, entries, overrides = {}) {
464
529
  reason: overrides.reason ?? role.reason ?? `configured ${role.role} role`,
465
530
  entries,
466
531
  activation: overrides.activation ?? role.activation,
467
- extraInputs: role.extraInputs,
532
+ extraInputs: unique([...(role.extraInputs ?? []), ...(overrides.extraInputs ?? [])]),
468
533
  writeScope: unique([...(role.writeScope ?? []), ...(overrides.writeScope ?? [])]),
469
534
  outputs: unique([...(role.outputs ?? []), ...(overrides.outputs ?? [])]),
470
535
  evidenceRequired: unique([...(role.evidenceRequired ?? []), ...(overrides.evidenceRequired ?? [])]),
@@ -559,7 +624,7 @@ export function buildStaffingPlan(target, config, opts = {}, rolePool = BUILTIN_
559
624
  const reviewMode = reviewModeForReviewTarget(reviewTarget);
560
625
  const timeoutPolicy = packetTimeoutPolicyFromConfig(config);
561
626
  const entries = flattenProjectSources(config.sources);
562
- const seeds = seedRoles(stage, skill, entries, rolePool, reviewMode);
627
+ const seeds = seedRoles(stage, skill, entries, rolePool, reviewMode, opts.module);
563
628
  const orchestration = buildOrchestrationPlan(surface, stage, entries, seeds, config);
564
629
  const roles = seeds.map((seed) => buildRole(seed, stage, skill, orchestration, config));
565
630
  const dir = resolve(target, getWorkspacePath(config, "evidenceRoot"), "dispatch", runId);
@@ -660,7 +725,7 @@ function markPacketStale(packet, now) {
660
725
  blocker: "No heartbeat was recorded before the packet deadline; route this lane through leader-sequential fallback or re-emit with a new packet.",
661
726
  };
662
727
  }
663
- function seedRoles(stage, skill, entries, rolePool, reviewMode) {
728
+ function seedRoles(stage, skill, entries, rolePool, reviewMode, module) {
664
729
  const seeds = new Map();
665
730
  const poolRoles = Object.values(rolePool.roles);
666
731
  for (const role of poolRoles) {
@@ -706,13 +771,26 @@ function seedRoles(stage, skill, entries, rolePool, reviewMode) {
706
771
  }
707
772
  }
708
773
  if (stage === "preproposal" || skill === "preproposal") {
774
+ const topic = module ?? "{topic}";
775
+ const moduleInputs = module
776
+ ? [
777
+ `.zsk/modules/${module}/module.yaml`,
778
+ `.zsk/modules/${module}/CONTEXT.md`,
779
+ `.zsk/modules/${module}/proposal.md when present`,
780
+ `.zsk/modules/${module}/spec.md when present`,
781
+ `.zsk/modules/${module}/design.md when present`,
782
+ `.zsk/modules/${module}/tasks.md when present`,
783
+ ]
784
+ : [];
709
785
  for (const roleName of PREPROPOSAL_ROLE_NAMES) {
710
786
  const role = rolePool.roles[roleName];
711
787
  if (!role)
712
788
  continue;
713
789
  addSeed(seeds, seedFromRolePool(role, [], {
714
790
  activation: "required for preproposal product/roadmap/UX/readiness checkpoint coverage",
715
- reason: role.reason || "produce and review preproposal raw resources before formal proposal",
791
+ reason: module
792
+ ? role.reason || `produce and review module-scoped preproposal raw resources for ${module} before formal proposal`
793
+ : role.reason || "produce and review preproposal raw resources before formal proposal",
716
794
  writeScope: [
717
795
  ".zsk/raws/prepare/product/**",
718
796
  ".zsk/raws/prepare/ux/**",
@@ -723,23 +801,37 @@ function seedRoles(stage, skill, entries, rolePool, reviewMode) {
723
801
  ...role.writeScope,
724
802
  ],
725
803
  outputs: [
726
- ".zsk/raws/prepare/product/{topic}/product-brief.md",
727
- ".zsk/raws/prepare/product/{topic}/roadmap.md",
728
- ".zsk/raws/prepare/ux/{topic}/ux-readiness.md",
729
- ".zsk/raws/prepare/design/{topic}/design-source-needs.md",
804
+ `.zsk/raws/prepare/product/${topic}/intake-clarity.md`,
805
+ `.zsk/raws/prepare/product/${topic}/product-brief.md`,
806
+ `.zsk/raws/prepare/product/${topic}/roadmap.md`,
807
+ `.zsk/raws/prepare/ux/${topic}/ux-readiness.md`,
808
+ `.zsk/raws/prepare/ux/${topic}/interaction-handoff.md when UI/UX/design-source interaction is triggered`,
809
+ `.zsk/raws/prepare/design/${topic}/design-source-needs.md`,
810
+ `.zsk/raws/prepare/design/${topic}/design-source-map.md when provider-backed design sources are used`,
730
811
  ".zsk/evidence/preproposal/{run}/checkpoint-*.md",
731
812
  ".zsk/evidence/preproposal/{run}/readiness-review.md",
732
813
  ...role.outputs,
733
814
  ],
734
815
  evidenceRequired: [
816
+ ...(module ? [
817
+ `module target is fixed to ${module}; module docs and raw sources are challenged before widening scope`,
818
+ `interaction handoff and design-source map use ${module} as the default raw topic unless a narrower page/topic is justified`,
819
+ ] : []),
820
+ "source-discoverable questions answered before user clarification",
821
+ "accepted clarifications, inferred assumptions, and one blocking question are separated",
735
822
  "product checkpoint passes before roadmap/decomposition",
736
823
  "roadmap/decomposition checkpoint passes before UX/design-readiness",
824
+ "AI brief drafts and candidate code component mapping exist for source-backed page/module interaction details before asking humans to fill gaps",
825
+ "interaction handoff separates sourced provider facts from accepted decisions, assumptions, and missing page/module interaction details when triggered",
737
826
  "UX/design-readiness checkpoint passes before readiness handoff",
738
827
  "final readiness review passes or blocks with owner, missing input, impact, and next action",
739
828
  ...role.evidenceRequired,
740
829
  ],
830
+ ...(moduleInputs.length > 0 ? { extraInputs: moduleInputs } : {}),
741
831
  contract: {
742
- owns: ["preproposal lane facts, source gaps, checkpoint evidence, and readiness handoff for this role"],
832
+ owns: module
833
+ ? [`preproposal lane facts, source gaps, checkpoint evidence, and readiness handoff for module ${module}`]
834
+ : ["preproposal lane facts, source gaps, checkpoint evidence, and readiness handoff for this role"],
743
835
  doesNotOwn: ["formal module proposal/spec/design/task artifacts", "formal test cases", "implementation edits"],
744
836
  forbiddenDecisions: ["Do not skip checkpoint order.", "Do not treat scenario seeds as formal post-design test cases."],
745
837
  stopCondition: "Preproposal lane output is reviewable, or blockers are recorded for the checkpoint owner.",