ultimate-pi 0.18.0 → 0.19.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/.agents/skills/harness-debate-plan/SKILL.md +1 -1
- package/.agents/skills/harness-decisions/SKILL.md +2 -3
- package/.agents/skills/harness-governor/SKILL.md +6 -5
- package/.agents/skills/harness-orchestration/SKILL.md +4 -4
- package/.agents/skills/harness-review/SKILL.md +7 -7
- package/.agents/skills/harness-sentrux-setup/SKILL.md +4 -3
- package/.agents/skills/harness-steer/SKILL.md +1 -1
- package/.agents/skills/sentrux/SKILL.md +9 -9
- package/.pi/PACKAGING.md +4 -4
- package/.pi/SYSTEM.md +54 -120
- package/.pi/agents/harness/incident-recorder.md +0 -1
- package/.pi/agents/harness/planning/decompose.md +1 -3
- package/.pi/agents/harness/planning/execution-plan-author.md +0 -2
- package/.pi/agents/harness/planning/hypothesis-validator.md +0 -2
- package/.pi/agents/harness/planning/hypothesis.md +0 -2
- package/.pi/agents/harness/planning/implementation-researcher.md +0 -2
- package/.pi/agents/harness/planning/plan-adversary.md +0 -2
- package/.pi/agents/harness/planning/plan-evaluator.md +1 -3
- package/.pi/agents/harness/planning/planning-context.md +0 -2
- package/.pi/agents/harness/planning/review-integrator.md +0 -2
- package/.pi/agents/harness/planning/sprint-contract-auditor.md +0 -2
- package/.pi/agents/harness/planning/stack-researcher.md +0 -2
- package/.pi/agents/harness/{adversary.md → reviewing/adversary.md} +0 -2
- package/.pi/agents/harness/{evaluator.md → reviewing/evaluator.md} +0 -2
- package/.pi/agents/harness/{tie-breaker.md → reviewing/tie-breaker.md} +0 -2
- package/.pi/agents/harness/{executor.md → running/executor.md} +0 -2
- package/.pi/agents/harness/sentrux-bootstrap.md +0 -1
- package/.pi/agents/harness/sentrux-steward.md +0 -2
- package/.pi/agents/harness/trace-librarian.md +0 -1
- package/.pi/extensions/00-harness-project-control.ts +133 -0
- package/.pi/extensions/00-posthog-network-bootstrap.ts +1 -1
- package/.pi/extensions/agt-kill-switch.ts +57 -0
- package/.pi/extensions/agt-prompt-guard.ts +32 -0
- package/.pi/extensions/budget-guard.ts +2 -0
- package/.pi/extensions/custom-footer.ts +46 -145
- package/.pi/extensions/custom-header.ts +1 -1
- package/.pi/extensions/custom-system-prompt.ts +1 -1
- package/.pi/extensions/debate-orchestrator.ts +7 -5
- package/.pi/extensions/harness-ask-user.ts +8 -8
- package/.pi/extensions/harness-debate-tools.ts +27 -43
- package/.pi/extensions/harness-lens.ts +94 -0
- package/.pi/extensions/harness-live-widget.ts +33 -2
- package/.pi/extensions/harness-plan-approval.ts +12 -12
- package/.pi/extensions/harness-run-context.ts +1214 -852
- package/.pi/extensions/harness-subagent-governance.ts +8 -0
- package/.pi/extensions/harness-subagent-submit.ts +36 -164
- package/.pi/extensions/harness-subagents.ts +4 -4
- package/.pi/extensions/harness-telemetry.ts +3 -1
- package/.pi/extensions/harness-web-tools.ts +3 -3
- package/.pi/extensions/observation-bus.ts +2 -0
- package/.pi/extensions/policy-gate.ts +27 -5
- package/.pi/extensions/review-integrity.ts +91 -10
- package/.pi/extensions/sentrux-rules-sync.ts +3 -1
- package/.pi/extensions/subagent-governance.ts +92 -0
- package/.pi/extensions/test-diff-integrity.ts +1 -0
- package/.pi/extensions/trace-recorder.ts +3 -1
- package/.pi/extensions/{ultimate-pi-vcc.ts → vcc-compaction.ts} +1 -1
- package/.pi/harness/README.md +6 -2
- package/.pi/harness/agents.manifest.json +38 -49
- package/.pi/harness/agents.policy.yaml +275 -0
- package/.pi/harness/corpus/graphify-kb-updater.config.json +55 -0
- package/.pi/harness/docs/adrs/0006-sentrux-dual-layer.md +2 -1
- package/.pi/harness/docs/adrs/0030-inhouse-vcc-compaction.md +1 -1
- package/.pi/harness/docs/adrs/0035-plan-phase-review-gate.md +1 -1
- package/.pi/harness/docs/adrs/0044-harness-steer-loop.md +3 -2
- package/.pi/harness/docs/adrs/0045-harness-lens-minimal-contract.md +49 -0
- package/.pi/harness/docs/adrs/0045-phase-scoped-agent-directories.md +33 -0
- package/.pi/harness/docs/adrs/0046-agt-policy-engine.md +51 -0
- package/.pi/harness/docs/adrs/0047-agt-layered-security.md +39 -0
- package/.pi/harness/docs/adrs/0048-tool-call-hook-order.md +25 -0
- package/.pi/harness/docs/adrs/0049-agents-policy-manifest.md +36 -0
- package/.pi/harness/docs/adrs/README.md +6 -0
- package/.pi/harness/docs/graphify-kb-updater-runbook.md +11 -5
- package/.pi/harness/docs/practice-map.md +2 -2
- package/.pi/harness/evolution/README.md +1 -2
- package/.pi/harness/examples/agents.policy.project.yaml +19 -0
- package/.pi/harness/examples/policies/custom-deny-bash.yaml +9 -0
- package/.pi/harness/policies/bash-denylists.yaml +5 -0
- package/.pi/harness/policies/defaults.yaml +51 -0
- package/.pi/harness/policies/orchestrator.yaml +18 -0
- package/.pi/harness/policies/phases.yaml +10 -0
- package/.pi/harness/policies/roles.yaml +5 -0
- package/.pi/harness/policies/web-guard.yaml +5 -0
- package/.pi/harness/policies/workflow-sequences.yaml +9 -0
- package/.pi/harness/sentrux/architecture.manifest.json +26 -4
- package/.pi/harness/specs/harness-spawn-context.schema.json +1 -1
- package/.pi/harness/specs/observation.schema.json +2 -1
- package/.pi/lib/agents-policy.d.mts +70 -0
- package/.pi/lib/agents-policy.mjs +325 -0
- package/.pi/lib/agents-policy.ts +19 -0
- package/.pi/lib/agt/audit-run-sink.ts +52 -0
- package/.pi/lib/agt/build-evaluation-context.ts +285 -0
- package/.pi/lib/agt/config.ts +28 -0
- package/.pi/lib/agt/delegation.ts +69 -0
- package/.pi/lib/agt/evaluate-policy.ts +56 -0
- package/.pi/lib/agt/identity-registry.ts +41 -0
- package/.pi/lib/agt/index.ts +55 -0
- package/.pi/lib/agt/kill-switch-state.ts +11 -0
- package/.pi/lib/agt/legacy-evaluate.ts +101 -0
- package/.pi/lib/agt/policy-engine.ts +154 -0
- package/.pi/lib/agt/rings.ts +21 -0
- package/.pi/lib/agt/sre-hooks.ts +45 -0
- package/.pi/lib/agt/trust-run-store.ts +26 -0
- package/.pi/lib/agt/workflow-history.ts +29 -0
- package/.pi/lib/agt-governance-active.ts +14 -0
- package/.pi/lib/agt-tool-guard.ts +78 -0
- package/.pi/lib/ask-user/dialog.ts +314 -0
- package/.pi/{extensions/lib → lib}/debate-bus-core.ts +10 -10
- package/.pi/{extensions/lib → lib}/debate-bus-state.ts +1 -1
- package/.pi/{extensions/lib → lib}/extension-load-guard.ts +21 -0
- package/.pi/lib/harness-agt-tool-guard.ts +5 -0
- package/.pi/{extensions/lib → lib}/harness-artifact-gate.ts +6 -16
- package/.pi/lib/harness-debate-core-deps.ts +14 -0
- package/.pi/lib/harness-debate-workflow-deps.ts +43 -0
- package/.pi/lib/harness-lens/.gitattributes +1 -0
- package/.pi/lib/harness-lens/clients/edit-autopatch.ts +88 -0
- package/.pi/lib/harness-lens/clients/file-kinds.ts +380 -0
- package/.pi/lib/harness-lens/clients/file-time.ts +215 -0
- package/.pi/lib/harness-lens/clients/file-utils.ts +484 -0
- package/.pi/lib/harness-lens/clients/format-service.ts +276 -0
- package/.pi/lib/harness-lens/clients/formatters.ts +1000 -0
- package/.pi/lib/harness-lens/clients/git-guard.ts +31 -0
- package/.pi/lib/harness-lens/clients/indent-retarget.ts +90 -0
- package/.pi/lib/harness-lens/clients/installer/index.ts +2368 -0
- package/.pi/lib/harness-lens/clients/latency-logger.ts +80 -0
- package/.pi/lib/harness-lens/clients/lens-config.ts +43 -0
- package/.pi/lib/harness-lens/clients/lens-events.ts +164 -0
- package/.pi/lib/harness-lens/clients/lsp/aggregation.ts +91 -0
- package/.pi/lib/harness-lens/clients/lsp/client.ts +1466 -0
- package/.pi/lib/harness-lens/clients/lsp/config.ts +216 -0
- package/.pi/lib/harness-lens/clients/lsp/edits.ts +297 -0
- package/.pi/lib/harness-lens/clients/lsp/index.ts +1355 -0
- package/.pi/lib/harness-lens/clients/lsp/interactive-install.ts +424 -0
- package/.pi/lib/harness-lens/clients/lsp/language.ts +223 -0
- package/.pi/lib/harness-lens/clients/lsp/launch.ts +939 -0
- package/.pi/lib/harness-lens/clients/lsp/lsp-index.ts +11 -0
- package/.pi/lib/harness-lens/clients/lsp/path-utils.ts +12 -0
- package/.pi/lib/harness-lens/clients/lsp/server-strategies.ts +81 -0
- package/.pi/lib/harness-lens/clients/lsp/server.ts +1971 -0
- package/.pi/lib/harness-lens/clients/path-utils.ts +182 -0
- package/.pi/lib/harness-lens/clients/pipeline.ts +360 -0
- package/.pi/lib/harness-lens/clients/project-profile.ts +117 -0
- package/.pi/lib/harness-lens/clients/runtime-agent-end.ts +112 -0
- package/.pi/lib/harness-lens/clients/runtime-config.ts +33 -0
- package/.pi/lib/harness-lens/clients/runtime-coordinator.ts +186 -0
- package/.pi/lib/harness-lens/clients/runtime-tool-result.ts +171 -0
- package/.pi/lib/harness-lens/clients/safe-spawn.ts +339 -0
- package/.pi/lib/harness-lens/clients/secrets-scanner.ts +214 -0
- package/.pi/lib/harness-lens/clients/tool-policy.ts +2072 -0
- package/.pi/lib/harness-lens/clients/types.ts +59 -0
- package/.pi/lib/harness-lens/clients/widget-state.ts +283 -0
- package/.pi/lib/harness-lens/index.ts +532 -0
- package/.pi/lib/harness-lens/tools/lsp-diagnostics.ts +706 -0
- package/.pi/lib/harness-lens/tools/lsp-navigation.ts +1246 -0
- package/.pi/{extensions/lib → lib}/harness-posthog.ts +3 -0
- package/.pi/lib/harness-project-config.ts +91 -0
- package/.pi/lib/harness-run-context-responses.ts +9 -0
- package/.pi/lib/harness-run-context.ts +1 -3
- package/.pi/{extensions/lib/spawn-policy.ts → lib/harness-spawn-policy.ts} +4 -3
- package/.pi/{extensions/lib → lib}/harness-spawn-topology.ts +5 -28
- package/.pi/lib/harness-subagent-auth.ts +51 -0
- package/.pi/{extensions/lib → lib}/harness-subagent-precheck.ts +13 -10
- package/.pi/{extensions/lib → lib}/harness-subagent-submit-pipeline.ts +3 -3
- package/.pi/lib/harness-subagent-submit-register.ts +163 -0
- package/.pi/{extensions/lib → lib}/harness-subagent-submit-registry.ts +1 -55
- package/.pi/{extensions/lib → lib}/harness-subagents-bridge.ts +53 -14
- package/.pi/{extensions/lib → lib}/harness-subprocess-bootstrap.ts +1 -1
- package/.pi/lib/harness-ui-state.ts +27 -12
- package/.pi/{extensions/lib → lib}/plan-approval/create-plan.ts +2 -2
- package/.pi/{extensions/lib → lib}/plan-approval/format-plan.ts +2 -2
- package/.pi/{extensions/lib → lib}/plan-approval/plan-review.ts +162 -201
- package/.pi/{extensions/lib → lib}/plan-approval/render.ts +1 -1
- package/.pi/{extensions/lib → lib}/plan-approval/resolve-disk.ts +2 -2
- package/.pi/{extensions/lib → lib}/plan-approval/types.ts +1 -1
- package/.pi/{extensions/lib → lib}/plan-approval/validate.ts +3 -3
- package/.pi/{extensions/lib → lib}/plan-approval-readiness.ts +3 -52
- package/.pi/{extensions/lib → lib}/plan-debate-envelope.ts +1 -1
- package/.pi/{extensions/lib → lib}/plan-debate-gate.ts +1 -1
- package/.pi/{extensions/lib → lib}/plan-debate-lane.ts +1 -4
- package/.pi/{extensions/lib → lib}/plan-messenger.ts +1 -1
- package/.pi/prompts/harness-auto.md +2 -2
- package/.pi/prompts/harness-plan.md +4 -6
- package/.pi/prompts/harness-review.md +9 -9
- package/.pi/prompts/harness-run.md +7 -7
- package/.pi/prompts/harness-setup.md +42 -68
- package/.pi/prompts/harness-steer.md +2 -2
- package/.pi/scripts/README.md +3 -5
- package/.pi/scripts/generate-agents-policy-yaml.mjs +148 -0
- package/.pi/scripts/graphify-kb-updater.mjs +48 -8
- package/.pi/scripts/harness-agents-manifest.mjs +61 -4
- package/.pi/scripts/harness-agt-doctor.ts +36 -0
- package/.pi/scripts/harness-cli-verify.sh +9 -2
- package/.pi/scripts/harness-project-toggle.mjs +129 -0
- package/.pi/scripts/harness-sentrux-cli.mjs +142 -0
- package/.pi/scripts/harness-verify.mjs +113 -39
- package/.pi/scripts/harness-web-policy-guard.mjs +2 -2
- package/.pi/scripts/validate-plan-dag.mjs +65 -74
- package/.pi/scripts/vendor-pi-vcc-settings.stub.ts +2 -2
- package/.pi/scripts/vendor-sync-pi-vcc.sh +1 -1
- package/.pi/skills/architecture/broker-domain/SKILL.md +65 -0
- package/.pi/skills/architecture/cqrs/SKILL.md +63 -0
- package/.pi/skills/architecture/event-driven/SKILL.md +60 -0
- package/.pi/skills/architecture/hexagonal-ports-adapters/SKILL.md +66 -0
- package/.pi/skills/architecture/layered/SKILL.md +68 -0
- package/.pi/skills/architecture/microkernel/SKILL.md +62 -0
- package/.pi/skills/architecture/microservices/SKILL.md +64 -0
- package/.pi/skills/architecture/modular-monolith/SKILL.md +65 -0
- package/.pi/skills/architecture/orchestration-driven-soa/SKILL.md +61 -0
- package/.pi/skills/architecture/pipeline/SKILL.md +63 -0
- package/.pi/skills/architecture/service-based/SKILL.md +64 -0
- package/.pi/skills/architecture/service-mesh/SKILL.md +60 -0
- package/.pi/skills/architecture/space-based/SKILL.md +60 -0
- package/.pi/skills/ast-grep/SKILL.md +40 -321
- package/.pi/skills/delivery/debugging-discipline/SKILL.md +36 -0
- package/.pi/skills/delivery/documentation-update/SKILL.md +33 -0
- package/.pi/skills/delivery/requirements-to-implementation/SKILL.md +34 -0
- package/.pi/skills/delivery/risk-based-verification/SKILL.md +43 -0
- package/.pi/skills/delivery/tradeoff-analysis/SKILL.md +34 -0
- package/.pi/skills/engineering/api-contract-design/SKILL.md +38 -0
- package/.pi/skills/engineering/cohesion-coupling/SKILL.md +43 -0
- package/.pi/skills/engineering/complexity-control/SKILL.md +31 -0
- package/.pi/skills/engineering/defensive-programming/SKILL.md +38 -0
- package/.pi/skills/engineering/dependency-management/SKILL.md +29 -0
- package/.pi/skills/engineering/domain-modeling/SKILL.md +32 -0
- package/.pi/skills/engineering/error-handling/SKILL.md +37 -0
- package/.pi/skills/engineering/legacy-code-seams/SKILL.md +35 -0
- package/.pi/skills/engineering/naming-and-intent/SKILL.md +29 -0
- package/.pi/skills/engineering/refactoring-safe-evolution/SKILL.md +35 -0
- package/.pi/skills/engineering/routine-function-design/SKILL.md +34 -0
- package/.pi/skills/engineering/small-change-discipline/SKILL.md +35 -0
- package/.pi/skills/lsp-navigation/SKILL.md +89 -0
- package/.pi/skills/quality/code-review-self-check/SKILL.md +35 -0
- package/.pi/skills/quality/privacy-data-handling/SKILL.md +26 -0
- package/.pi/skills/quality/security-review/SKILL.md +34 -0
- package/.pi/skills/quality/test-strategy/SKILL.md +33 -0
- package/.pi/skills/quality/testability-design/SKILL.md +33 -0
- package/.pi/skills/systems/concurrency-safety/SKILL.md +32 -0
- package/.pi/skills/systems/data-modeling-migrations/SKILL.md +31 -0
- package/.pi/skills/systems/observability-instrumentation/SKILL.md +32 -0
- package/.pi/skills/systems/performance-measurement/SKILL.md +35 -0
- package/.pi/skills/systems/reliability-design/SKILL.md +32 -0
- package/.sentrux/rules.toml +20 -4
- package/AGENTS.md +5 -0
- package/CHANGELOG.md +26 -0
- package/README.md +85 -58
- package/THIRD_PARTY_NOTICES.md +12 -21
- package/package.json +15 -7
- package/vendor/pi-subagents/src/agents.ts +45 -1
- package/vendor/pi-subagents/src/subagents.ts +866 -811
- package/vendor/pi-vcc/src/core/brief.ts +68 -99
- package/vendor/pi-vcc/src/core/settings.ts +2 -2
- package/.agents/skills/caveman/SKILL.md +0 -67
- package/.pi/agents/harness/meta-optimizer.md +0 -36
- package/.pi/agents/harness/planning/scout-graphify.md +0 -39
- package/.pi/agents/harness/planning/scout-semantic.md +0 -41
- package/.pi/agents/harness/planning/scout-structure.md +0 -37
- package/.pi/extensions/lib/ask-user/dialog.ts +0 -260
- package/.pi/extensions/lib/harness-subagent-auth.ts +0 -209
- package/.pi/extensions/lib/harness-subagent-policy.ts +0 -236
- package/.pi/extensions/pi-model-router-harness.ts +0 -42
- package/.pi/harness/evolution/meta-optimizer.mjs +0 -99
- package/.pi/harness/specs/router-tuning-proposal.schema.json +0 -114
- package/.pi/model-router.example.json +0 -36
- package/.pi/prompts/harness-critic.md +0 -10
- package/.pi/prompts/harness-eval.md +0 -10
- package/.pi/prompts/harness-router-tune.md +0 -52
- package/.pi/scripts/harness-generate-model-router.mjs +0 -327
- package/.pi/scripts/harness-model-router-routing.test.mjs +0 -97
- package/.pi/scripts/harness-sync-model-router.mjs +0 -97
- package/.pi/scripts/vendor-sync-pi-model-router.sh +0 -47
- package/vendor/pi-model-router/.prettierignore +0 -4
- package/vendor/pi-model-router/.prettierrc +0 -5
- package/vendor/pi-model-router/AGENTS.md +0 -39
- package/vendor/pi-model-router/LICENSE +0 -21
- package/vendor/pi-model-router/README.md +0 -99
- package/vendor/pi-model-router/UPSTREAM_PIN.md +0 -10
- package/vendor/pi-model-router/docs/ARCHITECTURE.md +0 -54
- package/vendor/pi-model-router/extensions/commands.ts +0 -720
- package/vendor/pi-model-router/extensions/config.ts +0 -348
- package/vendor/pi-model-router/extensions/constants.ts +0 -1
- package/vendor/pi-model-router/extensions/index.ts +0 -478
- package/vendor/pi-model-router/extensions/provider.ts +0 -580
- package/vendor/pi-model-router/extensions/routing.ts +0 -564
- package/vendor/pi-model-router/extensions/state.ts +0 -52
- package/vendor/pi-model-router/extensions/types.ts +0 -95
- package/vendor/pi-model-router/extensions/ui.ts +0 -144
- package/vendor/pi-model-router/model-router.example.json +0 -48
- package/vendor/pi-model-router/package.json +0 -48
- package/vendor/pi-model-router/tsconfig.json +0 -16
- /package/.pi/{prompts → harness/docs}/planning-rubrics.md +0 -0
- /package/.pi/{extensions/lib → lib}/ask-user/fallback.ts +0 -0
- /package/.pi/{extensions/lib → lib}/ask-user/render.ts +0 -0
- /package/.pi/{extensions/lib → lib}/ask-user/schema.ts +0 -0
- /package/.pi/{extensions/lib → lib}/ask-user/types.ts +0 -0
- /package/.pi/{extensions/lib → lib}/ask-user/validate-core.mjs +0 -0
- /package/.pi/{extensions/lib → lib}/ask-user/validate.ts +0 -0
- /package/.pi/{extensions/lib → lib}/harness-cocoindex-refresh.ts +0 -0
- /package/.pi/{extensions/lib → lib}/harness-paths.ts +0 -0
- /package/.pi/{extensions/lib → lib}/harness-spawn-budget.ts +0 -0
- /package/.pi/{extensions/lib → lib}/harness-vcc-settings.ts +0 -0
- /package/.pi/{extensions/lib → lib}/harness-web/run-cli.ts +0 -0
- /package/.pi/{extensions/lib → lib}/plan-approval/dialog.ts +0 -0
- /package/.pi/{extensions/lib → lib}/plan-approval/schema.ts +0 -0
- /package/.pi/{extensions/lib → lib}/plan-debate-eligibility.ts +0 -0
- /package/.pi/{extensions/lib → lib}/plan-debate-focus.ts +0 -0
- /package/.pi/{extensions/lib → lib}/plan-debate-id.ts +0 -0
- /package/.pi/{extensions/lib → lib}/plan-debate-lanes.ts +0 -0
- /package/.pi/{extensions/lib → lib}/plan-debate-round-status.ts +0 -0
- /package/.pi/{extensions/lib → lib}/plan-debate-write-guard.ts +0 -0
- /package/.pi/{extensions/lib → lib}/plan-review-gate.ts +0 -0
- /package/.pi/{extensions/lib → lib}/plan-review-integrator-rules.ts +0 -0
- /package/.pi/{extensions/lib → lib}/plan-scope-guard.ts +0 -0
- /package/.pi/{extensions/lib → lib}/posthog-client.ts +0 -0
- /package/.pi/{extensions/lib → lib}/posthog-node.d.ts +0 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Run the Sentrux CLI against the harness project root.
|
|
4
|
+
*
|
|
5
|
+
* Sentrux resolves `.sentrux/rules.toml` relative to the PATH argument, so
|
|
6
|
+
* harness commands must not rely on the current working directory. This helper
|
|
7
|
+
* finds the nearest ancestor with harness Sentrux config and passes that root
|
|
8
|
+
* explicitly to `sentrux check` / `sentrux gate`.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* node "$UP_PKG/.pi/scripts/harness-sentrux-cli.mjs" check [--root <PROJECT_ROOT>]
|
|
12
|
+
* node "$UP_PKG/.pi/scripts/harness-sentrux-cli.mjs" gate [--save] [--root <PROJECT_ROOT>]
|
|
13
|
+
* node "$UP_PKG/.pi/scripts/harness-sentrux-cli.mjs" --print-root
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { access } from "node:fs/promises";
|
|
17
|
+
import { constants } from "node:fs";
|
|
18
|
+
import { dirname, isAbsolute, join, resolve } from "node:path";
|
|
19
|
+
import { spawn } from "node:child_process";
|
|
20
|
+
|
|
21
|
+
const ROOT_MARKERS = [
|
|
22
|
+
join(".sentrux", "rules.toml"),
|
|
23
|
+
join(".pi", "harness", "sentrux", "architecture.manifest.json"),
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
async function fileExists(path) {
|
|
27
|
+
try {
|
|
28
|
+
await access(path, constants.R_OK);
|
|
29
|
+
return true;
|
|
30
|
+
} catch {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function hasRootMarker(dir) {
|
|
36
|
+
for (const marker of ROOT_MARKERS) {
|
|
37
|
+
if (await fileExists(join(dir, marker))) return true;
|
|
38
|
+
}
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function findProjectRoot(startDir) {
|
|
43
|
+
let dir = resolve(startDir || process.cwd());
|
|
44
|
+
while (true) {
|
|
45
|
+
if (await hasRootMarker(dir)) return dir;
|
|
46
|
+
const parent = dirname(dir);
|
|
47
|
+
if (parent === dir) return null;
|
|
48
|
+
dir = parent;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function takeRootArg(args) {
|
|
53
|
+
const next = [];
|
|
54
|
+
let explicitRoot = process.env.HARNESS_PROJECT_ROOT || "";
|
|
55
|
+
for (let i = 0; i < args.length; i++) {
|
|
56
|
+
const arg = args[i];
|
|
57
|
+
if (arg === "--root") {
|
|
58
|
+
explicitRoot = args[i + 1] || "";
|
|
59
|
+
i++;
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
if (arg.startsWith("--root=")) {
|
|
63
|
+
explicitRoot = arg.slice("--root=".length);
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
next.push(arg);
|
|
67
|
+
}
|
|
68
|
+
return { args: next, explicitRoot };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function resolveProjectRoot(explicitRoot) {
|
|
72
|
+
if (explicitRoot) {
|
|
73
|
+
const root = isAbsolute(explicitRoot)
|
|
74
|
+
? resolve(explicitRoot)
|
|
75
|
+
: resolve(process.cwd(), explicitRoot);
|
|
76
|
+
if (!(await hasRootMarker(root))) {
|
|
77
|
+
console.error(
|
|
78
|
+
`harness-sentrux-cli: ${root} has no .sentrux/rules.toml or .pi/harness/sentrux/architecture.manifest.json`,
|
|
79
|
+
);
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
return root;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const root = await findProjectRoot(process.cwd());
|
|
86
|
+
if (!root) {
|
|
87
|
+
console.error(
|
|
88
|
+
"harness-sentrux-cli: could not find a harness project root above the current directory",
|
|
89
|
+
);
|
|
90
|
+
process.exit(1);
|
|
91
|
+
}
|
|
92
|
+
return root;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function normalizeSentruxArgs(args, projectRoot) {
|
|
96
|
+
const command = args[0];
|
|
97
|
+
if (!command || command === "--help" || command === "-h") {
|
|
98
|
+
console.log(`Usage: node harness-sentrux-cli.mjs <check|gate> [sentrux flags] [--root PROJECT_ROOT]
|
|
99
|
+
|
|
100
|
+
Runs Sentrux with PROJECT_ROOT passed explicitly so .sentrux/rules.toml is found even when invoked from .pi/harness/runs/*.`);
|
|
101
|
+
process.exit(0);
|
|
102
|
+
}
|
|
103
|
+
if (command !== "check" && command !== "gate") {
|
|
104
|
+
console.error(
|
|
105
|
+
`harness-sentrux-cli: unsupported command "${command}" (expected check or gate)`,
|
|
106
|
+
);
|
|
107
|
+
process.exit(2);
|
|
108
|
+
}
|
|
109
|
+
return [command, ...args.slice(1), projectRoot];
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async function main() {
|
|
113
|
+
const parsed = takeRootArg(process.argv.slice(2));
|
|
114
|
+
const printRoot = parsed.args.includes("--print-root");
|
|
115
|
+
const sentruxArgs = parsed.args.filter((arg) => arg !== "--print-root");
|
|
116
|
+
const projectRoot = await resolveProjectRoot(parsed.explicitRoot);
|
|
117
|
+
|
|
118
|
+
if (printRoot) {
|
|
119
|
+
console.log(projectRoot);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const child = spawn("sentrux", normalizeSentruxArgs(sentruxArgs, projectRoot), {
|
|
124
|
+
cwd: projectRoot,
|
|
125
|
+
stdio: "inherit",
|
|
126
|
+
env: process.env,
|
|
127
|
+
});
|
|
128
|
+
child.on("error", (err) => {
|
|
129
|
+
if (err?.code === "ENOENT") {
|
|
130
|
+
console.error("harness-sentrux-cli: sentrux not installed");
|
|
131
|
+
process.exit(127);
|
|
132
|
+
}
|
|
133
|
+
console.error(`harness-sentrux-cli: ${err.message}`);
|
|
134
|
+
process.exit(1);
|
|
135
|
+
});
|
|
136
|
+
child.on("close", (code) => process.exit(code ?? 1));
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
main().catch((err) => {
|
|
140
|
+
console.error(err);
|
|
141
|
+
process.exit(1);
|
|
142
|
+
});
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { readFile, access } from "node:fs/promises";
|
|
7
7
|
import { constants } from "node:fs";
|
|
8
|
-
import { join, dirname
|
|
8
|
+
import { join, dirname } from "node:path";
|
|
9
9
|
import { fileURLToPath } from "node:url";
|
|
10
10
|
import { spawn } from "node:child_process";
|
|
11
11
|
|
|
@@ -42,6 +42,10 @@ const REQUIRED_ADRS = [
|
|
|
42
42
|
"0037-subagent-submit-tools.md",
|
|
43
43
|
"0038-budget-telemetry-only.md",
|
|
44
44
|
"0040-practice-grounded-orchestration.md",
|
|
45
|
+
"0045-harness-lens-minimal-contract.md",
|
|
46
|
+
"0046-agt-policy-engine.md",
|
|
47
|
+
"0047-agt-layered-security.md",
|
|
48
|
+
"0048-tool-call-hook-order.md",
|
|
45
49
|
];
|
|
46
50
|
|
|
47
51
|
const REQUIRED_EXTENSIONS = [
|
|
@@ -148,32 +152,59 @@ async function checkSentruxRules() {
|
|
|
148
152
|
ok(".sentrux/rules.toml present");
|
|
149
153
|
}
|
|
150
154
|
|
|
151
|
-
async function
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
155
|
+
async function checkHarnessLens(pkgJson) {
|
|
156
|
+
if (!pkgJson.files?.includes(".pi/lib/harness-lens")) {
|
|
157
|
+
fail(
|
|
158
|
+
'package.json "files" must include .pi/lib/harness-lens (npm publish ships harness lens extension)',
|
|
159
|
+
);
|
|
156
160
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
fail(
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
161
|
+
ok('package.json files includes .pi/lib/harness-lens');
|
|
162
|
+
|
|
163
|
+
const piExtensions = pkgJson.pi?.extensions ?? [];
|
|
164
|
+
if (!piExtensions.includes("./.pi/extensions")) {
|
|
165
|
+
fail('package.json pi.extensions must include ./.pi/extensions');
|
|
166
|
+
}
|
|
167
|
+
if (piExtensions.includes("./vendor/pi-lens/index.ts")) {
|
|
168
|
+
fail('package.json pi.extensions must not load vendor/pi-lens directly');
|
|
169
|
+
}
|
|
170
|
+
ok("package.json loads harness extension directory");
|
|
171
|
+
|
|
172
|
+
const harnessLens = join(ROOT, ".pi", "extensions", "harness-lens.ts");
|
|
173
|
+
if (!(await fileExists(harnessLens))) fail("missing .pi/extensions/harness-lens.ts");
|
|
174
|
+
ok("harness lens extension wrapper");
|
|
175
|
+
|
|
176
|
+
const lensIndex = join(ROOT, ".pi", "lib", "harness-lens", "index.ts");
|
|
177
|
+
if (!(await fileExists(lensIndex))) {
|
|
178
|
+
fail("missing .pi/lib/harness-lens/index.ts");
|
|
179
|
+
}
|
|
180
|
+
ok(".pi/lib/harness-lens/index.ts");
|
|
181
|
+
|
|
182
|
+
const legacyExtLib = join(ROOT, ".pi", "extensions", "lib");
|
|
183
|
+
if (await fileExists(legacyExtLib)) {
|
|
184
|
+
fail(".pi/extensions/lib must not exist (shared code lives in .pi/lib/)");
|
|
185
|
+
}
|
|
186
|
+
ok("no legacy .pi/extensions/lib directory");
|
|
187
|
+
|
|
188
|
+
const lensIndexSource = await readFile(lensIndex, "utf8");
|
|
189
|
+
if (lensIndexSource.includes("ast_grep_search")) {
|
|
190
|
+
fail("harness-lens index must not register ast_grep_search");
|
|
191
|
+
}
|
|
192
|
+
if (lensIndexSource.includes("lib/lens")) {
|
|
193
|
+
fail("harness-lens index must not import lib/lens");
|
|
194
|
+
}
|
|
195
|
+
ok("harness-lens index contract (no ast_grep, no lib/lens imports)");
|
|
196
|
+
|
|
197
|
+
const rulesDir = join(ROOT, ".pi", "lib", "harness-lens", "rules");
|
|
198
|
+
if (await fileExists(rulesDir)) {
|
|
199
|
+
fail("harness-lens bundled rules/ directory must not exist");
|
|
200
|
+
}
|
|
201
|
+
ok("no bundled harness-lens rules/ directory");
|
|
202
|
+
|
|
203
|
+
const upstreamPin = join(ROOT, ".pi", "lib", "harness-lens", "UPSTREAM_PIN.md");
|
|
204
|
+
if (await fileExists(upstreamPin)) {
|
|
205
|
+
fail("harness-lens UPSTREAM_PIN.md must not exist (harness-native, no upstream sync)");
|
|
175
206
|
}
|
|
176
|
-
ok("
|
|
207
|
+
ok("no harness-lens UPSTREAM_PIN.md");
|
|
177
208
|
}
|
|
178
209
|
|
|
179
210
|
async function checkSentruxGate() {
|
|
@@ -235,7 +266,7 @@ async function main() {
|
|
|
235
266
|
ok(`extension ${name}`);
|
|
236
267
|
}
|
|
237
268
|
|
|
238
|
-
const libPath = join(ROOT, ".pi", "
|
|
269
|
+
const libPath = join(ROOT, ".pi", "lib", "harness-posthog.ts");
|
|
239
270
|
if (!(await fileExists(libPath))) fail("missing lib/harness-posthog.ts");
|
|
240
271
|
ok("lib/harness-posthog.ts");
|
|
241
272
|
|
|
@@ -246,6 +277,8 @@ async function main() {
|
|
|
246
277
|
const pkgJson = JSON.parse(
|
|
247
278
|
await readFile(join(ROOT, "package.json"), "utf-8"),
|
|
248
279
|
);
|
|
280
|
+
await checkHarnessLens(pkgJson);
|
|
281
|
+
|
|
249
282
|
if (!pkgJson.files?.includes("vendor/pi-subagents")) {
|
|
250
283
|
fail(
|
|
251
284
|
'package.json "files" must include vendor/pi-subagents (npm publish ships subagents vendor)',
|
|
@@ -263,13 +296,7 @@ async function main() {
|
|
|
263
296
|
if (!(await fileExists(subagentsVendor))) {
|
|
264
297
|
fail("missing vendor/pi-subagents/src/subagents.ts");
|
|
265
298
|
}
|
|
266
|
-
const bridgePath = join(
|
|
267
|
-
ROOT,
|
|
268
|
-
".pi",
|
|
269
|
-
"extensions",
|
|
270
|
-
"lib",
|
|
271
|
-
"harness-subagents-bridge.ts",
|
|
272
|
-
);
|
|
299
|
+
const bridgePath = join(ROOT, ".pi", "lib", "harness-subagents-bridge.ts");
|
|
273
300
|
if (!(await fileExists(bridgePath))) {
|
|
274
301
|
fail("missing harness-subagents-bridge.ts");
|
|
275
302
|
}
|
|
@@ -280,6 +307,14 @@ async function main() {
|
|
|
280
307
|
if (!bridgeSrc.includes("packageRoot")) {
|
|
281
308
|
fail("harness-subagents-bridge must pass packageRoot for agent discovery");
|
|
282
309
|
}
|
|
310
|
+
if (
|
|
311
|
+
!bridgeSrc.includes("subprocessGovernanceExtensionPath") &&
|
|
312
|
+
!bridgeSrc.includes("subagentGovernanceExtensionPath")
|
|
313
|
+
) {
|
|
314
|
+
fail(
|
|
315
|
+
"harness-subagents-bridge must set subprocessGovernanceExtensionPath for all subagents",
|
|
316
|
+
);
|
|
317
|
+
}
|
|
283
318
|
const subagentsSrc = await readFile(subagentsVendor, "utf-8");
|
|
284
319
|
if (!subagentsSrc.includes("discoverAgents")) {
|
|
285
320
|
fail("vendor subagents.ts must implement discoverAgents");
|
|
@@ -301,12 +336,46 @@ async function main() {
|
|
|
301
336
|
if (!policyGateSrc.includes('pi.on("tool_call", async (event, ctx)')) {
|
|
302
337
|
fail("policy-gate tool_call must receive ctx for run context");
|
|
303
338
|
}
|
|
304
|
-
if (!policyGateSrc.includes("
|
|
305
|
-
fail(
|
|
306
|
-
"policy-gate.ts must evaluate context-mode execute tools via evaluateContextModeMutation",
|
|
307
|
-
);
|
|
339
|
+
if (!policyGateSrc.includes("evaluateAgtHarnessToolCall")) {
|
|
340
|
+
fail("policy-gate.ts must delegate tool_call to AGT evaluateAgtHarnessToolCall");
|
|
308
341
|
}
|
|
309
|
-
|
|
342
|
+
const govPath = join(ROOT, ".pi", "extensions", "subagent-governance.ts");
|
|
343
|
+
const govAlias = join(
|
|
344
|
+
ROOT,
|
|
345
|
+
".pi",
|
|
346
|
+
"extensions",
|
|
347
|
+
"harness-subagent-governance.ts",
|
|
348
|
+
);
|
|
349
|
+
if (!(await fileExists(govPath))) {
|
|
350
|
+
fail("missing subagent-governance.ts subprocess bundle");
|
|
351
|
+
}
|
|
352
|
+
if (!(await fileExists(govAlias))) {
|
|
353
|
+
fail("missing harness-subagent-governance.ts re-export alias");
|
|
354
|
+
}
|
|
355
|
+
ok("policy-gate + subprocess governance");
|
|
356
|
+
|
|
357
|
+
const agtDoctorPath = join(ROOT, ".pi", "scripts", "harness-agt-doctor.ts");
|
|
358
|
+
const { code: agtDoctorCode, out: agtDoctorOut } = await new Promise(
|
|
359
|
+
(resolve) => {
|
|
360
|
+
const child = spawn(
|
|
361
|
+
"npx",
|
|
362
|
+
["-y", "tsx", agtDoctorPath],
|
|
363
|
+
{ cwd: ROOT, stdio: ["ignore", "pipe", "pipe"], shell: true },
|
|
364
|
+
);
|
|
365
|
+
let out = "";
|
|
366
|
+
child.stdout?.on("data", (d) => {
|
|
367
|
+
out += d.toString();
|
|
368
|
+
});
|
|
369
|
+
child.stderr?.on("data", (d) => {
|
|
370
|
+
out += d.toString();
|
|
371
|
+
});
|
|
372
|
+
child.on("close", (code) => resolve({ code: code ?? 1, out }));
|
|
373
|
+
},
|
|
374
|
+
);
|
|
375
|
+
if (agtDoctorCode !== 0) {
|
|
376
|
+
fail(agtDoctorOut.trim() || "AGT policy doctor failed");
|
|
377
|
+
}
|
|
378
|
+
ok("AGT policy doctor");
|
|
310
379
|
|
|
311
380
|
const runCtxFixture = join(SMOKE, "run-context.fixture.json");
|
|
312
381
|
if (!(await fileExists(runCtxFixture))) {
|
|
@@ -332,7 +401,12 @@ async function main() {
|
|
|
332
401
|
ok("test-diff-golden.json");
|
|
333
402
|
|
|
334
403
|
await checkSentruxGate();
|
|
335
|
-
|
|
404
|
+
|
|
405
|
+
const AGENTS_POLICY = join(ROOT, ".pi", "harness", "agents.policy.yaml");
|
|
406
|
+
if (!(await fileExists(AGENTS_POLICY))) {
|
|
407
|
+
fail("missing .pi/harness/agents.policy.yaml");
|
|
408
|
+
}
|
|
409
|
+
ok("agents.policy.yaml present");
|
|
336
410
|
|
|
337
411
|
if (!(await fileExists(AGENTS_MANIFEST))) {
|
|
338
412
|
fail(
|
|
@@ -12,9 +12,9 @@ const ROOT = resolve(new URL("../..", import.meta.url).pathname);
|
|
|
12
12
|
const ALLOWED_FILES = new Set([
|
|
13
13
|
".pi/extensions/harness-web-guard.ts",
|
|
14
14
|
".pi/extensions/harness-web-tools.ts",
|
|
15
|
-
".pi/
|
|
15
|
+
".pi/lib/harness-web/run-cli.ts",
|
|
16
16
|
".pi/extensions/harness-run-context.ts",
|
|
17
|
-
".pi/
|
|
17
|
+
".pi/lib/ask-user/schema.ts",
|
|
18
18
|
".pi/scripts/harness-web.py",
|
|
19
19
|
".pi/scripts/harness-web-search.md",
|
|
20
20
|
".pi/scripts/harness-web-policy-guard.mjs",
|
|
@@ -88,49 +88,31 @@ function computeCriticalPath(workItems) {
|
|
|
88
88
|
return path;
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
|
|
91
|
+
function validateMinimumShape(packet, ep, phases, workItems) {
|
|
92
92
|
const errors = [];
|
|
93
|
-
const ep = packet.execution_plan;
|
|
94
|
-
if (!ep) {
|
|
95
|
-
errors.push("execution_plan required");
|
|
96
|
-
return { status: "fail", errors, report: null };
|
|
97
|
-
}
|
|
98
|
-
|
|
99
93
|
const risk = packet.risk_level ?? "med";
|
|
100
94
|
const min = MINIMUMS[risk] ?? MINIMUMS.med;
|
|
101
|
-
const phases = ep.phases ?? [];
|
|
102
|
-
const workItems = ep.work_items ?? [];
|
|
103
|
-
const conflicts = [];
|
|
104
|
-
|
|
105
|
-
if (phases.length < min.phases) {
|
|
106
|
-
errors.push(`need >= ${min.phases} phases for risk ${risk}`);
|
|
107
|
-
}
|
|
108
|
-
if (workItems.length < min.work_items) {
|
|
109
|
-
errors.push(`need >= ${min.work_items} work_items for risk ${risk}`);
|
|
110
|
-
}
|
|
111
95
|
const ac = packet.acceptance_checks ?? [];
|
|
112
|
-
if (
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
if ((ep.risk_register ?? []).length < min.risks) {
|
|
116
|
-
|
|
117
|
-
|
|
96
|
+
if (phases.length < min.phases) errors.push(`need >= ${min.phases} phases for risk ${risk}`);
|
|
97
|
+
if (workItems.length < min.work_items) errors.push(`need >= ${min.work_items} work_items for risk ${risk}`);
|
|
98
|
+
if (ac.length < min.acceptance_checks) errors.push(`need >= ${min.acceptance_checks} acceptance_checks`);
|
|
99
|
+
if ((ep.risk_register ?? []).length < min.risks) errors.push(`need >= ${min.risks} risks for risk ${risk}`);
|
|
100
|
+
return errors;
|
|
101
|
+
}
|
|
118
102
|
|
|
103
|
+
function validatePhaseAndWorkItemLinks(phases, workItems) {
|
|
104
|
+
const errors = [];
|
|
119
105
|
const phaseIds = new Set(phases.map((p) => p.phase_id));
|
|
120
|
-
const phaseIndex = new Map(phases.map((p, i) => [p.phase_id, i]));
|
|
121
106
|
const wiIds = new Set(workItems.map((w) => w.work_item_id));
|
|
122
|
-
|
|
123
107
|
for (const p of phases) {
|
|
124
108
|
if (!p.exit_criteria?.length) errors.push(`phase ${p.phase_id} missing exit_criteria`);
|
|
125
109
|
if (!p.work_item_ids?.length) errors.push(`phase ${p.phase_id} has no work items`);
|
|
110
|
+
for (const wid of p.work_item_ids ?? []) {
|
|
111
|
+
if (!wiIds.has(wid)) errors.push(`phase ${p.phase_id} references missing ${wid}`);
|
|
112
|
+
}
|
|
126
113
|
}
|
|
127
|
-
|
|
128
|
-
const wiInPhase = new Set();
|
|
129
114
|
for (const w of workItems) {
|
|
130
|
-
if (!phaseIds.has(w.phase_id)) {
|
|
131
|
-
errors.push(`work_item ${w.work_item_id} unknown phase_id`);
|
|
132
|
-
}
|
|
133
|
-
wiInPhase.add(w.work_item_id);
|
|
115
|
+
if (!phaseIds.has(w.phase_id)) errors.push(`work_item ${w.work_item_id} unknown phase_id`);
|
|
134
116
|
for (const d of w.depends_on ?? []) {
|
|
135
117
|
if (!wiIds.has(d)) errors.push(`work_item ${w.work_item_id} depends_on missing ${d}`);
|
|
136
118
|
}
|
|
@@ -138,17 +120,23 @@ export function validateExecutionPlan(packet, projectRoot = ROOT) {
|
|
|
138
120
|
errors.push(`work_item ${w.work_item_id} needs files[] or non_code: true`);
|
|
139
121
|
}
|
|
140
122
|
}
|
|
123
|
+
return errors;
|
|
124
|
+
}
|
|
141
125
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
126
|
+
function isReachable(workItems, from, to, seen = new Set()) {
|
|
127
|
+
if (from === to) return true;
|
|
128
|
+
if (seen.has(from)) return false;
|
|
129
|
+
seen.add(from);
|
|
130
|
+
const w = workItems.find((x) => x.work_item_id === from);
|
|
131
|
+
for (const d of w?.depends_on ?? []) {
|
|
132
|
+
if (isReachable(workItems, d, to, seen)) return true;
|
|
146
133
|
}
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
147
136
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
// File conflicts
|
|
137
|
+
function findFileConflicts(phases, workItems) {
|
|
138
|
+
const conflicts = [];
|
|
139
|
+
const phaseIndex = new Map(phases.map((p, i) => [p.phase_id, i]));
|
|
152
140
|
for (let i = 0; i < workItems.length; i++) {
|
|
153
141
|
for (let j = i + 1; j < workItems.length; j++) {
|
|
154
142
|
const a = workItems[i];
|
|
@@ -156,42 +144,34 @@ export function validateExecutionPlan(packet, projectRoot = ROOT) {
|
|
|
156
144
|
const filesA = new Set(a.files ?? []);
|
|
157
145
|
const overlap = (b.files ?? []).filter((f) => filesA.has(f));
|
|
158
146
|
if (overlap.length === 0) continue;
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
return false;
|
|
168
|
-
};
|
|
169
|
-
if (!reachable(a.work_item_id, b.work_item_id) && !reachable(b.work_item_id, a.work_item_id)) {
|
|
170
|
-
if ((phaseIndex.get(a.phase_id) ?? 0) === (phaseIndex.get(b.phase_id) ?? 0)) {
|
|
171
|
-
conflicts.push(
|
|
172
|
-
`file overlap ${overlap.join(",")} between ${a.work_item_id} and ${b.work_item_id} without dependency`,
|
|
173
|
-
);
|
|
174
|
-
}
|
|
147
|
+
const ordered =
|
|
148
|
+
isReachable(workItems, a.work_item_id, b.work_item_id) ||
|
|
149
|
+
isReachable(workItems, b.work_item_id, a.work_item_id);
|
|
150
|
+
const samePhase = (phaseIndex.get(a.phase_id) ?? 0) === (phaseIndex.get(b.phase_id) ?? 0);
|
|
151
|
+
if (!ordered && samePhase) {
|
|
152
|
+
conflicts.push(
|
|
153
|
+
`file overlap ${overlap.join(",")} between ${a.work_item_id} and ${b.work_item_id} without dependency`,
|
|
154
|
+
);
|
|
175
155
|
}
|
|
176
156
|
}
|
|
177
157
|
}
|
|
158
|
+
return conflicts;
|
|
159
|
+
}
|
|
178
160
|
|
|
161
|
+
function validateCriticalPath(ep, workItems) {
|
|
179
162
|
const computedCp = computeCriticalPath(workItems);
|
|
180
163
|
const authorCp = ep.schedule_metadata?.critical_path_work_item_ids ?? [];
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
`critical_path mismatch author=${authorCp.join("→")} computed=${computedCp.join("→")}`,
|
|
188
|
-
);
|
|
189
|
-
}
|
|
190
|
-
}
|
|
164
|
+
const same =
|
|
165
|
+
authorCp.length === computedCp.length &&
|
|
166
|
+
authorCp.every((id, i) => id === computedCp[i]);
|
|
167
|
+
if (computedCp.length < 3 || !authorCp.length || same) return [];
|
|
168
|
+
return [`critical_path mismatch author=${authorCp.join("→")} computed=${computedCp.join("→")}`];
|
|
169
|
+
}
|
|
191
170
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
171
|
+
function validateAcceptanceCheckLinks(packet, workItems) {
|
|
172
|
+
const errors = [];
|
|
173
|
+
const ac = packet.acceptance_checks ?? [];
|
|
174
|
+
const acIds = new Set(ac.map((c) => (typeof c === "string" ? c : c.id)).filter(Boolean));
|
|
195
175
|
for (const w of workItems) {
|
|
196
176
|
for (const acid of w.acceptance_check_ids ?? []) {
|
|
197
177
|
if (!acIds.has(acid)) errors.push(`${w.work_item_id} references orphan ${acid}`);
|
|
@@ -201,14 +181,25 @@ export function validateExecutionPlan(packet, projectRoot = ROOT) {
|
|
|
201
181
|
const used = workItems.some((w) => (w.acceptance_check_ids ?? []).includes(acid));
|
|
202
182
|
if (!used) errors.push(`orphan acceptance check ${acid}`);
|
|
203
183
|
}
|
|
184
|
+
return errors;
|
|
185
|
+
}
|
|
204
186
|
|
|
187
|
+
export function validateExecutionPlan(packet, projectRoot = ROOT) {
|
|
188
|
+
const ep = packet.execution_plan;
|
|
189
|
+
if (!ep) return { status: "fail", errors: ["execution_plan required"], report: null };
|
|
190
|
+
const phases = ep.phases ?? [];
|
|
191
|
+
const workItems = ep.work_items ?? [];
|
|
192
|
+
const { order, cycles } = topoSort(workItems);
|
|
193
|
+
const errors = [
|
|
194
|
+
...validateMinimumShape(packet, ep, phases, workItems),
|
|
195
|
+
...validatePhaseAndWorkItemLinks(phases, workItems),
|
|
196
|
+
...(cycles.length ? [`cycle detected: ${JSON.stringify(cycles[0])}`] : []),
|
|
197
|
+
...validateCriticalPath(ep, workItems),
|
|
198
|
+
...validateAcceptanceCheckLinks(packet, workItems),
|
|
199
|
+
];
|
|
200
|
+
const conflicts = findFileConflicts(phases, workItems);
|
|
205
201
|
const status = errors.length === 0 && conflicts.length === 0 ? "pass" : "fail";
|
|
206
|
-
const report = {
|
|
207
|
-
status,
|
|
208
|
-
topological_order: order,
|
|
209
|
-
cycles,
|
|
210
|
-
conflicts: [...conflicts, ...errors],
|
|
211
|
-
};
|
|
202
|
+
const report = { status, topological_order: order, cycles, conflicts: [...conflicts, ...errors] };
|
|
212
203
|
return { status, errors: [...errors, ...conflicts], report };
|
|
213
204
|
}
|
|
214
205
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ultimate-pi harness settings (env-only). Re-exported for vendored pi-vcc layout.
|
|
3
3
|
*/
|
|
4
|
-
export type { PiVccSettings } from "../../../../.pi/
|
|
4
|
+
export type { PiVccSettings } from "../../../../.pi/lib/harness-vcc-settings.js";
|
|
5
5
|
export {
|
|
6
6
|
loadSettings,
|
|
7
7
|
scaffoldSettings,
|
|
8
|
-
} from "../../../../.pi/
|
|
8
|
+
} from "../../../../.pi/lib/harness-vcc-settings.js";
|
|
@@ -29,7 +29,7 @@ cat >"$VEND/UPSTREAM_PIN.md" <<EOF
|
|
|
29
29
|
- **License:** MIT (see upstream repository)
|
|
30
30
|
- **Pinned upstream commit:** \`$COMMIT\`
|
|
31
31
|
- **Local changes:**
|
|
32
|
-
- \`src/core/settings.ts\` re-exports env-only [\`harness-vcc-settings\`](../../.pi/
|
|
32
|
+
- \`src/core/settings.ts\` re-exports env-only [\`harness-vcc-settings\`](../../.pi/lib/harness-vcc-settings.ts) (\`HARNESS_VCC_COMPACTION\`, \`HARNESS_VCC_DEBUG\`)
|
|
33
33
|
- No \`scaffoldSettings\` / no \`PI_VCC_CONFIG_PATH\`
|
|
34
34
|
- Compaction \`details.compactor\` is \`ultimate-pi-vcc\`
|
|
35
35
|
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: broker-domain
|
|
3
|
+
description: "Use when implementing the Broker-Domain pattern: event-brokered integration around cohesive domain services, separating broker concerns from domain behavior, with explicit event contracts and bounded context ownership."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Broker-Domain Pattern
|
|
7
|
+
|
|
8
|
+
Use this skill when event-driven integration is needed but domain logic must stay inside cohesive domain boundaries.
|
|
9
|
+
|
|
10
|
+
## Fit
|
|
11
|
+
|
|
12
|
+
Use when multiple domains coordinate through events and you need to avoid both a god orchestrator and an anemic event-bus blob.
|
|
13
|
+
Avoid when a simple in-process call or single workflow orchestrator is clearer.
|
|
14
|
+
|
|
15
|
+
## Agent Workflow
|
|
16
|
+
|
|
17
|
+
1. Read `graphify-out/GRAPH_REPORT.md`.
|
|
18
|
+
2. Run `graphify query "Broker-Domain pattern event broker domain services bounded contexts"`.
|
|
19
|
+
3. Identify domain services and event broker responsibilities separately.
|
|
20
|
+
4. Keep event routing/transport out of domain decisions.
|
|
21
|
+
5. Implement one domain event flow end to end.
|
|
22
|
+
6. Add contract, idempotency, and ownership checks.
|
|
23
|
+
|
|
24
|
+
## Target Shape
|
|
25
|
+
|
|
26
|
+
```text
|
|
27
|
+
codebase/domains/
|
|
28
|
+
orders/
|
|
29
|
+
domain/
|
|
30
|
+
application/
|
|
31
|
+
events # domain-owned event contracts
|
|
32
|
+
billing/
|
|
33
|
+
...
|
|
34
|
+
codebase/broker/
|
|
35
|
+
bus
|
|
36
|
+
subscriptions
|
|
37
|
+
serializers
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Implementation Rules
|
|
41
|
+
|
|
42
|
+
- Domains publish and consume meaningful business events.
|
|
43
|
+
- Broker code owns transport, serialization, subscription wiring, retries, and delivery mechanics.
|
|
44
|
+
- Domain code must not depend on broker vendor APIs.
|
|
45
|
+
- Event contracts belong to the producing domain and are versioned.
|
|
46
|
+
- Consumers translate external events into local application actions.
|
|
47
|
+
|
|
48
|
+
## Migration Steps
|
|
49
|
+
|
|
50
|
+
1. Select one cross-domain interaction.
|
|
51
|
+
2. Define producer-owned event contract.
|
|
52
|
+
3. Add broker adapter that maps domain event to transport message.
|
|
53
|
+
4. Add consumer subscription that calls local application service.
|
|
54
|
+
5. Add idempotency and dead-letter handling.
|
|
55
|
+
6. Remove direct cross-domain call if the event path replaces it safely.
|
|
56
|
+
|
|
57
|
+
## Verification
|
|
58
|
+
|
|
59
|
+
- `graphify explain "broker"` should show broker-to-domain adapter edges, not domain-to-vendor lock-in.
|
|
60
|
+
- Test event contract compatibility and duplicate delivery.
|
|
61
|
+
- Check no domain imports broker implementation packages.
|
|
62
|
+
|
|
63
|
+
## Output Contract
|
|
64
|
+
|
|
65
|
+
Return: domains, broker responsibilities, event contracts, migrated interaction, failure handling, and verification.
|