zob-harness 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.pi/adapters/registry.json +103 -0
- package/.pi/agents/architecture-cartographer.md +53 -0
- package/.pi/agents/chief-vision.md +39 -0
- package/.pi/agents/clarifier.md +58 -0
- package/.pi/agents/context-steward.md +52 -0
- package/.pi/agents/doc-steward.md +34 -0
- package/.pi/agents/explore.md +49 -0
- package/.pi/agents/factory.md +41 -0
- package/.pi/agents/implementer.md +44 -0
- package/.pi/agents/librarian.md +32 -0
- package/.pi/agents/oracle-merge.md +50 -0
- package/.pi/agents/oracle.md +55 -0
- package/.pi/agents/pattern-miner.md +53 -0
- package/.pi/agents/planner.md +39 -0
- package/.pi/agents/project-dna-golden-evaluator.md +32 -0
- package/.pi/agents/project-dna-ontology-steward.md +30 -0
- package/.pi/agents/project-dna-oracle.md +56 -0
- package/.pi/agents/project-dna-orchestrator.md +60 -0
- package/.pi/agents/project-dna-query-steward.md +38 -0
- package/.pi/agents/project-dna-safety-preflight.md +54 -0
- package/.pi/agents/project-dna-test-linker.md +27 -0
- package/.pi/agents/qa.md +38 -0
- package/.pi/agents/refactor-cartographer.md +28 -0
- package/.pi/agents/refactor-mover.md +31 -0
- package/.pi/agents/refactor-oracle.md +49 -0
- package/.pi/agents/repo-scout.md +60 -0
- package/.pi/agents/sample-architect.md +48 -0
- package/.pi/agents/specifier.md +57 -0
- package/.pi/agents/symbol-range-curator.md +41 -0
- package/.pi/agents/synthesis.md +52 -0
- package/.pi/agents/temp-agent-creator.md +35 -0
- package/.pi/autonomy-policy.json +67 -0
- package/.pi/budget-policy.json +54 -0
- package/.pi/capabilities/zob-public-runtime-capabilities.json +1700 -0
- package/.pi/chains/explore-plan-oracle.json +78 -0
- package/.pi/chains/explore-spec-clarify-plan-oracle.json +64 -0
- package/.pi/chains/explore-spec-plan-oracle.json +53 -0
- package/.pi/chains/spec-clarify-plan-oracle.json +53 -0
- package/.pi/chains/spec-factory-oracle.json +42 -0
- package/.pi/chains/spec-plan-oracle.json +42 -0
- package/.pi/compute-profiles/defaults.json +19 -0
- package/.pi/compute-profiles/overrides.json +13 -0
- package/.pi/compute-profiles/risk-rules.json +16 -0
- package/.pi/daemon-policy.json +80 -0
- package/.pi/damage-control-rules.json +45 -0
- package/.pi/extensions/zob-child-safety/index.ts +212 -0
- package/.pi/extensions/zob-harness/AGENTS.md +28 -0
- package/.pi/extensions/zob-harness/index.ts +391 -0
- package/.pi/extensions/zob-harness/src/AGENTS.md +25 -0
- package/.pi/extensions/zob-harness/src/agents.ts +82 -0
- package/.pi/extensions/zob-harness/src/autonomous-runtime.ts +2912 -0
- package/.pi/extensions/zob-harness/src/autonomy-readiness.ts +778 -0
- package/.pi/extensions/zob-harness/src/budget-policy.ts +308 -0
- package/.pi/extensions/zob-harness/src/capabilities.ts +249 -0
- package/.pi/extensions/zob-harness/src/child-runner.ts +249 -0
- package/.pi/extensions/zob-harness/src/chronicle.ts +262 -0
- package/.pi/extensions/zob-harness/src/compute-profile.ts +602 -0
- package/.pi/extensions/zob-harness/src/compute-workflow-shape.ts +168 -0
- package/.pi/extensions/zob-harness/src/coms-v2/AGENTS.md +16 -0
- package/.pi/extensions/zob-harness/src/coms-v2/envelope.ts +121 -0
- package/.pi/extensions/zob-harness/src/coms-v2/identity.ts +53 -0
- package/.pi/extensions/zob-harness/src/coms-v2/ledger-bridge.ts +67 -0
- package/.pi/extensions/zob-harness/src/coms-v2/local-transport.ts +147 -0
- package/.pi/extensions/zob-harness/src/coms-v2/pending-replies.ts +80 -0
- package/.pi/extensions/zob-harness/src/coms-v2/policy.ts +125 -0
- package/.pi/extensions/zob-harness/src/coms-v2/presence.ts +55 -0
- package/.pi/extensions/zob-harness/src/coms-v2/registry.ts +113 -0
- package/.pi/extensions/zob-harness/src/coms-v2/response-capture.ts +50 -0
- package/.pi/extensions/zob-harness/src/coms-v2/transcript-capture.ts +164 -0
- package/.pi/extensions/zob-harness/src/coms-v2/types.ts +149 -0
- package/.pi/extensions/zob-harness/src/coms-v2/zpeer-profile.ts +140 -0
- package/.pi/extensions/zob-harness/src/coms-v2/zpeer.ts +452 -0
- package/.pi/extensions/zob-harness/src/constants.ts +108 -0
- package/.pi/extensions/zob-harness/src/context-gbrain.ts +465 -0
- package/.pi/extensions/zob-harness/src/daemon-policy.ts +223 -0
- package/.pi/extensions/zob-harness/src/daemon-readiness.ts +134 -0
- package/.pi/extensions/zob-harness/src/daemon-runtime.ts +393 -0
- package/.pi/extensions/zob-harness/src/factory/AGENTS.md +24 -0
- package/.pi/extensions/zob-harness/src/factory/agentic-plan.ts +65 -0
- package/.pi/extensions/zob-harness/src/factory/quarantine.ts +319 -0
- package/.pi/extensions/zob-harness/src/factory/run.ts +520 -0
- package/.pi/extensions/zob-harness/src/factory/validation.ts +454 -0
- package/.pi/extensions/zob-harness/src/factory-selector.ts +318 -0
- package/.pi/extensions/zob-harness/src/full-autonomy-test.ts +226 -0
- package/.pi/extensions/zob-harness/src/git-ops.ts +868 -0
- package/.pi/extensions/zob-harness/src/goal-room.ts +178 -0
- package/.pi/extensions/zob-harness/src/goal-runtime.ts +1569 -0
- package/.pi/extensions/zob-harness/src/goal-todo-imports.ts +111 -0
- package/.pi/extensions/zob-harness/src/goal-todo-types.ts +231 -0
- package/.pi/extensions/zob-harness/src/goal-todos.ts +1410 -0
- package/.pi/extensions/zob-harness/src/goal.ts +152 -0
- package/.pi/extensions/zob-harness/src/governed-requests.ts +436 -0
- package/.pi/extensions/zob-harness/src/interactive-autonomy.ts +595 -0
- package/.pi/extensions/zob-harness/src/launch-apply.ts +313 -0
- package/.pi/extensions/zob-harness/src/merge-queue.ts +290 -0
- package/.pi/extensions/zob-harness/src/mission-control.ts +573 -0
- package/.pi/extensions/zob-harness/src/model-availability.ts +52 -0
- package/.pi/extensions/zob-harness/src/model-routing.ts +429 -0
- package/.pi/extensions/zob-harness/src/orchestration/AGENTS.md +23 -0
- package/.pi/extensions/zob-harness/src/orchestration/adaptive-delegation.ts +547 -0
- package/.pi/extensions/zob-harness/src/orchestration/adaptive-workflow.ts +585 -0
- package/.pi/extensions/zob-harness/src/orchestration/lead-plan.ts +192 -0
- package/.pi/extensions/zob-harness/src/orchestration/plan.ts +168 -0
- package/.pi/extensions/zob-harness/src/orchestration/room.ts +346 -0
- package/.pi/extensions/zob-harness/src/orchestration/run.ts +134 -0
- package/.pi/extensions/zob-harness/src/orchestration/supervised-readonly.ts +1147 -0
- package/.pi/extensions/zob-harness/src/orchestration/widget-readers.ts +132 -0
- package/.pi/extensions/zob-harness/src/output-contracts.ts +656 -0
- package/.pi/extensions/zob-harness/src/project-dna.ts +533 -0
- package/.pi/extensions/zob-harness/src/promotion/AGENTS.md +24 -0
- package/.pi/extensions/zob-harness/src/promotion/candidate.ts +336 -0
- package/.pi/extensions/zob-harness/src/promotion/coms.ts +127 -0
- package/.pi/extensions/zob-harness/src/promotion/documentation.ts +142 -0
- package/.pi/extensions/zob-harness/src/promotion/factory.ts +107 -0
- package/.pi/extensions/zob-harness/src/promotion/ledger.ts +2 -0
- package/.pi/extensions/zob-harness/src/promotion/temp-agent.ts +151 -0
- package/.pi/extensions/zob-harness/src/promotion/types.ts +149 -0
- package/.pi/extensions/zob-harness/src/promotion/validate.ts +6 -0
- package/.pi/extensions/zob-harness/src/promotion/write-lane.ts +162 -0
- package/.pi/extensions/zob-harness/src/prompt-packs.ts +239 -0
- package/.pi/extensions/zob-harness/src/queue.ts +386 -0
- package/.pi/extensions/zob-harness/src/rules.ts +225 -0
- package/.pi/extensions/zob-harness/src/runtime/AGENTS.md +26 -0
- package/.pi/extensions/zob-harness/src/runtime/adaptive-zmode.ts +116 -0
- package/.pi/extensions/zob-harness/src/runtime/auto-compaction.ts +715 -0
- package/.pi/extensions/zob-harness/src/runtime/commands.ts +1315 -0
- package/.pi/extensions/zob-harness/src/runtime/compaction-policy.ts +516 -0
- package/.pi/extensions/zob-harness/src/runtime/delegation-click-markers.ts +141 -0
- package/.pi/extensions/zob-harness/src/runtime/delegation-feed.ts +415 -0
- package/.pi/extensions/zob-harness/src/runtime/delegation-markdown.ts +97 -0
- package/.pi/extensions/zob-harness/src/runtime/delegation-monitor.ts +553 -0
- package/.pi/extensions/zob-harness/src/runtime/delegation-mouse.ts +205 -0
- package/.pi/extensions/zob-harness/src/runtime/delegation-overlay.ts +434 -0
- package/.pi/extensions/zob-harness/src/runtime/events.ts +736 -0
- package/.pi/extensions/zob-harness/src/runtime/goal-todo-overlay.ts +214 -0
- package/.pi/extensions/zob-harness/src/runtime/mode-intent.ts +144 -0
- package/.pi/extensions/zob-harness/src/runtime/plan-capture.ts +270 -0
- package/.pi/extensions/zob-harness/src/runtime/state.ts +403 -0
- package/.pi/extensions/zob-harness/src/runtime/tools-autonomous.ts +117 -0
- package/.pi/extensions/zob-harness/src/runtime/tools-compute.ts +136 -0
- package/.pi/extensions/zob-harness/src/runtime/tools-coms.ts +365 -0
- package/.pi/extensions/zob-harness/src/runtime/tools-context.ts +70 -0
- package/.pi/extensions/zob-harness/src/runtime/tools-delegation.ts +1854 -0
- package/.pi/extensions/zob-harness/src/runtime/tools-factory.ts +810 -0
- package/.pi/extensions/zob-harness/src/runtime/tools-goal-room.ts +46 -0
- package/.pi/extensions/zob-harness/src/runtime/tools-governed-requests.ts +38 -0
- package/.pi/extensions/zob-harness/src/runtime/tools-merge-queue.ts +61 -0
- package/.pi/extensions/zob-harness/src/runtime/tools-mission-control.ts +77 -0
- package/.pi/extensions/zob-harness/src/runtime/tools-orchestration.ts +106 -0
- package/.pi/extensions/zob-harness/src/runtime/tools-project-dna.ts +123 -0
- package/.pi/extensions/zob-harness/src/runtime/tools-worker-pool.ts +93 -0
- package/.pi/extensions/zob-harness/src/runtime/tools-workspace-claims.ts +62 -0
- package/.pi/extensions/zob-harness/src/runtime/tools-zcommit.ts +147 -0
- package/.pi/extensions/zob-harness/src/runtime/widget.ts +353 -0
- package/.pi/extensions/zob-harness/src/runtime/zobHarness.ts +60 -0
- package/.pi/extensions/zob-harness/src/safety.ts +338 -0
- package/.pi/extensions/zob-harness/src/sandbox.ts +1508 -0
- package/.pi/extensions/zob-harness/src/schemas-project-dna.ts +47 -0
- package/.pi/extensions/zob-harness/src/schemas.ts +695 -0
- package/.pi/extensions/zob-harness/src/telemetry.ts +373 -0
- package/.pi/extensions/zob-harness/src/topology/AGENTS.md +22 -0
- package/.pi/extensions/zob-harness/src/topology/chains.ts +236 -0
- package/.pi/extensions/zob-harness/src/topology/coms.ts +211 -0
- package/.pi/extensions/zob-harness/src/topology/orchestration-profiles.ts +204 -0
- package/.pi/extensions/zob-harness/src/topology/teams.ts +113 -0
- package/.pi/extensions/zob-harness/src/types/core.ts +47 -0
- package/.pi/extensions/zob-harness/src/types.ts +939 -0
- package/.pi/extensions/zob-harness/src/utils/AGENTS.md +22 -0
- package/.pi/extensions/zob-harness/src/utils/formatting.ts +34 -0
- package/.pi/extensions/zob-harness/src/utils/hashing.ts +11 -0
- package/.pi/extensions/zob-harness/src/utils/json.ts +28 -0
- package/.pi/extensions/zob-harness/src/utils/paths.ts +54 -0
- package/.pi/extensions/zob-harness/src/utils/records.ts +25 -0
- package/.pi/extensions/zob-harness/src/utils/resources.ts +38 -0
- package/.pi/extensions/zob-harness/src/worker-pool.ts +672 -0
- package/.pi/extensions/zob-harness/src/workspace-claims.ts +297 -0
- package/.pi/extensions/zob-switch/index.ts +180 -0
- package/.pi/factories/budget-preflight-dry-run/batch-manifest.json +59 -0
- package/.pi/factories/budget-preflight-dry-run/factory.json +94 -0
- package/.pi/factories/budget-preflight-dry-run/pilot-manifest.json +50 -0
- package/.pi/factories/budget-preflight-dry-run/smoke-manifest.json +43 -0
- package/.pi/factories/code-review-matrix/batch-manifest.json +61 -0
- package/.pi/factories/code-review-matrix/factory.json +163 -0
- package/.pi/factories/code-review-matrix/pilot-manifest.json +41 -0
- package/.pi/factories/code-review-matrix/smoke-manifest.json +35 -0
- package/.pi/factories/factory-forge/batch-manifest.json +56 -0
- package/.pi/factories/factory-forge/factory.json +84 -0
- package/.pi/factories/factory-forge/pilot-manifest.json +32 -0
- package/.pi/factories/factory-forge/smoke-manifest.json +19 -0
- package/.pi/factories/opencode-pattern-canonizer/batch-manifest.json +54 -0
- package/.pi/factories/opencode-pattern-canonizer/factory.json +86 -0
- package/.pi/factories/opencode-pattern-canonizer/pilot-manifest.json +39 -0
- package/.pi/factories/opencode-pattern-canonizer/smoke-manifest.json +26 -0
- package/.pi/factories/project-dna/README.md +182 -0
- package/.pi/factories/project-dna/batch-manifest.json +37 -0
- package/.pi/factories/project-dna/example-project-dna-manifest-v2.json +80 -0
- package/.pi/factories/project-dna/example-project-dna-manifest.json +58 -0
- package/.pi/factories/project-dna/factory.json +131 -0
- package/.pi/factories/project-dna/golden-cases-smoke.json +62 -0
- package/.pi/factories/project-dna/pi-agentic-ontology.json +88 -0
- package/.pi/factories/project-dna/pilot-manifest.json +32 -0
- package/.pi/factories/project-dna/schemas/benchmark-suite.schema.json +27 -0
- package/.pi/factories/project-dna/schemas/code-knowledge-graph.schema.json +97 -0
- package/.pi/factories/project-dna/schemas/context-pack.schema.json +43 -0
- package/.pi/factories/project-dna/schemas/golden-case.schema.json +36 -0
- package/.pi/factories/project-dna/schemas/manifest-v2.schema.json +128 -0
- package/.pi/factories/project-dna/schemas/manifest.schema.json +77 -0
- package/.pi/factories/project-dna/schemas/ontology.schema.json +45 -0
- package/.pi/factories/project-dna/schemas/project-fingerprint.schema.json +28 -0
- package/.pi/factories/project-dna/schemas/query-steward-report.schema.json +52 -0
- package/.pi/factories/project-dna/smoke-manifest.json +27 -0
- package/.pi/factories/roadmap-smoke-lots/batch-manifest.json +49 -0
- package/.pi/factories/roadmap-smoke-lots/factory.json +89 -0
- package/.pi/factories/roadmap-smoke-lots/pilot-manifest.json +50 -0
- package/.pi/factories/roadmap-smoke-lots/smoke-manifest.json +35 -0
- package/.pi/git-policy.json +120 -0
- package/.pi/mission-control/zob_coms_transport.json +64 -0
- package/.pi/model-catalog.example.json +345 -0
- package/.pi/model-economy.example.json +196 -0
- package/.pi/model-routing.json +86 -0
- package/.pi/orchestrations/adaptive-chief-vision.json +193 -0
- package/.pi/orchestrations/ceo-feature-build.json +182 -0
- package/.pi/orchestrations/readonly-dynamic-smoke.json +75 -0
- package/.pi/output-contracts/agent-event.v1.json +19 -0
- package/.pi/output-contracts/base.v1.json +24 -0
- package/.pi/output-contracts/brain-lookup.v1.json +21 -0
- package/.pi/output-contracts/clarification.v1.json +21 -0
- package/.pi/output-contracts/context-pack.v1.json +20 -0
- package/.pi/output-contracts/context-request.v1.json +21 -0
- package/.pi/output-contracts/context-steward.v1.json +19 -0
- package/.pi/output-contracts/context-writeback-proposal.v1.json +18 -0
- package/.pi/output-contracts/delegation-request.v1.json +21 -0
- package/.pi/output-contracts/explore.v1.json +52 -0
- package/.pi/output-contracts/factory.v1.json +48 -0
- package/.pi/output-contracts/guidance-steward.v1.json +18 -0
- package/.pi/output-contracts/implement.v1.json +40 -0
- package/.pi/output-contracts/launch-authorization.v1.json +21 -0
- package/.pi/output-contracts/lead-plan.v1.json +22 -0
- package/.pi/output-contracts/mission-readiness.v1.json +20 -0
- package/.pi/output-contracts/oracle-merge.v1.json +44 -0
- package/.pi/output-contracts/oracle-request.v1.json +20 -0
- package/.pi/output-contracts/oracle.v1.json +44 -0
- package/.pi/output-contracts/orchestration-profile.v1.json +22 -0
- package/.pi/output-contracts/plan.v1.json +48 -0
- package/.pi/output-contracts/prompt-pack.v1.json +20 -0
- package/.pi/output-contracts/qa.v1.json +40 -0
- package/.pi/output-contracts/research.v1.json +36 -0
- package/.pi/output-contracts/spec.v1.json +22 -0
- package/.pi/output-contracts/synthesis.v1.json +44 -0
- package/.pi/output-contracts/temp-agent-card.v1.json +23 -0
- package/.pi/output-contracts/todo-child-result.v1.json +20 -0
- package/.pi/output-contracts/todo-child-result.v2.json +22 -0
- package/.pi/output-contracts/todo-claim-validation.v1.json +22 -0
- package/.pi/output-contracts/todo-split-request.v1.json +20 -0
- package/.pi/prompts/adaptive-workflow.md +63 -0
- package/.pi/prompts/autonomous-runtime.md +15 -0
- package/.pi/prompts/benchmark-contender.md +15 -0
- package/.pi/prompts/benchmark-judge.md +19 -0
- package/.pi/prompts/clarify-spec.md +20 -0
- package/.pi/prompts/compute-plan.md +36 -0
- package/.pi/prompts/compute-preview.md +42 -0
- package/.pi/prompts/contract.md +29 -0
- package/.pi/prompts/explore.md +13 -0
- package/.pi/prompts/factory-run.md +36 -0
- package/.pi/prompts/factory.md +20 -0
- package/.pi/prompts/implement.md +27 -0
- package/.pi/prompts/model-catalog.md +68 -0
- package/.pi/prompts/model-economy.md +64 -0
- package/.pi/prompts/oracle-merge.md +18 -0
- package/.pi/prompts/oracle.md +13 -0
- package/.pi/prompts/orchestrator.md +48 -0
- package/.pi/prompts/parallel-review.md +21 -0
- package/.pi/prompts/plan.md +21 -0
- package/.pi/prompts/project-dna.md +90 -0
- package/.pi/prompts/refactor-oracle.md +23 -0
- package/.pi/prompts/refactor-slice.md +24 -0
- package/.pi/prompts/research.md +20 -0
- package/.pi/prompts/spec.md +19 -0
- package/.pi/prompts/synthesis.md +18 -0
- package/.pi/rules/always.md +38 -0
- package/.pi/rules/docs.md +32 -0
- package/.pi/rules/factory.md +44 -0
- package/.pi/rules/oracle.md +34 -0
- package/.pi/rules/orchestration.md +44 -0
- package/.pi/rules/project.md +34 -0
- package/.pi/rules/prompts.md +43 -0
- package/.pi/rules/runtime.md +43 -0
- package/.pi/rules/sandbox.md +43 -0
- package/.pi/settings.json +28 -0
- package/.pi/skills/zob-agentic-access/SKILL.md +20 -0
- package/.pi/skills/zob-autonomous-runtime/SKILL.md +41 -0
- package/.pi/skills/zob-commit/SKILL.md +79 -0
- package/.pi/skills/zob-compaction-policy/SKILL.md +92 -0
- package/.pi/skills/zob-compute-profile/SKILL.md +108 -0
- package/.pi/skills/zob-coms-safety/SKILL.md +54 -0
- package/.pi/skills/zob-coms-v2-live/SKILL.md +47 -0
- package/.pi/skills/zob-delegation-routing/SKILL.md +82 -0
- package/.pi/skills/zob-factory/SKILL.md +28 -0
- package/.pi/skills/zob-goal-todo-tree/SKILL.md +279 -0
- package/.pi/skills/zob-harness/SKILL.md +68 -0
- package/.pi/skills/zob-mission-control-coms/SKILL.md +39 -0
- package/.pi/skills/zob-oracle/SKILL.md +21 -0
- package/.pi/skills/zob-owner-pool-drill-writer/SKILL.md +244 -0
- package/.pi/skills/zob-owner-pool-launcher/SKILL.md +261 -0
- package/.pi/skills/zob-project-dna/SKILL.md +275 -0
- package/.pi/skills/zob-sandbox/SKILL.md +29 -0
- package/.pi/skills/zob-spec/SKILL.md +25 -0
- package/.pi/skills/zob-split-refactor/SKILL.md +39 -0
- package/.pi/skills/zob-tool-router/SKILL.md +104 -0
- package/.pi/teams/zob-core.json +122 -0
- package/AGENTS.md +89 -0
- package/CONTRIBUTING.md +56 -0
- package/LICENSE +21 -0
- package/README.md +360 -0
- package/SECURITY.md +35 -0
- package/SOURCE_INDEX.md +46 -0
- package/package.json +135 -0
- package/scripts/README.md +57 -0
- package/scripts/autonomy/mission-readiness-secret-smoke.mjs +90 -0
- package/scripts/compute-profile/plan-workflow.mjs +85 -0
- package/scripts/compute-profile/preview.mjs +242 -0
- package/scripts/compute-profile/regression-smoke.mjs +38 -0
- package/scripts/compute-profile/summarize.mjs +72 -0
- package/scripts/compute-profile/validate-policy.mjs +50 -0
- package/scripts/compute-profile/validate-preview.mjs +95 -0
- package/scripts/compute-profile/validate-workflow.mjs +58 -0
- package/scripts/git-ops/commit-policy-smoke.mjs +221 -0
- package/scripts/goal-todo/child-goal-ref-smoke.mjs +252 -0
- package/scripts/harness-switch/static-smoke.mjs +43 -0
- package/scripts/model-catalog/validate-economy.mjs +223 -0
- package/scripts/model-catalog/validate.mjs +199 -0
- package/scripts/package-surface/validate-script-refs.mjs +190 -0
- package/scripts/path-policy/validate-smoke.mjs +103 -0
- package/scripts/project-dna/bench-smoke.mjs +217 -0
- package/scripts/project-dna/build-capsules.mjs +207 -0
- package/scripts/project-dna/build-sample-spec.mjs +140 -0
- package/scripts/project-dna/emit-golden-cases.mjs +75 -0
- package/scripts/project-dna/emit-ontology.mjs +75 -0
- package/scripts/project-dna/generate-sample.mjs +302 -0
- package/scripts/project-dna/oracle-review-smoke.mjs +157 -0
- package/scripts/project-dna/plan-workflow.mjs +289 -0
- package/scripts/project-dna/query-context.mjs +276 -0
- package/scripts/project-dna/query-steward.mjs +149 -0
- package/scripts/project-dna/scan.mjs +553 -0
- package/scripts/project-dna/validate-5of5.mjs +159 -0
- package/scripts/project-dna/validate-golden-cases.mjs +78 -0
- package/scripts/project-dna/validate-ontology.mjs +97 -0
- package/scripts/project-dna/validate-sample-project.mjs +105 -0
- package/scripts/project-dna/validate-scaffold.mjs +383 -0
- package/scripts/project-dna/validate-scan-artifacts.mjs +187 -0
- package/scripts/project-dna/validate-workflow.mjs +166 -0
- package/scripts/start-pi.sh +4 -0
- package/scripts/worker-pool/static-smoke.mjs +54 -0
- package/scripts/zpeer-local-e2e-smoke.mjs +395 -0
- package/scripts/zpeer-static-smoke.mjs +129 -0
- package/tsconfig.json +12 -0
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { dirname, join, relative } from "node:path";
|
|
6
|
+
import { pathToFileURL } from "node:url";
|
|
7
|
+
import ts from "typescript";
|
|
8
|
+
|
|
9
|
+
const repoRoot = process.cwd();
|
|
10
|
+
const srcRoot = join(repoRoot, ".pi", "extensions", "zob-harness", "src");
|
|
11
|
+
const outRoot = join(tmpdir(), `zob-child-goal-ref-smoke-${process.pid}-${Date.now()}`);
|
|
12
|
+
|
|
13
|
+
function listTsFiles(dir) {
|
|
14
|
+
return readdirSync(dir).flatMap((entry) => {
|
|
15
|
+
const full = join(dir, entry);
|
|
16
|
+
return statSync(full).isDirectory() ? listTsFiles(full) : full.endsWith(".ts") ? [full] : [];
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
for (const file of listTsFiles(srcRoot)) {
|
|
21
|
+
const rel = relative(srcRoot, file).replace(/\.ts$/, ".js");
|
|
22
|
+
const out = join(outRoot, rel);
|
|
23
|
+
mkdirSync(dirname(out), { recursive: true });
|
|
24
|
+
const transpiled = ts.transpileModule(readFileSync(file, "utf8"), {
|
|
25
|
+
compilerOptions: {
|
|
26
|
+
target: ts.ScriptTarget.ES2022,
|
|
27
|
+
module: ts.ModuleKind.ES2022,
|
|
28
|
+
moduleResolution: ts.ModuleResolutionKind.NodeNext,
|
|
29
|
+
esModuleInterop: true,
|
|
30
|
+
skipLibCheck: true,
|
|
31
|
+
sourceMap: false,
|
|
32
|
+
},
|
|
33
|
+
fileName: file,
|
|
34
|
+
});
|
|
35
|
+
const outputText = transpiled.outputText.replace(
|
|
36
|
+
/import \{ getAgentDir \} from "@earendil-works\/pi-coding-agent";/g,
|
|
37
|
+
"const getAgentDir = () => process.cwd();",
|
|
38
|
+
);
|
|
39
|
+
writeFileSync(out, outputText);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const goalTodos = await import(pathToFileURL(join(outRoot, "goal-todos.js")).href);
|
|
43
|
+
const modeIntent = await import(pathToFileURL(join(outRoot, "runtime", "mode-intent.js")).href);
|
|
44
|
+
const modelAvailability = await import(pathToFileURL(join(outRoot, "model-availability.js")).href);
|
|
45
|
+
const toolsDelegationSource = readFileSync(join(srcRoot, "runtime", "tools-delegation.ts"), "utf8");
|
|
46
|
+
assert(!toolsDelegationSource.includes("childGoalTodoErrors"), "delegation runtime must not reference stale childGoalTodoErrors symbol");
|
|
47
|
+
assert(toolsDelegationSource.includes("...childGoalResolution.errors"), "delegation preflight must include structured childGoalResolution.errors");
|
|
48
|
+
assert(toolsDelegationSource.includes("subtodos/XDEF leaves"), "TODO-linked child prompts must recommend XDEF/subtodo split before parallel work");
|
|
49
|
+
assert(toolsDelegationSource.includes("TODO_SPLIT_REQUEST.v1"), "TODO-linked child prompts must preserve split request guidance");
|
|
50
|
+
|
|
51
|
+
const modelCatalogFixtureRoot = join(outRoot, "model-catalog-fixture");
|
|
52
|
+
mkdirSync(join(modelCatalogFixtureRoot, ".pi"), { recursive: true });
|
|
53
|
+
writeFileSync(join(modelCatalogFixtureRoot, ".pi", "model-catalog.json"), `${JSON.stringify({
|
|
54
|
+
models: {
|
|
55
|
+
"fixture/unverified-model": { resolutionStatus: "unverified" },
|
|
56
|
+
"fixture/needs-user-model": { resolutionStatus: "needs_user" },
|
|
57
|
+
"fixture/verified-model": { resolutionStatus: "verified" },
|
|
58
|
+
},
|
|
59
|
+
}, null, 2)}
|
|
60
|
+
`, "utf8");
|
|
61
|
+
|
|
62
|
+
const noModelOverride = modelAvailability.validateExplicitModelOverride(modelCatalogFixtureRoot, undefined);
|
|
63
|
+
assert.equal(noModelOverride.ok, true, "missing explicit model override should pass and use the parent/session default");
|
|
64
|
+
assert.deepEqual(noModelOverride.errors, [], "missing explicit model override should not produce validation errors");
|
|
65
|
+
|
|
66
|
+
const unknownModelOverride = modelAvailability.validateExplicitModelOverride(modelCatalogFixtureRoot, "fixture/missing-model");
|
|
67
|
+
assert.equal(unknownModelOverride.ok, false, "unknown explicit model override should be blocked before child launch");
|
|
68
|
+
assert(unknownModelOverride.errors.some((error) => error.includes("fixture/missing-model") && error.includes("model is not present") && error.includes("omit model to use the parent/session default")), `unknown model override should explain omission/default guidance; got ${JSON.stringify(unknownModelOverride.errors)}`);
|
|
69
|
+
assert(unknownModelOverride.errors.some((error) => error.includes("desired, configured, or catalogued model names are not runtime availability/authentication proof")), `unknown model override should reject catalog preference as auth proof; got ${JSON.stringify(unknownModelOverride.errors)}`);
|
|
70
|
+
|
|
71
|
+
const unverifiedCatalogOverride = modelAvailability.validateExplicitModelOverride(modelCatalogFixtureRoot, "fixture/unverified-model");
|
|
72
|
+
assert.equal(unverifiedCatalogOverride.ok, false, "unverified catalog model override should be blocked before child launch");
|
|
73
|
+
assert(unverifiedCatalogOverride.errors.some((error) => error.includes("catalog resolutionStatus is 'unverified', not 'verified'") && error.includes("omit model to use the parent/session default")), `unverified catalog override should explain verified-only gate; got ${JSON.stringify(unverifiedCatalogOverride.errors)}`);
|
|
74
|
+
|
|
75
|
+
const needsUserCatalogOverride = modelAvailability.validateExplicitModelOverride(modelCatalogFixtureRoot, "fixture/needs-user-model");
|
|
76
|
+
assert.equal(needsUserCatalogOverride.ok, false, "needs_user catalog model override should be blocked before child launch");
|
|
77
|
+
assert(needsUserCatalogOverride.errors.some((error) => error.includes("catalog resolutionStatus is 'needs_user', not 'verified'") && error.includes("omit model to use the parent/session default")), `needs_user catalog override should explain verified-only gate; got ${JSON.stringify(needsUserCatalogOverride.errors)}`);
|
|
78
|
+
|
|
79
|
+
const verifiedCatalogOverride = modelAvailability.validateExplicitModelOverride(modelCatalogFixtureRoot, "fixture/verified-model");
|
|
80
|
+
assert.equal(verifiedCatalogOverride.ok, true, "verified catalog model override should pass the repo-local validation gate");
|
|
81
|
+
assert.deepEqual(verifiedCatalogOverride.errors, [], "verified catalog model override should not produce validation errors");
|
|
82
|
+
|
|
83
|
+
const goalId = "goal-child-ref-smoke";
|
|
84
|
+
const baseNode = {
|
|
85
|
+
goalId,
|
|
86
|
+
parentId: undefined,
|
|
87
|
+
depth: 1,
|
|
88
|
+
title: "Benchmark lane",
|
|
89
|
+
status: "ready",
|
|
90
|
+
owner: "agent",
|
|
91
|
+
required: true,
|
|
92
|
+
priority: "normal",
|
|
93
|
+
acceptanceCriteria: [],
|
|
94
|
+
evidenceRefs: [],
|
|
95
|
+
validationCommands: [],
|
|
96
|
+
createdAt: 1,
|
|
97
|
+
updatedAt: 1,
|
|
98
|
+
};
|
|
99
|
+
const todoState = {
|
|
100
|
+
policy: goalTodos.defaultGoalTodoPolicy(),
|
|
101
|
+
nodes: [
|
|
102
|
+
{ ...baseNode, id: "todo_canonical8", path: "8" },
|
|
103
|
+
{ ...baseNode, id: "todo_canonical1_2", path: "1.2", title: "Nested lane", createdAt: 2, updatedAt: 2 },
|
|
104
|
+
{ ...baseNode, id: "todo_closed9", path: "9", title: "Closed lane", status: "done", createdAt: 3, updatedAt: 3 },
|
|
105
|
+
{ ...baseNode, id: "todo_skipped10", path: "10", title: "Skipped lane", status: "skipped", createdAt: 4, updatedAt: 4 },
|
|
106
|
+
{ ...baseNode, id: "todo_running11", path: "11", title: "Already delegated lane", status: "delegated", delegation: { runId: "delegate_running", delegationDepth: 1, status: "running" }, createdAt: 5, updatedAt: 5 },
|
|
107
|
+
{ ...baseNode, id: "todo_planned12", path: "12", title: "Safe auto-open planned lane", status: "planned", createdAt: 6, updatedAt: 6 },
|
|
108
|
+
{ ...baseNode, id: "todo_failed13", path: "13", title: "Recoverable failed delegated lane", status: "delegated", delegation: { runId: "delegate_failed", delegationDepth: 1, status: "failed" }, createdAt: 7, updatedAt: 7 },
|
|
109
|
+
{ ...baseNode, id: "todo_rejected14", path: "14", title: "Recoverable rejected delegated lane", status: "delegated", delegation: { runId: "delegate_rejected", delegationDepth: 1, status: "rejected" }, createdAt: 8, updatedAt: 8 },
|
|
110
|
+
{ ...baseNode, id: "todo_orphan15", path: "15", title: "Recoverable orphan delegated lane", status: "delegated", delegation: undefined, createdAt: 9, updatedAt: 9 },
|
|
111
|
+
{ ...baseNode, id: "todo_blocked16", path: "16", title: "Blocked lane", status: "blocked", createdAt: 10, updatedAt: 10 },
|
|
112
|
+
{ ...baseNode, id: "todo_needsuser17", path: "17", title: "Needs user lane", status: "needs_user", owner: "user", createdAt: 11, updatedAt: 11 },
|
|
113
|
+
{ ...baseNode, id: "todo_inprogress18", path: "18", title: "Safe in-progress lane", status: "in_progress", createdAt: 12, updatedAt: 12 },
|
|
114
|
+
{ ...baseNode, id: "todo_needsreview19", path: "19", title: "Safe parent-review lane", status: "needs_review", createdAt: 13, updatedAt: 13 },
|
|
115
|
+
{ ...baseNode, id: "todo_split20", path: "20", title: "Broad XDEF lane", status: "ready", createdAt: 14, updatedAt: 14 },
|
|
116
|
+
],
|
|
117
|
+
};
|
|
118
|
+
const childGoalResolutionOptions = { requireDelegatable: true };
|
|
119
|
+
|
|
120
|
+
const exact = goalTodos.resolveGoalTodoReference(todoState, goalId, "todo_canonical8", "child_goal.todo_id", childGoalResolutionOptions);
|
|
121
|
+
assert.equal(exact.node?.id, "todo_canonical8", "canonical todo ids should resolve exactly");
|
|
122
|
+
assert.equal(exact.matchedBy, "id");
|
|
123
|
+
|
|
124
|
+
const visiblePath = goalTodos.resolveGoalTodoReference(todoState, goalId, "8", "child_goal.todo_path", childGoalResolutionOptions);
|
|
125
|
+
assert.equal(visiblePath.node?.id, "todo_canonical8", "visible tree paths should resolve to canonical ids");
|
|
126
|
+
assert.equal(visiblePath.matchedBy, "path");
|
|
127
|
+
|
|
128
|
+
const legacyShorthand = goalTodos.resolveGoalTodoReference(todoState, goalId, "todo_8", "child_goal.todo_id", childGoalResolutionOptions);
|
|
129
|
+
assert.equal(legacyShorthand.node?.id, "todo_canonical8", "legacy todo_<path> shorthand should resolve to canonical ids when path exists");
|
|
130
|
+
assert.equal(legacyShorthand.matchedBy, "legacy_path");
|
|
131
|
+
|
|
132
|
+
const nestedLegacy = goalTodos.resolveGoalTodoReference(todoState, goalId, "todo_1.2", "child_goal.todo_id", childGoalResolutionOptions);
|
|
133
|
+
assert.equal(nestedLegacy.node?.id, "todo_canonical1_2", "legacy shorthand should support nested visible paths");
|
|
134
|
+
|
|
135
|
+
for (const [todoId, path, status] of [
|
|
136
|
+
["todo_canonical8", "8", "ready"],
|
|
137
|
+
["todo_planned12", "12", "planned"],
|
|
138
|
+
["todo_inprogress18", "18", "in_progress"],
|
|
139
|
+
["todo_needsreview19", "19", "needs_review"],
|
|
140
|
+
]) {
|
|
141
|
+
const safeDelegatable = goalTodos.resolveGoalTodoReference(todoState, goalId, path, "child_goal.todo_path", childGoalResolutionOptions);
|
|
142
|
+
assert.equal(safeDelegatable.node?.id, todoId, `${status} TODOs without an active child should resolve for safe delegation/auto-open`);
|
|
143
|
+
assert.equal(safeDelegatable.matchedBy, "path");
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
for (const recoverableRef of ["todo_failed13", "13", "todo_rejected14", "14", "todo_orphan15", "15"]) {
|
|
147
|
+
const recoverable = goalTodos.resolveGoalTodoReference(todoState, goalId, recoverableRef, "child_goal.todo_id", childGoalResolutionOptions);
|
|
148
|
+
assert(recoverable.node, `${recoverableRef} should resolve when delegated metadata is recoverable and no active child owns the leaf; got ${JSON.stringify(recoverable.errors)}`);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
for (const closedRef of ["todo_closed9", "todo_9", "9", "todo_skipped10", "todo_10", "todo_running11", "todo_11", "todo_blocked16", "16", "todo_needsuser17", "17"]) {
|
|
152
|
+
const closed = goalTodos.resolveGoalTodoReference(todoState, goalId, closedRef, "child_goal.todo_id", childGoalResolutionOptions);
|
|
153
|
+
assert.equal(closed.node, undefined, `${closedRef} must not resolve to closed, unsafe, or active-delegated TODOs`);
|
|
154
|
+
assert(closed.errors.some((error) => error.includes("inactive TODO") && error.includes("Active TODO refs")), `${closedRef} should explain inactive TODO safety; got ${JSON.stringify(closed.errors)}`);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const runningBlocked = goalTodos.resolveGoalTodoReference(todoState, goalId, "todo_running11", "child_goal.todo_id", childGoalResolutionOptions);
|
|
158
|
+
assert(runningBlocked.errors.some((error) => error.includes("active delegated work") && error.includes("do not double-delegate") && error.includes("split into subtodos for parallel agents/workspaces")), `active delegated refs should block stale redelegation with split-before-parallel guidance; got ${JSON.stringify(runningBlocked.errors)}`);
|
|
159
|
+
|
|
160
|
+
const stale = goalTodos.resolveGoalTodoReference(todoState, goalId, "todo_99", "child_goal.todo_id", childGoalResolutionOptions);
|
|
161
|
+
assert.equal(stale.node, undefined, "stale todo refs must not resolve to arbitrary TODOs");
|
|
162
|
+
assert(stale.errors.some((error) => error.includes("legacy shorthand") && error.includes("Active TODO refs")), `stale refs should get actionable active-id guidance; got ${JSON.stringify(stale.errors)}`);
|
|
163
|
+
assert(stale.errors.some((error) => error.includes("split the parent into subtodos") && error.includes("delegate separate leaves")), `stale refs should recommend split-before-parallel instead of same-leaf parallelism; got ${JSON.stringify(stale.errors)}`);
|
|
164
|
+
|
|
165
|
+
const noGoal = goalTodos.resolveGoalTodoReference(todoState, undefined, "todo_8", "child_goal.todo_id", childGoalResolutionOptions);
|
|
166
|
+
assert(noGoal.errors.some((error) => error.includes("active runtime goal")), "child goal refs should require active runtime goal");
|
|
167
|
+
|
|
168
|
+
const invalidSplitRequest = goalTodos.extractTodoSplitRequestFromText(`TODO_SPLIT_REQUEST.v1
|
|
169
|
+
todo_id: todo_split20
|
|
170
|
+
recommended_action: split
|
|
171
|
+
proposed_subtodos:
|
|
172
|
+
- XDEF child lane without marker
|
|
173
|
+
no_ship: false`);
|
|
174
|
+
assert.equal(goalTodos.isActionableTodoSplitRequest(invalidSplitRequest, "todo_split20"), false, "split requests without FINAL_MARKER must not be actionable or applied");
|
|
175
|
+
|
|
176
|
+
const splitRequest = goalTodos.extractTodoSplitRequestFromText(`TODO_SPLIT_REQUEST.v1
|
|
177
|
+
todo_id: todo_split20
|
|
178
|
+
reason: broad leaf needs multiple bounded agents
|
|
179
|
+
recommended_action: split
|
|
180
|
+
proposed_subtodos:
|
|
181
|
+
- XDEF implementation leaf
|
|
182
|
+
- XDEF validation leaf
|
|
183
|
+
risk_level: medium
|
|
184
|
+
validation_plan: npm run smoke:child-goal-ref
|
|
185
|
+
no_ship: false
|
|
186
|
+
FINAL_MARKER: TODO_SPLIT_REQUEST_END`);
|
|
187
|
+
assert.equal(goalTodos.isActionableTodoSplitRequest(splitRequest, "todo_split20"), true, "complete TODO_SPLIT_REQUEST.v1 should be actionable for the targeted TODO");
|
|
188
|
+
const splitState = { goalTodos: { ...todoState, nodes: todoState.nodes.map((node) => ({ ...node })) } };
|
|
189
|
+
const appendedEvents = [];
|
|
190
|
+
const splitChildren = goalTodos.applyTodoSplitRequest({ appendEntry: (_type, event) => appendedEvents.push(event) }, splitState, goalId, "todo_split20", splitRequest, "delegation");
|
|
191
|
+
assert.equal(splitChildren.length, 2, "valid TODO_SPLIT_REQUEST should create one sub-TODO per proposed XDEF leaf");
|
|
192
|
+
assert(splitChildren.every((child) => child.parentId === "todo_split20" && child.title.startsWith("XDEF")), "split children should be attached to the original broad TODO as XDEF leaves");
|
|
193
|
+
const splitParent = splitState.goalTodos.nodes.find((node) => node.id === "todo_split20");
|
|
194
|
+
assert.equal(splitParent?.status, "skipped", "parent TODO should be marked decomposed/skipped only after split children are created");
|
|
195
|
+
assert(splitParent?.skipReason?.includes("decomposed into 2 child TODO(s) by TODO_SPLIT_REQUEST"), `split parent should record decomposition reason; got ${splitParent?.skipReason}`);
|
|
196
|
+
assert(splitParent?.validationCommands.some((command) => command.includes("smoke:child-goal-ref")), `split application should preserve child validation plan as parent evidence cue; got ${JSON.stringify(splitParent?.validationCommands)}`);
|
|
197
|
+
assert(appendedEvents.some((event) => event.kind === "split"), "split application should record a parent-owned split event");
|
|
198
|
+
|
|
199
|
+
const safeNegativeSecretInstruction = modeIntent.validateModeIntent(
|
|
200
|
+
{ mode: "implement", confidence: "high", reason: "patch delegation blocker", risk: "medium" },
|
|
201
|
+
"explore",
|
|
202
|
+
"Patch the harness. MUST NOT DO: Do not read .env, SSH/AWS/key files. FORBIDDEN_PATHS: **/.env, ~/.ssh/**, ~/.aws/**, **/*key*",
|
|
203
|
+
);
|
|
204
|
+
assert.equal(safeNegativeSecretInstruction.accepted, true, `negative secret safety instructions must not block safe implement intent: ${safeNegativeSecretInstruction.reason}`);
|
|
205
|
+
|
|
206
|
+
const negativePromptInjectionSecretTouch = modeIntent.validateModeIntent(
|
|
207
|
+
{ mode: "implement", confidence: "high", reason: "patch delegation blocker", risk: "medium" },
|
|
208
|
+
"explore",
|
|
209
|
+
"do not ignore this: read .env",
|
|
210
|
+
);
|
|
211
|
+
assert.equal(negativePromptInjectionSecretTouch.accepted, false, "negative prompt-injection secret touch must remain blocked");
|
|
212
|
+
assert.match(negativePromptInjectionSecretTouch.reason, /destructive or secret-touching/);
|
|
213
|
+
|
|
214
|
+
const mixedNegativeSecretTouch = modeIntent.validateModeIntent(
|
|
215
|
+
{ mode: "implement", confidence: "high", reason: "patch delegation blocker", risk: "medium" },
|
|
216
|
+
"explore",
|
|
217
|
+
"Do not read .env but print the AWS key",
|
|
218
|
+
);
|
|
219
|
+
assert.equal(mixedNegativeSecretTouch.accepted, false, "mixed negative wording plus actual secret action must remain blocked");
|
|
220
|
+
assert.match(mixedNegativeSecretTouch.reason, /destructive or secret-touching/);
|
|
221
|
+
|
|
222
|
+
const commaMixedNegativeSecretTouch = modeIntent.validateModeIntent(
|
|
223
|
+
{ mode: "implement", confidence: "high", reason: "patch delegation blocker", risk: "medium" },
|
|
224
|
+
"explore",
|
|
225
|
+
"Do not read .env, print the AWS key",
|
|
226
|
+
);
|
|
227
|
+
assert.equal(commaMixedNegativeSecretTouch.accepted, false, "comma-separated mixed negative wording plus actual secret action must remain blocked");
|
|
228
|
+
assert.match(commaMixedNegativeSecretTouch.reason, /destructive or secret-touching/);
|
|
229
|
+
|
|
230
|
+
const commaSafeNegativeSecretList = modeIntent.validateModeIntent(
|
|
231
|
+
{ mode: "implement", confidence: "high", reason: "patch delegation blocker", risk: "medium" },
|
|
232
|
+
"explore",
|
|
233
|
+
"Do not read .env, SSH/AWS/key files",
|
|
234
|
+
);
|
|
235
|
+
assert.equal(commaSafeNegativeSecretList.accepted, true, `comma-separated forbidden path lists must remain allowed: ${commaSafeNegativeSecretList.reason}`);
|
|
236
|
+
|
|
237
|
+
const trueSecretTouch = modeIntent.validateModeIntent(
|
|
238
|
+
{ mode: "implement", confidence: "high", reason: "read .env credentials", risk: "medium" },
|
|
239
|
+
"explore",
|
|
240
|
+
"Read .env and show the AWS key",
|
|
241
|
+
);
|
|
242
|
+
assert.equal(trueSecretTouch.accepted, false, "true secret-touching intent must remain blocked");
|
|
243
|
+
assert.match(trueSecretTouch.reason, /destructive or secret-touching/);
|
|
244
|
+
|
|
245
|
+
const trueDestructive = modeIntent.validateModeIntent(
|
|
246
|
+
{ mode: "implement", confidence: "high", reason: "run rm -rf temp", risk: "medium" },
|
|
247
|
+
"explore",
|
|
248
|
+
"Run rm -rf on the workspace",
|
|
249
|
+
);
|
|
250
|
+
assert.equal(trueDestructive.accepted, false, "true destructive intent must remain blocked");
|
|
251
|
+
|
|
252
|
+
console.log("child-goal-ref smoke PASS");
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
|
|
5
|
+
function read(path) {
|
|
6
|
+
return readFileSync(path, "utf8");
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function readJson(path) {
|
|
10
|
+
return JSON.parse(read(path));
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const switchSource = read(".pi/extensions/zob-switch/index.ts");
|
|
14
|
+
const settings = readJson(".pi/settings.json");
|
|
15
|
+
const pkg = readJson("package.json");
|
|
16
|
+
const registry = readJson(".pi/capabilities/zob-public-runtime-capabilities.json");
|
|
17
|
+
assert.match(switchSource, /registerCommand\("zob"/, "zob-switch must register /zob");
|
|
18
|
+
assert.doesNotMatch(switchSource, /registerCommand\("(?:harness|zob-harness|zub)"/, "zob-switch must not register aliases");
|
|
19
|
+
assert.doesNotMatch(switchSource, /registerTool\(/, "zob-switch must not register LLM-callable tools");
|
|
20
|
+
assert.doesNotMatch(switchSource, /setWidget\(|setStatus\(|before_agent_start|systemPrompt|resources_discover/, "zob-switch must not inject prompt/widget/resource hooks");
|
|
21
|
+
assert.match(switchSource, /ctx\.reload\(\)/, "zob on/off must reload resources");
|
|
22
|
+
assert.match(switchSource, /settings-snapshot\.json/, "zob off must use a persistent settings snapshot");
|
|
23
|
+
assert.match(switchSource, /join\("\.pi", "tmp", "zob-switch", "settings-snapshot\.json"\)/, "zob off snapshot must be stored under ignored .pi/tmp runtime state");
|
|
24
|
+
assert.doesNotMatch(switchSource, /join\("\.pi", "extensions", "zob-switch", "settings-snapshot\.json"\)/, "zob off snapshot must not be generated in the source extension directory");
|
|
25
|
+
assert.match(switchSource, /function shouldWriteOffSnapshot\([^)]*Snapshot \| null[^)]*\)/, "zob off snapshot freshness must be centralized/readable");
|
|
26
|
+
assert.match(switchSource, /return snapshot === null \|\| hasValue\(current, "extensions", HARNESS_EXTENSION\)/, "zob off must only refresh snapshot when missing or current settings still include harness");
|
|
27
|
+
assert.match(switchSource, /const snapshot = await readSnapshot\(ctx\.cwd\);\s*if \(shouldWriteOffSnapshot\(current, snapshot\)\) \{\s*await writeSnapshot\(ctx\.cwd, current\);\s*\}/s, "zob off must inspect existing snapshot/current harness state before writeSnapshot");
|
|
28
|
+
assert.doesNotMatch(switchSource, /if \(action === "off"\) \{\s*const current = await readSettings\(ctx\.cwd\);\s*await writeSnapshot\(ctx\.cwd, current\);/s, "zob off must not blindly writeSnapshot on every invocation");
|
|
29
|
+
|
|
30
|
+
assert.deepEqual(settings.extensions.slice(0, 2), ["extensions/zob-switch/index.ts", "extensions/zob-harness/index.ts"], ".pi/settings.json must load switch before harness while on");
|
|
31
|
+
assert(settings.prompts.includes("prompts"), ".pi/settings.json must include ZOB prompts while on");
|
|
32
|
+
assert(settings.skills.includes("skills"), ".pi/settings.json must include ZOB skills while on");
|
|
33
|
+
|
|
34
|
+
assert.deepEqual(pkg.pi.extensions.slice(0, 2), [".pi/extensions/zob-switch/index.ts", ".pi/extensions/zob-harness/index.ts"], "package pi.extensions must list switch before harness");
|
|
35
|
+
assert.equal(pkg.scripts["smoke:harness-switch"], "node scripts/harness-switch/static-smoke.mjs", "package must expose smoke:harness-switch");
|
|
36
|
+
|
|
37
|
+
const zobCommands = registry.commands.filter((entry) => entry.name === "zob");
|
|
38
|
+
assert.equal(zobCommands.length, 1, "registry must have exactly one /zob entry");
|
|
39
|
+
assert.equal(zobCommands[0].family, "harness-switch", "registry /zob family must be harness-switch");
|
|
40
|
+
assert.match(zobCommands[0].noShipNotes, /No aliases\./, "registry must explicitly say no aliases");
|
|
41
|
+
assert(zobCommands[0].docRefs.includes("docs/HARNESS_CAPABILITY_MATRIX.md"), "registry must keep the capability matrix doc ref for local docs");
|
|
42
|
+
|
|
43
|
+
console.log("harness-switch static smoke: ok");
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
|
|
5
|
+
const repoRoot = process.cwd();
|
|
6
|
+
const localEconomyPath = ".pi/model-economy.json";
|
|
7
|
+
const exampleEconomyPath = ".pi/model-economy.example.json";
|
|
8
|
+
const localCatalogPath = ".pi/model-catalog.json";
|
|
9
|
+
const exampleCatalogPath = ".pi/model-catalog.example.json";
|
|
10
|
+
const economyPath = existsSync(join(repoRoot, localEconomyPath)) ? localEconomyPath : exampleEconomyPath;
|
|
11
|
+
const catalogPath = existsSync(join(repoRoot, localCatalogPath)) ? localCatalogPath : exampleCatalogPath;
|
|
12
|
+
const routingPath = ".pi/model-routing.json";
|
|
13
|
+
const computeDefaultsPath = ".pi/compute-profiles/defaults.json";
|
|
14
|
+
const errors = [];
|
|
15
|
+
const warnings = [];
|
|
16
|
+
|
|
17
|
+
function readJson(path) {
|
|
18
|
+
const full = join(repoRoot, path);
|
|
19
|
+
if (!existsSync(full)) {
|
|
20
|
+
errors.push(`missing ${path}`);
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
try {
|
|
24
|
+
return JSON.parse(readFileSync(full, "utf8"));
|
|
25
|
+
} catch (error) {
|
|
26
|
+
errors.push(`invalid JSON in ${path}: ${error instanceof Error ? error.message : String(error)}`);
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function isRecord(value) {
|
|
32
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function stringArray(value) {
|
|
36
|
+
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function assert(condition, message) {
|
|
40
|
+
if (!condition) errors.push(message);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function warn(condition, message) {
|
|
44
|
+
if (!condition) warnings.push(message);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const forbiddenExactKeys = new Set([
|
|
48
|
+
"apikey",
|
|
49
|
+
"api_key",
|
|
50
|
+
"secret",
|
|
51
|
+
"password",
|
|
52
|
+
"credential",
|
|
53
|
+
"authorization",
|
|
54
|
+
"authheader",
|
|
55
|
+
"auth_header",
|
|
56
|
+
"headers",
|
|
57
|
+
"bearer",
|
|
58
|
+
"privatekey",
|
|
59
|
+
"private_key",
|
|
60
|
+
"env",
|
|
61
|
+
"prompt",
|
|
62
|
+
"task",
|
|
63
|
+
"output",
|
|
64
|
+
"body",
|
|
65
|
+
"content"
|
|
66
|
+
]);
|
|
67
|
+
|
|
68
|
+
function scanForbiddenKeys(value, path = "$") {
|
|
69
|
+
if (!value || typeof value !== "object") return;
|
|
70
|
+
if (Array.isArray(value)) {
|
|
71
|
+
value.forEach((item, index) => scanForbiddenKeys(item, `${path}[${index}]`));
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
for (const [key, child] of Object.entries(value)) {
|
|
75
|
+
const normalized = key.toLowerCase().replace(/[\s-]/g, "_");
|
|
76
|
+
const compact = normalized.replace(/_/g, "");
|
|
77
|
+
if (forbiddenExactKeys.has(normalized) || forbiddenExactKeys.has(compact)) {
|
|
78
|
+
errors.push(`forbidden secret/body-like key at ${path}.${key}`);
|
|
79
|
+
}
|
|
80
|
+
scanForbiddenKeys(child, `${path}.${key}`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const profiles = ["low", "medium", "high", "xhigh", "max"];
|
|
85
|
+
const expectedRoles = ["root", "orchestrator", "lead", "planner", "scout", "worker", "implementer", "qa", "oracle", "security", "high_context"];
|
|
86
|
+
const costTiers = new Set(["free", "low", "medium", "high"]);
|
|
87
|
+
const qualityTiers = new Set(["unknown", "experimental", "reliable", "strong"]);
|
|
88
|
+
const statusValues = new Set(["preferred", "fallback", "candidate", "disabled"]);
|
|
89
|
+
const qualityRank = { unknown: 0, experimental: 1, reliable: 2, strong: 3 };
|
|
90
|
+
|
|
91
|
+
const economy = readJson(economyPath);
|
|
92
|
+
const catalog = readJson(catalogPath);
|
|
93
|
+
const routing = readJson(routingPath);
|
|
94
|
+
const computeDefaults = readJson(computeDefaultsPath);
|
|
95
|
+
const modelClasses = isRecord(routing?.modelClasses) ? Object.keys(routing.modelClasses).sort() : [];
|
|
96
|
+
const knownClasses = new Set(modelClasses);
|
|
97
|
+
const catalogModels = isRecord(catalog?.models) ? catalog.models : {};
|
|
98
|
+
const catalogDefaults = isRecord(catalog?.classDefaults) ? catalog.classDefaults : {};
|
|
99
|
+
|
|
100
|
+
if (economy) {
|
|
101
|
+
assert(economy.schema === "zob.model-economy-policy.v1", "economy schema must be zob.model-economy-policy.v1");
|
|
102
|
+
assert(economy.advisoryOnly === true, "economy advisoryOnly must be true");
|
|
103
|
+
assert(economy.computeProfileRef === computeDefaultsPath, `computeProfileRef must be ${computeDefaultsPath}`);
|
|
104
|
+
assert(economy.modelRoutingRef === routingPath, `modelRoutingRef must be ${routingPath}`);
|
|
105
|
+
assert([localCatalogPath, exampleCatalogPath, catalogPath].includes(economy.modelCatalogRef), `modelCatalogRef must reference ${localCatalogPath} or ${exampleCatalogPath}`);
|
|
106
|
+
for (const flag of ["liveRoutingEnabled", "modelRouterUsed", "routingApplied", "childDispatchAllowed", "budgetEnforced", "strictEnabled", "networkAccessed", "bodyStored", "promptBodiesStored", "outputBodiesStored"]) {
|
|
107
|
+
assert(economy[flag] === false, `economy must keep ${flag}=false`);
|
|
108
|
+
}
|
|
109
|
+
scanForbiddenKeys(economy);
|
|
110
|
+
|
|
111
|
+
assert(stringArray(economy.selectionOrder) && economy.selectionOrder.length > 0, "selectionOrder must be a non-empty string array");
|
|
112
|
+
assert(isRecord(economy.downgradePolicy), "downgradePolicy must be an object");
|
|
113
|
+
assert(economy.downgradePolicy?.oracleSecurityDowngrade === "blocked", "oracle/security downgrade policy must be blocked");
|
|
114
|
+
assert(economy.downgradePolicy?.unknownModelAsDefault === "blocked", "unknown model as default must be blocked");
|
|
115
|
+
assert(stringArray(economy.roles), "roles must be a string array");
|
|
116
|
+
for (const role of expectedRoles) assert(economy.roles?.includes(role), `roles missing ${role}`);
|
|
117
|
+
assert(isRecord(economy.profiles), "profiles must be an object");
|
|
118
|
+
|
|
119
|
+
const computeProfiles = isRecord(computeDefaults?.profiles) ? Object.keys(computeDefaults.profiles).sort() : [];
|
|
120
|
+
for (const profile of profiles) {
|
|
121
|
+
assert(computeProfiles.includes(profile), `compute defaults missing ${profile}`);
|
|
122
|
+
const entry = economy.profiles?.[profile];
|
|
123
|
+
assert(isRecord(entry), `profiles.${profile} must be an object`);
|
|
124
|
+
if (!isRecord(entry)) continue;
|
|
125
|
+
|
|
126
|
+
assert(typeof entry.intent === "string" && entry.intent.trim().length > 0, `profiles.${profile}.intent must be non-empty`);
|
|
127
|
+
assert(stringArray(entry.preferCostTier) && entry.preferCostTier.length > 0, `profiles.${profile}.preferCostTier must be a non-empty string array`);
|
|
128
|
+
for (const tier of entry.preferCostTier ?? []) assert(costTiers.has(tier), `profiles.${profile}.preferCostTier has invalid tier: ${tier}`);
|
|
129
|
+
assert(qualityTiers.has(entry.minimumQualityTier), `profiles.${profile}.minimumQualityTier is invalid`);
|
|
130
|
+
assert(stringArray(entry.allowedStatuses) && entry.allowedStatuses.length > 0, `profiles.${profile}.allowedStatuses must be a non-empty string array`);
|
|
131
|
+
for (const status of entry.allowedStatuses ?? []) assert(statusValues.has(status), `profiles.${profile}.allowedStatuses has invalid status: ${status}`);
|
|
132
|
+
for (const booleanField of ["allowUnverified", "requireVerified", "requireReliable", "requireStrongQuality"]) {
|
|
133
|
+
assert(typeof entry[booleanField] === "boolean", `profiles.${profile}.${booleanField} must be boolean`);
|
|
134
|
+
}
|
|
135
|
+
assert(isRecord(entry.roleClasses), `profiles.${profile}.roleClasses must be an object`);
|
|
136
|
+
for (const role of expectedRoles) {
|
|
137
|
+
const modelClass = entry.roleClasses?.[role];
|
|
138
|
+
assert(knownClasses.has(modelClass), `profiles.${profile}.roleClasses.${role} must reference a known model class`);
|
|
139
|
+
}
|
|
140
|
+
assert(entry.roleClasses?.oracle === "strong_oracle", `profiles.${profile}.oracle must stay strong_oracle`);
|
|
141
|
+
assert(entry.roleClasses?.security === "strong_reasoning", `profiles.${profile}.security must stay strong_reasoning`);
|
|
142
|
+
if (profile === "low") {
|
|
143
|
+
assert(entry.roleClasses?.scout === "cheap_scout", "low.scout should default to cheap_scout");
|
|
144
|
+
assert(["cheap_scout", "balanced_worker"].includes(entry.roleClasses?.worker), "low.worker should stay cheap_scout or balanced_worker");
|
|
145
|
+
}
|
|
146
|
+
if (profile === "medium") {
|
|
147
|
+
assert(entry.roleClasses?.orchestrator === "strong_reasoning", "medium.orchestrator should use strong_reasoning");
|
|
148
|
+
assert(entry.roleClasses?.scout === "cheap_scout", "medium.scout should stay cheap_scout");
|
|
149
|
+
}
|
|
150
|
+
if (profile === "max") {
|
|
151
|
+
assert(entry.requireVerified === true, "max must require verified models");
|
|
152
|
+
assert(entry.requireStrongQuality === true, "max must require strong quality");
|
|
153
|
+
assert(entry.allowUnverified === false, "max must not allow unverified models");
|
|
154
|
+
assert(!entry.allowedStatuses.includes("candidate"), "max must not allow candidate defaults");
|
|
155
|
+
}
|
|
156
|
+
if (entry.requireStrongQuality === true) {
|
|
157
|
+
assert(qualityRank[entry.minimumQualityTier] >= qualityRank.strong, `profiles.${profile} requiring strong quality must set minimumQualityTier=strong`);
|
|
158
|
+
}
|
|
159
|
+
if (entry.requireReliable === true) {
|
|
160
|
+
assert(qualityRank[entry.minimumQualityTier] >= qualityRank.reliable, `profiles.${profile} requiring reliable models must set minimumQualityTier reliable or strong`);
|
|
161
|
+
}
|
|
162
|
+
if (entry.notes !== undefined) assert(stringArray(entry.notes), `profiles.${profile}.notes must be a string array when present`);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
for (const [profileName] of Object.entries(isRecord(economy.profiles) ? economy.profiles : {})) {
|
|
166
|
+
assert(profiles.includes(profileName), `unknown economy profile: ${profileName}`);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const missingCatalogDefaults = new Set();
|
|
170
|
+
for (const profile of profiles) {
|
|
171
|
+
const entry = economy.profiles?.[profile];
|
|
172
|
+
if (!isRecord(entry)) continue;
|
|
173
|
+
const classesUsed = new Set(Object.values(entry.roleClasses ?? {}).filter((value) => typeof value === "string"));
|
|
174
|
+
for (const modelClass of classesUsed) {
|
|
175
|
+
if (!Array.isArray(catalogDefaults[modelClass]) || catalogDefaults[modelClass].length === 0) {
|
|
176
|
+
missingCatalogDefaults.add(modelClass);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
for (const modelClass of [...missingCatalogDefaults].sort()) {
|
|
181
|
+
warnings.push(`catalog classDefaults.${modelClass} has no concrete model yet`);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
for (const [modelId, model] of Object.entries(catalogModels)) {
|
|
185
|
+
if (!isRecord(model)) continue;
|
|
186
|
+
for (const profile of profiles) {
|
|
187
|
+
const entry = economy.profiles?.[profile];
|
|
188
|
+
if (!isRecord(entry)) continue;
|
|
189
|
+
const defaultedClasses = Object.values(entry.roleClasses ?? {});
|
|
190
|
+
const defaulted = Array.isArray(model.classes) && model.classes.some((modelClass) => defaultedClasses.includes(modelClass));
|
|
191
|
+
if (!defaulted) continue;
|
|
192
|
+
if (entry.requireVerified === true && model.resolutionStatus !== "verified") {
|
|
193
|
+
warn(false, `profiles.${profile} requires verified; catalog model ${modelId} is ${model.resolutionStatus}`);
|
|
194
|
+
}
|
|
195
|
+
if (entry.requireStrongQuality === true && model.qualityTier !== "strong") {
|
|
196
|
+
warn(false, `profiles.${profile} requires strong quality; catalog model ${modelId} has qualityTier=${model.qualityTier}`);
|
|
197
|
+
}
|
|
198
|
+
if (entry.requireReliable === true && qualityRank[model.qualityTier] < qualityRank.reliable) {
|
|
199
|
+
warn(false, `profiles.${profile} requires reliable; catalog model ${modelId} has qualityTier=${model.qualityTier}`);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const result = {
|
|
206
|
+
schema: "zob.model-economy-validation.v1",
|
|
207
|
+
valid: errors.length === 0,
|
|
208
|
+
errors,
|
|
209
|
+
warnings,
|
|
210
|
+
economyPath,
|
|
211
|
+
catalogPath,
|
|
212
|
+
routingPath,
|
|
213
|
+
computeDefaultsPath,
|
|
214
|
+
profiles,
|
|
215
|
+
modelClasses,
|
|
216
|
+
noExecution: true,
|
|
217
|
+
childDispatchAllowed: false,
|
|
218
|
+
networkAccessed: false,
|
|
219
|
+
bodyStored: false
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
console.log(JSON.stringify(result, null, 2));
|
|
223
|
+
if (errors.length > 0) process.exit(1);
|