@iowarp/clio-coder 0.4.1 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +92 -0
- package/CONTRIBUTING.md +59 -36
- package/README.md +404 -472
- package/SECURITY.md +2 -1
- package/dist/{acp-ZILU3AUO.js → acp-TMDQZDIG.js} +7 -7
- package/dist/{agents-HYWGBGQR.js → agents-5N5NG3XG.js} +28 -28
- package/dist/assets/codewiki.json +1 -1
- package/dist/{auth-N3QT7CBO.js → auth-Z5CCBXKQ.js} +8 -9
- package/dist/{builtins-UJLMOVOV.js → builtins-K6TNDT24.js} +4 -4
- package/dist/{chunk-GVQJ5CCZ.js → chunk-2HFQNRV3.js} +7 -7
- package/dist/{chunk-QMXC4JB7.js → chunk-2NHR3NAY.js} +163 -1401
- package/dist/chunk-2X4RYJTJ.js +39 -0
- package/dist/{chunk-Y45G3AXC.js → chunk-2Z2IKEXI.js} +6 -10
- package/dist/{chunk-EIMVLWB3.js → chunk-34BHNEE3.js} +7 -3
- package/dist/{chunk-GIZNH63R.js → chunk-35MSIRKH.js} +9 -4
- package/dist/chunk-3EBYEESD.js +314 -0
- package/dist/{chunk-CTJ4RNAA.js → chunk-3F7VUY77.js} +2 -2
- package/dist/{chunk-AP73CFDC.js → chunk-3KIPBMUA.js} +2 -2
- package/dist/{chunk-J5LZHVIT.js → chunk-3M6DQK6S.js} +113 -35
- package/dist/{chunk-VEGN6WIQ.js → chunk-462T4EGZ.js} +2 -2
- package/dist/{chunk-AFKWHWXF.js → chunk-4JDLP6ZS.js} +33 -16
- package/dist/{chunk-6FN3E6KX.js → chunk-4O6MANBS.js} +2 -2
- package/dist/{chunk-AKB4GYDL.js → chunk-54ODD65L.js} +5 -5
- package/dist/{chunk-BBTJOK6Y.js → chunk-5KW52TEP.js} +3 -3
- package/dist/{chunk-6CCS4G3W.js → chunk-5PFYMY2V.js} +2 -2
- package/dist/chunk-77QIVUZB.js +1334 -0
- package/dist/{chunk-7OBGU7UB.js → chunk-7BHIY2MW.js} +7 -13
- package/dist/{chunk-3QSOM6PA.js → chunk-AZ4WMN4W.js} +2 -2
- package/dist/{chunk-6NJQITNH.js → chunk-B74PXLU7.js} +6 -3
- package/dist/{chunk-R23Z6K6I.js → chunk-B7HM5Z7T.js} +15 -15
- package/dist/{chunk-R32CLGZ6.js → chunk-BO7Y52RY.js} +81 -20
- package/dist/{chunk-UEDMSP56.js → chunk-BYMNWQ7O.js} +123 -148
- package/dist/{chunk-ZJLUDYFY.js → chunk-CRFOIAX3.js} +4 -4
- package/dist/{chunk-2NM363SV.js → chunk-CYZW7JHJ.js} +7 -7
- package/dist/{chunk-6HMJX2VU.js → chunk-DYHAXKHD.js} +38 -10
- package/dist/{chunk-THYWACCR.js → chunk-DZAW46HP.js} +3 -3
- package/dist/{chunk-FYUN5KZ3.js → chunk-DZEK6CJN.js} +17 -17
- package/dist/{chunk-3I5NY75V.js → chunk-E7GT7O5N.js} +5 -5
- package/dist/{chunk-VKFQTNDV.js → chunk-F2I26BDK.js} +4 -4
- package/dist/{chunk-HLW2MRKE.js → chunk-F4EKGO4N.js} +3 -1
- package/dist/{chunk-IXJT6DCX.js → chunk-FVDGR2ZL.js} +3 -3
- package/dist/{chunk-TZSKNMZG.js → chunk-GTUD2WMY.js} +2 -1
- package/dist/{chunk-7EPLI7VL.js → chunk-HIICAHCJ.js} +2 -2
- package/dist/{chunk-E67WX76H.js → chunk-HKMD33FO.js} +29 -80
- package/dist/chunk-HLAFFSEK.js +360 -0
- package/dist/{chunk-UAPGZHYC.js → chunk-I64IFBLB.js} +9 -2
- package/dist/{chunk-XKA2ICR3.js → chunk-I66ZTYNP.js} +440 -175
- package/dist/{chunk-7PWAODYW.js → chunk-I7XBWTYH.js} +2 -2
- package/dist/{chunk-PVAMAVBB.js → chunk-IDNA72AH.js} +102 -2
- package/dist/{chunk-GCSMB2KY.js → chunk-IKOZFYBN.js} +1 -1
- package/dist/{chunk-2VG7KLYV.js → chunk-IKSLQ4XV.js} +5460 -3241
- package/dist/{chunk-QKIFBZKT.js → chunk-IMXMHHMQ.js} +166 -25
- package/dist/{chunk-74YWRRU5.js → chunk-JBCS7CRR.js} +2 -2
- package/dist/{chunk-BDPT6GTK.js → chunk-JWJGP5DQ.js} +2 -2
- package/dist/{chunk-K6BF4U2H.js → chunk-KKOJXO6R.js} +62 -14
- package/dist/chunk-KPXDY6QF.js +47 -0
- package/dist/{chunk-ABLSQ6JX.js → chunk-LJID3DYZ.js} +7 -1
- package/dist/{chunk-VKRH2TCS.js → chunk-M2DAX4F6.js} +2 -2
- package/dist/{chunk-6I5ILFOF.js → chunk-M2WXEHER.js} +2 -2
- package/dist/{chunk-YPI3QQCF.js → chunk-MCEPRMZW.js} +2 -4
- package/dist/{chunk-N5UK64DP.js → chunk-MCMZMDAC.js} +2 -2
- package/dist/{chunk-Y4CAGMM6.js → chunk-MNJGS2IN.js} +5 -6
- package/dist/{chunk-TVHHYFHE.js → chunk-NEDJ26B5.js} +2 -2
- package/dist/{chunk-U2WB7TZS.js → chunk-NMJXSHBJ.js} +97 -85
- package/dist/{chunk-HUAS7ITX.js → chunk-O3YUNJZ2.js} +13 -21
- package/dist/{chunk-MA3H6DM5.js → chunk-P75RZCJW.js} +25 -3
- package/dist/{chunk-IG7BCQBA.js → chunk-PGF63K6I.js} +2 -2
- package/dist/chunk-PJX3WQUQ.js +42 -0
- package/dist/{chunk-6DWBAZ5U.js → chunk-Q4XWMHX6.js} +4 -6
- package/dist/{chunk-OJTRZGR3.js → chunk-QQLGQY2A.js} +8 -8
- package/dist/{chunk-J4HBWF6Y.js → chunk-RLYRBIYQ.js} +115 -20
- package/dist/{chunk-NLFAQR7Z.js → chunk-S66XZJOF.js} +3 -23
- package/dist/{chunk-C537JADH.js → chunk-SSEYRH53.js} +6 -7
- package/dist/chunk-SZAA6XDG.js +30 -0
- package/dist/{chunk-MOPSG2X7.js → chunk-TPEQIQIE.js} +6 -6
- package/dist/{chunk-JA5QWE4Z.js → chunk-UBRFI4HS.js} +1879 -1650
- package/dist/{chunk-BTGG6BG2.js → chunk-UH347SHR.js} +154 -15
- package/dist/{chunk-5YHDIDBP.js → chunk-UH632ZYL.js} +2 -2
- package/dist/{chunk-BWW4HLO4.js → chunk-UXCU4E3T.js} +8 -6
- package/dist/{chunk-6VC4OV3Z.js → chunk-VIA6RFQZ.js} +3 -11
- package/dist/{chunk-ZAZB4JMW.js → chunk-VKPAQYEB.js} +27 -8
- package/dist/{chunk-UXN6JT4W.js → chunk-W4YEMFBX.js} +2 -2
- package/dist/{chunk-TD3PGPQA.js → chunk-W6NIE6OW.js} +2 -2
- package/dist/{chunk-TVH4ONAM.js → chunk-X7IARSHT.js} +3 -3
- package/dist/{chunk-PJJ6MY27.js → chunk-XE3PCIXH.js} +3 -3
- package/dist/{chunk-FEFIFZTL.js → chunk-XGDPUNND.js} +2 -2
- package/dist/{chunk-SCYB3HA4.js → chunk-XOXV5GKE.js} +51 -16
- package/dist/{chunk-QTFGO774.js → chunk-XQRY4DTA.js} +24 -11
- package/dist/{chunk-BJGUKIG4.js → chunk-YJISEZKC.js} +2 -2
- package/dist/{chunk-GPPB3JBE.js → chunk-ZGNYYXQ6.js} +2 -2
- package/dist/{chunk-SINK3QR6.js → chunk-ZNT2M6TG.js} +7 -7
- package/dist/{chunk-7RY5VZPH.js → chunk-ZW4HH5JJ.js} +6 -6
- package/dist/cli/index.js +33 -32
- package/dist/{clio-IT3G3VQH.js → clio-7VB377CC.js} +7 -7
- package/dist/{code-nav-RK6S7F6E.js → code-nav-YVLCYA7V.js} +85 -17
- package/dist/{config-3QZRWZJF.js → config-4HVOS65E.js} +88 -43
- package/dist/{configure-FL7Y3KJF.js → configure-PIWO7B24.js} +10 -10
- package/dist/{context-5HE7ODYK.js → context-IYEHL3WQ.js} +33 -31
- package/dist/{context-XNHL75JV.js → context-KQYIWPWT.js} +47 -34
- package/dist/{context-KYQFRVDC.js → context-N6ZE3LGJ.js} +11 -11
- package/dist/{context-clear-N545L53A.js → context-clear-G4OGZJDS.js} +33 -31
- package/dist/{context-working-set-QHKXSV2F.js → context-working-set-BWLF6LJP.js} +7 -7
- package/dist/{dispatch-runner-RGIE5PCT.js → dispatch-runner-2QQAITS3.js} +38 -38
- package/dist/{docs-5NAF6AU7.js → docs-PD3EXDKU.js} +21 -20
- package/dist/{doctor-ZGPEGHIP.js → doctor-LHBD36VU.js} +23 -22
- package/dist/{eval-GXLL44RD.js → eval-C45FYRJ6.js} +21 -20
- package/dist/{eval-inventory-HBWSWQOK.js → eval-inventory-6DEJPLBF.js} +2 -2
- package/dist/{evidence-HWLBRH3Q.js → evidence-6SHONYAF.js} +30 -28
- package/dist/{evolve-FTZBMNVW.js → evolve-KRKMV72X.js} +30 -28
- package/dist/{extensions-VHRBEID7.js → extensions-KPZ2UHBB.js} +5 -3
- package/dist/{fleet-CKZHJWZJ.js → fleet-IVTCKDHT.js} +62 -61
- package/dist/{fleet-commands-EXDXBMV6.js → fleet-commands-EDWL3IT7.js} +5 -5
- package/dist/{fleet-decisions-OTHB6KRL.js → fleet-decisions-YP3YEFGK.js} +4 -4
- package/dist/{fleet-graph-YTEZUCUT.js → fleet-graph-ZFWKHY2M.js} +16 -14
- package/dist/{fleet-inspect-SS6YMDCK.js → fleet-inspect-FVUNCBML.js} +31 -29
- package/dist/{fleet-preflight-PBY4VYOM.js → fleet-preflight-UN5XED4R.js} +2 -2
- package/dist/{fleet-validate-KMEM5L3S.js → fleet-validate-XOWC4HSX.js} +17 -15
- package/dist/{fleet-verify-QD5M7E7Q.js → fleet-verify-UN3SODEL.js} +30 -28
- package/dist/{fleet-view-WAMJYNDT.js → fleet-view-TWHJKCN6.js} +31 -29
- package/dist/{init-5XQRBOFV.js → init-T2QORQ3Y.js} +50 -49
- package/dist/{interop-34TVO25M.js → interop-IN5I2A66.js} +5 -5
- package/dist/{library-3QY6KF57.js → library-LSCATDLZ.js} +15 -13
- package/dist/{memory-L4UTIIIW.js → memory-HYOKAGGJ.js} +31 -29
- package/dist/{models-ZVX3QOWE.js → models-2GPMFYCM.js} +22 -21
- package/dist/{monitor-CEKVSYTS.js → monitor-E4ASVUJH.js} +34 -32
- package/dist/{orchestrator-77BAP6BC.js → orchestrator-DDMPR3PY.js} +984 -583
- package/dist/{panes-7STHOAUJ.js → panes-E3RUXOW5.js} +4 -4
- package/dist/{panes-SHAUIRXY.js → panes-IXKLOKA2.js} +23 -8
- package/dist/{reset-EOLM7GVE.js → reset-OAQP3W4O.js} +4 -4
- package/dist/{resources-74GKTLSF.js → resources-OTRSN34L.js} +15 -13
- package/dist/{run-HBAUJNNZ.js → run-5DEYH5QK.js} +60 -59
- package/dist/{share-G3APVLVP.js → share-IHWTLO3M.js} +19 -15
- package/dist/{skills-35HHUKCR.js → skills-IYMXMKW4.js} +17 -15
- package/dist/{skills-eval-QN4HSHDC.js → skills-eval-DROHSJAR.js} +36 -36
- package/dist/{skills-inventory-J357J34F.js → skills-inventory-D7X4L4ZX.js} +15 -13
- package/dist/{slash-commands-JZZCQA32.js → slash-commands-QBM7UZ3B.js} +21 -18
- package/dist/{steer-XAVHJM22.js → steer-Z5DO23FJ.js} +2 -2
- package/dist/{targets-DSM6CY3M.js → targets-P2FUC4IL.js} +25 -28
- package/dist/{terminal-lease-JOPFUVEM.js → terminal-lease-YREJ3JX2.js} +5 -5
- package/dist/{tools-MKNWVPBH.js → tools-5B7RO6MV.js} +4 -4
- package/dist/{trace-ECQ7TIYZ.js → trace-YMGMUM6A.js} +55 -7
- package/dist/{upgrade-H7TOM7YL.js → upgrade-PXK3S2YM.js} +11 -9
- package/dist/{usage-X52N3IDJ.js → usage-ME5MPXGX.js} +36 -34
- package/dist/{verifiers-EJTVVSMA.js → verifiers-BVZ7IWOO.js} +5 -5
- package/dist/{verify-YJL6XET2.js → verify-5K7ZKQFC.js} +4 -4
- package/dist/{web-fetch-MPIFL3LL.js → web-fetch-MPARV2K7.js} +2 -2
- package/dist/{wiki-generate-4NDZTQ4B.js → wiki-generate-F5W5QTYY.js} +48 -47
- package/dist/{with-panes-OBOBFIIR.js → with-panes-BYOJCLAM.js} +51 -255
- package/dist/worker/entry.js +45 -30
- package/docs/README.md +176 -81
- package/docs/{acp.md → architecture/acp.md} +36 -20
- package/docs/{alcf-provider.md → architecture/alcf-provider.md} +8 -5
- package/docs/{architecture.md → architecture/architecture.md} +43 -22
- package/docs/{artifact-placement.md → architecture/artifact-placement.md} +26 -23
- package/docs/architecture/artifact-versions.md +90 -0
- package/docs/{capacity-and-scheduling.md → architecture/capacity-and-scheduling.md} +26 -13
- package/docs/{context-engine.md → architecture/context-engine.md} +25 -25
- package/docs/{context-working-set.md → architecture/context-working-set.md} +13 -10
- package/docs/{dispatch-architecture-rationale.md → architecture/dispatch-architecture-rationale.md} +12 -9
- package/docs/{dispatch-typed-intent.md → architecture/dispatch-typed-intent.md} +68 -46
- package/docs/{evidence-and-memory.md → architecture/evidence-and-memory.md} +23 -16
- package/docs/{middleware-and-components.md → architecture/middleware-and-components.md} +11 -5
- package/docs/{model-catalog.md → architecture/model-catalog.md} +40 -17
- package/docs/{observability.md → architecture/observability.md} +26 -13
- package/docs/{pi-boundary.md → architecture/pi-boundary.md} +24 -11
- package/docs/{prompt-envelope-and-tools.md → architecture/prompt-envelope-and-tools.md} +55 -20
- package/docs/{provider-adapter-cookbook.md → architecture/provider-adapter-cookbook.md} +35 -24
- package/docs/{safety-model.md → architecture/safety-model.md} +20 -15
- package/docs/{session-lifecycle.md → architecture/session-lifecycle.md} +8 -5
- package/docs/architecture/time-conventions.md +125 -0
- package/docs/{trace-store.md → architecture/trace-store.md} +13 -5
- package/docs/{tui-design.md → architecture/tui-design.md} +13 -13
- package/docs/{worker-dispatch-mechanics.md → architecture/worker-dispatch-mechanics.md} +27 -30
- package/docs/{built-in-agents.md → guide/built-in-agents.md} +50 -34
- package/docs/{commands-and-modes.md → guide/commands-and-modes.md} +65 -60
- package/docs/{configuration-and-targets.md → guide/configuration-and-targets.md} +227 -289
- package/docs/guide/configuration-reference.md +1158 -0
- package/docs/{environment-variables.md → guide/environment-variables.md} +31 -28
- package/docs/{exit-codes-and-output.md → guide/exit-codes-and-output.md} +6 -3
- package/docs/{extensions-and-sharing.md → guide/extensions-and-sharing.md} +41 -14
- package/docs/{fleet-dispatch.md → guide/fleet-dispatch.md} +39 -43
- package/docs/{glossary.md → guide/glossary.md} +14 -11
- package/docs/{installation-and-lifecycle.md → guide/installation-and-lifecycle.md} +44 -13
- package/docs/guide/panes-and-files.md +290 -0
- package/docs/{proactive-memory.md → guide/proactive-memory.md} +79 -66
- package/docs/{resource-library.md → guide/resource-library.md} +13 -4
- package/docs/{skills-marketplace.md → guide/skills-marketplace.md} +7 -3
- package/docs/{tool-usage.md → guide/tool-usage.md} +87 -23
- package/docs/{troubleshooting.md → guide/troubleshooting.md} +9 -4
- package/docs/{config-knobs-audit.md → history/config-knobs-audit.md} +11 -11
- package/docs/{release-cut-checklist.md → history/release-cut-checklist.md} +29 -2
- package/docs/{development-pipeline.md → process/development-pipeline.md} +24 -26
- package/docs/process/documentation-coverage.md +100 -0
- package/docs/process/documentation-guide.md +187 -0
- package/docs/{eval-runner.md → process/eval-runner.md} +41 -50
- package/docs/{evals-internal.md → process/evals-internal.md} +10 -10
- package/docs/{evolution.md → process/evolution.md} +2 -2
- package/docs/{fleet-demo-runbook.md → process/fleet-demo-runbook.md} +11 -7
- package/docs/{git-commit-provenance.md → process/git-commit-provenance.md} +11 -4
- package/docs/{performance-methodology.md → process/performance-methodology.md} +87 -69
- package/docs/{scientific-validation.md → process/scientific-validation.md} +4 -4
- package/evals/README.md +2 -2
- package/package.json +9 -7
- package/skills/README.md +46 -37
- package/skills/coding/ast-grep/SKILL.md +2 -2
- package/skills/coding/coding-standards/SKILL.md +2 -2
- package/skills/coding/prototype/SKILL.md +2 -2
- package/skills/coding/tdd/SKILL.md +2 -2
- package/skills/context/context-handoff/SKILL.md +2 -2
- package/skills/context/context-prime/SKILL.md +2 -2
- package/skills/git/file-ticket/SKILL.md +2 -2
- package/skills/git/fix-issue/SKILL.md +3 -3
- package/skills/git/resolve-merge-conflicts/SKILL.md +2 -2
- package/skills/git/ship/SKILL.md +2 -2
- package/skills/git/worktree-create/SKILL.md +2 -2
- package/skills/git/worktree-merge/SKILL.md +2 -2
- package/skills/meta/clio-coder-dev/SKILL.md +9 -5
- package/skills/meta/clio-coder-dev/evals.md +3 -2
- package/skills/meta/clio-coder-test/SKILL.md +102 -95
- package/skills/meta/clio-coder-test/evals.md +9 -4
- package/skills/meta/clio-coder-test/references/harness.md +100 -124
- package/skills/meta/clio-coder-test/references/test-map.md +77 -50
- package/skills/meta/credentials/SKILL.md +2 -2
- package/skills/meta/find-skills/SKILL.md +2 -2
- package/skills/meta/herdr/SKILL.md +2 -2
- package/skills/meta/skill-craft/SKILL.md +22 -16
- package/skills/planning/architecture/SKILL.md +2 -2
- package/skills/planning/backlog/SKILL.md +2 -2
- package/skills/planning/prd/SKILL.md +2 -2
- package/skills/planning/product-intent/SKILL.md +2 -2
- package/skills/planning/tech-spec/SKILL.md +2 -2
- package/skills/registry.yaml +62 -62
- package/skills/research/arxiv-literature/SKILL.md +2 -2
- package/skills/research/experiment-protocol/SKILL.md +2 -2
- package/skills/research/scientific-debugging/SKILL.md +2 -2
- package/skills/research/scientific-modernization/SKILL.md +2 -2
- package/skills/skill-marketplace.json +62 -62
- package/skills/workflow/cut-it/SKILL.md +2 -2
- package/skills/workflow/design-council/SKILL.md +2 -2
- package/skills/workflow/grill-me/SKILL.md +2 -2
- package/skills/workflow/workflow-distiller/SKILL.md +2 -2
- package/src/cli/args.ts +2 -2
- package/src/cli/bootstrap-generate.ts +1 -1
- package/src/cli/config-inspect.ts +65 -12
- package/src/cli/configure.ts +0 -4
- package/src/cli/docs.ts +22 -14
- package/src/cli/doctor-naming.ts +5 -5
- package/src/cli/doctor-toolchain.ts +3 -3
- package/src/cli/eval.ts +1 -2
- package/src/cli/extensions.ts +2 -1
- package/src/cli/fleet.ts +1 -1
- package/src/cli/index.ts +2 -1
- package/src/cli/internal-dispatch.ts +3 -4
- package/src/cli/panes.ts +19 -5
- package/src/cli/run.ts +2 -2
- package/src/cli/share.ts +5 -1
- package/src/cli/skills-eval.ts +3 -3
- package/src/cli/targets.ts +2 -6
- package/src/cli/trace.ts +55 -4
- package/src/cli/wiki-generate.ts +1 -1
- package/src/core/artifact-paths.ts +1 -1
- package/src/core/bash-exec.ts +131 -86
- package/src/core/bus-events.ts +51 -6
- package/src/core/config.ts +5 -1
- package/src/core/defaults.ts +7 -4
- package/src/core/dispatch-outcome.ts +16 -0
- package/src/core/guardrails.ts +10 -49
- package/src/core/prompt-hint.ts +9 -0
- package/src/domains/agents/builtins/architect.md +2 -3
- package/src/domains/agents/builtins/coder.md +3 -2
- package/src/domains/agents/builtins/debugger.md +2 -2
- package/src/domains/agents/builtins/documenter.md +2 -2
- package/src/domains/agents/builtins/git-master.md +1 -1
- package/src/domains/agents/builtins/oracle.md +1 -1
- package/src/domains/agents/builtins/provenance.md +1 -1
- package/src/domains/agents/builtins/researcher.md +1 -1
- package/src/domains/agents/builtins/scout.md +1 -1
- package/src/domains/agents/builtins/tester.md +2 -2
- package/src/domains/agents/builtins/verifier.md +2 -2
- package/src/domains/agents/builtins/wiki-writer.md +1 -1
- package/src/domains/agents/catalog.ts +12 -14
- package/src/domains/agents/contract.ts +2 -0
- package/src/domains/agents/extension.ts +23 -1
- package/src/domains/config/keybindings.ts +8 -0
- package/src/domains/context/extension.ts +0 -3
- package/src/domains/context/working-set/path-index.ts +1 -0
- package/src/domains/dispatch/capability-match.ts +10 -0
- package/src/domains/dispatch/extension.ts +105 -22
- package/src/domains/dispatch/host-verification.ts +435 -39
- package/src/domains/dispatch/intent-requirements.ts +10 -0
- package/src/domains/dispatch/intent.ts +18 -1
- package/src/domains/dispatch/path-scope.ts +235 -24
- package/src/domains/dispatch/run-event-journal.ts +4 -15
- package/src/domains/dispatch/state.ts +2 -3
- package/src/domains/dispatch/transport.ts +45 -21
- package/src/domains/dispatch/types.ts +55 -3
- package/src/domains/eval/artifacts/store.ts +5 -0
- package/src/domains/eval/store.ts +8 -1
- package/src/domains/evidence/trust-status.ts +10 -1
- package/src/domains/extensions/contract.ts +15 -1
- package/src/domains/extensions/discovery.ts +238 -41
- package/src/domains/extensions/extension.ts +105 -6
- package/src/domains/extensions/index.ts +24 -0
- package/src/domains/extensions/integrity.ts +189 -0
- package/src/domains/extensions/manager.ts +17 -1
- package/src/domains/extensions/resource-path.ts +27 -0
- package/src/domains/extensions/resources.ts +18 -38
- package/src/domains/extensions/snapshot-store.ts +39 -0
- package/src/domains/extensions/snapshot.ts +180 -0
- package/src/domains/extensions/state.ts +385 -57
- package/src/domains/extensions/types.ts +118 -1
- package/src/domains/lifecycle/migrations/2026-09-01-extension-install-digests.ts +27 -0
- package/src/domains/lifecycle/migrations/index.ts +2 -0
- package/src/domains/lifecycle/naming-resources.ts +19 -4
- package/src/domains/lifecycle/naming-yazi.ts +10 -5
- package/src/domains/middleware/contract.ts +26 -0
- package/src/domains/middleware/extension.ts +24 -24
- package/src/domains/middleware/hook-receipts.ts +27 -4
- package/src/domains/middleware/hooks-io.ts +65 -32
- package/src/domains/middleware/hooks.ts +64 -0
- package/src/domains/middleware/index.ts +28 -4
- package/src/domains/middleware/registrations.ts +326 -0
- package/src/domains/middleware/runtime.ts +28 -0
- package/src/domains/middleware/snapshot.ts +20 -7
- package/src/domains/mux/contract.ts +38 -0
- package/src/domains/mux/detect.ts +6 -13
- package/src/domains/mux/index.ts +1 -1
- package/src/domains/mux/operations.ts +44 -5
- package/src/domains/mux/yazi/assets/yazi.toml +2 -2
- package/src/domains/mux/yazi/session.ts +53 -4
- package/src/domains/mux/yazi/theme.ts +117 -17
- package/src/domains/observability/contract.ts +10 -11
- package/src/domains/observability/extension.ts +11 -3
- package/src/domains/observability/projection.ts +14 -90
- package/src/domains/observability/trace-store.ts +43 -7
- package/src/domains/prompts/compiler.ts +73 -53
- package/src/domains/prompts/contract.ts +15 -3
- package/src/domains/prompts/extension.ts +97 -9
- package/src/domains/prompts/fragments/identity/clio-worker.md +1 -3
- package/src/domains/prompts/fragments/identity/clio.md +6 -12
- package/src/domains/prompts/fragments/identity/docs-routing.md +1 -2
- package/src/domains/prompts/fragments/identity/self-awareness.md +3 -11
- package/src/domains/prompts/fragments/operating/contract.md +7 -15
- package/src/domains/prompts/fragments/operating/delegation.md +32 -34
- package/src/domains/prompts/fragments/operating/skills.md +10 -24
- package/src/domains/prompts/fragments/operating/worker.md +1 -8
- package/src/domains/providers/index.ts +1 -1
- package/src/domains/providers/model-runtime-capabilities.ts +85 -21
- package/src/domains/providers/models/local-models/clio-coder-local-coding-targets.yaml +669 -104
- package/src/domains/providers/runtime-resolution.ts +31 -0
- package/src/domains/providers/runtimes/common/probe-helpers.ts +7 -2
- package/src/domains/providers/runtimes/local-native/llamacpp.ts +9 -1
- package/src/domains/providers/types/cost-provenance.ts +19 -0
- package/src/domains/providers/types/local-model-quirks.ts +85 -37
- package/src/domains/resources/skills/loader.ts +16 -19
- package/src/domains/safety/call-target.ts +1 -1
- package/src/domains/safety/loop-detector.ts +7 -4
- package/src/domains/session/task-board.ts +10 -9
- package/src/domains/share/archive.ts +164 -7
- package/src/engine/acp/server.ts +62 -9
- package/src/engine/apis/llamacpp-residency.ts +3 -4
- package/src/engine/apis/lmstudio.ts +3 -3
- package/src/engine/apis/ollama-native.ts +6 -6
- package/src/engine/apis/openai-completions.ts +28 -25
- package/src/engine/apis/output-budget.ts +8 -18
- package/src/engine/apis/residency.ts +8 -27
- package/src/engine/gemma-channel-filter.ts +19 -0
- package/src/engine/loop-guard.ts +92 -12
- package/src/engine/worker-runtime.ts +40 -11
- package/src/engine/worker-tools.ts +3 -1
- package/src/entry/extension-hook-sources.ts +28 -0
- package/src/entry/extension-reload.ts +309 -0
- package/src/entry/orchestrator.ts +59 -35
- package/src/interactive/application-controller.ts +2 -1
- package/src/interactive/bus-notices.ts +8 -1
- package/src/interactive/chat-loop-messages.ts +3 -13
- package/src/interactive/chat-loop.ts +10 -1
- package/src/interactive/chat-panel.ts +36 -13
- package/src/interactive/chat-renderer.ts +71 -7
- package/src/interactive/dispatch-board.ts +6 -11
- package/src/interactive/footer/widgets.ts +13 -0
- package/src/interactive/interactive-application.ts +39 -4
- package/src/interactive/interactive-input-runtime.ts +4 -0
- package/src/interactive/interactive-presentation.ts +2 -2
- package/src/interactive/interactive-slash-runtime.ts +2 -0
- package/src/interactive/overlays/extensions.ts +9 -1
- package/src/interactive/overlays/help-reference.ts +13 -0
- package/src/interactive/overlays/settings.ts +27 -16
- package/src/interactive/panes-runtime.ts +111 -35
- package/src/interactive/prompt-cache-identity.ts +88 -0
- package/src/interactive/slash-commands.ts +129 -14
- package/src/interactive/stream-pacing-policy.ts +0 -23
- package/src/interactive/turn-context.ts +30 -15
- package/src/interactive/yazi-bridge.ts +60 -6
- package/src/tools/agent-tools.ts +30 -1
- package/src/tools/artifact.ts +2 -2
- package/src/tools/ask-user.ts +3 -3
- package/src/tools/bash.ts +1 -1
- package/src/tools/bootstrap.ts +4 -0
- package/src/tools/builtin-tool-catalog.ts +52 -22
- package/src/tools/codewiki/code-nav-surface.ts +6 -0
- package/src/tools/codewiki/code-nav.ts +99 -13
- package/src/tools/context/docs-engine.ts +20 -7
- package/src/tools/context/index.ts +29 -12
- package/src/tools/core-bootstrap.ts +28 -6
- package/src/tools/credential-present.ts +1 -2
- package/src/tools/dispatch-arguments.ts +5 -1
- package/src/tools/dispatch-plan.ts +48 -4
- package/src/tools/dispatch-run-events.ts +1 -1
- package/src/tools/dispatch-schema.ts +338 -0
- package/src/tools/dispatch-types.ts +3 -0
- package/src/tools/dispatch.ts +9 -254
- package/src/tools/ledger.ts +3 -5
- package/src/tools/monitor-surface.ts +5 -13
- package/src/tools/observation.ts +4 -5
- package/src/tools/panes-surface.ts +4 -11
- package/src/tools/panes.ts +4 -2
- package/src/tools/policy.ts +15 -2
- package/src/tools/read.ts +5 -6
- package/src/tools/registry.ts +30 -7
- package/src/tools/result-shaping.ts +18 -14
- package/src/tools/steer-surface.ts +1 -1
- package/src/tools/tasks.ts +1 -1
- package/src/tools/truncate.ts +6 -5
- package/src/tools/verify/surface.ts +6 -12
- package/src/tools/web-fetch-surface.ts +1 -3
- package/dist/chunk-5QIAJV2D.js +0 -48
- package/dist/chunk-JZWT5J3Y.js +0 -814
- package/dist/chunk-K7VKOLQQ.js +0 -15
- package/dist/chunk-PMZCIOCJ.js +0 -25
- package/dist/chunk-SUW5DORT.js +0 -819
- package/dist/chunk-UOV2BYIW.js +0 -107
- package/dist/chunk-WR6U3OVP.js +0 -45
- package/docs/artifact-versions.md +0 -67
- package/docs/documentation-coverage.md +0 -46
- package/docs/documentation-guide.md +0 -167
- package/docs/time-conventions.md +0 -101
|
@@ -2,17 +2,34 @@ import { execFileSync } from "node:child_process";
|
|
|
2
2
|
import { createHash, randomUUID } from "node:crypto";
|
|
3
3
|
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { dirname, join, resolve } from "node:path";
|
|
5
|
+
import { PATH_BOUNDARY_MAX_ENTRIES, pathBoundaryCovers, resolvePathBoundary } from "../../core/path-boundary.js";
|
|
5
6
|
import { withStateFileLockSync } from "../../core/state-file-lock.js";
|
|
6
7
|
import { clioStateDir } from "../../core/xdg.js";
|
|
7
8
|
import { FLEET_COMMAND_BASE_ENV, type FleetCommand } from "../agents/fleet-commands.js";
|
|
8
9
|
import { runCodeStep } from "./code-step.js";
|
|
9
10
|
import type { DispatchRequest } from "./contract.js";
|
|
10
|
-
import type { RunHostVerification, RunHostVerificationCheck } from "./types.js";
|
|
11
|
+
import type { RunHostVerification, RunHostVerificationAttribution, RunHostVerificationCheck } from "./types.js";
|
|
11
12
|
import { captureWorkspaceSnapshot } from "./write-boundary.js";
|
|
12
13
|
|
|
13
14
|
const OUTPUT_TAIL_BYTES = 2_048;
|
|
14
15
|
const MEMO_VERSION = 1;
|
|
15
16
|
|
|
17
|
+
type ResolvedCheck = NonNullable<DispatchRequest["resolvedVerification"]>[number];
|
|
18
|
+
|
|
19
|
+
const HTTP_URL_RE = /\bhttps?:\/\/\S+/gu;
|
|
20
|
+
/**
|
|
21
|
+
* Path-like tokens a failing check named. A separator and a source or document
|
|
22
|
+
* extension are both required, which is the grammar `path-scope.ts:302` already
|
|
23
|
+
* uses to decide which prose tokens read as repository paths; without the
|
|
24
|
+
* extension list `10/20 passing` and `v1.2.3` become implicated files. Probed
|
|
25
|
+
* against node:test, tsc, and stack-trace output: relative, absolute, and
|
|
26
|
+
* `./`-prefixed forms all match and stop at a trailing `:line:col`.
|
|
27
|
+
*/
|
|
28
|
+
const CHECK_OUTPUT_PATH_RE =
|
|
29
|
+
/\/?(?:[A-Za-z0-9_.@+-]+\/)+[A-Za-z0-9_.@+-]*\.(?:[cm]?[jt]sx?|json|ya?ml|md|txt|py|go|rs|c|h|cc|cpp|hpp|java|rb|sh|toml|css|html|sql)\b/gu;
|
|
30
|
+
/** Implicated paths recorded on a receipt, matching the intent path-list cap. */
|
|
31
|
+
const IMPLICATED_PATH_CAP = PATH_BOUNDARY_MAX_ENTRIES;
|
|
32
|
+
|
|
16
33
|
interface MemoEntry {
|
|
17
34
|
key: string;
|
|
18
35
|
runId: string;
|
|
@@ -111,6 +128,69 @@ export function hostVerificationRejection(
|
|
|
111
128
|
};
|
|
112
129
|
}
|
|
113
130
|
|
|
131
|
+
async function runResolvedCheck(input: {
|
|
132
|
+
runId: string;
|
|
133
|
+
index: number;
|
|
134
|
+
resolvedCheck: ResolvedCheck;
|
|
135
|
+
stateDir: string;
|
|
136
|
+
env: NodeJS.ProcessEnv;
|
|
137
|
+
onDiagnostic?: (error: unknown) => void;
|
|
138
|
+
}): Promise<{ check: RunHostVerificationCheck; outputExcerpt: string }> {
|
|
139
|
+
const resolvedCheck = input.resolvedCheck;
|
|
140
|
+
const command: FleetCommand = {
|
|
141
|
+
id: resolvedCheck.check,
|
|
142
|
+
argv: [...resolvedCheck.argv],
|
|
143
|
+
cwd: "",
|
|
144
|
+
timeoutMs: resolvedCheck.timeoutMs,
|
|
145
|
+
env: [],
|
|
146
|
+
description: `Host verification check ${resolvedCheck.check}.`,
|
|
147
|
+
};
|
|
148
|
+
const fingerprint = workspaceFingerprint(resolvedCheck.cwd);
|
|
149
|
+
const key =
|
|
150
|
+
fingerprint === null ? null : memoKey({ ...command, cwd: resolve(resolvedCheck.cwd) }, fingerprint, input.env);
|
|
151
|
+
const hit =
|
|
152
|
+
key === null
|
|
153
|
+
? undefined
|
|
154
|
+
: readMemo(input.stateDir).entries.find((entry) => entry.key === key && entry.check.exitCode === 0);
|
|
155
|
+
// A memo hit is always a pass, so no caller ever needs its output for
|
|
156
|
+
// attribution; the empty excerpt says the fresh output does not exist.
|
|
157
|
+
if (hit !== undefined) {
|
|
158
|
+
return {
|
|
159
|
+
check: { ...hit.check, argv: [...hit.check.argv], memo: true, evidenceRunId: hit.runId },
|
|
160
|
+
outputExcerpt: "",
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
const outcome = await runCodeStep({
|
|
164
|
+
stepId: `verification-${input.index + 1}-${resolvedCheck.check.replace(/[^a-z0-9._-]/giu, "_")}`,
|
|
165
|
+
command,
|
|
166
|
+
workspaceRoot: resolvedCheck.cwd,
|
|
167
|
+
artifactDir: join(input.stateDir, "artifacts", input.runId, "verification"),
|
|
168
|
+
env: input.env,
|
|
169
|
+
});
|
|
170
|
+
const artifactPath = outcome.record.artifactPaths[0];
|
|
171
|
+
const check: RunHostVerificationCheck = {
|
|
172
|
+
check: resolvedCheck.check,
|
|
173
|
+
argv: [...outcome.record.argv],
|
|
174
|
+
cwd: outcome.record.cwd,
|
|
175
|
+
exitCode: outcome.record.exitCode,
|
|
176
|
+
durationMs: outcome.record.durationMs,
|
|
177
|
+
memo: false,
|
|
178
|
+
outputTail: outputTail(outcome.report.outputExcerpt),
|
|
179
|
+
...(artifactPath !== undefined ? { artifactPath } : {}),
|
|
180
|
+
};
|
|
181
|
+
if (check.exitCode === 0 && key !== null) {
|
|
182
|
+
try {
|
|
183
|
+
storeMemoEntry(input.stateDir, { key, runId: input.runId, check });
|
|
184
|
+
} catch (error) {
|
|
185
|
+
input.onDiagnostic?.(error);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
// The 8KB code-step excerpt (code-step.ts:37), not the 2KB receipt tail:
|
|
189
|
+
// attribution reads the runner's own failure summary and a 2KB window loses
|
|
190
|
+
// it on a large suite.
|
|
191
|
+
return { check, outputExcerpt: outcome.report.outputExcerpt };
|
|
192
|
+
}
|
|
193
|
+
|
|
114
194
|
export async function runHostVerification(input: {
|
|
115
195
|
runId: string;
|
|
116
196
|
request: Pick<DispatchRequest, "resolvedVerification">;
|
|
@@ -126,53 +206,369 @@ export async function runHostVerification(input: {
|
|
|
126
206
|
const env = input.env ?? process.env;
|
|
127
207
|
const results: RunHostVerificationCheck[] = [];
|
|
128
208
|
for (const [index, resolvedCheck] of checks.entries()) {
|
|
129
|
-
const
|
|
130
|
-
|
|
209
|
+
const outcome = await runResolvedCheck({
|
|
210
|
+
runId: input.runId,
|
|
211
|
+
index,
|
|
212
|
+
resolvedCheck,
|
|
213
|
+
stateDir,
|
|
214
|
+
env,
|
|
215
|
+
...(input.onDiagnostic !== undefined ? { onDiagnostic: input.onDiagnostic } : {}),
|
|
216
|
+
});
|
|
217
|
+
results.push(outcome.check);
|
|
218
|
+
}
|
|
219
|
+
return {
|
|
220
|
+
status: results.every((check) => check.exitCode === 0) ? "verified" : "rejected",
|
|
221
|
+
checks: results,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function implicatedPaths(outputExcerpt: string, checkCwd: string): string[] {
|
|
226
|
+
const withoutUrls = outputExcerpt.replace(HTTP_URL_RE, (url) => " ".repeat(url.length));
|
|
227
|
+
const paths = new Set<string>();
|
|
228
|
+
for (const match of withoutUrls.matchAll(CHECK_OUTPUT_PATH_RE)) paths.add(resolve(checkCwd, match[0]));
|
|
229
|
+
return [...paths].sort();
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/** Identity of a resolved check across batch members: same command, same cwd, same bound. */
|
|
233
|
+
function checkDedupeKey(resolvedCheck: ResolvedCheck): string {
|
|
234
|
+
return sha256(
|
|
235
|
+
JSON.stringify({
|
|
236
|
+
check: resolvedCheck.check,
|
|
131
237
|
argv: [...resolvedCheck.argv],
|
|
132
|
-
cwd:
|
|
238
|
+
cwd: resolve(resolvedCheck.cwd),
|
|
133
239
|
timeoutMs: resolvedCheck.timeoutMs,
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
240
|
+
}),
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export interface BatchVerificationParticipant {
|
|
245
|
+
runId: string;
|
|
246
|
+
request: Pick<DispatchRequest, "resolvedVerification" | "intent" | "cwd">;
|
|
247
|
+
workerSuccessful: boolean;
|
|
248
|
+
onDiagnostic?: (error: unknown) => void;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export interface BatchVerificationGate {
|
|
252
|
+
/** This run holds a capacity lease and its worker is spawned, so it may be writing. */
|
|
253
|
+
live(runId: string): void;
|
|
254
|
+
/** Park until every live member has arrived or left, then return this run's verdict. */
|
|
255
|
+
arrive(input: BatchVerificationParticipant): Promise<RunHostVerification | undefined>;
|
|
256
|
+
/** Release a member that will never arrive. Idempotent, and a no-op for one already parked. */
|
|
257
|
+
abandon(runId: string): void;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
interface ParkedMember {
|
|
261
|
+
participant: BatchVerificationParticipant;
|
|
262
|
+
settle: (value: RunHostVerification | undefined) => void;
|
|
263
|
+
/**
|
|
264
|
+
* Reject the parked promise so the member's finalization takes the identical
|
|
265
|
+
* path a single-task dispatch takes when its declared check cannot run at all
|
|
266
|
+
* (`extension.ts:5544` throws into the finalization catch at 5669 and the run
|
|
267
|
+
* is marked failed). Silently dropping a declared check must never look like
|
|
268
|
+
* never having declared one.
|
|
269
|
+
*/
|
|
270
|
+
fail: (error: unknown) => void;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* The member's declared write roots resolved in the frame its own request ran in.
|
|
275
|
+
*
|
|
276
|
+
* An empty result means UNCONFINED, never "writes nothing": `intent.ts:139`
|
|
277
|
+
* normalizes a `"."` scope entry to an empty list, a request may omit
|
|
278
|
+
* `write_roots` entirely, and `extension.ts:1949` only builds a write boundary
|
|
279
|
+
* for a non-empty list, so such a run executed with no write confinement at all.
|
|
280
|
+
*/
|
|
281
|
+
function memberBoundary(participant: BatchVerificationParticipant): string[] {
|
|
282
|
+
const writeRoots = participant.request.intent?.writeRoots ?? [];
|
|
283
|
+
return writeRoots.map((root) => resolvePathBoundary(participant.request.cwd ?? process.cwd(), root));
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Charge one failing batch check to the members that own it.
|
|
288
|
+
*
|
|
289
|
+
* The charge follows write-root coverage across the whole wave, not just across
|
|
290
|
+
* the members that declared the check: a live sibling that declared no check, or
|
|
291
|
+
* whose own worker failed, still edited the shared checkout, so a named path
|
|
292
|
+
* inside its boundary and inside no declarer's is positive evidence that the
|
|
293
|
+
* declarers are innocent. That exculpation demands every named path be owned
|
|
294
|
+
* elsewhere; one path nobody claims leaves the failure unowned and every
|
|
295
|
+
* declarer is charged. When the owner is a member that declared no check the
|
|
296
|
+
* charge set comes out empty, because a run has nothing to reject on for a check
|
|
297
|
+
* it never asked for; that is the attribution rule applied faithfully, and the
|
|
298
|
+
* alternative is rejecting a worker the evidence points away from, which is the
|
|
299
|
+
* defect this gate exists to remove.
|
|
300
|
+
*
|
|
301
|
+
* Exculpation always requires positive evidence, so a declarer that ran
|
|
302
|
+
* unconfined is charged unconditionally: with no boundary there is nothing to
|
|
303
|
+
* test the failing paths against, and it is the member most able to have broken
|
|
304
|
+
* the tree. `basis` is the weakest evidence behind the charge set, never the
|
|
305
|
+
* strongest, so `write_roots` on a receipt means every charged run has a named
|
|
306
|
+
* path inside its own declared boundary.
|
|
307
|
+
*/
|
|
308
|
+
function attributeFailure(input: {
|
|
309
|
+
implicated: ReadonlyArray<string>;
|
|
310
|
+
declarers: ReadonlyArray<BatchVerificationParticipant>;
|
|
311
|
+
wave: ReadonlyArray<BatchVerificationParticipant>;
|
|
312
|
+
}): { charged: Set<string>; basis: RunHostVerificationAttribution["basis"]; decisive: string[] } {
|
|
313
|
+
const declared = new Set(input.declarers.map((member) => member.runId));
|
|
314
|
+
const implicatedDeclarers = new Set<string>();
|
|
315
|
+
const unconfined = new Set<string>();
|
|
316
|
+
const decisive = new Set<string>();
|
|
317
|
+
for (const member of input.declarers) {
|
|
318
|
+
const boundary = memberBoundary(member);
|
|
319
|
+
if (boundary.length === 0) {
|
|
320
|
+
unconfined.add(member.runId);
|
|
145
321
|
continue;
|
|
146
322
|
}
|
|
147
|
-
const
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
323
|
+
const hits = input.implicated.filter((path) => pathBoundaryCovers(boundary, path));
|
|
324
|
+
if (hits.length === 0) continue;
|
|
325
|
+
implicatedDeclarers.add(member.runId);
|
|
326
|
+
for (const hit of hits) decisive.add(hit);
|
|
327
|
+
}
|
|
328
|
+
const others = input.wave.filter((member) => !declared.has(member.runId));
|
|
329
|
+
const ownedElsewhere =
|
|
330
|
+
input.implicated.length > 0 &&
|
|
331
|
+
input.implicated.every((path) => others.some((member) => pathBoundaryCovers(memberBoundary(member), path)));
|
|
332
|
+
const localized = implicatedDeclarers.size > 0 || ownedElsewhere;
|
|
333
|
+
const charged = localized ? new Set(implicatedDeclarers) : new Set(declared);
|
|
334
|
+
for (const runId of unconfined) charged.add(runId);
|
|
335
|
+
return {
|
|
336
|
+
charged,
|
|
337
|
+
basis:
|
|
338
|
+
!localized || unconfined.size > 0
|
|
339
|
+
? "unattributable"
|
|
340
|
+
: implicatedDeclarers.size > 0
|
|
341
|
+
? "write_roots"
|
|
342
|
+
: "attributed_elsewhere",
|
|
343
|
+
decisive: [...decisive].sort(),
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* One settlement barrier per wave of a parallel dispatch batch.
|
|
349
|
+
*
|
|
350
|
+
* A check run while a sibling is still editing the shared checkout judges a tree
|
|
351
|
+
* neither worker authored: three coders on one kvlog batch (round 5, 2026-09-02)
|
|
352
|
+
* all sealed `host_verification_rejected` for one worker's broken test. Every
|
|
353
|
+
* live member parks at the verification step, each distinct resolved check runs
|
|
354
|
+
* once for the whole wave on the settled tree, and a failure is charged by
|
|
355
|
+
* write-root coverage across the wave.
|
|
356
|
+
*
|
|
357
|
+
* Membership is what is LIVE, never what was requested. A run still queued for
|
|
358
|
+
* capacity holds no lease and cannot write, and waiting on it would deadlock
|
|
359
|
+
* every batch larger than `fleet.concurrency`: parked members keep their lease
|
|
360
|
+
* until dispatch finalization ends (`extension.ts:5720`), so the queued member's
|
|
361
|
+
* `admit` would time out (`admission.ts:244-291`) and take the whole batch down
|
|
362
|
+
* with it. Live-only membership still costs a queued member the difference
|
|
363
|
+
* between the first live member finishing and the last, charged against the same
|
|
364
|
+
* 60 s admission deadline (`extension.ts:2669`). That wait is not free: a batch
|
|
365
|
+
* whose first member finishes inside the deadline and whose last does not now
|
|
366
|
+
* expires the queued member's `admit`, and `dispatchBatch` aborts every already
|
|
367
|
+
* dispatched run when one member's admission throws (`extension.ts:6197-6205`).
|
|
368
|
+
* Releasing the lease before parking would let a fresh writer into the checkout
|
|
369
|
+
* the settlement is about to judge, which is the defect itself, so the wait
|
|
370
|
+
* stands until the lease lifecycle is restructured.
|
|
371
|
+
*
|
|
372
|
+
* The gate is multi-shot: a batch larger than the effective capacity admits in
|
|
373
|
+
* waves, and each wave forms its own barrier from the members live at that
|
|
374
|
+
* moment. Latching after the first settlement would drop every later member onto
|
|
375
|
+
* the per-run path and reproduce the original defect verbatim, since those
|
|
376
|
+
* members are admitted together and would run the shared check concurrently on
|
|
377
|
+
* the checkout their siblings are still editing.
|
|
378
|
+
*
|
|
379
|
+
* One narrow window survives, and it is the reason `live()` registers as early
|
|
380
|
+
* as the worker spawn: a member that goes live WHILE a wave's checks are
|
|
381
|
+
* executing joins the next wave, so its edits can land in the tree the running
|
|
382
|
+
* check is judging. Reaching it takes either a lease freed mid-settlement, which
|
|
383
|
+
* means a sibling whose finalization failed before the barrier, or a member of
|
|
384
|
+
* `dispatchBatch`'s sequential loop (`extension.ts:6194`) whose worker had not
|
|
385
|
+
* spawned yet when every earlier member had already finished. The next wave
|
|
386
|
+
* re-runs the check on the settled tree, so a stale verdict is bounded to the
|
|
387
|
+
* one wave that raced.
|
|
388
|
+
*/
|
|
389
|
+
export function createBatchVerificationGate(
|
|
390
|
+
options: { stateDir?: string; env?: NodeJS.ProcessEnv } = {},
|
|
391
|
+
): BatchVerificationGate {
|
|
392
|
+
const stateDir = options.stateDir ?? clioStateDir();
|
|
393
|
+
const env = options.env ?? process.env;
|
|
394
|
+
const liveMembers = new Set<string>();
|
|
395
|
+
const parked = new Map<string, ParkedMember>();
|
|
396
|
+
let settling = false;
|
|
397
|
+
|
|
398
|
+
const perRun = (participant: BatchVerificationParticipant): Promise<RunHostVerification | undefined> =>
|
|
399
|
+
runHostVerification({
|
|
400
|
+
runId: participant.runId,
|
|
401
|
+
request: participant.request,
|
|
402
|
+
workerSuccessful: participant.workerSuccessful,
|
|
403
|
+
stateDir,
|
|
152
404
|
env,
|
|
405
|
+
...(participant.onDiagnostic !== undefined ? { onDiagnostic: participant.onDiagnostic } : {}),
|
|
153
406
|
});
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
407
|
+
|
|
408
|
+
/** Today's value for a member that contributes no check; runs no command. */
|
|
409
|
+
const nothingToRun = (participant: BatchVerificationParticipant): RunHostVerification | undefined => {
|
|
410
|
+
const checks = participant.request.resolvedVerification;
|
|
411
|
+
if (checks === undefined || checks.length === 0) return undefined;
|
|
412
|
+
return { status: "skipped", reason: "worker_not_successful", checks: [] };
|
|
413
|
+
};
|
|
414
|
+
|
|
415
|
+
async function settleBatch(wave: ReadonlyArray<ParkedMember>): Promise<void> {
|
|
416
|
+
const members = wave.map((entry) => entry.participant);
|
|
417
|
+
const contributors = members.filter(
|
|
418
|
+
(member) => member.workerSuccessful && (member.request.resolvedVerification?.length ?? 0) > 0,
|
|
419
|
+
);
|
|
420
|
+
const distinct = new Map<
|
|
421
|
+
string,
|
|
422
|
+
{ resolvedCheck: ResolvedCheck; owner: BatchVerificationParticipant; index: number }
|
|
423
|
+
>();
|
|
424
|
+
for (const member of contributors) {
|
|
425
|
+
for (const resolvedCheck of member.request.resolvedVerification ?? []) {
|
|
426
|
+
const key = checkDedupeKey(resolvedCheck);
|
|
427
|
+
if (!distinct.has(key)) distinct.set(key, { resolvedCheck, owner: member, index: distinct.size });
|
|
171
428
|
}
|
|
172
429
|
}
|
|
430
|
+
const ran = new Map<string, { check: RunHostVerificationCheck; owner: string; outputExcerpt: string }>();
|
|
431
|
+
for (const [key, entry] of distinct) {
|
|
432
|
+
const outcome = await runResolvedCheck({
|
|
433
|
+
runId: entry.owner.runId,
|
|
434
|
+
index: entry.index,
|
|
435
|
+
resolvedCheck: entry.resolvedCheck,
|
|
436
|
+
stateDir,
|
|
437
|
+
env,
|
|
438
|
+
// The owning member's callback, because the artifact directory is
|
|
439
|
+
// named for that same run and the diagnostic text is closed over the
|
|
440
|
+
// run id it was built for (`extension.ts:5534`); any other member's
|
|
441
|
+
// callback would name a run whose artifacts the failure is not under.
|
|
442
|
+
...(entry.owner.onDiagnostic !== undefined ? { onDiagnostic: entry.owner.onDiagnostic } : {}),
|
|
443
|
+
});
|
|
444
|
+
ran.set(key, { check: outcome.check, owner: entry.owner.runId, outputExcerpt: outcome.outputExcerpt });
|
|
445
|
+
}
|
|
446
|
+
const attribution: RunHostVerificationAttribution[] = [];
|
|
447
|
+
const chargedByKey = new Map<string, Set<string>>();
|
|
448
|
+
for (const [key, entry] of ran) {
|
|
449
|
+
if (entry.check.exitCode === 0) continue;
|
|
450
|
+
const declarers = contributors.filter((member) =>
|
|
451
|
+
(member.request.resolvedVerification ?? []).some((candidate) => checkDedupeKey(candidate) === key),
|
|
452
|
+
);
|
|
453
|
+
const implicated = implicatedPaths(entry.outputExcerpt, entry.check.cwd);
|
|
454
|
+
const verdict = attributeFailure({ implicated, declarers, wave: members });
|
|
455
|
+
chargedByKey.set(key, verdict.charged);
|
|
456
|
+
const decisive = new Set(verdict.decisive);
|
|
457
|
+
attribution.push({
|
|
458
|
+
check: entry.check.check,
|
|
459
|
+
// The paths that decided the charge lead the capped list. A large
|
|
460
|
+
// suite names more than 32 files and the sealed evidence has to
|
|
461
|
+
// contain the path a receipt was charged on, or it documents nothing.
|
|
462
|
+
implicated: [...verdict.decisive, ...implicated.filter((path) => !decisive.has(path))].slice(
|
|
463
|
+
0,
|
|
464
|
+
IMPLICATED_PATH_CAP,
|
|
465
|
+
),
|
|
466
|
+
charged: [...verdict.charged].sort(),
|
|
467
|
+
basis: verdict.basis,
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
attribution.sort((first, second) => (first.check < second.check ? -1 : first.check > second.check ? 1 : 0));
|
|
471
|
+
for (const entry of wave) {
|
|
472
|
+
const member = entry.participant;
|
|
473
|
+
if (!contributors.includes(member)) {
|
|
474
|
+
entry.settle(nothingToRun(member));
|
|
475
|
+
continue;
|
|
476
|
+
}
|
|
477
|
+
const own: RunHostVerificationCheck[] = [];
|
|
478
|
+
let rejected = false;
|
|
479
|
+
let unimplicated = false;
|
|
480
|
+
for (const resolvedCheck of member.request.resolvedVerification ?? []) {
|
|
481
|
+
const key = checkDedupeKey(resolvedCheck);
|
|
482
|
+
const shared = ran.get(key);
|
|
483
|
+
if (shared === undefined) continue;
|
|
484
|
+
own.push(
|
|
485
|
+
shared.owner === member.runId || shared.check.evidenceRunId !== undefined
|
|
486
|
+
? { ...shared.check, argv: [...shared.check.argv] }
|
|
487
|
+
: { ...shared.check, argv: [...shared.check.argv], evidenceRunId: shared.owner },
|
|
488
|
+
);
|
|
489
|
+
if (shared.check.exitCode === 0) continue;
|
|
490
|
+
if (chargedByKey.get(key)?.has(member.runId) === true) rejected = true;
|
|
491
|
+
else unimplicated = true;
|
|
492
|
+
}
|
|
493
|
+
entry.settle({
|
|
494
|
+
// `verified` states that every declared check passed, which is what
|
|
495
|
+
// `trust-status.ts:619` certifies and what the board and
|
|
496
|
+
// `worker-evidence.ts:161` print. An exculpated member's checks array
|
|
497
|
+
// still carries a non-zero exit code, so it seals its own status
|
|
498
|
+
// instead of borrowing that claim.
|
|
499
|
+
status: rejected ? "rejected" : unimplicated ? "not_implicated" : "verified",
|
|
500
|
+
...(!rejected && unimplicated ? { reason: "batch_settled_not_implicated" } : {}),
|
|
501
|
+
checks: own,
|
|
502
|
+
strategy: "batch-settled",
|
|
503
|
+
// Copied per receipt so no two members of the batch share a mutable array.
|
|
504
|
+
...(attribution.length > 0
|
|
505
|
+
? {
|
|
506
|
+
attribution: attribution.map((record) => ({
|
|
507
|
+
...record,
|
|
508
|
+
implicated: [...record.implicated],
|
|
509
|
+
charged: [...record.charged],
|
|
510
|
+
})),
|
|
511
|
+
}
|
|
512
|
+
: {}),
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
function maybeSettle(): void {
|
|
518
|
+
if (settling || parked.size === 0) return;
|
|
519
|
+
for (const runId of liveMembers) {
|
|
520
|
+
if (!parked.has(runId)) return;
|
|
521
|
+
}
|
|
522
|
+
settling = true;
|
|
523
|
+
// The wave closes here. Its members leave `liveMembers` so a member that
|
|
524
|
+
// arrives during the settlement forms the next barrier instead of joining
|
|
525
|
+
// one whose checks are already executing.
|
|
526
|
+
const wave = [...parked.values()];
|
|
527
|
+
for (const runId of parked.keys()) liveMembers.delete(runId);
|
|
528
|
+
parked.clear();
|
|
529
|
+
void settleBatch(wave)
|
|
530
|
+
.catch(async (error) => {
|
|
531
|
+
// The barrier must never be the reason a receipt cannot seal, so a
|
|
532
|
+
// member whose own checks still run gets today's per-run result. A
|
|
533
|
+
// member whose per-run attempt throws the same way takes the throw:
|
|
534
|
+
// swallowing it would seal a run that declared a check it never
|
|
535
|
+
// executed as `host_verification=not_requested` and succeeded, while
|
|
536
|
+
// the identical single-task dispatch fails the run.
|
|
537
|
+
for (const entry of wave) {
|
|
538
|
+
entry.participant.onDiagnostic?.(error);
|
|
539
|
+
try {
|
|
540
|
+
entry.settle(await perRun(entry.participant));
|
|
541
|
+
} catch (fallbackError) {
|
|
542
|
+
entry.fail(fallbackError);
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
})
|
|
546
|
+
.finally(() => {
|
|
547
|
+
settling = false;
|
|
548
|
+
maybeSettle();
|
|
549
|
+
});
|
|
173
550
|
}
|
|
551
|
+
|
|
174
552
|
return {
|
|
175
|
-
|
|
176
|
-
|
|
553
|
+
live(runId) {
|
|
554
|
+
liveMembers.add(runId);
|
|
555
|
+
},
|
|
556
|
+
abandon(runId) {
|
|
557
|
+
// Dropping the id out of `liveMembers` rather than remembering it in a
|
|
558
|
+
// second set keeps the membership of a later wave exact: the same run
|
|
559
|
+
// abandons again when its finalPromise settles, long after its own wave.
|
|
560
|
+
if (parked.has(runId) || !liveMembers.delete(runId)) return;
|
|
561
|
+
maybeSettle();
|
|
562
|
+
},
|
|
563
|
+
arrive(input) {
|
|
564
|
+
liveMembers.add(input.runId);
|
|
565
|
+
// The executor runs synchronously, so the member is registered before
|
|
566
|
+
// arrive() yields and two concurrent arrivals cannot miss each other.
|
|
567
|
+
const parkedPromise = new Promise<RunHostVerification | undefined>((settle, fail) => {
|
|
568
|
+
parked.set(input.runId, { participant: input, settle, fail });
|
|
569
|
+
});
|
|
570
|
+
maybeSettle();
|
|
571
|
+
return parkedPromise;
|
|
572
|
+
},
|
|
177
573
|
};
|
|
178
574
|
}
|
|
@@ -7,6 +7,16 @@ export function renderDispatchIntentRequirements(intent: DispatchIntent | undefi
|
|
|
7
7
|
"# Declared Result Requirements",
|
|
8
8
|
"These entries are requirements to satisfy and verify. They are not evidence that an output exists or a check passed.",
|
|
9
9
|
];
|
|
10
|
+
// A run confined to write roots has no bash or verify tool: the rail cannot
|
|
11
|
+
// police a shell, so it is not offered. Left unsaid, a worker told to "run
|
|
12
|
+
// the tests" spent 40 code_nav calls looking for a way to run them and
|
|
13
|
+
// exhausted its budget before touching the source (kvlog exercise, r5).
|
|
14
|
+
if (intent.writeRoots.length > 0) {
|
|
15
|
+
lines.push(
|
|
16
|
+
"",
|
|
17
|
+
"This run is confined to its write roots and has no bash or verify tool, so you cannot run tests, scripts, or the checks below yourself. Write the code and the tests, re-read them, and report what you wrote; the host runs the declared checks after you finish and records the result on the receipt.",
|
|
18
|
+
);
|
|
19
|
+
}
|
|
10
20
|
if (intent.expectedOutputs.length > 0) {
|
|
11
21
|
lines.push("", "Expected outputs:", ...intent.expectedOutputs.map((output) => `- ${output}`));
|
|
12
22
|
}
|
|
@@ -135,6 +135,14 @@ function normalizePathList(
|
|
|
135
135
|
}
|
|
136
136
|
const normalized: string[] = [];
|
|
137
137
|
for (const [index, entry] of value.entries()) {
|
|
138
|
+
// A scope entry of "." or "./" names the repository root, which is what
|
|
139
|
+
// an empty scope list already means. Local models write it to say "the
|
|
140
|
+
// whole repository" and then spend a round on the rejection, so it is
|
|
141
|
+
// read as that rather than refused; an output path must still name a
|
|
142
|
+
// file, so the leniency stops at scope fields.
|
|
143
|
+
if (normalizeEntry === normalizeScopePathEntry && typeof entry === "string" && /^\.\/?$/u.test(entry.trim())) {
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
138
146
|
const result = normalizeEntry(entry, field, index);
|
|
139
147
|
if (typeof result !== "string") return result;
|
|
140
148
|
normalized.push(result);
|
|
@@ -286,10 +294,19 @@ export function normalizeDispatchIntent(
|
|
|
286
294
|
}
|
|
287
295
|
const check = entry.check.trim();
|
|
288
296
|
const declared = checks.get(check);
|
|
297
|
+
// The word a model writes to say "no verification". It is not a check
|
|
298
|
+
// id, and refusing it cost a dispatch round every time it appeared, so
|
|
299
|
+
// it reads as the empty declaration it means unless the project really
|
|
300
|
+
// declared a check by that name.
|
|
301
|
+
if (declared === undefined && check.toLowerCase() === "none") continue;
|
|
289
302
|
if (declared === undefined) {
|
|
303
|
+
// The model that hits this most often has put a shell command here
|
|
304
|
+
// ("git diff src/x.ts"), so the message has to say what an entry is
|
|
305
|
+
// and offer the move that always works: leave verification out and
|
|
306
|
+
// run the check after the receipt.
|
|
290
307
|
return fail(
|
|
291
308
|
"verification_check_undeclared",
|
|
292
|
-
`verification check '${check}' is undeclared
|
|
309
|
+
`verification check '${check}' is undeclared: intent.verification takes only check ids that verify() lists (package.json verification scripts or .clio-coder/verifiers.yaml entries), never a shell command; omit verification and run the check yourself after the receipt, or declare the check first`,
|
|
293
310
|
);
|
|
294
311
|
}
|
|
295
312
|
if (
|