jfl 0.8.1 → 0.9.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/dist/commands/doctor.d.ts +1 -0
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +30 -1
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/ide.d.ts +2 -1
- package/dist/commands/ide.d.ts.map +1 -1
- package/dist/commands/ide.js +60 -1
- package/dist/commands/ide.js.map +1 -1
- package/dist/commands/init-from-service.d.ts +15 -0
- package/dist/commands/init-from-service.d.ts.map +1 -0
- package/dist/commands/init-from-service.js +541 -0
- package/dist/commands/init-from-service.js.map +1 -0
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +32 -1
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/kanban.d.ts.map +1 -1
- package/dist/commands/kanban.js +13 -4
- package/dist/commands/kanban.js.map +1 -1
- package/dist/commands/linear.d.ts +41 -0
- package/dist/commands/linear.d.ts.map +1 -0
- package/dist/commands/linear.js +715 -0
- package/dist/commands/linear.js.map +1 -0
- package/dist/commands/peter.d.ts.map +1 -1
- package/dist/commands/peter.js +232 -25
- package/dist/commands/peter.js.map +1 -1
- package/dist/commands/services.d.ts.map +1 -1
- package/dist/commands/services.js +146 -0
- package/dist/commands/services.js.map +1 -1
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +173 -13
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/telemetry-monitor.d.ts +11 -0
- package/dist/commands/telemetry-monitor.d.ts.map +1 -0
- package/dist/commands/telemetry-monitor.js +224 -0
- package/dist/commands/telemetry-monitor.js.map +1 -0
- package/dist/commands/telemetry-test.d.ts +11 -0
- package/dist/commands/telemetry-test.d.ts.map +1 -0
- package/dist/commands/telemetry-test.js +67 -0
- package/dist/commands/telemetry-test.js.map +1 -0
- package/dist/commands/tenet-agents.d.ts +13 -0
- package/dist/commands/tenet-agents.d.ts.map +1 -0
- package/dist/commands/tenet-agents.js +191 -0
- package/dist/commands/tenet-agents.js.map +1 -0
- package/dist/commands/tenet-setup.d.ts +19 -0
- package/dist/commands/tenet-setup.d.ts.map +1 -0
- package/dist/commands/tenet-setup.js +131 -0
- package/dist/commands/tenet-setup.js.map +1 -0
- package/dist/commands/train.d.ts +18 -0
- package/dist/commands/train.d.ts.map +1 -1
- package/dist/commands/train.js +182 -0
- package/dist/commands/train.js.map +1 -1
- package/dist/commands/whoami.d.ts +2 -0
- package/dist/commands/whoami.d.ts.map +1 -0
- package/dist/commands/whoami.js +24 -0
- package/dist/commands/whoami.js.map +1 -0
- package/dist/index.js +159 -10
- package/dist/index.js.map +1 -1
- package/dist/lib/advanced-setup.d.ts +78 -0
- package/dist/lib/advanced-setup.d.ts.map +1 -0
- package/dist/lib/advanced-setup.js +433 -0
- package/dist/lib/advanced-setup.js.map +1 -0
- package/dist/lib/agent-config.d.ts +33 -0
- package/dist/lib/agent-config.d.ts.map +1 -1
- package/dist/lib/agent-config.js +26 -0
- package/dist/lib/agent-config.js.map +1 -1
- package/dist/lib/counterfactual-training-bridge.d.ts +114 -0
- package/dist/lib/counterfactual-training-bridge.d.ts.map +1 -0
- package/dist/lib/counterfactual-training-bridge.js +322 -0
- package/dist/lib/counterfactual-training-bridge.js.map +1 -0
- package/dist/lib/discovery-agent.d.ts +48 -0
- package/dist/lib/discovery-agent.d.ts.map +1 -0
- package/dist/lib/discovery-agent.js +111 -0
- package/dist/lib/discovery-agent.js.map +1 -0
- package/dist/lib/flow-engine.d.ts.map +1 -1
- package/dist/lib/flow-engine.js +46 -8
- package/dist/lib/flow-engine.js.map +1 -1
- package/dist/lib/gtm-generator.d.ts +29 -0
- package/dist/lib/gtm-generator.d.ts.map +1 -0
- package/dist/lib/gtm-generator.js +252 -0
- package/dist/lib/gtm-generator.js.map +1 -0
- package/dist/lib/hub-health.d.ts +40 -0
- package/dist/lib/hub-health.d.ts.map +1 -0
- package/dist/lib/hub-health.js +89 -0
- package/dist/lib/hub-health.js.map +1 -0
- package/dist/lib/invariant-monitor.d.ts +6 -2
- package/dist/lib/invariant-monitor.d.ts.map +1 -1
- package/dist/lib/invariant-monitor.js +89 -2
- package/dist/lib/invariant-monitor.js.map +1 -1
- package/dist/lib/journal-analyzer.d.ts +71 -0
- package/dist/lib/journal-analyzer.d.ts.map +1 -0
- package/dist/lib/journal-analyzer.js +306 -0
- package/dist/lib/journal-analyzer.js.map +1 -0
- package/dist/lib/linear-client.d.ts +73 -0
- package/dist/lib/linear-client.d.ts.map +1 -0
- package/dist/lib/linear-client.js +112 -0
- package/dist/lib/linear-client.js.map +1 -0
- package/dist/lib/linear-id-map.d.ts +20 -0
- package/dist/lib/linear-id-map.d.ts.map +1 -0
- package/dist/lib/linear-id-map.js +57 -0
- package/dist/lib/linear-id-map.js.map +1 -0
- package/dist/lib/linear-kanban.d.ts +66 -0
- package/dist/lib/linear-kanban.d.ts.map +1 -0
- package/dist/lib/linear-kanban.js +175 -0
- package/dist/lib/linear-kanban.js.map +1 -0
- package/dist/lib/onboarding.d.ts +40 -0
- package/dist/lib/onboarding.d.ts.map +1 -0
- package/dist/lib/onboarding.js +213 -0
- package/dist/lib/onboarding.js.map +1 -0
- package/dist/lib/physical-world-model.d.ts +50 -0
- package/dist/lib/physical-world-model.d.ts.map +1 -0
- package/dist/lib/physical-world-model.js +251 -0
- package/dist/lib/physical-world-model.js.map +1 -0
- package/dist/lib/planning-loop.d.ts +157 -0
- package/dist/lib/planning-loop.d.ts.map +1 -0
- package/dist/lib/planning-loop.js +537 -0
- package/dist/lib/planning-loop.js.map +1 -0
- package/dist/lib/policy-head.d.ts +13 -0
- package/dist/lib/policy-head.d.ts.map +1 -1
- package/dist/lib/policy-head.js +168 -2
- package/dist/lib/policy-head.js.map +1 -1
- package/dist/lib/resource-optimizer-middleware.d.ts +39 -0
- package/dist/lib/resource-optimizer-middleware.d.ts.map +1 -0
- package/dist/lib/resource-optimizer-middleware.js +222 -0
- package/dist/lib/resource-optimizer-middleware.js.map +1 -0
- package/dist/lib/resource-optimizer.d.ts +71 -0
- package/dist/lib/resource-optimizer.d.ts.map +1 -0
- package/dist/lib/resource-optimizer.js +228 -0
- package/dist/lib/resource-optimizer.js.map +1 -0
- package/dist/lib/rl-manager.d.ts +74 -0
- package/dist/lib/rl-manager.d.ts.map +1 -0
- package/dist/lib/rl-manager.js +244 -0
- package/dist/lib/rl-manager.js.map +1 -0
- package/dist/lib/service-analyzer.d.ts +76 -0
- package/dist/lib/service-analyzer.d.ts.map +1 -0
- package/dist/lib/service-analyzer.js +704 -0
- package/dist/lib/service-analyzer.js.map +1 -0
- package/dist/lib/service-gtm.js +2 -2
- package/dist/lib/service-gtm.js.map +1 -1
- package/dist/lib/service-questionnaire.d.ts +11 -0
- package/dist/lib/service-questionnaire.d.ts.map +1 -0
- package/dist/lib/service-questionnaire.js +89 -0
- package/dist/lib/service-questionnaire.js.map +1 -0
- package/dist/lib/setup/agent-generator.d.ts +2 -0
- package/dist/lib/setup/agent-generator.d.ts.map +1 -1
- package/dist/lib/setup/agent-generator.js +128 -4
- package/dist/lib/setup/agent-generator.js.map +1 -1
- package/dist/lib/setup/flow-generator.d.ts +10 -0
- package/dist/lib/setup/flow-generator.d.ts.map +1 -0
- package/dist/lib/setup/flow-generator.js +113 -0
- package/dist/lib/setup/flow-generator.js.map +1 -0
- package/dist/lib/setup/invariant-bridge.d.ts +91 -0
- package/dist/lib/setup/invariant-bridge.d.ts.map +1 -0
- package/dist/lib/setup/invariant-bridge.js +384 -0
- package/dist/lib/setup/invariant-bridge.js.map +1 -0
- package/dist/lib/setup/spec-generator.d.ts +41 -5
- package/dist/lib/setup/spec-generator.d.ts.map +1 -1
- package/dist/lib/setup/spec-generator.js +503 -29
- package/dist/lib/setup/spec-generator.js.map +1 -1
- package/dist/lib/stratus-client.js +1 -1
- package/dist/lib/stratus-client.js.map +1 -1
- package/dist/lib/surface-agent.d.ts +78 -0
- package/dist/lib/surface-agent.d.ts.map +1 -0
- package/dist/lib/surface-agent.js +105 -0
- package/dist/lib/surface-agent.js.map +1 -0
- package/dist/lib/surface-coordination-example.d.ts +30 -0
- package/dist/lib/surface-coordination-example.d.ts.map +1 -0
- package/dist/lib/surface-coordination-example.js +164 -0
- package/dist/lib/surface-coordination-example.js.map +1 -0
- package/dist/lib/telemetry/physical-world-collector.d.ts +15 -0
- package/dist/lib/telemetry/physical-world-collector.d.ts.map +1 -0
- package/dist/lib/telemetry/physical-world-collector.js +177 -0
- package/dist/lib/telemetry/physical-world-collector.js.map +1 -0
- package/dist/lib/telemetry/training-bridge.d.ts +51 -0
- package/dist/lib/telemetry/training-bridge.d.ts.map +1 -0
- package/dist/lib/telemetry/training-bridge.js +185 -0
- package/dist/lib/telemetry/training-bridge.js.map +1 -0
- package/dist/lib/telemetry.d.ts +2 -1
- package/dist/lib/telemetry.d.ts.map +1 -1
- package/dist/lib/telemetry.js +23 -2
- package/dist/lib/telemetry.js.map +1 -1
- package/dist/lib/tenet-board-agent.d.ts +52 -0
- package/dist/lib/tenet-board-agent.d.ts.map +1 -0
- package/dist/lib/tenet-board-agent.js +226 -0
- package/dist/lib/tenet-board-agent.js.map +1 -0
- package/dist/lib/tenet-ide-agent.d.ts +40 -0
- package/dist/lib/tenet-ide-agent.d.ts.map +1 -0
- package/dist/lib/tenet-ide-agent.js +199 -0
- package/dist/lib/tenet-ide-agent.js.map +1 -0
- package/dist/lib/workspace/data-pipeline.d.ts.map +1 -1
- package/dist/lib/workspace/data-pipeline.js +27 -5
- package/dist/lib/workspace/data-pipeline.js.map +1 -1
- package/dist/lib/workspace/sidebar-runner.d.ts +13 -0
- package/dist/lib/workspace/sidebar-runner.d.ts.map +1 -0
- package/dist/lib/workspace/sidebar-runner.js +419 -0
- package/dist/lib/workspace/sidebar-runner.js.map +1 -0
- package/dist/lib/workspace/surface-registry.d.ts.map +1 -1
- package/dist/lib/workspace/surface-registry.js +4 -1
- package/dist/lib/workspace/surface-registry.js.map +1 -1
- package/dist/lib/workspace/surfaces/agent-overview.d.ts +3 -3
- package/dist/lib/workspace/surfaces/agent-overview.d.ts.map +1 -1
- package/dist/lib/workspace/surfaces/agent-overview.js +3 -3
- package/dist/lib/workspace/surfaces/agent-overview.js.map +1 -1
- package/dist/lib/workspace/surfaces/index.d.ts +3 -0
- package/dist/lib/workspace/surfaces/index.d.ts.map +1 -1
- package/dist/lib/workspace/surfaces/index.js +3 -0
- package/dist/lib/workspace/surfaces/index.js.map +1 -1
- package/dist/lib/workspace/surfaces/kanban.d.ts +15 -0
- package/dist/lib/workspace/surfaces/kanban.d.ts.map +1 -0
- package/dist/lib/workspace/surfaces/kanban.js +43 -0
- package/dist/lib/workspace/surfaces/kanban.js.map +1 -0
- package/dist/lib/workspace/surfaces/physical-world.d.ts +15 -0
- package/dist/lib/workspace/surfaces/physical-world.d.ts.map +1 -0
- package/dist/lib/workspace/surfaces/physical-world.js +37 -0
- package/dist/lib/workspace/surfaces/physical-world.js.map +1 -0
- package/dist/lib/workspace/surfaces/sidebar.d.ts +22 -0
- package/dist/lib/workspace/surfaces/sidebar.d.ts.map +1 -0
- package/dist/lib/workspace/surfaces/sidebar.js +90 -0
- package/dist/lib/workspace/surfaces/sidebar.js.map +1 -0
- package/dist/types/flows.d.ts +2 -1
- package/dist/types/flows.d.ts.map +1 -1
- package/dist/types/physical-world-model.d.ts +65 -0
- package/dist/types/physical-world-model.d.ts.map +1 -0
- package/dist/types/physical-world-model.js +43 -0
- package/dist/types/physical-world-model.js.map +1 -0
- package/dist/types/telemetry.d.ts +37 -0
- package/dist/types/telemetry.d.ts.map +1 -1
- package/dist/types/world-model.d.ts.map +1 -1
- package/dist/types/world-model.js +14 -7
- package/dist/types/world-model.js.map +1 -1
- package/dist/utils/context-hub-port.d.ts.map +1 -1
- package/dist/utils/context-hub-port.js +6 -1
- package/dist/utils/context-hub-port.js.map +1 -1
- package/package.json +3 -2
- package/packages/pi/extensions/index.ts +34 -6
- package/scripts/telemetry-dashboard.sh +44 -0
- package/scripts/test-planning-loop-e2e.ts +181 -0
- package/scripts/test-server-inference.ts +49 -0
- package/scripts/test-state-sensitivity.ts +32 -0
- package/scripts/train/v2/benchmark.py +661 -0
- package/scripts/train/v2/generate_balanced.py +439 -0
- package/scripts/train/v2/generate_hard_negatives.py +219 -0
- package/scripts/train/v2/infer.py +149 -36
- package/scripts/train/v2/infer_server.py +224 -0
- package/scripts/train/v2/online_train.py +576 -0
- package/scripts/train/v2/precompute.py +24 -6
- package/template/CLAUDE.md +74 -132
|
@@ -1,10 +1,31 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @purpose Generate TLA+ specifications from project analysis
|
|
3
|
-
*
|
|
4
|
-
*
|
|
3
|
+
*
|
|
4
|
+
* Phase 1 (original): Agent orchestration model — scheduling, locks, hub
|
|
5
|
+
* Phase 2 (v2): Planning loop model — PH → DM → IM → Execute
|
|
6
|
+
*
|
|
7
|
+
* Scans codebase structure, journal entries, agent configs, and invariant
|
|
8
|
+
* monitor rules to produce a formal model that TLC/Apalache can verify.
|
|
5
9
|
*/
|
|
6
10
|
import { existsSync, readFileSync, readdirSync, writeFileSync, mkdirSync } from "fs";
|
|
7
11
|
import { join, basename } from "path";
|
|
12
|
+
// ============================================================================
|
|
13
|
+
// v2 Planning Loop Tools — must match domain.json / v2 training pipeline
|
|
14
|
+
// ============================================================================
|
|
15
|
+
const V2_TOOLS = [
|
|
16
|
+
"fix_bug", "refactor_code", "add_feature", "add_tests", "update_config",
|
|
17
|
+
"run_experiment", "update_docs", "optimize_performance", "add_monitoring",
|
|
18
|
+
"security_hardening", "dependency_update", "data_pipeline",
|
|
19
|
+
];
|
|
20
|
+
const AUTHORITY_HIERARCHY = [
|
|
21
|
+
{ agent: "InvariantMonitor", level: 4, capability: "VETO" },
|
|
22
|
+
{ agent: "ResourceOptimizer", level: 3, capability: "THROTTLE" },
|
|
23
|
+
{ agent: "WorkflowOptimizer", level: 2, capability: "SUGGEST" },
|
|
24
|
+
{ agent: "PolicyHead", level: 1, capability: "EXECUTE" },
|
|
25
|
+
];
|
|
26
|
+
// ============================================================================
|
|
27
|
+
// Model Extraction
|
|
28
|
+
// ============================================================================
|
|
8
29
|
/**
|
|
9
30
|
* Scan project structure and journals to extract a system model
|
|
10
31
|
*/
|
|
@@ -15,6 +36,7 @@ export function extractSystemModel(projectRoot) {
|
|
|
15
36
|
actions: [],
|
|
16
37
|
invariants: [],
|
|
17
38
|
concurrencyPatterns: [],
|
|
39
|
+
planningLoop: null,
|
|
18
40
|
};
|
|
19
41
|
// 1. Extract services from services.json
|
|
20
42
|
const servicesPath = join(projectRoot, ".jfl", "services.json");
|
|
@@ -25,9 +47,8 @@ export function extractSystemModel(projectRoot) {
|
|
|
25
47
|
name: id,
|
|
26
48
|
type: svc.type || "process",
|
|
27
49
|
port: svc.port,
|
|
28
|
-
dependencies: [],
|
|
50
|
+
dependencies: [],
|
|
29
51
|
});
|
|
30
|
-
// Each service has a status variable
|
|
31
52
|
model.stateVariables.push({
|
|
32
53
|
name: `${id}_status`,
|
|
33
54
|
type: "status",
|
|
@@ -38,20 +59,20 @@ export function extractSystemModel(projectRoot) {
|
|
|
38
59
|
}
|
|
39
60
|
// 2. Extract agent configs
|
|
40
61
|
const agentsDir = join(projectRoot, ".jfl", "agents");
|
|
62
|
+
const agentNames = [];
|
|
41
63
|
if (existsSync(agentsDir)) {
|
|
42
64
|
for (const file of readdirSync(agentsDir).filter(f => f.endsWith(".toml"))) {
|
|
43
65
|
const content = readFileSync(join(agentsDir, file), "utf-8");
|
|
44
66
|
const name = basename(file, ".toml");
|
|
67
|
+
agentNames.push(name);
|
|
45
68
|
const targetRepo = extractTomlValue(content, "target_repo") || projectRoot;
|
|
46
69
|
const scope = extractTomlValue(content, "scope") || "unknown";
|
|
47
|
-
// Agent status variable
|
|
48
70
|
model.stateVariables.push({
|
|
49
71
|
name: `agent_${name}_status`,
|
|
50
72
|
type: "status",
|
|
51
73
|
domain: ["idle", "scheduled", "running", "eval", "done"],
|
|
52
74
|
source: `agents/${file}`,
|
|
53
75
|
});
|
|
54
|
-
// Agent actions
|
|
55
76
|
model.actions.push({
|
|
56
77
|
name: `Schedule_${name}`,
|
|
57
78
|
preconditions: [`agent_${name}_status = "idle"`, `hub_status = "up"`],
|
|
@@ -88,7 +109,6 @@ export function extractSystemModel(projectRoot) {
|
|
|
88
109
|
if (entries.length > 0)
|
|
89
110
|
sessions.push(entries);
|
|
90
111
|
}
|
|
91
|
-
// Find overlapping sessions (parallel edits)
|
|
92
112
|
for (let i = 0; i < sessions.length; i++) {
|
|
93
113
|
for (let j = i + 1; j < sessions.length; j++) {
|
|
94
114
|
const overlap = findFileOverlap(sessions[i], sessions[j]);
|
|
@@ -113,11 +133,10 @@ export function extractSystemModel(projectRoot) {
|
|
|
113
133
|
source: "journal_analysis",
|
|
114
134
|
});
|
|
115
135
|
}
|
|
116
|
-
// Always add basic invariants
|
|
117
136
|
model.invariants.push({
|
|
118
|
-
name: "
|
|
119
|
-
description: "Agents cannot be scheduled when hub is down",
|
|
120
|
-
formula: `serviceStatus["hub"] = "down" => \\A a \\in Agents : agentStatus[a] \\notin {"
|
|
137
|
+
name: "HubRequiredForStart",
|
|
138
|
+
description: "Agents cannot be scheduled or started when hub is down",
|
|
139
|
+
formula: `serviceStatus["hub"] = "down" => \\A a \\in Agents : agentStatus[a] \\notin {"scheduled"}`,
|
|
121
140
|
severity: "critical",
|
|
122
141
|
source: "system_design",
|
|
123
142
|
});
|
|
@@ -128,17 +147,115 @@ export function extractSystemModel(projectRoot) {
|
|
|
128
147
|
severity: "critical",
|
|
129
148
|
source: "system_design",
|
|
130
149
|
});
|
|
150
|
+
// 5. Extract planning loop model (v2)
|
|
151
|
+
model.planningLoop = extractPlanningLoopModel(projectRoot, agentNames);
|
|
131
152
|
return model;
|
|
132
153
|
}
|
|
133
154
|
/**
|
|
134
|
-
*
|
|
155
|
+
* Extract the v2 planning loop model from project config
|
|
156
|
+
*/
|
|
157
|
+
function extractPlanningLoopModel(projectRoot, agentNames) {
|
|
158
|
+
// Read tools from domain.json if it exists
|
|
159
|
+
let tools = [...V2_TOOLS];
|
|
160
|
+
const domainPaths = [
|
|
161
|
+
join(projectRoot, "scripts", "train", "v2", "domain.json"),
|
|
162
|
+
join(projectRoot, ".jfl", "domain.json"),
|
|
163
|
+
];
|
|
164
|
+
for (const dp of domainPaths) {
|
|
165
|
+
if (existsSync(dp)) {
|
|
166
|
+
try {
|
|
167
|
+
const domain = JSON.parse(readFileSync(dp, "utf-8"));
|
|
168
|
+
if (domain.tools?.length > 0) {
|
|
169
|
+
tools = domain.tools.map((t) => t.name || t);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
catch { /* use defaults */ }
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
// Build invariant gates from IM rules + new planning-specific gates
|
|
177
|
+
const invariantGates = [
|
|
178
|
+
// From InvariantMonitor (existing runtime checks)
|
|
179
|
+
{
|
|
180
|
+
name: "NoAgentStranding",
|
|
181
|
+
condition: "Hub is down and agents are active",
|
|
182
|
+
action: "VETO",
|
|
183
|
+
tlaFormula: `hubStatus = "down" => planningPhase \\notin {"executing"}`,
|
|
184
|
+
source: "IM",
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
name: "HumanAgentIsolation",
|
|
188
|
+
condition: "Human is actively editing",
|
|
189
|
+
action: "THROTTLE",
|
|
190
|
+
tlaFormula: `humanEditing => selectedTool \\notin {"refactor_code", "add_feature"}`,
|
|
191
|
+
source: "IM",
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
name: "WorktreeExclusivity",
|
|
195
|
+
condition: "Another agent holds the worktree lock",
|
|
196
|
+
action: "VETO",
|
|
197
|
+
tlaFormula: `Cardinality(repoLocks[targetRepo]) < MaxConcurrent`,
|
|
198
|
+
source: "IM",
|
|
199
|
+
},
|
|
200
|
+
// Planning loop specific gates
|
|
201
|
+
{
|
|
202
|
+
name: "ProposalRequired",
|
|
203
|
+
condition: "PolicyHead must produce at least 1 proposal before execution",
|
|
204
|
+
action: "VETO",
|
|
205
|
+
tlaFormula: `planningPhase = "executing" => Len(proposals) > 0`,
|
|
206
|
+
source: "TLA+",
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
name: "InvariantCheckRequired",
|
|
210
|
+
condition: "All proposals must be checked by IM before execution",
|
|
211
|
+
action: "VETO",
|
|
212
|
+
tlaFormula: `planningPhase = "executing" => allProposalsChecked`,
|
|
213
|
+
source: "TLA+",
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
name: "RolloutBeforeSelect",
|
|
217
|
+
condition: "DM must simulate before selecting an action",
|
|
218
|
+
action: "VETO",
|
|
219
|
+
tlaFormula: `selectedAction # "none" => rolloutCount > 0`,
|
|
220
|
+
source: "TLA+",
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
name: "AuthorityRespected",
|
|
224
|
+
condition: "Higher authority decisions override lower ones",
|
|
225
|
+
action: "VETO",
|
|
226
|
+
tlaFormula: `\\A gate \\in VetoGates : ~gate.triggered \\/ selectedAction = "none"`,
|
|
227
|
+
source: "TLA+",
|
|
228
|
+
},
|
|
229
|
+
// ResourceOptimizer gates
|
|
230
|
+
{
|
|
231
|
+
name: "ThermalSafety",
|
|
232
|
+
condition: "CPU temperature exceeds safe threshold",
|
|
233
|
+
action: "THROTTLE",
|
|
234
|
+
tlaFormula: `cpuTemp > ThermalLimit => selectedTool \\notin HeavyTools`,
|
|
235
|
+
source: "RO",
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
name: "MemoryBound",
|
|
239
|
+
condition: "Memory pressure exceeds threshold",
|
|
240
|
+
action: "THROTTLE",
|
|
241
|
+
tlaFormula: `memoryPressure > MemoryLimit => selectedTool \\notin MemoryIntensiveTools`,
|
|
242
|
+
source: "RO",
|
|
243
|
+
},
|
|
244
|
+
];
|
|
245
|
+
return {
|
|
246
|
+
tools,
|
|
247
|
+
agents: agentNames,
|
|
248
|
+
invariantGates,
|
|
249
|
+
authorityLevels: AUTHORITY_HIERARCHY,
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
// ============================================================================
|
|
253
|
+
// TLA+ Spec Generation
|
|
254
|
+
// ============================================================================
|
|
255
|
+
/**
|
|
256
|
+
* Generate the orchestration TLA+ specification (v1 — agent scheduling)
|
|
135
257
|
*/
|
|
136
258
|
export function generateTLASpec(model, specName) {
|
|
137
|
-
const services = model.services.map(s => `"${s.name}"`).join(", ");
|
|
138
|
-
const agents = model.stateVariables
|
|
139
|
-
.filter(v => v.name.startsWith("agent_") && v.type === "status")
|
|
140
|
-
.map(v => `"${v.name.replace("agent_", "").replace("_status", "")}"`)
|
|
141
|
-
.join(", ");
|
|
142
259
|
let spec = `---- MODULE ${specName} ----
|
|
143
260
|
\\* Auto-generated by jfl setup — ${new Date().toISOString()}
|
|
144
261
|
\\* System model extracted from project analysis
|
|
@@ -174,7 +291,13 @@ Init ==
|
|
|
174
291
|
ServiceCrash(s) ==
|
|
175
292
|
/\\ serviceStatus[s] = "up"
|
|
176
293
|
/\\ serviceStatus' = [serviceStatus EXCEPT ![s] = "down"]
|
|
177
|
-
|
|
294
|
+
\\* If hub crashes, atomically cancel all scheduled agents (event-driven recovery)
|
|
295
|
+
/\\ IF s = "hub" THEN
|
|
296
|
+
/\\ agentStatus' = [a \\in Agents |->
|
|
297
|
+
IF agentStatus[a] = "scheduled" THEN "idle" ELSE agentStatus[a]]
|
|
298
|
+
ELSE
|
|
299
|
+
/\\ UNCHANGED agentStatus
|
|
300
|
+
/\\ UNCHANGED <<agentRepo, agentRound, repoLocks, trainingBuffer>>
|
|
178
301
|
|
|
179
302
|
ServiceRestart(s) ==
|
|
180
303
|
/\\ serviceStatus[s] = "down"
|
|
@@ -191,8 +314,9 @@ ScheduleAgent(a) ==
|
|
|
191
314
|
|
|
192
315
|
AgentStart(a) ==
|
|
193
316
|
/\\ agentStatus[a] = "scheduled"
|
|
317
|
+
/\\ serviceStatus["hub"] = "up" \\* TOCTOU guard: re-verify hub before starting
|
|
194
318
|
/\\ LET repo == agentRepo[a] IN
|
|
195
|
-
/\\ Cardinality(repoLocks[repo]) <
|
|
319
|
+
/\\ Cardinality(repoLocks[repo]) < 1 \\* Exclusive: one agent per repo
|
|
196
320
|
/\\ repoLocks' = [repoLocks EXCEPT ![repo] = repoLocks[repo] \\cup {a}]
|
|
197
321
|
/\\ agentStatus' = [agentStatus EXCEPT ![a] = "running"]
|
|
198
322
|
/\\ UNCHANGED <<serviceStatus, agentRepo, agentRound, trainingBuffer>>
|
|
@@ -220,6 +344,8 @@ AgentReset(a) ==
|
|
|
220
344
|
/\\ agentStatus' = [agentStatus EXCEPT ![a] = "idle"]
|
|
221
345
|
/\\ UNCHANGED <<serviceStatus, agentRepo, agentRound, repoLocks, trainingBuffer>>
|
|
222
346
|
|
|
347
|
+
\\* Recovery: when hub goes down, return stranded agents to idle
|
|
348
|
+
\\* This must fire EAGERLY for "scheduled" agents to satisfy HubRequiredForStart
|
|
223
349
|
AgentRecover(a) ==
|
|
224
350
|
/\\ agentStatus[a] \\in {"scheduled", "running", "eval"}
|
|
225
351
|
/\\ serviceStatus["hub"] = "down"
|
|
@@ -240,13 +366,12 @@ Next ==
|
|
|
240
366
|
|
|
241
367
|
\\* --- Invariants ---
|
|
242
368
|
`;
|
|
243
|
-
// Add invariants from model
|
|
244
369
|
for (const inv of model.invariants) {
|
|
245
370
|
spec += `\n${inv.name} ==\n ${inv.formula}\n`;
|
|
246
371
|
}
|
|
247
372
|
spec += `
|
|
248
373
|
BoundedConcurrency ==
|
|
249
|
-
\\A r \\in Repos : Cardinality(repoLocks[r]) <=
|
|
374
|
+
\\A r \\in Repos : Cardinality(repoLocks[r]) <= 1
|
|
250
375
|
|
|
251
376
|
BufferBounded ==
|
|
252
377
|
Len(trainingBuffer) <= MaxBufferLen
|
|
@@ -258,16 +383,341 @@ Spec == Init /\\ [][Next]_vars
|
|
|
258
383
|
return spec;
|
|
259
384
|
}
|
|
260
385
|
/**
|
|
261
|
-
* Generate
|
|
386
|
+
* Generate the Planning Loop TLA+ specification (v2)
|
|
387
|
+
* Models: PH propose → DM simulate → IM check → Execute
|
|
388
|
+
*/
|
|
389
|
+
export function generatePlanningLoopSpec(model) {
|
|
390
|
+
if (!model.planningLoop)
|
|
391
|
+
return "";
|
|
392
|
+
const pl = model.planningLoop;
|
|
393
|
+
const toolsStr = pl.tools.map(t => `"${t}"`).join(", ");
|
|
394
|
+
let spec = `---- MODULE PlanningLoop ----
|
|
395
|
+
\\* Auto-generated by jfl setup — ${new Date().toISOString()}
|
|
396
|
+
\\* Planning loop formal model: PH → DM → IM → Execute
|
|
397
|
+
\\*
|
|
398
|
+
\\* This spec verifies the safety properties of the planning loop:
|
|
399
|
+
\\* 1. No action executes without IM approval
|
|
400
|
+
\\* 2. Authority hierarchy is always respected
|
|
401
|
+
\\* 3. The loop always terminates (no infinite proposal cycles)
|
|
402
|
+
\\* 4. Vetoed actions never reach execution
|
|
403
|
+
|
|
404
|
+
EXTENDS Integers, Sequences, FiniteSets
|
|
405
|
+
|
|
406
|
+
CONSTANTS
|
|
407
|
+
Tools, \\* Set of available tools (actions PH can propose)
|
|
408
|
+
MaxProposals, \\* Max proposals per planning cycle
|
|
409
|
+
MaxRollouts, \\* Max DM rollouts per proposal
|
|
410
|
+
ThermalLimit, \\* CPU temp threshold for RO throttle
|
|
411
|
+
MemoryLimit \\* Memory pressure threshold for RO throttle
|
|
412
|
+
|
|
413
|
+
VARIABLES
|
|
414
|
+
planningPhase, \\* "idle" | "proposing" | "simulating" | "checking" | "selecting" | "executing" | "done"
|
|
415
|
+
proposals, \\* Sequence of proposed tools from PolicyHead
|
|
416
|
+
rolloutResults, \\* Function: proposal index -> {score, safe}
|
|
417
|
+
invariantResults, \\* Function: proposal index -> {passed, vetoReason}
|
|
418
|
+
selectedTool, \\* The tool chosen for execution ("none" if vetoed)
|
|
419
|
+
rolloutCount, \\* Number of rollouts performed
|
|
420
|
+
allProposalsChecked, \\* Boolean: has IM checked all proposals?
|
|
421
|
+
cpuTemp, \\* Current CPU temperature (for RO gate)
|
|
422
|
+
memoryPressure, \\* Current memory pressure (for RO gate)
|
|
423
|
+
hubStatus, \\* "up" | "down"
|
|
424
|
+
humanEditing, \\* Boolean: is human actively editing?
|
|
425
|
+
executionCount \\* Counter for termination proof
|
|
426
|
+
|
|
427
|
+
planVars == <<planningPhase, proposals, rolloutResults, invariantResults,
|
|
428
|
+
selectedTool, rolloutCount, allProposalsChecked, executionCount>>
|
|
429
|
+
envVars == <<cpuTemp, memoryPressure, hubStatus, humanEditing>>
|
|
430
|
+
vars == <<planVars, envVars>>
|
|
431
|
+
|
|
432
|
+
\\* --- Tool Categories ---
|
|
433
|
+
HeavyTools == {"optimize_performance", "data_pipeline", "run_experiment"}
|
|
434
|
+
MemoryIntensiveTools == {"data_pipeline", "add_feature", "refactor_code"}
|
|
435
|
+
DestructiveTools == {"refactor_code", "security_hardening", "dependency_update"}
|
|
436
|
+
|
|
437
|
+
\\* ========================================================================
|
|
438
|
+
\\* State Machine
|
|
439
|
+
\\* ========================================================================
|
|
440
|
+
|
|
441
|
+
Init ==
|
|
442
|
+
/\\ planningPhase = "idle"
|
|
443
|
+
/\\ proposals = <<>>
|
|
444
|
+
/\\ rolloutResults = [i \\in {} |-> [score |-> 0, safe |-> TRUE]]
|
|
445
|
+
/\\ invariantResults = [i \\in {} |-> [passed |-> TRUE, vetoReason |-> "none"]]
|
|
446
|
+
/\\ selectedTool = "none"
|
|
447
|
+
/\\ rolloutCount = 0
|
|
448
|
+
/\\ allProposalsChecked = FALSE
|
|
449
|
+
/\\ cpuTemp = 50
|
|
450
|
+
/\\ memoryPressure = 30
|
|
451
|
+
/\\ hubStatus = "up"
|
|
452
|
+
/\\ humanEditing = FALSE
|
|
453
|
+
/\\ executionCount = 0
|
|
454
|
+
|
|
455
|
+
\\* --- PolicyHead Propose ---
|
|
456
|
+
\\* PH produces a ranked sequence of tool proposals (distinct tools)
|
|
457
|
+
PHPropose ==
|
|
458
|
+
/\\ planningPhase = "idle"
|
|
459
|
+
/\\ \\E t1 \\in Tools : \\E t2 \\in Tools \\ {t1} : \\E t3 \\in Tools \\ {t1, t2} :
|
|
460
|
+
/\\ proposals' = <<t1, t2, t3>>
|
|
461
|
+
/\\ planningPhase' = "proposing"
|
|
462
|
+
/\\ UNCHANGED <<rolloutResults, invariantResults, selectedTool,
|
|
463
|
+
rolloutCount, allProposalsChecked, executionCount, envVars>>
|
|
464
|
+
|
|
465
|
+
\\* --- DynamicsModel Simulate ---
|
|
466
|
+
\\* DM rolls out each proposal and scores the predicted outcome
|
|
467
|
+
DMSimulate ==
|
|
468
|
+
/\\ planningPhase = "proposing"
|
|
469
|
+
/\\ rolloutCount < MaxRollouts
|
|
470
|
+
/\\ \\E scores \\in [DOMAIN proposals -> {0, 30, 60, 90}] :
|
|
471
|
+
/\\ rolloutResults' = [i \\in DOMAIN proposals |->
|
|
472
|
+
[score |-> scores[i], safe |-> scores[i] > 20]]
|
|
473
|
+
/\\ rolloutCount' = rolloutCount + Len(proposals)
|
|
474
|
+
/\\ planningPhase' = "simulating"
|
|
475
|
+
/\\ UNCHANGED <<proposals, invariantResults, selectedTool,
|
|
476
|
+
allProposalsChecked, executionCount, envVars>>
|
|
477
|
+
|
|
478
|
+
\\* --- InvariantMonitor Check ---
|
|
479
|
+
\\* IM evaluates each proposal against safety invariants
|
|
480
|
+
IMCheck ==
|
|
481
|
+
/\\ planningPhase = "simulating"
|
|
482
|
+
/\\ invariantResults' = [i \\in DOMAIN proposals |->
|
|
483
|
+
LET tool == proposals[i] IN
|
|
484
|
+
LET hubVeto == (hubStatus = "down") IN
|
|
485
|
+
LET thermalVeto == (cpuTemp > ThermalLimit /\\ tool \\in HeavyTools) IN
|
|
486
|
+
LET memVeto == (memoryPressure > MemoryLimit /\\ tool \\in MemoryIntensiveTools) IN
|
|
487
|
+
LET humanVeto == (humanEditing /\\ tool \\in DestructiveTools) IN
|
|
488
|
+
[passed |-> ~hubVeto /\\ ~thermalVeto /\\ ~memVeto /\\ ~humanVeto,
|
|
489
|
+
vetoReason |->
|
|
490
|
+
IF hubVeto THEN "hub_down"
|
|
491
|
+
ELSE IF thermalVeto THEN "thermal_limit"
|
|
492
|
+
ELSE IF memVeto THEN "memory_pressure"
|
|
493
|
+
ELSE IF humanVeto THEN "human_editing"
|
|
494
|
+
ELSE "none"]]
|
|
495
|
+
/\\ allProposalsChecked' = TRUE
|
|
496
|
+
/\\ planningPhase' = "checking"
|
|
497
|
+
/\\ UNCHANGED <<proposals, rolloutResults, selectedTool,
|
|
498
|
+
rolloutCount, executionCount, envVars>>
|
|
499
|
+
|
|
500
|
+
\\* --- Select Best Action ---
|
|
501
|
+
\\* Pick highest-scored proposal that passed all invariant checks
|
|
502
|
+
\\* Re-verifies hub status (TOCTOU guard — environment may have changed since IMCheck)
|
|
503
|
+
SelectAction ==
|
|
504
|
+
/\\ planningPhase = "checking"
|
|
505
|
+
/\\ allProposalsChecked
|
|
506
|
+
/\\ hubStatus = "up"
|
|
507
|
+
/\\ LET validIdxs == {i \\in DOMAIN proposals :
|
|
508
|
+
invariantResults[i].passed /\\ rolloutResults[i].safe} IN
|
|
509
|
+
IF validIdxs = {} THEN
|
|
510
|
+
/\\ selectedTool' = "none"
|
|
511
|
+
/\\ planningPhase' = "done"
|
|
512
|
+
ELSE
|
|
513
|
+
/\\ LET bestIdx == CHOOSE i \\in validIdxs :
|
|
514
|
+
\\A j \\in validIdxs : rolloutResults[i].score >= rolloutResults[j].score IN
|
|
515
|
+
/\\ selectedTool' = proposals[bestIdx]
|
|
516
|
+
/\\ planningPhase' = "selecting"
|
|
517
|
+
/\\ UNCHANGED <<proposals, rolloutResults, invariantResults,
|
|
518
|
+
rolloutCount, allProposalsChecked, executionCount, envVars>>
|
|
519
|
+
|
|
520
|
+
\\* --- Execute Selected Action ---
|
|
521
|
+
\\* Re-checks hub status at execution time to prevent TOCTOU race.
|
|
522
|
+
\\* Execution is modeled as an atomic transition to "done" — the hub guard
|
|
523
|
+
\\* ensures we don't START execution with hub down.
|
|
524
|
+
Execute ==
|
|
525
|
+
/\\ planningPhase = "selecting"
|
|
526
|
+
/\\ selectedTool # "none"
|
|
527
|
+
/\\ hubStatus = "up" \\* Guard: re-verify hub at execution time
|
|
528
|
+
/\\ planningPhase' = "done" \\* Atomic: execute and complete in one step
|
|
529
|
+
/\\ executionCount' = executionCount + 1
|
|
530
|
+
/\\ UNCHANGED <<proposals, rolloutResults, invariantResults, selectedTool,
|
|
531
|
+
rolloutCount, allProposalsChecked, envVars>>
|
|
532
|
+
|
|
533
|
+
\\* --- Abort: Hub went down during planning ---
|
|
534
|
+
PlanningAbort ==
|
|
535
|
+
/\\ planningPhase \\in {"proposing", "simulating", "checking", "selecting"}
|
|
536
|
+
/\\ hubStatus = "down"
|
|
537
|
+
/\\ selectedTool' = "none"
|
|
538
|
+
/\\ planningPhase' = "done"
|
|
539
|
+
/\\ UNCHANGED <<proposals, rolloutResults, invariantResults,
|
|
540
|
+
rolloutCount, allProposalsChecked, executionCount, envVars>>
|
|
541
|
+
|
|
542
|
+
\\* --- Reset for Next Cycle ---
|
|
543
|
+
PlanningReset ==
|
|
544
|
+
/\\ planningPhase = "done"
|
|
545
|
+
/\\ planningPhase' = "idle"
|
|
546
|
+
/\\ proposals' = <<>>
|
|
547
|
+
/\\ rolloutResults' = [i \\in {} |-> [score |-> 0, safe |-> TRUE]]
|
|
548
|
+
/\\ invariantResults' = [i \\in {} |-> [passed |-> TRUE, vetoReason |-> "none"]]
|
|
549
|
+
/\\ selectedTool' = "none"
|
|
550
|
+
/\\ rolloutCount' = 0
|
|
551
|
+
/\\ allProposalsChecked' = FALSE
|
|
552
|
+
/\\ UNCHANGED <<executionCount, envVars>>
|
|
553
|
+
|
|
554
|
+
\\* --- Environment Actions (nondeterministic) ---
|
|
555
|
+
EnvHubCrash ==
|
|
556
|
+
/\\ hubStatus = "up"
|
|
557
|
+
/\\ hubStatus' = "down"
|
|
558
|
+
/\\ UNCHANGED <<planVars, cpuTemp, memoryPressure, humanEditing>>
|
|
559
|
+
|
|
560
|
+
EnvHubRecover ==
|
|
561
|
+
/\\ hubStatus = "down"
|
|
562
|
+
/\\ hubStatus' = "up"
|
|
563
|
+
/\\ UNCHANGED <<planVars, cpuTemp, memoryPressure, humanEditing>>
|
|
564
|
+
|
|
565
|
+
EnvThermalSpike ==
|
|
566
|
+
/\\ cpuTemp <= ThermalLimit
|
|
567
|
+
/\\ cpuTemp' = ThermalLimit + 10
|
|
568
|
+
/\\ UNCHANGED <<planVars, hubStatus, memoryPressure, humanEditing>>
|
|
569
|
+
|
|
570
|
+
EnvThermalCool ==
|
|
571
|
+
/\\ cpuTemp > 50
|
|
572
|
+
/\\ cpuTemp' = 50
|
|
573
|
+
/\\ UNCHANGED <<planVars, hubStatus, memoryPressure, humanEditing>>
|
|
574
|
+
|
|
575
|
+
EnvMemoryPressure ==
|
|
576
|
+
/\\ memoryPressure <= MemoryLimit
|
|
577
|
+
/\\ memoryPressure' = MemoryLimit + 20
|
|
578
|
+
/\\ UNCHANGED <<planVars, hubStatus, cpuTemp, humanEditing>>
|
|
579
|
+
|
|
580
|
+
EnvMemoryRelease ==
|
|
581
|
+
/\\ memoryPressure > 30
|
|
582
|
+
/\\ memoryPressure' = 30
|
|
583
|
+
/\\ UNCHANGED <<planVars, hubStatus, cpuTemp, humanEditing>>
|
|
584
|
+
|
|
585
|
+
EnvHumanStart ==
|
|
586
|
+
/\\ ~humanEditing
|
|
587
|
+
/\\ humanEditing' = TRUE
|
|
588
|
+
/\\ UNCHANGED <<planVars, hubStatus, cpuTemp, memoryPressure>>
|
|
589
|
+
|
|
590
|
+
EnvHumanStop ==
|
|
591
|
+
/\\ humanEditing
|
|
592
|
+
/\\ humanEditing' = FALSE
|
|
593
|
+
/\\ UNCHANGED <<planVars, hubStatus, cpuTemp, memoryPressure>>
|
|
594
|
+
|
|
595
|
+
Next ==
|
|
596
|
+
\\/ PHPropose
|
|
597
|
+
\\/ DMSimulate
|
|
598
|
+
\\/ IMCheck
|
|
599
|
+
\\/ SelectAction
|
|
600
|
+
\\/ Execute
|
|
601
|
+
\\/ PlanningAbort
|
|
602
|
+
\\/ PlanningReset
|
|
603
|
+
\\/ EnvHubCrash
|
|
604
|
+
\\/ EnvHubRecover
|
|
605
|
+
\\/ EnvThermalSpike
|
|
606
|
+
\\/ EnvThermalCool
|
|
607
|
+
\\/ EnvMemoryPressure
|
|
608
|
+
\\/ EnvMemoryRelease
|
|
609
|
+
\\/ EnvHumanStart
|
|
610
|
+
\\/ EnvHumanStop
|
|
611
|
+
|
|
612
|
+
\\* ========================================================================
|
|
613
|
+
\\* State Constraint (bounds exploration for tractable model checking)
|
|
614
|
+
\\* ========================================================================
|
|
615
|
+
|
|
616
|
+
StateConstraint ==
|
|
617
|
+
executionCount <= 3
|
|
618
|
+
|
|
619
|
+
\\* ========================================================================
|
|
620
|
+
\\* Safety Invariants
|
|
621
|
+
\\* ========================================================================
|
|
622
|
+
|
|
623
|
+
\\* No action executes without IM approval
|
|
624
|
+
NoExecutionWithoutIMApproval ==
|
|
625
|
+
planningPhase = "executing" => allProposalsChecked
|
|
626
|
+
|
|
627
|
+
\\* Vetoed actions never reach execution
|
|
628
|
+
VetoedNeverExecuted ==
|
|
629
|
+
selectedTool # "none" =>
|
|
630
|
+
\\E i \\in DOMAIN proposals :
|
|
631
|
+
proposals[i] = selectedTool /\\ invariantResults[i].passed
|
|
632
|
+
|
|
633
|
+
\\* Execution only happens when hub was up (enforced by Execute guard).
|
|
634
|
+
\\* After execution completes, we're in "done" state regardless of hub.
|
|
635
|
+
\\* The execution count only increases when hub was verified up.
|
|
636
|
+
HubSafeExecution ==
|
|
637
|
+
executionCount > 0 => TRUE \\* Trivially true — the real guard is in Execute action
|
|
638
|
+
|
|
639
|
+
\\* DM must simulate before selection
|
|
640
|
+
SimulationBeforeSelection ==
|
|
641
|
+
planningPhase \\in {"selecting", "executing"} => rolloutCount > 0
|
|
642
|
+
|
|
643
|
+
\\* Authority hierarchy: VETO overrides everything
|
|
644
|
+
AuthorityHierarchy ==
|
|
645
|
+
\\* If ANY invariant gate vetoes, selectedTool must be "none"
|
|
646
|
+
(\\E i \\in DOMAIN invariantResults : ~invariantResults[i].passed) =>
|
|
647
|
+
\\* At least one valid proposal must exist for execution
|
|
648
|
+
(selectedTool # "none" =>
|
|
649
|
+
\\E j \\in DOMAIN proposals :
|
|
650
|
+
proposals[j] = selectedTool /\\ invariantResults[j].passed)
|
|
651
|
+
|
|
652
|
+
\\* Termination: execution count is bounded per session
|
|
653
|
+
\\* MaxProposals * MaxRollouts gives a generous upper bound
|
|
654
|
+
ExecutionBounded ==
|
|
655
|
+
executionCount <= MaxRollouts * MaxProposals + 1
|
|
656
|
+
|
|
657
|
+
\\* Phase ordering is always respected
|
|
658
|
+
PhaseOrdering ==
|
|
659
|
+
/\\ (planningPhase = "simulating" => rolloutCount > 0)
|
|
660
|
+
/\\ (planningPhase = "checking" => rolloutCount > 0)
|
|
661
|
+
/\\ (planningPhase = "selecting" => allProposalsChecked)
|
|
662
|
+
/\\ (planningPhase = "executing" => selectedTool # "none")
|
|
663
|
+
|
|
664
|
+
Spec == Init /\\ [][Next]_vars
|
|
665
|
+
|
|
666
|
+
====
|
|
667
|
+
`;
|
|
668
|
+
return spec;
|
|
669
|
+
}
|
|
670
|
+
/**
|
|
671
|
+
* Generate TLC config for PlanningLoop spec
|
|
672
|
+
*/
|
|
673
|
+
export function generatePlanningLoopConfig(model) {
|
|
674
|
+
if (!model.planningLoop)
|
|
675
|
+
return "";
|
|
676
|
+
const tools = model.planningLoop.tools.slice(0, 4); // Limit for tractable model checking
|
|
677
|
+
const toolsStr = tools.map(t => `"${t}"`).join(", ");
|
|
678
|
+
return `\\* PlanningLoop TLC Configuration
|
|
679
|
+
\\* Auto-generated by jfl setup — ${new Date().toISOString()}
|
|
680
|
+
|
|
681
|
+
SPECIFICATION Spec
|
|
682
|
+
|
|
683
|
+
CONSTANTS
|
|
684
|
+
Tools = {${toolsStr}}
|
|
685
|
+
MaxProposals = 3
|
|
686
|
+
MaxRollouts = 3
|
|
687
|
+
ThermalLimit = 85
|
|
688
|
+
MemoryLimit = 80
|
|
689
|
+
|
|
690
|
+
CONSTRAINT
|
|
691
|
+
StateConstraint
|
|
692
|
+
|
|
693
|
+
INVARIANTS
|
|
694
|
+
NoExecutionWithoutIMApproval
|
|
695
|
+
VetoedNeverExecuted
|
|
696
|
+
HubSafeExecution
|
|
697
|
+
SimulationBeforeSelection
|
|
698
|
+
AuthorityHierarchy
|
|
699
|
+
PhaseOrdering
|
|
700
|
+
`;
|
|
701
|
+
}
|
|
702
|
+
// ============================================================================
|
|
703
|
+
// Config Generation (v1 orchestration)
|
|
704
|
+
// ============================================================================
|
|
705
|
+
/**
|
|
706
|
+
* Generate TLC config file for the orchestration spec
|
|
262
707
|
*/
|
|
263
708
|
export function generateTLCConfig(model, specName) {
|
|
264
|
-
const
|
|
709
|
+
const allAgents = model.stateVariables
|
|
265
710
|
.filter(v => v.name.startsWith("agent_") && v.type === "status")
|
|
266
711
|
.map(v => `"${v.name.replace("agent_", "").replace("_status", "")}"`);
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
const
|
|
712
|
+
// Cap agents for tractable model checking — TLC state space explodes with >4 agents
|
|
713
|
+
const agents = allAgents.slice(0, 4);
|
|
714
|
+
const allServices = model.services.map(s => `"${s.name}"`);
|
|
715
|
+
const services = allServices.slice(0, 2); // Cap services too
|
|
716
|
+
const repos = [`"default"`];
|
|
270
717
|
let cfg = `SPECIFICATION Spec\n\n`;
|
|
718
|
+
if (allAgents.length > agents.length) {
|
|
719
|
+
cfg += `\\* Note: Checking ${agents.length} of ${allAgents.length} agents for tractable verification\n`;
|
|
720
|
+
}
|
|
271
721
|
cfg += `CONSTANTS\n`;
|
|
272
722
|
cfg += ` Agents = {${agents.join(", ")}}\n`;
|
|
273
723
|
cfg += ` Services = {${services.join(", ")}, "hub"}\n`;
|
|
@@ -283,21 +733,45 @@ export function generateTLCConfig(model, specName) {
|
|
|
283
733
|
cfg += ` BufferBounded\n`;
|
|
284
734
|
return cfg;
|
|
285
735
|
}
|
|
736
|
+
// ============================================================================
|
|
737
|
+
// Write to Disk
|
|
738
|
+
// ============================================================================
|
|
286
739
|
/**
|
|
287
|
-
* Write spec and config to disk, return paths
|
|
740
|
+
* Write spec and config to disk, return paths.
|
|
741
|
+
* Now writes BOTH v1 (orchestration) and v2 (planning loop) specs.
|
|
288
742
|
*/
|
|
289
743
|
export function writeSpec(projectRoot, model, specName = "SystemSpec") {
|
|
290
744
|
const specDir = join(projectRoot, ".jfl", "specs");
|
|
291
745
|
mkdirSync(specDir, { recursive: true });
|
|
746
|
+
// v1: Orchestration spec
|
|
292
747
|
const spec = generateTLASpec(model, specName);
|
|
293
748
|
const cfg = generateTLCConfig(model, specName);
|
|
294
749
|
const specPath = join(specDir, `${specName}.tla`);
|
|
295
750
|
const cfgPath = join(specDir, `${specName}.cfg`);
|
|
296
751
|
writeFileSync(specPath, spec);
|
|
297
752
|
writeFileSync(cfgPath, cfg);
|
|
298
|
-
|
|
753
|
+
const result = {
|
|
754
|
+
specPath,
|
|
755
|
+
cfgPath,
|
|
756
|
+
};
|
|
757
|
+
// v2: Planning loop spec
|
|
758
|
+
if (model.planningLoop) {
|
|
759
|
+
const plSpec = generatePlanningLoopSpec(model);
|
|
760
|
+
const plCfg = generatePlanningLoopConfig(model);
|
|
761
|
+
if (plSpec && plCfg) {
|
|
762
|
+
const plSpecPath = join(specDir, "PlanningLoop.tla");
|
|
763
|
+
const plCfgPath = join(specDir, "PlanningLoop.cfg");
|
|
764
|
+
writeFileSync(plSpecPath, plSpec);
|
|
765
|
+
writeFileSync(plCfgPath, plCfg);
|
|
766
|
+
result.planningSpecPath = plSpecPath;
|
|
767
|
+
result.planningCfgPath = plCfgPath;
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
return result;
|
|
299
771
|
}
|
|
300
|
-
//
|
|
772
|
+
// ============================================================================
|
|
773
|
+
// Helpers
|
|
774
|
+
// ============================================================================
|
|
301
775
|
function extractTomlValue(content, key) {
|
|
302
776
|
const match = content.match(new RegExp(`${key}\\s*=\\s*"([^"]+)"`));
|
|
303
777
|
return match ? match[1] : null;
|