@cleocode/core 2026.10.27 → 2026.11.1-canary.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/binaries/README.md +30 -10
- package/dist/.source-stamp.json +4 -0
- package/dist/audit/reconstruct.d.ts.map +1 -1
- package/dist/audit/reconstruct.js +3 -13
- package/dist/audit/reconstruct.js.map +1 -1
- package/dist/build-provenance.json +2 -2
- package/dist/cloud/nexus-cloud.d.ts.map +1 -1
- package/dist/cloud/nexus-cloud.js +3 -3
- package/dist/cloud/nexus-cloud.js.map +1 -1
- package/dist/cloud/nexus-device.d.ts +1 -1
- package/dist/cloud/nexus-link.d.ts +18 -2
- package/dist/cloud/nexus-link.d.ts.map +1 -1
- package/dist/cloud/nexus-link.js +26 -6
- package/dist/cloud/nexus-link.js.map +1 -1
- package/dist/cloud/nexus-vault.d.ts.map +1 -1
- package/dist/cloud/nexus-vault.js +3 -9
- package/dist/cloud/nexus-vault.js.map +1 -1
- package/dist/decide/sites/registry.d.ts +40 -0
- package/dist/decide/sites/registry.d.ts.map +1 -1
- package/dist/decide/sites/registry.js +38 -0
- package/dist/decide/sites/registry.js.map +1 -1
- package/dist/docs/adr-allocator.d.ts +3 -3
- package/dist/docs/adr-allocator.d.ts.map +1 -1
- package/dist/docs/adr-allocator.js +8 -5
- package/dist/docs/adr-allocator.js.map +1 -1
- package/dist/docs/blob-consolidation.d.ts.map +1 -1
- package/dist/docs/blob-consolidation.js +3 -2
- package/dist/docs/blob-consolidation.js.map +1 -1
- package/dist/docs/derive-links.d.ts +12 -0
- package/dist/docs/derive-links.d.ts.map +1 -1
- package/dist/docs/derive-links.js +19 -0
- package/dist/docs/derive-links.js.map +1 -1
- package/dist/docs/doc-name-rule.d.ts +105 -0
- package/dist/docs/doc-name-rule.d.ts.map +1 -0
- package/dist/docs/doc-name-rule.js +212 -0
- package/dist/docs/doc-name-rule.js.map +1 -0
- package/dist/docs/docs-inconsistency-detector.d.ts.map +1 -1
- package/dist/docs/docs-inconsistency-detector.js +4 -3
- package/dist/docs/docs-inconsistency-detector.js.map +1 -1
- package/dist/docs/docs-ops.d.ts +27 -5
- package/dist/docs/docs-ops.d.ts.map +1 -1
- package/dist/docs/docs-ops.js +149 -17
- package/dist/docs/docs-ops.js.map +1 -1
- package/dist/docs/docs-read-model.d.ts +45 -15
- package/dist/docs/docs-read-model.d.ts.map +1 -1
- package/dist/docs/docs-read-model.js +22 -102
- package/dist/docs/docs-read-model.js.map +1 -1
- package/dist/docs/docs-update.d.ts.map +1 -1
- package/dist/docs/docs-update.js +8 -2
- package/dist/docs/docs-update.js.map +1 -1
- package/dist/docs/doctor.d.ts +71 -4
- package/dist/docs/doctor.d.ts.map +1 -1
- package/dist/docs/doctor.js +324 -23
- package/dist/docs/doctor.js.map +1 -1
- package/dist/docs/duplicate-check.d.ts +138 -0
- package/dist/docs/duplicate-check.d.ts.map +1 -0
- package/dist/docs/duplicate-check.js +286 -0
- package/dist/docs/duplicate-check.js.map +1 -0
- package/dist/docs/fetch-result.d.ts.map +1 -1
- package/dist/docs/fetch-result.js +27 -0
- package/dist/docs/fetch-result.js.map +1 -1
- package/dist/docs/import/scanner.d.ts +2 -0
- package/dist/docs/import/scanner.d.ts.map +1 -1
- package/dist/docs/import/scanner.js +3 -1
- package/dist/docs/import/scanner.js.map +1 -1
- package/dist/docs/lineage.d.ts +66 -0
- package/dist/docs/lineage.d.ts.map +1 -0
- package/dist/docs/lineage.js +190 -0
- package/dist/docs/lineage.js.map +1 -0
- package/dist/docs/modernize/apply.d.ts +139 -0
- package/dist/docs/modernize/apply.d.ts.map +1 -0
- package/dist/docs/modernize/apply.js +760 -0
- package/dist/docs/modernize/apply.js.map +1 -0
- package/dist/docs/modernize/guard.d.ts +92 -0
- package/dist/docs/modernize/guard.d.ts.map +1 -0
- package/dist/docs/modernize/guard.js +214 -0
- package/dist/docs/modernize/guard.js.map +1 -0
- package/dist/docs/modernize/index.d.ts +68 -0
- package/dist/docs/modernize/index.d.ts.map +1 -0
- package/dist/docs/modernize/index.js +97 -0
- package/dist/docs/modernize/index.js.map +1 -0
- package/dist/docs/modernize/judge.d.ts +85 -0
- package/dist/docs/modernize/judge.d.ts.map +1 -0
- package/dist/docs/modernize/judge.js +186 -0
- package/dist/docs/modernize/judge.js.map +1 -0
- package/dist/docs/modernize/plan.d.ts +77 -0
- package/dist/docs/modernize/plan.d.ts.map +1 -0
- package/dist/docs/modernize/plan.js +510 -0
- package/dist/docs/modernize/plan.js.map +1 -0
- package/dist/docs/modernize/restore.d.ts +41 -0
- package/dist/docs/modernize/restore.d.ts.map +1 -0
- package/dist/docs/modernize/restore.js +103 -0
- package/dist/docs/modernize/restore.js.map +1 -0
- package/dist/docs/modernize/rules.d.ts +197 -0
- package/dist/docs/modernize/rules.d.ts.map +1 -0
- package/dist/docs/modernize/rules.js +510 -0
- package/dist/docs/modernize/rules.js.map +1 -0
- package/dist/docs/modernize/store.d.ts +64 -0
- package/dist/docs/modernize/store.d.ts.map +1 -0
- package/dist/docs/modernize/store.js +390 -0
- package/dist/docs/modernize/store.js.map +1 -0
- package/dist/docs/publications-store.d.ts +16 -2
- package/dist/docs/publications-store.d.ts.map +1 -1
- package/dist/docs/publications-store.js +23 -2
- package/dist/docs/publications-store.js.map +1 -1
- package/dist/docs/publish-pr.d.ts.map +1 -1
- package/dist/docs/publish-pr.js +12 -1
- package/dist/docs/publish-pr.js.map +1 -1
- package/dist/docs/rot.d.ts +94 -0
- package/dist/docs/rot.d.ts.map +1 -0
- package/dist/docs/rot.js +298 -0
- package/dist/docs/rot.js.map +1 -0
- package/dist/docs/similarity-check.d.ts +2 -0
- package/dist/docs/similarity-check.d.ts.map +1 -1
- package/dist/docs/similarity-check.js +6 -49
- package/dist/docs/similarity-check.js.map +1 -1
- package/dist/docs/slug-rename.d.ts +84 -0
- package/dist/docs/slug-rename.d.ts.map +1 -0
- package/dist/docs/slug-rename.js +166 -0
- package/dist/docs/slug-rename.js.map +1 -0
- package/dist/docs/slug-similarity.d.ts +24 -0
- package/dist/docs/slug-similarity.d.ts.map +1 -0
- package/dist/docs/slug-similarity.js +63 -0
- package/dist/docs/slug-similarity.js.map +1 -0
- package/dist/docs/wikilinks.d.ts +20 -4
- package/dist/docs/wikilinks.d.ts.map +1 -1
- package/dist/docs/wikilinks.js +52 -7
- package/dist/docs/wikilinks.js.map +1 -1
- package/dist/docs/writer-registry.d.ts.map +1 -1
- package/dist/docs/writer-registry.js +33 -0
- package/dist/docs/writer-registry.js.map +1 -1
- package/dist/doctor/type-matrix-repair.d.ts +194 -0
- package/dist/doctor/type-matrix-repair.d.ts.map +1 -0
- package/dist/doctor/type-matrix-repair.js +662 -0
- package/dist/doctor/type-matrix-repair.js.map +1 -0
- package/dist/gateway-client/generated/index.d.ts +2 -2
- package/dist/gateway-client/generated/index.d.ts.map +1 -1
- package/dist/gateway-client/generated/index.js +1 -1
- package/dist/gateway-client/generated/index.js.map +1 -1
- package/dist/gateway-client/generated/namespaces.gen.d.ts +5 -0
- package/dist/gateway-client/generated/namespaces.gen.d.ts.map +1 -1
- package/dist/gateway-client/generated/namespaces.gen.js +6 -1
- package/dist/gateway-client/generated/namespaces.gen.js.map +1 -1
- package/dist/gateway-client/generated/sdk.gen.d.ts +21 -1
- package/dist/gateway-client/generated/sdk.gen.d.ts.map +1 -1
- package/dist/gateway-client/generated/sdk.gen.js +55 -0
- package/dist/gateway-client/generated/sdk.gen.js.map +1 -1
- package/dist/gateway-client/generated/types.gen.d.ts +222 -0
- package/dist/gateway-client/generated/types.gen.d.ts.map +1 -1
- package/dist/gc/gc-subsystem.d.ts +4 -0
- package/dist/gc/gc-subsystem.d.ts.map +1 -1
- package/dist/gc/gc-subsystem.js +16 -0
- package/dist/gc/gc-subsystem.js.map +1 -1
- package/dist/git/work-tree.d.ts +48 -0
- package/dist/git/work-tree.d.ts.map +1 -1
- package/dist/git/work-tree.js +63 -1
- package/dist/git/work-tree.js.map +1 -1
- package/dist/hooks/handlers/session-hooks.d.ts +5 -4
- package/dist/hooks/handlers/session-hooks.d.ts.map +1 -1
- package/dist/hooks/handlers/session-hooks.js +5 -21
- package/dist/hooks/handlers/session-hooks.js.map +1 -1
- package/dist/internal.d.ts +5 -1
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +5 -1
- package/dist/internal.js.map +1 -1
- package/dist/memory/brain-doctor.d.ts.map +1 -1
- package/dist/memory/brain-doctor.js +6 -1
- package/dist/memory/brain-doctor.js.map +1 -1
- package/dist/memory/engine-compat.d.ts.map +1 -1
- package/dist/memory/engine-compat.js +6 -0
- package/dist/memory/engine-compat.js.map +1 -1
- package/dist/nexus/analyze-orchestrator.d.ts +24 -0
- package/dist/nexus/analyze-orchestrator.d.ts.map +1 -1
- package/dist/nexus/analyze-orchestrator.js +43 -5
- package/dist/nexus/analyze-orchestrator.js.map +1 -1
- package/dist/nexus/user-profile.d.ts +2 -1
- package/dist/nexus/user-profile.d.ts.map +1 -1
- package/dist/nexus/user-profile.js +13 -5
- package/dist/nexus/user-profile.js.map +1 -1
- package/dist/orchestrate/admission.d.ts +8 -0
- package/dist/orchestrate/admission.d.ts.map +1 -1
- package/dist/orchestrate/admission.js +3 -1
- package/dist/orchestrate/admission.js.map +1 -1
- package/dist/orchestrate/query-ops.d.ts.map +1 -1
- package/dist/orchestrate/query-ops.js +15 -4
- package/dist/orchestrate/query-ops.js.map +1 -1
- package/dist/orchestrate/spawn-ops.d.ts.map +1 -1
- package/dist/orchestrate/spawn-ops.js +43 -2
- package/dist/orchestrate/spawn-ops.js.map +1 -1
- package/dist/orchestration/index.d.ts +6 -0
- package/dist/orchestration/index.d.ts.map +1 -1
- package/dist/orchestration/index.js +3 -0
- package/dist/orchestration/index.js.map +1 -1
- package/dist/orchestration/spawn-prompt.d.ts.map +1 -1
- package/dist/orchestration/spawn-prompt.js +3 -1
- package/dist/orchestration/spawn-prompt.js.map +1 -1
- package/dist/paths.d.ts +43 -1
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +51 -1
- package/dist/paths.js.map +1 -1
- package/dist/project-lifecycle.d.ts.map +1 -1
- package/dist/project-lifecycle.js +31 -0
- package/dist/project-lifecycle.js.map +1 -1
- package/dist/release/authorization.d.ts +156 -0
- package/dist/release/authorization.d.ts.map +1 -0
- package/dist/release/authorization.js +460 -0
- package/dist/release/authorization.js.map +1 -0
- package/dist/release/ci-evidence.d.ts +55 -4
- package/dist/release/ci-evidence.d.ts.map +1 -1
- package/dist/release/ci-evidence.js +251 -22
- package/dist/release/ci-evidence.js.map +1 -1
- package/dist/release/ci-run-evidence.d.ts +107 -0
- package/dist/release/ci-run-evidence.d.ts.map +1 -0
- package/dist/release/ci-run-evidence.js +234 -0
- package/dist/release/ci-run-evidence.js.map +1 -0
- package/dist/release/cut.d.ts +231 -0
- package/dist/release/cut.d.ts.map +1 -0
- package/dist/release/cut.js +775 -0
- package/dist/release/cut.js.map +1 -0
- package/dist/release/deploy-evidence.d.ts +173 -0
- package/dist/release/deploy-evidence.d.ts.map +1 -0
- package/dist/release/deploy-evidence.js +381 -0
- package/dist/release/deploy-evidence.js.map +1 -0
- package/dist/release/engine-ops.d.ts.map +1 -1
- package/dist/release/engine-ops.js +10 -4
- package/dist/release/engine-ops.js.map +1 -1
- package/dist/release/index.d.ts +3 -0
- package/dist/release/index.d.ts.map +1 -1
- package/dist/release/index.js +2 -0
- package/dist/release/index.js.map +1 -1
- package/dist/release/invariants/archive-reason-invariant.d.ts.map +1 -1
- package/dist/release/invariants/archive-reason-invariant.js +4 -1
- package/dist/release/invariants/archive-reason-invariant.js.map +1 -1
- package/dist/release/open.d.ts.map +1 -1
- package/dist/release/open.js +2 -6
- package/dist/release/open.js.map +1 -1
- package/dist/release/plan.d.ts +34 -2
- package/dist/release/plan.d.ts.map +1 -1
- package/dist/release/plan.js +30 -6
- package/dist/release/plan.js.map +1 -1
- package/dist/release/preflight-skip.d.ts +4 -19
- package/dist/release/preflight-skip.d.ts.map +1 -1
- package/dist/release/preflight-skip.js +5 -110
- package/dist/release/preflight-skip.js.map +1 -1
- package/dist/release/reconcile-latest.d.ts +29 -0
- package/dist/release/reconcile-latest.d.ts.map +1 -0
- package/dist/release/reconcile-latest.js +71 -0
- package/dist/release/reconcile-latest.js.map +1 -0
- package/dist/release/reconcile.d.ts +3 -1
- package/dist/release/reconcile.d.ts.map +1 -1
- package/dist/release/reconcile.js +18 -6
- package/dist/release/reconcile.js.map +1 -1
- package/dist/release/release-published.d.ts +27 -0
- package/dist/release/release-published.d.ts.map +1 -0
- package/dist/release/release-published.js +35 -0
- package/dist/release/release-published.js.map +1 -0
- package/dist/release/tty-confirm.d.ts +23 -0
- package/dist/release/tty-confirm.d.ts.map +1 -0
- package/dist/release/tty-confirm.js +50 -0
- package/dist/release/tty-confirm.js.map +1 -0
- package/dist/remote/index.js +1 -1
- package/dist/remote/index.js.map +1 -1
- package/dist/resources/admission-ledger.d.ts +103 -1
- package/dist/resources/admission-ledger.d.ts.map +1 -1
- package/dist/resources/admission-ledger.js +200 -17
- package/dist/resources/admission-ledger.js.map +1 -1
- package/dist/resources/agent-lease.d.ts +82 -0
- package/dist/resources/agent-lease.d.ts.map +1 -0
- package/dist/resources/agent-lease.js +167 -0
- package/dist/resources/agent-lease.js.map +1 -0
- package/dist/resources/governor.d.ts +27 -1
- package/dist/resources/governor.d.ts.map +1 -1
- package/dist/resources/governor.js +88 -4
- package/dist/resources/governor.js.map +1 -1
- package/dist/resources/hook-footprint.d.ts +13 -1
- package/dist/resources/hook-footprint.d.ts.map +1 -1
- package/dist/resources/hook-footprint.js +33 -7
- package/dist/resources/hook-footprint.js.map +1 -1
- package/dist/resources/run-governed.d.ts +25 -0
- package/dist/resources/run-governed.d.ts.map +1 -1
- package/dist/resources/run-governed.js +44 -3
- package/dist/resources/run-governed.js.map +1 -1
- package/dist/sagas/reconcile.d.ts.map +1 -1
- package/dist/sagas/reconcile.js +11 -0
- package/dist/sagas/reconcile.js.map +1 -1
- package/dist/scaffold/ensure-dirs.d.ts.map +1 -1
- package/dist/scaffold/ensure-dirs.js +8 -6
- package/dist/scaffold/ensure-dirs.js.map +1 -1
- package/dist/security/override-cap-setting.d.ts +132 -0
- package/dist/security/override-cap-setting.d.ts.map +1 -1
- package/dist/security/override-cap-setting.js +88 -10
- package/dist/security/override-cap-setting.js.map +1 -1
- package/dist/security/owner-override-authority.d.ts.map +1 -1
- package/dist/security/owner-override-authority.js +5 -1
- package/dist/security/owner-override-authority.js.map +1 -1
- package/dist/selfimprove/draft-pr.d.ts.map +1 -1
- package/dist/selfimprove/draft-pr.js +4 -1
- package/dist/selfimprove/draft-pr.js.map +1 -1
- package/dist/session/engine-ops.d.ts.map +1 -1
- package/dist/session/engine-ops.js +4 -16
- package/dist/session/engine-ops.js.map +1 -1
- package/dist/sessions/briefing.d.ts +6 -0
- package/dist/sessions/briefing.d.ts.map +1 -1
- package/dist/sessions/briefing.js +34 -0
- package/dist/sessions/briefing.js.map +1 -1
- package/dist/sessions/session-end-snapshot-entry.js +2 -2
- package/dist/sessions/session-end-snapshot-entry.js.map +1 -1
- package/dist/sessions/session-end-snapshot.js +4 -4
- package/dist/sessions/session-end-snapshot.js.map +1 -1
- package/dist/skills/injection/token.d.ts.map +1 -1
- package/dist/skills/injection/token.js +3 -6
- package/dist/skills/injection/token.js.map +1 -1
- package/dist/skills/orchestrator/spawn.d.ts.map +1 -1
- package/dist/skills/orchestrator/spawn.js +3 -4
- package/dist/skills/orchestrator/spawn.js.map +1 -1
- package/dist/skills/propose-patch.d.ts.map +1 -1
- package/dist/skills/propose-patch.js +4 -1
- package/dist/skills/propose-patch.js.map +1 -1
- package/dist/spawn/branch-integration.d.ts.map +1 -1
- package/dist/spawn/branch-integration.js +5 -2
- package/dist/spawn/branch-integration.js.map +1 -1
- package/dist/store/attachment-repair.d.ts.map +1 -1
- package/dist/store/attachment-repair.js +5 -4
- package/dist/store/attachment-repair.js.map +1 -1
- package/dist/store/attachment-store.d.ts +6 -0
- package/dist/store/attachment-store.d.ts.map +1 -1
- package/dist/store/attachment-store.js +19 -2
- package/dist/store/attachment-store.js.map +1 -1
- package/dist/store/blob-ops.d.ts +1 -1
- package/dist/store/blob-ops.d.ts.map +1 -1
- package/dist/store/blob-ops.js +7 -3
- package/dist/store/blob-ops.js.map +1 -1
- package/dist/store/conduit-doc-history.d.ts +15 -1
- package/dist/store/conduit-doc-history.d.ts.map +1 -1
- package/dist/store/conduit-doc-history.js +16 -10
- package/dist/store/conduit-doc-history.js.map +1 -1
- package/dist/store/db-helpers.d.ts +29 -0
- package/dist/store/db-helpers.d.ts.map +1 -1
- package/dist/store/db-helpers.js +89 -1
- package/dist/store/db-helpers.js.map +1 -1
- package/dist/store/decision-adr-links.d.ts.map +1 -1
- package/dist/store/decision-adr-links.js +5 -2
- package/dist/store/decision-adr-links.js.map +1 -1
- package/dist/store/git-checkpoint.d.ts +11 -1
- package/dist/store/git-checkpoint.d.ts.map +1 -1
- package/dist/store/git-checkpoint.js +14 -3
- package/dist/store/git-checkpoint.js.map +1 -1
- package/dist/store/llmtxt-blob-adapter.d.ts.map +1 -1
- package/dist/store/llmtxt-blob-adapter.js +4 -1
- package/dist/store/llmtxt-blob-adapter.js.map +1 -1
- package/dist/store/schema/attachments.d.ts +7 -5
- package/dist/store/schema/attachments.d.ts.map +1 -1
- package/dist/store/schema/attachments.js +5 -2
- package/dist/store/schema/attachments.js.map +1 -1
- package/dist/store/session-store.d.ts.map +1 -1
- package/dist/store/session-store.js +6 -0
- package/dist/store/session-store.js.map +1 -1
- package/dist/store/sqlite-backup.d.ts +6 -1
- package/dist/store/sqlite-backup.d.ts.map +1 -1
- package/dist/store/sqlite-backup.js +9 -5
- package/dist/store/sqlite-backup.js.map +1 -1
- package/dist/store/sqlite-data-accessor.d.ts.map +1 -1
- package/dist/store/sqlite-data-accessor.js +63 -36
- package/dist/store/sqlite-data-accessor.js.map +1 -1
- package/dist/system/backup.d.ts +29 -0
- package/dist/system/backup.d.ts.map +1 -1
- package/dist/system/backup.js +240 -37
- package/dist/system/backup.js.map +1 -1
- package/dist/system/dependencies.d.ts +22 -0
- package/dist/system/dependencies.d.ts.map +1 -1
- package/dist/system/dependencies.js +49 -2
- package/dist/system/dependencies.js.map +1 -1
- package/dist/system/health.d.ts.map +1 -1
- package/dist/system/health.js +23 -1
- package/dist/system/health.js.map +1 -1
- package/dist/task-work/claims.d.ts.map +1 -1
- package/dist/task-work/claims.js +14 -0
- package/dist/task-work/claims.js.map +1 -1
- package/dist/task-work/index.d.ts.map +1 -1
- package/dist/task-work/index.js +22 -0
- package/dist/task-work/index.js.map +1 -1
- package/dist/tasks/ac-coverage-gate.d.ts +27 -0
- package/dist/tasks/ac-coverage-gate.d.ts.map +1 -1
- package/dist/tasks/ac-coverage-gate.js +63 -28
- package/dist/tasks/ac-coverage-gate.js.map +1 -1
- package/dist/tasks/affected-packages.d.ts +26 -30
- package/dist/tasks/affected-packages.d.ts.map +1 -1
- package/dist/tasks/affected-packages.js +27 -50
- package/dist/tasks/affected-packages.js.map +1 -1
- package/dist/tasks/affected-scope.d.ts +13 -3
- package/dist/tasks/affected-scope.d.ts.map +1 -1
- package/dist/tasks/affected-scope.js +29 -11
- package/dist/tasks/affected-scope.js.map +1 -1
- package/dist/tasks/change-set.d.ts.map +1 -1
- package/dist/tasks/change-set.js +39 -20
- package/dist/tasks/change-set.js.map +1 -1
- package/dist/tasks/command-gate-cmd.d.ts +33 -0
- package/dist/tasks/command-gate-cmd.d.ts.map +1 -0
- package/dist/tasks/command-gate-cmd.js +45 -0
- package/dist/tasks/command-gate-cmd.js.map +1 -0
- package/dist/tasks/complete-ci.d.ts +9 -0
- package/dist/tasks/complete-ci.d.ts.map +1 -1
- package/dist/tasks/complete-ci.js +21 -4
- package/dist/tasks/complete-ci.js.map +1 -1
- package/dist/tasks/complete.d.ts.map +1 -1
- package/dist/tasks/complete.js +14 -0
- package/dist/tasks/complete.js.map +1 -1
- package/dist/tasks/component-pr.d.ts +37 -0
- package/dist/tasks/component-pr.d.ts.map +1 -1
- package/dist/tasks/component-pr.js +106 -14
- package/dist/tasks/component-pr.js.map +1 -1
- package/dist/tasks/done-plan.d.ts +14 -1
- package/dist/tasks/done-plan.d.ts.map +1 -1
- package/dist/tasks/done-plan.js +125 -15
- package/dist/tasks/done-plan.js.map +1 -1
- package/dist/tasks/done-record.d.ts.map +1 -1
- package/dist/tasks/done-record.js +4 -0
- package/dist/tasks/done-record.js.map +1 -1
- package/dist/tasks/engine-converters.d.ts.map +1 -1
- package/dist/tasks/engine-converters.js +2 -0
- package/dist/tasks/engine-converters.js.map +1 -1
- package/dist/tasks/evidence-tree.d.ts +28 -1
- package/dist/tasks/evidence-tree.d.ts.map +1 -1
- package/dist/tasks/evidence-tree.js +66 -2
- package/dist/tasks/evidence-tree.js.map +1 -1
- package/dist/tasks/evidence.d.ts +39 -27
- package/dist/tasks/evidence.d.ts.map +1 -1
- package/dist/tasks/evidence.js +382 -188
- package/dist/tasks/evidence.js.map +1 -1
- package/dist/tasks/gate-notes.d.ts +17 -0
- package/dist/tasks/gate-notes.d.ts.map +1 -1
- package/dist/tasks/gate-notes.js.map +1 -1
- package/dist/tasks/gate-preview.d.ts +7 -1
- package/dist/tasks/gate-preview.d.ts.map +1 -1
- package/dist/tasks/gate-preview.js +30 -4
- package/dist/tasks/gate-preview.js.map +1 -1
- package/dist/tasks/gate-runner.d.ts +42 -0
- package/dist/tasks/gate-runner.d.ts.map +1 -1
- package/dist/tasks/gate-runner.js +234 -94
- package/dist/tasks/gate-runner.js.map +1 -1
- package/dist/tasks/manifest-scope.d.ts +115 -0
- package/dist/tasks/manifest-scope.d.ts.map +1 -0
- package/dist/tasks/manifest-scope.js +352 -0
- package/dist/tasks/manifest-scope.js.map +1 -0
- package/dist/tasks/merge-close.d.ts +376 -0
- package/dist/tasks/merge-close.d.ts.map +1 -0
- package/dist/tasks/merge-close.js +1497 -0
- package/dist/tasks/merge-close.js.map +1 -0
- package/dist/tasks/qa-run-binding.d.ts +5 -0
- package/dist/tasks/qa-run-binding.d.ts.map +1 -1
- package/dist/tasks/qa-run-binding.js +6 -1
- package/dist/tasks/qa-run-binding.js.map +1 -1
- package/dist/tasks/req.d.ts.map +1 -1
- package/dist/tasks/req.js +29 -1
- package/dist/tasks/req.js.map +1 -1
- package/dist/tasks/review-verdict.d.ts +159 -0
- package/dist/tasks/review-verdict.d.ts.map +1 -0
- package/dist/tasks/review-verdict.js +442 -0
- package/dist/tasks/review-verdict.js.map +1 -0
- package/dist/tasks/task-reparent.d.ts.map +1 -1
- package/dist/tasks/task-reparent.js +22 -3
- package/dist/tasks/task-reparent.js.map +1 -1
- package/dist/tasks/test-report-counts.d.ts +274 -0
- package/dist/tasks/test-report-counts.d.ts.map +1 -0
- package/dist/tasks/test-report-counts.js +746 -0
- package/dist/tasks/test-report-counts.js.map +1 -0
- package/dist/tasks/test-run-binding.d.ts +20 -45
- package/dist/tasks/test-run-binding.d.ts.map +1 -1
- package/dist/tasks/test-run-binding.js +34 -119
- package/dist/tasks/test-run-binding.js.map +1 -1
- package/dist/tasks/update.d.ts.map +1 -1
- package/dist/tasks/update.js +42 -0
- package/dist/tasks/update.js.map +1 -1
- package/dist/tasks/verification-policy.d.ts +12 -0
- package/dist/tasks/verification-policy.d.ts.map +1 -1
- package/dist/tasks/verification-policy.js +13 -0
- package/dist/tasks/verification-policy.js.map +1 -1
- package/dist/tasks/workspace-resolution.d.ts +225 -0
- package/dist/tasks/workspace-resolution.d.ts.map +1 -0
- package/dist/tasks/workspace-resolution.js +497 -0
- package/dist/tasks/workspace-resolution.js.map +1 -0
- package/dist/validation/doctor/checks.d.ts +20 -0
- package/dist/validation/doctor/checks.d.ts.map +1 -1
- package/dist/validation/doctor/checks.js +112 -0
- package/dist/validation/doctor/checks.js.map +1 -1
- package/dist/validation/engine-ops.d.ts.map +1 -1
- package/dist/validation/engine-ops.js +37 -3
- package/dist/validation/engine-ops.js.map +1 -1
- package/dist/validation/validate-ops.d.ts +4 -0
- package/dist/validation/validate-ops.d.ts.map +1 -1
- package/dist/validation/validate-ops.js +79 -2
- package/dist/validation/validate-ops.js.map +1 -1
- package/dist/worktree/list.d.ts +4 -21
- package/dist/worktree/list.d.ts.map +1 -1
- package/dist/worktree/list.js +14 -44
- package/dist/worktree/list.js.map +1 -1
- package/dist/worktree/merged-cleanup.d.ts +63 -0
- package/dist/worktree/merged-cleanup.d.ts.map +1 -0
- package/dist/worktree/merged-cleanup.js +571 -0
- package/dist/worktree/merged-cleanup.js.map +1 -0
- package/dist/worktree/prune.d.ts +64 -2
- package/dist/worktree/prune.d.ts.map +1 -1
- package/dist/worktree/prune.js +151 -6
- package/dist/worktree/prune.js.map +1 -1
- package/dist/worktree/sentinel-index.d.ts +14 -0
- package/dist/worktree/sentinel-index.d.ts.map +1 -1
- package/dist/worktree/sentinel-index.js +17 -0
- package/dist/worktree/sentinel-index.js.map +1 -1
- package/dist/worktree/worktree-adopt.d.ts +1 -13
- package/dist/worktree/worktree-adopt.d.ts.map +1 -1
- package/dist/worktree/worktree-adopt.js +2 -17
- package/dist/worktree/worktree-adopt.js.map +1 -1
- package/package.json +12 -12
- package/schemas/project-context.schema.json +33 -0
- package/templates/CLEO-INJECTION.md +6 -16
- package/templates/CLEO-REFERENCE.md +27 -4
- package/templates/git-hooks/legacy-hashes.json +2 -1
- package/templates/git-hooks/pre-commit +43 -11
- package/templates/workflows/release-prepare.yml.tmpl +9 -88
|
@@ -0,0 +1,662 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `cleo doctor type-matrix`: repair the parent/child pairs that break the
|
|
3
|
+
* type matrix (`PARENT_TYPE_MATRIX`: saga→epic, epic→task, task→subtask).
|
|
4
|
+
*
|
|
5
|
+
* The writers refuse new violations (T12883 R4); this repairs the rows written
|
|
6
|
+
* before they did (T14096). A dry run by default:
|
|
7
|
+
*
|
|
8
|
+
* - {@link planTypeMatrixRepair} lists every parented violation with the
|
|
9
|
+
* operations that would fix it. A pair with exactly one valid operation (a
|
|
10
|
+
* leaf `subtask` under an epic becomes a `task`; a leaf `task` under a task
|
|
11
|
+
* becomes a `subtask`) carries it as its `proposal`. A pair with none or
|
|
12
|
+
* several is ambiguous: its `options` are listed and nothing is applied
|
|
13
|
+
* until the calling agent names one in a resolution.
|
|
14
|
+
* - {@link applyTypeMatrixRepair} applies the proposals plus the agent's
|
|
15
|
+
* resolutions through the task accessor, in one transaction, guarded on each
|
|
16
|
+
* row being unchanged since it was read (or since the reviewed plan read
|
|
17
|
+
* it), and writes one receipt (before and after image of every touched row,
|
|
18
|
+
* with the version it wrote and its children) to the task audit log. A
|
|
19
|
+
* reparent is held to the reparent core's checks (no cancelled or archived
|
|
20
|
+
* target, no cycle, depth and sibling limits) and reopens done ancestors.
|
|
21
|
+
* - {@link rollbackTypeMatrixRepair} restores the before image, refused when a
|
|
22
|
+
* row changed after the repair (its version moved), and never for a row
|
|
23
|
+
* whose restore would break a child filed under it since. A row whose before image the type-matrix
|
|
24
|
+
* trigger now forbids (a legacy task under a task, say) is not restored; the
|
|
25
|
+
* result names it and why (owner decision, T14096).
|
|
26
|
+
*
|
|
27
|
+
* The contracts matrix is the authority (owner decision, T14096): a subtask
|
|
28
|
+
* belongs under a task, even though the trigger still accepts one under an
|
|
29
|
+
* epic until T14131 tightens it.
|
|
30
|
+
*
|
|
31
|
+
* A violation at the root (a parentless task) is outside this repair.
|
|
32
|
+
*
|
|
33
|
+
* @module
|
|
34
|
+
* @task T14096
|
|
35
|
+
*/
|
|
36
|
+
import { randomUUID } from 'node:crypto';
|
|
37
|
+
import { ExitCode } from '@cleocode/contracts/exit-codes.js';
|
|
38
|
+
import { canWorkGraphTaskTypeBeRoot, isAllowedWorkGraphParentType, } from '@cleocode/contracts/workgraph.js';
|
|
39
|
+
import { CleoError } from '../errors.js';
|
|
40
|
+
import { getTaskAccessor } from '../store/data-accessor.js';
|
|
41
|
+
import { nextTaskVersion } from '../store/task-version.js';
|
|
42
|
+
import { assertParentNotIrreversiblyTerminal } from '../tasks/child-disposition.js';
|
|
43
|
+
import { getHierarchyLimits } from '../tasks/task-tree.js';
|
|
44
|
+
/** Audit-log action of an applied repair (its row id is the receipt id). */
|
|
45
|
+
export const TYPE_MATRIX_REPAIR_ACTION = 'doctor.type-matrix.repair';
|
|
46
|
+
/** Audit-log action of a rollback. */
|
|
47
|
+
export const TYPE_MATRIX_ROLLBACK_ACTION = 'doctor.type-matrix.rollback';
|
|
48
|
+
const TASK_TYPES = ['saga', 'epic', 'task', 'subtask'];
|
|
49
|
+
/**
|
|
50
|
+
* Whether `id` sits where the matrix allows, given the model. A root is out
|
|
51
|
+
* of this repair's scope: it passes while it keeps its stored type, and may
|
|
52
|
+
* only be re-typed to a type that can be a root.
|
|
53
|
+
*/
|
|
54
|
+
function placed(id, model) {
|
|
55
|
+
const node = model.get(id);
|
|
56
|
+
if (node === undefined)
|
|
57
|
+
return true;
|
|
58
|
+
if (node.parentId === null) {
|
|
59
|
+
return node.type === node.storedType || canWorkGraphTaskTypeBeRoot(node.type);
|
|
60
|
+
}
|
|
61
|
+
const parent = model.get(node.parentId);
|
|
62
|
+
return parent !== undefined && isAllowedWorkGraphParentType(node.type, parent.type);
|
|
63
|
+
}
|
|
64
|
+
function applyOp(model, op) {
|
|
65
|
+
const node = model.get(op.taskId);
|
|
66
|
+
if (node === undefined)
|
|
67
|
+
return;
|
|
68
|
+
model.set(op.taskId, op.kind === 'retype' ? { ...node, type: op.to } : { ...node, parentId: op.toParentId });
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Every re-type of `id` that places `violating` (the child of the pair) and
|
|
72
|
+
* `id` itself, and breaks no pair that was valid.
|
|
73
|
+
*/
|
|
74
|
+
function retypeOptions(id, violating, model, children) {
|
|
75
|
+
const node = model.get(id);
|
|
76
|
+
if (node === undefined)
|
|
77
|
+
return [];
|
|
78
|
+
return TASK_TYPES.filter((to) => to !== node.type).flatMap((to) => {
|
|
79
|
+
const trial = new Map(model);
|
|
80
|
+
trial.set(id, { ...node, type: to });
|
|
81
|
+
const ok = placed(violating, trial) &&
|
|
82
|
+
placed(id, trial) &&
|
|
83
|
+
newlyBroken(id, model, trial, children).length === 0;
|
|
84
|
+
return ok ? [{ kind: 'retype', taskId: id, to }] : [];
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
function childIndex(model) {
|
|
88
|
+
const index = new Map();
|
|
89
|
+
for (const [id, node] of model) {
|
|
90
|
+
if (node.parentId === null)
|
|
91
|
+
continue;
|
|
92
|
+
const list = index.get(node.parentId) ?? [];
|
|
93
|
+
list.push(id);
|
|
94
|
+
index.set(node.parentId, list);
|
|
95
|
+
}
|
|
96
|
+
return index;
|
|
97
|
+
}
|
|
98
|
+
function modelOf(tasks) {
|
|
99
|
+
return new Map(tasks.map((t) => {
|
|
100
|
+
const type = t.type ?? 'task';
|
|
101
|
+
return [t.id, { type, parentId: t.parentId ?? null, storedType: type }];
|
|
102
|
+
}));
|
|
103
|
+
}
|
|
104
|
+
function planFor(tasks) {
|
|
105
|
+
const model = modelOf(tasks);
|
|
106
|
+
const children = childIndex(model);
|
|
107
|
+
const plans = [];
|
|
108
|
+
for (const [id, node] of model) {
|
|
109
|
+
if (node.parentId === null || placed(id, model))
|
|
110
|
+
continue;
|
|
111
|
+
const parent = model.get(node.parentId);
|
|
112
|
+
if (parent === undefined)
|
|
113
|
+
continue; // a dangling parent is the FK check's, not this one's
|
|
114
|
+
const options = [
|
|
115
|
+
...retypeOptions(id, id, model, children),
|
|
116
|
+
...retypeOptions(node.parentId, id, model, children),
|
|
117
|
+
];
|
|
118
|
+
plans.push({
|
|
119
|
+
taskId: id,
|
|
120
|
+
taskType: node.type,
|
|
121
|
+
parentId: node.parentId,
|
|
122
|
+
parentType: parent.type,
|
|
123
|
+
proposal: options.length === 1 ? (options[0] ?? null) : null,
|
|
124
|
+
options,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
plans.sort((a, b) => a.taskId.localeCompare(b.taskId));
|
|
128
|
+
return { model, children, plans };
|
|
129
|
+
}
|
|
130
|
+
function isTaskType(value) {
|
|
131
|
+
return typeof value === 'string' && TASK_TYPES.includes(value);
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Parse a `--resolutions` file: a JSON array of `retype` and `reparent`
|
|
135
|
+
* operations.
|
|
136
|
+
*
|
|
137
|
+
* @param text - The file's contents.
|
|
138
|
+
* @returns The operations, or why the file was refused.
|
|
139
|
+
* @example
|
|
140
|
+
* ```ts
|
|
141
|
+
* parseTypeMatrixResolutions('[{"kind":"retype","taskId":"T1","to":"epic"}]');
|
|
142
|
+
* ```
|
|
143
|
+
*/
|
|
144
|
+
export function parseTypeMatrixResolutions(text) {
|
|
145
|
+
let parsed;
|
|
146
|
+
try {
|
|
147
|
+
parsed = JSON.parse(text);
|
|
148
|
+
}
|
|
149
|
+
catch (err) {
|
|
150
|
+
return { ok: false, reason: `not JSON (${err instanceof Error ? err.message : String(err)})` };
|
|
151
|
+
}
|
|
152
|
+
if (!Array.isArray(parsed))
|
|
153
|
+
return { ok: false, reason: 'expected a JSON array of operations' };
|
|
154
|
+
const operations = [];
|
|
155
|
+
for (const [i, entry] of parsed.entries()) {
|
|
156
|
+
const op = typeof entry === 'object' && entry !== null ? entry : {};
|
|
157
|
+
const taskId = op['taskId'];
|
|
158
|
+
if (typeof taskId !== 'string' || taskId === '') {
|
|
159
|
+
return { ok: false, reason: `operation ${i} has no taskId` };
|
|
160
|
+
}
|
|
161
|
+
if (op['kind'] === 'retype' && isTaskType(op['to'])) {
|
|
162
|
+
operations.push({ kind: 'retype', taskId, to: op['to'] });
|
|
163
|
+
}
|
|
164
|
+
else if (op['kind'] === 'reparent' && typeof op['toParentId'] === 'string') {
|
|
165
|
+
operations.push({ kind: 'reparent', taskId, toParentId: op['toParentId'] });
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
return {
|
|
169
|
+
ok: false,
|
|
170
|
+
reason: `operation ${i} must be {"kind":"retype","to":<saga|epic|task|subtask>} or {"kind":"reparent","toParentId":<id>}`,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return { ok: true, operations };
|
|
175
|
+
}
|
|
176
|
+
function refuse(message, fix) {
|
|
177
|
+
// @sync-invariant none:input-shape a refused --resolutions file is rejected before anything is written
|
|
178
|
+
return new CleoError(ExitCode.VALIDATION_ERROR, message, {
|
|
179
|
+
...(fix ? { fix } : {}),
|
|
180
|
+
details: { field: 'resolutions' },
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Rows an operation on `id` newly breaks: `id` against its parent and each
|
|
185
|
+
* child of `id` against it, placed before and not after. A pair that was
|
|
186
|
+
* already a violation is not counted (it is another plan's to fix).
|
|
187
|
+
*/
|
|
188
|
+
function newlyBroken(id, before, after, children = childIndex(after)) {
|
|
189
|
+
const candidates = [id, ...(children.get(id) ?? [])];
|
|
190
|
+
return candidates.filter((c) => placed(c, before) && !placed(c, after));
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Decide the operations a repair applies. The agent's resolutions come first
|
|
194
|
+
* and must each leave their task validly placed and break no pair that was
|
|
195
|
+
* valid (refused otherwise, before anything is written). Then each proposal,
|
|
196
|
+
* on top of what is already chosen: one whose pair is already fixed, or that
|
|
197
|
+
* would now break a valid pair, is skipped and its violation stays unresolved.
|
|
198
|
+
*/
|
|
199
|
+
function decideOperations(plans, model, resolutions, guards) {
|
|
200
|
+
const operations = [];
|
|
201
|
+
const seen = new Set();
|
|
202
|
+
let after = new Map(model);
|
|
203
|
+
for (const op of resolutions) {
|
|
204
|
+
if (!model.has(op.taskId)) {
|
|
205
|
+
// @sync-invariant none:input-shape a resolution naming an unknown task is refused before anything is written
|
|
206
|
+
throw refuse(`Resolution names ${op.taskId}, which is not a task in this store.`);
|
|
207
|
+
}
|
|
208
|
+
if (op.kind === 'reparent' && !model.has(op.toParentId)) {
|
|
209
|
+
// @sync-invariant none:input-shape a resolution naming an unknown parent is refused before anything is written
|
|
210
|
+
throw refuse(`Resolution moves ${op.taskId} under ${op.toParentId}, which is not a task.`);
|
|
211
|
+
}
|
|
212
|
+
if (seen.has(op.taskId)) {
|
|
213
|
+
// @sync-invariant none:input-shape two resolutions for one task are refused before anything is written
|
|
214
|
+
throw refuse(`Two resolutions name ${op.taskId}; give one per task.`);
|
|
215
|
+
}
|
|
216
|
+
if (op.kind === 'reparent')
|
|
217
|
+
assertReparentAllowed(op, after, guards);
|
|
218
|
+
const trial = new Map(after);
|
|
219
|
+
applyOp(trial, op);
|
|
220
|
+
const broken = newlyBroken(op.taskId, after, trial);
|
|
221
|
+
if (!placed(op.taskId, trial) || broken.length > 0) {
|
|
222
|
+
// @sync-invariant task.parent.type-matrix a resolution that leaves the matrix broken is refused before anything is written
|
|
223
|
+
throw refuse(`The resolution for ${op.taskId} would leave ${[op.taskId, ...broken].join(', ')} where the type matrix (saga→epic, epic→task, task→subtask) does not allow it.`, 'Run cleo doctor type-matrix for the options, and resolve each ambiguous pair with one of them.');
|
|
224
|
+
}
|
|
225
|
+
seen.add(op.taskId);
|
|
226
|
+
operations.push(op);
|
|
227
|
+
after = trial;
|
|
228
|
+
}
|
|
229
|
+
for (const plan of plans) {
|
|
230
|
+
const proposal = plan.proposal;
|
|
231
|
+
if (proposal === null || seen.has(proposal.taskId) || placed(plan.taskId, after))
|
|
232
|
+
continue;
|
|
233
|
+
const trial = new Map(after);
|
|
234
|
+
applyOp(trial, proposal);
|
|
235
|
+
if (!placed(plan.taskId, trial) || newlyBroken(proposal.taskId, after, trial).length > 0) {
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
seen.add(proposal.taskId);
|
|
239
|
+
operations.push(proposal);
|
|
240
|
+
after = trial;
|
|
241
|
+
}
|
|
242
|
+
const unresolved = plans.filter((p) => !placed(p.taskId, after)).map((p) => p.taskId);
|
|
243
|
+
return { operations, unresolved };
|
|
244
|
+
}
|
|
245
|
+
function depthOf(id, model) {
|
|
246
|
+
let depth = 1;
|
|
247
|
+
let parent = model.get(id)?.parentId ?? null;
|
|
248
|
+
const seen = new Set([id]);
|
|
249
|
+
while (parent !== null && !seen.has(parent)) {
|
|
250
|
+
seen.add(parent);
|
|
251
|
+
depth++;
|
|
252
|
+
parent = model.get(parent)?.parentId ?? null;
|
|
253
|
+
}
|
|
254
|
+
return depth;
|
|
255
|
+
}
|
|
256
|
+
function subtreeHeight(id, children) {
|
|
257
|
+
const kids = children.get(id) ?? [];
|
|
258
|
+
return kids.length === 0 ? 0 : 1 + Math.max(...kids.map((k) => subtreeHeight(k, children)));
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Refuse a reparent resolution the reparent core (`coreTaskReparent`) would
|
|
262
|
+
* refuse: a cancelled or archived target (T12883 R1), a target inside the
|
|
263
|
+
* moved subtree (a cycle), and the depth and sibling limits. Done ancestors
|
|
264
|
+
* are reopened at apply time, as that core does.
|
|
265
|
+
*/
|
|
266
|
+
function assertReparentAllowed(op, model, guards) {
|
|
267
|
+
if (op.toParentId === op.taskId) {
|
|
268
|
+
// @sync-invariant none:input-shape a reparent under itself is refused before anything is written
|
|
269
|
+
throw refuse(`Moving ${op.taskId} under itself would create a cycle.`);
|
|
270
|
+
}
|
|
271
|
+
assertParentNotIrreversiblyTerminal({ id: op.toParentId, status: guards.status.get(op.toParentId) ?? 'pending' }, op.taskId, 'move');
|
|
272
|
+
let cursor = op.toParentId;
|
|
273
|
+
const seen = new Set();
|
|
274
|
+
while (cursor !== null && !seen.has(cursor)) {
|
|
275
|
+
if (cursor === op.taskId) {
|
|
276
|
+
// @sync-invariant none:input-shape a reparent into the moved subtree is refused before anything is written
|
|
277
|
+
throw refuse(`Moving ${op.taskId} under ${op.toParentId} would create a cycle: ${op.toParentId} is inside ${op.taskId}'s subtree.`);
|
|
278
|
+
}
|
|
279
|
+
seen.add(cursor);
|
|
280
|
+
cursor = model.get(cursor)?.parentId ?? null;
|
|
281
|
+
}
|
|
282
|
+
const children = childIndex(model);
|
|
283
|
+
const deepest = depthOf(op.toParentId, model) + 1 + subtreeHeight(op.taskId, children);
|
|
284
|
+
if (deepest > guards.limits.maxDepth) {
|
|
285
|
+
// @sync-invariant none:input-shape a reparent past the depth limit is refused before anything is written
|
|
286
|
+
throw refuse(`Moving ${op.taskId} under ${op.toParentId} would exceed the max depth of ${guards.limits.maxDepth}.`);
|
|
287
|
+
}
|
|
288
|
+
const siblings = (children.get(op.toParentId) ?? []).filter((c) => c !== op.taskId).length;
|
|
289
|
+
if (guards.limits.maxSiblings > 0 && siblings >= guards.limits.maxSiblings) {
|
|
290
|
+
// @sync-invariant none:input-shape a reparent past the sibling limit is refused before anything is written
|
|
291
|
+
throw refuse(`Cannot add ${op.taskId} under ${op.toParentId}: max siblings (${guards.limits.maxSiblings}) exceeded.`);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
function guardsFor(projectRoot, tasks) {
|
|
295
|
+
return {
|
|
296
|
+
status: new Map(tasks.map((t) => [t.id, t.status])),
|
|
297
|
+
limits: getHierarchyLimits(projectRoot),
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
function versionOf(task) {
|
|
301
|
+
return task.updatedAt ?? task.createdAt;
|
|
302
|
+
}
|
|
303
|
+
function rowVersions(operations, byId) {
|
|
304
|
+
const versions = {};
|
|
305
|
+
for (const op of operations) {
|
|
306
|
+
const task = byId.get(op.taskId);
|
|
307
|
+
if (task)
|
|
308
|
+
versions[op.taskId] = versionOf(task);
|
|
309
|
+
}
|
|
310
|
+
return versions;
|
|
311
|
+
}
|
|
312
|
+
function byPair(plans) {
|
|
313
|
+
const counts = {};
|
|
314
|
+
for (const p of plans) {
|
|
315
|
+
const k = `${p.parentType} → ${p.taskType}`;
|
|
316
|
+
counts[k] = (counts[k] ?? 0) + 1;
|
|
317
|
+
}
|
|
318
|
+
return counts;
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Plan the repair of the store's type-matrix violations without writing.
|
|
322
|
+
*
|
|
323
|
+
* @param projectRoot - Project root.
|
|
324
|
+
* @param resolutions - Operations the calling agent chose for ambiguous pairs.
|
|
325
|
+
* @returns Every violation, its options, and the operations an apply would run.
|
|
326
|
+
* @example
|
|
327
|
+
* ```ts
|
|
328
|
+
* const plan = await planTypeMatrixRepair(root);
|
|
329
|
+
* console.log(plan.byPair, plan.unresolved.length);
|
|
330
|
+
* ```
|
|
331
|
+
*/
|
|
332
|
+
export async function planTypeMatrixRepair(projectRoot, resolutions = []) {
|
|
333
|
+
const accessor = await getTaskAccessor(projectRoot);
|
|
334
|
+
const { tasks } = await accessor.queryTasks({});
|
|
335
|
+
const { model, plans } = planFor(tasks);
|
|
336
|
+
const guards = guardsFor(projectRoot, tasks);
|
|
337
|
+
const { operations, unresolved } = decideOperations(plans, model, resolutions, guards);
|
|
338
|
+
return {
|
|
339
|
+
dryRun: true,
|
|
340
|
+
violations: plans.length,
|
|
341
|
+
byPair: byPair(plans),
|
|
342
|
+
plans,
|
|
343
|
+
operations,
|
|
344
|
+
unresolved,
|
|
345
|
+
rowVersions: rowVersions(operations, new Map(tasks.map((t) => [t.id, t]))),
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
/** Ancestors of `id` in the model, nearest first. */
|
|
349
|
+
function ancestorsOf(id, model) {
|
|
350
|
+
const out = [];
|
|
351
|
+
const seen = new Set();
|
|
352
|
+
let cursor = id;
|
|
353
|
+
while (cursor !== null && !seen.has(cursor)) {
|
|
354
|
+
seen.add(cursor);
|
|
355
|
+
out.push(cursor);
|
|
356
|
+
cursor = model.get(cursor)?.parentId ?? null;
|
|
357
|
+
}
|
|
358
|
+
return out;
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Apply the proposals and the agent's resolutions in one transaction, and
|
|
362
|
+
* record the receipt. Each row is written only at the version it was read at
|
|
363
|
+
* (by this call, or by the reviewed `plan`): otherwise `E_CONFLICT`, and the
|
|
364
|
+
* transaction writes nothing. A reparent reopens the done ancestors of its old
|
|
365
|
+
* and new parent, as `cleo update --parent` does.
|
|
366
|
+
*
|
|
367
|
+
* @param projectRoot - Project root.
|
|
368
|
+
* @param resolutions - Operations the calling agent chose for ambiguous pairs.
|
|
369
|
+
* @param opts - The reviewed plan to hold the apply to.
|
|
370
|
+
* @returns The report with the receipt id and the rollback command.
|
|
371
|
+
* @example
|
|
372
|
+
* ```ts
|
|
373
|
+
* const done = await applyTypeMatrixRepair(root, [{ kind: 'retype', taskId: 'T9', to: 'saga' }]);
|
|
374
|
+
* console.log(done.rollback);
|
|
375
|
+
* ```
|
|
376
|
+
*/
|
|
377
|
+
export async function applyTypeMatrixRepair(projectRoot, resolutions = [], opts = {}) {
|
|
378
|
+
const accessor = await getTaskAccessor(projectRoot);
|
|
379
|
+
const { tasks } = await accessor.queryTasks({});
|
|
380
|
+
const byId = new Map(tasks.map((t) => [t.id, t]));
|
|
381
|
+
const { model, plans } = planFor(tasks);
|
|
382
|
+
const { operations, unresolved } = decideOperations(plans, model, resolutions, guardsFor(projectRoot, tasks));
|
|
383
|
+
if (opts.plan && JSON.stringify(opts.plan.operations) !== JSON.stringify(operations)) {
|
|
384
|
+
// @sync-invariant none:local-only an apply never runs operations other than the reviewed plan's
|
|
385
|
+
throw new CleoError(ExitCode.VERSION_CONFLICT, 'The store changed since the plan was made, so the repair would run different operations than the ones reviewed.', {
|
|
386
|
+
fix: 'Run cleo doctor type-matrix again and review the new plan.',
|
|
387
|
+
details: { field: 'plan' },
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
const versions = { ...rowVersions(operations, byId), ...(opts.plan?.rowVersions ?? {}) };
|
|
391
|
+
const report = {
|
|
392
|
+
dryRun: false,
|
|
393
|
+
violations: plans.length,
|
|
394
|
+
byPair: byPair(plans),
|
|
395
|
+
plans,
|
|
396
|
+
operations,
|
|
397
|
+
unresolved,
|
|
398
|
+
rowVersions: versions,
|
|
399
|
+
};
|
|
400
|
+
if (operations.length === 0)
|
|
401
|
+
return report;
|
|
402
|
+
const after = new Map(model);
|
|
403
|
+
for (const op of operations)
|
|
404
|
+
applyOp(after, op);
|
|
405
|
+
const childrenAfter = childIndex(after);
|
|
406
|
+
const reopen = new Set();
|
|
407
|
+
for (const op of operations) {
|
|
408
|
+
if (op.kind !== 'reparent')
|
|
409
|
+
continue;
|
|
410
|
+
const oldParent = model.get(op.taskId)?.parentId ?? null;
|
|
411
|
+
for (const id of [...ancestorsOf(oldParent, model), ...ancestorsOf(op.toParentId, after)]) {
|
|
412
|
+
if (byId.get(id)?.status === 'done')
|
|
413
|
+
reopen.add(id);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
const receiptId = `type-matrix-${randomUUID()}`;
|
|
417
|
+
const stamp = new Date().toISOString();
|
|
418
|
+
const rows = await accessor.transaction(async (tx) => {
|
|
419
|
+
const current = new Map();
|
|
420
|
+
const write = async (id, fields) => {
|
|
421
|
+
const task = byId.get(id);
|
|
422
|
+
const expected = current.get(id) ?? versions[id] ?? (task ? versionOf(task) : undefined);
|
|
423
|
+
const updatedAt = nextTaskVersion({ updatedAt: expected ?? null, createdAt: stamp }, stamp);
|
|
424
|
+
await tx.updateTaskFields(id, { ...fields, updatedAt }, expected === undefined ? undefined : { expectedUpdatedAt: expected });
|
|
425
|
+
current.set(id, updatedAt);
|
|
426
|
+
return updatedAt;
|
|
427
|
+
};
|
|
428
|
+
const images = [];
|
|
429
|
+
for (const op of operations) {
|
|
430
|
+
const before = model.get(op.taskId);
|
|
431
|
+
if (before === undefined)
|
|
432
|
+
continue;
|
|
433
|
+
const fields = op.kind === 'retype' ? { type: op.to } : { parentId: op.toParentId };
|
|
434
|
+
const was = { type: before.type, parentId: before.parentId };
|
|
435
|
+
const now = op.kind === 'retype' ? { ...was, type: op.to } : { ...was, parentId: op.toParentId };
|
|
436
|
+
images.push({
|
|
437
|
+
id: op.taskId,
|
|
438
|
+
before: was,
|
|
439
|
+
after: now,
|
|
440
|
+
updatedAt: await write(op.taskId, fields),
|
|
441
|
+
children: childrenAfter.get(op.taskId) ?? [],
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
for (const id of reopen)
|
|
445
|
+
await write(id, { status: 'pending', completedAt: null });
|
|
446
|
+
// Each image carries the version this transaction last wrote.
|
|
447
|
+
const final = images.map((r) => ({ ...r, updatedAt: current.get(r.id) ?? r.updatedAt }));
|
|
448
|
+
await tx.appendLog({
|
|
449
|
+
id: receiptId,
|
|
450
|
+
action: TYPE_MATRIX_REPAIR_ACTION,
|
|
451
|
+
taskId: final[0]?.id ?? 'doctor',
|
|
452
|
+
actor: 'cleo doctor type-matrix',
|
|
453
|
+
details: { operations, unresolved, reopened: [...reopen] },
|
|
454
|
+
before: final.map((r) => ({ id: r.id, ...r.before })),
|
|
455
|
+
after: final.map((r) => ({
|
|
456
|
+
id: r.id,
|
|
457
|
+
...r.after,
|
|
458
|
+
updatedAt: r.updatedAt,
|
|
459
|
+
children: r.children,
|
|
460
|
+
})),
|
|
461
|
+
});
|
|
462
|
+
return final;
|
|
463
|
+
});
|
|
464
|
+
return {
|
|
465
|
+
...report,
|
|
466
|
+
receiptId,
|
|
467
|
+
rollback: `cleo doctor type-matrix --rollback ${receiptId}`,
|
|
468
|
+
rows,
|
|
469
|
+
reopened: [...reopen],
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
/**
|
|
473
|
+
* The parents the `tasks_tasks_parent_type_matrix_*` triggers accept, per child
|
|
474
|
+
* type. Looser than `PARENT_TYPE_MATRIX` for a subtask (T14131 tightens it).
|
|
475
|
+
*/
|
|
476
|
+
const TRIGGER_PARENTS = {
|
|
477
|
+
saga: [],
|
|
478
|
+
epic: ['saga'],
|
|
479
|
+
task: ['epic'],
|
|
480
|
+
subtask: ['epic', 'task'],
|
|
481
|
+
};
|
|
482
|
+
/**
|
|
483
|
+
* Split a receipt's before image into the rows a rollback may restore, in an
|
|
484
|
+
* order that restores a parent before its child, and the rows it refuses with
|
|
485
|
+
* the reason. A row is refused when the type-matrix trigger forbids its
|
|
486
|
+
* before image (owner decision, T14096 Q2), or when restoring it would break
|
|
487
|
+
* a child filed under it after the repair.
|
|
488
|
+
*/
|
|
489
|
+
function restorable(before, after, typeOf, childrenNow) {
|
|
490
|
+
const pending = new Map(before.map((r) => [r.id, r]));
|
|
491
|
+
const refused = [];
|
|
492
|
+
const finalType = (id) => pending.get(id)?.type ?? typeOf.get(id);
|
|
493
|
+
let dropped = true;
|
|
494
|
+
while (dropped) {
|
|
495
|
+
dropped = false;
|
|
496
|
+
for (const row of pending.values()) {
|
|
497
|
+
const reason = refusalOf(row, after.get(row.id), finalType, childrenNow.get(row.id) ?? []);
|
|
498
|
+
if (reason === null)
|
|
499
|
+
continue;
|
|
500
|
+
pending.delete(row.id);
|
|
501
|
+
refused.push({ id: row.id, reason });
|
|
502
|
+
dropped = true;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
const restore = [];
|
|
506
|
+
const placedIds = new Set();
|
|
507
|
+
while (restore.length < pending.size) {
|
|
508
|
+
for (const row of pending.values()) {
|
|
509
|
+
if (placedIds.has(row.id))
|
|
510
|
+
continue;
|
|
511
|
+
const parentPending = row.parentId !== null && pending.has(row.parentId) && !placedIds.has(row.parentId);
|
|
512
|
+
if (parentPending)
|
|
513
|
+
continue;
|
|
514
|
+
restore.push(row);
|
|
515
|
+
placedIds.add(row.id);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
return { restore, refused };
|
|
519
|
+
}
|
|
520
|
+
function refusalOf(row, then, finalType, children) {
|
|
521
|
+
if (row.parentId !== null) {
|
|
522
|
+
const parentType = finalType(row.parentId);
|
|
523
|
+
if (parentType !== undefined && !TRIGGER_PARENTS[row.type].includes(parentType)) {
|
|
524
|
+
return `restoring ${row.id} as a ${row.type} under ${row.parentId} (a ${parentType}) is refused by the type-matrix trigger; the repair's value stays`;
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
const known = new Set(then?.children ?? []);
|
|
528
|
+
const later = children.find((c) => !known.has(c.id) &&
|
|
529
|
+
isAllowedWorkGraphParentType(c.type ?? 'task', then?.type ?? row.type) &&
|
|
530
|
+
!isAllowedWorkGraphParentType(c.type ?? 'task', row.type));
|
|
531
|
+
return later === undefined
|
|
532
|
+
? null
|
|
533
|
+
: `restoring ${row.id} as a ${row.type} would break ${later.id} (a ${later.type ?? 'task'} filed under it after the repair); the repair's value stays`;
|
|
534
|
+
}
|
|
535
|
+
function malformed(receiptId) {
|
|
536
|
+
// @sync-invariant none:input-shape a malformed receipt is refused before anything is written
|
|
537
|
+
return new CleoError(ExitCode.VALIDATION_ERROR, `Receipt ${receiptId} has a malformed row image.`, {
|
|
538
|
+
details: { field: 'receiptId' },
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
function parseImages(json, receiptId) {
|
|
542
|
+
if (json === null)
|
|
543
|
+
return [];
|
|
544
|
+
let parsed;
|
|
545
|
+
try {
|
|
546
|
+
parsed = JSON.parse(json);
|
|
547
|
+
}
|
|
548
|
+
catch {
|
|
549
|
+
// @sync-invariant none:input-shape a malformed receipt is refused before anything is written
|
|
550
|
+
throw malformed(receiptId);
|
|
551
|
+
}
|
|
552
|
+
// @sync-invariant none:input-shape a malformed receipt is refused before anything is written
|
|
553
|
+
if (!Array.isArray(parsed))
|
|
554
|
+
throw malformed(receiptId);
|
|
555
|
+
return parsed.map((entry) => {
|
|
556
|
+
const r = typeof entry === 'object' && entry !== null ? entry : {};
|
|
557
|
+
const { id, type, parentId, updatedAt, children } = r;
|
|
558
|
+
// @sync-invariant none:input-shape a malformed receipt is refused before anything is written
|
|
559
|
+
if (typeof id !== 'string' || !isTaskType(type))
|
|
560
|
+
throw malformed(receiptId);
|
|
561
|
+
// @sync-invariant none:input-shape a malformed receipt is refused before anything is written
|
|
562
|
+
if (parentId !== null && typeof parentId !== 'string')
|
|
563
|
+
throw malformed(receiptId);
|
|
564
|
+
return {
|
|
565
|
+
id,
|
|
566
|
+
type,
|
|
567
|
+
parentId,
|
|
568
|
+
...(typeof updatedAt === 'string' ? { updatedAt } : {}),
|
|
569
|
+
...(Array.isArray(children)
|
|
570
|
+
? { children: children.filter((c) => typeof c === 'string') }
|
|
571
|
+
: {}),
|
|
572
|
+
};
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
/**
|
|
576
|
+
* Restore the rows a repair changed to their before image. Refused when the
|
|
577
|
+
* receipt is unknown or already rolled back, or when any row no longer
|
|
578
|
+
* matches the repair's after image (a later edit is never overwritten). Only
|
|
579
|
+
* the rows the type-matrix trigger allows are restored; the rest are listed
|
|
580
|
+
* in `refused` with the reason, and refused outright when none can be.
|
|
581
|
+
*
|
|
582
|
+
* @param projectRoot - Project root.
|
|
583
|
+
* @param receiptId - `receiptId` of {@link applyTypeMatrixRepair}.
|
|
584
|
+
* @returns The restored rows and the rollback's audit row id.
|
|
585
|
+
* @example
|
|
586
|
+
* ```ts
|
|
587
|
+
* await rollbackTypeMatrixRepair(root, done.receiptId!);
|
|
588
|
+
* ```
|
|
589
|
+
*/
|
|
590
|
+
export async function rollbackTypeMatrixRepair(projectRoot, receiptId) {
|
|
591
|
+
const accessor = await getTaskAccessor(projectRoot);
|
|
592
|
+
const [receipt] = await accessor.queryAuditLog({
|
|
593
|
+
ids: [receiptId],
|
|
594
|
+
actions: [TYPE_MATRIX_REPAIR_ACTION],
|
|
595
|
+
limit: 1,
|
|
596
|
+
});
|
|
597
|
+
if (receipt === undefined) {
|
|
598
|
+
// @sync-invariant none:input-shape an unknown receipt id is refused before anything is written
|
|
599
|
+
throw new CleoError(ExitCode.NOT_FOUND, `No type-matrix repair has receipt ${receiptId}.`, {
|
|
600
|
+
details: { field: 'receiptId' },
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
const earlier = await accessor.queryAuditLog({
|
|
604
|
+
actions: [TYPE_MATRIX_ROLLBACK_ACTION],
|
|
605
|
+
since: receipt.timestamp,
|
|
606
|
+
limit: 1000,
|
|
607
|
+
});
|
|
608
|
+
if (earlier.some((row) => row.detailsJson?.includes(`"${receiptId}"`))) {
|
|
609
|
+
// @sync-invariant none:input-shape a receipt already rolled back is refused before anything is written
|
|
610
|
+
throw new CleoError(ExitCode.VALIDATION_ERROR, `Receipt ${receiptId} was already rolled back.`, {
|
|
611
|
+
details: { field: 'receiptId' },
|
|
612
|
+
});
|
|
613
|
+
}
|
|
614
|
+
const before = parseImages(receipt.beforeJson, receiptId);
|
|
615
|
+
const after = new Map(parseImages(receipt.afterJson, receiptId).map((r) => [r.id, r]));
|
|
616
|
+
const current = new Map((await accessor.loadTasks(before.map((r) => r.id))).map((t) => [t.id, t]));
|
|
617
|
+
const changed = before.filter((r) => {
|
|
618
|
+
const now = current.get(r.id);
|
|
619
|
+
const then = after.get(r.id);
|
|
620
|
+
return (now === undefined ||
|
|
621
|
+
then === undefined ||
|
|
622
|
+
(now.type ?? 'task') !== then.type ||
|
|
623
|
+
(now.parentId ?? null) !== then.parentId ||
|
|
624
|
+
(then.updatedAt !== undefined && versionOf(now) !== then.updatedAt));
|
|
625
|
+
});
|
|
626
|
+
if (changed.length > 0) {
|
|
627
|
+
// @sync-invariant none:local-only a rollback never overwrites a row edited after the repair
|
|
628
|
+
throw new CleoError(ExitCode.VERSION_CONFLICT, `Rows of receipt ${receiptId} changed after the repair (${changed.map((r) => r.id).join(', ')}); rolling back would overwrite that change.`, { details: { field: 'receiptId', changed: changed.map((r) => r.id) } });
|
|
629
|
+
}
|
|
630
|
+
const parentIds = before.flatMap((r) => (r.parentId === null ? [] : [r.parentId]));
|
|
631
|
+
const known = [...current.values(), ...(await accessor.loadTasks(parentIds))];
|
|
632
|
+
const typeOf = new Map(known.map((t) => [t.id, t.type ?? 'task']));
|
|
633
|
+
const childrenNow = new Map();
|
|
634
|
+
for (const row of before)
|
|
635
|
+
childrenNow.set(row.id, await accessor.getChildren(row.id));
|
|
636
|
+
const { restore, refused } = restorable(before, after, typeOf, childrenNow);
|
|
637
|
+
if (restore.length === 0) {
|
|
638
|
+
// @sync-invariant task.parent.type-matrix a rollback never restores a parent/child pair the trigger forbids
|
|
639
|
+
throw new CleoError(ExitCode.VALIDATION_ERROR, `Nothing in receipt ${receiptId} can be rolled back: ${refused.map((r) => r.reason).join('; ')}.`, { details: { field: 'receiptId', refused } });
|
|
640
|
+
}
|
|
641
|
+
const rollbackReceiptId = `type-matrix-rollback-${randomUUID()}`;
|
|
642
|
+
await accessor.transaction(async (tx) => {
|
|
643
|
+
// Parents first, so the trigger checks each child against its restored parent.
|
|
644
|
+
for (const row of restore) {
|
|
645
|
+
const now = current.get(row.id);
|
|
646
|
+
if (now === undefined)
|
|
647
|
+
continue;
|
|
648
|
+
await tx.updateTaskFields(row.id, { type: row.type, parentId: row.parentId }, { expectedUpdatedAt: versionOf(now) });
|
|
649
|
+
}
|
|
650
|
+
await tx.appendLog({
|
|
651
|
+
id: rollbackReceiptId,
|
|
652
|
+
action: TYPE_MATRIX_ROLLBACK_ACTION,
|
|
653
|
+
taskId: before[0]?.id ?? 'doctor',
|
|
654
|
+
actor: 'cleo doctor type-matrix',
|
|
655
|
+
details: { rolledBack: receiptId, refused },
|
|
656
|
+
before: restore.map((r) => after.get(r.id) ?? r),
|
|
657
|
+
after: restore,
|
|
658
|
+
});
|
|
659
|
+
});
|
|
660
|
+
return { receiptId, rollbackReceiptId, restored: restore.map((r) => r.id), refused };
|
|
661
|
+
}
|
|
662
|
+
//# sourceMappingURL=type-matrix-repair.js.map
|