chainlesschain 0.166.24 → 0.166.26
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 +52 -3
- package/package.json +1 -1
- package/src/assets/web-panel/assets/{AIOps-DR-MWcZc.js → AIOps-Z8_dnLd8.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-DT8B7sEb.js → ActionButton-C4bKCISV.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-asjh3dQJ.js → Analytics-GztAv2jy.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-DivQ491i.js → AppLayout-DEqNcuVx.js} +4 -4
- package/src/assets/web-panel/assets/{Artifacts-DPH2uUmy.js → Artifacts-D-R4Ehap.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-Bp2nLyc-.js → Audit-mzzdTIlM.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-CHS28khl.js → BackgroundAgents-DS2QupIC.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-CKeaFSfz.js → Backup-CQSirC39.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-CxwM9BAH.js → BaseInput-Bhuc-eOV.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-DYLDwaCv.js → Chat-B-RLVGnt.js} +6 -6
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-DdBJDBP0.js → ChatBubbleRenderer-CBbMoVuE.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-uYTWOB8j.js → Checkbox-DxwgBryW.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-CvYa_dwT.js → Codegen-Cwrm-7nh.js} +1 -1
- package/src/assets/web-panel/assets/{Col-CanMAX4m.js → Col-DrSCT-nF.js} +1 -1
- package/src/assets/web-panel/assets/{Community-B4WaDtHw.js → Community-DY2D9WNs.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-K93Uof_3.js → Compact-o-tV0URw.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-CKeDLgUW.js → Compliance-Bndh6S8f.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-P93grAeB.js → Cowork-CSNQODak.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-BSHnMtDn.js → Cron-CTgxRHdy.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-epAOpUVJ.js → Crosschain-BJoGQzDI.js} +1 -1
- package/src/assets/web-panel/assets/{DID-ByjXfP7t.js → DID-V0Ztm8ne.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-QztTU8tb.js → Dashboard-B-38THOj.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-DhUc9yhN.js → Dropdown-B3FdOwMO.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-C2sLhJJN.js → EmailListRenderer-YQDz4mUF.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-CdfbWNDn.js → FamilyGuardDashboard-DYE45IF9.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-CcF_R7NW.js → Federation-DjBFjdRZ.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-Cm_X2IBk.js → FormItemContext-CVTXEa0R.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-DUi4NWlb.js → GenericCardRenderer-B6Tkke8X.js} +1 -1
- package/src/assets/web-panel/assets/{Git-4Sq5FjBt.js → Git-CD0hyAzT.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-Dx2_v4V6.js → Governance-CWOQQJr8.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-7Vr1xekR.js → Inference-lHYwUk-C.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-Cnd2XOzf.js → KnowledgeGraph-Cledxv7t.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-B66QPVf-.js → Logs-D45mt-rM.js} +2 -2
- package/src/assets/web-panel/assets/{MarkdownRenderer-PLsEvhly.js → MarkdownRenderer-DJjPaOnM.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-BMIK9BmC.js → Marketplace-Yy30UZPw.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-BDbyp3J_.js → McpTools-Cil5l-bh.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-CJdzfZGi.js → Memory-DdJcnyqy.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-C2TH5Y3-.js → MobileBridge-IrzFYHEv.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-D1dYXkvV.js → MobileProjects-D8q64LMd.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-B-4A1I1j.js → Mtc-CpdTJzW7.js} +5 -5
- package/src/assets/web-panel/assets/{MtcAudit-Bgcc9UOl.js → MtcAudit-C7Tvw2m6.js} +2 -2
- package/src/assets/web-panel/assets/{Multisig-DqOe-etP.js → Multisig-Dmj9F5Kr.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-7VnhzE_6.js → NLProgramming-C9Xchob5.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-BxawpeUQ.js → Notes-Bzq6Yry5.js} +4 -4
- package/src/assets/web-panel/assets/{NotificationSettings-Cr08G2aE.js → NotificationSettings-DxG9eKb4.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-Ce8bEGlK.js → OrderTableRenderer-BS_IYoCe.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-BC7lveGv.js → Organization-kazsRvcq.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-CakOW-G0.js → Overflow-i4wP-hwn.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-Cf8CaOBZ.js → P2P-CfGeU3h-.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-Dc7b98W9.js → PdhVaultBrowser-CS-VhVUx.js} +5 -5
- package/src/assets/web-panel/assets/{Permissions-DGJyMEfS.js → Permissions-DnEHXs0I.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-DNIwSdwu.js → PersonalDataHub-DeD82qke.js} +6 -6
- package/src/assets/web-panel/assets/{Pipeline-5B1S12wa.js → Pipeline-CMGGLgBY.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-CMB6Ke5t.js → Privacy-CvHoFv32.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-D5PqTFiT.js → ProjectInit-DnJapCc1.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-CbKNdSjV.js → ProjectSettings-CqVdv3-g.js} +2 -2
- package/src/assets/web-panel/assets/Projects-B_o8vcs_.js +1 -0
- package/src/assets/web-panel/assets/{Providers-CJF5_-Gs.js → Providers-CPKseoDW.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-CT6iEAdi.js → QrScannerModal-_iBfKbPQ.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-Ci0h9mJ_.js → QuickAsk-CMrlZ69a.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-Da5mobZu.js → Recommend-BwDtSZ0l.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-BaCY8eey.js → RemoteSession-DomUAtn4.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-CaITfTsI.js → Reputation-Cuu2GCHr.js} +1 -1
- package/src/assets/web-panel/assets/{Row-DtUDamyP.js → Row-C7GVts31.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-DX27gFpg.js → RssFeed-DRKvgEog.js} +3 -3
- package/src/assets/web-panel/assets/{Search-DFL69osN.js → Search-BGI9wOeA.js} +1 -1
- package/src/assets/web-panel/assets/{Security-BqGeoWv-.js → Security-BlLaHy_L.js} +3 -3
- package/src/assets/web-panel/assets/{Services-3z1WnEAc.js → Services-DFqZbjmX.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-C_OJQQ9Z.js → Skeleton-BCGxdooP.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-BY7aT6Mo.js → Skills-CwFKgcmm.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-BZv7DSaD.js → Sla-CiXXh9eS.js} +1 -1
- package/src/assets/web-panel/assets/SpeechSettings-WSx2CLqO.js +1 -0
- package/src/assets/web-panel/assets/{SyncSettings-B6D7STlI.js → SyncSettings-BBIqQOtW.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-Co_FCZAO.js → Tasks-D1BGLjO1.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-CnY-9fgz.js → Templates-CUIl9axA.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-B71yXfZ8.js → Tenant-BFZJ0hNz.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-Dd_P7awo.js → Terminal-Cw-3eYVn.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-CvIEMyli.js → TimelineRenderer-bOVPB56L.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-DknFBGOi.js → Tokens-Czfyq2VY.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-D_4nJONB.js → Trigger-C-o4uJY0.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-BNl21fa7.js → Trust-BKq_S6iF.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-CFrCI7uK.js → UkeySign-fKlWUvMx.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-BVfY6JLg.js → VideoEditing-DB5MLuS7.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-Cj9CAK0J.js → Wallet-WGXBn7IS.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-DcqZlzA3.js → WebAuthn-B8TC4EMF.js} +4 -4
- package/src/assets/web-panel/assets/{WorkflowEditor-Dfr_dMld.js → WorkflowEditor-DLY--KmM.js} +1 -1
- package/src/assets/web-panel/assets/{chat-Yv6UF0AD.js → chat-Crw3XQaA.js} +1 -1
- package/src/assets/web-panel/assets/{colors-CDW_-1fs.js → colors-CxhWauPV.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-BYOQvd-6.js → compact-item-Cp3ojtk5.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-BCGO0BiP.js → createContext-CRuRXjSo.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-CKAQY2zn.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-DY-70cfQ.js → hasIn-XGW474dj.js} +1 -1
- package/src/assets/web-panel/assets/{index-B9ZogJJM.js → index-38TRdZb6.js} +1 -1
- package/src/assets/web-panel/assets/{index-BBs2mv5M.js → index-B45Vkh3T.js} +1 -1
- package/src/assets/web-panel/assets/{index-CCwWp3mT.js → index-B5fJS-kn.js} +1 -1
- package/src/assets/web-panel/assets/{index-CUo27q9J.js → index-BElgeKJ0.js} +3 -3
- package/src/assets/web-panel/assets/{index-CFa2fPCk.js → index-BUWZ57T4.js} +1 -1
- package/src/assets/web-panel/assets/{index-DCtsjhhT.js → index-BVmSfIkJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-NaCESahy.js → index-BW028Pwn.js} +1 -1
- package/src/assets/web-panel/assets/{index-CaT8trvf.js → index-BXnNbbwa.js} +1 -1
- package/src/assets/web-panel/assets/{index-DRFC2Qt0.js → index-BYkjs7eP.js} +1 -1
- package/src/assets/web-panel/assets/{index-Br3wQLVY.js → index-BetOkRLs.js} +1 -1
- package/src/assets/web-panel/assets/{index-DuIPq5MX.js → index-BuVA26SD.js} +1 -1
- package/src/assets/web-panel/assets/{index-C4UJA2ju.js → index-C73pqyDb.js} +1 -1
- package/src/assets/web-panel/assets/{index-BvK4fjkk.js → index-C8UOVaUH.js} +1 -1
- package/src/assets/web-panel/assets/{index-hZ0Eyrrw.js → index-CDIpH_8Q.js} +1 -1
- package/src/assets/web-panel/assets/{index-D--TEyDj.js → index-CPFam-me.js} +1 -1
- package/src/assets/web-panel/assets/{index-CUP-A2_h.js → index-CTE32qol.js} +1 -1
- package/src/assets/web-panel/assets/{index-CnZNbcux.js → index-CdlfrcC-.js} +1 -1
- package/src/assets/web-panel/assets/index-Cetfwq8j.js +1 -0
- package/src/assets/web-panel/assets/{index-C40UdtlS.js → index-Ctu98x57.js} +1 -1
- package/src/assets/web-panel/assets/{index-B9m5QAMg.js → index-DBcBsJYg.js} +1 -1
- package/src/assets/web-panel/assets/{index-BXlEx4UH.js → index-DHNixOg_.js} +1 -1
- package/src/assets/web-panel/assets/index-DOpcRO2-.js +1 -0
- package/src/assets/web-panel/assets/{index-Dg-AQYDf.js → index-DUSVsOGS.js} +1 -1
- package/src/assets/web-panel/assets/{index-CzeEEazr.js → index-De2FN0xN.js} +1 -1
- package/src/assets/web-panel/assets/{index-DUDoRk9r.js → index-DfPLIA-f.js} +1 -1
- package/src/assets/web-panel/assets/{index-Y9akzgFO.js → index-DhomnuaB.js} +1 -1
- package/src/assets/web-panel/assets/{index-366wfQBU.js → index-DoUC5_ry.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bb9nyts1.js → index-Dp2nC5On.js} +1 -1
- package/src/assets/web-panel/assets/{index-CIyHxV4u.js → index-DxOtBFVy.js} +1 -1
- package/src/assets/web-panel/assets/{index-BFv1hzF6.js → index-E-RSfImO.js} +1 -1
- package/src/assets/web-panel/assets/{index-DLOf7BSz.js → index-J_08R33H.js} +1 -1
- package/src/assets/web-panel/assets/{index-RjIMPG0u.js → index-QqAuH7-S.js} +1 -1
- package/src/assets/web-panel/assets/{index-CB_Iw73G.js → index-W8_0yAi_.js} +1 -1
- package/src/assets/web-panel/assets/{index-DM5c4pc-.js → index-WvNZXbIO.js} +1 -1
- package/src/assets/web-panel/assets/{index-DT_qVaaV.js → index-Yq3bFbYX.js} +1 -1
- package/src/assets/web-panel/assets/{index-NYVBUAVN.js → index-jrIcbPUa.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cy2NE6AO.js → index-nJR64pA_.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bf_Va0S-.js → index-qzmihx1d.js} +1 -1
- package/src/assets/web-panel/assets/{index-BayRTE5w.js → index-rsACoVT9.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-CG99CcR4.js → initDefaultProps-DAu-hgme.js} +1 -1
- package/src/assets/web-panel/assets/{motion-Z7MH0_0G.js → motion-Db8yk0Hu.js} +1 -1
- package/src/assets/web-panel/assets/{move-BX67lOdq.js → move-Cii2jOin.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-CmAbG1fE.js → mtc-parser-CnZBDkyZ.js} +1 -1
- package/src/assets/web-panel/assets/{omit-DiU5tI1_.js → omit-Buzt1FTL.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-BGfvLrYL.js → pickAttrs-CpZ_NmE6.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-CggLEgaJ.js → placementArrow-mBrx3dYm.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-t3nlghPZ.js → responsiveObserve-DC_QzljB.js} +1 -1
- package/src/assets/web-panel/assets/{slide-BkV4dL9s.js → slide-DT_UVED-.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-qec1PUoC.js → statusUtils-78t6eNLV.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-ChjX6T8D.js → styleChecker-CV_T1ULL.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-B1jLoZcM.js → useFlexGapSupport-s8edGuXD.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-C3X53s4E.js → useFs-Pk9dH2HC.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-DOvbbnkZ.js → usePersonalDataHub-B_GqAm8c.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-Ie0TEVFT.js → vnode-Cxyss24q.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-CRUMVS8l.js → zoom-0UY3cOuc.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/evolution-knowledge.js +40 -1
- package/src/commands/serve.js +11 -0
- package/src/data/changelog.json +29 -3
- package/src/generated/cc-agent-protocol.schema.json +3 -1
- package/src/lib/app-server/server.js +49 -0
- package/src/lib/app-server/websocket-transport.js +13 -0
- package/src/lib/evolution/agent-model-projection.js +252 -32
- package/src/lib/evolution/evidence-backed-wiki-maintainer.js +22 -9
- package/src/lib/evolution/evolution-artifact-ports.js +3 -0
- package/src/lib/evolution/evolution-deployment-loader.js +77 -0
- package/src/lib/evolution/evolution-evidence-projector.js +36 -5
- package/src/lib/evolution/evolution-ledger-ports.js +210 -18
- package/src/lib/evolution/governed-knowledge-dependency-inventory.js +392 -0
- package/src/lib/evolution/governed-knowledge-dependency-ledger-executor.js +123 -0
- package/src/lib/evolution/governed-knowledge-quarantine-release.js +1013 -0
- package/src/lib/evolution/governed-knowledge-revocation-host.js +59 -0
- package/src/lib/evolution/governed-knowledge-skill-rollback.js +62 -27
- package/src/lib/evolution/governed-knowledge-sync.js +164 -1
- package/src/lib/evolution/governed-knowledge-wiki-tombstone.js +88 -34
- package/src/lib/evolution/knowledge-wiki-source-admission.js +160 -1
- package/src/lib/evolution/knowledge-wiki-source-provenance.js +1 -0
- package/src/lib/evolution/skill-candidate-registry.js +21 -3
- package/src/lib/evolution/skill-release-registry.js +143 -0
- package/src/lib/evolution/wiki-maintainer-ledger-adapter.js +61 -0
- package/src/lib/evolution/wiki-target-tombstone-revision.js +15 -7
- package/src/lib/read-file-loop-guard.js +222 -0
- package/src/lib/read-file-page.js +55 -2
- package/src/repl/agent-repl.js +6 -1
- package/src/runtime/agent-core.js +177 -57
- package/src/assets/web-panel/assets/Projects-ccXaHzYL.js +0 -1
- package/src/assets/web-panel/assets/SpeechSettings-6lJKLhCn.js +0 -1
- package/src/assets/web-panel/assets/devWarning-H-kXSuf-.js +0 -1
- package/src/assets/web-panel/assets/index-B0o4MeNK.js +0 -1
- package/src/assets/web-panel/assets/index-BVafOL_I.js +0 -1
|
@@ -44,6 +44,10 @@ import {
|
|
|
44
44
|
GOVERNED_KNOWLEDGE_DEPENDENCY_PREPARED_EVENT_TYPE,
|
|
45
45
|
verifyGovernedKnowledgeDependencyPrepared,
|
|
46
46
|
} from "./governed-knowledge-dependency-ledger-executor.js";
|
|
47
|
+
import {
|
|
48
|
+
GOVERNED_KNOWLEDGE_QUARANTINE_RELEASE_EVENT_TYPE,
|
|
49
|
+
verifyGovernedKnowledgeQuarantineReleaseRecord,
|
|
50
|
+
} from "./governed-knowledge-quarantine-release.js";
|
|
47
51
|
import {
|
|
48
52
|
WIKI_LEDGER_EVENT_TYPE,
|
|
49
53
|
WikiMaintainerLedgerAdapter,
|
|
@@ -2588,16 +2592,166 @@ class EvolutionLedgerDomainPorts {
|
|
|
2588
2592
|
}
|
|
2589
2593
|
}
|
|
2590
2594
|
|
|
2595
|
+
*#knowledgeQuarantineReleases(snapshot, tenantId, audience) {
|
|
2596
|
+
const operations = new Set();
|
|
2597
|
+
for (const event of snapshot.events) {
|
|
2598
|
+
if (
|
|
2599
|
+
event.type !== GOVERNED_KNOWLEDGE_QUARANTINE_RELEASE_EVENT_TYPE ||
|
|
2600
|
+
event.tenantId !== tenantId
|
|
2601
|
+
) {
|
|
2602
|
+
continue;
|
|
2603
|
+
}
|
|
2604
|
+
if (
|
|
2605
|
+
event.schema !== EVOLUTION_LEDGER_DOMAIN_EVENT_SCHEMA ||
|
|
2606
|
+
event.artifactTenantId !== this.#artifactTenantId ||
|
|
2607
|
+
event.decision !== "committed" ||
|
|
2608
|
+
!Array.isArray(event.sourceRefs) ||
|
|
2609
|
+
event.sourceRefs.length !== 0
|
|
2610
|
+
) {
|
|
2611
|
+
throw portsError(
|
|
2612
|
+
EVOLUTION_LEDGER_PORTS_CORRUPT_CODE,
|
|
2613
|
+
"quarantine release event binding is invalid",
|
|
2614
|
+
);
|
|
2615
|
+
}
|
|
2616
|
+
const ref = normalizeArtifactRef(
|
|
2617
|
+
event.subjectRef,
|
|
2618
|
+
"quarantine release subjectRef",
|
|
2619
|
+
EVOLUTION_LEDGER_PORTS_CORRUPT_CODE,
|
|
2620
|
+
);
|
|
2621
|
+
const resolution = assertSynchronous(
|
|
2622
|
+
this.#artifactResolve(
|
|
2623
|
+
deepFreeze({
|
|
2624
|
+
epoch: event.epoch,
|
|
2625
|
+
ledgerId: event.ledgerId,
|
|
2626
|
+
ref,
|
|
2627
|
+
tenantId: this.#artifactTenantId,
|
|
2628
|
+
}),
|
|
2629
|
+
),
|
|
2630
|
+
"quarantine release artifact resolution",
|
|
2631
|
+
);
|
|
2632
|
+
assertAllExactRecord(
|
|
2633
|
+
resolution,
|
|
2634
|
+
LEDGER_RESOLUTION_KEYS,
|
|
2635
|
+
"quarantine release artifact resolution",
|
|
2636
|
+
EVOLUTION_LEDGER_PORTS_CORRUPT_CODE,
|
|
2637
|
+
);
|
|
2638
|
+
if (
|
|
2639
|
+
resolution.schema !== EVOLUTION_ARTIFACT_RESOLUTION_SCHEMA ||
|
|
2640
|
+
resolution.authenticated !== true ||
|
|
2641
|
+
resolution.found !== true ||
|
|
2642
|
+
resolution.ref !== ref.ref ||
|
|
2643
|
+
resolution.digest !== ref.digest
|
|
2644
|
+
) {
|
|
2645
|
+
throw portsError(
|
|
2646
|
+
EVOLUTION_LEDGER_PORTS_CORRUPT_CODE,
|
|
2647
|
+
"quarantine release artifact is not authenticated and exactly bound",
|
|
2648
|
+
);
|
|
2649
|
+
}
|
|
2650
|
+
const bytes = copyBytes(resolution.bytes);
|
|
2651
|
+
if (sha256(bytes) !== ref.digest) {
|
|
2652
|
+
throw portsError(
|
|
2653
|
+
EVOLUTION_LEDGER_PORTS_CORRUPT_CODE,
|
|
2654
|
+
"quarantine release artifact digest mismatch",
|
|
2655
|
+
);
|
|
2656
|
+
}
|
|
2657
|
+
let artifact;
|
|
2658
|
+
try {
|
|
2659
|
+
artifact = JSON.parse(bufferToString(bytes, "utf8"));
|
|
2660
|
+
} catch (cause) {
|
|
2661
|
+
throw portsError(
|
|
2662
|
+
EVOLUTION_LEDGER_PORTS_CORRUPT_CODE,
|
|
2663
|
+
"quarantine release artifact is not JSON",
|
|
2664
|
+
{ cause },
|
|
2665
|
+
);
|
|
2666
|
+
}
|
|
2667
|
+
assertAllExactRecord(
|
|
2668
|
+
artifact,
|
|
2669
|
+
ARTIFACT_RECORD_KEYS,
|
|
2670
|
+
"quarantine release artifact",
|
|
2671
|
+
EVOLUTION_LEDGER_PORTS_CORRUPT_CODE,
|
|
2672
|
+
);
|
|
2673
|
+
if (
|
|
2674
|
+
canonicalJson(artifact) !== bufferToString(bytes, "utf8") ||
|
|
2675
|
+
artifact.schema !== EVOLUTION_DURABLE_ARTIFACT_RECORD_SCHEMA ||
|
|
2676
|
+
artifact.tenantId !== this.#artifactTenantId ||
|
|
2677
|
+
artifact.type !== "governed-knowledge-quarantine-release" ||
|
|
2678
|
+
artifact.retention !== "ledger" ||
|
|
2679
|
+
artifact.purpose !== this.#purpose ||
|
|
2680
|
+
artifact.audience !== audience
|
|
2681
|
+
) {
|
|
2682
|
+
throw portsError(
|
|
2683
|
+
EVOLUTION_LEDGER_PORTS_CORRUPT_CODE,
|
|
2684
|
+
"quarantine release artifact boundary is invalid",
|
|
2685
|
+
);
|
|
2686
|
+
}
|
|
2687
|
+
let release;
|
|
2688
|
+
try {
|
|
2689
|
+
release = verifyGovernedKnowledgeQuarantineReleaseRecord(
|
|
2690
|
+
artifact.value,
|
|
2691
|
+
{ tenantId },
|
|
2692
|
+
);
|
|
2693
|
+
} catch (cause) {
|
|
2694
|
+
throw portsError(
|
|
2695
|
+
EVOLUTION_LEDGER_PORTS_CORRUPT_CODE,
|
|
2696
|
+
"quarantine release record is invalid",
|
|
2697
|
+
{ cause },
|
|
2698
|
+
);
|
|
2699
|
+
}
|
|
2700
|
+
if (
|
|
2701
|
+
event.eventId !==
|
|
2702
|
+
`${GOVERNED_KNOWLEDGE_QUARANTINE_RELEASE_EVENT_TYPE}.${release.operationDigest.slice(7)}` ||
|
|
2703
|
+
event.timestamp !== release.committedAt ||
|
|
2704
|
+
operations.has(release.operationDigest)
|
|
2705
|
+
) {
|
|
2706
|
+
throw portsError(
|
|
2707
|
+
EVOLUTION_LEDGER_PORTS_CORRUPT_CODE,
|
|
2708
|
+
"quarantine release event identity is invalid or ambiguous",
|
|
2709
|
+
);
|
|
2710
|
+
}
|
|
2711
|
+
operations.add(release.operationDigest);
|
|
2712
|
+
yield release;
|
|
2713
|
+
}
|
|
2714
|
+
}
|
|
2715
|
+
|
|
2591
2716
|
#assertReleaseNotRevoked(snapshot, binding, targetInput) {
|
|
2592
|
-
const
|
|
2717
|
+
const releases = new Map(
|
|
2718
|
+
[
|
|
2719
|
+
...this.#knowledgeQuarantineReleases(
|
|
2720
|
+
snapshot,
|
|
2721
|
+
binding.tenantId,
|
|
2722
|
+
binding.audience,
|
|
2723
|
+
),
|
|
2724
|
+
].map((release) => [release.operationDigest, release]),
|
|
2725
|
+
);
|
|
2726
|
+
const seenOperations = new Set();
|
|
2727
|
+
const releaseFor = (prepared) => {
|
|
2728
|
+
const release = releases.get(prepared.operationDigest) ?? null;
|
|
2729
|
+
if (!release) return null;
|
|
2730
|
+
seenOperations.add(prepared.operationDigest);
|
|
2731
|
+
const quarantines = prepared.knowledge.dependencies.filter(
|
|
2732
|
+
(dependency) => dependency.disposition === "quarantine",
|
|
2733
|
+
);
|
|
2593
2734
|
if (
|
|
2594
|
-
prepared.knowledge.
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
["reject-candidate", "quarantine"].includes(dependency.disposition),
|
|
2599
|
-
)
|
|
2735
|
+
release.knowledgeId !== prepared.knowledge.knowledgeId ||
|
|
2736
|
+
release.contentDigest !== prepared.knowledge.contentDigest ||
|
|
2737
|
+
release.preparedRecordDigest !== prepared.recordDigest ||
|
|
2738
|
+
canonicalJson(release.dependencies) !== canonicalJson(quarantines)
|
|
2600
2739
|
) {
|
|
2740
|
+
throw portsError(
|
|
2741
|
+
EVOLUTION_LEDGER_PORTS_CORRUPT_CODE,
|
|
2742
|
+
"quarantine release does not bind its prepared revocation",
|
|
2743
|
+
);
|
|
2744
|
+
}
|
|
2745
|
+
return release;
|
|
2746
|
+
};
|
|
2747
|
+
const assertCandidate = (prepared, candidateId) => {
|
|
2748
|
+
const blocked = prepared.knowledge.dependencies.find(
|
|
2749
|
+
(dependency) =>
|
|
2750
|
+
dependency.kind === "candidate" &&
|
|
2751
|
+
dependency.digest === candidateId &&
|
|
2752
|
+
["reject-candidate", "quarantine"].includes(dependency.disposition),
|
|
2753
|
+
);
|
|
2754
|
+
if (blocked) {
|
|
2601
2755
|
throw portsError(
|
|
2602
2756
|
EVOLUTION_LEDGER_CANDIDATE_REVOKED_CODE,
|
|
2603
2757
|
"candidate activation is blocked by a durable knowledge revocation",
|
|
@@ -2643,6 +2797,7 @@ class EvolutionLedgerDomainPorts {
|
|
|
2643
2797
|
binding.tenantId,
|
|
2644
2798
|
binding.audience,
|
|
2645
2799
|
)) {
|
|
2800
|
+
const release = releaseFor(prepared);
|
|
2646
2801
|
assertCandidate(prepared, binding.candidateId);
|
|
2647
2802
|
readTarget();
|
|
2648
2803
|
assertCandidate(prepared, target.candidateId);
|
|
@@ -2652,20 +2807,29 @@ class EvolutionLedgerDomainPorts {
|
|
|
2652
2807
|
binding,
|
|
2653
2808
|
target,
|
|
2654
2809
|
knowledge,
|
|
2810
|
+
release,
|
|
2655
2811
|
);
|
|
2656
2812
|
const sourceRef = `knowledge://${encodeURIComponent(binding.tenantId)}/${encodeURIComponent(knowledge.knowledgeId)}`;
|
|
2657
|
-
const
|
|
2813
|
+
const targetDependencies = knowledge.dependencies.filter(
|
|
2658
2814
|
(dependency) =>
|
|
2659
|
-
dependency.kind === "active-skill" &&
|
|
2660
|
-
|
|
2815
|
+
(dependency.kind === "active-skill" &&
|
|
2816
|
+
dependency.digest === target.releaseDigest) ||
|
|
2817
|
+
(dependency.kind === "candidate" &&
|
|
2818
|
+
dependency.digest === target.candidateId),
|
|
2661
2819
|
);
|
|
2820
|
+
// Immutable candidate/release identities named by the quarantine remain
|
|
2821
|
+
// fenced forever. A release restores only the reviewed Wiki source so a
|
|
2822
|
+
// newly evaluated successor can traverse the ordinary promotion gates.
|
|
2823
|
+
const revokedRelease = targetDependencies.length > 0;
|
|
2824
|
+
const supersedingTarget = release !== null && !revokedRelease;
|
|
2662
2825
|
if (
|
|
2663
2826
|
revokedRelease ||
|
|
2664
2827
|
wiki?.unsafePatternIds.length ||
|
|
2665
|
-
|
|
2666
|
-
(
|
|
2667
|
-
item
|
|
2668
|
-
|
|
2828
|
+
(!supersedingTarget &&
|
|
2829
|
+
target.candidate.sourceEvidenceRefs.some(
|
|
2830
|
+
(item) =>
|
|
2831
|
+
item.ref === sourceRef || item.digest === knowledge.contentDigest,
|
|
2832
|
+
))
|
|
2669
2833
|
) {
|
|
2670
2834
|
throw portsError(
|
|
2671
2835
|
EVOLUTION_LEDGER_SOURCE_REVOKED_CODE,
|
|
@@ -2678,12 +2842,18 @@ class EvolutionLedgerDomainPorts {
|
|
|
2678
2842
|
);
|
|
2679
2843
|
}
|
|
2680
2844
|
}
|
|
2845
|
+
if (seenOperations.size !== releases.size) {
|
|
2846
|
+
throw portsError(
|
|
2847
|
+
EVOLUTION_LEDGER_PORTS_CORRUPT_CODE,
|
|
2848
|
+
"quarantine release has no authenticated prepared revocation",
|
|
2849
|
+
);
|
|
2850
|
+
}
|
|
2681
2851
|
// Bare legacy calls remain compatible only before any Knowledge revocation.
|
|
2682
2852
|
// Once a fence exists, missing provenance must not mean unrelated/safe.
|
|
2683
2853
|
if (targetInput !== undefined) readTarget();
|
|
2684
2854
|
}
|
|
2685
2855
|
|
|
2686
|
-
#releaseWikiProvenance(snapshot, binding, target, knowledge) {
|
|
2856
|
+
#releaseWikiProvenance(snapshot, binding, target, knowledge, release = null) {
|
|
2687
2857
|
if (target.candidate.derivationMode === "wiki") {
|
|
2688
2858
|
// Discover the original run on the same authenticated ledger. Neither a
|
|
2689
2859
|
// configurable reader nor a self-reported safe subset establishes origin.
|
|
@@ -2720,9 +2890,8 @@ class EvolutionLedgerDomainPorts {
|
|
|
2720
2890
|
},
|
|
2721
2891
|
ledgerArtifactResolver: this.#artifactResolve,
|
|
2722
2892
|
});
|
|
2723
|
-
const
|
|
2724
|
-
|
|
2725
|
-
).readKnowledgeProvenance({
|
|
2893
|
+
const reader = captureWikiRevisionReader(adapter);
|
|
2894
|
+
const revision = reader.readKnowledgeProvenance({
|
|
2726
2895
|
tenantId: binding.tenantId,
|
|
2727
2896
|
revisionId: target.candidate.wikiRevision,
|
|
2728
2897
|
knowledgeId: knowledge.knowledgeId,
|
|
@@ -2741,6 +2910,29 @@ class EvolutionLedgerDomainPorts {
|
|
|
2741
2910
|
"ledger changed while checking immutable Wiki provenance",
|
|
2742
2911
|
);
|
|
2743
2912
|
}
|
|
2913
|
+
const releasedDependency = release?.dependencies.find(
|
|
2914
|
+
(dependency) =>
|
|
2915
|
+
dependency.kind === "wiki" &&
|
|
2916
|
+
dependency.disposition === "quarantine" &&
|
|
2917
|
+
dependency.digest === revision.stateDigest,
|
|
2918
|
+
);
|
|
2919
|
+
if (releasedDependency) {
|
|
2920
|
+
const current = reader.loadWiki();
|
|
2921
|
+
const restored = revision.affectedPatternIds.every(
|
|
2922
|
+
(patternId) =>
|
|
2923
|
+
current.state.patterns[patternId]?.status !== "quarantined" &&
|
|
2924
|
+
current.state.evolutionLog.some(
|
|
2925
|
+
(entry) =>
|
|
2926
|
+
entry.type === "pattern-quarantine-released" &&
|
|
2927
|
+
entry.subjectId === patternId &&
|
|
2928
|
+
entry.details?.operationDigest === release.operationDigest &&
|
|
2929
|
+
entry.details?.releaseRecordDigest === release.recordDigest,
|
|
2930
|
+
),
|
|
2931
|
+
);
|
|
2932
|
+
if (restored) {
|
|
2933
|
+
return deepFreeze({ ...revision, unsafePatternIds: [] });
|
|
2934
|
+
}
|
|
2935
|
+
}
|
|
2744
2936
|
return revision;
|
|
2745
2937
|
}
|
|
2746
2938
|
return null;
|
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { types as utilTypes } from "node:util";
|
|
3
|
+
import { captureSkillCandidateRegistryReader } from "./skill-candidate-registry.js";
|
|
4
|
+
import { captureSkillReleaseRegistryReader } from "./skill-release-registry.js";
|
|
5
|
+
import { verifyGovernedKnowledgeRecord } from "./governed-knowledge-record.js";
|
|
6
|
+
import { governedKnowledgeSourceRef } from "./governed-knowledge-skill-rollback.js";
|
|
7
|
+
import { captureWikiRevisionReader } from "./wiki-maintainer-ledger-adapter.js";
|
|
8
|
+
|
|
9
|
+
export const GOVERNED_KNOWLEDGE_DEPENDENCY_INVENTORY_SCHEMA =
|
|
10
|
+
"chainlesschain.governed-knowledge-dependency-inventory/v1";
|
|
11
|
+
const DIGEST = /^sha256:[a-f0-9]{64}$/u;
|
|
12
|
+
const ID = /^[A-Za-z0-9][A-Za-z0-9._:@/-]{0,255}$/u;
|
|
13
|
+
const PLANNERS = new WeakMap();
|
|
14
|
+
const PLANS = new WeakMap();
|
|
15
|
+
|
|
16
|
+
function canonical(value) {
|
|
17
|
+
if (value === null || typeof value !== "object") return JSON.stringify(value);
|
|
18
|
+
if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
|
|
19
|
+
return `{${Object.keys(value)
|
|
20
|
+
.sort()
|
|
21
|
+
.map((key) => `${JSON.stringify(key)}:${canonical(value[key])}`)
|
|
22
|
+
.join(",")}}`;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function freeze(value) {
|
|
26
|
+
if (value && typeof value === "object" && !Object.isFrozen(value)) {
|
|
27
|
+
Object.freeze(value);
|
|
28
|
+
for (const child of Object.values(value)) freeze(child);
|
|
29
|
+
}
|
|
30
|
+
return value;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function fail(message) {
|
|
34
|
+
throw new Error(`knowledge dependency inventory: ${message}`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function dataRecord(value, label) {
|
|
38
|
+
if (
|
|
39
|
+
!value ||
|
|
40
|
+
typeof value !== "object" ||
|
|
41
|
+
Array.isArray(value) ||
|
|
42
|
+
utilTypes.isProxy(value) ||
|
|
43
|
+
![Object.prototype, null].includes(Object.getPrototypeOf(value))
|
|
44
|
+
) {
|
|
45
|
+
throw new TypeError(`${label} must be a plain data object`);
|
|
46
|
+
}
|
|
47
|
+
for (const key of Reflect.ownKeys(value)) {
|
|
48
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
49
|
+
if (typeof key !== "string" || !Object.hasOwn(descriptor, "value")) {
|
|
50
|
+
throw new TypeError(`${label} must contain only data properties`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return value;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function sameHead(left, right) {
|
|
57
|
+
return [
|
|
58
|
+
"epoch",
|
|
59
|
+
"ledgerId",
|
|
60
|
+
"identityDigest",
|
|
61
|
+
"sequence",
|
|
62
|
+
"headDigest",
|
|
63
|
+
].every((key) => left?.[key] === right?.[key]);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function orderWikiRuns(affectedRuns, inventories, revisionOwners) {
|
|
67
|
+
const edges = new Map([...affectedRuns].map((run) => [run, new Set()]));
|
|
68
|
+
for (const [run, inventory] of inventories) {
|
|
69
|
+
if (!affectedRuns.has(run)) continue;
|
|
70
|
+
for (const revision of inventory.revisions) {
|
|
71
|
+
for (const sourceRevisionId of revision.wikiSourceRevisionIds) {
|
|
72
|
+
const parent = revisionOwners.get(sourceRevisionId);
|
|
73
|
+
if (parent && parent !== run && affectedRuns.has(parent)) {
|
|
74
|
+
edges.get(run).add(parent);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const indegree = new Map([...affectedRuns].map((run) => [run, 0]));
|
|
80
|
+
for (const parents of edges.values()) {
|
|
81
|
+
for (const parent of parents)
|
|
82
|
+
indegree.set(parent, indegree.get(parent) + 1);
|
|
83
|
+
}
|
|
84
|
+
const ready = [...affectedRuns]
|
|
85
|
+
.filter((run) => indegree.get(run) === 0)
|
|
86
|
+
.sort();
|
|
87
|
+
const ordered = [];
|
|
88
|
+
while (ready.length > 0) {
|
|
89
|
+
const run = ready.shift();
|
|
90
|
+
ordered.push(run);
|
|
91
|
+
for (const parent of [...edges.get(run)].sort()) {
|
|
92
|
+
indegree.set(parent, indegree.get(parent) - 1);
|
|
93
|
+
if (indegree.get(parent) === 0) {
|
|
94
|
+
ready.push(parent);
|
|
95
|
+
ready.sort();
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (ordered.length !== affectedRuns.size)
|
|
100
|
+
fail("Wiki source graph contains a cycle");
|
|
101
|
+
return ordered;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function buildGovernedKnowledgeDependencyInventory({
|
|
105
|
+
tenantId,
|
|
106
|
+
knowledgeId,
|
|
107
|
+
contentDigest,
|
|
108
|
+
candidateRegistry,
|
|
109
|
+
releaseRegistry,
|
|
110
|
+
wikiAdapters,
|
|
111
|
+
activeDisposition = "rollback-active",
|
|
112
|
+
candidateDisposition = "reject-candidate",
|
|
113
|
+
wikiDisposition = "tombstone",
|
|
114
|
+
} = {}) {
|
|
115
|
+
if (
|
|
116
|
+
typeof tenantId !== "string" ||
|
|
117
|
+
!ID.test(tenantId) ||
|
|
118
|
+
typeof knowledgeId !== "string" ||
|
|
119
|
+
!ID.test(knowledgeId) ||
|
|
120
|
+
!DIGEST.test(contentDigest ?? "") ||
|
|
121
|
+
!Array.isArray(wikiAdapters) ||
|
|
122
|
+
wikiAdapters.length < 1 ||
|
|
123
|
+
wikiAdapters.length > 64 ||
|
|
124
|
+
!["rollback-active", "quarantine"].includes(activeDisposition) ||
|
|
125
|
+
!["reject-candidate", "quarantine"].includes(candidateDisposition) ||
|
|
126
|
+
!["tombstone", "quarantine"].includes(wikiDisposition)
|
|
127
|
+
) {
|
|
128
|
+
throw new TypeError(
|
|
129
|
+
"bounded knowledge dependency inventory input is required",
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
const candidates = captureSkillCandidateRegistryReader(candidateRegistry);
|
|
133
|
+
const releases = captureSkillReleaseRegistryReader(releaseRegistry);
|
|
134
|
+
if (candidates.tenantId !== tenantId || releases.tenantId !== tenantId) {
|
|
135
|
+
throw new TypeError("knowledge dependency inventory tenant differs");
|
|
136
|
+
}
|
|
137
|
+
const readers = wikiAdapters.map(captureWikiRevisionReader);
|
|
138
|
+
if (readers.some((reader) => reader.descriptor.tenantId !== tenantId)) {
|
|
139
|
+
throw new TypeError("knowledge dependency Wiki tenant differs");
|
|
140
|
+
}
|
|
141
|
+
const inventories = new Map();
|
|
142
|
+
let ledgerHead = null;
|
|
143
|
+
let discoveredRuns = null;
|
|
144
|
+
const revisionOwners = new Map();
|
|
145
|
+
for (const reader of readers) {
|
|
146
|
+
const run = reader.descriptor.evolutionRunId;
|
|
147
|
+
if (inventories.has(run)) fail("duplicate Wiki run reader");
|
|
148
|
+
const inventory = reader.readInventory();
|
|
149
|
+
if (ledgerHead && !sameHead(ledgerHead, inventory.ledgerHead)) {
|
|
150
|
+
fail("Wiki inventories do not share one stable ledger head");
|
|
151
|
+
}
|
|
152
|
+
if (
|
|
153
|
+
discoveredRuns &&
|
|
154
|
+
canonical(discoveredRuns) !== canonical(inventory.tenantWikiRunIds)
|
|
155
|
+
) {
|
|
156
|
+
fail("Wiki inventories disagree about tenant run completeness");
|
|
157
|
+
}
|
|
158
|
+
ledgerHead = inventory.ledgerHead;
|
|
159
|
+
discoveredRuns = inventory.tenantWikiRunIds;
|
|
160
|
+
inventories.set(run, { inventory, reader });
|
|
161
|
+
for (const revision of inventory.revisions) {
|
|
162
|
+
if (revisionOwners.has(revision.revisionId))
|
|
163
|
+
fail("duplicate Wiki revision identity");
|
|
164
|
+
revisionOwners.set(revision.revisionId, run);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
const suppliedRuns = [...inventories.keys()].sort();
|
|
168
|
+
if (canonical(suppliedRuns) !== canonical(discoveredRuns)) {
|
|
169
|
+
fail("Wiki run manifest is incomplete for this tenant ledger");
|
|
170
|
+
}
|
|
171
|
+
const sourceRef = governedKnowledgeSourceRef({ tenantId, knowledgeId });
|
|
172
|
+
const proofs = new Map();
|
|
173
|
+
const wikiProof = (candidate) => {
|
|
174
|
+
if (candidate.derivationMode !== "wiki") return null;
|
|
175
|
+
const run = revisionOwners.get(candidate.wikiRevision);
|
|
176
|
+
if (!run) fail("Wiki-derived artifact references an unconfigured revision");
|
|
177
|
+
if (!proofs.has(candidate.wikiRevision)) {
|
|
178
|
+
proofs.set(
|
|
179
|
+
candidate.wikiRevision,
|
|
180
|
+
inventories.get(run).reader.readKnowledgeProvenance({
|
|
181
|
+
tenantId,
|
|
182
|
+
revisionId: candidate.wikiRevision,
|
|
183
|
+
knowledgeId,
|
|
184
|
+
contentDigest,
|
|
185
|
+
}),
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
return proofs.get(candidate.wikiRevision);
|
|
189
|
+
};
|
|
190
|
+
const depends = (candidate) => {
|
|
191
|
+
const proof = wikiProof(candidate);
|
|
192
|
+
return (
|
|
193
|
+
candidate.sourceEvidenceRefs.some(
|
|
194
|
+
(entry) => entry.ref === sourceRef && entry.digest === contentDigest,
|
|
195
|
+
) || (proof?.affectedPatternIds.length ?? 0) > 0
|
|
196
|
+
);
|
|
197
|
+
};
|
|
198
|
+
const unsafe = (candidate) => {
|
|
199
|
+
const proof = wikiProof(candidate);
|
|
200
|
+
return (
|
|
201
|
+
candidate.sourceEvidenceRefs.some(
|
|
202
|
+
(entry) => entry.ref === sourceRef || entry.digest === contentDigest,
|
|
203
|
+
) || (proof?.unsafePatternIds.length ?? 0) > 0
|
|
204
|
+
);
|
|
205
|
+
};
|
|
206
|
+
const candidateItems = candidates.readInventory();
|
|
207
|
+
const releaseItems = releases.readInventory();
|
|
208
|
+
const releasesByDigest = new Map(
|
|
209
|
+
releaseItems.releases.map((release) => [release.releaseDigest, release]),
|
|
210
|
+
);
|
|
211
|
+
const affectedActive = releaseItems.active.filter(({ release }) =>
|
|
212
|
+
depends(release.candidate),
|
|
213
|
+
);
|
|
214
|
+
for (const { state, release } of affectedActive) {
|
|
215
|
+
const lastKnownGood = releasesByDigest.get(
|
|
216
|
+
state.lastKnownGoodReleaseDigest,
|
|
217
|
+
);
|
|
218
|
+
if (
|
|
219
|
+
!lastKnownGood ||
|
|
220
|
+
lastKnownGood.releaseDigest === release.releaseDigest ||
|
|
221
|
+
lastKnownGood.skillName !== release.skillName ||
|
|
222
|
+
unsafe(lastKnownGood.candidate)
|
|
223
|
+
) {
|
|
224
|
+
fail(
|
|
225
|
+
"affected active Skill has no safe distinct last-known-good release",
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
const activeDependencies = affectedActive.map(({ release }) => ({
|
|
230
|
+
kind: "active-skill",
|
|
231
|
+
digest: release.releaseDigest,
|
|
232
|
+
disposition: activeDisposition,
|
|
233
|
+
}));
|
|
234
|
+
const candidateDependencies = candidateItems
|
|
235
|
+
.filter(depends)
|
|
236
|
+
.map((candidate) => ({
|
|
237
|
+
kind: "candidate",
|
|
238
|
+
digest: candidate.candidateId,
|
|
239
|
+
disposition: candidateDisposition,
|
|
240
|
+
}));
|
|
241
|
+
const wikiTargets = new Map();
|
|
242
|
+
for (const [run, { inventory, reader }] of inventories) {
|
|
243
|
+
for (const revision of inventory.revisions) {
|
|
244
|
+
const proof = reader.readKnowledgeProvenance({
|
|
245
|
+
tenantId,
|
|
246
|
+
revisionId: revision.revisionId,
|
|
247
|
+
knowledgeId,
|
|
248
|
+
contentDigest,
|
|
249
|
+
});
|
|
250
|
+
if (proof.affectedPatternIds.length > 0) {
|
|
251
|
+
wikiTargets.set(run, revision.stateDigest);
|
|
252
|
+
break;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
const wikiDependencies = orderWikiRuns(
|
|
257
|
+
new Set(wikiTargets.keys()),
|
|
258
|
+
new Map([...inventories].map(([run, value]) => [run, value.inventory])),
|
|
259
|
+
revisionOwners,
|
|
260
|
+
).map((run) => ({
|
|
261
|
+
kind: "wiki",
|
|
262
|
+
digest: wikiTargets.get(run),
|
|
263
|
+
disposition: wikiDisposition,
|
|
264
|
+
}));
|
|
265
|
+
const dependencies = [
|
|
266
|
+
...activeDependencies.sort((a, b) => a.digest.localeCompare(b.digest)),
|
|
267
|
+
...candidateDependencies.sort((a, b) => a.digest.localeCompare(b.digest)),
|
|
268
|
+
...wikiDependencies,
|
|
269
|
+
];
|
|
270
|
+
if (dependencies.length < 1 || dependencies.length > 256) {
|
|
271
|
+
fail("dependency result is empty or exceeds the governed record bound");
|
|
272
|
+
}
|
|
273
|
+
const finalCandidates = candidates.readInventory();
|
|
274
|
+
const finalReleases = releases.readInventory();
|
|
275
|
+
if (
|
|
276
|
+
canonical(candidateItems.map(({ candidateId }) => candidateId)) !==
|
|
277
|
+
canonical(finalCandidates.map(({ candidateId }) => candidateId)) ||
|
|
278
|
+
canonical(
|
|
279
|
+
releaseItems.releases.map(({ releaseDigest }) => releaseDigest),
|
|
280
|
+
) !==
|
|
281
|
+
canonical(
|
|
282
|
+
finalReleases.releases.map(({ releaseDigest }) => releaseDigest),
|
|
283
|
+
) ||
|
|
284
|
+
canonical(releaseItems.active.map(({ state }) => state.stateDigest)) !==
|
|
285
|
+
canonical(finalReleases.active.map(({ state }) => state.stateDigest))
|
|
286
|
+
) {
|
|
287
|
+
fail("Candidate or Release inventory changed while planning");
|
|
288
|
+
}
|
|
289
|
+
for (const [run, { inventory, reader }] of inventories) {
|
|
290
|
+
const finalInventory = reader.readInventory();
|
|
291
|
+
if (canonical(inventory) !== canonical(finalInventory)) {
|
|
292
|
+
fail(`Wiki inventory changed while planning run ${run}`);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
const core = {
|
|
296
|
+
schema: GOVERNED_KNOWLEDGE_DEPENDENCY_INVENTORY_SCHEMA,
|
|
297
|
+
tenantId,
|
|
298
|
+
knowledgeId,
|
|
299
|
+
contentDigest,
|
|
300
|
+
activeDisposition,
|
|
301
|
+
candidateDisposition,
|
|
302
|
+
wikiDisposition,
|
|
303
|
+
ledgerHead,
|
|
304
|
+
wikiRuns: suppliedRuns,
|
|
305
|
+
dependencies,
|
|
306
|
+
};
|
|
307
|
+
return freeze({
|
|
308
|
+
...core,
|
|
309
|
+
inventoryDigest: `sha256:${createHash("sha256").update(canonical(core)).digest("hex")}`,
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
export class GovernedKnowledgeDependencyInventoryPlanner {
|
|
314
|
+
constructor(options = {}) {
|
|
315
|
+
const tenantId = options.tenantId;
|
|
316
|
+
if (typeof tenantId !== "string" || !ID.test(tenantId)) {
|
|
317
|
+
throw new TypeError("dependency inventory planner tenantId is invalid");
|
|
318
|
+
}
|
|
319
|
+
// Capture and validate genuine sources now; the builder rechecks them on
|
|
320
|
+
// every plan so a substituted object can never inherit this authority.
|
|
321
|
+
const candidates = captureSkillCandidateRegistryReader(
|
|
322
|
+
options.candidateRegistry,
|
|
323
|
+
);
|
|
324
|
+
const releases = captureSkillReleaseRegistryReader(options.releaseRegistry);
|
|
325
|
+
const wikiAdapters = Array.isArray(options.wikiAdapters)
|
|
326
|
+
? [...options.wikiAdapters]
|
|
327
|
+
: null;
|
|
328
|
+
if (
|
|
329
|
+
candidates.tenantId !== tenantId ||
|
|
330
|
+
releases.tenantId !== tenantId ||
|
|
331
|
+
!wikiAdapters ||
|
|
332
|
+
wikiAdapters.length < 1 ||
|
|
333
|
+
wikiAdapters.length > 64
|
|
334
|
+
) {
|
|
335
|
+
throw new TypeError("dependency inventory planner sources are invalid");
|
|
336
|
+
}
|
|
337
|
+
wikiAdapters.forEach(captureWikiRevisionReader);
|
|
338
|
+
PLANNERS.set(
|
|
339
|
+
this,
|
|
340
|
+
Object.freeze({
|
|
341
|
+
tenantId,
|
|
342
|
+
candidateRegistry: options.candidateRegistry,
|
|
343
|
+
releaseRegistry: options.releaseRegistry,
|
|
344
|
+
wikiAdapters: Object.freeze(wikiAdapters),
|
|
345
|
+
activeDisposition: options.activeDisposition,
|
|
346
|
+
candidateDisposition: options.candidateDisposition,
|
|
347
|
+
wikiDisposition: options.wikiDisposition,
|
|
348
|
+
}),
|
|
349
|
+
);
|
|
350
|
+
Object.freeze(this);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
plan(input) {
|
|
354
|
+
const options = PLANNERS.get(this);
|
|
355
|
+
if (!options)
|
|
356
|
+
throw new TypeError("a genuine dependency planner is required");
|
|
357
|
+
dataRecord(input, "knowledge revocation draft");
|
|
358
|
+
if (
|
|
359
|
+
!["tombstone", "revoke"].includes(input.action) ||
|
|
360
|
+
Object.hasOwn(input, "dependencies")
|
|
361
|
+
) {
|
|
362
|
+
throw new TypeError(
|
|
363
|
+
"revocation draft must not supply its own dependency plan",
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
const inventory = buildGovernedKnowledgeDependencyInventory({
|
|
367
|
+
...options,
|
|
368
|
+
knowledgeId: input.knowledgeId,
|
|
369
|
+
contentDigest: input.contentDigest,
|
|
370
|
+
});
|
|
371
|
+
const knowledge = verifyGovernedKnowledgeRecord(
|
|
372
|
+
{ ...structuredClone(input), dependencies: inventory.dependencies },
|
|
373
|
+
{ tenantId: options.tenantId },
|
|
374
|
+
);
|
|
375
|
+
const result = freeze({ inventory, knowledge });
|
|
376
|
+
PLANS.set(result, this);
|
|
377
|
+
return result;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
Object.freeze(GovernedKnowledgeDependencyInventoryPlanner.prototype);
|
|
382
|
+
|
|
383
|
+
export function isGovernedKnowledgeDependencyInventoryPlanner(value) {
|
|
384
|
+
return PLANNERS.has(value);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
export function captureGovernedKnowledgeDependencyInventoryPlan(planner, plan) {
|
|
388
|
+
if (!PLANNERS.has(planner) || PLANS.get(plan) !== planner) {
|
|
389
|
+
throw new TypeError("a genuine dependency inventory plan is required");
|
|
390
|
+
}
|
|
391
|
+
return plan;
|
|
392
|
+
}
|