devrites 3.0.7 → 3.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/CHANGELOG.md +12 -0
- package/README.md +67 -47
- package/SECURITY.md +31 -29
- package/docs/adr/0001-go-engine-as-control-plane.md +1 -1
- package/docs/adr/0002-dual-host-harness.md +1 -1
- package/docs/adr/0004-state-schema-phases-sections.md +1 -2
- package/docs/adr/0006-clock-seam-and-engine-ci-gates.md +3 -4
- package/docs/adr/0009-prebuild-decision-coverage-and-readiness.md +67 -0
- package/docs/adr/0010-agent-first-fresh-context-orchestration.md +71 -0
- package/docs/adr/0011-define-plan-transition-rights.md +28 -0
- package/docs/adr/README.md +3 -0
- package/docs/agents/triage-labels.md +7 -7
- package/docs/architecture.md +157 -140
- package/docs/cli.md +41 -12
- package/docs/command-map.md +49 -35
- package/docs/engine/agent-contract.md +92 -15
- package/docs/engine/commands.md +242 -72
- package/docs/engine/state-schema.md +52 -23
- package/docs/engine/workspace-schema.md +94 -15
- package/docs/extensions.md +1 -1
- package/docs/flow.md +80 -50
- package/docs/harness-compliance.md +29 -5
- package/docs/orchestration.md +107 -79
- package/docs/quick-reference.md +7 -3
- package/docs/release.md +4 -3
- package/docs/skills.md +64 -38
- package/docs/usage.md +57 -38
- package/engine/cmd/releasepack/main.go +219 -0
- package/engine/cmd/releasepack/main_test.go +170 -0
- package/engine/commands.go +170 -23
- package/engine/git_guard.go +187 -0
- package/engine/git_guard_test.go +283 -0
- package/engine/hookpolicy.go +53 -55
- package/engine/hookpolicy_test.go +91 -1
- package/engine/hooks.go +296 -75
- package/engine/hooks_events_test.go +276 -6
- package/engine/hooks_workspace.go +640 -159
- package/engine/internal/devritespaths/paths.go +65 -10
- package/engine/internal/devritespaths/paths_test.go +110 -0
- package/engine/internal/doctor/doctor.go +153 -23
- package/engine/internal/doctor/doctor_test.go +74 -0
- package/engine/internal/forge/forge.go +940 -0
- package/engine/internal/forge/forge_test.go +576 -0
- package/engine/internal/forge/git.go +245 -0
- package/engine/internal/forge/liveness_unix.go +48 -0
- package/engine/internal/forge/liveness_windows.go +67 -0
- package/engine/internal/forge/manifest.go +402 -0
- package/engine/internal/gate/gate.go +90 -71
- package/engine/internal/gate/gate_test.go +71 -2
- package/engine/internal/harness/compliance.go +18 -24
- package/engine/internal/harness/harness.go +37 -44
- package/engine/internal/harness/harness_test.go +21 -5
- package/engine/internal/install/install.go +486 -38
- package/engine/internal/install/install_test.go +383 -10
- package/engine/internal/iohooks/iohooks.go +350 -59
- package/engine/internal/iohooks/iohooks_test.go +421 -1
- package/engine/internal/lib/buildreadiness.go +35 -18
- package/engine/internal/lib/clarifyreturn.go +91 -0
- package/engine/internal/lib/clarifyreturn_test.go +123 -0
- package/engine/internal/lib/context.go +54 -15
- package/engine/internal/lib/cursor_compat_test.go +8 -0
- package/engine/internal/lib/extensions.go +2 -3
- package/engine/internal/lib/gitauthority.go +601 -0
- package/engine/internal/lib/gitauthority_test.go +346 -0
- package/engine/internal/lib/jsonout.go +25 -11
- package/engine/internal/lib/jsonout_test.go +16 -0
- package/engine/internal/lib/lanes.go +14 -8
- package/engine/internal/lib/observability_test.go +358 -0
- package/engine/internal/lib/packageexistence.go +151 -35
- package/engine/internal/lib/packageexistence_test.go +163 -0
- package/engine/internal/lib/progress.go +11 -9
- package/engine/internal/lib/provenance.go +462 -0
- package/engine/internal/lib/provenance_test.go +154 -0
- package/engine/internal/lib/readiness_contract.json +111 -0
- package/engine/internal/lib/readinessartifact.go +510 -0
- package/engine/internal/lib/readinessartifact_test.go +321 -0
- package/engine/internal/lib/reconcile.go +712 -88
- package/engine/internal/lib/reconcile_test.go +335 -16
- package/engine/internal/lib/recoveryattempts.go +298 -0
- package/engine/internal/lib/recoveryattempts_test.go +215 -0
- package/engine/internal/lib/resolve.go +23 -19
- package/engine/internal/lib/runbook_context_test.go +20 -0
- package/engine/internal/lib/session.go +701 -9
- package/engine/internal/lib/session_test.go +80 -13
- package/engine/internal/lib/testintegrity.go +33 -37
- package/engine/internal/lib/testintegrity_test.go +63 -1
- package/engine/internal/migrate/migrate.go +81 -1
- package/engine/internal/migrate/migrate_test.go +22 -1
- package/engine/internal/reason/reason.go +180 -0
- package/engine/internal/reason/reason_test.go +35 -0
- package/engine/internal/rootfacts/facts.go +466 -0
- package/engine/internal/rootfacts/facts_test.go +306 -0
- package/engine/internal/safepath/safepath.go +55 -0
- package/engine/internal/safepath/safepath_test.go +69 -0
- package/engine/internal/safepath/safepath_windows_test.go +26 -0
- package/engine/internal/state/clarify_transition.go +165 -0
- package/engine/internal/state/clarify_transition_test.go +130 -0
- package/engine/internal/state/cmd/workflowmanifest/main.go +12 -8
- package/engine/internal/state/cursor.go +58 -0
- package/engine/internal/state/cursor_test.go +42 -1
- package/engine/internal/state/feature.go +35 -48
- package/engine/internal/state/schema.go +90 -37
- package/engine/internal/state/snapshot.go +18 -2
- package/engine/internal/state/state_test.go +187 -7
- package/engine/internal/state/status.go +48 -11
- package/engine/internal/state/workflow_manifest.json +231 -50
- package/engine/internal/toolpolicy/classifier.go +533 -0
- package/engine/internal/toolpolicy/classifier_test.go +424 -0
- package/engine/internal/toolpolicy/git.go +616 -0
- package/engine/internal/toolpolicy/scanner.go +382 -0
- package/engine/main.go +160 -77
- package/engine/observability_cli_test.go +52 -0
- package/engine/root_routing_test.go +277 -0
- package/engine/testdata/golden/TestParityBuildReadiness/arg=approved.golden +1 -1
- package/engine/testdata/golden/TestParityBuildReadiness/arg=emptystatus.golden +1 -1
- package/engine/testdata/golden/TestParityBuildReadiness/arg=noclarify.golden +1 -0
- package/engine/testdata/golden/TestParityBuildReadiness/arg=novet.golden +1 -0
- package/engine/testdata/golden/TestParityBuildReadiness/arg=stalevet.golden +1 -0
- package/engine/testdata/golden/TestParityBuildReadiness/arg=trailhash.golden +1 -1
- package/engine/testdata/golden/TestParityBuildReadiness/arg=trailpipe.golden +1 -1
- package/engine/testdata/golden/TestParityBuildReadiness/arg=vetnotready.golden +1 -0
- package/engine/testdata/golden/TestParityProgress/arg=allbuilt.golden +1 -1
- package/engine/testdata/golden/TestParityProgress/arg=done.golden +1 -1
- package/engine/testdata/golden/TestParityProgress/arg=mid.golden +1 -1
- package/engine/testdata/golden/TestParityProgress/arg=nophase.golden +1 -1
- package/engine/testdata/golden/TestParityProgress/arg=noslice.golden +1 -1
- package/engine/testdata/golden/TestParityProgress/arg=plan.golden +1 -1
- package/engine/testdata/golden/TestParityProgress/arg=seal.golden +1 -1
- package/engine/testdata/golden/TestParityReconcile/check-clean.golden +1 -1
- package/engine/testdata/golden/TestParityReconcile/inline-fallback.golden +1 -0
- package/engine/testdata/golden/TestParityReconcile/snapshot-no-allowlist.golden +1 -0
- package/engine/testdata/golden/TestParityWrightScope/devrites-edit-denied.golden +2 -0
- package/engine/testdata/golden/TestParityWrightScope/out-of-scope-enforce-denies.golden +1 -1
- package/engine/tests/adr_0011_define_plan_test.go +30 -0
- package/engine/tests/budget_test.go +19 -8
- package/engine/tests/concurrency_cli_test.go +7 -1
- package/engine/tests/doctor_cli_test.go +148 -2
- package/engine/tests/forge_cli_test.go +463 -0
- package/engine/tests/gate_test.go +63 -8
- package/engine/tests/hook_test.go +260 -8
- package/engine/tests/hooks_io_test.go +94 -3
- package/engine/tests/json_contract_test.go +127 -2
- package/engine/tests/migrate_cli_test.go +2 -2
- package/engine/tests/parity_buildreadiness_test.go +141 -10
- package/engine/tests/parity_learnings_test.go +16 -17
- package/engine/tests/parity_reconcile_test.go +20 -17
- package/engine/tests/parity_resolve_test.go +12 -11
- package/engine/tests/parity_test.go +21 -17
- package/pack/.claude/agents/devrites-code-reviewer.md +62 -48
- package/pack/.claude/agents/devrites-devex-reviewer.md +69 -53
- package/pack/.claude/agents/devrites-doubt-reviewer.md +29 -22
- package/pack/.claude/agents/devrites-evidence-scout.md +69 -0
- package/pack/.claude/agents/devrites-forge-judge.md +74 -61
- package/pack/.claude/agents/devrites-frontend-reviewer.md +48 -39
- package/pack/.claude/agents/devrites-performance-reviewer.md +49 -40
- package/pack/.claude/agents/devrites-plan-drafter.md +71 -0
- package/pack/.claude/agents/devrites-plan-reviewer.md +80 -47
- package/pack/.claude/agents/devrites-proof-runner.md +74 -0
- package/pack/.claude/agents/devrites-retrospector.md +48 -45
- package/pack/.claude/agents/devrites-security-auditor.md +46 -36
- package/pack/.claude/agents/devrites-simplifier-reviewer.md +50 -46
- package/pack/.claude/agents/devrites-slice-wright.md +153 -165
- package/pack/.claude/agents/devrites-spec-reviewer.md +34 -32
- package/pack/.claude/agents/devrites-strategy-reviewer.md +63 -34
- package/pack/.claude/agents/devrites-test-analyst.md +40 -30
- package/pack/.claude/settings.json +2 -1
- package/pack/.claude/skills/devrites-audit/SKILL.md +40 -61
- package/pack/.claude/skills/devrites-debug-recovery/SKILL.md +24 -13
- package/pack/.claude/skills/devrites-debug-recovery/reference/build-the-loop.md +24 -21
- package/pack/.claude/skills/devrites-debug-recovery/reference/cleanup-and-classify.md +34 -6
- package/pack/.claude/skills/devrites-debug-recovery/reference/instrumentation.md +2 -2
- package/pack/.claude/skills/devrites-doubt/SKILL.md +25 -17
- package/pack/.claude/skills/devrites-interview/SKILL.md +53 -52
- package/pack/.claude/skills/devrites-lib/SKILL.md +11 -9
- package/pack/.claude/skills/devrites-lib/reference/intent-map.md +3 -2
- package/pack/.claude/skills/devrites-lib/reference/parallel-dispatch.md +82 -137
- package/pack/.claude/skills/devrites-lib/reference/reply-contract.md +8 -1
- package/pack/.claude/skills/devrites-lib/reference/standards/README.md +38 -55
- package/pack/.claude/skills/devrites-lib/reference/standards/afk-hitl.md +53 -27
- package/pack/.claude/skills/devrites-lib/reference/standards/agents.md +198 -190
- package/pack/.claude/skills/devrites-lib/reference/standards/anti-patterns.md +5 -15
- package/pack/.claude/skills/devrites-lib/reference/standards/ci-cd.md +27 -58
- package/pack/.claude/skills/devrites-lib/reference/standards/code-review.md +16 -52
- package/pack/.claude/skills/devrites-lib/reference/standards/coding-style.md +2 -10
- package/pack/.claude/skills/devrites-lib/reference/standards/context-hygiene.md +18 -61
- package/pack/.claude/skills/devrites-lib/reference/standards/core.md +23 -18
- package/pack/.claude/skills/devrites-lib/reference/standards/deprecation.md +17 -57
- package/pack/.claude/skills/devrites-lib/reference/standards/development-workflow.md +4 -33
- package/pack/.claude/skills/devrites-lib/reference/standards/git-workflow.md +4 -20
- package/pack/.claude/skills/devrites-lib/reference/standards/hooks.md +3 -14
- package/pack/.claude/skills/devrites-lib/reference/standards/patterns.md +9 -25
- package/pack/.claude/skills/devrites-lib/reference/standards/performance.md +0 -9
- package/pack/.claude/skills/devrites-lib/reference/standards/principles.md +1 -3
- package/pack/.claude/skills/devrites-lib/reference/standards/security.md +17 -7
- package/pack/.claude/skills/devrites-lib/reference/standards/testing.md +1 -27
- package/pack/.claude/skills/devrites-lib/reference/standards/tooling.md +46 -50
- package/pack/.claude/skills/devrites-lib/reference/workspace-artifact-schema.md +23 -10
- package/pack/.claude/skills/devrites-source-driven/SKILL.md +23 -22
- package/pack/.claude/skills/rite/SKILL.md +8 -6
- package/pack/.claude/skills/rite/reference/menu.md +8 -6
- package/pack/.claude/skills/rite-adopt/SKILL.md +33 -37
- package/pack/.claude/skills/rite-autocomplete/SKILL.md +33 -28
- package/pack/.claude/skills/rite-autocomplete/reference/loop.md +21 -21
- package/pack/.claude/skills/rite-autocomplete/reference/stop-conditions.md +15 -6
- package/pack/.claude/skills/rite-build/SKILL.md +35 -30
- package/pack/.claude/skills/rite-build/reference/afk-discipline.md +38 -38
- package/pack/.claude/skills/rite-build/reference/evidence-standard.md +10 -0
- package/pack/.claude/skills/rite-build/reference/forge.md +186 -156
- package/pack/.claude/skills/rite-build/reference/one-slice-cycle.md +4 -3
- package/pack/.claude/skills/rite-build/reference/phase-contract.md +96 -175
- package/pack/.claude/skills/rite-build/reference/wright-dispatch.md +129 -134
- package/pack/.claude/skills/rite-clarify/SKILL.md +89 -0
- package/pack/.claude/skills/rite-clarify/reference/decision-coverage.md +59 -0
- package/pack/.claude/skills/rite-converge/SKILL.md +35 -25
- package/pack/.claude/skills/rite-define/SKILL.md +57 -32
- package/pack/.claude/skills/rite-define/reference/gates.md +16 -15
- package/pack/.claude/skills/rite-define/reference/plan-template.md +16 -8
- package/pack/.claude/skills/rite-frame/reference/failure-modes.md +22 -24
- package/pack/.claude/skills/rite-plan/SKILL.md +44 -16
- package/pack/.claude/skills/rite-plan/reference/task-breakdown.md +4 -0
- package/pack/.claude/skills/rite-polish/SKILL.md +27 -23
- package/pack/.claude/skills/rite-prototype/SKILL.md +25 -26
- package/pack/.claude/skills/rite-prove/SKILL.md +27 -17
- package/pack/.claude/skills/rite-resolve/SKILL.md +12 -11
- package/pack/.claude/skills/rite-resolve/reference/answer-protocol.md +3 -0
- package/pack/.claude/skills/rite-review/SKILL.md +37 -28
- package/pack/.claude/skills/rite-seal/reference/phase-contract.md +27 -92
- package/pack/.claude/skills/rite-seal/reference/risk-and-rollback.md +13 -0
- package/pack/.claude/skills/rite-ship/reference/design-memory.md +31 -36
- package/pack/.claude/skills/rite-spec/SKILL.md +84 -135
- package/pack/.claude/skills/rite-spec/reference/investigation.md +37 -33
- package/pack/.claude/skills/rite-spec/reference/question-protocol.md +6 -2
- package/pack/.claude/skills/rite-spec/reference/spec-checklists.md +25 -24
- package/pack/.claude/skills/rite-spec/reference/spec-template.md +9 -2
- package/pack/.claude/skills/rite-spec/reference/state-workspace.md +13 -3
- package/pack/.claude/skills/rite-temper/SKILL.md +62 -47
- package/pack/.claude/skills/rite-temper/reference/review-dimensions.md +24 -22
- package/pack/.claude/skills/rite-vet/SKILL.md +110 -113
- package/pack/.claude/skills/rite-vet/reference/artifacts.md +42 -9
- package/pack/.claude/skills/rite-vet/reference/review-axes.md +39 -37
- package/pack/generated/claude/agents/devrites-code-reviewer.md +62 -48
- package/pack/generated/claude/agents/devrites-devex-reviewer.md +69 -53
- package/pack/generated/claude/agents/devrites-doubt-reviewer.md +29 -22
- package/pack/generated/claude/agents/devrites-evidence-scout.md +69 -0
- package/pack/generated/claude/agents/devrites-forge-judge.md +74 -61
- package/pack/generated/claude/agents/devrites-frontend-reviewer.md +48 -39
- package/pack/generated/claude/agents/devrites-performance-reviewer.md +49 -40
- package/pack/generated/claude/agents/devrites-plan-drafter.md +71 -0
- package/pack/generated/claude/agents/devrites-plan-reviewer.md +80 -47
- package/pack/generated/claude/agents/devrites-proof-runner.md +74 -0
- package/pack/generated/claude/agents/devrites-retrospector.md +48 -45
- package/pack/generated/claude/agents/devrites-security-auditor.md +46 -36
- package/pack/generated/claude/agents/devrites-simplifier-reviewer.md +50 -46
- package/pack/generated/claude/agents/devrites-slice-wright.md +153 -165
- package/pack/generated/claude/agents/devrites-spec-reviewer.md +34 -32
- package/pack/generated/claude/agents/devrites-strategy-reviewer.md +63 -34
- package/pack/generated/claude/agents/devrites-test-analyst.md +40 -30
- package/pack/generated/claude/settings.json +2 -1
- package/pack/generated/claude/skills/devrites-audit/SKILL.md +40 -61
- package/pack/generated/claude/skills/devrites-debug-recovery/SKILL.md +24 -13
- package/pack/generated/claude/skills/devrites-debug-recovery/reference/build-the-loop.md +24 -21
- package/pack/generated/claude/skills/devrites-debug-recovery/reference/cleanup-and-classify.md +34 -6
- package/pack/generated/claude/skills/devrites-debug-recovery/reference/instrumentation.md +2 -2
- package/pack/generated/claude/skills/devrites-doubt/SKILL.md +25 -17
- package/pack/generated/claude/skills/devrites-interview/SKILL.md +53 -52
- package/pack/generated/claude/skills/devrites-lib/SKILL.md +11 -9
- package/pack/generated/claude/skills/devrites-lib/reference/intent-map.md +3 -2
- package/pack/generated/claude/skills/devrites-lib/reference/parallel-dispatch.md +82 -137
- package/pack/generated/claude/skills/devrites-lib/reference/reply-contract.md +8 -1
- package/pack/generated/claude/skills/devrites-lib/reference/standards/README.md +38 -55
- package/pack/generated/claude/skills/devrites-lib/reference/standards/afk-hitl.md +53 -27
- package/pack/generated/claude/skills/devrites-lib/reference/standards/agents.md +198 -190
- package/pack/generated/claude/skills/devrites-lib/reference/standards/anti-patterns.md +5 -15
- package/pack/generated/claude/skills/devrites-lib/reference/standards/ci-cd.md +27 -58
- package/pack/generated/claude/skills/devrites-lib/reference/standards/code-review.md +16 -52
- package/pack/generated/claude/skills/devrites-lib/reference/standards/coding-style.md +2 -10
- package/pack/generated/claude/skills/devrites-lib/reference/standards/context-hygiene.md +18 -61
- package/pack/generated/claude/skills/devrites-lib/reference/standards/core.md +23 -18
- package/pack/generated/claude/skills/devrites-lib/reference/standards/deprecation.md +17 -57
- package/pack/generated/claude/skills/devrites-lib/reference/standards/development-workflow.md +4 -33
- package/pack/generated/claude/skills/devrites-lib/reference/standards/git-workflow.md +4 -20
- package/pack/generated/claude/skills/devrites-lib/reference/standards/hooks.md +3 -14
- package/pack/generated/claude/skills/devrites-lib/reference/standards/patterns.md +9 -25
- package/pack/generated/claude/skills/devrites-lib/reference/standards/performance.md +0 -9
- package/pack/generated/claude/skills/devrites-lib/reference/standards/principles.md +1 -3
- package/pack/generated/claude/skills/devrites-lib/reference/standards/security.md +17 -7
- package/pack/generated/claude/skills/devrites-lib/reference/standards/testing.md +1 -27
- package/pack/generated/claude/skills/devrites-lib/reference/standards/tooling.md +46 -50
- package/pack/generated/claude/skills/devrites-lib/reference/workspace-artifact-schema.md +23 -10
- package/pack/generated/claude/skills/devrites-source-driven/SKILL.md +23 -22
- package/pack/generated/claude/skills/rite/SKILL.md +8 -6
- package/pack/generated/claude/skills/rite/reference/menu.md +8 -6
- package/pack/generated/claude/skills/rite-adopt/SKILL.md +33 -37
- package/pack/generated/claude/skills/rite-autocomplete/SKILL.md +33 -28
- package/pack/generated/claude/skills/rite-autocomplete/reference/loop.md +21 -21
- package/pack/generated/claude/skills/rite-autocomplete/reference/stop-conditions.md +15 -6
- package/pack/generated/claude/skills/rite-build/SKILL.md +35 -30
- package/pack/generated/claude/skills/rite-build/reference/afk-discipline.md +38 -38
- package/pack/generated/claude/skills/rite-build/reference/evidence-standard.md +10 -0
- package/pack/generated/claude/skills/rite-build/reference/forge.md +186 -156
- package/pack/generated/claude/skills/rite-build/reference/one-slice-cycle.md +4 -3
- package/pack/generated/claude/skills/rite-build/reference/phase-contract.md +96 -175
- package/pack/generated/claude/skills/rite-build/reference/wright-dispatch.md +129 -134
- package/pack/generated/claude/skills/rite-clarify/SKILL.md +89 -0
- package/pack/generated/claude/skills/rite-clarify/reference/decision-coverage.md +59 -0
- package/pack/generated/claude/skills/rite-converge/SKILL.md +35 -25
- package/pack/generated/claude/skills/rite-define/SKILL.md +57 -32
- package/pack/generated/claude/skills/rite-define/reference/gates.md +16 -15
- package/pack/generated/claude/skills/rite-define/reference/plan-template.md +16 -8
- package/pack/generated/claude/skills/rite-frame/reference/failure-modes.md +22 -24
- package/pack/generated/claude/skills/rite-plan/SKILL.md +44 -16
- package/pack/generated/claude/skills/rite-plan/reference/task-breakdown.md +4 -0
- package/pack/generated/claude/skills/rite-polish/SKILL.md +27 -23
- package/pack/generated/claude/skills/rite-prototype/SKILL.md +25 -26
- package/pack/generated/claude/skills/rite-prove/SKILL.md +27 -17
- package/pack/generated/claude/skills/rite-resolve/SKILL.md +12 -11
- package/pack/generated/claude/skills/rite-resolve/reference/answer-protocol.md +3 -0
- package/pack/generated/claude/skills/rite-review/SKILL.md +37 -28
- package/pack/generated/claude/skills/rite-seal/reference/phase-contract.md +27 -92
- package/pack/generated/claude/skills/rite-seal/reference/risk-and-rollback.md +13 -0
- package/pack/generated/claude/skills/rite-ship/reference/design-memory.md +31 -36
- package/pack/generated/claude/skills/rite-spec/SKILL.md +84 -135
- package/pack/generated/claude/skills/rite-spec/reference/investigation.md +37 -33
- package/pack/generated/claude/skills/rite-spec/reference/question-protocol.md +6 -2
- package/pack/generated/claude/skills/rite-spec/reference/spec-checklists.md +25 -24
- package/pack/generated/claude/skills/rite-spec/reference/spec-template.md +9 -2
- package/pack/generated/claude/skills/rite-spec/reference/state-workspace.md +13 -3
- package/pack/generated/claude/skills/rite-temper/SKILL.md +62 -47
- package/pack/generated/claude/skills/rite-temper/reference/review-dimensions.md +24 -22
- package/pack/generated/claude/skills/rite-vet/SKILL.md +110 -113
- package/pack/generated/claude/skills/rite-vet/reference/artifacts.md +42 -9
- package/pack/generated/claude/skills/rite-vet/reference/review-axes.md +39 -37
- package/pack/generated/codex/AGENTS.md +5 -2
- package/pack/generated/codex/agents/devrites-code-reviewer.toml +69 -47
- package/pack/generated/codex/agents/devrites-devex-reviewer.toml +75 -51
- package/pack/generated/codex/agents/devrites-doubt-reviewer.toml +35 -20
- package/pack/generated/codex/agents/devrites-evidence-scout.toml +75 -0
- package/pack/generated/codex/agents/devrites-forge-judge.toml +80 -59
- package/pack/generated/codex/agents/devrites-frontend-reviewer.toml +54 -37
- package/pack/generated/codex/agents/devrites-performance-reviewer.toml +55 -38
- package/pack/generated/codex/agents/devrites-plan-drafter.toml +77 -0
- package/pack/generated/codex/agents/devrites-plan-reviewer.toml +82 -47
- package/pack/generated/codex/agents/devrites-proof-runner.toml +80 -0
- package/pack/generated/codex/agents/devrites-retrospector.toml +54 -43
- package/pack/generated/codex/agents/devrites-security-auditor.toml +53 -35
- package/pack/generated/codex/agents/devrites-simplifier-reviewer.toml +56 -44
- package/pack/generated/codex/agents/devrites-slice-wright.toml +159 -163
- package/pack/generated/codex/agents/devrites-spec-reviewer.toml +40 -30
- package/pack/generated/codex/agents/devrites-strategy-reviewer.toml +65 -34
- package/pack/generated/codex/agents/devrites-test-analyst.toml +46 -28
- package/pack/generated/codex/hooks.json +4 -14
- package/pack/generated/codex/skills/devrites-api-interface/SKILL.md +7 -3
- package/pack/generated/codex/skills/devrites-audit/SKILL.md +47 -64
- package/pack/generated/codex/skills/devrites-browser-proof/SKILL.md +7 -3
- package/pack/generated/codex/skills/devrites-debug-recovery/SKILL.md +31 -16
- package/pack/generated/codex/skills/devrites-debug-recovery/reference/build-the-loop.md +24 -21
- package/pack/generated/codex/skills/devrites-debug-recovery/reference/cleanup-and-classify.md +34 -6
- package/pack/generated/codex/skills/devrites-debug-recovery/reference/instrumentation.md +2 -2
- package/pack/generated/codex/skills/devrites-doubt/SKILL.md +32 -20
- package/pack/generated/codex/skills/devrites-frontend-craft/SKILL.md +7 -3
- package/pack/generated/codex/skills/devrites-interview/SKILL.md +60 -55
- package/pack/generated/codex/skills/devrites-lib/SKILL.md +18 -12
- package/pack/generated/codex/skills/devrites-lib/reference/intent-map.md +3 -2
- package/pack/generated/codex/skills/devrites-lib/reference/parallel-dispatch.md +82 -137
- package/pack/generated/codex/skills/devrites-lib/reference/reply-contract.md +8 -1
- package/pack/generated/codex/skills/devrites-lib/reference/standards/README.md +38 -55
- package/pack/generated/codex/skills/devrites-lib/reference/standards/afk-hitl.md +53 -27
- package/pack/generated/codex/skills/devrites-lib/reference/standards/agents.md +198 -190
- package/pack/generated/codex/skills/devrites-lib/reference/standards/anti-patterns.md +5 -15
- package/pack/generated/codex/skills/devrites-lib/reference/standards/ci-cd.md +27 -58
- package/pack/generated/codex/skills/devrites-lib/reference/standards/code-review.md +16 -52
- package/pack/generated/codex/skills/devrites-lib/reference/standards/coding-style.md +2 -10
- package/pack/generated/codex/skills/devrites-lib/reference/standards/context-hygiene.md +18 -61
- package/pack/generated/codex/skills/devrites-lib/reference/standards/core.md +23 -18
- package/pack/generated/codex/skills/devrites-lib/reference/standards/deprecation.md +17 -57
- package/pack/generated/codex/skills/devrites-lib/reference/standards/development-workflow.md +4 -33
- package/pack/generated/codex/skills/devrites-lib/reference/standards/git-workflow.md +4 -20
- package/pack/generated/codex/skills/devrites-lib/reference/standards/hooks.md +3 -14
- package/pack/generated/codex/skills/devrites-lib/reference/standards/patterns.md +9 -25
- package/pack/generated/codex/skills/devrites-lib/reference/standards/performance.md +0 -9
- package/pack/generated/codex/skills/devrites-lib/reference/standards/principles.md +1 -3
- package/pack/generated/codex/skills/devrites-lib/reference/standards/security.md +17 -7
- package/pack/generated/codex/skills/devrites-lib/reference/standards/testing.md +1 -27
- package/pack/generated/codex/skills/devrites-lib/reference/standards/tooling.md +46 -50
- package/pack/generated/codex/skills/devrites-lib/reference/workspace-artifact-schema.md +23 -10
- package/pack/generated/codex/skills/devrites-prose-craft/SKILL.md +7 -3
- package/pack/generated/codex/skills/devrites-refresh-indexes/SKILL.md +7 -3
- package/pack/generated/codex/skills/devrites-source-driven/SKILL.md +29 -24
- package/pack/generated/codex/skills/devrites-ux-shape/SKILL.md +7 -3
- package/pack/generated/codex/skills/rite/SKILL.md +19 -13
- package/pack/generated/codex/skills/rite/reference/menu.md +8 -6
- package/pack/generated/codex/skills/rite-adopt/SKILL.md +40 -40
- package/pack/generated/codex/skills/rite-autocomplete/SKILL.md +40 -31
- package/pack/generated/codex/skills/rite-autocomplete/reference/loop.md +21 -21
- package/pack/generated/codex/skills/rite-autocomplete/reference/stop-conditions.md +15 -6
- package/pack/generated/codex/skills/rite-build/SKILL.md +42 -33
- package/pack/generated/codex/skills/rite-build/reference/afk-discipline.md +38 -38
- package/pack/generated/codex/skills/rite-build/reference/evidence-standard.md +10 -0
- package/pack/generated/codex/skills/rite-build/reference/forge.md +186 -156
- package/pack/generated/codex/skills/rite-build/reference/one-slice-cycle.md +4 -3
- package/pack/generated/codex/skills/rite-build/reference/phase-contract.md +96 -175
- package/pack/generated/codex/skills/rite-build/reference/wright-dispatch.md +129 -134
- package/pack/generated/codex/skills/rite-clarify/SKILL.md +105 -0
- package/pack/generated/codex/skills/rite-clarify/reference/decision-coverage.md +59 -0
- package/pack/generated/codex/skills/rite-converge/SKILL.md +42 -28
- package/pack/generated/codex/skills/rite-customize/SKILL.md +7 -3
- package/pack/generated/codex/skills/rite-define/SKILL.md +64 -35
- package/pack/generated/codex/skills/rite-define/reference/gates.md +16 -15
- package/pack/generated/codex/skills/rite-define/reference/plan-template.md +16 -8
- package/pack/generated/codex/skills/rite-doctor/SKILL.md +7 -3
- package/pack/generated/codex/skills/rite-dogfood/SKILL.md +7 -3
- package/pack/generated/codex/skills/rite-explain/SKILL.md +7 -3
- package/pack/generated/codex/skills/rite-frame/SKILL.md +7 -3
- package/pack/generated/codex/skills/rite-frame/reference/failure-modes.md +22 -24
- package/pack/generated/codex/skills/rite-handoff/SKILL.md +7 -3
- package/pack/generated/codex/skills/rite-learn/SKILL.md +7 -3
- package/pack/generated/codex/skills/rite-plan/SKILL.md +51 -19
- package/pack/generated/codex/skills/rite-plan/reference/task-breakdown.md +5 -1
- package/pack/generated/codex/skills/rite-polish/SKILL.md +34 -26
- package/pack/generated/codex/skills/rite-pov/SKILL.md +7 -3
- package/pack/generated/codex/skills/rite-pr-feedback/SKILL.md +7 -3
- package/pack/generated/codex/skills/rite-pressure-test/SKILL.md +7 -3
- package/pack/generated/codex/skills/rite-prototype/SKILL.md +32 -29
- package/pack/generated/codex/skills/rite-prove/SKILL.md +34 -20
- package/pack/generated/codex/skills/rite-quick/SKILL.md +7 -3
- package/pack/generated/codex/skills/rite-resolve/SKILL.md +19 -14
- package/pack/generated/codex/skills/rite-resolve/reference/answer-protocol.md +3 -0
- package/pack/generated/codex/skills/rite-review/SKILL.md +44 -31
- package/pack/generated/codex/skills/rite-seal/SKILL.md +7 -3
- package/pack/generated/codex/skills/rite-seal/reference/phase-contract.md +27 -92
- package/pack/generated/codex/skills/rite-seal/reference/risk-and-rollback.md +13 -0
- package/pack/generated/codex/skills/rite-ship/SKILL.md +7 -3
- package/pack/generated/codex/skills/rite-ship/reference/design-memory.md +31 -36
- package/pack/generated/codex/skills/rite-spec/SKILL.md +91 -138
- package/pack/generated/codex/skills/rite-spec/reference/investigation.md +37 -33
- package/pack/generated/codex/skills/rite-spec/reference/question-protocol.md +6 -2
- package/pack/generated/codex/skills/rite-spec/reference/spec-checklists.md +25 -24
- package/pack/generated/codex/skills/rite-spec/reference/spec-template.md +9 -2
- package/pack/generated/codex/skills/rite-spec/reference/state-workspace.md +13 -3
- package/pack/generated/codex/skills/rite-status/SKILL.md +7 -3
- package/pack/generated/codex/skills/rite-temper/SKILL.md +69 -50
- package/pack/generated/codex/skills/rite-temper/reference/review-dimensions.md +24 -22
- package/pack/generated/codex/skills/rite-vet/SKILL.md +117 -116
- package/pack/generated/codex/skills/rite-vet/reference/artifacts.md +42 -9
- package/pack/generated/codex/skills/rite-vet/reference/review-axes.md +40 -38
- package/pack/generated/codex/skills/rite-zoom-out/SKILL.md +7 -3
- package/package.json +1 -1
- package/scripts/build-release-tarball.sh +32 -15
- package/scripts/check-authority-drift.py +125 -0
- package/scripts/check-instruction-size-baseline.mjs +19 -11
- package/scripts/check-invocation-integrity.py +2 -0
- package/scripts/codex-generate.sh +69 -33
- package/scripts/grade-feature.sh +121 -40
- package/scripts/live-hosts/agent-result.schema.json +230 -0
- package/scripts/live-hosts/claude.sh +87 -0
- package/scripts/live-hosts/codex.sh +81 -0
- package/scripts/live-hosts/common.sh +113 -0
- package/scripts/live-hosts/fake-host.py +264 -0
- package/scripts/live-hosts/host-transport.py +287 -0
- package/scripts/release-check.sh +5 -1
- package/scripts/run-agent-contract-evals.py +1380 -0
- package/scripts/run-behavioral-evals.sh +24 -30
- package/scripts/run-evals.sh +1 -5
- package/scripts/run-live-behavioral-evals.py +1274 -144
- package/scripts/run-outcome-evals.sh +414 -88
- package/scripts/run-tests.mjs +30 -2
- package/scripts/skills-inventory.mjs +1 -1
- package/scripts/validate-workflow-security.py +39 -20
- package/scripts/validate-workspace-schema.py +362 -10
- package/scripts/validate.sh +21 -15
- package/engine/testdata/golden/TestParityWrightScope/devrites-edit-allowed.golden +0 -1
- /package/engine/testdata/golden/{TestParityReconcile/check-no-claimed.golden → TestParityBuildReadiness/arg=clarifyopen.golden} +0 -0
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
package forge
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"crypto/rand"
|
|
5
|
+
"encoding/hex"
|
|
6
|
+
"encoding/json"
|
|
7
|
+
"errors"
|
|
8
|
+
"fmt"
|
|
9
|
+
"io/fs"
|
|
10
|
+
"os"
|
|
11
|
+
"path/filepath"
|
|
12
|
+
"regexp"
|
|
13
|
+
"sort"
|
|
14
|
+
"strings"
|
|
15
|
+
"time"
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
const Schema = "devrites-forge/v1"
|
|
19
|
+
|
|
20
|
+
var (
|
|
21
|
+
safeSlug = regexp.MustCompile(`^[a-z0-9][a-z0-9._-]{0,63}$`)
|
|
22
|
+
safeRunID = regexp.MustCompile(`^forge-[a-f0-9]{24}$`)
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
type CandidateID string
|
|
26
|
+
|
|
27
|
+
const (
|
|
28
|
+
CandidateA CandidateID = "A"
|
|
29
|
+
CandidateB CandidateID = "B"
|
|
30
|
+
CandidateC CandidateID = "C"
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
type CandidateState string
|
|
34
|
+
|
|
35
|
+
const (
|
|
36
|
+
StatePlanned CandidateState = "planned"
|
|
37
|
+
StateCreated CandidateState = "created"
|
|
38
|
+
StateRunning CandidateState = "running"
|
|
39
|
+
StateFinished CandidateState = "finished"
|
|
40
|
+
StateFailed CandidateState = "failed"
|
|
41
|
+
StateExtracted CandidateState = "extracted"
|
|
42
|
+
StateMerged CandidateState = "merged"
|
|
43
|
+
StateCleaned CandidateState = "cleaned"
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
type Primary struct {
|
|
47
|
+
Branch string `json:"branch"`
|
|
48
|
+
BaseCommit string `json:"base_commit"`
|
|
49
|
+
BaselineTree string `json:"baseline_tree"`
|
|
50
|
+
BaselineSHA256 string `json:"baseline_sha256"`
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
type Worker struct {
|
|
54
|
+
ID string `json:"id,omitempty"`
|
|
55
|
+
PID int `json:"pid,omitempty"`
|
|
56
|
+
ProcessStart string `json:"process_start,omitempty"`
|
|
57
|
+
StartedAt string `json:"started_at,omitempty"`
|
|
58
|
+
FinishedAt string `json:"finished_at,omitempty"`
|
|
59
|
+
Result string `json:"result,omitempty"`
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
type Candidate struct {
|
|
63
|
+
ID CandidateID `json:"id"`
|
|
64
|
+
Strategy string `json:"strategy"`
|
|
65
|
+
Worktree string `json:"worktree"`
|
|
66
|
+
Branch string `json:"branch"`
|
|
67
|
+
InitialBase string `json:"initial_base"`
|
|
68
|
+
State CandidateState `json:"state"`
|
|
69
|
+
Worker Worker `json:"worker"`
|
|
70
|
+
Commit string `json:"commit,omitempty"`
|
|
71
|
+
Tree string `json:"tree,omitempty"`
|
|
72
|
+
DeltaSHA256 string `json:"delta_sha256,omitempty"`
|
|
73
|
+
Preservation string `json:"preservation,omitempty"`
|
|
74
|
+
LastTransition string `json:"last_transition"`
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
type Winner struct {
|
|
78
|
+
Candidate CandidateID `json:"candidate,omitempty"`
|
|
79
|
+
RecordedBy string `json:"recorded_by,omitempty"`
|
|
80
|
+
RecordedAt string `json:"recorded_at,omitempty"`
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
type MergeState struct {
|
|
84
|
+
State string `json:"state,omitempty"`
|
|
85
|
+
Commit string `json:"commit,omitempty"`
|
|
86
|
+
Tree string `json:"tree,omitempty"`
|
|
87
|
+
RecordedAt string `json:"recorded_at,omitempty"`
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
type VerificationState struct {
|
|
91
|
+
State string `json:"state,omitempty"`
|
|
92
|
+
RecordedBy string `json:"recorded_by,omitempty"`
|
|
93
|
+
RecordedAt string `json:"recorded_at,omitempty"`
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
type CleanupState struct {
|
|
97
|
+
State string `json:"state,omitempty"`
|
|
98
|
+
Preserved map[string]string `json:"preserved,omitempty"`
|
|
99
|
+
RecordedAt string `json:"recorded_at,omitempty"`
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
type Manifest struct {
|
|
103
|
+
Schema string `json:"schema"`
|
|
104
|
+
RunID string `json:"run_id"`
|
|
105
|
+
CreationNonce string `json:"creation_nonce"`
|
|
106
|
+
CreatedAt string `json:"created_at"`
|
|
107
|
+
FeatureSlug string `json:"feature_slug"`
|
|
108
|
+
SliceID string `json:"slice_id"`
|
|
109
|
+
AcceptanceHash string `json:"acceptance_hash"`
|
|
110
|
+
TestPlanHash string `json:"test_plan_hash"`
|
|
111
|
+
PrimaryRoot string `json:"primary_root"`
|
|
112
|
+
GitCommonDir string `json:"git_common_dir"`
|
|
113
|
+
ForgeRoot string `json:"forge_root"`
|
|
114
|
+
Primary Primary `json:"primary"`
|
|
115
|
+
Candidates []Candidate `json:"candidates"`
|
|
116
|
+
Winner Winner `json:"winner"`
|
|
117
|
+
Merge MergeState `json:"merge"`
|
|
118
|
+
Verification VerificationState `json:"verification"`
|
|
119
|
+
Cleanup CleanupState `json:"cleanup"`
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
func (m *Manifest) Candidate(id CandidateID) (*Candidate, error) {
|
|
123
|
+
for i := range m.Candidates {
|
|
124
|
+
if m.Candidates[i].ID == id {
|
|
125
|
+
return &m.Candidates[i], nil
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return nil, fmt.Errorf("forge: candidate %q is not in manifest", id)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
func ManifestPath(root, slug, runID string) (string, error) {
|
|
132
|
+
if !safeSlug.MatchString(slug) {
|
|
133
|
+
return "", fmt.Errorf("forge: invalid feature slug %q", slug)
|
|
134
|
+
}
|
|
135
|
+
if !safeRunID.MatchString(runID) {
|
|
136
|
+
return "", fmt.Errorf("forge: invalid run ID %q", runID)
|
|
137
|
+
}
|
|
138
|
+
root, err := physicalExisting(root)
|
|
139
|
+
if err != nil {
|
|
140
|
+
return "", err
|
|
141
|
+
}
|
|
142
|
+
return filepath.Join(root, ".devrites", "work", slug, ".forge", runID, "manifest.json"), nil
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Load returns the unique manifest for runID below root. Duplicate manifests
|
|
146
|
+
// fail closed instead of letting a caller choose which ownership record wins.
|
|
147
|
+
func Load(root, runID string) (*Manifest, string, error) {
|
|
148
|
+
if !safeRunID.MatchString(runID) {
|
|
149
|
+
return nil, "", fmt.Errorf("forge: invalid run ID %q", runID)
|
|
150
|
+
}
|
|
151
|
+
root, err := physicalExisting(root)
|
|
152
|
+
if err != nil {
|
|
153
|
+
return nil, "", err
|
|
154
|
+
}
|
|
155
|
+
paths, err := manifestPaths(root, "")
|
|
156
|
+
if err != nil {
|
|
157
|
+
return nil, "", err
|
|
158
|
+
}
|
|
159
|
+
var matches []string
|
|
160
|
+
for _, path := range paths {
|
|
161
|
+
if filepath.Base(filepath.Dir(path)) == runID {
|
|
162
|
+
matches = append(matches, path)
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
if len(matches) != 1 {
|
|
166
|
+
return nil, "", fmt.Errorf("forge: run %s has %d manifests; want exactly one", runID, len(matches))
|
|
167
|
+
}
|
|
168
|
+
m, err := loadAt(root, matches[0])
|
|
169
|
+
if err != nil {
|
|
170
|
+
return nil, "", err
|
|
171
|
+
}
|
|
172
|
+
return m, matches[0], nil
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
func loadAt(root, path string) (*Manifest, error) {
|
|
176
|
+
if err := validateNoSymlink(root, path); err != nil {
|
|
177
|
+
return nil, err
|
|
178
|
+
}
|
|
179
|
+
info, err := os.Lstat(path)
|
|
180
|
+
if err != nil {
|
|
181
|
+
return nil, fmt.Errorf("forge: stat manifest: %w", err)
|
|
182
|
+
}
|
|
183
|
+
if !info.Mode().IsRegular() {
|
|
184
|
+
return nil, fmt.Errorf("forge: manifest is not a regular file: %s", path)
|
|
185
|
+
}
|
|
186
|
+
raw, err := os.ReadFile(path)
|
|
187
|
+
if err != nil {
|
|
188
|
+
return nil, fmt.Errorf("forge: read manifest: %w", err)
|
|
189
|
+
}
|
|
190
|
+
var m Manifest
|
|
191
|
+
if err := json.Unmarshal(raw, &m); err != nil {
|
|
192
|
+
return nil, fmt.Errorf("forge: decode manifest: %w", err)
|
|
193
|
+
}
|
|
194
|
+
if err := validateManifest(root, path, &m); err != nil {
|
|
195
|
+
return nil, err
|
|
196
|
+
}
|
|
197
|
+
return &m, nil
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
func validateManifest(root, path string, m *Manifest) error {
|
|
201
|
+
if m.Schema != Schema || !safeRunID.MatchString(m.RunID) || !safeSlug.MatchString(m.FeatureSlug) {
|
|
202
|
+
return errors.New("forge: invalid manifest identity")
|
|
203
|
+
}
|
|
204
|
+
wantPath, _ := ManifestPath(root, m.FeatureSlug, m.RunID)
|
|
205
|
+
if filepath.Clean(path) != wantPath {
|
|
206
|
+
return fmt.Errorf("forge: manifest path mismatch: got %s want %s", path, wantPath)
|
|
207
|
+
}
|
|
208
|
+
if m.PrimaryRoot != root {
|
|
209
|
+
return fmt.Errorf("forge: primary root mismatch: got %s want %s", m.PrimaryRoot, root)
|
|
210
|
+
}
|
|
211
|
+
wantForge := forgeRoot(root, m.RunID)
|
|
212
|
+
if m.ForgeRoot != wantForge {
|
|
213
|
+
return fmt.Errorf("forge: staging root mismatch: got %s want %s", m.ForgeRoot, wantForge)
|
|
214
|
+
}
|
|
215
|
+
if len(m.Candidates) < 2 || len(m.Candidates) > 3 {
|
|
216
|
+
return fmt.Errorf("forge: manifest has %d candidates; want 2 or 3", len(m.Candidates))
|
|
217
|
+
}
|
|
218
|
+
seen := map[CandidateID]bool{}
|
|
219
|
+
for _, c := range m.Candidates {
|
|
220
|
+
if seen[c.ID] || !validCandidateID(c.ID) {
|
|
221
|
+
return fmt.Errorf("forge: invalid or duplicate candidate %q", c.ID)
|
|
222
|
+
}
|
|
223
|
+
seen[c.ID] = true
|
|
224
|
+
wantWorktree := filepath.Join(wantForge, "candidate-"+strings.ToLower(string(c.ID)))
|
|
225
|
+
wantBranch := candidateBranch(m.RunID, c.ID)
|
|
226
|
+
if c.Worktree != wantWorktree || c.Branch != wantBranch || c.InitialBase != m.Primary.BaseCommit {
|
|
227
|
+
return fmt.Errorf("forge: candidate %s ownership fields do not match derived values", c.ID)
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return nil
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
func save(path string, m *Manifest) error {
|
|
234
|
+
root := m.PrimaryRoot
|
|
235
|
+
if err := validateManifest(root, path, m); err != nil {
|
|
236
|
+
return err
|
|
237
|
+
}
|
|
238
|
+
if err := validateNoSymlink(root, filepath.Dir(path)); err != nil && !errors.Is(err, fs.ErrNotExist) {
|
|
239
|
+
return err
|
|
240
|
+
}
|
|
241
|
+
if err := os.MkdirAll(filepath.Dir(path), 0o700); err != nil {
|
|
242
|
+
return fmt.Errorf("forge: create manifest directory: %w", err)
|
|
243
|
+
}
|
|
244
|
+
if err := validateNoSymlink(root, filepath.Dir(path)); err != nil {
|
|
245
|
+
return err
|
|
246
|
+
}
|
|
247
|
+
if info, err := os.Lstat(path); err == nil && !info.Mode().IsRegular() {
|
|
248
|
+
return fmt.Errorf("forge: refusing to replace non-regular manifest %s", path)
|
|
249
|
+
} else if err != nil && !errors.Is(err, fs.ErrNotExist) {
|
|
250
|
+
return fmt.Errorf("forge: stat manifest: %w", err)
|
|
251
|
+
}
|
|
252
|
+
raw, err := json.MarshalIndent(m, "", " ")
|
|
253
|
+
if err != nil {
|
|
254
|
+
return fmt.Errorf("forge: encode manifest: %w", err)
|
|
255
|
+
}
|
|
256
|
+
raw = append(raw, '\n')
|
|
257
|
+
tmp, err := os.CreateTemp(filepath.Dir(path), ".manifest-*.tmp")
|
|
258
|
+
if err != nil {
|
|
259
|
+
return fmt.Errorf("forge: create manifest temp file: %w", err)
|
|
260
|
+
}
|
|
261
|
+
name := tmp.Name()
|
|
262
|
+
defer os.Remove(name)
|
|
263
|
+
if err := tmp.Chmod(0o600); err != nil {
|
|
264
|
+
_ = tmp.Close()
|
|
265
|
+
return fmt.Errorf("forge: chmod manifest temp file: %w", err)
|
|
266
|
+
}
|
|
267
|
+
if _, err := tmp.Write(raw); err != nil {
|
|
268
|
+
_ = tmp.Close()
|
|
269
|
+
return fmt.Errorf("forge: write manifest: %w", err)
|
|
270
|
+
}
|
|
271
|
+
if err := tmp.Sync(); err != nil {
|
|
272
|
+
_ = tmp.Close()
|
|
273
|
+
return fmt.Errorf("forge: sync manifest: %w", err)
|
|
274
|
+
}
|
|
275
|
+
if err := tmp.Close(); err != nil {
|
|
276
|
+
return fmt.Errorf("forge: close manifest: %w", err)
|
|
277
|
+
}
|
|
278
|
+
if err := os.Rename(name, path); err != nil {
|
|
279
|
+
return fmt.Errorf("forge: replace manifest: %w", err)
|
|
280
|
+
}
|
|
281
|
+
return nil
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
func manifestPaths(root, slug string) ([]string, error) {
|
|
285
|
+
base := filepath.Join(root, ".devrites", "work")
|
|
286
|
+
if err := validateNoSymlink(root, base); err != nil {
|
|
287
|
+
if errors.Is(err, fs.ErrNotExist) {
|
|
288
|
+
return nil, nil
|
|
289
|
+
}
|
|
290
|
+
return nil, err
|
|
291
|
+
}
|
|
292
|
+
var paths []string
|
|
293
|
+
err := filepath.WalkDir(base, func(path string, d fs.DirEntry, err error) error {
|
|
294
|
+
if errors.Is(err, fs.ErrNotExist) {
|
|
295
|
+
return nil
|
|
296
|
+
}
|
|
297
|
+
if err != nil {
|
|
298
|
+
return err
|
|
299
|
+
}
|
|
300
|
+
if d.Type()&os.ModeSymlink != 0 {
|
|
301
|
+
if d.IsDir() {
|
|
302
|
+
return filepath.SkipDir
|
|
303
|
+
}
|
|
304
|
+
return nil
|
|
305
|
+
}
|
|
306
|
+
if d.IsDir() {
|
|
307
|
+
return nil
|
|
308
|
+
}
|
|
309
|
+
rel, err := filepath.Rel(base, path)
|
|
310
|
+
if err != nil {
|
|
311
|
+
return err
|
|
312
|
+
}
|
|
313
|
+
parts := strings.Split(filepath.ToSlash(rel), "/")
|
|
314
|
+
if len(parts) == 4 && parts[1] == ".forge" && parts[3] == "manifest.json" && safeSlug.MatchString(parts[0]) && safeRunID.MatchString(parts[2]) {
|
|
315
|
+
if slug == "" || parts[0] == slug {
|
|
316
|
+
paths = append(paths, path)
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
return nil
|
|
320
|
+
})
|
|
321
|
+
if errors.Is(err, fs.ErrNotExist) {
|
|
322
|
+
return nil, nil
|
|
323
|
+
}
|
|
324
|
+
sort.Strings(paths)
|
|
325
|
+
return paths, err
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
func validatedManifestPaths(root string) ([]string, error) {
|
|
329
|
+
paths, err := manifestPaths(root, "")
|
|
330
|
+
if err != nil {
|
|
331
|
+
return nil, err
|
|
332
|
+
}
|
|
333
|
+
for _, path := range paths {
|
|
334
|
+
if _, err := loadAt(root, path); err != nil {
|
|
335
|
+
return nil, err
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
return paths, nil
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
func newIdentity() (runID, nonce string, err error) {
|
|
342
|
+
raw := make([]byte, 28)
|
|
343
|
+
if _, err := rand.Read(raw); err != nil {
|
|
344
|
+
return "", "", fmt.Errorf("forge: generate identity: %w", err)
|
|
345
|
+
}
|
|
346
|
+
return "forge-" + hex.EncodeToString(raw[:12]), hex.EncodeToString(raw[12:]), nil
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
func forgeRoot(root, runID string) string {
|
|
350
|
+
return filepath.Join(filepath.Dir(root), "."+filepath.Base(root)+".devrites-forge", runID)
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
func candidateBranch(runID string, id CandidateID) string {
|
|
354
|
+
return "devrites/forge/" + runID + "/" + strings.ToLower(string(id))
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
func validCandidateID(id CandidateID) bool {
|
|
358
|
+
return id == CandidateA || id == CandidateB || id == CandidateC
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
func now() string {
|
|
362
|
+
return time.Now().UTC().Format(time.RFC3339Nano)
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
func physicalExisting(path string) (string, error) {
|
|
366
|
+
abs, err := filepath.Abs(path)
|
|
367
|
+
if err != nil {
|
|
368
|
+
return "", fmt.Errorf("forge: resolve path: %w", err)
|
|
369
|
+
}
|
|
370
|
+
real, err := filepath.EvalSymlinks(abs)
|
|
371
|
+
if err != nil {
|
|
372
|
+
return "", fmt.Errorf("forge: resolve physical path %s: %w", abs, err)
|
|
373
|
+
}
|
|
374
|
+
return filepath.Clean(real), nil
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
func validateNoSymlink(root, path string) error {
|
|
378
|
+
root = filepath.Clean(root)
|
|
379
|
+
path = filepath.Clean(path)
|
|
380
|
+
rel, err := filepath.Rel(root, path)
|
|
381
|
+
if err != nil || rel == ".." || strings.HasPrefix(rel, ".."+string(filepath.Separator)) {
|
|
382
|
+
return fmt.Errorf("forge: path escapes primary root: %s", path)
|
|
383
|
+
}
|
|
384
|
+
current := root
|
|
385
|
+
if rel == "." {
|
|
386
|
+
return nil
|
|
387
|
+
}
|
|
388
|
+
for _, part := range strings.Split(rel, string(filepath.Separator)) {
|
|
389
|
+
current = filepath.Join(current, part)
|
|
390
|
+
info, err := os.Lstat(current)
|
|
391
|
+
if errors.Is(err, fs.ErrNotExist) {
|
|
392
|
+
return fs.ErrNotExist
|
|
393
|
+
}
|
|
394
|
+
if err != nil {
|
|
395
|
+
return fmt.Errorf("forge: inspect path %s: %w", current, err)
|
|
396
|
+
}
|
|
397
|
+
if info.Mode()&os.ModeSymlink != 0 {
|
|
398
|
+
return fmt.Errorf("forge: symlink is not allowed in manifest path: %s", current)
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
return nil
|
|
402
|
+
}
|
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
// Package gate implements
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
// run: never on every tool call. A block is a HITL pause (a structured
|
|
6
|
-
// "missing X" the human resolves and retries), never a crash. Judgment gates
|
|
7
|
-
// stay advisory; these deterministic gates are the ones allowed to block.
|
|
1
|
+
// Package gate implements deterministic completeness checks. Each gate checks
|
|
2
|
+
// only the files required for its transition when the command runs. Missing
|
|
3
|
+
// content returns a human-resolvable block instead of an error. Judgment gates
|
|
4
|
+
// remain advisory; only deterministic checks can block.
|
|
8
5
|
package gate
|
|
9
6
|
|
|
10
7
|
import (
|
|
@@ -15,6 +12,7 @@ import (
|
|
|
15
12
|
|
|
16
13
|
"github.com/devrites/devrites/internal/devritespaths"
|
|
17
14
|
"github.com/devrites/devrites/internal/orient"
|
|
15
|
+
"github.com/devrites/devrites/internal/reason"
|
|
18
16
|
"github.com/devrites/devrites/internal/state"
|
|
19
17
|
)
|
|
20
18
|
|
|
@@ -33,19 +31,20 @@ const (
|
|
|
33
31
|
// Result is a gate outcome. Blocked is true iff a required section is missing;
|
|
34
32
|
// Missing lists them in canonical order for an actionable message.
|
|
35
33
|
type Result struct {
|
|
36
|
-
Kind
|
|
37
|
-
Slug
|
|
38
|
-
Phase
|
|
39
|
-
Target
|
|
40
|
-
Missing
|
|
41
|
-
|
|
34
|
+
Kind Kind
|
|
35
|
+
Slug string
|
|
36
|
+
Phase state.Phase
|
|
37
|
+
Target state.Phase // the phase whose requirements were checked
|
|
38
|
+
Missing []state.Section // compact legacy view
|
|
39
|
+
MissingFiles []string // authoritative per-file view
|
|
40
|
+
LegacyLayout bool
|
|
41
|
+
Blocked bool
|
|
42
|
+
ReasonID reason.ID
|
|
42
43
|
}
|
|
43
44
|
|
|
44
|
-
// Check runs a gate against feature <slug> under root
|
|
45
|
-
// directly
|
|
46
|
-
//
|
|
47
|
-
// (unknown slug, unreadable state): a legitimately incomplete feature is a
|
|
48
|
-
// Result with Blocked set, not an error.
|
|
45
|
+
// Check runs a gate against feature <slug> under root. It reads workspace files
|
|
46
|
+
// directly instead of trusting a cache. Invalid requests and unreadable state
|
|
47
|
+
// return errors; missing required content returns a blocked Result.
|
|
49
48
|
func Check(kind Kind, root, slug string) (*Result, error) {
|
|
50
49
|
f, err := state.LoadFeature(root, slug)
|
|
51
50
|
if err != nil {
|
|
@@ -56,19 +55,46 @@ func Check(kind Kind, root, slug string) (*Result, error) {
|
|
|
56
55
|
target = state.PhaseSeal
|
|
57
56
|
}
|
|
58
57
|
missing := state.MissingFor(f, target)
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
58
|
+
var missingFiles []string
|
|
59
|
+
blocked := len(missing) > 0
|
|
60
|
+
if !f.LegacyLayout {
|
|
61
|
+
missingFiles = state.MissingWorkspaceFiles(f, target)
|
|
62
|
+
blocked = len(missingFiles) > 0
|
|
63
|
+
}
|
|
64
|
+
result := &Result{
|
|
65
|
+
Kind: kind,
|
|
66
|
+
Slug: slug,
|
|
67
|
+
Phase: f.Phase,
|
|
68
|
+
Target: target,
|
|
69
|
+
Missing: missing,
|
|
70
|
+
MissingFiles: missingFiles,
|
|
71
|
+
LegacyLayout: f.LegacyLayout,
|
|
72
|
+
Blocked: blocked,
|
|
73
|
+
}
|
|
74
|
+
result.ReasonID = ResultReasonID(kind, blocked)
|
|
75
|
+
return result, nil
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// ResultReasonID returns the typed outcome owned by the lifecycle gate.
|
|
79
|
+
func ResultReasonID(kind Kind, blocked bool) reason.ID {
|
|
80
|
+
switch kind {
|
|
81
|
+
case Readiness:
|
|
82
|
+
if blocked {
|
|
83
|
+
return reason.GateReadinessMissing
|
|
84
|
+
}
|
|
85
|
+
return reason.GateReadinessPassed
|
|
86
|
+
case Seal:
|
|
87
|
+
if blocked {
|
|
88
|
+
return reason.GateSealMissing
|
|
89
|
+
}
|
|
90
|
+
return reason.GateSealPassed
|
|
91
|
+
default:
|
|
92
|
+
return ""
|
|
93
|
+
}
|
|
67
94
|
}
|
|
68
95
|
|
|
69
|
-
// Render
|
|
70
|
-
//
|
|
71
|
-
// a human (or an AFK agent) knows precisely what to fix.
|
|
96
|
+
// Render returns stable, greppable output with a trailing newline. A blocked
|
|
97
|
+
// result names the missing files and the command to rerun.
|
|
72
98
|
func (r *Result) Render() string {
|
|
73
99
|
var b strings.Builder
|
|
74
100
|
fmt.Fprintf(&b, "gate: %s\n", r.Kind)
|
|
@@ -78,26 +104,28 @@ func (r *Result) Render() string {
|
|
|
78
104
|
b.WriteString("result: pass\n")
|
|
79
105
|
return b.String()
|
|
80
106
|
}
|
|
81
|
-
|
|
107
|
+
missing := r.MissingFiles
|
|
108
|
+
if r.LegacyLayout {
|
|
109
|
+
missing = make([]string, len(r.Missing))
|
|
110
|
+
for i, section := range r.Missing {
|
|
111
|
+
missing[i] = string(section)
|
|
112
|
+
}
|
|
113
|
+
}
|
|
82
114
|
if r.Kind == Seal {
|
|
83
|
-
fmt.Fprintf(&b, "result: blocked (missing to seal: %s)\n", strings.Join(
|
|
115
|
+
fmt.Fprintf(&b, "result: blocked (missing to seal: %s)\n", strings.Join(missing, ", "))
|
|
84
116
|
} else {
|
|
85
|
-
fmt.Fprintf(&b, "result: blocked (missing to leave %q: %s)\n", r.Phase, strings.Join(
|
|
117
|
+
fmt.Fprintf(&b, "result: blocked (missing to leave %q: %s)\n", r.Phase, strings.Join(missing, ", "))
|
|
86
118
|
}
|
|
87
119
|
fmt.Fprintf(&b, "next: add real content to %s, then re-run: devrites-engine %s %s\n",
|
|
88
|
-
strings.Join(
|
|
120
|
+
strings.Join(missing, ", "), r.Kind, r.Slug)
|
|
89
121
|
return b.String()
|
|
90
122
|
}
|
|
91
123
|
|
|
92
|
-
// StopGate
|
|
93
|
-
//
|
|
94
|
-
//
|
|
95
|
-
// whole-feature completeness: normal in-progress incompleteness never trips it,
|
|
96
|
-
// so a mid-build turn is never blocked.
|
|
124
|
+
// StopGate checks whether the active feature can end a turn in its current state.
|
|
125
|
+
// A feature in seal, ship, or done must have proof. Normal incompleteness during
|
|
126
|
+
// earlier phases does not block.
|
|
97
127
|
//
|
|
98
|
-
//
|
|
99
|
-
// is no active feature or the workspace can't be read, keeping the stop hook
|
|
100
|
-
// fail-open.
|
|
128
|
+
// Missing or unreadable workspace state returns an unblocked zero result.
|
|
101
129
|
func StopGate(root string) (StopResult, error) {
|
|
102
130
|
slug, err := orient.ActiveSlug(root)
|
|
103
131
|
if err != nil || slug == "" {
|
|
@@ -105,16 +133,17 @@ func StopGate(root string) (StopResult, error) {
|
|
|
105
133
|
}
|
|
106
134
|
f, err := state.LoadFeature(root, slug)
|
|
107
135
|
if err != nil {
|
|
108
|
-
return StopResult{}, nil //
|
|
136
|
+
return StopResult{}, nil // Unreadable state does not block Stop.
|
|
109
137
|
}
|
|
110
|
-
//
|
|
111
|
-
//
|
|
112
|
-
// invariant (a concrete, provable inconsistency), not whole-feature completeness.
|
|
138
|
+
// redwatch writes .red for a known failing suite. Stop blocks while that file
|
|
139
|
+
// exists, independently of whole-feature completeness.
|
|
113
140
|
featureDir := devritespaths.FeatureDir(root, slug)
|
|
114
141
|
if _, statErr := os.Stat(filepath.Join(featureDir, ".red")); statErr == nil {
|
|
115
142
|
return StopResult{
|
|
116
|
-
Slug:
|
|
117
|
-
Blocked:
|
|
143
|
+
Slug: slug,
|
|
144
|
+
Blocked: true,
|
|
145
|
+
ReasonID: reason.HookStopRed,
|
|
146
|
+
EvidenceFiles: []string{".red"},
|
|
118
147
|
Reason: fmt.Sprintf(
|
|
119
148
|
"feature %q has tests/build RED (.red is set): fix to green, or record the failure and next step, before stopping",
|
|
120
149
|
slug),
|
|
@@ -122,8 +151,10 @@ func StopGate(root string) (StopResult, error) {
|
|
|
122
151
|
}
|
|
123
152
|
if gates, blocked := unsurfacedHumanGates(featureDir); blocked {
|
|
124
153
|
return StopResult{
|
|
125
|
-
Slug:
|
|
126
|
-
Blocked:
|
|
154
|
+
Slug: slug,
|
|
155
|
+
Blocked: true,
|
|
156
|
+
ReasonID: reason.HookStopUnsurfacedHumanGate,
|
|
157
|
+
EvidenceFiles: []string{"questions.md", "state.md"},
|
|
127
158
|
Reason: fmt.Sprintf(
|
|
128
159
|
"feature %q has open %s human question(s) in questions.md but state.md is not awaiting_human: surface the gate before stopping",
|
|
129
160
|
slug, strings.Join(gates, "/")),
|
|
@@ -132,14 +163,16 @@ func StopGate(root string) (StopResult, error) {
|
|
|
132
163
|
claimsDone := state.ShippablePhase(f.Phase)
|
|
133
164
|
if claimsDone && !f.Present[state.SectionProof] {
|
|
134
165
|
return StopResult{
|
|
135
|
-
Slug:
|
|
136
|
-
Blocked:
|
|
166
|
+
Slug: slug,
|
|
167
|
+
Blocked: true,
|
|
168
|
+
ReasonID: reason.HookStopMissingProof,
|
|
169
|
+
EvidenceFiles: []string{"evidence.md", "proof.md", "state.md"},
|
|
137
170
|
Reason: fmt.Sprintf(
|
|
138
171
|
"feature %q is at phase %q but proof.md is empty: record acceptance evidence, or move the phase back, before stopping",
|
|
139
172
|
slug, f.Phase),
|
|
140
173
|
}, nil
|
|
141
174
|
}
|
|
142
|
-
return StopResult{Slug: slug}, nil
|
|
175
|
+
return StopResult{Slug: slug, ReasonID: reason.HookStopClear}, nil
|
|
143
176
|
}
|
|
144
177
|
|
|
145
178
|
const gateSpaceChars = " \t\n\v\f\r"
|
|
@@ -216,23 +249,9 @@ func splitLinesNoTrailing(data []byte) []string {
|
|
|
216
249
|
// none); Blocked is true iff the rest-point invariant is violated; Reason is the
|
|
217
250
|
// actionable explanation when Blocked.
|
|
218
251
|
type StopResult struct {
|
|
219
|
-
Slug
|
|
220
|
-
Reason
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
func sectionNames(ss []state.Section) []string {
|
|
225
|
-
out := make([]string, len(ss))
|
|
226
|
-
for i, s := range ss {
|
|
227
|
-
out[i] = string(s)
|
|
228
|
-
}
|
|
229
|
-
return out
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
func fileNames(ss []state.Section) []string {
|
|
233
|
-
out := make([]string, len(ss))
|
|
234
|
-
for i, s := range ss {
|
|
235
|
-
out[i] = string(s) + ".md"
|
|
236
|
-
}
|
|
237
|
-
return out
|
|
252
|
+
Slug string
|
|
253
|
+
Reason string
|
|
254
|
+
ReasonID reason.ID
|
|
255
|
+
EvidenceFiles []string
|
|
256
|
+
Blocked bool
|
|
238
257
|
}
|