harnery 0.38.0 → 0.40.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/agents.d.ts.map +1 -1
- package/dist/commands/agents.js +32 -8
- package/dist/commands/artifacts.d.ts.map +1 -1
- package/dist/commands/artifacts.js +97 -6
- package/dist/commands/backup.d.ts +16 -0
- package/dist/commands/backup.d.ts.map +1 -1
- package/dist/commands/backup.js +239 -33
- package/dist/commands/browse.d.ts +8 -0
- package/dist/commands/browse.d.ts.map +1 -1
- package/dist/commands/browse.js +466 -231
- package/dist/commands/diagnostics.d.ts.map +1 -1
- package/dist/commands/diagnostics.js +6 -3
- package/dist/commands/ledger-v3.d.ts.map +1 -1
- package/dist/commands/ledger-v3.js +20 -4
- package/dist/commands/qa-run.d.ts.map +1 -1
- package/dist/commands/qa-run.js +26 -3
- package/dist/commands/qa-status.d.ts.map +1 -1
- package/dist/commands/qa-status.js +7 -2
- package/dist/commands/resources.d.ts.map +1 -1
- package/dist/commands/resources.js +23 -3
- package/dist/commands/review-pack.d.ts.map +1 -1
- package/dist/commands/review-pack.js +98 -23
- package/dist/commands/tunnel.d.ts.map +1 -1
- package/dist/commands/tunnel.js +2 -2
- package/dist/core/adapters/profiles.d.ts +1 -1
- package/dist/core/agents/canonical-emit.d.ts +3 -2
- package/dist/core/agents/canonical-emit.d.ts.map +1 -1
- package/dist/core/agents/canonical-emit.js +2 -0
- package/dist/core/agents/cli.js +13 -6
- package/dist/core/agents/live-authority-v3.d.ts.map +1 -1
- package/dist/core/agents/live-authority-v3.js +5 -3
- package/dist/core/agents/live-lifecycle-v3.d.ts +2 -2
- package/dist/core/agents/live-lifecycle-v3.d.ts.map +1 -1
- package/dist/core/agents/render/prompt-context.d.ts +7 -5
- package/dist/core/agents/render/prompt-context.d.ts.map +1 -1
- package/dist/core/agents/render/prompt-context.js +63 -8
- package/dist/core/agents/session-finalizer-v3.d.ts +2 -2
- package/dist/core/agents/session-finalizer-v3.d.ts.map +1 -1
- package/dist/core/agents/state/live-coordination-view.d.ts +3 -3
- package/dist/core/agents/state/live-coordination-view.d.ts.map +1 -1
- package/dist/core/agents/state/live-coordination-view.js +2 -1
- package/dist/core/agents/state/names.d.ts +35 -2
- package/dist/core/agents/state/names.d.ts.map +1 -1
- package/dist/core/agents/state/names.js +80 -5
- package/dist/core/agents/state/stale-sweep.d.ts +2 -1
- package/dist/core/agents/state/stale-sweep.d.ts.map +1 -1
- package/dist/core/agents/state/stale-sweep.js +23 -6
- package/dist/core/agents/turn-elapsed.d.ts +51 -0
- package/dist/core/agents/turn-elapsed.d.ts.map +1 -0
- package/dist/core/agents/turn-elapsed.js +91 -0
- package/dist/core/artifacts/activity.d.ts +14 -0
- package/dist/core/artifacts/activity.d.ts.map +1 -0
- package/dist/core/artifacts/activity.js +52 -0
- package/dist/core/artifacts/delivery-card.d.ts +39 -0
- package/dist/core/artifacts/delivery-card.d.ts.map +1 -0
- package/dist/core/artifacts/delivery-card.js +282 -0
- package/dist/core/artifacts/index.d.ts +42 -7
- package/dist/core/artifacts/index.d.ts.map +1 -1
- package/dist/core/artifacts/index.js +288 -76
- package/dist/core/artifacts/mutation-lock.d.ts +2 -0
- package/dist/core/artifacts/mutation-lock.d.ts.map +1 -0
- package/dist/core/artifacts/mutation-lock.js +91 -0
- package/dist/core/backup/host-snapshot.d.ts +18 -0
- package/dist/core/backup/host-snapshot.d.ts.map +1 -0
- package/dist/core/backup/host-snapshot.js +64 -0
- package/dist/core/config.d.ts +20 -5
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +44 -10
- package/dist/core/diagnostics/advice.d.ts +38 -3
- package/dist/core/diagnostics/advice.d.ts.map +1 -1
- package/dist/core/diagnostics/advice.js +103 -99
- package/dist/core/diagnostics/bundle.d.ts.map +1 -1
- package/dist/core/diagnostics/bundle.js +111 -47
- package/dist/core/diagnostics/comparison.d.ts.map +1 -1
- package/dist/core/diagnostics/comparison.js +3 -2
- package/dist/core/diagnostics/contract.d.ts +15 -31
- package/dist/core/diagnostics/contract.d.ts.map +1 -1
- package/dist/core/diagnostics/contract.js +2 -5
- package/dist/core/diagnostics/index.d.ts +2 -0
- package/dist/core/diagnostics/index.d.ts.map +1 -1
- package/dist/core/diagnostics/index.js +2 -0
- package/dist/core/diagnostics/pressure-contract.d.ts +260 -0
- package/dist/core/diagnostics/pressure-contract.d.ts.map +1 -0
- package/dist/core/diagnostics/pressure-contract.js +70 -0
- package/dist/core/diagnostics/pressure.d.ts +25 -0
- package/dist/core/diagnostics/pressure.d.ts.map +1 -0
- package/dist/core/diagnostics/pressure.js +1138 -0
- package/dist/core/diagnostics/replay.d.ts.map +1 -1
- package/dist/core/diagnostics/replay.js +90 -10
- package/dist/core/events/v3/adapter-id.d.ts +6 -0
- package/dist/core/events/v3/adapter-id.d.ts.map +1 -0
- package/dist/core/events/v3/adapter-id.js +7 -0
- package/dist/core/events/v3/archive-retention.d.ts.map +1 -1
- package/dist/core/events/v3/archive-retention.js +3 -2
- package/dist/core/events/v3/bootstrap.d.ts +41 -4
- package/dist/core/events/v3/bootstrap.d.ts.map +1 -1
- package/dist/core/events/v3/bootstrap.js +120 -17
- package/dist/core/events/v3/capabilities.d.ts +16 -9
- package/dist/core/events/v3/capabilities.d.ts.map +1 -1
- package/dist/core/events/v3/capabilities.js +37 -2
- package/dist/core/events/v3/capability-drift.d.ts +2 -2
- package/dist/core/events/v3/capability-drift.d.ts.map +1 -1
- package/dist/core/events/v3/control-witness.d.ts +48 -5
- package/dist/core/events/v3/control-witness.d.ts.map +1 -1
- package/dist/core/events/v3/control-witness.js +91 -36
- package/dist/core/events/v3/control.d.ts.map +1 -1
- package/dist/core/events/v3/control.js +22 -2
- package/dist/core/events/v3/index.d.ts +2 -1
- package/dist/core/events/v3/index.d.ts.map +1 -1
- package/dist/core/events/v3/index.js +2 -1
- package/dist/core/events/v3/live-feed.d.ts +7 -1
- package/dist/core/events/v3/live-feed.d.ts.map +1 -1
- package/dist/core/events/v3/live-feed.js +21 -3
- package/dist/core/events/v3/live-routing.d.ts.map +1 -1
- package/dist/core/events/v3/live-routing.js +10 -2
- package/dist/core/events/v3/producers/command-recorder.d.ts +2 -2
- package/dist/core/events/v3/producers/command-recorder.d.ts.map +1 -1
- package/dist/core/events/v3/producers/command-recorder.js +2 -1
- package/dist/core/events/v3/producers/coordination-recorder.d.ts +2 -2
- package/dist/core/events/v3/producers/coordination-recorder.d.ts.map +1 -1
- package/dist/core/events/v3/producers/coordination-recorder.js +2 -1
- package/dist/core/events/v3/producers/hook-base.d.ts +2 -2
- package/dist/core/events/v3/producers/hook-base.d.ts.map +1 -1
- package/dist/core/events/v3/producers/hook-base.js +15 -5
- package/dist/core/events/v3/producers/hook.d.ts.map +1 -1
- package/dist/core/events/v3/producers/hook.js +17 -4
- package/dist/core/events/v3/producers/intake.d.ts +4 -4
- package/dist/core/events/v3/producers/intake.d.ts.map +1 -1
- package/dist/core/events/v3/producers/intake.js +3 -2
- package/dist/core/events/v3/producers/producer-state-observer.d.ts.map +1 -1
- package/dist/core/events/v3/producers/producer-state-observer.js +2 -1
- package/dist/core/events/v3/producers/recorder.d.ts +16 -5
- package/dist/core/events/v3/producers/recorder.d.ts.map +1 -1
- package/dist/core/events/v3/producers/recorder.js +31 -9
- package/dist/core/events/v3/reader.d.ts.map +1 -1
- package/dist/core/events/v3/reader.js +10 -13
- package/dist/core/events/v3/runtime-build.d.ts.map +1 -1
- package/dist/core/events/v3/runtime-build.js +3 -2
- package/dist/core/events/v3/runtime-telemetry-capabilities.d.ts +2 -2
- package/dist/core/events/v3/runtime-telemetry-capabilities.d.ts.map +1 -1
- package/dist/core/events/v3/turn-telemetry.d.ts +2 -2
- package/dist/core/events/v3/turn-telemetry.d.ts.map +1 -1
- package/dist/core/hooks/adapter/output.d.ts +16 -14
- package/dist/core/hooks/adapter/output.d.ts.map +1 -1
- package/dist/core/hooks/adapter/output.js +36 -38
- package/dist/core/hooks/backup-schedule.d.ts +34 -0
- package/dist/core/hooks/backup-schedule.d.ts.map +1 -0
- package/dist/core/hooks/backup-schedule.js +114 -0
- package/dist/core/hooks/cli.js +61 -18
- package/dist/core/hooks/codex-wsl-bridge.d.ts +2 -0
- package/dist/core/hooks/codex-wsl-bridge.d.ts.map +1 -1
- package/dist/core/hooks/codex-wsl-bridge.js +18 -4
- package/dist/core/hooks/index.d.ts +1 -0
- package/dist/core/hooks/index.d.ts.map +1 -1
- package/dist/core/hooks/index.js +1 -0
- package/dist/core/hooks/prompt-context/contract.d.ts +61 -0
- package/dist/core/hooks/prompt-context/contract.d.ts.map +1 -0
- package/dist/core/hooks/prompt-context/contract.js +102 -0
- package/dist/core/hooks/prompt-context/index.d.ts +5 -0
- package/dist/core/hooks/prompt-context/index.d.ts.map +1 -0
- package/dist/core/hooks/prompt-context/index.js +2 -0
- package/dist/core/hooks/prompt-context/runner.d.ts +24 -0
- package/dist/core/hooks/prompt-context/runner.d.ts.map +1 -0
- package/dist/core/hooks/prompt-context/runner.js +249 -0
- package/dist/core/qa-artifacts.d.ts +3 -0
- package/dist/core/qa-artifacts.d.ts.map +1 -1
- package/dist/core/qa-artifacts.js +25 -3
- package/dist/core/resources/contract.d.ts +69 -3
- package/dist/core/resources/contract.d.ts.map +1 -1
- package/dist/core/resources/contract.js +1 -1
- package/dist/core/resources/index.d.ts +1 -0
- package/dist/core/resources/index.d.ts.map +1 -1
- package/dist/core/resources/index.js +1 -0
- package/dist/core/resources/native.d.ts +36 -0
- package/dist/core/resources/native.d.ts.map +1 -0
- package/dist/core/resources/native.js +418 -0
- package/dist/core/resources/sampler.d.ts +2 -0
- package/dist/core/resources/sampler.d.ts.map +1 -1
- package/dist/core/resources/sampler.js +31 -1
- package/dist/core/resources/status.d.ts +61 -0
- package/dist/core/resources/status.d.ts.map +1 -0
- package/dist/core/resources/status.js +536 -0
- package/dist/core/resources/system.d.ts +27 -0
- package/dist/core/resources/system.d.ts.map +1 -0
- package/dist/core/resources/system.js +439 -0
- package/dist/core/semantic/contract.d.ts +13 -13
- package/dist/core/semantic/service.d.ts +2 -0
- package/dist/core/semantic/service.d.ts.map +1 -1
- package/dist/core/semantic/service.js +27 -4
- package/dist/core/storage/builtins.js +41 -0
- package/dist/core/supervisor/contract.d.ts +41 -3
- package/dist/core/supervisor/contract.d.ts.map +1 -1
- package/dist/core/supervisor/contract.js +3 -2
- package/dist/core/supervisor/findings.d.ts +3 -0
- package/dist/core/supervisor/findings.d.ts.map +1 -1
- package/dist/core/supervisor/findings.js +88 -0
- package/dist/core/supervisor/history.d.ts.map +1 -1
- package/dist/core/supervisor/history.js +14 -0
- package/dist/core/supervisor/ledger-state.d.ts +55 -0
- package/dist/core/supervisor/ledger-state.d.ts.map +1 -0
- package/dist/core/supervisor/ledger-state.js +0 -0
- package/dist/core/supervisor/service.d.ts.map +1 -1
- package/dist/core/supervisor/service.js +120 -7
- package/dist/core/supervisor/storage.d.ts +3 -1
- package/dist/core/supervisor/storage.d.ts.map +1 -1
- package/dist/core/supervisor/storage.js +4 -0
- package/dist/core/workflow/admission.d.ts +3 -0
- package/dist/core/workflow/admission.d.ts.map +1 -1
- package/dist/core/workflow/admission.js +28 -16
- package/dist/core/workflow/engine.js +2 -2
- package/dist/core/workflow/live-session-v3.d.ts.map +1 -1
- package/dist/core/workflow/live-session-v3.js +6 -1
- package/dist/core/workflow/proof.d.ts.map +1 -1
- package/dist/core/workflow/proof.js +4 -3
- package/dist/lib/browser/capture-fidelity.d.ts +7 -0
- package/dist/lib/browser/capture-fidelity.d.ts.map +1 -1
- package/dist/lib/browser/capture-fidelity.js +17 -0
- package/dist/lib/browser/client.d.ts +9 -0
- package/dist/lib/browser/client.d.ts.map +1 -1
- package/dist/lib/browser/client.js +85 -37
- package/dist/lib/browser/index.d.ts +3 -1
- package/dist/lib/browser/index.d.ts.map +1 -1
- package/dist/lib/browser/index.js +2 -1
- package/dist/lib/browser/page-review-budget.d.ts +13 -0
- package/dist/lib/browser/page-review-budget.d.ts.map +1 -0
- package/dist/lib/browser/page-review-budget.js +251 -0
- package/dist/lib/browser/page-review-contracts.d.ts +127 -0
- package/dist/lib/browser/page-review-contracts.d.ts.map +1 -0
- package/dist/lib/browser/page-review-contracts.js +5 -0
- package/dist/lib/browser/page-review-evidence.d.ts +48 -0
- package/dist/lib/browser/page-review-evidence.d.ts.map +1 -0
- package/dist/lib/browser/page-review-evidence.js +340 -0
- package/dist/lib/browser/page-review-judge.d.ts +1 -1
- package/dist/lib/browser/page-review-judge.d.ts.map +1 -1
- package/dist/lib/browser/page-review-judge.js +46 -38
- package/dist/lib/browser/page-review-pack.d.ts +44 -22
- package/dist/lib/browser/page-review-pack.d.ts.map +1 -1
- package/dist/lib/browser/page-review-pack.js +116 -36
- package/dist/lib/browser/page-review-source.d.ts +12 -0
- package/dist/lib/browser/page-review-source.d.ts.map +1 -0
- package/dist/lib/browser/page-review-source.js +22 -0
- package/dist/lib/browser/qa-run-contracts.d.ts +35 -9
- package/dist/lib/browser/qa-run-contracts.d.ts.map +1 -1
- package/dist/lib/browser/qa-run-contracts.js +48 -14
- package/dist/lib/browser/qa-run.d.ts.map +1 -1
- package/dist/lib/browser/qa-run.js +215 -68
- package/dist/lib/browser/qa-snapshot.d.ts +6 -0
- package/dist/lib/browser/qa-snapshot.d.ts.map +1 -1
- package/dist/lib/browser/qa-snapshot.js +259 -48
- package/dist/lib/browser/review-capture-transaction.d.ts +13 -0
- package/dist/lib/browser/review-capture-transaction.d.ts.map +1 -0
- package/dist/lib/browser/review-capture-transaction.js +79 -0
- package/dist/lib/browser/standalone-html.d.ts.map +1 -1
- package/dist/lib/browser/standalone-html.js +4 -2
- package/dist/lib/browser/thumbnail-association.d.ts +34 -0
- package/dist/lib/browser/thumbnail-association.d.ts.map +1 -0
- package/dist/lib/browser/thumbnail-association.js +158 -0
- package/dist/lib/browser/tiling.d.ts +30 -0
- package/dist/lib/browser/tiling.d.ts.map +1 -1
- package/dist/lib/browser/tiling.js +38 -0
- package/dist/lib/docs-metadata-managed.d.ts.map +1 -1
- package/dist/lib/docs-metadata-managed.js +4 -1
- package/dist/lib/identities/assume.d.ts.map +1 -1
- package/dist/lib/identities/assume.js +7 -1
- package/dist/lib/instructions/templates.d.ts.map +1 -1
- package/dist/lib/instructions/templates.js +1 -0
- package/dist/lib/local-file-url.d.ts +12 -0
- package/dist/lib/local-file-url.d.ts.map +1 -1
- package/dist/lib/local-file-url.js +16 -2
- package/dist/lib/tunnel/state.d.ts +19 -4
- package/dist/lib/tunnel/state.d.ts.map +1 -1
- package/dist/lib/tunnel/state.js +53 -13
- package/package.json +7 -4
- package/schemas/config.schema.json +69 -2
- package/src/commands/agents.ts +42 -9
- package/src/commands/artifacts.ts +130 -6
- package/src/commands/backup.ts +287 -34
- package/src/commands/browse.ts +564 -267
- package/src/commands/diagnostics.ts +6 -3
- package/src/commands/ledger-v3.ts +20 -3
- package/src/commands/qa-run.ts +37 -3
- package/src/commands/qa-status.ts +11 -2
- package/src/commands/resources.ts +23 -3
- package/src/commands/review-pack.ts +165 -62
- package/src/commands/tunnel.ts +2 -1
- package/src/core/agents/canonical-emit.ts +4 -2
- package/src/core/agents/cli.ts +14 -6
- package/src/core/agents/live-authority-v3.ts +7 -3
- package/src/core/agents/live-lifecycle-v3.ts +2 -2
- package/src/core/agents/render/prompt-context.ts +68 -14
- package/src/core/agents/session-finalizer-v3.ts +2 -2
- package/src/core/agents/state/live-coordination-view.ts +4 -4
- package/src/core/agents/state/names.ts +92 -7
- package/src/core/agents/state/stale-sweep.ts +22 -8
- package/src/core/agents/turn-elapsed.ts +135 -0
- package/src/core/artifacts/activity.ts +71 -0
- package/src/core/artifacts/delivery-card.ts +375 -0
- package/src/core/artifacts/index.ts +385 -91
- package/src/core/artifacts/mutation-lock.ts +106 -0
- package/src/core/backup/host-snapshot.ts +76 -0
- package/src/core/config.ts +66 -13
- package/src/core/diagnostics/advice.ts +148 -120
- package/src/core/diagnostics/bundle.ts +138 -52
- package/src/core/diagnostics/comparison.ts +6 -2
- package/src/core/diagnostics/contract.ts +39 -42
- package/src/core/diagnostics/index.ts +2 -0
- package/src/core/diagnostics/pressure-contract.ts +294 -0
- package/src/core/diagnostics/pressure.ts +1361 -0
- package/src/core/diagnostics/replay.ts +116 -11
- package/src/core/events/v3/adapter-id.ts +12 -0
- package/src/core/events/v3/archive-retention.ts +3 -6
- package/src/core/events/v3/bootstrap.ts +163 -16
- package/src/core/events/v3/capabilities.ts +75 -25
- package/src/core/events/v3/capability-drift.ts +2 -2
- package/src/core/events/v3/control-witness.ts +172 -41
- package/src/core/events/v3/control.ts +34 -2
- package/src/core/events/v3/index.ts +3 -0
- package/src/core/events/v3/live-feed.ts +23 -5
- package/src/core/events/v3/live-routing.ts +14 -2
- package/src/core/events/v3/producers/command-recorder.ts +4 -4
- package/src/core/events/v3/producers/coordination-recorder.ts +4 -4
- package/src/core/events/v3/producers/hook-base.ts +21 -16
- package/src/core/events/v3/producers/hook.ts +21 -4
- package/src/core/events/v3/producers/intake.ts +6 -6
- package/src/core/events/v3/producers/producer-state-observer.ts +2 -1
- package/src/core/events/v3/producers/recorder.ts +50 -21
- package/src/core/events/v3/reader.ts +14 -13
- package/src/core/events/v3/runtime-build.ts +3 -2
- package/src/core/events/v3/runtime-telemetry-capabilities.ts +3 -3
- package/src/core/events/v3/turn-telemetry.ts +4 -4
- package/src/core/hooks/adapter/output.ts +50 -36
- package/src/core/hooks/backup-schedule.ts +147 -0
- package/src/core/hooks/cli.ts +65 -14
- package/src/core/hooks/codex-wsl-bridge.ts +21 -5
- package/src/core/hooks/index.ts +1 -0
- package/src/core/hooks/prompt-context/contract.ts +177 -0
- package/src/core/hooks/prompt-context/index.ts +22 -0
- package/src/core/hooks/prompt-context/runner.ts +303 -0
- package/src/core/qa-artifacts.ts +29 -3
- package/src/core/resources/contract.ts +76 -3
- package/src/core/resources/index.ts +1 -0
- package/src/core/resources/native.ts +481 -0
- package/src/core/resources/sampler.ts +46 -1
- package/src/core/resources/status.ts +717 -0
- package/src/core/resources/system.ts +530 -0
- package/src/core/semantic/service.ts +37 -3
- package/src/core/storage/builtins.ts +41 -0
- package/src/core/supervisor/contract.ts +45 -3
- package/src/core/supervisor/findings.ts +159 -0
- package/src/core/supervisor/history.ts +15 -0
- package/src/core/supervisor/ledger-state.ts +0 -0
- package/src/core/supervisor/service.ts +133 -5
- package/src/core/supervisor/storage.ts +7 -0
- package/src/core/workflow/admission.ts +33 -17
- package/src/core/workflow/engine.ts +2 -2
- package/src/core/workflow/live-session-v3.ts +6 -8
- package/src/core/workflow/proof.ts +4 -3
- package/src/lib/browser/capture-fidelity.ts +23 -0
- package/src/lib/browser/client.ts +96 -44
- package/src/lib/browser/index.ts +15 -1
- package/src/lib/browser/page-review-budget.ts +309 -0
- package/src/lib/browser/page-review-contracts.ts +129 -0
- package/src/lib/browser/page-review-evidence.ts +465 -0
- package/src/lib/browser/page-review-judge.ts +56 -40
- package/src/lib/browser/page-review-pack.ts +181 -43
- package/src/lib/browser/page-review-source.ts +30 -0
- package/src/lib/browser/qa-run-contracts.ts +62 -18
- package/src/lib/browser/qa-run.ts +259 -71
- package/src/lib/browser/qa-snapshot.ts +289 -48
- package/src/lib/browser/review-capture-transaction.ts +106 -0
- package/src/lib/browser/standalone-html.ts +4 -2
- package/src/lib/browser/thumbnail-association.ts +190 -0
- package/src/lib/browser/tiling.ts +63 -0
- package/src/lib/docs-metadata-managed.ts +4 -1
- package/src/lib/identities/assume.ts +12 -4
- package/src/lib/instructions/templates.ts +1 -0
- package/src/lib/local-file-url.ts +17 -2
- package/src/lib/tunnel/state.ts +70 -13
|
@@ -17,6 +17,17 @@ import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "
|
|
|
17
17
|
import { cpus, freemem, loadavg, totalmem } from "node:os";
|
|
18
18
|
import { basename, join } from "node:path";
|
|
19
19
|
import { type CritiqueProvider, DEFAULT_CRITIQUE_RUBRIC } from "./critique.js";
|
|
20
|
+
import {
|
|
21
|
+
allocateTileBudget,
|
|
22
|
+
capturePlanDigest,
|
|
23
|
+
validatePageReviewCapturePlan,
|
|
24
|
+
} from "./page-review-budget.js";
|
|
25
|
+
import {
|
|
26
|
+
PAGE_REVIEW_DEFAULT_TILE_BUDGET,
|
|
27
|
+
type PageReviewCapturePlan,
|
|
28
|
+
type PageReviewContextAllocation,
|
|
29
|
+
} from "./page-review-contracts.js";
|
|
30
|
+
import { applyPackReviewDecisions, buildPackNativeEvidence } from "./page-review-evidence.js";
|
|
20
31
|
import { type JudgedContext, judgePageReviewPack, toCritiqueRecords } from "./page-review-judge.js";
|
|
21
32
|
import {
|
|
22
33
|
finalizePageReviewPack,
|
|
@@ -31,12 +42,12 @@ import {
|
|
|
31
42
|
type PageReviewGateRecord,
|
|
32
43
|
readPackContext,
|
|
33
44
|
readPackDom,
|
|
34
|
-
readPackFullPage,
|
|
35
45
|
readPackManifest,
|
|
36
46
|
readPackSignature,
|
|
47
|
+
readPackTiles,
|
|
37
48
|
} from "./page-review-pack.js";
|
|
38
49
|
import type { QaManifest } from "./qa-plan.js";
|
|
39
|
-
import {
|
|
50
|
+
import type { PersistedCritique } from "./qa-reuse.js";
|
|
40
51
|
import {
|
|
41
52
|
computeJobDigest,
|
|
42
53
|
computeVerdict,
|
|
@@ -57,7 +68,7 @@ import {
|
|
|
57
68
|
type QaRunStatusDocument,
|
|
58
69
|
type QaRunStatusState,
|
|
59
70
|
} from "./qa-run-contracts.js";
|
|
60
|
-
import { type QaSnapshotStoreOptions, saveQaSnapshot } from "./qa-snapshot.js";
|
|
71
|
+
import { loadReviewDecisions, type QaSnapshotStoreOptions, saveQaSnapshot } from "./qa-snapshot.js";
|
|
61
72
|
|
|
62
73
|
/** Set on the runner's own environment before the host's critique provider is
|
|
63
74
|
* loaded for the judge stage, unless the job permits metered critique: the
|
|
@@ -130,7 +141,6 @@ const DEFAULT_COMMAND_TIMEOUT_MS = 120_000;
|
|
|
130
141
|
const DEFAULT_RUN_DEADLINE_MS = 900_000;
|
|
131
142
|
const DEFAULT_COMMAND_CONCURRENCY = 2;
|
|
132
143
|
/** Gate-hit rectangles forwarded to one context's capture child (`--review-pack-hit-rect`). */
|
|
133
|
-
const REVIEW_PACK_HIT_RECTS_PER_CONTEXT = 50;
|
|
134
144
|
const EXEC_MAX_BUFFER = 16 * 1024 * 1024;
|
|
135
145
|
|
|
136
146
|
/** The planner's deterministic vocabulary is an executable contract, not a
|
|
@@ -775,14 +785,25 @@ export async function runQaMatrix(options: QaRunMatrixOptions): Promise<QaRunRes
|
|
|
775
785
|
ctx.viewport,
|
|
776
786
|
...(ctx.theme === "dark" ? ["--color-scheme", "dark"] : []),
|
|
777
787
|
...(ctx.args ?? []),
|
|
788
|
+
...(job.interaction_states ?? [])
|
|
789
|
+
.filter((state) => state.name === ctx.state)
|
|
790
|
+
.flatMap((state) => [
|
|
791
|
+
...state.setup,
|
|
792
|
+
...state.assertions.flatMap((assertion) => ["--assert", assertion]),
|
|
793
|
+
"--assert-fail",
|
|
794
|
+
]),
|
|
778
795
|
];
|
|
779
796
|
|
|
780
797
|
// The planner's tile ceiling and every critique child must agree on the
|
|
781
798
|
// per-context band cap, or the predicted cost and the real coverage drift.
|
|
782
|
-
const critiqueMaxTilesArgs =
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
799
|
+
const critiqueMaxTilesArgs = [
|
|
800
|
+
"--check-critique-max-tiles",
|
|
801
|
+
String(
|
|
802
|
+
job.policy?.critique_max_tiles ??
|
|
803
|
+
job.policy?.critique_tile_budget ??
|
|
804
|
+
PAGE_REVIEW_DEFAULT_TILE_BUDGET,
|
|
805
|
+
),
|
|
806
|
+
];
|
|
786
807
|
|
|
787
808
|
// ------------------------------------------------------------------ plan
|
|
788
809
|
const planArgv = [
|
|
@@ -870,6 +891,35 @@ export async function runQaMatrix(options: QaRunMatrixOptions): Promise<QaRunRes
|
|
|
870
891
|
}
|
|
871
892
|
return finalize();
|
|
872
893
|
}
|
|
894
|
+
// The planning render and capture must use the same native band geometry.
|
|
895
|
+
// Load the provider's pixel budget before gates, without making model calls.
|
|
896
|
+
const visual = manifest.checks.visual;
|
|
897
|
+
const runsVisual = visual !== "none";
|
|
898
|
+
const priorHeadlessOnly = process.env[QA_RUN_HEADLESS_ONLY_ENV];
|
|
899
|
+
const restoreHeadlessOnly = (): void => {
|
|
900
|
+
if (priorHeadlessOnly === undefined) delete process.env[QA_RUN_HEADLESS_ONLY_ENV];
|
|
901
|
+
else process.env[QA_RUN_HEADLESS_ONLY_ENV] = priorHeadlessOnly;
|
|
902
|
+
};
|
|
903
|
+
let provider: CritiqueProvider | undefined;
|
|
904
|
+
if (runsVisual) {
|
|
905
|
+
if (!job.policy?.allow_metered_critique) process.env[QA_RUN_HEADLESS_ONLY_ENV] = "1";
|
|
906
|
+
try {
|
|
907
|
+
provider =
|
|
908
|
+
options.critiqueProvider ??
|
|
909
|
+
(options.critiqueProviderLoader ? await options.critiqueProviderLoader() : undefined);
|
|
910
|
+
} catch (err: unknown) {
|
|
911
|
+
log(`critique provider failed to load: ${err instanceof Error ? err.message : String(err)}`);
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
const bandArgs =
|
|
915
|
+
provider?.tileBudgetPx !== undefined
|
|
916
|
+
? ["--check-critique-band", String(Math.max(200, Math.min(1400, provider.tileBudgetPx)))]
|
|
917
|
+
: [];
|
|
918
|
+
const scopeArgs =
|
|
919
|
+
visual === "full-page"
|
|
920
|
+
? []
|
|
921
|
+
: manifest.scopes.flatMap((s) => ["--review-pack-scope", s.selector]);
|
|
922
|
+
const gateCapturePlans = new Map<string, PageReviewCapturePlan>();
|
|
873
923
|
const manifestGateArgs = manifest.checks.deterministic.flatMap(
|
|
874
924
|
(check) => MANIFEST_DETERMINISTIC_ARGS[check] ?? [],
|
|
875
925
|
);
|
|
@@ -901,6 +951,20 @@ export async function runQaMatrix(options: QaRunMatrixOptions): Promise<QaRunRes
|
|
|
901
951
|
...(manifest.checks.visual !== "none" ? ["--no-screenshot"] : []),
|
|
902
952
|
...manifestGateArgs,
|
|
903
953
|
...applicable.flatMap((check) => check.args),
|
|
954
|
+
...(runsVisual
|
|
955
|
+
? [
|
|
956
|
+
"--review-pack-plan",
|
|
957
|
+
"--review-pack-context",
|
|
958
|
+
ctx.id,
|
|
959
|
+
"--qa-theme",
|
|
960
|
+
ctx.theme,
|
|
961
|
+
"--qa-state",
|
|
962
|
+
ctx.state,
|
|
963
|
+
...scopeArgs,
|
|
964
|
+
...bandArgs,
|
|
965
|
+
...critiqueMaxTilesArgs,
|
|
966
|
+
]
|
|
967
|
+
: []),
|
|
904
968
|
];
|
|
905
969
|
log(`gate ${ctx.id}: ${checkId}`);
|
|
906
970
|
const { res, wallTimeMs } = await timedExec(argv, baseEnv);
|
|
@@ -908,6 +972,26 @@ export async function runQaMatrix(options: QaRunMatrixOptions): Promise<QaRunRes
|
|
|
908
972
|
const failures: string[] = [];
|
|
909
973
|
let outcome: QaRunCommandOutcome["outcome"];
|
|
910
974
|
const envelope = existsSync(jsonPath) ? readEnvelope(jsonPath) : undefined;
|
|
975
|
+
if (runsVisual) {
|
|
976
|
+
try {
|
|
977
|
+
const capturePlan = validatePageReviewCapturePlan(envelope?.review_pack_capture_plan);
|
|
978
|
+
if (
|
|
979
|
+
capturePlan.context_id !== ctx.id ||
|
|
980
|
+
capturePlan.viewport !== ctx.viewport ||
|
|
981
|
+
capturePlan.theme !== ctx.theme ||
|
|
982
|
+
capturePlan.state !== ctx.state
|
|
983
|
+
) {
|
|
984
|
+
throw new Error("capture plan belongs to a different render context");
|
|
985
|
+
}
|
|
986
|
+
gateCapturePlans.set(ctx.id, capturePlan);
|
|
987
|
+
} catch (err: unknown) {
|
|
988
|
+
blockers.push({
|
|
989
|
+
stage: "capture",
|
|
990
|
+
context_id: ctx.id,
|
|
991
|
+
reason: `capture plan unavailable: ${err instanceof Error ? err.message : String(err)}`,
|
|
992
|
+
});
|
|
993
|
+
}
|
|
994
|
+
}
|
|
911
995
|
const consoleFailures = enforceConsole ? parseConsoleFailures(envelope) : [];
|
|
912
996
|
if (!res.error && res.exitCode === 0 && existsSync(jsonPath)) {
|
|
913
997
|
if (consoleFailures.length > 0) {
|
|
@@ -1030,9 +1114,6 @@ export async function runQaMatrix(options: QaRunMatrixOptions): Promise<QaRunRes
|
|
|
1030
1114
|
// Each context is rendered ONCE into the run's page review pack (full-page
|
|
1031
1115
|
// screenshot, tiles as PNG files, DOM, signature) through the same bounded
|
|
1032
1116
|
// pool the gates used, and its browser closes before any vision call.
|
|
1033
|
-
const visual = manifest.checks.visual;
|
|
1034
|
-
const cleanSoFar =
|
|
1035
|
-
blockers.length === 0 && commands.every((command) => command.outcome === "passed");
|
|
1036
1117
|
const packDir = join(outDir, PAGE_REVIEW_PACK_DIRNAME);
|
|
1037
1118
|
const capturedRecords: PageReviewContextRecord[] = [];
|
|
1038
1119
|
const judgeCommand = options.reviewPackJudgeCommand?.(packDir);
|
|
@@ -1102,61 +1183,62 @@ export async function runQaMatrix(options: QaRunMatrixOptions): Promise<QaRunRes
|
|
|
1102
1183
|
}
|
|
1103
1184
|
};
|
|
1104
1185
|
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
// for --check-critique) and the judge reuses the same instance.
|
|
1108
|
-
const priorHeadlessOnly = process.env[QA_RUN_HEADLESS_ONLY_ENV];
|
|
1109
|
-
const restoreHeadlessOnly = (): void => {
|
|
1110
|
-
if (priorHeadlessOnly === undefined) delete process.env[QA_RUN_HEADLESS_ONLY_ENV];
|
|
1111
|
-
else process.env[QA_RUN_HEADLESS_ONLY_ENV] = priorHeadlessOnly;
|
|
1112
|
-
};
|
|
1113
|
-
let provider: CritiqueProvider | undefined;
|
|
1114
|
-
const runsVisual = cleanSoFar && visual !== "none";
|
|
1186
|
+
enterStage("capture");
|
|
1187
|
+
const captureStart = Date.now();
|
|
1115
1188
|
if (runsVisual) {
|
|
1116
|
-
|
|
1189
|
+
writeFileSync(
|
|
1190
|
+
join(outDir, "preliminary-gates.json"),
|
|
1191
|
+
`${JSON.stringify(gateOutcomes, null, 2)}\n`,
|
|
1192
|
+
);
|
|
1193
|
+
for (const gate of gateOutcomes)
|
|
1194
|
+
if (gate) {
|
|
1195
|
+
gate.outcome = "unknown";
|
|
1196
|
+
gate.failures = ["Authoritative same-visit gates and capture have not completed."];
|
|
1197
|
+
}
|
|
1198
|
+
const allocations = new Map<string, PageReviewContextAllocation>();
|
|
1117
1199
|
try {
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
// gates ran first, so the rects are known before the page is captured.
|
|
1137
|
-
const hitRectsByContext = new Map<string, string[]>();
|
|
1138
|
-
for (const gate of gateRecords()) {
|
|
1139
|
-
for (const hit of gate.hits ?? []) {
|
|
1140
|
-
const list = hitRectsByContext.get(gate.context_id) ?? [];
|
|
1141
|
-
if (list.length >= REVIEW_PACK_HIT_RECTS_PER_CONTEXT) break;
|
|
1142
|
-
const r = hit.rect;
|
|
1143
|
-
list.push(
|
|
1144
|
-
[r.x, r.y, Math.max(0, r.width), Math.max(0, r.height)].map((n) => Math.round(n)).join(","),
|
|
1200
|
+
const plans = contexts.map((ctx) => {
|
|
1201
|
+
const candidate = gateCapturePlans.get(ctx.id);
|
|
1202
|
+
if (!candidate)
|
|
1203
|
+
throw new Error(
|
|
1204
|
+
`no valid capture plan for ${ctx.id}; rerun after fixing its gate capture`,
|
|
1205
|
+
);
|
|
1206
|
+
return candidate;
|
|
1207
|
+
});
|
|
1208
|
+
const ceilings =
|
|
1209
|
+
job.policy?.critique_max_tiles === undefined
|
|
1210
|
+
? undefined
|
|
1211
|
+
: Object.fromEntries(
|
|
1212
|
+
contexts.map((ctx) => [ctx.id, job.policy?.critique_max_tiles as number]),
|
|
1213
|
+
);
|
|
1214
|
+
const allocation = allocateTileBudget(
|
|
1215
|
+
plans,
|
|
1216
|
+
job.policy?.critique_tile_budget ?? PAGE_REVIEW_DEFAULT_TILE_BUDGET,
|
|
1217
|
+
ceilings,
|
|
1145
1218
|
);
|
|
1146
|
-
|
|
1219
|
+
for (const row of allocation.contexts) allocations.set(row.context_id, row);
|
|
1220
|
+
writeFileSync(
|
|
1221
|
+
join(outDir, "tile-allocation.json"),
|
|
1222
|
+
`${JSON.stringify(allocation, null, 2)}\n`,
|
|
1223
|
+
);
|
|
1224
|
+
log(
|
|
1225
|
+
`tile budget: ${allocation.used}/${allocation.budget} allocated across ${allocations.size} contexts`,
|
|
1226
|
+
);
|
|
1227
|
+
} catch (err: unknown) {
|
|
1228
|
+
blockers.push({
|
|
1229
|
+
stage: "capture",
|
|
1230
|
+
reason: `tile allocation failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
1231
|
+
});
|
|
1147
1232
|
}
|
|
1148
|
-
}
|
|
1149
|
-
const hitRectArgs = (contextId: string): string[] =>
|
|
1150
|
-
(hitRectsByContext.get(contextId) ?? []).flatMap((spec) => ["--review-pack-hit-rect", spec]);
|
|
1151
|
-
|
|
1152
|
-
enterStage("capture");
|
|
1153
|
-
const captureStart = Date.now();
|
|
1154
|
-
if (runsVisual) {
|
|
1155
1233
|
const captureOutcomes = new Array<QaRunCommandOutcome>(contexts.length);
|
|
1156
1234
|
const captureRecords = new Array<PageReviewContextRecord | undefined>(contexts.length);
|
|
1157
1235
|
await runPool(contexts, concurrency, async (ctx, index) => {
|
|
1158
1236
|
if (pastDeadline()) return;
|
|
1237
|
+
const allocation = allocations.get(ctx.id);
|
|
1238
|
+
if (!allocation) return;
|
|
1159
1239
|
const outPrefix = join(outDir, `${ctx.id}-capture`);
|
|
1240
|
+
const allocationFile = `${outPrefix}-allocation.json`;
|
|
1241
|
+
writeFileSync(allocationFile, `${JSON.stringify(allocation)}\n`);
|
|
1160
1242
|
const argv = [
|
|
1161
1243
|
...browseArgv,
|
|
1162
1244
|
job.target,
|
|
@@ -1172,11 +1254,16 @@ export async function runQaMatrix(options: QaRunMatrixOptions): Promise<QaRunRes
|
|
|
1172
1254
|
ctx.theme,
|
|
1173
1255
|
"--qa-state",
|
|
1174
1256
|
ctx.state,
|
|
1175
|
-
|
|
1257
|
+
"--review-pack-reservation",
|
|
1258
|
+
allocationFile,
|
|
1259
|
+
"--review-pack-plan",
|
|
1260
|
+
...manifestGateArgs,
|
|
1261
|
+
...checks
|
|
1262
|
+
.filter((check) => check.contexts === undefined || check.contexts.includes(ctx.id))
|
|
1263
|
+
.flatMap((check) => check.args),
|
|
1176
1264
|
...scopeArgs,
|
|
1177
1265
|
...critiqueMaxTilesArgs,
|
|
1178
1266
|
...bandArgs,
|
|
1179
|
-
...hitRectArgs(ctx.id),
|
|
1180
1267
|
];
|
|
1181
1268
|
log(
|
|
1182
1269
|
`capture ${ctx.id}${scopeArgs.length > 0 ? ` [${manifest.scopes.map((s) => s.selector).join(",")}]` : ""}`,
|
|
@@ -1188,15 +1275,54 @@ export async function runQaMatrix(options: QaRunMatrixOptions): Promise<QaRunRes
|
|
|
1188
1275
|
const failures: string[] = [];
|
|
1189
1276
|
let outcome: QaRunCommandOutcome["outcome"];
|
|
1190
1277
|
let record: PageReviewContextRecord | undefined;
|
|
1191
|
-
if (
|
|
1278
|
+
if (
|
|
1279
|
+
!res.error &&
|
|
1280
|
+
(res.exitCode === 0 || res.exitCode === 2) &&
|
|
1281
|
+
envelope &&
|
|
1282
|
+
report?.context_id
|
|
1283
|
+
) {
|
|
1192
1284
|
try {
|
|
1193
1285
|
record = readPackContext(packDir, report.context_id);
|
|
1194
|
-
|
|
1286
|
+
const currentPlan = validatePageReviewCapturePlan(envelope.review_pack_capture_plan);
|
|
1287
|
+
const transaction = envelope.review_pack_transaction as
|
|
1288
|
+
| { source_digest?: string; attempts?: Array<{ status?: string }> }
|
|
1289
|
+
| undefined;
|
|
1290
|
+
if (
|
|
1291
|
+
!record.capture_plan ||
|
|
1292
|
+
record.id !== ctx.id ||
|
|
1293
|
+
capturePlanDigest(record.capture_plan) !== capturePlanDigest(currentPlan) ||
|
|
1294
|
+
transaction?.source_digest !== currentPlan.source_digest ||
|
|
1295
|
+
transaction.attempts?.at(-1)?.status !== "captured" ||
|
|
1296
|
+
record.tiles.length > allocation.selected_ids.length
|
|
1297
|
+
) {
|
|
1298
|
+
throw new Error(
|
|
1299
|
+
"Fresh gates, source identity and native capture are not bound to the same transaction.",
|
|
1300
|
+
);
|
|
1301
|
+
}
|
|
1302
|
+
const freshFailures = [
|
|
1303
|
+
...parseGateFailures(envelope),
|
|
1304
|
+
...(enforceConsole ? parseConsoleFailures(envelope) : []),
|
|
1305
|
+
];
|
|
1306
|
+
if (res.exitCode === 2 && !freshFailures.length)
|
|
1307
|
+
freshFailures.push("Fresh gate command returned exit code 2.");
|
|
1308
|
+
const preliminaryGate = gateOutcomes[index];
|
|
1309
|
+
if (!preliminaryGate) throw new Error("Missing context gate identity.");
|
|
1310
|
+
Object.assign(preliminaryGate, {
|
|
1311
|
+
argv,
|
|
1312
|
+
exit_code: res.exitCode,
|
|
1313
|
+
outcome: freshFailures.length ? "failed" : "passed",
|
|
1314
|
+
failures: freshFailures,
|
|
1315
|
+
artifacts: gatherArtifacts(outPrefix),
|
|
1316
|
+
wall_time_ms: wallTimeMs,
|
|
1317
|
+
});
|
|
1318
|
+
outcome = freshFailures.length ? "failed" : "passed";
|
|
1319
|
+
failures.push(...freshFailures);
|
|
1195
1320
|
} catch (err: unknown) {
|
|
1196
1321
|
outcome = "unknown";
|
|
1197
1322
|
const reason = `pack context unreadable: ${err instanceof Error ? err.message : String(err)}`;
|
|
1198
1323
|
failures.push(reason);
|
|
1199
1324
|
blockers.push({ stage: "capture", context_id: ctx.id, reason });
|
|
1325
|
+
record = undefined;
|
|
1200
1326
|
}
|
|
1201
1327
|
} else {
|
|
1202
1328
|
outcome = "unknown";
|
|
@@ -1274,8 +1400,60 @@ export async function runQaMatrix(options: QaRunMatrixOptions): Promise<QaRunRes
|
|
|
1274
1400
|
};
|
|
1275
1401
|
for (const row of judged.contexts) {
|
|
1276
1402
|
judgedById.set(row.context_id, row);
|
|
1403
|
+
const captured = capturedRecords.find((record) => record.id === row.context_id);
|
|
1404
|
+
if (captured) captured.evidence_context = row.record.evidence_context;
|
|
1277
1405
|
let contextOutcome: QaRunCritiqueOutcome["outcome"] =
|
|
1278
1406
|
row.outcome === "pass" ? "passed" : row.outcome === "fail" ? "failed" : "unknown";
|
|
1407
|
+
let dismissed: NonNullable<QaRunCritiqueOutcome["dismissed"]> = [];
|
|
1408
|
+
if (
|
|
1409
|
+
job.policy?.accept_dispositions === true &&
|
|
1410
|
+
(row.outcome === "pass" || row.outcome === "fail") &&
|
|
1411
|
+
row.tiles_unjudged === 0 &&
|
|
1412
|
+
!row.record.capture_incomplete &&
|
|
1413
|
+
!row.coverage.capped
|
|
1414
|
+
) {
|
|
1415
|
+
try {
|
|
1416
|
+
const evidence = buildPackNativeEvidence(
|
|
1417
|
+
row.record,
|
|
1418
|
+
readPackTiles(packDir, row.record),
|
|
1419
|
+
DEFAULT_CRITIQUE_RUBRIC,
|
|
1420
|
+
);
|
|
1421
|
+
const slot = loadReviewDecisions(
|
|
1422
|
+
job.target,
|
|
1423
|
+
{ viewport: row.record.viewport, theme: row.record.theme, state: row.record.state },
|
|
1424
|
+
options.snapshotStore ?? {},
|
|
1425
|
+
);
|
|
1426
|
+
const reviewed = applyPackReviewDecisions({
|
|
1427
|
+
target: job.target,
|
|
1428
|
+
evidence,
|
|
1429
|
+
findings: row.findings,
|
|
1430
|
+
slot,
|
|
1431
|
+
enabled: true,
|
|
1432
|
+
});
|
|
1433
|
+
dismissed = reviewed.applied;
|
|
1434
|
+
if (
|
|
1435
|
+
dismissed.length > 0 &&
|
|
1436
|
+
reviewed.findings.every((finding) => finding.severity !== "high")
|
|
1437
|
+
) {
|
|
1438
|
+
contextOutcome = "passed";
|
|
1439
|
+
}
|
|
1440
|
+
} catch (err: unknown) {
|
|
1441
|
+
contextOutcome = "unknown";
|
|
1442
|
+
blockers.push({
|
|
1443
|
+
stage: "critique",
|
|
1444
|
+
context_id: row.context_id,
|
|
1445
|
+
reason: `reviewed disposition evidence unavailable: ${err instanceof Error ? err.message : String(err)}`,
|
|
1446
|
+
});
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
if (row.record.capture_incomplete) {
|
|
1450
|
+
if (contextOutcome !== "failed") contextOutcome = "unknown";
|
|
1451
|
+
blockers.push({
|
|
1452
|
+
stage: "capture",
|
|
1453
|
+
context_id: row.context_id,
|
|
1454
|
+
reason: "native capture evidence is incomplete; recapture this context before signoff",
|
|
1455
|
+
});
|
|
1456
|
+
}
|
|
1279
1457
|
if (row.outcome === "skipped") {
|
|
1280
1458
|
const detail = row.error ?? "critique reported no conclusive outcome";
|
|
1281
1459
|
blockers.push({
|
|
@@ -1292,13 +1470,13 @@ export async function runQaMatrix(options: QaRunMatrixOptions): Promise<QaRunRes
|
|
|
1292
1470
|
stage: "critique",
|
|
1293
1471
|
context_id: row.context_id,
|
|
1294
1472
|
reason:
|
|
1473
|
+
row.error ??
|
|
1295
1474
|
`judge did not review ${row.tiles_unjudged} of ${row.tiles_total} tile(s) before the ` +
|
|
1296
|
-
|
|
1475
|
+
"run deadline; nothing is proven for them",
|
|
1297
1476
|
});
|
|
1298
1477
|
}
|
|
1299
|
-
//
|
|
1300
|
-
//
|
|
1301
|
-
// `coverage` and leaves the verdict to the tiles that were seen.
|
|
1478
|
+
// Selected bands may leave gaps anywhere. Signoff needs complete
|
|
1479
|
+
// coverage, while review mode reports what was actually captured.
|
|
1302
1480
|
if (job.mode === "signoff" && row.coverage.capped && contextOutcome !== "failed") {
|
|
1303
1481
|
contextOutcome = "unknown";
|
|
1304
1482
|
blockers.push({
|
|
@@ -1307,7 +1485,7 @@ export async function runQaMatrix(options: QaRunMatrixOptions): Promise<QaRunRes
|
|
|
1307
1485
|
reason:
|
|
1308
1486
|
`critique coverage capped: ${row.coverage.bands_reviewed} of ${row.coverage.bands_total} bands ` +
|
|
1309
1487
|
`reviewed (${row.coverage.reviewed_height_px} of ${row.coverage.page_height_px} px); raise ` +
|
|
1310
|
-
"policy.
|
|
1488
|
+
"policy.critique_tile_budget (and any explicit per-context ceiling) for full signoff coverage",
|
|
1311
1489
|
});
|
|
1312
1490
|
}
|
|
1313
1491
|
const scopeById = new Map(row.record.tiles.map((tile) => [tile.id, tile.scope]));
|
|
@@ -1318,6 +1496,7 @@ export async function runQaMatrix(options: QaRunMatrixOptions): Promise<QaRunRes
|
|
|
1318
1496
|
tiles_reviewed: row.tiles_reviewed,
|
|
1319
1497
|
tiles_reused: row.tiles_reused,
|
|
1320
1498
|
outcome: contextOutcome,
|
|
1499
|
+
...(dismissed.length > 0 ? { dismissed } : {}),
|
|
1321
1500
|
findings: row.findings.map((finding) => {
|
|
1322
1501
|
const selector = scopeById.get(finding.tile_id);
|
|
1323
1502
|
return {
|
|
@@ -1339,7 +1518,7 @@ export async function runQaMatrix(options: QaRunMatrixOptions): Promise<QaRunRes
|
|
|
1339
1518
|
// -------------------------------------------------------------- snapshot
|
|
1340
1519
|
// Signoff persists each context's baseline from the pack's own files (no
|
|
1341
1520
|
// browser). The critique rides along only when the whole tile set was
|
|
1342
|
-
//
|
|
1521
|
+
// judged or safely reused, uncapped, and unscoped — a partial or scoped review must
|
|
1343
1522
|
// never become the next baseline's finding record. When the manifest
|
|
1344
1523
|
// required no visual pass, a dedicated browse --qa-snapshot pass still runs.
|
|
1345
1524
|
enterStage("snapshot");
|
|
@@ -1352,15 +1531,15 @@ export async function runQaMatrix(options: QaRunMatrixOptions): Promise<QaRunRes
|
|
|
1352
1531
|
const persistCritique =
|
|
1353
1532
|
row !== undefined &&
|
|
1354
1533
|
(row.outcome === "pass" || row.outcome === "fail") &&
|
|
1534
|
+
!record.capture_incomplete &&
|
|
1355
1535
|
!record.coverage.capped &&
|
|
1356
|
-
row.tiles_reused === 0 &&
|
|
1357
1536
|
row.tiles_unjudged === 0 &&
|
|
1358
1537
|
record.scopes.length === 0;
|
|
1359
1538
|
const persisted: PersistedCritique | undefined =
|
|
1360
1539
|
persistCritique && row
|
|
1361
1540
|
? {
|
|
1362
|
-
contract_version:
|
|
1363
|
-
rubric_digest:
|
|
1541
|
+
contract_version: 2,
|
|
1542
|
+
rubric_digest: "",
|
|
1364
1543
|
outcome: row.outcome as "pass" | "fail",
|
|
1365
1544
|
findings: row.findings.map(({ tile_id: _tileId, ...finding }) => finding),
|
|
1366
1545
|
tiles: record.tiles.map((tile) => ({
|
|
@@ -1374,13 +1553,22 @@ export async function runQaMatrix(options: QaRunMatrixOptions): Promise<QaRunRes
|
|
|
1374
1553
|
}
|
|
1375
1554
|
: undefined;
|
|
1376
1555
|
try {
|
|
1556
|
+
const tileEvidence = buildPackNativeEvidence(
|
|
1557
|
+
record,
|
|
1558
|
+
readPackTiles(packDir, record),
|
|
1559
|
+
DEFAULT_CRITIQUE_RUBRIC,
|
|
1560
|
+
);
|
|
1561
|
+
if (persisted) {
|
|
1562
|
+
persisted.contract_version = tileEvidence.context.critique_contract_version;
|
|
1563
|
+
persisted.rubric_digest = tileEvidence.context.rubric_digest;
|
|
1564
|
+
}
|
|
1377
1565
|
const saved = saveQaSnapshot(
|
|
1378
1566
|
job.target,
|
|
1379
1567
|
{ viewport: record.viewport, theme: record.theme, state: record.state },
|
|
1380
1568
|
{
|
|
1381
1569
|
signature: readPackSignature(packDir, record),
|
|
1382
1570
|
domHtml: readPackDom(packDir, record),
|
|
1383
|
-
|
|
1571
|
+
tileEvidence,
|
|
1384
1572
|
...(persisted ? { critique: persisted } : {}),
|
|
1385
1573
|
},
|
|
1386
1574
|
options.snapshotStore ?? {},
|