harnery 0.29.0 → 0.31.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/README.md +4 -4
- package/dist/commander.d.ts +8 -0
- package/dist/commander.d.ts.map +1 -1
- package/dist/commander.js +14 -13
- package/dist/commands/adapter.d.ts +12 -0
- package/dist/commands/adapter.d.ts.map +1 -0
- package/dist/commands/{harness.js → adapter.js} +35 -35
- package/dist/commands/agents.d.ts +3 -2
- package/dist/commands/agents.d.ts.map +1 -1
- package/dist/commands/agents.js +61 -60
- package/dist/commands/artifacts.d.ts +4 -0
- package/dist/commands/artifacts.d.ts.map +1 -0
- package/dist/commands/artifacts.js +143 -0
- package/dist/commands/backup.d.ts +1 -1
- package/dist/commands/backup.d.ts.map +1 -1
- package/dist/commands/backup.js +5 -2
- package/dist/commands/browse.d.ts.map +1 -1
- package/dist/commands/browse.js +310 -7
- package/dist/commands/checkpoint.d.ts +12 -0
- package/dist/commands/checkpoint.d.ts.map +1 -0
- package/dist/commands/checkpoint.js +162 -0
- package/dist/commands/context.d.ts +2 -2
- package/dist/commands/context.d.ts.map +1 -1
- package/dist/commands/context.js +3 -151
- package/dist/commands/decision.d.ts.map +1 -1
- package/dist/commands/decision.js +31 -7
- package/dist/commands/deinit.d.ts +2 -2
- package/dist/commands/deinit.js +15 -15
- package/dist/commands/doctor.js +23 -23
- package/dist/commands/governor.d.ts +4 -0
- package/dist/commands/governor.d.ts.map +1 -0
- package/dist/commands/{supervisor.js → governor.js} +133 -108
- package/dist/commands/init.d.ts +12 -12
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +44 -30
- package/dist/commands/{session.d.ts → journal.d.ts} +2 -2
- package/dist/commands/journal.d.ts.map +1 -0
- package/dist/commands/{scratch.js → journal.js} +47 -47
- package/dist/commands/policy.js +1 -1
- package/dist/commands/presence.js +2 -2
- package/dist/commands/sync.d.ts +1 -1
- package/dist/commands/sync.js +3 -3
- package/dist/commands/tunnel.d.ts +16 -0
- package/dist/commands/tunnel.d.ts.map +1 -1
- package/dist/commands/tunnel.js +193 -23
- package/dist/commands/web-fetch.js +1 -1
- package/dist/commands/web.d.ts +19 -0
- package/dist/commands/web.d.ts.map +1 -1
- package/dist/commands/web.js +51 -0
- package/dist/commands/work.d.ts +2 -1
- package/dist/commands/work.d.ts.map +1 -1
- package/dist/commands/work.js +55 -25
- package/dist/commands/workflow.d.ts.map +1 -1
- package/dist/commands/workflow.js +35 -28
- package/dist/core/{harnesses → adapters}/attest-projection.d.ts +2 -2
- package/dist/core/adapters/attest-projection.d.ts.map +1 -0
- package/dist/core/{harnesses → adapters}/attest.d.ts +16 -16
- package/dist/core/adapters/attest.d.ts.map +1 -0
- package/dist/core/{harnesses → adapters}/attest.js +9 -9
- package/dist/core/{harnesses → adapters}/attestation.d.ts +16 -16
- package/dist/core/adapters/attestation.d.ts.map +1 -0
- package/dist/core/{harnesses → adapters}/attestation.js +20 -20
- package/dist/core/{harnesses → adapters}/bench.d.ts +12 -12
- package/dist/core/adapters/bench.d.ts.map +1 -0
- package/dist/core/{harnesses → adapters}/bench.js +12 -12
- package/dist/core/adapters/index.d.ts +13 -0
- package/dist/core/adapters/index.d.ts.map +1 -0
- package/dist/core/adapters/index.js +6 -0
- package/dist/core/{harnesses → adapters}/profiles.d.ts +11 -11
- package/dist/core/adapters/profiles.d.ts.map +1 -0
- package/dist/core/{harnesses → adapters}/profiles.js +11 -11
- package/dist/core/adapters/registry.d.ts +15 -0
- package/dist/core/adapters/registry.d.ts.map +1 -0
- package/dist/core/{harnesses → adapters}/registry.js +11 -11
- package/dist/core/{harnesses → adapters}/types.d.ts +28 -23
- package/dist/core/adapters/types.d.ts.map +1 -0
- package/dist/core/{harnesses → adapters}/types.js +1 -1
- package/dist/core/agents/canonical-emit.d.ts +4 -4
- package/dist/core/agents/canonical-emit.js +5 -5
- package/dist/core/agents/cli-emit.js +1 -1
- package/dist/core/agents/cli.js +30 -26
- package/dist/core/agents/codex-replay.d.ts +1 -1
- package/dist/core/agents/codex-replay.js +6 -6
- package/dist/core/agents/coord-client.d.ts +21 -2
- package/dist/core/agents/coord-client.d.ts.map +1 -1
- package/dist/core/agents/coord-client.js +122 -27
- package/dist/core/agents/events/consume.d.ts +1 -1
- package/dist/core/agents/events/emit.d.ts +5 -6
- package/dist/core/agents/events/emit.d.ts.map +1 -1
- package/dist/core/agents/events/emit.js +2 -2
- package/dist/core/agents/render/prompt-context.d.ts +21 -9
- package/dist/core/agents/render/prompt-context.d.ts.map +1 -1
- package/dist/core/agents/render/prompt-context.js +82 -30
- package/dist/core/agents/render/session-context.d.ts +1 -1
- package/dist/core/agents/render/session-context.js +5 -5
- package/dist/core/agents/rules/claim-conflict.d.ts +2 -6
- package/dist/core/agents/rules/claim-conflict.d.ts.map +1 -1
- package/dist/core/agents/rules/claim-conflict.js +1 -1
- package/dist/core/agents/rules/commit-conflict.js +98 -22
- package/dist/core/agents/rules/stop-hook.d.ts +9 -9
- package/dist/core/agents/rules/stop-hook.d.ts.map +1 -1
- package/dist/core/agents/rules/stop-hook.js +23 -5
- package/dist/core/agents/rules/verdict.d.ts +14 -0
- package/dist/core/agents/rules/verdict.d.ts.map +1 -0
- package/dist/core/agents/rules/verdict.js +1 -0
- package/dist/core/agents/session-events.js +3 -3
- package/dist/core/agents/state/council.d.ts +1 -1
- package/dist/core/agents/state/council.js +1 -1
- package/dist/core/agents/state/heartbeat-projector.d.ts +3 -2
- package/dist/core/agents/state/heartbeat-projector.d.ts.map +1 -1
- package/dist/core/agents/state/heartbeat-projector.js +14 -10
- package/dist/core/agents/state/heartbeat-writer.d.ts +1 -1
- package/dist/core/agents/state/heartbeat-writer.js +12 -12
- package/dist/core/agents/state/{scratch.d.ts → journal.d.ts} +7 -7
- package/dist/core/agents/state/{scratch.d.ts.map → journal.d.ts.map} +1 -1
- package/dist/core/agents/state/{scratch.js → journal.js} +12 -12
- package/dist/core/agents/state/pidmap.d.ts +28 -4
- package/dist/core/agents/state/pidmap.d.ts.map +1 -1
- package/dist/core/agents/state/pidmap.js +130 -15
- package/dist/core/agents/state/proc-start.d.ts +95 -0
- package/dist/core/agents/state/proc-start.d.ts.map +1 -0
- package/dist/core/agents/state/proc-start.js +199 -0
- package/dist/core/agents/state/shell-mutation.d.ts +1 -1
- package/dist/core/agents/state/shell-mutation.js +1 -1
- package/dist/core/agents/state/stale-sweep.js +7 -7
- package/dist/core/artifacts/index.d.ts +86 -0
- package/dist/core/artifacts/index.d.ts.map +1 -0
- package/dist/core/artifacts/index.js +421 -0
- package/dist/core/config.d.ts +9 -2
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +26 -2
- package/dist/core/context/index.d.ts +12 -12
- package/dist/core/context/index.js +10 -10
- package/dist/core/governor/adapter-spread.d.ts +52 -0
- package/dist/core/governor/adapter-spread.d.ts.map +1 -0
- package/dist/core/governor/adapter-spread.js +52 -0
- package/dist/core/governor/index.d.ts +7 -0
- package/dist/core/governor/index.d.ts.map +1 -0
- package/dist/core/governor/index.js +6 -0
- package/dist/core/governor/plan-api.d.ts +38 -0
- package/dist/core/governor/plan-api.d.ts.map +1 -0
- package/dist/core/governor/plan-api.js +83 -0
- package/dist/core/governor/plan-read.d.ts +5 -0
- package/dist/core/governor/plan-read.d.ts.map +1 -0
- package/dist/core/{supervisor → governor}/plan-read.js +71 -51
- package/dist/core/governor/plan-types.d.ts +181 -0
- package/dist/core/governor/plan-types.d.ts.map +1 -0
- package/dist/core/{supervisor → governor}/plan-types.js +4 -4
- package/dist/core/governor/planning.d.ts +48 -0
- package/dist/core/governor/planning.d.ts.map +1 -0
- package/dist/core/{supervisor → governor}/planning.js +143 -94
- package/dist/core/governor/read.d.ts +6 -0
- package/dist/core/governor/read.d.ts.map +1 -0
- package/dist/core/governor/read.js +4 -0
- package/dist/core/governor/runner.d.ts +37 -0
- package/dist/core/governor/runner.d.ts.map +1 -0
- package/dist/core/{supervisor → governor}/runner.js +15 -15
- package/dist/core/governor/service-read.d.ts +6 -0
- package/dist/core/governor/service-read.d.ts.map +1 -0
- package/dist/core/{supervisor → governor}/service-read.js +12 -12
- package/dist/core/governor/service.d.ts +114 -0
- package/dist/core/governor/service.d.ts.map +1 -0
- package/dist/core/{supervisor → governor}/service.js +57 -57
- package/dist/core/governor/state.d.ts +116 -0
- package/dist/core/governor/state.d.ts.map +1 -0
- package/dist/core/{supervisor → governor}/state.js +290 -116
- package/dist/core/hooks/adapter/detect.d.ts +9 -0
- package/dist/core/hooks/{harness → adapter}/detect.d.ts.map +1 -1
- package/dist/core/hooks/{harness → adapter}/detect.js +8 -8
- package/dist/core/hooks/{harness → adapter}/events.d.ts +15 -14
- package/dist/core/hooks/adapter/events.d.ts.map +1 -0
- package/dist/core/hooks/{harness → adapter}/events.js +5 -5
- package/dist/core/hooks/{harness → adapter}/output.d.ts +20 -14
- package/dist/core/hooks/adapter/output.d.ts.map +1 -0
- package/dist/core/hooks/{harness → adapter}/output.js +30 -22
- package/dist/core/hooks/{harness → adapter}/parse.d.ts +5 -5
- package/dist/core/hooks/{harness → adapter}/parse.d.ts.map +1 -1
- package/dist/core/hooks/{harness → adapter}/parse.js +4 -4
- package/dist/core/hooks/{harness → adapter}/wiring.d.ts +15 -15
- package/dist/core/hooks/{harness → adapter}/wiring.d.ts.map +1 -1
- package/dist/core/hooks/{harness → adapter}/wiring.js +13 -13
- package/dist/core/hooks/cli.d.ts +3 -3
- package/dist/core/hooks/cli.js +175 -151
- package/dist/core/hooks/effects/image-capture.d.ts +5 -5
- package/dist/core/hooks/effects/image-capture.d.ts.map +1 -1
- package/dist/core/hooks/effects/image-capture.js +3 -3
- package/dist/core/hooks/effects/index.d.ts +12 -12
- package/dist/core/hooks/effects/index.d.ts.map +1 -1
- package/dist/core/hooks/effects/index.js +17 -17
- package/dist/core/hooks/events/emit.d.ts +2 -2
- package/dist/core/hooks/events/emit.d.ts.map +1 -1
- package/dist/core/hooks/events/emit.js +1 -1
- package/dist/core/hooks/events/rotate.js +1 -1
- package/dist/core/hooks/events/schema.d.ts +13 -5
- package/dist/core/hooks/events/schema.d.ts.map +1 -1
- package/dist/core/hooks/guard-path.d.ts +2 -2
- package/dist/core/hooks/guard-path.js +2 -2
- package/dist/core/hooks/index.d.ts +1 -1
- package/dist/core/hooks/index.d.ts.map +1 -1
- package/dist/core/hooks/resolve/anchor.d.ts +14 -5
- package/dist/core/hooks/resolve/anchor.d.ts.map +1 -1
- package/dist/core/hooks/resolve/anchor.js +64 -7
- package/dist/core/hooks/resolve/coord-root.d.ts +3 -3
- package/dist/core/hooks/resolve/coord-root.js +4 -4
- package/dist/core/hooks/resolve/intent.js +1 -1
- package/dist/core/hooks/resolve/owner.d.ts +1 -1
- package/dist/core/hooks/resolve/owner.d.ts.map +1 -1
- package/dist/core/hooks/resolve/owner.js +11 -5
- package/dist/core/{scratch → journal}/index.d.ts +25 -25
- package/dist/core/{scratch → journal}/index.d.ts.map +1 -1
- package/dist/core/{scratch → journal}/index.js +38 -38
- package/dist/core/policy/evaluate.d.ts +1 -1
- package/dist/core/policy/evaluate.d.ts.map +1 -1
- package/dist/core/policy/evaluate.js +7 -7
- package/dist/core/policy/types.d.ts +3 -3
- package/dist/core/policy/types.d.ts.map +1 -1
- package/dist/core/work/runner.d.ts.map +1 -1
- package/dist/core/work/runner.js +11 -2
- package/dist/core/work/state.d.ts +28 -3
- package/dist/core/work/state.d.ts.map +1 -1
- package/dist/core/work/state.js +148 -16
- package/dist/core/workflow/adapters.d.ts +17 -0
- package/dist/core/workflow/adapters.d.ts.map +1 -0
- package/dist/core/workflow/adapters.js +21 -0
- package/dist/core/workflow/approvals.js +6 -6
- package/dist/core/workflow/billing.d.ts +9 -9
- package/dist/core/workflow/billing.js +9 -9
- package/dist/core/workflow/child-env.d.ts +10 -2
- package/dist/core/workflow/child-env.d.ts.map +1 -1
- package/dist/core/workflow/child-env.js +12 -4
- package/dist/core/workflow/engine.d.ts +15 -5
- package/dist/core/workflow/engine.d.ts.map +1 -1
- package/dist/core/workflow/engine.js +151 -83
- package/dist/core/workflow/evidence-preflight.d.ts +24 -0
- package/dist/core/workflow/evidence-preflight.d.ts.map +1 -0
- package/dist/core/workflow/evidence-preflight.js +134 -0
- package/dist/core/workflow/index.d.ts +3 -2
- package/dist/core/workflow/index.d.ts.map +1 -1
- package/dist/core/workflow/index.js +2 -1
- package/dist/core/workflow/proof.d.ts +28 -5
- package/dist/core/workflow/proof.d.ts.map +1 -1
- package/dist/core/workflow/proof.js +77 -34
- package/dist/core/workflow/run-state.d.ts +1 -1
- package/dist/core/workflow/run-state.js +7 -7
- package/dist/core/workflow/sandbox-projection.d.ts +6 -6
- package/dist/core/workflow/sandbox-projection.d.ts.map +1 -1
- package/dist/core/workflow/sandbox-projection.js +11 -11
- package/dist/core/workflow/spawn-claude.d.ts +3 -3
- package/dist/core/workflow/spawn-claude.d.ts.map +1 -1
- package/dist/core/workflow/spawn-claude.js +28 -8
- package/dist/core/workflow/spawn-codex.d.ts +4 -4
- package/dist/core/workflow/spawn-codex.d.ts.map +1 -1
- package/dist/core/workflow/spawn-codex.js +26 -8
- package/dist/core/workflow/spawn-cursor.d.ts +4 -4
- package/dist/core/workflow/spawn-cursor.d.ts.map +1 -1
- package/dist/core/workflow/spawn-cursor.js +27 -8
- package/dist/core/workflow/spawn-failure.d.ts +15 -1
- package/dist/core/workflow/spawn-failure.d.ts.map +1 -1
- package/dist/core/workflow/spawn-failure.js +35 -1
- package/dist/core/workflow/specialists.js +2 -2
- package/dist/core/workflow/{journal.d.ts → transcript.d.ts} +8 -8
- package/dist/core/workflow/transcript.d.ts.map +1 -0
- package/dist/core/workflow/{journal.js → transcript.js} +14 -14
- package/dist/core/workflow/types.d.ts +119 -46
- package/dist/core/workflow/types.d.ts.map +1 -1
- package/dist/core/workflow/types.js +11 -1
- package/dist/core/workflow/validate.d.ts +1 -1
- package/dist/core/workflow/validate.js +1 -1
- package/dist/core/workflow/workspaces/cancellation.js +2 -2
- package/dist/core/workflow/workspaces/cleanup.js +2 -2
- package/dist/core/workflow/workspaces/execution.js +6 -6
- package/dist/core/workflow/workspaces/inspect.js +9 -9
- package/dist/core/workflow/workspaces/integration.js +14 -14
- package/dist/core/workflow/workspaces/lifecycle.d.ts +1 -1
- package/dist/core/workflow/workspaces/lifecycle.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/lifecycle.js +9 -9
- package/dist/core/workflow/workspaces/local-git.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/local-git.js +39 -5
- package/dist/core/workflow/workspaces/recovery.js +3 -3
- package/dist/core/workflow/workspaces/state.d.ts +1 -1
- package/dist/core/workflow/workspaces/state.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/state.js +3 -3
- package/dist/core/workflow/workspaces/types.d.ts +1 -1
- package/dist/core/workflow/workspaces/types.d.ts.map +1 -1
- package/dist/lib/browser/asserts.d.ts +28 -0
- package/dist/lib/browser/asserts.d.ts.map +1 -0
- package/dist/lib/browser/asserts.js +152 -0
- package/dist/lib/browser/client.d.ts +49 -0
- package/dist/lib/browser/client.d.ts.map +1 -1
- package/dist/lib/browser/client.js +155 -14
- package/dist/lib/browser/content-checks.d.ts +108 -0
- package/dist/lib/browser/content-checks.d.ts.map +1 -0
- package/dist/lib/browser/content-checks.js +474 -0
- package/dist/lib/browser/critique.d.ts +91 -0
- package/dist/lib/browser/critique.d.ts.map +1 -0
- package/dist/lib/browser/critique.js +167 -0
- package/dist/lib/browser/geometry.d.ts +32 -0
- package/dist/lib/browser/geometry.d.ts.map +1 -1
- package/dist/lib/browser/geometry.js +244 -1
- package/dist/lib/browser/index.d.ts +4 -1
- package/dist/lib/browser/index.d.ts.map +1 -1
- package/dist/lib/browser/index.js +2 -0
- package/dist/lib/claude-desktop.d.ts.map +1 -1
- package/dist/lib/claude-desktop.js +4 -1
- package/dist/lib/council/index.d.ts +6 -6
- package/dist/lib/council/index.d.ts.map +1 -1
- package/dist/lib/council/index.js +8 -8
- package/dist/lib/exec.d.ts +7 -0
- package/dist/lib/exec.d.ts.map +1 -1
- package/dist/lib/exec.js +6 -3
- package/dist/lib/identities/assume.d.ts +2 -2
- package/dist/lib/identities/assume.js +7 -7
- package/dist/lib/instructions/apply.d.ts +3 -3
- package/dist/lib/instructions/apply.d.ts.map +1 -1
- package/dist/lib/instructions/apply.js +75 -27
- package/dist/lib/instructions/host-addendum.d.ts +54 -0
- package/dist/lib/instructions/host-addendum.d.ts.map +1 -0
- package/dist/lib/instructions/host-addendum.js +107 -0
- package/dist/lib/instructions/templates.d.ts +10 -3
- package/dist/lib/instructions/templates.d.ts.map +1 -1
- package/dist/lib/instructions/templates.js +41 -12
- package/dist/lib/machine.d.ts +2 -2
- package/dist/lib/machine.js +2 -2
- package/package.json +26 -21
- package/schemas/config.schema.json +26 -1
- package/src/commander.ts +22 -13
- package/src/commands/{harness.ts → adapter.ts} +46 -46
- package/src/commands/agents.ts +71 -64
- package/src/commands/artifacts.ts +168 -0
- package/src/commands/backup.ts +47 -33
- package/src/commands/browse.ts +438 -2
- package/src/commands/checkpoint.ts +196 -0
- package/src/commands/context.ts +3 -176
- package/src/commands/decision.ts +41 -7
- package/src/commands/deinit.ts +16 -16
- package/src/commands/doctor.ts +28 -28
- package/src/commands/{supervisor.ts → governor.ts} +184 -154
- package/src/commands/init.ts +50 -34
- package/src/commands/{scratch.ts → journal.ts} +60 -60
- package/src/commands/policy.ts +1 -1
- package/src/commands/presence.ts +2 -2
- package/src/commands/sync.ts +3 -3
- package/src/commands/tunnel.ts +237 -34
- package/src/commands/web-fetch.ts +1 -1
- package/src/commands/web.ts +56 -1
- package/src/commands/work.ts +72 -38
- package/src/commands/workflow.ts +43 -34
- package/src/core/{harnesses → adapters}/attest-projection.ts +2 -2
- package/src/core/{harnesses → adapters}/attest.ts +27 -27
- package/src/core/{harnesses → adapters}/attestation.ts +41 -41
- package/src/core/{harnesses → adapters}/bench.ts +34 -43
- package/src/core/adapters/index.ts +56 -0
- package/src/core/{harnesses → adapters}/profiles.ts +16 -16
- package/src/core/{harnesses → adapters}/registry.ts +23 -23
- package/src/core/{harnesses → adapters}/types.ts +28 -23
- package/src/core/agents/canonical-emit.ts +6 -6
- package/src/core/agents/cli-emit.ts +1 -1
- package/src/core/agents/cli.ts +30 -28
- package/src/core/agents/codex-replay.ts +6 -6
- package/src/core/agents/coord-client.ts +119 -28
- package/src/core/agents/events/consume.ts +1 -1
- package/src/core/agents/events/emit.ts +7 -6
- package/src/core/agents/render/prompt-context.ts +107 -31
- package/src/core/agents/render/session-context.ts +6 -6
- package/src/core/agents/rules/claim-conflict.ts +4 -7
- package/src/core/agents/rules/commit-conflict.ts +103 -22
- package/src/core/agents/rules/stop-hook.ts +33 -17
- package/src/core/agents/rules/verdict.ts +13 -0
- package/src/core/agents/session-events.ts +4 -4
- package/src/core/agents/state/council.ts +1 -1
- package/src/core/agents/state/heartbeat-projector.ts +16 -12
- package/src/core/agents/state/heartbeat-writer.ts +12 -12
- package/src/core/agents/state/{scratch.ts → journal.ts} +12 -12
- package/src/core/agents/state/pidmap.ts +130 -15
- package/src/core/agents/state/proc-start.ts +208 -0
- package/src/core/agents/state/shell-mutation.ts +1 -1
- package/src/core/agents/state/stale-sweep.ts +7 -7
- package/src/core/artifacts/index.ts +557 -0
- package/src/core/config.ts +30 -2
- package/src/core/context/index.ts +15 -15
- package/src/core/governor/adapter-spread.ts +82 -0
- package/src/core/governor/index.ts +91 -0
- package/src/core/governor/plan-api.ts +115 -0
- package/src/core/{supervisor → governor}/plan-read.ts +120 -96
- package/src/core/{supervisor → governor}/plan-types.ts +64 -50
- package/src/core/{supervisor → governor}/planning.ts +240 -188
- package/src/core/governor/read.ts +59 -0
- package/src/core/{supervisor → governor}/runner.ts +39 -39
- package/src/core/{supervisor → governor}/service-read.ts +22 -22
- package/src/core/{supervisor → governor}/service.ts +117 -119
- package/src/core/{supervisor → governor}/state.ts +389 -174
- package/src/core/hooks/adapter/detect.ts +30 -0
- package/src/core/hooks/{harness → adapter}/events.ts +16 -14
- package/src/core/hooks/{harness → adapter}/output.ts +31 -23
- package/src/core/hooks/{harness → adapter}/parse.ts +5 -5
- package/src/core/hooks/{harness → adapter}/wiring.ts +21 -21
- package/src/core/hooks/cli.ts +185 -162
- package/src/core/hooks/effects/image-capture.ts +6 -6
- package/src/core/hooks/effects/index.ts +17 -17
- package/src/core/hooks/events/emit.ts +3 -3
- package/src/core/hooks/events/rotate.ts +1 -1
- package/src/core/hooks/events/schema.ts +15 -7
- package/src/core/hooks/guard-path.ts +2 -2
- package/src/core/hooks/index.ts +1 -1
- package/src/core/hooks/resolve/anchor.ts +63 -8
- package/src/core/hooks/resolve/coord-root.ts +4 -4
- package/src/core/hooks/resolve/intent.ts +1 -1
- package/src/core/hooks/resolve/owner.ts +11 -5
- package/src/core/{scratch → journal}/index.ts +53 -53
- package/src/core/policy/evaluate.ts +8 -8
- package/src/core/policy/types.ts +3 -3
- package/src/core/work/runner.ts +11 -2
- package/src/core/work/state.ts +185 -21
- package/src/core/workflow/adapters.ts +33 -0
- package/src/core/workflow/approvals.ts +7 -7
- package/src/core/workflow/billing.ts +12 -12
- package/src/core/workflow/child-env.ts +13 -4
- package/src/core/workflow/engine.ts +166 -90
- package/src/core/workflow/evidence-preflight.ts +165 -0
- package/src/core/workflow/index.ts +6 -3
- package/src/core/workflow/proof.ts +94 -48
- package/src/core/workflow/run-state.ts +7 -7
- package/src/core/workflow/sandbox-projection.ts +16 -16
- package/src/core/workflow/spawn-claude.ts +34 -12
- package/src/core/workflow/spawn-codex.ts +30 -12
- package/src/core/workflow/spawn-cursor.ts +31 -12
- package/src/core/workflow/spawn-failure.ts +41 -1
- package/src/core/workflow/specialists.ts +2 -2
- package/src/core/workflow/{journal.ts → transcript.ts} +16 -16
- package/src/core/workflow/types.ts +130 -46
- package/src/core/workflow/validate.ts +1 -1
- package/src/core/workflow/workspaces/cancellation.ts +2 -2
- package/src/core/workflow/workspaces/cleanup.ts +2 -2
- package/src/core/workflow/workspaces/execution.ts +6 -6
- package/src/core/workflow/workspaces/inspect.ts +14 -11
- package/src/core/workflow/workspaces/integration.ts +18 -14
- package/src/core/workflow/workspaces/lifecycle.ts +10 -10
- package/src/core/workflow/workspaces/local-git.ts +47 -5
- package/src/core/workflow/workspaces/recovery.ts +3 -3
- package/src/core/workflow/workspaces/state.ts +4 -3
- package/src/core/workflow/workspaces/types.ts +1 -1
- package/src/lib/browser/asserts.ts +177 -0
- package/src/lib/browser/client.ts +189 -14
- package/src/lib/browser/content-checks.ts +571 -0
- package/src/lib/browser/critique.ts +231 -0
- package/src/lib/browser/geometry.ts +297 -1
- package/src/lib/browser/index.ts +34 -0
- package/src/lib/claude-desktop.ts +4 -1
- package/src/lib/council/index.ts +9 -9
- package/src/lib/exec.ts +13 -3
- package/src/lib/identities/assume.ts +7 -7
- package/src/lib/instructions/apply.ts +87 -25
- package/src/lib/instructions/host-addendum.ts +126 -0
- package/src/lib/instructions/templates.ts +44 -15
- package/src/lib/machine.ts +2 -2
- package/dist/commands/harness.d.ts +0 -12
- package/dist/commands/harness.d.ts.map +0 -1
- package/dist/commands/scratch.d.ts +0 -4
- package/dist/commands/scratch.d.ts.map +0 -1
- package/dist/commands/session.d.ts.map +0 -1
- package/dist/commands/session.js +0 -162
- package/dist/commands/supervisor.d.ts +0 -4
- package/dist/commands/supervisor.d.ts.map +0 -1
- package/dist/core/harnesses/attest-projection.d.ts.map +0 -1
- package/dist/core/harnesses/attest.d.ts.map +0 -1
- package/dist/core/harnesses/attestation.d.ts.map +0 -1
- package/dist/core/harnesses/bench.d.ts.map +0 -1
- package/dist/core/harnesses/index.d.ts +0 -13
- package/dist/core/harnesses/index.d.ts.map +0 -1
- package/dist/core/harnesses/index.js +0 -6
- package/dist/core/harnesses/profiles.d.ts.map +0 -1
- package/dist/core/harnesses/registry.d.ts +0 -15
- package/dist/core/harnesses/registry.d.ts.map +0 -1
- package/dist/core/harnesses/types.d.ts.map +0 -1
- package/dist/core/hooks/harness/detect.d.ts +0 -9
- package/dist/core/hooks/harness/events.d.ts.map +0 -1
- package/dist/core/hooks/harness/output.d.ts.map +0 -1
- package/dist/core/supervisor/index.d.ts +0 -7
- package/dist/core/supervisor/index.d.ts.map +0 -1
- package/dist/core/supervisor/index.js +0 -6
- package/dist/core/supervisor/plan-api.d.ts +0 -23
- package/dist/core/supervisor/plan-api.d.ts.map +0 -1
- package/dist/core/supervisor/plan-api.js +0 -50
- package/dist/core/supervisor/plan-read.d.ts +0 -5
- package/dist/core/supervisor/plan-read.d.ts.map +0 -1
- package/dist/core/supervisor/plan-types.d.ts +0 -170
- package/dist/core/supervisor/plan-types.d.ts.map +0 -1
- package/dist/core/supervisor/planning.d.ts +0 -33
- package/dist/core/supervisor/planning.d.ts.map +0 -1
- package/dist/core/supervisor/read.d.ts +0 -6
- package/dist/core/supervisor/read.d.ts.map +0 -1
- package/dist/core/supervisor/read.js +0 -4
- package/dist/core/supervisor/runner.d.ts +0 -37
- package/dist/core/supervisor/runner.d.ts.map +0 -1
- package/dist/core/supervisor/service-read.d.ts +0 -6
- package/dist/core/supervisor/service-read.d.ts.map +0 -1
- package/dist/core/supervisor/service.d.ts +0 -114
- package/dist/core/supervisor/service.d.ts.map +0 -1
- package/dist/core/supervisor/state.d.ts +0 -95
- package/dist/core/supervisor/state.d.ts.map +0 -1
- package/dist/core/workflow/harnesses.d.ts +0 -17
- package/dist/core/workflow/harnesses.d.ts.map +0 -1
- package/dist/core/workflow/harnesses.js +0 -21
- package/dist/core/workflow/journal.d.ts.map +0 -1
- package/src/commands/session.ts +0 -187
- package/src/core/harnesses/index.ts +0 -56
- package/src/core/hooks/harness/detect.ts +0 -30
- package/src/core/supervisor/index.ts +0 -89
- package/src/core/supervisor/plan-api.ts +0 -73
- package/src/core/supervisor/read.ts +0 -59
- package/src/core/workflow/harnesses.ts +0 -33
- /package/dist/core/{harnesses → adapters}/attest-projection.js +0 -0
package/src/commands/browse.ts
CHANGED
|
@@ -5,16 +5,25 @@ import type { Command } from "commander";
|
|
|
5
5
|
import type { EmitContext, HarneryProgramContext } from "../commander.ts";
|
|
6
6
|
import { resolveBinName } from "../core/config.ts";
|
|
7
7
|
import {
|
|
8
|
+
type AssertResult,
|
|
8
9
|
Browser,
|
|
10
|
+
type ContentAnnotationBox,
|
|
11
|
+
type ContentChecksResult,
|
|
12
|
+
type CritiqueResult,
|
|
13
|
+
type CritiqueTile,
|
|
9
14
|
captureDevOverlay,
|
|
15
|
+
DEFAULT_CRITIQUE_RUBRIC,
|
|
10
16
|
type DevOverlayResult,
|
|
11
17
|
type Diagnostics,
|
|
12
18
|
type LayoutAxis,
|
|
13
19
|
type LayoutLintResult,
|
|
14
20
|
type OverflowResult,
|
|
21
|
+
parseAssertSpec,
|
|
15
22
|
type RuntsResult,
|
|
23
|
+
runCritique,
|
|
16
24
|
type TargetSizeProfile,
|
|
17
25
|
type TargetSizeResult,
|
|
26
|
+
tilesFromFullPage,
|
|
18
27
|
type VisibilityResult,
|
|
19
28
|
type WidthResult,
|
|
20
29
|
wslHeadedLaunchArgs,
|
|
@@ -121,6 +130,35 @@ interface BrowseOpts {
|
|
|
121
130
|
checkOverlapThreshold?: string;
|
|
122
131
|
checkOverlapFail?: boolean;
|
|
123
132
|
checkOverlapAnnotate?: boolean;
|
|
133
|
+
checkCrowd?: string[];
|
|
134
|
+
checkCrowdMin?: string;
|
|
135
|
+
checkCrowdFail?: boolean;
|
|
136
|
+
checkCrowdAnnotate?: boolean;
|
|
137
|
+
// Content checks (optional selector scope; true = whole body)
|
|
138
|
+
checkPlaceholder?: boolean | string;
|
|
139
|
+
checkPlaceholderFail?: boolean;
|
|
140
|
+
checkPlaceholderAnnotate?: boolean;
|
|
141
|
+
checkImages?: boolean | string;
|
|
142
|
+
checkImagesTolerance?: string;
|
|
143
|
+
checkImagesFail?: boolean;
|
|
144
|
+
checkImagesAnnotate?: boolean;
|
|
145
|
+
checkTruncation?: boolean | string;
|
|
146
|
+
checkTruncationTolerance?: string;
|
|
147
|
+
checkTruncationFail?: boolean;
|
|
148
|
+
checkTruncationAnnotate?: boolean;
|
|
149
|
+
checkContrast?: boolean | string;
|
|
150
|
+
checkContrastFail?: boolean;
|
|
151
|
+
checkContrastAnnotate?: boolean;
|
|
152
|
+
// Vision-model critique (optional selector = semantic per-element tiling)
|
|
153
|
+
checkCritique?: boolean | string;
|
|
154
|
+
checkCritiqueBand?: string;
|
|
155
|
+
checkCritiqueOverlap?: string;
|
|
156
|
+
checkCritiqueMaxTiles?: string;
|
|
157
|
+
checkCritiqueRubric?: string;
|
|
158
|
+
checkCritiqueFail?: boolean;
|
|
159
|
+
// Value assertions
|
|
160
|
+
assert?: string[];
|
|
161
|
+
assertFail?: boolean;
|
|
124
162
|
// Optional selector; null means the full document. Repeatable.
|
|
125
163
|
checkHit?: Array<string | null>;
|
|
126
164
|
checkHitProfile?: string;
|
|
@@ -357,6 +395,105 @@ export function registerBrowseCommand(
|
|
|
357
395
|
)
|
|
358
396
|
.option("--check-overlap-fail", "Exit 2 when an overlap target fails or is missing.")
|
|
359
397
|
.option("--no-check-overlap-annotate", "Skip overlap screenshot annotations.")
|
|
398
|
+
.option(
|
|
399
|
+
"--check-crowd <selector>",
|
|
400
|
+
"Flag adjacent card panels (full border, modest corner radius, or box-shadow) " +
|
|
401
|
+
"that touch or nearly touch. Also treats wrappers that contain panels as " +
|
|
402
|
+
"peers, measuring the nearest face panels inside them so a card-grid " +
|
|
403
|
+
"flush against the next card is caught. Walks the whole subtree under the " +
|
|
404
|
+
"selector, so one --check-crowd .wrap catches nested cases. Fills the gap " +
|
|
405
|
+
"between --check-overlap (needs a 2D intersection) and --check-gap (flags " +
|
|
406
|
+
"uneven spacing, so a uniformly-flush stack passes). Repeatable.",
|
|
407
|
+
(value: string, previous: string[] = []) => [...previous, value],
|
|
408
|
+
[] as string[],
|
|
409
|
+
)
|
|
410
|
+
.option(
|
|
411
|
+
"--check-crowd-min <px>",
|
|
412
|
+
"Minimum acceptable edge gap between adjacent panel faces in CSS px (default 6). " +
|
|
413
|
+
"Pairs closer than this are flagged; negative separation (overlap) always flags.",
|
|
414
|
+
"6",
|
|
415
|
+
)
|
|
416
|
+
.option("--check-crowd-fail", "Exit 2 when a crowd target fails or is missing.")
|
|
417
|
+
.option("--no-check-crowd-annotate", "Skip crowd screenshot annotations.")
|
|
418
|
+
.option(
|
|
419
|
+
"--check-placeholder [selector]",
|
|
420
|
+
"Scan rendered text for unrendered template tokens and bound-value tells: " +
|
|
421
|
+
"JS template literals, `{{x}}` handlebars, `[object Object]`, `Invalid Date`, " +
|
|
422
|
+
"`NaN`, or an element whose whole text is literally 'undefined' / 'null'. Optional selector " +
|
|
423
|
+
"scopes the sweep (default: whole body). Catches the most common generation " +
|
|
424
|
+
"bug on funnels and reports. Do not point it at a page that documents template " +
|
|
425
|
+
"syntax in prose.",
|
|
426
|
+
)
|
|
427
|
+
.option("--check-placeholder-fail", "Exit 2 if any placeholder tell is found.")
|
|
428
|
+
.option("--no-check-placeholder-annotate", "Skip placeholder screenshot annotations.")
|
|
429
|
+
.option(
|
|
430
|
+
"--check-images [selector]",
|
|
431
|
+
"Audit <img> elements for load + aspect health: a failed load (naturalWidth 0), " +
|
|
432
|
+
"a still-loading image, or a stretched image (rendered aspect ratio far from the " +
|
|
433
|
+
"intrinsic one, with an object-fit that does not correct it). Optional selector " +
|
|
434
|
+
"scopes the sweep (default: whole body).",
|
|
435
|
+
)
|
|
436
|
+
.option(
|
|
437
|
+
"--check-images-tolerance <ratio>",
|
|
438
|
+
"Aspect-ratio deviation above which an image counts as stretched (0–1, default 0.1 = 10%).",
|
|
439
|
+
"0.1",
|
|
440
|
+
)
|
|
441
|
+
.option("--check-images-fail", "Exit 2 if any image failed to load or is stretched.")
|
|
442
|
+
.option("--no-check-images-annotate", "Skip image screenshot annotations.")
|
|
443
|
+
.option(
|
|
444
|
+
"--check-truncation [selector]",
|
|
445
|
+
"Flag text actively cut off by an ellipsis or -webkit-line-clamp — the author asked " +
|
|
446
|
+
"to truncate AND the content overflows. High precision: it does not flag plain " +
|
|
447
|
+
"overflow:hidden clips. Optional selector scopes the sweep (default: whole body).",
|
|
448
|
+
)
|
|
449
|
+
.option(
|
|
450
|
+
"--check-truncation-tolerance <px>",
|
|
451
|
+
"Overflow past the clip box, in CSS px, above which truncation counts (default 2).",
|
|
452
|
+
"2",
|
|
453
|
+
)
|
|
454
|
+
.option("--check-truncation-fail", "Exit 2 if any text is actively truncated.")
|
|
455
|
+
.option("--no-check-truncation-annotate", "Skip truncation screenshot annotations.")
|
|
456
|
+
.option(
|
|
457
|
+
"--check-contrast [selector]",
|
|
458
|
+
"Flag rendered text below the WCAG AA contrast ratio (4.5:1 normal, 3:1 large) against " +
|
|
459
|
+
"its effective background. Runs at whatever theme the page is in, so toggle the theme " +
|
|
460
|
+
"with --batch to cover light and dark. Text over an image or gradient is reported as " +
|
|
461
|
+
"`unknown`, not failed. Optional selector scopes the sweep (default: whole body).",
|
|
462
|
+
)
|
|
463
|
+
.option("--check-contrast-fail", "Exit 2 if any text fails the contrast ratio.")
|
|
464
|
+
.option("--no-check-contrast-annotate", "Skip contrast screenshot annotations.")
|
|
465
|
+
.option(
|
|
466
|
+
"--check-critique [selector]",
|
|
467
|
+
"Hand the rendered page to a vision model for a visual-defect review, tile by " +
|
|
468
|
+
"tile. Catches the long tail heuristic checks can't enumerate. Default tiling " +
|
|
469
|
+
"cuts the page into overlapping vertical bands; pass a selector to tile one " +
|
|
470
|
+
"screenshot per matching element (semantic tiling). Requires the host to inject " +
|
|
471
|
+
"a critiqueProvider (harnery ships no model client) — without one the check " +
|
|
472
|
+
"reports `skipped`. Findings land under `critique` in the JSON envelope.",
|
|
473
|
+
)
|
|
474
|
+
.option("--check-critique-band <px>", "Band height for default tiling (default 1400).", "1400")
|
|
475
|
+
.option(
|
|
476
|
+
"--check-critique-overlap <px>",
|
|
477
|
+
"Vertical overlap between bands so a finding at a seam stays visible (default 120).",
|
|
478
|
+
"120",
|
|
479
|
+
)
|
|
480
|
+
.option(
|
|
481
|
+
"--check-critique-max-tiles <n>",
|
|
482
|
+
"Cap on tiles sent to the model, to bound cost (default 24).",
|
|
483
|
+
"24",
|
|
484
|
+
)
|
|
485
|
+
.option("--check-critique-rubric <text>", "Override the default critique rubric.")
|
|
486
|
+
.option("--check-critique-fail", "Exit 2 if the critique returns any high-severity finding.")
|
|
487
|
+
.option(
|
|
488
|
+
"--assert <expr>",
|
|
489
|
+
"Assert a page value (repeatable). Grammar: '<op> <selector> => <expected>', where op is " +
|
|
490
|
+
"text (trimmed text equals), contains (text includes), matches (text matches a regex), " +
|
|
491
|
+
"count (match count vs a number or >=/<=/>/< comparator), exists / absent (no => needed). " +
|
|
492
|
+
"e.g. --assert 'text h1 => Welcome' --assert 'count .card => >=3' --assert 'absent .error'.",
|
|
493
|
+
(value: string, previous: string[] = []) => [...previous, value],
|
|
494
|
+
[] as string[],
|
|
495
|
+
)
|
|
496
|
+
.option("--assert-fail", "Exit 2 if any --assert fails.")
|
|
360
497
|
.option(
|
|
361
498
|
"--check-hit [selector]",
|
|
362
499
|
"Check pointer-target size and spacing in the document or optional scope (repeatable).",
|
|
@@ -437,6 +574,7 @@ async function runBrowse(
|
|
|
437
574
|
opts.checkOverlapThreshold ?? "0",
|
|
438
575
|
"--check-overlap-threshold",
|
|
439
576
|
);
|
|
577
|
+
const crowdMin = parseNonNegativeNumber(opts.checkCrowdMin ?? "6", "--check-crowd-min");
|
|
440
578
|
const hitProfile = parseTargetSizeProfile(opts.checkHitProfile ?? "wcag-aa");
|
|
441
579
|
|
|
442
580
|
// Commander: `--no-cookies` turns `opts.cookies` into `false`. Default is `true`.
|
|
@@ -523,7 +661,8 @@ async function runBrowse(
|
|
|
523
661
|
(opts.checkAlign?.length ?? 0) > 0 ||
|
|
524
662
|
(opts.checkGap?.length ?? 0) > 0 ||
|
|
525
663
|
(opts.checkClip?.length ?? 0) > 0 ||
|
|
526
|
-
(opts.checkOverlap?.length ?? 0) > 0
|
|
664
|
+
(opts.checkOverlap?.length ?? 0) > 0 ||
|
|
665
|
+
(opts.checkCrowd?.length ?? 0) > 0;
|
|
527
666
|
let layoutLint: LayoutLintResult | undefined;
|
|
528
667
|
if (hasLayoutLint) {
|
|
529
668
|
layoutLint = await browser.checkLayoutLint({
|
|
@@ -546,12 +685,69 @@ async function runBrowse(
|
|
|
546
685
|
selector,
|
|
547
686
|
tolerancePx: overlapThreshold,
|
|
548
687
|
})),
|
|
688
|
+
crowd: (opts.checkCrowd ?? []).map((selector) => ({
|
|
689
|
+
selector,
|
|
690
|
+
minGapPx: crowdMin,
|
|
691
|
+
})),
|
|
549
692
|
});
|
|
550
693
|
}
|
|
551
694
|
let hit: TargetSizeResult[] | undefined;
|
|
552
695
|
if (opts.checkHit && opts.checkHit.length > 0) {
|
|
553
696
|
hit = await browser.checkTargetSize(opts.checkHit, hitProfile);
|
|
554
697
|
}
|
|
698
|
+
const hasContentChecks =
|
|
699
|
+
opts.checkPlaceholder !== undefined ||
|
|
700
|
+
opts.checkImages !== undefined ||
|
|
701
|
+
opts.checkTruncation !== undefined ||
|
|
702
|
+
opts.checkContrast !== undefined;
|
|
703
|
+
let content: ContentChecksResult | undefined;
|
|
704
|
+
if (hasContentChecks) {
|
|
705
|
+
content = await browser.checkContent({
|
|
706
|
+
placeholder:
|
|
707
|
+
opts.checkPlaceholder !== undefined
|
|
708
|
+
? { scope: contentScope(opts.checkPlaceholder) }
|
|
709
|
+
: null,
|
|
710
|
+
image:
|
|
711
|
+
opts.checkImages !== undefined
|
|
712
|
+
? {
|
|
713
|
+
scope: contentScope(opts.checkImages),
|
|
714
|
+
tolerance: parseNonNegativeNumber(
|
|
715
|
+
opts.checkImagesTolerance ?? "0.1",
|
|
716
|
+
"--check-images-tolerance",
|
|
717
|
+
),
|
|
718
|
+
}
|
|
719
|
+
: null,
|
|
720
|
+
truncation:
|
|
721
|
+
opts.checkTruncation !== undefined
|
|
722
|
+
? {
|
|
723
|
+
scope: contentScope(opts.checkTruncation),
|
|
724
|
+
tolerance: parseNonNegativeNumber(
|
|
725
|
+
opts.checkTruncationTolerance ?? "2",
|
|
726
|
+
"--check-truncation-tolerance",
|
|
727
|
+
),
|
|
728
|
+
}
|
|
729
|
+
: null,
|
|
730
|
+
contrast:
|
|
731
|
+
opts.checkContrast !== undefined ? { scope: contentScope(opts.checkContrast) } : null,
|
|
732
|
+
});
|
|
733
|
+
}
|
|
734
|
+
// Vision critique. Capture tiles BEFORE any annotation overlays are injected
|
|
735
|
+
// so the model sees the real page, not our boxes.
|
|
736
|
+
let critique: CritiqueResult | undefined;
|
|
737
|
+
if (opts.checkCritique !== undefined) {
|
|
738
|
+
const tiles = await captureCritiqueTiles(browser, opts);
|
|
739
|
+
critique = await runCritique({
|
|
740
|
+
url: navResult.url,
|
|
741
|
+
rubric: opts.checkCritiqueRubric ?? DEFAULT_CRITIQUE_RUBRIC,
|
|
742
|
+
tiles,
|
|
743
|
+
provider: context?.critiqueProvider,
|
|
744
|
+
});
|
|
745
|
+
}
|
|
746
|
+
let asserts: AssertResult[] | undefined;
|
|
747
|
+
if (opts.assert && opts.assert.length > 0) {
|
|
748
|
+
const specs = opts.assert.map(parseAssertSpec);
|
|
749
|
+
asserts = await browser.checkAsserts(specs);
|
|
750
|
+
}
|
|
555
751
|
const widthThreshold = Number.parseFloat(opts.checkWidthThreshold ?? "0.9");
|
|
556
752
|
const annotateWidth = widths && opts.checkWidthAnnotate !== false;
|
|
557
753
|
const annotateOverflow = overflow && opts.checkOverflowAnnotate !== false;
|
|
@@ -572,6 +768,7 @@ async function runBrowse(
|
|
|
572
768
|
gap: opts.checkGapAnnotate === false ? [] : layoutLint.gap,
|
|
573
769
|
clip: opts.checkClipAnnotate === false ? [] : layoutLint.clip,
|
|
574
770
|
overlap: opts.checkOverlapAnnotate === false ? [] : layoutLint.overlap,
|
|
771
|
+
crowd: opts.checkCrowdAnnotate === false ? [] : layoutLint.crowd,
|
|
575
772
|
}
|
|
576
773
|
: undefined;
|
|
577
774
|
if (
|
|
@@ -579,7 +776,8 @@ async function runBrowse(
|
|
|
579
776
|
(annotateLayoutLint.align.length > 0 ||
|
|
580
777
|
annotateLayoutLint.gap.length > 0 ||
|
|
581
778
|
annotateLayoutLint.clip.length > 0 ||
|
|
582
|
-
annotateLayoutLint.overlap.length > 0
|
|
779
|
+
annotateLayoutLint.overlap.length > 0 ||
|
|
780
|
+
annotateLayoutLint.crowd.length > 0)
|
|
583
781
|
) {
|
|
584
782
|
await browser.annotateLayoutLint(annotateLayoutLint);
|
|
585
783
|
}
|
|
@@ -587,6 +785,10 @@ async function runBrowse(
|
|
|
587
785
|
if (annotateHit) {
|
|
588
786
|
await browser.annotateTargetSize(hit!);
|
|
589
787
|
}
|
|
788
|
+
const contentBoxes = content ? collectContentBoxes(content, opts) : [];
|
|
789
|
+
if (contentBoxes.length > 0) {
|
|
790
|
+
await browser.annotateContent(contentBoxes);
|
|
791
|
+
}
|
|
590
792
|
|
|
591
793
|
if (opts.login) {
|
|
592
794
|
await new Promise<void>((res) => {
|
|
@@ -627,6 +829,9 @@ async function runBrowse(
|
|
|
627
829
|
runts,
|
|
628
830
|
layoutLint,
|
|
629
831
|
hit,
|
|
832
|
+
content,
|
|
833
|
+
critique,
|
|
834
|
+
asserts,
|
|
630
835
|
devOverlay,
|
|
631
836
|
batchResult,
|
|
632
837
|
);
|
|
@@ -642,6 +847,9 @@ async function runBrowse(
|
|
|
642
847
|
runts,
|
|
643
848
|
layoutLint,
|
|
644
849
|
hit,
|
|
850
|
+
content,
|
|
851
|
+
critique,
|
|
852
|
+
asserts,
|
|
645
853
|
devOverlay,
|
|
646
854
|
batchResult,
|
|
647
855
|
);
|
|
@@ -662,6 +870,9 @@ async function runBrowse(
|
|
|
662
870
|
if (annotateHit) {
|
|
663
871
|
await browser.clearTargetSizeAnnotations();
|
|
664
872
|
}
|
|
873
|
+
if (contentBoxes.length > 0) {
|
|
874
|
+
await browser.clearContentAnnotations();
|
|
875
|
+
}
|
|
665
876
|
|
|
666
877
|
if (opts.checkVisibleFail && visibility) {
|
|
667
878
|
const threshold = Number.parseFloat(opts.checkVisibleThreshold ?? "0.9");
|
|
@@ -723,6 +934,22 @@ async function runBrowse(
|
|
|
723
934
|
}
|
|
724
935
|
|
|
725
936
|
applyLayoutLintFailGates(opts, layoutLint, hit);
|
|
937
|
+
applyContentFailGates(opts, content);
|
|
938
|
+
if (opts.checkCritiqueFail && critique && critique.outcome === "fail") {
|
|
939
|
+
const high = critique.findings.filter((f) => f.severity === "high").length;
|
|
940
|
+
emit.log(`check-critique FAIL: ${high} high-severity finding(s)`, "warn");
|
|
941
|
+
process.exitCode = 2;
|
|
942
|
+
}
|
|
943
|
+
if (opts.assertFail && asserts) {
|
|
944
|
+
const failed = asserts.filter((a) => a.outcome === "fail");
|
|
945
|
+
for (const a of failed) {
|
|
946
|
+
emit.log(
|
|
947
|
+
`assert FAIL: ${a.op} ${a.selector} → "${a.actual}"${a.error ? ` (${a.error})` : ""}`,
|
|
948
|
+
"warn",
|
|
949
|
+
);
|
|
950
|
+
}
|
|
951
|
+
if (failed.length > 0) process.exitCode = 2;
|
|
952
|
+
}
|
|
726
953
|
} finally {
|
|
727
954
|
await browser.close();
|
|
728
955
|
}
|
|
@@ -847,6 +1074,9 @@ async function runPrintMode(
|
|
|
847
1074
|
runts: RuntsResult | undefined,
|
|
848
1075
|
layoutLint: LayoutLintResult | undefined,
|
|
849
1076
|
hit: TargetSizeResult[] | undefined,
|
|
1077
|
+
content: ContentChecksResult | undefined,
|
|
1078
|
+
critique: CritiqueResult | undefined,
|
|
1079
|
+
asserts: AssertResult[] | undefined,
|
|
850
1080
|
devOverlay: DevOverlayResult | undefined,
|
|
851
1081
|
batchResult: BatchResult | undefined,
|
|
852
1082
|
): Promise<void> {
|
|
@@ -876,8 +1106,12 @@ async function runPrintMode(
|
|
|
876
1106
|
result.gap = layoutLint.gap;
|
|
877
1107
|
result.clip = layoutLint.clip;
|
|
878
1108
|
result.overlap = layoutLint.overlap;
|
|
1109
|
+
result.crowd = layoutLint.crowd;
|
|
879
1110
|
}
|
|
880
1111
|
if (hit) result.hit = hit;
|
|
1112
|
+
if (content) assignContent(result, content);
|
|
1113
|
+
if (critique) result.critique = critique;
|
|
1114
|
+
if (asserts) result.asserts = asserts;
|
|
881
1115
|
if (devOverlay) result.devOverlay = devOverlay;
|
|
882
1116
|
if (batchResult && batchResult.clipboardReads.length > 0) {
|
|
883
1117
|
result.batchClipboardReads = batchResult.clipboardReads;
|
|
@@ -910,6 +1144,9 @@ async function runTrioMode(
|
|
|
910
1144
|
runts: RuntsResult | undefined,
|
|
911
1145
|
layoutLint: LayoutLintResult | undefined,
|
|
912
1146
|
hit: TargetSizeResult[] | undefined,
|
|
1147
|
+
content: ContentChecksResult | undefined,
|
|
1148
|
+
critique: CritiqueResult | undefined,
|
|
1149
|
+
asserts: AssertResult[] | undefined,
|
|
913
1150
|
devOverlay: DevOverlayResult | undefined,
|
|
914
1151
|
batchResult: BatchResult | undefined,
|
|
915
1152
|
): Promise<void> {
|
|
@@ -979,8 +1216,12 @@ async function runTrioMode(
|
|
|
979
1216
|
envelope.gap = layoutLint.gap;
|
|
980
1217
|
envelope.clip = layoutLint.clip;
|
|
981
1218
|
envelope.overlap = layoutLint.overlap;
|
|
1219
|
+
envelope.crowd = layoutLint.crowd;
|
|
982
1220
|
}
|
|
983
1221
|
if (hit) envelope.hit = hit;
|
|
1222
|
+
if (content) assignContent(envelope, content);
|
|
1223
|
+
if (critique) envelope.critique = critique;
|
|
1224
|
+
if (asserts) envelope.asserts = asserts;
|
|
984
1225
|
if (devOverlay) envelope.devOverlay = devOverlay;
|
|
985
1226
|
if (batchResult && batchResult.clipboardReads.length > 0) {
|
|
986
1227
|
envelope.batchClipboardReads = batchResult.clipboardReads;
|
|
@@ -1098,6 +1339,23 @@ async function runTrioMode(
|
|
|
1098
1339
|
});
|
|
1099
1340
|
emit.log(`check-hit:\n${lines.join("\n")}`, "info");
|
|
1100
1341
|
}
|
|
1342
|
+
if (content) {
|
|
1343
|
+
logContentSummary(content);
|
|
1344
|
+
}
|
|
1345
|
+
if (critique) {
|
|
1346
|
+
logCritiqueSummary(critique);
|
|
1347
|
+
}
|
|
1348
|
+
if (asserts && asserts.length > 0) {
|
|
1349
|
+
const lines = asserts.map((a) => {
|
|
1350
|
+
const detail =
|
|
1351
|
+
a.error ??
|
|
1352
|
+
(a.op === "count" || a.op === "exists" || a.op === "absent"
|
|
1353
|
+
? `${a.selector} → ${a.actual}${a.expected ? ` (want ${a.expected})` : ""}`
|
|
1354
|
+
: `${a.selector} → "${a.actual.slice(0, 60)}"${a.expected ? ` (want ${a.op} "${a.expected}")` : ""}`);
|
|
1355
|
+
return ` [${a.outcome.toUpperCase()}] ${a.op} ${detail}`;
|
|
1356
|
+
});
|
|
1357
|
+
emit.log(`asserts:\n${lines.join("\n")}`, "info");
|
|
1358
|
+
}
|
|
1101
1359
|
|
|
1102
1360
|
if (savedBaseline) {
|
|
1103
1361
|
emit.log(
|
|
@@ -1185,6 +1443,15 @@ function logLayoutLintSummary(result: LayoutLintResult): void {
|
|
|
1185
1443
|
` [${check.outcome.toUpperCase()}] overlap ${check.selector}: ${check.issues.length} collisions, worst ${worst.toFixed(0)}px²`,
|
|
1186
1444
|
);
|
|
1187
1445
|
}
|
|
1446
|
+
for (const check of result.crowd) {
|
|
1447
|
+
const tightest =
|
|
1448
|
+
check.issues.length > 0
|
|
1449
|
+
? Math.min(...check.issues.map((issue) => issue.separationPx))
|
|
1450
|
+
: check.minGapPx;
|
|
1451
|
+
lines.push(
|
|
1452
|
+
` [${check.outcome.toUpperCase()}] crowd ${check.selector}: ${check.issues.length} tight pair${check.issues.length === 1 ? "" : "s"} (min gap ${check.minGapPx}px, tightest ${tightest.toFixed(1)}px)`,
|
|
1453
|
+
);
|
|
1454
|
+
}
|
|
1188
1455
|
if (lines.length > 0) emit.log(`layout-lint:\n${lines.join("\n")}`, "info");
|
|
1189
1456
|
}
|
|
1190
1457
|
|
|
@@ -1213,6 +1480,7 @@ function applyLayoutLintFailGates(
|
|
|
1213
1480
|
gate(opts.checkGapFail, "gap", layoutLint?.gap);
|
|
1214
1481
|
gate(opts.checkClipFail, "clip", layoutLint?.clip);
|
|
1215
1482
|
gate(opts.checkOverlapFail, "overlap", layoutLint?.overlap);
|
|
1483
|
+
gate(opts.checkCrowdFail, "crowd", layoutLint?.crowd);
|
|
1216
1484
|
|
|
1217
1485
|
if (opts.checkHitFail && hit) {
|
|
1218
1486
|
const failed = hit.filter(
|
|
@@ -1228,6 +1496,174 @@ function applyLayoutLintFailGates(
|
|
|
1228
1496
|
}
|
|
1229
1497
|
}
|
|
1230
1498
|
|
|
1499
|
+
// ---------------------------------------------------------------------------
|
|
1500
|
+
// Content checks (placeholder / image / truncation / contrast)
|
|
1501
|
+
// ---------------------------------------------------------------------------
|
|
1502
|
+
|
|
1503
|
+
const CONTENT_COLORS = {
|
|
1504
|
+
placeholder: "#dc2626",
|
|
1505
|
+
image: "#f97316",
|
|
1506
|
+
truncation: "#a855f7",
|
|
1507
|
+
contrast: "#0891b2",
|
|
1508
|
+
} as const;
|
|
1509
|
+
|
|
1510
|
+
// Optional-selector flags resolve to a scope: a string selector, or null for
|
|
1511
|
+
// the whole body when the flag was passed bare.
|
|
1512
|
+
function contentScope(value: boolean | string): string | null {
|
|
1513
|
+
return typeof value === "string" ? value : null;
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
function assignContent(target: Record<string, unknown>, content: ContentChecksResult): void {
|
|
1517
|
+
if (content.placeholder) target.placeholder = content.placeholder;
|
|
1518
|
+
if (content.image) target.image = content.image;
|
|
1519
|
+
if (content.truncation) target.truncation = content.truncation;
|
|
1520
|
+
if (content.contrast) target.contrast = content.contrast;
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
function collectContentBoxes(
|
|
1524
|
+
content: ContentChecksResult,
|
|
1525
|
+
opts: BrowseOpts,
|
|
1526
|
+
): ContentAnnotationBox[] {
|
|
1527
|
+
const boxes: ContentAnnotationBox[] = [];
|
|
1528
|
+
if (content.placeholder && opts.checkPlaceholderAnnotate !== false) {
|
|
1529
|
+
for (const hit of content.placeholder.hits) {
|
|
1530
|
+
boxes.push({
|
|
1531
|
+
rect: hit.rect,
|
|
1532
|
+
label: `placeholder ${hit.token}`,
|
|
1533
|
+
color: CONTENT_COLORS.placeholder,
|
|
1534
|
+
});
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
if (content.image && opts.checkImagesAnnotate !== false) {
|
|
1538
|
+
for (const issue of content.image.issues) {
|
|
1539
|
+
boxes.push({ rect: issue.rect, label: `image ${issue.reason}`, color: CONTENT_COLORS.image });
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1542
|
+
if (content.truncation && opts.checkTruncationAnnotate !== false) {
|
|
1543
|
+
for (const hit of content.truncation.hits) {
|
|
1544
|
+
boxes.push({
|
|
1545
|
+
rect: hit.rect,
|
|
1546
|
+
label: `truncated ${hit.how}`,
|
|
1547
|
+
color: CONTENT_COLORS.truncation,
|
|
1548
|
+
});
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1551
|
+
if (content.contrast && opts.checkContrastAnnotate !== false) {
|
|
1552
|
+
for (const hit of content.contrast.hits) {
|
|
1553
|
+
if (hit.outcome !== "fail") continue;
|
|
1554
|
+
boxes.push({
|
|
1555
|
+
rect: hit.rect,
|
|
1556
|
+
label: `contrast ${hit.ratio}:1 < ${hit.required}`,
|
|
1557
|
+
color: CONTENT_COLORS.contrast,
|
|
1558
|
+
});
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
return boxes;
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1564
|
+
function logContentSummary(content: ContentChecksResult): void {
|
|
1565
|
+
const lines: string[] = [];
|
|
1566
|
+
if (content.placeholder) {
|
|
1567
|
+
const c = content.placeholder;
|
|
1568
|
+
lines.push(
|
|
1569
|
+
` [${c.outcome.toUpperCase()}] placeholder: ${c.hits.length} tell(s) in ${c.scanned} node(s)`,
|
|
1570
|
+
);
|
|
1571
|
+
}
|
|
1572
|
+
if (content.image) {
|
|
1573
|
+
const c = content.image;
|
|
1574
|
+
const missing = c.issues.filter((i) => i.reason === "missing").length;
|
|
1575
|
+
const stretched = c.issues.filter((i) => i.reason === "stretched").length;
|
|
1576
|
+
lines.push(
|
|
1577
|
+
` [${c.outcome.toUpperCase()}] image: ${missing} missing, ${stretched} stretched of ${c.scanned}`,
|
|
1578
|
+
);
|
|
1579
|
+
}
|
|
1580
|
+
if (content.truncation) {
|
|
1581
|
+
const c = content.truncation;
|
|
1582
|
+
lines.push(
|
|
1583
|
+
` [${c.outcome.toUpperCase()}] truncation: ${c.hits.length} clipped of ${c.scanned}`,
|
|
1584
|
+
);
|
|
1585
|
+
}
|
|
1586
|
+
if (content.contrast) {
|
|
1587
|
+
const c = content.contrast;
|
|
1588
|
+
const fails = c.hits.filter((h) => h.outcome === "fail").length;
|
|
1589
|
+
const unknown = c.hits.filter((h) => h.outcome === "unknown").length;
|
|
1590
|
+
lines.push(
|
|
1591
|
+
` [${c.outcome.toUpperCase()}] contrast: ${fails} fail, ${unknown} unknown of ${c.scanned}`,
|
|
1592
|
+
);
|
|
1593
|
+
}
|
|
1594
|
+
if (lines.length > 0) emit.log(`content-checks:\n${lines.join("\n")}`, "info");
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
function applyContentFailGates(opts: BrowseOpts, content: ContentChecksResult | undefined): void {
|
|
1598
|
+
if (!content) return;
|
|
1599
|
+
const gate = (
|
|
1600
|
+
enabled: boolean | undefined,
|
|
1601
|
+
rule: string,
|
|
1602
|
+
result: { found: boolean; outcome: string } | undefined,
|
|
1603
|
+
): void => {
|
|
1604
|
+
if (!enabled || !result) return;
|
|
1605
|
+
if (!result.found || result.outcome === "fail") {
|
|
1606
|
+
emit.log(`check-${rule} FAIL: ${result.found ? "issues found" : "scope not found"}`, "warn");
|
|
1607
|
+
process.exitCode = 2;
|
|
1608
|
+
}
|
|
1609
|
+
};
|
|
1610
|
+
gate(opts.checkPlaceholderFail, "placeholder", content.placeholder);
|
|
1611
|
+
gate(opts.checkImagesFail, "images", content.image);
|
|
1612
|
+
gate(opts.checkTruncationFail, "truncation", content.truncation);
|
|
1613
|
+
gate(opts.checkContrastFail, "contrast", content.contrast);
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
// ---------------------------------------------------------------------------
|
|
1617
|
+
// Vision critique tiling
|
|
1618
|
+
// ---------------------------------------------------------------------------
|
|
1619
|
+
|
|
1620
|
+
async function captureCritiqueTiles(browser: Browser, opts: BrowseOpts): Promise<CritiqueTile[]> {
|
|
1621
|
+
const maxTiles = Math.max(1, Number.parseInt(opts.checkCritiqueMaxTiles ?? "24", 10));
|
|
1622
|
+
const band = Math.max(200, Number.parseInt(opts.checkCritiqueBand ?? "1400", 10));
|
|
1623
|
+
const overlap = Math.max(0, Number.parseInt(opts.checkCritiqueOverlap ?? "120", 10));
|
|
1624
|
+
|
|
1625
|
+
// Capture the whole page ONCE, then crop tiles from the pixels. Playwright's
|
|
1626
|
+
// clip is viewport-relative, so a below-fold band clip throws ("clipped area
|
|
1627
|
+
// outside the resulting image") — cropping the full-page image sidesteps that
|
|
1628
|
+
// and keeps every tile at full resolution.
|
|
1629
|
+
const buffer = await browser.fullPageScreenshotBuffer();
|
|
1630
|
+
const elementRects =
|
|
1631
|
+
typeof opts.checkCritique === "string"
|
|
1632
|
+
? await browser.elementTiles(opts.checkCritique)
|
|
1633
|
+
: undefined;
|
|
1634
|
+
const tiles = tilesFromFullPage(buffer, { elementRects, bandHeight: band, overlap, maxTiles });
|
|
1635
|
+
|
|
1636
|
+
if (elementRects && elementRects.length > maxTiles) {
|
|
1637
|
+
emit.log(
|
|
1638
|
+
`check-critique: ${elementRects.length} elements matched, capped to ${maxTiles} tiles`,
|
|
1639
|
+
"warn",
|
|
1640
|
+
);
|
|
1641
|
+
} else if (!elementRects && tiles.length >= maxTiles) {
|
|
1642
|
+
emit.log(
|
|
1643
|
+
`check-critique: page banded to the ${maxTiles}-tile cap (raise --check-critique-max-tiles for full coverage)`,
|
|
1644
|
+
"warn",
|
|
1645
|
+
);
|
|
1646
|
+
}
|
|
1647
|
+
return tiles;
|
|
1648
|
+
}
|
|
1649
|
+
function logCritiqueSummary(critique: CritiqueResult): void {
|
|
1650
|
+
if (critique.outcome === "skipped") {
|
|
1651
|
+
emit.log(`check-critique: SKIPPED (${critique.error ?? "no provider"})`, "warn");
|
|
1652
|
+
return;
|
|
1653
|
+
}
|
|
1654
|
+
const high = critique.findings.filter((f) => f.severity === "high").length;
|
|
1655
|
+
const med = critique.findings.filter((f) => f.severity === "medium").length;
|
|
1656
|
+
const low = critique.findings.filter((f) => f.severity === "low").length;
|
|
1657
|
+
const lines = critique.findings
|
|
1658
|
+
.slice(0, 20)
|
|
1659
|
+
.map((f) => ` [${f.severity}] tile ${f.tile} ${f.category}: ${f.description}`);
|
|
1660
|
+
emit.log(
|
|
1661
|
+
`check-critique [${critique.outcome.toUpperCase()}] ${critique.tiles} tiles → ${high} high / ${med} med / ${low} low` +
|
|
1662
|
+
(lines.length ? `\n${lines.join("\n")}` : ""),
|
|
1663
|
+
critique.outcome === "fail" ? "warn" : "info",
|
|
1664
|
+
);
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1231
1667
|
function summarizeDiagnostics(diag: Diagnostics): Record<string, unknown> {
|
|
1232
1668
|
return {
|
|
1233
1669
|
viewport: diag.viewport,
|