@cassiomc1/forgeloop 1.13.0 → 1.14.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/AGENT_COMPATIBILITY.md +8 -0
- package/DOCS_INDEX.md +35 -3
- package/ENG/nodejs-backend-development-eng.md +2 -2
- package/ENG/sec-code-eng.md +7 -7
- package/EXECUTION_STATE.md +12 -0
- package/LOOP_ENGINEERING.md +28 -2
- package/ORCHESTRATOR_INTEGRATION.md +9 -5
- package/PROTOCOL_INTEGRATION.md +55 -2
- package/README.md +40 -25
- package/TERMINOLOGY.md +2 -0
- package/THREAT_MODEL.md +140 -1
- package/completions/_forgeloop +19 -1
- package/completions/forgeloop.bash +37 -1
- package/completions/forgeloop.fish +123 -1
- package/docs/ADVISORY_CONTEXT.md +25 -0
- package/docs/AGENT_BROWSER_ADAPTER.md +81 -0
- package/docs/AGENT_BROWSER_VERIFICATION.md +6 -0
- package/docs/AGENT_PROTOCOL_SUMMARY.md +27 -2
- package/docs/AGENT_SKILL.md +66 -0
- package/docs/ARTIFACT_REFERENCE.md +123 -0
- package/docs/AUDIT_UX.md +46 -0
- package/docs/BROWSER_VERIFICATION.md +136 -0
- package/docs/CLI_REFERENCE.md +366 -6
- package/docs/CODE_ATTESTATION.md +2 -2
- package/docs/DOCUMENTATION_GUIDE.md +32 -11
- package/docs/JEV_BENCHMARKS.md +31 -0
- package/docs/MODEL_ROUTING.md +37 -0
- package/docs/OPENSRC_ADAPTER.md +241 -0
- package/docs/PACKAGE_CONTENTS.md +35 -8
- package/docs/PROVIDERS.md +126 -0
- package/docs/PROVIDER_ARCHITECTURE.md +199 -0
- package/docs/RECIPES.md +9 -0
- package/docs/RELEASE_CHECKLIST.md +38 -5
- package/docs/SECURITY_REVIEW.md +71 -0
- package/docs/SEMANTIC_DECISION_PLANE.md +71 -0
- package/docs/TEST_INTELLIGENCE.md +29 -0
- package/docs/TEST_PRUNING.md +14 -0
- package/docs/TROUBLESHOOTING.md +198 -1
- package/docs/UNIVERSAL_INTEGRATION.md +31 -0
- package/docs/assets/diagrams/forgeloop-code-attestation-flow.html +2 -2
- package/docs/assets/diagrams/forgeloop-code-attestation-flow.receipt.json +5 -5
- package/docs/assets/diagrams/forgeloop-code-attestation-flow.svg +1 -1
- package/docs/assets/diagrams/forgeloop-engineering-flow.html +39 -26
- package/docs/assets/diagrams/forgeloop-engineering-flow.receipt.json +6 -6
- package/docs/assets/diagrams/forgeloop-engineering-flow.svg +26 -26
- package/docs/assets/diagrams/forgeloop-verification-trust-flow.html +2 -1
- package/docs/assets/diagrams/forgeloop-verification-trust-flow.receipt.json +5 -5
- package/docs/assets/diagrams/forgeloop-verification-trust-flow.svg +1 -1
- package/docs/diagrams/README.md +13 -9
- package/docs/diagrams/forgeloop-code-attestation-flow.workflow.json +1 -1
- package/docs/diagrams/forgeloop-engineering-flow.workflow.json +24 -19
- package/docs/diagrams/forgeloop-verification-trust-flow.workflow.json +1 -0
- package/docs/diagrams/reviews/forgeloop-code-attestation-flow.review.json +4 -4
- package/docs/diagrams/reviews/forgeloop-engineering-flow.review.json +4 -4
- package/docs/diagrams/reviews/forgeloop-verification-trust-flow.review.json +4 -4
- package/docs/documentation-manifest.json +750 -5
- package/docs/protocol-requirements.json +24 -0
- package/package.json +30 -3
- package/schemas/config.schema.json +14 -0
- package/schemas/context-plan.schema.json +18 -0
- package/schemas/semantic-decision.schema.json +46 -0
- package/schemas/test-utility.schema.json +44 -0
- package/scripts/CI_VALIDATORS.md +6 -6
- package/scripts/benchmark-jev.mjs +5 -0
- package/scripts/benchmark-test-intelligence.mjs +4 -0
- package/scripts/generate-agent-protocol-summary.mjs +4 -1
- package/scripts/generate-forgeloop-skill.mjs +133 -0
- package/scripts/jev-smoke.mjs +19 -0
- package/skills/forgeloop/README.md +9 -0
- package/skills/forgeloop/SKILL.md +77 -0
- package/skills/forgeloop/references/lifecycle.md +9 -0
- package/skills/forgeloop/references/recovery.md +7 -0
- package/skills/forgeloop/references/verification.md +7 -0
- package/src/adapters/agent-browser/assertions.js +47 -0
- package/src/adapters/agent-browser/commands.js +54 -0
- package/src/adapters/agent-browser/index.js +3 -0
- package/src/adapters/agent-browser/locator.js +40 -0
- package/src/adapters/agent-browser/process.js +215 -0
- package/src/adapters/agent-browser/provider.js +313 -0
- package/src/adapters/emulated-services/constants.js +24 -0
- package/src/adapters/emulated-services/index.js +7 -0
- package/src/adapters/emulated-services/process.js +162 -0
- package/src/adapters/emulated-services/provider.js +282 -0
- package/src/adapters/opensrc/normalize.js +90 -0
- package/src/adapters/opensrc/process.js +248 -0
- package/src/adapters/opensrc/provider.js +338 -0
- package/src/adapters/opensrc/search.js +264 -0
- package/src/adapters/typesafe/client.js +28 -0
- package/src/adapters/typesafe/engine.js +63 -0
- package/src/adapters/typesafe/normalize.js +41 -0
- package/src/cli.js +108 -0
- package/src/commands/checkpoint-revalidate.js +176 -0
- package/src/commands/context-plan.js +38 -0
- package/src/commands/contract-create.js +264 -0
- package/src/commands/contract-revise.js +236 -0
- package/src/commands/decision-show.js +14 -0
- package/src/commands/decision-status.js +22 -0
- package/src/commands/discover.js +41 -0
- package/src/commands/doctor.js +15 -0
- package/src/commands/gate-record.js +205 -0
- package/src/commands/gate-revalidate.js +137 -0
- package/src/commands/model-route.js +32 -0
- package/src/commands/route.js +146 -18
- package/src/commands/semantic-plan.js +17 -0
- package/src/commands/task-abandon.js +224 -0
- package/src/commands/task-migrate-contract-bootstrap-repair.js +288 -0
- package/src/commands/task-repair-contract-bootstrap.js +263 -0
- package/src/commands/test-inventory.js +5 -0
- package/src/commands/test-prune-plan.js +5 -0
- package/src/commands/test-prune-probe.js +5 -0
- package/src/commands/test-utility.js +5 -0
- package/src/commands/validate-protocol.js +10 -1
- package/src/core/artifact-registry.js +24 -0
- package/src/core/audit-ux.js +514 -0
- package/src/core/browser-verification/constants.js +149 -0
- package/src/core/browser-verification/normalize.js +254 -0
- package/src/core/browser-verification/provider.js +519 -0
- package/src/core/browser-verification/service.js +115 -0
- package/src/core/checkpoint-revalidation.js +319 -0
- package/src/core/cli-command-definitions.js +241 -0
- package/src/core/command-executors.js +110 -0
- package/src/core/command-input.js +115 -43
- package/src/core/completion-artifacts.js +14 -5
- package/src/core/completion.js +4 -6
- package/src/core/config.js +3 -0
- package/src/core/context-compiler/budget.js +9 -0
- package/src/core/context-compiler/candidates.js +39 -0
- package/src/core/context-compiler/compiler.js +63 -0
- package/src/core/context-compiler/fingerprint.js +11 -0
- package/src/core/context-compiler/policy.js +13 -0
- package/src/core/context-compiler/result.js +23 -0
- package/src/core/contract-bootstrap-recovery.js +655 -0
- package/src/core/contract-revision.js +210 -0
- package/src/core/decision/artifact.js +69 -0
- package/src/core/decision/benchmarks.js +103 -0
- package/src/core/decision/cache.js +27 -0
- package/src/core/decision/constants.js +58 -0
- package/src/core/decision/cutover.js +34 -0
- package/src/core/decision/engine.js +22 -0
- package/src/core/decision/errors.js +68 -0
- package/src/core/decision/events.js +101 -0
- package/src/core/decision/freshness.js +19 -0
- package/src/core/decision/normalizers/index.js +115 -0
- package/src/core/decision/policy.js +18 -0
- package/src/core/decision/projection.js +16 -0
- package/src/core/decision/question-registry.js +201 -0
- package/src/core/decision/request.js +26 -0
- package/src/core/decision/resolver.js +130 -0
- package/src/core/decision/result.js +58 -0
- package/src/core/decision/service.js +156 -0
- package/src/core/decision/state-builder.js +65 -0
- package/src/core/decision/task-bindings.js +30 -0
- package/src/core/decision/test-provider.js +32 -0
- package/src/core/decision/thresholds.js +15 -0
- package/src/core/error-codes.js +278 -0
- package/src/core/events.js +226 -57
- package/src/core/evidence-readiness.js +9 -0
- package/src/core/execution-prerequisites.js +14 -0
- package/src/core/execution-profile.js +63 -38
- package/src/core/gate-provenance.js +124 -0
- package/src/core/integration-invocation-policy.js +27 -4
- package/src/core/integration-resources.js +86 -61
- package/src/core/model-router/constants.js +10 -0
- package/src/core/model-router/policy.js +103 -0
- package/src/core/model-router/router.js +37 -0
- package/src/core/next-action-model.js +58 -0
- package/src/core/next-action-phases.js +130 -42
- package/src/core/next-action-refresh.js +43 -9
- package/src/core/next-action-review-phase.js +7 -2
- package/src/core/next-action.js +35 -7
- package/src/core/phase.js +128 -10
- package/src/core/preflight-consistency.js +23 -9
- package/src/core/preflight-loaders.js +37 -5
- package/src/core/protocol-info.js +65 -0
- package/src/core/protocol.js +20 -0
- package/src/core/reconcile-closure.js +128 -52
- package/src/core/recovery-history.js +1 -0
- package/src/core/resumability.js +154 -44
- package/src/core/route-artifact.js +15 -1
- package/src/core/router.js +67 -1
- package/src/core/runtime-context.js +118 -61
- package/src/core/schema-validation.js +3 -0
- package/src/core/security-review/constants.js +64 -0
- package/src/core/security-review/normalize.js +245 -0
- package/src/core/security-review/provider.js +204 -0
- package/src/core/security-review/service.js +134 -0
- package/src/core/semantic-planning/constants.js +19 -0
- package/src/core/semantic-planning/projection.js +94 -0
- package/src/core/semantic-planning/service.js +15 -0
- package/src/core/sources.js +37 -0
- package/src/core/task-claim-state.js +201 -1
- package/src/core/task-conflict-inspection.js +31 -5
- package/src/core/task-paths.js +13 -0
- package/src/core/task-recovery.js +1 -0
- package/src/core/templates.js +3 -0
- package/src/core/test-intelligence/benchmarks.js +68 -0
- package/src/core/test-intelligence/inventory.js +73 -0
- package/src/core/test-intelligence/prune.js +90 -0
- package/src/core/test-intelligence/semantic-state.js +15 -0
- package/src/core/test-intelligence/service.js +40 -0
- package/src/core/test-intelligence/utility.js +50 -0
- package/src/core/trace.js +11 -7
- package/src/core/transaction.js +1 -0
- package/src/integration.d.ts +492 -0
- package/src/integration.js +54 -0
- package/src/providers/README.md +47 -0
- package/src/providers/capabilities.js +46 -0
- package/src/providers/errors.js +15 -0
- package/src/providers/index.js +29 -0
- package/src/providers/json-snapshot.js +105 -0
- package/src/providers/registry.js +152 -0
|
@@ -24,7 +24,7 @@ export const INTEGRATION_RISK_CLASSES = Object.freeze({
|
|
|
24
24
|
});
|
|
25
25
|
|
|
26
26
|
const READ_ONLY_COMMANDS = Object.freeze(new Set([
|
|
27
|
-
"protocol-info", "status", "next", "continuity", "reconcile-continuity",
|
|
27
|
+
"protocol-info", "decision-status", "decision-show", "context-plan", "model-route", "semantic-plan", "test-inventory", "status", "next", "continuity", "reconcile-continuity",
|
|
28
28
|
"task-list", "task-show", "task-lock-status", "progress", "audit", "report",
|
|
29
29
|
"inspect", "validate-state", "validate-protocol", "validate-receipt",
|
|
30
30
|
"policy-status", "policy-diff", "rule-verify", "policy",
|
|
@@ -37,7 +37,7 @@ const READ_ONLY_COMMANDS = Object.freeze(new Set([
|
|
|
37
37
|
]));
|
|
38
38
|
|
|
39
39
|
const LOOP_MUTATION_COMMANDS = Object.freeze(new Set([
|
|
40
|
-
"route", "preflight", "advance", "task-create", "task-scope",
|
|
40
|
+
"discover", "contract-create", "route", "preflight", "advance", "task-create", "task-scope",
|
|
41
41
|
"record-continuity", "clear-continuity", "prepare-completion",
|
|
42
42
|
"record-check", "record-diagnosis", "record-decision-criterion",
|
|
43
43
|
"record-terminal-result", "complete",
|
|
@@ -86,7 +86,17 @@ const STATIC_RISK_CLASSES = Object.freeze({
|
|
|
86
86
|
bundle: INTEGRATION_RISK_CLASSES.MAINTENANCE,
|
|
87
87
|
"profile-interview": INTEGRATION_RISK_CLASSES.MAINTENANCE,
|
|
88
88
|
"task-recover": INTEGRATION_RISK_CLASSES.CLAIM_RELEASE_RECOVERY,
|
|
89
|
+
"task-abandon": INTEGRATION_RISK_CLASSES.CLAIM_RELEASE_RECOVERY,
|
|
89
90
|
"task-repair-legacy-recovery": INTEGRATION_RISK_CLASSES.LEGACY_MIGRATION,
|
|
91
|
+
"gate-record": INTEGRATION_RISK_CLASSES.MAINTENANCE,
|
|
92
|
+
"gate-revalidate": INTEGRATION_RISK_CLASSES.MAINTENANCE,
|
|
93
|
+
"test-utility": INTEGRATION_RISK_CLASSES.MAINTENANCE,
|
|
94
|
+
"test-prune-plan": INTEGRATION_RISK_CLASSES.READ_ONLY,
|
|
95
|
+
"test-prune-probe": INTEGRATION_RISK_CLASSES.EXTERNAL_EXECUTION,
|
|
96
|
+
"task-repair-contract-bootstrap": INTEGRATION_RISK_CLASSES.LEGACY_MIGRATION,
|
|
97
|
+
"task-migrate-contract-bootstrap-repair": INTEGRATION_RISK_CLASSES.LEGACY_MIGRATION,
|
|
98
|
+
"checkpoint-revalidate": INTEGRATION_RISK_CLASSES.MAINTENANCE,
|
|
99
|
+
"contract-revise": INTEGRATION_RISK_CLASSES.MAINTENANCE,
|
|
90
100
|
});
|
|
91
101
|
|
|
92
102
|
// Sparse input-dependent refinements over the static table.
|
|
@@ -169,6 +179,18 @@ export function getForgeLoopCapabilities({ packageVersion = null } = {}) {
|
|
|
169
179
|
commands: ["next", "task-show"],
|
|
170
180
|
preservesDefaultOutput: true,
|
|
171
181
|
},
|
|
182
|
+
auditUx: {
|
|
183
|
+
version: 1,
|
|
184
|
+
supported: true,
|
|
185
|
+
readOnly: true,
|
|
186
|
+
resource: "task/audit-view",
|
|
187
|
+
timeline: true,
|
|
188
|
+
lifecycleAuthority: false,
|
|
189
|
+
evidenceAuthority: false,
|
|
190
|
+
completionAuthority: false,
|
|
191
|
+
mutationAuthority: false,
|
|
192
|
+
externalExecution: false,
|
|
193
|
+
},
|
|
172
194
|
usageTelemetry: {
|
|
173
195
|
version: 1,
|
|
174
196
|
supported: true,
|
|
@@ -295,6 +317,7 @@ export function getForgeLoopCapabilities({ packageVersion = null } = {}) {
|
|
|
295
317
|
{ name: "task/approvals", scope: "TASK" },
|
|
296
318
|
{ name: "task/metrics", scope: "TASK" },
|
|
297
319
|
{ name: "task/context", scope: "TASK" },
|
|
320
|
+
{ name: "task/audit-view", scope: "TASK" },
|
|
298
321
|
{ name: "task/evaluations", scope: "TASK" },
|
|
299
322
|
{ name: "project/capability-policy", scope: "PROJECT" },
|
|
300
323
|
{ name: "repository/index-status", scope: "PROJECT" },
|
|
@@ -352,8 +375,8 @@ export function classifyForgeLoopInvocation(command, input = {}) {
|
|
|
352
375
|
removesArtifacts: definition.removes.length > 0,
|
|
353
376
|
executesExternalProcess: definition.mayExecuteExternalProcess === true,
|
|
354
377
|
affectsClaimAuthority: [
|
|
355
|
-
"task-resume", "task-recover", "task-repair-legacy-recovery",
|
|
356
|
-
"task-create", "task-scope", "complete",
|
|
378
|
+
"task-resume", "task-recover", "task-repair-legacy-recovery", "task-repair-contract-bootstrap",
|
|
379
|
+
"task-create", "task-scope", "complete", "task-migrate-contract-bootstrap-repair",
|
|
357
380
|
].includes(command),
|
|
358
381
|
destructive: [
|
|
359
382
|
INTEGRATION_RISK_CLASSES.AUTHORITY_MUTATION,
|
|
@@ -21,6 +21,12 @@ import { resolveAttestationStatus } from "./attestation.js";
|
|
|
21
21
|
import { buildExecutionProfileContext } from "./execution-profile-context.js";
|
|
22
22
|
import { projectStructuralQualityStatus } from "./structural-quality/service.js";
|
|
23
23
|
import { getRepositoryIndexStatus } from "../repository-index/status.js";
|
|
24
|
+
import { buildAuditUxView } from "./audit-ux.js";
|
|
25
|
+
import { runContextPlan } from "../commands/context-plan.js";
|
|
26
|
+
import { runModelRoute } from "../commands/model-route.js";
|
|
27
|
+
import { readPersistedRoute } from "./route-artifact.js";
|
|
28
|
+
import { readWorkState } from "./work-state.js";
|
|
29
|
+
import { readTestUtility } from "./test-intelligence/service.js";
|
|
24
30
|
|
|
25
31
|
/**
|
|
26
32
|
* Canonical integration resource allowlist.
|
|
@@ -86,11 +92,18 @@ export const INTEGRATION_RESOURCE_DEFINITIONS = Object.freeze({
|
|
|
86
92
|
"task/approvals": Object.freeze({ scope: "TASK", description: "Durable approval artifacts for one task." }),
|
|
87
93
|
"task/metrics": Object.freeze({ scope: "TASK", description: "Read-only trajectory metrics for one task." }),
|
|
88
94
|
"task/context": Object.freeze({ scope: "TASK", description: "Read-only profile-aware task context with bounded presentation policy." }),
|
|
95
|
+
"task/audit-view": Object.freeze({ scope: "TASK", description: "Bounded read-only Audit UX projection composed from canonical task resolvers." }),
|
|
89
96
|
"task/evaluations": Object.freeze({ scope: "TASK", description: "Persisted trajectory evaluations for one task." }),
|
|
97
|
+
"task/decisions": Object.freeze({ scope: "TASK", description: "Persisted, fingerprint-bound semantic decisions without lifecycle or evidence authority." }),
|
|
98
|
+
"task/context-plan": Object.freeze({ scope: "TASK", description: "Bounded non-authoritative semantic context-plan projection." }),
|
|
99
|
+
"task/model-route": Object.freeze({ scope: "TASK", description: "Deterministic model-routing floor with advisory semantic escalation." }),
|
|
100
|
+
"task/test-utility": Object.freeze({ scope: "TASK", description: "Non-evidence test inventory and utility analysis; never deletion authority." }),
|
|
90
101
|
"project/capability-policy": Object.freeze({ scope: "PROJECT", description: "Project capability policy, never host authority." }),
|
|
91
102
|
"repository/index-status": Object.freeze({ scope: "PROJECT", description: "Provider-neutral repository-index health and owned-server status." }),
|
|
92
103
|
});
|
|
93
104
|
|
|
105
|
+
const TASK_RESOURCE_URIS = new Set(Object.keys(INTEGRATION_RESOURCE_DEFINITIONS).filter((uri) => uri.startsWith("task/")));
|
|
106
|
+
|
|
94
107
|
function ownershipProjection(projection) {
|
|
95
108
|
return {
|
|
96
109
|
taskId: projection.taskId,
|
|
@@ -120,67 +133,20 @@ async function readForgeLoopIntegrationResourceCore(uri, {
|
|
|
120
133
|
throw error;
|
|
121
134
|
}
|
|
122
135
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
mutationAllowed: task.mutationAllowed !== false,
|
|
138
|
-
})),
|
|
139
|
-
},
|
|
140
|
-
};
|
|
141
|
-
}
|
|
142
|
-
case "task/status":
|
|
143
|
-
case "task/ownership":
|
|
144
|
-
case "task/contract":
|
|
145
|
-
case "task/continuity": {
|
|
146
|
-
if (typeof taskId !== "string" || !taskId) {
|
|
147
|
-
const error = new Error(`Resource ${uri} requires a taskId`);
|
|
148
|
-
error.code = "E_TASK_REQUIRED";
|
|
149
|
-
throw error;
|
|
150
|
-
}
|
|
151
|
-
break;
|
|
152
|
-
}
|
|
153
|
-
case "task/workspace-binding":
|
|
154
|
-
case "task/handoffs":
|
|
155
|
-
case "task/responsibility":
|
|
156
|
-
case "task/verification-scope":
|
|
157
|
-
case "task/attestation": {
|
|
158
|
-
if (typeof taskId !== "string" || !taskId) {
|
|
159
|
-
const error = new Error(`Resource ${uri} requires a taskId`);
|
|
160
|
-
error.code = "E_TASK_REQUIRED";
|
|
161
|
-
throw error;
|
|
162
|
-
}
|
|
163
|
-
break;
|
|
164
|
-
}
|
|
165
|
-
case "task/structural-quality": {
|
|
166
|
-
if (typeof taskId !== "string" || !taskId) {
|
|
167
|
-
const error = new Error(`Resource ${uri} requires a taskId`);
|
|
168
|
-
error.code = "E_TASK_REQUIRED";
|
|
169
|
-
throw error;
|
|
170
|
-
}
|
|
171
|
-
break;
|
|
172
|
-
}
|
|
173
|
-
case "task/actions":
|
|
174
|
-
case "task/action":
|
|
175
|
-
case "task/approvals":
|
|
176
|
-
case "task/metrics":
|
|
177
|
-
case "task/context":
|
|
178
|
-
case "task/evaluations": {
|
|
179
|
-
if (typeof taskId !== "string" || !taskId) {
|
|
180
|
-
const error = new Error(`Resource ${uri} requires a taskId`); error.code = "E_TASK_REQUIRED"; throw error;
|
|
181
|
-
}
|
|
182
|
-
break;
|
|
183
|
-
}
|
|
136
|
+
if (uri === "protocol/info") return { uri, data: await runProtocolInfo({ packageVersion }) };
|
|
137
|
+
if (uri === "project/tasks") {
|
|
138
|
+
const tasks = await discoverTasks(projectPath, packageRoot);
|
|
139
|
+
return { uri, data: { count: tasks.length, tasks: tasks.map((task) => ({
|
|
140
|
+
taskId: task.taskId,
|
|
141
|
+
healthy: task.healthy !== false,
|
|
142
|
+
phase: task.phase ?? null,
|
|
143
|
+
mutationAllowed: task.mutationAllowed !== false,
|
|
144
|
+
})) } };
|
|
145
|
+
}
|
|
146
|
+
if (TASK_RESOURCE_URIS.has(uri) && (typeof taskId !== "string" || !taskId)) {
|
|
147
|
+
const error = new Error(`Resource ${uri} requires a taskId`);
|
|
148
|
+
error.code = "E_TASK_REQUIRED";
|
|
149
|
+
throw error;
|
|
184
150
|
}
|
|
185
151
|
|
|
186
152
|
if (uri === "task/ownership") {
|
|
@@ -270,6 +236,45 @@ async function readForgeLoopIntegrationResourceCore(uri, {
|
|
|
270
236
|
}
|
|
271
237
|
return { uri, taskId, data: { evaluations } };
|
|
272
238
|
}
|
|
239
|
+
if (uri === "task/decisions") {
|
|
240
|
+
const dir = path.join(projectPath, taskDirectory(taskId), "decisions");
|
|
241
|
+
let names = []; try { names = await readdir(dir); } catch { /* absent is an empty projection */ }
|
|
242
|
+
const decisions = [];
|
|
243
|
+
for (const name of names.filter((entry) => /^[A-Za-z0-9][A-Za-z0-9_-]{0,127}\.json$/.test(entry)).sort()) {
|
|
244
|
+
decisions.push((await readJsonArtifact(projectPath, `${taskDirectory(taskId)}/decisions/${name}`, "semantic-decision", packageRoot)).value);
|
|
245
|
+
}
|
|
246
|
+
return { uri, taskId, data: { taskId, decisions } };
|
|
247
|
+
}
|
|
248
|
+
if (uri === "task/context-plan") {
|
|
249
|
+
return { uri, taskId, data: await runContextPlan({ target: projectPath, packageRoot, taskId, profile: runtimeContext?.contextProfile ?? "balanced", readOnly: true }) };
|
|
250
|
+
}
|
|
251
|
+
if (uri === "task/model-route") {
|
|
252
|
+
const state = await readWorkState(projectPath, { packageRoot, taskId });
|
|
253
|
+
const route = await readPersistedRoute(projectPath, packageRoot, { taskId });
|
|
254
|
+
return {
|
|
255
|
+
uri,
|
|
256
|
+
taskId,
|
|
257
|
+
data: await runModelRoute({
|
|
258
|
+
target: projectPath,
|
|
259
|
+
packageRoot,
|
|
260
|
+
taskId,
|
|
261
|
+
workType: route.value.input?.workType,
|
|
262
|
+
surfaces: route.value.input?.surfaces,
|
|
263
|
+
risks: route.value.input?.risks,
|
|
264
|
+
platforms: route.value.input?.platforms,
|
|
265
|
+
behaviorChange: route.value.input?.behaviorChange,
|
|
266
|
+
executableChange: route.value.input?.executableChange,
|
|
267
|
+
generationRequired: route.value.input?.generationRequired,
|
|
268
|
+
architectureChange: route.value.input?.architectureChange,
|
|
269
|
+
ambiguity: route.value.input?.ambiguity,
|
|
270
|
+
contract: { phase: state.phase },
|
|
271
|
+
}),
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
if (uri === "task/test-utility") {
|
|
275
|
+
const artifact = await readTestUtility({ target: projectPath, packageRoot, taskId });
|
|
276
|
+
return { uri, taskId, data: artifact.value };
|
|
277
|
+
}
|
|
273
278
|
if (uri === "project/capability-policy") {
|
|
274
279
|
return { uri, data: await loadCapabilityPolicy(projectPath, packageRoot) };
|
|
275
280
|
}
|
|
@@ -292,6 +297,26 @@ async function readForgeLoopIntegrationResourceCore(uri, {
|
|
|
292
297
|
}
|
|
293
298
|
|
|
294
299
|
export async function readForgeLoopIntegrationResource(uri, options = {}) {
|
|
300
|
+
if (uri === "task/audit-view") {
|
|
301
|
+
if (typeof options.taskId !== "string" || !options.taskId) {
|
|
302
|
+
const error = new Error("Resource task/audit-view requires a taskId"); error.code = "E_TASK_REQUIRED"; throw error;
|
|
303
|
+
}
|
|
304
|
+
return {
|
|
305
|
+
uri,
|
|
306
|
+
taskId: options.taskId,
|
|
307
|
+
data: await buildAuditUxView({
|
|
308
|
+
target: options.projectPath ?? ".",
|
|
309
|
+
packageRoot: options.packageRoot,
|
|
310
|
+
taskId: options.taskId,
|
|
311
|
+
limit: options.limit,
|
|
312
|
+
beforeSequence: options.beforeSequence,
|
|
313
|
+
afterSequence: options.afterSequence,
|
|
314
|
+
categories: options.categories,
|
|
315
|
+
authorityContext: options.runtimeContext?.authorityContext,
|
|
316
|
+
runtimeContext: options.runtimeContext,
|
|
317
|
+
}),
|
|
318
|
+
};
|
|
319
|
+
}
|
|
295
320
|
if (uri === "repository/index-status") {
|
|
296
321
|
return {
|
|
297
322
|
uri,
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { DECISION_AUTHORITY, DECISION_EVIDENCE_AUTHORITY, PINNED_JEV_MODEL } from "../decision/constants.js";
|
|
2
|
+
|
|
3
|
+
export const MODEL_ROUTE_TIERS = Object.freeze(["NONE", "FAST", "STANDARD", "PRIMARY"]);
|
|
4
|
+
export const MODEL_ROUTE_POLICY_VERSION = 1;
|
|
5
|
+
export const MODEL_ROUTE_PROJECTION_AUTHORITY = DECISION_AUTHORITY;
|
|
6
|
+
export const MODEL_ROUTE_PROJECTION_EVIDENCE_AUTHORITY = DECISION_EVIDENCE_AUTHORITY;
|
|
7
|
+
export const MODEL_ROUTE_JEV_MODEL = PINNED_JEV_MODEL;
|
|
8
|
+
|
|
9
|
+
export const MODEL_ROUTE_RANK = Object.freeze({ NONE: 0, FAST: 1, STANDARD: 2, PRIMARY: 3 });
|
|
10
|
+
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { MODEL_ROUTE_RANK, MODEL_ROUTE_TIERS } from "./constants.js";
|
|
2
|
+
|
|
3
|
+
const GENERATION_WORK_TYPES = new Set([
|
|
4
|
+
"code", "bug", "refactor", "backend", "api", "infrastructure", "security-review", "release",
|
|
5
|
+
]);
|
|
6
|
+
const PRIMARY_WORK_TYPES = new Set(["infrastructure", "security-review", "release"]);
|
|
7
|
+
const PRIMARY_RISKS = new Set(["secrets", "personal-data", "publication", "critical-path", "destructive", "irreversible", "migration", "credentials", "payment"]);
|
|
8
|
+
const PRIMARY_SURFACES = new Set(["auth", "critical-path", "database"]);
|
|
9
|
+
|
|
10
|
+
function text(value) {
|
|
11
|
+
return typeof value === "string" ? value.toLowerCase() : "";
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function array(value) {
|
|
15
|
+
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function contractText(contract) {
|
|
19
|
+
if (!contract || typeof contract !== "object" || Array.isArray(contract)) return "";
|
|
20
|
+
const values = [contract.objective, contract.summary, ...(contract.deliverables ?? []), ...(contract.successCriteria ?? []), ...(contract.risks ?? []), ...(contract.constraints ?? [])];
|
|
21
|
+
return values.map((value) => value && typeof value === "object" ? value.text : value).filter((value) => typeof value === "string").join(" ").toLowerCase();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function normalizedInput(input = {}) {
|
|
25
|
+
return {
|
|
26
|
+
workType: text(input.workType),
|
|
27
|
+
surfaces: array(input.surfaces).map(text),
|
|
28
|
+
risks: array(input.risks).map(text),
|
|
29
|
+
platforms: array(input.platforms).map(text),
|
|
30
|
+
behaviorChange: input.behaviorChange === true,
|
|
31
|
+
executableChange: input.executableChange === true,
|
|
32
|
+
generationRequired: input.generationRequired === true,
|
|
33
|
+
architectureChange: input.architectureChange === true,
|
|
34
|
+
ambiguity: input.ambiguity === true,
|
|
35
|
+
contract: input.contract,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function deterministicFloor(input) {
|
|
40
|
+
const normalized = normalizedInput(input);
|
|
41
|
+
const reasons = [];
|
|
42
|
+
const combined = `${contractText(normalized.contract)} ${normalized.workType} ${normalized.surfaces.join(" ")} ${normalized.risks.join(" ")}`;
|
|
43
|
+
const generationRequired = normalized.generationRequired
|
|
44
|
+
|| normalized.behaviorChange
|
|
45
|
+
|| normalized.executableChange
|
|
46
|
+
|| GENERATION_WORK_TYPES.has(normalized.workType)
|
|
47
|
+
|| /\b(implement|fix|refactor|change|build|generate|write|modify|code)\b/.test(combined);
|
|
48
|
+
const primary = normalized.architectureChange
|
|
49
|
+
|| normalized.ambiguity
|
|
50
|
+
|| PRIMARY_WORK_TYPES.has(normalized.workType)
|
|
51
|
+
|| normalized.risks.some((risk) => PRIMARY_RISKS.has(risk))
|
|
52
|
+
|| normalized.surfaces.some((surface) => PRIMARY_SURFACES.has(surface))
|
|
53
|
+
|| /\b(architecture|architectural|security boundary|migration|ambiguous|unknown requirements)\b/.test(combined);
|
|
54
|
+
|
|
55
|
+
if (!generationRequired) {
|
|
56
|
+
reasons.push("NO_GENERATION_REQUIRED");
|
|
57
|
+
return { floor: "NONE", reasons };
|
|
58
|
+
}
|
|
59
|
+
reasons.push("GENERATION_REQUIRED");
|
|
60
|
+
if (primary) {
|
|
61
|
+
reasons.push("PRIMARY_COMPLEXITY_OR_RISK");
|
|
62
|
+
return { floor: "PRIMARY", reasons };
|
|
63
|
+
}
|
|
64
|
+
reasons.push("STANDARD_GENERATION_FLOOR");
|
|
65
|
+
return { floor: "STANDARD", reasons };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function normalizeRecommendation(recommendation) {
|
|
69
|
+
if (recommendation === null || recommendation === undefined) return null;
|
|
70
|
+
if (typeof recommendation === "string") {
|
|
71
|
+
return MODEL_ROUTE_TIERS.includes(recommendation) ? { tier: recommendation, confidence: null, requiresEscalation: false } : null;
|
|
72
|
+
}
|
|
73
|
+
if (!recommendation || typeof recommendation !== "object" || Array.isArray(recommendation)) return null;
|
|
74
|
+
const tier = typeof recommendation.tier === "string" && MODEL_ROUTE_TIERS.includes(recommendation.tier) ? recommendation.tier : null;
|
|
75
|
+
if (!tier) return null;
|
|
76
|
+
return {
|
|
77
|
+
tier,
|
|
78
|
+
confidence: typeof recommendation.confidence === "number" && Number.isFinite(recommendation.confidence) ? recommendation.confidence : null,
|
|
79
|
+
requiresEscalation: recommendation.requiresEscalation === true,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function resolveModelRoute({ input = {}, semanticRecommendation = null } = {}) {
|
|
84
|
+
const deterministic = deterministicFloor(input);
|
|
85
|
+
const recommendation = normalizeRecommendation(semanticRecommendation);
|
|
86
|
+
let resolved = deterministic.floor;
|
|
87
|
+
const reasons = [...deterministic.reasons];
|
|
88
|
+
if (recommendation?.requiresEscalation) {
|
|
89
|
+
if (MODEL_ROUTE_RANK[resolved] < MODEL_ROUTE_RANK.PRIMARY) resolved = "PRIMARY";
|
|
90
|
+
reasons.push("JEV_LOW_CONFIDENCE_ESCALATION");
|
|
91
|
+
} else if (recommendation && MODEL_ROUTE_RANK[recommendation.tier] > MODEL_ROUTE_RANK[resolved]) {
|
|
92
|
+
resolved = recommendation.tier;
|
|
93
|
+
reasons.push("JEV_ESCALATION_ONLY");
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
floor: deterministic.floor,
|
|
97
|
+
resolved,
|
|
98
|
+
reasons: [...new Set(reasons)],
|
|
99
|
+
semanticRecommendation: recommendation,
|
|
100
|
+
generationRequired: deterministic.floor !== "NONE",
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MODEL_ROUTE_JEV_MODEL,
|
|
3
|
+
MODEL_ROUTE_POLICY_VERSION,
|
|
4
|
+
MODEL_ROUTE_PROJECTION_AUTHORITY,
|
|
5
|
+
MODEL_ROUTE_PROJECTION_EVIDENCE_AUTHORITY,
|
|
6
|
+
MODEL_ROUTE_RANK,
|
|
7
|
+
MODEL_ROUTE_TIERS,
|
|
8
|
+
} from "./constants.js";
|
|
9
|
+
import { resolveModelRoute } from "./policy.js";
|
|
10
|
+
|
|
11
|
+
export function projectModelRoute({ input = {}, semanticRecommendation = null } = {}) {
|
|
12
|
+
const route = resolveModelRoute({ input, semanticRecommendation });
|
|
13
|
+
if (!MODEL_ROUTE_TIERS.includes(route.floor) || !MODEL_ROUTE_TIERS.includes(route.resolved)
|
|
14
|
+
|| MODEL_ROUTE_RANK[route.resolved] < MODEL_ROUTE_RANK[route.floor]) {
|
|
15
|
+
const error = new Error("Model route violates its deterministic safety floor.");
|
|
16
|
+
error.code = "E_MODEL_ROUTE_SAFETY_FLOOR_INVALID";
|
|
17
|
+
throw error;
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
schemaVersion: 1,
|
|
21
|
+
policyVersion: MODEL_ROUTE_POLICY_VERSION,
|
|
22
|
+
engine: "typesafe-jev",
|
|
23
|
+
model: MODEL_ROUTE_JEV_MODEL,
|
|
24
|
+
floor: route.floor,
|
|
25
|
+
resolved: route.resolved,
|
|
26
|
+
reasons: route.reasons,
|
|
27
|
+
generationRequired: route.generationRequired,
|
|
28
|
+
semanticRecommendation: route.semanticRecommendation,
|
|
29
|
+
authority: MODEL_ROUTE_PROJECTION_AUTHORITY,
|
|
30
|
+
evidenceAuthority: MODEL_ROUTE_PROJECTION_EVIDENCE_AUTHORITY,
|
|
31
|
+
lifecycleAuthority: false,
|
|
32
|
+
completionAuthority: false,
|
|
33
|
+
ownershipAuthority: false,
|
|
34
|
+
installationAuthority: false,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
@@ -6,6 +6,7 @@ export const NEXT_ACTIONS = Object.freeze({
|
|
|
6
6
|
CREATE_CONTRACT: "CREATE_CONTRACT",
|
|
7
7
|
ROUTE: "ROUTE",
|
|
8
8
|
SATISFY_GATES: "SATISFY_GATES",
|
|
9
|
+
REVALIDATE_GATES: "REVALIDATE_GATES",
|
|
9
10
|
RUN_PREFLIGHT: "RUN_PREFLIGHT",
|
|
10
11
|
PLAN: "PLAN",
|
|
11
12
|
START_EXECUTION: "START_EXECUTION",
|
|
@@ -47,6 +48,9 @@ export const NEXT_ACTIONS = Object.freeze({
|
|
|
47
48
|
RECOVER_TASK: "RECOVER_TASK",
|
|
48
49
|
RESUME_RECOVERED_TASK: "RESUME_RECOVERED_TASK",
|
|
49
50
|
RESOLVE_RECOVERY_INCONSISTENCY: "RESOLVE_RECOVERY_INCONSISTENCY",
|
|
51
|
+
REPAIR_CONTRACT_BOOTSTRAP: "REPAIR_CONTRACT_BOOTSTRAP",
|
|
52
|
+
MIGRATE_CONTRACT_BOOTSTRAP_REPAIR: "MIGRATE_CONTRACT_BOOTSTRAP_REPAIR",
|
|
53
|
+
REVALIDATE_CHECKPOINT: "REVALIDATE_CHECKPOINT",
|
|
50
54
|
NONE: "NONE",
|
|
51
55
|
});
|
|
52
56
|
|
|
@@ -60,6 +64,33 @@ export function directCommandSpec(commandId, taskId, requiredInputs = []) {
|
|
|
60
64
|
};
|
|
61
65
|
}
|
|
62
66
|
|
|
67
|
+
export function routeCommandSpec(taskId) {
|
|
68
|
+
return {
|
|
69
|
+
commandId: "route",
|
|
70
|
+
executable: "forgeloop",
|
|
71
|
+
subcommand: "route",
|
|
72
|
+
argv: ["route", `--task=${taskId}`, "--json"],
|
|
73
|
+
requiredInputs: [
|
|
74
|
+
{ name: "workType", option: "--work=<type>" },
|
|
75
|
+
{ name: "surface", option: "--surface=<value>", repeatable: true, optional: true },
|
|
76
|
+
{ name: "risk", option: "--risk=<value>", repeatable: true, optional: true },
|
|
77
|
+
{ name: "platform", option: "--platform=<value>", repeatable: true, optional: true },
|
|
78
|
+
],
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function gateRevalidationCommandSpec(taskId, gate) {
|
|
83
|
+
return {
|
|
84
|
+
commandId: "gate-revalidate",
|
|
85
|
+
executable: "forgeloop",
|
|
86
|
+
subcommand: "gate-revalidate",
|
|
87
|
+
argv: ["gate-revalidate", `--task=${taskId}`, `--gate=${gate}`, "--acknowledge-stale", "--json"],
|
|
88
|
+
requiredInputs: [
|
|
89
|
+
{ name: "acknowledgeStale", option: "--acknowledge-stale" },
|
|
90
|
+
],
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
63
94
|
export function recoveryGuidanceForClassification(classification, taskId) {
|
|
64
95
|
if (classification === "RECOVERABLE") {
|
|
65
96
|
return {
|
|
@@ -167,8 +198,35 @@ export function result({
|
|
|
167
198
|
};
|
|
168
199
|
}
|
|
169
200
|
|
|
201
|
+
export function contractBootstrapRepairGuidance(taskId, reason = null) {
|
|
202
|
+
const command = `forgeloop task-repair-contract-bootstrap --task ${taskId} --acknowledge-repair --json`;
|
|
203
|
+
return {
|
|
204
|
+
nextAction: NEXT_ACTIONS.REPAIR_CONTRACT_BOOTSTRAP,
|
|
205
|
+
commands: [command],
|
|
206
|
+
commandSpecs: [directCommandSpec("task-repair-contract-bootstrap", taskId, [
|
|
207
|
+
{ name: "acknowledgeRepair", option: "--acknowledge-repair", description: "Explicit caller acknowledgement of the exact repair signature." },
|
|
208
|
+
])],
|
|
209
|
+
reason: reason ?? { code: "E_CONTRACT_BOOTSTRAP_REPAIR_AVAILABLE", message: "The exact duplicate contract bootstrap defect is repairable through the official append-only command." },
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export function contractBootstrapRepairMigrationGuidance(taskId, reason = null) {
|
|
214
|
+
const command = `forgeloop task-migrate-contract-bootstrap-repair --task ${taskId} --acknowledge-migration --json`;
|
|
215
|
+
return {
|
|
216
|
+
nextAction: NEXT_ACTIONS.MIGRATE_CONTRACT_BOOTSTRAP_REPAIR,
|
|
217
|
+
commands: [command],
|
|
218
|
+
commandSpecs: [directCommandSpec("task-migrate-contract-bootstrap-repair", taskId, [
|
|
219
|
+
{ name: "acknowledgeMigration", option: "--acknowledge-migration", description: "Fresh explicit caller acknowledgement of the exact legacy marker migration." },
|
|
220
|
+
])],
|
|
221
|
+
reason: reason ?? { code: "E_CONTRACT_BOOTSTRAP_REPAIR_MIGRATION_AVAILABLE", message: "The exact legacy contract bootstrap repair marker can be migrated through the official append-only command." },
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
170
225
|
export function commandFor(action) {
|
|
171
226
|
return {
|
|
227
|
+
[NEXT_ACTIONS.ROUTE]: "forgeloop route --task <id> --work <type> --json",
|
|
228
|
+
[NEXT_ACTIONS.RESOLVE_STALE_ROUTE]: "forgeloop route --task <id> --work <type> --json",
|
|
229
|
+
[NEXT_ACTIONS.REVALIDATE_GATES]: "forgeloop gate-revalidate --task <id> --gate <name> --acknowledge-stale --json",
|
|
172
230
|
[NEXT_ACTIONS.PLAN]: "forgeloop advance --to PLANNED",
|
|
173
231
|
[NEXT_ACTIONS.RUN_PREFLIGHT]: "forgeloop preflight --json",
|
|
174
232
|
[NEXT_ACTIONS.START_EXECUTION]: "forgeloop advance --to EXECUTING",
|