@mjasnikovs/pi-task 0.38.29 → 0.38.31
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/dist/config/config.d.ts +70 -70
- package/dist/config/config.js +26 -35
- package/dist/config/extension-list.d.ts +6 -5
- package/dist/config/extension-list.js +3 -2
- package/dist/config/reasoning-args.d.ts +9 -7
- package/dist/config/reasoning-args.js +12 -10
- package/dist/config/reasoning.d.ts +44 -105
- package/dist/config/reasoning.js +27 -704
- package/dist/config/register.d.ts +34 -48
- package/dist/config/register.js +41 -51
- package/dist/config/tool-list.d.ts +16 -16
- package/dist/config/tool-list.js +1 -1
- package/dist/index.js +2 -0
- package/dist/remote/bridge.d.ts +19 -10
- package/dist/remote/bridge.js +3 -2
- package/dist/remote/broadcast.js +3 -1
- package/dist/remote/events.js +12 -11
- package/dist/remote/history.d.ts +1 -1
- package/dist/remote/protocol.d.ts +6 -3
- package/dist/remote/protocol.js +2 -1
- package/dist/remote/push.d.ts +16 -16
- package/dist/remote/push.js +27 -27
- package/dist/remote/register.d.ts +3 -3
- package/dist/remote/register.js +17 -19
- package/dist/remote/server.d.ts +9 -8
- package/dist/remote/server.js +15 -14
- package/dist/remote/session-state.d.ts +5 -4
- package/dist/remote/session-state.js +8 -5
- package/dist/remote/sw.d.ts +7 -6
- package/dist/remote/sw.js +7 -6
- package/dist/remote/tailscale.d.ts +4 -2
- package/dist/remote/tailscale.js +4 -2
- package/dist/remote/ui-highlight.js +6 -5
- package/dist/remote/ui-render.js +4 -4
- package/dist/remote/ui-script.js +24 -24
- package/dist/remote/ui-styles.d.ts +1 -1
- package/dist/remote/ui-styles.js +10 -13
- package/dist/remote/ui-tools.js +9 -6
- package/dist/shared/child-extensions.d.ts +29 -17
- package/dist/shared/child-extensions.js +29 -17
- package/dist/shared/child-output.d.ts +30 -24
- package/dist/shared/child-output.js +25 -17
- package/dist/shared/child-process.d.ts +47 -40
- package/dist/shared/child-process.js +50 -59
- package/dist/shared/command-watchdog.d.ts +85 -16
- package/dist/shared/command-watchdog.js +115 -21
- package/dist/shared/fs-text.d.ts +16 -10
- package/dist/shared/fs-text.js +16 -10
- package/dist/shared/git-runner.d.ts +25 -25
- package/dist/shared/git-runner.js +25 -25
- package/dist/shared/leaked-tool-call.d.ts +17 -11
- package/dist/shared/leaked-tool-call.js +23 -15
- package/dist/shared/model-endpoint.d.ts +29 -16
- package/dist/shared/model-endpoint.js +33 -21
- package/dist/shared/pi-invocation.d.ts +7 -4
- package/dist/shared/pi-invocation.js +12 -7
- package/dist/shared/pkg-version.d.ts +13 -5
- package/dist/shared/pkg-version.js +13 -5
- package/dist/shared/reasoning-capability.d.ts +35 -24
- package/dist/shared/reasoning-capability.js +35 -24
- package/dist/shared/stream-watchdog.d.ts +60 -44
- package/dist/shared/stream-watchdog.js +62 -45
- package/dist/task/accept-debt.d.ts +41 -43
- package/dist/task/accept-debt.js +73 -65
- package/dist/task/api-synthesis.d.ts +24 -21
- package/dist/task/api-synthesis.js +32 -26
- package/dist/task/apis-contract.d.ts +32 -64
- package/dist/task/apis-contract.js +32 -64
- package/dist/task/artifact-closure.d.ts +27 -13
- package/dist/task/artifact-closure.js +95 -67
- package/dist/task/auto-commit.d.ts +46 -35
- package/dist/task/auto-commit.js +51 -38
- package/dist/task/auto-io.d.ts +45 -25
- package/dist/task/auto-io.js +57 -29
- package/dist/task/auto-orchestrator.d.ts +26 -24
- package/dist/task/auto-orchestrator.js +192 -165
- package/dist/task/auto-prompts.d.ts +36 -24
- package/dist/task/auto-prompts.js +40 -26
- package/dist/task/autofix-ledger.d.ts +27 -25
- package/dist/task/autofix-ledger.js +29 -26
- package/dist/task/batch-test-task.d.ts +20 -12
- package/dist/task/batch-test-task.js +67 -60
- package/dist/task/boot-probe.d.ts +60 -44
- package/dist/task/boot-probe.js +91 -72
- package/dist/task/cancel-input.d.ts +30 -16
- package/dist/task/cancel-input.js +20 -11
- package/dist/task/cancel-points.d.ts +27 -20
- package/dist/task/cancel-points.js +30 -22
- package/dist/task/child-runner.d.ts +124 -55
- package/dist/task/child-runner.js +298 -90
- package/dist/task/child-status.d.ts +23 -16
- package/dist/task/child-status.js +23 -16
- package/dist/task/clamp-output.js +12 -5
- package/dist/task/command-run.d.ts +31 -28
- package/dist/task/command-run.js +44 -35
- package/dist/task/command-shrink.d.ts +25 -18
- package/dist/task/command-shrink.js +37 -31
- package/dist/task/command-watchdog.d.ts +9 -6
- package/dist/task/command-watchdog.js +21 -15
- package/dist/task/context-attribution.d.ts +34 -26
- package/dist/task/context-attribution.js +34 -26
- package/dist/task/context-silence.d.ts +39 -29
- package/dist/task/context-silence.js +35 -25
- package/dist/task/context-usage.d.ts +16 -9
- package/dist/task/context-usage.js +16 -9
- package/dist/task/contracts.d.ts +8 -4
- package/dist/task/contracts.js +25 -17
- package/dist/task/coverage-loop.d.ts +22 -18
- package/dist/task/coverage-loop.js +35 -30
- package/dist/task/critique-probes.d.ts +13 -14
- package/dist/task/critique-probes.js +50 -39
- package/dist/task/debug-log.d.ts +13 -5
- package/dist/task/debug-log.js +32 -20
- package/dist/task/decompose-fidelity.d.ts +11 -9
- package/dist/task/decompose-fidelity.js +38 -33
- package/dist/task/decompose-granularity.d.ts +41 -38
- package/dist/task/decompose-granularity.js +41 -38
- package/dist/task/deep-render-check.d.ts +22 -14
- package/dist/task/deep-render-check.js +40 -31
- package/dist/task/dropped-input.d.ts +12 -7
- package/dist/task/dropped-input.js +5 -2
- package/dist/task/enforce-attribution.d.ts +38 -47
- package/dist/task/enforce-attribution.js +46 -52
- package/dist/task/enforce-guidelines.d.ts +31 -20
- package/dist/task/enforce-guidelines.js +32 -21
- package/dist/task/enrichment.d.ts +7 -2
- package/dist/task/enrichment.js +26 -14
- package/dist/task/env-notes.d.ts +16 -7
- package/dist/task/env-notes.js +48 -31
- package/dist/task/env-template-closure.d.ts +4 -4
- package/dist/task/env-template-closure.js +42 -34
- package/dist/task/external-context.d.ts +28 -21
- package/dist/task/external-context.js +17 -12
- package/dist/task/failure-classifier.d.ts +4 -5
- package/dist/task/failure-classifier.js +30 -8
- package/dist/task/file-inventory.d.ts +15 -11
- package/dist/task/file-inventory.js +25 -22
- package/dist/task/final-gate-fix.d.ts +74 -86
- package/dist/task/final-gate-fix.js +97 -116
- package/dist/task/final-gate-progress.d.ts +29 -46
- package/dist/task/final-gate-progress.js +40 -51
- package/dist/task/final-gate.d.ts +64 -97
- package/dist/task/final-gate.js +192 -199
- package/dist/task/fix-child.d.ts +21 -27
- package/dist/task/fix-child.js +21 -27
- package/dist/task/foreign-path.d.ts +6 -5
- package/dist/task/foreign-path.js +0 -0
- package/dist/task/frozen-conflict.d.ts +9 -10
- package/dist/task/frozen-conflict.js +61 -64
- package/dist/task/frozen-path-guard.d.ts +35 -14
- package/dist/task/frozen-path-guard.js +56 -39
- package/dist/task/gate-child.d.ts +27 -28
- package/dist/task/gate-child.js +36 -35
- package/dist/task/gate-deps.d.ts +34 -27
- package/dist/task/gate-deps.js +169 -159
- package/dist/task/gate-tally.d.ts +77 -80
- package/dist/task/gate-tally.js +65 -68
- package/dist/task/git-state-guard.d.ts +15 -11
- package/dist/task/git-state-guard.js +76 -66
- package/dist/task/impl-widget.d.ts +25 -16
- package/dist/task/impl-widget.js +27 -17
- package/dist/task/implementation-guards.d.ts +26 -0
- package/dist/task/implementation-guards.js +177 -0
- package/dist/task/implementation-thinking.d.ts +33 -31
- package/dist/task/implementation-thinking.js +5 -6
- package/dist/task/implementation-turn.d.ts +39 -31
- package/dist/task/implementation-turn.js +41 -28
- package/dist/task/inline-markdown.d.ts +20 -7
- package/dist/task/inline-markdown.js +15 -6
- package/dist/task/launch-config-gap.js +25 -39
- package/dist/task/launch-contract.d.ts +18 -21
- package/dist/task/launch-contract.js +28 -30
- package/dist/task/launch-manifest.d.ts +6 -2
- package/dist/task/launch-manifest.js +35 -34
- package/dist/task/ledger.js +16 -14
- package/dist/task/lint-fix.d.ts +6 -8
- package/dist/task/lint-fix.js +67 -69
- package/dist/task/loop-detector.d.ts +27 -8
- package/dist/task/loop-detector.js +38 -14
- package/dist/task/mid-run-input.d.ts +17 -15
- package/dist/task/mid-run-input.js +17 -15
- package/dist/task/orchestrator.d.ts +24 -28
- package/dist/task/orchestrator.js +89 -66
- package/dist/task/orientation.d.ts +18 -23
- package/dist/task/orientation.js +24 -31
- package/dist/task/owned-freeze-conflict.d.ts +21 -20
- package/dist/task/owned-freeze-conflict.js +52 -85
- package/dist/task/owned-freeze-reassign.d.ts +40 -60
- package/dist/task/owned-freeze-reassign.js +41 -61
- package/dist/task/parsers.d.ts +4 -2
- package/dist/task/parsers.js +4 -4
- package/dist/task/phases.d.ts +41 -48
- package/dist/task/phases.js +196 -252
- package/dist/task/plan-io.d.ts +6 -7
- package/dist/task/plan-io.js +6 -7
- package/dist/task/plan-orchestrator.d.ts +10 -8
- package/dist/task/plan-orchestrator.js +14 -10
- package/dist/task/plan-prompts.d.ts +6 -5
- package/dist/task/plan-prompts.js +6 -5
- package/dist/task/plan-readonly.d.ts +4 -5
- package/dist/task/plan-readonly.js +4 -5
- package/dist/task/plan-rounds.d.ts +17 -29
- package/dist/task/plan-rounds.js +21 -34
- package/dist/task/plan-session.d.ts +58 -72
- package/dist/task/plan-session.js +61 -83
- package/dist/task/probe-gaming.d.ts +28 -27
- package/dist/task/probe-gaming.js +0 -0
- package/dist/task/prohibition-probe.d.ts +14 -16
- package/dist/task/prompts.d.ts +3 -4
- package/dist/task/prompts.js +17 -26
- package/dist/task/qa-transcript.d.ts +15 -22
- package/dist/task/qa-transcript.js +15 -21
- package/dist/task/question-box.d.ts +17 -13
- package/dist/task/question-box.js +19 -15
- package/dist/task/question-dedup.d.ts +6 -7
- package/dist/task/question-dedup.js +13 -14
- package/dist/task/question-dialog.d.ts +22 -32
- package/dist/task/question-dialog.js +22 -32
- package/dist/task/question-source.d.ts +18 -44
- package/dist/task/question-source.js +22 -51
- package/dist/task/refuted-constraint.d.ts +11 -31
- package/dist/task/refuted-constraint.js +27 -51
- package/dist/task/regenerable-artifacts.d.ts +12 -31
- package/dist/task/regenerable-artifacts.js +12 -31
- package/dist/task/render-check.d.ts +11 -22
- package/dist/task/render-check.js +33 -46
- package/dist/task/repo-health-check.d.ts +10 -14
- package/dist/task/repo-health-check.js +17 -23
- package/dist/task/requirements.d.ts +38 -71
- package/dist/task/requirements.js +78 -126
- package/dist/task/research-fanout-budget.d.ts +51 -88
- package/dist/task/research-fanout-budget.js +51 -88
- package/dist/task/research-worker.d.ts +29 -39
- package/dist/task/research-worker.js +37 -61
- package/dist/task/resume-gap.d.ts +14 -15
- package/dist/task/root-cause-repair.d.ts +9 -9
- package/dist/task/root-cause-repair.js +28 -40
- package/dist/task/run-bracket.d.ts +10 -13
- package/dist/task/run-end.d.ts +12 -22
- package/dist/task/run-end.js +8 -16
- package/dist/task/run-final-gate.d.ts +19 -21
- package/dist/task/run-final-gate.js +62 -80
- package/dist/task/runner-globs.d.ts +12 -13
- package/dist/task/runner-globs.js +12 -13
- package/dist/task/runner-resolve.d.ts +9 -9
- package/dist/task/runner-resolve.js +22 -23
- package/dist/task/script-escape.d.ts +10 -12
- package/dist/task/script-escape.js +13 -14
- package/dist/task/serve-entry.d.ts +1 -1
- package/dist/task/serve-entry.js +22 -25
- package/dist/task/service-blocks.js +4 -2
- package/dist/task/shipped-source.d.ts +11 -29
- package/dist/task/shipped-source.js +11 -29
- package/dist/task/skip-escape.js +10 -14
- package/dist/task/spec-urls.d.ts +26 -65
- package/dist/task/spec-urls.js +26 -65
- package/dist/task/spec-validation.d.ts +17 -20
- package/dist/task/spec-validation.js +17 -20
- package/dist/task/stall-detector.d.ts +23 -30
- package/dist/task/stall-detector.js +23 -30
- package/dist/task/stream-watchdog.d.ts +14 -12
- package/dist/task/stream-watchdog.js +14 -12
- package/dist/task/substitution-probe.d.ts +17 -20
- package/dist/task/substitution-probe.js +17 -20
- package/dist/task/task-gates.d.ts +36 -41
- package/dist/task/task-gates.js +95 -106
- package/dist/task/task-io.d.ts +4 -4
- package/dist/task/task-io.js +4 -4
- package/dist/task/task-parsers.js +4 -3
- package/dist/task/task-provenance.d.ts +2 -2
- package/dist/task/task-provenance.js +11 -13
- package/dist/task/task-types.d.ts +4 -3
- package/dist/task/terminal-outcome.d.ts +14 -16
- package/dist/task/terminal-outcome.js +12 -14
- package/dist/task/test-assembly.d.ts +13 -20
- package/dist/task/test-assembly.js +13 -20
- package/dist/task/timings.d.ts +5 -3
- package/dist/task/timings.js +5 -3
- package/dist/task/title-label.d.ts +9 -4
- package/dist/task/title-label.js +9 -4
- package/dist/task/type-only-answer.d.ts +44 -52
- package/dist/task/type-only-answer.js +44 -52
- package/dist/task/unfailable-command.d.ts +18 -24
- package/dist/task/unfailable-command.js +21 -27
- package/dist/task/unknown-routing.d.ts +10 -4
- package/dist/task/unknown-routing.js +10 -4
- package/dist/task/user-directives.d.ts +5 -8
- package/dist/task/user-directives.js +5 -8
- package/dist/task/verify-quality.d.ts +18 -22
- package/dist/task/verify-quality.js +45 -46
- package/dist/task/verify-reconcile.d.ts +15 -10
- package/dist/task/verify-reconcile.js +45 -43
- package/dist/task/verify-resolution.d.ts +24 -20
- package/dist/task/verify-resolution.js +51 -50
- package/dist/task/verify-work.d.ts +59 -66
- package/dist/task/verify-work.js +101 -138
- package/dist/task/widget.d.ts +15 -14
- package/dist/task/widget.js +22 -17
- package/dist/task/wiring-claims.d.ts +25 -32
- package/dist/task/wiring-claims.js +30 -35
- package/dist/task/write-guard.d.ts +39 -39
- package/dist/task/write-guard.js +48 -51
- package/dist/task/yolo.d.ts +34 -30
- package/dist/task/yolo.js +42 -37
- package/dist/workers/abstention.d.ts +21 -41
- package/dist/workers/abstention.js +27 -48
- package/dist/workers/brave-search.d.ts +4 -3
- package/dist/workers/brave-search.js +5 -2
- package/dist/workers/brave-warning.d.ts +7 -4
- package/dist/workers/brave-warning.js +19 -7
- package/dist/workers/ddg-search.d.ts +6 -6
- package/dist/workers/ddg-search.js +18 -12
- package/dist/workers/docs-cache.js +5 -2
- package/dist/workers/docs-chunk.d.ts +30 -37
- package/dist/workers/docs-chunk.js +37 -41
- package/dist/workers/docs-core.d.ts +28 -44
- package/dist/workers/docs-core.js +25 -44
- package/dist/workers/docs-index.js +4 -3
- package/dist/workers/docs-lookup.d.ts +15 -22
- package/dist/workers/docs-lookup.js +12 -21
- package/dist/workers/docs-project.d.ts +15 -9
- package/dist/workers/docs-project.js +17 -10
- package/dist/workers/docs-resolve.d.ts +19 -20
- package/dist/workers/docs-resolve.js +35 -32
- package/dist/workers/docs-retrieve.d.ts +5 -6
- package/dist/workers/docs-retrieve.js +18 -15
- package/dist/workers/exa-search.d.ts +9 -6
- package/dist/workers/exa-search.js +23 -12
- package/dist/workers/fetch-core.d.ts +13 -16
- package/dist/workers/fetch-core.js +23 -23
- package/dist/workers/focused-extractor.d.ts +13 -12
- package/dist/workers/focused-extractor.js +27 -19
- package/dist/workers/html-clean.js +24 -14
- package/dist/workers/http-request.d.ts +28 -20
- package/dist/workers/http-request.js +22 -17
- package/dist/workers/npm-version.d.ts +28 -11
- package/dist/workers/npm-version.js +24 -15
- package/dist/workers/phantom-imports.d.ts +15 -12
- package/dist/workers/phantom-imports.js +30 -24
- package/dist/workers/pi-worker-core.d.ts +65 -96
- package/dist/workers/pi-worker-core.js +93 -181
- package/dist/workers/pi-worker-docs.d.ts +24 -19
- package/dist/workers/pi-worker-docs.js +67 -76
- package/dist/workers/pi-worker-fetch.d.ts +7 -3
- package/dist/workers/pi-worker-fetch.js +27 -19
- package/dist/workers/pi-worker-search.js +12 -8
- package/dist/workers/pi-worker.d.ts +9 -4
- package/dist/workers/pi-worker.js +21 -14
- package/dist/workers/reasoning-warning.d.ts +18 -17
- package/dist/workers/reasoning-warning.js +22 -20
- package/dist/workers/research-cache.js +50 -78
- package/dist/workers/search-core.js +7 -5
- package/dist/workers/search-types.d.ts +10 -9
- package/dist/workers/search-types.js +9 -8
- package/dist/workers/session-hint.d.ts +13 -14
- package/dist/workers/session-hint.js +8 -9
- package/dist/workers/shared.d.ts +21 -25
- package/dist/workers/shared.js +0 -0
- package/dist/workers/single-read-extension.d.ts +14 -7
- package/dist/workers/single-read-extension.js +14 -7
- package/dist/workers/single-read-guard.d.ts +27 -30
- package/dist/workers/single-read-guard.js +36 -36
- package/dist/workers/typeonly-log.d.ts +12 -9
- package/dist/workers/typeonly-log.js +29 -33
- package/dist/workers/worker-channels.d.ts +15 -23
- package/dist/workers/worker-channels.js +15 -23
- package/dist/workers/worker-failure.d.ts +38 -46
- package/dist/workers/worker-failure.js +31 -39
- package/dist/workers/worker-kill.d.ts +25 -26
- package/dist/workers/worker-kill.js +16 -19
- package/dist/workers/worker-profiles.d.ts +54 -56
- package/dist/workers/worker-profiles.js +63 -39
- package/package.json +10 -8
|
@@ -2,21 +2,22 @@
|
|
|
2
2
|
* ChildStatus — the live status of the child pi currently running under a
|
|
3
3
|
* status loader: its latest output line and its context usage.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
5
|
+
* Without it each spawn site keeps this state by hand — `let lastLine; let contextUsage;`
|
|
6
6
|
* plus two callbacks (`onChildOutput` writes the line, `onContextUsage` folds a
|
|
7
7
|
* snapshot through `resolveContextUsage` with the parent window), a reset before
|
|
8
|
-
* every child, and a loader whose every tick
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
8
|
+
* every child, and a loader whose every tick reads both.
|
|
9
|
+
*
|
|
10
|
+
* Three sites share that ritual and are now this class: `/task-auto`'s planning
|
|
11
|
+
* `runChild`, `buildGateDeps`, and `/task-plan` — one `new ChildStatus` each, and
|
|
12
|
+
* no others. The single-task `TaskRunner` deliberately stays where it is: its
|
|
13
|
+
* state is the whole-run `WidgetState`, shared by reference with `PhaseContext`
|
|
14
|
+
* and written by the phases themselves (see `orchestrator.ts`).
|
|
14
15
|
*
|
|
15
16
|
* `track` is the loader ritual: reset, raise the loader reading this status on
|
|
16
17
|
* every tick, run, always stop. The status OUTLIVES a track — `buildGateDeps`
|
|
17
18
|
* shares one across every gate child, and the verify gate raises its own
|
|
18
|
-
* gate-wide loader over a child that renders none (`frame: null
|
|
19
|
-
* see the same object.
|
|
19
|
+
* gate-wide loader over a child that renders none (`frame: null`, reached when
|
|
20
|
+
* `deps.loader === false` in gate-child), so both must see the same object.
|
|
20
21
|
*/
|
|
21
22
|
import { runPhaseChild } from './child-runner.js';
|
|
22
23
|
import { resolveContextUsage } from './context-usage.js';
|
|
@@ -59,12 +60,17 @@ export class ChildStatus {
|
|
|
59
60
|
}
|
|
60
61
|
/**
|
|
61
62
|
* Run `run` under the loader: reset, raise a loader whose every tick is
|
|
62
|
-
* `frame()`
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
63
|
+
* `frame()` spread OVER the live line and gauge, and stop it in a `finally`.
|
|
64
|
+
* All four behaviours were run against a fake loader:
|
|
65
|
+
* - the reset lands first, so the previous child's line never reaches the
|
|
66
|
+
* new loader's first tick;
|
|
67
|
+
* - `frame` wins on a clash, because it is spread last — which is how the
|
|
68
|
+
* verify gate shows its deterministic-stage label until the child has a
|
|
69
|
+
* line of its own;
|
|
70
|
+
* - `frame: null` raises NO loader at all (the caller already has one
|
|
71
|
+
* reading this status) but still resets;
|
|
72
|
+
* - a child that THROWS still stops the loader, so a failure never leaves
|
|
73
|
+
* the widget up.
|
|
68
74
|
*/
|
|
69
75
|
async track(ctx, frame, run) {
|
|
70
76
|
this.reset();
|
|
@@ -99,7 +105,8 @@ export async function runPlanningChild(opts) {
|
|
|
99
105
|
}
|
|
100
106
|
/**
|
|
101
107
|
* Wire a `ChildStatus` as a phase child's stream callbacks — plus the window the
|
|
102
|
-
* child must be TOLD, since pi's
|
|
108
|
+
* child must be TOLD, since pi's `--mode json` stream reports token counts but no
|
|
109
|
+
* context window.
|
|
103
110
|
*/
|
|
104
111
|
export function statusCallbacks(status) {
|
|
105
112
|
return {
|
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* clamp-output — the ONE trail-side ceiling for captured tool output.
|
|
3
3
|
*
|
|
4
|
-
* `appendGateRecord`
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* `appendGateRecord` already collapses every run of whitespace-around-newline
|
|
5
|
+
* into a single space, so each trail entry is one line. What it does NOT do is
|
|
6
|
+
* bound the LENGTH, which is this. A wedged tool has no upper limit on what it
|
|
7
|
+
* prints, and the trail is a task file a human reads.
|
|
8
|
+
*
|
|
9
|
+
* Both probes that carry tool output into a failure detail — the render check and
|
|
10
|
+
* the gate's own command records — call THIS function, so the two trails cannot
|
|
11
|
+
* start disagreeing about what was captured. A second clamp with a second ceiling
|
|
12
|
+
* is exactly how that happens.
|
|
13
|
+
*
|
|
14
|
+
* At the boundary: 1200 characters pass through untouched, 1201 or more come back
|
|
15
|
+
* clamped to 1200 plus a single ellipsis.
|
|
9
16
|
*/
|
|
10
17
|
const TRAIL_OUTPUT_MAX_CHARS = 1200;
|
|
11
18
|
export function clampOutput(output) {
|
|
@@ -9,23 +9,24 @@
|
|
|
9
9
|
* Only a command that demonstrably ran and exited non-zero for a reason of its
|
|
10
10
|
* own is a failure, and only exit 0 is a pass.
|
|
11
11
|
*
|
|
12
|
-
* Why a module.
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
12
|
+
* Why a module. The same ladder is needed by every caller that runs a project
|
|
13
|
+
* command and must decide what its ending MEANS — the gate's own
|
|
14
|
+
* `runGateCommand`, `runVerifyCommandLine` here, and accept-debt's
|
|
15
|
+
* `rerunDebtVerifyCommand`. Stated separately at each site the copies drift: one
|
|
16
|
+
* applies the infrastructure pattern unconditionally, the next only on request,
|
|
17
|
+
* and two trails then disagree about whether the same output was a gap or a
|
|
18
|
+
* failure. repo-health-check.ts defers to this module rather than carrying its
|
|
19
|
+
* own version — its header says so in as many words.
|
|
19
20
|
*
|
|
20
21
|
* The second half is the seam. Classification is now pure over a `CommandRun`
|
|
21
22
|
* value, and SPAWNING is a `CommandRunner` the caller injects. That is what lets
|
|
22
23
|
* the gate's tests state a case as `{status: 1, stdout: "…"}` instead of writing
|
|
23
|
-
* a real `node -e` child, creating a temp directory for it, and — for the
|
|
24
|
-
*
|
|
25
|
-
* resetting a module-level cache, which
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* had none.
|
|
24
|
+
* a real `node -e` child, creating a temp directory for it, and — for the cases
|
|
25
|
+
* only reachable that way — shadowing a binary on `process.env.PATH` and
|
|
26
|
+
* resetting a module-level cache, which makes those tests order-sensitive.
|
|
27
|
+
*
|
|
28
|
+
* It also closes an asymmetry: `BootDeps` carries twelve injectable probes for
|
|
29
|
+
* the gate's boot half, while its command half had none.
|
|
29
30
|
*/
|
|
30
31
|
/** What one finished command looks like, stripped of how it was spawned. */
|
|
31
32
|
export interface CommandRun {
|
|
@@ -57,14 +58,12 @@ export interface CommandSpec {
|
|
|
57
58
|
* The injectable half. The gate takes one of these so its tests can script
|
|
58
59
|
* verdicts instead of paying process-spawn cost for every classification case.
|
|
59
60
|
*
|
|
60
|
-
* ASYNC by contract
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
* doc comment already told gate callers not to do it, while `final-gate.ts`'s
|
|
67
|
-
* repo-health call did exactly that.
|
|
61
|
+
* ASYNC by contract, and the type is what enforces it. A synchronous
|
|
62
|
+
* `(spec) => CommandRun` can only be implemented with `spawnSync`, which blocks
|
|
63
|
+
* the event loop for the whole command — so the run-end gate, which runs
|
|
64
|
+
* repo-health and then every lockfile, test, build, launch and ACCEPT-debt
|
|
65
|
+
* command in sequence, would hold the loop for all of it and no loader could
|
|
66
|
+
* paint a single frame through it.
|
|
68
67
|
*/
|
|
69
68
|
export type CommandRunner = (spec: CommandSpec) => Promise<CommandRun>;
|
|
70
69
|
/**
|
|
@@ -76,15 +75,19 @@ export type CommandRunner = (spec: CommandSpec) => Promise<CommandRun>;
|
|
|
76
75
|
* THE RUN SETTLES ON THE CHILD, NOT ON THE PIPE. `close` fires only once every
|
|
77
76
|
* stdio pipe has reached EOF, and a backgrounded grandchild INHERITS stdout: a
|
|
78
77
|
* seed script that starts a daemon, a build that leaves a watcher, a launch
|
|
79
|
-
* script.
|
|
80
|
-
*
|
|
81
|
-
*
|
|
78
|
+
* script.
|
|
79
|
+
*
|
|
80
|
+
* Measured on exactly that shape — a child that backgrounds a sleeper and exits
|
|
81
|
+
* at once — `exit` fired after 1ms and `close` NEVER fired. Sending SIGKILL to
|
|
82
|
+
* the direct child did not release it either, because the grandchild holds the
|
|
83
|
+
* pipe. Waiting for `close` is therefore waiting for something no timeout can
|
|
84
|
+
* reach. `exit` settles the run, and the deadline settles it itself.
|
|
82
85
|
*/
|
|
83
86
|
export declare const spawnCommand: CommandRunner;
|
|
84
87
|
/**
|
|
85
88
|
* A non-zero exit whose output shows an EXTERNAL runtime dependency is missing, not
|
|
86
89
|
* a code fault: a browser suite (Playwright/Cypress) whose browser binaries or system
|
|
87
|
-
* libraries were never installed here (
|
|
90
|
+
* libraries were never installed here (a component-test runner must run in the
|
|
88
91
|
* gate, but on a box with no Playwright browsers it is an environment gap, not a FAIL).
|
|
89
92
|
* These exit non-zero (not 127), so they need output-shape recognition to skip.
|
|
90
93
|
*/
|
|
@@ -164,15 +167,15 @@ export type VerifyRerunOutcome = {
|
|
|
164
167
|
detail: string;
|
|
165
168
|
};
|
|
166
169
|
/**
|
|
167
|
-
* Re-run one VERIFY-block command line
|
|
170
|
+
* Re-run one VERIFY-block command line under the gate's existing
|
|
168
171
|
* env-gap contract, so a debt whose reason NAMES that command can be closed by the
|
|
169
172
|
* command itself rather than by a judgement about it.
|
|
170
173
|
*
|
|
171
|
-
* Runs through `sh -c` because a VERIFY line is a shell line, not an argv:
|
|
174
|
+
* Runs through `sh -c` because a VERIFY line is a shell line, not an argv: a
|
|
172
175
|
* is `AGENT=1 bun test test/listings.test.ts`, and env prefixes, `&&` and redirects
|
|
173
176
|
* are all ordinary there. The leading command word is still resolved through
|
|
174
177
|
* runner-resolve so a login-shell-stripped PATH cannot make every re-run look like a
|
|
175
|
-
* gap
|
|
178
|
+
* gap.
|
|
176
179
|
*
|
|
177
180
|
* The asymmetry is the point: only exit 0 is conclusive. Every other ending — real
|
|
178
181
|
* failure, missing tool, unreachable database, timeout, no POSIX shell — leaves the
|
package/dist/task/command-run.js
CHANGED
|
@@ -9,36 +9,41 @@
|
|
|
9
9
|
* Only a command that demonstrably ran and exited non-zero for a reason of its
|
|
10
10
|
* own is a failure, and only exit 0 is a pass.
|
|
11
11
|
*
|
|
12
|
-
* Why a module.
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
12
|
+
* Why a module. The same ladder is needed by every caller that runs a project
|
|
13
|
+
* command and must decide what its ending MEANS — the gate's own
|
|
14
|
+
* `runGateCommand`, `runVerifyCommandLine` here, and accept-debt's
|
|
15
|
+
* `rerunDebtVerifyCommand`. Stated separately at each site the copies drift: one
|
|
16
|
+
* applies the infrastructure pattern unconditionally, the next only on request,
|
|
17
|
+
* and two trails then disagree about whether the same output was a gap or a
|
|
18
|
+
* failure. repo-health-check.ts defers to this module rather than carrying its
|
|
19
|
+
* own version — its header says so in as many words.
|
|
19
20
|
*
|
|
20
21
|
* The second half is the seam. Classification is now pure over a `CommandRun`
|
|
21
22
|
* value, and SPAWNING is a `CommandRunner` the caller injects. That is what lets
|
|
22
23
|
* the gate's tests state a case as `{status: 1, stdout: "…"}` instead of writing
|
|
23
|
-
* a real `node -e` child, creating a temp directory for it, and — for the
|
|
24
|
-
*
|
|
25
|
-
* resetting a module-level cache, which
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* had none.
|
|
24
|
+
* a real `node -e` child, creating a temp directory for it, and — for the cases
|
|
25
|
+
* only reachable that way — shadowing a binary on `process.env.PATH` and
|
|
26
|
+
* resetting a module-level cache, which makes those tests order-sensitive.
|
|
27
|
+
*
|
|
28
|
+
* It also closes an asymmetry: `BootDeps` carries twelve injectable probes for
|
|
29
|
+
* the gate's boot half, while its command half had none.
|
|
29
30
|
*/
|
|
30
31
|
import { spawn } from 'node:child_process';
|
|
31
32
|
import { isCommandNotFound, resolveRunner, runnerEnv } from './runner-resolve.js';
|
|
32
33
|
/**
|
|
33
34
|
* How much of ONE stream may be held in the HOST process, and how it is split.
|
|
34
35
|
*
|
|
35
|
-
* `spawnSync`
|
|
36
|
-
*
|
|
37
|
-
*
|
|
36
|
+
* `spawnSync` came with a bound for free — its default `maxBuffer` is 1 MiB, and
|
|
37
|
+
* exceeding it fails the call with ENOBUFS (checked: 2 MiB of output truncates at
|
|
38
|
+
* 1,114,112 bytes). An async runner has no such bound, so without this two
|
|
39
|
+
* strings grow in the host's own process for as long as the command keeps
|
|
40
|
+
* talking.
|
|
38
41
|
*
|
|
39
|
-
* BOTH ENDS are kept, because both are read
|
|
40
|
-
* regexes match wording a runner prints FIRST
|
|
41
|
-
* reason take the LAST 400 characters. A single-ended cap loses one of
|
|
42
|
+
* BOTH ENDS are kept, because both are read: `isCommandNotFound` and the gap
|
|
43
|
+
* regexes match wording a runner prints FIRST, while `outputTail` and every
|
|
44
|
+
* failure reason take the LAST 400 characters. A single-ended cap loses one of
|
|
45
|
+
* them. Run against a real 2 MB command, the capture keeps its opening bytes,
|
|
46
|
+
* its closing bytes, and an elision marker between them.
|
|
42
47
|
*/
|
|
43
48
|
const OUTPUT_HEAD_CAP = 256 * 1024;
|
|
44
49
|
const OUTPUT_TAIL_CAP = 768 * 1024;
|
|
@@ -81,9 +86,13 @@ const DRAIN_MS = 50;
|
|
|
81
86
|
* THE RUN SETTLES ON THE CHILD, NOT ON THE PIPE. `close` fires only once every
|
|
82
87
|
* stdio pipe has reached EOF, and a backgrounded grandchild INHERITS stdout: a
|
|
83
88
|
* seed script that starts a daemon, a build that leaves a watcher, a launch
|
|
84
|
-
* script.
|
|
85
|
-
*
|
|
86
|
-
*
|
|
89
|
+
* script.
|
|
90
|
+
*
|
|
91
|
+
* Measured on exactly that shape — a child that backgrounds a sleeper and exits
|
|
92
|
+
* at once — `exit` fired after 1ms and `close` NEVER fired. Sending SIGKILL to
|
|
93
|
+
* the direct child did not release it either, because the grandchild holds the
|
|
94
|
+
* pipe. Waiting for `close` is therefore waiting for something no timeout can
|
|
95
|
+
* reach. `exit` settles the run, and the deadline settles it itself.
|
|
87
96
|
*/
|
|
88
97
|
export const spawnCommand = spec => new Promise(resolve => {
|
|
89
98
|
const out = new BoundedOutput();
|
|
@@ -97,8 +106,8 @@ export const spawnCommand = spec => new Promise(resolve => {
|
|
|
97
106
|
cwd: spec.cwd,
|
|
98
107
|
// stdin CLOSED. `spawnSync` gave the child none; the default `spawn`
|
|
99
108
|
// stdio is a live pipe nobody ever ends, so a check that reads stdin —
|
|
100
|
-
// a `cat`-style pipeline, a tool that prompts, a pager —
|
|
101
|
-
// the kill timer
|
|
109
|
+
// a `cat`-style pipeline, a tool that prompts, a pager — would block
|
|
110
|
+
// until the kill timer fires instead of returning at once.
|
|
102
111
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
103
112
|
...(spec.env ? { env: spec.env } : {})
|
|
104
113
|
});
|
|
@@ -140,12 +149,12 @@ export const spawnCommand = spec => new Promise(resolve => {
|
|
|
140
149
|
clearTimeout(drain);
|
|
141
150
|
drain = setTimeout(() => done(null), DRAIN_MS);
|
|
142
151
|
};
|
|
143
|
-
// NOT unref'd.
|
|
144
|
-
//
|
|
145
|
-
//
|
|
146
|
-
//
|
|
147
|
-
//
|
|
148
|
-
//
|
|
152
|
+
// NOT unref'd. This timer is the only bound left on every gate command,
|
|
153
|
+
// repo-health command and ACCEPT-debt re-run, and an unref'd timer is only
|
|
154
|
+
// guaranteed to fire while something ref'd is still pending — not a
|
|
155
|
+
// guarantee this can rely on. It is cleared the moment the run settles, so
|
|
156
|
+
// it holds the loop open only while a command the caller is awaiting
|
|
157
|
+
// anyway is still running.
|
|
149
158
|
const timer = setTimeout(killAndSettle, spec.timeoutMs);
|
|
150
159
|
if (spec.signal) {
|
|
151
160
|
if (spec.signal.aborted)
|
|
@@ -180,7 +189,7 @@ export const spawnCommand = spec => new Promise(resolve => {
|
|
|
180
189
|
/**
|
|
181
190
|
* A non-zero exit whose output shows an EXTERNAL runtime dependency is missing, not
|
|
182
191
|
* a code fault: a browser suite (Playwright/Cypress) whose browser binaries or system
|
|
183
|
-
* libraries were never installed here (
|
|
192
|
+
* libraries were never installed here (a component-test runner must run in the
|
|
184
193
|
* gate, but on a box with no Playwright browsers it is an environment gap, not a FAIL).
|
|
185
194
|
* These exit non-zero (not 127), so they need output-shape recognition to skip.
|
|
186
195
|
*/
|
|
@@ -202,7 +211,7 @@ export const INFRA_GAP_OUTPUT_RE = /ECONNREFUSED|connection refused|ENOTFOUND|EA
|
|
|
202
211
|
* `spawn-failed` is first and is kept distinguishable from every other row by its
|
|
203
212
|
* id: a tool-level gap (127 inside the chain, a missing browser, a timeout) means
|
|
204
213
|
* the runner demonstrably RAN, and only genuine spawn failures feed the gate's
|
|
205
|
-
* full-blindness guard (
|
|
214
|
+
* full-blindness guard (see observabilityGapFailure).
|
|
206
215
|
*/
|
|
207
216
|
const GAP_RULES = [
|
|
208
217
|
{
|
|
@@ -281,15 +290,15 @@ function leadingBin(line) {
|
|
|
281
290
|
return null;
|
|
282
291
|
}
|
|
283
292
|
/**
|
|
284
|
-
* Re-run one VERIFY-block command line
|
|
293
|
+
* Re-run one VERIFY-block command line under the gate's existing
|
|
285
294
|
* env-gap contract, so a debt whose reason NAMES that command can be closed by the
|
|
286
295
|
* command itself rather than by a judgement about it.
|
|
287
296
|
*
|
|
288
|
-
* Runs through `sh -c` because a VERIFY line is a shell line, not an argv:
|
|
297
|
+
* Runs through `sh -c` because a VERIFY line is a shell line, not an argv: a
|
|
289
298
|
* is `AGENT=1 bun test test/listings.test.ts`, and env prefixes, `&&` and redirects
|
|
290
299
|
* are all ordinary there. The leading command word is still resolved through
|
|
291
300
|
* runner-resolve so a login-shell-stripped PATH cannot make every re-run look like a
|
|
292
|
-
* gap
|
|
301
|
+
* gap.
|
|
293
302
|
*
|
|
294
303
|
* The asymmetry is the point: only exit 0 is conclusive. Every other ending — real
|
|
295
304
|
* failure, missing tool, unreachable database, timeout, no POSIX shell — leaves the
|
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
* SCOPE-SHRINK detection for the final-gate fix pass.
|
|
3
3
|
*
|
|
4
4
|
* The label guard (final-gate-fix.ts) compares the SET of discoverable gate
|
|
5
|
-
* commands before and after the fix child
|
|
6
|
-
* it
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
5
|
+
* commands before and after the fix child, and a fix child walks straight through
|
|
6
|
+
* it. Rewrite `"test": "AGENT=1 bun test"` to `"AGENT=1 bun test ./test"` and the
|
|
7
|
+
* label `bun run test` exists before and after, so the set difference is empty —
|
|
8
|
+
* while the gate now re-runs a suite that no longer covers the repository, and
|
|
9
|
+
* reports that the autofix converged. Fed to `classifyBodyChange`, those two
|
|
10
|
+
* bodies come back `narrow` / `path-added`, which is the whole point of this
|
|
11
|
+
* module.
|
|
10
12
|
*
|
|
11
13
|
* What changed was not the command's NAME but its SCOPE. This module compares
|
|
12
14
|
* the RESOLVED BODIES (`scripts[name]` for package.json, the recipe lines for a
|
|
@@ -16,23 +18,27 @@
|
|
|
16
18
|
* 1. a path/glob argument is ADDED to a command member that had none
|
|
17
19
|
* (`bun test` → `bun test ./test`);
|
|
18
20
|
* 2. an existing path/glob argument is replaced by a strict SUBPATH of itself
|
|
19
|
-
* (`eslint src` → `eslint src/server`)
|
|
21
|
+
* (`eslint ./src` → `eslint ./src/server`). Note the argument has to LOOK
|
|
22
|
+
* like a path — a bare `src` with no separator, dot or glob is not treated
|
|
23
|
+
* as one, so `eslint src` → `eslint src/server` reads as neutral;
|
|
20
24
|
* 3. an exclusion flag/pattern is ADDED (`--ignore`, `--exclude`,
|
|
21
25
|
* `--testPathIgnorePatterns`, `-x`, …);
|
|
22
26
|
* 4. a config-file argument is swapped for one the fix pass itself created.
|
|
23
27
|
*
|
|
24
|
-
* With ONE excuse,
|
|
25
|
-
*
|
|
26
|
-
*
|
|
28
|
+
* With ONE excuse, documented at `excusedByRelocation`: rule 3 does not fire when
|
|
29
|
+
* the same pass hands the excluded input to another command the gate already
|
|
30
|
+
* runs. Rules 1, 2 and 4 have no excuse.
|
|
27
31
|
*
|
|
28
|
-
* Everything else passes: new env
|
|
29
|
-
* input set, reordering, added chain
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
+
* Everything else passes, each checked by running it: a new env var, a flag that
|
|
33
|
+
* does not restrict the input set, reordering, an added chain member, an
|
|
34
|
+
* identical body. A path replaced by its PARENT classifies as `widen`, not
|
|
35
|
+
* `narrow`. The guard's job is to stop the fix pass from silently redefining the
|
|
36
|
+
* gate's own success criterion — not to review the fix.
|
|
32
37
|
*
|
|
33
|
-
* PURE by construction
|
|
34
|
-
*
|
|
35
|
-
* from git blobs.
|
|
38
|
+
* PURE by construction: the module has no imports at all — no fs, no git, no
|
|
39
|
+
* clock. The live guard feeds it two body maps from `discoverGateCommandBodies`;
|
|
40
|
+
* anything replaying history feeds it two maps built from git blobs. The same
|
|
41
|
+
* code decides both.
|
|
36
42
|
*/
|
|
37
43
|
/** Which of the four mechanical shapes fired. */
|
|
38
44
|
export type ShrinkKind = 'path-added' | 'path-narrowed' | 'exclusion-added' | 'config-swapped';
|
|
@@ -64,8 +70,9 @@ export interface Narrowing {
|
|
|
64
70
|
kinds: ShrinkKind[];
|
|
65
71
|
reasons: string[];
|
|
66
72
|
}
|
|
67
|
-
/** Split a shell body into chain members
|
|
68
|
-
*
|
|
73
|
+
/** Split a shell body into chain members on `&&`, `||`, `;`, `|` and newlines
|
|
74
|
+
* (Makefile recipes arrive as multiple lines). Quote-aware, and it matters:
|
|
75
|
+
* `bun test --name 'a && b' && lint` splits into two members, not three. */
|
|
69
76
|
export declare function splitChainMembers(body: string): string[];
|
|
70
77
|
/** Is this token SYNTACTICALLY a path or glob? Deliberately lexical: no fs. */
|
|
71
78
|
export declare function looksLikePath(tok: string): boolean;
|
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
* SCOPE-SHRINK detection for the final-gate fix pass.
|
|
3
3
|
*
|
|
4
4
|
* The label guard (final-gate-fix.ts) compares the SET of discoverable gate
|
|
5
|
-
* commands before and after the fix child
|
|
6
|
-
* it
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
5
|
+
* commands before and after the fix child, and a fix child walks straight through
|
|
6
|
+
* it. Rewrite `"test": "AGENT=1 bun test"` to `"AGENT=1 bun test ./test"` and the
|
|
7
|
+
* label `bun run test` exists before and after, so the set difference is empty —
|
|
8
|
+
* while the gate now re-runs a suite that no longer covers the repository, and
|
|
9
|
+
* reports that the autofix converged. Fed to `classifyBodyChange`, those two
|
|
10
|
+
* bodies come back `narrow` / `path-added`, which is the whole point of this
|
|
11
|
+
* module.
|
|
10
12
|
*
|
|
11
13
|
* What changed was not the command's NAME but its SCOPE. This module compares
|
|
12
14
|
* the RESOLVED BODIES (`scripts[name]` for package.json, the recipe lines for a
|
|
@@ -16,27 +18,32 @@
|
|
|
16
18
|
* 1. a path/glob argument is ADDED to a command member that had none
|
|
17
19
|
* (`bun test` → `bun test ./test`);
|
|
18
20
|
* 2. an existing path/glob argument is replaced by a strict SUBPATH of itself
|
|
19
|
-
* (`eslint src` → `eslint src/server`)
|
|
21
|
+
* (`eslint ./src` → `eslint ./src/server`). Note the argument has to LOOK
|
|
22
|
+
* like a path — a bare `src` with no separator, dot or glob is not treated
|
|
23
|
+
* as one, so `eslint src` → `eslint src/server` reads as neutral;
|
|
20
24
|
* 3. an exclusion flag/pattern is ADDED (`--ignore`, `--exclude`,
|
|
21
25
|
* `--testPathIgnorePatterns`, `-x`, …);
|
|
22
26
|
* 4. a config-file argument is swapped for one the fix pass itself created.
|
|
23
27
|
*
|
|
24
|
-
* With ONE excuse,
|
|
25
|
-
*
|
|
26
|
-
*
|
|
28
|
+
* With ONE excuse, documented at `excusedByRelocation`: rule 3 does not fire when
|
|
29
|
+
* the same pass hands the excluded input to another command the gate already
|
|
30
|
+
* runs. Rules 1, 2 and 4 have no excuse.
|
|
27
31
|
*
|
|
28
|
-
* Everything else passes: new env
|
|
29
|
-
* input set, reordering, added chain
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
+
* Everything else passes, each checked by running it: a new env var, a flag that
|
|
33
|
+
* does not restrict the input set, reordering, an added chain member, an
|
|
34
|
+
* identical body. A path replaced by its PARENT classifies as `widen`, not
|
|
35
|
+
* `narrow`. The guard's job is to stop the fix pass from silently redefining the
|
|
36
|
+
* gate's own success criterion — not to review the fix.
|
|
32
37
|
*
|
|
33
|
-
* PURE by construction
|
|
34
|
-
*
|
|
35
|
-
* from git blobs.
|
|
38
|
+
* PURE by construction: the module has no imports at all — no fs, no git, no
|
|
39
|
+
* clock. The live guard feeds it two body maps from `discoverGateCommandBodies`;
|
|
40
|
+
* anything replaying history feeds it two maps built from git blobs. The same
|
|
41
|
+
* code decides both.
|
|
36
42
|
*/
|
|
37
43
|
// ── tokenizing ──────────────────────────────────────────────────────────────
|
|
38
|
-
/** Split a shell body into chain members
|
|
39
|
-
*
|
|
44
|
+
/** Split a shell body into chain members on `&&`, `||`, `;`, `|` and newlines
|
|
45
|
+
* (Makefile recipes arrive as multiple lines). Quote-aware, and it matters:
|
|
46
|
+
* `bun test --name 'a && b' && lint` splits into two members, not three. */
|
|
40
47
|
export function splitChainMembers(body) {
|
|
41
48
|
const out = [];
|
|
42
49
|
let cur = '';
|
|
@@ -262,21 +269,20 @@ export function makefileRecipe(makefileText, target) {
|
|
|
262
269
|
}
|
|
263
270
|
// ── the rules ───────────────────────────────────────────────────────────────
|
|
264
271
|
/**
|
|
265
|
-
* RELOCATION EXCUSE
|
|
272
|
+
* RELOCATION EXCUSE.
|
|
266
273
|
*
|
|
267
|
-
*
|
|
268
|
-
*
|
|
269
|
-
*
|
|
270
|
-
*
|
|
271
|
-
* command, so the excluded specs never left the gate's coverage — the pass
|
|
272
|
-
* separated two runners that collide, it did not shrink what is measured.
|
|
273
|
-
* Rejecting it would be a false positive, and the rule as first written did.
|
|
274
|
+
* A fix pass can split two runners that collide — excluding a set of specs from
|
|
275
|
+
* one command while adding, in the same body, a chain member that another
|
|
276
|
+
* ALREADY-DISCOVERED gate command runs over exactly those specs. Nothing left the
|
|
277
|
+
* gate's coverage there, so rejecting it would be a false positive.
|
|
274
278
|
*
|
|
275
|
-
* The excuse is deliberately narrow and hard to game: the pass must have
|
|
276
|
-
*
|
|
277
|
-
* discovered gate command's body.
|
|
278
|
-
* near-copy pointed at a different config
|
|
279
|
-
*
|
|
279
|
+
* The excuse is deliberately narrow and hard to game: the pass must have ADDED a
|
|
280
|
+
* chain member that is VERBATIM (whitespace-normalized) a member of another
|
|
281
|
+
* discovered gate command's body. Run both ways — with the sibling present the
|
|
282
|
+
* exclusion classifies neutral, and a near-copy pointed at a different config
|
|
283
|
+
* does NOT excuse it. An added FLAG alone can never qualify, because there is no
|
|
284
|
+
* added member to match. Nothing excuses rules 1, 2 or 4: a path narrowing stands
|
|
285
|
+
* on its own.
|
|
280
286
|
*
|
|
281
287
|
* Returns the matching sibling member, or null.
|
|
282
288
|
*/
|
|
@@ -6,14 +6,17 @@ import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';
|
|
|
6
6
|
* returns — `godot --headless --check-only` with no timeout, a dev server, a
|
|
7
7
|
* hung test — and the run wedges until the user manually aborts and tells the
|
|
8
8
|
* model to add a timeout. pi's bash tool takes an OPTIONAL `timeout` with NO
|
|
9
|
-
* default
|
|
10
|
-
*
|
|
9
|
+
* default: its schema in `core/tools/bash.js` says "optional, no default
|
|
10
|
+
* timeout" and its resolver returns undefined for an absent value, so no timer is
|
|
11
|
+
* ever armed. This supplies the missing default from the host side.
|
|
11
12
|
*
|
|
12
13
|
* HOW: arm a wall-clock timer on `tool_execution_start`, disarm it on
|
|
13
|
-
* `tool_execution_end
|
|
14
|
-
* operation
|
|
15
|
-
*
|
|
16
|
-
*
|
|
14
|
+
* `tool_execution_end` — both real pi events. If it elapses, `ctx.abort()`
|
|
15
|
+
* cancels the in-flight operation, which fires the tool's AbortSignal; pi's bash
|
|
16
|
+
* executor imports `killProcessTree` and its own comment on that listener reads
|
|
17
|
+
* "Handle abort signal by killing the entire process tree". A follow-up user turn
|
|
18
|
+
* then tells the model what happened so it retries with a timeout instead of
|
|
19
|
+
* hanging again.
|
|
17
20
|
*
|
|
18
21
|
* Tool-agnostic by default: it arms on every tool except exact names listed in
|
|
19
22
|
* `commandTimeoutExemptTools`, which /task-config fills from the live tool list
|
|
@@ -7,14 +7,17 @@ import { CommandWatchdog, realTimerDeps, reminderMessage } from '../shared/comma
|
|
|
7
7
|
* returns — `godot --headless --check-only` with no timeout, a dev server, a
|
|
8
8
|
* hung test — and the run wedges until the user manually aborts and tells the
|
|
9
9
|
* model to add a timeout. pi's bash tool takes an OPTIONAL `timeout` with NO
|
|
10
|
-
* default
|
|
11
|
-
*
|
|
10
|
+
* default: its schema in `core/tools/bash.js` says "optional, no default
|
|
11
|
+
* timeout" and its resolver returns undefined for an absent value, so no timer is
|
|
12
|
+
* ever armed. This supplies the missing default from the host side.
|
|
12
13
|
*
|
|
13
14
|
* HOW: arm a wall-clock timer on `tool_execution_start`, disarm it on
|
|
14
|
-
* `tool_execution_end
|
|
15
|
-
* operation
|
|
16
|
-
*
|
|
17
|
-
*
|
|
15
|
+
* `tool_execution_end` — both real pi events. If it elapses, `ctx.abort()`
|
|
16
|
+
* cancels the in-flight operation, which fires the tool's AbortSignal; pi's bash
|
|
17
|
+
* executor imports `killProcessTree` and its own comment on that listener reads
|
|
18
|
+
* "Handle abort signal by killing the entire process tree". A follow-up user turn
|
|
19
|
+
* then tells the model what happened so it retries with a timeout instead of
|
|
20
|
+
* hanging again.
|
|
18
21
|
*
|
|
19
22
|
* Tool-agnostic by default: it arms on every tool except exact names listed in
|
|
20
23
|
* `commandTimeoutExemptTools`, which /task-config fills from the live tool list
|
|
@@ -33,16 +36,19 @@ import { CommandWatchdog, realTimerDeps, reminderMessage } from '../shared/comma
|
|
|
33
36
|
export { CommandWatchdog, commandTimeoutHint, realTimerDeps, reminderMessage, WATCHDOG_CANCEL_MARKER } from '../shared/command-watchdog.js';
|
|
34
37
|
/**
|
|
35
38
|
* One-shot marker: the most recent turn abort was issued BY THE WATCHDOG, not by
|
|
36
|
-
* a human ESC. Both end the assistant turn
|
|
37
|
-
*
|
|
38
|
-
* steer loop can win the race against the watchdog's queued
|
|
39
|
-
* show a steering prompt to an empty room
|
|
39
|
+
* a human ESC. Both end the assistant turn the same way, and `classifyTurnEnd`
|
|
40
|
+
* has nothing else to go on — it decides with `last?.stopReason === 'aborted'`.
|
|
41
|
+
* Without this flag the steer loop can win the race against the watchdog's queued
|
|
42
|
+
* follow-up turn and show a steering prompt to an empty room, wedging an
|
|
43
|
+
* unattended run.
|
|
40
44
|
*
|
|
41
|
-
* Set synchronously in onFire BEFORE ctx.abort(), so it is observable by the
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
45
|
+
* Set synchronously in onFire BEFORE ctx.abort(), so it is observable by the time
|
|
46
|
+
* any waitForIdle resolves; consumed by the first reader. Run: it reads false
|
|
47
|
+
* before any abort, true once after a note, false again immediately after — and
|
|
48
|
+
* two notes still yield exactly one true. A stale flag (the aborted turn was not
|
|
49
|
+
* one the steer loop was watching) only costs the consumer a bounded wait before
|
|
50
|
+
* it falls back to prompting; it can never permanently suppress a human's steer
|
|
51
|
+
* prompt.
|
|
46
52
|
*/
|
|
47
53
|
let watchdogAbortPending = false;
|
|
48
54
|
/** @internal Set by onFire when it aborts a turn. Exported for the adapter and tests. */
|