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,533 @@
|
|
|
1
|
+
import { appendFileSync, existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
2
|
+
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
3
|
+
|
|
4
|
+
import { sha256 } from "./utils/hashing.js";
|
|
5
|
+
import { isRecord } from "./utils/records.js";
|
|
6
|
+
import { safeRunId } from "./utils/paths.js";
|
|
7
|
+
|
|
8
|
+
const STOPWORDS = new Set(["the", "and", "for", "with", "how", "does", "this", "that", "dans", "avec", "pour", "comment", "faire", "using", "use", "project", "style"]);
|
|
9
|
+
const DEFAULT_SCAN_DIR = "reports/project-dna-scans/project-dna-factory-smoke";
|
|
10
|
+
const SAFE_SCAN_PREFIXES = ["reports/project-dna-scans/"];
|
|
11
|
+
const MAX_CONTEXT_TOKENS_CAP = 8000;
|
|
12
|
+
|
|
13
|
+
const FORBIDDEN_PATH_MARKERS = [
|
|
14
|
+
".env",
|
|
15
|
+
".npmrc",
|
|
16
|
+
"id_rsa",
|
|
17
|
+
"id_ed25519",
|
|
18
|
+
"credential",
|
|
19
|
+
"credentials",
|
|
20
|
+
"secret",
|
|
21
|
+
"secrets",
|
|
22
|
+
"node_modules/",
|
|
23
|
+
".git/",
|
|
24
|
+
"dist/",
|
|
25
|
+
"build/",
|
|
26
|
+
"coverage/",
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
export interface ProjectDnaQueryInput {
|
|
30
|
+
scanDir?: string;
|
|
31
|
+
query: string;
|
|
32
|
+
maxFiles?: number;
|
|
33
|
+
maxContextTokens?: number;
|
|
34
|
+
allowedSources?: string[];
|
|
35
|
+
contextScopeId?: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface ProjectDnaFederatedQueryInput {
|
|
39
|
+
scanDirs: string[];
|
|
40
|
+
query: string;
|
|
41
|
+
maxFilesPerSource?: number;
|
|
42
|
+
maxTotalFiles?: number;
|
|
43
|
+
maxContextTokens?: number;
|
|
44
|
+
allowedSources?: string[];
|
|
45
|
+
contextScopeId?: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface ProjectDnaWritebackProposalInput {
|
|
49
|
+
runId: string;
|
|
50
|
+
proposalId?: string;
|
|
51
|
+
sourceIds: string[];
|
|
52
|
+
observedPatternHash: string;
|
|
53
|
+
proposedCapsuleHash: string;
|
|
54
|
+
evidenceRefs: string[];
|
|
55
|
+
recommendedArtifact: string;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface ProjectDnaPlanWorkflowInput {
|
|
59
|
+
manifestPath: string;
|
|
60
|
+
scanDir?: string;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
interface SafePathResult {
|
|
64
|
+
absolutePath: string;
|
|
65
|
+
relativePath: string;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
interface FileMapEntry {
|
|
69
|
+
path: string;
|
|
70
|
+
kind?: string;
|
|
71
|
+
language?: string;
|
|
72
|
+
imports?: string[];
|
|
73
|
+
exports?: string[];
|
|
74
|
+
citations?: string[];
|
|
75
|
+
symbolCitations?: string[];
|
|
76
|
+
primarySymbolCitation?: string;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
interface ScoredFile {
|
|
80
|
+
file: FileMapEntry;
|
|
81
|
+
score: number;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function normalizeSlashes(value: string): string {
|
|
85
|
+
return value.split(sep).join("/");
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function assertSafeRelativePath(input: string, label: string, repoRoot: string, options: { allowMissing?: boolean; allowedPrefixes?: string[] } = {}): SafePathResult {
|
|
89
|
+
if (!input || input.trim().length === 0) throw new Error(`${label} is required`);
|
|
90
|
+
if (isAbsolute(input)) throw new Error(`${label} must be repo-relative`);
|
|
91
|
+
const root = resolve(repoRoot);
|
|
92
|
+
const absolutePath = resolve(root, input);
|
|
93
|
+
const relativePath = normalizeSlashes(relative(root, absolutePath));
|
|
94
|
+
if (relativePath === "" || relativePath.startsWith("..") || isAbsolute(relativePath)) throw new Error(`${label} must stay inside repo and not be repo root`);
|
|
95
|
+
if (hasForbiddenPathMarker(relativePath)) throw new Error(`${label} contains forbidden path marker: ${input}`);
|
|
96
|
+
const prefixes = options.allowedPrefixes ?? [];
|
|
97
|
+
if (prefixes.length > 0 && !prefixes.some((prefix) => relativePath === prefix.replace(/\/$/, "") || relativePath.startsWith(prefix))) {
|
|
98
|
+
throw new Error(`${label} must be under one of: ${prefixes.join(", ")}`);
|
|
99
|
+
}
|
|
100
|
+
if (!options.allowMissing && !existsSync(absolutePath)) throw new Error(`${label} not found: ${relativePath}`);
|
|
101
|
+
return { absolutePath, relativePath };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function hasForbiddenPathMarker(value: string): boolean {
|
|
105
|
+
const normalized = normalizeSlashes(value).toLowerCase();
|
|
106
|
+
return FORBIDDEN_PATH_MARKERS.some((marker) => normalized === marker || normalized.includes(marker));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function readJsonObject(path: string, label: string): Record<string, unknown> {
|
|
110
|
+
const parsed = JSON.parse(readFileSync(path, "utf8")) as unknown;
|
|
111
|
+
if (!isRecord(parsed)) throw new Error(`${label} must be a JSON object`);
|
|
112
|
+
return parsed;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function readScanJson(scanDir: string, fileName: string): Record<string, unknown> {
|
|
116
|
+
const path = join(scanDir, fileName);
|
|
117
|
+
if (!existsSync(path)) throw new Error(`missing ProjectDNA scan artifact: ${fileName}`);
|
|
118
|
+
return readJsonObject(path, fileName);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function stringValue(record: Record<string, unknown>, key: string, fallback = ""): string {
|
|
122
|
+
const value = record[key];
|
|
123
|
+
return typeof value === "string" ? value : fallback;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function arrayOfRecords(record: Record<string, unknown>, key: string): Record<string, unknown>[] {
|
|
127
|
+
const value = record[key];
|
|
128
|
+
return Array.isArray(value) ? value.filter(isRecord) : [];
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function stringArray(value: unknown): string[] {
|
|
132
|
+
return Array.isArray(value) ? value.filter((item): item is string => typeof item === "string") : [];
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function unique(values: string[]): string[] {
|
|
136
|
+
return [...new Set(values.filter(Boolean))];
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function queryTerms(query: string): string[] {
|
|
140
|
+
return unique(query.toLowerCase().split(/[^a-z0-9_.$-]+/).filter((term) => term.length >= 3 && !STOPWORDS.has(term)));
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function clampPositiveInteger(value: number | undefined, fallback: number, max: number): number {
|
|
144
|
+
if (!Number.isFinite(value) || value === undefined || value <= 0) return fallback;
|
|
145
|
+
return Math.min(max, Math.floor(value));
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function fileEntryFromRecord(record: Record<string, unknown>): FileMapEntry | undefined {
|
|
149
|
+
const path = stringValue(record, "path");
|
|
150
|
+
if (!path || hasForbiddenPathMarker(path)) return undefined;
|
|
151
|
+
return {
|
|
152
|
+
path,
|
|
153
|
+
kind: stringValue(record, "kind", "unknown"),
|
|
154
|
+
language: stringValue(record, "language", "unknown"),
|
|
155
|
+
imports: stringArray(record.imports),
|
|
156
|
+
exports: stringArray(record.exports),
|
|
157
|
+
citations: stringArray(record.citations).filter((citation) => !hasForbiddenPathMarker(citation)),
|
|
158
|
+
symbolCitations: stringArray(record.symbol_citations).filter((citation) => !hasForbiddenPathMarker(citation)),
|
|
159
|
+
primarySymbolCitation: typeof record.primary_symbol_citation === "string" && !hasForbiddenPathMarker(record.primary_symbol_citation) ? record.primary_symbol_citation : undefined,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function scoreFile(file: FileMapEntry, terms: string[]): number {
|
|
164
|
+
const haystack = [file.path, file.kind, file.language, ...(file.imports ?? []), ...(file.exports ?? [])].join(" ").toLowerCase();
|
|
165
|
+
let score = 0;
|
|
166
|
+
for (const term of terms) {
|
|
167
|
+
if (haystack.includes(term)) score += 3;
|
|
168
|
+
}
|
|
169
|
+
if (terms.includes("queue") && file.kind === "queue") score += 8;
|
|
170
|
+
if ((terms.includes("worker") || terms.includes("job")) && file.kind === "worker") score += 8;
|
|
171
|
+
if ((terms.includes("route") || terms.includes("api") || terms.includes("controller")) && file.kind === "route") score += 8;
|
|
172
|
+
if (terms.includes("service") && file.kind === "service") score += 8;
|
|
173
|
+
if ((terms.includes("test") || terms.includes("testing")) && file.kind === "test") score += 8;
|
|
174
|
+
if ((terms.includes("config") || terms.includes("configuration")) && file.kind === "config") score += 8;
|
|
175
|
+
if ((terms.includes("command") || terms.includes("slash")) && file.path.toLowerCase().includes("command")) score += 8;
|
|
176
|
+
if ((terms.includes("skill") || terms.includes("prompt") || terms.includes("agent")) && /skill|prompt|agent/.test(file.path.toLowerCase())) score += 8;
|
|
177
|
+
return score;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function sourceAllowed(sourceId: string, allowedSources: string[] | undefined): boolean {
|
|
181
|
+
return !allowedSources || allowedSources.length === 0 || allowedSources.includes(sourceId);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function loadScanArtifacts(repoRoot: string, scanDirInput: string | undefined): { scanDirPath: string; scanDirRef: string; fingerprint: Record<string, unknown>; fileMap: Record<string, unknown>; architectureMap: Record<string, unknown>; contextPack: Record<string, unknown>; graph: Record<string, unknown> } {
|
|
185
|
+
const scanDir = assertSafeRelativePath(scanDirInput ?? DEFAULT_SCAN_DIR, "scan_dir", repoRoot, { allowedPrefixes: SAFE_SCAN_PREFIXES });
|
|
186
|
+
return {
|
|
187
|
+
scanDirPath: scanDir.absolutePath,
|
|
188
|
+
scanDirRef: scanDir.relativePath,
|
|
189
|
+
fingerprint: readScanJson(scanDir.absolutePath, "project-fingerprint.json"),
|
|
190
|
+
fileMap: readScanJson(scanDir.absolutePath, "file-map.json"),
|
|
191
|
+
architectureMap: readScanJson(scanDir.absolutePath, "architecture-map.json"),
|
|
192
|
+
contextPack: readScanJson(scanDir.absolutePath, "context-pack-smoke.json"),
|
|
193
|
+
graph: readScanJson(scanDir.absolutePath, "code-knowledge-graph.json"),
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export function buildProjectDnaQueryResult(repoRoot: string, input: ProjectDnaQueryInput): Record<string, unknown> {
|
|
198
|
+
const query = input.query.trim();
|
|
199
|
+
if (!query) throw new Error("query is required");
|
|
200
|
+
const terms = queryTerms(query);
|
|
201
|
+
const maxFiles = clampPositiveInteger(input.maxFiles, 8, 20);
|
|
202
|
+
const maxContextTokens = clampPositiveInteger(input.maxContextTokens, MAX_CONTEXT_TOKENS_CAP, MAX_CONTEXT_TOKENS_CAP);
|
|
203
|
+
const artifacts = loadScanArtifacts(repoRoot, input.scanDir);
|
|
204
|
+
const sourceId = stringValue(artifacts.fingerprint, "source_id", "unknown-source");
|
|
205
|
+
if (!sourceAllowed(sourceId, input.allowedSources)) throw new Error(`source_id ${sourceId} is not in allowed_sources`);
|
|
206
|
+
|
|
207
|
+
const files = arrayOfRecords(artifacts.fileMap, "files").map(fileEntryFromRecord).filter((file): file is FileMapEntry => Boolean(file));
|
|
208
|
+
const scored = files
|
|
209
|
+
.map((file) => ({ file, score: scoreFile(file, terms) }))
|
|
210
|
+
.filter((item) => item.score > 0)
|
|
211
|
+
.sort((left, right) => right.score - left.score || left.file.path.localeCompare(right.file.path));
|
|
212
|
+
const selected: ScoredFile[] = (scored.length > 0 ? scored : files.slice(0, maxFiles).map((file) => ({ file, score: 0 }))).slice(0, maxFiles);
|
|
213
|
+
const selectedCitations = unique(selected.flatMap((item) => [...(item.file.symbolCitations ?? []), ...(item.file.citations ?? [])]));
|
|
214
|
+
const patterns = arrayOfRecords(artifacts.architectureMap, "patterns");
|
|
215
|
+
const patternHits = patterns.filter((pattern) => terms.some((term) => stringValue(pattern, "id").toLowerCase().includes(term)));
|
|
216
|
+
const edges = arrayOfRecords(artifacts.graph, "edges");
|
|
217
|
+
const graphEdges = edges.filter((edge) => selected.some((item) => edge.to === `source:${item.file.path}` || edge.from === `source:${item.file.path}`)).slice(0, 20);
|
|
218
|
+
const contextCitations = stringArray(artifacts.contextPack.citations).filter((citation) => !hasForbiddenPathMarker(citation)).slice(0, 4);
|
|
219
|
+
const citations = unique([...selectedCitations, ...patternHits.flatMap((pattern) => stringArray(pattern.evidence)), ...contextCitations]);
|
|
220
|
+
|
|
221
|
+
return {
|
|
222
|
+
schema: "zob.project-dna-query-result.v1",
|
|
223
|
+
source_id: sourceId,
|
|
224
|
+
scan_dir: artifacts.scanDirRef,
|
|
225
|
+
context_scope: {
|
|
226
|
+
scope_id: input.contextScopeId ?? `project-dna-${sha256(`${artifacts.scanDirRef}:${sourceId}`).slice(0, 12)}`,
|
|
227
|
+
allowed_brains: ["project-dna"],
|
|
228
|
+
allowed_sources: input.allowedSources && input.allowedSources.length > 0 ? input.allowedSources : [sourceId],
|
|
229
|
+
forbidden_sources: ["secrets", "raw-conversation-history", ".env", "credentials"],
|
|
230
|
+
max_context_tokens: maxContextTokens,
|
|
231
|
+
write_policy: "proposal_only",
|
|
232
|
+
},
|
|
233
|
+
query_hash: sha256(query),
|
|
234
|
+
query_stored: false,
|
|
235
|
+
query_terms_stored: false,
|
|
236
|
+
raw_query_persisted: false,
|
|
237
|
+
answer: selected.length > 0
|
|
238
|
+
? "ProjectDNA found bounded cited pointers in scan metadata. Read cited files before implementation."
|
|
239
|
+
: "ProjectDNA did not find matching files; treat this as a context gap.",
|
|
240
|
+
files_to_read_first: selected.map((item) => ({
|
|
241
|
+
path: item.file.path,
|
|
242
|
+
kind: item.file.kind,
|
|
243
|
+
score: item.score,
|
|
244
|
+
line_range: (item.file.primarySymbolCitation ?? item.file.citations?.[0])?.split(":").at(-1) ?? null,
|
|
245
|
+
reason: item.score > 0 ? "metadata_match" : "fallback_from_context_pack",
|
|
246
|
+
citations: unique([...(item.file.symbolCitations ?? []), ...(item.file.citations ?? [])]),
|
|
247
|
+
})),
|
|
248
|
+
citations,
|
|
249
|
+
graph_edges: graphEdges,
|
|
250
|
+
pattern_hits: patternHits.map((pattern) => ({ id: pattern.id, confidence: pattern.confidence, evidence: stringArray(pattern.evidence) })),
|
|
251
|
+
gaps: selected.length === 0 ? ["No files available in ProjectDNA file map."] : patternHits.length === 0 ? ["No architecture pattern id matched the query terms; use file-level citations only."] : [],
|
|
252
|
+
loading_rules: {
|
|
253
|
+
bounded_context_only: true,
|
|
254
|
+
citation_required: true,
|
|
255
|
+
agent_loads_entire_project: false,
|
|
256
|
+
writeback_policy: "proposal_only",
|
|
257
|
+
},
|
|
258
|
+
safety: {
|
|
259
|
+
scan_metadata_only: true,
|
|
260
|
+
source_project_modified: false,
|
|
261
|
+
knowledge_backend_write_enabled: false,
|
|
262
|
+
network_accessed: false,
|
|
263
|
+
child_dispatch_allowed: false,
|
|
264
|
+
child_direct_dispatch: false,
|
|
265
|
+
parent_owned_dispatch: true,
|
|
266
|
+
},
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
export function buildProjectDnaFederatedQueryResult(repoRoot: string, input: ProjectDnaFederatedQueryInput): Record<string, unknown> {
|
|
271
|
+
if (!Array.isArray(input.scanDirs) || input.scanDirs.length === 0) throw new Error("scan_dirs requires at least one scan directory");
|
|
272
|
+
if (input.scanDirs.length > 10) throw new Error("scan_dirs is capped at 10 sources for P5 federation smoke");
|
|
273
|
+
const maxFilesPerSource = clampPositiveInteger(input.maxFilesPerSource, 5, 10);
|
|
274
|
+
const maxTotalFiles = clampPositiveInteger(input.maxTotalFiles, 20, 50);
|
|
275
|
+
const sourceResults = input.scanDirs.map((scanDir) => buildProjectDnaQueryResult(repoRoot, {
|
|
276
|
+
scanDir,
|
|
277
|
+
query: input.query,
|
|
278
|
+
maxFiles: maxFilesPerSource,
|
|
279
|
+
maxContextTokens: input.maxContextTokens,
|
|
280
|
+
allowedSources: input.allowedSources,
|
|
281
|
+
contextScopeId: input.contextScopeId,
|
|
282
|
+
}));
|
|
283
|
+
const files = sourceResults.flatMap((result) => {
|
|
284
|
+
const sourceId = typeof result.source_id === "string" ? result.source_id : "unknown-source";
|
|
285
|
+
const filesToRead = Array.isArray(result.files_to_read_first) ? result.files_to_read_first.filter(isRecord) : [];
|
|
286
|
+
return filesToRead.map((file) => ({ ...file, source_id: sourceId }));
|
|
287
|
+
}).slice(0, maxTotalFiles);
|
|
288
|
+
const citations = unique(sourceResults.flatMap((result) => stringArray(result.citations))).slice(0, 100);
|
|
289
|
+
const sourceIds = unique(sourceResults.map((result) => typeof result.source_id === "string" ? result.source_id : "unknown-source"));
|
|
290
|
+
return {
|
|
291
|
+
schema: "zob.project-dna-federated-query-result.v1",
|
|
292
|
+
query_hash: sha256(input.query),
|
|
293
|
+
query_stored: false,
|
|
294
|
+
source_count: sourceResults.length,
|
|
295
|
+
source_ids: sourceIds,
|
|
296
|
+
files_to_read_first: files,
|
|
297
|
+
citations,
|
|
298
|
+
source_results: sourceResults.map((result) => ({
|
|
299
|
+
source_id: result.source_id,
|
|
300
|
+
scan_dir: result.scan_dir,
|
|
301
|
+
citation_count: stringArray(result.citations).length,
|
|
302
|
+
file_count: Array.isArray(result.files_to_read_first) ? result.files_to_read_first.length : 0,
|
|
303
|
+
safety: result.safety,
|
|
304
|
+
})),
|
|
305
|
+
federation_policy: {
|
|
306
|
+
cross_source_merge: "metadata_only",
|
|
307
|
+
source_isolation_preserved: true,
|
|
308
|
+
durable_promotion_allowed: false,
|
|
309
|
+
human_approval_required_for_promotion: true,
|
|
310
|
+
writeback_policy: "proposal_only",
|
|
311
|
+
},
|
|
312
|
+
safety: {
|
|
313
|
+
scan_metadata_only: true,
|
|
314
|
+
source_project_modified: false,
|
|
315
|
+
knowledge_backend_write_enabled: false,
|
|
316
|
+
network_accessed: false,
|
|
317
|
+
child_dispatch_allowed: false,
|
|
318
|
+
child_direct_dispatch: false,
|
|
319
|
+
parent_owned_dispatch: true,
|
|
320
|
+
},
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const PROJECT_DNA_PROFILES = new Set(["auto", "low", "medium", "high", "xhigh", "max"]);
|
|
325
|
+
const PROJECT_DNA_EFFECTIVE_PROFILES = new Set(["low", "medium", "high", "xhigh", "max"]);
|
|
326
|
+
const PROJECT_DNA_SEMANTIC_CAPTURE_MODES = new Set(["full_capture", "architecture_only", "targeted_capture", "sample_first", "context_only"]);
|
|
327
|
+
|
|
328
|
+
function effectiveProjectDnaProfile(requested: string): string {
|
|
329
|
+
if (requested === "auto") return "medium";
|
|
330
|
+
return PROJECT_DNA_EFFECTIVE_PROFILES.has(requested) ? requested : "low";
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
function projectDnaLaneCatalog(profile: string): Record<string, unknown>[] {
|
|
334
|
+
const lanes: Record<string, unknown>[] = [
|
|
335
|
+
{ id: "manifest_preflight", phase: "p0", agent: "project-dna-safety-preflight", required: true, artifact_contract: "safety-metadata.v1", tools: ["read"] },
|
|
336
|
+
{ id: "repo_scout", phase: "p0", agent: "repo-scout", required: true, artifact_contract: "repo-complexity-report.v1", tools: ["read", "grep", "find", "ls"] },
|
|
337
|
+
{ id: "read_only_scan", phase: "p1", agent: "agent", required: true, artifact_contract: "scan-metadata.v1", tools: ["read", "grep", "find", "ls"] },
|
|
338
|
+
{ id: "scan_validation", phase: "p1", agent: "qa", required: true, artifact_contract: "validation-metadata.v1", tools: ["read", "bash"] },
|
|
339
|
+
];
|
|
340
|
+
if (["medium", "high", "xhigh", "max"].includes(profile)) lanes.push(
|
|
341
|
+
{ id: "architecture_cartography", phase: "p1", agent: "architecture-cartographer", required: true, artifact_contract: "architecture-map.v2", tools: ["read", "grep", "find", "ls"] },
|
|
342
|
+
{ id: "pattern_mining", phase: "p1", agent: "pattern-miner", required: true, artifact_contract: "pattern-index.partial.v1", tools: ["read", "grep", "find", "ls"] },
|
|
343
|
+
{ id: "sample_spec", phase: "p1", agent: "sample-architect", required: true, artifact_contract: "sample-spec-metadata.v1", tools: ["read"] },
|
|
344
|
+
);
|
|
345
|
+
if (["high", "xhigh", "max"].includes(profile)) lanes.push(
|
|
346
|
+
{ id: "symbol_range_curation", phase: "p1", agent: "symbol-range-curator", required: true, artifact_contract: "range-quality-report.v1", tools: ["read", "grep", "find", "ls"] },
|
|
347
|
+
{ id: "quarantine_sample", phase: "p1", agent: "implementer", required: false, artifact_contract: "quarantine-sample-metadata.v1", tools: ["read", "bash"] },
|
|
348
|
+
{ id: "project_dna_oracle", phase: "p1", agent: "project-dna-oracle", required: true, artifact_contract: "oracle.v1", tools: ["read", "grep", "find", "ls", "bash"] },
|
|
349
|
+
);
|
|
350
|
+
if (["xhigh", "max"].includes(profile)) lanes.push({ id: "adversarial_review", phase: "p1", agent: "project-dna-oracle", required: true, artifact_contract: "adversarial-oracle.v1", tools: ["read", "grep", "find", "ls"] });
|
|
351
|
+
if (profile === "max") lanes.push({ id: "human_promotion_packet", phase: "p1", agent: "planner", required: true, artifact_contract: "promotion-packet.v1", tools: ["read", "grep", "find", "ls"] });
|
|
352
|
+
return lanes.map((lane, index) => ({
|
|
353
|
+
...lane,
|
|
354
|
+
order: index + 1,
|
|
355
|
+
parent_owned_dispatch: true,
|
|
356
|
+
child_direct_dispatch: false,
|
|
357
|
+
live_dispatch_enabled: false,
|
|
358
|
+
source_mutation_allowed: false,
|
|
359
|
+
backend_writes_enabled: false,
|
|
360
|
+
durable_promotion_allowed: false,
|
|
361
|
+
stores_raw_bodies: false,
|
|
362
|
+
}));
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function readOptionalProjectDnaScanSummary(repoRoot: string, scanDirInput: string | undefined): Record<string, unknown> {
|
|
366
|
+
if (!scanDirInput) return {};
|
|
367
|
+
const scanDir = assertSafeRelativePath(scanDirInput, "scan_dir", repoRoot, { allowMissing: true, allowedPrefixes: SAFE_SCAN_PREFIXES });
|
|
368
|
+
const summaryPath = join(scanDir.absolutePath, "scan-summary.json");
|
|
369
|
+
if (!existsSync(summaryPath)) return {};
|
|
370
|
+
return readJsonObject(summaryPath, "scan-summary.json");
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
function deriveProjectDnaCaptureMode(manifest: Record<string, unknown>, effectiveProfile: string, scanSummary: Record<string, unknown>): string {
|
|
374
|
+
const policy = isRecord(manifest.capture_mode_policy) ? manifest.capture_mode_policy : {};
|
|
375
|
+
const goal = isRecord(manifest.capture_goal) ? manifest.capture_goal : {};
|
|
376
|
+
const requested = stringValue(policy, "semantic_mode", "full_capture");
|
|
377
|
+
const fallback = stringValue(policy, "large_repo_fallback", "architecture_only");
|
|
378
|
+
const intent = `${stringValue(manifest, "user_note")} ${stringValue(goal, "objective")}`.toLowerCase();
|
|
379
|
+
const fileCount = typeof scanSummary.files_scanned === "number" ? scanSummary.files_scanned : 0;
|
|
380
|
+
if (requested !== "full_capture" && PROJECT_DNA_SEMANTIC_CAPTURE_MODES.has(requested)) return requested;
|
|
381
|
+
if (/\b(sample|exemple|example|neutral)\b/.test(intent)) return "sample_first";
|
|
382
|
+
if (/\b(context|query|lookup|retrieval)\b/.test(intent)) return "context_only";
|
|
383
|
+
if (/\b(architecture|archi|structure|scaffold)\b/.test(intent)) return "architecture_only";
|
|
384
|
+
if (fileCount >= 1000 || effectiveProfile === "xhigh" || effectiveProfile === "max") return PROJECT_DNA_SEMANTIC_CAPTURE_MODES.has(fallback) ? fallback : "architecture_only";
|
|
385
|
+
return PROJECT_DNA_SEMANTIC_CAPTURE_MODES.has(requested) ? requested : "full_capture";
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
export function buildProjectDnaAgenticPlan(repoRoot: string, input: ProjectDnaPlanWorkflowInput): Record<string, unknown> {
|
|
389
|
+
const manifestPath = assertSafeRelativePath(input.manifestPath, "manifest_path", repoRoot);
|
|
390
|
+
const manifest = readJsonObject(manifestPath.absolutePath, "ProjectDNA manifest");
|
|
391
|
+
if (manifest.schema !== "zob.project-dna-manifest.v2") throw new Error("manifest schema must be zob.project-dna-manifest.v2");
|
|
392
|
+
const sourceProject = isRecord(manifest.source_project) ? manifest.source_project : {};
|
|
393
|
+
const sourceId = stringValue(sourceProject, "source_id");
|
|
394
|
+
if (!sourceId) throw new Error("source_project.source_id is required");
|
|
395
|
+
const requestedProfile = stringValue(manifest, "requested_compute_profile", "auto");
|
|
396
|
+
if (!PROJECT_DNA_PROFILES.has(requestedProfile)) throw new Error("requested_compute_profile invalid");
|
|
397
|
+
const effectiveProfile = effectiveProjectDnaProfile(requestedProfile);
|
|
398
|
+
const scanSummary = readOptionalProjectDnaScanSummary(repoRoot, input.scanDir);
|
|
399
|
+
const effectiveCaptureMode = deriveProjectDnaCaptureMode(manifest, effectiveProfile, scanSummary);
|
|
400
|
+
const policy = isRecord(manifest.capture_mode_policy) ? manifest.capture_mode_policy : {};
|
|
401
|
+
const goal = isRecord(manifest.capture_goal) ? manifest.capture_goal : {};
|
|
402
|
+
const targetDomains = Array.isArray(policy.targeted_domains) ? policy.targeted_domains.filter((item): item is string => typeof item === "string") : [];
|
|
403
|
+
return {
|
|
404
|
+
schema: "zob.project-dna-agentic-plan.v1",
|
|
405
|
+
run_id: stringValue(manifest, "run_id", `project-dna-${sha256(manifestPath.relativePath).slice(0, 8)}`),
|
|
406
|
+
source_id: sourceId,
|
|
407
|
+
manifest_path: manifestPath.relativePath,
|
|
408
|
+
manifest_hash: sha256(JSON.stringify(manifest)),
|
|
409
|
+
manifest_embedded: false,
|
|
410
|
+
scan_dir: input.scanDir ?? DEFAULT_SCAN_DIR,
|
|
411
|
+
requested_compute_profile: requestedProfile,
|
|
412
|
+
effective_compute_profile: effectiveProfile,
|
|
413
|
+
requested_capture_mode: stringValue(policy, "semantic_mode", "full_capture"),
|
|
414
|
+
effective_capture_mode: effectiveCaptureMode,
|
|
415
|
+
capture_mode_rationale: "derived_from_manifest_user_note_profile_and_optional_scan_summary",
|
|
416
|
+
targeted_domains: targetDomains,
|
|
417
|
+
objective_hash: sha256(stringValue(goal, "objective")),
|
|
418
|
+
metadata_only: true,
|
|
419
|
+
no_execution: true,
|
|
420
|
+
parent_owned_dispatch: true,
|
|
421
|
+
child_direct_dispatch: false,
|
|
422
|
+
live_dispatch_enabled: false,
|
|
423
|
+
network_accessed: false,
|
|
424
|
+
source_project_modified: false,
|
|
425
|
+
source_mutation_allowed: false,
|
|
426
|
+
knowledge_backend_write_enabled: false,
|
|
427
|
+
durable_promotion_allowed: false,
|
|
428
|
+
raw_bodies_stored: false,
|
|
429
|
+
prompt_bodies_stored: false,
|
|
430
|
+
result_bodies_stored: false,
|
|
431
|
+
promotion_policy: "proposal_only",
|
|
432
|
+
lanes: projectDnaLaneCatalog(effectiveProfile),
|
|
433
|
+
safety: {
|
|
434
|
+
source_read_only: true,
|
|
435
|
+
forbidden_paths_excluded: true,
|
|
436
|
+
secret_like_paths_forbidden: true,
|
|
437
|
+
external_knowledge_backend_disabled: true,
|
|
438
|
+
durable_promotion_requires_human_approval: true,
|
|
439
|
+
citations_required: true,
|
|
440
|
+
bounded_context_only: true,
|
|
441
|
+
},
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
export function buildProjectDnaReadinessAudit(repoRoot: string, input: { scanDir?: string } = {}): Record<string, unknown> {
|
|
446
|
+
const requiredRepoFiles = [
|
|
447
|
+
"docs/ZOB_PROJECT_DNA_CODE_KNOWLEDGE_GRAPH_PLAN.md",
|
|
448
|
+
".pi/skills/zob-project-dna/SKILL.md",
|
|
449
|
+
".pi/prompts/project-dna.md",
|
|
450
|
+
".pi/factories/project-dna/factory.json",
|
|
451
|
+
".pi/factories/project-dna/schemas/manifest.schema.json",
|
|
452
|
+
"scripts/project-dna/scan.mjs",
|
|
453
|
+
"scripts/project-dna/query-context.mjs",
|
|
454
|
+
"scripts/project-dna/bench-smoke.mjs",
|
|
455
|
+
];
|
|
456
|
+
const missingRepoFiles = requiredRepoFiles.filter((path) => !existsSync(resolve(repoRoot, path)));
|
|
457
|
+
const scanDir = input.scanDir ?? DEFAULT_SCAN_DIR;
|
|
458
|
+
let scanErrors: string[] = [];
|
|
459
|
+
let sourceId = "unknown-source";
|
|
460
|
+
try {
|
|
461
|
+
const artifacts = loadScanArtifacts(repoRoot, scanDir);
|
|
462
|
+
sourceId = stringValue(artifacts.fingerprint, "source_id", sourceId);
|
|
463
|
+
} catch (error) {
|
|
464
|
+
scanErrors = [error instanceof Error ? error.message : String(error)];
|
|
465
|
+
}
|
|
466
|
+
const p4RuntimeReady = existsSync(resolve(repoRoot, ".pi/extensions/zob-harness/src/project-dna.ts")) && existsSync(resolve(repoRoot, ".pi/extensions/zob-harness/src/runtime/tools-project-dna.ts"));
|
|
467
|
+
const p5FederationReady = p4RuntimeReady;
|
|
468
|
+
const errors = [...missingRepoFiles.map((file) => `missing repo file: ${file}`), ...scanErrors];
|
|
469
|
+
return {
|
|
470
|
+
schema: "zob.project-dna-readiness.v1",
|
|
471
|
+
verdict: errors.length === 0 ? "ready" : "blocked",
|
|
472
|
+
source_id: sourceId,
|
|
473
|
+
scan_dir: scanDir,
|
|
474
|
+
phases: {
|
|
475
|
+
p0_plan_contracts: missingRepoFiles.length === 0 ? "done" : "partial",
|
|
476
|
+
p1_scanner_smoke: scanErrors.length === 0 ? "done" : "blocked",
|
|
477
|
+
p2_code_graph_capsules: scanErrors.length === 0 ? "partial_smoke_ready" : "blocked",
|
|
478
|
+
p3_neutral_sample: scanErrors.length === 0 ? "partial_smoke_ready" : "blocked",
|
|
479
|
+
p4_runtime_integration: p4RuntimeReady ? "done" : "missing",
|
|
480
|
+
p5_multi_project_learning: p5FederationReady ? "proposal_only_ready" : "missing",
|
|
481
|
+
},
|
|
482
|
+
errors,
|
|
483
|
+
no_ship: errors.length > 0,
|
|
484
|
+
safety: {
|
|
485
|
+
source_project_modified: false,
|
|
486
|
+
knowledge_backend_write_enabled: false,
|
|
487
|
+
durable_promotion_allowed: false,
|
|
488
|
+
human_approval_required_for_promotion: true,
|
|
489
|
+
proposal_only: true,
|
|
490
|
+
},
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
function validateSha256(value: string, label: string): void {
|
|
495
|
+
if (!/^[a-f0-9]{64}$/i.test(value)) throw new Error(`${label} must be a sha256 hex digest`);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
function validateEvidenceRef(ref: string): void {
|
|
499
|
+
assertSafeRelativePath(ref, "evidence_ref", process.cwd(), { allowMissing: true });
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
export function writeProjectDnaWritebackProposal(repoRoot: string, input: ProjectDnaWritebackProposalInput): Record<string, unknown> {
|
|
503
|
+
if (!input.runId.trim()) throw new Error("run_id is required");
|
|
504
|
+
if (!Array.isArray(input.sourceIds) || input.sourceIds.length === 0) throw new Error("source_ids requires at least one source id");
|
|
505
|
+
validateSha256(input.observedPatternHash, "observed_pattern_hash");
|
|
506
|
+
validateSha256(input.proposedCapsuleHash, "proposed_capsule_hash");
|
|
507
|
+
const artifact = assertSafeRelativePath(input.recommendedArtifact, "recommended_artifact", repoRoot, { allowMissing: true });
|
|
508
|
+
for (const ref of input.evidenceRefs) {
|
|
509
|
+
const safeRef = assertSafeRelativePath(ref, "evidence_ref", repoRoot, { allowMissing: true });
|
|
510
|
+
if (hasForbiddenPathMarker(safeRef.relativePath)) throw new Error(`forbidden evidence_ref: ${ref}`);
|
|
511
|
+
}
|
|
512
|
+
const proposalId = safeRunId(input.proposalId, "project-dna-writeback-proposal");
|
|
513
|
+
const proposal = {
|
|
514
|
+
schema: "zob.project-dna-writeback-proposal.v1",
|
|
515
|
+
proposal_id: proposalId,
|
|
516
|
+
run_id_hash: sha256(input.runId),
|
|
517
|
+
source_ids: unique(input.sourceIds).sort(),
|
|
518
|
+
observed_pattern_hash: input.observedPatternHash,
|
|
519
|
+
proposed_capsule_hash: input.proposedCapsuleHash,
|
|
520
|
+
evidence_refs: input.evidenceRefs,
|
|
521
|
+
recommended_artifact: artifact.relativePath,
|
|
522
|
+
raw_problem_stored: false,
|
|
523
|
+
raw_pattern_stored: false,
|
|
524
|
+
durable_promotion_allowed: false,
|
|
525
|
+
human_approval_required: true,
|
|
526
|
+
external_knowledge_backend_write_enabled: false,
|
|
527
|
+
created_at: new Date().toISOString(),
|
|
528
|
+
};
|
|
529
|
+
const outPath = resolve(repoRoot, ".pi/project-dna/writeback-proposals.jsonl");
|
|
530
|
+
mkdirSync(dirname(outPath), { recursive: true });
|
|
531
|
+
appendFileSync(outPath, `${JSON.stringify(proposal)}\n`, "utf8");
|
|
532
|
+
return proposal;
|
|
533
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Promotion module guardrail
|
|
2
|
+
|
|
3
|
+
## Scope
|
|
4
|
+
|
|
5
|
+
- This folder owns promotion candidate metadata, promotion coms refs, and prepared/quarantine artifacts for documentation, temp-agent, factory, and write-lane flows.
|
|
6
|
+
- Promotion here is proposal-only unless an explicit human/oracle-approved quarantine path says otherwise; never turn preparation helpers into production apply behavior.
|
|
7
|
+
|
|
8
|
+
## Safety invariants
|
|
9
|
+
|
|
10
|
+
- Do not add auto-apply, hidden transport, worker-to-worker direct chat, or production writes.
|
|
11
|
+
- Never store prompt/output/body content in promotion metadata; keep body flags false and use hashes/refs only.
|
|
12
|
+
- Do not read or reference secrets (`.env`, keys, `~/.ssh`, `~/.aws`) or generated/vendor paths (`node_modules`, `dist`, `build`).
|
|
13
|
+
- Preserve public types, schemas, artifact names/refs, status transitions, validation gates, hashes, and parent-owned approval/oracle semantics.
|
|
14
|
+
|
|
15
|
+
## Refactor rules
|
|
16
|
+
|
|
17
|
+
- Split-only/no behavior drift: keep strings, defaults, ordering, validation errors, and observable artifacts stable.
|
|
18
|
+
- Use NodeNext runtime imports with `.js` suffix and `import type` for type-only imports.
|
|
19
|
+
- Do not import from extension `index.ts`/`index.js`.
|
|
20
|
+
|
|
21
|
+
## Validation
|
|
22
|
+
|
|
23
|
+
- For any doc-only guardrail change here: `git diff --check` and `npm run check -- --pretty false`.
|
|
24
|
+
- For any runtime-facing change in this folder: also run the narrow relevant promotion tests/smokes if available, then `npm run smoke:harness`.
|