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
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { CritiqueFinding, CritiqueTile } from "./critique.js";
|
|
2
|
+
import { type PageReviewDecisionSlot, type PageReviewEvidenceContext, type PageReviewNativeEvidence, type PageReviewNativeTile, type PageReviewRect } from "./page-review-contracts.js";
|
|
3
|
+
import { type PageReviewContextRecord, type PageReviewCritiqueRecord } from "./page-review-pack.js";
|
|
4
|
+
import { type CritiqueReusePlan, type PersistedCritique } from "./qa-reuse.js";
|
|
5
|
+
import { type QaSnapshotStoreOptions } from "./qa-snapshot.js";
|
|
6
|
+
export declare const PAGE_REVIEW_CRITIQUE_CONTRACT_VERSION = 2;
|
|
7
|
+
export declare const PAGE_REVIEW_CRITIQUE_SCHEMA = "harnery-page-review-critique/v2";
|
|
8
|
+
export declare const packRubricDigest: (rubric: string) => string;
|
|
9
|
+
export declare function machineEvidenceDigest(target: string, critique: PageReviewCritiqueRecord[] | null): string;
|
|
10
|
+
export declare function nativePixelDigest(png: Buffer): string;
|
|
11
|
+
export declare function evidenceContextKey(context: PageReviewEvidenceContext): string;
|
|
12
|
+
export declare function nativeRectKey(rect: PageReviewRect): string;
|
|
13
|
+
export declare function validateNativeEvidence(evidence: PageReviewNativeEvidence): boolean;
|
|
14
|
+
export declare function buildPackNativeEvidence(record: PageReviewContextRecord, tiles: Array<CritiqueTile & {
|
|
15
|
+
id: string;
|
|
16
|
+
}>, rubric: string): PageReviewNativeEvidence;
|
|
17
|
+
/** A baseline finding forces review even when it was previously dismissed. */
|
|
18
|
+
export declare function planNativeTileReuse(input: {
|
|
19
|
+
current: PageReviewNativeEvidence;
|
|
20
|
+
baseline?: PageReviewNativeEvidence;
|
|
21
|
+
critique?: PersistedCritique;
|
|
22
|
+
tiles: Array<CritiqueTile & {
|
|
23
|
+
id: string;
|
|
24
|
+
}>;
|
|
25
|
+
mismatchThreshold?: number;
|
|
26
|
+
}): CritiqueReusePlan;
|
|
27
|
+
export declare function machineFindingKey(context: PageReviewEvidenceContext, tile: Pick<PageReviewNativeTile, "rect" | "pixel_digest">, finding: Pick<CritiqueFinding, "severity" | "category" | "description">): string;
|
|
28
|
+
export declare function applyPackReviewDecisions(input: {
|
|
29
|
+
target: string;
|
|
30
|
+
evidence: PageReviewNativeEvidence;
|
|
31
|
+
findings: Array<CritiqueFinding & {
|
|
32
|
+
tile_id: string;
|
|
33
|
+
}>;
|
|
34
|
+
slot: PageReviewDecisionSlot | null;
|
|
35
|
+
enabled: boolean;
|
|
36
|
+
}): {
|
|
37
|
+
findings: Array<CritiqueFinding & {
|
|
38
|
+
tile_id: string;
|
|
39
|
+
}>;
|
|
40
|
+
applied: Array<{
|
|
41
|
+
finding_key: string;
|
|
42
|
+
target: string;
|
|
43
|
+
disposition: "artifact" | "not-a-defect";
|
|
44
|
+
}>;
|
|
45
|
+
};
|
|
46
|
+
/** Validate the complete submission and all native bindings before writing any slot. */
|
|
47
|
+
export declare function persistPackReviewDecisions(packDir: string, store?: QaSnapshotStoreOptions): number;
|
|
48
|
+
//# sourceMappingURL=page-review-evidence.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"page-review-evidence.d.ts","sourceRoot":"","sources":["../../../src/lib/browser/page-review-evidence.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AACnE,OAAO,EAGL,KAAK,sBAAsB,EAC3B,KAAK,yBAAyB,EAC9B,KAAK,wBAAwB,EAC7B,KAAK,oBAAoB,EACzB,KAAK,cAAc,EACpB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAGL,KAAK,uBAAuB,EAC5B,KAAK,wBAAwB,EAO9B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,KAAK,iBAAiB,EAEtB,KAAK,iBAAiB,EACvB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,KAAK,sBAAsB,EAAuB,MAAM,kBAAkB,CAAC;AAEpF,eAAO,MAAM,qCAAqC,IAAI,CAAC;AACvD,eAAO,MAAM,2BAA2B,oCAAoC,CAAC;AAE7E,eAAO,MAAM,gBAAgB,GAAI,QAAQ,MAAM,KAAG,MAAsB,CAAC;AAEzE,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,wBAAwB,EAAE,GAAG,IAAI,GAC1C,MAAM,CAcR;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAErD;AASD,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,yBAAyB,GAAG,MAAM,CAY7E;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,cAAc,GAAG,MAAM,CAE1D;AAED,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,wBAAwB,GAAG,OAAO,CAwClF;AAED,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,uBAAuB,EAC/B,KAAK,EAAE,KAAK,CAAC,YAAY,GAAG;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC,EAC3C,MAAM,EAAE,MAAM,GACb,wBAAwB,CA6B1B;AAED,8EAA8E;AAC9E,wBAAgB,mBAAmB,CAAC,KAAK,EAAE;IACzC,OAAO,EAAE,wBAAwB,CAAC;IAClC,QAAQ,CAAC,EAAE,wBAAwB,CAAC;IACpC,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,KAAK,EAAE,KAAK,CAAC,YAAY,GAAG;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC5C,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B,GAAG,iBAAiB,CA+EpB;AAED,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,yBAAyB,EAClC,IAAI,EAAE,IAAI,CAAC,oBAAoB,EAAE,MAAM,GAAG,cAAc,CAAC,EACzD,OAAO,EAAE,IAAI,CAAC,eAAe,EAAE,UAAU,GAAG,UAAU,GAAG,aAAa,CAAC,GACtE,MAAM,CAYR;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE;IAC9C,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,wBAAwB,CAAC;IACnC,QAAQ,EAAE,KAAK,CAAC,eAAe,GAAG;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACvD,IAAI,EAAE,sBAAsB,GAAG,IAAI,CAAC;IACpC,OAAO,EAAE,OAAO,CAAC;CAClB,GAAG;IACF,QAAQ,EAAE,KAAK,CAAC,eAAe,GAAG;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACvD,OAAO,EAAE,KAAK,CAAC;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,UAAU,GAAG,cAAc,CAAA;KAAE,CAAC,CAAC;CACnG,CA+CA;AAED,wFAAwF;AACxF,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,MAAM,EACf,KAAK,GAAE,sBAA2B,GACjC,MAAM,CAoIR"}
|
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
// Native pixels are the evidence. Overviews and PNG encodings are not identity.
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import pixelmatch from "pixelmatch";
|
|
5
|
+
import { PNG } from "pngjs";
|
|
6
|
+
import { PAGE_REVIEW_DECISIONS_SCHEMA, PAGE_REVIEW_NATIVE_EVIDENCE_SCHEMA, } from "./page-review-contracts.js";
|
|
7
|
+
import { buildInspectionPlan, machineFindingTargets, packPaths, readPackFindings, readPackManifest, readPackTiles, resolvePackVerdict, validateFindingsDocument, } from "./page-review-pack.js";
|
|
8
|
+
import { DEFAULT_REUSE_MISMATCH_RATIO, } from "./qa-reuse.js";
|
|
9
|
+
import { saveReviewDecisions } from "./qa-snapshot.js";
|
|
10
|
+
export const PAGE_REVIEW_CRITIQUE_CONTRACT_VERSION = 2;
|
|
11
|
+
export const PAGE_REVIEW_CRITIQUE_SCHEMA = "harnery-page-review-critique/v2";
|
|
12
|
+
const hash = (value) => createHash("sha256").update(value).digest("hex");
|
|
13
|
+
export const packRubricDigest = (rubric) => hash(rubric);
|
|
14
|
+
export function machineEvidenceDigest(target, critique) {
|
|
15
|
+
return hash(JSON.stringify({
|
|
16
|
+
target,
|
|
17
|
+
contexts: critique?.map((row) => ({
|
|
18
|
+
context_id: row.context_id,
|
|
19
|
+
outcome: row.outcome,
|
|
20
|
+
findings: row.findings,
|
|
21
|
+
evidence: row.evidence ?? null,
|
|
22
|
+
coverage: row.coverage,
|
|
23
|
+
})) ?? null,
|
|
24
|
+
}));
|
|
25
|
+
}
|
|
26
|
+
export function nativePixelDigest(png) {
|
|
27
|
+
return decodedPixelDigest(PNG.sync.read(png));
|
|
28
|
+
}
|
|
29
|
+
function decodedPixelDigest(decoded) {
|
|
30
|
+
const dimensions = Buffer.alloc(8);
|
|
31
|
+
dimensions.writeUInt32BE(decoded.width, 0);
|
|
32
|
+
dimensions.writeUInt32BE(decoded.height, 4);
|
|
33
|
+
return createHash("sha256").update(dimensions).update(decoded.data).digest("hex");
|
|
34
|
+
}
|
|
35
|
+
export function evidenceContextKey(context) {
|
|
36
|
+
return JSON.stringify([
|
|
37
|
+
context.viewport,
|
|
38
|
+
context.viewport_width,
|
|
39
|
+
context.viewport_height,
|
|
40
|
+
context.theme,
|
|
41
|
+
context.state,
|
|
42
|
+
context.dpr,
|
|
43
|
+
context.recipe_version,
|
|
44
|
+
context.rubric_digest,
|
|
45
|
+
context.critique_contract_version,
|
|
46
|
+
]);
|
|
47
|
+
}
|
|
48
|
+
export function nativeRectKey(rect) {
|
|
49
|
+
return JSON.stringify([rect.x, rect.y, rect.width, rect.height]);
|
|
50
|
+
}
|
|
51
|
+
export function validateNativeEvidence(evidence) {
|
|
52
|
+
try {
|
|
53
|
+
const ctx = evidence.context;
|
|
54
|
+
if (evidence.schema !== PAGE_REVIEW_NATIVE_EVIDENCE_SCHEMA ||
|
|
55
|
+
ctx.critique_contract_version !== PAGE_REVIEW_CRITIQUE_CONTRACT_VERSION ||
|
|
56
|
+
![ctx.viewport_width, ctx.viewport_height, ctx.dpr].every((n) => Number.isFinite(n) && n > 0) ||
|
|
57
|
+
!ctx.recipe_version ||
|
|
58
|
+
!ctx.rubric_digest ||
|
|
59
|
+
!ctx.viewport ||
|
|
60
|
+
!ctx.state ||
|
|
61
|
+
!["light", "dark"].includes(ctx.theme) ||
|
|
62
|
+
!Array.isArray(evidence.tiles))
|
|
63
|
+
return false;
|
|
64
|
+
const ids = new Set();
|
|
65
|
+
return evidence.tiles.every((tile) => {
|
|
66
|
+
if (!tile.tile_id || ids.has(tile.tile_id))
|
|
67
|
+
return false;
|
|
68
|
+
ids.add(tile.tile_id);
|
|
69
|
+
const { x, y, width, height } = tile.rect;
|
|
70
|
+
if (![x, y, width, height].every(Number.isInteger) ||
|
|
71
|
+
x < 0 ||
|
|
72
|
+
y < 0 ||
|
|
73
|
+
width < 1 ||
|
|
74
|
+
height < 1)
|
|
75
|
+
return false;
|
|
76
|
+
const decoded = PNG.sync.read(tile.png);
|
|
77
|
+
return (decoded.width === width &&
|
|
78
|
+
decoded.height === height &&
|
|
79
|
+
decodedPixelDigest(decoded) === tile.pixel_digest);
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
export function buildPackNativeEvidence(record, tiles, rubric) {
|
|
87
|
+
const evidence = {
|
|
88
|
+
schema: PAGE_REVIEW_NATIVE_EVIDENCE_SCHEMA,
|
|
89
|
+
context: {
|
|
90
|
+
viewport: record.viewport,
|
|
91
|
+
viewport_width: record.viewport_size.width,
|
|
92
|
+
viewport_height: record.viewport_size.height,
|
|
93
|
+
theme: record.theme,
|
|
94
|
+
state: record.state,
|
|
95
|
+
dpr: record.dpr,
|
|
96
|
+
recipe_version: `${record.recipe_version};source=${record.capture_fidelity?.source ?? "full-page"}`,
|
|
97
|
+
rubric_digest: packRubricDigest(rubric),
|
|
98
|
+
critique_contract_version: PAGE_REVIEW_CRITIQUE_CONTRACT_VERSION,
|
|
99
|
+
},
|
|
100
|
+
tiles: tiles.map((tile) => {
|
|
101
|
+
const png = Buffer.from(tile.pngBase64, "base64");
|
|
102
|
+
return {
|
|
103
|
+
tile_id: tile.id,
|
|
104
|
+
index: tile.index,
|
|
105
|
+
label: tile.label,
|
|
106
|
+
rect: { x: tile.x ?? 0, y: tile.scrollY, width: tile.width, height: tile.height },
|
|
107
|
+
pixel_digest: nativePixelDigest(png),
|
|
108
|
+
png,
|
|
109
|
+
};
|
|
110
|
+
}),
|
|
111
|
+
};
|
|
112
|
+
if (!validateNativeEvidence(evidence))
|
|
113
|
+
throw new Error("Native tile evidence has invalid geometry or pixels; recapture this context.");
|
|
114
|
+
return evidence;
|
|
115
|
+
}
|
|
116
|
+
/** A baseline finding forces review even when it was previously dismissed. */
|
|
117
|
+
export function planNativeTileReuse(input) {
|
|
118
|
+
const threshold = input.mismatchThreshold ?? DEFAULT_REUSE_MISMATCH_RATIO;
|
|
119
|
+
const { current, baseline, critique } = input;
|
|
120
|
+
const validCritique = critique &&
|
|
121
|
+
["pass", "fail"].includes(critique.outcome) &&
|
|
122
|
+
Array.isArray(critique.findings) &&
|
|
123
|
+
Array.isArray(critique.tiles) &&
|
|
124
|
+
critique.findings.every((f) => Number.isInteger(f.tile) &&
|
|
125
|
+
typeof f.description === "string" &&
|
|
126
|
+
typeof f.category === "string" &&
|
|
127
|
+
["high", "medium", "low"].includes(f.severity));
|
|
128
|
+
const invalidation = !baseline || !critique
|
|
129
|
+
? "native-evidence-missing"
|
|
130
|
+
: !validCritique
|
|
131
|
+
? "native-critique-invalid"
|
|
132
|
+
: !validateNativeEvidence(baseline) || !validateNativeEvidence(current)
|
|
133
|
+
? "native-evidence-invalid"
|
|
134
|
+
: critique.contract_version !== PAGE_REVIEW_CRITIQUE_CONTRACT_VERSION ||
|
|
135
|
+
critique.rubric_digest !== current.context.rubric_digest ||
|
|
136
|
+
evidenceContextKey(current.context) !== evidenceContextKey(baseline.context)
|
|
137
|
+
? "native-evidence-contract-changed"
|
|
138
|
+
: undefined;
|
|
139
|
+
const decisions = input.tiles.map((tile) => {
|
|
140
|
+
const cur = current.tiles.find((t) => t.tile_id === tile.id);
|
|
141
|
+
const prior = cur && baseline?.tiles.find((t) => nativeRectKey(t.rect) === nativeRectKey(cur.rect));
|
|
142
|
+
let reason = invalidation;
|
|
143
|
+
let ratio;
|
|
144
|
+
if (!reason && (!cur || !prior))
|
|
145
|
+
reason = "native-tile-missing";
|
|
146
|
+
if (!reason && prior && critique) {
|
|
147
|
+
const affected = critique.findings.some((finding) => {
|
|
148
|
+
const found = baseline?.tiles.filter((t) => t.index === finding.tile);
|
|
149
|
+
if (!found?.length)
|
|
150
|
+
return true;
|
|
151
|
+
return found.some((t) => t.rect.x < prior.rect.x + prior.rect.width &&
|
|
152
|
+
t.rect.x + t.rect.width > prior.rect.x &&
|
|
153
|
+
t.rect.y < prior.rect.y + prior.rect.height &&
|
|
154
|
+
t.rect.y + t.rect.height > prior.rect.y);
|
|
155
|
+
});
|
|
156
|
+
if (affected)
|
|
157
|
+
reason = "baseline-finding";
|
|
158
|
+
}
|
|
159
|
+
if (!reason && cur && prior) {
|
|
160
|
+
const a = PNG.sync.read(prior.png);
|
|
161
|
+
const b = PNG.sync.read(cur.png);
|
|
162
|
+
ratio =
|
|
163
|
+
pixelmatch(a.data, b.data, undefined, a.width, a.height, {
|
|
164
|
+
threshold: 0.1,
|
|
165
|
+
includeAA: false,
|
|
166
|
+
}) /
|
|
167
|
+
(a.width * a.height);
|
|
168
|
+
if (ratio > threshold)
|
|
169
|
+
reason = "pixels-changed";
|
|
170
|
+
}
|
|
171
|
+
return {
|
|
172
|
+
index: tile.index,
|
|
173
|
+
label: tile.label,
|
|
174
|
+
reuse: !reason,
|
|
175
|
+
reason: reason ?? "unchanged-clean-native-tile",
|
|
176
|
+
...(ratio === undefined ? {} : { mismatch_ratio: ratio }),
|
|
177
|
+
};
|
|
178
|
+
});
|
|
179
|
+
const review = input.tiles.filter((_, i) => !decisions[i]?.reuse);
|
|
180
|
+
return {
|
|
181
|
+
mode: "band-diff",
|
|
182
|
+
review,
|
|
183
|
+
decisions,
|
|
184
|
+
tiles_total: input.tiles.length,
|
|
185
|
+
tiles_reviewed: review.length,
|
|
186
|
+
tiles_reused: input.tiles.length - review.length,
|
|
187
|
+
provider_calls_avoided: input.tiles.length - review.length,
|
|
188
|
+
mismatch_threshold: threshold,
|
|
189
|
+
...(invalidation ? { invalidation } : {}),
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
export function machineFindingKey(context, tile, finding) {
|
|
193
|
+
const normalize = (value) => value.replace(/\s+/g, " ").trim();
|
|
194
|
+
return hash(JSON.stringify([
|
|
195
|
+
evidenceContextKey(context),
|
|
196
|
+
nativeRectKey(tile.rect),
|
|
197
|
+
tile.pixel_digest,
|
|
198
|
+
normalize(finding.severity),
|
|
199
|
+
normalize(finding.category),
|
|
200
|
+
normalize(finding.description),
|
|
201
|
+
]));
|
|
202
|
+
}
|
|
203
|
+
export function applyPackReviewDecisions(input) {
|
|
204
|
+
const applied = [];
|
|
205
|
+
const { slot, evidence } = input;
|
|
206
|
+
if (!input.enabled ||
|
|
207
|
+
!slot?.complete ||
|
|
208
|
+
slot.target !== input.target ||
|
|
209
|
+
evidenceContextKey(slot.context) !== evidenceContextKey(evidence.context) ||
|
|
210
|
+
!validateNativeEvidence(evidence))
|
|
211
|
+
return { findings: [...input.findings], applied };
|
|
212
|
+
const ordinals = new Map();
|
|
213
|
+
const findings = input.findings.filter((finding) => {
|
|
214
|
+
const ordinal = ordinals.get(finding.tile_id) ?? 0;
|
|
215
|
+
ordinals.set(finding.tile_id, ordinal + 1);
|
|
216
|
+
if (finding.category === "provider-error")
|
|
217
|
+
return true;
|
|
218
|
+
const tile = evidence.tiles.find((t) => t.tile_id === finding.tile_id);
|
|
219
|
+
if (!tile)
|
|
220
|
+
return true;
|
|
221
|
+
const key = machineFindingKey(evidence.context, tile, finding);
|
|
222
|
+
const decision = slot.decisions.find((d) => d.finding_key === key &&
|
|
223
|
+
d.tile_pixel_digest === tile.pixel_digest &&
|
|
224
|
+
nativeRectKey(d.rect) === nativeRectKey(tile.rect) &&
|
|
225
|
+
machineFindingKey(slot.context, { rect: d.rect, pixel_digest: d.tile_pixel_digest }, d.finding) === key);
|
|
226
|
+
if (!decision ||
|
|
227
|
+
(decision.disposition !== "artifact" && decision.disposition !== "not-a-defect"))
|
|
228
|
+
return true;
|
|
229
|
+
applied.push({
|
|
230
|
+
finding_key: key,
|
|
231
|
+
target: `${finding.tile_id}#${ordinal}`,
|
|
232
|
+
disposition: decision.disposition,
|
|
233
|
+
});
|
|
234
|
+
return false;
|
|
235
|
+
});
|
|
236
|
+
return { findings, applied };
|
|
237
|
+
}
|
|
238
|
+
/** Validate the complete submission and all native bindings before writing any slot. */
|
|
239
|
+
export function persistPackReviewDecisions(packDir, store = {}) {
|
|
240
|
+
const manifest = readPackManifest(packDir);
|
|
241
|
+
const doc = readPackFindings(packDir);
|
|
242
|
+
const errors = validateFindingsDocument(doc);
|
|
243
|
+
if (errors.length)
|
|
244
|
+
throw new Error(`Invalid findings document: ${errors.join("; ")}`);
|
|
245
|
+
if (doc.machine_evidence_digest !== machineEvidenceDigest(manifest.target, manifest.critique)) {
|
|
246
|
+
throw new Error("Reviewer submission belongs to different machine evidence; review the current findings again");
|
|
247
|
+
}
|
|
248
|
+
if (doc.target !== manifest.target || !doc.reviewer?.trim() || !doc.reviewed_at)
|
|
249
|
+
throw new Error("Review target or reviewer identity is missing or mismatched");
|
|
250
|
+
// Recompute required coverage from machine evidence, rather than trusting an edited plan.
|
|
251
|
+
const plan = buildInspectionPlan(manifest.contexts, manifest.critique, manifest.gates);
|
|
252
|
+
const verdict = resolvePackVerdict(manifest, plan, doc);
|
|
253
|
+
if (!manifest.critique?.length ||
|
|
254
|
+
verdict.uncovered_primary_tiles.length ||
|
|
255
|
+
verdict.primary_tiles_total === 0 ||
|
|
256
|
+
doc.delegated_reviews.some((r) => r.status !== "complete" || r.completed_tiles.length !== r.assigned_tiles.length) ||
|
|
257
|
+
manifest.contexts.some((c) => c.capture_incomplete ||
|
|
258
|
+
c.coverage.capped ||
|
|
259
|
+
c.coverage.reviewed_height_px < c.coverage.page_height_px ||
|
|
260
|
+
c.allocation_coverage?.omitted_scopes.length ||
|
|
261
|
+
c.allocation_coverage?.uncovered_intervals.length) ||
|
|
262
|
+
manifest.critique.some((c) => c.outcome === "incomplete" ||
|
|
263
|
+
c.outcome === "skipped" ||
|
|
264
|
+
c.tiles_reviewed + c.tiles_reused !== c.tiles_total)) {
|
|
265
|
+
throw new Error("Durable decisions require complete delegated review and complete native capture/judging");
|
|
266
|
+
}
|
|
267
|
+
const machineFile = JSON.parse(readFileSync(packPaths(packDir).critique, "utf8"));
|
|
268
|
+
if (machineFile.schema !== PAGE_REVIEW_CRITIQUE_SCHEMA ||
|
|
269
|
+
JSON.stringify(machineFile.contexts) !== JSON.stringify(manifest.critique))
|
|
270
|
+
throw new Error("Machine critique binding mismatch; recapture and judge this pack");
|
|
271
|
+
const targets = machineFindingTargets(manifest.critique);
|
|
272
|
+
if (verdict.unmatched_dispositions.length)
|
|
273
|
+
throw new Error("Disposition names an absent machine finding");
|
|
274
|
+
const primary = new Set(plan.contexts.flatMap((c) => c.primary_tiles.map((t) => `${c.context_id}/${t.id}`)));
|
|
275
|
+
if (doc.delegated_reviews.some((r) => r.assigned_tiles.some((t) => !primary.has(t))))
|
|
276
|
+
throw new Error("Delegated review refers to an unknown primary tile");
|
|
277
|
+
for (const finding of doc.findings) {
|
|
278
|
+
const ctx = manifest.contexts.find((c) => c.id === finding.context_id);
|
|
279
|
+
if (!ctx ||
|
|
280
|
+
finding.evidence.some((ref) => !ctx.tiles.some((t) => ref === t.id || ref === t.file || ref === `${ctx.id}/${t.id}`)))
|
|
281
|
+
throw new Error("Reviewer finding has an invalid evidence binding");
|
|
282
|
+
}
|
|
283
|
+
const slots = manifest.contexts.map((record) => {
|
|
284
|
+
const row = manifest.critique?.find((c) => c.context_id === record.id);
|
|
285
|
+
if (!row?.evidence || row.evidence.schema !== PAGE_REVIEW_CRITIQUE_SCHEMA)
|
|
286
|
+
throw new Error("Missing v2 machine evidence; rejudge this pack");
|
|
287
|
+
const tiles = readPackTiles(packDir, record);
|
|
288
|
+
const evidence = buildPackNativeEvidence(record, tiles, "");
|
|
289
|
+
evidence.context.rubric_digest = row.evidence.context.rubric_digest;
|
|
290
|
+
if (evidenceContextKey(evidence.context) !== evidenceContextKey(row.evidence.context) ||
|
|
291
|
+
row.evidence.tiles.length !== evidence.tiles.length ||
|
|
292
|
+
row.evidence.tiles.some((tile) => !evidence.tiles.some((cur) => cur.tile_id === tile.tile_id &&
|
|
293
|
+
cur.pixel_digest === tile.pixel_digest &&
|
|
294
|
+
nativeRectKey(cur.rect) === nativeRectKey(tile.rect)))) {
|
|
295
|
+
throw new Error("Native tile binding changed after judging; recapture and rejudge");
|
|
296
|
+
}
|
|
297
|
+
if (row.findings.some((f) => !evidence.tiles.some((tile) => tile.tile_id === f.tile_id && tile.index === f.tile)))
|
|
298
|
+
throw new Error("Machine finding names an absent native tile");
|
|
299
|
+
const decisions = new Map();
|
|
300
|
+
for (const disposition of doc.dispositions ?? []) {
|
|
301
|
+
const finding = targets.get(disposition.target);
|
|
302
|
+
if (!finding || finding.context_id !== record.id)
|
|
303
|
+
continue;
|
|
304
|
+
if (finding.category === "provider-error")
|
|
305
|
+
throw new Error("Provider errors cannot receive durable dispositions");
|
|
306
|
+
const tile = evidence.tiles.find((t) => t.tile_id === finding.tile_id);
|
|
307
|
+
if (!tile)
|
|
308
|
+
throw new Error("Disposition tile binding is absent");
|
|
309
|
+
const key = machineFindingKey(evidence.context, tile, finding);
|
|
310
|
+
decisions.set(key, {
|
|
311
|
+
finding_key: key,
|
|
312
|
+
tile_pixel_digest: tile.pixel_digest,
|
|
313
|
+
rect: tile.rect,
|
|
314
|
+
finding: {
|
|
315
|
+
severity: finding.severity,
|
|
316
|
+
category: finding.category,
|
|
317
|
+
description: finding.description,
|
|
318
|
+
},
|
|
319
|
+
disposition: disposition.disposition,
|
|
320
|
+
reviewer: disposition.by?.trim() || doc.reviewer,
|
|
321
|
+
at: disposition.at ?? doc.reviewed_at,
|
|
322
|
+
...(disposition.note === undefined ? {} : { note: disposition.note }),
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
return {
|
|
326
|
+
schema: PAGE_REVIEW_DECISIONS_SCHEMA,
|
|
327
|
+
target: manifest.target,
|
|
328
|
+
context: evidence.context,
|
|
329
|
+
source_run: manifest.created_at,
|
|
330
|
+
source_revision: manifest.tested_revision ?? null,
|
|
331
|
+
reviewer: doc.reviewer,
|
|
332
|
+
reviewed_at: doc.reviewed_at,
|
|
333
|
+
complete: true,
|
|
334
|
+
decisions: [...decisions.values()],
|
|
335
|
+
};
|
|
336
|
+
});
|
|
337
|
+
for (const slot of slots)
|
|
338
|
+
saveReviewDecisions(manifest.target, slot.context, slot, store);
|
|
339
|
+
return slots.length;
|
|
340
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { CritiqueProvider } from "./critique.js";
|
|
2
2
|
import { type PageReviewContextRecord, type PageReviewCritiqueRecord } from "./page-review-pack.js";
|
|
3
|
-
import {
|
|
3
|
+
import type { CritiqueReusePlan } from "./qa-reuse.js";
|
|
4
4
|
import { type QaSnapshotStoreOptions } from "./qa-snapshot.js";
|
|
5
5
|
export interface JudgePageReviewPackOptions {
|
|
6
6
|
packDir: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"page-review-judge.d.ts","sourceRoot":"","sources":["../../../src/lib/browser/page-review-judge.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"page-review-judge.d.ts","sourceRoot":"","sources":["../../../src/lib/browser/page-review-judge.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAmB,gBAAgB,EAAgB,MAAM,eAAe,CAAC;AAOrF,OAAO,EAEL,KAAK,uBAAuB,EAC5B,KAAK,wBAAwB,EAG9B,MAAM,uBAAuB,CAAC;AAE/B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAkB,KAAK,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAE/E,MAAM,WAAW,0BAA0B;IACzC,OAAO,EAAE,MAAM,CAAC;IAChB,0EAA0E;IAC1E,QAAQ,EAAE,gBAAgB,GAAG,SAAS,CAAC;IACvC,MAAM,EAAE,MAAM,CAAC;IACf;kCAC8B;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,2EAA2E;IAC3E,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,qEAAqE;IACrE,KAAK,CAAC,EAAE;QACN,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,CAAC,EAAE,sBAAsB,CAAC;QAC/B,iBAAiB,CAAC,EAAE,MAAM,CAAC;KAC5B,CAAC;IACF;2EACuE;IACvE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACnC;AAED,MAAM,WAAW,aAAc,SAAQ,wBAAwB;IAC7D,MAAM,EAAE,uBAAuB,CAAC;IAChC,wEAAwE;IACxE,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,IAAI,CACV,iBAAiB,EACjB,aAAa,GAAG,cAAc,GAAG,gBAAgB,GAAG,cAAc,GAAG,oBAAoB,CAC1F,CAAC;CACH;AAED,MAAM,WAAW,yBAAyB;IACxC,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,IAAI,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IACtE,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,GAAG,YAAY,CAAC;IACpD,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACzC;AAUD;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,uBAAuB,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CA+E1F;AAaD;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,0BAA0B,GAClC,OAAO,CAAC,yBAAyB,CAAC,CAoLpC;AAED,+EAA+E;AAC/E,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,yBAAyB,GAAG,wBAAwB,EAAE,CAI/F"}
|
|
@@ -8,9 +8,8 @@
|
|
|
8
8
|
// finding (false reviews are allowed, false passes never).
|
|
9
9
|
//
|
|
10
10
|
// Toolkit tier: this module must not import src/core (layering check).
|
|
11
|
-
import {
|
|
12
|
-
import { listPackContexts, readPackContext,
|
|
13
|
-
import { planCritiqueReuse } from "./qa-reuse.js";
|
|
11
|
+
import { buildPackNativeEvidence, PAGE_REVIEW_CRITIQUE_SCHEMA, planNativeTileReuse, } from "./page-review-evidence.js";
|
|
12
|
+
import { listPackContexts, readPackContext, readPackTiles, } from "./page-review-pack.js";
|
|
14
13
|
import { loadQaSnapshot } from "./qa-snapshot.js";
|
|
15
14
|
/**
|
|
16
15
|
* Orientation the provider reads before the rubric: which band this tile is,
|
|
@@ -39,7 +38,7 @@ export function tileRubricPreamble(record, tileId) {
|
|
|
39
38
|
.sort((a, b) => a.scrollY - b.scrollY || a.index - b.index);
|
|
40
39
|
const n = bands.findIndex((entry) => entry.id === tile.id);
|
|
41
40
|
const capped = record.coverage.capped
|
|
42
|
-
? ` (the page has ${record.coverage.bands_total} bands;
|
|
41
|
+
? ` (the page has ${record.coverage.bands_total} bands; unselected bands are not in this pack)`
|
|
43
42
|
: "";
|
|
44
43
|
lines.push(`Tile ${tile.id}: band ${n + 1} of ${bands.length}${capped}, ` +
|
|
45
44
|
`document y ${top}-${bottom} px of a ${pageHeight} px tall page.`);
|
|
@@ -50,13 +49,17 @@ export function tileRubricPreamble(record, tileId) {
|
|
|
50
49
|
const above = Math.max(0, prev.scrollY + prev.height - top);
|
|
51
50
|
overlaps.push(above > 0
|
|
52
51
|
? `its top ${above} px repeat the bottom of ${prev.id}`
|
|
53
|
-
:
|
|
52
|
+
: prev.scrollY + prev.height === top
|
|
53
|
+
? `it starts where ${prev.id} ends, with no overlap`
|
|
54
|
+
: `an unreviewed gap of ${top - prev.scrollY - prev.height} px separates it from ${prev.id}`);
|
|
54
55
|
}
|
|
55
56
|
if (next) {
|
|
56
57
|
const below = Math.max(0, bottom - next.scrollY);
|
|
57
58
|
overlaps.push(below > 0
|
|
58
59
|
? `its bottom ${below} px repeat the top of ${next.id}`
|
|
59
|
-
:
|
|
60
|
+
: bottom === next.scrollY
|
|
61
|
+
? `it ends where ${next.id} starts, with no overlap`
|
|
62
|
+
: `an unreviewed gap of ${next.scrollY - bottom} px separates it from ${next.id}`);
|
|
60
63
|
}
|
|
61
64
|
if (overlaps.length > 0) {
|
|
62
65
|
const joined = overlaps.join("; ");
|
|
@@ -69,13 +72,13 @@ export function tileRubricPreamble(record, tileId) {
|
|
|
69
72
|
lines.push("This tile covers the whole page height.");
|
|
70
73
|
}
|
|
71
74
|
else if (touchesTop) {
|
|
72
|
-
lines.push("This tile touches the page top;
|
|
75
|
+
lines.push("This tile touches the page top; the capture continues below its bottom edge, which alone does not prove clipping.");
|
|
73
76
|
}
|
|
74
77
|
else if (touchesBottom) {
|
|
75
|
-
lines.push("This tile touches the page bottom;
|
|
78
|
+
lines.push("This tile touches the page bottom; the capture continues above its top edge, which alone does not prove clipping.");
|
|
76
79
|
}
|
|
77
80
|
else {
|
|
78
|
-
lines.push("This tile is interior:
|
|
81
|
+
lines.push("This tile is interior: the page continues beyond both edges. Use document positions to distinguish overlapping bands from unreviewed gaps.");
|
|
79
82
|
}
|
|
80
83
|
const source = record.capture_fidelity?.source ?? "full-page";
|
|
81
84
|
lines.push(source === "scrolled-bands"
|
|
@@ -114,41 +117,41 @@ export async function judgePageReviewPack(options) {
|
|
|
114
117
|
const tiles = readPackTiles(packDir, record);
|
|
115
118
|
let review = tiles;
|
|
116
119
|
let reuse;
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
}
|
|
125
|
-
if (options.reuse && provider && record.scopes.length === 0 && !scrolled) {
|
|
120
|
+
const evidence = buildPackNativeEvidence(record, tiles, rubric);
|
|
121
|
+
record.evidence_context = evidence.context;
|
|
122
|
+
if (options.reuse &&
|
|
123
|
+
provider &&
|
|
124
|
+
record.scopes.length === 0 &&
|
|
125
|
+
!record.capture_incomplete &&
|
|
126
|
+
!record.coverage.capped) {
|
|
126
127
|
const context = {
|
|
127
128
|
viewport: record.viewport,
|
|
128
129
|
theme: record.theme,
|
|
129
130
|
state: record.state,
|
|
130
131
|
};
|
|
131
132
|
const stored = loadQaSnapshot(options.reuse.target, context, options.reuse.store ?? {});
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
// planCritiqueReuse returns CritiqueTile objects; re-attach ids by
|
|
145
|
-
// tiler index so the pool cites the pack's stable tile ids.
|
|
146
|
-
review = tiles.filter((tile) => keep.has(tile.index));
|
|
147
|
-
log(`judge ${record.id}: ${reuse.tiles_reused}/${reuse.tiles_total} tiles reused (band-diff)` +
|
|
148
|
-
(reuse.invalidation ? ` — ${reuse.invalidation}` : ""));
|
|
149
|
-
}
|
|
133
|
+
reuse = planNativeTileReuse({
|
|
134
|
+
current: evidence,
|
|
135
|
+
baseline: stored?.tileEvidence,
|
|
136
|
+
critique: stored?.critique,
|
|
137
|
+
tiles,
|
|
138
|
+
...(options.reuse.mismatchThreshold !== undefined
|
|
139
|
+
? { mismatchThreshold: options.reuse.mismatchThreshold }
|
|
140
|
+
: {}),
|
|
141
|
+
});
|
|
142
|
+
review = tiles.filter((_, i) => !reuse?.decisions[i]?.reuse);
|
|
143
|
+
log(`judge ${record.id}: ${reuse.tiles_reused}/${reuse.tiles_total} tiles reused (band-diff)` +
|
|
144
|
+
(reuse.invalidation ? ` — ${reuse.invalidation}` : ""));
|
|
150
145
|
}
|
|
151
|
-
return {
|
|
146
|
+
return {
|
|
147
|
+
record,
|
|
148
|
+
tiles,
|
|
149
|
+
review,
|
|
150
|
+
evidence,
|
|
151
|
+
...(reuse ? { reuse } : {}),
|
|
152
|
+
findings: [],
|
|
153
|
+
judged: new Set(),
|
|
154
|
+
};
|
|
152
155
|
});
|
|
153
156
|
const work = [];
|
|
154
157
|
perContext.forEach((entry, contextIndex) => {
|
|
@@ -212,7 +215,7 @@ export async function judgePageReviewPack(options) {
|
|
|
212
215
|
? "skipped"
|
|
213
216
|
: findings.some((f) => f.severity === "high")
|
|
214
217
|
? "fail"
|
|
215
|
-
: unjudged > 0
|
|
218
|
+
: unjudged > 0 || entry.record.capture_incomplete
|
|
216
219
|
? "incomplete"
|
|
217
220
|
: "pass";
|
|
218
221
|
return {
|
|
@@ -231,6 +234,11 @@ export async function judgePageReviewPack(options) {
|
|
|
231
234
|
error: "no critiqueProvider injected by the host (see HarneryProgramContext.critiqueProvider)",
|
|
232
235
|
}),
|
|
233
236
|
record: entry.record,
|
|
237
|
+
evidence: {
|
|
238
|
+
schema: PAGE_REVIEW_CRITIQUE_SCHEMA,
|
|
239
|
+
context: entry.evidence.context,
|
|
240
|
+
tiles: entry.evidence.tiles.map(({ png: _png, ...tile }) => tile),
|
|
241
|
+
},
|
|
234
242
|
...(entry.reuse
|
|
235
243
|
? {
|
|
236
244
|
reuse: {
|