repo-harness 0.23.1 → 0.24.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +7 -2
- package/CLAUDE.md +7 -2
- package/README.md +32 -11
- package/assets/herdr/webhook-notify/notify.mjs +182 -3
- package/assets/herdr/webhook-notify/pane-status.mjs +74 -0
- package/assets/reference-configs/agentic-development-flow.md +21 -2
- package/assets/reference-configs/document-generation.md +1 -1
- package/assets/reference-configs/external-tooling.md +151 -69
- package/assets/reference-configs/global-working-rules.md +4 -1
- package/assets/reference-configs/harness-overview.md +12 -12
- package/assets/reference-configs/heartbeat-triage.md +0 -2
- package/assets/reference-configs/herdr-notify.md +35 -0
- package/assets/reference-configs/hook-operations.md +193 -42
- package/assets/reference-configs/minimal-change-hooks.md +8 -7
- package/assets/reference-configs/release-deploy.md +7 -0
- package/assets/reference-configs/strategy.md +6 -22
- package/assets/reference-configs/workflow-orchestration.md +3 -0
- package/assets/skill-commands/manifest.json +21 -1
- package/assets/skill-commands/repo-harness-architecture/references/worker.md +39 -24
- package/assets/skill-commands/repo-harness-check/references/architecture-evidence.md +13 -18
- package/assets/skill-version.json +2 -2
- package/assets/skills/repo-harness-chatgpt/references/orchestrate.md +11 -10
- package/assets/skills/repo-harness-cross-review/references/generic-review.md +22 -5
- package/assets/skills/repo-harness-herdr-portal/SKILL.md +62 -0
- package/assets/skills/repo-harness-herdr-portal/references/messaging.md +46 -0
- package/assets/skills/repo-harness-herdr-portal/references/spawn.md +144 -0
- package/assets/skills/repo-harness-setup/SKILL.md +3 -2
- package/assets/skills/repo-harness-setup/references/ci-scope.md +138 -0
- package/assets/templates/contract.template.md +7 -5
- package/assets/templates/helpers/capability-resolver.ts +307 -111
- package/assets/templates/plan.template.md +7 -6
- package/assets/templates/sprint.template.md +1 -1
- package/assets/tool-catalog.v1.json +16 -2
- package/assets/workflow-contract.v1.json +6 -9
- package/dist/hook-entry.js +2883 -2090
- package/dist/oar-review-host.js +1222 -178
- package/dist/operator-ui/assets/ArchitectureWorkspace-9kTFQyoX.js +1 -0
- package/dist/operator-ui/assets/index-BPO_QTWf.js +18 -0
- package/dist/operator-ui/assets/{index-DY7Cw9qc.css → index-DDqPSgxK.css} +1 -1
- package/dist/operator-ui/index.html +2 -2
- package/package.json +5 -8
- package/references/pm-boundary.md +1 -1
- package/scripts/acceptance-receipt.ts +79 -1980
- package/scripts/archive-workflow.sh +24 -111
- package/scripts/axr5-archctx-clean-room.ts +31 -24
- package/scripts/capability-resolver.ts +25 -525
- package/scripts/change-assessment.ts +21 -24
- package/scripts/check-agent-tooling.sh +19 -7
- package/scripts/check-ci.sh +27 -26
- package/scripts/check-context-map.ts +2 -1
- package/scripts/check-protected-paths.ts +31 -0
- package/scripts/check-state-boundaries.ts +217 -48
- package/scripts/check-tarball-install-smoke.sh +44 -47
- package/scripts/ci-path-consumers.ts +3 -1
- package/scripts/classify-historical-plans.ts +1 -1
- package/scripts/contract-worktree.sh +0 -1391
- package/scripts/heartbeat-triage.sh +0 -78
- package/scripts/install-agent-fleet.sh +1 -1
- package/scripts/lib/axr5-clean-room-runtime.ts +73 -0
- package/scripts/lib/ci-run-tests.sh +1 -1
- package/scripts/lib/project-init-lib.sh +3 -8
- package/scripts/merge-gate.ts +16 -32
- package/scripts/new-plan.sh +7 -6
- package/scripts/recovery-view-cli.ts +0 -2
- package/scripts/refresh-current-status.sh +10 -6
- package/scripts/report-ci.ts +10 -11
- package/scripts/run-harness-profile-benchmark.ts +10 -25
- package/scripts/run-skill-evals.ts +25 -47
- package/scripts/select-ci-coverage.ts +48 -8
- package/scripts/select-test-suite.ts +0 -2
- package/scripts/ship-worktrees.sh +17 -373
- package/scripts/skill-surface-select.ts +8 -2
- package/scripts/sync-codex-installed-copies.sh +18 -32
- package/scripts/sync-helper-sources.ts +42 -9
- package/scripts/sync-reference-configs.ts +1 -1
- package/scripts/test-file-durations.json +0 -8
- package/scripts/verification-plan.ts +32 -13
- package/scripts/verify-contract.sh +6 -6
- package/src/cli/chatgpt-browser/oracle-provider.ts +11 -18
- package/src/cli/chatgpt-skill/installer.ts +2 -2
- package/src/cli/commands/adoption-plan.ts +15 -2
- package/src/cli/commands/architecture-configuration.ts +3 -3
- package/src/cli/commands/architecture-debt.ts +5 -5
- package/src/cli/commands/architecture-projection.ts +60 -5
- package/src/cli/commands/brain.ts +2 -2
- package/src/cli/{runtime → commands}/candidate-reconciliation.ts +18 -7
- package/src/cli/commands/capability-context.ts +5 -7
- package/src/cli/commands/doctor.ts +5 -4
- package/src/cli/commands/external-source.ts +22 -14
- package/src/cli/commands/fleet.ts +106 -387
- package/src/cli/commands/global-runtime.ts +51 -105
- package/src/cli/commands/herdr-skill.ts +8 -8
- package/src/cli/commands/herdr.ts +25 -2
- package/src/cli/commands/init-hook.ts +72 -48
- package/src/cli/commands/init.ts +38 -756
- package/src/cli/commands/integration.ts +27 -24
- package/src/cli/commands/module.ts +1 -1
- package/src/cli/commands/operator.ts +14 -0
- package/src/cli/commands/publication.ts +21 -452
- package/src/cli/commands/refactor.ts +14 -7
- package/src/cli/commands/retention.ts +59 -10
- package/src/cli/commands/review.ts +62 -7
- package/src/cli/commands/run.ts +2 -2
- package/src/cli/commands/security.ts +2 -2
- package/src/cli/commands/state.ts +50 -67
- package/src/cli/commands/status.ts +6 -6
- package/src/cli/commands/tools.ts +2 -2
- package/src/cli/commands/upgrade.ts +14 -8
- package/src/cli/hook/agent-notice.ts +284 -0
- package/src/cli/hook/board-slice-context.ts +29 -162
- package/src/cli/hook/event-telemetry.ts +2 -2
- package/src/cli/hook/handler-registry.ts +25 -4
- package/src/cli/hook/mutation-bash.ts +165 -0
- package/src/cli/hook/mutation-guard.ts +15 -12
- package/src/cli/hook/mutation-protected-dirs.ts +6 -0
- package/src/cli/hook/protected-path-notice.ts +149 -0
- package/src/cli/hook/route-registry.ts +22 -6
- package/src/cli/hook/runtime.ts +22 -18
- package/src/cli/hook/session-context.ts +13 -19
- package/src/cli/hook/stop-handler.ts +33 -94
- package/src/cli/index.ts +31 -19
- package/src/cli/installer/configuration-ownership.ts +49 -44
- package/src/cli/installer/global-context.ts +554 -0
- package/src/cli/installer/install-profile.ts +41 -93
- package/src/cli/installer/managed-entries.ts +3 -12
- package/src/cli/installer/runtime-host-lock.ts +48 -8
- package/src/cli/installer/skill-projection.ts +7 -5
- package/src/cli/installer/targets/claude.ts +3 -3
- package/src/cli/installer/targets/codex.ts +5 -5
- package/src/cli/installer/uninstall.ts +30 -34
- package/src/cli/installer/waza-rules.ts +140 -0
- package/src/cli/mcp/audit.ts +6 -6
- package/src/cli/mcp/auth.ts +2 -6
- package/src/cli/mcp/coding-tools.ts +3 -6
- package/src/cli/mcp/coding-workspaces.ts +9 -9
- package/src/cli/mcp/fleet-tools.ts +20 -198
- package/src/cli/mcp/general-repo-access.ts +37 -132
- package/src/cli/mcp/guarded-write.ts +1 -1
- package/src/cli/mcp/process-sessions.ts +30 -45
- package/src/cli/mcp/repo.ts +3 -3
- package/src/cli/mcp/setup-ownership.ts +3 -3
- package/src/cli/mcp/setup.ts +3 -3
- package/src/cli/mcp/state-tools.ts +4 -4
- package/src/cli/mcp/uninstall.ts +9 -9
- package/src/cli/repo-adoption/target.ts +2 -2
- package/src/cli/tools/codegraph.ts +4 -4
- package/src/cli/tty-prompt.ts +2 -5
- package/src/core/adoption/minimum-harness-version.ts +9 -30
- package/src/core/adoption/workflow-contract-asset.ts +3 -9
- package/src/core/architecture/module-view.ts +4 -1
- package/src/core/architecture/projection.ts +39 -0
- package/src/core/capabilities/registry.ts +1 -1
- package/src/core/capabilities/resolver.ts +146 -0
- package/src/core/decision/pane-status.d.mts +9 -0
- package/src/core/decision/pane-status.mjs +74 -0
- package/src/core/engineers/agent-runtime-effect.ts +60 -61
- package/src/core/engineers/engineering-overlay.ts +58 -0
- package/src/core/engineers/principal-claim.ts +0 -4
- package/src/core/engineers/profile-binding.ts +1 -445
- package/src/core/engineers/scheduling.ts +359 -178
- package/src/core/evidence/fold.ts +11 -1
- package/src/core/evidence/idempotency.ts +7 -1
- package/src/core/evidence/types.ts +23 -2
- package/src/core/external-sources/admitted-binding.ts +76 -0
- package/src/core/fleet/admitted-task-message.ts +408 -0
- package/src/core/fleet/board.ts +113 -88
- package/src/core/fleet/task-inbox-layout.ts +7 -0
- package/src/core/integration/product-acceptance.ts +88 -88
- package/src/core/messages/task-message-claim.ts +18 -0
- package/src/core/messages/task-message-error.ts +13 -0
- package/src/core/operator/admitted-task-activity.ts +88 -0
- package/src/core/operator/admitted-task-context.ts +40 -0
- package/src/core/operator/fleet-snapshot.ts +37 -22
- package/src/core/operator/organization-snapshot.ts +130 -118
- package/src/core/operator/planning-snapshot.ts +75 -100
- package/src/core/operator/runtime-status.ts +5 -0
- package/src/core/operator/task-activity.ts +5 -17
- package/src/core/operator/task-diff.ts +18 -31
- package/src/core/publication/feedback.ts +19 -250
- package/src/core/publication/merge-readiness.ts +17 -45
- package/src/core/publication/publication-receipt.ts +1 -347
- package/src/core/review/acceptance-receipt.ts +618 -0
- package/src/core/review/authority-context.ts +207 -0
- package/src/core/review/change-assessment.ts +4 -4
- package/src/core/review/control-delegation.ts +134 -0
- package/src/core/review/module-review-prompt.ts +1 -4
- package/src/core/review/review-packet.ts +3 -3
- package/src/core/sessions/session-selector.ts +38 -0
- package/src/core/sessions/task-sessions.ts +47 -0
- package/src/core/setup/types.ts +1 -1
- package/src/core/skill-surface/catalog.ts +1 -41
- package/src/core/skill-surface/waza-shared-rules.json +4 -0
- package/src/core/state/artifact-parsers.ts +1 -1
- package/src/core/state/attempt-ledger.ts +6 -2
- package/src/core/state/coordination-identity.ts +28 -820
- package/src/core/state/project-state-snapshot.ts +0 -5
- package/src/core/state/provider-board.ts +55 -0
- package/src/core/state/sprint-backlog-rows.ts +1 -31
- package/src/core/state/types.ts +2 -235
- package/src/core/tools/catalog.ts +1 -1
- package/src/effects/admission/admission-source.ts +32 -0
- package/src/effects/admission/admission-store.ts +196 -0
- package/src/effects/admission/control-admission.ts +25 -0
- package/src/effects/admission/issue-goal.ts +71 -0
- package/src/{core → effects}/adoption/gitignore-plan.ts +2 -2
- package/src/{core → effects}/adoption/manifest-templates.ts +1 -1
- package/src/{core → effects}/adoption/plan.ts +5 -5
- package/src/{core → effects}/adoption/standard-plan.ts +7 -9
- package/src/effects/adoption/workflow-marker.ts +31 -0
- package/src/effects/{capabilities → architecture}/archcontext-owners.ts +21 -10
- package/src/effects/architecture/archcontext-resolve-cache.ts +77 -0
- package/src/effects/architecture/archctx-provider.ts +81 -38
- package/src/effects/architecture/capability-resolver.ts +428 -0
- package/src/effects/architecture/model-reader.ts +10 -11
- package/src/effects/architecture/projection-writer.ts +44 -0
- package/src/effects/artifact-retention.ts +101 -23
- package/src/effects/{evidence/atomic-append.ts → atomic-append.ts} +1 -1
- package/src/effects/dev-activity/collector.ts +26 -16
- package/src/effects/engineers/agent-runtime-adapters/herdr-cli-agent.ts +11 -4
- package/src/effects/engineers/agent-runtime-effect-store.ts +120 -118
- package/src/effects/engineers/dependency-authority.ts +256 -255
- package/src/effects/engineers/engineering-overlay.ts +65 -256
- package/src/effects/engineers/scheduling.ts +117 -153
- package/src/effects/evidence/attested-import.ts +37 -116
- package/src/effects/evidence/blob-store.ts +1 -1
- package/src/effects/evidence/eval-catalog.ts +101 -0
- package/src/effects/evidence/event-log.ts +99 -31
- package/src/effects/evidence/recovery-materializer.ts +21 -108
- package/src/effects/evidence/verification-execution.ts +246 -145
- package/src/effects/expensive-run-lock.ts +2 -2
- package/src/effects/external-sources/binding.ts +77 -172
- package/src/effects/external-sources/refresh.ts +2 -2
- package/src/effects/external-sources/store.ts +159 -21
- package/src/effects/fleet/board.ts +223 -228
- package/src/effects/{publication/feedback.ts → fleet/provider-feedback.ts} +134 -833
- package/src/effects/fleet/task-inbox-layout-migration.ts +6 -5
- package/src/effects/fleet/task-inbox.ts +273 -209
- package/src/effects/fs-transaction.ts +200 -138
- package/src/effects/git/common-directory.ts +4 -6
- package/src/effects/git/readonly-virtual-tree.ts +3 -2
- package/src/effects/git/repository-identity.ts +75 -0
- package/src/effects/git/run-git.ts +15 -0
- package/src/effects/git/runner.ts +114 -0
- package/src/effects/{terminal/task-worktree.ts → git/task-repository.ts} +7 -6
- package/src/effects/git/worktree-topology.ts +3 -2
- package/src/effects/{external-sources/github.ts → github/issues.ts} +7 -2
- package/src/effects/hook-state-file.ts +42 -0
- package/src/effects/host-skill-roots.ts +39 -0
- package/src/effects/integration/product-acceptance.ts +118 -100
- package/src/effects/locking/exclusive-directory-lock.ts +61 -11
- package/src/effects/locking/process-start-time.ts +107 -0
- package/src/effects/locking/task-lock.ts +17 -0
- package/src/effects/managed-tree.ts +78 -0
- package/src/effects/operator/admitted-task-activity.ts +36 -0
- package/src/effects/operator/architecture.ts +1 -1
- package/src/effects/operator/fleet-collector-process.ts +2 -2
- package/src/effects/operator/repository-snapshot.ts +17 -91
- package/src/effects/operator/runtime-source.ts +2 -2
- package/src/effects/operator/runtime-status.ts +1 -6
- package/src/effects/operator/server.ts +106 -103
- package/src/effects/operator/task-activity.ts +2 -3
- package/src/effects/operator/task-context.ts +30 -40
- package/src/effects/operator/task-diff.ts +11 -73
- package/src/effects/operator/task-history.ts +3 -3
- package/src/effects/operator/task-read-process.ts +15 -7
- package/src/effects/process-group-registry.ts +169 -0
- package/src/effects/process-group-termination.ts +132 -0
- package/src/effects/process-runner.ts +28 -43
- package/src/effects/process-supervisor.ts +42 -77
- package/src/effects/{engineers → profiles}/profile-store.ts +3 -42
- package/src/effects/publication/feedback-store.ts +3 -245
- package/src/effects/publication/merge-readiness.ts +174 -351
- package/src/effects/refactor/archctx-provider.ts +19 -9
- package/src/effects/refactor/recommendations.ts +7 -5
- package/src/effects/repo-registry.ts +50 -99
- package/src/effects/review/acceptance-receipt.ts +1223 -0
- package/src/effects/review/change-assessment.ts +10 -61
- package/src/effects/review/diff-fingerprint.ts +6 -5
- package/src/effects/review/generic-review.ts +67 -42
- package/src/effects/review/oar-review-host.ts +11 -3
- package/src/effects/review/pre-prompt-inbox-consumer.ts +94 -0
- package/src/effects/review/pre-prompt-inbox.ts +112 -0
- package/src/effects/runtime/helper-runner.ts +16 -16
- package/src/effects/runtime/node-candidates.ts +2 -2
- package/src/effects/runtime/node-runtime.ts +66 -0
- package/src/effects/runtime/os-account-home.ts +44 -0
- package/src/effects/{hook-event-log.ts → segmented-log.ts} +21 -16
- package/src/effects/sessions/admitted-source.ts +110 -0
- package/src/effects/sessions/task-sessions.ts +73 -0
- package/src/effects/setup/collector.ts +33 -16
- package/src/{core/adoption → effects}/source-checkout.ts +1 -1
- package/src/effects/state/attempt-ledger-store.ts +103 -5
- package/src/effects/state/canonical-target.ts +36 -0
- package/src/effects/state/collect-provider-board-inputs.ts +43 -0
- package/src/effects/state/collect-slice-inputs.ts +20 -122
- package/src/effects/state/collect-state-inputs.ts +1 -1
- package/src/effects/state/coordination-canonical-source.ts +3 -7
- package/src/effects/state/coordination-provider-merge.ts +43 -0
- package/src/effects/state/coordination-worktree-topology.ts +16 -20
- package/src/effects/state/git-state-version-store.ts +17 -41
- package/src/effects/state/pruned-worktree-intents.ts +1 -1
- package/src/effects/state/resolve-continuation-envelope.ts +2 -2
- package/src/effects/state/resolve-effective-state.ts +161 -127
- package/src/effects/state/worktree-trash.ts +5 -7
- package/src/effects/strategy/context.ts +24 -12
- package/src/effects/strategy/skill.ts +5 -3
- package/src/effects/terminal/herdr-decision.ts +98 -0
- package/src/effects/terminal/herdr-observation.ts +1 -2
- package/src/effects/terminal/native-task-session.ts +53 -0
- package/src/effects/terminal/task-session-io.ts +29 -0
- package/src/effects/terminal/task-session-reader.ts +63 -0
- package/src/effects/terminal/task-session.ts +193 -160
- package/src/{core → effects}/upgrade/legacy-inventory.ts +6 -6
- package/src/operator-web/AdmittedTaskActivity.tsx +61 -0
- package/src/operator-web/AdmittedTaskContext.tsx +31 -0
- package/src/operator-web/App.tsx +3 -3
- package/src/operator-web/OrganizationSummary.tsx +48 -17
- package/src/operator-web/PlanningView.tsx +29 -34
- package/src/operator-web/RepositoryWorkspace.tsx +29 -34
- package/src/operator-web/TaskDiff.tsx +59 -12
- package/src/operator-web/TaskEvidence.tsx +8 -45
- package/src/operator-web/TaskHistory.tsx +3 -1
- package/src/operator-web/app.css +1 -2
- package/src/operator-web/fixture.ts +60 -50
- package/src/operator-web/fixtures/index.ts +2 -2
- package/src/operator-web/i18n.ts +60 -64
- package/src/operator-web/task-activity.ts +1 -1
- package/src/operator-web/task-context.ts +8 -10
- package/src/operator-web/task-location.ts +21 -12
- package/src/operator-web/types.ts +56 -45
- package/src/operator-web/worklist.ts +5 -4
- package/assets/pipeline-record.v2.schema.json +0 -1216
- package/dist/oar-coding-host.js +0 -708
- package/dist/operator-ui/assets/ArchitectureWorkspace-DYg_H3WQ.js +0 -1
- package/dist/operator-ui/assets/index-aOt5ob6i.js +0 -18
- package/scripts/run-bounded-verifier-command.ts +0 -243
- package/scripts/sprint-backlog.sh +0 -352
- package/src/cli/commands/sprint.ts +0 -85
- package/src/core/docs/docs-graph.ts +0 -287
- package/src/core/fleet/task-offer.ts +0 -223
- package/src/core/messages/document-handoff.ts +0 -281
- package/src/core/operator/task-context.ts +0 -117
- package/src/core/publication/publication-lifecycle.ts +0 -246
- package/src/core/state/lease-liveness.ts +0 -186
- package/src/core/state/project-board-slice.ts +0 -129
- package/src/core/state/project-board.ts +0 -414
- package/src/core/state/sprint-row-completion.ts +0 -128
- package/src/core/state/sprint-schema-v1.ts +0 -206
- package/src/core/strategy/wake.ts +0 -62
- package/src/effects/engineers/binding-store.ts +0 -187
- package/src/effects/engineers/claim-actor-store.ts +0 -116
- package/src/effects/fleet/acquire.ts +0 -189
- package/src/effects/operator/docs-graph.ts +0 -158
- package/src/effects/publication/publication-lifecycle.ts +0 -1050
- package/src/effects/publication/publication-receipt.ts +0 -765
- package/src/effects/state/collect-board-inputs.ts +0 -276
- package/src/effects/state/coordination-claim-token.ts +0 -152
- package/src/effects/state/coordination-cutover.ts +0 -308
- package/src/effects/state/coordination-lease-liveness-store.ts +0 -86
- package/src/effects/state/coordination-lease-store.ts +0 -369
- package/src/effects/state/coordination-sprint.ts +0 -866
- package/src/effects/state/owed-capabilities.ts +0 -159
- package/src/effects/state/resolve-board.ts +0 -64
- package/src/effects/state/state-cache.ts +0 -80
- package/src/effects/state/write-journal.ts +0 -101
- package/src/effects/strategy/wake.ts +0 -20
- package/src/effects/terminal/coding-isolation.ts +0 -102
- package/src/effects/terminal/coding-session.ts +0 -95
- package/src/effects/terminal/oar-coding-host.ts +0 -145
- /package/src/effects/{external-sources → github}/policy.ts +0 -0
- /package/src/effects/{state/readonly-observation.ts → readonly-observation.ts} +0 -0
- /package/src/{core → effects}/source-projection.ts +0 -0
package/AGENTS.md
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
- Keep four hard boundaries: main publication, deletion, credentials/permissions, and release/production operations.
|
|
11
11
|
- Only when the current task explicitly authorizes main publication may a model squash-merge after automated checks pass and GitHub has no outstanding change requests or unresolved review threads; an explicit no-merge instruction always wins. Push/tag still require task authorization; credentials/permissions, other deletion and release/production retain their separate approval boundaries. Record authorized publication, the provider-confirmed merge SHA and parent SHA, `git revert <squash-commit>`, and the daily report. Do not create rollback tags.
|
|
12
12
|
- Default worktrees are at `/tmp/<repo>-wt-<slug>` (Windows: `os.tmpdir()`). Commit or push before a reboot or a tmp cleanup.
|
|
13
|
-
- Closeout and sweep remove only merged, clean and unowned worktrees. To keep one, give it an unmerged commit, keep it dirty, or mark or
|
|
13
|
+
- Closeout and sweep remove only merged, clean and unowned worktrees. To keep one, give it an unmerged commit, keep it dirty, or mark or lock it. Details: [agentic-development-flow](docs/reference-configs/agentic-development-flow.md).
|
|
14
14
|
- Automatically delete only merged, clean worktrees/branches; ask the user for every other deletion. Credentials/permissions (including confirmation bypass) and release/production operations require user approval.
|
|
15
15
|
- Select independent review by the diff's [Review by risk](docs/reference-configs/agentic-development-flow.md#review-by-risk) tier: none for low, one fresh-context review for medium, gatekeeper plus cross-model review for high; ordinary steps record diagnostics without approval loops.
|
|
16
16
|
- When you delegate, investigate first, then split work by write ownership. Give each parallel writer its own worktree from a clean source. Implementation sub-agents edit only their scope. They do not run build, typecheck or tests. They commit early in their own worktree and return early. In a shared checkout they do not commit; the parent commits. Evidence roles run the named check that is their task and do not commit. The parent agent owns task management, conflict resolution, build, typecheck, tests and integration. After integration, the parent removes each sub-agent worktree and branch whose commits are in the task branch and whose checkout is clean. It also removes the intermediate build output and temporary files that the work created. Other deletion still needs the user.
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
- GitHub operations (PRs, reviews, merges, labels, releases, comments) go only through the gh/git CLI or the GitHub API. Never use simulated browser clicks on github.com: the account can be banned.
|
|
19
19
|
- When one PR changes both test assertions and implementation code, run one read-only review focused on tests bent to fit a bug (on-demand, not a restored gate; dispatch per the herdr guide).
|
|
20
20
|
- `.archcontext/model/` owns architecture boundaries; read `docs/architecture/` on demand and update only real responsibility changes. Architecture diagnostics do not block work or author nested agent instructions.
|
|
21
|
-
- When a branch changes a capability's responsibility, entrypoints, relations or flows,
|
|
21
|
+
- When a branch changes a capability's responsibility, entrypoints, relations or flows, follow `repo-harness-architecture` (`assets/skill-commands/repo-harness-architecture/references/worker.md`). Handle architecture debt in the model and human prose. The archctx rolling writer publishes generated regions and the projection manifest; do not apply and commit them in a feature PR.
|
|
22
22
|
- Preserve `agents/fleet/`, release checklists and downstream `assets/templates`. `.ai/harness/workflow-contract.json` is the opt-in marker. `assets/workflow-contract.v1.json` is the contract.
|
|
23
23
|
- `_ops/` is ignored private operations state: never commit or agent-edit it. `_ref/` is an ignored reference cache; cite influential revisions. Follow deploy SQL policy, otherwise use ascending 4-digit files under `deploy/sql/`.
|
|
24
24
|
|
|
@@ -53,6 +53,8 @@
|
|
|
53
53
|
- Isolate mutable HOME, repositories and process state; use real synchronization signals and preserve complete failure output.
|
|
54
54
|
- Report failed, timed-out, incomplete or omitted coverage explicitly; do not manufacture receipts, waiver files or pre-fix logs.
|
|
55
55
|
- Never modify or delete test assertions just to make tests pass; justify every necessary test change item by item in the PR description.
|
|
56
|
+
- Before you write or change tests, load the `repo-harness-test` skill (`assets/skills/repo-harness-test/SKILL.md`).
|
|
57
|
+
- When a local check and CI disagree, CI is the authority. Reproduce the failure with `bash scripts/check-ci.sh <lane>` before you change code; a global `repo-harness` or `repo-harness-hook` on `PATH` can hide a missing test setup.
|
|
56
58
|
|
|
57
59
|
## Handoff
|
|
58
60
|
- Create a checkpoint only when context/session rollover or unresolved work needs it; ordinary completion stays in the PR description.
|
|
@@ -73,6 +75,9 @@
|
|
|
73
75
|
- Prefer real companies, products, contracts, prices and user behavior as examples. Use a table for multi-object or multi-metric comparison. Split sections by question; drop introductions, transitions and summaries that exist only for completeness.
|
|
74
76
|
- Paragraph deletion test: if removing a paragraph leaves the reader's understanding of facts, numbers, mechanism, cause, conditions and conclusion unchanged, remove it. Check first the paragraphs that start with "this shows", "note that", "therefore", "however" or "it must be stressed".
|
|
75
77
|
|
|
78
|
+
## UI
|
|
79
|
+
- Avoid unnecessary secondary labels and descriptions. Let the layout do the explaining.
|
|
80
|
+
|
|
76
81
|
## Codex
|
|
77
82
|
- Own backend implementation and testing; Claude owns frontend implementation and interaction, and the parent coordinates scope and integration.
|
|
78
83
|
- User-level `~/.codex/hooks.json` hooks invoke `repo-harness-hook`; its typed route registry selects one in-process handler. Operator helpers run from the package through `repo-harness run <helper>`.
|
package/CLAUDE.md
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
- Keep four hard boundaries: main publication, deletion, credentials/permissions, and release/production operations.
|
|
11
11
|
- Only when the current task explicitly authorizes main publication may a model squash-merge after automated checks pass and GitHub has no outstanding change requests or unresolved review threads; an explicit no-merge instruction always wins. Push/tag still require task authorization; credentials/permissions, other deletion and release/production retain their separate approval boundaries. Record authorized publication, the provider-confirmed merge SHA and parent SHA, `git revert <squash-commit>`, and the daily report. Do not create rollback tags.
|
|
12
12
|
- Default worktrees are at `/tmp/<repo>-wt-<slug>` (Windows: `os.tmpdir()`). Commit or push before a reboot or a tmp cleanup.
|
|
13
|
-
- Closeout and sweep remove only merged, clean and unowned worktrees. To keep one, give it an unmerged commit, keep it dirty, or mark or
|
|
13
|
+
- Closeout and sweep remove only merged, clean and unowned worktrees. To keep one, give it an unmerged commit, keep it dirty, or mark or lock it. Details: [agentic-development-flow](docs/reference-configs/agentic-development-flow.md).
|
|
14
14
|
- Automatically delete only merged, clean worktrees/branches; ask the user for every other deletion. Credentials/permissions (including confirmation bypass) and release/production operations require user approval.
|
|
15
15
|
- Select independent review by the diff's [Review by risk](docs/reference-configs/agentic-development-flow.md#review-by-risk) tier: none for low, one fresh-context review for medium, gatekeeper plus cross-model review for high; ordinary steps record diagnostics without approval loops.
|
|
16
16
|
- When you delegate, investigate first, then split work by write ownership. Give each parallel writer its own worktree from a clean source. Implementation sub-agents edit only their scope. They do not run build, typecheck or tests. They commit early in their own worktree and return early. In a shared checkout they do not commit; the parent commits. Evidence roles run the named check that is their task and do not commit. The parent agent owns task management, conflict resolution, build, typecheck, tests and integration. After integration, the parent removes each sub-agent worktree and branch whose commits are in the task branch and whose checkout is clean. It also removes the intermediate build output and temporary files that the work created. Other deletion still needs the user.
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
- GitHub operations (PRs, reviews, merges, labels, releases, comments) go only through the gh/git CLI or the GitHub API. Never use simulated browser clicks on github.com: the account can be banned.
|
|
19
19
|
- When one PR changes both test assertions and implementation code, run one read-only review focused on tests bent to fit a bug (on-demand, not a restored gate; dispatch per the herdr guide).
|
|
20
20
|
- `.archcontext/model/` owns architecture boundaries; read `docs/architecture/` on demand and update only real responsibility changes. Architecture diagnostics do not block work or author nested agent instructions.
|
|
21
|
-
- When a branch changes a capability's responsibility, entrypoints, relations or flows,
|
|
21
|
+
- When a branch changes a capability's responsibility, entrypoints, relations or flows, follow `repo-harness-architecture` (`assets/skill-commands/repo-harness-architecture/references/worker.md`). Handle architecture debt in the model and human prose. The archctx rolling writer publishes generated regions and the projection manifest; do not apply and commit them in a feature PR.
|
|
22
22
|
- Preserve `agents/fleet/`, release checklists and downstream `assets/templates`. `.ai/harness/workflow-contract.json` is the opt-in marker. `assets/workflow-contract.v1.json` is the contract.
|
|
23
23
|
- `_ops/` is ignored private operations state: never commit or agent-edit it. `_ref/` is an ignored reference cache; cite influential revisions. Follow deploy SQL policy, otherwise use ascending 4-digit files under `deploy/sql/`.
|
|
24
24
|
|
|
@@ -53,6 +53,8 @@
|
|
|
53
53
|
- Isolate mutable HOME, repositories and process state; use real synchronization signals and preserve complete failure output.
|
|
54
54
|
- Report failed, timed-out, incomplete or omitted coverage explicitly; do not manufacture receipts, waiver files or pre-fix logs.
|
|
55
55
|
- Never modify or delete test assertions just to make tests pass; justify every necessary test change item by item in the PR description.
|
|
56
|
+
- Before you write or change tests, load the `repo-harness-test` skill (`assets/skills/repo-harness-test/SKILL.md`).
|
|
57
|
+
- When a local check and CI disagree, CI is the authority. Reproduce the failure with `bash scripts/check-ci.sh <lane>` before you change code; a global `repo-harness` or `repo-harness-hook` on `PATH` can hide a missing test setup.
|
|
56
58
|
|
|
57
59
|
## Handoff
|
|
58
60
|
- Create a checkpoint only when context/session rollover or unresolved work needs it; ordinary completion stays in the PR description.
|
|
@@ -73,6 +75,9 @@
|
|
|
73
75
|
- Prefer real companies, products, contracts, prices and user behavior as examples. Use a table for multi-object or multi-metric comparison. Split sections by question; drop introductions, transitions and summaries that exist only for completeness.
|
|
74
76
|
- Paragraph deletion test: if removing a paragraph leaves the reader's understanding of facts, numbers, mechanism, cause, conditions and conclusion unchanged, remove it. Check first the paragraphs that start with "this shows", "note that", "therefore", "however" or "it must be stressed".
|
|
75
77
|
|
|
78
|
+
## UI
|
|
79
|
+
- Avoid unnecessary secondary labels and descriptions. Let the layout do the explaining.
|
|
80
|
+
|
|
76
81
|
## Claude Code
|
|
77
82
|
- Own frontend implementation and interaction; Codex owns backend implementation and testing, and the parent coordinates scope and integration.
|
|
78
83
|
- Use Claude Code's available tools within granted permissions; changes to tool grants, hooks or confirmation bypass require approval of the exact change.
|
package/README.md
CHANGED
|
@@ -115,10 +115,12 @@ bun test
|
|
|
115
115
|
|
|
116
116
|
### Success looks like this
|
|
117
117
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
118
|
+
Init writes repo-local files and two files under `HOME`: the repo registry
|
|
119
|
+
entry and, when verification runs, the global Codex handoff packet under
|
|
120
|
+
`~/.codex/handoffs/`. It writes no install-managed host files. When the
|
|
121
|
+
required Herdr skill or the architecture projection preference is missing, init
|
|
122
|
+
reports a skipped step that names `repo-harness update`. repo-harness supplies tools and evidence; the Bot
|
|
123
|
+
decides what to schedule.
|
|
122
124
|
|
|
123
125
|
Apply ends with `=== Migration Report ===`, naming where generated hook behavior
|
|
124
126
|
comes from, the user-level `~/.claude/settings.json` and `~/.codex/hooks.json`
|
|
@@ -327,20 +329,39 @@ accepted suggestion goes through the normal plan and pull request workflow.
|
|
|
327
329
|
|
|
328
330
|
```bash
|
|
329
331
|
repo-harness external-source refresh # one bounded, explicitly enabled GitHub observation
|
|
330
|
-
repo-harness external-source bind
|
|
332
|
+
repo-harness external-source bind --repo <id> --source-revision <digest> --session <selector.json>
|
|
333
|
+
repo-harness external-source historical-bindings --repo <id> # retired V1 records only
|
|
331
334
|
repo-harness external-source bindings # binding edges and current drift attention
|
|
332
335
|
```
|
|
333
336
|
|
|
334
337
|
Intake is inert by design. An observed Issue mints no execution authority and
|
|
335
338
|
does not become a runnable task on its own; binding attaches an immutable source
|
|
336
|
-
revision to
|
|
339
|
+
revision to an existing admitted session selected by its provider issue, immutable
|
|
340
|
+
claim, approved grant and session binding digest. The selector comes from task
|
|
341
|
+
session discovery. Binding never creates a claim or approval.
|
|
337
342
|
|
|
338
343
|
### Persistent acceptance review
|
|
339
344
|
|
|
345
|
+
Before review, configure the repository's manual `external_sources` policy and
|
|
346
|
+
provide working provider access. The default policy is off; initialization does
|
|
347
|
+
not enable it. The trusted upstream Bot must authenticate the human, authorize
|
|
348
|
+
the scope, and supply the bound request. Its integration calls
|
|
349
|
+
`repo-harness review admit --repo <execution-checkout> --request <authorized-request.json>`.
|
|
350
|
+
The request contains `{admission, approval, expectedRevision}`. Admission requires
|
|
351
|
+
an explicit `execution_mode`: `contract` or `inline`. The mode is part of the
|
|
352
|
+
approved admission digest; changing it requires new approval. Do not create an
|
|
353
|
+
approval from these examples: actor IDs and authorization references are binding
|
|
354
|
+
data, not authentication. Select the resulting active grant and policy digests
|
|
355
|
+
below, and supply the current immutable issue claim. A contract or plan file
|
|
356
|
+
cannot grant permission. A user waiver requires a separate upstream-authorized
|
|
357
|
+
decision; waiver eligibility in an admission does not grant one.
|
|
358
|
+
|
|
359
|
+
See [the control Bot boundary](docs/control-bot-boundary.md) for the integration contract.
|
|
360
|
+
|
|
340
361
|
```bash
|
|
341
|
-
repo-harness review round --
|
|
342
|
-
repo-harness review status --
|
|
343
|
-
repo-harness review close --
|
|
362
|
+
repo-harness review round --repository-id <repository-id> --issue-id <issue-id> --campaign-id <campaign-id> --grant-sha256 <approved-grant-sha256> --policy-sha256 <policy-sha256> --actor <actor-id> --reviewer-repo <linked-checkout> --herdr-endpoint <address.json> --verification <native-report> --claim-ref <claim-reference> --claim-revision <claim-sha256>
|
|
363
|
+
repo-harness review status --repository-id <repository-id> --issue-id <issue-id> --campaign-id <campaign-id> --grant-sha256 <approved-grant-sha256> --policy-sha256 <policy-sha256> --actor <actor-id>
|
|
364
|
+
repo-harness review close --repository-id <repository-id> --issue-id <issue-id> --campaign-id <campaign-id> --grant-sha256 <approved-grant-sha256> --policy-sha256 <policy-sha256> --actor <actor-id>
|
|
344
365
|
```
|
|
345
366
|
|
|
346
367
|
In the Claude desktop app without Herdr, add `--launch app-terminal` and omit `--herdr-endpoint`. The first call prints the fixed host command. Claude runs it once in an app Terminal tab, then repeats the round. OAR, Seatbelt and the Receipt stay the same.
|
|
@@ -564,8 +585,8 @@ repo-harness commit scripts or hooks unless that repo adopts the same policy.
|
|
|
564
585
|
|
|
565
586
|
## Current Release
|
|
566
587
|
|
|
567
|
-
- npm package: `repo-harness@0.
|
|
568
|
-
- Generated workflow stamp: `repo-harness@0.
|
|
588
|
+
- npm package: `repo-harness@0.24.2`
|
|
589
|
+
- Generated workflow stamp: `repo-harness@0.24.2+template@0.24.2`
|
|
569
590
|
- GitHub repository: `Ancienttwo/repo-harness`
|
|
570
591
|
- Release notes and history: [`docs/CHANGELOG.md`](docs/CHANGELOG.md)
|
|
571
592
|
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
// Adapted from ~/herdr-plugins/webhook-notify. Config and identity come from Herdr.
|
|
2
|
-
import { existsSync, lstatSync, readFileSync, realpathSync, renameSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { constants, closeSync, fchmodSync, fstatSync, mkdirSync, openSync, readSync, rmdirSync, existsSync, lstatSync, readFileSync, realpathSync, renameSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
3
3
|
import { systemWorktreeRoot } from './worktree-location.mjs';
|
|
4
|
-
import { randomUUID } from 'node:crypto';
|
|
4
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
5
5
|
import { hostname, tmpdir } from 'node:os';
|
|
6
6
|
import { basename, dirname, isAbsolute, join, resolve, sep } from 'node:path';
|
|
7
7
|
import { parseEnv } from 'node:util';
|
|
8
8
|
import { pathToFileURL } from 'node:url';
|
|
9
9
|
|
|
10
|
+
import { execFile } from 'node:child_process';
|
|
11
|
+
import { promisify } from 'node:util';
|
|
12
|
+
import { POLICY_VERSION, paneStatusRequest, decodeClefAnswer, decisionBand } from './pane-status.mjs';
|
|
13
|
+
|
|
10
14
|
const DEBOUNCE_MS = 120_000;
|
|
11
15
|
const TIMEOUT_MS = 5_000;
|
|
12
16
|
const log = (message) => console.error(`[webhook-notify] ${message}`);
|
|
@@ -61,7 +65,7 @@ function isTemporary(path, managedRoot) {
|
|
|
61
65
|
export async function notify(env = process.env, send = fetch, options = {}) {
|
|
62
66
|
const event = objectJson(env.HERDR_PLUGIN_EVENT_JSON);
|
|
63
67
|
const context = objectJson(env.HERDR_PLUGIN_CONTEXT_JSON);
|
|
64
|
-
if (event.event !== '
|
|
68
|
+
if (event.event !== 'pane_agent_status_changed') return;
|
|
65
69
|
const data = event.data;
|
|
66
70
|
if (!data || !['done', 'blocked'].includes(data.agent_status)) return;
|
|
67
71
|
if (typeof data.pane_id !== 'string' || !data.pane_id || typeof data.workspace_id !== 'string') throw new Error('Invalid event identity.');
|
|
@@ -131,6 +135,181 @@ export async function notify(env = process.env, send = fetch, options = {}) {
|
|
|
131
135
|
}
|
|
132
136
|
} catch { log(`${target.channel}: delivery failed.`); }
|
|
133
137
|
}
|
|
138
|
+
// Shadow work starts only after all existing notification attempts finish.
|
|
139
|
+
if (config.CLEF_MODE === 'shadow' && config.CLOUDFLARE_ACCOUNT_ID && config.CLOUDFLARE_API_TOKEN
|
|
140
|
+
&& data.agent_status === 'done' && ['claude', 'codex'].includes(data.agent)) {
|
|
141
|
+
try { await shadowDecision(env, config, data, send); }
|
|
142
|
+
catch { log('Shadow record failed.'); }
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const runFile = promisify(execFile);
|
|
147
|
+
const SHADOW_LIMIT = 20 * 1024 * 1024;
|
|
148
|
+
const RETENTION_MS = 14 * 24 * 60 * 60 * 1000;
|
|
149
|
+
const MODELS = ['clef-flash', 'clef'];
|
|
150
|
+
|
|
151
|
+
// Fixed data-safety rules. These patterns never classify the pane outcome.
|
|
152
|
+
export function redactTail(text, config = {}) {
|
|
153
|
+
let value = text.replace(/\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)/g, '').replace(/\x1b\[[0-?]*[ -/]*[@-~]/g, '');
|
|
154
|
+
for (const [key, secret] of Object.entries(config)) {
|
|
155
|
+
if (secret && /(?:TOKEN|KEY|URL|ACCOUNT_ID|CHAT_ID)$/.test(key)) value = value.replaceAll(secret, '[redacted]');
|
|
156
|
+
}
|
|
157
|
+
return value
|
|
158
|
+
.replace(/\b[A-Z][A-Z0-9_]*(?:\s*=\s*)(?:"[^"\r\n]*"|'[^'\r\n]*'|[^\s]+)/gi, '[redacted]')
|
|
159
|
+
.replace(/\bBearer\s+[^\s"'<>]+/gi, 'Bearer [redacted]')
|
|
160
|
+
.replace(/\b(?:sk-[A-Za-z0-9_-]+|gh[pousr]_[A-Za-z0-9_]+|github_pat_[A-Za-z0-9_]+)/g, '[redacted]')
|
|
161
|
+
.replace(/\b[0-9a-fA-F]{32,}\b/g, '[redacted]')
|
|
162
|
+
.replace(/[A-Za-z0-9+/_-]{40,}={0,2}/g, '[redacted]');
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function boundedTail(raw, config) {
|
|
166
|
+
const lines = raw.replace(/\r\n/g, '\n').split('\n');
|
|
167
|
+
if (lines.at(-1) === '') lines.pop();
|
|
168
|
+
const redacted = redactTail(lines.slice(-120).join('\n'), config);
|
|
169
|
+
const bytes = Buffer.from(redacted);
|
|
170
|
+
// Avoid splitting a UTF-8 character at the start of the retained suffix.
|
|
171
|
+
let start = Math.max(0, bytes.length - 16 * 1024);
|
|
172
|
+
while (start < bytes.length && (bytes[start] & 0xc0) === 0x80) start++;
|
|
173
|
+
const tail = bytes.subarray(start).toString('utf8');
|
|
174
|
+
return { tail, tail_lines: tail ? tail.split('\n').length : 0, tail_truncated: lines.length >= 120 || start > 0 };
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function privateRead(path, limit) {
|
|
178
|
+
let fd;
|
|
179
|
+
try {
|
|
180
|
+
fd = openSync(path, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
|
|
181
|
+
const stat = fstatSync(fd);
|
|
182
|
+
if (!stat.isFile() || stat.size > limit || stat.uid !== process.getuid?.()) throw new Error('Invalid shadow file.');
|
|
183
|
+
fchmodSync(fd, 0o600);
|
|
184
|
+
const bytes = Buffer.alloc(stat.size);
|
|
185
|
+
let count = 0;
|
|
186
|
+
while (count < bytes.length) {
|
|
187
|
+
const read = readSync(fd, bytes, count, bytes.length - count, count);
|
|
188
|
+
if (!read) throw new Error('Incomplete shadow file.');
|
|
189
|
+
count += read;
|
|
190
|
+
}
|
|
191
|
+
return bytes.toString('utf8');
|
|
192
|
+
} catch (error) { if (error.code === 'ENOENT') return ''; throw error; }
|
|
193
|
+
finally { if (fd !== undefined) closeSync(fd); }
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function privateWrite(path, text) {
|
|
197
|
+
const temporary = `${path}-${randomUUID()}`;
|
|
198
|
+
try {
|
|
199
|
+
writeFileSync(temporary, text, { mode: 0o600, flag: 'wx' });
|
|
200
|
+
renameSync(temporary, path);
|
|
201
|
+
} finally { try { unlinkSync(temporary); } catch (error) { if (error.code !== 'ENOENT') throw error; } }
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
async function withShadowLock(dir, action) {
|
|
205
|
+
const lock = join(dir, 'decision-shadow.lock');
|
|
206
|
+
const deadline = Date.now() + 2000;
|
|
207
|
+
for (;;) {
|
|
208
|
+
try { mkdirSync(lock, { mode: 0o700 }); break; }
|
|
209
|
+
catch (error) {
|
|
210
|
+
if (error.code !== 'EEXIST' || Date.now() >= deadline) throw error;
|
|
211
|
+
await new Promise(resolve => setTimeout(resolve, 10));
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
try { return action(); } finally { rmdirSync(lock); }
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
async function reserveCalls(dir, now) {
|
|
218
|
+
return withShadowLock(dir, () => {
|
|
219
|
+
const path = join(dir, 'decision-budget.json');
|
|
220
|
+
const raw = privateRead(path, 1024);
|
|
221
|
+
const day = new Date(now).toISOString().slice(0, 10);
|
|
222
|
+
const saved = raw ? JSON.parse(raw) : { day, calls: 0 };
|
|
223
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(saved.day) || !Number.isSafeInteger(saved.calls)
|
|
224
|
+
|| saved.calls < 0 || saved.calls > 2000 || saved.day > day) throw new Error('Invalid call budget.');
|
|
225
|
+
const count = saved.day === day ? saved.calls : 0;
|
|
226
|
+
const allowed = Math.min(MODELS.length, 2000 - count);
|
|
227
|
+
privateWrite(path, JSON.stringify({ day, calls: count + allowed }));
|
|
228
|
+
return allowed;
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
async function saveShadow(dir, record) {
|
|
233
|
+
await withShadowLock(dir, () => {
|
|
234
|
+
const path = join(dir, 'decision-shadow.jsonl');
|
|
235
|
+
const raw = privateRead(path, SHADOW_LIMIT);
|
|
236
|
+
const cutoff = Date.now() - RETENTION_MS;
|
|
237
|
+
const lines = raw.split('\n').filter(Boolean).filter(line => {
|
|
238
|
+
const ts = Date.parse(JSON.parse(line).ts);
|
|
239
|
+
if (!Number.isFinite(ts)) throw new Error('Invalid shadow timestamp.');
|
|
240
|
+
return ts >= cutoff;
|
|
241
|
+
});
|
|
242
|
+
lines.push(JSON.stringify(record));
|
|
243
|
+
let size = lines.reduce((sum, line) => sum + Buffer.byteLength(line) + 1, 0);
|
|
244
|
+
let start = 0;
|
|
245
|
+
while (size > SHADOW_LIMIT && start < lines.length) size -= Buffer.byteLength(lines[start++]) + 1;
|
|
246
|
+
privateWrite(path, lines.slice(start).join('\n') + '\n');
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
async function saveRecord(env, record) {
|
|
251
|
+
await saveShadow(env.HERDR_PLUGIN_STATE_DIR, record);
|
|
252
|
+
privateWrite(join(env.HERDR_PLUGIN_CONFIG_DIR, 'decision-state.json'), JSON.stringify({ protocol: 1, state_dir: env.HERDR_PLUGIN_STATE_DIR }));
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
async function modelDecision(model, state, config, send) {
|
|
256
|
+
const started = performance.now();
|
|
257
|
+
const controller = new AbortController();
|
|
258
|
+
let timer;
|
|
259
|
+
try {
|
|
260
|
+
const response = await Promise.race([
|
|
261
|
+
(async () => {
|
|
262
|
+
const reply = await send(`https://api.cloudflare.com/client/v4/accounts/${config.CLOUDFLARE_ACCOUNT_ID}/ai/run/@cf/cloudflare/${model}`, {
|
|
263
|
+
method: 'POST', redirect: 'error', signal: controller.signal,
|
|
264
|
+
headers: { 'content-type': 'application/json', authorization: `Bearer ${config.CLOUDFLARE_API_TOKEN}` },
|
|
265
|
+
body: JSON.stringify(paneStatusRequest(state, model)),
|
|
266
|
+
});
|
|
267
|
+
if (!reply.ok || reply.redirected) throw new Error('Clef unavailable.');
|
|
268
|
+
const envelope = await reply.json();
|
|
269
|
+
if (envelope?.success !== true) throw new Error('Clef unavailable.');
|
|
270
|
+
return decodeClefAnswer(envelope.result, model);
|
|
271
|
+
})(),
|
|
272
|
+
new Promise((_, reject) => { timer = setTimeout(() => { controller.abort(); reject(new Error('Clef timeout.')); }, 3000); }),
|
|
273
|
+
]);
|
|
274
|
+
return { model, ...response, band: decisionBand(response), latency_ms: Math.round(performance.now() - started) };
|
|
275
|
+
} catch { return { model, status: 'unavailable', latency_ms: Math.round(performance.now() - started) }; }
|
|
276
|
+
finally { clearTimeout(timer); }
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
async function shadowDecision(env, config, data, send) {
|
|
280
|
+
const record = {
|
|
281
|
+
event_id: randomUUID(), ts: new Date().toISOString(), pane_id: data.pane_id,
|
|
282
|
+
agent: data.agent, herdr_status: data.agent_status, policy_version: POLICY_VERSION,
|
|
283
|
+
tail_sha256: null, tail: null, models: [],
|
|
284
|
+
};
|
|
285
|
+
let state;
|
|
286
|
+
try {
|
|
287
|
+
if (!/^[a-f0-9]{32}$/i.test(config.CLOUDFLARE_ACCOUNT_ID)) throw new Error('Invalid account id.');
|
|
288
|
+
if (typeof env.HERDR_BIN_PATH !== 'string' || !isAbsolute(env.HERDR_BIN_PATH)) throw new Error('Herdr binary path is missing.');
|
|
289
|
+
// Do not pass channel or Cloudflare secrets to the read-only Herdr child.
|
|
290
|
+
const childEnv = { ...env };
|
|
291
|
+
for (const key of Object.keys(childEnv)) if (key.startsWith('CLOUDFLARE_') || /^(WEBHOOK_|SLACK_|DISCORD_|TELEGRAM_)/.test(key)) delete childEnv[key];
|
|
292
|
+
const raw = (await runFile(env.HERDR_BIN_PATH, ['pane', 'read', data.pane_id, '--source', 'recent', '--lines', '120'], {
|
|
293
|
+
env: childEnv, encoding: 'utf8', timeout: 2000, maxBuffer: 1024 * 1024,
|
|
294
|
+
})).stdout;
|
|
295
|
+
state = { agent: data.agent, herdr_status: data.agent_status, pane_title: redactTail(String(data.title ?? ''), config).slice(0, 512), ...boundedTail(raw, config) };
|
|
296
|
+
record.tail = state.tail;
|
|
297
|
+
record.tail_sha256 = createHash('sha256').update(state.tail).digest('hex');
|
|
298
|
+
} catch {
|
|
299
|
+
record.models = MODELS.map(model => ({ model, status: 'unavailable' }));
|
|
300
|
+
await saveRecord(env, record);
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
let allowed;
|
|
304
|
+
try { allowed = await reserveCalls(env.HERDR_PLUGIN_STATE_DIR, Date.now()); }
|
|
305
|
+
catch {
|
|
306
|
+
record.models = MODELS.map(model => ({ model, status: 'unavailable' }));
|
|
307
|
+
await saveRecord(env, record);
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
record.models = await Promise.all(MODELS.map((model, index) => index < allowed
|
|
311
|
+
? modelDecision(model, state, config, send) : { model, status: 'skipped_cap' }));
|
|
312
|
+
await saveRecord(env, record);
|
|
134
313
|
}
|
|
135
314
|
|
|
136
315
|
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// Pure pane decision contract. The plugin projection must match these bytes.
|
|
2
|
+
export const POLICY_VERSION = 'pane-status-shadow-v1';
|
|
3
|
+
export const OPTIONS = Object.freeze(['done', 'stuck', 'model_error', 'quota', 'unclear']);
|
|
4
|
+
const thresholds = Object.freeze({ suggest: null, apply: null });
|
|
5
|
+
export const POLICY = Object.freeze({
|
|
6
|
+
review: 0.60, shadow: 0.85,
|
|
7
|
+
labels: Object.freeze({ stuck: thresholds, model_error: thresholds, quota: thresholds }),
|
|
8
|
+
escalate: Object.freeze(['stuck']),
|
|
9
|
+
});
|
|
10
|
+
const QUESTIONS = {
|
|
11
|
+
"pane.outcome": {
|
|
12
|
+
"type": "choice",
|
|
13
|
+
"instructions": "A terminal coding agent stopped and now waits at its input prompt. Read the end of its terminal output. Decide why it stopped.",
|
|
14
|
+
"criteria": {
|
|
15
|
+
"done": "The agent finished the work it was given. Its last message reports a result: a summary, a RESULT block, a PR or commit link, or a test outcome (pass or fail). It waits for a new task.",
|
|
16
|
+
"stuck": "The agent stopped before the work was finished, for a reason inside the task. It asks the operator a question, waits for a choice or confirmation, repeats the same failed step, or says it cannot continue without help.",
|
|
17
|
+
"model_error": "The model provider or the agent CLI reported an error that ended the turn. Examples of situations: an API or server error, the model is overloaded or unavailable, the request or context is too large, the connection failed, the CLI crashed.",
|
|
18
|
+
"quota": "The provider refused more work because of a usage limit: a rate limit, a usage or plan cap, a credit or billing limit, or a message that the limit resets at a later time.",
|
|
19
|
+
"unclear": "The output does not show enough to decide, or the screen shows something other than an agent session (a shell prompt, an editor, empty output)."
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"pane.result_present": {
|
|
23
|
+
"type": "noul",
|
|
24
|
+
"instructions": "Does the last agent message state a final result of the assigned work?"
|
|
25
|
+
},
|
|
26
|
+
"pane.provider_message": {
|
|
27
|
+
"type": "noul",
|
|
28
|
+
"instructions": "Does the output end with a message from the model provider or the CLI, not from the agent's own reasoning?"
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export function paneStatusRequest(state, model = 'clef-flash') {
|
|
33
|
+
if (!['clef-flash', 'clef'].includes(model) || !['claude', 'codex'].includes(state.agent)
|
|
34
|
+
|| state.herdr_status !== 'done' || typeof state.pane_title !== 'string'
|
|
35
|
+
|| !Number.isInteger(state.tail_lines) || state.tail_lines < 0 || state.tail_lines > 120
|
|
36
|
+
|| typeof state.tail_truncated !== 'boolean' || typeof state.tail !== 'string'
|
|
37
|
+
|| new TextEncoder().encode(state.tail).length > 16 * 1024) throw new Error('Invalid pane state.');
|
|
38
|
+
return { model, state: {
|
|
39
|
+
agent: state.agent, herdr_status: state.herdr_status, pane_title: state.pane_title,
|
|
40
|
+
tail_lines: state.tail_lines, tail_truncated: state.tail_truncated, tail: state.tail,
|
|
41
|
+
}, questions: structuredClone(QUESTIONS) };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const object = value => value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
45
|
+
const probability = value => typeof value === 'number' && Number.isFinite(value) && value >= 0 && value <= 1;
|
|
46
|
+
export function decodeClefAnswer(value, model = 'clef-flash') {
|
|
47
|
+
const invalid = { status: 'invalid' };
|
|
48
|
+
if (!object(value) || value.model !== model || !object(value.answers) || !object(value.usage)
|
|
49
|
+
|| !Number.isSafeInteger(value.usage.input_tokens) || value.usage.input_tokens < 0
|
|
50
|
+
|| !Number.isSafeInteger(value.usage.output_tokens) || value.usage.output_tokens < 0) return invalid;
|
|
51
|
+
const answer = value.answers['pane.outcome'];
|
|
52
|
+
if (!object(answer) || answer.type !== 'choice' || !OPTIONS.includes(answer.choice)
|
|
53
|
+
|| !probability(answer.confidence) || !object(answer.probabilities)
|
|
54
|
+
|| Object.keys(answer.probabilities).length !== OPTIONS.length
|
|
55
|
+
|| OPTIONS.some(option => !probability(answer.probabilities[option]))) return invalid;
|
|
56
|
+
const probabilities = Object.fromEntries(OPTIONS.map(option => [option, answer.probabilities[option]]));
|
|
57
|
+
if (Math.abs(Object.values(probabilities).reduce((sum, p) => sum + p, 0) - 1) > 0.010000000001
|
|
58
|
+
|| Object.values(probabilities).some(p => p > probabilities[answer.choice])) return invalid;
|
|
59
|
+
const evidence = {};
|
|
60
|
+
for (const id of ['pane.result_present', 'pane.provider_message']) {
|
|
61
|
+
const item = value.answers[id];
|
|
62
|
+
if (!object(item) || item.type !== 'noul' || !probability(item.noul)) return invalid;
|
|
63
|
+
evidence[id] = item.noul;
|
|
64
|
+
}
|
|
65
|
+
return { status: 'ok', choice: answer.choice, probabilities, confidence: answer.confidence,
|
|
66
|
+
input_tokens: value.usage.input_tokens, evidence };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Promoted thresholds stay null. Shadow bands use the provisional threshold.
|
|
70
|
+
export function decisionBand(answer, mode = 'shadow') {
|
|
71
|
+
if (answer.status !== 'ok' || answer.choice === 'unclear' || answer.confidence < POLICY.review) return 'abstain';
|
|
72
|
+
const threshold = mode === 'shadow' ? POLICY.shadow : POLICY.labels[answer.choice]?.[mode];
|
|
73
|
+
return threshold !== null && threshold !== undefined && answer.confidence >= threshold ? 'act' : 'review';
|
|
74
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Agentic development flow
|
|
2
2
|
|
|
3
3
|
- Default task worktrees use `/tmp/<repo>-wt-<slug>` on POSIX. Native Windows uses `os.tmpdir()` as the root. The stored default is `{{system_tmp}}/{{repo}}-wt-{{slug}}`. Runtime code resolves the temp token. Explicit stored templates stay unchanged. Commit or push work before a reboot or age-based tmp cleanup, such as `systemd-tmpfiles`. These cleaners can remove checkouts. Missing entries are pruned. The sweep saves missing registration identities before global prune. Later batches finish deferred branch cleanup. Unmerged branches remain.
|
|
4
|
-
- Closeout removes a merged, clean checkout and its branch. It releases only that checkout's own canonical marker after merge proof under the topology lock. The sweep keeps dirty, locked
|
|
4
|
+
- Closeout removes a merged, clean checkout and its branch. It releases only that checkout's own canonical marker after merge proof under the topology lock. The sweep keeps dirty, locked and marked worktrees. It keeps branch reflogs without a commit-creation message and missing or unreadable reflogs. Fast-forward, target-only automatic merges, reset and rebase-finish messages do not count. Dispatch must establish an unmerged commit or keep the worktree dirty, locked or marked. A merged, clean, unowned branch with its own commits can be swept.
|
|
5
5
|
- `start` recovers only a real target directory registered by this clone on the expected branch, under the topology lock, with no lock or other owner. Recovery requires a current-user-owned checkout directory without group or other write access, a regular owned Git pointer, its matching admin backpointer, and the original directory identity saved by start. Git metadata can be group-writable but must not be other-writable. Start still creates a checkout when uid is unavailable; recovery then refuses. Exact removal retires the identity after Git readback. Missing identity or a replacement directory blocks recovery. Files, symlinks, unregistered paths and other branches are refused. Cleanup refuses a registered worktree whose branch is missing. MCP cleanup also refuses active markers. Sweep reports name processed paths. Deferred entries are reported as counts. `kept` excludes deferred entries. A command timeout sends SIGTERM to the direct child only. A grandchild can briefly extend the deadline. No detached cleanup process starts. Herdr notifications accept registered default task worktrees and paths inside them. Other temporary paths and `rh-herdr-` labels stay filtered.
|
|
6
6
|
|
|
7
7
|
## Daily Flow
|
|
@@ -9,10 +9,24 @@
|
|
|
9
9
|
2. Open a branch; isolate a worktree when concurrent edits overlap. Preserve unrelated work.
|
|
10
10
|
3. Make small, bounded commits. Ordinary tasks live in the PR description: goal, changes, verification, risk and rollback.
|
|
11
11
|
4. Freeze the candidate and verify once: typecheck plus affected tests. Reviewers and closeout consume the same evidence.
|
|
12
|
-
5. A model may squash-merge into main only when the current task explicitly authorizes main publication,
|
|
12
|
+
5. A model may squash-merge into main only when the current task explicitly authorizes main publication, the branch contains the current main tip, the required checks pass on that exact head, and GitHub has no outstanding change requests or unresolved review threads. An explicit no-merge instruction always wins. The next merge does not wait for the main push CI of the previous merge. Main push CI is an alarm: when it fails, the next merge is the revert or the fix. Record the GitHub merge SHA and parent SHA. Record `git revert --no-edit <squash-commit>`. Do not create rollback tags.
|
|
13
13
|
6. Run the full suite daily, automatically open a repair task on failure, and produce the daily merge/check/rollback report. Run the [Lessons Retro](#lessons-retro) in the same nightly window.
|
|
14
14
|
7. Use the [Report SOP](#report-sop) for formal progress, phase and final reports to the user, including the daily user summary. Raw automated CI reports are evidence inputs; they do not replace that summary and do not require this format.
|
|
15
15
|
|
|
16
|
+
## CI cost
|
|
17
|
+
Size PR CI by the diff, the same as step 4. A full suite on each small PR spends runner time and adds no evidence that the affected tests and the daily run do not already give. The repo-harness source repository uses this shape in `.github/workflows/ci.yml`.
|
|
18
|
+
|
|
19
|
+
- Triggers: run PR checks on `pull_request`. Do not also trigger `push` on feature branches; that runs each commit twice. A `push` to the default branch runs the affected selection for the merged diff, not the full suite.
|
|
20
|
+
- Superseded runs: set `concurrency` to one group per PR number with `cancel-in-progress: true` for `pull_request` events only. Do not cancel scheduled runs.
|
|
21
|
+
- Selection: a first job reads the diff against the PR base and outputs a mode. Later jobs read the mode in a job-level `if:`.
|
|
22
|
+
- A docs-only diff (`docs/**/*.md`, README, CHANGELOG, `tasks/`, `plans/`) skips build and test jobs when no test or script reads the changed files. Keep link and format checks. A document that is program input, such as a projected copy, a parsed contract or a generated region, runs its consumer tests.
|
|
23
|
+
- A code diff runs typecheck plus the tests that the diff affects. Use the runner's change detection (`vitest --changed`, `jest --changedSince`, `nx affected`, `turbo run --filter=...[<base>]`, `pytest --testmon`) or an import-graph selector.
|
|
24
|
+
- A change to a shared input (lockfile, root build or test config, CI workflow, a test helper with no mapped consumer) runs the full suite on that PR. A selector that cannot map a changed file widens the run; it never shrinks it.
|
|
25
|
+
- Required checks: skip a job with a job-level `if:`, not with workflow-level `paths` or `paths-ignore`. GitHub counts a skipped job as passing for a required check. A workflow that never starts leaves its required check pending and blocks the merge.
|
|
26
|
+
- Full suite: run it on `schedule` (daily) and `workflow_dispatch` against one default-branch SHA, and on the release SHA before a release. A daily failure opens a repair task (step 6).
|
|
27
|
+
- Matrix: each axis must change the runtime that the tests run on. When the test runner is pinned, for example one Bun version, a Node-version axis reaches only the scripts and tests that spawn Node. Run only that subset on the axis; a full suite on 3 OS × 3 Node versions repeats each OS run three times. On a PR, run a matrix cell only when the diff touches code that depends on its axis. The daily run covers each cell with its dependent subset. Set `fail-fast: false` so one cell failure does not hide the others.
|
|
28
|
+
- Limits: set `timeout-minutes` on each job; the GitHub default of 360 minutes lets a hung job spend all of it. Cache dependencies keyed on the lockfile.
|
|
29
|
+
|
|
16
30
|
## Report SOP
|
|
17
31
|
- Give a Feynman report: explain the work to a reader who does not know this project. Use plain language. Explain the goal, user value, how it works, completed work and remaining gaps. Explain each technical term at first use.
|
|
18
32
|
- State the conclusion, changes, reasons, verification and remaining risks. Link each claim to its source or label it as an inference. Use observed results. Name failed, incomplete and omitted checks.
|
|
@@ -109,6 +123,9 @@ Rules for every tier:
|
|
|
109
123
|
- From the second round, the reviewer reads only the hunks that changed after the previous round. The three-round limit stays.
|
|
110
124
|
- When an independent review ran, the PR description lists each finding with its disposition: fixed, or closed with the reason.
|
|
111
125
|
- A low tier adds no review. A new test file or a rebase does not raise the tier.
|
|
126
|
+
- A parent or coordinator reuses a worker's recorded independent review of the same diff when that review meets the diff's tier. Only a changed diff, or a missing tier review, needs a new review.
|
|
127
|
+
- A downstream repository maps its own release surfaces, for example public API, wire format, DDL/SQL and permissions, to the High tier.
|
|
128
|
+
|
|
112
129
|
A worker gets Goal, Scope, Context, Acceptance, Verify, Rollback, Timebox, Forbidden, Report and Standing as defined in [the delegation brief](sprint-contracts.md#delegation-brief), then loads concrete techniques on demand. Do not put routing catalogs or every technology guide in its brief.
|
|
113
130
|
|
|
114
131
|
A coding session that spawns sub-agents is their parent. Investigate first, then split work by write ownership. Give each parallel writer its own worktree from a clean source. Implementation sub-agents edit only their scope. They do not run build, typecheck or tests. They commit early in their own worktree and return early. In a shared checkout they do not commit; the parent commits. An evidence role whose task is a named check, such as a root-cause reproduction or an evaluation profile, still runs that check and does not commit. The brief's Verify section names the checks that the parent runs after integration. The parent owns task management, conflict resolution, build, typecheck, tests and integration. After integration, the parent removes each sub-agent worktree and branch whose commits are in the task branch and whose checkout is clean. It also removes the intermediate build output and temporary files that the work created. Other deletion still needs the user.
|
|
@@ -121,3 +138,5 @@ Stop and ask the user when a command or result names `unknown`, `ambiguous`, `pe
|
|
|
121
138
|
Architecture docs remain references. Update them only when responsibilities or boundaries change; no per-edit drift queue, workstream sync or automatic agent-context block. Ordinary work has no plan/contract/review/notes or promote/archive prerequisite. Notes are optional for non-obvious decisions; PRDs are for real new products.
|
|
122
139
|
|
|
123
140
|
Park a gate; do not block on it. A gate is a decision that only the human can make. It is one of four kinds: an irreversible action, a product or preference call that no experiment settles, a standing rule that contradicts observed reality, or a dead end that survived a replan. Frontier nudges, retries, CI flake triage, review-thread triage, format fixes and "should I keep going" never reach the human. The GitHub issue and PR are the task authority. When the task has an issue, post a parked gate in the issue status comment (the `repo-harness issue status` format) under a `Gates:` list. When the task has a PR but no issue, park it under a `Gates:` list in the PR description. When the task has neither, park it in the catch-up reply of the current conversation. Give each gate an id, the question, the options, the recommended option, and what happens if no answer arrives by a stated time. Route the remaining work around the gate. The [catch-up reply](#report-sop) lists open gates first. A reply to a gate is a human comment on the same issue. The Bot reads that comment back before it acts on it.
|
|
141
|
+
|
|
142
|
+
For a multi-PR coordination run, the issue and its PRs are the coordination state. An active plan's `## Task Breakdown` still lists the execution steps. A run log, when kept, is an append-only list of events. Derive an owner summary from the run log, or write the summary once at closeout. Approval text goes in the PR description, not in a separate file.
|
|
@@ -33,6 +33,6 @@ Create these only when the agent has concrete repo evidence or the user asks:
|
|
|
33
33
|
- Let capability `CLAUDE.md` and `AGENTS.md` carry local contract projections; root docs stay concise.
|
|
34
34
|
- Keep complete workstream TODOs in `tasks/workstreams/<domain>/<capability>/`; contract blocks should link to them instead of becoming task logs.
|
|
35
35
|
- Keep onboarding docs split by reader: agents read active source artifacts first; humans review the Human Review Card, diff, latest trace, and rollback first.
|
|
36
|
-
- `.archcontext/model/` owns architecture boundaries.
|
|
36
|
+
- `.archcontext/model/` owns architecture boundaries. Agents change the model, semantic decisions and human-written sections. The archctx rolling writer publishes generated regions and diagrams. Do not apply and commit generated output in feature PRs.
|
|
37
37
|
- Treat Markdown Mermaid as the only architecture diagram artifact; do not generate standalone HTML.
|
|
38
38
|
- Treat `mermaid` as an external authoring/review skill at `~/.codex/skills/mermaid`; do not copy or inline its assets into generated repos.
|