@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
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
import { canonicalFingerprint } from "../core/artifacts.js";
|
|
2
|
+
import { readContract } from "../core/contract.js";
|
|
3
|
+
import { appendProtocolEvent, validateEventLedger, validateStateLedgerCoherence } from "../core/events.js";
|
|
4
|
+
import { readPersistedRoute } from "../core/route-artifact.js";
|
|
5
|
+
import { resolveTaskContext } from "../core/task-context.js";
|
|
6
|
+
import { taskArtifactPath } from "../core/task-paths.js";
|
|
7
|
+
import {
|
|
8
|
+
classifyLockStaleness,
|
|
9
|
+
readLockInfo,
|
|
10
|
+
releaseStaleTaskLockIfUnchanged,
|
|
11
|
+
withProjectClaimsLock,
|
|
12
|
+
} from "../core/task-lock.js";
|
|
13
|
+
import { withTaskTransaction } from "../core/transaction.js";
|
|
14
|
+
import { resolveTaskClaimState } from "../core/task-claim-state.js";
|
|
15
|
+
import { readWorkState } from "../core/work-state.js";
|
|
16
|
+
import {
|
|
17
|
+
CONTRACT_BOOTSTRAP_REPAIR_AUTHORITY,
|
|
18
|
+
CONTRACT_BOOTSTRAP_REPAIR_MIGRATION_DEFECT,
|
|
19
|
+
CONTRACT_BOOTSTRAP_REPAIR_MIGRATION_EVENT,
|
|
20
|
+
CONTRACT_BOOTSTRAP_REPAIR_MIGRATION_VERSION,
|
|
21
|
+
assertContractBootstrapRepairMigrationDetails,
|
|
22
|
+
isLegacyContractBootstrapRepairMigrationCandidate,
|
|
23
|
+
resolveEffectiveContractBootstrapRepairAnchor,
|
|
24
|
+
} from "../core/contract-bootstrap-recovery.js";
|
|
25
|
+
import {
|
|
26
|
+
E_CONTRACT_BOOTSTRAP_REPAIR_MIGRATION_AUTHORIZATION_REQUIRED,
|
|
27
|
+
E_CONTRACT_BOOTSTRAP_REPAIR_MIGRATION_INVALID,
|
|
28
|
+
E_TASK_LOCKED,
|
|
29
|
+
} from "../core/error-codes.js";
|
|
30
|
+
|
|
31
|
+
function migrationError(code, message, details = {}) {
|
|
32
|
+
const error = new Error(message);
|
|
33
|
+
error.code = code;
|
|
34
|
+
Object.assign(error, details);
|
|
35
|
+
return error;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function routePath(taskId) {
|
|
39
|
+
return taskArtifactPath(taskId, "route");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function proveLegacyRepairState(target, packageRoot, taskId, marker) {
|
|
43
|
+
const contract = await readContract(target, packageRoot, { taskId });
|
|
44
|
+
if (contract.fingerprint !== marker.details.contractFingerprint) {
|
|
45
|
+
throw migrationError(
|
|
46
|
+
E_CONTRACT_BOOTSTRAP_REPAIR_MIGRATION_INVALID,
|
|
47
|
+
"Current contract does not match the legacy repair marker fingerprint",
|
|
48
|
+
{ artifacts: [taskArtifactPath(taskId, "contract")] },
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const state = await readWorkState(target, { packageRoot, taskId });
|
|
53
|
+
if (!state) {
|
|
54
|
+
throw migrationError(
|
|
55
|
+
E_CONTRACT_BOOTSTRAP_REPAIR_MIGRATION_INVALID,
|
|
56
|
+
"Current work-state is required to prove the legacy repair boundary",
|
|
57
|
+
{ artifacts: [taskArtifactPath(taskId, "state")] },
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
if (state.taskId !== taskId
|
|
61
|
+
|| state.phase !== marker.details.reconstructedPhase
|
|
62
|
+
|| state.contractFingerprint !== marker.details.contractFingerprint
|
|
63
|
+
|| canonicalFingerprint(state) !== marker.details.reconstructedStateFingerprint) {
|
|
64
|
+
throw migrationError(
|
|
65
|
+
E_CONTRACT_BOOTSTRAP_REPAIR_MIGRATION_INVALID,
|
|
66
|
+
"Current work-state does not exactly match the legacy repair marker reconstruction",
|
|
67
|
+
{ artifacts: [taskArtifactPath(taskId, "state")] },
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
if (!Number.isInteger(state.revision) || state.revision < 0) {
|
|
71
|
+
throw migrationError(
|
|
72
|
+
E_CONTRACT_BOOTSTRAP_REPAIR_MIGRATION_INVALID,
|
|
73
|
+
"Current work-state revision is not a valid non-negative integer",
|
|
74
|
+
{ artifacts: [taskArtifactPath(taskId, "state")] },
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
let route = null;
|
|
79
|
+
if (state.phase === "ROUTED") {
|
|
80
|
+
try {
|
|
81
|
+
route = await readPersistedRoute(target, packageRoot, { taskId });
|
|
82
|
+
} catch (error) {
|
|
83
|
+
throw migrationError(
|
|
84
|
+
E_CONTRACT_BOOTSTRAP_REPAIR_MIGRATION_INVALID,
|
|
85
|
+
`ROUTED legacy repair requires a readable persisted route: ${error.message}`,
|
|
86
|
+
{ artifacts: [routePath(taskId)] },
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
if (marker.details.routeFingerprint === null
|
|
90
|
+
|| state.routeFingerprint !== marker.details.routeFingerprint
|
|
91
|
+
|| route.fingerprint !== marker.details.routeFingerprint
|
|
92
|
+
|| route.value.contractFingerprint !== marker.details.contractFingerprint) {
|
|
93
|
+
throw migrationError(
|
|
94
|
+
E_CONTRACT_BOOTSTRAP_REPAIR_MIGRATION_INVALID,
|
|
95
|
+
"Persisted route does not exactly match the legacy repair marker reconstruction",
|
|
96
|
+
{ artifacts: [routePath(taskId)] },
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
} else if (marker.details.routeFingerprint !== null || state.routeFingerprint !== undefined) {
|
|
100
|
+
throw migrationError(
|
|
101
|
+
E_CONTRACT_BOOTSTRAP_REPAIR_MIGRATION_INVALID,
|
|
102
|
+
"CONTRACT_READY legacy repair must not claim a route",
|
|
103
|
+
{ artifacts: [taskArtifactPath(taskId, "state")] },
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return { contract, state, route };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function migrationResult(taskId, candidate, migrationEvent, alreadyMigrated) {
|
|
111
|
+
return {
|
|
112
|
+
taskId,
|
|
113
|
+
migrated: !alreadyMigrated,
|
|
114
|
+
alreadyMigrated,
|
|
115
|
+
legacyMarkerSeq: candidate.marker.seq,
|
|
116
|
+
legacyRepairCommitSeq: candidate.repairCommit.seq,
|
|
117
|
+
migrationEventSeq: migrationEvent?.seq ?? null,
|
|
118
|
+
repairId: candidate.marker.details.repairId,
|
|
119
|
+
reconstructedPhase: candidate.marker.details.reconstructedPhase,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async function inspectAlreadyMigrated(target, packageRoot, taskId, ledger) {
|
|
124
|
+
const anchor = resolveEffectiveContractBootstrapRepairAnchor(ledger.events);
|
|
125
|
+
if (!ledger.valid || !anchor || anchor.kind !== "MIGRATED_LEGACY") {
|
|
126
|
+
throw migrationError(
|
|
127
|
+
E_CONTRACT_BOOTSTRAP_REPAIR_MIGRATION_INVALID,
|
|
128
|
+
"Existing contract bootstrap migration is invalid or the ledger is not strict",
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
const ownership = await resolveTaskClaimState(target, { taskId, packageRoot });
|
|
132
|
+
if (!ownership.ownershipValid) {
|
|
133
|
+
throw migrationError(
|
|
134
|
+
E_CONTRACT_BOOTSTRAP_REPAIR_MIGRATION_INVALID,
|
|
135
|
+
"Current task state no longer satisfies the migrated task ownership invariants",
|
|
136
|
+
{ errors: ownership.errors },
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
return migrationResult(taskId, { marker: anchor.sourceMarker, repairCommit: ledger.events[anchor.sourceMarker.seq] }, anchor.migrationEvent, true);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export async function runTaskMigrateContractBootstrapRepair({ target, packageRoot, taskId, acknowledgeMigration } = {}) {
|
|
143
|
+
if (!acknowledgeMigration) {
|
|
144
|
+
throw migrationError(
|
|
145
|
+
E_CONTRACT_BOOTSTRAP_REPAIR_MIGRATION_AUTHORIZATION_REQUIRED,
|
|
146
|
+
"task-migrate-contract-bootstrap-repair requires fresh explicit --acknowledge-migration",
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const context = await resolveTaskContext(target, { taskId, packageRoot, explicitRequired: true });
|
|
151
|
+
const effectiveTaskId = context.taskId;
|
|
152
|
+
const initial = await validateEventLedger(target, packageRoot, {
|
|
153
|
+
taskId: effectiveTaskId,
|
|
154
|
+
allowUnmigratedLegacyContractBootstrapRepairMarkers: true,
|
|
155
|
+
});
|
|
156
|
+
if (!initial.valid) {
|
|
157
|
+
const strict = await validateEventLedger(target, packageRoot, { taskId: effectiveTaskId });
|
|
158
|
+
if (strict.valid) return inspectAlreadyMigrated(target, packageRoot, effectiveTaskId, strict);
|
|
159
|
+
throw migrationError(
|
|
160
|
+
E_CONTRACT_BOOTSTRAP_REPAIR_MIGRATION_INVALID,
|
|
161
|
+
"Ledger is not structurally trustworthy for legacy contract bootstrap migration",
|
|
162
|
+
{ errors: initial.errors },
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
const initialCandidate = isLegacyContractBootstrapRepairMigrationCandidate(initial.events, effectiveTaskId);
|
|
166
|
+
if (!initialCandidate) {
|
|
167
|
+
const strict = await validateEventLedger(target, packageRoot, { taskId: effectiveTaskId });
|
|
168
|
+
if (strict.valid) return inspectAlreadyMigrated(target, packageRoot, effectiveTaskId, strict);
|
|
169
|
+
throw migrationError(
|
|
170
|
+
E_CONTRACT_BOOTSTRAP_REPAIR_MIGRATION_INVALID,
|
|
171
|
+
"Ledger does not match the exact legacy contract bootstrap repair migration boundary",
|
|
172
|
+
{ errors: initial.errors },
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return withProjectClaimsLock(target, "task-migrate-contract-bootstrap-repair", async () => {
|
|
177
|
+
const lock = await readLockInfo(target, effectiveTaskId);
|
|
178
|
+
const status = classifyLockStaleness(lock);
|
|
179
|
+
if (status.status === "LIVE") throw migrationError(E_TASK_LOCKED, `Task ${effectiveTaskId} has a live mutation lock`);
|
|
180
|
+
if (status.status === "UNKNOWN" || status.status === "CORRUPT") {
|
|
181
|
+
throw migrationError(
|
|
182
|
+
E_CONTRACT_BOOTSTRAP_REPAIR_MIGRATION_INVALID,
|
|
183
|
+
`Task ${effectiveTaskId} lock state is ${status.status}; refusing unsafe migration`,
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
if (status.status === "STALE") {
|
|
187
|
+
const released = await releaseStaleTaskLockIfUnchanged(target, effectiveTaskId, lock);
|
|
188
|
+
if (!released.released && released.reason !== "LOCK_MISSING") {
|
|
189
|
+
throw migrationError(
|
|
190
|
+
E_CONTRACT_BOOTSTRAP_REPAIR_MIGRATION_INVALID,
|
|
191
|
+
`Task ${effectiveTaskId} stale lock could not be safely released: ${released.reason}`,
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const result = await withTaskTransaction({
|
|
197
|
+
target,
|
|
198
|
+
taskId: effectiveTaskId,
|
|
199
|
+
operation: "task-migrate-contract-bootstrap-repair",
|
|
200
|
+
packageRoot,
|
|
201
|
+
recordCommitEvent: true,
|
|
202
|
+
}, async () => {
|
|
203
|
+
const locked = await validateEventLedger(target, packageRoot, {
|
|
204
|
+
taskId: effectiveTaskId,
|
|
205
|
+
allowUnmigratedLegacyContractBootstrapRepairMarkers: true,
|
|
206
|
+
});
|
|
207
|
+
if (!locked.valid) {
|
|
208
|
+
const strict = await validateEventLedger(target, packageRoot, { taskId: effectiveTaskId });
|
|
209
|
+
if (strict.valid) return inspectAlreadyMigrated(target, packageRoot, effectiveTaskId, strict);
|
|
210
|
+
throw migrationError(
|
|
211
|
+
E_CONTRACT_BOOTSTRAP_REPAIR_MIGRATION_INVALID,
|
|
212
|
+
"Pre-commit ledger revalidation failed inside the migration transaction",
|
|
213
|
+
{ errors: locked.errors },
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
const candidate = isLegacyContractBootstrapRepairMigrationCandidate(locked.events, effectiveTaskId);
|
|
217
|
+
if (!candidate
|
|
218
|
+
|| candidate.marker.seq !== initialCandidate.marker.seq
|
|
219
|
+
|| candidate.marker.hash !== initialCandidate.marker.hash
|
|
220
|
+
|| candidate.repairCommit.hash !== initialCandidate.repairCommit.hash) {
|
|
221
|
+
const strict = await validateEventLedger(target, packageRoot, { taskId: effectiveTaskId });
|
|
222
|
+
if (strict.valid) return inspectAlreadyMigrated(target, packageRoot, effectiveTaskId, strict);
|
|
223
|
+
throw migrationError(
|
|
224
|
+
E_CONTRACT_BOOTSTRAP_REPAIR_MIGRATION_INVALID,
|
|
225
|
+
"Ledger changed between inspection and migration lock; aborting",
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const proof = await proveLegacyRepairState(target, packageRoot, effectiveTaskId, candidate.marker);
|
|
230
|
+
const coherenceErrors = validateStateLedgerCoherence(proof.state, locked.events);
|
|
231
|
+
if (coherenceErrors.length > 0) {
|
|
232
|
+
throw migrationError(
|
|
233
|
+
E_CONTRACT_BOOTSTRAP_REPAIR_MIGRATION_INVALID,
|
|
234
|
+
"Work-state and ledger are not coherent at the legacy repair boundary",
|
|
235
|
+
{ errors: coherenceErrors },
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
const migratedAt = new Date().toISOString();
|
|
239
|
+
const details = {
|
|
240
|
+
migrationVersion: CONTRACT_BOOTSTRAP_REPAIR_MIGRATION_VERSION,
|
|
241
|
+
defect: CONTRACT_BOOTSTRAP_REPAIR_MIGRATION_DEFECT,
|
|
242
|
+
legacyMarkerSeq: candidate.marker.seq,
|
|
243
|
+
legacyMarkerHash: candidate.marker.hash,
|
|
244
|
+
legacyRepairId: candidate.marker.details.repairId,
|
|
245
|
+
legacyRepairCommitSeq: candidate.repairCommit.seq,
|
|
246
|
+
legacyRepairCommitHash: candidate.repairCommit.hash,
|
|
247
|
+
legacyRepairTransactionId: candidate.repairCommit.details.transactionId,
|
|
248
|
+
taskId: effectiveTaskId,
|
|
249
|
+
contractFingerprint: candidate.marker.details.contractFingerprint,
|
|
250
|
+
reconstructedPhase: candidate.marker.details.reconstructedPhase,
|
|
251
|
+
routeFingerprint: candidate.marker.details.routeFingerprint,
|
|
252
|
+
reconstructedStateFingerprint: candidate.marker.details.reconstructedStateFingerprint,
|
|
253
|
+
reconstructedStateRevision: proof.state.revision,
|
|
254
|
+
migratedAt,
|
|
255
|
+
authorityKind: CONTRACT_BOOTSTRAP_REPAIR_AUTHORITY,
|
|
256
|
+
};
|
|
257
|
+
assertContractBootstrapRepairMigrationDetails(details);
|
|
258
|
+
const migrationEvent = await appendProtocolEvent(target, {
|
|
259
|
+
taskId: effectiveTaskId,
|
|
260
|
+
event: CONTRACT_BOOTSTRAP_REPAIR_MIGRATION_EVENT,
|
|
261
|
+
at: migratedAt,
|
|
262
|
+
details,
|
|
263
|
+
}, packageRoot, { taskId: effectiveTaskId });
|
|
264
|
+
return migrationResult(effectiveTaskId, candidate, migrationEvent, false);
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
const finalLedger = await validateEventLedger(target, packageRoot, { taskId: effectiveTaskId });
|
|
268
|
+
if (!finalLedger.valid) {
|
|
269
|
+
throw migrationError(
|
|
270
|
+
E_CONTRACT_BOOTSTRAP_REPAIR_MIGRATION_INVALID,
|
|
271
|
+
"Post-migration ledger validation failed; the legacy repair remains unverified",
|
|
272
|
+
{ errors: finalLedger.errors },
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
return result;
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
export function formatTaskMigrateContractBootstrapRepairResult(result) {
|
|
280
|
+
return [
|
|
281
|
+
`taskId: ${result.taskId}`,
|
|
282
|
+
`migration: ${result.alreadyMigrated ? "already migrated" : "migrated"}`,
|
|
283
|
+
`legacy marker seq: ${result.legacyMarkerSeq}`,
|
|
284
|
+
`migration event seq: ${result.migrationEventSeq}`,
|
|
285
|
+
`repairId: ${result.repairId}`,
|
|
286
|
+
"",
|
|
287
|
+
].join("\n");
|
|
288
|
+
}
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import { canonicalFingerprint } from "../core/artifacts.js";
|
|
2
|
+
import { readContract } from "../core/contract.js";
|
|
3
|
+
import { appendProtocolEvent, validateEventLedger } from "../core/events.js";
|
|
4
|
+
import { readPersistedRoute } from "../core/route-artifact.js";
|
|
5
|
+
import { currentRepositoryFingerprint } from "../core/repository.js";
|
|
6
|
+
import { resolveTaskContext } from "../core/task-context.js";
|
|
7
|
+
import { taskArtifactPath } from "../core/task-paths.js";
|
|
8
|
+
import { withProjectClaimsLock, readLockInfo, classifyLockStaleness, releaseStaleTaskLockIfUnchanged } from "../core/task-lock.js";
|
|
9
|
+
import { withTaskTransaction } from "../core/transaction.js";
|
|
10
|
+
import { createWorkState, mutateWorkState, readWorkState } from "../core/work-state.js";
|
|
11
|
+
import { resolveTaskClaimState } from "../core/task-claim-state.js";
|
|
12
|
+
import {
|
|
13
|
+
CONTRACT_BOOTSTRAP_REPAIR_AUTHORITY,
|
|
14
|
+
CONTRACT_BOOTSTRAP_REPAIR_DEFECT,
|
|
15
|
+
CONTRACT_BOOTSTRAP_REPAIR_EVENT,
|
|
16
|
+
CONTRACT_BOOTSTRAP_REPAIR_VERSION,
|
|
17
|
+
assertContractBootstrapRepairDetails,
|
|
18
|
+
contractBootstrapRepairId,
|
|
19
|
+
isContractBootstrapRepairCandidate,
|
|
20
|
+
resolveEffectiveContractBootstrapRepairAnchor,
|
|
21
|
+
} from "../core/contract-bootstrap-recovery.js";
|
|
22
|
+
import {
|
|
23
|
+
E_CONTRACT_BOOTSTRAP_REPAIR_AUTHORIZATION_REQUIRED,
|
|
24
|
+
E_CONTRACT_BOOTSTRAP_REPAIR_INVALID,
|
|
25
|
+
E_CONTRACT_BOOTSTRAP_REPAIR_UNSAFE,
|
|
26
|
+
E_TASK_LOCKED,
|
|
27
|
+
} from "../core/error-codes.js";
|
|
28
|
+
|
|
29
|
+
function repairError(code, message, details = {}) {
|
|
30
|
+
const error = new Error(message);
|
|
31
|
+
error.code = code;
|
|
32
|
+
Object.assign(error, details);
|
|
33
|
+
return error;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async function readRouteIfProven(target, packageRoot, taskId, events, contractFingerprint) {
|
|
37
|
+
const routePath = taskArtifactPath(taskId, "route");
|
|
38
|
+
const hasRouteValidated = events.some((event) => event.event === "ROUTE_VALIDATED");
|
|
39
|
+
if (!hasRouteValidated) return null;
|
|
40
|
+
try {
|
|
41
|
+
const route = await readPersistedRoute(target, packageRoot, { taskId });
|
|
42
|
+
if (route.value.contractFingerprint !== contractFingerprint) {
|
|
43
|
+
throw repairError(E_CONTRACT_BOOTSTRAP_REPAIR_UNSAFE, "Persisted route does not bind to the canonical contract fingerprint");
|
|
44
|
+
}
|
|
45
|
+
return route;
|
|
46
|
+
} catch (error) {
|
|
47
|
+
if (error.code === "ARTIFACT_MISSING") {
|
|
48
|
+
throw repairError(E_CONTRACT_BOOTSTRAP_REPAIR_UNSAFE, "ROUTE_VALIDATED history requires a persisted route artifact", { artifacts: [routePath] });
|
|
49
|
+
}
|
|
50
|
+
if (error.code === E_CONTRACT_BOOTSTRAP_REPAIR_UNSAFE) throw error;
|
|
51
|
+
throw repairError(E_CONTRACT_BOOTSTRAP_REPAIR_UNSAFE, `Persisted route cannot be proven safe: ${error.message}`, { artifacts: [routePath] });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function stateForRepair(state, { taskId, contractFingerprint, route, repository }) {
|
|
56
|
+
const routed = Boolean(route);
|
|
57
|
+
const phase = routed ? "ROUTED" : "CONTRACT_READY";
|
|
58
|
+
const steps = routed
|
|
59
|
+
? { completedSteps: ["contract", "route"], pendingSteps: ["planning", "implementation", "verification"] }
|
|
60
|
+
: { completedSteps: ["contract"], pendingSteps: ["route", "planning", "implementation", "verification"] };
|
|
61
|
+
const base = state ? {
|
|
62
|
+
...state,
|
|
63
|
+
taskId,
|
|
64
|
+
contractFingerprint,
|
|
65
|
+
...(routed ? { routeFingerprint: route.fingerprint } : { routeFingerprint: undefined }),
|
|
66
|
+
repositoryFingerprint: repository,
|
|
67
|
+
phase,
|
|
68
|
+
selectedGuides: routed ? [...route.value.guides] : [],
|
|
69
|
+
completedSteps: steps.completedSteps,
|
|
70
|
+
pendingSteps: steps.pendingSteps,
|
|
71
|
+
revision: (state.revision ?? 0) + 1,
|
|
72
|
+
lastUpdated: new Date().toISOString(),
|
|
73
|
+
...(state.phase !== phase ? { previousPhase: state.phase } : {}),
|
|
74
|
+
} : createWorkState({
|
|
75
|
+
taskId,
|
|
76
|
+
contractFingerprint,
|
|
77
|
+
...(routed ? { routeFingerprint: route.fingerprint } : {}),
|
|
78
|
+
repositoryFingerprint: repository,
|
|
79
|
+
phase,
|
|
80
|
+
selectedGuides: routed ? [...route.value.guides] : [],
|
|
81
|
+
requiredGates: [],
|
|
82
|
+
satisfiedGates: [],
|
|
83
|
+
completedSteps: steps.completedSteps,
|
|
84
|
+
pendingSteps: steps.pendingSteps,
|
|
85
|
+
requiredArtifacts: [],
|
|
86
|
+
checks: [],
|
|
87
|
+
failures: [],
|
|
88
|
+
blockers: [],
|
|
89
|
+
verificationEvidence: [],
|
|
90
|
+
revision: 0,
|
|
91
|
+
});
|
|
92
|
+
if (base.routeFingerprint === undefined) delete base.routeFingerprint;
|
|
93
|
+
return createWorkState(base);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function assertStateCompatible(state, taskId, contractFingerprint, route) {
|
|
97
|
+
if (!state) return;
|
|
98
|
+
if (state.taskId !== taskId || state.contractFingerprint !== contractFingerprint) {
|
|
99
|
+
throw repairError(E_CONTRACT_BOOTSTRAP_REPAIR_UNSAFE, "Existing work-state is bound to a different task or contract");
|
|
100
|
+
}
|
|
101
|
+
if (!["CONTRACT_READY", "ROUTED"].includes(state.phase)) {
|
|
102
|
+
throw repairError(E_CONTRACT_BOOTSTRAP_REPAIR_UNSAFE, `Existing work-state phase ${state.phase} is beyond the repair boundary`);
|
|
103
|
+
}
|
|
104
|
+
if (route && state.routeFingerprint !== undefined && state.routeFingerprint !== route.fingerprint) {
|
|
105
|
+
throw repairError(E_CONTRACT_BOOTSTRAP_REPAIR_UNSAFE, "Existing work-state route fingerprint conflicts with the proven route");
|
|
106
|
+
}
|
|
107
|
+
if (!route && state.routeFingerprint !== undefined) {
|
|
108
|
+
throw repairError(E_CONTRACT_BOOTSTRAP_REPAIR_UNSAFE, "Existing work-state claims a route that cannot be proven from the artifacts");
|
|
109
|
+
}
|
|
110
|
+
if (state.phase === "CONTRACT_READY") {
|
|
111
|
+
if (state.routeFingerprint !== undefined) {
|
|
112
|
+
throw repairError(E_CONTRACT_BOOTSTRAP_REPAIR_UNSAFE, "CONTRACT_READY work-state must not claim a route");
|
|
113
|
+
}
|
|
114
|
+
if (Array.isArray(state.selectedGuides) && state.selectedGuides.length > 0) {
|
|
115
|
+
throw repairError(E_CONTRACT_BOOTSTRAP_REPAIR_UNSAFE, "CONTRACT_READY work-state must not have selected guides");
|
|
116
|
+
}
|
|
117
|
+
if (Array.isArray(state.requiredGates) && state.requiredGates.length > 0) {
|
|
118
|
+
throw repairError(E_CONTRACT_BOOTSTRAP_REPAIR_UNSAFE, "CONTRACT_READY work-state must not have required gates");
|
|
119
|
+
}
|
|
120
|
+
if (Array.isArray(state.satisfiedGates) && state.satisfiedGates.length > 0) {
|
|
121
|
+
throw repairError(E_CONTRACT_BOOTSTRAP_REPAIR_UNSAFE, "CONTRACT_READY work-state must not have satisfied gates");
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function markerResult(taskId, marker, state, alreadyRepaired) {
|
|
127
|
+
return {
|
|
128
|
+
taskId,
|
|
129
|
+
repaired: alreadyRepaired ? false : true,
|
|
130
|
+
alreadyRepaired,
|
|
131
|
+
repairId: marker.details.repairId,
|
|
132
|
+
phase: state.phase,
|
|
133
|
+
reconstructedPhase: marker.details.reconstructedPhase,
|
|
134
|
+
markerEventSeq: marker.seq,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async function verifyExistingRepair(target, packageRoot, taskId, ledger) {
|
|
139
|
+
const anchor = resolveEffectiveContractBootstrapRepairAnchor(ledger.events);
|
|
140
|
+
if (!anchor || !ledger.valid) {
|
|
141
|
+
throw repairError(E_CONTRACT_BOOTSTRAP_REPAIR_INVALID, "Existing contract bootstrap repair marker is invalid or the repaired ledger is no longer strict");
|
|
142
|
+
}
|
|
143
|
+
const contract = await readContract(target, packageRoot, { taskId });
|
|
144
|
+
if (contract.fingerprint !== anchor.details.contractFingerprint) {
|
|
145
|
+
throw repairError(E_CONTRACT_BOOTSTRAP_REPAIR_INVALID, "Contract fingerprint no longer matches the repair marker");
|
|
146
|
+
}
|
|
147
|
+
const state = await readWorkState(target, { packageRoot, taskId });
|
|
148
|
+
const ownership = await resolveTaskClaimState(target, { taskId, packageRoot });
|
|
149
|
+
if (!ownership.ownershipValid) {
|
|
150
|
+
throw repairError(E_CONTRACT_BOOTSTRAP_REPAIR_INVALID, "Current task state no longer satisfies the repaired-task invariants", {
|
|
151
|
+
errors: ownership.errors,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
return markerResult(taskId, { ...anchor.sourceMarker, details: anchor.details }, state, true);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export async function runTaskRepairContractBootstrap({ target, packageRoot, taskId, acknowledgeRepair } = {}) {
|
|
158
|
+
if (!acknowledgeRepair) {
|
|
159
|
+
throw repairError(E_CONTRACT_BOOTSTRAP_REPAIR_AUTHORIZATION_REQUIRED, "task-repair-contract-bootstrap requires fresh explicit --acknowledge-repair");
|
|
160
|
+
}
|
|
161
|
+
const context = await resolveTaskContext(target, { taskId, packageRoot, explicitRequired: true });
|
|
162
|
+
const effectiveTaskId = context.taskId;
|
|
163
|
+
const initial = await validateEventLedger(target, packageRoot, { taskId: effectiveTaskId });
|
|
164
|
+
if (initial.events.some((event) => event.event === CONTRACT_BOOTSTRAP_REPAIR_EVENT)) {
|
|
165
|
+
return verifyExistingRepair(target, packageRoot, effectiveTaskId, initial);
|
|
166
|
+
}
|
|
167
|
+
const candidate = isContractBootstrapRepairCandidate(initial.events, initial.errors, effectiveTaskId);
|
|
168
|
+
if (!candidate) {
|
|
169
|
+
throw repairError(E_CONTRACT_BOOTSTRAP_REPAIR_UNSAFE, "Ledger does not match the exact contract bootstrap repair signature", { errors: initial.errors });
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return withProjectClaimsLock(target, "task-repair-contract-bootstrap", async () => {
|
|
173
|
+
const lock = await readLockInfo(target, effectiveTaskId);
|
|
174
|
+
const status = classifyLockStaleness(lock);
|
|
175
|
+
if (status.status === "LIVE") throw repairError(E_TASK_LOCKED, `Task ${effectiveTaskId} has a live mutation lock`);
|
|
176
|
+
if (status.status === "UNKNOWN" || status.status === "CORRUPT") {
|
|
177
|
+
throw repairError(E_CONTRACT_BOOTSTRAP_REPAIR_UNSAFE, `Task ${effectiveTaskId} lock state is ${status.status}`);
|
|
178
|
+
}
|
|
179
|
+
if (status.status === "STALE") {
|
|
180
|
+
const released = await releaseStaleTaskLockIfUnchanged(target, effectiveTaskId, lock);
|
|
181
|
+
if (!released.released && released.reason !== "LOCK_MISSING") {
|
|
182
|
+
throw repairError(E_CONTRACT_BOOTSTRAP_REPAIR_UNSAFE,
|
|
183
|
+
`Task ${effectiveTaskId} stale lock could not be safely released: ${released.reason}`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return withTaskTransaction({
|
|
187
|
+
target,
|
|
188
|
+
taskId: effectiveTaskId,
|
|
189
|
+
operation: "task-repair-contract-bootstrap",
|
|
190
|
+
packageRoot,
|
|
191
|
+
recordCommitEvent: true,
|
|
192
|
+
}, async () => {
|
|
193
|
+
const locked = await validateEventLedger(target, packageRoot, { taskId: effectiveTaskId });
|
|
194
|
+
if (locked.events.some((event) => event.event === CONTRACT_BOOTSTRAP_REPAIR_EVENT)) {
|
|
195
|
+
return verifyExistingRepair(target, packageRoot, effectiveTaskId, locked);
|
|
196
|
+
}
|
|
197
|
+
const lockedCandidate = isContractBootstrapRepairCandidate(locked.events, locked.errors, effectiveTaskId);
|
|
198
|
+
if (!lockedCandidate
|
|
199
|
+
|| lockedCandidate.duplicateContractEvent.hash !== candidate.duplicateContractEvent.hash
|
|
200
|
+
|| lockedCandidate.duplicateContractCommit.hash !== candidate.duplicateContractCommit.hash) {
|
|
201
|
+
throw repairError(E_CONTRACT_BOOTSTRAP_REPAIR_INVALID, "Ledger changed between inspection and repair lock");
|
|
202
|
+
}
|
|
203
|
+
const contract = await readContract(target, packageRoot, { taskId: effectiveTaskId });
|
|
204
|
+
if (contract.fingerprint !== lockedCandidate.contractFingerprint) {
|
|
205
|
+
throw repairError(E_CONTRACT_BOOTSTRAP_REPAIR_UNSAFE, "Current contract does not match the canonical historical contract fingerprint");
|
|
206
|
+
}
|
|
207
|
+
const route = await readRouteIfProven(target, packageRoot, effectiveTaskId, locked.events, contract.fingerprint);
|
|
208
|
+
const state = await readWorkState(target, { packageRoot, taskId: effectiveTaskId });
|
|
209
|
+
assertStateCompatible(state, effectiveTaskId, contract.fingerprint, route);
|
|
210
|
+
if (!state) {
|
|
211
|
+
throw repairError(E_CONTRACT_BOOTSTRAP_REPAIR_UNSAFE, "Existing work-state is required to repair the contract bootstrap defect");
|
|
212
|
+
}
|
|
213
|
+
const repository = await currentRepositoryFingerprint(target);
|
|
214
|
+
const reconstructed = await mutateWorkState(target, {
|
|
215
|
+
expectedRevision: state.revision ?? 0,
|
|
216
|
+
packageRoot,
|
|
217
|
+
taskId: effectiveTaskId,
|
|
218
|
+
}, (current) => {
|
|
219
|
+
assertStateCompatible(current, effectiveTaskId, contract.fingerprint, route);
|
|
220
|
+
return stateForRepair(current, {
|
|
221
|
+
taskId: effectiveTaskId,
|
|
222
|
+
contractFingerprint: contract.fingerprint,
|
|
223
|
+
route,
|
|
224
|
+
repository,
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
const repairedAt = new Date().toISOString();
|
|
228
|
+
const details = {
|
|
229
|
+
repairVersion: CONTRACT_BOOTSTRAP_REPAIR_VERSION,
|
|
230
|
+
taskId: effectiveTaskId,
|
|
231
|
+
defect: CONTRACT_BOOTSTRAP_REPAIR_DEFECT,
|
|
232
|
+
canonicalContractEventSeq: lockedCandidate.canonicalContractEvent.seq,
|
|
233
|
+
canonicalContractEventHash: lockedCandidate.canonicalContractEvent.hash,
|
|
234
|
+
duplicateContractEventSeq: lockedCandidate.duplicateContractEvent.seq,
|
|
235
|
+
duplicateContractEventHash: lockedCandidate.duplicateContractEvent.hash,
|
|
236
|
+
contractCreateCommitSeq: lockedCandidate.duplicateContractCommit.seq,
|
|
237
|
+
contractCreateCommitHash: lockedCandidate.duplicateContractCommit.hash,
|
|
238
|
+
contractCreateTransactionId: lockedCandidate.duplicateContractCommit.details.transactionId,
|
|
239
|
+
contractFingerprint: contract.fingerprint,
|
|
240
|
+
reconstructedPhase: reconstructed.phase,
|
|
241
|
+
routeFingerprint: route?.fingerprint ?? null,
|
|
242
|
+
previousStateFingerprint: canonicalFingerprint(state),
|
|
243
|
+
reconstructedStateFingerprint: canonicalFingerprint(reconstructed),
|
|
244
|
+
reconstructedStateRevision: reconstructed.revision,
|
|
245
|
+
repairedAt,
|
|
246
|
+
authorityKind: CONTRACT_BOOTSTRAP_REPAIR_AUTHORITY,
|
|
247
|
+
};
|
|
248
|
+
details.repairId = contractBootstrapRepairId(details);
|
|
249
|
+
assertContractBootstrapRepairDetails(details);
|
|
250
|
+
const marker = await appendProtocolEvent(target, {
|
|
251
|
+
taskId: effectiveTaskId,
|
|
252
|
+
event: CONTRACT_BOOTSTRAP_REPAIR_EVENT,
|
|
253
|
+
at: repairedAt,
|
|
254
|
+
details,
|
|
255
|
+
}, packageRoot, { taskId: effectiveTaskId });
|
|
256
|
+
return markerResult(effectiveTaskId, marker, reconstructed, false);
|
|
257
|
+
});
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export function formatTaskRepairContractBootstrapResult(result) {
|
|
262
|
+
return `taskId: ${result.taskId}\nphase: ${result.phase}\nrepairId: ${result.repairId}\n${result.alreadyRepaired ? "alreadyRepaired: true" : "repaired: true"}\n`;
|
|
263
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { runTestInventory as inventory } from "../core/test-intelligence/service.js";
|
|
2
|
+
|
|
3
|
+
export async function runTestInventory({ target = process.cwd() } = {}) { return inventory({ projectRoot: target }); }
|
|
4
|
+
export function formatTestInventoryResult(result) { return `${JSON.stringify(result, null, 2)}\n`; }
|
|
5
|
+
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { buildPrunePlan } from "../core/test-intelligence/prune.js";
|
|
2
|
+
|
|
3
|
+
export async function runTestPrunePlan({ target, packageRoot, taskId } = {}) { return buildPrunePlan({ target, packageRoot, taskId }); }
|
|
4
|
+
export function formatTestPrunePlanResult(result) { return `${JSON.stringify(result, null, 2)}\n`; }
|
|
5
|
+
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { runPruneProbe as probe } from "../core/test-intelligence/prune.js";
|
|
2
|
+
|
|
3
|
+
export async function runTestPruneProbe({ target, packageRoot, taskId, testId } = {}) { return probe({ target, packageRoot, taskId, testId }); }
|
|
4
|
+
export function formatTestPruneProbeResult(result) { return `${JSON.stringify(result, null, 2)}\n`; }
|
|
5
|
+
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { runTestUtility as analyze } from "../core/test-intelligence/service.js";
|
|
2
|
+
|
|
3
|
+
export async function runTestUtility({ target, packageRoot, taskId, semanticStatus } = {}) { return analyze({ target, packageRoot, taskId, semanticStatus }); }
|
|
4
|
+
export function formatTestUtilityResult(result) { return `${JSON.stringify(result, null, 2)}\n`; }
|
|
5
|
+
|
|
@@ -44,6 +44,10 @@ async function readArtifact(target, relativePath, label) {
|
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
function isOptionalContinuityAbsence(item) {
|
|
48
|
+
return item.label === "continuity" && item.error?.code === "ARTIFACT_MISSING";
|
|
49
|
+
}
|
|
50
|
+
|
|
47
51
|
import { taskArtifactPath } from "../core/task-paths.js";
|
|
48
52
|
import { withResolvedTask } from "../core/task-command.js";
|
|
49
53
|
|
|
@@ -84,7 +88,12 @@ export async function runValidateProtocol({
|
|
|
84
88
|
loaded.push({ label, relativePath, ...artifact });
|
|
85
89
|
}
|
|
86
90
|
|
|
87
|
-
|
|
91
|
+
// Continuity is an optional operational-context artifact. Its absence is
|
|
92
|
+
// represented by evaluateContinuityConformance as NOT_APPLICABLE; only a
|
|
93
|
+
// present but malformed continuity artifact is a validation failure.
|
|
94
|
+
const readErrors = loaded
|
|
95
|
+
.filter((item) => item.error && !isOptionalContinuityAbsence(item))
|
|
96
|
+
.map((item) => item.error);
|
|
88
97
|
const route = loaded.find((item) => item.label === "route")?.value ?? null;
|
|
89
98
|
const state = loaded.find((item) => item.label === "state")?.value ?? null;
|
|
90
99
|
const receipt = loaded.find((item) => item.label === "receipt")?.value ?? null;
|
|
@@ -391,4 +391,28 @@ export const ARTIFACT_REGISTRY = Object.freeze({
|
|
|
391
391
|
isPersisted: true,
|
|
392
392
|
description: "Optional external signing-provider bundle; existence alone never establishes a valid signature.",
|
|
393
393
|
}),
|
|
394
|
+
semanticDecisions: Object.freeze({
|
|
395
|
+
key: "semanticDecisions",
|
|
396
|
+
scope: "TASK",
|
|
397
|
+
path: `${TASK_STATE_ROOT}/<task-key>/${TASK_ARTIFACT_FILES.decisions}/<decision-id>.json`,
|
|
398
|
+
schema: "semantic-decision",
|
|
399
|
+
owner: "PROTOCOL_COMPILED",
|
|
400
|
+
mutability: "IMMUTABLE_ONCE_WRITTEN",
|
|
401
|
+
trustRole: "SEMANTIC_DECISION",
|
|
402
|
+
isPublic: true,
|
|
403
|
+
isPersisted: true,
|
|
404
|
+
description: "Fingerprint-bound, normalized Jev semantic decision; never lifecycle, evidence, ownership, installation, or completion authority.",
|
|
405
|
+
}),
|
|
406
|
+
testUtility: Object.freeze({
|
|
407
|
+
key: "testUtility",
|
|
408
|
+
scope: "TASK",
|
|
409
|
+
path: `${TASK_STATE_ROOT}/<task-key>/${TASK_ARTIFACT_FILES.testUtility}`,
|
|
410
|
+
schema: "test-utility",
|
|
411
|
+
owner: "PROTOCOL_COMPILED",
|
|
412
|
+
mutability: "OVERWRITTEN_ON_ANALYSIS",
|
|
413
|
+
trustRole: "NON_EVIDENCE_TEST_ANALYSIS",
|
|
414
|
+
isPublic: true,
|
|
415
|
+
isPersisted: true,
|
|
416
|
+
description: "Deterministic test inventory and bounded utility analysis; never completion evidence or deletion authority.",
|
|
417
|
+
}),
|
|
394
418
|
});
|