@workflow-cannon/workspace-kit 0.18.0 → 0.24.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/README.md +23 -9
- package/dist/cli/doctor-planning-issues.js +3 -22
- package/dist/cli/run-command.js +22 -38
- package/dist/cli.js +95 -4
- package/dist/contracts/command-manifest.d.ts +17 -0
- package/dist/contracts/command-manifest.js +1 -0
- package/dist/contracts/index.d.ts +1 -1
- package/dist/contracts/module-contract.d.ts +12 -11
- package/dist/core/agent-instruction-surface.d.ts +33 -0
- package/dist/core/agent-instruction-surface.js +46 -0
- package/dist/core/config-cli.js +13 -17
- package/dist/core/config-metadata.js +61 -2
- package/dist/core/index.d.ts +4 -1
- package/dist/core/index.js +3 -0
- package/dist/core/module-command-router.js +19 -1
- package/dist/core/module-registry-resolve.d.ts +27 -0
- package/dist/core/module-registry-resolve.js +91 -0
- package/dist/core/module-registry.d.ts +14 -0
- package/dist/core/module-registry.js +57 -0
- package/dist/core/planning/build-plan-session-file.d.ts +29 -0
- package/dist/core/planning/build-plan-session-file.js +58 -0
- package/dist/core/planning/index.d.ts +17 -0
- package/dist/core/planning/index.js +15 -0
- package/dist/core/policy.js +18 -8
- package/dist/core/state/unified-state-db.d.ts +21 -0
- package/dist/core/state/unified-state-db.js +80 -0
- package/dist/core/workspace-kit-config.js +8 -0
- package/dist/modules/agent-behavior/builtins.d.ts +3 -0
- package/dist/modules/agent-behavior/builtins.js +71 -0
- package/dist/modules/agent-behavior/explain.d.ts +6 -0
- package/dist/modules/agent-behavior/explain.js +46 -0
- package/dist/modules/agent-behavior/index.d.ts +4 -0
- package/dist/modules/agent-behavior/index.js +461 -0
- package/dist/modules/agent-behavior/interview-session-file.d.ts +9 -0
- package/dist/modules/agent-behavior/interview-session-file.js +43 -0
- package/dist/modules/agent-behavior/interview.d.ts +13 -0
- package/dist/modules/agent-behavior/interview.js +88 -0
- package/dist/modules/agent-behavior/persistence.d.ts +6 -0
- package/dist/modules/agent-behavior/persistence.js +89 -0
- package/dist/modules/agent-behavior/store.d.ts +34 -0
- package/dist/modules/agent-behavior/store.js +119 -0
- package/dist/modules/agent-behavior/types.d.ts +28 -0
- package/dist/modules/agent-behavior/types.js +1 -0
- package/dist/modules/agent-behavior/validate.d.ts +11 -0
- package/dist/modules/agent-behavior/validate.js +123 -0
- package/dist/modules/approvals/index.js +54 -51
- package/dist/modules/approvals/policy-sensitive-commands.d.ts +4 -0
- package/dist/modules/approvals/policy-sensitive-commands.js +4 -0
- package/dist/modules/approvals/review-runtime.js +1 -2
- package/dist/modules/documentation/index.js +47 -45
- package/dist/modules/documentation/normalizer.d.ts +3 -0
- package/dist/modules/documentation/normalizer.js +171 -0
- package/dist/modules/documentation/parser.d.ts +7 -0
- package/dist/modules/documentation/parser.js +39 -0
- package/dist/modules/documentation/policy-sensitive-commands.d.ts +5 -0
- package/dist/modules/documentation/policy-sensitive-commands.js +8 -0
- package/dist/modules/documentation/renderer.d.ts +23 -0
- package/dist/modules/documentation/renderer.js +105 -0
- package/dist/modules/documentation/runtime-batch.d.ts +10 -0
- package/dist/modules/documentation/runtime-batch.js +67 -0
- package/dist/modules/documentation/runtime-config.d.ts +11 -0
- package/dist/modules/documentation/runtime-config.js +54 -0
- package/dist/modules/documentation/runtime-render-support.d.ts +8 -0
- package/dist/modules/documentation/runtime-render-support.js +36 -0
- package/dist/modules/documentation/runtime.js +22 -510
- package/dist/modules/documentation/types.d.ts +182 -0
- package/dist/modules/documentation/validator.d.ts +8 -0
- package/dist/modules/documentation/validator.js +234 -0
- package/dist/modules/documentation/view-models.d.ts +3 -0
- package/dist/modules/documentation/view-models.js +124 -0
- package/dist/modules/improvement/generate-recommendations-runtime.js +3 -3
- package/dist/modules/improvement/improvement-state.d.ts +2 -2
- package/dist/modules/improvement/improvement-state.js +52 -23
- package/dist/modules/improvement/index.js +140 -138
- package/dist/modules/improvement/ingest.d.ts +1 -1
- package/dist/modules/improvement/policy-sensitive-commands.d.ts +4 -0
- package/dist/modules/improvement/policy-sensitive-commands.js +7 -0
- package/dist/modules/index.d.ts +6 -0
- package/dist/modules/index.js +17 -0
- package/dist/modules/planning/index.js +384 -50
- package/dist/modules/planning/question-engine.d.ts +2 -0
- package/dist/modules/planning/question-engine.js +8 -1
- package/dist/modules/task-engine/doctor-planning-persistence.js +21 -13
- package/dist/modules/task-engine/index.d.ts +1 -2
- package/dist/modules/task-engine/index.js +1 -1143
- package/dist/modules/task-engine/migrate-task-persistence-runtime.js +31 -4
- package/dist/modules/task-engine/migrate-wishlist-intake-runtime.d.ts +2 -0
- package/dist/modules/task-engine/migrate-wishlist-intake-runtime.js +146 -0
- package/dist/modules/task-engine/planning-open.d.ts +2 -9
- package/dist/modules/task-engine/planning-open.js +4 -15
- package/dist/modules/task-engine/policy-sensitive-commands.d.ts +5 -0
- package/dist/modules/task-engine/policy-sensitive-commands.js +5 -0
- package/dist/modules/task-engine/sqlite-dual-planning.d.ts +11 -2
- package/dist/modules/task-engine/sqlite-dual-planning.js +134 -28
- package/dist/modules/task-engine/strict-task-validation.js +3 -0
- package/dist/modules/task-engine/suggestions.js +2 -1
- package/dist/modules/task-engine/task-engine-internal.d.ts +2 -0
- package/dist/modules/task-engine/task-engine-internal.js +1304 -0
- package/dist/modules/task-engine/task-type-validation.js +40 -0
- package/dist/modules/task-engine/wishlist-intake.d.ts +22 -0
- package/dist/modules/task-engine/wishlist-intake.js +180 -0
- package/dist/modules/task-engine/wishlist-validation.d.ts +4 -0
- package/dist/modules/task-engine/wishlist-validation.js +19 -0
- package/dist/modules/workspace-config/index.js +9 -11
- package/package.json +2 -2
- package/schemas/agent-behavior-profile.schema.json +52 -0
- package/schemas/task-engine-run-contracts.schema.json +80 -5
- package/src/modules/documentation/README.md +16 -25
- package/src/modules/documentation/RULES.md +9 -9
- package/src/modules/documentation/index.ts +54 -49
- package/src/modules/documentation/instructions/document-project.md +6 -6
- package/src/modules/documentation/instructions/generate-document.md +4 -4
- package/src/modules/documentation/normalizer.ts +187 -0
- package/src/modules/documentation/parser.ts +41 -0
- package/src/modules/documentation/policy-sensitive-commands.ts +8 -0
- package/src/modules/documentation/renderer.ts +121 -0
- package/src/modules/documentation/runtime-batch.ts +74 -0
- package/src/modules/documentation/runtime-config.ts +68 -0
- package/src/modules/documentation/runtime-render-support.ts +39 -0
- package/src/modules/documentation/runtime.ts +28 -600
- package/src/modules/documentation/schemas/documentation-schema.md +37 -54
- package/src/modules/documentation/types.ts +228 -0
- package/src/modules/documentation/validator.ts +247 -0
- package/src/modules/documentation/view-models.ts +132 -0
- package/src/modules/documentation/views/agents.view.yaml +18 -0
- package/src/modules/documentation/views/architecture.view.yaml +18 -0
- package/src/modules/documentation/views/principles.view.yaml +18 -0
- package/src/modules/documentation/views/readme.view.yaml +18 -0
- package/src/modules/documentation/views/releasing.view.yaml +18 -0
- package/src/modules/documentation/views/roadmap.view.yaml +18 -0
- package/src/modules/documentation/views/runbooks-consumer-cadence.view.yaml +18 -0
- package/src/modules/documentation/views/runbooks-parity-validation-flow.view.yaml +18 -0
- package/src/modules/documentation/views/runbooks-release-channels.view.yaml +18 -0
- package/src/modules/documentation/views/security.view.yaml +18 -0
- package/src/modules/documentation/views/support.view.yaml +18 -0
- package/src/modules/documentation/views/terms.view.yaml +18 -0
- package/src/modules/documentation/views/workbooks-phase2-config-policy-workbook.view.yaml +18 -0
- package/src/modules/documentation/views/workbooks-task-engine-workbook.view.yaml +18 -0
- package/src/modules/documentation/views/workbooks-transcript-automation-baseline.view.yaml +18 -0
- package/src/modules/documentation/state.md +0 -8
package/dist/core/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { ModuleRegistry, ModuleRegistryError, validateModuleSet } from "./module-registry.js";
|
|
2
|
+
export { moduleRegistryOptionsFromEffectiveConfig, pickModuleContractWorkspacePath, resolveRegistryAndConfig } from "./module-registry-resolve.js";
|
|
2
3
|
export { ModuleCommandRouter, ModuleCommandRouterError } from "./module-command-router.js";
|
|
4
|
+
export { buildAgentInstructionSurface, classifyInstructionExecution, isInstructionExecutableForRegistry } from "./agent-instruction-surface.js";
|
|
3
5
|
export { buildBaseConfigLayers, deepMerge, envToConfigOverlay, explainConfigPath, getAtPath, getProjectConfigPath, getUserConfigFilePath, KIT_CONFIG_DEFAULTS, loadUserLayer, mergeConfigLayers, MODULE_CONFIG_CONTRIBUTIONS, normalizeConfigForExport, PROJECT_CONFIG_REL, resolveWorkspaceConfigWithLayers, stableStringifyConfig } from "./workspace-kit-config.js";
|
|
4
6
|
export { appendPolicyTrace, getExtraSensitiveModuleCommandsFromEffective, getOperationIdForCommand, isSensitiveModuleCommand, isSensitiveModuleCommandForEffective, parsePolicyApproval, parsePolicyApprovalFromEnv, POLICY_APPROVAL_HUMAN_DOC, POLICY_TRACE_SCHEMA_VERSION, resolveActor, resolvePolicyOperationIdForCommand } from "./policy.js";
|
|
5
7
|
export { getSessionGrant, loadSessionPolicyDocument, recordSessionGrant, resolveSessionId, SESSION_POLICY_SCHEMA_VERSION } from "./session-policy.js";
|
|
@@ -13,3 +15,4 @@ export { appendConfigMutation, CONFIG_MUTATIONS_SCHEMA_VERSION, summarizeForEvid
|
|
|
13
15
|
export { generateConfigReferenceDocs, runWorkspaceConfigCli } from "./config-cli.js";
|
|
14
16
|
export { LINEAGE_SCHEMA_VERSION, lineageCorrelationRoot } from "./lineage-contract.js";
|
|
15
17
|
export { appendLineageEvent, newLineageEventId, queryLineageChain, readLineageEvents } from "./lineage-store.js";
|
|
18
|
+
export { UnifiedStateDb } from "./state/unified-state-db.js";
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { classifyInstructionExecution, isInstructionExecutableForRegistry } from "./agent-instruction-surface.js";
|
|
1
2
|
export class ModuleCommandRouterError extends Error {
|
|
2
3
|
code;
|
|
3
4
|
constructor(code, message) {
|
|
@@ -40,6 +41,19 @@ export class ModuleCommandRouter {
|
|
|
40
41
|
if (!indexed.module.onCommand) {
|
|
41
42
|
throw new ModuleCommandRouterError("command-not-implemented", `Module '${indexed.descriptor.moduleId}' does not implement onCommand for '${commandName}'`);
|
|
42
43
|
}
|
|
44
|
+
if (!isInstructionExecutableForRegistry(indexed.module, indexed.entry, this.registry)) {
|
|
45
|
+
const deg = classifyInstructionExecution(indexed.module, indexed.entry, this.registry);
|
|
46
|
+
const detail = deg.kind === "peer_disabled"
|
|
47
|
+
? `missing peers: ${deg.missingPeers.join(", ")}`
|
|
48
|
+
: deg.kind === "module_disabled"
|
|
49
|
+
? "owning module disabled"
|
|
50
|
+
: "not executable";
|
|
51
|
+
return {
|
|
52
|
+
ok: false,
|
|
53
|
+
code: "peer-module-disabled",
|
|
54
|
+
message: `Command '${commandName}' is not executable (${detail}). Enable required modules or follow the instruction markdown for manual guidance. Instruction: ${indexed.descriptor.instructionFile}`
|
|
55
|
+
};
|
|
56
|
+
}
|
|
43
57
|
const command = {
|
|
44
58
|
name: commandName,
|
|
45
59
|
args
|
|
@@ -49,6 +63,9 @@ export class ModuleCommandRouter {
|
|
|
49
63
|
indexEnabledModuleCommands() {
|
|
50
64
|
for (const module of this.registry.getEnabledModules()) {
|
|
51
65
|
for (const entry of module.registration.instructions.entries) {
|
|
66
|
+
if (!isInstructionExecutableForRegistry(module, entry, this.registry)) {
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
52
69
|
if (this.commands.has(entry.name)) {
|
|
53
70
|
const existing = this.commands.get(entry.name);
|
|
54
71
|
throw new ModuleCommandRouterError("duplicate-command", `Command '${entry.name}' is declared by both '${existing?.descriptor.moduleId}' and '${module.registration.id}'`);
|
|
@@ -60,7 +77,8 @@ export class ModuleCommandRouter {
|
|
|
60
77
|
instructionFile: `${module.registration.instructions.directory}/${entry.file}`,
|
|
61
78
|
description: entry.description
|
|
62
79
|
},
|
|
63
|
-
module
|
|
80
|
+
module,
|
|
81
|
+
entry
|
|
64
82
|
});
|
|
65
83
|
}
|
|
66
84
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { WorkflowModule } from "../contracts/module-contract.js";
|
|
2
|
+
import { ModuleRegistry, type ModuleRegistryOptions } from "./module-registry.js";
|
|
3
|
+
import { type ConfigLayer, type EffectiveWorkspaceConfig } from "./workspace-kit-config.js";
|
|
4
|
+
/**
|
|
5
|
+
* Instruction paths in module registration are repo-relative. Use `workspacePath` when it
|
|
6
|
+
* contains the kit sources; otherwise fall back to `process.cwd()` (tests / ephemeral cwd).
|
|
7
|
+
*/
|
|
8
|
+
export declare function pickModuleContractWorkspacePath(workspacePath: string): string;
|
|
9
|
+
/**
|
|
10
|
+
* Reads `modules.enabled` / `modules.disabled` from effective config and maps them
|
|
11
|
+
* to ModuleRegistryOptions. Unknown module ids throw — fail fast on typos.
|
|
12
|
+
*
|
|
13
|
+
* Semantics (matches resolveEnabledModuleIds):
|
|
14
|
+
* - If `modules.enabled` is non-empty: only those ids are candidates, then `modules.disabled` subtracts.
|
|
15
|
+
* - If `modules.enabled` is empty/absent: start from each module's enabledByDefault, then subtract disabled.
|
|
16
|
+
*/
|
|
17
|
+
export declare function moduleRegistryOptionsFromEffectiveConfig(effective: Record<string, unknown>, knownModuleIds: Set<string>): Pick<ModuleRegistryOptions, "enabledModules" | "disabledModules">;
|
|
18
|
+
/**
|
|
19
|
+
* Resolves layered config together with module enablement toggles from that config.
|
|
20
|
+
* Iterates until the enabled module set stabilizes (module config contributions can
|
|
21
|
+
* change when modules drop out of startup order).
|
|
22
|
+
*/
|
|
23
|
+
export declare function resolveRegistryAndConfig(workspacePath: string, allModules: WorkflowModule[], invocationConfig?: Record<string, unknown>): Promise<{
|
|
24
|
+
registry: ModuleRegistry;
|
|
25
|
+
effective: EffectiveWorkspaceConfig;
|
|
26
|
+
layers: ConfigLayer[];
|
|
27
|
+
}>;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { ModuleRegistry, ModuleRegistryError } from "./module-registry.js";
|
|
4
|
+
import { resolveWorkspaceConfigWithLayers } from "./workspace-kit-config.js";
|
|
5
|
+
/**
|
|
6
|
+
* Instruction paths in module registration are repo-relative. Use `workspacePath` when it
|
|
7
|
+
* contains the kit sources; otherwise fall back to `process.cwd()` (tests / ephemeral cwd).
|
|
8
|
+
*/
|
|
9
|
+
export function pickModuleContractWorkspacePath(workspacePath) {
|
|
10
|
+
const marker = resolve(workspacePath, "src/modules/task-engine/config.md");
|
|
11
|
+
if (existsSync(marker)) {
|
|
12
|
+
return workspacePath;
|
|
13
|
+
}
|
|
14
|
+
return process.cwd();
|
|
15
|
+
}
|
|
16
|
+
function readNonEmptyStringArray(value) {
|
|
17
|
+
if (!Array.isArray(value)) {
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
const out = value.filter((x) => typeof x === "string" && x.trim().length > 0);
|
|
21
|
+
return out.length > 0 ? out : undefined;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Reads `modules.enabled` / `modules.disabled` from effective config and maps them
|
|
25
|
+
* to ModuleRegistryOptions. Unknown module ids throw — fail fast on typos.
|
|
26
|
+
*
|
|
27
|
+
* Semantics (matches resolveEnabledModuleIds):
|
|
28
|
+
* - If `modules.enabled` is non-empty: only those ids are candidates, then `modules.disabled` subtracts.
|
|
29
|
+
* - If `modules.enabled` is empty/absent: start from each module's enabledByDefault, then subtract disabled.
|
|
30
|
+
*/
|
|
31
|
+
export function moduleRegistryOptionsFromEffectiveConfig(effective, knownModuleIds) {
|
|
32
|
+
const root = effective.modules;
|
|
33
|
+
if (root === undefined || root === null) {
|
|
34
|
+
return {};
|
|
35
|
+
}
|
|
36
|
+
if (typeof root !== "object" || Array.isArray(root)) {
|
|
37
|
+
throw new ModuleRegistryError("invalid-modules-config", "effectiveConfig.modules must be an object when present");
|
|
38
|
+
}
|
|
39
|
+
const mod = root;
|
|
40
|
+
const enabled = readNonEmptyStringArray(mod.enabled);
|
|
41
|
+
const disabled = readNonEmptyStringArray(mod.disabled);
|
|
42
|
+
for (const id of [...(enabled ?? []), ...(disabled ?? [])]) {
|
|
43
|
+
if (!knownModuleIds.has(id)) {
|
|
44
|
+
throw new ModuleRegistryError("unknown-module-in-config", `Unknown module id in modules.enabled / modules.disabled: '${id}'`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
enabledModules: enabled,
|
|
49
|
+
disabledModules: disabled
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function enabledSignature(registry) {
|
|
53
|
+
return registry
|
|
54
|
+
.getEnabledModules()
|
|
55
|
+
.map((m) => m.registration.id)
|
|
56
|
+
.sort()
|
|
57
|
+
.join(",");
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Resolves layered config together with module enablement toggles from that config.
|
|
61
|
+
* Iterates until the enabled module set stabilizes (module config contributions can
|
|
62
|
+
* change when modules drop out of startup order).
|
|
63
|
+
*/
|
|
64
|
+
export async function resolveRegistryAndConfig(workspacePath, allModules, invocationConfig) {
|
|
65
|
+
const knownIds = new Set(allModules.map((m) => m.registration.id));
|
|
66
|
+
const moduleContractPath = pickModuleContractWorkspacePath(workspacePath);
|
|
67
|
+
let registry = new ModuleRegistry(allModules, { workspacePath: moduleContractPath });
|
|
68
|
+
for (let i = 0; i < 8; i++) {
|
|
69
|
+
const { effective, layers } = await resolveWorkspaceConfigWithLayers({
|
|
70
|
+
workspacePath,
|
|
71
|
+
registry,
|
|
72
|
+
invocationConfig
|
|
73
|
+
});
|
|
74
|
+
const extra = moduleRegistryOptionsFromEffectiveConfig(effective, knownIds);
|
|
75
|
+
const candidate = new ModuleRegistry(allModules, { workspacePath: moduleContractPath, ...extra });
|
|
76
|
+
if (enabledSignature(registry) === enabledSignature(candidate)) {
|
|
77
|
+
const fin = await resolveWorkspaceConfigWithLayers({
|
|
78
|
+
workspacePath,
|
|
79
|
+
registry: candidate,
|
|
80
|
+
invocationConfig
|
|
81
|
+
});
|
|
82
|
+
return {
|
|
83
|
+
registry: candidate,
|
|
84
|
+
effective: fin.effective,
|
|
85
|
+
layers: fin.layers
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
registry = candidate;
|
|
89
|
+
}
|
|
90
|
+
throw new ModuleRegistryError("module-enablement-unstable", "modules.enabled / modules.disabled did not stabilize after repeated config resolution; check config layers");
|
|
91
|
+
}
|
|
@@ -3,6 +3,18 @@ export declare class ModuleRegistryError extends Error {
|
|
|
3
3
|
readonly code: string;
|
|
4
4
|
constructor(code: string, message: string);
|
|
5
5
|
}
|
|
6
|
+
export type ModuleActivationEntry = {
|
|
7
|
+
moduleId: string;
|
|
8
|
+
enabled: boolean;
|
|
9
|
+
/** dependsOn entries not present in the enabled set (non-empty only if misconfigured). */
|
|
10
|
+
unsatisfiedHardDependencies: string[];
|
|
11
|
+
/** optionalPeers not currently enabled (informational). */
|
|
12
|
+
missingOptionalPeers: string[];
|
|
13
|
+
};
|
|
14
|
+
export type ModuleActivationReport = {
|
|
15
|
+
schemaVersion: 1;
|
|
16
|
+
modules: ModuleActivationEntry[];
|
|
17
|
+
};
|
|
6
18
|
export declare function validateModuleSet(modules: WorkflowModule[], workspacePath?: string): void;
|
|
7
19
|
export type ModuleRegistryOptions = {
|
|
8
20
|
enabledModules?: string[];
|
|
@@ -21,4 +33,6 @@ export declare class ModuleRegistry {
|
|
|
21
33
|
isModuleEnabled(id: string): boolean;
|
|
22
34
|
getEnabledModules(): WorkflowModule[];
|
|
23
35
|
getStartupOrder(): WorkflowModule[];
|
|
36
|
+
/** Snapshot for doctor / tooling: enablement and peer satisfaction. */
|
|
37
|
+
getActivationReport(): ModuleActivationReport;
|
|
24
38
|
}
|
|
@@ -32,6 +32,32 @@ function validateDependencies(moduleMap) {
|
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
|
+
function validateOptionalPeers(moduleMap) {
|
|
36
|
+
for (const module of moduleMap.values()) {
|
|
37
|
+
const moduleId = module.registration.id;
|
|
38
|
+
const peers = module.registration.optionalPeers ?? [];
|
|
39
|
+
const hard = new Set(module.registration.dependsOn);
|
|
40
|
+
for (const peerId of peers) {
|
|
41
|
+
if (peerId === moduleId) {
|
|
42
|
+
throw new ModuleRegistryError("self-optional-peer", `Module '${moduleId}' cannot list itself in optionalPeers`);
|
|
43
|
+
}
|
|
44
|
+
if (!moduleMap.has(peerId)) {
|
|
45
|
+
throw new ModuleRegistryError("missing-optional-peer", `Module '${moduleId}' lists unknown optional peer '${peerId}'`);
|
|
46
|
+
}
|
|
47
|
+
if (hard.has(peerId)) {
|
|
48
|
+
throw new ModuleRegistryError("optional-peer-overlap-dependsOn", `Module '${moduleId}' lists '${peerId}' in both dependsOn and optionalPeers`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function validateRegistrationSchemas(moduleMap) {
|
|
54
|
+
for (const module of moduleMap.values()) {
|
|
55
|
+
const schema = module.registration.stateSchema;
|
|
56
|
+
if (!Number.isInteger(schema) || schema < 1) {
|
|
57
|
+
throw new ModuleRegistryError("invalid-state-schema", `Module '${module.registration.id}' must declare integer stateSchema >= 1`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
35
61
|
function topologicalSort(moduleMap) {
|
|
36
62
|
const visited = new Set();
|
|
37
63
|
const inStack = new Set();
|
|
@@ -139,12 +165,23 @@ function validateInstructionContracts(moduleMap, workspacePath) {
|
|
|
139
165
|
if (!statSync(instructionFilePath).isFile()) {
|
|
140
166
|
throw new ModuleRegistryError("invalid-instruction-file", `Module '${id}' instruction path '${instructionFilePath}' is not a file`);
|
|
141
167
|
}
|
|
168
|
+
const reqPeers = entry.requiresPeers ?? [];
|
|
169
|
+
for (const peerId of reqPeers) {
|
|
170
|
+
if (peerId === id) {
|
|
171
|
+
throw new ModuleRegistryError("instruction-requires-self", `Module '${id}' instruction '${entry.name}' cannot list its own module id in requiresPeers`);
|
|
172
|
+
}
|
|
173
|
+
if (!moduleMap.has(peerId)) {
|
|
174
|
+
throw new ModuleRegistryError("unknown-requires-peer", `Module '${id}' instruction '${entry.name}' lists unknown requiresPeers module '${peerId}'`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
142
177
|
}
|
|
143
178
|
}
|
|
144
179
|
}
|
|
145
180
|
export function validateModuleSet(modules, workspacePath) {
|
|
146
181
|
const moduleMap = buildModuleMap(modules);
|
|
147
182
|
validateDependencies(moduleMap);
|
|
183
|
+
validateOptionalPeers(moduleMap);
|
|
184
|
+
validateRegistrationSchemas(moduleMap);
|
|
148
185
|
validateInstructionContracts(moduleMap, workspacePath ?? process.cwd());
|
|
149
186
|
topologicalSort(moduleMap);
|
|
150
187
|
}
|
|
@@ -157,6 +194,8 @@ export class ModuleRegistry {
|
|
|
157
194
|
constructor(modules, options) {
|
|
158
195
|
this.moduleMap = buildModuleMap(modules);
|
|
159
196
|
validateDependencies(this.moduleMap);
|
|
197
|
+
validateOptionalPeers(this.moduleMap);
|
|
198
|
+
validateRegistrationSchemas(this.moduleMap);
|
|
160
199
|
validateInstructionContracts(this.moduleMap, options?.workspacePath ?? process.cwd());
|
|
161
200
|
this.modules = [...modules];
|
|
162
201
|
const enabledModuleIds = resolveEnabledModuleIds(this.modules, options);
|
|
@@ -180,4 +219,22 @@ export class ModuleRegistry {
|
|
|
180
219
|
getStartupOrder() {
|
|
181
220
|
return [...this.sortedModules];
|
|
182
221
|
}
|
|
222
|
+
/** Snapshot for doctor / tooling: enablement and peer satisfaction. */
|
|
223
|
+
getActivationReport() {
|
|
224
|
+
const enabledIds = new Set(this.enabledModuleMap.keys());
|
|
225
|
+
const modules = [];
|
|
226
|
+
for (const mod of this.modules) {
|
|
227
|
+
const id = mod.registration.id;
|
|
228
|
+
const optionalPeers = mod.registration.optionalPeers ?? [];
|
|
229
|
+
const missingOptionalPeers = optionalPeers.filter((p) => !enabledIds.has(p));
|
|
230
|
+
const unsatisfiedHardDependencies = mod.registration.dependsOn.filter((d) => !enabledIds.has(d));
|
|
231
|
+
modules.push({
|
|
232
|
+
moduleId: id,
|
|
233
|
+
enabled: enabledIds.has(id),
|
|
234
|
+
unsatisfiedHardDependencies,
|
|
235
|
+
missingOptionalPeers
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
return { schemaVersion: 1, modules };
|
|
239
|
+
}
|
|
183
240
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/** Local operator snapshot so dashboards and agents can resume `build-plan` without re-entering answers. */
|
|
2
|
+
export type BuildPlanSessionSnapshotV1 = {
|
|
3
|
+
schemaVersion: 1;
|
|
4
|
+
updatedAt: string;
|
|
5
|
+
planningType: string;
|
|
6
|
+
outputMode: string;
|
|
7
|
+
status: string;
|
|
8
|
+
completionPct: number;
|
|
9
|
+
answeredCritical: number;
|
|
10
|
+
totalCritical: number;
|
|
11
|
+
answers: Record<string, unknown>;
|
|
12
|
+
/** Single-line `workspace-kit run build-plan '…'` hint (shell-escaped JSON inside quotes is caller responsibility). */
|
|
13
|
+
resumeCli: string;
|
|
14
|
+
};
|
|
15
|
+
export type DashboardPlanningSessionV1 = {
|
|
16
|
+
schemaVersion: 1;
|
|
17
|
+
updatedAt: string;
|
|
18
|
+
planningType: string;
|
|
19
|
+
outputMode: string;
|
|
20
|
+
status: string;
|
|
21
|
+
completionPct: number;
|
|
22
|
+
answeredCritical: number;
|
|
23
|
+
totalCritical: number;
|
|
24
|
+
resumeCli: string;
|
|
25
|
+
};
|
|
26
|
+
export declare function persistBuildPlanSession(workspacePath: string, snapshot: Omit<BuildPlanSessionSnapshotV1, "schemaVersion" | "updatedAt">): Promise<void>;
|
|
27
|
+
export declare function clearBuildPlanSession(workspacePath: string): Promise<void>;
|
|
28
|
+
export declare function readBuildPlanSession(workspacePath: string): Promise<BuildPlanSessionSnapshotV1 | null>;
|
|
29
|
+
export declare function toDashboardPlanningSession(snap: BuildPlanSessionSnapshotV1 | null): DashboardPlanningSessionV1 | null;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
const REL_DIR = path.join(".workspace-kit", "planning");
|
|
4
|
+
const FILE_NAME = "build-plan-session.json";
|
|
5
|
+
function sessionPath(workspacePath) {
|
|
6
|
+
return path.join(workspacePath, REL_DIR, FILE_NAME);
|
|
7
|
+
}
|
|
8
|
+
export async function persistBuildPlanSession(workspacePath, snapshot) {
|
|
9
|
+
const dir = path.join(workspacePath, REL_DIR);
|
|
10
|
+
await fs.mkdir(dir, { recursive: true });
|
|
11
|
+
const full = {
|
|
12
|
+
schemaVersion: 1,
|
|
13
|
+
updatedAt: new Date().toISOString(),
|
|
14
|
+
...snapshot
|
|
15
|
+
};
|
|
16
|
+
await fs.writeFile(sessionPath(workspacePath), `${JSON.stringify(full, null, 2)}\n`, "utf8");
|
|
17
|
+
}
|
|
18
|
+
export async function clearBuildPlanSession(workspacePath) {
|
|
19
|
+
try {
|
|
20
|
+
await fs.unlink(sessionPath(workspacePath));
|
|
21
|
+
}
|
|
22
|
+
catch (err) {
|
|
23
|
+
const code = err.code;
|
|
24
|
+
if (code !== "ENOENT")
|
|
25
|
+
throw err;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export async function readBuildPlanSession(workspacePath) {
|
|
29
|
+
try {
|
|
30
|
+
const raw = await fs.readFile(sessionPath(workspacePath), "utf8");
|
|
31
|
+
const parsed = JSON.parse(raw);
|
|
32
|
+
if (parsed?.schemaVersion !== 1 || typeof parsed.planningType !== "string") {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
if (typeof parsed.resumeCli !== "string") {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
return parsed;
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export function toDashboardPlanningSession(snap) {
|
|
45
|
+
if (!snap)
|
|
46
|
+
return null;
|
|
47
|
+
return {
|
|
48
|
+
schemaVersion: 1,
|
|
49
|
+
updatedAt: snap.updatedAt,
|
|
50
|
+
planningType: snap.planningType,
|
|
51
|
+
outputMode: snap.outputMode,
|
|
52
|
+
status: snap.status,
|
|
53
|
+
completionPct: snap.completionPct,
|
|
54
|
+
answeredCritical: snap.answeredCritical,
|
|
55
|
+
totalCritical: snap.totalCritical,
|
|
56
|
+
resumeCli: snap.resumeCli
|
|
57
|
+
};
|
|
58
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared planning-domain exports for cross-module consumers.
|
|
3
|
+
*
|
|
4
|
+
* **Planning persistence** (task + wishlist stores, SQLite/JSON) lives in `src/modules/task-engine/`.
|
|
5
|
+
* The **`planning` module** (`src/modules/planning/`) is the CLI interview surface (`build-plan`, …).
|
|
6
|
+
* This facade keeps non-task-engine modules from importing deep task-engine paths; implementations stay in task-engine.
|
|
7
|
+
*/
|
|
8
|
+
export { openPlanningStores } from "../../modules/task-engine/planning-open.js";
|
|
9
|
+
export { TaskStore } from "../../modules/task-engine/store.js";
|
|
10
|
+
export { WishlistStore } from "../../modules/task-engine/wishlist-store.js";
|
|
11
|
+
export { TransitionService } from "../../modules/task-engine/service.js";
|
|
12
|
+
export { validateKnownTaskTypeRequirements } from "../../modules/task-engine/task-type-validation.js";
|
|
13
|
+
export { buildWishlistItemFromIntake, validateWishlistContentFields, validateWishlistIntakePayload, validateWishlistUpdatePayload, WISHLIST_ID_RE } from "../../modules/task-engine/wishlist-validation.js";
|
|
14
|
+
export { allocateNextTaskNumericId, taskEntityFromNewIntake } from "../../modules/task-engine/wishlist-intake.js";
|
|
15
|
+
export type { TaskEntity, TaskPriority, TaskStatus, TransitionEvidence, TransitionGuard, TransitionContext, GuardResult } from "../../modules/task-engine/types.js";
|
|
16
|
+
export type { WishlistItem, WishlistStatus, WishlistConversionDecomposition } from "../../modules/task-engine/wishlist-types.js";
|
|
17
|
+
export { persistBuildPlanSession, clearBuildPlanSession, readBuildPlanSession, toDashboardPlanningSession, type BuildPlanSessionSnapshotV1, type DashboardPlanningSessionV1 } from "./build-plan-session-file.js";
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared planning-domain exports for cross-module consumers.
|
|
3
|
+
*
|
|
4
|
+
* **Planning persistence** (task + wishlist stores, SQLite/JSON) lives in `src/modules/task-engine/`.
|
|
5
|
+
* The **`planning` module** (`src/modules/planning/`) is the CLI interview surface (`build-plan`, …).
|
|
6
|
+
* This facade keeps non-task-engine modules from importing deep task-engine paths; implementations stay in task-engine.
|
|
7
|
+
*/
|
|
8
|
+
export { openPlanningStores } from "../../modules/task-engine/planning-open.js";
|
|
9
|
+
export { TaskStore } from "../../modules/task-engine/store.js";
|
|
10
|
+
export { WishlistStore } from "../../modules/task-engine/wishlist-store.js";
|
|
11
|
+
export { TransitionService } from "../../modules/task-engine/service.js";
|
|
12
|
+
export { validateKnownTaskTypeRequirements } from "../../modules/task-engine/task-type-validation.js";
|
|
13
|
+
export { buildWishlistItemFromIntake, validateWishlistContentFields, validateWishlistIntakePayload, validateWishlistUpdatePayload, WISHLIST_ID_RE } from "../../modules/task-engine/wishlist-validation.js";
|
|
14
|
+
export { allocateNextTaskNumericId, taskEntityFromNewIntake } from "../../modules/task-engine/wishlist-intake.js";
|
|
15
|
+
export { persistBuildPlanSession, clearBuildPlanSession, readBuildPlanSession, toDashboardPlanningSession } from "./build-plan-session-file.js";
|
package/dist/core/policy.js
CHANGED
|
@@ -1,19 +1,29 @@
|
|
|
1
1
|
import fs from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { execFile } from "node:child_process";
|
|
4
|
+
import { APPROVALS_POLICY_COMMAND_NAMES } from "../modules/approvals/policy-sensitive-commands.js";
|
|
5
|
+
import { DOCUMENTATION_POLICY_COMMAND_NAMES } from "../modules/documentation/policy-sensitive-commands.js";
|
|
6
|
+
import { IMPROVEMENT_POLICY_COMMAND_NAMES } from "../modules/improvement/policy-sensitive-commands.js";
|
|
7
|
+
import { TASK_ENGINE_POLICY_COMMAND_NAMES } from "../modules/task-engine/policy-sensitive-commands.js";
|
|
4
8
|
export const POLICY_TRACE_SCHEMA_VERSION = 1;
|
|
5
9
|
/** Maintainer doc (repo-relative) linked from policy denial output for `workspace-kit run`. */
|
|
6
10
|
export const POLICY_APPROVAL_HUMAN_DOC = "docs/maintainers/POLICY-APPROVAL.md";
|
|
7
11
|
/** Maintainer doc: tier table + copy-paste patterns for agents (Tier A/B `run` vs CLI env approval). */
|
|
8
12
|
export const AGENT_CLI_MAP_HUMAN_DOC = "docs/maintainers/AGENT-CLI-MAP.md";
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
};
|
|
13
|
+
function buildBuiltinCommandToOperation() {
|
|
14
|
+
const pairs = [
|
|
15
|
+
...DOCUMENTATION_POLICY_COMMAND_NAMES,
|
|
16
|
+
...TASK_ENGINE_POLICY_COMMAND_NAMES,
|
|
17
|
+
...APPROVALS_POLICY_COMMAND_NAMES,
|
|
18
|
+
...IMPROVEMENT_POLICY_COMMAND_NAMES
|
|
19
|
+
];
|
|
20
|
+
const out = {};
|
|
21
|
+
for (const [name, op] of pairs) {
|
|
22
|
+
out[name] = op;
|
|
23
|
+
}
|
|
24
|
+
return out;
|
|
25
|
+
}
|
|
26
|
+
const COMMAND_TO_OPERATION = buildBuiltinCommandToOperation();
|
|
17
27
|
export function getOperationIdForCommand(commandName) {
|
|
18
28
|
return COMMAND_TO_OPERATION[commandName];
|
|
19
29
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export type ModuleStateRow = {
|
|
2
|
+
moduleId: string;
|
|
3
|
+
stateSchemaVersion: number;
|
|
4
|
+
state: Record<string, unknown>;
|
|
5
|
+
updatedAt: string;
|
|
6
|
+
};
|
|
7
|
+
type UnifiedStateDbOptions = {
|
|
8
|
+
exportSnapshotRelativePath?: string;
|
|
9
|
+
};
|
|
10
|
+
export declare class UnifiedStateDb {
|
|
11
|
+
private db;
|
|
12
|
+
readonly dbPath: string;
|
|
13
|
+
readonly exportSnapshotPath: string | null;
|
|
14
|
+
constructor(workspacePath: string, databaseRelativePath: string, options?: UnifiedStateDbOptions);
|
|
15
|
+
private ensureDb;
|
|
16
|
+
getModuleState(moduleId: string): ModuleStateRow | null;
|
|
17
|
+
setModuleState(moduleId: string, stateSchemaVersion: number, state: Record<string, unknown>): void;
|
|
18
|
+
listModuleStates(): ModuleStateRow[];
|
|
19
|
+
private maybeExportSnapshot;
|
|
20
|
+
}
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import Database from "better-sqlite3";
|
|
4
|
+
const DDL = `
|
|
5
|
+
CREATE TABLE IF NOT EXISTS workspace_module_state (
|
|
6
|
+
module_id TEXT PRIMARY KEY,
|
|
7
|
+
state_schema_version INTEGER NOT NULL,
|
|
8
|
+
state_json TEXT NOT NULL,
|
|
9
|
+
updated_at TEXT NOT NULL
|
|
10
|
+
);
|
|
11
|
+
`;
|
|
12
|
+
export class UnifiedStateDb {
|
|
13
|
+
db = null;
|
|
14
|
+
dbPath;
|
|
15
|
+
exportSnapshotPath;
|
|
16
|
+
constructor(workspacePath, databaseRelativePath, options) {
|
|
17
|
+
this.dbPath = path.resolve(workspacePath, databaseRelativePath);
|
|
18
|
+
this.exportSnapshotPath = options?.exportSnapshotRelativePath
|
|
19
|
+
? path.resolve(workspacePath, options.exportSnapshotRelativePath)
|
|
20
|
+
: null;
|
|
21
|
+
}
|
|
22
|
+
ensureDb() {
|
|
23
|
+
if (this.db)
|
|
24
|
+
return this.db;
|
|
25
|
+
fs.mkdirSync(path.dirname(this.dbPath), { recursive: true });
|
|
26
|
+
this.db = new Database(this.dbPath);
|
|
27
|
+
this.db.pragma("journal_mode = WAL");
|
|
28
|
+
this.db.exec(DDL);
|
|
29
|
+
return this.db;
|
|
30
|
+
}
|
|
31
|
+
getModuleState(moduleId) {
|
|
32
|
+
const db = this.ensureDb();
|
|
33
|
+
const row = db
|
|
34
|
+
.prepare("SELECT module_id, state_schema_version, state_json, updated_at FROM workspace_module_state WHERE module_id = ?")
|
|
35
|
+
.get(moduleId);
|
|
36
|
+
if (!row)
|
|
37
|
+
return null;
|
|
38
|
+
return {
|
|
39
|
+
moduleId: row.module_id,
|
|
40
|
+
stateSchemaVersion: row.state_schema_version,
|
|
41
|
+
state: JSON.parse(row.state_json),
|
|
42
|
+
updatedAt: row.updated_at
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
setModuleState(moduleId, stateSchemaVersion, state) {
|
|
46
|
+
const db = this.ensureDb();
|
|
47
|
+
const updatedAt = new Date().toISOString();
|
|
48
|
+
db.prepare(`INSERT INTO workspace_module_state (module_id, state_schema_version, state_json, updated_at)
|
|
49
|
+
VALUES (?, ?, ?, ?)
|
|
50
|
+
ON CONFLICT(module_id) DO UPDATE SET
|
|
51
|
+
state_schema_version=excluded.state_schema_version,
|
|
52
|
+
state_json=excluded.state_json,
|
|
53
|
+
updated_at=excluded.updated_at`).run(moduleId, stateSchemaVersion, JSON.stringify(state), updatedAt);
|
|
54
|
+
this.maybeExportSnapshot();
|
|
55
|
+
}
|
|
56
|
+
listModuleStates() {
|
|
57
|
+
const db = this.ensureDb();
|
|
58
|
+
const rows = db
|
|
59
|
+
.prepare("SELECT module_id, state_schema_version, state_json, updated_at FROM workspace_module_state ORDER BY module_id ASC")
|
|
60
|
+
.all();
|
|
61
|
+
return rows.map((row) => ({
|
|
62
|
+
moduleId: row.module_id,
|
|
63
|
+
stateSchemaVersion: row.state_schema_version,
|
|
64
|
+
state: JSON.parse(row.state_json),
|
|
65
|
+
updatedAt: row.updated_at
|
|
66
|
+
}));
|
|
67
|
+
}
|
|
68
|
+
maybeExportSnapshot() {
|
|
69
|
+
if (!this.exportSnapshotPath)
|
|
70
|
+
return;
|
|
71
|
+
const snapshot = {
|
|
72
|
+
schemaVersion: 1,
|
|
73
|
+
exportedAt: new Date().toISOString(),
|
|
74
|
+
dbPath: this.dbPath,
|
|
75
|
+
modules: this.listModuleStates()
|
|
76
|
+
};
|
|
77
|
+
fs.mkdirSync(path.dirname(this.exportSnapshotPath), { recursive: true });
|
|
78
|
+
fs.writeFileSync(this.exportSnapshotPath, JSON.stringify(snapshot, null, 2) + "\n", "utf8");
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -10,6 +10,14 @@ export function getProjectConfigPath(workspacePath) {
|
|
|
10
10
|
/** Built-in defaults (lowest layer). */
|
|
11
11
|
export const KIT_CONFIG_DEFAULTS = {
|
|
12
12
|
core: {},
|
|
13
|
+
/**
|
|
14
|
+
* Module enablement: `enabled` whitelist (non-empty replaces default-by-flag set), then `disabled` subtracts.
|
|
15
|
+
* Empty arrays = no effect (all modules use registration.enabledByDefault).
|
|
16
|
+
*/
|
|
17
|
+
modules: {
|
|
18
|
+
enabled: [],
|
|
19
|
+
disabled: []
|
|
20
|
+
},
|
|
13
21
|
tasks: {
|
|
14
22
|
storeRelativePath: ".workspace-kit/tasks/state.json",
|
|
15
23
|
strictValidation: false
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { BEHAVIOR_PROFILE_SCHEMA_VERSION } from "./types.js";
|
|
2
|
+
export const DEFAULT_BUILTIN_PROFILE_ID = "builtin:balanced";
|
|
3
|
+
const nowMeta = () => ({
|
|
4
|
+
source: "builtin",
|
|
5
|
+
createdAt: new Date().toISOString()
|
|
6
|
+
});
|
|
7
|
+
export const BUILTIN_PROFILES = {
|
|
8
|
+
"builtin:cautious": {
|
|
9
|
+
schemaVersion: BEHAVIOR_PROFILE_SCHEMA_VERSION,
|
|
10
|
+
id: "builtin:cautious",
|
|
11
|
+
label: "Cautious",
|
|
12
|
+
summary: "Prefer small steps, frequent check-ins, and explicit confirmation before larger edits.",
|
|
13
|
+
dimensions: {
|
|
14
|
+
deliberationDepth: "high",
|
|
15
|
+
changeAppetite: "conservative",
|
|
16
|
+
checkInFrequency: "often",
|
|
17
|
+
explanationVerbosity: "verbose",
|
|
18
|
+
explorationStyle: "linear",
|
|
19
|
+
ambiguityHandling: "ask"
|
|
20
|
+
},
|
|
21
|
+
interactionNotes: "Surface risks early; default to the smallest reversible change.",
|
|
22
|
+
metadata: nowMeta()
|
|
23
|
+
},
|
|
24
|
+
"builtin:balanced": {
|
|
25
|
+
schemaVersion: BEHAVIOR_PROFILE_SCHEMA_VERSION,
|
|
26
|
+
id: "builtin:balanced",
|
|
27
|
+
label: "Balanced",
|
|
28
|
+
summary: "Default collaboration: clear reasoning, normal autonomy when intent is clear.",
|
|
29
|
+
dimensions: {
|
|
30
|
+
deliberationDepth: "medium",
|
|
31
|
+
changeAppetite: "balanced",
|
|
32
|
+
checkInFrequency: "normal",
|
|
33
|
+
explanationVerbosity: "normal",
|
|
34
|
+
explorationStyle: "linear",
|
|
35
|
+
ambiguityHandling: "ask"
|
|
36
|
+
},
|
|
37
|
+
metadata: nowMeta()
|
|
38
|
+
},
|
|
39
|
+
"builtin:calculated": {
|
|
40
|
+
schemaVersion: BEHAVIOR_PROFILE_SCHEMA_VERSION,
|
|
41
|
+
id: "builtin:calculated",
|
|
42
|
+
label: "Calculated",
|
|
43
|
+
summary: "Structured analysis and explicit tradeoffs before acting; still respects governance.",
|
|
44
|
+
dimensions: {
|
|
45
|
+
deliberationDepth: "high",
|
|
46
|
+
changeAppetite: "balanced",
|
|
47
|
+
checkInFrequency: "normal",
|
|
48
|
+
explanationVerbosity: "verbose",
|
|
49
|
+
explorationStyle: "linear",
|
|
50
|
+
ambiguityHandling: "ask"
|
|
51
|
+
},
|
|
52
|
+
interactionNotes: "Lay out options with pros/cons; prefer evidence-backed recommendations.",
|
|
53
|
+
metadata: nowMeta()
|
|
54
|
+
},
|
|
55
|
+
"builtin:experimental": {
|
|
56
|
+
schemaVersion: BEHAVIOR_PROFILE_SCHEMA_VERSION,
|
|
57
|
+
id: "builtin:experimental",
|
|
58
|
+
label: "Experimental",
|
|
59
|
+
summary: "Try alternatives and parallel approaches in low-risk areas; still obey policy gates.",
|
|
60
|
+
dimensions: {
|
|
61
|
+
deliberationDepth: "medium",
|
|
62
|
+
changeAppetite: "bold",
|
|
63
|
+
checkInFrequency: "normal",
|
|
64
|
+
explanationVerbosity: "normal",
|
|
65
|
+
explorationStyle: "parallel",
|
|
66
|
+
ambiguityHandling: "decide"
|
|
67
|
+
},
|
|
68
|
+
interactionNotes: "Experimental does not mean skipping tests, approvals, or PRINCIPLES—only style of exploration.",
|
|
69
|
+
metadata: nowMeta()
|
|
70
|
+
}
|
|
71
|
+
};
|