@cassiomc1/forgeloop 1.13.0 → 2.0.0-rc.2
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/CONTRIBUTING.md +11 -7
- package/DOCS_INDEX.md +37 -4
- package/ENG/c-development-eng.md +2 -2
- package/ENG/cpp-development-eng.md +2 -2
- package/ENG/dotnet-aspnetcore-development-eng.md +7 -1
- package/ENG/flutter-development-eng.md +4 -2
- package/ENG/go-development-eng.md +2 -2
- package/ENG/java-development-eng.md +3 -3
- package/ENG/nodejs-backend-development-eng.md +10 -6
- package/ENG/php-development-eng.md +3 -3
- package/ENG/python-development-eng.md +205 -0
- package/ENG/rust-development-eng.md +42 -1
- package/ENG/sec-code-eng.md +7 -7
- package/ENG/sql-development-eng.md +4 -4
- package/ENG/swift-development-eng.md +5 -4
- package/ENG/test-code-eng.md +1 -1
- package/ENG/typescript-development-eng.md +4 -4
- package/EXECUTION_STATE.md +12 -0
- package/GUIDE_ROUTER.md +6 -0
- package/LICENSE +133 -21
- package/LOOP_ENGINEERING.md +28 -2
- package/LOOP_SYSTEM_DESIGN.md +10 -10
- package/ORCHESTRATOR_INTEGRATION.md +9 -5
- package/PROTOCOL_INTEGRATION.md +55 -2
- package/README.md +63 -59
- package/TERMINOLOGY.md +2 -0
- package/THIRD_PARTY_NOTICES.md +3 -4
- package/THREAT_MODEL.md +140 -1
- package/completions/_forgeloop +29 -3
- package/completions/forgeloop.bash +55 -3
- package/completions/forgeloop.fish +183 -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 +74 -39
- package/docs/AGENT_SKILL.md +66 -0
- package/docs/ARTIFACT_REFERENCE.md +171 -35
- package/docs/AUDIT_UX.md +46 -0
- package/docs/BROWSER_VERIFICATION.md +136 -0
- package/docs/CLI_REFERENCE.md +512 -8
- package/docs/CODE_ATTESTATION.md +2 -2
- package/docs/DOCUMENTATION_GUIDE.md +34 -11
- package/docs/GETTING_STARTED.md +3 -3
- 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 +46 -18
- package/docs/PROVIDERS.md +126 -0
- package/docs/PROVIDER_ARCHITECTURE.md +199 -0
- package/docs/RECIPES.md +9 -0
- package/docs/RELEASE_CHECKLIST.md +116 -12
- package/docs/SECURITY_REVIEW.md +71 -0
- package/docs/SELF_HOSTED_TESTS.md +121 -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 +774 -16
- package/docs/protocol-requirements.json +24 -0
- package/package.json +43 -11
- 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/task-bundle.schema.json +7 -2
- 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 +6 -3
- package/scripts/generate-forgeloop-skill.mjs +133 -0
- package/scripts/jev-smoke.mjs +19 -0
- package/scripts/run-mcp-clean-tests.mjs +30 -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 +316 -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 +289 -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/temporary-paths.js +18 -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 +218 -174
- package/src/commands/action-show.js +5 -2
- package/src/commands/advance.js +5 -1
- package/src/commands/baseline.js +13 -11
- package/src/commands/checkpoint-revalidate.js +192 -0
- package/src/commands/complete.js +19 -3
- package/src/commands/context-plan.js +38 -0
- package/src/commands/contract-create.js +266 -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 +51 -13
- package/src/commands/gate-record.js +194 -0
- package/src/commands/gate-revalidate.js +135 -0
- package/src/commands/handoff-list.js +6 -1
- package/src/commands/handoff-show.js +6 -1
- package/src/commands/migrate-protocol.js +2 -2
- package/src/commands/model-route.js +40 -0
- package/src/commands/next.js +7 -1
- package/src/commands/policy-diff.js +25 -4
- package/src/commands/progress.js +17 -11
- package/src/commands/record-diagnosis.js +3 -0
- package/src/commands/route.js +146 -18
- package/src/commands/semantic-plan.js +41 -0
- package/src/commands/status.js +6 -1
- package/src/commands/storage-backup.js +15 -0
- package/src/commands/storage-migrate.js +5 -0
- package/src/commands/storage-migration-resume.js +5 -0
- package/src/commands/storage-migration-status.js +44 -0
- package/src/commands/storage-restore-resume.js +7 -0
- package/src/commands/storage-restore.js +8 -0
- package/src/commands/task-abandon.js +224 -0
- package/src/commands/task-list.js +8 -43
- package/src/commands/task-migrate-contract-bootstrap-repair.js +288 -0
- package/src/commands/task-migrate.js +3 -3
- package/src/commands/task-recover.js +10 -2
- package/src/commands/task-repair-contract-bootstrap.js +263 -0
- package/src/commands/task-scope.js +18 -2
- package/src/commands/task-show.js +16 -2
- 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 +39 -15
- package/src/commands/validate-receipt.js +8 -1
- package/src/config/guides.json +4 -0
- package/src/core/action-ledger-projection.js +10 -3
- package/src/core/actions.js +38 -75
- package/src/core/activation.js +11 -3
- package/src/core/approvals.js +17 -51
- package/src/core/artifact-registry.js +83 -2
- package/src/core/artifacts.js +81 -27
- package/src/core/attachment-paths.js +45 -0
- package/src/core/attestation-coverage.js +6 -1
- package/src/core/attestation-verifier.js +21 -2
- package/src/core/attestation.js +17 -6
- package/src/core/audit-ux.js +514 -0
- package/src/core/audit.js +6 -1
- 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/bundles.js +160 -66
- package/src/core/checkpoint-revalidation.js +321 -0
- package/src/core/cli-command-definitions.js +350 -7
- package/src/core/code-manifest.js +9 -3
- package/src/core/command-executors.js +274 -108
- package/src/core/command-input.js +136 -44
- package/src/core/completion-artifacts.js +85 -34
- package/src/core/completion-ownership.js +19 -8
- package/src/core/completion-recovery-rebind.js +11 -1
- package/src/core/completion.js +64 -15
- package/src/core/config.js +3 -0
- package/src/core/conformance.js +2 -1
- 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/continuity-lint.js +13 -2
- package/src/core/continuity-reconciliation.js +16 -3
- package/src/core/continuity.js +16 -8
- package/src/core/contract-bootstrap-recovery.js +656 -0
- package/src/core/contract-revision.js +209 -0
- package/src/core/decision/artifact.js +70 -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 +104 -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 +138 -0
- package/src/core/decision/result.js +58 -0
- package/src/core/decision/service.js +181 -0
- package/src/core/decision/state-builder.js +65 -0
- package/src/core/decision/task-bindings.js +35 -0
- package/src/core/decision/test-provider.js +32 -0
- package/src/core/decision/thresholds.js +15 -0
- package/src/core/diagnosis-model.js +11 -0
- package/src/core/diagnosis.js +10 -1
- package/src/core/diagnostic-projection.js +29 -12
- package/src/core/diagnostic-record.js +28 -3
- package/src/core/efficiency.js +6 -1
- package/src/core/error-codes.js +278 -0
- package/src/core/events.js +535 -147
- package/src/core/evidence-readiness.js +9 -0
- package/src/core/execution-prerequisites.js +36 -13
- package/src/core/execution-profile.js +63 -38
- package/src/core/execution.js +26 -40
- package/src/core/filesystem.js +11 -0
- package/src/core/gate-artifact.js +105 -19
- package/src/core/gate-provenance.js +125 -0
- package/src/core/handoff.js +26 -14
- package/src/core/history.js +39 -35
- package/src/core/hypothesis-projection.js +32 -0
- package/src/core/information-gain-projection.js +104 -100
- package/src/core/inspect.js +12 -3
- package/src/core/integration-invocation-policy.js +37 -4
- package/src/core/integration-resources.js +119 -87
- package/src/core/ledger-event-collection.js +189 -0
- package/src/core/ledger-relations.js +23 -0
- 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/native-storage.js +12 -0
- package/src/core/next-action-continuity.js +31 -9
- package/src/core/next-action-model.js +58 -0
- package/src/core/next-action-phases.js +131 -43
- package/src/core/next-action-refresh.js +43 -9
- package/src/core/next-action-review-phase.js +9 -4
- package/src/core/next-action-verification-phase.js +2 -1
- package/src/core/next-action.js +56 -9
- package/src/core/phase.js +266 -103
- package/src/core/policy-engine.js +12 -13
- package/src/core/preflight-consistency.js +89 -62
- package/src/core/preflight-loaders.js +48 -9
- package/src/core/preflight-model.js +6 -1
- package/src/core/preflight.js +29 -12
- package/src/core/progress.js +21 -10
- package/src/core/protocol-info.js +68 -0
- package/src/core/protocol-migration.js +7 -6
- package/src/core/protocol.js +20 -0
- package/src/core/reconcile-closure.js +281 -67
- package/src/core/recovery-history.js +67 -47
- package/src/core/reflection.js +72 -77
- package/src/core/responsibility.js +25 -13
- package/src/core/resumability.js +154 -44
- package/src/core/route-artifact.js +28 -4
- package/src/core/router.js +67 -1
- package/src/core/runtime-context.js +123 -61
- package/src/core/schema-validation.js +11 -3
- 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/settlement.js +12 -1
- package/src/core/signing/bundle-file.js +53 -0
- package/src/core/signing/bundle-reference.js +15 -0
- package/src/core/signing/sigstore.js +60 -33
- package/src/core/signing/statement-file.js +27 -0
- package/src/core/sources.js +37 -0
- package/src/core/storage-runtime-registry.js +41 -0
- package/src/core/structural-quality/artifacts.js +20 -14
- package/src/core/structural-quality/service.js +81 -56
- package/src/core/target-layout.js +1 -1
- package/src/core/task-claim-state.js +249 -15
- package/src/core/task-command.js +21 -1
- package/src/core/task-conflict-inspection.js +133 -102
- package/src/core/task-descriptor.js +29 -22
- package/src/core/task-discovery.js +288 -90
- package/src/core/task-lock.js +66 -306
- package/src/core/task-migration-validation.js +5 -5
- package/src/core/task-migration.js +17 -287
- package/src/core/task-paths.js +13 -0
- package/src/core/task-recovery.js +1 -0
- package/src/core/task-snapshot.js +51 -8
- package/src/core/task-storage-migration.js +18 -0
- package/src/core/templates.js +23 -8
- 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 +94 -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 +164 -105
- package/src/core/trajectory-evaluation.js +15 -3
- package/src/core/trajectory-metrics.js +47 -32
- package/src/core/transaction.js +16 -287
- package/src/core/verification-scope.js +20 -12
- package/src/core/work-state.js +100 -59
- 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
- package/src/repository-index/binary-manager.js +7 -5
- package/src/storage/artifact-bytes.js +22 -0
- package/src/storage/attachment-binding.js +11 -0
- package/src/storage/attachment-files.js +82 -0
- package/src/storage/attachment-references.js +36 -0
- package/src/storage/backup.js +53 -0
- package/src/storage/capabilities.js +31 -0
- package/src/storage/connection-owner.js +119 -0
- package/src/storage/connection.js +376 -0
- package/src/storage/existing-project-scope.js +45 -0
- package/src/storage/export-destination.js +44 -0
- package/src/storage/exporter.js +284 -0
- package/src/storage/file-durability.js +46 -0
- package/src/storage/importer.js +461 -0
- package/src/storage/index.js +74 -0
- package/src/storage/leases.js +63 -0
- package/src/storage/ledger-event-snapshot.js +150 -0
- package/src/storage/ledger-relations.js +220 -0
- package/src/storage/legacy-maintenance-boundary.js +18 -0
- package/src/storage/legacy-signature.js +115 -0
- package/src/storage/maintenance-handoff.js +129 -0
- package/src/storage/maintenance-owner.js +42 -0
- package/src/storage/maintenance.js +310 -0
- package/src/storage/metadata-json.js +35 -0
- package/src/storage/migration-activate.js +59 -0
- package/src/storage/migration-archive.js +57 -0
- package/src/storage/migration-attachment-root.js +61 -0
- package/src/storage/migration-candidate.js +398 -0
- package/src/storage/migration-publication.js +143 -0
- package/src/storage/migration-rollback-admission.js +54 -0
- package/src/storage/migration-rollback-preparation.js +132 -0
- package/src/storage/migration-rollback-publication.js +183 -0
- package/src/storage/migration-rollback-release.js +66 -0
- package/src/storage/migration-rollback-source-stage.js +103 -0
- package/src/storage/migration-rollback.js +43 -0
- package/src/storage/migration-source-partition.js +56 -0
- package/src/storage/migration-source.js +255 -0
- package/src/storage/migration-terminal.js +39 -0
- package/src/storage/migration-validation.js +75 -0
- package/src/storage/migration.js +43 -0
- package/src/storage/operational-attachments.js +49 -0
- package/src/storage/operational-context.js +50 -0
- package/src/storage/project-backup.js +98 -0
- package/src/storage/project-bootstrap.js +24 -0
- package/src/storage/project-boundary.js +79 -0
- package/src/storage/project-read-snapshot.js +18 -0
- package/src/storage/project-replacement.js +137 -0
- package/src/storage/project-restore.js +203 -0
- package/src/storage/record-definitions.js +45 -0
- package/src/storage/repository.js +507 -0
- package/src/storage/restore-journal.js +62 -0
- package/src/storage/restore-layout.js +30 -0
- package/src/storage/restore-preparation.js +84 -0
- package/src/storage/restore-replacement-archive.js +162 -0
- package/src/storage/restore-replacement-owner.js +31 -0
- package/src/storage/restore-replacement-preparation.js +128 -0
- package/src/storage/restore-replacement.js +142 -0
- package/src/storage/restore-terminal.js +32 -0
- package/src/storage/runtime.js +31 -0
- package/src/storage/schema.js +214 -0
- package/src/storage/singleton-import.js +84 -0
- package/src/storage/snapshot-continuity.js +16 -0
- package/src/storage/snapshot.js +36 -0
- package/src/storage/storage-marker.js +79 -0
- package/src/storage/task-guards.js +107 -0
- package/src/storage/topology.js +42 -0
- package/src/storage/transaction.js +179 -0
- package/src/storage/unit-of-work.js +663 -0
- package/src/storage/windows-process-incarnation.js +206 -0
- package/.forgeloop/forgeloop.gitignore +0 -4
- package/LICENSE-DOCS.md +0 -13
- package/src/core/transaction-maintenance.js +0 -70
package/src/core/handoff.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { withProjectReadSnapshot } from "../storage/project-read-snapshot.js";
|
|
1
2
|
import { randomUUID } from "node:crypto";
|
|
2
3
|
import { readdir } from "node:fs/promises";
|
|
4
|
+
import { listOperationalArtifactNames, operationalArtifactExists } from "../storage/operational-context.js";
|
|
3
5
|
|
|
4
6
|
import { canonicalFingerprint, readJsonArtifact, writeJsonArtifact } from "./artifacts.js";
|
|
5
7
|
import { assertPortableContextSafe, normalizePortableText } from "./portable-context.js";
|
|
@@ -165,14 +167,19 @@ export async function validateCanonicalHandoff(target, handoff, {
|
|
|
165
167
|
return validateHandoffDigest(handoff);
|
|
166
168
|
}
|
|
167
169
|
|
|
168
|
-
export async function writeCanonicalHandoff(target, handoff, {
|
|
170
|
+
export async function writeCanonicalHandoff(target, handoff, options = {}) {
|
|
171
|
+
return withTaskTransaction({ target, taskId: options.taskId ?? handoff?.taskId, packageRoot: options.packageRoot, operation: "handoff-create" }, () => writeCanonicalHandoffInTransaction(target, handoff, options));
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async function writeCanonicalHandoffInTransaction(target, handoff, {
|
|
169
175
|
packageRoot = getPackageRoot(),
|
|
170
176
|
taskId = handoff?.taskId,
|
|
171
177
|
} = {}) {
|
|
172
178
|
await validateCanonicalHandoff(target, handoff, { taskId, packageRoot });
|
|
173
179
|
const relativePath = taskHandoffPath(taskId, handoff.handoffId);
|
|
174
|
-
|
|
175
|
-
if (await
|
|
180
|
+
const operationalExists = operationalArtifactExists(target, relativePath);
|
|
181
|
+
if (operationalExists === null) await assertSafePath(target, relativePath);
|
|
182
|
+
if (operationalExists ?? await fileExists(ensureWithin(target, relativePath))) {
|
|
176
183
|
throw handoffError("E_HANDOFF_INVALID", "An existing handoff cannot be overwritten", [relativePath]);
|
|
177
184
|
}
|
|
178
185
|
const artifact = await writeJsonArtifact(target, relativePath, handoff, "handoff-envelope", packageRoot, { taskId, operation: "handoff-create" });
|
|
@@ -219,17 +226,22 @@ export async function readCanonicalHandoff(target, {
|
|
|
219
226
|
}
|
|
220
227
|
|
|
221
228
|
export async function listCanonicalHandoffs(target, { taskId, packageRoot = getPackageRoot() } = {}) {
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
const
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
229
|
+
return withProjectReadSnapshot(target, async () => {
|
|
230
|
+
const directory = taskHandoffDirectory(taskId);
|
|
231
|
+
let names = listOperationalArtifactNames(target, taskId, "handoffs");
|
|
232
|
+
if (names === null) {
|
|
233
|
+
if (!(await fileExists(ensureWithin(target, directory)))) return [];
|
|
234
|
+
names = (await readdir(ensureWithin(target, directory), { withFileTypes: true })).filter(entry => entry.isFile()).map(entry => entry.name);
|
|
235
|
+
}
|
|
236
|
+
const handoffs = [];
|
|
237
|
+
for (const name of names) {
|
|
238
|
+
if (!/^handoff-[A-Za-z0-9_-]+\.json$/u.test(name)) continue;
|
|
239
|
+
const handoffId = name.slice(0, -5);
|
|
240
|
+
const artifact = await readCanonicalHandoff(target, { taskId, handoffId, packageRoot });
|
|
241
|
+
handoffs.push(artifact.value);
|
|
242
|
+
}
|
|
243
|
+
return handoffs.sort((left, right) => left.createdAt.localeCompare(right.createdAt) || left.handoffId.localeCompare(right.handoffId));
|
|
244
|
+
});
|
|
233
245
|
}
|
|
234
246
|
|
|
235
247
|
export async function resolveLatestHandoff(target, { taskId, packageRoot = getPackageRoot() } = {}) {
|
package/src/core/history.js
CHANGED
|
@@ -10,46 +10,50 @@ export const HISTORY_FILTER_OPTIONS = Object.freeze([
|
|
|
10
10
|
"limit",
|
|
11
11
|
]);
|
|
12
12
|
|
|
13
|
+
function historyEventProjection(filters) {
|
|
14
|
+
const predicates = [];
|
|
15
|
+
for (const key of ["type", "phase"]) {
|
|
16
|
+
if (!filters[key]) continue;
|
|
17
|
+
const values = String(filters[key]).split(",").map(value => value.trim()).filter(Boolean);
|
|
18
|
+
if (values.length) predicates.push(event => values.includes(event[key])
|
|
19
|
+
|| (key === "type" && values.includes(event.category)));
|
|
20
|
+
}
|
|
21
|
+
if (filters.failures) predicates.push(event => event.category === "verification"
|
|
22
|
+
&& ["failed", "blocked"].includes(String(event.data?.status ?? "")));
|
|
23
|
+
if (filters.checks) predicates.push(event => event.category === "verification");
|
|
24
|
+
for (const key of ["since", "until"]) {
|
|
25
|
+
if (!filters[key]) continue;
|
|
26
|
+
const bound = Date.parse(filters[key]);
|
|
27
|
+
if (!Number.isNaN(bound)) predicates.push(event => event.timestamp
|
|
28
|
+
&& (key === "since" ? Date.parse(event.timestamp) >= bound : Date.parse(event.timestamp) <= bound));
|
|
29
|
+
}
|
|
30
|
+
const limit = Number.isInteger(filters.limit) && filters.limit >= 0 ? filters.limit : null;
|
|
31
|
+
const events = [];
|
|
32
|
+
let matched = 0;
|
|
33
|
+
let cursor = 0;
|
|
34
|
+
return {
|
|
35
|
+
add(event) {
|
|
36
|
+
if (!predicates.every(predicate => predicate(event))) return;
|
|
37
|
+
matched += 1;
|
|
38
|
+
if (limit === 0) return;
|
|
39
|
+
if (limit === null || events.length < limit) events.push(event);
|
|
40
|
+
else { events[cursor] = event; cursor = (cursor + 1) % limit; }
|
|
41
|
+
},
|
|
42
|
+
result: () => cursor === 0 ? events : events.slice(cursor).concat(events.slice(0, cursor)),
|
|
43
|
+
get omittedEvents() { return matched - events.length; },
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
13
47
|
export async function buildTaskHistory({
|
|
14
48
|
target,
|
|
15
49
|
packageRoot,
|
|
16
50
|
taskId = null,
|
|
17
51
|
filters = {},
|
|
18
52
|
} = {}) {
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const types = String(filters.type).split(",").map((value) => value.trim()).filter(Boolean);
|
|
24
|
-
if (types.length > 0) {
|
|
25
|
-
events = events.filter((event) => types.includes(event.type) || types.includes(event.category));
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
if (filters.phase) {
|
|
29
|
-
const phases = String(filters.phase).split(",").map((value) => value.trim()).filter(Boolean);
|
|
30
|
-
if (phases.length > 0) events = events.filter((event) => phases.includes(event.phase));
|
|
31
|
-
}
|
|
32
|
-
if (filters.failures) {
|
|
33
|
-
events = events.filter((event) => event.category === "verification"
|
|
34
|
-
&& ["failed", "blocked"].includes(String(event.data?.status ?? "")));
|
|
35
|
-
}
|
|
36
|
-
if (filters.checks) {
|
|
37
|
-
events = events.filter((event) => event.category === "verification");
|
|
38
|
-
}
|
|
39
|
-
if (filters.since) {
|
|
40
|
-
const since = Date.parse(filters.since);
|
|
41
|
-
if (!Number.isNaN(since)) events = events.filter((event) => event.timestamp && Date.parse(event.timestamp) >= since);
|
|
42
|
-
}
|
|
43
|
-
if (filters.until) {
|
|
44
|
-
const until = Date.parse(filters.until);
|
|
45
|
-
if (!Number.isNaN(until)) events = events.filter((event) => event.timestamp && Date.parse(event.timestamp) <= until);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
let omittedEvents = 0;
|
|
49
|
-
if (Number.isInteger(filters.limit) && filters.limit >= 0 && events.length > filters.limit) {
|
|
50
|
-
omittedEvents = events.length - filters.limit;
|
|
51
|
-
events = events.slice(-filters.limit);
|
|
52
|
-
}
|
|
53
|
+
const projection = historyEventProjection(filters);
|
|
54
|
+
const trace = await buildTaskTrace({ target, packageRoot, taskId }, projection);
|
|
55
|
+
const events = trace.events;
|
|
56
|
+
const omittedEvents = projection.omittedEvents;
|
|
53
57
|
|
|
54
58
|
const checkAttempts = trace.checks.reduce(
|
|
55
59
|
(total, check) => total + check.attemptCount,
|
|
@@ -67,7 +71,7 @@ export async function buildTaskHistory({
|
|
|
67
71
|
snapshot: trace.snapshot,
|
|
68
72
|
summary: {
|
|
69
73
|
eventCount: events.length,
|
|
70
|
-
totalEventCount: trace.
|
|
74
|
+
totalEventCount: trace.totalEventCount,
|
|
71
75
|
checkAttemptCount: checkAttempts,
|
|
72
76
|
failedAttemptCount: failedAttempts,
|
|
73
77
|
diagnosticCaseCount: trace.diagnostics.cases.length,
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { isLedgerEventCollection, ledgerEventsOfTypes } from "./ledger-event-collection.js";
|
|
2
|
+
import { ledgerRelationMap } from "./ledger-relations.js";
|
|
1
3
|
import { DISPOSITION_TRANSITIONS } from "./diagnostic-model.js";
|
|
2
4
|
|
|
3
5
|
const TERMINAL_STATUSES = Object.freeze(["FALSIFIED", "SUPERSEDED", "UNRESOLVED"]);
|
|
@@ -83,3 +85,33 @@ export function getOpenHypotheses(projection) {
|
|
|
83
85
|
export function getHypothesisState(projection, id) {
|
|
84
86
|
return projection?.hypotheses.find((hypothesis) => hypothesis.id === id) ?? null;
|
|
85
87
|
}
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
/** Internal context needs current IDs, not every historical disposition payload. */
|
|
91
|
+
export function summarizeOpenHypotheses(events, taskId = null) {
|
|
92
|
+
if (Array.isArray(events)) return projectHypothesisStates(events.filter(event => !taskId || event.taskId === taskId)).openHypotheses;
|
|
93
|
+
if (!isLedgerEventCollection(events)) throw new TypeError("Hypothesis context requires a ledger collection");
|
|
94
|
+
let previous = -Infinity;
|
|
95
|
+
for (const event of events) {
|
|
96
|
+
if (!Number.isFinite(event.seq) || event.seq < previous) {
|
|
97
|
+
return projectHypothesisStates([...events].filter(candidate => !taskId || candidate.taskId === taskId)).openHypotheses;
|
|
98
|
+
}
|
|
99
|
+
previous = event.seq;
|
|
100
|
+
}
|
|
101
|
+
const statuses = ledgerRelationMap();
|
|
102
|
+
const ensure = id => { if (!statuses.has(id)) statuses.set(id, "OPEN"); };
|
|
103
|
+
for (const event of ledgerEventsOfTypes(events, ["DIAGNOSTIC_CASE_RECORDED", "DIAGNOSIS_RECORDED", "HYPOTHESIS_DISPOSITION_RECORDED"])) {
|
|
104
|
+
if (taskId && event.taskId !== taskId) continue;
|
|
105
|
+
const details = event.details ?? {};
|
|
106
|
+
if (event.event === "DIAGNOSTIC_CASE_RECORDED") {
|
|
107
|
+
for (const hypothesis of details.hypotheses ?? []) if (hypothesis?.id) ensure(hypothesis.id);
|
|
108
|
+
} else if (event.event === "DIAGNOSIS_RECORDED") ensure(HYPOTHESIS_LEGACY_ID);
|
|
109
|
+
else if (details.hypothesisRef && details.status) {
|
|
110
|
+
ensure(details.hypothesisRef);
|
|
111
|
+
if (canTransition(statuses.get(details.hypothesisRef), details.status)) statuses.set(details.hypothesisRef, details.status);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
const open = [];
|
|
115
|
+
for (const [id, status] of statuses) if (status === "OPEN") open.push(id);
|
|
116
|
+
return open.sort();
|
|
117
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { ledgerRelationSet, ledgerGroupedSet } from "./ledger-relations.js";
|
|
2
|
+
import { isLedgerEventCollection, ledgerEventsOfTypes } from "./ledger-event-collection.js";
|
|
3
|
+
import { normalizeDiagnosticSnapshot, compareDiagnosticCycles, classifyGain, isEffectiveGain } from "./information-gain.js";
|
|
3
4
|
import { computeFailureSignature } from "./failure-signature.js";
|
|
4
5
|
|
|
5
6
|
function snapshotFor(event) {
|
|
@@ -19,23 +20,43 @@ const sameSortedSet = (a, b) =>
|
|
|
19
20
|
// current diagnostic sequence inclusive.
|
|
20
21
|
// Events inside an interval belong to the *current* cycle's knowledge state;
|
|
21
22
|
// they are never attributed retroactively to the earlier diagnosis.
|
|
22
|
-
function intervalEvents(taskEvents, fromExclusive, toInclusive) {
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
function* intervalEvents(taskEvents, fromExclusive, toInclusive) {
|
|
24
|
+
for (const event of taskEvents) if (event.seq > fromExclusive && event.seq <= toInclusive) yield event;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function orderedTaskEvents(events, taskId) {
|
|
28
|
+
const belongs = event => !taskId || !event.taskId || event.taskId === taskId;
|
|
29
|
+
if (!Array.isArray(events) && isLedgerEventCollection(events)) {
|
|
30
|
+
let previous = -Infinity;
|
|
31
|
+
let ordered = true;
|
|
32
|
+
for (const event of events) {
|
|
33
|
+
if (!Number.isFinite(event.seq) || event.seq < previous) { ordered = false; break; }
|
|
34
|
+
previous = event.seq;
|
|
35
|
+
}
|
|
36
|
+
if (ordered) return { *[Symbol.iterator]() { for (const event of ledgerEventsOfTypes(events, ["DIAGNOSTIC_CASE_RECORDED", "DIAGNOSIS_RECORDED", "VERIFICATION_STARTED", "VERIFICATION_RECORDED", "INTERVENTION_RECORDED", "HYPOTHESIS_DISPOSITION_RECORDED"])) if (belongs(event)) yield event; } };
|
|
37
|
+
}
|
|
38
|
+
return [...(events ?? [])].filter(belongs).sort((a, b) => (a.seq ?? 0) - (b.seq ?? 0));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function eliminatedHypothesis(previousDiagnostic, diagnostic) {
|
|
42
|
+
if (previousDiagnostic?.event !== "DIAGNOSTIC_CASE_RECORDED" || diagnostic.event !== "DIAGNOSTIC_CASE_RECORDED") return false;
|
|
43
|
+
const previous = previousDiagnostic.details?.hypotheses ?? [];
|
|
44
|
+
const current = diagnostic.details?.hypotheses ?? [];
|
|
45
|
+
const statements = new Set(current.map(hypothesis => `${hypothesis.statement}`.trim().toLowerCase()));
|
|
46
|
+
return previous.some(hypothesis => !current.some(candidate => candidate.id === hypothesis.id)
|
|
47
|
+
&& !statements.has(`${hypothesis.statement}`.trim().toLowerCase()));
|
|
25
48
|
}
|
|
26
49
|
|
|
27
50
|
function failureStateByCycle(taskEvents) {
|
|
28
|
-
const surfaces =
|
|
29
|
-
const signatures =
|
|
51
|
+
const surfaces = ledgerGroupedSet();
|
|
52
|
+
const signatures = ledgerGroupedSet();
|
|
30
53
|
const record = (cycle, details) => {
|
|
31
54
|
if (!Number.isInteger(cycle)) cycle = Number(cycle) || 1;
|
|
32
|
-
if (!surfaces.has(cycle)) surfaces.set(cycle, new Set());
|
|
33
|
-
if (!signatures.has(cycle)) signatures.set(cycle, new Set());
|
|
34
55
|
const requirement = details.requirement ?? details.id ?? details.checkId;
|
|
35
56
|
if (!requirement) return;
|
|
36
57
|
if (details.status === "failed" || details.status === "blocked") {
|
|
37
|
-
surfaces.
|
|
38
|
-
signatures.
|
|
58
|
+
surfaces.add(cycle, requirement);
|
|
59
|
+
signatures.add(cycle, computeFailureSignature({
|
|
39
60
|
requirement,
|
|
40
61
|
status: details.status,
|
|
41
62
|
exitCode: Number.isInteger(details.exitCode) ? details.exitCode : null,
|
|
@@ -44,13 +65,6 @@ function failureStateByCycle(taskEvents) {
|
|
|
44
65
|
}
|
|
45
66
|
};
|
|
46
67
|
for (const event of taskEvents) {
|
|
47
|
-
if (event.event === "VERIFICATION_STARTED") {
|
|
48
|
-
const cycle = event.details?.verificationCycle;
|
|
49
|
-
if (Number.isInteger(cycle)) {
|
|
50
|
-
if (!surfaces.has(cycle)) surfaces.set(cycle, new Set());
|
|
51
|
-
if (!signatures.has(cycle)) signatures.set(cycle, new Set());
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
68
|
if (event.event === "VERIFICATION_RECORDED") {
|
|
55
69
|
record(event.details?.verificationCycle ?? 1, event.details ?? {});
|
|
56
70
|
}
|
|
@@ -102,46 +116,45 @@ function snapshotHasContentDelta(previousDetails, currentDetails) {
|
|
|
102
116
|
|| differs(prev.evidence, cur.evidence);
|
|
103
117
|
}
|
|
104
118
|
|
|
105
|
-
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
119
|
+
function intervalKnowledge(taskEvents, previousDiagnostic, diagnostic) {
|
|
120
|
+
const intervalStart = previousDiagnostic ? previousDiagnostic.seq : -Infinity;
|
|
121
|
+
let hypothesisDispositionChanged = false;
|
|
122
|
+
const knownUpToPrev = ledgerRelationSet();
|
|
123
|
+
for (const event of taskEvents) {
|
|
124
|
+
if (event.event === "INTERVENTION_RECORDED" && previousDiagnostic && (event.seq ?? 0) <= previousDiagnostic.seq) {
|
|
125
|
+
const fingerprint = event.details?.interventionSemanticFingerprint
|
|
126
|
+
?? `${event.details?.intervention?.statement ?? ""}`.trim().toLowerCase();
|
|
127
|
+
if (fingerprint) knownUpToPrev.add(fingerprint);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
let novelIntervention = false;
|
|
131
|
+
for (const event of intervalEvents(taskEvents, intervalStart, diagnostic.seq)) {
|
|
132
|
+
if (event.event === "HYPOTHESIS_DISPOSITION_RECORDED") hypothesisDispositionChanged = true;
|
|
133
|
+
if (event.event === "INTERVENTION_RECORDED") {
|
|
134
|
+
const fingerprint = event.details?.interventionSemanticFingerprint
|
|
135
|
+
?? `${event.details?.intervention?.statement ?? ""}`.trim().toLowerCase();
|
|
136
|
+
if (fingerprint && !knownUpToPrev.has(fingerprint)) novelIntervention = true;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return { hypothesisDispositionChanged, novelIntervention };
|
|
140
|
+
}
|
|
109
141
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
142
|
+
function* iterateInformationGainProjection(events, taskId) {
|
|
143
|
+
const taskEvents = orderedTaskEvents(events, taskId);
|
|
144
|
+
const diagnosticEvents = { *[Symbol.iterator]() {
|
|
145
|
+
for (const event of taskEvents) {
|
|
146
|
+
if (["DIAGNOSTIC_CASE_RECORDED", "DIAGNOSIS_RECORDED"].includes(event.event)
|
|
147
|
+
&& (!taskId || event.taskId === taskId)) yield event;
|
|
148
|
+
}
|
|
149
|
+
} };
|
|
113
150
|
|
|
114
151
|
const failure = failureStateByCycle(taskEvents);
|
|
115
152
|
|
|
116
|
-
// Build final per-cycle entries first; effectiveGain is computed once at the
|
|
117
|
-
// end from fully final dimensions (no post-mutation anywhere).
|
|
118
|
-
const built = [];
|
|
119
153
|
let previousDiagnostic = null;
|
|
154
|
+
let previousSnapshot = null;
|
|
120
155
|
for (const diagnostic of diagnosticEvents) {
|
|
121
|
-
const
|
|
122
|
-
const interval = intervalEvents(taskEvents, intervalStart, diagnostic.seq);
|
|
123
|
-
|
|
124
|
-
const hypothesisDispositionChanged =
|
|
125
|
-
interval.some((event) => event.event === "HYPOTHESIS_DISPOSITION_RECORDED");
|
|
156
|
+
const { hypothesisDispositionChanged, novelIntervention } = intervalKnowledge(taskEvents, previousDiagnostic, diagnostic);
|
|
126
157
|
|
|
127
|
-
// Intervention deltas recognize only genuinely NEW semantic interventions:
|
|
128
|
-
// recorded after the previous diagnosis, attributed to the current
|
|
129
|
-
// correction cycle, and never attempted before. Repeating an already-known
|
|
130
|
-
// intervention — or merely having executed the previous cycle's corrective
|
|
131
|
-
// action — is not new information.
|
|
132
|
-
const knownUpToPrev = new Set(
|
|
133
|
-
taskEvents
|
|
134
|
-
.filter((event) => event.event === "INTERVENTION_RECORDED"
|
|
135
|
-
&& (previousDiagnostic ? (event.seq ?? 0) <= previousDiagnostic.seq : false))
|
|
136
|
-
.map((event) => event.details?.interventionSemanticFingerprint
|
|
137
|
-
?? `${event.details?.intervention?.statement ?? ""}`.trim().toLowerCase())
|
|
138
|
-
.filter(Boolean),
|
|
139
|
-
);
|
|
140
|
-
const novelInInterval = interval
|
|
141
|
-
.filter((event) => event.event === "INTERVENTION_RECORDED")
|
|
142
|
-
.map((event) => event.details?.interventionSemanticFingerprint
|
|
143
|
-
?? `${event.details?.intervention?.statement ?? ""}`.trim().toLowerCase())
|
|
144
|
-
.filter((fingerprint) => fingerprint && !knownUpToPrev.has(fingerprint));
|
|
145
158
|
// A genuinely new intervention counts as information only when the
|
|
146
159
|
// diagnosis itself moves: an identical re-proposal of the previous
|
|
147
160
|
// diagnosis after executing its already-known corrective action carries
|
|
@@ -149,18 +162,18 @@ export function buildInformationGainProjection(events, taskId) {
|
|
|
149
162
|
const sameSemanticsAsPrevious = Boolean(previousDiagnostic)
|
|
150
163
|
&& !snapshotHasContentDelta(previousDiagnostic.details ?? {}, diagnostic.details ?? {});
|
|
151
164
|
const interventionChanged = Boolean(previousDiagnostic)
|
|
152
|
-
&&
|
|
165
|
+
&& novelIntervention
|
|
153
166
|
&& !sameSemanticsAsPrevious;
|
|
154
167
|
|
|
155
168
|
const cycle = diagnostic.details?.verificationCycle ?? 1;
|
|
156
169
|
const previousCycle = previousDiagnostic?.details?.verificationCycle ?? null;
|
|
157
|
-
const surface = [...(failure.surfaces.
|
|
158
|
-
const signatures = [...(failure.signatures.
|
|
170
|
+
const surface = [...(failure.surfaces.values(cycle))].sort();
|
|
171
|
+
const signatures = [...(failure.signatures.values(cycle))].sort();
|
|
159
172
|
const previousSurface = previousCycle !== null && previousCycle !== undefined
|
|
160
|
-
? [...(failure.surfaces.
|
|
173
|
+
? [...(failure.surfaces.values(previousCycle))].sort()
|
|
161
174
|
: null;
|
|
162
175
|
const previousSignatures = previousCycle !== null && previousCycle !== undefined
|
|
163
|
-
? [...(failure.signatures.
|
|
176
|
+
? [...(failure.signatures.values(previousCycle))].sort()
|
|
164
177
|
: null;
|
|
165
178
|
const hasPreviousFailureState = previousSurface !== null;
|
|
166
179
|
const failureSurfaceChanged = hasPreviousFailureState
|
|
@@ -181,7 +194,7 @@ export function buildInformationGainProjection(events, taskId) {
|
|
|
181
194
|
|
|
182
195
|
const snapshot = snapshotFor(diagnostic);
|
|
183
196
|
|
|
184
|
-
|
|
197
|
+
const entry = {
|
|
185
198
|
verificationCycle: cycle,
|
|
186
199
|
sequence: diagnostic.seq ?? null,
|
|
187
200
|
diagnosticSequence: diagnostic.seq ?? null,
|
|
@@ -195,62 +208,53 @@ export function buildInformationGainProjection(events, taskId) {
|
|
|
195
208
|
failureSurfaceChanged,
|
|
196
209
|
interventionChanged,
|
|
197
210
|
strategyChanged,
|
|
198
|
-
hypothesisEliminated:
|
|
211
|
+
hypothesisEliminated: eliminatedHypothesis(previousDiagnostic, diagnostic),
|
|
199
212
|
},
|
|
200
213
|
evidence: {
|
|
201
214
|
semanticRefs: [...(snapshot.evidenceRefs ?? [])].sort(),
|
|
202
215
|
surface, signatures, strategyFingerprint,
|
|
203
216
|
},
|
|
204
|
-
}
|
|
205
|
-
|
|
217
|
+
};
|
|
218
|
+
const dimensions = compareDiagnosticCycles(previousSnapshot, snapshot, entry.dimensionsInput);
|
|
219
|
+
const classification = classifyGain(dimensions, { first: previousDiagnostic === null });
|
|
220
|
+
const effectiveGain = isEffectiveGain(dimensions, classification);
|
|
221
|
+
yield finalizeGainEntry(entry, { dimensions, classification, effectiveGain });
|
|
206
222
|
previousDiagnostic = diagnostic;
|
|
223
|
+
previousSnapshot = snapshot;
|
|
207
224
|
}
|
|
225
|
+
}
|
|
208
226
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
// churn is artificial novelty and must never create gain.
|
|
212
|
-
for (let i = 1; i < built.length; i++) {
|
|
213
|
-
if (built[i].sourceModel !== "STRUCTURED_DIAGNOSTIC_CASE_V1"
|
|
214
|
-
|| built[i - 1].sourceModel !== "STRUCTURED_DIAGNOSTIC_CASE_V1") continue;
|
|
215
|
-
const previousHypotheses = diagnosticEvents[i - 1]?.details?.hypotheses ?? [];
|
|
216
|
-
const currentHypotheses = diagnosticEvents[i]?.details?.hypotheses ?? [];
|
|
217
|
-
const currentStatements = new Set(currentHypotheses.map(
|
|
218
|
-
(hypothesis) => `${hypothesis.statement}`.trim().toLowerCase()));
|
|
219
|
-
built[i].dimensionsInput.hypothesisEliminated = previousHypotheses.some((hypothesis) => {
|
|
220
|
-
const survivedById = currentHypotheses.some((candidate) => candidate.id === hypothesis.id);
|
|
221
|
-
return !survivedById
|
|
222
|
-
&& !currentStatements.has(`${hypothesis.statement}`.trim().toLowerCase());
|
|
223
|
-
});
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
// Final classification + single-point effectiveGain computation.
|
|
227
|
-
const entries = computeInformationGain(built.map((entry) => ({
|
|
227
|
+
function finalizeGainEntry(entry, { dimensions, classification, effectiveGain }) {
|
|
228
|
+
return Object.freeze({
|
|
228
229
|
verificationCycle: entry.verificationCycle,
|
|
229
230
|
sequence: entry.sequence,
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
evidence: Object.freeze({
|
|
242
|
-
semanticRefs: Object.freeze(entry.evidence.semanticRefs),
|
|
243
|
-
failureSurface: Object.freeze(entry.evidence.surface),
|
|
244
|
-
failureSignatures: Object.freeze(entry.evidence.signatures),
|
|
245
|
-
strategyFingerprint: entry.evidence.strategyFingerprint,
|
|
246
|
-
}),
|
|
247
|
-
dimensions: Object.freeze({ ...dimensions }),
|
|
248
|
-
classification,
|
|
249
|
-
effectiveGain,
|
|
250
|
-
});
|
|
231
|
+
diagnosticSequence: entry.diagnosticSequence,
|
|
232
|
+
sourceModel: entry.sourceModel,
|
|
233
|
+
evidence: Object.freeze({
|
|
234
|
+
semanticRefs: Object.freeze(entry.evidence.semanticRefs),
|
|
235
|
+
failureSurface: Object.freeze(entry.evidence.surface),
|
|
236
|
+
failureSignatures: Object.freeze(entry.evidence.signatures),
|
|
237
|
+
strategyFingerprint: entry.evidence.strategyFingerprint,
|
|
238
|
+
}),
|
|
239
|
+
dimensions: Object.freeze({ ...dimensions }),
|
|
240
|
+
classification,
|
|
241
|
+
effectiveGain,
|
|
251
242
|
});
|
|
252
243
|
}
|
|
253
244
|
|
|
245
|
+
export function buildInformationGainProjection(events, taskId) {
|
|
246
|
+
return [...iterateInformationGainProjection(events, taskId)];
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/** Internal latest-only use does not retain all diagnostic result objects. */
|
|
250
|
+
export function summarizeInformationGain(events, taskId, { verificationCycle = null } = {}) {
|
|
251
|
+
let latest = null;
|
|
252
|
+
for (const entry of iterateInformationGainProjection(events, taskId)) {
|
|
253
|
+
if (verificationCycle === null || verificationCycle === undefined || entry.verificationCycle === verificationCycle) latest = entry;
|
|
254
|
+
}
|
|
255
|
+
return evaluateStructuredDiagnosticStall(latest ? [latest] : []);
|
|
256
|
+
}
|
|
257
|
+
|
|
254
258
|
// One canonical structured-stall policy (fail-fast):
|
|
255
259
|
// The latest comparable diagnostic state that produces no effective
|
|
256
260
|
// information gain is stalled and may not trigger another blind correction
|
package/src/core/inspect.js
CHANGED
|
@@ -17,6 +17,8 @@ import { evaluateProgress } from "./progress.js";
|
|
|
17
17
|
import { readEvents } from "./events.js";
|
|
18
18
|
import { taskStructuralQualityDirectory } from "./task-paths.js";
|
|
19
19
|
import { projectStructuralQualityStatus } from "./structural-quality/status.js";
|
|
20
|
+
import { operationalArtifactExists } from "../storage/operational-context.js";
|
|
21
|
+
import { withProjectReadSnapshot } from "../storage/project-read-snapshot.js";
|
|
20
22
|
|
|
21
23
|
function reasonCodesFor({ state, trace, progress }) {
|
|
22
24
|
const codes = [];
|
|
@@ -169,7 +171,15 @@ function profileMetadata(bytes) {
|
|
|
169
171
|
|
|
170
172
|
import { taskArtifactPath } from "./task-paths.js";
|
|
171
173
|
|
|
172
|
-
|
|
174
|
+
async function inspectStatePresence(target, relativePath) {
|
|
175
|
+
return operationalArtifactExists(target, relativePath) ?? await fileExists(ensureWithin(target, relativePath));
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export async function inspectTarget(options = {}) {
|
|
179
|
+
return withProjectReadSnapshot(options.target, () => inspectSelectedTarget(options));
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
async function inspectSelectedTarget({ target, packageRoot, contractFile = null, authorityContext, runtimeContext, taskId = null, stateFile = null } = {}) {
|
|
173
183
|
let manifest = null;
|
|
174
184
|
let manifestError = null;
|
|
175
185
|
try {
|
|
@@ -184,8 +194,7 @@ export async function inspectTarget({ target, packageRoot, contractFile = null,
|
|
|
184
194
|
? { ...profileMetadata(await readBytes(profilePath)), path: profileRelativePath }
|
|
185
195
|
: { mode: null, status: null };
|
|
186
196
|
const effectiveStateRel = stateFile ?? (taskId ? taskArtifactPath(taskId, "state") : WORK_STATE_PATH);
|
|
187
|
-
const
|
|
188
|
-
const statePresent = await fileExists(statePath);
|
|
197
|
+
const statePresent = await inspectStatePresence(target, effectiveStateRel);
|
|
189
198
|
const classifiedState = await readAndClassifyWorkState({ target, packageRoot, contractFile, taskId, stateFile: effectiveStateRel });
|
|
190
199
|
const rawState = classifiedState?.state ?? null;
|
|
191
200
|
const taskInfo = taskId ? await findTaskById(target, taskId, packageRoot) : null;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getStorageCapabilities } from "../storage/capabilities.js";
|
|
1
2
|
import { CLI_COMMAND_DEFINITIONS } from "./cli-command-definitions.js";
|
|
2
3
|
import { COMMAND_EXECUTORS } from "./command-executors.js";
|
|
3
4
|
import { PROTOCOL_VERSION } from "./protocol.js";
|
|
@@ -24,7 +25,8 @@ export const INTEGRATION_RISK_CLASSES = Object.freeze({
|
|
|
24
25
|
});
|
|
25
26
|
|
|
26
27
|
const READ_ONLY_COMMANDS = Object.freeze(new Set([
|
|
27
|
-
"
|
|
28
|
+
"storage-migration-status",
|
|
29
|
+
"protocol-info", "decision-status", "decision-show", "context-plan", "model-route", "semantic-plan", "test-inventory", "status", "next", "continuity", "reconcile-continuity",
|
|
28
30
|
"task-list", "task-show", "task-lock-status", "progress", "audit", "report",
|
|
29
31
|
"inspect", "validate-state", "validate-protocol", "validate-receipt",
|
|
30
32
|
"policy-status", "policy-diff", "rule-verify", "policy",
|
|
@@ -37,7 +39,7 @@ const READ_ONLY_COMMANDS = Object.freeze(new Set([
|
|
|
37
39
|
]));
|
|
38
40
|
|
|
39
41
|
const LOOP_MUTATION_COMMANDS = Object.freeze(new Set([
|
|
40
|
-
"route", "preflight", "advance", "task-create", "task-scope",
|
|
42
|
+
"discover", "contract-create", "route", "preflight", "advance", "task-create", "task-scope",
|
|
41
43
|
"record-continuity", "clear-continuity", "prepare-completion",
|
|
42
44
|
"record-check", "record-diagnosis", "record-decision-criterion",
|
|
43
45
|
"record-terminal-result", "complete",
|
|
@@ -69,6 +71,13 @@ const STATIC_RISK_CLASSES = Object.freeze({
|
|
|
69
71
|
"usage-record": INTEGRATION_RISK_CLASSES.LOOP_MUTATION,
|
|
70
72
|
init: INTEGRATION_RISK_CLASSES.MAINTENANCE,
|
|
71
73
|
update: INTEGRATION_RISK_CLASSES.MAINTENANCE,
|
|
74
|
+
"storage-migration-resume": INTEGRATION_RISK_CLASSES.MAINTENANCE,
|
|
75
|
+
"storage-rollback-resume": INTEGRATION_RISK_CLASSES.MAINTENANCE,
|
|
76
|
+
"storage-rollback": INTEGRATION_RISK_CLASSES.MAINTENANCE,
|
|
77
|
+
"storage-migrate": INTEGRATION_RISK_CLASSES.MAINTENANCE,
|
|
78
|
+
"storage-backup": INTEGRATION_RISK_CLASSES.MAINTENANCE,
|
|
79
|
+
"storage-restore": INTEGRATION_RISK_CLASSES.MAINTENANCE,
|
|
80
|
+
"storage-restore-resume": INTEGRATION_RISK_CLASSES.MAINTENANCE,
|
|
72
81
|
activate: INTEGRATION_RISK_CLASSES.MAINTENANCE,
|
|
73
82
|
"task-migrate": INTEGRATION_RISK_CLASSES.MAINTENANCE,
|
|
74
83
|
"migrate-protocol": INTEGRATION_RISK_CLASSES.MAINTENANCE,
|
|
@@ -86,7 +95,17 @@ const STATIC_RISK_CLASSES = Object.freeze({
|
|
|
86
95
|
bundle: INTEGRATION_RISK_CLASSES.MAINTENANCE,
|
|
87
96
|
"profile-interview": INTEGRATION_RISK_CLASSES.MAINTENANCE,
|
|
88
97
|
"task-recover": INTEGRATION_RISK_CLASSES.CLAIM_RELEASE_RECOVERY,
|
|
98
|
+
"task-abandon": INTEGRATION_RISK_CLASSES.CLAIM_RELEASE_RECOVERY,
|
|
89
99
|
"task-repair-legacy-recovery": INTEGRATION_RISK_CLASSES.LEGACY_MIGRATION,
|
|
100
|
+
"gate-record": INTEGRATION_RISK_CLASSES.MAINTENANCE,
|
|
101
|
+
"gate-revalidate": INTEGRATION_RISK_CLASSES.MAINTENANCE,
|
|
102
|
+
"test-utility": INTEGRATION_RISK_CLASSES.MAINTENANCE,
|
|
103
|
+
"test-prune-plan": INTEGRATION_RISK_CLASSES.READ_ONLY,
|
|
104
|
+
"test-prune-probe": INTEGRATION_RISK_CLASSES.EXTERNAL_EXECUTION,
|
|
105
|
+
"task-repair-contract-bootstrap": INTEGRATION_RISK_CLASSES.LEGACY_MIGRATION,
|
|
106
|
+
"task-migrate-contract-bootstrap-repair": INTEGRATION_RISK_CLASSES.LEGACY_MIGRATION,
|
|
107
|
+
"checkpoint-revalidate": INTEGRATION_RISK_CLASSES.MAINTENANCE,
|
|
108
|
+
"contract-revise": INTEGRATION_RISK_CLASSES.MAINTENANCE,
|
|
90
109
|
});
|
|
91
110
|
|
|
92
111
|
// Sparse input-dependent refinements over the static table.
|
|
@@ -133,6 +152,7 @@ export function getForgeLoopCapabilities({ packageVersion = null } = {}) {
|
|
|
133
152
|
integrationApiVersion: FORGELOOP_INTEGRATION_RUNTIME_VERSION,
|
|
134
153
|
executorParity: Object.keys(COMMAND_EXECUTORS).length === Object.keys(CLI_COMMAND_DEFINITIONS).length,
|
|
135
154
|
features: {
|
|
155
|
+
operationalStorage: getStorageCapabilities(),
|
|
136
156
|
taskClaimRecovery: {
|
|
137
157
|
version: 1,
|
|
138
158
|
durableRecoveryState: true,
|
|
@@ -169,6 +189,18 @@ export function getForgeLoopCapabilities({ packageVersion = null } = {}) {
|
|
|
169
189
|
commands: ["next", "task-show"],
|
|
170
190
|
preservesDefaultOutput: true,
|
|
171
191
|
},
|
|
192
|
+
auditUx: {
|
|
193
|
+
version: 1,
|
|
194
|
+
supported: true,
|
|
195
|
+
readOnly: true,
|
|
196
|
+
resource: "task/audit-view",
|
|
197
|
+
timeline: true,
|
|
198
|
+
lifecycleAuthority: false,
|
|
199
|
+
evidenceAuthority: false,
|
|
200
|
+
completionAuthority: false,
|
|
201
|
+
mutationAuthority: false,
|
|
202
|
+
externalExecution: false,
|
|
203
|
+
},
|
|
172
204
|
usageTelemetry: {
|
|
173
205
|
version: 1,
|
|
174
206
|
supported: true,
|
|
@@ -295,6 +327,7 @@ export function getForgeLoopCapabilities({ packageVersion = null } = {}) {
|
|
|
295
327
|
{ name: "task/approvals", scope: "TASK" },
|
|
296
328
|
{ name: "task/metrics", scope: "TASK" },
|
|
297
329
|
{ name: "task/context", scope: "TASK" },
|
|
330
|
+
{ name: "task/audit-view", scope: "TASK" },
|
|
298
331
|
{ name: "task/evaluations", scope: "TASK" },
|
|
299
332
|
{ name: "project/capability-policy", scope: "PROJECT" },
|
|
300
333
|
{ name: "repository/index-status", scope: "PROJECT" },
|
|
@@ -352,8 +385,8 @@ export function classifyForgeLoopInvocation(command, input = {}) {
|
|
|
352
385
|
removesArtifacts: definition.removes.length > 0,
|
|
353
386
|
executesExternalProcess: definition.mayExecuteExternalProcess === true,
|
|
354
387
|
affectsClaimAuthority: [
|
|
355
|
-
"task-resume", "task-recover", "task-repair-legacy-recovery",
|
|
356
|
-
"task-create", "task-scope", "complete",
|
|
388
|
+
"task-resume", "task-recover", "task-repair-legacy-recovery", "task-repair-contract-bootstrap",
|
|
389
|
+
"task-create", "task-scope", "complete", "task-migrate-contract-bootstrap-repair",
|
|
357
390
|
].includes(command),
|
|
358
391
|
destructive: [
|
|
359
392
|
INTEGRATION_RISK_CLASSES.AUTHORITY_MUTATION,
|