frontend-project-context 1.0.1 → 1.3.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.
Files changed (31) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +164 -8
  3. package/UPGRADING.md +44 -0
  4. package/docs/00-PRODUCT-CONSTITUTION.md +42 -10
  5. package/docs/04-PROGRAM-DESIGN.md +89 -2
  6. package/docs/05-ACCEPTANCE-CONTRACT.md +52 -3
  7. package/docs/08-INSTALLATION-AND-DISTRIBUTION.md +27 -15
  8. package/docs/14-FORMAL-RELEASE-READINESS.md +15 -0
  9. package/docs/16-GUIDED-ONBOARDING-AND-AI-RECONCILIATION-DESIGN.md +469 -0
  10. package/docs/17-AI-EXCHANGE-BOUNDARY-DESIGN.md +270 -0
  11. package/docs/18-BRANCH-AWARE-STAGED-CONTEXT-DESIGN.md +348 -0
  12. package/docs/README.md +17 -5
  13. package/examples/README.md +15 -2
  14. package/examples/package.json +6 -2
  15. package/package.json +4 -3
  16. package/schemas/action-plan.schema.json +250 -0
  17. package/schemas/assist-bundle.schema.json +75 -0
  18. package/schemas/capabilities.schema.json +146 -0
  19. package/schemas/integration-review-bundle.schema.json +43 -0
  20. package/schemas/review-bundle.schema.json +109 -0
  21. package/schemas/stage-context-bundle.schema.json +56 -0
  22. package/schemas/stage-receipt.schema.json +55 -0
  23. package/schemas/task-context-plan.schema.json +85 -0
  24. package/src/project-context/assist.mjs +422 -0
  25. package/src/project-context/capabilities.mjs +74 -0
  26. package/src/project-context/cli.mjs +168 -13
  27. package/src/project-context/exchange-schema.mjs +528 -0
  28. package/src/project-context/exchange.mjs +565 -0
  29. package/src/project-context/project-store.mjs +22 -0
  30. package/src/project-context/task-context-schema.mjs +290 -0
  31. package/src/project-context/task-context.mjs +361 -0
