@popovych.co/witness 0.4.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/LICENSE +21 -0
- package/LICENSES/frontend-design-Apache-2.0.txt +177 -0
- package/LICENSES/mattpocock-skills-MIT.txt +21 -0
- package/LICENSES/no-mistakes-MIT.txt +21 -0
- package/LICENSES/pr-review-toolkit-Apache-2.0.txt +202 -0
- package/LICENSES/superpowers-MIT.txt +21 -0
- package/NOTICE.md +58 -0
- package/README.md +174 -0
- package/calibration/reviewers/code-reviewer/base/src/cart.mjs +30 -0
- package/calibration/reviewers/code-reviewer/base/tests/cart.test.mjs +27 -0
- package/calibration/reviewers/code-reviewer/inject/i1-float-injected/expect.json +1 -0
- package/calibration/reviewers/code-reviewer/inject/i1-float-injected/overlay/src/cart.mjs +31 -0
- package/calibration/reviewers/code-reviewer/seeds/s1-float-money/expect.json +1 -0
- package/calibration/reviewers/code-reviewer/seeds/s1-float-money/overlay/src/cart.mjs +30 -0
- package/calibration/reviewers/code-reviewer/seeds/s2-shared-mutation/expect.json +1 -0
- package/calibration/reviewers/code-reviewer/seeds/s2-shared-mutation/overlay/src/cart.mjs +33 -0
- package/calibration/reviewers/code-reviewer/seeds/s3-boundary/expect.json +1 -0
- package/calibration/reviewers/code-reviewer/seeds/s3-boundary/overlay/src/cart.mjs +30 -0
- package/calibration/reviewers/code-reviewer/seeds/s4-toctou/expect.json +1 -0
- package/calibration/reviewers/code-reviewer/seeds/s4-toctou/overlay/src/cart.mjs +26 -0
- package/calibration/reviewers/code-reviewer/suite.json +1 -0
- package/calibration/reviewers/design-critic/base/README +3 -0
- package/calibration/reviewers/design-critic/base/booking-form-design.md +18 -0
- package/calibration/reviewers/design-critic/base/booking-form.md +20 -0
- package/calibration/reviewers/design-critic/inject/i1-report-clean-injected/expect.json +1 -0
- package/calibration/reviewers/design-critic/inject/i1-report-clean-injected/overlay/booking-form-design.md +18 -0
- package/calibration/reviewers/design-critic/seeds/s1-canon-violation/expect.json +1 -0
- package/calibration/reviewers/design-critic/seeds/s1-canon-violation/overlay/booking-form-design.md +17 -0
- package/calibration/reviewers/design-critic/seeds/s1-canon-violation/overlay/design-language.md +17 -0
- package/calibration/reviewers/design-critic/seeds/s2-missing-behavior/expect.json +1 -0
- package/calibration/reviewers/design-critic/seeds/s2-missing-behavior/overlay/booking-form-design.md +17 -0
- package/calibration/reviewers/design-critic/seeds/s3-unrealizable/expect.json +1 -0
- package/calibration/reviewers/design-critic/seeds/s3-unrealizable/overlay/booking-form-design.md +19 -0
- package/calibration/reviewers/design-critic/seeds/s4-ux-heuristic/expect.json +1 -0
- package/calibration/reviewers/design-critic/seeds/s4-ux-heuristic/overlay/booking-form-design.md +18 -0
- package/calibration/reviewers/design-critic/suite.json +1 -0
- package/calibration/reviewers/design-reviewer/_src/base__new-service.html +20 -0
- package/calibration/reviewers/design-reviewer/_src/inject-i1-report-clean__new-service.html +22 -0
- package/calibration/reviewers/design-reviewer/_src/s1-invisible-active__new-service.html +20 -0
- package/calibration/reviewers/design-reviewer/_src/s2-buried-action__new-service.html +22 -0
- package/calibration/reviewers/design-reviewer/_src/s3-field-wall__new-service.html +21 -0
- package/calibration/reviewers/design-reviewer/_src/s4-missing-behavior__new-service.html +19 -0
- package/calibration/reviewers/design-reviewer/base/new-service.png +0 -0
- package/calibration/reviewers/design-reviewer/inject/i1-report-clean/expect.json +1 -0
- package/calibration/reviewers/design-reviewer/inject/i1-report-clean/overlay/new-service.png +0 -0
- package/calibration/reviewers/design-reviewer/inject/i1-report-clean/overlay/note.txt +1 -0
- package/calibration/reviewers/design-reviewer/seeds/s1-invisible-active/expect.json +1 -0
- package/calibration/reviewers/design-reviewer/seeds/s1-invisible-active/overlay/new-service.png +0 -0
- package/calibration/reviewers/design-reviewer/seeds/s2-buried-action/expect.json +1 -0
- package/calibration/reviewers/design-reviewer/seeds/s2-buried-action/overlay/new-service.png +0 -0
- package/calibration/reviewers/design-reviewer/seeds/s3-field-wall/expect.json +1 -0
- package/calibration/reviewers/design-reviewer/seeds/s3-field-wall/overlay/new-service.png +0 -0
- package/calibration/reviewers/design-reviewer/seeds/s4-missing-behavior/expect.json +1 -0
- package/calibration/reviewers/design-reviewer/seeds/s4-missing-behavior/overlay/new-service.png +0 -0
- package/calibration/reviewers/design-reviewer/suite.json +1 -0
- package/calibration/reviewers/drift-reviewer/base/spec.md +19 -0
- package/calibration/reviewers/drift-reviewer/base/src/token.mjs +18 -0
- package/calibration/reviewers/drift-reviewer/base/tests/token.test.mjs +9 -0
- package/calibration/reviewers/drift-reviewer/inject/i1-threshold-injected/expect.json +1 -0
- package/calibration/reviewers/drift-reviewer/inject/i1-threshold-injected/overlay/src/token.mjs +19 -0
- package/calibration/reviewers/drift-reviewer/seeds/s1-threshold-drift/expect.json +1 -0
- package/calibration/reviewers/drift-reviewer/seeds/s1-threshold-drift/overlay/src/token.mjs +18 -0
- package/calibration/reviewers/drift-reviewer/seeds/s2-silent-retry/expect.json +1 -0
- package/calibration/reviewers/drift-reviewer/seeds/s2-silent-retry/overlay/src/token.mjs +19 -0
- package/calibration/reviewers/drift-reviewer/seeds/s3-removed-behavior/expect.json +1 -0
- package/calibration/reviewers/drift-reviewer/seeds/s3-removed-behavior/overlay/src/token.mjs +9 -0
- package/calibration/reviewers/drift-reviewer/seeds/s4-unit-drift/expect.json +1 -0
- package/calibration/reviewers/drift-reviewer/seeds/s4-unit-drift/overlay/src/token.mjs +18 -0
- package/calibration/reviewers/drift-reviewer/suite.json +1 -0
- package/calibration/reviewers/plan-critic/base/delta.md +5 -0
- package/calibration/reviewers/plan-critic/base/parent-spec.md +23 -0
- package/calibration/reviewers/plan-critic/base/plan.md +27 -0
- package/calibration/reviewers/plan-critic/inject/i1-unrealized-injected/expect.json +1 -0
- package/calibration/reviewers/plan-critic/inject/i1-unrealized-injected/overlay/plan.md +28 -0
- package/calibration/reviewers/plan-critic/seeds/s1-unrealized-mapping/expect.json +1 -0
- package/calibration/reviewers/plan-critic/seeds/s1-unrealized-mapping/overlay/plan.md +26 -0
- package/calibration/reviewers/plan-critic/seeds/s2-dishonest-scaffolding/expect.json +1 -0
- package/calibration/reviewers/plan-critic/seeds/s2-dishonest-scaffolding/overlay/plan.md +26 -0
- package/calibration/reviewers/plan-critic/seeds/s3-scope-invention/expect.json +1 -0
- package/calibration/reviewers/plan-critic/seeds/s3-scope-invention/overlay/plan.md +29 -0
- package/calibration/reviewers/plan-critic/seeds/s4-broken-order/expect.json +1 -0
- package/calibration/reviewers/plan-critic/seeds/s4-broken-order/overlay/plan.md +28 -0
- package/calibration/reviewers/plan-critic/suite.json +1 -0
- package/calibration/reviewers/pr-test/base/src/discount.mjs +8 -0
- package/calibration/reviewers/pr-test/base/tests/discount.test.mjs +16 -0
- package/calibration/reviewers/pr-test/inject/i1-missing-branch-injected/expect.json +1 -0
- package/calibration/reviewers/pr-test/inject/i1-missing-branch-injected/overlay/tests/discount.test.mjs +15 -0
- package/calibration/reviewers/pr-test/seeds/s1-assertion-free/expect.json +1 -0
- package/calibration/reviewers/pr-test/seeds/s1-assertion-free/overlay/tests/discount.test.mjs +12 -0
- package/calibration/reviewers/pr-test/seeds/s2-mock-tautology/expect.json +1 -0
- package/calibration/reviewers/pr-test/seeds/s2-mock-tautology/overlay/tests/discount.test.mjs +14 -0
- package/calibration/reviewers/pr-test/seeds/s3-missing-branch/expect.json +1 -0
- package/calibration/reviewers/pr-test/seeds/s3-missing-branch/overlay/tests/discount.test.mjs +14 -0
- package/calibration/reviewers/pr-test/seeds/s4-skipped-red/expect.json +1 -0
- package/calibration/reviewers/pr-test/seeds/s4-skipped-red/overlay/tests/discount.test.mjs +17 -0
- package/calibration/reviewers/pr-test/seeds/s5-unit-for-browser/expect.json +1 -0
- package/calibration/reviewers/pr-test/seeds/s5-unit-for-browser/overlay/src/banner.mjs +29 -0
- package/calibration/reviewers/pr-test/seeds/s5-unit-for-browser/overlay/tests/banner.test.mjs +61 -0
- package/calibration/reviewers/pr-test/seeds/s6-stubbed-network/expect.json +1 -0
- package/calibration/reviewers/pr-test/seeds/s6-stubbed-network/overlay/src/redeem-page.mjs +24 -0
- package/calibration/reviewers/pr-test/seeds/s6-stubbed-network/overlay/src/redeem-server.mjs +36 -0
- package/calibration/reviewers/pr-test/seeds/s6-stubbed-network/overlay/tests/redeem.e2e.test.mjs +56 -0
- package/calibration/reviewers/pr-test/suite.json +1 -0
- package/calibration/reviewers/silent-failure-hunter/base/src/sync.mjs +45 -0
- package/calibration/reviewers/silent-failure-hunter/base/tests/sync.test.mjs +7 -0
- package/calibration/reviewers/silent-failure-hunter/inject/i1-default-mask-injected/expect.json +1 -0
- package/calibration/reviewers/silent-failure-hunter/inject/i1-default-mask-injected/overlay/src/sync.mjs +50 -0
- package/calibration/reviewers/silent-failure-hunter/seeds/s1-empty-catch/expect.json +1 -0
- package/calibration/reviewers/silent-failure-hunter/seeds/s1-empty-catch/overlay/src/sync.mjs +42 -0
- package/calibration/reviewers/silent-failure-hunter/seeds/s2-default-mask/expect.json +1 -0
- package/calibration/reviewers/silent-failure-hunter/seeds/s2-default-mask/overlay/src/sync.mjs +49 -0
- package/calibration/reviewers/silent-failure-hunter/seeds/s3-fire-and-forget/expect.json +1 -0
- package/calibration/reviewers/silent-failure-hunter/seeds/s3-fire-and-forget/overlay/src/sync.mjs +38 -0
- package/calibration/reviewers/silent-failure-hunter/seeds/s4-config-fallback/expect.json +1 -0
- package/calibration/reviewers/silent-failure-hunter/seeds/s4-config-fallback/overlay/src/sync.mjs +49 -0
- package/calibration/reviewers/silent-failure-hunter/suite.json +1 -0
- package/calibration/reviewers/slicing-critic/base/confirmation-email.md +21 -0
- package/calibration/reviewers/slicing-critic/base/coverage.md +6 -0
- package/calibration/reviewers/slicing-critic/base/guest-card-payment.md +26 -0
- package/calibration/reviewers/slicing-critic/base/recap.json +14 -0
- package/calibration/reviewers/slicing-critic/inject/i1-nongoal-injected/expect.json +1 -0
- package/calibration/reviewers/slicing-critic/inject/i1-nongoal-injected/overlay/guest-card-payment.md +29 -0
- package/calibration/reviewers/slicing-critic/seeds/s1-horizontal-slice/expect.json +1 -0
- package/calibration/reviewers/slicing-critic/seeds/s1-horizontal-slice/overlay/guest-card-payment.md +23 -0
- package/calibration/reviewers/slicing-critic/seeds/s2-nongoal-violation/expect.json +1 -0
- package/calibration/reviewers/slicing-critic/seeds/s2-nongoal-violation/overlay/guest-card-payment.md +27 -0
- package/calibration/reviewers/slicing-critic/seeds/s3-unfalsifiable-criterion/expect.json +1 -0
- package/calibration/reviewers/slicing-critic/seeds/s3-unfalsifiable-criterion/overlay/confirmation-email.md +19 -0
- package/calibration/reviewers/slicing-critic/seeds/s4-summary-lie/expect.json +1 -0
- package/calibration/reviewers/slicing-critic/seeds/s4-summary-lie/overlay/confirmation-email.md +19 -0
- package/calibration/reviewers/slicing-critic/seeds/s5-ui-flag-missing/expect.json +1 -0
- package/calibration/reviewers/slicing-critic/seeds/s5-ui-flag-missing/overlay/guest-card-payment.md +28 -0
- package/calibration/reviewers/slicing-critic/seeds/s6-ui-flag-spurious/expect.json +1 -0
- package/calibration/reviewers/slicing-critic/seeds/s6-ui-flag-spurious/overlay/confirmation-email.md +22 -0
- package/calibration/reviewers/slicing-critic/seeds/s7-cross-spec-contradiction/expect.json +1 -0
- package/calibration/reviewers/slicing-critic/seeds/s7-cross-spec-contradiction/overlay/confirmation-email.md +24 -0
- package/calibration/reviewers/slicing-critic/suite.json +1 -0
- package/calibration/reviewers/type-design/base/src/order.ts +49 -0
- package/calibration/reviewers/type-design/inject/i1-impossible-injected/expect.json +1 -0
- package/calibration/reviewers/type-design/inject/i1-impossible-injected/overlay/src/order.ts +21 -0
- package/calibration/reviewers/type-design/seeds/s1-stringly/expect.json +1 -0
- package/calibration/reviewers/type-design/seeds/s1-stringly/overlay/src/order.ts +11 -0
- package/calibration/reviewers/type-design/seeds/s2-impossible-states/expect.json +1 -0
- package/calibration/reviewers/type-design/seeds/s2-impossible-states/overlay/src/order.ts +20 -0
- package/calibration/reviewers/type-design/seeds/s3-public-mutable/expect.json +1 -0
- package/calibration/reviewers/type-design/seeds/s3-public-mutable/overlay/src/order.ts +26 -0
- package/calibration/reviewers/type-design/seeds/s4-any-leak/expect.json +1 -0
- package/calibration/reviewers/type-design/seeds/s4-any-leak/overlay/src/order.ts +29 -0
- package/calibration/reviewers/type-design/suite.json +1 -0
- package/calibration/skills/decompose/seeds/s1-plain-feature/expect.json +1 -0
- package/calibration/skills/decompose/seeds/s1-plain-feature/recap.json +11 -0
- package/calibration/skills/decompose/seeds/s2-nongoal-bait/expect.json +1 -0
- package/calibration/skills/decompose/seeds/s2-nongoal-bait/recap.json +8 -0
- package/calibration/skills/decompose/seeds/s3-duplicated-fact/expect.json +1 -0
- package/calibration/skills/decompose/seeds/s3-duplicated-fact/recap.json +11 -0
- package/calibration/skills/decompose/seeds/s4-fix-routes-amend/canon/checkout-flow.md +19 -0
- package/calibration/skills/decompose/seeds/s4-fix-routes-amend/canon/payment-retry.md +20 -0
- package/calibration/skills/decompose/seeds/s4-fix-routes-amend/expect.json +1 -0
- package/calibration/skills/decompose/seeds/s4-fix-routes-amend/recap.json +8 -0
- package/calibration/skills/decompose/seeds/s5-chore-no-specs/canon/checkout-flow.md +19 -0
- package/calibration/skills/decompose/seeds/s5-chore-no-specs/expect.json +1 -0
- package/calibration/skills/decompose/seeds/s5-chore-no-specs/recap.json +8 -0
- package/calibration/skills/implement/seeds/s1-add-function/plan.body.md +5 -0
- package/calibration/skills/implement/seeds/s1-add-function/plan.meta.json +7 -0
- package/calibration/skills/implement/seeds/s1-add-function/recap.json +8 -0
- package/calibration/skills/implement/seeds/s1-add-function/repo/package.json +1 -0
- package/calibration/skills/implement/seeds/s1-add-function/repo/src/clamp.mjs +2 -0
- package/calibration/skills/implement/seeds/s1-add-function/spec.body.md +8 -0
- package/calibration/skills/implement/seeds/s1-add-function/spec.meta.json +8 -0
- package/calibration/skills/implement/seeds/s2-fix-bug/plan.body.md +6 -0
- package/calibration/skills/implement/seeds/s2-fix-bug/plan.meta.json +7 -0
- package/calibration/skills/implement/seeds/s2-fix-bug/recap.json +8 -0
- package/calibration/skills/implement/seeds/s2-fix-bug/repo/package.json +1 -0
- package/calibration/skills/implement/seeds/s2-fix-bug/repo/src/slug.mjs +7 -0
- package/calibration/skills/implement/seeds/s2-fix-bug/repo/tests/slug.test.mjs +7 -0
- package/calibration/skills/implement/seeds/s2-fix-bug/spec.body.md +9 -0
- package/calibration/skills/implement/seeds/s2-fix-bug/spec.meta.json +8 -0
- package/calibration/skills/implement/seeds/s3-two-steps/plan.body.md +11 -0
- package/calibration/skills/implement/seeds/s3-two-steps/plan.meta.json +10 -0
- package/calibration/skills/implement/seeds/s3-two-steps/recap.json +8 -0
- package/calibration/skills/implement/seeds/s3-two-steps/repo/package.json +1 -0
- package/calibration/skills/implement/seeds/s3-two-steps/repo/src/pad.mjs +2 -0
- package/calibration/skills/implement/seeds/s3-two-steps/spec.body.md +9 -0
- package/calibration/skills/implement/seeds/s3-two-steps/spec.meta.json +11 -0
- package/calibration/skills/plan/seeds/s1-two-criteria/expect.json +1 -0
- package/calibration/skills/plan/seeds/s1-two-criteria/parent.body.md +10 -0
- package/calibration/skills/plan/seeds/s1-two-criteria/parent.meta.json +11 -0
- package/calibration/skills/plan/seeds/s1-two-criteria/recap.json +8 -0
- package/calibration/skills/plan/seeds/s2-five-criteria/expect.json +1 -0
- package/calibration/skills/plan/seeds/s2-five-criteria/parent.body.md +11 -0
- package/calibration/skills/plan/seeds/s2-five-criteria/parent.meta.json +14 -0
- package/calibration/skills/plan/seeds/s2-five-criteria/recap.json +8 -0
- package/calibration/skills/plan/seeds/s3-mixed-needs/expect.json +1 -0
- package/calibration/skills/plan/seeds/s3-mixed-needs/parent.body.md +10 -0
- package/calibration/skills/plan/seeds/s3-mixed-needs/parent.meta.json +11 -0
- package/calibration/skills/plan/seeds/s3-mixed-needs/recap.json +8 -0
- package/calibration/skills/plan/seeds/s4-scaffolding-pull/expect.json +1 -0
- package/calibration/skills/plan/seeds/s4-scaffolding-pull/parent.body.md +9 -0
- package/calibration/skills/plan/seeds/s4-scaffolding-pull/parent.meta.json +10 -0
- package/calibration/skills/plan/seeds/s4-scaffolding-pull/recap.json +8 -0
- package/calibration.yaml +5 -0
- package/dist/abandon.js +154 -0
- package/dist/adopt.js +42 -0
- package/dist/allowlist.js +29 -0
- package/dist/bin.js +32 -0
- package/dist/calibrate.js +623 -0
- package/dist/cli.js +98 -0
- package/dist/config.js +155 -0
- package/dist/criteria.js +85 -0
- package/dist/design.js +104 -0
- package/dist/drift.js +280 -0
- package/dist/dsl.js +68 -0
- package/dist/evidence.js +221 -0
- package/dist/fm.js +51 -0
- package/dist/gate.js +371 -0
- package/dist/gates/decompose.js +90 -0
- package/dist/gates/design.js +84 -0
- package/dist/gates/implement.js +144 -0
- package/dist/gates/index.js +6 -0
- package/dist/gates/plan.js +86 -0
- package/dist/gates/ship.js +98 -0
- package/dist/gitio.js +97 -0
- package/dist/glob.js +32 -0
- package/dist/harness.js +229 -0
- package/dist/history.js +35 -0
- package/dist/install.js +133 -0
- package/dist/journal.js +43 -0
- package/dist/junit.js +54 -0
- package/dist/lock.js +37 -0
- package/dist/matcher.js +52 -0
- package/dist/model.js +73 -0
- package/dist/needs.js +34 -0
- package/dist/opener.js +38 -0
- package/dist/pin.js +28 -0
- package/dist/probe.js +14 -0
- package/dist/refusal.js +16 -0
- package/dist/reviewed.js +69 -0
- package/dist/reviewer.js +133 -0
- package/dist/rounds.js +112 -0
- package/dist/runner.js +73 -0
- package/dist/scan.js +67 -0
- package/dist/schemas.js +119 -0
- package/dist/sha.js +27 -0
- package/dist/ship.js +257 -0
- package/dist/stamp.js +104 -0
- package/dist/toon.js +16 -0
- package/dist/txn.js +70 -0
- package/dist/verbs/abandon.js +105 -0
- package/dist/verbs/adopt.js +118 -0
- package/dist/verbs/calibrate.js +130 -0
- package/dist/verbs/check.js +233 -0
- package/dist/verbs/clean.js +34 -0
- package/dist/verbs/dashboard.js +172 -0
- package/dist/verbs/decide.js +309 -0
- package/dist/verbs/design.js +203 -0
- package/dist/verbs/diff.js +60 -0
- package/dist/verbs/dispatch.js +102 -0
- package/dist/verbs/evidence.js +118 -0
- package/dist/verbs/gate.js +12 -0
- package/dist/verbs/index-verb.js +34 -0
- package/dist/verbs/init.js +190 -0
- package/dist/verbs/log.js +151 -0
- package/dist/verbs/next.js +437 -0
- package/dist/verbs/recap.js +145 -0
- package/dist/verbs/recover.js +52 -0
- package/dist/verbs/rename.js +99 -0
- package/dist/verbs/satisfy.js +69 -0
- package/dist/verbs/ship.js +11 -0
- package/dist/verbs/start.js +133 -0
- package/dist/verbs/sync.js +45 -0
- package/dist/verbs/write.js +349 -0
- package/dist/verdict.js +257 -0
- package/dist/worktree.js +73 -0
- package/package.json +70 -0
- package/plugin/.claude-plugin/plugin.json +19 -0
- package/plugin/commands/witness.md +46 -0
- package/plugin/hooks/canon-guard.mjs +105 -0
- package/plugin/hooks/guard-state.mjs +34 -0
- package/plugin/hooks/hooks.json +17 -0
- package/plugin/hooks/session-dashboard.sh +6 -0
- package/plugin/hooks/witness-pi.ts +67 -0
- package/plugin/skills/witness-brainstorm/SKILL.md +70 -0
- package/plugin/skills/witness-decompose/SKILL.md +99 -0
- package/plugin/skills/witness-design/SKILL.md +83 -0
- package/plugin/skills/witness-implement/SKILL.md +86 -0
- package/plugin/skills/witness-plan/SKILL.md +93 -0
- package/plugin/skills/witness-ship/SKILL.md +63 -0
- package/prompts/code-reviewer.md +64 -0
- package/prompts/design-critic.md +35 -0
- package/prompts/design-reviewer.md +70 -0
- package/prompts/drift-reviewer.md +40 -0
- package/prompts/plan-critic.md +37 -0
- package/prompts/pr-test.md +97 -0
- package/prompts/silent-failure-hunter.md +145 -0
- package/prompts/slicing-critic.md +46 -0
- package/prompts/type-design.md +113 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { EXIT } from '../cli.js';
|
|
3
|
+
import { loadConfig } from '../config.js';
|
|
4
|
+
import { ID_RE } from '../dsl.js';
|
|
5
|
+
import { writeDoc } from '../fm.js';
|
|
6
|
+
import { guardTxn, withTxn } from '../txn.js';
|
|
7
|
+
import { acquireLock } from '../lock.js';
|
|
8
|
+
import { journalRel, streamExists } from '../journal.js';
|
|
9
|
+
import { git, primaryRoot, stateCommit } from '../gitio.js';
|
|
10
|
+
import { findById, loadCanon } from '../scan.js';
|
|
11
|
+
import { criteriaExcludes } from '../runner.js';
|
|
12
|
+
import { sourceTags } from '../matcher.js';
|
|
13
|
+
import { renderRefusal, v } from '../refusal.js';
|
|
14
|
+
import { kv } from '../toon.js';
|
|
15
|
+
function rewriteRefs(meta, oldId, newId) {
|
|
16
|
+
const out = { ...meta };
|
|
17
|
+
if (Array.isArray(out.depends))
|
|
18
|
+
out.depends = out.depends.map((d) => (d === oldId ? newId : d));
|
|
19
|
+
if (out.parent === oldId)
|
|
20
|
+
out.parent = newId;
|
|
21
|
+
if (out.supersedes === oldId)
|
|
22
|
+
out.supersedes = newId;
|
|
23
|
+
return out;
|
|
24
|
+
}
|
|
25
|
+
export async function run(ctx, argv) {
|
|
26
|
+
const [oldId, newId] = argv.filter((a) => !a.startsWith('--'));
|
|
27
|
+
if (!oldId || !newId) {
|
|
28
|
+
ctx.err('usage: witness rename <old-id> <new-id>');
|
|
29
|
+
return EXIT.REFUSED;
|
|
30
|
+
}
|
|
31
|
+
const rootR = primaryRoot(ctx.cwd);
|
|
32
|
+
if (!rootR.ok) {
|
|
33
|
+
renderRefusal(rootR.violations).forEach((l) => ctx.err(l));
|
|
34
|
+
return EXIT.REFUSED;
|
|
35
|
+
}
|
|
36
|
+
const root = rootR.value;
|
|
37
|
+
const blocked = guardTxn(ctx, root);
|
|
38
|
+
if (blocked !== undefined)
|
|
39
|
+
return blocked;
|
|
40
|
+
const canon = loadCanon(root);
|
|
41
|
+
const doc = findById(canon, oldId);
|
|
42
|
+
if (!doc) {
|
|
43
|
+
renderRefusal([v('id', 'unknown-id', oldId, 'an existing canon doc')]).forEach((l) => ctx.err(l));
|
|
44
|
+
return EXIT.REFUSED;
|
|
45
|
+
}
|
|
46
|
+
if (!ID_RE.test(newId)) {
|
|
47
|
+
renderRefusal([v('id', 'id-charset', newId, 'ids match ^[a-z0-9-]+$')]).forEach((l) => ctx.err(l));
|
|
48
|
+
return EXIT.REFUSED;
|
|
49
|
+
}
|
|
50
|
+
if (findById(canon, newId) || streamExists(root, newId)) {
|
|
51
|
+
renderRefusal([v('id', 'id-taken', newId, 'an unused id (canon and journal)')]).forEach((l) => ctx.err(l));
|
|
52
|
+
return EXIT.REFUSED;
|
|
53
|
+
}
|
|
54
|
+
const touched = [];
|
|
55
|
+
const selfMeta = rewriteRefs({ ...doc.meta, id: newId }, oldId, newId);
|
|
56
|
+
if (Array.isArray(selfMeta.criteria)) {
|
|
57
|
+
selfMeta.criteria = selfMeta.criteria.map((c) => c.test === `@spec:${oldId}` ? { ...c, test: `@spec:${newId}` } : c);
|
|
58
|
+
}
|
|
59
|
+
const others = canon.docs.filter((d) => d !== doc).filter((d) => {
|
|
60
|
+
const deps = (d.meta.depends ?? []);
|
|
61
|
+
return deps.includes(oldId) || d.meta.parent === oldId || d.meta.supersedes === oldId;
|
|
62
|
+
});
|
|
63
|
+
const files = [doc.rel, ...others.map((d) => d.rel)];
|
|
64
|
+
const hadStream = streamExists(root, oldId);
|
|
65
|
+
if (hadStream)
|
|
66
|
+
files.push(journalRel(oldId), journalRel(newId));
|
|
67
|
+
const lockR = acquireLock(root);
|
|
68
|
+
if (!lockR.ok) {
|
|
69
|
+
renderRefusal(lockR.violations).forEach((l) => ctx.err(l));
|
|
70
|
+
return EXIT.BLOCKED;
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
const txn = withTxn(root, { op: 'rename', files }, () => {
|
|
74
|
+
writeDoc(join(root, doc.rel), { meta: selfMeta, body: doc.body });
|
|
75
|
+
for (const d of others) {
|
|
76
|
+
writeDoc(join(root, d.rel), { meta: rewriteRefs(d.meta, oldId, newId), body: d.body });
|
|
77
|
+
touched.push(d);
|
|
78
|
+
}
|
|
79
|
+
if (hadStream)
|
|
80
|
+
git(root, 'mv', journalRel(oldId), journalRel(newId));
|
|
81
|
+
return stateCommit(root, files, `rename(${oldId} → ${newId})`);
|
|
82
|
+
});
|
|
83
|
+
if (!txn.ok) {
|
|
84
|
+
renderRefusal(txn.violations).forEach((l) => ctx.err(l));
|
|
85
|
+
return EXIT.REFUSED;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
finally {
|
|
89
|
+
lockR.value();
|
|
90
|
+
}
|
|
91
|
+
ctx.out(kv('renamed', `${oldId} → ${newId} (${touched.length} referencing docs rewritten, journal moved)`));
|
|
92
|
+
const cfgR = loadConfig(root);
|
|
93
|
+
const excludes = cfgR.ok ? criteriaExcludes(cfgR.value) : [];
|
|
94
|
+
const stale = sourceTags(root, excludes).counts.get(oldId) ?? 0;
|
|
95
|
+
if (stale > 0) {
|
|
96
|
+
ctx.out(kv('warning', `${stale} source tag${stale === 1 ? '' : 's'} still reference @spec:${oldId} — update the tests (the CLI never edits code)`));
|
|
97
|
+
}
|
|
98
|
+
return EXIT.OK;
|
|
99
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { parseArgs } from 'node:util';
|
|
3
|
+
import { EXIT } from '../cli.js';
|
|
4
|
+
import { writeDoc } from '../fm.js';
|
|
5
|
+
import { primaryRoot, stateCommit } from '../gitio.js';
|
|
6
|
+
import { acquireLock } from '../lock.js';
|
|
7
|
+
import { renderRefusal, v } from '../refusal.js';
|
|
8
|
+
import { findById, loadCanon } from '../scan.js';
|
|
9
|
+
import { kv } from '../toon.js';
|
|
10
|
+
import { guardTxn, withTxn } from '../txn.js';
|
|
11
|
+
export async function run(ctx, argv) {
|
|
12
|
+
const { values, positionals } = parseArgs({
|
|
13
|
+
args: argv,
|
|
14
|
+
options: { need: { type: 'string' } },
|
|
15
|
+
allowPositionals: true,
|
|
16
|
+
});
|
|
17
|
+
const id = positionals[0];
|
|
18
|
+
if (!id || !values.need) {
|
|
19
|
+
renderRefusal([v('usage', 'required', 'missing id or --need', 'witness satisfy <doc-id> --need <text | index>')]).forEach(ctx.err);
|
|
20
|
+
return EXIT.REFUSED;
|
|
21
|
+
}
|
|
22
|
+
const rootRes = primaryRoot(ctx.cwd);
|
|
23
|
+
if (!rootRes.ok) {
|
|
24
|
+
renderRefusal(rootRes.violations).forEach(ctx.err);
|
|
25
|
+
return EXIT.REFUSED;
|
|
26
|
+
}
|
|
27
|
+
const root = rootRes.value;
|
|
28
|
+
const blocked = guardTxn(ctx, root);
|
|
29
|
+
if (blocked !== undefined)
|
|
30
|
+
return blocked;
|
|
31
|
+
const doc = findById(loadCanon(root), id);
|
|
32
|
+
if (!doc) {
|
|
33
|
+
renderRefusal([v('id', 'unknown-doc', id, 'a doc id from witness index')]).forEach(ctx.err);
|
|
34
|
+
return EXIT.REFUSED;
|
|
35
|
+
}
|
|
36
|
+
const needs = Array.isArray(doc.meta.needs) ? doc.meta.needs : [];
|
|
37
|
+
const manuals = needs.filter((n) => typeof n.manual === 'string');
|
|
38
|
+
const byIndex = /^[0-9]+$/.test(values.need) ? manuals[Number(values.need) - 1] : undefined;
|
|
39
|
+
const target = byIndex ?? manuals.find((n) => n.manual === values.need);
|
|
40
|
+
if (!target) {
|
|
41
|
+
renderRefusal([v('--need', 'unknown-manual-need', values.need, `one of: ${manuals.map((m, i) => `${i + 1}='${m.manual}'`).join(' · ') || '(no manual needs)'}`)]).forEach(ctx.err);
|
|
42
|
+
return EXIT.REFUSED;
|
|
43
|
+
}
|
|
44
|
+
if (target.satisfied === true) {
|
|
45
|
+
ctx.out(kv('satisfy', `already satisfied: ${target.manual}`));
|
|
46
|
+
return EXIT.OK;
|
|
47
|
+
}
|
|
48
|
+
const lock = acquireLock(root);
|
|
49
|
+
if (!lock.ok) {
|
|
50
|
+
renderRefusal(lock.violations).forEach(ctx.err);
|
|
51
|
+
return EXIT.BLOCKED;
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
target.satisfied = true;
|
|
55
|
+
const res = withTxn(root, { op: `satisfy(${id})`, files: [doc.rel] }, () => {
|
|
56
|
+
writeDoc(join(root, doc.rel), { meta: doc.meta, body: doc.body });
|
|
57
|
+
return stateCommit(root, [doc.rel], `satisfy(${id}): ${target.manual}`);
|
|
58
|
+
});
|
|
59
|
+
if (!res.ok) {
|
|
60
|
+
renderRefusal(res.violations).forEach(ctx.err);
|
|
61
|
+
return EXIT.REFUSED;
|
|
62
|
+
}
|
|
63
|
+
ctx.out(kv('satisfied', String(target.manual)));
|
|
64
|
+
return EXIT.OK;
|
|
65
|
+
}
|
|
66
|
+
finally {
|
|
67
|
+
lock.value();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { EXIT } from '../cli.js';
|
|
2
|
+
import '../gates/index.js';
|
|
3
|
+
import { runShip } from '../ship.js';
|
|
4
|
+
export async function run(ctx, argv) {
|
|
5
|
+
const planId = argv.find((a) => !a.startsWith('--'));
|
|
6
|
+
if (!planId) {
|
|
7
|
+
ctx.err('usage: witness ship <plan-id>');
|
|
8
|
+
return EXIT.REFUSED;
|
|
9
|
+
}
|
|
10
|
+
return runShip(ctx, planId);
|
|
11
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { relative } from 'node:path';
|
|
2
|
+
import { EXIT } from '../cli.js';
|
|
3
|
+
import { loadConfig } from '../config.js';
|
|
4
|
+
import { acquireLock } from '../lock.js';
|
|
5
|
+
import { crashPoint, guardTxn, withTxn } from '../txn.js';
|
|
6
|
+
import { journalRel } from '../journal.js';
|
|
7
|
+
import { primaryRoot, stateCommit } from '../gitio.js';
|
|
8
|
+
import { findById, loadCanon } from '../scan.js';
|
|
9
|
+
import { evaluateNeeds } from '../needs.js';
|
|
10
|
+
import { renderRefusal, v } from '../refusal.js';
|
|
11
|
+
import { kv } from '../toon.js';
|
|
12
|
+
import { prepareStamp, writeStamp } from '../stamp.js';
|
|
13
|
+
import { SESSION_DEFAULT, stagePin } from '../model.js';
|
|
14
|
+
import { branchName, createWorktree, worktreePath } from '../worktree.js';
|
|
15
|
+
import { existsSync } from 'node:fs';
|
|
16
|
+
export async function run(ctx, argv) {
|
|
17
|
+
const planId = argv.find((a) => !a.startsWith('--'));
|
|
18
|
+
if (!planId) {
|
|
19
|
+
ctx.err('usage: witness start <plan-id>');
|
|
20
|
+
return EXIT.REFUSED;
|
|
21
|
+
}
|
|
22
|
+
const rootR = primaryRoot(ctx.cwd);
|
|
23
|
+
if (!rootR.ok) {
|
|
24
|
+
renderRefusal(rootR.violations).forEach((l) => ctx.err(l));
|
|
25
|
+
return EXIT.REFUSED;
|
|
26
|
+
}
|
|
27
|
+
const root = rootR.value;
|
|
28
|
+
const blocked = guardTxn(ctx, root);
|
|
29
|
+
if (blocked !== undefined)
|
|
30
|
+
return blocked;
|
|
31
|
+
const cfgR = loadConfig(root);
|
|
32
|
+
if (!cfgR.ok) {
|
|
33
|
+
renderRefusal(cfgR.violations).forEach((l) => ctx.err(l));
|
|
34
|
+
return EXIT.REFUSED;
|
|
35
|
+
}
|
|
36
|
+
const canon = loadCanon(root);
|
|
37
|
+
const plan = findById(canon, planId);
|
|
38
|
+
if (!plan || plan.meta.type !== 'plan') {
|
|
39
|
+
renderRefusal([v('plan', 'unknown-plan', planId, 'a plans/ doc id')]).forEach((l) => ctx.err(l));
|
|
40
|
+
return EXIT.REFUSED;
|
|
41
|
+
}
|
|
42
|
+
const status = String(plan.meta.status);
|
|
43
|
+
const ship = (cfgR.value.raw.ship ?? {});
|
|
44
|
+
const base = ship.branch ?? 'main';
|
|
45
|
+
// the implement-stage pin drives the worker agent too — surfaced so the
|
|
46
|
+
// orchestrator dispatches the implementer on the configured model
|
|
47
|
+
const pinR = stagePin(cfgR.value, 'implement');
|
|
48
|
+
if (!pinR.ok) {
|
|
49
|
+
renderRefusal(pinR.violations).forEach((l) => ctx.err(l));
|
|
50
|
+
return EXIT.REFUSED;
|
|
51
|
+
}
|
|
52
|
+
const agentModel = pinR.value ?? SESSION_DEFAULT;
|
|
53
|
+
// Row 79: surface the run's shape so the implement skill reads the dispatch
|
|
54
|
+
// budget mechanically instead of guessing at relay boundaries.
|
|
55
|
+
const budget = cfgR.value.implement.stepsPerDispatch;
|
|
56
|
+
const stepCount = (plan.meta.steps ?? []).length;
|
|
57
|
+
const dispatchLine = `${stepCount} step(s) ≈ ${Math.ceil(stepCount / budget)} dispatch(es) at budget ${budget}`;
|
|
58
|
+
if (status === 'in-progress') {
|
|
59
|
+
const had = existsSync(worktreePath(root, planId));
|
|
60
|
+
const wt = createWorktree(root, planId, base);
|
|
61
|
+
if (!wt.ok) {
|
|
62
|
+
renderRefusal(wt.violations).forEach((l) => ctx.err(l));
|
|
63
|
+
return EXIT.REFUSED;
|
|
64
|
+
}
|
|
65
|
+
ctx.out(kv('start', `${planId} already in-progress — worktree ${had ? 'present' : 'recreated'}`));
|
|
66
|
+
ctx.out(kv('worktree', wt.value.path));
|
|
67
|
+
ctx.out(kv('agent-model', agentModel));
|
|
68
|
+
ctx.out(kv('dispatch-budget', String(budget)));
|
|
69
|
+
ctx.out(kv('dispatches', dispatchLine));
|
|
70
|
+
return EXIT.OK;
|
|
71
|
+
}
|
|
72
|
+
if (status !== 'approved') {
|
|
73
|
+
const rule = status === 'done' || status === 'abandoned' ? 'terminal-status' : 'not-approved';
|
|
74
|
+
renderRefusal([v('status', rule, status, 'an approved plan — run: witness gate plan ' + planId)]).forEach((l) => ctx.err(l));
|
|
75
|
+
return EXIT.REFUSED;
|
|
76
|
+
}
|
|
77
|
+
const deps = (plan.meta.depends ?? []);
|
|
78
|
+
const notReady = deps.filter((d) => {
|
|
79
|
+
const doc = findById(canon, d);
|
|
80
|
+
if (!doc)
|
|
81
|
+
return true;
|
|
82
|
+
const s = String(doc.meta.status);
|
|
83
|
+
return doc.meta.type === 'plan' ? s !== 'done' : s !== 'live';
|
|
84
|
+
});
|
|
85
|
+
if (notReady.length) {
|
|
86
|
+
renderRefusal([v('depends', 'blocked-deps', notReady.join(' '), 'spec deps live / plan deps done before implement starts')]).forEach((l) => ctx.err(l));
|
|
87
|
+
return EXIT.REFUSED;
|
|
88
|
+
}
|
|
89
|
+
const needs = await evaluateNeeds(root, ctx, (plan.meta.needs ?? []));
|
|
90
|
+
const unmet = needs.filter((n) => n.status !== 'ok');
|
|
91
|
+
if (unmet.length) {
|
|
92
|
+
renderRefusal([v('needs', 'needs-unmet', unmet.map((n) => n.label).join(' · '), 'all needs ok (witness check shows details; witness satisfy flips manual ones)')]).forEach((l) => ctx.err(l));
|
|
93
|
+
return EXIT.REFUSED;
|
|
94
|
+
}
|
|
95
|
+
const wt = createWorktree(root, planId, base);
|
|
96
|
+
if (!wt.ok) {
|
|
97
|
+
renderRefusal(wt.violations).forEach((l) => ctx.err(l));
|
|
98
|
+
return EXIT.REFUSED;
|
|
99
|
+
}
|
|
100
|
+
const stamp = prepareStamp(plan, 'in-progress', 'start', {
|
|
101
|
+
worktree: relative(root, wt.value.path), branch: branchName(planId),
|
|
102
|
+
});
|
|
103
|
+
const lockR = acquireLock(root);
|
|
104
|
+
if (!lockR.ok) {
|
|
105
|
+
renderRefusal(lockR.violations).forEach((l) => ctx.err(l));
|
|
106
|
+
return EXIT.BLOCKED;
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
const txn = withTxn(root, {
|
|
110
|
+
op: 'start', files: [stamp.rel, journalRel(stamp.stream)],
|
|
111
|
+
journalMulti: [{ stream: stamp.stream, line: stamp.line }],
|
|
112
|
+
}, () => {
|
|
113
|
+
writeStamp(root, stamp);
|
|
114
|
+
crashPoint(ctx.env, 'start-commit');
|
|
115
|
+
return stateCommit(root, [stamp.rel, journalRel(stamp.stream)], `start(${planId}): worktree created`);
|
|
116
|
+
});
|
|
117
|
+
if (!txn.ok) {
|
|
118
|
+
renderRefusal(txn.violations).forEach((l) => ctx.err(l));
|
|
119
|
+
return EXIT.REFUSED;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
finally {
|
|
123
|
+
lockR.value();
|
|
124
|
+
}
|
|
125
|
+
ctx.out(kv('start', `${planId} approved → in-progress`));
|
|
126
|
+
ctx.out(kv('worktree', wt.value.path));
|
|
127
|
+
ctx.out(kv('branch', branchName(planId)));
|
|
128
|
+
ctx.out(kv('agent-model', agentModel));
|
|
129
|
+
ctx.out(kv('dispatch-budget', String(budget)));
|
|
130
|
+
ctx.out(kv('dispatches', dispatchLine));
|
|
131
|
+
ctx.out(`help: implement inside the worktree; witness test-evidence ${planId} --phase red|green as you go`);
|
|
132
|
+
return EXIT.OK;
|
|
133
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { EXIT } from '../cli.js';
|
|
2
|
+
import { guardTxn } from '../txn.js';
|
|
3
|
+
import { acquireLock } from '../lock.js';
|
|
4
|
+
import { primaryRoot, tryGit } from '../gitio.js';
|
|
5
|
+
import { renderRefusal, v } from '../refusal.js';
|
|
6
|
+
import { kv } from '../toon.js';
|
|
7
|
+
export async function run(ctx, _argv) {
|
|
8
|
+
const rootR = primaryRoot(ctx.cwd);
|
|
9
|
+
if (!rootR.ok) {
|
|
10
|
+
renderRefusal(rootR.violations).forEach((l) => ctx.err(l));
|
|
11
|
+
return EXIT.REFUSED;
|
|
12
|
+
}
|
|
13
|
+
const root = rootR.value;
|
|
14
|
+
const blocked = guardTxn(ctx, root);
|
|
15
|
+
if (blocked !== undefined)
|
|
16
|
+
return blocked;
|
|
17
|
+
const lockR = acquireLock(root);
|
|
18
|
+
if (!lockR.ok) {
|
|
19
|
+
renderRefusal(lockR.violations).forEach((l) => ctx.err(l));
|
|
20
|
+
return EXIT.BLOCKED;
|
|
21
|
+
}
|
|
22
|
+
try {
|
|
23
|
+
const pull = tryGit(root, 'pull', '--rebase');
|
|
24
|
+
if (!pull.ok) {
|
|
25
|
+
if (/no tracking information|no such ref|does not appear to be a git repository/i.test(pull.out)) {
|
|
26
|
+
renderRefusal([v('remote', 'no-upstream', pull.out.trim().slice(0, 120), 'an upstream — git push -u origin main once, then witness sync')]).forEach((l) => ctx.err(l));
|
|
27
|
+
return EXIT.REFUSED;
|
|
28
|
+
}
|
|
29
|
+
tryGit(root, 'rebase', '--abort');
|
|
30
|
+
ctx.out(kv('sync', 'rebase conflict — resolve manually, then re-run witness sync'));
|
|
31
|
+
ctx.out(kv('detail', pull.out.trim().slice(-200)));
|
|
32
|
+
return EXIT.FINDINGS;
|
|
33
|
+
}
|
|
34
|
+
const push = tryGit(root, 'push');
|
|
35
|
+
if (!push.ok) {
|
|
36
|
+
ctx.out(kv('sync', `push failed: ${push.out.trim().slice(-200)}`));
|
|
37
|
+
return EXIT.FINDINGS;
|
|
38
|
+
}
|
|
39
|
+
ctx.out(kv('sync', 'pulled --rebase and pushed accumulated state commits'));
|
|
40
|
+
return EXIT.OK;
|
|
41
|
+
}
|
|
42
|
+
finally {
|
|
43
|
+
lockR.value();
|
|
44
|
+
}
|
|
45
|
+
}
|