@@ -0,0 +1,422 @@
1
+ import { digestJson } from "./canonical-json.mjs";
2
+ import { checkProject } from "./checker.mjs";
3
+ import { sourceStatus } from "./contract-schema.mjs";
4
+ import { fail } from "./errors.mjs";
5
+ import { reviewSource } from "./maintenance.mjs";
6
+ import { normalizeRelativePath } from "./path-policy.mjs";
7
+ import { effectiveItems, scopeApplies } from "./scope-compiler.mjs";
8
+
9
+ const LOCAL_SOURCE_KINDS = new Set(["file", "path", "json-pointer"]);
10
+ const LARGE_IMPACT_THRESHOLD = 100;
11
+ export const ASSIST_BUNDLE_SCHEMA_VERSION = 1;
12
+
13
+ function uniqueSorted(values) {
14
+ return [...new Set(values)].sort((left, right) => left.localeCompare(right));
15
+ }
16
+
17
+ function snapshots(project) {
18
+ return {
19
+ contract: project.contractDigest ?? digestJson(project.contract),
20
+ sourcesLock: project.sourcesLockDigest ?? digestJson(project.sourcesLock),
21
+ projectionsLock: project.projectionsLockDigest ?? digestJson(project.projectionsLock),
22
+ };
23
+ }
24
+
25
+ function sourceLocator(source) {
26
+ if (source.kind === "json-pointer") return { path: source.path, pointer: source.pointer };
27
+ if (LOCAL_SOURCE_KINDS.has(source.kind)) return { path: source.path };
28
+ return { reference: source.reference };
29
+ }
30
+
31
+ function sourceTarget(source, reason, priority) {
32
+ return {
33
+ sourceId: source.id,
34
+ kind: source.kind,
35
+ locator: sourceLocator(source),
36
+ reason,
37
+ priority,
38
+ };
39
+ }
40
+
41
+ function stableFindings(findings) {
42
+ const seen = new Set();
43
+ return findings.filter((finding) => {
44
+ const key = JSON.stringify(finding);
45
+ if (seen.has(key)) return false;
46
+ seen.add(key);
47
+ return true;
48
+ });
49
+ }
50
+
51
+ function setupWorkUnits(proposal) {
52
+ const factAndReferenceItems = proposal.items
53
+ .filter((item) => item.kind === "fact" || item.kind === "reference")
54
+ .map((item) => item.id);
55
+ const guidanceItems = proposal.items.filter((item) => item.kind === "reference");
56
+ const units = [];
57
+ if (factAndReferenceItems.length > 0) {
58
+ units.push({
59
+ id: "onboarding-facts",
60
+ kind: "onboarding-facts",
61
+ sourceIds: uniqueSorted(proposal.items
62
+ .filter((item) => factAndReferenceItems.includes(item.id))
63
+ .flatMap((item) => item.sources)),
64
+ itemIds: uniqueSorted(factAndReferenceItems),
65
+ });
66
+ }
67
+ if (guidanceItems.length > 0) {
68
+ units.push({
69
+ id: "authoritative-guidance",
70
+ kind: "authoritative-guidance",
71
+ sourceIds: uniqueSorted(guidanceItems.flatMap((item) => item.sources)),
72
+ itemIds: uniqueSorted(guidanceItems.map((item) => item.id)),
73
+ });
74
+ }
75
+ return units;
76
+ }
77
+
78
+ function pendingSourceIds(items) {
79
+ return uniqueSorted(items.flatMap((item) => [
80
+ ...item.sources,
81
+ ...(item.verification?.source ? [item.verification.source] : []),
82
+ ]));
83
+ }
84
+
85
+ function pendingReviewUnit(items) {
86
+ return {
87
+ id: "pending-review",
88
+ kind: "pending-review",
89
+ sourceIds: pendingSourceIds(items),
90
+ itemIds: items.map((item) => item.id),
91
+ };
92
+ }
93
+
94
+ function mergeReadTargets(targets) {
95
+ const bySource = new Map();
96
+ for (const target of targets) {
97
+ const existing = bySource.get(target.sourceId);
98
+ if (!existing || target.priority < existing.priority) bySource.set(target.sourceId, target);
99
+ }
100
+ return [...bySource.values()].sort((left, right) => left.sourceId.localeCompare(right.sourceId));
101
+ }
102
+
103
+ export function buildSetupAssistBundle(project, proposal, options = {}) {
104
+ const guidanceSourceIds = new Set(
105
+ proposal.items.filter((item) => item.kind === "reference").flatMap((item) => item.sources),
106
+ );
107
+ const pending = project.contract.items
108
+ .filter((item) => item.status === "proposed")
109
+ .map(pendingItem)
110
+ .sort((left, right) => left.id.localeCompare(right.id));
111
+ const sourceById = new Map([
112
+ ...project.contract.sources.map((source) => [source.id, source]),
113
+ ...proposal.sources.map((source) => [source.id, source]),
114
+ ]);
115
+ const guidanceTargets = [...guidanceSourceIds]
116
+ .map((id) => sourceById.get(id))
117
+ .filter(Boolean)
118
+ .map((source) => sourceTarget(source, "authoritative-guidance", 1));
119
+ const pendingTargets = pendingSourceIds(pending)
120
+ .map((id) => sourceById.get(id))
121
+ .filter((source) => source && sourceStatus(source) === "active" && LOCAL_SOURCE_KINDS.has(source.kind))
122
+ .map((source) => sourceTarget(source, "pending-review", 2));
123
+ const workUnits = setupWorkUnits(proposal);
124
+ if (pending.length > 0) workUnits.push(pendingReviewUnit(pending));
125
+ return {
126
+ schemaVersion: ASSIST_BUNDLE_SCHEMA_VERSION,
127
+ mode: "setup",
128
+ project: {
129
+ id: project.contract.project.id,
130
+ name: project.contract.project.name,
131
+ initialized: Boolean(options.initialized),
132
+ },
133
+ snapshots: snapshots(project),
134
+ summary: {
135
+ candidateSources: proposal.sources.length,
136
+ candidateItems: proposal.items.length,
137
+ changedSources: 0,
138
+ affectedItems: 0,
139
+ pendingItems: pending.length,
140
+ affectedProjections: 0,
141
+ },
142
+ sourceChanges: [],
143
+ affectedItems: [],
144
+ pendingItems: pending,
145
+ pathSignals: [],
146
+ projectionPaths: [],
147
+ findings: [],
148
+ readTargets: mergeReadTargets([...guidanceTargets, ...pendingTargets]),
149
+ workUnits: workUnits.sort((left, right) => left.id.localeCompare(right.id)),
150
+ proposal: structuredClone(proposal),
151
+ artifacts: [{
152
+ kind: "proposal",
153
+ path: options.proposalPath ?? ".project-context/setup.proposal.json",
154
+ action: options.proposalAction ?? "preview",
155
+ persisted: Boolean(options.proposalPersisted),
156
+ }],
157
+ };
158
+ }
159
+
160
+ function syncPath(value) {
161
+ try {
162
+ return normalizeRelativePath(value, { allowRoot: true, label: "changed path" });
163
+ } catch (error) {
164
+ fail("sync-path-invalid", `changed path is invalid: ${value}`, {
165
+ details: { path: value, reason: error.code ?? "invalid-path" },
166
+ });
167
+ }
168
+ }
169
+
170
+ function registeredSourcesForPath(contract, targetPath) {
171
+ return contract.sources
172
+ .filter((source) => sourceStatus(source) === "active" && LOCAL_SOURCE_KINDS.has(source.kind))
173
+ .filter((source) => {
174
+ if (source.kind === "json-pointer") return targetPath === source.path;
175
+ return targetPath === source.path || targetPath.startsWith(`${source.path}/`);
176
+ })
177
+ .map((source) => source.id)
178
+ .sort((left, right) => left.localeCompare(right));
179
+ }
180
+
181
+ function pathSignals(contract, changedPaths) {
182
+ return uniqueSorted(changedPaths.map(syncPath)).map((targetPath) => {
183
+ const registeredSourceIds = registeredSourcesForPath(contract, targetPath);
184
+ let approvedItemIds = [];
185
+ try {
186
+ approvedItemIds = effectiveItems(contract.items, targetPath).map((item) => item.id);
187
+ } catch (error) {
188
+ if (error?.code !== "contract-conflict" && !error?.code?.startsWith("scope-")) throw error;
189
+ approvedItemIds = contract.items
190
+ .filter((item) => item.status === "approved" && scopeApplies(item.scope, targetPath))
191
+ .map((item) => item.id)
192
+ .sort((left, right) => left.localeCompare(right));
193
+ }
194
+ return {
195
+ path: targetPath,
196
+ approvedItemIds,
197
+ registeredSourceIds,
198
+ classification: registeredSourceIds.length > 0
199
+ ? "registered-source"
200
+ : approvedItemIds.length > 0
201
+ ? "scope-only"
202
+ : "unrelated",
203
+ };
204
+ });
205
+ }
206
+
207
+ function impactReasonsByItem(reviews) {
208
+ const byItem = new Map();
209
+ for (const review of reviews) {
210
+ if (!["changed", "missing", "unreadable"].includes(review.status)) continue;
211
+ for (const item of review.impact.items) {
212
+ if (!byItem.has(item.id)) byItem.set(item.id, []);
213
+ byItem.get(item.id).push({ sourceId: review.source.id, reasons: uniqueSorted(item.reasons ?? []) });
214
+ }
215
+ }
216
+ for (const impacts of byItem.values()) impacts.sort((left, right) => left.sourceId.localeCompare(right.sourceId));
217
+ return byItem;
218
+ }
219
+
220
+ function affectedItem(item, sourceImpacts, largeImpact) {
221
+ const base = {
222
+ id: item.id,
223
+ kind: item.kind,
224
+ subject: item.subject,
225
+ scope: structuredClone(item.scope),
226
+ status: item.status,
227
+ itemDigest: digestJson(item),
228
+ sourceImpacts: structuredClone(sourceImpacts),
229
+ };
230
+ if (largeImpact) return { ...base, largeImpact: true };
231
+ return {
232
+ ...base,
233
+ value: structuredClone(item.value),
234
+ statement: item.statement,
235
+ sources: [...item.sources],
236
+ overrides: [...item.overrides],
237
+ ...(item.verification ? { verification: structuredClone(item.verification) } : {}),
238
+ };
239
+ }
240
+
241
+ function pendingItem(item) {
242
+ return {
243
+ id: item.id,
244
+ kind: item.kind,
245
+ subject: item.subject,
246
+ scope: structuredClone(item.scope),
247
+ status: item.status,
248
+ itemDigest: digestJson(item),
249
+ value: structuredClone(item.value),
250
+ statement: item.statement,
251
+ sources: [...item.sources],
252
+ overrides: [...item.overrides],
253
+ ...(item.verification ? { verification: structuredClone(item.verification) } : {}),
254
+ };
255
+ }
256
+
257
+ function sourceChange(review) {
258
+ const hasProblem = ["changed", "missing", "unreadable"].includes(review.status);
259
+ const itemIds = hasProblem ? review.impact.items.map((item) => item.id) : [];
260
+ const largeImpact = itemIds.length > LARGE_IMPACT_THRESHOLD;
261
+ return {
262
+ sourceId: review.source.id,
263
+ kind: review.source.kind,
264
+ locator: sourceLocator(review.source),
265
+ status: review.status,
266
+ lockedDigest: review.lockedDigest,
267
+ currentDigest: review.currentDigest,
268
+ ...(review.reason ? { reason: review.reason } : {}),
269
+ affectedItemIds: itemIds,
270
+ itemRelations: hasProblem && !largeImpact
271
+ ? review.impact.items.map((item) => ({ id: item.id, reasons: uniqueSorted(item.reasons ?? []) }))
272
+ : [],
273
+ fallbackItemIds: hasProblem ? review.impact.fallbackItems.map((item) => item.id) : [],
274
+ directProjectionPaths: hasProblem ? [...review.impact.directProjectionPaths] : [],
275
+ staleProjectionPaths: hasProblem ? [...review.impact.staleProjectionPaths] : [],
276
+ ...(largeImpact ? { largeImpact: true } : {}),
277
+ };
278
+ }
279
+
280
+ function nonLocalSourceChange(source) {
281
+ const deprecated = sourceStatus(source) === "deprecated";
282
+ return {
283
+ sourceId: source.id,
284
+ kind: source.kind,
285
+ locator: sourceLocator(source),
286
+ status: deprecated ? "deprecated" : "not-local-checkable",
287
+ lockedDigest: null,
288
+ currentDigest: null,
289
+ affectedItemIds: [],
290
+ itemRelations: [],
291
+ fallbackItemIds: [],
292
+ directProjectionPaths: [],
293
+ staleProjectionPaths: [],
294
+ };
295
+ }
296
+
297
+ function syncWorkUnits(problemReviews, signals, findings, pendingItems) {
298
+ const units = problemReviews.map((review) => ({
299
+ id: `source-change:${review.source.id}`,
300
+ kind: "source-change",
301
+ sourceIds: [review.source.id],
302
+ itemIds: review.impact.items.map((item) => item.id),
303
+ fallbackItemIds: review.impact.fallbackItems.map((item) => item.id),
304
+ projectionPaths: uniqueSorted([
305
+ ...review.impact.directProjectionPaths,
306
+ ...review.impact.staleProjectionPaths,
307
+ ]),
308
+ ...(review.impact.items.length > LARGE_IMPACT_THRESHOLD ? { largeImpact: true } : {}),
309
+ }));
310
+ for (const signal of signals) {
311
+ units.push({
312
+ id: `path-signal:${signal.path}`,
313
+ kind: "path-signal",
314
+ paths: [signal.path],
315
+ sourceIds: [...signal.registeredSourceIds],
316
+ itemIds: [...signal.approvedItemIds],
317
+ });
318
+ }
319
+ if (pendingItems.length > 0) {
320
+ units.push(pendingReviewUnit(pendingItems));
321
+ }
322
+ const projectionFindings = findings.filter((finding) => finding.code.startsWith("projection-") && finding.path);
323
+ for (const projectionPath of uniqueSorted(projectionFindings.map((finding) => finding.path))) {
324
+ units.push({
325
+ id: `projection-review:${projectionPath}`,
326
+ kind: "projection-review",
327
+ projectionPaths: [projectionPath],
328
+ findingCodes: uniqueSorted(projectionFindings
329
+ .filter((finding) => finding.path === projectionPath)
330
+ .map((finding) => finding.code)),
331
+ });
332
+ }
333
+ const conflictFindings = findings.filter((finding) =>
334
+ finding.code.includes("conflict") || finding.code.startsWith("scope-") || finding.code.startsWith("verification-"),
335
+ );
336
+ if (conflictFindings.length > 0) {
337
+ units.push({
338
+ id: "conflicts",
339
+ kind: "conflict",
340
+ findingCodes: uniqueSorted(conflictFindings.map((finding) => finding.code)),
341
+ itemIds: uniqueSorted(conflictFindings.flatMap((finding) => finding.items ?? (finding.item ? [finding.item] : []))),
342
+ sourceIds: uniqueSorted(conflictFindings.flatMap((finding) => finding.source ? [finding.source] : [])),
343
+ paths: uniqueSorted(conflictFindings.flatMap((finding) => finding.path ? [finding.path] : [])),
344
+ });
345
+ }
346
+ return units.sort((left, right) => left.id.localeCompare(right.id));
347
+ }
348
+
349
+ export async function buildSyncAssistBundle(root, project, changedPaths = []) {
350
+ const reviewResults = await Promise.all(project.contract.sources.map(async (source) => {
351
+ if (sourceStatus(source) === "deprecated" || !LOCAL_SOURCE_KINDS.has(source.kind)) {
352
+ return { source, nonLocal: nonLocalSourceChange(source) };
353
+ }
354
+ return { source, review: await reviewSource(root, project, source.id) };
355
+ }));
356
+ const reviews = reviewResults.filter((entry) => entry.review).map((entry) => entry.review);
357
+ const problemReviews = reviews.filter((review) => ["changed", "missing", "unreadable"].includes(review.status));
358
+ const sourceChanges = reviewResults
359
+ .filter((entry) => !entry.review || entry.review.status !== "unchanged")
360
+ .map((entry) => entry.review ? sourceChange(entry.review) : entry.nonLocal)
361
+ .sort((left, right) => left.sourceId.localeCompare(right.sourceId));
362
+ const sourceImpacts = impactReasonsByItem(problemReviews);
363
+ const largeImpactIds = new Set(problemReviews
364
+ .filter((review) => review.impact.items.length > LARGE_IMPACT_THRESHOLD)
365
+ .flatMap((review) => review.impact.items.map((item) => item.id)));
366
+ const itemById = new Map(project.contract.items.map((item) => [item.id, item]));
367
+ const affectedItems = [...sourceImpacts.keys()]
368
+ .map((id) => affectedItem(itemById.get(id), sourceImpacts.get(id), largeImpactIds.has(id)))
369
+ .sort((left, right) => left.id.localeCompare(right.id));
370
+ const signals = pathSignals(project.contract, changedPaths);
371
+ const findings = stableFindings(await checkProject(root, project));
372
+ const pending = project.contract.items
373
+ .filter((item) => item.status === "proposed")
374
+ .map(pendingItem)
375
+ .sort((left, right) => left.id.localeCompare(right.id));
376
+ const projectionPaths = uniqueSorted([
377
+ ...problemReviews.flatMap((review) => [
378
+ ...review.impact.directProjectionPaths,
379
+ ...review.impact.staleProjectionPaths,
380
+ ]),
381
+ ...findings
382
+ .filter((finding) => finding.code.startsWith("projection-") && finding.path)
383
+ .map((finding) => finding.path),
384
+ ]);
385
+ const changedTargets = problemReviews
386
+ .filter((review) => review.status === "changed")
387
+ .map((review) => sourceTarget(review.source, "source-change", 1));
388
+ const sourceById = new Map(project.contract.sources.map((source) => [source.id, source]));
389
+ const pendingTargets = pendingSourceIds(pending)
390
+ .map((id) => sourceById.get(id))
391
+ .filter((source) => source && sourceStatus(source) === "active" && LOCAL_SOURCE_KINDS.has(source.kind))
392
+ .map((source) => sourceTarget(source, "pending-review", 2));
393
+ const readTargets = mergeReadTargets([...changedTargets, ...pendingTargets]);
394
+ return {
395
+ schemaVersion: ASSIST_BUNDLE_SCHEMA_VERSION,
396
+ mode: "sync",
397
+ project: {
398
+ id: project.contract.project.id,
399
+ name: project.contract.project.name,
400
+ initialized: true,
401
+ },
402
+ snapshots: snapshots(project),
403
+ summary: {
404
+ candidateSources: 0,
405
+ candidateItems: 0,
406
+ changedSources: problemReviews.length,
407
+ affectedItems: affectedItems.length,
408
+ pendingItems: pending.length,
409
+ affectedProjections: projectionPaths.length,
410
+ },
411
+ sourceChanges,
412
+ affectedItems,
413
+ pendingItems: pending,
414
+ pathSignals: signals,
415
+ projectionPaths,
416
+ findings,
417
+ readTargets,
418
+ workUnits: syncWorkUnits(problemReviews, signals, findings, pending),
419
+ proposal: null,
420
+ artifacts: [],
421
+ };
422
+ }
@@ -0,0 +1,74 @@
1
+ import { ASSIST_BUNDLE_SCHEMA_VERSION } from "./assist.mjs";
2
+ import { DASHBOARD_SCHEMA_VERSION } from "./dashboard-model.mjs";
3
+ import {
4
+ ACTION_KINDS,
5
+ ACTION_PLAN_SCHEMA_VERSION,
6
+ CAPABILITIES_SCHEMA_VERSION,
7
+ COMMANDS,
8
+ EXCHANGE_PROTOCOL_VERSION,
9
+ PACKAGE_VERSION,
10
+ REVIEW_BUNDLE_SCHEMA_VERSION,
11
+ } from "./exchange-schema.mjs";
12
+ import { inspectProjectInitialization, loadProject } from "./project-store.mjs";
13
+ import { RENDERER_VERSION } from "./renderer.mjs";
14
+ import {
15
+ CONTEXT_BUDGET_UNIT,
16
+ INTEGRATION_REVIEW_BUNDLE_SCHEMA_VERSION,
17
+ STAGE_CONTEXT_BUNDLE_SCHEMA_VERSION,
18
+ STAGE_RECEIPT_SCHEMA_VERSION,
19
+ TASK_CONTEXT_PLAN_SCHEMA_VERSION,
20
+ } from "./task-context-schema.mjs";
21
+
22
+ function schemas() {
23
+ return {
24
+ actionPlan: ACTION_PLAN_SCHEMA_VERSION,
25
+ assistBundle: ASSIST_BUNDLE_SCHEMA_VERSION,
26
+ capabilities: CAPABILITIES_SCHEMA_VERSION,
27
+ contract: 2,
28
+ dashboardViewModel: DASHBOARD_SCHEMA_VERSION,
29
+ integrationReviewBundle: INTEGRATION_REVIEW_BUNDLE_SCHEMA_VERSION,
30
+ projectionLock: 1,
31
+ projectionRenderer: RENDERER_VERSION,
32
+ proposal: 1,
33
+ reviewBundle: REVIEW_BUNDLE_SCHEMA_VERSION,
34
+ sourceLock: 1,
35
+ stageContextBundle: STAGE_CONTEXT_BUNDLE_SCHEMA_VERSION,
36
+ stageReceipt: STAGE_RECEIPT_SCHEMA_VERSION,
37
+ taskContextPlan: TASK_CONTEXT_PLAN_SCHEMA_VERSION,
38
+ };
39
+ }
40
+
41
+ export async function buildCapabilities(root) {
42
+ const initialization = await inspectProjectInitialization(root);
43
+ let project = null;
44
+ if (initialization.status === "initialized") {
45
+ const loaded = await loadProject(root);
46
+ project = { id: loaded.contract.project.id, name: loaded.contract.project.name };
47
+ }
48
+ return {
49
+ schemaVersion: CAPABILITIES_SCHEMA_VERSION,
50
+ package: { name: "frontend-project-context", version: PACKAGE_VERSION },
51
+ exchangeProtocolVersion: EXCHANGE_PROTOCOL_VERSION,
52
+ schemas: schemas(),
53
+ commands: [...COMMANDS],
54
+ actionKinds: [...ACTION_KINDS],
55
+ contextBudget: { unit: CONTEXT_BUDGET_UNIT, modelTokens: false, callerMustProvideLimit: true },
56
+ initialization: initialization.status,
57
+ initialized: initialization.status === "initialized",
58
+ project,
59
+ boundaries: {
60
+ provider: false,
61
+ agentRuntime: false,
62
+ git: false,
63
+ network: false,
64
+ dependencyInstallation: false,
65
+ automaticApproval: false,
66
+ businessCodeWrites: false,
67
+ taskExecution: false,
68
+ stagePathBodyReads: false,
69
+ applyPlan: false,
70
+ scheduler: false,
71
+ daemon: false,
72
+ },
73
+ };
74
+ }