diff --git a/.github/workflows/ease-proof.yml b/.github/workflows/ease-proof.yml new file mode 100644 index 0000000..dd1328c --- /dev/null +++ b/.github/workflows/ease-proof.yml @@ -0,0 +1,47 @@ +name: NodeKit EaseProof + +on: + pull_request: + workflow_dispatch: + inputs: + cache_class: + description: Evidence label; use cold only on a genuinely clean runner. + required: true + default: cold + type: choice + options: [cold, warm] + +jobs: + developer-ease: + name: ${{ matrix.os }} / ${{ matrix.package_manager }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [windows-latest, ubuntu-latest, macos-latest] + package_manager: [npm, pnpm] + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 22 + - uses: pnpm/action-setup@v4 + if: matrix.package_manager == 'pnpm' + with: + version: 10 + - run: npm ci + - run: npx playwright install chromium + - name: Run fresh generated-candidate journey + run: npm run acceptance:factory + env: + NODEKIT_CACHE_CLASS: ${{ inputs.cache_class || 'warm-or-unknown' }} + NODEKIT_PACKAGE_MANAGER: ${{ matrix.package_manager }} + - uses: actions/upload-artifact@v4 + if: always() + with: + name: ease-proof-${{ matrix.os }}-${{ matrix.package_manager }} + if-no-files-found: error + path: | + proof/factory-acceptance.json + proof/ease/developer-timing-run.json + proof/ease/latest/** diff --git a/.github/workflows/evolution.yml b/.github/workflows/evolution.yml new file mode 100644 index 0000000..440daf9 --- /dev/null +++ b/.github/workflows/evolution.yml @@ -0,0 +1,26 @@ +name: Evolution Ledger + +on: + pull_request: + push: + branches: [main] + +jobs: + verify: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + - run: npm ci + - run: npm run evolution:verify + - name: Material change coverage + if: github.event_name == 'pull_request' + run: node src/cli.mjs evolution materiality --from ${{ github.event.pull_request.base.sha }} --to ${{ github.event.pull_request.head.sha }} + - run: npm run evolution:docs + - name: Projection drift + run: git diff --exit-code -- evolution/projections diff --git a/.gitignore b/.gitignore index 30bb1d3..c1e129b 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ coverage/ .nodekit/ npm-debug.log* changes/nodekit-proof-campaign-2026-07-20/video/output/ +proof/ease/agents/ diff --git a/.qa/evidence/nodekit-base-20260721-1949/REPORT.md b/.qa/evidence/nodekit-base-20260721-1949/REPORT.md new file mode 100644 index 0000000..fb1042f --- /dev/null +++ b/.qa/evidence/nodekit-base-20260721-1949/REPORT.md @@ -0,0 +1,46 @@ +# NodeKit base agentic UI QA + +Mode: SANDBOX DOGFOOD + +Target: a fresh domain-blank application generated from the current NodeKit source. + +## Journey results + +| Journey | Result | Evidence | +|---|---|---| +| A0 smoke | PASS | Six light/dark desktop/tablet/mobile PNGs; UTF-8, zero console errors, zero horizontal overflow | +| A1 private core creation | PASS | `proposal-desktop.png`; canonical artifact remains v1 while proposal is pending | +| A2 live AI | SKIPPED | The neutral base deliberately has no live provider or egress claim | +| A3 provenance | PASS | Pending proposal shows base version; completion shows artifact v2 and receipt digest | +| A4 output/share | PARTIAL | Content-addressed receipt exists; export/deployment are intentionally not implemented | +| A5 themes/access | PASS | Six viewport/theme artifacts, semantic landmarks, skip link, focus-visible rules, reduced-motion rule | +| A6 adversarial | PASS | Second proposal while one is pending returns HTTP 400; one approval advances v1 to v2 exactly once | + +## Finding + +FINDING 1 · P2 · dark-theme contrast + +Symptom: the initial dark render made the hero description too faint. Root cause: `.lede` used a hard-coded light-theme gray instead of the shared muted token. Evidence: first `desktop-dark.png` visual inspection. Fix: bind `.lede` to `var(--muted)`. Re-verify: current `desktop-dark.png` and `mobile-dark.png`. + +## Agentic UI Bar + +| B1 | B2 | B3 | B4 | B5 | B6 | B7 | B8 | B9 | B10 | B11 | +|---|---|---|---|---|---|---|---|---|---|---| +| 2 | 1 | 2 | 2 | 1 | 2 | 1 | 2 | 2 | 1 | 2 | + +The base is intentionally not a live AI product, so model/cost/token attribution is not fabricated. The next product-level targets are visible exception recovery, explicit restore/undo, richer receipt inspection, and domain-specific content quality after specialization. + +## Gate artifacts + +- `desktop-light.png` +- `desktop-dark.png` +- `tablet-light.png` +- `tablet-dark.png` +- `mobile-light.png` +- `mobile-dark.png` +- `proposal-desktop.png` +- `completed-desktop.png` +- `completed-mobile-dark.png` +- `pixels.json` + +The structural factory receipt is `proof/factory-acceptance.json`; its candidate is separate from this rendered sandbox and is bound to its own compiled application hash. diff --git a/.qa/evidence/nodekit-base-20260721-1949/completed-desktop.png b/.qa/evidence/nodekit-base-20260721-1949/completed-desktop.png new file mode 100644 index 0000000..dbe7d0f Binary files /dev/null and b/.qa/evidence/nodekit-base-20260721-1949/completed-desktop.png differ diff --git a/.qa/evidence/nodekit-base-20260721-1949/completed-mobile-dark.png b/.qa/evidence/nodekit-base-20260721-1949/completed-mobile-dark.png new file mode 100644 index 0000000..a02bd7b Binary files /dev/null and b/.qa/evidence/nodekit-base-20260721-1949/completed-mobile-dark.png differ diff --git a/.qa/evidence/nodekit-base-20260721-1949/desktop-dark.png b/.qa/evidence/nodekit-base-20260721-1949/desktop-dark.png new file mode 100644 index 0000000..22138a7 Binary files /dev/null and b/.qa/evidence/nodekit-base-20260721-1949/desktop-dark.png differ diff --git a/.qa/evidence/nodekit-base-20260721-1949/desktop-light.png b/.qa/evidence/nodekit-base-20260721-1949/desktop-light.png new file mode 100644 index 0000000..d363d4c Binary files /dev/null and b/.qa/evidence/nodekit-base-20260721-1949/desktop-light.png differ diff --git a/.qa/evidence/nodekit-base-20260721-1949/mobile-dark.png b/.qa/evidence/nodekit-base-20260721-1949/mobile-dark.png new file mode 100644 index 0000000..8b1010c Binary files /dev/null and b/.qa/evidence/nodekit-base-20260721-1949/mobile-dark.png differ diff --git a/.qa/evidence/nodekit-base-20260721-1949/mobile-light.png b/.qa/evidence/nodekit-base-20260721-1949/mobile-light.png new file mode 100644 index 0000000..125c71c Binary files /dev/null and b/.qa/evidence/nodekit-base-20260721-1949/mobile-light.png differ diff --git a/.qa/evidence/nodekit-base-20260721-1949/pixels.json b/.qa/evidence/nodekit-base-20260721-1949/pixels.json new file mode 100644 index 0000000..4a4c85a --- /dev/null +++ b/.qa/evidence/nodekit-base-20260721-1949/pixels.json @@ -0,0 +1,17 @@ +{ + "repo": "D:/VSCode Projects/cafecorner_nodebench/nodebench_ai4/nodebench-ai", + "url": "http://127.0.0.1:4173/", + "outDir": "D:/VSCode Projects/cafecorner_nodebench/nodebench_ai4/node-platform/.qa/evidence/nodekit-base-20260721-1949", + "assert": ["One clear job.", "Primary artifact", "Review proposal", "Blank in domain. Figured out in behavior."], + "shots": [ + { "name": "desktop-light", "scheme": "light", "viewport": { "width": 1440, "height": 900 }, "fullPage": true }, + { "name": "desktop-dark", "scheme": "dark", "viewport": { "width": 1440, "height": 900 }, "fullPage": true }, + { "name": "tablet-light", "scheme": "light", "viewport": { "width": 1024, "height": 768 }, "fullPage": true }, + { "name": "tablet-dark", "scheme": "dark", "viewport": { "width": 1024, "height": 768 }, "fullPage": true }, + { "name": "mobile-light", "scheme": "light", "viewport": { "width": 390, "height": 844 }, "fullPage": true }, + { "name": "mobile-dark", "scheme": "dark", "viewport": { "width": 390, "height": 844 }, "fullPage": true, "reducedMotion": true }, + { "name": "proposal-desktop", "scheme": "light", "viewport": { "width": 1440, "height": 900 }, "clicks": ["#reset", "#propose", "#artifact"], "assert": ["Proposed change", "Based on artifact v1"], "fullPage": false }, + { "name": "completed-desktop", "scheme": "light", "viewport": { "width": 1440, "height": 900 }, "clicks": ["#reset", "#propose", "#approve", "#artifact"], "assert": ["Completion verified", "Receipt"], "fullPage": false }, + { "name": "completed-mobile-dark", "scheme": "dark", "viewport": { "width": 390, "height": 844 }, "clicks": ["#reset", "#propose", "#approve", "#artifact"], "assert": ["Completion verified", "Receipt"], "fullPage": false } + ] +} diff --git a/.qa/evidence/nodekit-base-20260721-1949/proposal-desktop.png b/.qa/evidence/nodekit-base-20260721-1949/proposal-desktop.png new file mode 100644 index 0000000..019477e Binary files /dev/null and b/.qa/evidence/nodekit-base-20260721-1949/proposal-desktop.png differ diff --git a/.qa/evidence/nodekit-base-20260721-1949/tablet-dark.png b/.qa/evidence/nodekit-base-20260721-1949/tablet-dark.png new file mode 100644 index 0000000..85c4ed9 Binary files /dev/null and b/.qa/evidence/nodekit-base-20260721-1949/tablet-dark.png differ diff --git a/.qa/evidence/nodekit-base-20260721-1949/tablet-light.png b/.qa/evidence/nodekit-base-20260721-1949/tablet-light.png new file mode 100644 index 0000000..fa6fa7b Binary files /dev/null and b/.qa/evidence/nodekit-base-20260721-1949/tablet-light.png differ diff --git a/.qa/memory/findings.jsonl b/.qa/memory/findings.jsonl new file mode 100644 index 0000000..e1ba749 --- /dev/null +++ b/.qa/memory/findings.jsonl @@ -0,0 +1,11 @@ +{"id":"nodekit-ui-001","rootCause":"The lede used a hard-coded light-theme gray instead of the shared muted token","sev":"P2","symptom":"Hero description was too faint in dark mode","status":"fixed","area":"dark-theme contrast","evidence":".qa/evidence/nodekit-base-20260721-1949/desktop-dark.png","fix":"Bind .lede color to var(--muted)","ts":"2026-07-21T19:54:08.694Z","fp":"1a956063993b"} +{"sev":"P0","area":"browser-proof-language","symptom":"Structural HTTP and source-DOM checks were labeled browser certified.","rootCause":"Release proof collapsed a non-rendered contract result into browser certification.","evidence":"proof/ease/latest/browser/screenshot-manifest.json","fix":"Separated structural and rendered-browser fields.","status":"fixed","ts":"2026-07-21T21:12:34.138Z","fp":"3d0a99ae9bbf"} +{"sev":"P1","area":"browser-state-coverage","symptom":"The real browser matrix covers only first arrival proposal and completion while required exception and recovery states remain absent.","rootCause":"The neutral base currently exposes only the happy-path controls.","evidence":"proof/ease/latest/browser/screenshot-manifest.json missingStates","fix":"Implement and capture remaining states before certification.","status":"open","ts":"2026-07-21T21:13:05.646Z","fp":"cce1cabe0304"} +{"sev":"P1","area":"fresh-agent-harness","symptom":"Two fresh coding-agent pilot attempts failed at the recorder boundary before a domain change was captured.","rootCause":"Windows quoting failed first then the wrapper discarded process fields.","evidence":"proof/ease/agents pilot directories","fix":"Recorder fixed and regression-tested no third attempt in this pass.","status":"open","ts":"2026-07-21T21:13:05.676Z","fp":"dd3549d104c6"} +{"sev":"P2","area":"generated-ui-charset","symptom":"The generated browser title rendered a mojibake separator.","rootCause":"A corrupted middle-dot byte sequence existed in the base HTML title.","evidence":"screenshot sidecars report mojibakeDetected false","fix":"Replaced the separator and added rendered-text checks.","status":"fixed","ts":"2026-07-21T21:13:05.675Z","fp":"3121d92760e7"} +{"sev":"P2","area":"mobile-landscape-first-action","symptom":"At 844 by 390 the primary workflow action was below the first viewport.","rootCause":"Hero spacing was width-responsive but not short-viewport responsive.","evidence":"proof/ease/latest/browser/screenshots/first_arrival--mobile-landscape--dark.png","fix":"Added a short-viewport media rule; regenerated pixels show the action and progress above the fold.","status":"fixed","ts":"2026-07-21T21:13:05.690Z","fp":"338d99b241e4"} +{"sev":"P0","area":"fresh-agent-semantic-gate","symptom":"A blocked no-change coding-agent run was initially labeled as a passing pilot because the untouched starter passed generic gates.","rootCause":"The harness checked process exit and starter health but did not require substantive candidate changes or reject a blocked final report.","evidence":"local proof/ease/agents/agent_volunteer-onboarding_b169f08e2cae/adjudication.json","fix":"Fail closed unless non-proof files changed and the final report is non-blocked; preserve an adjudication receipt for the false positive.","status":"fixed","ts":"2026-07-21T21:23:00.000Z","fp":"fresh-agent-semantic-gate"} +{"sev":"P1","area":"browser-state-coverage","symptom":"The real browser matrix covers only first arrival proposal and completion while required exception and recovery states remain absent.","rootCause":"The neutral base exposed only the happy path.","evidence":"proof/ease/latest/browser/screenshot-manifest.json: 180 screenshots, 15 states, zero missing","fix":"Added deterministic caseflow-backed state scenarios, responsive capture, and Axe checks.","status":"fixed","ts":"2026-07-21T22:13:00.000Z","fp":"cce1cabe0304"} +{"sev":"P1","area":"fresh-agent-harness","symptom":"Two fresh coding-agent pilot attempts failed at the recorder boundary before a domain change was captured.","rootCause":"Codex bubblewrap cannot create an unprivileged user namespace inside Docker Desktop; native execution remains read-only.","evidence":"proof/ease/agents/agent_volunteer-onboarding_165fb8466787/agent/session.jsonl","fix":"Container runner and semantic fail-close are implemented; nested sandbox configuration remains blocked and no third retry was attempted.","status":"open","ts":"2026-07-21T22:13:00.000Z","fp":"dd3549d104c6"} +{"sev":"P1","area":"dark-failure-contrast","symptom":"Failure banner text was unreadable in dark mode despite machine journey success.","rootCause":"The light failure background inherited dark-theme light text.","evidence":"proof/ease/latest/browser/screenshots/recoverable_failure--tablet-landscape--dark.png","fix":"Added state-specific dark-theme foregrounds and Axe serious/critical gating.","status":"fixed","ts":"2026-07-21T22:13:00.000Z","fp":"nodekit-dark-failure-contrast"} +{"sev":"P1","area":"mobile-receipt-framing","symptom":"Mobile landscape export evidence showed the page hero instead of the proof object.","rootCause":"Viewport capture did not move the state-defining receipt into view.","evidence":"proof/ease/latest/browser/screenshots/export_share--mobile-landscape--dark.png","fix":"Scroll the receipt detail into view for mobile receipt and export states.","status":"fixed","ts":"2026-07-21T22:13:00.000Z","fp":"nodekit-mobile-receipt-framing"} diff --git a/.qa/memory/runs.jsonl b/.qa/memory/runs.jsonl new file mode 100644 index 0000000..9e1861f --- /dev/null +++ b/.qa/memory/runs.jsonl @@ -0,0 +1,4 @@ +{"evidenceDir":".qa/evidence/nodekit-base-20260721-1949","executor":"Codex","gates":{"factory":"PASS","tests":"PASS","pixels":"PASS"},"bar":{"B2":1,"B1":2,"B10":1,"B3":2,"B8":2,"B7":1,"B11":2,"B9":2,"B4":2,"B6":2,"B5":1},"journeys":{"A1":"PASS","A2":"SKIPPED(no live AI in neutral base)","A6":"PASS","A0":"PASS","A3":"PASS","A5":"PASS","A4":"PARTIAL(receipt only)"},"ts":"2026-07-21T19:54:23.812Z"} +{"executor":"Codex","mode":"SANDBOX DOGFOOD","commit":"054a74cf5a1cc072b783223aadd8384773e21e8b","result":"EASE_NOT_CERTIFIED","journeys":{"factory":"passed","renderedBrowser":"passed-coverage-incomplete","freshAgent":"blocked-read-only","nodeProofIntegrity":"passed"},"evidence":"proof/ease/latest/manifest.json","openP0":0,"openP1":2,"notes":"A fresh-agent false positive was caught and repaired. Human usability, repeatable agents, full state coverage, three Convex consumers, preview deployment, and cross-platform proof remain open.","ts":"2026-07-21T21:25:40.000Z"} +{"executor":"Codex","mode":"SANDBOX DOGFOOD","commit":"4bdc60d0b0e224f7d4884d7857b6f968bb3a901d","result":"EASE_NOT_CERTIFIED","journeys":{"A0":"PASS","A1":"PASS","A2":"SKIPPED(no live AI in neutral base)","A3":"PASS","A4":"PASS(receipt inspection and export state)","A5":"PASS","A6":"PASS","freshAgent":"BLOCKED(nested bwrap namespace)"},"bar":{"B1":2,"B2":1,"B3":2,"B4":2,"B5":2,"B6":2,"B7":1,"B8":2,"B9":2,"B10":2,"B11":2},"gates":{"tests":"PASS(68)","factory":"PASS(138751ms)","browser":"PASS(180/180)","axe":"PASS(0 serious/critical)","console":"PASS(0)","network":"PASS(0)","freshAgent":"BLOCKED"},"evidenceDir":"proof/ease/latest","notes":"Browser state coverage closed after pixel repair. Submission remains blocked by fresh agents, five-user human study, three Convex consumers, preview deployment, and independent ProofLoop verification.","ts":"2026-07-21T22:13:00.000Z"} +{"executor":"Codex","mode":"SANDBOX DOGFOOD","commit":"1036650717c1b8cff76939cf6e2a5bef3c965da9","result":"EASE_NOT_CERTIFIED","runId":"ease_e320fe62fdc746a0a286","journeys":{"proposalApprovalReceiptReload":"PASS","stateMatrix":"PASS(180/180)","freshAgent":"BLOCKED(nested bwrap namespace)","nodeProofIntegrity":"PASS(180 screenshots, 2 replay artifacts)"},"gates":{"factory":"PASS(157768ms)","firstMeaningfulPaint":"PASS(1348ms)","trace":"PASS(13376187 bytes)","video":"PASS(2288708 bytes)","axe":"PASS(0 serious/critical)","console":"PASS(0)","network":"PASS(0)","overflow":"PASS(0)","mojibake":"PASS(0)"},"evidenceDir":"proof/ease/latest","notes":"Replayable browser proof is clean and independently hashed. Ease certification remains honestly false pending the repeated developer timing matrix, held-out fresh-agent success, five-person usability study, three real Convex consumers, and shareable preview proof.","ts":"2026-07-21T22:29:06.074Z"} diff --git a/.understand-anything/.understandignore b/.understand-anything/.understandignore new file mode 100644 index 0000000..786b1a9 --- /dev/null +++ b/.understand-anything/.understandignore @@ -0,0 +1,21 @@ +# .understandignore — paths excluded from architecture analysis +# Syntax follows .gitignore. + +# Generated evidence and temporary work are not source architecture. +proof/ +outputs/ +.tmp/ +.qa/ + +# Generated or vendored dependencies are covered by built-in exclusions. +# node_modules/ +# dist/ +# coverage/ +# vendor/ + +# Optional scopes; leave included for the first NodeKit architecture graph. +# test/ +# docs/ +# scripts/ +# templates/ +# plugins/ diff --git a/README.md b/README.md index eae3470..5f2ef9a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # NodeKit / Node Platform -NodeKit is the portable setup and conformance layer for proof-carrying agent applications. It turns an empty directory or an existing repository into a filesystem-authored agent harness with a compiled definition, capability packs, deterministic fixtures, live-provider gates, browser proof, and phase-by-phase receipts. +NodeKit is the figured-out product foundation and conformance layer for proof-carrying agent applications. It turns an empty directory or existing repository into a domain-blank application with one guided lifecycle, a compiled definition, deterministic fixtures, browser proof, and receipts. + +> Blank in domain. Figured out in behavior. Convex-first, not Convex-locked. Node Platform remains its ownership layer: it records which repository owns each shared contract and fails CI when a new fork or clear layer violation appears. @@ -13,8 +15,7 @@ Or use the CLI directly: ```bash node src/cli.mjs create ../my-agent-app \ --name my-agent-app \ - --brief "A persistent research agent that users can steer mid-run" \ - --sponsors pi-ai,convex \ + --brief "Carry one user intention to a reviewed and verified artifact" \ --package-manager pnpm \ --local-proof @@ -24,31 +25,15 @@ npm run eval npm run dev ``` -The first certified preset is `research-loop`: a small reference runtime with an objective held-out metric, deterministic keep/revert decisions, versioned human intervention, interrupted-run recovery, a strict Pi smoke, and sanitized reproduction receipts. It is a reference adapter to the NodeAgent application contract; it is not presented as the still-unfinished extraction of NodeRoom's deeper production runtime. - -`smb-lending-fde` is the first clean-room, domain-specific preset. It generates an independent synthetic forward-deployment lab with a primary restaurant working-capital case and a second medical-practice equipment case. Its agent can only propose a request for an explicitly missing document; a human approval is required before a request is applied, and the starter never makes or simulates a lending decision. Its two-case deterministic conformance harness is not a claim about Casca, a bank, a human operator, an external model, sealed held-out performance, or graph-agent superiority. +The primary creation path has no preset and no silent domain default. It generates the stable product grammar: -```bash -node src/cli.mjs create ../casca-fde-deployment-lab \ - --name casca-fde-deployment-lab \ - --preset smb-lending-fde \ - --brief "Map a synthetic SMB lending file without making a lending decision" \ - --local-proof +```text +Case -> Run -> Stage -> Artifact -> Proposal -> Approval -> Receipt ``` -`agentic-rl-research` generates a separate **replay-only** FounderQuest research -lab. It has synthetic train, validation, and held-out task files plus a protected -reward that rejects external/public/financial actions. It is a reproducible -environment and baseline, not an RL-training result or proof of real-world -generalization. +The generated app includes `docs/FIGURED_OUT.md`, a product brief, audience placeholder, user journey, service blueprint, experience contract, design direction, taste contract, a responsive artifact-first UI, and a deterministic in-memory conformance demo. -```bash -node src/cli.mjs create ../founderquest-rl \ - --name founderquest-rl \ - --preset agentic-rl-research \ - --brief "Evaluate safe next-action selection in synthetic founder journeys" \ - --local-proof -``` +There is no domain chooser, public preset catalog, or silent fallback. Narrow examples in this source repository are regression fixtures and historical demonstrations only: they are not shipped in the npm package and cannot be selected by the creation CLI. The coding agent starts from the same blank foundation every time, researches the user's actual workflow, and adds only the domain behavior that job requires. `npm run proof` works before credentials exist: it emits a passing `local-ready` receipt after the deterministic demo and evaluation. If live Pi, browser, or deployment receipts are present, every attempted gate must pass; the receipt becomes `release-ready` only when all three are present and green. @@ -84,27 +69,42 @@ package or immutable Git commit should replace the vendored runtime. The bundle is deliberately runtime-only; use the source repository or a future published package to scaffold additional projects. -The two production-oriented presets have an executable empty-directory gate: +The domain-blank base has an executable empty-directory gate: ```bash npm run acceptance:factory ``` -It creates clean temporary projects for `agentic-rl-research` and -`smb-lending-fde`, installs their dependencies, compiles them, runs tests, -deterministic demos, evaluations, benchmarks, and proof, then writes a -hash-bound summary to `proof/factory-acceptance.json`. +It creates a clean temporary base application, installs dependencies, compiles it, runs tests, the deterministic guided journey, stale-proposal evaluation, the structural live HTTP/DOM contract, and a real Playwright journey. Phase-level timers and candidate-bound screenshot sidecars are written under `proof/ease/latest/`; the fail-closed summary remains `proof/factory-acceptance.json`. A passing core browser journey is not Ease certification while required states, fresh-agent trials, human usability, deployment, and real consumers remain open. + +The consolidated strategy, evidence-backed status, Harness Gym roadmap, and ordered checklist are in +[`docs/NODEKIT_MASTER_PLAN.md`](docs/NODEKIT_MASTER_PLAN.md). The complete submission lock, +cross-platform workflow, fresh-agent protocol, and uncoached human-study thresholds are documented +in [`docs/EASE_PROOF.md`](docs/EASE_PROOF.md). The cross-platform workflow is manual so a reviewer +can deliberately label a run cold or warm; one matrix run is evidence, not a percentile claim. + +The adopted evidence-driven model profiling, executable skill, and capability-routing architecture +is in [`docs/MODEL_INTELLIGENCE.md`](docs/MODEL_INTELLIGENCE.md). Its implemented P0 commands do not +make provider calls or certify routing from an empty evidence set. Factory commands (a `--local-proof` run creates an initial local Git commit so receipts have an immutable candidate to bind to): ```bash -nodekit create --name --brief [--preset research-loop|smb-lending-fde|agentic-rl-research] +nodekit create --name --brief nodekit adopt --name --brief nodekit compile --repo-root nodekit inspect --repo-root nodekit graph import --repo-root --commit +nodekit graph init --repo-root nodekit graph query --repo-root +nodekit graph gaps --repo-root +nodekit graph harness-sync --repo-root +nodekit harness init --repo-root +nodekit models baseline --repo-root +nodekit models profile --repo-root +nodekit models inspect --repo-root +nodekit models diagnose --repo-root ``` From any repository with `nodekit.yaml`: @@ -122,6 +122,36 @@ consumers that do not use `nodekit create` should use an immutable Git or packed-tarball reference until the package is released. The unscoped `nodekit` npm name belongs to an unrelated project. +## PostgreSQL adapter + +NodeKit ships a driver-neutral, owner-scoped PostgreSQL Caseflow adapter. Apply the exported +`@homenshum/nodekit/adapters/postgres/migration.sql` once, then provide a pool compatible with +`pg.Pool`: + +```js +import { Pool } from "pg"; +import { createPostgresCaseflow } from "@homenshum/nodekit/adapters/postgres"; + +const pool = new Pool({ connectionString: process.env.DATABASE_URL }); +const caseflow = createPostgresCaseflow({ + pool, + ownerId: authenticatedPrincipal.id, +}); +``` + +The adapter uses transactions, row locks, advisory sequence locks, owner-scoped queries, +idempotent active-run/decision/completion retries, and conditional artifact-version application. +Run the shared suite against a disposable database with: + +```bash +NODEKIT_POSTGRES_URL=postgresql://... npm run conformance:postgres +``` + +The Supabase profile is a separate SQL export at +`@homenshum/nodekit/adapters/supabase/profile.sql`. It layers complete owner RLS and explicit +Realtime publication over the PostgreSQL schema. A checked-in profile is not a claim that live +Supabase Auth, Storage, Realtime, Queue, or Cron conformance has passed. + ## Contracts - [`ownership.yaml`](ownership.yaml) names one owner, current package, target package, status, version, and consumers for every governed concept. @@ -131,8 +161,11 @@ npm name belongs to an unrelated project. - [`schemas/nodeagent.application.v1.schema.json`](schemas/nodeagent.application.v1.schema.json) and [`schemas/nodeagent.pack.v1.schema.json`](schemas/nodeagent.pack.v1.schema.json) enforce the application and capability-pack contracts during compilation. - [`schemas/nodeagent.event.v1.schema.json`](schemas/nodeagent.event.v1.schema.json) defines the canonical portable event envelope. Applications resolve `nodeagent.event/v1` and `nodeagent.trace/v1` contract references even when an older v1 manifest omits the optional `contracts` block. - `nodekit compile` discovers authored files, validates pack references, rejects literal secrets, and emits a full application identity in `.nodeagent/`. The identity binds the agent, packs, integrations, backend, UI/app surface, scripts, workflow definitions, evaluations, fixtures, dependency locks, and recognized deployment configuration. -- `nodekit graph import` imports a pinned Understand Anything `knowledge-graph.json` as a namespaced, commit-bound code graph snapshot. `nodekit graph query` performs local graph retrieval; it never turns the code graph into a write authority. +- `nodekit graph import` imports a pinned Understand Anything `knowledge-graph.json` as a namespaced, commit-bound code graph snapshot. `nodekit graph query --code` retrieves from that snapshot; it never turns the code graph into a write authority. - [`docs/UNDERSTAND_ANYTHING_CODE_GRAPH.md`](docs/UNDERSTAND_ANYTHING_CODE_GRAPH.md) defines the graph authority, privacy, freshness, and NodeGraph/NodeRoom projection boundary. +- [`docs/KNOWLEDGE_EVOLUTION.md`](docs/KNOWLEDGE_EVOLUTION.md) defines the EvoGraph-R1-inspired, backend-neutral Knowledge Evolution Plane: immutable multimodal evidence, n-ary hyperedges, typed gaps, proposal-only graph mutations, explicit approval, stale-version conflicts, replay, receipts, Harness Gym projection, and evaluation boundaries. +- [`docs/FRONTEND_SPECIALIST.md`](docs/FRONTEND_SPECIALIST.md) defines the evidence-ranked frontend route, protected product packet, mandatory three-direction tournament, bounded repair, and fresh-user canary. +- [`docs/EVOLUTION_LEDGER.md`](docs/EVOLUTION_LEDGER.md) defines the permanent reasoning ledger from limitation and failed assumption through invariant, immutable evidence, verifier, and verified downstream adoption. - `nodekit create` refuses non-empty targets. `nodekit adopt` writes missing files only, preserves host scripts, and emits a collision receipt. - `nodekit repo check` validates ownership declarations, command aliases, migration origins, signature classification, and source rules. - `nodekit ecosystem check` checks all active local clones together. @@ -150,7 +183,7 @@ The earlier planning-only `apiVersion` / `kind` / `metadata` / `spec` envelope i ## Honest boundary -`planned`, `migration-planned`, and `canonical-unpackaged` remain intentionally distinct from released shared packages. NodeKit now has one end-to-end reference preset; it does not claim that every runtime adapter, backend, template, codemod, or production deployment target is complete. In particular, NodeRoom still contains the deepest live runtime and its extraction into a published NodeAgent package remains separate work. +`planned`, `migration-planned`, and `canonical-unpackaged` remain intentionally distinct from released shared packages. NodeKit now has one domain-blank base, portable in-memory Caseflow semantics, and an exact-revision PostgreSQL 17.10 conformance receipt. Convex Caseflow extraction, live Supabase managed-profile conformance, and deeper application adoption remain separately evidenced work until their respective gates pass. See [`docs/DECISIONS.md`](docs/DECISIONS.md) for the ownership split and migration rules. The coordinated consumer commits, pull requests, hosted checks, and known limits are recorded in [`docs/P0_ROLLOUT.md`](docs/P0_ROLLOUT.md) and [`proof/p0-rollout.json`](proof/p0-rollout.json). diff --git a/adapters/postgres/001_caseflow.sql b/adapters/postgres/001_caseflow.sql new file mode 100644 index 0000000..30db706 --- /dev/null +++ b/adapters/postgres/001_caseflow.sql @@ -0,0 +1,187 @@ +create schema if not exists nodekit; + +create table if not exists nodekit.cases ( + case_id text primary key, + owner_id text not null, + title text not null, + primary_job text not null, + status text not null check (status in ('ready', 'in_progress', 'completed')), + current_run_id text, + created_at timestamptz not null, + updated_at timestamptz not null +); + +create table if not exists nodekit.runs ( + run_id text primary key, + owner_id text not null, + case_id text not null references nodekit.cases(case_id), + status text not null check (status in ('active', 'blocked', 'cancelled', 'completed', 'failed_safely')), + current_stage_id text not null, + next_action text not null, + next_action_owner text not null, + stages jsonb not null, + created_at timestamptz not null, + updated_at timestamptz not null +); + +do $$ begin + alter table nodekit.cases add constraint nodekit_cases_current_run_fk + foreign key (current_run_id) references nodekit.runs(run_id); +exception when duplicate_object then null; +end $$; + +create table if not exists nodekit.artifacts ( + artifact_id text primary key, + owner_id text not null, + case_id text not null references nodekit.cases(case_id), + run_id text not null references nodekit.runs(run_id), + kind text not null, + title text not null, + canonical_version integer not null check (canonical_version >= 1), + created_at timestamptz not null, + updated_at timestamptz not null +); + +create table if not exists nodekit.artifact_versions ( + artifact_id text not null references nodekit.artifacts(artifact_id), + version integer not null check (version >= 1), + content jsonb not null, + content_hash text not null check (content_hash ~ '^[a-f0-9]{64}$'), + proposal_id text, + created_at timestamptz not null, + primary key (artifact_id, version) +); + +create table if not exists nodekit.proposals ( + proposal_id text primary key, + owner_id text not null, + artifact_id text not null references nodekit.artifacts(artifact_id), + base_version integer not null check (base_version >= 1), + patch jsonb not null, + rationale text not null default '', + status text not null check (status in ('pending', 'accepted', 'rejected', 'conflicted')), + created_at timestamptz not null, + decided_at timestamptz +); + +create table if not exists nodekit.approvals ( + approval_id text primary key, + owner_id text not null, + proposal_id text not null unique references nodekit.proposals(proposal_id), + decision text not null check (decision in ('accepted', 'rejected')), + comment text not null default '', + decided_at timestamptz not null +); + +create table if not exists nodekit.exceptions ( + exception_id text primary key, + owner_id text not null, + run_id text not null references nodekit.runs(run_id), + code text not null, + message text not null, + preserved_state jsonb not null, + status text not null check (status in ('open', 'resolved')), + resolution text, + raised_at timestamptz not null, + resolved_at timestamptz +); + +create table if not exists nodekit.receipts ( + receipt_id text primary key, + owner_id text not null, + run_id text not null unique references nodekit.runs(run_id), + receipt_hash text not null check (receipt_hash ~ '^[a-f0-9]{64}$'), + body jsonb not null, + generated_at timestamptz not null +); + +create table if not exists nodekit.events ( + event_id text primary key, + owner_id text not null, + aggregate_type text not null, + aggregate_id text not null, + sequence integer not null, + event_type text not null, + actor jsonb not null, + payload jsonb not null, + occurred_at timestamptz not null, + unique (aggregate_id, sequence) +); + +create index if not exists nodekit_cases_owner on nodekit.cases(owner_id, created_at); +create index if not exists nodekit_runs_owner_case on nodekit.runs(owner_id, case_id, created_at); +create index if not exists nodekit_artifacts_owner_run on nodekit.artifacts(owner_id, run_id, created_at); +create index if not exists nodekit_proposals_owner_artifact on nodekit.proposals(owner_id, artifact_id, created_at); +create index if not exists nodekit_exceptions_owner_run on nodekit.exceptions(owner_id, run_id, raised_at); +create index if not exists nodekit_events_owner_aggregate on nodekit.events(owner_id, aggregate_id, sequence); + +create or replace function nodekit.apply_proposal( + requested_owner_id text, + requested_proposal_id text, + requested_decision text, + requested_approval_id text, + requested_comment text, + next_content_hash text, + requested_at timestamptz +) returns table(status text, previous_version integer, next_version integer, approval_id text, reused boolean) +language plpgsql +as $$ +declare + proposal_row nodekit.proposals%rowtype; + artifact_row nodekit.artifacts%rowtype; + approval_row nodekit.approvals%rowtype; +begin + if requested_decision not in ('accepted', 'rejected') then + raise exception 'decision must be accepted or rejected'; + end if; + + select * into proposal_row from nodekit.proposals + where proposal_id = requested_proposal_id and owner_id = requested_owner_id for update; + if not found then raise exception 'proposal not found: %', requested_proposal_id; end if; + + select * into artifact_row from nodekit.artifacts + where artifact_id = proposal_row.artifact_id and owner_id = requested_owner_id for update; + if not found then raise exception 'artifact not found: %', proposal_row.artifact_id; end if; + + if proposal_row.status <> 'pending' then + select * into approval_row from nodekit.approvals + where proposal_id = requested_proposal_id and owner_id = requested_owner_id; + if not found or approval_row.decision <> requested_decision + or not (proposal_row.status = requested_decision or (proposal_row.status = 'conflicted' and requested_decision = 'accepted')) then + raise exception 'proposal is already %', proposal_row.status; + end if; + return query select proposal_row.status, artifact_row.canonical_version, artifact_row.canonical_version, + approval_row.approval_id, true; + return; + end if; + + insert into nodekit.approvals (approval_id, owner_id, proposal_id, decision, comment, decided_at) + values (requested_approval_id, requested_owner_id, requested_proposal_id, requested_decision, coalesce(requested_comment, ''), requested_at); + + if requested_decision = 'rejected' then + update nodekit.proposals set status = 'rejected', decided_at = requested_at + where proposal_id = requested_proposal_id; + return query select 'rejected'::text, artifact_row.canonical_version, artifact_row.canonical_version, + requested_approval_id, false; + return; + end if; + + if proposal_row.base_version <> artifact_row.canonical_version then + update nodekit.proposals set status = 'conflicted', decided_at = requested_at + where proposal_id = requested_proposal_id; + return query select 'conflicted'::text, artifact_row.canonical_version, null::integer, + requested_approval_id, false; + return; + end if; + + insert into nodekit.artifact_versions (artifact_id, version, content, content_hash, proposal_id, created_at) + values (artifact_row.artifact_id, artifact_row.canonical_version + 1, proposal_row.patch, next_content_hash, + requested_proposal_id, requested_at); + update nodekit.artifacts set canonical_version = canonical_version + 1, updated_at = requested_at + where artifact_id = artifact_row.artifact_id; + update nodekit.proposals set status = 'accepted', decided_at = requested_at + where proposal_id = requested_proposal_id; + return query select 'accepted'::text, artifact_row.canonical_version, artifact_row.canonical_version + 1, + requested_approval_id, false; +end; +$$; diff --git a/adapters/postgres/README.md b/adapters/postgres/README.md new file mode 100644 index 0000000..b18878e --- /dev/null +++ b/adapters/postgres/README.md @@ -0,0 +1,7 @@ +# PostgreSQL adapter contract + +This directory contains the provider-native persistence contract for a future `@nodekit/adapter-postgres` package. The checked-in SQL establishes portable NodeKit IDs, append-only events, canonical artifact versions, pending proposals, and a conditional proposal-apply function. + +`apply_proposal` is the critical semantic boundary: it locks the artifact row, compares the proposal base version to the canonical version, records a conflict when stale, and advances exactly one version when current. Applications must authorize the principal before calling it. + +This is a migration and conformance foundation, not evidence of a deployed PostgreSQL adapter. diff --git a/adapters/supabase/001_profile.sql b/adapters/supabase/001_profile.sql new file mode 100644 index 0000000..cffb1af --- /dev/null +++ b/adapters/supabase/001_profile.sql @@ -0,0 +1,72 @@ +-- Apply adapters/postgres/001_caseflow.sql first. NodeKit keeps owner IDs as text so +-- portable exports do not depend on provider-native primary-key types. Supabase +-- wrappers must pass auth.uid()::text as the owner authority. + +alter table nodekit.cases enable row level security; +alter table nodekit.runs enable row level security; +alter table nodekit.artifacts enable row level security; +alter table nodekit.artifact_versions enable row level security; +alter table nodekit.proposals enable row level security; +alter table nodekit.approvals enable row level security; +alter table nodekit.exceptions enable row level security; +alter table nodekit.receipts enable row level security; +alter table nodekit.events enable row level security; + +drop policy if exists nodekit_case_owner on nodekit.cases; +create policy nodekit_case_owner on nodekit.cases + using (owner_id = auth.uid()::text) with check (owner_id = auth.uid()::text); + +drop policy if exists nodekit_run_owner on nodekit.runs; +create policy nodekit_run_owner on nodekit.runs + using (owner_id = auth.uid()::text) with check (owner_id = auth.uid()::text); + +drop policy if exists nodekit_artifact_owner on nodekit.artifacts; +create policy nodekit_artifact_owner on nodekit.artifacts + using (owner_id = auth.uid()::text) with check (owner_id = auth.uid()::text); + +drop policy if exists nodekit_artifact_version_owner on nodekit.artifact_versions; +create policy nodekit_artifact_version_owner on nodekit.artifact_versions + using (exists ( + select 1 from nodekit.artifacts a + where a.artifact_id = artifact_versions.artifact_id and a.owner_id = auth.uid()::text + )) + with check (exists ( + select 1 from nodekit.artifacts a + where a.artifact_id = artifact_versions.artifact_id and a.owner_id = auth.uid()::text + )); + +drop policy if exists nodekit_proposal_owner on nodekit.proposals; +create policy nodekit_proposal_owner on nodekit.proposals + using (owner_id = auth.uid()::text) with check (owner_id = auth.uid()::text); + +drop policy if exists nodekit_approval_owner on nodekit.approvals; +create policy nodekit_approval_owner on nodekit.approvals + using (owner_id = auth.uid()::text) with check (owner_id = auth.uid()::text); + +drop policy if exists nodekit_exception_owner on nodekit.exceptions; +create policy nodekit_exception_owner on nodekit.exceptions + using (owner_id = auth.uid()::text) with check (owner_id = auth.uid()::text); + +drop policy if exists nodekit_receipt_owner on nodekit.receipts; +create policy nodekit_receipt_owner on nodekit.receipts + using (owner_id = auth.uid()::text) with check (owner_id = auth.uid()::text); + +drop policy if exists nodekit_event_owner on nodekit.events; +create policy nodekit_event_owner on nodekit.events + using (owner_id = auth.uid()::text) with check (owner_id = auth.uid()::text); + +-- Realtime publication is provider-native and intentionally explicit. The DO +-- blocks make repeated local/profile setup idempotent. +do $$ +declare table_name text; +begin + foreach table_name in array array['cases', 'runs', 'artifacts', 'proposals', 'approvals', 'exceptions', 'receipts'] + loop + if not exists ( + select 1 from pg_publication_tables + where pubname = 'supabase_realtime' and schemaname = 'nodekit' and tablename = table_name + ) then + execute format('alter publication supabase_realtime add table nodekit.%I', table_name); + end if; + end loop; +end $$; diff --git a/adapters/supabase/README.md b/adapters/supabase/README.md new file mode 100644 index 0000000..2335d13 --- /dev/null +++ b/adapters/supabase/README.md @@ -0,0 +1,5 @@ +# Supabase managed profile + +Supabase implements NodeKit's PostgreSQL semantics with Auth identity, Row Level Security, Storage, Realtime Broadcast/Presence, Queues, and Cron. `001_profile.sql` refuses anonymous cross-tenant reads and writes; application RPC wrappers must set `owner_id` from the authenticated principal rather than accept it as untrusted client authority. + +This profile declares the intended managed mapping. Live Auth, Storage, Realtime, Queue, and Cron conformance still requires a provisioned Supabase project and deployment-bound receipts. diff --git a/docs/BACKEND_PORTABILITY.md b/docs/BACKEND_PORTABILITY.md new file mode 100644 index 0000000..0b8b5c1 --- /dev/null +++ b/docs/BACKEND_PORTABILITY.md @@ -0,0 +1,35 @@ +# NodeKit backend portability + +NodeKit is Convex-first, not Convex-locked. The application experience and lifecycle remain stable while one authoritative transactional backend implements them. + +## Portable semantics + +```text +Case -> Run -> Stage -> Artifact -> Proposal -> Approval -> Receipt +``` + +Every adapter must preserve: + +- NodeKit-owned public IDs; +- proposal-before-mutation; +- canonical artifact versions; +- stale-proposal conflict instead of overwrite; +- explicit next-action ownership; +- safe exception recovery; +- one terminal run state; +- portable, content-addressed receipts; +- a normalized event envelope. + +Mechanics remain provider-native. Memory uses in-process compare-and-swap. Convex should use transactional mutations and reactive queries. PostgreSQL should use transactions and conditional updates. Supabase should build on PostgreSQL plus its managed Auth, Storage, Realtime, queue, and function capabilities. + +`runCaseflowConformance()` is the executable behavioral boundary. Provider-specific optimizations are additive and cannot weaken it. + +## Capability negotiation + +Adapters disclose transactions, optimistic concurrency, subscription mode, durable-job mode, file storage, presence, and provider identity. NodeKit uses a native implementation, a documented fallback, or fails clearly. It does not silently claim equivalent behavior. + +Shared UI consumes normalized NodeKit view models. It must not import Convex hooks, Supabase clients, or database row/document types. + +## Authority + +Each deployment has one authoritative backend for cases, runs, canonical artifact versions, proposals, approvals, exceptions, and receipts. Other systems may be tools, sources, indexes, analytics stores, or migration targets, but NodeKit does not introduce active-active writes across authorities. diff --git a/docs/CONVEX_CASEFLOW_EXTRACTION.md b/docs/CONVEX_CASEFLOW_EXTRACTION.md new file mode 100644 index 0000000..269fc5f --- /dev/null +++ b/docs/CONVEX_CASEFLOW_EXTRACTION.md @@ -0,0 +1,34 @@ +# Convex Caseflow extraction gate + +NodeKit Caseflow is the intended bounded Convex component, not the whole NodeKit factory. + +The component will own isolated lifecycle state for cases, runs, stages, artifacts, artifact versions, proposals, approvals, exceptions, receipts, timeline events, and external references. It will enforce proposal-before-mutation, base-version checks, explicit terminal states, next-action ownership, idempotent lifecycle operations, and immutable receipt records. + +It will not own application authentication, organization roles, model providers, prompts, RAG, general chat, or arbitrary host artifacts. Authenticated application functions must validate the principal and pass scoped identifiers into the component because component functions do not have `ctx.auth`. All public component functions require argument and return validators. + +The host application composes existing Convex Agent, Workflow, Workpool, persistent streaming, RAG, Presence, rate limiting, and file components as required. Caseflow records typed external references and lifecycle events; it does not rebuild those capabilities. + +## Extraction gate + +Do not publish the component until three materially different applications use the portable Caseflow contract and the repeated Convex implementation is visible: + +1. NodeRoom or NodeSheet artifact collaboration; +2. NodeSlide presentation production; +3. NodeVideo or another long-running artifact workflow. + +Before extraction: + +- all three pass `runCaseflowConformance()` semantics; +- their authenticated wrappers demonstrate owner scoping; +- stale proposals and retry/idempotency are exercised; +- component-owned versus app-owned data is documented; +- `convex-test` covers the packaged API; +- the example app exercises the bundled package, not source-only shortcuts. + +Official current references: + +- https://docs.convex.dev/components/authoring +- https://docs.convex.dev/components/using +- https://docs.convex.dev/agents/workflows + +This document is an extraction contract, not evidence that a Convex component has already been published or submitted. diff --git a/docs/CONVEX_CONSUMER_AUDIT.md b/docs/CONVEX_CONSUMER_AUDIT.md new file mode 100644 index 0000000..33e4312 --- /dev/null +++ b/docs/CONVEX_CONSUMER_AUDIT.md @@ -0,0 +1,170 @@ +# Convex consumer audit + +Audit date: 2026-07-22 + +This audit answers one narrow submission question: do three materially different Convex applications consume the portable NodeKit Caseflow contract through authenticated, owner-scoped wrappers and pass the packaged conformance behavior? + +## Verdict + +**The submission-grade consumer count remains 0/3.** + +All three applications already contain useful Caseflow-like behavior. None of the three locally recorded `origin/main` revisions imports `@homenshum/nodekit`, exposes a Convex adapter for the portable Caseflow API, and runs `runCaseflowConformance()` against that adapter. Existing domain tests are valuable prerequisite evidence, but they are not package-consumption evidence. + +Do not extract, publish, or submit NodeKit Caseflow yet. + +The audit used the local `origin/main` refs listed below without fetching or mutating a consumer repository. A release audit must fetch and re-pin all three refs before accepting them as current upstream state. + +## Counting rule + +A consumer counts only when one reviewed revision proves all of the following: + +1. It consumes the packed `@homenshum/nodekit` boundary rather than a copied source implementation. +2. A Convex adapter maps the application's real lifecycle to `Case`, `Run`, `Stage`, `Artifact`, `Proposal`, `Exception`, and `Receipt` semantics. +3. An application-owned wrapper authenticates the caller and resolves an owner or workspace before invoking lifecycle operations. +4. Stale writes fail closed; idempotent retry/reuse, exception recovery, explicit next-action ownership, and content-addressed completion receipts are exercised. +5. The packaged `runCaseflowConformance()` behavior runs against the Convex adapter with `convex-test`. +6. Component-owned and application-owned data are documented. + +Passing similar domain tests without the shared package does not demonstrate reuse. Importing the package without authenticated, real-backend behavior does not demonstrate production adoption. + +## Audited revisions + +| Lane | Repository | Local `origin/main` revision | Working-copy note | +|---|---|---|---| +| NodeRoom / NodeSheet artifact collaboration | `D:\VSCode Projects\cafecorner_nodebench\nodebench_ai4\noderoom` | `c3143fcbe8dcbc4d4f3b31392854cc546fd9f1c2` - `docs: publish Casca FDE NodeRoom proof package (#240)` | Working branch `codex/nodekit-contract-alignment` was 1 ahead and 27 behind; audit used `origin/main`. NodeRoom is the finance/spreadsheet consumer in this workspace; no separate canonical NodeSheet repository was found. | +| NodeSlide presentation production | `D:\VSCode Projects\cafecorner_nodebench\nodebench_ai4\NodeSlide` | `4ac27f7fecb7c98229aca200e8e840c65e7dfb4f` - `ci(packages): prove packed consumer boundary (#9)` | Working branch `codex/injectable-core` was 1 ahead and 14 behind; audit used `origin/main`. | +| NodeVideo long-running artifact workflow | `D:\VSCode Projects\cafecorner_nodebench\nodebench_ai4\NodeVideo` | `7a741cb606ac64a05bb1eb5ed57429082b97ed5e` - `Export accepted edits as local MP4 (#31)` | Working tree and `origin/main` pointed at the same commit. | + +## Evidence matrix + +| Requirement | NodeRoom / NodeSheet | NodeSlide | NodeVideo | +|---|---|---|---| +| Packed `@homenshum/nodekit` consumption | **No** | **No** | **No** | +| Existing Caseflow-like lifecycle | Strong room/job/artifact/proposal lifecycle | Strong portable deck repository, proposal, version, and receipt contracts | Strong case/job/stage/artifact/proposal/receipt lifecycle | +| Authenticated owner/workspace wrapper | **Strong prerequisite:** `ctx.auth` plus verified room membership in production | **Gap:** opaque owner bearer capability, not `ctx.auth` identity/organization ownership | **Gap:** plane-wide environment bearer tokens and caller-supplied `projectId` | +| Stale-write/CAS behavior | Yes, real Convex element and artifact CAS | Yes in the memory repository and deck patch engine; not proven as shared Convex Caseflow | Partial: recipe-version validation and lease fencing, but no portable artifact-version conformance | +| Idempotency/retry | Yes, real Convex claim-or-reuse and journal retry coverage | Yes in memory repository resolution/apply behavior; no real Convex adapter proof | Yes in schema/workflow primitives; only helper-level durability tests found | +| Exception recovery/next owner | Domain recovery exists; portable Caseflow exception semantics not mapped | Not mapped to portable Caseflow | Stage retry/cancel exists; portable exception and next-owner semantics not mapped | +| Receipts | Extensive domain proof and audit receipts | Portable NodeSlide receipt envelope in memory repository | Freeze and evaluation receipts plus job events | +| Packaged Caseflow conformance on Convex | **No** | **No** | **No** | +| Submission-grade consumer | **0** | **0** | **0** | + +## Consumer findings + +### 1. NodeRoom / NodeSheet + +NodeRoom is the most production-shaped authorization and collaboration consumer. + +Existing evidence: + +- `convex/lib.ts` implements `requireActorProof()`. In production identity mode it requires `ctx.auth.getUserIdentity()`, matches the authenticated subject to the room member, and rejects revoked or mismatched members. +- `convex/artifacts.ts` implements proposal-before-apply, per-element and artifact-version CAS, conflict-as-data, final validation on approval, and receipt/audit updates. +- `convex/agentRuns.ts` and `convex/agentJobs.ts` contain idempotency-key claim/reuse and durable execution machinery. +- `tests/deckObjectCas.test.ts` exercises two users, stale overwrite rejection, agent proposal approval, invalid proposal rejection, history, and restoration against `convex-test`. +- `tests/idempotencyRuntime.test.ts` exercises atomic claim-or-reuse and retry/accounting behavior against the real Convex functions. +- `tests/freshRoomProofReceipts.test.ts` validates structured screenshot, trace, export, reopen, scorer, and model-accounting requirements for domain proof receipts. +- `tests/nodeSlideHostAuthorization.test.ts` proves verified membership, role-scoped read/propose/apply rules, resource scoping, and credential-free authorization evidence for the mounted NodeSlide surface. + +Why it does not count: + +- `package.json`, the lockfiles, `nodekit.yaml`, and application sources do not consume `@homenshum/nodekit` or call `runCaseflowConformance()`. +- The room lifecycle has not been adapted to the portable Caseflow method surface. +- Domain traces and proof receipts have not been normalized into a content-addressed Caseflow completion receipt. +- Component-owned versus NodeRoom-owned tables are not documented as an extraction boundary. + +Smallest adoption patch: + +1. Add the packed `@homenshum/nodekit` artifact to the test/build dependency boundary. +2. Add `src/integrations/nodekit/caseflowAdapter.ts` that maps existing room, job, artifact, proposal, exception/recovery, and receipt operations to the portable runtime without replacing `RoomTools` or NodeRoom's CAS spine. +3. Add application-owned Convex wrappers that call `requireActorProof()` first and pass only resolved `roomId`, member ID, role, and scoped resource IDs into the adapter. +4. Add `tests/nodekitCaseflowConformance.test.ts` using `convex-test`, the packaged conformance runner, two authenticated members, stale proposal rejection, retry/reload, and receipt hash verification. +5. Document NodeRoom-owned membership, domain artifacts, and export data versus the future Caseflow component's isolated lifecycle records. + +### 2. NodeSlide + +NodeSlide is closest to an injectable domain package, but the proven portable repository is currently memory-backed rather than a NodeKit Convex consumer. + +Existing evidence: + +- `packages/backend/src/index.ts` defines a backend-neutral `NodeSlideRepository`, normalized `NodeSlidePrincipal`, versioned patch commands, proposal resolution, asset storage, telemetry, and a portable receipt envelope. +- `packages/testing/src/conformance.ts` proves that proposal creation does not mutate the canonical deck and acceptance advances the authoritative version exactly once. +- `packages/testing/src/memoryRepository.ts` implements idempotent apply/resolve behavior, stale-proposal conflict handling, versions, authorization callbacks, and receipts. +- `packages/testing/src/memoryRepository.test.ts` exercises the conformance flow, two proposals racing from the same base version, stale receipts, and idempotent results. +- `convex/lib/nodeslideAccess.ts` correctly treats a deck ID as a locator rather than authority and gates operations with an unguessable owner capability. + +Why it does not count: + +- Its package set is `@nodeslide/contracts`, `@nodeslide/backend`, and `@nodeslide/testing`; none imports `@homenshum/nodekit`. +- No Convex-backed implementation of `NodeSlideRepository` exists under the extracted package boundary. +- `requireOwnerAccess()` checks a raw bearer capability stored with the deck. No application-owned `ctx.auth` wrapper binds the principal to an authenticated user or organization. +- The NodeSlide repository conformance suite is domain-specific and is not the packaged NodeKit Caseflow conformance suite. + +Smallest adoption patch: + +1. Keep the existing deck repository contract and add a thin `packages/convex-caseflow` adapter rather than moving slide logic into NodeKit. +2. Add an application-owned Convex wrapper that derives a `NodeSlidePrincipal` from `ctx.auth`, verifies deck/workspace ownership, and only then calls the adapter. Retain owner capabilities only as an explicitly bounded preview/bootstrap mechanism, not as the submission-grade owner identity. +3. Map deck to artifact, generation attempt to run, deck patch to proposal, validation failure to exception, and `NodeSlideReceipt` to a Caseflow receipt reference. +4. Run packaged `runCaseflowConformance()` through `convex-test`, then add authenticated cross-owner denial, same-base proposal race, idempotent repeat decision, reload/version, and receipt-hash tests. +5. Document which deck/media rows remain application-owned and which lifecycle rows would be component-owned. + +### 3. NodeVideo + +NodeVideo is the closest structural candidate for the long-running workflow lane, but its authority boundary and tests are not yet submission-grade. + +Existing evidence: + +- `convex/schema.ts` owns `sourceOnlyCases`, jobs, stages, events, artifacts, proposals, freeze receipts, and evaluation receipts. +- `convex/workflow.ts` implements idempotent case/job creation, stage leases, retries, cancellation, proposal approval binding, frozen generation inputs, and evaluation unsealing. +- `convex/lib/durability.ts` supplies canonical JSON, input-digest reuse checks, lease fencing, and digest validation. +- `tests/unit/durability.test.ts` tests canonical hashes, idempotent-input conflicts, stale lease fencing, monotonic event sequence numbers, and approval binding. +- `src/lib/nodeVideoWorkflowCandidate.test.ts` rejects stale recipe versions and incomplete or mismatched candidates and emits an application-validation/CAS-review receipt. + +Why it does not count: + +- No source or package manifest imports `@homenshum/nodekit` or runs packaged Caseflow conformance. +- Owner HTTP routes in `convex/http.ts` authorize one environment token (`NODEVIDEO_OWNER_TOKEN`) and then call internal functions. They do not resolve a user, organization, workspace, or project owner through `ctx.auth`. +- `projectId` is caller-supplied lifecycle scope; the schema does not bind it to an authenticated owner. +- No `convex-test` coverage was found for the real workflow functions, so helper tests do not prove transactional behavior of the packaged API. +- Stage retry/cancel and freeze/evaluation receipts are not mapped to portable exception recovery, explicit next-action ownership, and the Caseflow completion receipt. + +Smallest adoption patch: + +1. Add the packed `@homenshum/nodekit` boundary and a NodeVideo Caseflow adapter over the existing tables; do not replace the working stage engine. +2. Add an application-owned owner API that authenticates a user, resolves an owned project/workspace, and supplies that server-resolved scope. Keep separate worker/evaluator service credentials for machine planes. +3. Map case/job/stage/artifact/proposal/retry/freeze receipts to the portable runtime and emit a content-addressed completion receipt referencing NodeVideo's domain receipts. +4. Add `convex-test` coverage that invokes the packaged conformance runner and separately proves cross-owner denial, duplicate start reuse, stale proposal failure, lease-recovery resume, reload, and receipt integrity. +5. Document NodeVideo media, hidden evaluator data, and worker artifacts as application-owned; only shared lifecycle state is a Caseflow extraction candidate. + +## EvoGraph-R1 integration consequence + +EvoGraph-R1 is not absent from NodeKit. The adapted pattern already exists in: + +- `src/lib/knowledge-evolution.mjs` and `docs/KNOWLEDGE_EVOLUTION.md`: a persistent, versioned knowledge environment with typed gaps, evidence-grounded graph patches, explicit approval, stale-version rejection, replay, and evolution receipts; +- `src/lib/evolution-ledger.mjs` and `docs/EVOLUTION_LEDGER.md`: human-reviewed causal memory for product, architecture, and harness evolution; +- the `nodekit graph ...` and `nodekit evolution ...` CLI families. + +The missing integration is consumer evidence flow. Each adoption patch should produce immutable evidence from its packaged Convex conformance run, record a human-reviewed evolution event, and propose a Knowledge Evolution graph patch linking: + +```text +consumer revision ++ packed NodeKit version and config hash ++ authenticated wrapper ++ Caseflow conformance receipt ++ stale/idempotency/recovery evidence ++ browser/export proof where applicable +``` + +The graph must not grant adoption status automatically. `nodekit evolution verify` and human review remain the authority boundary, and `nodekit evolution sync-graph` must continue to create a proposal rather than mutating canonical knowledge directly. + +## Ordered closure plan + +1. **NodeVideo first:** its persisted lifecycle already resembles Caseflow. Close authenticated project ownership and real `convex-test` conformance. +2. **NodeRoom / NodeSheet second:** adapt the mature auth, CAS, idempotency, and receipt spine without replacing domain collaboration logic. +3. **NodeSlide third:** retain the clean injectable repository packages, add a real Convex adapter, and replace owner-capability-only authority with an authenticated host wrapper for the certification lane. +4. For each accepted consumer revision, ingest its immutable proof into the Evolution Ledger and propose the associated Knowledge Evolution update. +5. Re-audit all three fetched upstream commits. Count a lane only after its package import, authenticated wrapper, packaged conformance result, and proof artifacts are present in the same reviewed revision. +6. Extract the smallest repeated Convex lifecycle kernel only after the count reaches 3/3. Then run the component's own `convex-test`, packed example, timer, screenshot, fresh-coding-agent, and production proof gates before submission. + +## Current blocker statement + +The blockers are not missing ideas. The shared Caseflow contract, Knowledge Evolution plane, Evolution Ledger, and substantial domain behaviors exist. The blockers are **cross-repository adoption evidence and authenticated backend convergence**. Until those are closed, claiming three consumers or submitting a reusable Convex component would overstate what the code proves. diff --git a/docs/EASE_PROOF.md b/docs/EASE_PROOF.md new file mode 100644 index 0000000..33cd74c --- /dev/null +++ b/docs/EASE_PROOF.md @@ -0,0 +1,60 @@ +# NodeKit EaseProof v1 + +NodeKit is not submission-ready merely because its engineering foundation passes CI. Submission remains locked until strangers can use it without architectural coaching. + +## The three independent subjects + +1. Developer ease: an empty directory reaches a rendered, reload-safe receipt with no key, manual source edit, or setup decision. +2. Coding-agent ease: a fresh agent receives only the generated repository and one short product goal, then builds and proves a vertical slice without routine steering. +3. End-user ease: a fresh person completes the visible job, understands the outcome, and can locate the artifact, unresolved issues, and receipt. + +Passing one subject never implies another. + +## Local evidence commands + +```bash +npm run acceptance:factory +npm run acceptance:agent -- --task=volunteer-onboarding +``` + +The factory command produces phase-level timers, 36 candidate-bound PNGs across six viewports and two themes, screenshot sidecars, an archived generated candidate, and a fail-closed manifest under `proof/ease/latest/`. + +The coding-agent command creates a new repository and a fresh ephemeral Codex session. It records the exact prompt, JSONL session, command hashes, token-bearing events, zero-intervention ledger, candidate diff/archive, post-agent gates, screenshots, and verdict. A process exit is insufficient: the run fails unless the agent makes substantive non-proof changes and its final report is not blocked. One successful run is a pilot, not repeatability certification. + +## Human protocol + +Recruit at least five people who did not build NodeKit. Give only: + +> Use this app to complete the job shown on screen. + +For each participant record UTC start/end, first meaningful action, completion, wrong turns, help requests, unclear terms, backtracking, whether they can explain what happened, whether they can find the canonical artifact and unresolved issues, and a 1–7 Single Ease Question score. Do not coach. + +The human gate requires at least 4/5 unassisted completions, median first meaningful action under 30 seconds, median neutral journey under three minutes, median SEQ at least 6/7, and zero P0/P1 usability failures. + +Use `proof/ease/fresh-users.template.json` as the append-only recording shape. Identifying information is intentionally excluded. + +## Required repeatability + +- Developer: five cold and five warm trials for Windows/npm, Windows/pnpm, Ubuntu/npm, Ubuntu/pnpm, macOS/npm, and macOS/pnpm before percentile claims. +- Coding agent: three fresh Codex sessions for each of the three held-out tasks, plus one fresh Claude Code session and one lower-cost-agent session per task. Report every attempt. +- End user: five uncoached people. +- Convex: NodeRoom, NodeSlide, and NodeVideo must materially adopt the same lifecycle and pass reactive two-session, reload, preview, and fresh-user journeys. + +## Submission formula + +```text +SUBMISSION_READY = + blank_factory_cross_platform_pass + AND cold_setup_timer_pass + AND fresh_agent_heldout_pass + AND responsive_ui_matrix_pass + AND accessibility_pass + AND fresh_human_usability_pass + AND convex_real_consumer_count >= 3 + AND fresh_preview_deployment_pass + AND proofloop_ease_verification_pass + AND unresolved_P0_count == 0 + AND unresolved_P1_count == 0 +``` + +Until every term is supported by evidence, the only valid public status is `EASE_NOT_CERTIFIED`, `BLOCKED_HUMAN_TEST`, or `BLOCKED_EXTERNAL`. Both implementation PRs stay draft; no Convex Component is extracted or submitted. diff --git a/docs/EASE_SUBMISSION_READINESS.md b/docs/EASE_SUBMISSION_READINESS.md new file mode 100644 index 0000000..d210d8e --- /dev/null +++ b/docs/EASE_SUBMISSION_READINESS.md @@ -0,0 +1,158 @@ +# NodeKit Ease submission readiness + +This document governs **E3 Ease certification and C3 Convex submission**, not E0 engineering merge +readiness. See `RELEASE_LADDER.md`. Implemented code and passing unit tests may be merged while +product-evidence and submission claims remain fail-closed. + +Status: **DO NOT SUBMIT** + +Baseline revision audited before the exact-candidate hardening in this update: +`5b9c4d73c286020fe7b7c52d208d7e0cbfeef626`. + +No complete Ease certification bundle is bound to that revision. Passing evidence from +`e398398d7f1dd4ff0b65409d2c8da971e83bc488` or +`0cc282c68c316068447956f5c6729f98ba3435f8` remains useful regression history, but it does not +certify `5b9c4d7` or any later candidate. + +## Evidence identity rule + +Every certification claim must name one immutable NodeKit commit and source hash. A passing receipt +may not be silently carried forward after source changes, even when those changes are additive. + +The current local evidence directory contains artifacts from different revisions: + +| Evidence | Bound revision | Honest interpretation | +| --------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------- | ----------------------------------------------------------------------------------------------- | +| `proof/factory-acceptance.json` and `proof/ease/latest/manifest.json` | `0cc282c` | Passing factory/browser run for that earlier revision only. | +| `proof/ease/developer-timing-runs-0cc282c*.json` | `0cc282c` | Passing 60-run cold/warm matrix for that earlier revision only. | +| `proof/ease/developer-timing-runs-e398398*.json` | `e398398` | Passing 60-run cold/warm matrix for that earlier revision only. | +| `proof/ease/fresh-agent-verdict-e398398.json` | `e398398` | All three required held-out coding-agent tasks passed on one exact earlier identity. | +| `proof/package-install-verdict-e398398.json` | `e398398` | Fresh-consumer tarball install, create, compile, check, demo, and eval passed for that package. | +| `proof/local-closure-e398398.json` | `e398398` | Records local/code-owned closure and explicitly leaves external evidence open. | +| `proof/ease/developer-timing-verdict.json`, `proof/ease/fresh-agent-verdict.json`, and `proof/package-install-verdict.json` | aliases currently resolving to `e398398` evidence | Convenience filenames, not current-revision proof. | +| `proof/ease/latest/proofloop-receipt.json` | absent in the current working tree | Independent Ease integrity verification is not currently evidenced. | + +The `0cc282c` and `e398398` matrices each contain five cold and five warm runs for npm and pnpm on +Windows, Ubuntu, and macOS. They support an earlier-revision stability claim. They do not support a +`5b9c4d7` onboarding claim, and five samples per cell still do not justify a per-cell p95. + +## Implemented mechanics versus certification + +The current source includes deterministic machinery for: + +- domain-blank application scaffolding and package creation; +- exact server-process binding in browser certification; +- canonical responsive UI-state screenshots and sidecars; +- cold/warm timing aggregation that requires all 60 trials; +- fresh coding-agent held-out aggregation; +- package-install verification; +- fail-closed submission-manifest evaluation; +- P0-P3 Model Intelligence and Harness Gym mechanics; +- governed Knowledge Evolution and frontend-topology evolution. + +Those are implementation facts. They are not equivalent to: + +- a real project-scoped model capability card; +- a completed application gym; +- an authenticated Convex consumer; +- a real-person usability result; +- a deployed fresh-user production proof; or +- a current-revision Ease certificate. + +## Current-revision certification gate + +All items below must be repeated on one clean immutable candidate that contains the Knowledge +Evolution and frontend-specialist changes now present after `e398398`. + +- [ ] Run factory generation, fresh dependency installation, compile, check, deterministic demo, + evaluation, and receipt generation on the exact candidate. +- [ ] Complete the real browser journey and retain exact process identity, trace, video, + screenshots, sidecars, accessibility results, network health, reload behavior, and export proof. +- [ ] Run five isolated cold and five warm trials for all six OS/package-manager lanes and retain + the 60 raw receipts plus the aggregate verdict. +- [ ] Re-run research map, volunteer onboarding, and launch presentation as writable isolated + coding-agent tasks with zero human reprompts and substantive non-proof changes. +- [ ] Pack the candidate and pass the fresh-consumer install/create/compile/check/demo/eval path. +- [ ] Run independent ProofLoop integrity verification over the final candidate archive and every + referenced artifact. +- [ ] Bind every receipt to the same commit, source hash, package tarball hash, and generated-app + identity. +- [ ] Publish no per-lane p95 unless that lane has at least 20 cold and 20 warm samples. + +The earlier passing matrices and held-out runs reduce regression risk and prove the harness can +work. They do not close this gate for the current revision. + +## External submission blockers + +### 1. Five-person fresh-user study + +- [ ] Five participants receive only: "Use this app to complete the job shown on screen." +- [ ] At least four finish unassisted. +- [ ] Median first meaningful action is at most 30 seconds. +- [ ] Median neutral journey is at most 180 seconds. +- [ ] Median Single Ease Question score is at least 6/7. +- [ ] No P0/P1 usability failures remain. +- [ ] Consent, timestamps, recordings or exact screenshots, interventions, and participant-level + receipts are retained. + +Template: `proof/ease/fresh-users.template.json`. This gate requires real people and cannot be +replaced by an agent simulation. Evaluate the completed study with +`npm run ease:evaluate-humans -- proof/ease/fresh-users.json`. + +### 2. Three authenticated Convex-backed consumers + +- [ ] NodeRoom or NodeSheet passes portable Caseflow conformance through authenticated, + owner-scoped Convex wrappers. +- [ ] NodeSlide presentation production passes the same contract. +- [ ] NodeVideo or another long-running artifact workflow passes the same contract. +- [ ] Each consumer exercises stale proposals, idempotent retries, exception recovery, receipt + integrity, and component/application ownership boundaries. +- [ ] Repeated Caseflow behavior is covered with `convex-test` before extraction. + +Repository contract checks, filesystem adapters, local durable candidates, or unauthenticated +owner-capability locators do not count as authenticated Convex consumers. No component extraction +is authorized until all three real consumers exist. + +### 3. Live backend portability + +- [ ] Run the portable Caseflow suite against a live PostgreSQL deployment. +- [ ] Run it against a live Supabase project with Auth, RLS, Storage, and Realtime enabled. +- [ ] Preserve provider-specific receipts and verify export/import compatibility. + +Checked-in migrations and deterministic adapter tests prove implementation shape, not managed +service behavior. + +### 4. Real Model Intelligence evidence + +- [ ] Normalize live exact-model observations into at least one project-scoped provisional + capability card. +- [ ] Complete a real application gym using protected tasks and independent evaluation. +- [ ] Keep routing provisional until a fresh-agent canary and NodeProof receipt pass. + +P1-P3 mechanics are implemented and tested with fixtures. No live model capability or routing +claim is currently authorized. + +### 5. Shareable preview and production-first-user proof + +- [ ] Deploy the exact tested commit to isolated preview frontend and backend environments. +- [ ] Use a fresh browser identity and real fixture bytes through the rendered UI. +- [ ] Download, reopen, and independently score the exported artifact. +- [ ] Preserve deployment identity, screenshots, health, cleanup, and proof receipt. + +This requires credentials and explicit authorization. Local browser evidence is not a deployment. + +## Submission sequence after every blocker closes + +1. Freeze one immutable candidate and discard or archive stale convenience aliases. +2. Re-run the current-revision local certification gate. +3. Bind the five-person, three-consumer, portability, model-evidence, and production-preview + receipts into one submission manifest. +4. Re-run independent ProofLoop verification over the complete evidence set. +5. Extract only the repeated Convex Caseflow kernel; keep the NodeKit CLI, React experience kit, + Knowledge Evolution plane, Harness Gym, and factory outside the component. +6. Run `convex-test`, package-install example tests, and the fresh-user preview journey. +7. Publish the npm package only with explicit authorization. +8. Submit only when `npm run submission:evaluate` returns `submissionReady: true` for the frozen + candidate and explicit publication approval is present. + +Until then, the required verdict is `EASE_NOT_CERTIFIED`. diff --git a/docs/EVOLUTION_LEDGER.md b/docs/EVOLUTION_LEDGER.md new file mode 100644 index 0000000..5c56cf7 --- /dev/null +++ b/docs/EVOLUTION_LEDGER.md @@ -0,0 +1,47 @@ +# NodeKit Evolution Ledger + +The Evolution Ledger records why material NodeKit guarantees exist. It is an institutional reasoning system, not a feature changelog. + +```text +Observed limitation +-> evidence +-> assumption disproven or scope-limited +-> architectural response +-> invariant +-> verifier +-> adoption +-> later validation, drift, supersession, or invalidation +``` + +## Canonical records + +- `nodekit.evolution-event/v1` +- `nodekit.assumption/v1` +- `nodekit.invariant-claim/v1` +- `nodekit.evolution-evidence/v1` +- `nodekit.evolution-adoption/v1` + +Canonical JSON lives under `evolution/`. Markdown timelines and adoption maps are generated projections. Events are separated into product, architecture, and harness tracks while sharing evidence and causal links. + +## Authority and verification + +Agents may draft an interpretation. Canonical events require a named human reviewer. Records are immutable; later changes supersede rather than overwrite them. + +`nodekit evolution verify` fails closed on missing commits, missing or hash-drifted evidence, unverified invariants, unsupported adoption claims, circular supersession, incomplete model identity, incomplete screenshot or benchmark identity, and possible secrets. + +`nodekit evolution sync-graph` converts verified records into a Knowledge Evolution patch. It never mutates the canonical graph directly; normal validation and approval remain mandatory. + +## Commands + +```bash +nodekit evolution init +nodekit evolution draft --id --track architecture --category runtime --challenge --resolution --reviewed-by +nodekit evolution record --file evolution/drafts/.json +nodekit evolution verify +nodekit evolution query --invariant +nodekit evolution diff --from --to +nodekit evolution build-docs +nodekit evolution sync-graph +``` + +Material changes include user workflow, public contracts, architectural ownership, security or authority, proof requirements, model routing, harness behavior, benchmark conclusions, and downstream guarantees. Routine formatting and dependency churn remain ordinary changelog entries. diff --git a/docs/FINAL_CANDIDATE_EVIDENCE_AUDIT.md b/docs/FINAL_CANDIDATE_EVIDENCE_AUDIT.md new file mode 100644 index 0000000..1b78eb1 --- /dev/null +++ b/docs/FINAL_CANDIDATE_EVIDENCE_AUDIT.md @@ -0,0 +1,188 @@ +# NodeKit final-candidate evidence audit + +Audit snapshot: 2026-07-22, branch `codex/nodekit-figured-out-base`. + +## Verdict + +**Do not submit or publish.** The repository contains useful historical proof, but it does not +contain one complete, internally consistent evidence bundle for the current NodeKit candidate. +The correct final-candidate status is `EASE_NOT_CERTIFIED`. + +The audited Git `HEAD` was: + +```text +5b9c4d73c286020fe7b7c52d208d7e0cbfeef626 +``` + +The working tree was not a final candidate: it had 409 changed/untracked paths, including 391 under +`proof/`. The distribution source hash observed before this report was added was: + +```text +f7954a3402bd68ce6c7a2c6b55b1c91e7508a9d3b58fedc6b638d2f8b85ad603 +``` + +That hash is only an audit snapshot. It must not be used as the final identity because the working +tree is still changing and this report itself may be included in the packaged distribution. Freeze +and commit the complete EvoGraph/Knowledge Evolution implementation and every other source change +before computing the final candidate identity. + +## Authoritative evidence inventory + +“Authoritative” below means authoritative for the named historical run, not proof of the current +candidate. + +| Evidence | Exact identity or status | Audit conclusion | +| --- | --- | --- | +| `proof/ease/developer-timing-runs.json` | `e398398d7f1dd4ff0b65409d2c8da971e83bc488/97989e9914aee93b83d5b120599bf4d9c8f69b3434040a3e06164cb58c84b987` | 60 receipts pass the current read-only evaluator. Historical only. | +| `proof/ease/developer-timing-verdict.json` | SHA-256 `632cab7302d8c8efc3b782702cf4bae285a01d8295b09ec43d51f65706668795` | Passed historically, but the stored verdict has no `nodekitCommit`, `nodekitSourceHash`, or `nodekitIdentity`; it cannot be decisive evidence under the hardened submission gate. Regenerate it from exact-candidate receipts. | +| `proof/ease/fresh-agent-verdict.json` | `e398398d7f1dd4ff0b65409d2c8da971e83bc488/97989e9914aee93b83d5b120599bf4d9c8f69b3434040a3e06164cb58c84b987`; SHA-256 `ca8bd83ca623add4b6834608b647fac6174599a346cba4944f6595eb1105bd02` | All three required tasks passed historically with zero interventions and zero reprompts. Not current-candidate proof. | +| `proof/package-install-verdict.json` | Candidate `e398398d7f1dd4ff0b65409d2c8da971e83bc488`; tarball SHA-256 `e17b7521f39b09a06fdf9b5646d8a0088430914556c0bf3b30c6ae81bd98a7a9` | The referenced tarball still exists and its hash matches. The verdict file begins with a UTF-8 BOM and fails Node `JSON.parse`, so it cannot be decisive evidence. Historical only. | +| `proof/ease/latest/manifest.json` and `proof/factory-acceptance.json` | Byte-identical; SHA-256 `08be58ea09d4d68b84b03d08da91b35ff9bf50bcbc868c2025e83e8292e80cad`; run `ease_baf96caf40fe4254ba25` | Passed core factory/browser work but correctly says `EASE_NOT_CERTIFIED`. The recorded commit/hash pair is not a clean immutable revision: commit `0cc282c...` is paired with source hash `97989e...`, used by the later `e398398...` evidence. Never promote this mutable `latest` directory. | +| `proof/ease/latest/browser/screenshot-manifest.json` | SHA-256 `24e57592ca65aa1978f2474d4a45697344b879d208f711c15172764db49f1f1a`; manifest digest `97eed4335e854f07ab5430b8af4bc8d7654df8b8d30b0033f5183e52e064a085` | Internally intact for the historical run: 180/180 PNG hashes match, 180/180 sidecars match, all 15 states are covered across six viewport profiles and two themes, both trace/video hashes match, and console/network/accessibility failures are zero. It is still stale and uses the mixed identity above. | +| `proof/ease/fresh-users-verdict.json` | `passed: false`, participant count `0` | Honest open external gate. | +| `proof/ease/latest/proofloop-receipt.json` | Tracked file is currently deleted | No final independent Ease integrity receipt exists. | +| `proof/submission-manifest.json` | Missing | There is no final evidence binding. | +| `proof/submission-verdict.json` | Missing | There is no submission-ready verdict. | + +The immutable, identity-suffixed files such as +`proof/ease/developer-timing-runs-e398398.json` and +`proof/ease/fresh-agent-verdict-e398398.json` are the safest historical records. Mutable aliases such +as `latest`, `developer-timing-verdict.json`, and `fresh-agent-verdict.json` are convenience paths, +not final evidence identities. + +## Exact screenshot and browser audit + +The historical browser packet is structurally strong but not current-candidate proof. + +- Required states: 15; covered states: 15; missing states: 0. +- Viewports: `1440x900`, `1920x1080`, `1024x768`, `768x1024`, `390x844`, and + `844x390`. +- Themes: light and dark. +- Expected screenshots: `15 x 6 x 2 = 180`; present: 180. +- PNG bytes matching each sidecar/manifest `pngSha256`: 180/180. +- Sidecar payloads matching the manifest entries: 180/180. +- Sidecars matching the run, NodeKit commit/hash, and generated candidate identity: 180/180. +- Playwright trace and browser video hashes: 2/2 matched. +- Manifest self-digest: matched. +- Journey assertions: all true. +- Console errors, failed requests, and accessibility violations: zero. + +This proves that exact screenshot accounting works. It does not prove the post-`e398398` product or +the eventual EvoGraph-inclusive final candidate. + +## Stale or mismatched identities + +1. Current source is newer than every passing timing, fresh-agent, package, and browser packet. +2. `proof/ease/latest/manifest.json` records commit `0cc282c...` with source hash `97989e...`, while + the passing `0cc282c` timing matrix is bound to source hash `bbe21d...` and the passing + `e398398` matrix is bound to `97989e...`. That `latest` commit/hash pair must be treated as a + dirty-tree historical run, not an immutable revision. +3. The stored timing verdict predates the new identity fields even though its raw receipts contain + one valid identity. Re-evaluate after the final candidate is frozen. +4. The package-install verdict is bound to `e398398`, is not bound to a source hash, and is not + standard UTF-8 JSON consumable by Node because of the BOM. +5. The selected fresh-agent trials are legitimate historical experiments, but their output cannot + be rebased onto a later commit. Their receipt hashes are self-digests in their trial manifests, + not separate stored receipt files. +6. The current `proof/ease/latest/` tree is heavily dirty. Preserve it as user-owned evidence; do + not clean, overwrite, or silently rename it as final proof. + +## Submission-gate status + +The current working tree contains uncommitted hardening that is directionally correct: + +- timing and fresh-user verdicts gain exact commit/source-hash identity; +- fresh-agent verdicts gain explicit commit/source-hash fields; +- the submission evaluator rejects stale source revisions, cross-gate evidence reuse, evidence + bound to another commit, malformed decisive JSON, and decisive verdicts that do not satisfy the + gate-specific contract. + +The targeted tests passed (11/11): + +```powershell +node --test test/ease-evidence.test.mjs test/ease-proof.test.mjs test/submission-gate.test.mjs +``` + +However, those protections are modified working-tree code, not a committed final-candidate +guarantee. They must be reviewed, committed, and included before the candidate is frozen. The +current template evaluates fail-closed, and the real manifest is absent. + +Read-only evaluation used: + +```powershell +node --input-type=module -e "import {evaluateSubmissionManifest} from './src/lib/submission-gate.mjs'; console.log(await evaluateSubmissionManifest(process.cwd(),'proof/submission-manifest.template.json'))" +``` + +It correctly reports the placeholder candidate, stale/malformed evidence, missing Convex, +preview, ProofLoop, and approval verdicts, and no exact-candidate decisive evidence. + +## Missing final gates + +| Gate | Current evidence | Can close locally? | What is actually required | +| --- | --- | --- | --- | +| Developer timing matrix | Historical pass only | Partly | Freeze candidate, then run 5 cold + 5 warm trials for Windows/npm, Windows/pnpm, Ubuntu/npm, Ubuntu/pnpm, macOS/npm, and macOS/pnpm. The 60-run cross-OS matrix normally requires CI runners. | +| Fresh-agent held-out | Historical three-task pass only | With authenticated coding-agent runtime | Repeat research map, volunteer onboarding, and launch presentation from clean isolated directories on the exact candidate with zero reprompts and substantive source changes. | +| Fresh-human usability | 0 participants, failed | No | Five consented fresh people, exact timestamps and screenshots/recordings, at least four unassisted completions, median first action <=30s, median journey <=180s, median SEQ >=6/7, no P0/P1 failures. | +| Three Convex consumers | `proof/convex-consumers-verdict.json` missing | No | Three authenticated, owner-scoped real consumers exercising stale proposals, retries, exception recovery, receipts, and component/app boundaries. Do not deploy from this audit. | +| Preview deployment | `proof/preview-verdict.json` missing | No | Authorized isolated frontend/backend preview of the exact commit, fresh identity, real fixture bytes, export/reopen score, screenshots, health, and cleanup. | +| ProofLoop Ease verification | `proof/proofloop-final.json` and latest receipt missing | No; independent last step | Verify the final archive and every referenced hash only after all prior exact-candidate evidence is complete. | +| Package-install proof | Historical tarball pass; malformed verdict JSON | Yes | Pack the exact candidate, install in a fresh consumer, create/compile/check/demo/eval, retain tarball, write BOM-free identity-bound JSON. | +| Publication approval | `proof/publication-approval.json` missing | No | Explicit owner approval naming both `npm-publish` and `convex-directory-submit`. | + +Live PostgreSQL/Supabase portability and real Model Intelligence/EvoGraph observations are also +required by the readiness policy before broader claims, even though they are not separate IDs in +the current eight-gate submission schema. + +## Safe reconciliation plan + +1. Finish and review all source work, including the EvoGraph-R1-inspired Knowledge Evolution/model + intelligence path. Do not collect final evidence while source is still moving. +2. Commit one immutable candidate. Confirm a clean distribution surface, then record both: + + ```powershell + git rev-parse HEAD + node --input-type=module -e "import {computeNodeKitSourceHash} from './src/lib/source-hash.mjs'; console.log(await computeNodeKitSourceHash(process.cwd()))" + ``` + +3. Create an identity-scoped evidence root such as + `proof/ease/candidates///`. Never reuse `latest` as the authoritative + source. +4. Re-run factory acceptance and the exact browser journey first. Verify every PNG, sidecar, + trace, video, generated-candidate commit, export, and reload receipt before continuing. +5. Run the 60 timing trials and aggregate only receipts with the exact same commit and source hash. +6. Run the three held-out coding-agent trials on that same identity. Pass the expected identity to + the aggregator or isolate its input directory so older passing trials cannot be selected. +7. Pack and install from that exact commit. Emit standard UTF-8 JSON without a BOM, include both + candidate commit and source hash, and keep the tarball at a durable evidence path. +8. Complete the five-person, three-consumer, live preview, portability, and live model/knowledge + evolution evidence without modifying candidate source. +9. Run independent ProofLoop verification last and retain `proof/proofloop-final.json`. +10. Create `proof/submission-manifest.json` only after every decisive verdict is exact-candidate, + BOM-free parseable JSON with the correct SHA-256. Then run: + + ```powershell + npm run submission:evaluate + ``` + +11. Publish or submit only when the emitted verdict has both `passed: true` and + `submissionReady: true`, and the explicit publication approval is present. + +## Locally rerunnable checks + +These can be run without deployment or publication, although some produce new proof and should be +directed to a new identity-scoped directory rather than the dirty historical tree: + +```powershell +npm test +npm run check +npm run factory:acceptance +npm run ease:evaluate-developer -- +npm run ease:evaluate-agents -- +npm run submission:evaluate +``` + +The package-install, browser, and fresh-agent commands require their normal package manager, +Playwright/browser, Docker or coding-agent credentials, and sufficient time. The cross-platform +timing matrix requires Windows, Ubuntu, and macOS execution. None of these local or CI checks can +substitute for fresh humans, authenticated real Convex consumers, an authorized deployment, +independent ProofLoop review, or owner publication approval. diff --git a/docs/FRONTEND_SPECIALIST.md b/docs/FRONTEND_SPECIALIST.md new file mode 100644 index 0000000..7aee2cc --- /dev/null +++ b/docs/FRONTEND_SPECIALIST.md @@ -0,0 +1,33 @@ +# NodeKit Frontend Specialist + +NodeKit routes frontend implementation as a bounded specialist role. The specialist receives a protected product-design contract; it does not choose the primary user, primary job, canonical workflow, data authority, permission boundaries, completion criteria, or final verdict. + +## Required pipeline + +```text +Product design contract +-> evidence-ranked route +-> three materially different rendered directions +-> browser, accessibility, overflow, and visual checks +-> independent blind comparison +-> bounded repair +-> fresh-user canary +-> NodeProof and human approval +``` + +The route starts unprofiled. A model may become preferred only when its requested and resolved identities are supported by scoped Model Intelligence evidence. Model revision and tool-surface changes invalidate the preference. + +## Commands + +```bash +nodekit frontend init +nodekit frontend plan --contract harness/frontend/product-packets/product.yaml +nodekit frontend directions --plan harness/frontend/receipts/.json +nodekit frontend benchmark --manifest harness/frontend/benchmark.json +nodekit frontend repair --benchmark harness/frontend/benchmark.json +nodekit frontend canary --receipt harness/frontend/canary.json +``` + +The three required archetypes are collaborative workspace, artifact studio, and a domain-native interpretation. Each must render desktop first arrival, active workflow, proposal review, mobile artifact, mobile agent, and mobile review states. + +No tournament, repair, or canary grants deployment authority. NodeProof and an authorized human retain that decision. diff --git a/docs/KNOWLEDGE_EVOLUTION.md b/docs/KNOWLEDGE_EVOLUTION.md new file mode 100644 index 0000000..d6ea0e2 --- /dev/null +++ b/docs/KNOWLEDGE_EVOLUTION.md @@ -0,0 +1,145 @@ +# Knowledge Evolution Plane + +NodeKit adapts the useful systems pattern from +[EvoGraph-R1](https://arxiv.org/abs/2607.12764): knowledge is a persistent environment that an +agent can retrieve, expand, correct, and use across runs. NodeKit does **not** copy the paper's +training system or permit autonomous canonical graph mutation. + +The NodeKit interpretation is: + +```text +Observe current graph +-> retrieve known context +-> identify typed gaps or contradictions +-> research only those gaps +-> propose a source-grounded graph patch +-> validate schema, authority, freshness, and conflicts +-> request an explicit decision +-> apply atomically against the pinned graph version +-> retain an evolution receipt +``` + +## Authority boundary + +There are two distinct graph products: + +```text +Understand Anything code graph + read-only, commit-pinned repository evidence + +NodeKit Knowledge Evolution graph + backend-neutral, versioned, proposal-governed project knowledge +``` + +Importing a code graph does not grant knowledge-write authority. A knowledge graph cannot modify +source code, an artifact, a Caseflow record, a benchmark, or a proof receipt. It can only advance +its own canonical version through an accepted `nodekit.graph-patch/v1` proposal. + +## Six epistemic layers + +| Layer | Purpose | Mutation rule | +|---|---|---| +| `source` | Immutable documents, code, screenshots, video, traces, datasets, and web anchors | Ingest proposal only; never updated or deprecated in place | +| `derived` | Machine-extracted entities and relationships | Graph patch | +| `working` | Run-scoped task knowledge | Graph patch | +| `proposal` | Candidate understanding awaiting validation or review | Graph patch | +| `canonical` | Approved durable project knowledge | Accepted graph patch only | +| `hypothesis` | Explicitly unverified ideas | Graph patch; always visibly labeled | + +`DEPRECATE` preserves history. Destructive delete is not part of the v1 operation set. + +## Multimodal anchors and hyperedges + +An evidence node has a content hash, source URI, capture time, and optional spatial or temporal +locator. Claims and relationships reference those immutable anchors. An n-ary hyperedge can retain +the full context of an observation, for example: + +```text +model + exact model revision + task + harness + tool surface ++ failure + screenshot + repair skill + proof receipt +``` + +This avoids flattening a real observation into unrelated binary facts. + +## Agent action receipts + +NodeKit records the bounded action pool without storing private chain-of-thought: + +- `GRAPH_RETRIEVE` +- `EXTERNAL_RESEARCH` +- `PROPOSE_GRAPH_PATCH` +- `INSPECT_ARTIFACT` +- `EXECUTE_TOOL` +- `REQUEST_APPROVAL` +- `COMPLETE` +- `ABSTAIN` + +Each receipt binds the graph version, run and case identity, actor, inputs, output references, +evidence references, budget, status, and time. + +## CLI + +```bash +nodekit graph init --repo-root . +nodekit graph ingest --input evidence.json --repo-root . +nodekit graph inspect --repo-root . +nodekit graph query "stale proposal failures" --repo-root . +nodekit graph gaps --repo-root . +nodekit graph research "official source for gap:123" --repo-root . +nodekit graph propose --patch patch.json --repo-root . +nodekit graph validate --patch patch_123 --repo-root . +nodekit graph apply --patch patch_123 --approved-by human:reviewer --repo-root . +nodekit graph diff --from 2 --to 5 --repo-root . +nodekit graph replay --version 4 --repo-root . +nodekit graph benchmark --cases evals/knowledge-cases.json --repo-root . +nodekit graph harness-sync --repo-root . +``` + +`ingest`, `propose`, and `harness-sync` create proposals only. `apply` requires an approving +principal and re-validates the patch against the current graph. A stale base version conflicts. + +`research` records a typed, budgeted research action. It does not silently call a provider. Search +results must return as immutable evidence anchors and a separate graph patch. + +## Harness Gym integration + +`nodekit graph harness-sync` compiles evaluated model observations into a proposed harness +hypergraph: + +```text +observation evidence ++ task ++ resolved model ++ exact harness hash ++ failures ++ cognitive/execution/artifact/efficiency verdicts +``` + +The command does not promote a model, skill, route, or patch. Existing Harness Gym protected-task, +fresh-agent canary, independent-critic, rollback, and NodeProof requirements remain decisive. + +## Backend contract + +The current implementation is a portable JSON document and in-memory/file adapter suitable for +deterministic tests. Future provider adapters must preserve the same observable semantics: + +```text +Memory/file -> reference and replay +NodeGraph -> portable document/rendering +Neo4j -> traversal and analytics projection +Convex -> collaborative proposal/review state +PostgreSQL/Supabase -> managed relational persistence +``` + +One deployment has one authoritative canonical graph. Provider projections are not independent +write authorities. + +## Evaluation before learning + +The v1 benchmark compares flat retrieval, a static `source + canonical` graph, and the composed +evolving graph. Receipts report whether INSERT, UPDATE, DEPRECATE, and EXTERNAL_RESEARCH appear in +the measured trajectory. Production claims still require fixed tasks, repeated runs, unsupported +edge rate, success, turns, tokens, latency, cost, and protected evaluators. + +Do not begin RL training from this layer. Accepted and rejected trajectories become training data +only after non-RL graph governance, evaluators, rollback, and rewards are stable. diff --git a/docs/MODEL_INTELLIGENCE.md b/docs/MODEL_INTELLIGENCE.md new file mode 100644 index 0000000..2189288 --- /dev/null +++ b/docs/MODEL_INTELLIGENCE.md @@ -0,0 +1,238 @@ +# NodeKit Model Intelligence and Skill Compiler + +Status: **P0-P3 mechanics implemented and deterministically tested; real capability evidence, +application gyms, and current-revision certification remain open** + +## Claim boundary + +The repository contains working schemas, CLI commands, compilers, fail-closed gates, and +fixture-backed tests for P0 through P3. That proves the mechanics behave as specified under the +checked-in test cases. It does **not** prove that any live provider or exact resolved model is good +at a real NodeKit task family. + +Current evidence status: + +- no live provider run in this repository has produced a project-scoped capability card; +- no real model route is certified; +- no NodeKit Builder, NodeSlide, NodeVideo, NodeRoom, NodeSheet, or NodeBenchAI gym has completed + its application-specific evidence program; +- automatic promotion remains disabled, and deterministic tests cannot substitute for a fresh + agent canary plus an independently verified NodeProof receipt. + +## Adopted decision + +> NodeKit does not treat models as interchangeable providers. It observes each exact resolved +> model on the application's real task families, separates model behavior from harness behavior, +> produces evidence-backed capability cards, and only then proposes focused skills or routing +> changes. + +Model Intelligence is a first-class subsystem of Harness Gym: + +```text +Application task +-> model + harness execution +-> NodeTrace observation +-> deterministic and independent evaluation +-> typed failure classification +-> capability-card evidence +-> skill or routing candidate +-> controlled ablation +-> fresh-agent canary +-> NodeProof promotion receipt +-> NodeMem retention +``` + +The Deck Gym is the first artifact-specific reference design. The portable subsystem serves the +NodeKit Builder Gym and the runtime, critic, repair, browser, and domain agents in NodeSlide, +NodeVideo, NodeRoom, NodeSheet, and NodeBenchAI. + +## Blank-factory boundary + +`nodekit create` remains domain blank and does not assign model brands to roles. Model +intelligence is initialized only after a real vertical slice exists: + +```bash +nodekit harness init +nodekit models baseline +nodekit models profile +nodekit models inspect +nodekit models diagnose +``` + +The current `baseline` command validates stored observations, binds them to content hashes, and +writes an honest zero-provider-call receipt. It does not run a model, infer capability from a +brand, or certify routing. + +## Evidence hierarchy + +Capability cards have three scopes: + +1. **Project** evidence from the exact application, harness, tools, and task family. +2. **Domain** evidence from the same kind of work across applications. +3. **Ecosystem** evidence across materially different NodeKit applications. + +Routing precedence is: + +```text +Project evidence > Domain evidence > Ecosystem prior > Unprofiled fallback +``` + +Every observation records both the requested route and exact resolved provider/model. Dynamic +aliases such as `openrouter/free` never inherit conclusions from a previously resolved model. + +## Observation contract + +`nodekit.model-observation/v1` separates: + +- cognitive behavior: brief understanding, decomposition, constraint retention, ambiguity, + references, and repair reasoning; +- execution: tool selection, valid arguments, ordering, inspection, recovery, scoped changes, and + completion; +- artifact quality: correctness, usability, domain quality, evidence integrity, and optional + visual, editability, and export scores; +- efficiency: latency, tokens, cost, tool calls, and retries; +- identity: model route/revision plus harness, tool, context, and skill hashes; +- proof: typed failures, evidence references, and one proof receipt. + +Scores use a normalized `0..1` scale. A malformed observation or unsupported failure class fails +compilation rather than being silently ignored. + +## Failure vocabulary + +The shared taxonomy includes brief, planning, primitive, density, tool, inspection, completion, +repair, context, orchestration, evidence, cost, repository, reference, UI, authority, stale-write, +export, and recovery failures. Findings describe observable behavior, expected behavior, probable +cause, severity, and evidence. They do not use anthropomorphic model mythology. + +## Capability-card contract + +`nodekit.model-capability-card/v1` binds a behavioral claim to requested and resolved model +identity; project, domain, or ecosystem scope; exact evidence window, task count, run count, and +harness versions; strengths, weaknesses, roles, avoidance guidance, scaffolding, metrics, +confidence, evidence references, expiry triggers, and provisional/certified/expired status. + +No card is generated from NodeKit's deterministic factory proof because that proof contains no +live model comparison. Existing NodeSlide or NodeVideo evidence must be normalized into valid +observations before it can support a card. + +## Implemented skill compiler mechanics + +`nodekit harness init` creates the five skill roots for the resolved stack: + +```text +Role skill ++ Domain skill ++ Model adapter ++ Guardrail skills ++ Conditional recovery skill +``` + +The implemented `nodekit skills propose` path clusters repeated findings only after the configured +multi-run and multi-brief threshold, then writes proposal-only candidates with executable skill +contracts. Every executable skill must define typed triggers, inputs, required tools, procedure, +constraints, completion checks, failure behavior, positive and negative examples, expected tool +traces, test fixtures, and supporting evidence. A skill is not motivational prose. + +Normal promotion threshold: + +```text +same finding in at least 3 independent runs ++ at least 2 task briefs ++ measurable improvement in with-skill versus without-skill comparison ++ no material regression +``` + +Accuracy, cost, latency, safety, artifact editability, export, and user completion are all +regression dimensions. A P0 authority or safety incident may produce an emergency guardrail after +one incident, but the regression suite is still mandatory before ordinary promotion. + +## Controlled comparisons + +```text +Model A + Harness v1 versus Model A + Harness v2 # harness gain +Model A + Harness v2 versus Model B + Harness v2 # model difference +Model A + Harness v2 +/- candidate skill # skill effect +``` + +Tasks, evidence, tools, budgets, judges, and scoring remain fixed. Candidate code cannot edit +held-out tasks, decisive judges, thresholds, safety requirements, or official outcomes. + +## Implemented routing mechanics + +The routing compiler orders eligible non-expired cards using project-before-domain-before-ecosystem +evidence precedence, then compiles task requirements through quality, safety, latency, cost, +availability, and tool compatibility into a role/domain/adapter/guardrail stack, bounded tools, +deterministic fallback, and routing-decision receipt. + +Routes expire when material model, harness, tool-surface, context-policy, or skill-stack identity +changes. Routing and skill changes remain provisional proposals. Automatic promotion is disabled +by default, and the compiler never reports routing certification. + +## Implemented tournament and promotion guards + +The tournament evaluator rejects self-judging candidates, requires an unchanged protected +evaluator, and returns a provisional winner without authorizing promotion. Manual promotion +requires a passing controlled comparison, fresh-context canary, verified NodeProof receipt, and an +explicit `approvedBy` identity; promoted harness versions retain a rollback pointer. These guards +are covered by deterministic fixtures, not yet by a live application tournament. + +## Implementation ladder + +### P0 — implemented mechanics + +- [x] `nodekit.harness/v1` schema. +- [x] `nodekit.model-observation/v1` schema. +- [x] Shared failure taxonomy. +- [x] `nodekit.model-capability-card/v1` with scope, confidence, evidence, and expiry. +- [x] `nodekit harness init` application-specific scaffold. +- [x] Strict observation/card validation and compiled hashes. +- [x] `models baseline`, `profile`, `inspect`, and `diagnose` commands. +- [x] Exact requested/resolved model fields required by schema. +- [x] Automatic promotion and routing certification fail closed. +- [ ] Normalize one real NodeSlide or NodeVideo model run into a manual provisional card. + +### P1 — implemented mechanics + +- [x] Executable skill schema and five skill directories. +- [x] Findings clustering into proposal-only candidates. +- [x] Positive/negative examples, fixtures, traces, completion, and failure assertions. +- [x] With-skill/without-skill comparison schema and fail-closed benchmark evaluator. +- [ ] Run the comparison against real application tasks and live model observations. + +### P2 — implemented mechanics + +- [x] Routing-policy schema and project/domain/ecosystem evidence precedence. +- [x] Confidence, expiry, cost, latency, availability, tool compatibility, and deterministic fallback. +- [x] Provisional decision receipt and canary verification behavior. +- [ ] Compile and canary a route from real, non-expired capability cards. + +### P3 — implemented mechanics + +- [x] Blind pairwise comparison contract and independent-critic enforcement. +- [x] Fresh-agent canary and NodeProof promotion-receipt gates. +- [x] Explicit manual promotion/rejection and versioned rollback. +- [ ] Complete a real tournament, canary, independently verified promotion, and rollback drill. + +### P4 — application gyms + +- [ ] NodeKit Builder Gym. +- [ ] NodeSlide Deck Gym. +- [ ] NodeVideo Creator Gym. +- [ ] NodeRoom Collaboration Gym. +- [ ] NodeSheet Data Gym. +- [ ] NodeBenchAI Research Gym. + +### P5 — learning export + +- [ ] Accepted, rejected, failed, and repaired trajectories. +- [ ] Human pairwise preferences and protected rewards. +- [ ] NodeRL consumption only after harness and benchmark contracts stabilize. + +## Non-negotiable rule + +> Do not erase model differences with one giant prompt. Measure exact resolved models, exploit +> demonstrated specialization, compensate for recurring failures, and prove every compensation +> against protected tasks before promotion. + +The checked-in P1-P3 tests prove this rule is enforced mechanically. They do not provide the model +measurements needed to make a capability or routing claim. diff --git a/docs/NODEKIT_FIGURED_OUT_HANDOFF.md b/docs/NODEKIT_FIGURED_OUT_HANDOFF.md new file mode 100644 index 0000000..e07a9c9 --- /dev/null +++ b/docs/NODEKIT_FIGURED_OUT_HANDOFF.md @@ -0,0 +1,68 @@ +# NodeKit figured-out foundation handoff + +## Current outcome + +NodeKit's primary factory is now domain-blank and behaviorally figured out. + +```text +nodekit create + -> product and service-design contract + -> Case -> Run -> Stage -> Artifact -> Proposal -> Approval -> Receipt + -> deterministic memory runtime + -> responsive artifact-first application + -> adapter conformance + -> browser and proof receipts +``` + +The CLI accepts no `--preset`, domain selector, or public reference-creation path. Narrow applications under `reference-apps/` are repository-internal regression fixtures and historical demonstrations only; they are excluded from the published package. Every real application begins from the same blank, figured-out foundation and is specialized by the coding agent from the user's stated job. + +## Portable kernel + +`src/lib/caseflow.mjs` defines the current portable semantic implementation. It provides NodeKit-owned IDs, canonical artifact versions, proposal-before-mutation, stale-proposal conflicts, explicit next-action ownership, exception recovery, terminal run states, portable events, and content-addressed receipts. + +`src/lib/caseflow-conformance.mjs` is the provider-neutral executable boundary. The memory runtime passes it. Future Convex, PostgreSQL, and Supabase adapters must pass the same observable semantics without flattening provider-native strengths. + +## Generated product contract + +Every new app receives: + +- `docs/FIGURED_OUT.md`; +- `product/BRIEF.md`; +- `product/AUDIENCE.md`; +- `product/USER_JOURNEY.md`; +- `product/SERVICE_BLUEPRINT.md`; +- `product/EXPERIENCE.yaml`; +- `product/DESIGN.md`; +- `product/TASTE.md`; +- a no-key deterministic demonstration; +- an artifact-first responsive shell; +- light and dark theme behavior; +- proposal, conflict, recovery, and receipt tests; +- a Convex capability plan that stays off until the real workflow is researched. + +## Proof + +The empty-directory acceptance runs installation, compilation, generated tests, deterministic demo, stale-proposal evaluation, live HTTP/DOM browser assertions, and proof generation. Its current receipt is `proof/factory-acceptance.json`. + +The canonical rendered QA packet is `proof/ease/latest/`. It contains 180 screenshots and sidecars covering 15 required states across six viewport/theme combinations, plus a Playwright trace and browser video of a real proposal, approval, receipt, and reload journey. The browser manifest reports zero missing states, serious/critical Axe violations, console errors, failed requests, horizontal overflow, or detected mojibake. Independent ProofLoop verification re-hashed all 180 screenshots, both replay artifacts, and the candidate/timer manifests. QA memory is append-only under `.qa/memory/`. + +The current audience-facing proof deck is `outputs/nodekit-figured-out-proof.pptx`. It replaces the obsolete two-preset story with the neutral base, portable Caseflow, reference-app boundary, and Convex-first portability decision. Its byte hash, exact-template fidelity, overflow result, and per-slide visual inspection are recorded in `proof/nodekit-figured-out-presentation.json`. + +Durable supervision is implemented in the companion NodeProof branch through a dependency-ordered ProofProgram, immutable runner-plan bindings, local read/proposal authority, budget and attempt ceilings, resumable ledgers, and NodeKit candidate/config/receipt verification. + +## Convex boundary + +Convex remains the preferred managed implementation, not the NodeKit semantic definition. `docs/CONVEX_CASEFLOW_EXTRACTION.md` records the component boundary and extraction gate. The component is intentionally not claimed as published: current Convex guidance keeps component state isolated, requires app-owned authentication wrappers, and recommends composing Workflow and Workpool for durable agent execution. + +The component extraction begins only after three materially different consumers prove the repeated lifecycle implementation. Until then, NodeKit ships the portable contract, memory reference runtime, capability plan, and conformance suite. + +## Remaining gated work + +1. Adapt NodeRoom/NodeSheet, NodeSlide, and NodeVideo to the portable Caseflow contract in isolated, reviewed changes. +2. Extract the repeated Convex implementation and cover it with `convex-test` only after those consumers agree. +3. Add generic PostgreSQL semantics, then the Supabase managed profile. +4. Repair the isolated fresh-agent executor in a new QA pass and run all three held-out tasks. +5. Run the five-person usability study and an authorized shareable-preview journey. + +The live checklist, exact evidence counts, and submission order are maintained in `docs/EASE_SUBMISSION_READINESS.md`. +These are explicit future gates, not hidden claims about the current release. diff --git a/docs/NODEKIT_MASTER_PLAN.md b/docs/NODEKIT_MASTER_PLAN.md new file mode 100644 index 0000000..efa89c6 --- /dev/null +++ b/docs/NODEKIT_MASTER_PLAN.md @@ -0,0 +1,529 @@ +# NodeKit master plan and evidence checklist + +Status: **engineering foundation proven; Ease certification and Convex submission blocked** + +Last reconciled: 2026-07-22 + +This document is the canonical strategic summary for NodeKit. It separates implemented behavior, +evidence-backed claims, active work, and future architecture. Detailed gate definitions remain in +`EASE_SUBMISSION_READINESS.md`, `CONVEX_CASEFLOW_EXTRACTION.md`, and `RELEASE_LADDER.md`. + +## Locked thesis + +> NodeKit is the domain-blank, figured-out foundation for agent applications and the improvement +> system for the harnesses that build and operate them. + +Short form: + +> **Blank in domain. Figured out in behavior. Convex-first, not Convex-locked.** + +NodeKit standardizes the parts every serious agent application otherwise rebuilds: + +- repository and coding-agent orientation; +- guided product progression; +- Caseflow lifecycle and canonical-state safety; +- agent authority, proposal, approval, and recovery boundaries; +- artifact-first responsive UI topology; +- deterministic demo, evaluation, proof, and deployment contracts; +- evidence-backed improvement of the builder, runtime, and interaction harnesses. + +Domain applications still own the user's specific job, domain tools, artifact semantics, validators, +fixtures, and distinctive product experience. + +## Current verdict + +| Area | Evidence-backed status | Claim boundary | +|---|---|---| +| Domain-blank empty-directory factory | PASS | One complete factory receipt exists | +| Figured-out neutral journey | PASS | Case through receipt, reload, conflict, failure, and export states | +| Portable Caseflow semantics | PASS | Memory implementation and conformance behavior exist | +| Deterministic no-key runtime | PASS | Demo, compile, eval, browser contract, and receipt pass | +| Cross-platform compatibility | PASS, one run per lane | Not a repeatability or percentile claim | +| Rendered UI coverage | PASS | 15 states, 6 viewport/theme profiles, 180 hashed screenshots | +| Evidence integrity | PASS | Independent ProofLoop re-hash passed | +| Repeated cold/warm timing | PASS AT `e398398` | 60/60 exact Windows, Ubuntu, and macOS npm/pnpm trials exist; final-candidate proof remains revision-bound | +| Fresh coding-agent specialization | PASS AT `e398398` | Three writable zero-reprompt held-outs passed; final-candidate proof remains revision-bound | +| Fresh-human usability | INCOMPLETE | Five real participants still required | +| NodeVideo Caseflow adoption | ENGINEERING PASS | Production candidate; authenticated wrapper still missing | +| Submission-grade Convex consumers | 0/3 | NodeVideo does not count until owner-scoped auth boundary passes | +| Shareable NodeKit factory preview | INCOMPLETE | NodeVideo production proof is not factory-preview proof | +| Harness Gym | P0 FOUNDATION | Schemas, init, observation/card compiler, hashes, and diagnosis exist; no protected benchmark or promotion receipt yet | +| Model Intelligence | P0 FOUNDATION | Strict observations/cards and fail-closed registry exist; no certified live-model card or routing policy yet | +| Frontend Specialist | V1 ENGINEERING PASS | Protected product contract, evidence-ranked route, three-direction tournament, bounded repair, and canary gate exist; real consumer tournament proof remains | +| Knowledge Evolution Plane | V1 ENGINEERING PASS | Six-layer hypergraph, evidence anchors, governed patches, receipts, replay, CLI, Harness projection, and tests exist; live task evidence and provider adapters remain | +| Evolution Ledger | V1 ENGINEERING PASS | Immutable causal records, evidence hashes, adoption map, projections, CI materiality, and graph proposal exist; live consumer adoption remains | +| Convex component extraction | BLOCKED | Requires three qualifying consumers and repeated implementation | +| Convex directory submission | BLOCKED | `submissionReady` must be deterministically true first | +| PostgreSQL/Supabase portability | FUTURE | Contracts should be preserved now; adapters follow later | + +The current mandatory verdict is `EASE_NOT_CERTIFIED`. Do not submit NodeKit or Caseflow to the +Convex Components directory yet. + +## Stable product grammar + +Every NodeKit application uses the same observable lifecycle: + +```text +Case +→ Run +→ Stage +→ Artifact +→ Proposal +→ Approval +→ Receipt +``` + +Every state answers: + +1. What has happened? +2. What is happening now? +3. Who owns the next action? +4. What is required to continue? +5. What happens afterward? + +The default product standard is: + +```text +One primary job +One obvious starting action +One guided progression +One canonical output +One recovery path +One verifiable completion state +``` + +## Architecture boundary + +```text +User goal and evidence + ↓ +NodeKit orientation and figured-out compiler + ↓ +Domain application pack + ↓ +NodeKit experience layer + ↓ +NodeAgent execution and tool runtime + ↓ +Frontend Specialist + Model Intelligence + Harness Gym + ↓ +Evolution Ledger (human-reviewed causal memory) + ↓ +Knowledge Evolution Plane + ↓ +Portable Caseflow core + ↓ +Memory | Convex | PostgreSQL | Supabase +``` + +Provider mechanics may differ, but observable behavior may not. In particular, every backend must +preserve proposal-before-mutation, base-version checks, idempotency, explicit terminal states, +next-action ownership, safe failure, and content-addressed receipts. + +Convex is the golden reactive implementation. It is not the semantic definition of NodeKit. +Shared product code and shared React components must consume NodeKit ports and view models rather +than importing provider-specific database documents or hooks. + +The Knowledge Evolution Plane is a separate authority from Caseflow and imported code graphs. It +maintains a backend-neutral multimodal hypergraph across `source`, `derived`, `working`, `proposal`, +`canonical`, and `hypothesis` layers. Only a source-grounded, validated, explicitly accepted patch +against the current base version advances canonical graph state. See `KNOWLEDGE_EVOLUTION.md`. + +## Primary creation experience + +The public initialization path remains: + +```bash +nodekit create my-app +cd my-app +pnpm demo +``` + +Rules: + +- no domain preset picker; +- no silent research-loop default; +- no initial backend, framework, model, or artifact questionnaire; +- the base application is blank in domain and complete in behavior; +- the neutral demo is visibly labeled `NODEKIT SYSTEM DEMO`; +- use `nodekit adopt` for existing repositories; +- backend and provider decisions occur only after the coding agent understands the real job. + +The generated repository must orient a fresh coding agent through this reading order: + +1. `docs/FIGURED_OUT.md` +2. `product/BRIEF.md` +3. `product/AUDIENCE.md` +4. `product/USER_JOURNEY.md` +5. `product/SERVICE_BLUEPRINT.md` +6. `product/EXPERIENCE.yaml` +7. `docs/ARCHITECTURE.md` +8. `nodekit.yaml` +9. `nodeagent.yaml` +10. current proof receipts + +## UI contract + +NodeKit maintains a curated UI Gold Registry rather than copying entire product UIs. + +```text +Generic primitives +→ agent primitives +→ Node trust components +→ artifact compositions +→ domain UI +→ optional expressive effects +``` + +The stable workspace topology is artifact-first: + +- case and history navigation; +- primary artifact stage; +- adjacent agent/review rail; +- visible stage progress and current action; +- collected proposals and decisions; +- separate ephemeral activity; +- advanced traces available but secondary; +- stacked or tabbed mobile behavior rather than a crushed desktop layout. + +## Caseflow component boundary + +The future Convex submission is **NodeKit Caseflow**, not the entire NodeKit factory. + +Caseflow may own: + +- cases, runs, stages, and timeline events; +- artifacts and artifact versions; +- proposals and approvals; +- exceptions and recovery state; +- receipts and external references. + +Caseflow must not own: + +- application authentication or organization roles; +- model providers, prompts, RAG, or general chat; +- arbitrary host-application artifacts; +- the NodeKit CLI, React experience kit, factory, or Harness Gym. + +The host application authenticates the principal and passes scoped identity into the component. +Existing Convex Agent, Workflow, Workpool, streaming, RAG, Presence, rate-limit, and file components +should be composed rather than rebuilt. + +## Harness Gym + +Harness Gym is the next compounding layer: + +> NodeKit builds the app, proves the app, and improves the harness future agents use to build and +> operate the app. + +It treats three changeable harnesses and one protected evaluator as separate systems: + +1. builder harness: repository map, required reading, references, commands, and completion contract; +2. runtime harness: planner, tools, context, routing, memory, retry, and authority policy; +3. interaction harness: UI topology, journeys, states, selectors, artifacts, and exceptions; +4. evaluation harness: frozen tasks, fixtures, judges, safety rules, and proof thresholds. + +The optimizing agent may propose changes to the first three. It may not change held-out tasks, +official outcomes, decisive judges, safety requirements, or proof thresholds in the same candidate. + +### Harness Gym implementation order + +- [x] Add the `nodekit.harness/v1` schema and content-addressed harness identity. +- [x] Generate a minimal `harness/` directory after the first application vertical slice. +- [x] Add protected validation, held-out, and adversarial task indexes. +- [x] Add `init`, `baseline`, `inspect`, `diagnose`, `propose`, `benchmark`, `tournament`, `canary`, + `review`, `promote`, `rollback`, `status`, and `gate` commands. +- [x] Require a hypothesis, expected impact, and risk for every harness candidate. +- [ ] Capture complete NodeTrace trajectories and separate task, artifact, UI, safety, efficiency, + evidence, and human-preference verdicts. +- [x] Require fresh-agent canaries, rollback versions, and NodeProof promotion receipts. +- [x] Keep automatic promotion disabled by default. +- [ ] Implement NodeKit Builder Gym first. +- [ ] Implement NodeSlide Deck Gym, NodeVideo Creator Gym, and NodeRoom Collaboration Gym next. +- [ ] Prove Harness v1 beats or holds Harness v0 with a fresh agent on frozen tasks. + +Harness Gym comes before NodeRL. Model training consumes accepted and rejected trajectories only +after documentation, tools, context, routing, UI contracts, and deterministic repair have been +optimized. + +### Model Intelligence and Skill Compiler + +Harness Gym now includes a first-class model-intelligence subsystem. Exact resolved models are +observed on real application task families; cognitive behavior, tool execution, artifact quality, +and efficiency are evaluated separately. Behavioral findings update scoped capability cards and +may later propose focused skill or routing changes. + +Evidence precedence is `project > domain > ecosystem > unprofiled fallback`. A dynamic provider +alias never inherits another resolved model's card. Model, harness, tool-surface, context-policy, +and skill-stack identities are bound to every observation. + +The future resolved stack is `role + domain + model adapter + guardrails + conditional recovery`. +Candidate skills and routes require controlled ablations, protected tasks, independent critics, a +fresh-agent canary, rollback, and NodeProof receipt. Automatic promotion remains disabled. + +See `MODEL_INTELLIGENCE.md` for the canonical contracts and phase gates. + +### Frontend Specialist Routing + +Frontend work is a protected Harness Gym specialization rather than a prompt convention. The +product-design contract retains authority over the primary job, canonical artifact, required +states, mobile topology, evidence, approvals, and forbidden patterns. Model routing is evidence +ranked and remains unprofiled until real observations justify a preferred route. + +Every major frontend must render exactly three materially different directions: collaborative +workspace, artifact studio, and domain-native. An independent critic performs pairwise selection; +the winner may receive only bounded repairs before a fresh-browser canary. Promotion requires the +exact model and commit, a fresh identity, complete desktop/mobile journeys, screenshots, NodeProof, +zero major findings, and human approval. Automatic deployment and promotion remain disabled. + +See `FRONTEND_SPECIALIST.md` for the CLI and authority boundaries. + +## Knowledge Evolution Plane + +The graph is an agent environment, not merely a visualization: + +```text +retrieve known subgraph +-> identify a gap or contradiction +-> perform bounded external research +-> anchor multimodal evidence +-> propose graph patch +-> validate and approve +-> apply atomically +-> retain evolution receipt +``` + +Implemented v1: + +- [x] Add backend-neutral knowledge-state, graph, graph-patch, and action-receipt schemas. +- [x] Add immutable multimodal source anchors and n-ary hyperedges with typed roles. +- [x] Separate source, derived, working, proposal, canonical, and hypothesis layers. +- [x] Add INSERT, UPDATE, and DEPRECATE; omit destructive DELETE. +- [x] Require source grounding, schema validity, authority, freshness, and base-version checks. +- [x] Require an explicit approving principal before apply. +- [x] Preserve stale proposals as conflicts instead of overwrites. +- [x] Add query, gaps, diff, replay, benchmark, and action/evolution receipts. +- [x] Project Harness Gym observations into proposal-only task/model/harness/failure hyperedges. +- [x] Preserve the Understand Anything graph as read-only, commit-pinned code evidence. +- [ ] Run protected flat/static/evolving comparisons on real application tasks. +- [ ] Add NodeGraph document/rendering, Neo4j traversal, Convex collaboration, and SQL persistence projections. +- [ ] Add graph-aware NodeRoom review and multimodal source-region navigation. +- [ ] Export trajectories to NodeRL only after protected rewards stabilize. + +## Evolution Ledger + +The Evolution Ledger is NodeKit's durable causal memory. It records human-reviewed evolution +events, challenged assumptions, invariant claims, immutable evidence, and consumer adoptions. Git +commits, content hashes, verifier references, model identity, sample size, viewport, and candidate +identity are checked where applicable. Records are append-only; corrections supersede history +instead of overwriting it. + +The ledger can be queried and diffed, generates `EVOLUTION.md` and an adoption map, and can only +propose evidence-grounded Knowledge Evolution patches. It does not silently mutate the canonical +knowledge graph. CI blocks material product, schema, template, harness, ownership, or workflow +changes when the commit range contains no human-reviewed evolution event. + +See `EVOLUTION_LEDGER.md` for record contracts and commands. + +## Release ladder + +```text +E0 Engineering merge +→ E1 Deterministic factory +→ E2 Rendered interaction proof +→ E3 Ease certification +→ C1 Three qualifying Convex consumers +→ C2 Caseflow extraction and package proof +→ C3 Convex directory submission +→ P1 PostgreSQL adapter +→ P2 Supabase managed profile +``` + +Passing an earlier level never implies a later claim. + +## Master checklist + +### Proven foundation + +- [x] Domain-blank base template. +- [x] Empty-directory-only `nodekit create` and brownfield `nodekit adopt` distinction. +- [x] Thin repository contract and compiled application identity. +- [x] Deterministic memory runtime. +- [x] Portable Caseflow lifecycle and stale-proposal semantics. +- [x] Neutral no-key demo from Case through Receipt. +- [x] Compile, check, evaluation, browser-contract, browser, and proof commands. +- [x] Exact factory phase ledger and content hashes. +- [x] Fifteen rendered application states. +- [x] Six viewport/theme profiles and 180 PNG/JSON screenshot pairs. +- [x] Accessibility, overflow, console, network, and mojibake gates. +- [x] Proposal approval, canonical versioning, reload persistence, and safe conflict behavior. +- [x] Independent ProofLoop archive and screenshot re-hash. +- [x] One npm/pnpm compatibility run on Windows, macOS, and Ubuntu. + +### Ease certification + +- [ ] Run five isolated cold and five warm trials for every supported OS/package-manager lane. +- [ ] Preserve all 60 raw timing receipts and compute median, range, and maximum. +- [ ] Require at least 20 cold and 20 warm samples per lane before publishing p95. +- [ ] Pass all three fresh-agent held-out tasks from writable isolated sessions. +- [ ] Require substantive non-proof changes, no human reprompt, final report, diff, screenshots, and + proof receipt in every fresh-agent run. +- [ ] Run five fresh-human sessions with consent and participant-level receipts. +- [ ] Require at least four unassisted completions, median first action at most 30 seconds, median + neutral journey at most 180 seconds, and median SEQ at least 6/7. +- [ ] Deploy an exact tested NodeKit factory candidate to an isolated preview frontend and backend. +- [ ] Complete a fresh-browser upload, journey, export, reopen, independent score, cleanup, and + deployment-bound receipt. +- [ ] Re-run independent ProofLoop verification over the final immutable candidate. + +### Convex adoption and extraction + +- [x] NodeVideo demonstrates production Convex Caseflow behavior, two-session reactivity, stale + conflict rejection, exact approval, reload persistence, and governed external planning. +- [ ] Replace NodeVideo's owner-capability locator with authenticated owner-scoped wrappers. +- [ ] Make NodeVideo pass packaged Caseflow conformance as a submission-grade consumer. +- [ ] Make NodeRoom or NodeSheet pass authenticated artifact-collaboration Caseflow conformance. +- [ ] Make NodeSlide pass authenticated presentation-production Caseflow conformance. +- [ ] Exercise stale proposals, idempotent retries, exception recovery, receipt integrity, and data + ownership boundaries in all three consumers. +- [ ] Identify the repeated Convex implementation only after the three consumers converge. +- [ ] Extract the isolated Caseflow component. +- [ ] Add argument and return validators to every public component function. +- [ ] Cover the packaged API with `convex-test` and a package-install example. +- [ ] Publish npm only with explicit authorization. +- [ ] Submit to the Convex Components directory only when the deterministic manifest says ready. + +### Backend portability + +- [x] Keep canonical IDs, events, schemas, receipts, and React view models provider-neutral. +- [x] Maintain one authoritative transactional backend per deployment. +- [x] Publish adapter capability negotiation and conformance suites. +- [x] Implement generic PostgreSQL persistence and conditional version application. +- [ ] Implement the Supabase managed profile: Auth, RLS, Storage, Realtime, queues, and Cron. +- [ ] Prove export from Convex and import into Supabase with matching canonical artifact and receipt + hashes. + +### Distribution and learning + +- [ ] Complete review, merge, and package publication gates in dependency order. +- [ ] Produce a revision-bound presentation and demonstration packet. +- [ ] Certify editable export and reopen fidelity for presentation artifacts. +- [ ] Observe real application use and convert failures into minimized regression tasks. +- [ ] Store repeated corrections and winning references in NodeMem. +- [ ] Export accepted and rejected harness trajectories to NodeRL only after protected evaluation + exists. + +### Model Intelligence and Skill Compiler + +- [x] Add strict harness, model-observation, and model-capability-card schemas. +- [x] Require requested route, resolved provider/model, and harness/tool/context/skill hashes. +- [x] Separate cognitive, execution, artifact, and efficiency evaluation. +- [x] Add the shared behavioral failure taxonomy. +- [x] Add application-specific harness initialization without changing the blank factory. +- [x] Compile evidence registries and benchmark/harness hashes fail closed. +- [x] Add baseline, profile, inspect, and failure-diagnosis commands. +- [ ] Normalize real application evidence into the first provisional capability card. +- [x] Add executable role, domain, model-adapter, guardrail, and recovery skill contracts. +- [x] Add without-skill versus with-skill protected comparisons. +- [x] Add capability-driven routing, deterministic fallbacks, expiry, and decision receipts. +- [x] Add blind tournaments, independent critics, fresh-agent canaries, rollback, and NodeProof + promotion receipts. +- [x] Keep automatic skill and routing promotion disabled by default. + +### Knowledge Evolution Plane + +- [x] Add the governed six-layer knowledge hypergraph and portable schemas. +- [x] Add immutable multimodal evidence anchors and n-ary hyperedges. +- [x] Add proposal, validation, approval, conflict, apply, diff, and replay semantics. +- [x] Add graph CLI commands and Harness Gym proposal projection. +- [x] Add deterministic conformance and safety tests. +- [ ] Run protected real-task comparisons against flat retrieval, static graph, and search-only baselines. +- [ ] Add NodeGraph, Neo4j, Convex, and SQL provider projections in that order. +- [ ] Add graph-aware review UI only after the portable proposal contract is stable. + +### Frontend Specialist + +- [x] Add protected product-design, route, direction-set, benchmark, and decision contracts. +- [x] Default routing to unprofiled; require evidence before naming a preferred model. +- [x] Require three distinct directions and the full desktop/mobile view set. +- [x] Add independent pairwise selection, bounded repair, and prior-implementation preservation. +- [x] Require exact model, exact commit, fresh identity, screenshots, NodeProof, human approval, and + zero major findings for canary success. +- [x] Keep promotion and deployment disabled by default. +- [ ] Run the first complete tournament and fresh-browser canary in NodeVideo or NodeSlide. + +### Evolution Ledger + +- [x] Add event, assumption, invariant, evidence, and adoption schemas. +- [x] Enforce human-reviewed canonical events and immutable append-or-supersede history. +- [x] Verify Git provenance, content hashes, references, secrets, benchmark identity, screenshots, + model identity, invariant proof, and consumer adoption. +- [x] Add init, draft, record, verify, query, diff, materiality, docs, and graph-proposal commands. +- [x] Add generated chronology/adoption projections and CI drift/materiality gates. +- [x] Backfill domain-blank factory and rendered-browser-certification decisions. +- [x] Complete the NodeVideo topology-failure backfill against its exact source commit. +- [ ] Verify adoption of the frontend tournament invariant in a real consumer. + +## Ordered execution plan + +1. **Repair fresh-agent execution.** Run the Docker executor without the incompatible nested Linux + namespace while retaining the disposable outer container. Re-run the volunteer canary in a new + QA pass, then research-map and launch-presentation held-outs. +2. **Collect the developer timing matrix.** Automate isolated caches and retain all raw receipts; + do not optimize against undocumented samples. +3. **Run the five-person study.** Use the frozen candidate and uncoached prompt. +4. **Finish NodeVideo's authenticated wrapper.** Promote it from engineering consumer to the first + submission-grade consumer. +5. **Adopt Caseflow in NodeRoom/NodeSheet and NodeSlide.** Keep domain logic in each application and + require the shared conformance suite. +6. **Prove the NodeKit preview journey.** Deploy the exact candidate, run a fresh browser, export, + reopen, score, clean up, and bind deployment identity. +7. **Run the first Frontend Specialist tournament.** Freeze the product contract, render all three + directions across desktop and mobile, select with an independent critic, apply bounded repair, + and retain a fresh-browser NodeProof canary without promoting automatically. +8. **Complete Harness Gym v0 and Knowledge Evolution evaluation.** Freeze the NodeKit Builder Gym + benchmark, normalize one real model run into a provisional card, and run protected + flat/static/evolving graph cases. Record material conclusions in the Evolution Ledger. +9. **Extract Caseflow.** Only after three consumer implementations reveal the genuinely repeated + kernel. +10. **Package and submit.** Run `convex-test`, installed-example proof, independent review, explicit + publication approval, and the deterministic submission gate. +11. **Finish managed SQL portability.** The executable PostgreSQL adapter now passes the shared + conformance suite on PostgreSQL 17.10. Complete the Supabase managed profile and migration proof. + +## Why the remaining blockers are hard + +- Repeated timing requires controlled caches and many real samples; one fast run cannot prove ease. +- Fresh-agent evidence must come from a writable session with no conversational memory. A blocked + agent or a manifest that contradicts its final report is not a pass. +- Human usability can only be established with real people; simulated participants cannot replace + consented observation. +- Consumer convergence cannot be inferred from one application. Premature extraction would encode + NodeVideo-specific assumptions into a supposedly reusable component. +- Authentication and tenant ownership are security boundaries, not UI polish. +- Production proof requires credentials, exact revision identity, cleanup, and independently + validated exported artifacts. +- Harness self-improvement is unsafe unless candidate generation is separated from protected + evaluation and every promotion is reversible. + +## Final submission rule + +Do not submit because the architecture looks coherent or the demo looks polished. + +Submit only when: + +```text +repeated developer timing passes ++ three fresh-agent held-outs pass ++ five-person usability passes ++ exact preview proof passes ++ three authenticated Convex consumers pass ++ packaged Caseflow tests pass ++ independent ProofLoop verification passes ++ submissionReady === true +``` + +Until then, continue engineering and evidence collection under the explicit verdict: + +> **NodeKit is a strong engineering foundation and a promising product system. It is not yet an +> ease-certified or submission-ready Convex component.** diff --git a/docs/NODEKIT_ULTRA_V1_HANDOFF.md b/docs/NODEKIT_ULTRA_V1_HANDOFF.md index 6e9ee16..ab67543 100644 --- a/docs/NODEKIT_ULTRA_V1_HANDOFF.md +++ b/docs/NODEKIT_ULTRA_V1_HANDOFF.md @@ -1,5 +1,7 @@ # NodeKit Ultra V1: overnight delivery and morning verification +> Historical handoff: the preset-first factory described below has been superseded by the domain-blank foundation in `NODEKIT_FIGURED_OUT_HANDOFF.md`. Domain examples now live under `reference-apps/` and require the explicit `nodekit reference create` command. + ## Honest outcome This delivery makes NodeKit usable for a **next bounded application** without diff --git a/docs/RELEASE_LADDER.md b/docs/RELEASE_LADDER.md new file mode 100644 index 0000000..7663947 --- /dev/null +++ b/docs/RELEASE_LADDER.md @@ -0,0 +1,51 @@ +# NodeKit release and certification ladder + +Engineering readiness, developer alpha, ease certification, Convex adapter maturity, Convex submission, and backend portability are separate targets. A later product-validation gate must not indefinitely block an earlier engineering decision. + +## E0 - Engineering foundation + +Status: **ready for independent PR review**. + +Requires green CI, reviewed contracts, honest claims, valid evidence manifests, compatibility review, and no unresolved P0/P1 engineering defects. E0 does not claim that unfamiliar people or agents find NodeKit easy. + +## E1 - Developer alpha + +Status: **incomplete**. + +Requires an immutable packaged install, cross-platform smoke, one authentic fresh-agent specialization canary, one revision-bound shareable preview, fresh-user browser completion, an honest limitations page, and no unresolved P0/P1 alpha defects. + +## E2 - Ease beta + +Status: **incomplete**. + +Requires repeated setup measurements, a transparently reported fresh-agent pass rate, formative developer and end-user studies, and repair of every P0/P1 usability finding. Small timing samples report `n`, median, range, and observed maximum, not a misleading p95. + +## E3 - Ease certified + +Status: **not certified**. + +Requires a second confirmation cohort on a frozen repaired candidate, a stable agent success threshold, meaningful timing statistics, deployed first-user proof, and aggregate independent NodeProof verification. + +## C1 - Convex adapter proven + +Status: **not yet proven**. + +Requires one real consumer, two-session reactive state, exactly-once proposal application, conflict and reload safety, revision-bound preview deployment, and a fresh-user receipt. + +## C2 - Convex component candidate + +Status: **blocked by consumer evidence**. + +Requires three materially different consumers, an extracted repeated kernel, `convex-test`, an installable example, typed validators, and an upgrade story. + +## C3 - Convex directory submission + +Status: **blocked**. + +Requires publication authority, a published npm package, final component proof, documentation, and explicit submission approval. + +## P1 - Backend portability + +Status: **future work**. + +Requires a PostgreSQL implementation, a Supabase managed profile, export/import proof, and matching observable Caseflow semantics. Convex-first must never be presented as Convex-only. diff --git a/docs/REMAINING_GAPS.md b/docs/REMAINING_GAPS.md new file mode 100644 index 0000000..ab9cb34 --- /dev/null +++ b/docs/REMAINING_GAPS.md @@ -0,0 +1,46 @@ +# NodeKit remaining-gap ledger + +Status date: 2026-07-21 + +This is the authoritative distinction between work NodeKit can close inside this repository and evidence that must come from independent people, authenticated applications, or provisioned services. A missing external receipt is a real blocker, not permission to synthesize one. + +## Closed in the repository + +- [x] Domain-blank factory, portable compiled identity, deterministic memory Caseflow, proposal-before-apply, conflict safety, receipts, and no-key demo. +- [x] Rendered browser certification across 15 lifecycle states, six viewport/theme profiles, exact PNG sidecars, Playwright trace/video, accessibility, console, network, overflow, mojibake, reload, and receipt checks. +- [x] Cold/warm cache isolation mechanics and normalized per-run timing receipts for all seven required phase measurements. +- [x] Cross-platform GitHub matrix definition for Windows, Ubuntu, and macOS with npm and pnpm. +- [x] Developer-timing aggregation and a fail-closed 60-run evaluator. +- [x] Disposable-container fresh-agent executor with read-only credentials, zero-reprompt ledger, substantive-edit gate, generated checks, screenshots, diff, transcript, and proof capture. +- [x] Model observation and capability-card compiler with exact requested/resolved model identity and project-first evidence precedence. +- [x] Executable skill contracts, evidence-threshold candidate proposals, fixed protected comparisons, regression rejection, routing decisions, independent tournaments, canary verification, manual proof-bound promotion, and rollback. +- [x] Automatic skill and routing promotion remains disabled. +- [x] Provider capability negotiation and the shared Caseflow conformance suite. +- [x] PostgreSQL schema foundation with transactional row locks and stale-proposal conditional apply. +- [x] Executable, owner-scoped PostgreSQL adapter with public TypeScript API and exact-revision live + conformance on PostgreSQL 17.10, including same-base races, retries, recovery, reload, and receipts. +- [x] Supabase managed-profile foundation with owner-scoped RLS policies and explicit Realtime tables. +- [x] Submission manifest schema and evaluator requiring all evidence hashes plus explicit publication approval. + +## Evidence collection still required + +| Gate | Exact closure condition | Why it cannot be manufactured locally | +|---|---|---| +| Developer timing matrix | Five cold and five warm receipts for each Windows/Ubuntu/macOS x npm/pnpm lane; 60 total | Requires repeated independent hosted runners; automation exists but results must execute against the immutable revision | +| Fresh coding agents | Research-map, volunteer-onboarding, and launch-presentation held-outs all pass with zero reprompts | Requires three actual isolated model executions and their authentic transcripts | +| Fresh humans | Five consented participants; at least four unassisted; timing and SEQ thresholds pass; no P0/P1 issue remains | Agent simulations are not human usability evidence | +| Convex consumers | Authenticated NodeRoom/NodeSheet, NodeSlide, and NodeVideo wrappers each pass lifecycle and ownership conformance | The component boundary can only be discovered from real, materially different consumers | +| Preview proof | Exact commit deployed to isolated frontend/backend; fresh identity uploads real bytes, completes, exports, reopens, scores, and cleans up | Requires live deployment credentials and a deployment-bound browser journey | +| Supabase profile | Auth, RLS, Storage, Realtime, Queue, and Cron pass owner-scoped live conformance | Requires a provisioned Supabase project and authenticated multi-user tests | +| Final ProofLoop | Independent verifier re-hashes the final immutable candidate and every release receipt | Must run after all other receipts exist | +| Package/publication | Packed install example passes, npm publication is authorized, and a human approves final public claims | Publication is externally visible and intentionally last | + +## Deterministic final rule + +Copy `proof/submission-manifest.template.json` to `proof/submission-manifest.json` only after real evidence exists. Bind every file by SHA-256, then run: + +```bash +npm run submission:evaluate +``` + +The Convex submission is authorized only when the resulting `proof/submission-verdict.json` contains both `passed: true` and `submissionReady: true`. Until then the honest verdict remains `EASE_NOT_CERTIFIED` and **DO NOT SUBMIT**. diff --git a/evals/ease/heldout-tasks.json b/evals/ease/heldout-tasks.json new file mode 100644 index 0000000..dc2606e --- /dev/null +++ b/evals/ease/heldout-tasks.json @@ -0,0 +1,17 @@ +{ + "schemaVersion": "nodekit.agent-ease-tasks/v1", + "tasks": [ + { + "id": "research-map", + "goal": "Use this repository to build an application that helps a researcher compare the latest work on a narrowly defined Agentic RL question and produce a source-backed research map.\n\nResearch the current workflow, build the smallest useful vertical slice, and prove it end to end. Continue until complete or legitimately blocked." + }, + { + "id": "volunteer-onboarding", + "goal": "Use this repository to build an application that helps a small nonprofit onboard a volunteer from application through document review and confirmed completion.\n\nResearch the current workflow, build the smallest useful vertical slice, and prove it end to end. Continue until complete or legitimately blocked." + }, + { + "id": "launch-presentation", + "goal": "Use this repository to build an application that helps a founder turn a product brief and metrics file into a reviewable launch presentation.\n\nResearch the current workflow, build the smallest useful vertical slice, and prove it end to end. Continue until complete or legitimately blocked." + } + ] +} diff --git a/evolution/adoptions/adp-nodekit-browser-certification.json b/evolution/adoptions/adp-nodekit-browser-certification.json new file mode 100644 index 0000000..c14a888 --- /dev/null +++ b/evolution/adoptions/adp-nodekit-browser-certification.json @@ -0,0 +1,9 @@ +{ + "schemaVersion": "nodekit.evolution-adoption/v1", + "id": "adp:nodekit-browser-certification", + "invariantId": "inv:rendered-browser-evidence", + "consumer": { "repository": "HomenShum/node-platform", "application": "NodeKit factory", "package": "@homenshum/nodekit" }, + "adoptedAtCommit": "e398398d7f1dd4ff0b65409d2c8da971e83bc488", + "evidenceIds": ["evd:rendered-browser-certification"], + "status": "verified" +} diff --git a/evolution/adoptions/adp-nodekit-domain-blank.json b/evolution/adoptions/adp-nodekit-domain-blank.json new file mode 100644 index 0000000..68ffed1 --- /dev/null +++ b/evolution/adoptions/adp-nodekit-domain-blank.json @@ -0,0 +1,9 @@ +{ + "schemaVersion": "nodekit.evolution-adoption/v1", + "id": "adp:nodekit-domain-blank", + "invariantId": "inv:domain-blank-create", + "consumer": { "repository": "HomenShum/node-platform", "application": "NodeKit factory", "package": "@homenshum/nodekit" }, + "adoptedAtCommit": "8d4f7e6e8235751d07c456208f841b15edf8de39", + "evidenceIds": ["evd:domain-blank-factory"], + "status": "verified" +} diff --git a/evolution/adoptions/adp-nodekit-frontend-tournament.json b/evolution/adoptions/adp-nodekit-frontend-tournament.json new file mode 100644 index 0000000..7b70669 --- /dev/null +++ b/evolution/adoptions/adp-nodekit-frontend-tournament.json @@ -0,0 +1,12 @@ +{ + "schemaVersion": "nodekit.evolution-adoption/v1", + "id": "adp:nodekit-frontend-tournament", + "invariantId": "inv:major-frontend-direction-tournament", + "consumer": { + "repository": "HomenShum/node-platform", + "package": "@homenshum/nodekit" + }, + "adoptedAtCommit": "678f2e0b82c2c50c3741c8bbb2a80ed95ff5b159", + "evidenceIds": [], + "status": "declared" +} diff --git a/evolution/artifacts/nodevideo-topology-failure.md b/evolution/artifacts/nodevideo-topology-failure.md new file mode 100644 index 0000000..885118a --- /dev/null +++ b/evolution/artifacts/nodevideo-topology-failure.md @@ -0,0 +1,5 @@ +# NodeVideo topology failure observation + +The generated interface was visually polished but organized as a proof dashboard rather than the requested creator workspace. The user could not keep the primary media artifact, agent context, and proposal review boundary legible across desktop and mobile. + +This is human-reviewed failure evidence, not proof that any model is universally weak. The scoped response is a protected product-design contract, three rendered directions, independent criticism, explicit mobile topology, and NodeProof-controlled completion. diff --git a/evolution/assumptions/asm-caseflow-idempotency-can-be-inferred.json b/evolution/assumptions/asm-caseflow-idempotency-can-be-inferred.json new file mode 100644 index 0000000..eda0d27 --- /dev/null +++ b/evolution/assumptions/asm-caseflow-idempotency-can-be-inferred.json @@ -0,0 +1,23 @@ +{ + "schemaVersion": "nodekit.assumption/v1", + "id": "asm:caseflow-idempotency-can-be-inferred", + "statement": "A lifecycle adapter can be treated as idempotent without executing the same start, decision, and completion operations twice.", + "scope": { + "applications": [ + "NodeKit factory", + "NodeRoom", + "NodeSlide", + "NodeVideo" + ], + "taskFamilies": [ + "Caseflow adapter conformance" + ] + }, + "status": "disproven", + "introducedByEventId": "evt:caseflow-idempotent-retries", + "invalidatedByEventId": "evt:caseflow-idempotent-retries", + "supportingEvidenceIds": [], + "contradictingEvidenceIds": [ + "evd:caseflow-idempotent-retries" + ] +} diff --git a/evolution/assumptions/asm-domain-preset-harmless.json b/evolution/assumptions/asm-domain-preset-harmless.json new file mode 100644 index 0000000..ab96dcc --- /dev/null +++ b/evolution/assumptions/asm-domain-preset-harmless.json @@ -0,0 +1,11 @@ +{ + "schemaVersion": "nodekit.assumption/v1", + "id": "asm:domain-preset-harmless", + "statement": "Silently choosing a reference workflow is harmless when the user makes no domain choice.", + "scope": { "applications": ["NodeKit factory"], "taskFamilies": ["new-agent-application"] }, + "status": "disproven", + "introducedByEventId": "evt:domain-blank-factory", + "invalidatedByEventId": "evt:domain-blank-factory", + "supportingEvidenceIds": [], + "contradictingEvidenceIds": ["evd:domain-blank-factory"] +} diff --git a/evolution/assumptions/asm-evidence-filename-sufficient.json b/evolution/assumptions/asm-evidence-filename-sufficient.json new file mode 100644 index 0000000..b635079 --- /dev/null +++ b/evolution/assumptions/asm-evidence-filename-sufficient.json @@ -0,0 +1,20 @@ +{ + "schemaVersion": "nodekit.assumption/v1", + "id": "asm:evidence-filename-sufficient", + "statement": "A familiar proof filename and matching file hash are enough to certify a later NodeKit revision.", + "scope": { + "applications": [ + "NodeKit factory" + ], + "taskFamilies": [ + "release-certification" + ] + }, + "status": "disproven", + "introducedByEventId": "evt:exact-candidate-contract", + "invalidatedByEventId": "evt:exact-candidate-contract", + "supportingEvidenceIds": [], + "contradictingEvidenceIds": [ + "evd:exact-candidate-gate" + ] +} diff --git a/evolution/assumptions/asm-postgres-ddl-is-adapter.json b/evolution/assumptions/asm-postgres-ddl-is-adapter.json new file mode 100644 index 0000000..a5d0ac3 --- /dev/null +++ b/evolution/assumptions/asm-postgres-ddl-is-adapter.json @@ -0,0 +1,23 @@ +{ + "schemaVersion": "nodekit.assumption/v1", + "id": "asm:postgres-ddl-is-adapter", + "statement": "A transactional PostgreSQL DDL foundation is sufficient to claim an implemented NodeKit backend adapter.", + "scope": { + "applications": [ + "NodeKit factory" + ], + "packages": [ + "@homenshum/nodekit" + ], + "taskFamilies": [ + "backend portability" + ] + }, + "status": "disproven", + "introducedByEventId": "evt:domain-blank-factory", + "invalidatedByEventId": "evt:postgres-caseflow-adapter", + "supportingEvidenceIds": [], + "contradictingEvidenceIds": [ + "evd:postgres-caseflow-conformance" + ] +} diff --git a/evolution/assumptions/asm-strong-model-infers-topology.json b/evolution/assumptions/asm-strong-model-infers-topology.json new file mode 100644 index 0000000..cb8b0f1 --- /dev/null +++ b/evolution/assumptions/asm-strong-model-infers-topology.json @@ -0,0 +1,19 @@ +{ + "schemaVersion": "nodekit.assumption/v1", + "id": "asm:strong-model-infers-topology", + "statement": "A strong general model can infer the intended product topology from an open-ended frontend request without a protected product contract or rendered direction comparison.", + "scope": { + "applications": [ + "NodeVideo" + ], + "taskFamilies": [ + "major-frontend-generation" + ] + }, + "status": "scope-limited", + "introducedByEventId": "evt:nodevideo-topology-contract", + "supportingEvidenceIds": [], + "contradictingEvidenceIds": [ + "evd:nodevideo-topology-failure" + ] +} diff --git a/evolution/assumptions/asm-structural-browser-proof-sufficient.json b/evolution/assumptions/asm-structural-browser-proof-sufficient.json new file mode 100644 index 0000000..1466813 --- /dev/null +++ b/evolution/assumptions/asm-structural-browser-proof-sufficient.json @@ -0,0 +1,11 @@ +{ + "schemaVersion": "nodekit.assumption/v1", + "id": "asm:structural-browser-proof-sufficient", + "statement": "HTTP reachability and DOM string checks are sufficient browser certification.", + "scope": { "applications": ["NodeKit factory"], "taskFamilies": ["production-certification"] }, + "status": "disproven", + "introducedByEventId": "evt:rendered-browser-certification", + "invalidatedByEventId": "evt:rendered-browser-certification", + "supportingEvidenceIds": [], + "contradictingEvidenceIds": ["evd:rendered-browser-certification"] +} diff --git a/evolution/events/architecture/evt-caseflow-idempotent-retries.json b/evolution/events/architecture/evt-caseflow-idempotent-retries.json new file mode 100644 index 0000000..f743df5 --- /dev/null +++ b/evolution/events/architecture/evt-caseflow-idempotent-retries.json @@ -0,0 +1,36 @@ +{ + "schemaVersion": "nodekit.evolution-event/v1", + "id": "evt:caseflow-idempotent-retries", + "projectId": "nodekit", + "repository": "HomenShum/node-platform", + "source": { + "commitSha": "f3471c7fd31b4839ffeb9c9f43bd0d4ab7ef6bfc", + "occurredAt": "2026-07-22T01:08:47-07:00" + }, + "track": "architecture", + "category": "runtime", + "challenge": "The portable Caseflow conformance suite named idempotency as a requirement without repeating decisions or completion calls.", + "observedFailure": "An adapter could pass conformance while duplicating artifact versions, approvals, or receipts during an ordinary retry.", + "resolution": "Caseflow now reuses an active run, returns the original approval for a repeated matching decision, returns the original completion receipt, and verifies all three behaviors in shared conformance.", + "assumptionIds": [ + "asm:caseflow-idempotency-can-be-inferred" + ], + "invariantIds": [ + "inv:caseflow-idempotent-retries" + ], + "evidenceIds": [ + "evd:caseflow-idempotent-retries" + ], + "predecessorIds": [ + "evt:exact-candidate-contract" + ], + "knownLimitations": [ + "Each provider adapter must still demonstrate this contract against its real transactional backend.", + "Cross-tenant authorization remains an application-wrapper responsibility and is tested separately by each consumer." + ], + "interpretation": { + "status": "human-reviewed", + "reviewedBy": "project-owner", + "reviewedAt": "2026-07-22T01:08:47-07:00" + } +} diff --git a/evolution/events/architecture/evt-exact-candidate-contract.json b/evolution/events/architecture/evt-exact-candidate-contract.json new file mode 100644 index 0000000..4e1b66d --- /dev/null +++ b/evolution/events/architecture/evt-exact-candidate-contract.json @@ -0,0 +1,38 @@ +{ + "schemaVersion": "nodekit.evolution-event/v1", + "id": "evt:exact-candidate-contract", + "projectId": "nodekit", + "repository": "HomenShum/node-platform", + "source": { + "commitSha": "1df155370258239ddd315c1f8842ecf0aa55b7e0", + "occurredAt": "2026-07-22T01:00:51-07:00" + }, + "track": "architecture", + "category": "release", + "challenge": "Historical receipts and internal deep imports could be mistaken for current package and submission guarantees.", + "observedFailure": "Existing proof aliases mixed source identities, omitted decisive revision fields, and exposed Caseflow through an unstable internal path.", + "resolution": "NodeKit now exposes a supported Caseflow package entry point and requires eight distinct, contract-valid decisive verdicts bound to one clean candidate revision before submission.", + "assumptionIds": [ + "asm:evidence-filename-sufficient" + ], + "invariantIds": [ + "inv:exact-candidate-evidence", + "inv:stable-caseflow-package-entrypoint" + ], + "evidenceIds": [ + "evd:exact-candidate-gate", + "evd:caseflow-public-api" + ], + "predecessorIds": [ + "evt:rendered-browser-certification" + ], + "knownLimitations": [ + "Current-revision timing, fresh-agent, human, consumer, preview, package, and independent ProofLoop evidence still must be collected.", + "The public Caseflow entry point is portable, but no authenticated Convex consumer has yet earned submission-grade adoption status." + ], + "interpretation": { + "status": "human-reviewed", + "reviewedBy": "project-owner", + "reviewedAt": "2026-07-22T01:00:51-07:00" + } +} diff --git a/evolution/events/architecture/evt-postgres-caseflow-adapter.json b/evolution/events/architecture/evt-postgres-caseflow-adapter.json new file mode 100644 index 0000000..f7f93fa --- /dev/null +++ b/evolution/events/architecture/evt-postgres-caseflow-adapter.json @@ -0,0 +1,36 @@ +{ + "schemaVersion": "nodekit.evolution-event/v1", + "id": "evt:postgres-caseflow-adapter", + "projectId": "nodekit", + "repository": "HomenShum/node-platform", + "source": { + "commitSha": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "occurredAt": "2026-07-22T01:41:03-07:00" + }, + "track": "architecture", + "category": "runtime", + "challenge": "A checked-in SQL schema was being tracked as portability progress even though no executable NodeKit runtime implemented or proved the full Caseflow contract.", + "observedFailure": "The PostgreSQL layer lacked cases, runs, approvals, exceptions, receipts, owner-scoped runtime methods, public package exports, TypeScript declarations, and live provider conformance.", + "resolution": "NodeKit now ships a driver-neutral PostgreSQL Caseflow adapter, complete transactional schema, owner-scoped operations, stable public and typed exports, a disposable-provider conformance runner, and a complete Supabase RLS projection over the portable tables.", + "assumptionIds": [ + "asm:postgres-ddl-is-adapter" + ], + "invariantIds": [ + "inv:postgres-caseflow-conformance" + ], + "evidenceIds": [ + "evd:postgres-caseflow-conformance" + ], + "predecessorIds": [ + "evt:caseflow-idempotent-retries" + ], + "knownLimitations": [ + "The Supabase SQL projection has not yet passed authenticated live Auth, Storage, Realtime, Queue, and Cron conformance.", + "PostgreSQL subscriptions use polling and durable jobs remain an external runtime capability by design." + ], + "interpretation": { + "status": "human-reviewed", + "reviewedBy": "project-owner", + "reviewedAt": "2026-07-22T01:41:03-07:00" + } +} diff --git a/evolution/events/architecture/evt-rendered-browser-certification.json b/evolution/events/architecture/evt-rendered-browser-certification.json new file mode 100644 index 0000000..7213a63 --- /dev/null +++ b/evolution/events/architecture/evt-rendered-browser-certification.json @@ -0,0 +1,17 @@ +{ + "schemaVersion": "nodekit.evolution-event/v1", + "id": "evt:rendered-browser-certification", + "projectId": "nodekit", + "repository": "HomenShum/node-platform", + "source": { "commitSha": "e398398d7f1dd4ff0b65409d2c8da971e83bc488", "occurredAt": "2026-07-21T20:52:14-07:00" }, + "track": "architecture", + "category": "evaluation", + "challenge": "Structural availability was mislabeled as browser certification.", + "observedFailure": "HTTP and DOM checks could pass without proving the rendered user journey.", + "resolution": "Structural checks and rendered Playwright evidence are separate, revision-bound proof classes.", + "assumptionIds": ["asm:structural-browser-proof-sufficient"], + "invariantIds": ["inv:rendered-browser-evidence"], + "evidenceIds": ["evd:rendered-browser-certification"], + "knownLimitations": ["External fresh-user timing evidence remains a separate submission gate."], + "interpretation": { "status": "human-reviewed", "reviewedBy": "founder", "reviewedAt": "2026-07-21T20:52:14-07:00" } +} diff --git a/evolution/events/harness/evt-nodevideo-topology-contract.json b/evolution/events/harness/evt-nodevideo-topology-contract.json new file mode 100644 index 0000000..709edf2 --- /dev/null +++ b/evolution/events/harness/evt-nodevideo-topology-contract.json @@ -0,0 +1,33 @@ +{ + "schemaVersion": "nodekit.evolution-event/v1", + "id": "evt:nodevideo-topology-contract", + "projectId": "nodekit", + "repository": "HomenShum/node-platform", + "source": { + "commitSha": "678f2e0b82c2c50c3741c8bbb2a80ed95ff5b159", + "occurredAt": "2026-07-22T00:36:59-07:00" + }, + "track": "harness", + "category": "ui", + "challenge": "Visually polished frontend output could still miss the intended creator-workspace topology.", + "observedFailure": "NodeVideo was organized as a proof dashboard instead of keeping the primary media artifact, agent context, and proposal review boundary legible across desktop and mobile.", + "resolution": "Added Frontend Specialist Routing with a protected product contract, three required rendered directions, independent criticism, explicit mobile topology, bounded repair, and a NodeProof-controlled canary.", + "assumptionIds": [ + "asm:strong-model-infers-topology" + ], + "invariantIds": [ + "inv:major-frontend-direction-tournament" + ], + "evidenceIds": [ + "evd:nodevideo-topology-failure" + ], + "knownLimitations": [ + "No real NodeVideo or NodeSlide consumer has yet completed the three-direction tournament and fresh-browser canary.", + "No exact resolved model identity was preserved for the historical failed attempt, so this event makes no model capability claim." + ], + "interpretation": { + "status": "human-reviewed", + "reviewedBy": "project-owner", + "reviewedAt": "2026-07-22T00:36:59-07:00" + } +} diff --git a/evolution/events/product/evt-domain-blank-factory.json b/evolution/events/product/evt-domain-blank-factory.json new file mode 100644 index 0000000..2972044 --- /dev/null +++ b/evolution/events/product/evt-domain-blank-factory.json @@ -0,0 +1,17 @@ +{ + "schemaVersion": "nodekit.evolution-event/v1", + "id": "evt:domain-blank-factory", + "projectId": "nodekit", + "repository": "HomenShum/node-platform", + "source": { "commitSha": "8d4f7e6e8235751d07c456208f841b15edf8de39", "occurredAt": "2026-07-21T15:53:03-07:00" }, + "track": "product", + "category": "workflow", + "challenge": "The primary initializer silently specialized the product.", + "observedFailure": "A blank creation command selected a reference workflow without user authority.", + "resolution": "NodeKit now exposes one domain-blank factory and keeps narrow applications as references.", + "assumptionIds": ["asm:domain-preset-harmless"], + "invariantIds": ["inv:domain-blank-create"], + "evidenceIds": ["evd:domain-blank-factory"], + "knownLimitations": ["Reference applications still require explicit user selection after creation."], + "interpretation": { "status": "human-reviewed", "reviewedBy": "founder", "reviewedAt": "2026-07-21T15:53:03-07:00" } +} diff --git a/evolution/events/product/evt-submission-cli-named-options.json b/evolution/events/product/evt-submission-cli-named-options.json new file mode 100644 index 0000000..2381345 --- /dev/null +++ b/evolution/events/product/evt-submission-cli-named-options.json @@ -0,0 +1,34 @@ +{ + "schemaVersion": "nodekit.evolution-event/v1", + "id": "evt:submission-cli-named-options", + "projectId": "nodekit", + "repository": "HomenShum/node-platform", + "source": { + "commitSha": "d0d9bb5b0a2cde1cdc2b236025ac985595613a5d", + "occurredAt": "2026-07-22T01:20:48-07:00" + }, + "track": "product", + "category": "release", + "challenge": "The natural named-option form for selecting a submission candidate failed before evidence evaluation began.", + "observedFailure": "npm run submission:prepare -- --candidate treated --candidate as a literal Git reference and produced a spawn error instead of a readiness manifest.", + "resolution": "Submission preparation and evaluation now accept explicit named options, retain positional compatibility, reject unknown options clearly, and have subprocess coverage for the public command form.", + "assumptionIds": [], + "invariantIds": [ + "inv:submission-cli-explicit-options" + ], + "evidenceIds": [ + "evd:submission-cli-explicit-options" + ], + "predecessorIds": [ + "evt:exact-candidate-contract" + ], + "knownLimitations": [ + "A generated readiness manifest remains fail-closed until every external evidence gate passes.", + "This change improves command usability but does not authorize publication or Convex submission." + ], + "interpretation": { + "status": "human-reviewed", + "reviewedBy": "project-owner", + "reviewedAt": "2026-07-22T01:20:48-07:00" + } +} diff --git a/evolution/evidence/evd-caseflow-idempotent-retries.json b/evolution/evidence/evd-caseflow-idempotent-retries.json new file mode 100644 index 0000000..3b11d42 --- /dev/null +++ b/evolution/evidence/evd-caseflow-idempotent-retries.json @@ -0,0 +1,18 @@ +{ + "schemaVersion": "nodekit.evolution-evidence/v1", + "id": "evd:caseflow-idempotent-retries", + "kind": "test", + "artifactRef": "git:f3471c7fd31b4839ffeb9c9f43bd0d4ab7ef6bfc:src/lib/caseflow-conformance.mjs", + "sha256": "24371533189699e1a519bf331e0c4babe95e9c1b985191ef738fff8351b37481", + "sourceCommit": "f3471c7fd31b4839ffeb9c9f43bd0d4ab7ef6bfc", + "generatedAt": "2026-07-22T01:08:47-07:00", + "command": "npm run check", + "environment": { + "repository": "HomenShum/node-platform", + "tests": "105 passed" + }, + "verifiesInvariantIds": [ + "inv:caseflow-idempotent-retries" + ], + "result": "pass" +} diff --git a/evolution/evidence/evd-caseflow-public-api.json b/evolution/evidence/evd-caseflow-public-api.json new file mode 100644 index 0000000..1962723 --- /dev/null +++ b/evolution/evidence/evd-caseflow-public-api.json @@ -0,0 +1,18 @@ +{ + "schemaVersion": "nodekit.evolution-evidence/v1", + "id": "evd:caseflow-public-api", + "kind": "code", + "artifactRef": "git:1df155370258239ddd315c1f8842ecf0aa55b7e0:src/caseflow.mjs", + "sha256": "08c8c36a131b8ad6fdc8ba84e25f1a5331adc5f745936bbe9b756b32952856a1", + "sourceCommit": "1df155370258239ddd315c1f8842ecf0aa55b7e0", + "generatedAt": "2026-07-22T01:00:51-07:00", + "command": "node --test test/public-api.test.mjs test/caseflow.test.mjs test/factory.test.mjs", + "environment": { + "repository": "HomenShum/node-platform", + "entrypoint": "@homenshum/nodekit/caseflow" + }, + "verifiesInvariantIds": [ + "inv:stable-caseflow-package-entrypoint" + ], + "result": "pass" +} diff --git a/evolution/evidence/evd-domain-blank-factory.json b/evolution/evidence/evd-domain-blank-factory.json new file mode 100644 index 0000000..71672ce --- /dev/null +++ b/evolution/evidence/evd-domain-blank-factory.json @@ -0,0 +1,14 @@ +{ + "schemaVersion": "nodekit.evolution-evidence/v1", + "id": "evd:domain-blank-factory", + "kind": "code", + "artifactRef": "git:8d4f7e6e8235751d07c456208f841b15edf8de39:src/lib/scaffold.mjs", + "sha256": "53e2201233593f537f3ec729e48f920b1a9a7ce3879168f4fe5b24e6c376409a", + "sourceCommit": "8d4f7e6e8235751d07c456208f841b15edf8de39", + "generatedAt": "2026-07-21T15:53:03-07:00", + "command": "node --test test/factory.test.mjs", + "environment": { "repository": "HomenShum/node-platform" }, + "verifiesInvariantIds": ["inv:domain-blank-create"], + "nodeProofReceiptId": "nodeproof:factory-8d4f7e6", + "result": "pass" +} diff --git a/evolution/evidence/evd-exact-candidate-gate.json b/evolution/evidence/evd-exact-candidate-gate.json new file mode 100644 index 0000000..0ccffcb --- /dev/null +++ b/evolution/evidence/evd-exact-candidate-gate.json @@ -0,0 +1,18 @@ +{ + "schemaVersion": "nodekit.evolution-evidence/v1", + "id": "evd:exact-candidate-gate", + "kind": "code", + "artifactRef": "git:1df155370258239ddd315c1f8842ecf0aa55b7e0:src/lib/submission-gate.mjs", + "sha256": "0a945ece362755e218b3b016f4c789b70671f8017643c2db201833d8f0c78002", + "sourceCommit": "1df155370258239ddd315c1f8842ecf0aa55b7e0", + "generatedAt": "2026-07-22T01:00:51-07:00", + "command": "npm run check", + "environment": { + "repository": "HomenShum/node-platform", + "tests": "104 passed" + }, + "verifiesInvariantIds": [ + "inv:exact-candidate-evidence" + ], + "result": "pass" +} diff --git a/evolution/evidence/evd-nodevideo-topology-failure.json b/evolution/evidence/evd-nodevideo-topology-failure.json new file mode 100644 index 0000000..be62745 --- /dev/null +++ b/evolution/evidence/evd-nodevideo-topology-failure.json @@ -0,0 +1,17 @@ +{ + "schemaVersion": "nodekit.evolution-evidence/v1", + "id": "evd:nodevideo-topology-failure", + "kind": "human-review", + "artifactRef": "git:678f2e0b82c2c50c3741c8bbb2a80ed95ff5b159:evolution/artifacts/nodevideo-topology-failure.md", + "sha256": "80676ad0c40ed9e38ae84c4af0aba7ccc979ef7c1d373914f4e312eafc808ac4", + "sourceCommit": "678f2e0b82c2c50c3741c8bbb2a80ed95ff5b159", + "generatedAt": "2026-07-22T00:36:59-07:00", + "environment": { + "reviewSurface": "NodeVideo", + "evidenceBoundary": "human-reviewed topology failure; not a universal model verdict" + }, + "verifiesInvariantIds": [ + "inv:major-frontend-direction-tournament" + ], + "result": "partial" +} diff --git a/evolution/evidence/evd-postgres-caseflow-conformance.json b/evolution/evidence/evd-postgres-caseflow-conformance.json new file mode 100644 index 0000000..a042299 --- /dev/null +++ b/evolution/evidence/evd-postgres-caseflow-conformance.json @@ -0,0 +1,19 @@ +{ + "schemaVersion": "nodekit.evolution-evidence/v1", + "id": "evd:postgres-caseflow-conformance", + "kind": "test", + "artifactRef": "proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/backend/postgres-conformance.json", + "sha256": "e76b5931769e68367b42cfc8ce777298bb281328bef7c5dec616508be63e4195", + "sourceCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "generatedAt": "2026-07-22T01:41:03-07:00", + "command": "NODEKIT_POSTGRES_URL= npm run conformance:postgres", + "environment": { + "database": "PostgreSQL 17.10 disposable Docker container", + "repository": "HomenShum/node-platform", + "sourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b" + }, + "verifiesInvariantIds": [ + "inv:postgres-caseflow-conformance" + ], + "result": "pass" +} diff --git a/evolution/evidence/evd-rendered-browser-certification.json b/evolution/evidence/evd-rendered-browser-certification.json new file mode 100644 index 0000000..cdc3489 --- /dev/null +++ b/evolution/evidence/evd-rendered-browser-certification.json @@ -0,0 +1,14 @@ +{ + "schemaVersion": "nodekit.evolution-evidence/v1", + "id": "evd:rendered-browser-certification", + "kind": "code", + "artifactRef": "git:e398398d7f1dd4ff0b65409d2c8da971e83bc488:templates/base/scripts/browser-certify.mjs", + "sha256": "83632d43ca75a7afc3d071cd9d1e05b8c779324a6cb17d7b935bfe9fdeaed239", + "sourceCommit": "e398398d7f1dd4ff0b65409d2c8da971e83bc488", + "generatedAt": "2026-07-21T20:52:14-07:00", + "command": "node --test test/ease-proof.test.mjs test/factory.test.mjs", + "environment": { "repository": "HomenShum/node-platform" }, + "verifiesInvariantIds": ["inv:rendered-browser-evidence"], + "nodeProofReceiptId": "nodeproof:ease-e398398", + "result": "pass" +} diff --git a/evolution/evidence/evd-submission-cli-explicit-options.json b/evolution/evidence/evd-submission-cli-explicit-options.json new file mode 100644 index 0000000..2d38497 --- /dev/null +++ b/evolution/evidence/evd-submission-cli-explicit-options.json @@ -0,0 +1,18 @@ +{ + "schemaVersion": "nodekit.evolution-evidence/v1", + "id": "evd:submission-cli-explicit-options", + "kind": "test", + "artifactRef": "git:d0d9bb5b0a2cde1cdc2b236025ac985595613a5d:test/submission-cli.test.mjs", + "sha256": "5a90c892d1df1b57014bcb08cebe39922a8d4156544a174c5f46fe882d7e705c", + "sourceCommit": "d0d9bb5b0a2cde1cdc2b236025ac985595613a5d", + "generatedAt": "2026-07-22T01:20:48-07:00", + "command": "node --test test/submission-cli.test.mjs test/submission-preparation.test.mjs test/submission-gate.test.mjs", + "environment": { + "repository": "HomenShum/node-platform", + "tests": "9 passed" + }, + "verifiesInvariantIds": [ + "inv:submission-cli-explicit-options" + ], + "result": "pass" +} diff --git a/evolution/invariants/inv-caseflow-idempotent-retries.json b/evolution/invariants/inv-caseflow-idempotent-retries.json new file mode 100644 index 0000000..89bbe0f --- /dev/null +++ b/evolution/invariants/inv-caseflow-idempotent-retries.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": "nodekit.invariant-claim/v1", + "id": "inv:caseflow-idempotent-retries", + "statement": "Repeating an active-run start, matching proposal decision, or completed-run request never creates an extra run, artifact version, approval, or receipt.", + "scope": { + "applications": [ + "NodeKit factory", + "NodeRoom", + "NodeSlide", + "NodeVideo" + ], + "packages": [ + "@homenshum/nodekit" + ], + "surfaces": [ + "Caseflow runtime", + "adapter conformance" + ] + }, + "enforcement": "test", + "verifierRefs": [ + "src/lib/caseflow-conformance.mjs", + "test/caseflow.test.mjs" + ], + "introducedByEventId": "evt:caseflow-idempotent-retries", + "status": "verified" +} diff --git a/evolution/invariants/inv-domain-blank-create.json b/evolution/invariants/inv-domain-blank-create.json new file mode 100644 index 0000000..578f138 --- /dev/null +++ b/evolution/invariants/inv-domain-blank-create.json @@ -0,0 +1,10 @@ +{ + "schemaVersion": "nodekit.invariant-claim/v1", + "id": "inv:domain-blank-create", + "statement": "The primary NodeKit creation path makes no domain decision for the user.", + "scope": { "applications": ["NodeKit factory"], "packages": ["@homenshum/nodekit"], "surfaces": ["nodekit create"] }, + "enforcement": "test", + "verifierRefs": ["test/factory.test.mjs#primary-create-rejects-domain-presets"], + "introducedByEventId": "evt:domain-blank-factory", + "status": "verified" +} diff --git a/evolution/invariants/inv-exact-candidate-evidence.json b/evolution/invariants/inv-exact-candidate-evidence.json new file mode 100644 index 0000000..8cf1ca4 --- /dev/null +++ b/evolution/invariants/inv-exact-candidate-evidence.json @@ -0,0 +1,26 @@ +{ + "schemaVersion": "nodekit.invariant-claim/v1", + "id": "inv:exact-candidate-evidence", + "statement": "Submission requires eight distinct decisive verdicts whose contracts, hashes, and candidate identity match one clean immutable NodeKit revision.", + "scope": { + "applications": [ + "NodeKit factory" + ], + "packages": [ + "@homenshum/nodekit" + ], + "surfaces": [ + "submission preparation", + "submission evaluation" + ] + }, + "enforcement": "runtime-gate", + "verifierRefs": [ + "src/lib/submission-gate.mjs", + "src/lib/submission-preparation.mjs", + "test/submission-gate.test.mjs", + "test/submission-preparation.test.mjs" + ], + "introducedByEventId": "evt:exact-candidate-contract", + "status": "verified" +} diff --git a/evolution/invariants/inv-major-frontend-direction-tournament.json b/evolution/invariants/inv-major-frontend-direction-tournament.json new file mode 100644 index 0000000..99a0053 --- /dev/null +++ b/evolution/invariants/inv-major-frontend-direction-tournament.json @@ -0,0 +1,20 @@ +{ + "schemaVersion": "nodekit.invariant-claim/v1", + "id": "inv:major-frontend-direction-tournament", + "statement": "A major frontend must preserve a protected product-design contract, render three materially distinct desktop/mobile directions, use independent pairwise selection, and pass a fresh-browser NodeProof canary before promotion.", + "scope": { + "applications": [ + "NodeKit applications" + ], + "surfaces": [ + "major frontend creation and redesign" + ] + }, + "enforcement": "runtime-gate", + "verifierRefs": [ + "src/lib/frontend-specialist.mjs", + "test/frontend-specialist.test.mjs" + ], + "introducedByEventId": "evt:nodevideo-topology-contract", + "status": "partially-verified" +} diff --git a/evolution/invariants/inv-postgres-caseflow-conformance.json b/evolution/invariants/inv-postgres-caseflow-conformance.json new file mode 100644 index 0000000..d9fee70 --- /dev/null +++ b/evolution/invariants/inv-postgres-caseflow-conformance.json @@ -0,0 +1,24 @@ +{ + "schemaVersion": "nodekit.invariant-claim/v1", + "id": "inv:postgres-caseflow-conformance", + "statement": "The PostgreSQL adapter is owner-scoped, transactionally applies at most one same-base proposal, preserves idempotent retries and recovery, survives reload, and emits a content-addressed completion receipt under the shared Caseflow contract.", + "scope": { + "applications": [ + "NodeKit factory" + ], + "packages": [ + "@homenshum/nodekit/adapters/postgres" + ], + "surfaces": [ + "PostgreSQL 17" + ] + }, + "enforcement": "test", + "verifierRefs": [ + "scripts/run-postgres-conformance.mjs", + "test/postgres-adapter.test.mjs", + "proof/postgres-conformance.json" + ], + "introducedByEventId": "evt:postgres-caseflow-adapter", + "status": "verified" +} diff --git a/evolution/invariants/inv-rendered-browser-evidence.json b/evolution/invariants/inv-rendered-browser-evidence.json new file mode 100644 index 0000000..d1658f9 --- /dev/null +++ b/evolution/invariants/inv-rendered-browser-evidence.json @@ -0,0 +1,10 @@ +{ + "schemaVersion": "nodekit.invariant-claim/v1", + "id": "inv:rendered-browser-evidence", + "statement": "Browser certification requires rendered interaction evidence bound to the exact generated candidate.", + "scope": { "applications": ["NodeKit factory"], "packages": ["@homenshum/nodekit"], "surfaces": ["browser certification"] }, + "enforcement": "runtime-gate", + "verifierRefs": ["templates/base/scripts/browser-certify.mjs", "test/ease-proof.test.mjs"], + "introducedByEventId": "evt:rendered-browser-certification", + "status": "verified" +} diff --git a/evolution/invariants/inv-stable-caseflow-package-entrypoint.json b/evolution/invariants/inv-stable-caseflow-package-entrypoint.json new file mode 100644 index 0000000..00df4c4 --- /dev/null +++ b/evolution/invariants/inv-stable-caseflow-package-entrypoint.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": "nodekit.invariant-claim/v1", + "id": "inv:stable-caseflow-package-entrypoint", + "statement": "Consumers use the supported NodeKit Caseflow export rather than coupling to an internal source-file path.", + "scope": { + "applications": [ + "NodeKit factory", + "NodeRoom", + "NodeSlide", + "NodeVideo" + ], + "packages": [ + "@homenshum/nodekit" + ], + "surfaces": [ + "Caseflow adapter conformance" + ] + }, + "enforcement": "test", + "verifierRefs": [ + "package.json", + "src/caseflow.mjs", + "test/public-api.test.mjs" + ], + "introducedByEventId": "evt:exact-candidate-contract", + "status": "verified" +} diff --git a/evolution/invariants/inv-submission-cli-explicit-options.json b/evolution/invariants/inv-submission-cli-explicit-options.json new file mode 100644 index 0000000..f07e128 --- /dev/null +++ b/evolution/invariants/inv-submission-cli-explicit-options.json @@ -0,0 +1,23 @@ +{ + "schemaVersion": "nodekit.invariant-claim/v1", + "id": "inv:submission-cli-explicit-options", + "statement": "Submission preparation and evaluation accept explicit named paths and candidate references, preserve positional compatibility, and fail clearly on unknown or incomplete options.", + "scope": { + "applications": [ + "NodeKit factory" + ], + "packages": [ + "@homenshum/nodekit" + ], + "surfaces": [ + "submission preparation CLI", + "submission evaluation CLI" + ] + }, + "enforcement": "test", + "verifierRefs": [ + "test/submission-cli.test.mjs" + ], + "introducedByEventId": "evt:submission-cli-named-options", + "status": "verified" +} diff --git a/evolution/ledger.json b/evolution/ledger.json new file mode 100644 index 0000000..786bbb0 --- /dev/null +++ b/evolution/ledger.json @@ -0,0 +1,26 @@ +{ + "schemaVersion": "nodekit.evolution-ledger/v1", + "authority": { + "canonicalRecords": "human-reviewed-only", + "mutation": "append-or-supersede", + "delete": "prohibited" + }, + "materiality": [ + "primary-user-workflow", + "public-contract", + "architectural-ownership", + "security-authority", + "proof-requirement", + "model-routing", + "harness-behavior", + "benchmark-conclusion", + "downstream-guarantee" + ], + "recordSchemas": [ + "nodekit.evolution-event/v1", + "nodekit.assumption/v1", + "nodekit.invariant-claim/v1", + "nodekit.evolution-evidence/v1", + "nodekit.evolution-adoption/v1" + ] +} diff --git a/evolution/projections/EVOLUTION.md b/evolution/projections/EVOLUTION.md new file mode 100644 index 0000000..538f76e --- /dev/null +++ b/evolution/projections/EVOLUTION.md @@ -0,0 +1,80 @@ +# NodeKit Evolution Ledger + +Canonical JSON records remain authoritative. This projection explains why material system guarantees exist. + +## Product evolution + +### The primary initializer silently specialized the product. + +- Event: `evt:domain-blank-factory` +- Source: `8d4f7e6e8235751d07c456208f841b15edf8de39` +- Resolution: NodeKit now exposes one domain-blank factory and keeps narrow applications as references. +- Observed failure: A blank creation command selected a reference workflow without user authority. +- Invariants: `inv:domain-blank-create` (verified) +- Evidence: `evd:domain-blank-factory` (pass) +- Known limitations: Reference applications still require explicit user selection after creation. + +### The natural named-option form for selecting a submission candidate failed before evidence evaluation began. + +- Event: `evt:submission-cli-named-options` +- Source: `d0d9bb5b0a2cde1cdc2b236025ac985595613a5d` +- Resolution: Submission preparation and evaluation now accept explicit named options, retain positional compatibility, reject unknown options clearly, and have subprocess coverage for the public command form. +- Observed failure: npm run submission:prepare -- --candidate treated --candidate as a literal Git reference and produced a spawn error instead of a readiness manifest. +- Invariants: `inv:submission-cli-explicit-options` (verified) +- Evidence: `evd:submission-cli-explicit-options` (pass) +- Known limitations: A generated readiness manifest remains fail-closed until every external evidence gate passes.; This change improves command usability but does not authorize publication or Convex submission. + +## Architecture evolution + +### Structural availability was mislabeled as browser certification. + +- Event: `evt:rendered-browser-certification` +- Source: `e398398d7f1dd4ff0b65409d2c8da971e83bc488` +- Resolution: Structural checks and rendered Playwright evidence are separate, revision-bound proof classes. +- Observed failure: HTTP and DOM checks could pass without proving the rendered user journey. +- Invariants: `inv:rendered-browser-evidence` (verified) +- Evidence: `evd:rendered-browser-certification` (pass) +- Known limitations: External fresh-user timing evidence remains a separate submission gate. + +### Historical receipts and internal deep imports could be mistaken for current package and submission guarantees. + +- Event: `evt:exact-candidate-contract` +- Source: `1df155370258239ddd315c1f8842ecf0aa55b7e0` +- Resolution: NodeKit now exposes a supported Caseflow package entry point and requires eight distinct, contract-valid decisive verdicts bound to one clean candidate revision before submission. +- Observed failure: Existing proof aliases mixed source identities, omitted decisive revision fields, and exposed Caseflow through an unstable internal path. +- Invariants: `inv:exact-candidate-evidence` (verified), `inv:stable-caseflow-package-entrypoint` (verified) +- Evidence: `evd:exact-candidate-gate` (pass), `evd:caseflow-public-api` (pass) +- Known limitations: Current-revision timing, fresh-agent, human, consumer, preview, package, and independent ProofLoop evidence still must be collected.; The public Caseflow entry point is portable, but no authenticated Convex consumer has yet earned submission-grade adoption status. + +### The portable Caseflow conformance suite named idempotency as a requirement without repeating decisions or completion calls. + +- Event: `evt:caseflow-idempotent-retries` +- Source: `f3471c7fd31b4839ffeb9c9f43bd0d4ab7ef6bfc` +- Resolution: Caseflow now reuses an active run, returns the original approval for a repeated matching decision, returns the original completion receipt, and verifies all three behaviors in shared conformance. +- Observed failure: An adapter could pass conformance while duplicating artifact versions, approvals, or receipts during an ordinary retry. +- Invariants: `inv:caseflow-idempotent-retries` (verified) +- Evidence: `evd:caseflow-idempotent-retries` (pass) +- Known limitations: Each provider adapter must still demonstrate this contract against its real transactional backend.; Cross-tenant authorization remains an application-wrapper responsibility and is tested separately by each consumer. + +### A checked-in SQL schema was being tracked as portability progress even though no executable NodeKit runtime implemented or proved the full Caseflow contract. + +- Event: `evt:postgres-caseflow-adapter` +- Source: `5cc61578b3c1bd5b5c8195b83347b91f8b83242b` +- Resolution: NodeKit now ships a driver-neutral PostgreSQL Caseflow adapter, complete transactional schema, owner-scoped operations, stable public and typed exports, a disposable-provider conformance runner, and a complete Supabase RLS projection over the portable tables. +- Observed failure: The PostgreSQL layer lacked cases, runs, approvals, exceptions, receipts, owner-scoped runtime methods, public package exports, TypeScript declarations, and live provider conformance. +- Invariants: `inv:postgres-caseflow-conformance` (verified) +- Evidence: `evd:postgres-caseflow-conformance` (pass) +- Known limitations: The Supabase SQL projection has not yet passed authenticated live Auth, Storage, Realtime, Queue, and Cron conformance.; PostgreSQL subscriptions use polling and durable jobs remain an external runtime capability by design. + +## Harness evolution + +### Visually polished frontend output could still miss the intended creator-workspace topology. + +- Event: `evt:nodevideo-topology-contract` +- Source: `678f2e0b82c2c50c3741c8bbb2a80ed95ff5b159` +- Resolution: Added Frontend Specialist Routing with a protected product contract, three required rendered directions, independent criticism, explicit mobile topology, bounded repair, and a NodeProof-controlled canary. +- Observed failure: NodeVideo was organized as a proof dashboard instead of keeping the primary media artifact, agent context, and proposal review boundary legible across desktop and mobile. +- Invariants: `inv:major-frontend-direction-tournament` (partially-verified) +- Evidence: `evd:nodevideo-topology-failure` (partial) +- Known limitations: No real NodeVideo or NodeSlide consumer has yet completed the three-direction tournament and fresh-browser canary.; No exact resolved model identity was preserved for the historical failed attempt, so this event makes no model capability claim. + diff --git a/evolution/projections/adoption-map.json b/evolution/projections/adoption-map.json new file mode 100644 index 0000000..0ea738b --- /dev/null +++ b/evolution/projections/adoption-map.json @@ -0,0 +1,35 @@ +[ + { + "invariantId": "inv:rendered-browser-evidence", + "consumer": { + "repository": "HomenShum/node-platform", + "application": "NodeKit factory", + "package": "@homenshum/nodekit" + }, + "status": "verified", + "evidenceIds": [ + "evd:rendered-browser-certification" + ] + }, + { + "invariantId": "inv:domain-blank-create", + "consumer": { + "repository": "HomenShum/node-platform", + "application": "NodeKit factory", + "package": "@homenshum/nodekit" + }, + "status": "verified", + "evidenceIds": [ + "evd:domain-blank-factory" + ] + }, + { + "invariantId": "inv:major-frontend-direction-tournament", + "consumer": { + "repository": "HomenShum/node-platform", + "package": "@homenshum/nodekit" + }, + "status": "declared", + "evidenceIds": [] + } +] diff --git a/infra/ease-agent/Dockerfile b/infra/ease-agent/Dockerfile new file mode 100644 index 0000000..e0e918f --- /dev/null +++ b/infra/ease-agent/Dockerfile @@ -0,0 +1,11 @@ +FROM node:22-bookworm + +ARG CODEX_VERSION=0.142.5 + +RUN apt-get update \ + && apt-get install -y --no-install-recommends ca-certificates git ripgrep \ + && rm -rf /var/lib/apt/lists/* \ + && npm install --global "@openai/codex@${CODEX_VERSION}" \ + && codex --version + +WORKDIR /workspace diff --git a/nodekit.yaml b/nodekit.yaml index 2185bde..69e99d3 100644 --- a/nodekit.yaml +++ b/nodekit.yaml @@ -8,6 +8,9 @@ commandProfile: platform canonicalFor: - nodeplatform.repo-contract - nodeplatform.change-story + - nodekit.knowledge-evolution + - nodekit.frontend-specialist + - nodekit.evolution-ledger consumes: - betterprhandoff.handoff @@ -40,5 +43,17 @@ proof: command: npm run check receiptSchema: nodeplatform.conformance/v1 -contractDeclarations: [] +contractDeclarations: + - concept: nodekit.knowledge-evolution + signature: knowledge-graph-schema + path: src/lib/knowledge-evolution.mjs + mode: canonical + - concept: nodekit.frontend-specialist + signature: frontend-route-schema + path: src/lib/frontend-specialist.mjs + mode: canonical + - concept: nodekit.evolution-ledger + signature: evolution-event-schema + path: src/lib/evolution-ledger.mjs + mode: canonical architectureExceptions: [] diff --git a/outputs/nodekit-figured-out-proof.pptx b/outputs/nodekit-figured-out-proof.pptx new file mode 100644 index 0000000..e1ef8ed Binary files /dev/null and b/outputs/nodekit-figured-out-proof.pptx differ diff --git a/ownership.yaml b/ownership.yaml index 796904f..762e856 100644 --- a/ownership.yaml +++ b/ownership.yaml @@ -31,6 +31,39 @@ concepts: - BetterPRHandoff - FeatureClipStudio + nodekit.knowledge-evolution: + owner: node-platform + package: "@homenshum/nodekit" + targetPackage: "@homenshum/nodekit" + status: canonical + contractVersion: nodekit.knowledge-graph/v1 + consumers: [] + signatures: + - id: knowledge-graph-schema + regex: '^export const KNOWLEDGE_GRAPH_SCHEMA = "nodekit\.knowledge-graph/v1";' + + nodekit.frontend-specialist: + owner: node-platform + package: "@homenshum/nodekit" + targetPackage: "@homenshum/nodekit" + status: canonical + contractVersion: nodekit.frontend-route/v1 + consumers: [] + signatures: + - id: frontend-route-schema + regex: '^export const FRONTEND_ROUTE_SCHEMA = "nodekit\.frontend-route/v1";' + + nodekit.evolution-ledger: + owner: node-platform + package: "@homenshum/nodekit" + targetPackage: "@homenshum/nodekit" + status: canonical + contractVersion: nodekit.evolution-event/v1 + consumers: [] + signatures: + - id: evolution-event-schema + regex: '^export const EVOLUTION_EVENT_SCHEMA = "nodekit\.evolution-event/v1";' + featureclip.evidence-manifest: owner: FeatureClipStudio package: null @@ -99,6 +132,7 @@ concepts: - NodeRoom - NodeSlide - NodeVideo + - NodeTasks - NodeVoice - NodeProof signatures: @@ -153,6 +187,7 @@ concepts: contractVersion: nodeagent.provider-pi/v1 consumers: - NodeRoom + - NodeTasks signatures: - id: agent-model-adapter regex: '^\s*export\s+function\s+createPiAiAdapter\b' @@ -172,6 +207,7 @@ concepts: - NodeVideo - NodeTrace - NodeProof + - NodeTasks signatures: - id: trace-schema regex: '^\s*export\s+const\s+NODEAGENT_TRACE_SCHEMA\b' diff --git a/package-lock.json b/package-lock.json index 4a138cc..f9497bb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,10 +15,355 @@ "bin": { "nodekit": "src/cli.mjs" }, + "devDependencies": { + "pg": "^8.22.0", + "playwright": "^1.61.1", + "typescript": "^7.0.2" + }, "engines": { "node": ">=20" } }, + "node_modules/@typescript/typescript-aix-ppc64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-aix-ppc64/-/typescript-aix-ppc64-7.0.2.tgz", + "integrity": "sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-darwin-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-arm64/-/typescript-darwin-arm64-7.0.2.tgz", + "integrity": "sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-darwin-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-x64/-/typescript-darwin-x64-7.0.2.tgz", + "integrity": "sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-freebsd-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-arm64/-/typescript-freebsd-arm64-7.0.2.tgz", + "integrity": "sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-freebsd-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-x64/-/typescript-freebsd-x64-7.0.2.tgz", + "integrity": "sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-arm": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm/-/typescript-linux-arm-7.0.2.tgz", + "integrity": "sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm64/-/typescript-linux-arm64-7.0.2.tgz", + "integrity": "sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-loong64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-loong64/-/typescript-linux-loong64-7.0.2.tgz", + "integrity": "sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-mips64el": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-mips64el/-/typescript-linux-mips64el-7.0.2.tgz", + "integrity": "sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-ppc64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-ppc64/-/typescript-linux-ppc64-7.0.2.tgz", + "integrity": "sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-riscv64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-riscv64/-/typescript-linux-riscv64-7.0.2.tgz", + "integrity": "sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-s390x": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-s390x/-/typescript-linux-s390x-7.0.2.tgz", + "integrity": "sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-x64/-/typescript-linux-x64-7.0.2.tgz", + "integrity": "sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-netbsd-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-arm64/-/typescript-netbsd-arm64-7.0.2.tgz", + "integrity": "sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-netbsd-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-x64/-/typescript-netbsd-x64-7.0.2.tgz", + "integrity": "sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-openbsd-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-arm64/-/typescript-openbsd-arm64-7.0.2.tgz", + "integrity": "sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-openbsd-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-x64/-/typescript-openbsd-x64-7.0.2.tgz", + "integrity": "sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-sunos-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-sunos-x64/-/typescript-sunos-x64-7.0.2.tgz", + "integrity": "sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-win32-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-win32-arm64/-/typescript-win32-arm64-7.0.2.tgz", + "integrity": "sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-win32-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-win32-x64/-/typescript-win32-x64-7.0.2.tgz", + "integrity": "sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=16.20.0" + } + }, "node_modules/ajv": { "version": "8.20.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", @@ -57,12 +402,199 @@ ], "license": "BSD-3-Clause" }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "license": "MIT" }, + "node_modules/pg": { + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.22.0.tgz", + "integrity": "sha512-8wih1vVIBMxoUM2oB4soJsD9tDnDpLv4OXBJ+EJzFsvycD+lfyIreC2gGHq78f8jbLLt+bvlPTFdFZfJkOuzAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pg-connection-string": "^2.14.0", + "pg-pool": "^3.14.0", + "pg-protocol": "^1.15.0", + "pg-types": "2.2.0", + "pgpass": "1.0.5" + }, + "engines": { + "node": ">= 16.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.4.0" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-cloudflare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.4.0.tgz", + "integrity": "sha512-Vo7z/6rrQYxpNRylp4Tlob2elzbh+N/MOQbxFVWCxS7oEx6jF53GTJFxK2WWpKuBRkmiin4Mt+xofFDjx09R0A==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/pg-connection-string": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.14.0.tgz", + "integrity": "sha512-XwWDGcLRGCXAR8F/AM5bG7Q+A3Wm2s6QeEjlOKZLlH3UYcguiqCWKyWXVag5TLTIjR7oOJUY8kcADaZgWPyLeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-pool": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.14.0.tgz", + "integrity": "sha512-gKtPkFdQPU3DksooVLi9LsjZxrsBUZIpa+7aVx+LV5pNh0KzP4Zleud2po+ConrxbuXGBJ6Hfer6hdgpIBpBaw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.15.0.tgz", + "integrity": "sha512-cq9sECI5s0+uPUXjbz8ioyPJni6RzsRib0US67i5IoTZKw8fNeYlVE7u8F4dG7vEJJtc5wdD1K189lCCUwqWTQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "split2": "^4.1.0" + } + }, + "node_modules/playwright": { + "version": "1.61.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.61.1.tgz", + "integrity": "sha512-DWnY5o3YbLWK4GovuAVwpqL+1VwGNdUGrRr++8j8PtQQzvAVZUIMjKQ90fY689sEJZJBbZVw1rXaOKSTitkzPQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.61.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.61.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.61.1.tgz", + "integrity": "sha512-h7Qlt6m4REp25qvIdvbDtVmD4LqVXfpRxhORv9L0jzETM05p4fuPJ3dKyuSXQxDSbXnmS79HAgi9589lGSpLkg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.1.tgz", + "integrity": "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", @@ -72,6 +604,61 @@ "node": ">=0.10.0" } }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/typescript": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-7.0.2.tgz", + "integrity": "sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc" + }, + "engines": { + "node": ">=16.20.0" + }, + "optionalDependencies": { + "@typescript/typescript-aix-ppc64": "7.0.2", + "@typescript/typescript-darwin-arm64": "7.0.2", + "@typescript/typescript-darwin-x64": "7.0.2", + "@typescript/typescript-freebsd-arm64": "7.0.2", + "@typescript/typescript-freebsd-x64": "7.0.2", + "@typescript/typescript-linux-arm": "7.0.2", + "@typescript/typescript-linux-arm64": "7.0.2", + "@typescript/typescript-linux-loong64": "7.0.2", + "@typescript/typescript-linux-mips64el": "7.0.2", + "@typescript/typescript-linux-ppc64": "7.0.2", + "@typescript/typescript-linux-riscv64": "7.0.2", + "@typescript/typescript-linux-s390x": "7.0.2", + "@typescript/typescript-linux-x64": "7.0.2", + "@typescript/typescript-netbsd-arm64": "7.0.2", + "@typescript/typescript-netbsd-x64": "7.0.2", + "@typescript/typescript-openbsd-arm64": "7.0.2", + "@typescript/typescript-openbsd-x64": "7.0.2", + "@typescript/typescript-sunos-x64": "7.0.2", + "@typescript/typescript-win32-arm64": "7.0.2", + "@typescript/typescript-win32-x64": "7.0.2" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, "node_modules/yaml": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", diff --git a/package.json b/package.json index 132d0d1..c57f5b3 100644 --- a/package.json +++ b/package.json @@ -3,14 +3,37 @@ "version": "0.2.1", "description": "Portable agent-application scaffolding, compilation, conformance, and proof tooling", "type": "module", + "types": "./src/index.d.mts", + "exports": { + ".": { + "types": "./src/index.d.mts", + "import": "./src/index.mjs", + "default": "./src/index.mjs" + }, + "./caseflow": { + "types": "./src/caseflow.d.mts", + "import": "./src/caseflow.mjs", + "default": "./src/caseflow.mjs" + }, + "./adapters/postgres": { + "types": "./src/adapters/postgres.d.mts", + "import": "./src/adapters/postgres.mjs", + "default": "./src/adapters/postgres.mjs" + }, + "./adapters/postgres/migration.sql": "./adapters/postgres/001_caseflow.sql", + "./adapters/supabase/profile.sql": "./adapters/supabase/001_profile.sql", + "./package.json": "./package.json" + }, "bin": { "nodekit": "src/cli.mjs" }, "files": [ "src", + "adapters", "schemas", "templates", "plugins", + "evolution", "ownership.yaml", "repositories.yaml", "architecture.yaml", @@ -20,13 +43,24 @@ "scripts": { "doctor": "node src/cli.mjs registry check --registry-root .", "audit:prod": "npm audit --omit=dev", + "typecheck:public": "tsc -p tsconfig.public.json", "test": "node --test test/*.test.mjs", "presentation:nodekit-proof": "node scripts/generate-nodekit-proof-presentation.mjs", "acceptance:factory": "node src/factory-acceptance.mjs", + "ease:aggregate-developer": "node scripts/aggregate-developer-timing.mjs", + "acceptance:agent": "node scripts/run-agent-ease-trial.mjs", + "ease:evaluate-agents": "node scripts/evaluate-agent-ease.mjs", + "ease:evaluate-developer": "node scripts/evaluate-ease-evidence.mjs developer", + "ease:evaluate-humans": "node scripts/evaluate-ease-evidence.mjs humans", + "submission:prepare": "node scripts/prepare-submission.mjs", + "submission:evaluate": "node scripts/evaluate-submission.mjs", + "conformance:postgres": "node scripts/run-postgres-conformance.mjs", "registry:check": "node src/cli.mjs registry check --registry-root .", "ecosystem:check": "node src/cli.mjs ecosystem check --registry-root . --workspace ..", "dashboard": "node src/cli.mjs dashboard --registry-root . --workspace .. --write", - "check": "npm run test && npm run audit:prod && npm run registry:check && npm run ecosystem:check" + "evolution:verify": "node src/cli.mjs evolution verify --repo-root .", + "evolution:docs": "node src/cli.mjs evolution build-docs --repo-root .", + "check": "npm run test && npm run typecheck:public && npm run audit:prod && npm run registry:check && npm run ecosystem:check" }, "engines": { "node": ">=20" @@ -38,5 +72,10 @@ "ajv": "8.20.0", "yaml": "^2.9.0" }, - "license": "MIT" + "license": "MIT", + "devDependencies": { + "pg": "^8.22.0", + "playwright": "^1.61.1", + "typescript": "^7.0.2" + } } diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/backend/postgres-conformance.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/backend/postgres-conformance.json new file mode 100644 index 0000000..394783a --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/backend/postgres-conformance.json @@ -0,0 +1,71 @@ +{ + "schemaVersion": "nodekit.postgres-conformance/v1", + "adapter": "@homenshum/nodekit/adapters/postgres", + "assertions": { + "crossOwnerDenied": true, + "ownerIsolation": true, + "receiptIntegrity": true, + "reloadPreservedState": true, + "sameBaseRaceFailedClosed": true, + "sharedConformancePassed": true + }, + "candidateCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "capabilities": { + "schemaVersion": "nodekit.runtime-capabilities/v1", + "provider": "postgres", + "durableState": true, + "transactions": true, + "optimisticConcurrency": true, + "subscriptions": "polling", + "durableJobs": "external", + "fileStorage": false, + "presence": false, + "scheduledJobs": false, + "localDevelopment": true + }, + "conformance": { + "assertions": { + "activeRunStartIsIdempotent": true, + "canonicalVersionAdvancedOnce": true, + "contentAddressedReceipt": true, + "exceptionStatePreserved": true, + "nextActionOwnerExplicit": true, + "oneAuthoritativeCase": true, + "repeatedCompletionIsIdempotent": true, + "repeatedDecisionIsIdempotent": true, + "staleProposalFailedClosed": true + }, + "capabilityNegotiation": { + "missing": [], + "passed": true, + "provider": "postgres", + "schemaVersion": "nodekit.runtime-capability-negotiation/v1" + }, + "capabilities": { + "schemaVersion": "nodekit.runtime-capabilities/v1", + "provider": "postgres", + "durableState": true, + "transactions": true, + "optimisticConcurrency": true, + "subscriptions": "polling", + "durableJobs": "external", + "fileStorage": false, + "presence": false, + "scheduledJobs": false, + "localDevelopment": true + }, + "passed": true, + "schemaVersion": "nodekit.adapter-conformance/v1" + }, + "generatedAt": "2026-07-22T08:41:03.302Z", + "migration": { + "path": "adapters/postgres/001_caseflow.sql", + "sha256": "63e4cd3af94f5db8204f3c80ad3faadd53ed0f1b8019bae414f3785e6c1051a3" + }, + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "ownerScope": "isolated-test-identities", + "passed": true, + "postgres": { + "serverVersion": "17.10" + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/console.jsonl b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/console.jsonl new file mode 100644 index 0000000..e69de29 diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/journey.webm b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/journey.webm new file mode 100644 index 0000000..641557b Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/journey.webm differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/network.jsonl b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/network.jsonl new file mode 100644 index 0000000..e69de29 diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/playwright-trace.zip b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/playwright-trace.zip new file mode 100644 index 0000000..b86ff31 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/playwright-trace.zip differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshot-manifest.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshot-manifest.json new file mode 100644 index 0000000..a6e5895 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshot-manifest.json @@ -0,0 +1,5346 @@ +{ + "accessibilityViolations": [], + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "certified": true, + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": [], + "coveredStates": [ + "first_arrival", + "orientation", + "input", + "validation_error", + "running", + "partial_result", + "external_wait", + "proposal_pending", + "approval", + "conflict", + "recoverable_failure", + "reload_resume", + "completed_receipt", + "receipt_inspection", + "export_share" + ], + "durationMs": 150710, + "error": null, + "evidenceArtifacts": [ + { + "byteSize": 15346021, + "id": "playwright-trace", + "path": "browser/playwright-trace.zip", + "sha256": "8cc9e14c6d90218565212311d8ed02a49cadf338776ed326045acc88d6d34496" + }, + { + "byteSize": 2553298, + "id": "browser-video", + "path": "browser/journey.webm", + "sha256": "93fbc3a0fa9ad03f1fe6c04ba64ffef586c0d6670f9e0608a8be6e18083b36b6" + } + ], + "firstMeaningfulPaintMs": 2044, + "generatedAt": "2026-07-22T08:51:53.912Z", + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "journeyAssertions": { + "approvalApplied": true, + "proposalVisible": true, + "receiptSurvivedReload": true, + "receiptVisible": true + }, + "milestones": [ + { + "at": "2026-07-22T08:49:24.291Z", + "elapsedMs": 1089, + "name": "canonical_state_reset" + }, + { + "at": "2026-07-22T08:49:25.246Z", + "elapsedMs": 2044, + "name": "first_meaningful_paint" + }, + { + "at": "2026-07-22T08:49:25.246Z", + "elapsedMs": 2044, + "name": "neutral_case_visible" + }, + { + "at": "2026-07-22T08:49:25.348Z", + "elapsedMs": 2146, + "name": "proposal_visible" + }, + { + "at": "2026-07-22T08:49:25.453Z", + "elapsedMs": 2251, + "name": "approval_applied" + }, + { + "at": "2026-07-22T08:49:25.453Z", + "elapsedMs": 2251, + "name": "receipt_visible" + }, + { + "at": "2026-07-22T08:49:26.020Z", + "elapsedMs": 2818, + "name": "receipt_reload_confirmed" + } + ], + "missingStates": [], + "networkFailures": [], + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "passed": true, + "phases": [ + { + "at": "2026-07-22T08:49:23.534Z", + "durationMs": 247, + "name": "server_readiness" + } + ], + "requiredStates": [ + "first_arrival", + "orientation", + "input", + "validation_error", + "running", + "partial_result", + "external_wait", + "proposal_pending", + "approval", + "conflict", + "recoverable_failure", + "reload_resume", + "completed_receipt", + "receipt_inspection", + "export_share" + ], + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.browser-certification/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "screenshots": [ + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:49:27.735Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 4533, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=first_arrival", + "pngSha256": "0c61e989d4f960cc9ea0738b5ded4931b9e7446e4281a398603cd2abfa1861de", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "first_arrival", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/first_arrival--desktop--light.png", + "sidecarPath": "browser/screenshots/first_arrival--desktop--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:49:29.501Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 6299, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=orientation", + "pngSha256": "d46d551bb2f79728e3c604c2552202e51d3b6892f9125469aa23158289766ab6", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "orientation", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/orientation--desktop--light.png", + "sidecarPath": "browser/screenshots/orientation--desktop--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:49:31.143Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 7941, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=input", + "pngSha256": "af7b5e8e19e7939f286f4296d92301523e2e497775b5125b2f861c2a416546c5", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "input", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/input--desktop--light.png", + "sidecarPath": "browser/screenshots/input--desktop--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:49:32.829Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 9627, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=validation_error", + "pngSha256": "0a325a05fb5acbe6f4fcf919ff6253f44f23a9e413cce5bd95e434cd9b7ffda6", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "validation_error", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/validation_error--desktop--light.png", + "sidecarPath": "browser/screenshots/validation_error--desktop--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:49:34.463Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 11261, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=running", + "pngSha256": "161d47aea949363c2b0e6b5423ef9735b9dbb71d15d0f3ace1a169a03f583742", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "running", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/running--desktop--light.png", + "sidecarPath": "browser/screenshots/running--desktop--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:49:36.001Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 12799, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=partial_result", + "pngSha256": "52d1192350f56d2218a236dcf173f29a537379ab3997836430446c82f4601df1", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "partial_result", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/partial_result--desktop--light.png", + "sidecarPath": "browser/screenshots/partial_result--desktop--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:49:37.603Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 14401, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=external_wait", + "pngSha256": "d4eb9120482d2f61c5287dc4a42f03cb283dec3b2c7a5ada680432efab9c2f09", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "external_wait", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/external_wait--desktop--light.png", + "sidecarPath": "browser/screenshots/external_wait--desktop--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:49:39.152Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 15950, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=proposal_pending", + "pngSha256": "99c82a7eca195fe447379215c284d8968e4cc1e75ddbddbccc9c34d6e2eacf69", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "proposal_pending", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/proposal_pending--desktop--light.png", + "sidecarPath": "browser/screenshots/proposal_pending--desktop--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:49:40.734Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 17532, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=approval", + "pngSha256": "626716422cf6e1790176fae18f323892155a49f66aa15e91475e99889144063e", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "approval", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/approval--desktop--light.png", + "sidecarPath": "browser/screenshots/approval--desktop--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:49:42.521Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 19319, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=conflict", + "pngSha256": "cec43c461a25b8cc966bcdd0aaeee09f89baa3bc82bcf84103c9a63b9e9b6c13", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "conflict", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/conflict--desktop--light.png", + "sidecarPath": "browser/screenshots/conflict--desktop--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:49:44.336Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 21134, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=recoverable_failure", + "pngSha256": "2b3af8ca27a709e13d61367d375beb8caf4f87edff0da94d6d0b3791c6f692f2", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "recoverable_failure", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/recoverable_failure--desktop--light.png", + "sidecarPath": "browser/screenshots/recoverable_failure--desktop--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:49:46.069Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 22867, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=reload_resume", + "pngSha256": "c122312cba0a30b186de2f0ec6c3bb269e2cc10bd2cf46ca09209837a97117d4", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "reload_resume", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/reload_resume--desktop--light.png", + "sidecarPath": "browser/screenshots/reload_resume--desktop--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:49:48.347Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 25145, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=completed_receipt", + "pngSha256": "62eb8dc21032a528851ab16341a70a25bd162a424f4169c11d94f942e9deb363", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "completed_receipt", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/completed_receipt--desktop--light.png", + "sidecarPath": "browser/screenshots/completed_receipt--desktop--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:49:50.669Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 27467, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=receipt_inspection", + "pngSha256": "50cbd1590459128287928baad002a7d628359275ca34973c2ba166f3b7b0667e", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "receipt_inspection", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/receipt_inspection--desktop--light.png", + "sidecarPath": "browser/screenshots/receipt_inspection--desktop--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:49:52.271Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 29069, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=export_share", + "pngSha256": "81d637b418b55f771b316592c40d546ff2d9925ff13c81c32eff46827f589b47", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "export_share", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/export_share--desktop--light.png", + "sidecarPath": "browser/screenshots/export_share--desktop--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:49:56.467Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 33265, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=first_arrival", + "pngSha256": "19b8f4c64edd08342024e61c057215f6d913c943c8444bdedc57c5ca932eab53", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "first_arrival", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/first_arrival--desktop--dark.png", + "sidecarPath": "browser/screenshots/first_arrival--desktop--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:49:57.083Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 33881, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=orientation", + "pngSha256": "5e031fc3fc738f94ca951722db175c52c50025ba151e3a19203fe7e10d181012", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "orientation", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/orientation--desktop--dark.png", + "sidecarPath": "browser/screenshots/orientation--desktop--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:49:57.706Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 34504, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=input", + "pngSha256": "89f62eb41eeb8cb9b050b82dee9f5cc57d922958ef3908cff6f491d86c34ba93", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "input", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/input--desktop--dark.png", + "sidecarPath": "browser/screenshots/input--desktop--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:49:58.347Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 35145, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=validation_error", + "pngSha256": "302bcfd157f8221d500fae3008341768e19c622c2328d33dbd181bee2ca2e10a", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "validation_error", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/validation_error--desktop--dark.png", + "sidecarPath": "browser/screenshots/validation_error--desktop--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:49:58.954Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 35751, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=running", + "pngSha256": "a162f9e1f34c4fcbe9f8c7da8a2e4f622e7a83da3a361c8c8bee210c04e162d7", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "running", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/running--desktop--dark.png", + "sidecarPath": "browser/screenshots/running--desktop--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:49:59.577Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 36375, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=partial_result", + "pngSha256": "0cdb519fe686bcd808b217695ee5b2980906d507b6b832a0777a12ecba2396ce", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "partial_result", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/partial_result--desktop--dark.png", + "sidecarPath": "browser/screenshots/partial_result--desktop--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:00.206Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 37004, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=external_wait", + "pngSha256": "9670134746f4d82d7e72e97f2c3531f696f8634d17bf3361a4f1ab65c82cbeaa", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "external_wait", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/external_wait--desktop--dark.png", + "sidecarPath": "browser/screenshots/external_wait--desktop--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:00.839Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 37637, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=proposal_pending", + "pngSha256": "6e919b4c94bdd4cb37a874bbca9c83e5be672989af07dca9854ef876f2e7322e", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "proposal_pending", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/proposal_pending--desktop--dark.png", + "sidecarPath": "browser/screenshots/proposal_pending--desktop--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:01.461Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 38259, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=approval", + "pngSha256": "4b0cd3df06be782e4da9446523ed0652e94815bd7e4ecb8b09ab8d0e67901edd", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "approval", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/approval--desktop--dark.png", + "sidecarPath": "browser/screenshots/approval--desktop--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:02.102Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 38900, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=conflict", + "pngSha256": "2278dfaf634373c6ba1ba15edd36fbac50244bc51dc7d4b810c7c9c64ff18e46", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "conflict", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/conflict--desktop--dark.png", + "sidecarPath": "browser/screenshots/conflict--desktop--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:02.757Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 39555, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=recoverable_failure", + "pngSha256": "b99a5860194bab5fa02929746aef2ce619becfa926b15c93142ef9f7c7a99efb", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "recoverable_failure", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/recoverable_failure--desktop--dark.png", + "sidecarPath": "browser/screenshots/recoverable_failure--desktop--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:03.402Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 40200, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=reload_resume", + "pngSha256": "7bbb614a5b24a17e49b969c00d8a56f27373ce356645d064a79d19b98b5482a4", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "reload_resume", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/reload_resume--desktop--dark.png", + "sidecarPath": "browser/screenshots/reload_resume--desktop--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:04.617Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 41415, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=completed_receipt", + "pngSha256": "da0ee6791b226ca8ec35361feb8136723318724683c3e3889fd33ebd1e4b6e86", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "completed_receipt", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/completed_receipt--desktop--dark.png", + "sidecarPath": "browser/screenshots/completed_receipt--desktop--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:05.764Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 42562, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=receipt_inspection", + "pngSha256": "0015b991d190f09f6f98885e4465a8d0d2e0099466457b7ba52915f03112922d", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "receipt_inspection", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/receipt_inspection--desktop--dark.png", + "sidecarPath": "browser/screenshots/receipt_inspection--desktop--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:06.355Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 43153, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=export_share", + "pngSha256": "f4ff6428074635456ba40e1974fb1d07064a3ce4fd8b64ab42d91ac056dcf83d", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "export_share", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/export_share--desktop--dark.png", + "sidecarPath": "browser/screenshots/export_share--desktop--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:07.123Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 43921, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=first_arrival", + "pngSha256": "212650139cc72cd82320cab8bf0c63c01207070c1c9d6590f4b2442cfcdd1649", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "first_arrival", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/first_arrival--wide--light.png", + "sidecarPath": "browser/screenshots/first_arrival--wide--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:07.755Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 44553, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=orientation", + "pngSha256": "26fc5d643f728918edee2b3ed50e0e76c6e3babfdcc03266252bbef6c48753db", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "orientation", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/orientation--wide--light.png", + "sidecarPath": "browser/screenshots/orientation--wide--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:08.391Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 45189, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=input", + "pngSha256": "e0eddf8dece870c73b862219d43e7295b0583a59a010f4eeabb7bb9e3bc89138", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "input", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/input--wide--light.png", + "sidecarPath": "browser/screenshots/input--wide--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:09.090Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 45888, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=validation_error", + "pngSha256": "d79d5ffcaed7357b9f48fca59d41720e45c04df12984a5108a02b5c1689bf859", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "validation_error", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/validation_error--wide--light.png", + "sidecarPath": "browser/screenshots/validation_error--wide--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:09.751Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 46549, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=running", + "pngSha256": "67e455edf5c5705c1f80a9d14cc2392727cbbb0d0b5523cb66945e2a86b94eb4", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "running", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/running--wide--light.png", + "sidecarPath": "browser/screenshots/running--wide--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:10.361Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 47159, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=partial_result", + "pngSha256": "aa4eb4b15a8df144037a3065841976ba375f20072fb182cd1990f29dd640b688", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "partial_result", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/partial_result--wide--light.png", + "sidecarPath": "browser/screenshots/partial_result--wide--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:11.006Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 47804, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=external_wait", + "pngSha256": "09222a087cf4479eef7e41ae4d7a1b15e29bc97e8ad047d14fabb03a66da1d2f", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "external_wait", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/external_wait--wide--light.png", + "sidecarPath": "browser/screenshots/external_wait--wide--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:11.655Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 48453, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=proposal_pending", + "pngSha256": "98aa197bb1eaa3ee491b370165a12c431dee200003e501fa953231694a4e1806", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "proposal_pending", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/proposal_pending--wide--light.png", + "sidecarPath": "browser/screenshots/proposal_pending--wide--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:12.290Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 49088, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=approval", + "pngSha256": "f6d906b5166e211f28d56ff33d7f05d9b3f236647621824073301c0946804b49", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "approval", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/approval--wide--light.png", + "sidecarPath": "browser/screenshots/approval--wide--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:12.901Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 49699, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=conflict", + "pngSha256": "954da561ebff1b52e9a402789f64211f5a49a0e722cc3443b8d8f3f4fe91fc5d", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "conflict", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/conflict--wide--light.png", + "sidecarPath": "browser/screenshots/conflict--wide--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:13.578Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 50376, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=recoverable_failure", + "pngSha256": "266557e6efd127b8e8e7eec26d9721a0e9b3ada25c86b4a75fe7dc8091f0b0b2", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "recoverable_failure", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/recoverable_failure--wide--light.png", + "sidecarPath": "browser/screenshots/recoverable_failure--wide--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:14.223Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 51021, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=reload_resume", + "pngSha256": "3d580d57f70eafa16be5129d690b4872b3776907f5cd56137b7a23757fed87be", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "reload_resume", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/reload_resume--wide--light.png", + "sidecarPath": "browser/screenshots/reload_resume--wide--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:15.429Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 52227, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=completed_receipt", + "pngSha256": "888416e0fa3eda4aace27e864a65d24ae55270466084ec10ad87a94e57f93fbb", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "completed_receipt", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/completed_receipt--wide--light.png", + "sidecarPath": "browser/screenshots/completed_receipt--wide--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:16.621Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 53419, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=receipt_inspection", + "pngSha256": "c0c36a1385a2d7022564366c929c6b94f5b7dc32732b135ec1093fcd0f00d132", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "receipt_inspection", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/receipt_inspection--wide--light.png", + "sidecarPath": "browser/screenshots/receipt_inspection--wide--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:17.279Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 54077, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=export_share", + "pngSha256": "ffc5031685acb434865f216145bfae44d7f90a7ea9b58a00c460b28329628bc1", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "export_share", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/export_share--wide--light.png", + "sidecarPath": "browser/screenshots/export_share--wide--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:18.092Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 54890, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=first_arrival", + "pngSha256": "b05f84a2df28fb171c36f44dea49a6c2d1834355130eee929ce4f97191249836", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "first_arrival", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/first_arrival--wide--dark.png", + "sidecarPath": "browser/screenshots/first_arrival--wide--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:18.744Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 55542, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=orientation", + "pngSha256": "48892a54f79c10954cdd50feb8ae715e2e824daf817900dd3f11fdb8be2b2995", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "orientation", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/orientation--wide--dark.png", + "sidecarPath": "browser/screenshots/orientation--wide--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:19.404Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 56202, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=input", + "pngSha256": "4bbf00a5183c8e2577dd0b90355bad203207e1f0910911e8e4a4514bb273ebd4", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "input", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/input--wide--dark.png", + "sidecarPath": "browser/screenshots/input--wide--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:20.124Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 56922, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=validation_error", + "pngSha256": "eabdf2a71e6a86b7509c168e67b9c7f91ef3d40f969b5271a0bc24b7d05cd2df", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "validation_error", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/validation_error--wide--dark.png", + "sidecarPath": "browser/screenshots/validation_error--wide--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:20.780Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 57578, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=running", + "pngSha256": "35be095bdc770b3cb67281d9a60fbc13c6e9c1496c835e7cb714fd2c18008790", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "running", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/running--wide--dark.png", + "sidecarPath": "browser/screenshots/running--wide--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:21.451Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 58249, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=partial_result", + "pngSha256": "4c499f1c8dd5e79738a5b9fca18e2ab55de5f24a9631bf4615af2c115de5ca94", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "partial_result", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/partial_result--wide--dark.png", + "sidecarPath": "browser/screenshots/partial_result--wide--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:22.126Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 58924, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=external_wait", + "pngSha256": "c8d84831e2b6e0531b34906fa4feb781d703f665e2a7988e58ef526e08b3d338", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "external_wait", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/external_wait--wide--dark.png", + "sidecarPath": "browser/screenshots/external_wait--wide--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:22.767Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 59565, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=proposal_pending", + "pngSha256": "85e81f29350d8707aa3d93c2342134f8a9808e516fa675e9f1a6b2b815f9832f", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "proposal_pending", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/proposal_pending--wide--dark.png", + "sidecarPath": "browser/screenshots/proposal_pending--wide--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:23.426Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 60224, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=approval", + "pngSha256": "701c484d220547277deabecc901dc39ef1788b24a95bf42d103732c8fbc8f04d", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "approval", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/approval--wide--dark.png", + "sidecarPath": "browser/screenshots/approval--wide--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:24.109Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 60907, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=conflict", + "pngSha256": "1fa62695b020a4c43c2a7fc0118a8eff41b4da719e2590d158d39ffe176f7f41", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "conflict", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/conflict--wide--dark.png", + "sidecarPath": "browser/screenshots/conflict--wide--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:24.764Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 61562, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=recoverable_failure", + "pngSha256": "643e60253754ed2e184e371ecf8d616fc506d356ea1583a81063591034424062", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "recoverable_failure", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/recoverable_failure--wide--dark.png", + "sidecarPath": "browser/screenshots/recoverable_failure--wide--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:25.430Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 62228, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=reload_resume", + "pngSha256": "504106686b5b579be864bcb430ee05ca6f7795f3ad9d1ebc2cab7240cd925d6c", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "reload_resume", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/reload_resume--wide--dark.png", + "sidecarPath": "browser/screenshots/reload_resume--wide--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:26.630Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 63429, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=completed_receipt", + "pngSha256": "7e7c2a7b74ac05d3cb28fc323d894e247e70fe0f0244cb51a90335e27931bb0d", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "completed_receipt", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/completed_receipt--wide--dark.png", + "sidecarPath": "browser/screenshots/completed_receipt--wide--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:27.820Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 64619, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=receipt_inspection", + "pngSha256": "ad1e10814c4a940c662729276fcdd0358248bd4a0dbc3258c761b300d5fa994b", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "receipt_inspection", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/receipt_inspection--wide--dark.png", + "sidecarPath": "browser/screenshots/receipt_inspection--wide--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:28.474Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 65272, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=export_share", + "pngSha256": "30440f29170a462c7eb9c21f219f497b186deb6fa7f5e0e917a42705f1c026cc", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "export_share", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/export_share--wide--dark.png", + "sidecarPath": "browser/screenshots/export_share--wide--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:29.252Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 66050, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=first_arrival", + "pngSha256": "d0bd94d230776ff7179cd460e05fabd1abfc4bc4b0272947851f62c79cb7d6cd", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "first_arrival", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/first_arrival--tablet-landscape--light.png", + "sidecarPath": "browser/screenshots/first_arrival--tablet-landscape--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:29.876Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 66674, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=orientation", + "pngSha256": "49767f90f776ef9b457b36e90a81fa030dc08f7de5937e7e8ea8bc90d5e66afc", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "orientation", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/orientation--tablet-landscape--light.png", + "sidecarPath": "browser/screenshots/orientation--tablet-landscape--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:30.501Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 67299, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=input", + "pngSha256": "0359eecb5e44b7e959660fc8f42ddab622ce3fc888271d823f2038ac18480028", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "input", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/input--tablet-landscape--light.png", + "sidecarPath": "browser/screenshots/input--tablet-landscape--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:31.183Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 67981, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=validation_error", + "pngSha256": "54e9ee395d17540d7a042cc1cb123de0c99b0c53c953538ad7da59d03ee93918", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "validation_error", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/validation_error--tablet-landscape--light.png", + "sidecarPath": "browser/screenshots/validation_error--tablet-landscape--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:31.806Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 68605, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=running", + "pngSha256": "58d39d76c56ace010ab5ec90b2bc80a47dc84802ef22069d8151e9a3d89ea69a", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "running", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/running--tablet-landscape--light.png", + "sidecarPath": "browser/screenshots/running--tablet-landscape--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:32.417Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 69216, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=partial_result", + "pngSha256": "d25868a2d4f793a5d7416444d10e22b6d53d16faac28ab1a4e03dfc3105adfa2", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "partial_result", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/partial_result--tablet-landscape--light.png", + "sidecarPath": "browser/screenshots/partial_result--tablet-landscape--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:33.041Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 69839, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=external_wait", + "pngSha256": "b901c9ac40ea6c343d5b9baab123e957230a3dff759f04cee33187f6cbb940c1", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "external_wait", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/external_wait--tablet-landscape--light.png", + "sidecarPath": "browser/screenshots/external_wait--tablet-landscape--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:33.677Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 70475, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=proposal_pending", + "pngSha256": "4fbc34d58d737de01c1a4631f7f8c2fd577beeb2c9a306eda072dff98e36ab36", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "proposal_pending", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/proposal_pending--tablet-landscape--light.png", + "sidecarPath": "browser/screenshots/proposal_pending--tablet-landscape--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:34.325Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 71123, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=approval", + "pngSha256": "8cc1a59acd80b9147c5e514835b14eeacf32b3ab8c05b84d02477aab3724e979", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "approval", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/approval--tablet-landscape--light.png", + "sidecarPath": "browser/screenshots/approval--tablet-landscape--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:34.941Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 71740, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=conflict", + "pngSha256": "2d045b3c00ba7f16672206e9ae35bc9127b02d27b59280df5400b5f5cb0a5561", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "conflict", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/conflict--tablet-landscape--light.png", + "sidecarPath": "browser/screenshots/conflict--tablet-landscape--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:35.565Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 72363, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=recoverable_failure", + "pngSha256": "6f1e02f72267ffaf20f5756fc90ece33f425301d32c2fc25caf03e91dd697f92", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "recoverable_failure", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/recoverable_failure--tablet-landscape--light.png", + "sidecarPath": "browser/screenshots/recoverable_failure--tablet-landscape--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:36.190Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 72988, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=reload_resume", + "pngSha256": "61f0b0388f909fe0fdd7c6810b0e1873dce203e6bc4163639e5b937a9f484c54", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "reload_resume", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/reload_resume--tablet-landscape--light.png", + "sidecarPath": "browser/screenshots/reload_resume--tablet-landscape--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:37.357Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 74156, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=completed_receipt", + "pngSha256": "2e75016b90565b78103fd75256f2d5ac011cde206a6da16520efd15908e7a959", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "completed_receipt", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/completed_receipt--tablet-landscape--light.png", + "sidecarPath": "browser/screenshots/completed_receipt--tablet-landscape--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:38.555Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 75353, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=receipt_inspection", + "pngSha256": "2c2d6a2b85990b55d62e76d9a0f1feac32a9c69eaf687c906155b842ff9b3b2c", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "receipt_inspection", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/receipt_inspection--tablet-landscape--light.png", + "sidecarPath": "browser/screenshots/receipt_inspection--tablet-landscape--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:39.176Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 75974, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=export_share", + "pngSha256": "089cf9665d88e7bd627d94ead5725d05a3eb079442b7c6c4b450ade4238e3eef", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "export_share", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/export_share--tablet-landscape--light.png", + "sidecarPath": "browser/screenshots/export_share--tablet-landscape--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:40.102Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 76900, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=first_arrival", + "pngSha256": "7822be75b26fa056c95240deabe8a9f298b7a5fe51b95752747305941d23982a", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "first_arrival", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/first_arrival--tablet-landscape--dark.png", + "sidecarPath": "browser/screenshots/first_arrival--tablet-landscape--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:40.725Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 77523, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=orientation", + "pngSha256": "d700780cfee88cd80ac60caf6ebfc8ee52e1772c009a42f6e347769b960cdb25", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "orientation", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/orientation--tablet-landscape--dark.png", + "sidecarPath": "browser/screenshots/orientation--tablet-landscape--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:41.331Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 78129, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=input", + "pngSha256": "6d63f2e27abe53e7427d59c027c0678a6daf6ebacfa3b8ceeb4bfc4b1ef13061", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "input", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/input--tablet-landscape--dark.png", + "sidecarPath": "browser/screenshots/input--tablet-landscape--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:42.040Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 78838, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=validation_error", + "pngSha256": "6cb7e6cf614e47f8c2d65d3f195be8912aa6aca5fd30fcb026c7d521f9da6662", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "validation_error", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/validation_error--tablet-landscape--dark.png", + "sidecarPath": "browser/screenshots/validation_error--tablet-landscape--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:42.651Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 79449, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=running", + "pngSha256": "1045979b0c0d6de1d7c6a3e44f60514c575a72d209a8f18ce3139f7f1af97ae1", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "running", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/running--tablet-landscape--dark.png", + "sidecarPath": "browser/screenshots/running--tablet-landscape--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:43.253Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 80051, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=partial_result", + "pngSha256": "edafb481e4ee8b4ad1c8b0be2feb153b1a35ec0c91d660c0d87dbb5a37793496", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "partial_result", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/partial_result--tablet-landscape--dark.png", + "sidecarPath": "browser/screenshots/partial_result--tablet-landscape--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:43.875Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 80673, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=external_wait", + "pngSha256": "56cd1a5d922139ab957521ad0ea09f7f36968c911e9f13a66beccf55e07c037f", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "external_wait", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/external_wait--tablet-landscape--dark.png", + "sidecarPath": "browser/screenshots/external_wait--tablet-landscape--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:44.483Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 81281, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=proposal_pending", + "pngSha256": "3c6fa0a75c5375a0ae4f24e7bce337bfbadf8de0a634ab673aaebd44e3194284", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "proposal_pending", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/proposal_pending--tablet-landscape--dark.png", + "sidecarPath": "browser/screenshots/proposal_pending--tablet-landscape--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:45.097Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 81896, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=approval", + "pngSha256": "55dd6ec368b57b594b7bc9b44849a8a577fb61e4fac0387bf6443b9ff58af269", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "approval", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/approval--tablet-landscape--dark.png", + "sidecarPath": "browser/screenshots/approval--tablet-landscape--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:45.716Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 82514, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=conflict", + "pngSha256": "0a9b600dbdfbfd297cb7fea047e4ef29bd5b46d1551921cdabe04290a918b26b", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "conflict", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/conflict--tablet-landscape--dark.png", + "sidecarPath": "browser/screenshots/conflict--tablet-landscape--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:46.336Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 83134, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=recoverable_failure", + "pngSha256": "d2beea0a51db9efe5f9df422e922497f13d674c1ba5765afbf1de22955809d97", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "recoverable_failure", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/recoverable_failure--tablet-landscape--dark.png", + "sidecarPath": "browser/screenshots/recoverable_failure--tablet-landscape--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:46.926Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 83724, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=reload_resume", + "pngSha256": "5318d9ea5a2c999969ec544c77ea5e11d368b4935a16fe2f4650cab3e08f40db", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "reload_resume", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/reload_resume--tablet-landscape--dark.png", + "sidecarPath": "browser/screenshots/reload_resume--tablet-landscape--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:48.061Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 84860, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=completed_receipt", + "pngSha256": "30548a77fa323aa8689fe879ee0ce0ef1326ff5a31c096f18cec119f9a5a24ab", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "completed_receipt", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/completed_receipt--tablet-landscape--dark.png", + "sidecarPath": "browser/screenshots/completed_receipt--tablet-landscape--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:49.206Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 86004, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=receipt_inspection", + "pngSha256": "64b1bd6f4fcc71ea77c4bd24a1296a31f601cf7ca485345a1bc2413ad78d3169", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "receipt_inspection", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/receipt_inspection--tablet-landscape--dark.png", + "sidecarPath": "browser/screenshots/receipt_inspection--tablet-landscape--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:49.812Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 86611, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=export_share", + "pngSha256": "f75156daef32135549ede5b5af5957044aea825ca716554d3746256f18755834", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "export_share", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/export_share--tablet-landscape--dark.png", + "sidecarPath": "browser/screenshots/export_share--tablet-landscape--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:50.671Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 87469, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=first_arrival", + "pngSha256": "b6231d65b934df0975fabb5d945c6c1fcbc00a2ba5439df8268fe26941981c7b", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "first_arrival", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/first_arrival--tablet-portrait--light.png", + "sidecarPath": "browser/screenshots/first_arrival--tablet-portrait--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:51.294Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 88092, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=orientation", + "pngSha256": "b6d331c8e6616e1d62b56666c1e57602217ab7ffd13082a60bc44fc0c646c576", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "orientation", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/orientation--tablet-portrait--light.png", + "sidecarPath": "browser/screenshots/orientation--tablet-portrait--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:51.925Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 88723, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=input", + "pngSha256": "6e25da65adfa86898f551517bc4e009b8f85cdac8097e6c22023130a1d64d5d1", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "input", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/input--tablet-portrait--light.png", + "sidecarPath": "browser/screenshots/input--tablet-portrait--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:52.619Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 89417, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=validation_error", + "pngSha256": "6b6393d8be685c59bd60ad8c1a49db04aad663192074a1f9041d76cb0a15a25e", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "validation_error", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/validation_error--tablet-portrait--light.png", + "sidecarPath": "browser/screenshots/validation_error--tablet-portrait--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:53.250Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 90048, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=running", + "pngSha256": "fc788223f3ebb0f6706f3be14cafbbbb78888cb468c13c4fe6464a3165973740", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "running", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/running--tablet-portrait--light.png", + "sidecarPath": "browser/screenshots/running--tablet-portrait--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:53.867Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 90665, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=partial_result", + "pngSha256": "99e380752a9556a887e386e4b9c8da5bb668c870e3e83ab7ac877fecbd305b13", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "partial_result", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/partial_result--tablet-portrait--light.png", + "sidecarPath": "browser/screenshots/partial_result--tablet-portrait--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:54.485Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 91283, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=external_wait", + "pngSha256": "d5ab52989a35a5fef9d272e08d840073c2d468942e515cbdb802ff48bfd9ba2a", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "external_wait", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/external_wait--tablet-portrait--light.png", + "sidecarPath": "browser/screenshots/external_wait--tablet-portrait--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:55.096Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 91894, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=proposal_pending", + "pngSha256": "429502d7d3f6090c99a17d850f462c63eaf2d6748ff810253b976418dbd0fbbe", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "proposal_pending", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/proposal_pending--tablet-portrait--light.png", + "sidecarPath": "browser/screenshots/proposal_pending--tablet-portrait--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:55.713Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 92511, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=approval", + "pngSha256": "459ee0d8eb7a75abf31b51b12b740776b09c656f3b4b0ce1da7abd2639387f06", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "approval", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/approval--tablet-portrait--light.png", + "sidecarPath": "browser/screenshots/approval--tablet-portrait--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:56.350Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 93149, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=conflict", + "pngSha256": "32adf918ca5e830f786921b6f391412a9cf8b157f5e7aaaa7854868ffd6ac533", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "conflict", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/conflict--tablet-portrait--light.png", + "sidecarPath": "browser/screenshots/conflict--tablet-portrait--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:56.969Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 93767, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=recoverable_failure", + "pngSha256": "b4c97ae024b85494bf6ccf7b3df04f4dbdfc700faab17412c1ca2e5de87f2e00", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "recoverable_failure", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/recoverable_failure--tablet-portrait--light.png", + "sidecarPath": "browser/screenshots/recoverable_failure--tablet-portrait--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:57.570Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 94368, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=reload_resume", + "pngSha256": "2dc3c316f11eb5528c95a0305882d586ae3dcb2ce9356b76f473666f59b72d8c", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "reload_resume", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/reload_resume--tablet-portrait--light.png", + "sidecarPath": "browser/screenshots/reload_resume--tablet-portrait--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:58.700Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 95498, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=completed_receipt", + "pngSha256": "21f2689823020a5611569c933adfdb710b97a85834d4fbcd421c49802d996107", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "completed_receipt", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/completed_receipt--tablet-portrait--light.png", + "sidecarPath": "browser/screenshots/completed_receipt--tablet-portrait--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:59.869Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 96667, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=receipt_inspection", + "pngSha256": "c9916c7dd636f02b62d721125d65b4d73b399352950e8678458ee94201c2aad2", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "receipt_inspection", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/receipt_inspection--tablet-portrait--light.png", + "sidecarPath": "browser/screenshots/receipt_inspection--tablet-portrait--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:00.501Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 97299, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=export_share", + "pngSha256": "12b56a599f3a593f97d26aac15d9bda8e48588dfb30a9cb92b59ab36612e8820", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "export_share", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/export_share--tablet-portrait--light.png", + "sidecarPath": "browser/screenshots/export_share--tablet-portrait--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:01.369Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 98168, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=first_arrival", + "pngSha256": "53863ed7d18c1e4077ea034b4ab3e85f932c363f60ea36d728281178399b891f", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "first_arrival", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/first_arrival--tablet-portrait--dark.png", + "sidecarPath": "browser/screenshots/first_arrival--tablet-portrait--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:01.986Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 98784, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=orientation", + "pngSha256": "2915d3fd97d30a3ccc79e063a478c654ead810f431b5a8247ae835300601bdc4", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "orientation", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/orientation--tablet-portrait--dark.png", + "sidecarPath": "browser/screenshots/orientation--tablet-portrait--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:02.607Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 99405, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=input", + "pngSha256": "e39595d5af8c1c01b44a7ab397c7ddedf1078788241f3e8422b80d5190fe3270", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "input", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/input--tablet-portrait--dark.png", + "sidecarPath": "browser/screenshots/input--tablet-portrait--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:03.300Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 100098, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=validation_error", + "pngSha256": "4980a38de8e69db29258c63d8e8fa4c67a9a17fe02fbe6396ae1b99ac20e48cf", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "validation_error", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/validation_error--tablet-portrait--dark.png", + "sidecarPath": "browser/screenshots/validation_error--tablet-portrait--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:03.940Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 100738, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=running", + "pngSha256": "1a7c5b04f35b215cd6ace5a18ba73a459c21ee508573450223866ccfd7c09369", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "running", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/running--tablet-portrait--dark.png", + "sidecarPath": "browser/screenshots/running--tablet-portrait--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:04.565Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 101363, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=partial_result", + "pngSha256": "372035595410bbf9801076c95fb39aa9ed36f03f21f7c3b5d0b11dfea8af15e7", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "partial_result", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/partial_result--tablet-portrait--dark.png", + "sidecarPath": "browser/screenshots/partial_result--tablet-portrait--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:05.200Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 101998, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=external_wait", + "pngSha256": "923d3f17550a81102fe0031ba5f17570693e795586f7d3ecaae1d10680f7d0bc", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "external_wait", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/external_wait--tablet-portrait--dark.png", + "sidecarPath": "browser/screenshots/external_wait--tablet-portrait--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:05.822Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 102620, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=proposal_pending", + "pngSha256": "26c13e6244ee613c5362007b9e642b585972e1bc6d5925dcb8f56cd2287d775c", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "proposal_pending", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/proposal_pending--tablet-portrait--dark.png", + "sidecarPath": "browser/screenshots/proposal_pending--tablet-portrait--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:06.477Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 103275, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=approval", + "pngSha256": "355ae39c5c112052e5289473329fde1065d410b3630ea6cdb1c05b8579dc5f50", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "approval", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/approval--tablet-portrait--dark.png", + "sidecarPath": "browser/screenshots/approval--tablet-portrait--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:07.126Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 103924, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=conflict", + "pngSha256": "502471464b4af6cef621da6844c97e6a98ec2704cd60db115a44bb8018c5fc95", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "conflict", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/conflict--tablet-portrait--dark.png", + "sidecarPath": "browser/screenshots/conflict--tablet-portrait--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:07.749Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 104547, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=recoverable_failure", + "pngSha256": "bfc27e19f1ca8dae55176d5c2e64cbb910caec238c79f743d3e082811f1b817f", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "recoverable_failure", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/recoverable_failure--tablet-portrait--dark.png", + "sidecarPath": "browser/screenshots/recoverable_failure--tablet-portrait--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:08.371Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 105170, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=reload_resume", + "pngSha256": "7a0d536972e47c843c5ae4bd90d7aa787ddb56f93dfa21aa4317035cf45038da", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "reload_resume", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/reload_resume--tablet-portrait--dark.png", + "sidecarPath": "browser/screenshots/reload_resume--tablet-portrait--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:09.589Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 106388, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=completed_receipt", + "pngSha256": "945ff90be43a3212a7918b2ad9fee5eecb116f3746bb1ce404e194b485a63704", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "completed_receipt", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/completed_receipt--tablet-portrait--dark.png", + "sidecarPath": "browser/screenshots/completed_receipt--tablet-portrait--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:10.832Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 107631, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=receipt_inspection", + "pngSha256": "b54b03bda649ebdff14e1cd223c608f4c2cc31ebf93d930af0e91ba7d2b3a014", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "receipt_inspection", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/receipt_inspection--tablet-portrait--dark.png", + "sidecarPath": "browser/screenshots/receipt_inspection--tablet-portrait--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:11.479Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 108278, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=export_share", + "pngSha256": "f9fffc7a289684e1b186b2c8580e8ec44690f707dc2a5873ca29f64a07fbc42e", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "export_share", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/export_share--tablet-portrait--dark.png", + "sidecarPath": "browser/screenshots/export_share--tablet-portrait--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:12.797Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 109595, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=first_arrival", + "pngSha256": "cda2a3175533d6bd2387c0777496b953f7a9eb1dd9fc689a955cecbf45004b1a", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "first_arrival", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/first_arrival--mobile-portrait--light.png", + "sidecarPath": "browser/screenshots/first_arrival--mobile-portrait--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:13.427Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 110225, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=orientation", + "pngSha256": "81024d4b4d3c1b820694b94cef5a93863cff4e2b955425dfc04d7135a7d40f55", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "orientation", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/orientation--mobile-portrait--light.png", + "sidecarPath": "browser/screenshots/orientation--mobile-portrait--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:14.056Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 110854, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=input", + "pngSha256": "64ab730d41b6625c8ed7ba24c97c5aae148c7f7f91423d1edcff5cfeaed9c32b", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "input", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/input--mobile-portrait--light.png", + "sidecarPath": "browser/screenshots/input--mobile-portrait--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:14.736Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 111534, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=validation_error", + "pngSha256": "0285d1a0cfac6293412bc86210ee563de7ea6e53527477a5e0aa730d1325439b", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "validation_error", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/validation_error--mobile-portrait--light.png", + "sidecarPath": "browser/screenshots/validation_error--mobile-portrait--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:15.353Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 112151, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=running", + "pngSha256": "598de7c35afd84412f52de7c741bbb49b3828c41765866a18b171380877ee1dd", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "running", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/running--mobile-portrait--light.png", + "sidecarPath": "browser/screenshots/running--mobile-portrait--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:16.001Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 112799, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=partial_result", + "pngSha256": "0fe8b7820d8815ee98ef6bd2bf6aa98b3ce756779d0e0a2090e0f5221a64e3a3", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "partial_result", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/partial_result--mobile-portrait--light.png", + "sidecarPath": "browser/screenshots/partial_result--mobile-portrait--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:16.657Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 113455, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=external_wait", + "pngSha256": "e9af1a346a602baa2962bd0fee41f0a0f339f8c2148440362fb87d7e4ba3affa", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "external_wait", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/external_wait--mobile-portrait--light.png", + "sidecarPath": "browser/screenshots/external_wait--mobile-portrait--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:17.296Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 114094, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=proposal_pending", + "pngSha256": "be46d748b8e5206d11fb54273487df33bf5e36acedd86b6d27cbd867df34d58f", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "proposal_pending", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/proposal_pending--mobile-portrait--light.png", + "sidecarPath": "browser/screenshots/proposal_pending--mobile-portrait--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:17.948Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 114746, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=approval", + "pngSha256": "3edad8d646f4a75442d1daf94c436edcda1f30540540e27c58725276a991811d", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "approval", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/approval--mobile-portrait--light.png", + "sidecarPath": "browser/screenshots/approval--mobile-portrait--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:18.547Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 115345, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=conflict", + "pngSha256": "d2cc440b884c3cb46f767aa2cb224a8bed31aa1fce3f4631520f1ba7d9045d28", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "conflict", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/conflict--mobile-portrait--light.png", + "sidecarPath": "browser/screenshots/conflict--mobile-portrait--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:19.147Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 115945, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=recoverable_failure", + "pngSha256": "bd440302423d9569996e8367afff9e02509dd283ae402fd2ea35d9305256836b", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "recoverable_failure", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/recoverable_failure--mobile-portrait--light.png", + "sidecarPath": "browser/screenshots/recoverable_failure--mobile-portrait--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:19.767Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 116565, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=reload_resume", + "pngSha256": "cc92c7733377b742470adc7775609ec1955dca15891390f703637e9bd3d9b895", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "reload_resume", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/reload_resume--mobile-portrait--light.png", + "sidecarPath": "browser/screenshots/reload_resume--mobile-portrait--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:20.997Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 117795, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=completed_receipt", + "pngSha256": "86c9eba07eb7cad3aacff6c9c86525f92bebb6097cf401199ba6599616402a77", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "completed_receipt", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/completed_receipt--mobile-portrait--light.png", + "sidecarPath": "browser/screenshots/completed_receipt--mobile-portrait--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:22.216Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 119014, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=receipt_inspection", + "pngSha256": "4d83eda8121c580f1cf7baf9a07f9dbbe13dd268b5dab9d8d32ebe297c14a60b", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "receipt_inspection", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/receipt_inspection--mobile-portrait--light.png", + "sidecarPath": "browser/screenshots/receipt_inspection--mobile-portrait--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:22.862Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 119660, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=export_share", + "pngSha256": "9d1ab338b1b40fbfbd68392cc76c901ec2a5b8beeaed92ad4ae966121ac37816", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "export_share", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/export_share--mobile-portrait--light.png", + "sidecarPath": "browser/screenshots/export_share--mobile-portrait--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:23.701Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 120499, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=first_arrival", + "pngSha256": "23ada60213740d1b971f4c1e52acfbd684a7a4d77bacb2079211c8f0ba128811", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "first_arrival", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/first_arrival--mobile-portrait--dark.png", + "sidecarPath": "browser/screenshots/first_arrival--mobile-portrait--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:24.299Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 121097, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=orientation", + "pngSha256": "2f0e5b1d53b7964c45f6af14f369888d2acc54d7bf92cf847ba5a2722a937f6b", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "orientation", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/orientation--mobile-portrait--dark.png", + "sidecarPath": "browser/screenshots/orientation--mobile-portrait--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:24.905Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 121702, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=input", + "pngSha256": "6ae7792ff2989b9419cce3c9fc233fd31e65734b16a3dae3e108ff7437745030", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "input", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/input--mobile-portrait--dark.png", + "sidecarPath": "browser/screenshots/input--mobile-portrait--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:25.560Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 122358, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=validation_error", + "pngSha256": "e24ac361b533ce1a79d4b4d272da8510b26a0a3333afcad4a6038082a9559e55", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "validation_error", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/validation_error--mobile-portrait--dark.png", + "sidecarPath": "browser/screenshots/validation_error--mobile-portrait--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:26.134Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 122932, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=running", + "pngSha256": "5f43f578b6f216e456e6d5dc377757da80dc652355a5f984d6e97dfaf4ca33b8", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "running", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/running--mobile-portrait--dark.png", + "sidecarPath": "browser/screenshots/running--mobile-portrait--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:26.720Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 123518, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=partial_result", + "pngSha256": "4840f0319cc0b7cd880c413562f2e0484fb3bb158c64a2d7512ff160990d03da", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "partial_result", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/partial_result--mobile-portrait--dark.png", + "sidecarPath": "browser/screenshots/partial_result--mobile-portrait--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:27.309Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 124107, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=external_wait", + "pngSha256": "c811840f6110500508908dd3a9f8747ced542e51c7ce0845be6126bf7bff8ddd", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "external_wait", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/external_wait--mobile-portrait--dark.png", + "sidecarPath": "browser/screenshots/external_wait--mobile-portrait--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:27.889Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 124686, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=proposal_pending", + "pngSha256": "a77a181b682ec73b2a8f2ad21714f4e8acac565ee924be938cdb997db2f0000c", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "proposal_pending", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/proposal_pending--mobile-portrait--dark.png", + "sidecarPath": "browser/screenshots/proposal_pending--mobile-portrait--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:28.473Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 125271, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=approval", + "pngSha256": "70ac78ee7195f55f2aaf448b33e44f1911ecc733e40b5d473d4f6fceebb9557f", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "approval", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/approval--mobile-portrait--dark.png", + "sidecarPath": "browser/screenshots/approval--mobile-portrait--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:29.056Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 125854, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=conflict", + "pngSha256": "4b42089f8163e9dccf08454b16e023ce216d99da822d12c6ffb32b338bf3e701", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "conflict", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/conflict--mobile-portrait--dark.png", + "sidecarPath": "browser/screenshots/conflict--mobile-portrait--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:29.634Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 126432, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=recoverable_failure", + "pngSha256": "c235f8f53766082f453457c0cd854f06c5a05f1e5a3da5ebdbca1bf8aec91923", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "recoverable_failure", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/recoverable_failure--mobile-portrait--dark.png", + "sidecarPath": "browser/screenshots/recoverable_failure--mobile-portrait--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:30.210Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 127008, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=reload_resume", + "pngSha256": "e21ec190d69f1fb4a47f518f021510cfebedc393109e5b8c21cae59f8134f7e4", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "reload_resume", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/reload_resume--mobile-portrait--dark.png", + "sidecarPath": "browser/screenshots/reload_resume--mobile-portrait--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:31.329Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 128127, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=completed_receipt", + "pngSha256": "0f0f96624b34b2e9560f0af595b58775adfdf13aef5829a189b2fd7638226df2", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "completed_receipt", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/completed_receipt--mobile-portrait--dark.png", + "sidecarPath": "browser/screenshots/completed_receipt--mobile-portrait--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:32.532Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 129330, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=receipt_inspection", + "pngSha256": "d9dfb5a0d3e3f9f782658997f8cfd220e25a421dcf3e364766ac2aaea528e6bf", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "receipt_inspection", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/receipt_inspection--mobile-portrait--dark.png", + "sidecarPath": "browser/screenshots/receipt_inspection--mobile-portrait--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:33.177Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 129975, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=export_share", + "pngSha256": "a10b7843287afc835f37700e4822f61eba4fc7677b1ef007823742c71e3973cb", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "export_share", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/export_share--mobile-portrait--dark.png", + "sidecarPath": "browser/screenshots/export_share--mobile-portrait--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:33.931Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 130729, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=first_arrival", + "pngSha256": "7b03f1b3e8770c1152ede0db7110fbf3c447b800908d74f0f776a7d374bbe27e", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "first_arrival", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/first_arrival--mobile-landscape--light.png", + "sidecarPath": "browser/screenshots/first_arrival--mobile-landscape--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:34.528Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 131326, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=orientation", + "pngSha256": "851b928430b7cdba1dbeb1d553c2f5b081bc2d3c5a75c9d17d788f17dd99b402", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "orientation", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/orientation--mobile-landscape--light.png", + "sidecarPath": "browser/screenshots/orientation--mobile-landscape--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:35.107Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 131905, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=input", + "pngSha256": "a302f1142ef98aef9da182f0bcd99f7fc6643790db94278554a3855d599250ad", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "input", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/input--mobile-landscape--light.png", + "sidecarPath": "browser/screenshots/input--mobile-landscape--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:35.740Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 132538, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=validation_error", + "pngSha256": "3b301bda0b4014f245213e2faab91a57aa5e62c4c3afa82817e8f38ced90d49e", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "validation_error", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/validation_error--mobile-landscape--light.png", + "sidecarPath": "browser/screenshots/validation_error--mobile-landscape--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:36.332Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 133130, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=running", + "pngSha256": "0fa8a65172b0c282fd177fb8ebd137a0ea24e92e86f2b064b9129d8494ce70e3", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "running", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/running--mobile-landscape--light.png", + "sidecarPath": "browser/screenshots/running--mobile-landscape--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:36.915Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 133713, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=partial_result", + "pngSha256": "c1eaa665df04a7f86f552d0b39cf0b4a890fdfd4024080f6484a99eeb8420dec", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "partial_result", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/partial_result--mobile-landscape--light.png", + "sidecarPath": "browser/screenshots/partial_result--mobile-landscape--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:37.509Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 134307, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=external_wait", + "pngSha256": "0f219f88e8e6fe36d50b04455d82774bd6e6cfc4a1f47d78f3deecdea67752e9", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "external_wait", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/external_wait--mobile-landscape--light.png", + "sidecarPath": "browser/screenshots/external_wait--mobile-landscape--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:38.087Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 134885, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=proposal_pending", + "pngSha256": "2849c9e362cb3e3a578176be5e162b2bfbecb10d22737d97024093f2e79bb8b3", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "proposal_pending", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/proposal_pending--mobile-landscape--light.png", + "sidecarPath": "browser/screenshots/proposal_pending--mobile-landscape--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:38.690Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 135488, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=approval", + "pngSha256": "7f134a7b655354ad3324536451d7b4dfa32e7d82834dc30dc331c407d66df118", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "approval", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/approval--mobile-landscape--light.png", + "sidecarPath": "browser/screenshots/approval--mobile-landscape--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:39.265Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 136063, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=conflict", + "pngSha256": "2618cfa2c664f5b9171d315853c8c56e857d62127a157af29b6cb87d356ba7d5", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "conflict", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/conflict--mobile-landscape--light.png", + "sidecarPath": "browser/screenshots/conflict--mobile-landscape--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:39.865Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 136663, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=recoverable_failure", + "pngSha256": "d451c5482b70cbeb2308d183c00c8970690fed1cc74e52550fe215102c08d3a0", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "recoverable_failure", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/recoverable_failure--mobile-landscape--light.png", + "sidecarPath": "browser/screenshots/recoverable_failure--mobile-landscape--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:40.453Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 137251, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=reload_resume", + "pngSha256": "b79c2beea7065de77d48f07b83ab27d5400012873fb563ece050b8e0438db54e", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "reload_resume", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/reload_resume--mobile-landscape--light.png", + "sidecarPath": "browser/screenshots/reload_resume--mobile-landscape--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:41.582Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 138380, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=completed_receipt", + "pngSha256": "97c4331f366b68fed93d861ede1226333cb734e5e33662600632da4acec142bf", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "completed_receipt", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/completed_receipt--mobile-landscape--light.png", + "sidecarPath": "browser/screenshots/completed_receipt--mobile-landscape--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:42.772Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 139570, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=receipt_inspection", + "pngSha256": "af78263a1ad13e5152c0895a0bc7e4bf6955a20c2807300dfc2f16a731cd4824", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "receipt_inspection", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/receipt_inspection--mobile-landscape--light.png", + "sidecarPath": "browser/screenshots/receipt_inspection--mobile-landscape--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:43.407Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 140205, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=export_share", + "pngSha256": "21d295c46658a32aa2eb941547bf0d3b66030c0d260e207e697ec94a6feb8ba6", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "export_share", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/export_share--mobile-landscape--light.png", + "sidecarPath": "browser/screenshots/export_share--mobile-landscape--light.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:44.145Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 140943, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=first_arrival", + "pngSha256": "e918db030bf30d65bd3590a8d70bf988aeda4abd649694c42accf7baac3cbf8e", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "first_arrival", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/first_arrival--mobile-landscape--dark.png", + "sidecarPath": "browser/screenshots/first_arrival--mobile-landscape--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:44.723Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 141521, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=orientation", + "pngSha256": "bb4ec470503c6278a80b3e058b1016797305a8ce681288183248ef83dcb7d89d", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "orientation", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/orientation--mobile-landscape--dark.png", + "sidecarPath": "browser/screenshots/orientation--mobile-landscape--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:45.307Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 142105, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=input", + "pngSha256": "06809cfc85368c8f81bb8cc15047aa89b190a9ed7e1b62d762f0efc70602b39d", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "input", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/input--mobile-landscape--dark.png", + "sidecarPath": "browser/screenshots/input--mobile-landscape--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:45.943Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 142741, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=validation_error", + "pngSha256": "39d1845ca1591d5e440def8afdfb4ca810f9bd20884340b63599916fc1c315ea", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "validation_error", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/validation_error--mobile-landscape--dark.png", + "sidecarPath": "browser/screenshots/validation_error--mobile-landscape--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:46.519Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 143317, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=running", + "pngSha256": "c690d0a7449aaeb3b77bdb18bd6196cb85415cc407e8f97df75b4b1c4061546e", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "running", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/running--mobile-landscape--dark.png", + "sidecarPath": "browser/screenshots/running--mobile-landscape--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:47.094Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 143892, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=partial_result", + "pngSha256": "64706db152768df76b65c8f29ab371075e4b41a3c353c359f66d983f3ab92355", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "partial_result", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/partial_result--mobile-landscape--dark.png", + "sidecarPath": "browser/screenshots/partial_result--mobile-landscape--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:47.673Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 144471, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=external_wait", + "pngSha256": "34a03098873894880e737252308bba67c58fa0db3602e5d2e3aa1b7049e4baca", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "external_wait", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/external_wait--mobile-landscape--dark.png", + "sidecarPath": "browser/screenshots/external_wait--mobile-landscape--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:48.261Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 145059, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=proposal_pending", + "pngSha256": "077c60d0e3c3eb806bacf6ce6ac98f15fe619044abb967021415059ec2383c30", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "proposal_pending", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/proposal_pending--mobile-landscape--dark.png", + "sidecarPath": "browser/screenshots/proposal_pending--mobile-landscape--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:48.859Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 145657, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=approval", + "pngSha256": "72c6c9b48a90fab1a15427fb260bfeaee3d8d357fe0c57ce554e37397949c673", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "approval", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/approval--mobile-landscape--dark.png", + "sidecarPath": "browser/screenshots/approval--mobile-landscape--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:49.439Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 146237, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=conflict", + "pngSha256": "c6efb44c983cd0e70996483d3465366365b56ea94d3f9cc3b459a429105339cc", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "conflict", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/conflict--mobile-landscape--dark.png", + "sidecarPath": "browser/screenshots/conflict--mobile-landscape--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:50.018Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 146816, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=recoverable_failure", + "pngSha256": "23949a59cc968deb19568c1ff997fa92e447087fcc7d9d5e777b9bc40b2aaf9f", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "recoverable_failure", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/recoverable_failure--mobile-landscape--dark.png", + "sidecarPath": "browser/screenshots/recoverable_failure--mobile-landscape--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:50.599Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 147397, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=reload_resume", + "pngSha256": "4e660d97c0c91ce99791ead3215f757ee46f73b721610b0621d6242f8947dc3c", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "reload_resume", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/reload_resume--mobile-landscape--dark.png", + "sidecarPath": "browser/screenshots/reload_resume--mobile-landscape--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:51.729Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 148527, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=completed_receipt", + "pngSha256": "8cc3b50c1f1d3f0f25d5f4b17bae738c1cefbb93ee33f4a2ba481960255a6cdb", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "completed_receipt", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/completed_receipt--mobile-landscape--dark.png", + "sidecarPath": "browser/screenshots/completed_receipt--mobile-landscape--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:52.910Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 149708, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=receipt_inspection", + "pngSha256": "1a8fb92041755e4fe5961abc7af321e6c08ba26c60e139257fd5eb23c8466ac8", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "receipt_inspection", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/receipt_inspection--mobile-landscape--dark.png", + "sidecarPath": "browser/screenshots/receipt_inspection--mobile-landscape--dark.json" + }, + { + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:53.536Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 150334, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=export_share", + "pngSha256": "e7286658fc443f9f642b69d3b57ae3aeddf8a73091191ea8abfbadd4e2243e90", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "export_share", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/export_share--mobile-landscape--dark.png", + "sidecarPath": "browser/screenshots/export_share--mobile-landscape--dark.json" + } + ], + "startedAt": "2026-07-22T08:49:23.201Z", + "verdict": "BROWSER_CERTIFIED", + "manifestSha256": "c52c2825017390bc96610f3b7491510ad16d21e26d6dc178d53ae84d4a503f71" +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--desktop--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--desktop--dark.json new file mode 100644 index 0000000..87535bd --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--desktop--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:01.461Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 38259, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=approval", + "pngSha256": "4b0cd3df06be782e4da9446523ed0652e94815bd7e4ecb8b09ab8d0e67901edd", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "approval", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--desktop--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--desktop--dark.png new file mode 100644 index 0000000..6564494 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--desktop--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--desktop--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--desktop--light.json new file mode 100644 index 0000000..e701d88 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--desktop--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:49:40.734Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 17532, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=approval", + "pngSha256": "626716422cf6e1790176fae18f323892155a49f66aa15e91475e99889144063e", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "approval", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--desktop--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--desktop--light.png new file mode 100644 index 0000000..9839c8e Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--desktop--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--mobile-landscape--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--mobile-landscape--dark.json new file mode 100644 index 0000000..0d33ce2 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--mobile-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:48.859Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 145657, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=approval", + "pngSha256": "72c6c9b48a90fab1a15427fb260bfeaee3d8d357fe0c57ce554e37397949c673", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "approval", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--mobile-landscape--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--mobile-landscape--dark.png new file mode 100644 index 0000000..8a4b7af Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--mobile-landscape--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--mobile-landscape--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--mobile-landscape--light.json new file mode 100644 index 0000000..2e2f931 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--mobile-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:38.690Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 135488, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=approval", + "pngSha256": "7f134a7b655354ad3324536451d7b4dfa32e7d82834dc30dc331c407d66df118", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "approval", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--mobile-landscape--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--mobile-landscape--light.png new file mode 100644 index 0000000..34a647e Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--mobile-landscape--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--mobile-portrait--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--mobile-portrait--dark.json new file mode 100644 index 0000000..99e6035 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--mobile-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:28.473Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 125271, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=approval", + "pngSha256": "70ac78ee7195f55f2aaf448b33e44f1911ecc733e40b5d473d4f6fceebb9557f", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "approval", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--mobile-portrait--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--mobile-portrait--dark.png new file mode 100644 index 0000000..fa06054 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--mobile-portrait--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--mobile-portrait--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--mobile-portrait--light.json new file mode 100644 index 0000000..2b244fc --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--mobile-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:17.948Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 114746, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=approval", + "pngSha256": "3edad8d646f4a75442d1daf94c436edcda1f30540540e27c58725276a991811d", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "approval", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--mobile-portrait--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--mobile-portrait--light.png new file mode 100644 index 0000000..4ffcec0 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--mobile-portrait--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--tablet-landscape--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--tablet-landscape--dark.json new file mode 100644 index 0000000..93c6b60 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--tablet-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:45.097Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 81896, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=approval", + "pngSha256": "55dd6ec368b57b594b7bc9b44849a8a577fb61e4fac0387bf6443b9ff58af269", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "approval", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--tablet-landscape--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--tablet-landscape--dark.png new file mode 100644 index 0000000..0bbd38c Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--tablet-landscape--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--tablet-landscape--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--tablet-landscape--light.json new file mode 100644 index 0000000..9a3f4b2 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--tablet-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:34.325Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 71123, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=approval", + "pngSha256": "8cc1a59acd80b9147c5e514835b14eeacf32b3ab8c05b84d02477aab3724e979", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "approval", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--tablet-landscape--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--tablet-landscape--light.png new file mode 100644 index 0000000..e42aaa3 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--tablet-landscape--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--tablet-portrait--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--tablet-portrait--dark.json new file mode 100644 index 0000000..aadae54 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--tablet-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:06.477Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 103275, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=approval", + "pngSha256": "355ae39c5c112052e5289473329fde1065d410b3630ea6cdb1c05b8579dc5f50", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "approval", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--tablet-portrait--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--tablet-portrait--dark.png new file mode 100644 index 0000000..884dcf7 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--tablet-portrait--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--tablet-portrait--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--tablet-portrait--light.json new file mode 100644 index 0000000..88dec80 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--tablet-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:55.713Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 92511, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=approval", + "pngSha256": "459ee0d8eb7a75abf31b51b12b740776b09c656f3b4b0ce1da7abd2639387f06", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "approval", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--tablet-portrait--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--tablet-portrait--light.png new file mode 100644 index 0000000..c6a3a6c Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--tablet-portrait--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--wide--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--wide--dark.json new file mode 100644 index 0000000..ec93f6e --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--wide--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:23.426Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 60224, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=approval", + "pngSha256": "701c484d220547277deabecc901dc39ef1788b24a95bf42d103732c8fbc8f04d", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "approval", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--wide--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--wide--dark.png new file mode 100644 index 0000000..494ff3a Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--wide--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--wide--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--wide--light.json new file mode 100644 index 0000000..21a45a1 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--wide--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:12.290Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 49088, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=approval", + "pngSha256": "f6d906b5166e211f28d56ff33d7f05d9b3f236647621824073301c0946804b49", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "approval", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--wide--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--wide--light.png new file mode 100644 index 0000000..eac5ea9 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/approval--wide--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--desktop--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--desktop--dark.json new file mode 100644 index 0000000..5b8c3cd --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--desktop--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:04.617Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 41415, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=completed_receipt", + "pngSha256": "da0ee6791b226ca8ec35361feb8136723318724683c3e3889fd33ebd1e4b6e86", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "completed_receipt", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--desktop--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--desktop--dark.png new file mode 100644 index 0000000..c89240d Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--desktop--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--desktop--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--desktop--light.json new file mode 100644 index 0000000..4d40921 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--desktop--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:49:48.347Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 25145, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=completed_receipt", + "pngSha256": "62eb8dc21032a528851ab16341a70a25bd162a424f4169c11d94f942e9deb363", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "completed_receipt", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--desktop--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--desktop--light.png new file mode 100644 index 0000000..70190b1 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--desktop--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--mobile-landscape--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--mobile-landscape--dark.json new file mode 100644 index 0000000..260d3e7 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--mobile-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:51.729Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 148527, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=completed_receipt", + "pngSha256": "8cc3b50c1f1d3f0f25d5f4b17bae738c1cefbb93ee33f4a2ba481960255a6cdb", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "completed_receipt", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--mobile-landscape--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--mobile-landscape--dark.png new file mode 100644 index 0000000..8d568f8 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--mobile-landscape--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--mobile-landscape--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--mobile-landscape--light.json new file mode 100644 index 0000000..c7f7796 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--mobile-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:41.582Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 138380, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=completed_receipt", + "pngSha256": "97c4331f366b68fed93d861ede1226333cb734e5e33662600632da4acec142bf", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "completed_receipt", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--mobile-landscape--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--mobile-landscape--light.png new file mode 100644 index 0000000..b0713a3 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--mobile-landscape--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--mobile-portrait--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--mobile-portrait--dark.json new file mode 100644 index 0000000..9f8ba96 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--mobile-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:31.329Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 128127, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=completed_receipt", + "pngSha256": "0f0f96624b34b2e9560f0af595b58775adfdf13aef5829a189b2fd7638226df2", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "completed_receipt", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--mobile-portrait--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--mobile-portrait--dark.png new file mode 100644 index 0000000..5e160d8 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--mobile-portrait--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--mobile-portrait--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--mobile-portrait--light.json new file mode 100644 index 0000000..3fbd072 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--mobile-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:20.997Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 117795, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=completed_receipt", + "pngSha256": "86c9eba07eb7cad3aacff6c9c86525f92bebb6097cf401199ba6599616402a77", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "completed_receipt", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--mobile-portrait--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--mobile-portrait--light.png new file mode 100644 index 0000000..632720c Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--mobile-portrait--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--tablet-landscape--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--tablet-landscape--dark.json new file mode 100644 index 0000000..bbea986 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--tablet-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:48.061Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 84860, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=completed_receipt", + "pngSha256": "30548a77fa323aa8689fe879ee0ce0ef1326ff5a31c096f18cec119f9a5a24ab", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "completed_receipt", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--tablet-landscape--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--tablet-landscape--dark.png new file mode 100644 index 0000000..67914d5 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--tablet-landscape--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--tablet-landscape--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--tablet-landscape--light.json new file mode 100644 index 0000000..800a692 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--tablet-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:37.357Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 74156, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=completed_receipt", + "pngSha256": "2e75016b90565b78103fd75256f2d5ac011cde206a6da16520efd15908e7a959", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "completed_receipt", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--tablet-landscape--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--tablet-landscape--light.png new file mode 100644 index 0000000..e251b2b Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--tablet-landscape--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--tablet-portrait--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--tablet-portrait--dark.json new file mode 100644 index 0000000..1813cbb --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--tablet-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:09.589Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 106388, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=completed_receipt", + "pngSha256": "945ff90be43a3212a7918b2ad9fee5eecb116f3746bb1ce404e194b485a63704", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "completed_receipt", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--tablet-portrait--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--tablet-portrait--dark.png new file mode 100644 index 0000000..6c4ec52 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--tablet-portrait--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--tablet-portrait--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--tablet-portrait--light.json new file mode 100644 index 0000000..37adda3 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--tablet-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:58.700Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 95498, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=completed_receipt", + "pngSha256": "21f2689823020a5611569c933adfdb710b97a85834d4fbcd421c49802d996107", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "completed_receipt", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--tablet-portrait--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--tablet-portrait--light.png new file mode 100644 index 0000000..6a7fd7c Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--tablet-portrait--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--wide--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--wide--dark.json new file mode 100644 index 0000000..2bcce6f --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--wide--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:26.630Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 63429, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=completed_receipt", + "pngSha256": "7e7c2a7b74ac05d3cb28fc323d894e247e70fe0f0244cb51a90335e27931bb0d", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "completed_receipt", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--wide--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--wide--dark.png new file mode 100644 index 0000000..4b032a1 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--wide--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--wide--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--wide--light.json new file mode 100644 index 0000000..c7ff41d --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--wide--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:15.429Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 52227, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=completed_receipt", + "pngSha256": "888416e0fa3eda4aace27e864a65d24ae55270466084ec10ad87a94e57f93fbb", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "completed_receipt", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--wide--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--wide--light.png new file mode 100644 index 0000000..9b87df4 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/completed_receipt--wide--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--desktop--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--desktop--dark.json new file mode 100644 index 0000000..a197f8d --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--desktop--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:02.102Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 38900, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=conflict", + "pngSha256": "2278dfaf634373c6ba1ba15edd36fbac50244bc51dc7d4b810c7c9c64ff18e46", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "conflict", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--desktop--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--desktop--dark.png new file mode 100644 index 0000000..094588b Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--desktop--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--desktop--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--desktop--light.json new file mode 100644 index 0000000..e243a98 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--desktop--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:49:42.521Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 19319, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=conflict", + "pngSha256": "cec43c461a25b8cc966bcdd0aaeee09f89baa3bc82bcf84103c9a63b9e9b6c13", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "conflict", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--desktop--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--desktop--light.png new file mode 100644 index 0000000..dff86ed Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--desktop--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--mobile-landscape--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--mobile-landscape--dark.json new file mode 100644 index 0000000..73b1429 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--mobile-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:49.439Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 146237, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=conflict", + "pngSha256": "c6efb44c983cd0e70996483d3465366365b56ea94d3f9cc3b459a429105339cc", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "conflict", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--mobile-landscape--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--mobile-landscape--dark.png new file mode 100644 index 0000000..c4943c1 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--mobile-landscape--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--mobile-landscape--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--mobile-landscape--light.json new file mode 100644 index 0000000..b631986 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--mobile-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:39.265Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 136063, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=conflict", + "pngSha256": "2618cfa2c664f5b9171d315853c8c56e857d62127a157af29b6cb87d356ba7d5", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "conflict", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--mobile-landscape--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--mobile-landscape--light.png new file mode 100644 index 0000000..bed21d3 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--mobile-landscape--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--mobile-portrait--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--mobile-portrait--dark.json new file mode 100644 index 0000000..e9a60a6 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--mobile-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:29.056Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 125854, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=conflict", + "pngSha256": "4b42089f8163e9dccf08454b16e023ce216d99da822d12c6ffb32b338bf3e701", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "conflict", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--mobile-portrait--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--mobile-portrait--dark.png new file mode 100644 index 0000000..0f10313 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--mobile-portrait--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--mobile-portrait--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--mobile-portrait--light.json new file mode 100644 index 0000000..b7c04d6 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--mobile-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:18.547Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 115345, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=conflict", + "pngSha256": "d2cc440b884c3cb46f767aa2cb224a8bed31aa1fce3f4631520f1ba7d9045d28", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "conflict", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--mobile-portrait--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--mobile-portrait--light.png new file mode 100644 index 0000000..46caef7 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--mobile-portrait--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--tablet-landscape--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--tablet-landscape--dark.json new file mode 100644 index 0000000..9bdd748 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--tablet-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:45.716Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 82514, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=conflict", + "pngSha256": "0a9b600dbdfbfd297cb7fea047e4ef29bd5b46d1551921cdabe04290a918b26b", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "conflict", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--tablet-landscape--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--tablet-landscape--dark.png new file mode 100644 index 0000000..c5ce918 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--tablet-landscape--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--tablet-landscape--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--tablet-landscape--light.json new file mode 100644 index 0000000..8557d99 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--tablet-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:34.941Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 71740, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=conflict", + "pngSha256": "2d045b3c00ba7f16672206e9ae35bc9127b02d27b59280df5400b5f5cb0a5561", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "conflict", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--tablet-landscape--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--tablet-landscape--light.png new file mode 100644 index 0000000..7382220 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--tablet-landscape--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--tablet-portrait--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--tablet-portrait--dark.json new file mode 100644 index 0000000..b7fedff --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--tablet-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:07.126Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 103924, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=conflict", + "pngSha256": "502471464b4af6cef621da6844c97e6a98ec2704cd60db115a44bb8018c5fc95", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "conflict", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--tablet-portrait--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--tablet-portrait--dark.png new file mode 100644 index 0000000..1ab5590 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--tablet-portrait--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--tablet-portrait--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--tablet-portrait--light.json new file mode 100644 index 0000000..5bdc8c4 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--tablet-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:56.350Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 93149, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=conflict", + "pngSha256": "32adf918ca5e830f786921b6f391412a9cf8b157f5e7aaaa7854868ffd6ac533", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "conflict", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--tablet-portrait--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--tablet-portrait--light.png new file mode 100644 index 0000000..7edee38 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--tablet-portrait--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--wide--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--wide--dark.json new file mode 100644 index 0000000..787e5a6 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--wide--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:24.109Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 60907, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=conflict", + "pngSha256": "1fa62695b020a4c43c2a7fc0118a8eff41b4da719e2590d158d39ffe176f7f41", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "conflict", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--wide--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--wide--dark.png new file mode 100644 index 0000000..37f6498 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--wide--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--wide--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--wide--light.json new file mode 100644 index 0000000..791d96e --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--wide--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:12.901Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 49699, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=conflict", + "pngSha256": "954da561ebff1b52e9a402789f64211f5a49a0e722cc3443b8d8f3f4fe91fc5d", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "conflict", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--wide--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--wide--light.png new file mode 100644 index 0000000..3cf6712 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/conflict--wide--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--desktop--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--desktop--dark.json new file mode 100644 index 0000000..f23d5fa --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--desktop--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:06.355Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 43153, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=export_share", + "pngSha256": "f4ff6428074635456ba40e1974fb1d07064a3ce4fd8b64ab42d91ac056dcf83d", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "export_share", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--desktop--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--desktop--dark.png new file mode 100644 index 0000000..1707dd0 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--desktop--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--desktop--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--desktop--light.json new file mode 100644 index 0000000..6ef73f5 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--desktop--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:49:52.271Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 29069, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=export_share", + "pngSha256": "81d637b418b55f771b316592c40d546ff2d9925ff13c81c32eff46827f589b47", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "export_share", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--desktop--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--desktop--light.png new file mode 100644 index 0000000..4ff5582 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--desktop--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--mobile-landscape--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--mobile-landscape--dark.json new file mode 100644 index 0000000..e6f72e3 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--mobile-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:53.536Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 150334, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=export_share", + "pngSha256": "e7286658fc443f9f642b69d3b57ae3aeddf8a73091191ea8abfbadd4e2243e90", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "export_share", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--mobile-landscape--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--mobile-landscape--dark.png new file mode 100644 index 0000000..d4988d8 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--mobile-landscape--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--mobile-landscape--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--mobile-landscape--light.json new file mode 100644 index 0000000..353a573 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--mobile-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:43.407Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 140205, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=export_share", + "pngSha256": "21d295c46658a32aa2eb941547bf0d3b66030c0d260e207e697ec94a6feb8ba6", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "export_share", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--mobile-landscape--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--mobile-landscape--light.png new file mode 100644 index 0000000..197173e Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--mobile-landscape--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--mobile-portrait--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--mobile-portrait--dark.json new file mode 100644 index 0000000..77241b3 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--mobile-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:33.177Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 129975, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=export_share", + "pngSha256": "a10b7843287afc835f37700e4822f61eba4fc7677b1ef007823742c71e3973cb", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "export_share", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--mobile-portrait--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--mobile-portrait--dark.png new file mode 100644 index 0000000..0cff57a Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--mobile-portrait--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--mobile-portrait--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--mobile-portrait--light.json new file mode 100644 index 0000000..d4d23fd --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--mobile-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:22.862Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 119660, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=export_share", + "pngSha256": "9d1ab338b1b40fbfbd68392cc76c901ec2a5b8beeaed92ad4ae966121ac37816", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "export_share", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--mobile-portrait--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--mobile-portrait--light.png new file mode 100644 index 0000000..40225f2 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--mobile-portrait--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--tablet-landscape--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--tablet-landscape--dark.json new file mode 100644 index 0000000..ba26244 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--tablet-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:49.812Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 86611, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=export_share", + "pngSha256": "f75156daef32135549ede5b5af5957044aea825ca716554d3746256f18755834", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "export_share", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--tablet-landscape--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--tablet-landscape--dark.png new file mode 100644 index 0000000..a5df389 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--tablet-landscape--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--tablet-landscape--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--tablet-landscape--light.json new file mode 100644 index 0000000..d40b8d1 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--tablet-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:39.176Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 75974, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=export_share", + "pngSha256": "089cf9665d88e7bd627d94ead5725d05a3eb079442b7c6c4b450ade4238e3eef", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "export_share", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--tablet-landscape--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--tablet-landscape--light.png new file mode 100644 index 0000000..7b49b56 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--tablet-landscape--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--tablet-portrait--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--tablet-portrait--dark.json new file mode 100644 index 0000000..f3e42ca --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--tablet-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:11.479Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 108278, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=export_share", + "pngSha256": "f9fffc7a289684e1b186b2c8580e8ec44690f707dc2a5873ca29f64a07fbc42e", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "export_share", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--tablet-portrait--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--tablet-portrait--dark.png new file mode 100644 index 0000000..16e7cda Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--tablet-portrait--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--tablet-portrait--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--tablet-portrait--light.json new file mode 100644 index 0000000..2719858 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--tablet-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:00.501Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 97299, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=export_share", + "pngSha256": "12b56a599f3a593f97d26aac15d9bda8e48588dfb30a9cb92b59ab36612e8820", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "export_share", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--tablet-portrait--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--tablet-portrait--light.png new file mode 100644 index 0000000..ecc4301 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--tablet-portrait--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--wide--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--wide--dark.json new file mode 100644 index 0000000..05bb47a --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--wide--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:28.474Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 65272, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=export_share", + "pngSha256": "30440f29170a462c7eb9c21f219f497b186deb6fa7f5e0e917a42705f1c026cc", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "export_share", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--wide--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--wide--dark.png new file mode 100644 index 0000000..1e6e72d Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--wide--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--wide--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--wide--light.json new file mode 100644 index 0000000..9569561 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--wide--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:17.279Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 54077, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=export_share", + "pngSha256": "ffc5031685acb434865f216145bfae44d7f90a7ea9b58a00c460b28329628bc1", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "export_share", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--wide--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--wide--light.png new file mode 100644 index 0000000..17fc026 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/export_share--wide--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--desktop--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--desktop--dark.json new file mode 100644 index 0000000..4768958 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--desktop--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:00.206Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 37004, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=external_wait", + "pngSha256": "9670134746f4d82d7e72e97f2c3531f696f8634d17bf3361a4f1ab65c82cbeaa", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "external_wait", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--desktop--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--desktop--dark.png new file mode 100644 index 0000000..1fa3614 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--desktop--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--desktop--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--desktop--light.json new file mode 100644 index 0000000..3ac0771 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--desktop--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:49:37.603Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 14401, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=external_wait", + "pngSha256": "d4eb9120482d2f61c5287dc4a42f03cb283dec3b2c7a5ada680432efab9c2f09", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "external_wait", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--desktop--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--desktop--light.png new file mode 100644 index 0000000..da1d720 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--desktop--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--mobile-landscape--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--mobile-landscape--dark.json new file mode 100644 index 0000000..4a250ca --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--mobile-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:47.673Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 144471, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=external_wait", + "pngSha256": "34a03098873894880e737252308bba67c58fa0db3602e5d2e3aa1b7049e4baca", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "external_wait", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--mobile-landscape--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--mobile-landscape--dark.png new file mode 100644 index 0000000..05368a8 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--mobile-landscape--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--mobile-landscape--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--mobile-landscape--light.json new file mode 100644 index 0000000..143fc6b --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--mobile-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:37.509Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 134307, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=external_wait", + "pngSha256": "0f219f88e8e6fe36d50b04455d82774bd6e6cfc4a1f47d78f3deecdea67752e9", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "external_wait", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--mobile-landscape--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--mobile-landscape--light.png new file mode 100644 index 0000000..24e31f9 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--mobile-landscape--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--mobile-portrait--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--mobile-portrait--dark.json new file mode 100644 index 0000000..561fd0e --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--mobile-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:27.309Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 124107, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=external_wait", + "pngSha256": "c811840f6110500508908dd3a9f8747ced542e51c7ce0845be6126bf7bff8ddd", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "external_wait", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--mobile-portrait--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--mobile-portrait--dark.png new file mode 100644 index 0000000..0bbf459 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--mobile-portrait--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--mobile-portrait--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--mobile-portrait--light.json new file mode 100644 index 0000000..8fe22c0 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--mobile-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:16.657Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 113455, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=external_wait", + "pngSha256": "e9af1a346a602baa2962bd0fee41f0a0f339f8c2148440362fb87d7e4ba3affa", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "external_wait", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--mobile-portrait--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--mobile-portrait--light.png new file mode 100644 index 0000000..4b6d3f8 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--mobile-portrait--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--tablet-landscape--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--tablet-landscape--dark.json new file mode 100644 index 0000000..2a4f269 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--tablet-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:43.875Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 80673, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=external_wait", + "pngSha256": "56cd1a5d922139ab957521ad0ea09f7f36968c911e9f13a66beccf55e07c037f", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "external_wait", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--tablet-landscape--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--tablet-landscape--dark.png new file mode 100644 index 0000000..88b689b Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--tablet-landscape--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--tablet-landscape--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--tablet-landscape--light.json new file mode 100644 index 0000000..3c7f940 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--tablet-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:33.041Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 69839, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=external_wait", + "pngSha256": "b901c9ac40ea6c343d5b9baab123e957230a3dff759f04cee33187f6cbb940c1", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "external_wait", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--tablet-landscape--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--tablet-landscape--light.png new file mode 100644 index 0000000..c046e4e Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--tablet-landscape--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--tablet-portrait--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--tablet-portrait--dark.json new file mode 100644 index 0000000..e52cc9d --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--tablet-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:05.200Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 101998, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=external_wait", + "pngSha256": "923d3f17550a81102fe0031ba5f17570693e795586f7d3ecaae1d10680f7d0bc", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "external_wait", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--tablet-portrait--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--tablet-portrait--dark.png new file mode 100644 index 0000000..3b58127 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--tablet-portrait--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--tablet-portrait--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--tablet-portrait--light.json new file mode 100644 index 0000000..08ad3b3 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--tablet-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:54.485Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 91283, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=external_wait", + "pngSha256": "d5ab52989a35a5fef9d272e08d840073c2d468942e515cbdb802ff48bfd9ba2a", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "external_wait", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--tablet-portrait--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--tablet-portrait--light.png new file mode 100644 index 0000000..4d5d3c1 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--tablet-portrait--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--wide--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--wide--dark.json new file mode 100644 index 0000000..abfe400 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--wide--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:22.126Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 58924, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=external_wait", + "pngSha256": "c8d84831e2b6e0531b34906fa4feb781d703f665e2a7988e58ef526e08b3d338", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "external_wait", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--wide--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--wide--dark.png new file mode 100644 index 0000000..dfcec5b Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--wide--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--wide--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--wide--light.json new file mode 100644 index 0000000..178b9d0 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--wide--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:11.006Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 47804, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=external_wait", + "pngSha256": "09222a087cf4479eef7e41ae4d7a1b15e29bc97e8ad047d14fabb03a66da1d2f", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "external_wait", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--wide--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--wide--light.png new file mode 100644 index 0000000..e9301ab Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/external_wait--wide--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--desktop--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--desktop--dark.json new file mode 100644 index 0000000..ff81b90 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--desktop--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:49:56.467Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 33265, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=first_arrival", + "pngSha256": "19b8f4c64edd08342024e61c057215f6d913c943c8444bdedc57c5ca932eab53", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "first_arrival", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--desktop--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--desktop--dark.png new file mode 100644 index 0000000..5e2bd33 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--desktop--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--desktop--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--desktop--light.json new file mode 100644 index 0000000..546b060 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--desktop--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:49:27.735Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 4533, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=first_arrival", + "pngSha256": "0c61e989d4f960cc9ea0738b5ded4931b9e7446e4281a398603cd2abfa1861de", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "first_arrival", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--desktop--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--desktop--light.png new file mode 100644 index 0000000..ba57bff Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--desktop--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--mobile-landscape--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--mobile-landscape--dark.json new file mode 100644 index 0000000..5a6b88b --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--mobile-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:44.145Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 140943, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=first_arrival", + "pngSha256": "e918db030bf30d65bd3590a8d70bf988aeda4abd649694c42accf7baac3cbf8e", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "first_arrival", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--mobile-landscape--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--mobile-landscape--dark.png new file mode 100644 index 0000000..b44aa84 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--mobile-landscape--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--mobile-landscape--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--mobile-landscape--light.json new file mode 100644 index 0000000..6ca805e --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--mobile-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:33.931Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 130729, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=first_arrival", + "pngSha256": "7b03f1b3e8770c1152ede0db7110fbf3c447b800908d74f0f776a7d374bbe27e", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "first_arrival", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--mobile-landscape--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--mobile-landscape--light.png new file mode 100644 index 0000000..26492ef Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--mobile-landscape--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--mobile-portrait--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--mobile-portrait--dark.json new file mode 100644 index 0000000..ff43417 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--mobile-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:23.701Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 120499, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=first_arrival", + "pngSha256": "23ada60213740d1b971f4c1e52acfbd684a7a4d77bacb2079211c8f0ba128811", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "first_arrival", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--mobile-portrait--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--mobile-portrait--dark.png new file mode 100644 index 0000000..0d4b992 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--mobile-portrait--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--mobile-portrait--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--mobile-portrait--light.json new file mode 100644 index 0000000..f01b075 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--mobile-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:12.797Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 109595, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=first_arrival", + "pngSha256": "cda2a3175533d6bd2387c0777496b953f7a9eb1dd9fc689a955cecbf45004b1a", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "first_arrival", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--mobile-portrait--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--mobile-portrait--light.png new file mode 100644 index 0000000..62cee7e Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--mobile-portrait--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--tablet-landscape--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--tablet-landscape--dark.json new file mode 100644 index 0000000..85b59de --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--tablet-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:40.102Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 76900, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=first_arrival", + "pngSha256": "7822be75b26fa056c95240deabe8a9f298b7a5fe51b95752747305941d23982a", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "first_arrival", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--tablet-landscape--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--tablet-landscape--dark.png new file mode 100644 index 0000000..63b57f3 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--tablet-landscape--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--tablet-landscape--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--tablet-landscape--light.json new file mode 100644 index 0000000..d3ff02d --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--tablet-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:29.252Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 66050, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=first_arrival", + "pngSha256": "d0bd94d230776ff7179cd460e05fabd1abfc4bc4b0272947851f62c79cb7d6cd", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "first_arrival", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--tablet-landscape--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--tablet-landscape--light.png new file mode 100644 index 0000000..0e8c9a4 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--tablet-landscape--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--tablet-portrait--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--tablet-portrait--dark.json new file mode 100644 index 0000000..d0ad843 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--tablet-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:01.369Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 98168, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=first_arrival", + "pngSha256": "53863ed7d18c1e4077ea034b4ab3e85f932c363f60ea36d728281178399b891f", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "first_arrival", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--tablet-portrait--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--tablet-portrait--dark.png new file mode 100644 index 0000000..e40303b Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--tablet-portrait--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--tablet-portrait--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--tablet-portrait--light.json new file mode 100644 index 0000000..c9596ff --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--tablet-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:50.671Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 87469, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=first_arrival", + "pngSha256": "b6231d65b934df0975fabb5d945c6c1fcbc00a2ba5439df8268fe26941981c7b", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "first_arrival", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--tablet-portrait--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--tablet-portrait--light.png new file mode 100644 index 0000000..611a85a Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--tablet-portrait--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--wide--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--wide--dark.json new file mode 100644 index 0000000..89f45b1 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--wide--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:18.092Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 54890, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=first_arrival", + "pngSha256": "b05f84a2df28fb171c36f44dea49a6c2d1834355130eee929ce4f97191249836", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "first_arrival", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--wide--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--wide--dark.png new file mode 100644 index 0000000..8019134 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--wide--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--wide--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--wide--light.json new file mode 100644 index 0000000..75133fe --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--wide--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:07.123Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 43921, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=first_arrival", + "pngSha256": "212650139cc72cd82320cab8bf0c63c01207070c1c9d6590f4b2442cfcdd1649", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "first_arrival", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--wide--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--wide--light.png new file mode 100644 index 0000000..99a889b Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/first_arrival--wide--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--desktop--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--desktop--dark.json new file mode 100644 index 0000000..e50f389 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--desktop--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:49:57.706Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 34504, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=input", + "pngSha256": "89f62eb41eeb8cb9b050b82dee9f5cc57d922958ef3908cff6f491d86c34ba93", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "input", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--desktop--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--desktop--dark.png new file mode 100644 index 0000000..709d632 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--desktop--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--desktop--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--desktop--light.json new file mode 100644 index 0000000..b17f0c0 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--desktop--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:49:31.143Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 7941, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=input", + "pngSha256": "af7b5e8e19e7939f286f4296d92301523e2e497775b5125b2f861c2a416546c5", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "input", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--desktop--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--desktop--light.png new file mode 100644 index 0000000..93d8247 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--desktop--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--mobile-landscape--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--mobile-landscape--dark.json new file mode 100644 index 0000000..d1a9ac9 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--mobile-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:45.307Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 142105, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=input", + "pngSha256": "06809cfc85368c8f81bb8cc15047aa89b190a9ed7e1b62d762f0efc70602b39d", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "input", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--mobile-landscape--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--mobile-landscape--dark.png new file mode 100644 index 0000000..8fc4172 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--mobile-landscape--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--mobile-landscape--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--mobile-landscape--light.json new file mode 100644 index 0000000..6de9e8d --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--mobile-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:35.107Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 131905, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=input", + "pngSha256": "a302f1142ef98aef9da182f0bcd99f7fc6643790db94278554a3855d599250ad", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "input", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--mobile-landscape--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--mobile-landscape--light.png new file mode 100644 index 0000000..d79acdb Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--mobile-landscape--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--mobile-portrait--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--mobile-portrait--dark.json new file mode 100644 index 0000000..81a88f9 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--mobile-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:24.905Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 121702, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=input", + "pngSha256": "6ae7792ff2989b9419cce3c9fc233fd31e65734b16a3dae3e108ff7437745030", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "input", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--mobile-portrait--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--mobile-portrait--dark.png new file mode 100644 index 0000000..67f2a57 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--mobile-portrait--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--mobile-portrait--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--mobile-portrait--light.json new file mode 100644 index 0000000..089ab6c --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--mobile-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:14.056Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 110854, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=input", + "pngSha256": "64ab730d41b6625c8ed7ba24c97c5aae148c7f7f91423d1edcff5cfeaed9c32b", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "input", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--mobile-portrait--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--mobile-portrait--light.png new file mode 100644 index 0000000..25b0dc9 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--mobile-portrait--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--tablet-landscape--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--tablet-landscape--dark.json new file mode 100644 index 0000000..0b87a5d --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--tablet-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:41.331Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 78129, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=input", + "pngSha256": "6d63f2e27abe53e7427d59c027c0678a6daf6ebacfa3b8ceeb4bfc4b1ef13061", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "input", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--tablet-landscape--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--tablet-landscape--dark.png new file mode 100644 index 0000000..f717aef Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--tablet-landscape--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--tablet-landscape--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--tablet-landscape--light.json new file mode 100644 index 0000000..694a07c --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--tablet-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:30.501Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 67299, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=input", + "pngSha256": "0359eecb5e44b7e959660fc8f42ddab622ce3fc888271d823f2038ac18480028", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "input", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--tablet-landscape--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--tablet-landscape--light.png new file mode 100644 index 0000000..d850644 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--tablet-landscape--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--tablet-portrait--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--tablet-portrait--dark.json new file mode 100644 index 0000000..763b06e --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--tablet-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:02.607Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 99405, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=input", + "pngSha256": "e39595d5af8c1c01b44a7ab397c7ddedf1078788241f3e8422b80d5190fe3270", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "input", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--tablet-portrait--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--tablet-portrait--dark.png new file mode 100644 index 0000000..c6b4a19 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--tablet-portrait--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--tablet-portrait--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--tablet-portrait--light.json new file mode 100644 index 0000000..ca52919 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--tablet-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:51.925Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 88723, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=input", + "pngSha256": "6e25da65adfa86898f551517bc4e009b8f85cdac8097e6c22023130a1d64d5d1", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "input", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--tablet-portrait--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--tablet-portrait--light.png new file mode 100644 index 0000000..3c8eebe Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--tablet-portrait--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--wide--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--wide--dark.json new file mode 100644 index 0000000..a26fa2d --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--wide--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:19.404Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 56202, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=input", + "pngSha256": "4bbf00a5183c8e2577dd0b90355bad203207e1f0910911e8e4a4514bb273ebd4", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "input", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--wide--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--wide--dark.png new file mode 100644 index 0000000..2b98faf Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--wide--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--wide--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--wide--light.json new file mode 100644 index 0000000..c6e7ca0 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--wide--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:08.391Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 45189, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=input", + "pngSha256": "e0eddf8dece870c73b862219d43e7295b0583a59a010f4eeabb7bb9e3bc89138", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "input", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--wide--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--wide--light.png new file mode 100644 index 0000000..b2e8c21 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/input--wide--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--desktop--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--desktop--dark.json new file mode 100644 index 0000000..944db40 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--desktop--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:49:57.083Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 33881, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=orientation", + "pngSha256": "5e031fc3fc738f94ca951722db175c52c50025ba151e3a19203fe7e10d181012", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "orientation", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--desktop--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--desktop--dark.png new file mode 100644 index 0000000..f48e53a Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--desktop--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--desktop--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--desktop--light.json new file mode 100644 index 0000000..dc9b1db --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--desktop--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:49:29.501Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 6299, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=orientation", + "pngSha256": "d46d551bb2f79728e3c604c2552202e51d3b6892f9125469aa23158289766ab6", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "orientation", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--desktop--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--desktop--light.png new file mode 100644 index 0000000..aae1364 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--desktop--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--mobile-landscape--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--mobile-landscape--dark.json new file mode 100644 index 0000000..a0b46b8 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--mobile-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:44.723Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 141521, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=orientation", + "pngSha256": "bb4ec470503c6278a80b3e058b1016797305a8ce681288183248ef83dcb7d89d", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "orientation", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--mobile-landscape--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--mobile-landscape--dark.png new file mode 100644 index 0000000..f29a105 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--mobile-landscape--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--mobile-landscape--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--mobile-landscape--light.json new file mode 100644 index 0000000..9cba5b5 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--mobile-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:34.528Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 131326, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=orientation", + "pngSha256": "851b928430b7cdba1dbeb1d553c2f5b081bc2d3c5a75c9d17d788f17dd99b402", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "orientation", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--mobile-landscape--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--mobile-landscape--light.png new file mode 100644 index 0000000..87b1c3e Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--mobile-landscape--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--mobile-portrait--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--mobile-portrait--dark.json new file mode 100644 index 0000000..a96bd6b --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--mobile-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:24.299Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 121097, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=orientation", + "pngSha256": "2f0e5b1d53b7964c45f6af14f369888d2acc54d7bf92cf847ba5a2722a937f6b", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "orientation", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--mobile-portrait--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--mobile-portrait--dark.png new file mode 100644 index 0000000..f4a5734 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--mobile-portrait--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--mobile-portrait--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--mobile-portrait--light.json new file mode 100644 index 0000000..99fb766 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--mobile-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:13.427Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 110225, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=orientation", + "pngSha256": "81024d4b4d3c1b820694b94cef5a93863cff4e2b955425dfc04d7135a7d40f55", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "orientation", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--mobile-portrait--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--mobile-portrait--light.png new file mode 100644 index 0000000..5b3e4ee Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--mobile-portrait--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--tablet-landscape--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--tablet-landscape--dark.json new file mode 100644 index 0000000..48e7052 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--tablet-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:40.725Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 77523, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=orientation", + "pngSha256": "d700780cfee88cd80ac60caf6ebfc8ee52e1772c009a42f6e347769b960cdb25", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "orientation", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--tablet-landscape--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--tablet-landscape--dark.png new file mode 100644 index 0000000..6cab94a Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--tablet-landscape--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--tablet-landscape--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--tablet-landscape--light.json new file mode 100644 index 0000000..9ce239f --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--tablet-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:29.876Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 66674, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=orientation", + "pngSha256": "49767f90f776ef9b457b36e90a81fa030dc08f7de5937e7e8ea8bc90d5e66afc", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "orientation", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--tablet-landscape--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--tablet-landscape--light.png new file mode 100644 index 0000000..5b229dc Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--tablet-landscape--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--tablet-portrait--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--tablet-portrait--dark.json new file mode 100644 index 0000000..92bd312 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--tablet-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:01.986Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 98784, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=orientation", + "pngSha256": "2915d3fd97d30a3ccc79e063a478c654ead810f431b5a8247ae835300601bdc4", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "orientation", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--tablet-portrait--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--tablet-portrait--dark.png new file mode 100644 index 0000000..ffbe6eb Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--tablet-portrait--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--tablet-portrait--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--tablet-portrait--light.json new file mode 100644 index 0000000..c94dc12 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--tablet-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:51.294Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 88092, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=orientation", + "pngSha256": "b6d331c8e6616e1d62b56666c1e57602217ab7ffd13082a60bc44fc0c646c576", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "orientation", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--tablet-portrait--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--tablet-portrait--light.png new file mode 100644 index 0000000..9e5c470 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--tablet-portrait--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--wide--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--wide--dark.json new file mode 100644 index 0000000..02a671b --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--wide--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:18.744Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 55542, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=orientation", + "pngSha256": "48892a54f79c10954cdd50feb8ae715e2e824daf817900dd3f11fdb8be2b2995", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "orientation", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--wide--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--wide--dark.png new file mode 100644 index 0000000..3ba4926 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--wide--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--wide--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--wide--light.json new file mode 100644 index 0000000..1256219 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--wide--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:07.755Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 44553, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=orientation", + "pngSha256": "26fc5d643f728918edee2b3ed50e0e76c6e3babfdcc03266252bbef6c48753db", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "orientation", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--wide--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--wide--light.png new file mode 100644 index 0000000..5040e34 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/orientation--wide--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--desktop--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--desktop--dark.json new file mode 100644 index 0000000..ce76c97 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--desktop--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:49:59.577Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 36375, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=partial_result", + "pngSha256": "0cdb519fe686bcd808b217695ee5b2980906d507b6b832a0777a12ecba2396ce", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "partial_result", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--desktop--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--desktop--dark.png new file mode 100644 index 0000000..1dd6a4a Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--desktop--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--desktop--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--desktop--light.json new file mode 100644 index 0000000..0863d7e --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--desktop--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:49:36.001Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 12799, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=partial_result", + "pngSha256": "52d1192350f56d2218a236dcf173f29a537379ab3997836430446c82f4601df1", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "partial_result", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--desktop--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--desktop--light.png new file mode 100644 index 0000000..20f6ac1 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--desktop--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--mobile-landscape--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--mobile-landscape--dark.json new file mode 100644 index 0000000..cbb6b23 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--mobile-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:47.094Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 143892, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=partial_result", + "pngSha256": "64706db152768df76b65c8f29ab371075e4b41a3c353c359f66d983f3ab92355", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "partial_result", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--mobile-landscape--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--mobile-landscape--dark.png new file mode 100644 index 0000000..db485e5 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--mobile-landscape--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--mobile-landscape--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--mobile-landscape--light.json new file mode 100644 index 0000000..30e2243 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--mobile-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:36.915Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 133713, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=partial_result", + "pngSha256": "c1eaa665df04a7f86f552d0b39cf0b4a890fdfd4024080f6484a99eeb8420dec", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "partial_result", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--mobile-landscape--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--mobile-landscape--light.png new file mode 100644 index 0000000..f83f023 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--mobile-landscape--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--mobile-portrait--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--mobile-portrait--dark.json new file mode 100644 index 0000000..8c6f84e --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--mobile-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:26.720Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 123518, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=partial_result", + "pngSha256": "4840f0319cc0b7cd880c413562f2e0484fb3bb158c64a2d7512ff160990d03da", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "partial_result", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--mobile-portrait--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--mobile-portrait--dark.png new file mode 100644 index 0000000..9a2e774 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--mobile-portrait--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--mobile-portrait--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--mobile-portrait--light.json new file mode 100644 index 0000000..07c3635 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--mobile-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:16.001Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 112799, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=partial_result", + "pngSha256": "0fe8b7820d8815ee98ef6bd2bf6aa98b3ce756779d0e0a2090e0f5221a64e3a3", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "partial_result", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--mobile-portrait--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--mobile-portrait--light.png new file mode 100644 index 0000000..c7fdc42 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--mobile-portrait--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--tablet-landscape--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--tablet-landscape--dark.json new file mode 100644 index 0000000..621dd42 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--tablet-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:43.253Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 80051, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=partial_result", + "pngSha256": "edafb481e4ee8b4ad1c8b0be2feb153b1a35ec0c91d660c0d87dbb5a37793496", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "partial_result", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--tablet-landscape--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--tablet-landscape--dark.png new file mode 100644 index 0000000..e241b95 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--tablet-landscape--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--tablet-landscape--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--tablet-landscape--light.json new file mode 100644 index 0000000..350375f --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--tablet-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:32.417Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 69216, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=partial_result", + "pngSha256": "d25868a2d4f793a5d7416444d10e22b6d53d16faac28ab1a4e03dfc3105adfa2", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "partial_result", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--tablet-landscape--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--tablet-landscape--light.png new file mode 100644 index 0000000..95664c0 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--tablet-landscape--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--tablet-portrait--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--tablet-portrait--dark.json new file mode 100644 index 0000000..3f17a09 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--tablet-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:04.565Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 101363, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=partial_result", + "pngSha256": "372035595410bbf9801076c95fb39aa9ed36f03f21f7c3b5d0b11dfea8af15e7", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "partial_result", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--tablet-portrait--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--tablet-portrait--dark.png new file mode 100644 index 0000000..33560ad Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--tablet-portrait--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--tablet-portrait--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--tablet-portrait--light.json new file mode 100644 index 0000000..a12d7eb --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--tablet-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:53.867Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 90665, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=partial_result", + "pngSha256": "99e380752a9556a887e386e4b9c8da5bb668c870e3e83ab7ac877fecbd305b13", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "partial_result", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--tablet-portrait--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--tablet-portrait--light.png new file mode 100644 index 0000000..25db558 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--tablet-portrait--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--wide--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--wide--dark.json new file mode 100644 index 0000000..477c73d --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--wide--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:21.451Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 58249, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=partial_result", + "pngSha256": "4c499f1c8dd5e79738a5b9fca18e2ab55de5f24a9631bf4615af2c115de5ca94", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "partial_result", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--wide--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--wide--dark.png new file mode 100644 index 0000000..ac4e0c8 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--wide--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--wide--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--wide--light.json new file mode 100644 index 0000000..7db1c93 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--wide--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:10.361Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 47159, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=partial_result", + "pngSha256": "aa4eb4b15a8df144037a3065841976ba375f20072fb182cd1990f29dd640b688", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "partial_result", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--wide--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--wide--light.png new file mode 100644 index 0000000..355ced7 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/partial_result--wide--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--desktop--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--desktop--dark.json new file mode 100644 index 0000000..503ea0a --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--desktop--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:00.839Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 37637, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=proposal_pending", + "pngSha256": "6e919b4c94bdd4cb37a874bbca9c83e5be672989af07dca9854ef876f2e7322e", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "proposal_pending", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--desktop--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--desktop--dark.png new file mode 100644 index 0000000..2e1fb6d Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--desktop--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--desktop--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--desktop--light.json new file mode 100644 index 0000000..d2260bf --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--desktop--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:49:39.152Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 15950, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=proposal_pending", + "pngSha256": "99c82a7eca195fe447379215c284d8968e4cc1e75ddbddbccc9c34d6e2eacf69", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "proposal_pending", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--desktop--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--desktop--light.png new file mode 100644 index 0000000..09eaf79 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--desktop--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--mobile-landscape--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--mobile-landscape--dark.json new file mode 100644 index 0000000..ac525fb --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--mobile-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:48.261Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 145059, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=proposal_pending", + "pngSha256": "077c60d0e3c3eb806bacf6ce6ac98f15fe619044abb967021415059ec2383c30", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "proposal_pending", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--mobile-landscape--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--mobile-landscape--dark.png new file mode 100644 index 0000000..7af09e5 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--mobile-landscape--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--mobile-landscape--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--mobile-landscape--light.json new file mode 100644 index 0000000..adbc43a --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--mobile-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:38.087Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 134885, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=proposal_pending", + "pngSha256": "2849c9e362cb3e3a578176be5e162b2bfbecb10d22737d97024093f2e79bb8b3", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "proposal_pending", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--mobile-landscape--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--mobile-landscape--light.png new file mode 100644 index 0000000..c375c84 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--mobile-landscape--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--mobile-portrait--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--mobile-portrait--dark.json new file mode 100644 index 0000000..4b98f7c --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--mobile-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:27.889Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 124686, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=proposal_pending", + "pngSha256": "a77a181b682ec73b2a8f2ad21714f4e8acac565ee924be938cdb997db2f0000c", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "proposal_pending", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--mobile-portrait--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--mobile-portrait--dark.png new file mode 100644 index 0000000..a0c64e2 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--mobile-portrait--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--mobile-portrait--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--mobile-portrait--light.json new file mode 100644 index 0000000..049abbd --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--mobile-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:17.296Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 114094, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=proposal_pending", + "pngSha256": "be46d748b8e5206d11fb54273487df33bf5e36acedd86b6d27cbd867df34d58f", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "proposal_pending", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--mobile-portrait--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--mobile-portrait--light.png new file mode 100644 index 0000000..9087eae Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--mobile-portrait--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--tablet-landscape--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--tablet-landscape--dark.json new file mode 100644 index 0000000..30ccb51 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--tablet-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:44.483Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 81281, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=proposal_pending", + "pngSha256": "3c6fa0a75c5375a0ae4f24e7bce337bfbadf8de0a634ab673aaebd44e3194284", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "proposal_pending", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--tablet-landscape--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--tablet-landscape--dark.png new file mode 100644 index 0000000..65f9732 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--tablet-landscape--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--tablet-landscape--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--tablet-landscape--light.json new file mode 100644 index 0000000..5544f3d --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--tablet-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:33.677Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 70475, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=proposal_pending", + "pngSha256": "4fbc34d58d737de01c1a4631f7f8c2fd577beeb2c9a306eda072dff98e36ab36", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "proposal_pending", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--tablet-landscape--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--tablet-landscape--light.png new file mode 100644 index 0000000..48dc0ea Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--tablet-landscape--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--tablet-portrait--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--tablet-portrait--dark.json new file mode 100644 index 0000000..c8c6f62 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--tablet-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:05.822Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 102620, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=proposal_pending", + "pngSha256": "26c13e6244ee613c5362007b9e642b585972e1bc6d5925dcb8f56cd2287d775c", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "proposal_pending", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--tablet-portrait--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--tablet-portrait--dark.png new file mode 100644 index 0000000..0af7776 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--tablet-portrait--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--tablet-portrait--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--tablet-portrait--light.json new file mode 100644 index 0000000..47c7b4c --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--tablet-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:55.096Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 91894, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=proposal_pending", + "pngSha256": "429502d7d3f6090c99a17d850f462c63eaf2d6748ff810253b976418dbd0fbbe", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "proposal_pending", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--tablet-portrait--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--tablet-portrait--light.png new file mode 100644 index 0000000..dc9a4ae Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--tablet-portrait--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--wide--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--wide--dark.json new file mode 100644 index 0000000..ede7587 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--wide--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:22.767Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 59565, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=proposal_pending", + "pngSha256": "85e81f29350d8707aa3d93c2342134f8a9808e516fa675e9f1a6b2b815f9832f", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "proposal_pending", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--wide--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--wide--dark.png new file mode 100644 index 0000000..627794b Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--wide--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--wide--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--wide--light.json new file mode 100644 index 0000000..0cc2681 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--wide--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:11.655Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 48453, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=proposal_pending", + "pngSha256": "98aa197bb1eaa3ee491b370165a12c431dee200003e501fa953231694a4e1806", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "proposal_pending", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--wide--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--wide--light.png new file mode 100644 index 0000000..a182934 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/proposal_pending--wide--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--desktop--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--desktop--dark.json new file mode 100644 index 0000000..be4e886 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--desktop--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:05.764Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 42562, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=receipt_inspection", + "pngSha256": "0015b991d190f09f6f98885e4465a8d0d2e0099466457b7ba52915f03112922d", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "receipt_inspection", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--desktop--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--desktop--dark.png new file mode 100644 index 0000000..c5e7ffb Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--desktop--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--desktop--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--desktop--light.json new file mode 100644 index 0000000..b649582 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--desktop--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:49:50.669Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 27467, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=receipt_inspection", + "pngSha256": "50cbd1590459128287928baad002a7d628359275ca34973c2ba166f3b7b0667e", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "receipt_inspection", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--desktop--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--desktop--light.png new file mode 100644 index 0000000..750444f Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--desktop--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--mobile-landscape--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--mobile-landscape--dark.json new file mode 100644 index 0000000..19ee81e --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--mobile-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:52.910Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 149708, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=receipt_inspection", + "pngSha256": "1a8fb92041755e4fe5961abc7af321e6c08ba26c60e139257fd5eb23c8466ac8", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "receipt_inspection", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--mobile-landscape--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--mobile-landscape--dark.png new file mode 100644 index 0000000..d5c77e1 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--mobile-landscape--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--mobile-landscape--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--mobile-landscape--light.json new file mode 100644 index 0000000..5675b49 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--mobile-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:42.772Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 139570, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=receipt_inspection", + "pngSha256": "af78263a1ad13e5152c0895a0bc7e4bf6955a20c2807300dfc2f16a731cd4824", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "receipt_inspection", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--mobile-landscape--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--mobile-landscape--light.png new file mode 100644 index 0000000..faf15c8 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--mobile-landscape--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--mobile-portrait--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--mobile-portrait--dark.json new file mode 100644 index 0000000..c4cd1dc --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--mobile-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:32.532Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 129330, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=receipt_inspection", + "pngSha256": "d9dfb5a0d3e3f9f782658997f8cfd220e25a421dcf3e364766ac2aaea528e6bf", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "receipt_inspection", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--mobile-portrait--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--mobile-portrait--dark.png new file mode 100644 index 0000000..ba4c2f7 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--mobile-portrait--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--mobile-portrait--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--mobile-portrait--light.json new file mode 100644 index 0000000..85478cf --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--mobile-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:22.216Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 119014, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=receipt_inspection", + "pngSha256": "4d83eda8121c580f1cf7baf9a07f9dbbe13dd268b5dab9d8d32ebe297c14a60b", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "receipt_inspection", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--mobile-portrait--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--mobile-portrait--light.png new file mode 100644 index 0000000..c0df633 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--mobile-portrait--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--tablet-landscape--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--tablet-landscape--dark.json new file mode 100644 index 0000000..5c557ae --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--tablet-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:49.206Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 86004, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=receipt_inspection", + "pngSha256": "64b1bd6f4fcc71ea77c4bd24a1296a31f601cf7ca485345a1bc2413ad78d3169", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "receipt_inspection", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--tablet-landscape--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--tablet-landscape--dark.png new file mode 100644 index 0000000..8cc484a Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--tablet-landscape--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--tablet-landscape--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--tablet-landscape--light.json new file mode 100644 index 0000000..67a2018 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--tablet-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:38.555Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 75353, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=receipt_inspection", + "pngSha256": "2c2d6a2b85990b55d62e76d9a0f1feac32a9c69eaf687c906155b842ff9b3b2c", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "receipt_inspection", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--tablet-landscape--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--tablet-landscape--light.png new file mode 100644 index 0000000..0d95700 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--tablet-landscape--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--tablet-portrait--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--tablet-portrait--dark.json new file mode 100644 index 0000000..ffb1613 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--tablet-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:10.832Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 107631, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=receipt_inspection", + "pngSha256": "b54b03bda649ebdff14e1cd223c608f4c2cc31ebf93d930af0e91ba7d2b3a014", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "receipt_inspection", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--tablet-portrait--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--tablet-portrait--dark.png new file mode 100644 index 0000000..c5b5711 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--tablet-portrait--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--tablet-portrait--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--tablet-portrait--light.json new file mode 100644 index 0000000..7fd8a71 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--tablet-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:59.869Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 96667, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=receipt_inspection", + "pngSha256": "c9916c7dd636f02b62d721125d65b4d73b399352950e8678458ee94201c2aad2", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "receipt_inspection", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--tablet-portrait--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--tablet-portrait--light.png new file mode 100644 index 0000000..c00a079 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--tablet-portrait--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--wide--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--wide--dark.json new file mode 100644 index 0000000..704e905 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--wide--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:27.820Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 64619, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=receipt_inspection", + "pngSha256": "ad1e10814c4a940c662729276fcdd0358248bd4a0dbc3258c761b300d5fa994b", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "receipt_inspection", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--wide--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--wide--dark.png new file mode 100644 index 0000000..3ed0ad6 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--wide--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--wide--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--wide--light.json new file mode 100644 index 0000000..a21659b --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--wide--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:16.621Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 53419, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=receipt_inspection", + "pngSha256": "c0c36a1385a2d7022564366c929c6b94f5b7dc32732b135ec1093fcd0f00d132", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "receipt_inspection", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--wide--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--wide--light.png new file mode 100644 index 0000000..94b87d6 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/receipt_inspection--wide--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--desktop--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--desktop--dark.json new file mode 100644 index 0000000..12ce6ee --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--desktop--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:02.757Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 39555, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=recoverable_failure", + "pngSha256": "b99a5860194bab5fa02929746aef2ce619becfa926b15c93142ef9f7c7a99efb", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "recoverable_failure", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--desktop--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--desktop--dark.png new file mode 100644 index 0000000..b0072de Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--desktop--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--desktop--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--desktop--light.json new file mode 100644 index 0000000..a3fd84a --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--desktop--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:49:44.336Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 21134, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=recoverable_failure", + "pngSha256": "2b3af8ca27a709e13d61367d375beb8caf4f87edff0da94d6d0b3791c6f692f2", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "recoverable_failure", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--desktop--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--desktop--light.png new file mode 100644 index 0000000..62e5b1e Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--desktop--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--mobile-landscape--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--mobile-landscape--dark.json new file mode 100644 index 0000000..580989f --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--mobile-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:50.018Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 146816, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=recoverable_failure", + "pngSha256": "23949a59cc968deb19568c1ff997fa92e447087fcc7d9d5e777b9bc40b2aaf9f", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "recoverable_failure", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--mobile-landscape--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--mobile-landscape--dark.png new file mode 100644 index 0000000..24ff3fd Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--mobile-landscape--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--mobile-landscape--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--mobile-landscape--light.json new file mode 100644 index 0000000..0099279 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--mobile-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:39.865Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 136663, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=recoverable_failure", + "pngSha256": "d451c5482b70cbeb2308d183c00c8970690fed1cc74e52550fe215102c08d3a0", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "recoverable_failure", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--mobile-landscape--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--mobile-landscape--light.png new file mode 100644 index 0000000..1ae0da1 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--mobile-landscape--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--mobile-portrait--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--mobile-portrait--dark.json new file mode 100644 index 0000000..5bcb1cc --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--mobile-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:29.634Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 126432, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=recoverable_failure", + "pngSha256": "c235f8f53766082f453457c0cd854f06c5a05f1e5a3da5ebdbca1bf8aec91923", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "recoverable_failure", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--mobile-portrait--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--mobile-portrait--dark.png new file mode 100644 index 0000000..fe1a928 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--mobile-portrait--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--mobile-portrait--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--mobile-portrait--light.json new file mode 100644 index 0000000..c356e3c --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--mobile-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:19.147Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 115945, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=recoverable_failure", + "pngSha256": "bd440302423d9569996e8367afff9e02509dd283ae402fd2ea35d9305256836b", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "recoverable_failure", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--mobile-portrait--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--mobile-portrait--light.png new file mode 100644 index 0000000..f8a3b75 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--mobile-portrait--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--tablet-landscape--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--tablet-landscape--dark.json new file mode 100644 index 0000000..917ef2d --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--tablet-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:46.336Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 83134, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=recoverable_failure", + "pngSha256": "d2beea0a51db9efe5f9df422e922497f13d674c1ba5765afbf1de22955809d97", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "recoverable_failure", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--tablet-landscape--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--tablet-landscape--dark.png new file mode 100644 index 0000000..e6eff3f Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--tablet-landscape--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--tablet-landscape--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--tablet-landscape--light.json new file mode 100644 index 0000000..f5ddfe0 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--tablet-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:35.565Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 72363, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=recoverable_failure", + "pngSha256": "6f1e02f72267ffaf20f5756fc90ece33f425301d32c2fc25caf03e91dd697f92", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "recoverable_failure", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--tablet-landscape--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--tablet-landscape--light.png new file mode 100644 index 0000000..8dc89e4 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--tablet-landscape--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--tablet-portrait--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--tablet-portrait--dark.json new file mode 100644 index 0000000..bcb6929 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--tablet-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:07.749Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 104547, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=recoverable_failure", + "pngSha256": "bfc27e19f1ca8dae55176d5c2e64cbb910caec238c79f743d3e082811f1b817f", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "recoverable_failure", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--tablet-portrait--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--tablet-portrait--dark.png new file mode 100644 index 0000000..2c7a7e8 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--tablet-portrait--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--tablet-portrait--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--tablet-portrait--light.json new file mode 100644 index 0000000..c30306b --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--tablet-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:56.969Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 93767, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=recoverable_failure", + "pngSha256": "b4c97ae024b85494bf6ccf7b3df04f4dbdfc700faab17412c1ca2e5de87f2e00", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "recoverable_failure", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--tablet-portrait--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--tablet-portrait--light.png new file mode 100644 index 0000000..2c1eb58 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--tablet-portrait--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--wide--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--wide--dark.json new file mode 100644 index 0000000..1b3fe63 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--wide--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:24.764Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 61562, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=recoverable_failure", + "pngSha256": "643e60253754ed2e184e371ecf8d616fc506d356ea1583a81063591034424062", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "recoverable_failure", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--wide--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--wide--dark.png new file mode 100644 index 0000000..87d2ea6 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--wide--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--wide--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--wide--light.json new file mode 100644 index 0000000..e825eaa --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--wide--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:13.578Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 50376, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=recoverable_failure", + "pngSha256": "266557e6efd127b8e8e7eec26d9721a0e9b3ada25c86b4a75fe7dc8091f0b0b2", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "recoverable_failure", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--wide--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--wide--light.png new file mode 100644 index 0000000..8a1f74e Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/recoverable_failure--wide--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--desktop--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--desktop--dark.json new file mode 100644 index 0000000..ef5b1bb --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--desktop--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:03.402Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 40200, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=reload_resume", + "pngSha256": "7bbb614a5b24a17e49b969c00d8a56f27373ce356645d064a79d19b98b5482a4", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "reload_resume", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--desktop--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--desktop--dark.png new file mode 100644 index 0000000..2364828 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--desktop--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--desktop--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--desktop--light.json new file mode 100644 index 0000000..f9f8130 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--desktop--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:49:46.069Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 22867, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=reload_resume", + "pngSha256": "c122312cba0a30b186de2f0ec6c3bb269e2cc10bd2cf46ca09209837a97117d4", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "reload_resume", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--desktop--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--desktop--light.png new file mode 100644 index 0000000..a879368 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--desktop--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--mobile-landscape--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--mobile-landscape--dark.json new file mode 100644 index 0000000..4f3b5b3 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--mobile-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:50.599Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 147397, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=reload_resume", + "pngSha256": "4e660d97c0c91ce99791ead3215f757ee46f73b721610b0621d6242f8947dc3c", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "reload_resume", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--mobile-landscape--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--mobile-landscape--dark.png new file mode 100644 index 0000000..d64de60 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--mobile-landscape--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--mobile-landscape--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--mobile-landscape--light.json new file mode 100644 index 0000000..01c6de1 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--mobile-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:40.453Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 137251, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=reload_resume", + "pngSha256": "b79c2beea7065de77d48f07b83ab27d5400012873fb563ece050b8e0438db54e", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "reload_resume", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--mobile-landscape--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--mobile-landscape--light.png new file mode 100644 index 0000000..57e2479 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--mobile-landscape--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--mobile-portrait--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--mobile-portrait--dark.json new file mode 100644 index 0000000..927d1db --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--mobile-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:30.210Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 127008, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=reload_resume", + "pngSha256": "e21ec190d69f1fb4a47f518f021510cfebedc393109e5b8c21cae59f8134f7e4", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "reload_resume", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--mobile-portrait--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--mobile-portrait--dark.png new file mode 100644 index 0000000..636a39f Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--mobile-portrait--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--mobile-portrait--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--mobile-portrait--light.json new file mode 100644 index 0000000..8ab675b --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--mobile-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:19.767Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 116565, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=reload_resume", + "pngSha256": "cc92c7733377b742470adc7775609ec1955dca15891390f703637e9bd3d9b895", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "reload_resume", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--mobile-portrait--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--mobile-portrait--light.png new file mode 100644 index 0000000..a3b7960 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--mobile-portrait--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--tablet-landscape--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--tablet-landscape--dark.json new file mode 100644 index 0000000..e9b9caa --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--tablet-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:46.926Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 83724, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=reload_resume", + "pngSha256": "5318d9ea5a2c999969ec544c77ea5e11d368b4935a16fe2f4650cab3e08f40db", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "reload_resume", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--tablet-landscape--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--tablet-landscape--dark.png new file mode 100644 index 0000000..b48baf2 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--tablet-landscape--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--tablet-landscape--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--tablet-landscape--light.json new file mode 100644 index 0000000..95a4716 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--tablet-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:36.190Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 72988, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=reload_resume", + "pngSha256": "61f0b0388f909fe0fdd7c6810b0e1873dce203e6bc4163639e5b937a9f484c54", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "reload_resume", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--tablet-landscape--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--tablet-landscape--light.png new file mode 100644 index 0000000..c65d53a Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--tablet-landscape--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--tablet-portrait--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--tablet-portrait--dark.json new file mode 100644 index 0000000..2a78aa0 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--tablet-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:08.371Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 105170, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=reload_resume", + "pngSha256": "7a0d536972e47c843c5ae4bd90d7aa787ddb56f93dfa21aa4317035cf45038da", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "reload_resume", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--tablet-portrait--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--tablet-portrait--dark.png new file mode 100644 index 0000000..c40ac11 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--tablet-portrait--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--tablet-portrait--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--tablet-portrait--light.json new file mode 100644 index 0000000..fb3d32d --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--tablet-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:57.570Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 94368, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=reload_resume", + "pngSha256": "2dc3c316f11eb5528c95a0305882d586ae3dcb2ce9356b76f473666f59b72d8c", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "reload_resume", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--tablet-portrait--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--tablet-portrait--light.png new file mode 100644 index 0000000..d029ee6 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--tablet-portrait--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--wide--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--wide--dark.json new file mode 100644 index 0000000..55a51d3 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--wide--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:25.430Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 62228, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=reload_resume", + "pngSha256": "504106686b5b579be864bcb430ee05ca6f7795f3ad9d1ebc2cab7240cd925d6c", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "reload_resume", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--wide--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--wide--dark.png new file mode 100644 index 0000000..cbe53e4 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--wide--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--wide--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--wide--light.json new file mode 100644 index 0000000..c5b0f80 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--wide--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:14.223Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 51021, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=reload_resume", + "pngSha256": "3d580d57f70eafa16be5129d690b4872b3776907f5cd56137b7a23757fed87be", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "reload_resume", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--wide--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--wide--light.png new file mode 100644 index 0000000..2e4215e Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/reload_resume--wide--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--desktop--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--desktop--dark.json new file mode 100644 index 0000000..0cf32b3 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--desktop--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:49:58.954Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 35751, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=running", + "pngSha256": "a162f9e1f34c4fcbe9f8c7da8a2e4f622e7a83da3a361c8c8bee210c04e162d7", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "running", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--desktop--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--desktop--dark.png new file mode 100644 index 0000000..0c966f8 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--desktop--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--desktop--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--desktop--light.json new file mode 100644 index 0000000..fba6d81 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--desktop--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:49:34.463Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 11261, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=running", + "pngSha256": "161d47aea949363c2b0e6b5423ef9735b9dbb71d15d0f3ace1a169a03f583742", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "running", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--desktop--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--desktop--light.png new file mode 100644 index 0000000..29996e0 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--desktop--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--mobile-landscape--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--mobile-landscape--dark.json new file mode 100644 index 0000000..8144345 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--mobile-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:46.519Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 143317, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=running", + "pngSha256": "c690d0a7449aaeb3b77bdb18bd6196cb85415cc407e8f97df75b4b1c4061546e", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "running", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--mobile-landscape--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--mobile-landscape--dark.png new file mode 100644 index 0000000..0bef604 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--mobile-landscape--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--mobile-landscape--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--mobile-landscape--light.json new file mode 100644 index 0000000..b2736eb --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--mobile-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:36.332Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 133130, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=running", + "pngSha256": "0fa8a65172b0c282fd177fb8ebd137a0ea24e92e86f2b064b9129d8494ce70e3", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "running", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--mobile-landscape--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--mobile-landscape--light.png new file mode 100644 index 0000000..20293e3 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--mobile-landscape--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--mobile-portrait--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--mobile-portrait--dark.json new file mode 100644 index 0000000..3927752 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--mobile-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:26.134Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 122932, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=running", + "pngSha256": "5f43f578b6f216e456e6d5dc377757da80dc652355a5f984d6e97dfaf4ca33b8", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "running", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--mobile-portrait--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--mobile-portrait--dark.png new file mode 100644 index 0000000..23080c0 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--mobile-portrait--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--mobile-portrait--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--mobile-portrait--light.json new file mode 100644 index 0000000..0ac511d --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--mobile-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:15.353Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 112151, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=running", + "pngSha256": "598de7c35afd84412f52de7c741bbb49b3828c41765866a18b171380877ee1dd", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "running", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--mobile-portrait--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--mobile-portrait--light.png new file mode 100644 index 0000000..28c4410 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--mobile-portrait--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--tablet-landscape--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--tablet-landscape--dark.json new file mode 100644 index 0000000..65376e7 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--tablet-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:42.651Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 79449, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=running", + "pngSha256": "1045979b0c0d6de1d7c6a3e44f60514c575a72d209a8f18ce3139f7f1af97ae1", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "running", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--tablet-landscape--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--tablet-landscape--dark.png new file mode 100644 index 0000000..b2eb806 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--tablet-landscape--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--tablet-landscape--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--tablet-landscape--light.json new file mode 100644 index 0000000..74a8c5d --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--tablet-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:31.806Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 68605, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=running", + "pngSha256": "58d39d76c56ace010ab5ec90b2bc80a47dc84802ef22069d8151e9a3d89ea69a", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "running", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--tablet-landscape--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--tablet-landscape--light.png new file mode 100644 index 0000000..c2a41bb Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--tablet-landscape--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--tablet-portrait--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--tablet-portrait--dark.json new file mode 100644 index 0000000..eb5511f --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--tablet-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:03.940Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 100738, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=running", + "pngSha256": "1a7c5b04f35b215cd6ace5a18ba73a459c21ee508573450223866ccfd7c09369", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "running", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--tablet-portrait--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--tablet-portrait--dark.png new file mode 100644 index 0000000..c008fd9 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--tablet-portrait--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--tablet-portrait--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--tablet-portrait--light.json new file mode 100644 index 0000000..49cc595 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--tablet-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:53.250Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 90048, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=running", + "pngSha256": "fc788223f3ebb0f6706f3be14cafbbbb78888cb468c13c4fe6464a3165973740", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "running", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--tablet-portrait--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--tablet-portrait--light.png new file mode 100644 index 0000000..669696c Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--tablet-portrait--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--wide--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--wide--dark.json new file mode 100644 index 0000000..f784e90 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--wide--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:20.780Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 57578, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=running", + "pngSha256": "35be095bdc770b3cb67281d9a60fbc13c6e9c1496c835e7cb714fd2c18008790", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "running", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--wide--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--wide--dark.png new file mode 100644 index 0000000..8f940ce Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--wide--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--wide--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--wide--light.json new file mode 100644 index 0000000..328cee3 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--wide--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:09.751Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 46549, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=running", + "pngSha256": "67e455edf5c5705c1f80a9d14cc2392727cbbb0d0b5523cb66945e2a86b94eb4", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "running", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--wide--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--wide--light.png new file mode 100644 index 0000000..ada270f Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/running--wide--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--desktop--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--desktop--dark.json new file mode 100644 index 0000000..2727e8b --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--desktop--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:49:58.347Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 35145, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=validation_error", + "pngSha256": "302bcfd157f8221d500fae3008341768e19c622c2328d33dbd181bee2ca2e10a", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "validation_error", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--desktop--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--desktop--dark.png new file mode 100644 index 0000000..9c63807 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--desktop--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--desktop--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--desktop--light.json new file mode 100644 index 0000000..48be610 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--desktop--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:49:32.829Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 9627, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=validation_error", + "pngSha256": "0a325a05fb5acbe6f4fcf919ff6253f44f23a9e413cce5bd95e434cd9b7ffda6", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "validation_error", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--desktop--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--desktop--light.png new file mode 100644 index 0000000..88fe4e0 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--desktop--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--mobile-landscape--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--mobile-landscape--dark.json new file mode 100644 index 0000000..08f6a91 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--mobile-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:45.943Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 142741, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=validation_error", + "pngSha256": "39d1845ca1591d5e440def8afdfb4ca810f9bd20884340b63599916fc1c315ea", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "validation_error", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--mobile-landscape--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--mobile-landscape--dark.png new file mode 100644 index 0000000..0a04631 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--mobile-landscape--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--mobile-landscape--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--mobile-landscape--light.json new file mode 100644 index 0000000..cd13f24 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--mobile-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:35.740Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 132538, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=validation_error", + "pngSha256": "3b301bda0b4014f245213e2faab91a57aa5e62c4c3afa82817e8f38ced90d49e", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "validation_error", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--mobile-landscape--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--mobile-landscape--light.png new file mode 100644 index 0000000..aeef7ea Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--mobile-landscape--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--mobile-portrait--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--mobile-portrait--dark.json new file mode 100644 index 0000000..7b1500a --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--mobile-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:25.560Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 122358, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=validation_error", + "pngSha256": "e24ac361b533ce1a79d4b4d272da8510b26a0a3333afcad4a6038082a9559e55", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "validation_error", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--mobile-portrait--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--mobile-portrait--dark.png new file mode 100644 index 0000000..e2e261b Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--mobile-portrait--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--mobile-portrait--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--mobile-portrait--light.json new file mode 100644 index 0000000..20cd9cc --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--mobile-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:14.736Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 111534, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=validation_error", + "pngSha256": "0285d1a0cfac6293412bc86210ee563de7ea6e53527477a5e0aa730d1325439b", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "validation_error", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--mobile-portrait--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--mobile-portrait--light.png new file mode 100644 index 0000000..d32f694 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--mobile-portrait--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--tablet-landscape--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--tablet-landscape--dark.json new file mode 100644 index 0000000..5bac588 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--tablet-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:42.040Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 78838, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=validation_error", + "pngSha256": "6cb7e6cf614e47f8c2d65d3f195be8912aa6aca5fd30fcb026c7d521f9da6662", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "validation_error", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--tablet-landscape--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--tablet-landscape--dark.png new file mode 100644 index 0000000..3b687f3 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--tablet-landscape--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--tablet-landscape--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--tablet-landscape--light.json new file mode 100644 index 0000000..309af03 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--tablet-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:31.183Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 67981, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=validation_error", + "pngSha256": "54e9ee395d17540d7a042cc1cb123de0c99b0c53c953538ad7da59d03ee93918", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "validation_error", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--tablet-landscape--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--tablet-landscape--light.png new file mode 100644 index 0000000..ad5c2cb Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--tablet-landscape--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--tablet-portrait--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--tablet-portrait--dark.json new file mode 100644 index 0000000..5f4aeb9 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--tablet-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:51:03.300Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 100098, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=validation_error", + "pngSha256": "4980a38de8e69db29258c63d8e8fa4c67a9a17fe02fbe6396ae1b99ac20e48cf", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "validation_error", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--tablet-portrait--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--tablet-portrait--dark.png new file mode 100644 index 0000000..2fcd98b Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--tablet-portrait--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--tablet-portrait--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--tablet-portrait--light.json new file mode 100644 index 0000000..13da9d8 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--tablet-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:52.619Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 89417, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=validation_error", + "pngSha256": "6b6393d8be685c59bd60ad8c1a49db04aad663192074a1f9041d76cb0a15a25e", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "validation_error", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--tablet-portrait--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--tablet-portrait--light.png new file mode 100644 index 0000000..36ba3d0 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--tablet-portrait--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--wide--dark.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--wide--dark.json new file mode 100644 index 0000000..3e51d1a --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--wide--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:20.124Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 56922, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=validation_error", + "pngSha256": "eabdf2a71e6a86b7509c168e67b9c7f91ef3d40f969b5271a0bc24b7d05cd2df", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "validation_error", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--wide--dark.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--wide--dark.png new file mode 100644 index 0000000..fd10b87 Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--wide--dark.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--wide--light.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--wide--light.json new file mode 100644 index 0000000..39af5e5 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--wide--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "capturedAt": "2026-07-22T08:50:09.090Z", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "consoleErrors": 0, + "elapsedMs": 45888, + "failedRequests": 0, + "generatedCandidateCommit": "d6e1fd54fedeac143f1c2b16dafc262d11685906", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "pageUrl": "http://127.0.0.1:43172/?scenario=validation_error", + "pngSha256": "d79d5ffcaed7357b9f48fca59d41720e45c04df12984a5108a02b5c1689bf859", + "runId": "browser_5cc6157_20260722", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 24808 + }, + "state": "validation_error", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--wide--light.png b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--wide--light.png new file mode 100644 index 0000000..3eb3c4f Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/browser/screenshots/validation_error--wide--light.png differ diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/package/application-identity.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/package/application-identity.json new file mode 100644 index 0000000..9e655c2 --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/package/application-identity.json @@ -0,0 +1,243 @@ +{ + "applicationHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "configHash": "df5321e8adbece38f1a0c66b684f3b3ad55f8e92b98889fa3941dab0791bc933", + "identity": { + "files": [ + { + "bytes": 36, + "digest": "9d6fc5919cd132fde6e40ef9bba6875218ae78d6f8029a01fd35d7d0820c88b4", + "path": ".dockerignore" + }, + { + "bytes": 19, + "digest": "d60f352d0db1404c70afb4bb8b2ca3fd1c610572aa40720e8a0b7baa7885418c", + "path": ".gitattributes" + }, + { + "bytes": 89, + "digest": "55ec5d517810c1e9962d9640dcbaaca1a81444d805774ab80510d030fd8a84cd", + "path": ".gitignore" + }, + { + "bytes": 260, + "digest": "7dfb37bb520b6fb180c50f7af450cd34347228bbb4acba1c8d379f06721ee850", + "path": "adw/workflows/launch.yaml" + }, + { + "bytes": 336, + "digest": "0ef4755e9a5c3f01c30e5e02eb3d474e4146c0f04d25a012ffd4f7f6a0525f47", + "path": "agent/instructions.md" + }, + { + "bytes": 2140, + "digest": "7365f940c07c7ca3bd69643a6c1dcc9852a261e17ec537e1396e818d42dab1dd", + "path": "agent/workflow.mjs" + }, + { + "bytes": 5643, + "digest": "f52276b8fe2572f02efa954b4598d84e163aae5fa9340db71500efc2ba4e3bfb", + "path": "apps/web/public/app.js" + }, + { + "bytes": 4220, + "digest": "fc02b7ca29933f412d7bf132592c3daa5c28109995aefaeba15e91c5470faa28", + "path": "apps/web/public/index.html" + }, + { + "bytes": 10621, + "digest": "025fe2934dea3117036045c27ff035a39d8798ae0037b0734cdf610390118320", + "path": "apps/web/public/styles.css" + }, + { + "bytes": 8615, + "digest": "ed3449ac48ed5549ac65d2970ab9fd4726b9e90a313649c6254ef027e0408e23", + "path": "apps/web/server.mjs" + }, + { + "bytes": 191, + "digest": "016bf6921aaea9bbfe804eae0400c0ee10aaee19d094b03d9cd83fc22ed17b8d", + "path": "Dockerfile" + }, + { + "bytes": 161, + "digest": "511a24b4039f43755ec97e9fb86fb5c92bb963445fddc53859e7e08c5987adb7", + "path": "evals/figured-out-journey.json" + }, + { + "bytes": 160, + "digest": "dc72ffb91ce882c50c89943532f4688e98b35e894519c5fdc189b953f41fafce", + "path": "evals/stale-proposal-conflict.json" + }, + { + "bytes": 91, + "digest": "da0d5619735d0818f89cfedeb4a3cacd8413ab0a5d8ce0695a564ce21a73fbee", + "path": "fixtures/corpus/validation.txt" + }, + { + "bytes": 597, + "digest": "43219fddcfcf0fdb16942ae29d5b858a2dfcfe3afcbde84c21a47751f9885e4a", + "path": "hackathon.yaml" + }, + { + "bytes": 597, + "digest": "02f5e3f50a855d30251dc13064610a253c7f67114d760a0ae679085ff6f10648", + "path": "integrations/convex/capability-plan.yaml" + }, + { + "bytes": 714, + "digest": "bca07e75ef691b96f1998456534497ed910e350c42d26bd8d6482650519963cb", + "path": "integrations/convex/README.md" + }, + { + "bytes": 868, + "digest": "5a015365f21bfa82556bfb95ccc2e20f759fa9c8dea26aaa076cf6efe6d11139", + "path": "nodekit.yaml" + }, + { + "bytes": 5836, + "digest": "bcd6438ed0a4174093b83175c3c12ac9836fdd1b2e1aecf1b2ef7f7b7b4c6718", + "path": "package-lock.json" + }, + { + "bytes": 896, + "digest": "4164d3350b339a7219c193066bf6a3a6398abe4378d128f2b77a6b773104756f", + "path": "package.json" + }, + { + "bytes": 342, + "digest": "1f25c7d2707019691ae036901bcbd458419cfd42d1b5765dae46e797cb3b4ab5", + "path": "packs/primary/pack.yaml" + }, + { + "bytes": 123, + "digest": "f62c2c3838c464f1146874919b4c8e4dec72ac0c6f2d238821910acde430776b", + "path": "render.yaml" + }, + { + "bytes": 328, + "digest": "23eb74d85b55e102aa4ec4213b56666c8993ed5c84d9e2c36572986cd5e3bf5c", + "path": "scripts/audit-prod.mjs" + }, + { + "bytes": 15184, + "digest": "83632d43ca75a7afc3d071cd9d1e05b8c779324a6cb17d7b935bfe9fdeaed239", + "path": "scripts/browser-certify.mjs" + }, + { + "bytes": 2279, + "digest": "6f0335536b2a3b30c6a538b340016cdabc96996a34d9303e025001450d730a75", + "path": "scripts/browser-proof.mjs" + }, + { + "bytes": 777, + "digest": "1040c9466364f5e348b68177759dc6b9456c0153b44c2b6aa610a8351c37496f", + "path": "scripts/check.mjs" + }, + { + "bytes": 1386, + "digest": "95813e7c557e069d87d5b52088b209f838e4dab7c30ebfac0e46900745cc0f28", + "path": "scripts/demo.mjs" + }, + { + "bytes": 1947, + "digest": "d0309beccd122a1b2bf8a63a91eca61c58f6638d6438af81d56c436f338633a4", + "path": "scripts/eval.mjs" + }, + { + "bytes": 681, + "digest": "02d3ead38d129440339675dc1906f8edfdeabc16512cf68b9b8a197169c8b05a", + "path": "scripts/lib/friction.mjs" + }, + { + "bytes": 2500, + "digest": "c32a024944ab9a523ad41073faf41baf83c0f81b8fe00840cd0cf4174087bf89", + "path": "scripts/proof.mjs" + }, + { + "bytes": 692, + "digest": "6ef82b96ea99e761c70ca627ef778ebabb999f8fdc9c6ce8e66ce53d3424037b", + "path": "test/workflow.test.mjs" + } + ], + "roots": { + "directories": [ + "api", + "agent", + "packs", + "integrations", + "backend", + "workers", + "evals", + "fixtures", + "schemas", + "src", + "apps", + "scripts", + "adw", + "test", + "tests", + "e2e" + ], + "identityOnlyDirectories": [ + "vendor" + ], + "resolvedDirectories": [ + "adw", + "agent", + "api", + "apps", + "backend", + "e2e", + "evals", + "fixtures", + "integrations", + "packs", + "packs/primary", + "schemas", + "scripts", + "src", + "test", + "tests", + "vendor", + "workers" + ], + "files": [ + ".dockerignore", + ".gitattributes", + ".gitignore", + ".npmrc", + ".nvmrc", + ".vercelignore", + "nodekit.yaml", + "hackathon.yaml", + "package.json", + "package-lock.json", + "pnpm-lock.yaml", + "yarn.lock", + "bun.lockb", + "Dockerfile", + "server.ts", + "server.js", + "server.mjs", + "docker-compose.yml", + "docker-compose.yaml", + "render.yaml", + "railway.json", + "vercel.json", + "netlify.toml", + "fly.toml", + "Procfile", + "convex.json", + "vite.config.js", + "vite.config.ts", + "next.config.js", + "next.config.mjs", + "playwright.config.js", + "playwright.config.ts", + "tsconfig.json" + ] + } + }, + "manifestDigest": "646dd36854d72763e636511c7547d15cbadcf0abcf1ce1a1741cf26323db7080", + "schemaVersion": "nodeagent.application-identity/v1" +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/package/demo-receipt.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/package/demo-receipt.json new file mode 100644 index 0000000..1003bfd --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/package/demo-receipt.json @@ -0,0 +1,431 @@ +{ + "assertions": { + "canonicalVersionAdvanced": true, + "explicitNextAction": true, + "proposalAccepted": true, + "receiptCreated": true + }, + "receipt": { + "artifactIds": [ + "artifact_752bad94e55441aebcdb62370ac5b9c2" + ], + "caseId": "case_671dee2544f442b4afb1dfb88b2e48f6", + "eventIds": [ + "event_4682a8c6370d4afcbb48ca65a4b8feb6", + "event_6ecb93f7235d412685c67ff608ac3ada", + "event_3d41e2811f2b441599fc55d52666675c", + "event_df7ad9f847e44f4b9baf27b2f9cf0b72", + "event_f7a3f6119f4d49cc9decc47494e9f82c", + "event_69c2d021f9a940868dc54016e5c73b12", + "event_13dfd3428be9431baf7f6fdb0e70a9c6", + "event_f904992b491b4f439e9c828792e5e7fd", + "event_dd37dfe3fc6245199a156d2e4f526b07", + "event_bd70efefe7ca4981bc5b9f4f332418d3" + ], + "generatedAt": "2026-07-22T08:43:47.421Z", + "proposalIds": [ + "proposal_5f5f69d8d939420fbc481e0a126a8849" + ], + "runId": "run_d72d57d133ce418689caa525194bc92d", + "schemaVersion": "nodekit.receipt/v1", + "status": "completed", + "receiptId": "receipt_53f48d8f32da4337815d9030cf4e40ff", + "receiptHash": "b0f6b06d27f09781457fd9faded4e2d8b458eaa9028f868e52f5497a9570a1e9" + }, + "schemaVersion": "nodekit.figured-out-demo/v1", + "snapshot": { + "approvals": [ + { + "approvalId": "approval_fbaf901c67b84ed59692cbafb795aa9e", + "comment": "", + "decidedAt": "2026-07-22T08:43:47.421Z", + "decision": "accepted", + "proposalId": "proposal_5f5f69d8d939420fbc481e0a126a8849", + "schemaVersion": "nodekit.approval/v1" + } + ], + "artifacts": [ + { + "artifactId": "artifact_752bad94e55441aebcdb62370ac5b9c2", + "caseId": "case_671dee2544f442b4afb1dfb88b2e48f6", + "canonicalVersion": 2, + "createdAt": "2026-07-22T08:43:47.419Z", + "kind": "structured-document", + "runId": "run_d72d57d133ce418689caa525194bc92d", + "schemaVersion": "nodekit.artifact/v1", + "title": "Primary artifact", + "updatedAt": "2026-07-22T08:43:47.421Z", + "versions": [ + { + "content": { + "summary": "The last approved result remains here.", + "status": "baseline" + }, + "contentHash": "1077e34cc8ffae57d4f0bcd7a40d9e5f152b05da45ab643d36195773c10a7de8", + "createdAt": "2026-07-22T08:43:47.419Z", + "version": 1 + }, + { + "content": { + "summary": "A bounded, reviewable result is ready. Replace this with the researched domain artifact.", + "status": "proposed" + }, + "contentHash": "5cbf8cdb4ab5ba5d3ad2b6c2b938190dbd2e470769604929a5f8d5924808c2f8", + "createdAt": "2026-07-22T08:43:47.421Z", + "proposalId": "proposal_5f5f69d8d939420fbc481e0a126a8849", + "version": 2 + } + ] + } + ], + "cases": [ + { + "caseId": "case_671dee2544f442b4afb1dfb88b2e48f6", + "createdAt": "2026-07-22T08:43:47.417Z", + "currentRunId": "run_d72d57d133ce418689caa525194bc92d", + "primaryJob": "Carry one bounded intention to a reviewed artifact and receipt.", + "schemaVersion": "nodekit.case/v1", + "status": "completed", + "title": "Package Proof case", + "updatedAt": "2026-07-22T08:43:47.421Z" + } + ], + "events": [ + { + "actor": { + "type": "system", + "id": "nodekit" + }, + "aggregateId": "case_671dee2544f442b4afb1dfb88b2e48f6", + "aggregateType": "case", + "eventId": "event_d0dba8f6aa2849fca54286d5f88bb6d5", + "eventType": "case.created", + "occurredAt": "2026-07-22T08:43:47.419Z", + "payload": { + "caseId": "case_671dee2544f442b4afb1dfb88b2e48f6", + "createdAt": "2026-07-22T08:43:47.417Z", + "currentRunId": null, + "primaryJob": "Carry one bounded intention to a reviewed artifact and receipt.", + "schemaVersion": "nodekit.case/v1", + "status": "ready", + "title": "Package Proof case", + "updatedAt": "2026-07-22T08:43:47.417Z" + }, + "schemaVersion": "nodekit.caseflow-event/v1", + "sequence": 1 + }, + { + "actor": { + "type": "system", + "id": "nodekit" + }, + "aggregateId": "run_d72d57d133ce418689caa525194bc92d", + "aggregateType": "run", + "eventId": "event_4682a8c6370d4afcbb48ca65a4b8feb6", + "eventType": "run.started", + "occurredAt": "2026-07-22T08:43:47.419Z", + "payload": { + "caseId": "case_671dee2544f442b4afb1dfb88b2e48f6", + "createdAt": "2026-07-22T08:43:47.419Z", + "currentStageId": "intake", + "nextAction": "Confirm the intended outcome", + "nextActionOwner": "user", + "runId": "run_d72d57d133ce418689caa525194bc92d", + "schemaVersion": "nodekit.run/v1", + "stages": [ + { + "id": "intake", + "label": "Confirm the intended outcome", + "owner": "user", + "status": "active" + }, + { + "id": "working", + "label": "Prepare a proposal", + "owner": "agent", + "status": "pending" + }, + { + "id": "review", + "label": "Review the proposed change", + "owner": "user", + "status": "pending" + }, + { + "id": "complete", + "label": "Verify and export", + "owner": "system", + "status": "pending" + } + ], + "status": "active", + "updatedAt": "2026-07-22T08:43:47.419Z" + }, + "schemaVersion": "nodekit.caseflow-event/v1", + "sequence": 1 + }, + { + "actor": { + "type": "system", + "id": "nodekit" + }, + "aggregateId": "run_d72d57d133ce418689caa525194bc92d", + "aggregateType": "run", + "eventId": "event_6ecb93f7235d412685c67ff608ac3ada", + "eventType": "stage.entered", + "occurredAt": "2026-07-22T08:43:47.419Z", + "payload": { + "stageId": "intake" + }, + "schemaVersion": "nodekit.caseflow-event/v1", + "sequence": 2 + }, + { + "actor": { + "type": "system", + "id": "nodekit" + }, + "aggregateId": "artifact_752bad94e55441aebcdb62370ac5b9c2", + "aggregateType": "artifact", + "eventId": "event_3d41e2811f2b441599fc55d52666675c", + "eventType": "artifact.created", + "occurredAt": "2026-07-22T08:43:47.420Z", + "payload": { + "artifactId": "artifact_752bad94e55441aebcdb62370ac5b9c2", + "version": 1 + }, + "schemaVersion": "nodekit.caseflow-event/v1", + "sequence": 1 + }, + { + "actor": { + "type": "system", + "id": "nodekit" + }, + "aggregateId": "run_d72d57d133ce418689caa525194bc92d", + "aggregateType": "run", + "eventId": "event_df7ad9f847e44f4b9baf27b2f9cf0b72", + "eventType": "stage.entered", + "occurredAt": "2026-07-22T08:43:47.420Z", + "payload": { + "nextAction": "Prepare a proposal", + "nextActionOwner": "agent", + "stageId": "working" + }, + "schemaVersion": "nodekit.caseflow-event/v1", + "sequence": 3 + }, + { + "actor": { + "type": "system", + "id": "nodekit" + }, + "aggregateId": "proposal_5f5f69d8d939420fbc481e0a126a8849", + "aggregateType": "proposal", + "eventId": "event_f7a3f6119f4d49cc9decc47494e9f82c", + "eventType": "proposal.created", + "occurredAt": "2026-07-22T08:43:47.420Z", + "payload": { + "artifactId": "artifact_752bad94e55441aebcdb62370ac5b9c2", + "baseVersion": 1, + "createdAt": "2026-07-22T08:43:47.420Z", + "patch": { + "summary": "A bounded, reviewable result is ready. Replace this with the researched domain artifact.", + "status": "proposed" + }, + "proposalId": "proposal_5f5f69d8d939420fbc481e0a126a8849", + "rationale": "Demonstrate proposal-before-mutation without assuming a domain.", + "schemaVersion": "nodekit.proposal/v1", + "status": "pending" + }, + "schemaVersion": "nodekit.caseflow-event/v1", + "sequence": 1 + }, + { + "actor": { + "type": "system", + "id": "nodekit" + }, + "aggregateId": "run_d72d57d133ce418689caa525194bc92d", + "aggregateType": "run", + "eventId": "event_69c2d021f9a940868dc54016e5c73b12", + "eventType": "stage.entered", + "occurredAt": "2026-07-22T08:43:47.420Z", + "payload": { + "nextAction": "Approve or reject the proposed change", + "nextActionOwner": "user", + "stageId": "review" + }, + "schemaVersion": "nodekit.caseflow-event/v1", + "sequence": 4 + }, + { + "actor": { + "type": "system", + "id": "nodekit" + }, + "aggregateId": "artifact_752bad94e55441aebcdb62370ac5b9c2", + "aggregateType": "artifact", + "eventId": "event_13dfd3428be9431baf7f6fdb0e70a9c6", + "eventType": "artifact.version_created", + "occurredAt": "2026-07-22T08:43:47.421Z", + "payload": { + "proposalId": "proposal_5f5f69d8d939420fbc481e0a126a8849", + "version": 2 + }, + "schemaVersion": "nodekit.caseflow-event/v1", + "sequence": 2 + }, + { + "actor": { + "type": "system", + "id": "nodekit" + }, + "aggregateId": "proposal_5f5f69d8d939420fbc481e0a126a8849", + "aggregateType": "proposal", + "eventId": "event_f904992b491b4f439e9c828792e5e7fd", + "eventType": "proposal.accepted", + "occurredAt": "2026-07-22T08:43:47.421Z", + "payload": { + "approvalId": "approval_fbaf901c67b84ed59692cbafb795aa9e" + }, + "schemaVersion": "nodekit.caseflow-event/v1", + "sequence": 2 + }, + { + "actor": { + "type": "system", + "id": "nodekit" + }, + "aggregateId": "run_d72d57d133ce418689caa525194bc92d", + "aggregateType": "run", + "eventId": "event_dd37dfe3fc6245199a156d2e4f526b07", + "eventType": "stage.entered", + "occurredAt": "2026-07-22T08:43:47.421Z", + "payload": { + "nextAction": "Verify and export", + "nextActionOwner": "system", + "stageId": "complete" + }, + "schemaVersion": "nodekit.caseflow-event/v1", + "sequence": 5 + }, + { + "actor": { + "type": "system", + "id": "nodekit" + }, + "aggregateId": "run_d72d57d133ce418689caa525194bc92d", + "aggregateType": "run", + "eventId": "event_bd70efefe7ca4981bc5b9f4f332418d3", + "eventType": "run.completed", + "occurredAt": "2026-07-22T08:43:47.421Z", + "payload": {}, + "schemaVersion": "nodekit.caseflow-event/v1", + "sequence": 6 + }, + { + "actor": { + "type": "system", + "id": "nodekit" + }, + "aggregateId": "run_d72d57d133ce418689caa525194bc92d", + "aggregateType": "run", + "eventId": "event_9f127bcfc7b44d32b6b71622f6659244", + "eventType": "receipt.created", + "occurredAt": "2026-07-22T08:43:47.422Z", + "payload": { + "receiptHash": "b0f6b06d27f09781457fd9faded4e2d8b458eaa9028f868e52f5497a9570a1e9", + "receiptId": "receipt_53f48d8f32da4337815d9030cf4e40ff" + }, + "schemaVersion": "nodekit.caseflow-event/v1", + "sequence": 7 + } + ], + "exceptions": [], + "proposals": [ + { + "artifactId": "artifact_752bad94e55441aebcdb62370ac5b9c2", + "baseVersion": 1, + "createdAt": "2026-07-22T08:43:47.420Z", + "patch": { + "summary": "A bounded, reviewable result is ready. Replace this with the researched domain artifact.", + "status": "proposed" + }, + "proposalId": "proposal_5f5f69d8d939420fbc481e0a126a8849", + "rationale": "Demonstrate proposal-before-mutation without assuming a domain.", + "schemaVersion": "nodekit.proposal/v1", + "status": "accepted" + } + ], + "receipts": [ + { + "artifactIds": [ + "artifact_752bad94e55441aebcdb62370ac5b9c2" + ], + "caseId": "case_671dee2544f442b4afb1dfb88b2e48f6", + "eventIds": [ + "event_4682a8c6370d4afcbb48ca65a4b8feb6", + "event_6ecb93f7235d412685c67ff608ac3ada", + "event_3d41e2811f2b441599fc55d52666675c", + "event_df7ad9f847e44f4b9baf27b2f9cf0b72", + "event_f7a3f6119f4d49cc9decc47494e9f82c", + "event_69c2d021f9a940868dc54016e5c73b12", + "event_13dfd3428be9431baf7f6fdb0e70a9c6", + "event_f904992b491b4f439e9c828792e5e7fd", + "event_dd37dfe3fc6245199a156d2e4f526b07", + "event_bd70efefe7ca4981bc5b9f4f332418d3" + ], + "generatedAt": "2026-07-22T08:43:47.421Z", + "proposalIds": [ + "proposal_5f5f69d8d939420fbc481e0a126a8849" + ], + "runId": "run_d72d57d133ce418689caa525194bc92d", + "schemaVersion": "nodekit.receipt/v1", + "status": "completed", + "receiptId": "receipt_53f48d8f32da4337815d9030cf4e40ff", + "receiptHash": "b0f6b06d27f09781457fd9faded4e2d8b458eaa9028f868e52f5497a9570a1e9" + } + ], + "runs": [ + { + "caseId": "case_671dee2544f442b4afb1dfb88b2e48f6", + "createdAt": "2026-07-22T08:43:47.419Z", + "currentStageId": "complete", + "nextAction": "Review receipt", + "nextActionOwner": "user", + "runId": "run_d72d57d133ce418689caa525194bc92d", + "schemaVersion": "nodekit.run/v1", + "stages": [ + { + "id": "intake", + "label": "Confirm the intended outcome", + "owner": "user", + "status": "completed" + }, + { + "id": "working", + "label": "Prepare a proposal", + "owner": "agent", + "status": "completed" + }, + { + "id": "review", + "label": "Review the proposed change", + "owner": "user", + "status": "completed" + }, + { + "id": "complete", + "label": "Verify and export", + "owner": "system", + "status": "completed" + } + ], + "status": "completed", + "updatedAt": "2026-07-22T08:43:47.421Z" + } + ] + }, + "passed": true +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/package/eval-receipt.json b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/package/eval-receipt.json new file mode 100644 index 0000000..d4e2b7c --- /dev/null +++ b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/package/eval-receipt.json @@ -0,0 +1,11 @@ +{ + "assertions": { + "canonicalStatePreserved": true, + "exceptionPreservedState": true, + "recoveryOwnerExplicit": true, + "staleProposalConflicted": true + }, + "generatedAt": "2026-07-22T08:43:48.053Z", + "passed": true, + "schemaVersion": "nodekit.eval-receipt/v1" +} diff --git a/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/package/homenshum-nodekit-0.2.1.tgz b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/package/homenshum-nodekit-0.2.1.tgz new file mode 100644 index 0000000..d5e69da Binary files /dev/null and b/proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/package/homenshum-nodekit-0.2.1.tgz differ diff --git a/proof/ease/candidates/f3471c7fd31b4839ffeb9c9f43bd0d4ab7ef6bfc/e4e6a8849115fc70203e1d56228a5bdc3cd44af92ff20f87626657ff4d017d0f/package/application-identity.json b/proof/ease/candidates/f3471c7fd31b4839ffeb9c9f43bd0d4ab7ef6bfc/e4e6a8849115fc70203e1d56228a5bdc3cd44af92ff20f87626657ff4d017d0f/package/application-identity.json new file mode 100644 index 0000000..c53d9de --- /dev/null +++ b/proof/ease/candidates/f3471c7fd31b4839ffeb9c9f43bd0d4ab7ef6bfc/e4e6a8849115fc70203e1d56228a5bdc3cd44af92ff20f87626657ff4d017d0f/package/application-identity.json @@ -0,0 +1,243 @@ +{ + "applicationHash": "54b73137eb0c1ae6f8904e3812eec1142e33f56ea06d3ecd752e43bd7f9a5daf", + "configHash": "54b73137eb0c1ae6f8904e3812eec1142e33f56ea06d3ecd752e43bd7f9a5daf", + "identity": { + "files": [ + { + "bytes": 36, + "digest": "9d6fc5919cd132fde6e40ef9bba6875218ae78d6f8029a01fd35d7d0820c88b4", + "path": ".dockerignore" + }, + { + "bytes": 19, + "digest": "d60f352d0db1404c70afb4bb8b2ca3fd1c610572aa40720e8a0b7baa7885418c", + "path": ".gitattributes" + }, + { + "bytes": 89, + "digest": "55ec5d517810c1e9962d9640dcbaaca1a81444d805774ab80510d030fd8a84cd", + "path": ".gitignore" + }, + { + "bytes": 260, + "digest": "7dfb37bb520b6fb180c50f7af450cd34347228bbb4acba1c8d379f06721ee850", + "path": "adw/workflows/launch.yaml" + }, + { + "bytes": 336, + "digest": "0ef4755e9a5c3f01c30e5e02eb3d474e4146c0f04d25a012ffd4f7f6a0525f47", + "path": "agent/instructions.md" + }, + { + "bytes": 2140, + "digest": "7365f940c07c7ca3bd69643a6c1dcc9852a261e17ec537e1396e818d42dab1dd", + "path": "agent/workflow.mjs" + }, + { + "bytes": 5643, + "digest": "f52276b8fe2572f02efa954b4598d84e163aae5fa9340db71500efc2ba4e3bfb", + "path": "apps/web/public/app.js" + }, + { + "bytes": 4220, + "digest": "fc02b7ca29933f412d7bf132592c3daa5c28109995aefaeba15e91c5470faa28", + "path": "apps/web/public/index.html" + }, + { + "bytes": 10621, + "digest": "025fe2934dea3117036045c27ff035a39d8798ae0037b0734cdf610390118320", + "path": "apps/web/public/styles.css" + }, + { + "bytes": 8615, + "digest": "ed3449ac48ed5549ac65d2970ab9fd4726b9e90a313649c6254ef027e0408e23", + "path": "apps/web/server.mjs" + }, + { + "bytes": 191, + "digest": "016bf6921aaea9bbfe804eae0400c0ee10aaee19d094b03d9cd83fc22ed17b8d", + "path": "Dockerfile" + }, + { + "bytes": 161, + "digest": "511a24b4039f43755ec97e9fb86fb5c92bb963445fddc53859e7e08c5987adb7", + "path": "evals/figured-out-journey.json" + }, + { + "bytes": 160, + "digest": "dc72ffb91ce882c50c89943532f4688e98b35e894519c5fdc189b953f41fafce", + "path": "evals/stale-proposal-conflict.json" + }, + { + "bytes": 91, + "digest": "da0d5619735d0818f89cfedeb4a3cacd8413ab0a5d8ce0695a564ce21a73fbee", + "path": "fixtures/corpus/validation.txt" + }, + { + "bytes": 597, + "digest": "43219fddcfcf0fdb16942ae29d5b858a2dfcfe3afcbde84c21a47751f9885e4a", + "path": "hackathon.yaml" + }, + { + "bytes": 597, + "digest": "02f5e3f50a855d30251dc13064610a253c7f67114d760a0ae679085ff6f10648", + "path": "integrations/convex/capability-plan.yaml" + }, + { + "bytes": 714, + "digest": "bca07e75ef691b96f1998456534497ed910e350c42d26bd8d6482650519963cb", + "path": "integrations/convex/README.md" + }, + { + "bytes": 868, + "digest": "5a015365f21bfa82556bfb95ccc2e20f759fa9c8dea26aaa076cf6efe6d11139", + "path": "nodekit.yaml" + }, + { + "bytes": 5836, + "digest": "e6f341aea25d0996184fa100a7f3479048325bf3a3da1d119ccbaa78d9240fb9", + "path": "package-lock.json" + }, + { + "bytes": 896, + "digest": "ac2e65fa7e05097bc85388930899aca895f73fd6f8c52a2036e46139daf6d8f6", + "path": "package.json" + }, + { + "bytes": 342, + "digest": "1f25c7d2707019691ae036901bcbd458419cfd42d1b5765dae46e797cb3b4ab5", + "path": "packs/primary/pack.yaml" + }, + { + "bytes": 123, + "digest": "f62c2c3838c464f1146874919b4c8e4dec72ac0c6f2d238821910acde430776b", + "path": "render.yaml" + }, + { + "bytes": 328, + "digest": "23eb74d85b55e102aa4ec4213b56666c8993ed5c84d9e2c36572986cd5e3bf5c", + "path": "scripts/audit-prod.mjs" + }, + { + "bytes": 15184, + "digest": "83632d43ca75a7afc3d071cd9d1e05b8c779324a6cb17d7b935bfe9fdeaed239", + "path": "scripts/browser-certify.mjs" + }, + { + "bytes": 2279, + "digest": "6f0335536b2a3b30c6a538b340016cdabc96996a34d9303e025001450d730a75", + "path": "scripts/browser-proof.mjs" + }, + { + "bytes": 777, + "digest": "1040c9466364f5e348b68177759dc6b9456c0153b44c2b6aa610a8351c37496f", + "path": "scripts/check.mjs" + }, + { + "bytes": 1386, + "digest": "95813e7c557e069d87d5b52088b209f838e4dab7c30ebfac0e46900745cc0f28", + "path": "scripts/demo.mjs" + }, + { + "bytes": 1947, + "digest": "d0309beccd122a1b2bf8a63a91eca61c58f6638d6438af81d56c436f338633a4", + "path": "scripts/eval.mjs" + }, + { + "bytes": 681, + "digest": "02d3ead38d129440339675dc1906f8edfdeabc16512cf68b9b8a197169c8b05a", + "path": "scripts/lib/friction.mjs" + }, + { + "bytes": 2500, + "digest": "c32a024944ab9a523ad41073faf41baf83c0f81b8fe00840cd0cf4174087bf89", + "path": "scripts/proof.mjs" + }, + { + "bytes": 692, + "digest": "6ef82b96ea99e761c70ca627ef778ebabb999f8fdc9c6ce8e66ce53d3424037b", + "path": "test/workflow.test.mjs" + } + ], + "roots": { + "directories": [ + "api", + "agent", + "packs", + "integrations", + "backend", + "workers", + "evals", + "fixtures", + "schemas", + "src", + "apps", + "scripts", + "adw", + "test", + "tests", + "e2e" + ], + "identityOnlyDirectories": [ + "vendor" + ], + "resolvedDirectories": [ + "adw", + "agent", + "api", + "apps", + "backend", + "e2e", + "evals", + "fixtures", + "integrations", + "packs", + "packs/primary", + "schemas", + "scripts", + "src", + "test", + "tests", + "vendor", + "workers" + ], + "files": [ + ".dockerignore", + ".gitattributes", + ".gitignore", + ".npmrc", + ".nvmrc", + ".vercelignore", + "nodekit.yaml", + "hackathon.yaml", + "package.json", + "package-lock.json", + "pnpm-lock.yaml", + "yarn.lock", + "bun.lockb", + "Dockerfile", + "server.ts", + "server.js", + "server.mjs", + "docker-compose.yml", + "docker-compose.yaml", + "render.yaml", + "railway.json", + "vercel.json", + "netlify.toml", + "fly.toml", + "Procfile", + "convex.json", + "vite.config.js", + "vite.config.ts", + "next.config.js", + "next.config.mjs", + "playwright.config.js", + "playwright.config.ts", + "tsconfig.json" + ] + } + }, + "manifestDigest": "646dd36854d72763e636511c7547d15cbadcf0abcf1ce1a1741cf26323db7080", + "schemaVersion": "nodeagent.application-identity/v1" +} diff --git a/proof/ease/candidates/f3471c7fd31b4839ffeb9c9f43bd0d4ab7ef6bfc/e4e6a8849115fc70203e1d56228a5bdc3cd44af92ff20f87626657ff4d017d0f/package/demo-receipt.json b/proof/ease/candidates/f3471c7fd31b4839ffeb9c9f43bd0d4ab7ef6bfc/e4e6a8849115fc70203e1d56228a5bdc3cd44af92ff20f87626657ff4d017d0f/package/demo-receipt.json new file mode 100644 index 0000000..e92c66d --- /dev/null +++ b/proof/ease/candidates/f3471c7fd31b4839ffeb9c9f43bd0d4ab7ef6bfc/e4e6a8849115fc70203e1d56228a5bdc3cd44af92ff20f87626657ff4d017d0f/package/demo-receipt.json @@ -0,0 +1,431 @@ +{ + "assertions": { + "canonicalVersionAdvanced": true, + "explicitNextAction": true, + "proposalAccepted": true, + "receiptCreated": true + }, + "receipt": { + "artifactIds": [ + "artifact_ad0f28432f074ff48939d61d35eb2cf4" + ], + "caseId": "case_57b7c1d61e9648e3be77ae6b520b5e12", + "eventIds": [ + "event_8bbbd064114b40eaa27951b812dc1828", + "event_abafba80e5cd4d878980711d45f8fccd", + "event_a13bea4832e542b48de4f4e7a3c26075", + "event_77e2856b5b5848c9a5e85b127c7fdc45", + "event_841fd077a2e44323a20d2b3307bec927", + "event_7cbaa4bccb4f4ddc8ce2667464a0719a", + "event_9e74cdec472e4905b0e3daa8bf66f789", + "event_574156ad5b364a12ba2ba7d1d33c0729", + "event_eb1706b45a134793b4dfef968f4759ce", + "event_65415805714c46f1b4a3497a46282a1d" + ], + "generatedAt": "2026-07-22T08:14:08.926Z", + "proposalIds": [ + "proposal_bd874e2380624f229d160605001f8b7f" + ], + "runId": "run_672b968cb5f34778bcd616b7fbab5939", + "schemaVersion": "nodekit.receipt/v1", + "status": "completed", + "receiptId": "receipt_f4800a8706474304be088759eee42695", + "receiptHash": "8211c34cceac6fde06877c19211132e603e0752bc3e279bda9c0dc3435406c39" + }, + "schemaVersion": "nodekit.figured-out-demo/v1", + "snapshot": { + "approvals": [ + { + "approvalId": "approval_ab75fb9ed9e64b4b86776cf4a93d94b6", + "comment": "", + "decidedAt": "2026-07-22T08:14:08.925Z", + "decision": "accepted", + "proposalId": "proposal_bd874e2380624f229d160605001f8b7f", + "schemaVersion": "nodekit.approval/v1" + } + ], + "artifacts": [ + { + "artifactId": "artifact_ad0f28432f074ff48939d61d35eb2cf4", + "caseId": "case_57b7c1d61e9648e3be77ae6b520b5e12", + "canonicalVersion": 2, + "createdAt": "2026-07-22T08:14:08.921Z", + "kind": "structured-document", + "runId": "run_672b968cb5f34778bcd616b7fbab5939", + "schemaVersion": "nodekit.artifact/v1", + "title": "Primary artifact", + "updatedAt": "2026-07-22T08:14:08.925Z", + "versions": [ + { + "content": { + "summary": "The last approved result remains here.", + "status": "baseline" + }, + "contentHash": "1077e34cc8ffae57d4f0bcd7a40d9e5f152b05da45ab643d36195773c10a7de8", + "createdAt": "2026-07-22T08:14:08.921Z", + "version": 1 + }, + { + "content": { + "summary": "A bounded, reviewable result is ready. Replace this with the researched domain artifact.", + "status": "proposed" + }, + "contentHash": "5cbf8cdb4ab5ba5d3ad2b6c2b938190dbd2e470769604929a5f8d5924808c2f8", + "createdAt": "2026-07-22T08:14:08.925Z", + "proposalId": "proposal_bd874e2380624f229d160605001f8b7f", + "version": 2 + } + ] + } + ], + "cases": [ + { + "caseId": "case_57b7c1d61e9648e3be77ae6b520b5e12", + "createdAt": "2026-07-22T08:14:08.917Z", + "currentRunId": "run_672b968cb5f34778bcd616b7fbab5939", + "primaryJob": "Carry one bounded intention to a reviewed artifact and receipt.", + "schemaVersion": "nodekit.case/v1", + "status": "completed", + "title": "Package Proof case", + "updatedAt": "2026-07-22T08:14:08.925Z" + } + ], + "events": [ + { + "actor": { + "type": "system", + "id": "nodekit" + }, + "aggregateId": "case_57b7c1d61e9648e3be77ae6b520b5e12", + "aggregateType": "case", + "eventId": "event_f20c5afc34bb49d3bfd842e9bc4c2a00", + "eventType": "case.created", + "occurredAt": "2026-07-22T08:14:08.920Z", + "payload": { + "caseId": "case_57b7c1d61e9648e3be77ae6b520b5e12", + "createdAt": "2026-07-22T08:14:08.917Z", + "currentRunId": null, + "primaryJob": "Carry one bounded intention to a reviewed artifact and receipt.", + "schemaVersion": "nodekit.case/v1", + "status": "ready", + "title": "Package Proof case", + "updatedAt": "2026-07-22T08:14:08.917Z" + }, + "schemaVersion": "nodekit.caseflow-event/v1", + "sequence": 1 + }, + { + "actor": { + "type": "system", + "id": "nodekit" + }, + "aggregateId": "run_672b968cb5f34778bcd616b7fbab5939", + "aggregateType": "run", + "eventId": "event_8bbbd064114b40eaa27951b812dc1828", + "eventType": "run.started", + "occurredAt": "2026-07-22T08:14:08.920Z", + "payload": { + "caseId": "case_57b7c1d61e9648e3be77ae6b520b5e12", + "createdAt": "2026-07-22T08:14:08.920Z", + "currentStageId": "intake", + "nextAction": "Confirm the intended outcome", + "nextActionOwner": "user", + "runId": "run_672b968cb5f34778bcd616b7fbab5939", + "schemaVersion": "nodekit.run/v1", + "stages": [ + { + "id": "intake", + "label": "Confirm the intended outcome", + "owner": "user", + "status": "active" + }, + { + "id": "working", + "label": "Prepare a proposal", + "owner": "agent", + "status": "pending" + }, + { + "id": "review", + "label": "Review the proposed change", + "owner": "user", + "status": "pending" + }, + { + "id": "complete", + "label": "Verify and export", + "owner": "system", + "status": "pending" + } + ], + "status": "active", + "updatedAt": "2026-07-22T08:14:08.920Z" + }, + "schemaVersion": "nodekit.caseflow-event/v1", + "sequence": 1 + }, + { + "actor": { + "type": "system", + "id": "nodekit" + }, + "aggregateId": "run_672b968cb5f34778bcd616b7fbab5939", + "aggregateType": "run", + "eventId": "event_abafba80e5cd4d878980711d45f8fccd", + "eventType": "stage.entered", + "occurredAt": "2026-07-22T08:14:08.921Z", + "payload": { + "stageId": "intake" + }, + "schemaVersion": "nodekit.caseflow-event/v1", + "sequence": 2 + }, + { + "actor": { + "type": "system", + "id": "nodekit" + }, + "aggregateId": "artifact_ad0f28432f074ff48939d61d35eb2cf4", + "aggregateType": "artifact", + "eventId": "event_a13bea4832e542b48de4f4e7a3c26075", + "eventType": "artifact.created", + "occurredAt": "2026-07-22T08:14:08.923Z", + "payload": { + "artifactId": "artifact_ad0f28432f074ff48939d61d35eb2cf4", + "version": 1 + }, + "schemaVersion": "nodekit.caseflow-event/v1", + "sequence": 1 + }, + { + "actor": { + "type": "system", + "id": "nodekit" + }, + "aggregateId": "run_672b968cb5f34778bcd616b7fbab5939", + "aggregateType": "run", + "eventId": "event_77e2856b5b5848c9a5e85b127c7fdc45", + "eventType": "stage.entered", + "occurredAt": "2026-07-22T08:14:08.923Z", + "payload": { + "nextAction": "Prepare a proposal", + "nextActionOwner": "agent", + "stageId": "working" + }, + "schemaVersion": "nodekit.caseflow-event/v1", + "sequence": 3 + }, + { + "actor": { + "type": "system", + "id": "nodekit" + }, + "aggregateId": "proposal_bd874e2380624f229d160605001f8b7f", + "aggregateType": "proposal", + "eventId": "event_841fd077a2e44323a20d2b3307bec927", + "eventType": "proposal.created", + "occurredAt": "2026-07-22T08:14:08.924Z", + "payload": { + "artifactId": "artifact_ad0f28432f074ff48939d61d35eb2cf4", + "baseVersion": 1, + "createdAt": "2026-07-22T08:14:08.924Z", + "patch": { + "summary": "A bounded, reviewable result is ready. Replace this with the researched domain artifact.", + "status": "proposed" + }, + "proposalId": "proposal_bd874e2380624f229d160605001f8b7f", + "rationale": "Demonstrate proposal-before-mutation without assuming a domain.", + "schemaVersion": "nodekit.proposal/v1", + "status": "pending" + }, + "schemaVersion": "nodekit.caseflow-event/v1", + "sequence": 1 + }, + { + "actor": { + "type": "system", + "id": "nodekit" + }, + "aggregateId": "run_672b968cb5f34778bcd616b7fbab5939", + "aggregateType": "run", + "eventId": "event_7cbaa4bccb4f4ddc8ce2667464a0719a", + "eventType": "stage.entered", + "occurredAt": "2026-07-22T08:14:08.924Z", + "payload": { + "nextAction": "Approve or reject the proposed change", + "nextActionOwner": "user", + "stageId": "review" + }, + "schemaVersion": "nodekit.caseflow-event/v1", + "sequence": 4 + }, + { + "actor": { + "type": "system", + "id": "nodekit" + }, + "aggregateId": "artifact_ad0f28432f074ff48939d61d35eb2cf4", + "aggregateType": "artifact", + "eventId": "event_9e74cdec472e4905b0e3daa8bf66f789", + "eventType": "artifact.version_created", + "occurredAt": "2026-07-22T08:14:08.925Z", + "payload": { + "proposalId": "proposal_bd874e2380624f229d160605001f8b7f", + "version": 2 + }, + "schemaVersion": "nodekit.caseflow-event/v1", + "sequence": 2 + }, + { + "actor": { + "type": "system", + "id": "nodekit" + }, + "aggregateId": "proposal_bd874e2380624f229d160605001f8b7f", + "aggregateType": "proposal", + "eventId": "event_574156ad5b364a12ba2ba7d1d33c0729", + "eventType": "proposal.accepted", + "occurredAt": "2026-07-22T08:14:08.925Z", + "payload": { + "approvalId": "approval_ab75fb9ed9e64b4b86776cf4a93d94b6" + }, + "schemaVersion": "nodekit.caseflow-event/v1", + "sequence": 2 + }, + { + "actor": { + "type": "system", + "id": "nodekit" + }, + "aggregateId": "run_672b968cb5f34778bcd616b7fbab5939", + "aggregateType": "run", + "eventId": "event_eb1706b45a134793b4dfef968f4759ce", + "eventType": "stage.entered", + "occurredAt": "2026-07-22T08:14:08.925Z", + "payload": { + "nextAction": "Verify and export", + "nextActionOwner": "system", + "stageId": "complete" + }, + "schemaVersion": "nodekit.caseflow-event/v1", + "sequence": 5 + }, + { + "actor": { + "type": "system", + "id": "nodekit" + }, + "aggregateId": "run_672b968cb5f34778bcd616b7fbab5939", + "aggregateType": "run", + "eventId": "event_65415805714c46f1b4a3497a46282a1d", + "eventType": "run.completed", + "occurredAt": "2026-07-22T08:14:08.925Z", + "payload": {}, + "schemaVersion": "nodekit.caseflow-event/v1", + "sequence": 6 + }, + { + "actor": { + "type": "system", + "id": "nodekit" + }, + "aggregateId": "run_672b968cb5f34778bcd616b7fbab5939", + "aggregateType": "run", + "eventId": "event_5f3a6da7a0ba4ebcad9a07660af1a02a", + "eventType": "receipt.created", + "occurredAt": "2026-07-22T08:14:08.926Z", + "payload": { + "receiptHash": "8211c34cceac6fde06877c19211132e603e0752bc3e279bda9c0dc3435406c39", + "receiptId": "receipt_f4800a8706474304be088759eee42695" + }, + "schemaVersion": "nodekit.caseflow-event/v1", + "sequence": 7 + } + ], + "exceptions": [], + "proposals": [ + { + "artifactId": "artifact_ad0f28432f074ff48939d61d35eb2cf4", + "baseVersion": 1, + "createdAt": "2026-07-22T08:14:08.924Z", + "patch": { + "summary": "A bounded, reviewable result is ready. Replace this with the researched domain artifact.", + "status": "proposed" + }, + "proposalId": "proposal_bd874e2380624f229d160605001f8b7f", + "rationale": "Demonstrate proposal-before-mutation without assuming a domain.", + "schemaVersion": "nodekit.proposal/v1", + "status": "accepted" + } + ], + "receipts": [ + { + "artifactIds": [ + "artifact_ad0f28432f074ff48939d61d35eb2cf4" + ], + "caseId": "case_57b7c1d61e9648e3be77ae6b520b5e12", + "eventIds": [ + "event_8bbbd064114b40eaa27951b812dc1828", + "event_abafba80e5cd4d878980711d45f8fccd", + "event_a13bea4832e542b48de4f4e7a3c26075", + "event_77e2856b5b5848c9a5e85b127c7fdc45", + "event_841fd077a2e44323a20d2b3307bec927", + "event_7cbaa4bccb4f4ddc8ce2667464a0719a", + "event_9e74cdec472e4905b0e3daa8bf66f789", + "event_574156ad5b364a12ba2ba7d1d33c0729", + "event_eb1706b45a134793b4dfef968f4759ce", + "event_65415805714c46f1b4a3497a46282a1d" + ], + "generatedAt": "2026-07-22T08:14:08.926Z", + "proposalIds": [ + "proposal_bd874e2380624f229d160605001f8b7f" + ], + "runId": "run_672b968cb5f34778bcd616b7fbab5939", + "schemaVersion": "nodekit.receipt/v1", + "status": "completed", + "receiptId": "receipt_f4800a8706474304be088759eee42695", + "receiptHash": "8211c34cceac6fde06877c19211132e603e0752bc3e279bda9c0dc3435406c39" + } + ], + "runs": [ + { + "caseId": "case_57b7c1d61e9648e3be77ae6b520b5e12", + "createdAt": "2026-07-22T08:14:08.920Z", + "currentStageId": "complete", + "nextAction": "Review receipt", + "nextActionOwner": "user", + "runId": "run_672b968cb5f34778bcd616b7fbab5939", + "schemaVersion": "nodekit.run/v1", + "stages": [ + { + "id": "intake", + "label": "Confirm the intended outcome", + "owner": "user", + "status": "completed" + }, + { + "id": "working", + "label": "Prepare a proposal", + "owner": "agent", + "status": "completed" + }, + { + "id": "review", + "label": "Review the proposed change", + "owner": "user", + "status": "completed" + }, + { + "id": "complete", + "label": "Verify and export", + "owner": "system", + "status": "completed" + } + ], + "status": "completed", + "updatedAt": "2026-07-22T08:14:08.925Z" + } + ] + }, + "passed": true +} diff --git a/proof/ease/candidates/f3471c7fd31b4839ffeb9c9f43bd0d4ab7ef6bfc/e4e6a8849115fc70203e1d56228a5bdc3cd44af92ff20f87626657ff4d017d0f/package/eval-receipt.json b/proof/ease/candidates/f3471c7fd31b4839ffeb9c9f43bd0d4ab7ef6bfc/e4e6a8849115fc70203e1d56228a5bdc3cd44af92ff20f87626657ff4d017d0f/package/eval-receipt.json new file mode 100644 index 0000000..45b5427 --- /dev/null +++ b/proof/ease/candidates/f3471c7fd31b4839ffeb9c9f43bd0d4ab7ef6bfc/e4e6a8849115fc70203e1d56228a5bdc3cd44af92ff20f87626657ff4d017d0f/package/eval-receipt.json @@ -0,0 +1,11 @@ +{ + "assertions": { + "canonicalStatePreserved": true, + "exceptionPreservedState": true, + "recoveryOwnerExplicit": true, + "staleProposalConflicted": true + }, + "generatedAt": "2026-07-22T08:14:09.826Z", + "passed": true, + "schemaVersion": "nodekit.eval-receipt/v1" +} diff --git a/proof/ease/candidates/f3471c7fd31b4839ffeb9c9f43bd0d4ab7ef6bfc/e4e6a8849115fc70203e1d56228a5bdc3cd44af92ff20f87626657ff4d017d0f/package/homenshum-nodekit-0.2.1.tgz b/proof/ease/candidates/f3471c7fd31b4839ffeb9c9f43bd0d4ab7ef6bfc/e4e6a8849115fc70203e1d56228a5bdc3cd44af92ff20f87626657ff4d017d0f/package/homenshum-nodekit-0.2.1.tgz new file mode 100644 index 0000000..0e5e530 Binary files /dev/null and b/proof/ease/candidates/f3471c7fd31b4839ffeb9c9f43bd0d4ab7ef6bfc/e4e6a8849115fc70203e1d56228a5bdc3cd44af92ff20f87626657ff4d017d0f/package/homenshum-nodekit-0.2.1.tgz differ diff --git a/proof/ease/developer-timing-runs.template.json b/proof/ease/developer-timing-runs.template.json new file mode 100644 index 0000000..3a53f89 --- /dev/null +++ b/proof/ease/developer-timing-runs.template.json @@ -0,0 +1,4 @@ +{ + "schemaVersion": "nodekit.developer-timing-runs/v1", + "runs": [] +} diff --git a/proof/ease/developer-timing-verdict.json b/proof/ease/developer-timing-verdict.json new file mode 100644 index 0000000..0f89bc2 --- /dev/null +++ b/proof/ease/developer-timing-verdict.json @@ -0,0 +1,898 @@ +{ + "schemaVersion": "nodekit.developer-timing-verdict/v1", + "passed": false, + "errors": [ + "windows/npm/cold: requires five runs, found 0", + "windows/npm/warm: requires five runs, found 0", + "windows/pnpm/cold: requires five runs, found 0", + "windows/pnpm/warm: requires five runs, found 0", + "ubuntu/npm/cold: requires five runs, found 0", + "ubuntu/npm/warm: requires five runs, found 0", + "ubuntu/pnpm/cold: requires five runs, found 0", + "ubuntu/pnpm/warm: requires five runs, found 0", + "macos/npm/cold: requires five runs, found 0", + "macos/npm/warm: requires five runs, found 0", + "macos/pnpm/cold: requires five runs, found 0", + "macos/pnpm/warm: requires five runs, found 0" + ], + "requiredRuns": 60, + "observedRuns": 0, + "cells": { + "windows/npm": { + "cold": { + "scaffoldGenerationMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "dependencyInstallationMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "compileMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "serverReadinessMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "firstMeaningfulPaintMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "neutralJourneyMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "totalMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + } + }, + "warm": { + "scaffoldGenerationMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "dependencyInstallationMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "compileMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "serverReadinessMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "firstMeaningfulPaintMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "neutralJourneyMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "totalMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + } + } + }, + "windows/pnpm": { + "cold": { + "scaffoldGenerationMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "dependencyInstallationMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "compileMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "serverReadinessMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "firstMeaningfulPaintMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "neutralJourneyMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "totalMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + } + }, + "warm": { + "scaffoldGenerationMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "dependencyInstallationMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "compileMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "serverReadinessMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "firstMeaningfulPaintMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "neutralJourneyMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "totalMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + } + } + }, + "ubuntu/npm": { + "cold": { + "scaffoldGenerationMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "dependencyInstallationMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "compileMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "serverReadinessMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "firstMeaningfulPaintMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "neutralJourneyMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "totalMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + } + }, + "warm": { + "scaffoldGenerationMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "dependencyInstallationMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "compileMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "serverReadinessMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "firstMeaningfulPaintMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "neutralJourneyMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "totalMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + } + } + }, + "ubuntu/pnpm": { + "cold": { + "scaffoldGenerationMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "dependencyInstallationMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "compileMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "serverReadinessMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "firstMeaningfulPaintMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "neutralJourneyMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "totalMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + } + }, + "warm": { + "scaffoldGenerationMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "dependencyInstallationMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "compileMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "serverReadinessMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "firstMeaningfulPaintMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "neutralJourneyMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "totalMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + } + } + }, + "macos/npm": { + "cold": { + "scaffoldGenerationMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "dependencyInstallationMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "compileMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "serverReadinessMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "firstMeaningfulPaintMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "neutralJourneyMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "totalMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + } + }, + "warm": { + "scaffoldGenerationMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "dependencyInstallationMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "compileMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "serverReadinessMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "firstMeaningfulPaintMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "neutralJourneyMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "totalMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + } + } + }, + "macos/pnpm": { + "cold": { + "scaffoldGenerationMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "dependencyInstallationMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "compileMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "serverReadinessMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "firstMeaningfulPaintMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "neutralJourneyMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "totalMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + } + }, + "warm": { + "scaffoldGenerationMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "dependencyInstallationMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "compileMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "serverReadinessMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "firstMeaningfulPaintMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "neutralJourneyMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + }, + "totalMs": { + "samples": 0, + "median": null, + "minimum": null, + "maximum": null, + "q1": null, + "q3": null, + "p95": null, + "p95Eligible": false + } + } + } + } +} diff --git a/proof/ease/fresh-users-verdict.json b/proof/ease/fresh-users-verdict.json new file mode 100644 index 0000000..a1450bb --- /dev/null +++ b/proof/ease/fresh-users-verdict.json @@ -0,0 +1,23 @@ +{ + "schemaVersion": "nodekit.fresh-user-verdict/v1", + "passed": false, + "errors": [ + "at least five fresh participants are required" + ], + "checks": { + "participantCount": false, + "unassistedCompletion": false, + "firstMeaningfulAction": false, + "neutralJourney": false, + "singleEaseQuestion": false, + "noP0P1Failures": true + }, + "metrics": { + "participantCount": 0, + "unassistedCompletions": 0, + "medianFirstMeaningfulActionMs": null, + "medianNeutralJourneyMs": null, + "medianSingleEaseQuestion": null, + "p0P1Failures": 0 + } +} diff --git a/proof/ease/fresh-users.template.json b/proof/ease/fresh-users.template.json new file mode 100644 index 0000000..39e443d --- /dev/null +++ b/proof/ease/fresh-users.template.json @@ -0,0 +1,15 @@ +{ + "schemaVersion": "nodekit.fresh-user-study/v1", + "nodekitCommit": "0000000000000000000000000000000000000000", + "nodekitSourceHash": "0000000000000000000000000000000000000000000000000000000000000000", + "instruction": "Use this app to complete the job shown on screen.", + "participants": [], + "thresholds": { + "minimumUnassistedCompletions": 4, + "maximumMedianFirstMeaningfulActionMs": 30000, + "maximumMedianNeutralJourneyMs": 180000, + "minimumMedianSingleEaseQuestion": 6, + "maximumP0P1Failures": 0 + }, + "status": "not-run" +} diff --git a/proof/ease/latest/browser/console.jsonl b/proof/ease/latest/browser/console.jsonl new file mode 100644 index 0000000..e69de29 diff --git a/proof/ease/latest/browser/journey.webm b/proof/ease/latest/browser/journey.webm new file mode 100644 index 0000000..bf58a9f Binary files /dev/null and b/proof/ease/latest/browser/journey.webm differ diff --git a/proof/ease/latest/browser/network.jsonl b/proof/ease/latest/browser/network.jsonl new file mode 100644 index 0000000..e69de29 diff --git a/proof/ease/latest/browser/playwright-trace.zip b/proof/ease/latest/browser/playwright-trace.zip new file mode 100644 index 0000000..4564fac Binary files /dev/null and b/proof/ease/latest/browser/playwright-trace.zip differ diff --git a/proof/ease/latest/browser/screenshot-manifest.json b/proof/ease/latest/browser/screenshot-manifest.json new file mode 100644 index 0000000..62dc5f7 --- /dev/null +++ b/proof/ease/latest/browser/screenshot-manifest.json @@ -0,0 +1,5341 @@ +{ + "accessibilityViolations": [], + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "certified": true, + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": [], + "coveredStates": [ + "first_arrival", + "orientation", + "input", + "validation_error", + "running", + "partial_result", + "external_wait", + "proposal_pending", + "approval", + "conflict", + "recoverable_failure", + "reload_resume", + "completed_receipt", + "receipt_inspection", + "export_share" + ], + "durationMs": 144245, + "error": null, + "evidenceArtifacts": [ + { + "byteSize": 13376187, + "id": "playwright-trace", + "path": "browser/playwright-trace.zip", + "sha256": "1e75d01735d50da8c8d4053099869ef40f436266515ad333b438d7e894299cfa" + }, + { + "byteSize": 2288708, + "id": "browser-video", + "path": "browser/journey.webm", + "sha256": "d10b13c9bb0b7dff932cae2de0abf852ce817dc19b165785d0e8448b15fe2bd6" + } + ], + "firstMeaningfulPaintMs": 1348, + "generatedAt": "2026-07-21T22:28:56.455Z", + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "journeyAssertions": { + "approvalApplied": true, + "proposalVisible": true, + "receiptSurvivedReload": true, + "receiptVisible": true + }, + "milestones": [ + { + "at": "2026-07-21T22:26:33.558Z", + "elapsedMs": 1349, + "name": "first_meaningful_paint" + }, + { + "at": "2026-07-21T22:26:33.558Z", + "elapsedMs": 1349, + "name": "neutral_case_visible" + }, + { + "at": "2026-07-21T22:26:33.645Z", + "elapsedMs": 1436, + "name": "proposal_visible" + }, + { + "at": "2026-07-21T22:26:33.769Z", + "elapsedMs": 1559, + "name": "approval_applied" + }, + { + "at": "2026-07-21T22:26:33.769Z", + "elapsedMs": 1559, + "name": "receipt_visible" + }, + { + "at": "2026-07-21T22:26:34.339Z", + "elapsedMs": 2129, + "name": "receipt_reload_confirmed" + } + ], + "missingStates": [], + "networkFailures": [], + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "passed": true, + "phases": [ + { + "at": "2026-07-21T22:26:32.408Z", + "durationMs": 133, + "name": "server_readiness" + } + ], + "requiredStates": [ + "first_arrival", + "orientation", + "input", + "validation_error", + "running", + "partial_result", + "external_wait", + "proposal_pending", + "approval", + "conflict", + "recoverable_failure", + "reload_resume", + "completed_receipt", + "receipt_inspection", + "export_share" + ], + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.browser-certification/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "screenshots": [ + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:26:36.179Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 3970, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=first_arrival", + "pngSha256": "41b27c1fed98be0a51bebc0eedf8851cf5b144826cc6d9caa0a1029e6ac3cf62", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "first_arrival", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/first_arrival--desktop--light.png", + "sidecarPath": "browser/screenshots/first_arrival--desktop--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:26:37.609Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 5399, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=orientation", + "pngSha256": "9a5e78314cb69e20d63993a8f84ca2402de1fd4357664bb082adbe7f0427a7c9", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "orientation", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/orientation--desktop--light.png", + "sidecarPath": "browser/screenshots/orientation--desktop--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:26:39.318Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 7109, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=input", + "pngSha256": "b83128bb17f5a55763edae4b6d573a333b37ca38c6fd7f6dcfd2d78c42b14d4b", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "input", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/input--desktop--light.png", + "sidecarPath": "browser/screenshots/input--desktop--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:26:41.321Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 9112, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=validation_error", + "pngSha256": "f15834e766d0b1a69d2fe550b2f71d7b403900e167219b635fdd1cb7d561ce82", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "validation_error", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/validation_error--desktop--light.png", + "sidecarPath": "browser/screenshots/validation_error--desktop--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:26:42.812Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 10602, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=running", + "pngSha256": "63971e350a3cfca7d6e0cfae70a27324cfe6efa7d0defd290fb51173da71c9ba", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "running", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/running--desktop--light.png", + "sidecarPath": "browser/screenshots/running--desktop--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:26:44.010Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 11801, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=partial_result", + "pngSha256": "bc56d7518de84fd388a24105d9745dd08dafb46ad7a310531dcf473435b19690", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "partial_result", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/partial_result--desktop--light.png", + "sidecarPath": "browser/screenshots/partial_result--desktop--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:26:45.192Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 12983, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=external_wait", + "pngSha256": "eb3938592250d844857164c8e702e3ff95ef78a0bcc4e10dc00ee1e2b58cd4b7", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "external_wait", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/external_wait--desktop--light.png", + "sidecarPath": "browser/screenshots/external_wait--desktop--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:26:46.407Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 14197, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=proposal_pending", + "pngSha256": "fcfe2f205e143c39529039db367db3e47a76a85b27a5917ba2fa649f7c269914", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "proposal_pending", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/proposal_pending--desktop--light.png", + "sidecarPath": "browser/screenshots/proposal_pending--desktop--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:26:47.715Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 15505, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=approval", + "pngSha256": "3c19b2d45969202bd549380be7ea803229ccc813291943592bccb7d38dd358b8", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "approval", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/approval--desktop--light.png", + "sidecarPath": "browser/screenshots/approval--desktop--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:26:49.014Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 16805, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=conflict", + "pngSha256": "7897f016075f11df9f51c52a301f34b0a821a67938ddd5ee377d7acfeb3297b7", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "conflict", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/conflict--desktop--light.png", + "sidecarPath": "browser/screenshots/conflict--desktop--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:26:50.399Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 18189, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=recoverable_failure", + "pngSha256": "c3bc749a87e94946816db6d6361015828ea834d89182539e6fdd21cfc444ef42", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "recoverable_failure", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/recoverable_failure--desktop--light.png", + "sidecarPath": "browser/screenshots/recoverable_failure--desktop--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:26:51.578Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 19369, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=reload_resume", + "pngSha256": "a277adeea31da7eb8d652d2afec26006c3ea2c0b468722cc98ca60be11ce81c3", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "reload_resume", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/reload_resume--desktop--light.png", + "sidecarPath": "browser/screenshots/reload_resume--desktop--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:26:53.328Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 21118, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=completed_receipt", + "pngSha256": "b9962eccf1e1caea4a55956216d6055fd182ab5e4032f8c745ac7ba06bd909dd", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "completed_receipt", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/completed_receipt--desktop--light.png", + "sidecarPath": "browser/screenshots/completed_receipt--desktop--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:26:55.141Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 22931, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=receipt_inspection", + "pngSha256": "930b571cf7b9d6bfcb680f8363e5cbd0a573d737fd7213cdb07853979d1d07a1", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "receipt_inspection", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/receipt_inspection--desktop--light.png", + "sidecarPath": "browser/screenshots/receipt_inspection--desktop--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:26:56.377Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 24167, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=export_share", + "pngSha256": "72c8ddfcc3270616e9adb67abc6c7ba1e1fe7574ce8d82c5263cccb32d9d4eb6", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "export_share", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/export_share--desktop--light.png", + "sidecarPath": "browser/screenshots/export_share--desktop--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:26:59.267Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 27057, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=first_arrival", + "pngSha256": "a57a3c197de2726f943f7141f54a974ec6d3803b1cf19a58a0d16fd1ebfe3f3a", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "first_arrival", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/first_arrival--desktop--dark.png", + "sidecarPath": "browser/screenshots/first_arrival--desktop--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:26:59.833Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 27623, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=orientation", + "pngSha256": "b66c1507eb0357de6239383543931ff8ef725ae35cef356e2f0c4429c7c67e32", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "orientation", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/orientation--desktop--dark.png", + "sidecarPath": "browser/screenshots/orientation--desktop--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:00.419Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 28209, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=input", + "pngSha256": "b92eea59ad4126b66be93a505c2f041b1a5f59e92501f8c3fac30a0a7308ef37", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "input", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/input--desktop--dark.png", + "sidecarPath": "browser/screenshots/input--desktop--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:01.060Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 28850, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=validation_error", + "pngSha256": "4b290ccb18c7fa205637ecb774496ea6b829057b725fa39e6efaf617be70dc14", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "validation_error", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/validation_error--desktop--dark.png", + "sidecarPath": "browser/screenshots/validation_error--desktop--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:01.634Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 29424, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=running", + "pngSha256": "25f0953dda7641393995c4fb2cfcf9e0ac8009a13d5bee03824a3138e8be3d8c", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "running", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/running--desktop--dark.png", + "sidecarPath": "browser/screenshots/running--desktop--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:02.203Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 29993, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=partial_result", + "pngSha256": "7fe1598bc3d7aa34ff115c8a37606c3acd20c951196a279ae3cdab1c6a4d6284", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "partial_result", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/partial_result--desktop--dark.png", + "sidecarPath": "browser/screenshots/partial_result--desktop--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:02.784Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 30574, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=external_wait", + "pngSha256": "cc760027415b86e8e27ea37b909a623e4c968d26778bbf199cee0a4f56fb544a", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "external_wait", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/external_wait--desktop--dark.png", + "sidecarPath": "browser/screenshots/external_wait--desktop--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:03.373Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 31163, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=proposal_pending", + "pngSha256": "885061f0cb3f21682b765941392ca4eff6152eb6dd4a62c5591e3262a6b26f9c", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "proposal_pending", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/proposal_pending--desktop--dark.png", + "sidecarPath": "browser/screenshots/proposal_pending--desktop--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:03.955Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 31745, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=approval", + "pngSha256": "c6d0cc0da5d1ab2fb17960d09dd214b97c1be7abbc9fc4a1f54d9405c1eafc8c", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "approval", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/approval--desktop--dark.png", + "sidecarPath": "browser/screenshots/approval--desktop--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:04.535Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 32325, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=conflict", + "pngSha256": "af2b4ed40f0263a81bcadc5c9f83277ea7c38b28c8379445f586ebdae861450b", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "conflict", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/conflict--desktop--dark.png", + "sidecarPath": "browser/screenshots/conflict--desktop--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:05.144Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 32935, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=recoverable_failure", + "pngSha256": "bf75db99449a2f5968e7a04d6d4ec454f0873caee1606bf3e8d2580cc784bf97", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "recoverable_failure", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/recoverable_failure--desktop--dark.png", + "sidecarPath": "browser/screenshots/recoverable_failure--desktop--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:05.730Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 33520, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=reload_resume", + "pngSha256": "d07253685da5c7d3da8c91440fabeaedc97ffda14bb754dc815a38b7850b83ed", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "reload_resume", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/reload_resume--desktop--dark.png", + "sidecarPath": "browser/screenshots/reload_resume--desktop--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:06.860Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 34650, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=completed_receipt", + "pngSha256": "c88e589107382aa6effe6bcf7f549774f2e93b561264d3df1b5f81963fae7fae", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "completed_receipt", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/completed_receipt--desktop--dark.png", + "sidecarPath": "browser/screenshots/completed_receipt--desktop--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:07.982Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 35773, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=receipt_inspection", + "pngSha256": "727fac9157c7dc5aaa50bcd2f247885f4247992f642f808f76b5d5ef27f0d8aa", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "receipt_inspection", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/receipt_inspection--desktop--dark.png", + "sidecarPath": "browser/screenshots/receipt_inspection--desktop--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:08.552Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 36342, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=export_share", + "pngSha256": "d99a011a24ae208486ee428f54056667f3225a5b232edf8ff3fa3f019fd77f5d", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "export_share", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + }, + "path": "browser/screenshots/export_share--desktop--dark.png", + "sidecarPath": "browser/screenshots/export_share--desktop--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:09.235Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 37026, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=first_arrival", + "pngSha256": "3de1c119f175c6b30381fc22fd3f9b85807d3cc88a266669648099283e5d5f90", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "first_arrival", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/first_arrival--wide--light.png", + "sidecarPath": "browser/screenshots/first_arrival--wide--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:09.837Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 37627, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=orientation", + "pngSha256": "a08b0807c6784d9bcbf850263843e4883a80da887fc293dd4212d410fc626642", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "orientation", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/orientation--wide--light.png", + "sidecarPath": "browser/screenshots/orientation--wide--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:10.433Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 38223, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=input", + "pngSha256": "f63bda5c27b460ef82088039a9e5d3e098776804ef3a3ac26555de67a6b0a81d", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "input", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/input--wide--light.png", + "sidecarPath": "browser/screenshots/input--wide--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:11.066Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 38857, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=validation_error", + "pngSha256": "240431908327645e91e5b5dc0598f7fa2c0d0caf7774ba54d227a0cb77d087af", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "validation_error", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/validation_error--wide--light.png", + "sidecarPath": "browser/screenshots/validation_error--wide--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:11.666Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 39456, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=running", + "pngSha256": "e98b0c2b9d899f354c55d54acc224a99e73aa00e6897f35e07e2059c52b3fbb4", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "running", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/running--wide--light.png", + "sidecarPath": "browser/screenshots/running--wide--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:12.248Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 40039, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=partial_result", + "pngSha256": "8c3895dede848e0a19c6f32438c1c9583a7fb3820788be15bc35a08448bc6068", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "partial_result", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/partial_result--wide--light.png", + "sidecarPath": "browser/screenshots/partial_result--wide--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:12.844Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 40634, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=external_wait", + "pngSha256": "c513f779fde386e0ee8eecda6abaaa1b7a32d4ba1ce26cc24b6b4e60f7e3866c", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "external_wait", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/external_wait--wide--light.png", + "sidecarPath": "browser/screenshots/external_wait--wide--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:13.439Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 41229, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=proposal_pending", + "pngSha256": "20261b18a983cdb0bb59471bfe54ca8393728f613d68f371b624cf59378755a8", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "proposal_pending", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/proposal_pending--wide--light.png", + "sidecarPath": "browser/screenshots/proposal_pending--wide--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:14.040Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 41830, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=approval", + "pngSha256": "f474501a676387b93bf761ac805c99bded731649dc45dd17e8731f30728f3f55", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "approval", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/approval--wide--light.png", + "sidecarPath": "browser/screenshots/approval--wide--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:14.639Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 42429, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=conflict", + "pngSha256": "0a403ecb9708e8a66b0f79c159da3f47e18f4ed1fffc29750531fbc7f289e55d", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "conflict", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/conflict--wide--light.png", + "sidecarPath": "browser/screenshots/conflict--wide--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:15.227Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 43018, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=recoverable_failure", + "pngSha256": "210d798bdb3882df8d00af9f4b3ca75b0ffa96a68e62f7dcb816bc7c8f899e4c", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "recoverable_failure", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/recoverable_failure--wide--light.png", + "sidecarPath": "browser/screenshots/recoverable_failure--wide--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:15.815Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 43605, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=reload_resume", + "pngSha256": "00e7c528ba8c859b7362d8b134c89c03ca685fb4093dc672010801c65832d3fd", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "reload_resume", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/reload_resume--wide--light.png", + "sidecarPath": "browser/screenshots/reload_resume--wide--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:16.929Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 44720, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=completed_receipt", + "pngSha256": "9d4e4ba587cf1616788343f7a0c32625f4882273e400dab3158a8e23d66698f7", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "completed_receipt", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/completed_receipt--wide--light.png", + "sidecarPath": "browser/screenshots/completed_receipt--wide--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:28.620Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 56410, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=receipt_inspection", + "pngSha256": "53b00756209876fceffb828d36c8cd0f4b5a9d9da5ec509ab0a4b5a714bbea8b", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "receipt_inspection", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/receipt_inspection--wide--light.png", + "sidecarPath": "browser/screenshots/receipt_inspection--wide--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:29.215Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 57006, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=export_share", + "pngSha256": "ab15e4369e7601f5c052a3965e948dc2e2ab07f7d62d2f315fe95b41159d1ae0", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "export_share", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/export_share--wide--light.png", + "sidecarPath": "browser/screenshots/export_share--wide--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:29.900Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 57690, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=first_arrival", + "pngSha256": "e967f444bdcff348ced0855448454acf6bb9301c0eb5c600910de6207a7b7dfa", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "first_arrival", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/first_arrival--wide--dark.png", + "sidecarPath": "browser/screenshots/first_arrival--wide--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:30.559Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 58349, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=orientation", + "pngSha256": "ed37c21fad900e0c68f4d6e7cc40cd8e5670c5363bced2e55e2ade336d0ff8e5", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "orientation", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/orientation--wide--dark.png", + "sidecarPath": "browser/screenshots/orientation--wide--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:31.207Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 58998, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=input", + "pngSha256": "f9da38328d057bfe72062aaba7b071c0728afd202c78fc1a0fdfc606356e0aec", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "input", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/input--wide--dark.png", + "sidecarPath": "browser/screenshots/input--wide--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:31.847Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 59637, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=validation_error", + "pngSha256": "41b2287c1e95e283eeb7dbc811812057af8965586314b6348515bab216120403", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "validation_error", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/validation_error--wide--dark.png", + "sidecarPath": "browser/screenshots/validation_error--wide--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:32.430Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 60220, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=running", + "pngSha256": "c929d419ff30ea0d3f103baa537e500b42078200b661bbfa0be4b70b637743f4", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "running", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/running--wide--dark.png", + "sidecarPath": "browser/screenshots/running--wide--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:33.012Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 60802, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=partial_result", + "pngSha256": "17e82dd3dccb4905ab4cf0147b6a4d35fbb2aa66b69f996b0d5409c96e28604a", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "partial_result", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/partial_result--wide--dark.png", + "sidecarPath": "browser/screenshots/partial_result--wide--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:33.595Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 61385, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=external_wait", + "pngSha256": "719b4c23c7fb85e2d49e14ed789c64cbdded494625010ff4b09c8a3dd30af6b5", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "external_wait", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/external_wait--wide--dark.png", + "sidecarPath": "browser/screenshots/external_wait--wide--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:34.186Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 61976, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=proposal_pending", + "pngSha256": "9caf980541cf87aee67952081658ed4134e1058580c920b1b93e39bcd14c61af", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "proposal_pending", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/proposal_pending--wide--dark.png", + "sidecarPath": "browser/screenshots/proposal_pending--wide--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:34.783Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 62573, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=approval", + "pngSha256": "3b0c2069deb0f48d5bba9cc042079d2ef248c69a127190fe5e98b6fba1655787", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "approval", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/approval--wide--dark.png", + "sidecarPath": "browser/screenshots/approval--wide--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:35.371Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 63162, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=conflict", + "pngSha256": "c1b4485d6cb04f22e3aee8e8bfc4c4d7d963227b28459cbaa4cea0ed965667af", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "conflict", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/conflict--wide--dark.png", + "sidecarPath": "browser/screenshots/conflict--wide--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:36.007Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 63798, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=recoverable_failure", + "pngSha256": "edb8ab6554ce2c3bf83f9dc4772962dffc83e4669cfb20233966348944e5dda0", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "recoverable_failure", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/recoverable_failure--wide--dark.png", + "sidecarPath": "browser/screenshots/recoverable_failure--wide--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:36.594Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 64384, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=reload_resume", + "pngSha256": "9a4b725e5f42a2265cb0eb473e772b0c425a6ec98929561eec78580994bd9926", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "reload_resume", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/reload_resume--wide--dark.png", + "sidecarPath": "browser/screenshots/reload_resume--wide--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:37.710Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 65501, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=completed_receipt", + "pngSha256": "5186f384ddf2c5a67f2cde21fe03d2705436b1b94455c847784d079c8b65bbb0", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "completed_receipt", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/completed_receipt--wide--dark.png", + "sidecarPath": "browser/screenshots/completed_receipt--wide--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:38.829Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 66620, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=receipt_inspection", + "pngSha256": "ccfb416779fe418e21f16bf0f16cf0ceaa6b86f5deaff08d6edcbf5ed5ad9c51", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "receipt_inspection", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/receipt_inspection--wide--dark.png", + "sidecarPath": "browser/screenshots/receipt_inspection--wide--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:39.411Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 67202, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=export_share", + "pngSha256": "2623316cf64d698b65b19d9c45ace488ab6aa46ad1505aab65d8edde862c76cb", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "export_share", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + }, + "path": "browser/screenshots/export_share--wide--dark.png", + "sidecarPath": "browser/screenshots/export_share--wide--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:40.053Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 67843, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=first_arrival", + "pngSha256": "cda6d5d64ae2540fe450720bf590d1e2529a65111385ed5cc1de924f35689f44", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "first_arrival", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/first_arrival--tablet-landscape--light.png", + "sidecarPath": "browser/screenshots/first_arrival--tablet-landscape--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:40.620Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 68411, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=orientation", + "pngSha256": "ae495e396bcb9eb5c6378d91035778591bac6917fdb110e8b64713d7f882f57a", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "orientation", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/orientation--tablet-landscape--light.png", + "sidecarPath": "browser/screenshots/orientation--tablet-landscape--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:41.185Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 68976, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=input", + "pngSha256": "b0d40d0833208426b20ce9b4998f82f6de4288fbc65ce5fa7be9c068666191a4", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "input", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/input--tablet-landscape--light.png", + "sidecarPath": "browser/screenshots/input--tablet-landscape--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:41.789Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 69579, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=validation_error", + "pngSha256": "f29543f60947f18b05928e27ee5296a8e4504a62f51c1389de625a33f70942a0", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "validation_error", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/validation_error--tablet-landscape--light.png", + "sidecarPath": "browser/screenshots/validation_error--tablet-landscape--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:42.355Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 70145, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=running", + "pngSha256": "21ea362237822e386d94c236d2dbff012e4203cce654ce3fe84793e78a7e2027", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "running", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/running--tablet-landscape--light.png", + "sidecarPath": "browser/screenshots/running--tablet-landscape--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:42.902Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 70692, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=partial_result", + "pngSha256": "45e9c846ceced75e36a03a9063737437741665ec79dd170a5de919f8087b1cd9", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "partial_result", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/partial_result--tablet-landscape--light.png", + "sidecarPath": "browser/screenshots/partial_result--tablet-landscape--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:43.474Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 71264, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=external_wait", + "pngSha256": "c427f8ea2a923506e7d3ad1a8525157cd88d81f63eb42bdac90223ddb442559b", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "external_wait", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/external_wait--tablet-landscape--light.png", + "sidecarPath": "browser/screenshots/external_wait--tablet-landscape--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:44.035Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 71826, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=proposal_pending", + "pngSha256": "44f4b9a1bdd0356c85c0161134fd89782926c8fdf8275fcfaf8ec734a7a464d1", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "proposal_pending", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/proposal_pending--tablet-landscape--light.png", + "sidecarPath": "browser/screenshots/proposal_pending--tablet-landscape--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:44.587Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 72378, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=approval", + "pngSha256": "f5455da68f7a2a4daab3507bcbc0f03dcd367f8f314a31b473da9056b561d3d9", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "approval", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/approval--tablet-landscape--light.png", + "sidecarPath": "browser/screenshots/approval--tablet-landscape--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:45.140Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 72931, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=conflict", + "pngSha256": "c34055192591e8c8714a71a931d8cdab5453710fe0a48af8256fb04d03c82a79", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "conflict", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/conflict--tablet-landscape--light.png", + "sidecarPath": "browser/screenshots/conflict--tablet-landscape--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:45.700Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 73490, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=recoverable_failure", + "pngSha256": "448d05c1d94a49c8e392082e9ca3fe6e634f29369926105c40f30e82bc0bc899", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "recoverable_failure", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/recoverable_failure--tablet-landscape--light.png", + "sidecarPath": "browser/screenshots/recoverable_failure--tablet-landscape--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:46.252Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 74043, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=reload_resume", + "pngSha256": "70647d82506c5492331835fd0833ec8ef629e795caf9c85372dad50414cbf5d5", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "reload_resume", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/reload_resume--tablet-landscape--light.png", + "sidecarPath": "browser/screenshots/reload_resume--tablet-landscape--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:47.335Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 75126, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=completed_receipt", + "pngSha256": "db442baadd225a30982e6a97e2a4b0000f341a1d941c7a7a682d7810fcb83129", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "completed_receipt", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/completed_receipt--tablet-landscape--light.png", + "sidecarPath": "browser/screenshots/completed_receipt--tablet-landscape--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:48.420Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 76210, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=receipt_inspection", + "pngSha256": "364929c96cc74e088b83d5d62ffafecb3709247dadb59a1f632334fcfb966561", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "receipt_inspection", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/receipt_inspection--tablet-landscape--light.png", + "sidecarPath": "browser/screenshots/receipt_inspection--tablet-landscape--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:48.986Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 76777, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=export_share", + "pngSha256": "45f741a7bd513bc69a8f7dba2c5b4c07af60223223b46081ed2bbef935a42dbb", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "export_share", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/export_share--tablet-landscape--light.png", + "sidecarPath": "browser/screenshots/export_share--tablet-landscape--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:49.636Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 77426, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=first_arrival", + "pngSha256": "9deec21252677430894e0df1698bfb5e22b87a9f48d140a09a6cb2fc62aec278", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "first_arrival", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/first_arrival--tablet-landscape--dark.png", + "sidecarPath": "browser/screenshots/first_arrival--tablet-landscape--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:50.186Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 77976, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=orientation", + "pngSha256": "c64a1fa435da9c786eac01feead9f08c7f81273cf42664a021bfaa5173771c84", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "orientation", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/orientation--tablet-landscape--dark.png", + "sidecarPath": "browser/screenshots/orientation--tablet-landscape--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:50.752Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 78543, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=input", + "pngSha256": "3465ebc56542b64ee7bac812862001bf3197ef4ab6eb2bc46fe34eeb7c49634f", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "input", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/input--tablet-landscape--dark.png", + "sidecarPath": "browser/screenshots/input--tablet-landscape--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:51.356Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 79147, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=validation_error", + "pngSha256": "6d8072d39c0320148f614f001433c913c497f2a6dcaaaa989937f7eec87e0f37", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "validation_error", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/validation_error--tablet-landscape--dark.png", + "sidecarPath": "browser/screenshots/validation_error--tablet-landscape--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:51.920Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 79710, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=running", + "pngSha256": "2843cb3c783b2632f8033a8b957abace622a5a58dc317cfc00651c78502efad5", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "running", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/running--tablet-landscape--dark.png", + "sidecarPath": "browser/screenshots/running--tablet-landscape--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:52.468Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 80259, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=partial_result", + "pngSha256": "689ec45c6fcab70ada5969d7bf0c84fe85b0289f54ae25c9c46f478bc99aa1f1", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "partial_result", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/partial_result--tablet-landscape--dark.png", + "sidecarPath": "browser/screenshots/partial_result--tablet-landscape--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:53.023Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 80814, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=external_wait", + "pngSha256": "f0071a6cc94a36f7bdc97231cbcbfb43f2c1492aa68e4637ac48509e9fab661e", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "external_wait", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/external_wait--tablet-landscape--dark.png", + "sidecarPath": "browser/screenshots/external_wait--tablet-landscape--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:53.586Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 81376, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=proposal_pending", + "pngSha256": "eeac98cd587fe710bc483d46dcb9fe48e183c223c2c3cebb561a791093b15cc7", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "proposal_pending", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/proposal_pending--tablet-landscape--dark.png", + "sidecarPath": "browser/screenshots/proposal_pending--tablet-landscape--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:54.153Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 81944, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=approval", + "pngSha256": "97545aefb5e8b026880aef18a6188495835163fd704b3a2fc26d0d57fc5377e9", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "approval", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/approval--tablet-landscape--dark.png", + "sidecarPath": "browser/screenshots/approval--tablet-landscape--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:54.717Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 82508, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=conflict", + "pngSha256": "185ae733d261b64b000648ef3a3b64751d263ea80ad0865e5f3d4ccb4b04cf8f", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "conflict", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/conflict--tablet-landscape--dark.png", + "sidecarPath": "browser/screenshots/conflict--tablet-landscape--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:55.285Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 83075, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=recoverable_failure", + "pngSha256": "bea5876d8e8c9a7258861cbe26467e1360471f94a50ce523c396a8ebf11eaa66", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "recoverable_failure", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/recoverable_failure--tablet-landscape--dark.png", + "sidecarPath": "browser/screenshots/recoverable_failure--tablet-landscape--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:55.852Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 83642, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=reload_resume", + "pngSha256": "4dc645feeb3435a498b3a00b81ede0f3591f763f1db7e572e99d3cf8bd74ef01", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "reload_resume", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/reload_resume--tablet-landscape--dark.png", + "sidecarPath": "browser/screenshots/reload_resume--tablet-landscape--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:56.936Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 84727, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=completed_receipt", + "pngSha256": "70ee9845d565cfe1f3760b0ed7f78c05237c57892047fb82096b43b6b5dd37b1", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "completed_receipt", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/completed_receipt--tablet-landscape--dark.png", + "sidecarPath": "browser/screenshots/completed_receipt--tablet-landscape--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:58.024Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 85814, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=receipt_inspection", + "pngSha256": "8e9aa0f71cdfeff60bb2838601ecc3895d85ea42c946aabecfb313ffc9337001", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "receipt_inspection", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/receipt_inspection--tablet-landscape--dark.png", + "sidecarPath": "browser/screenshots/receipt_inspection--tablet-landscape--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:58.588Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 86379, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=export_share", + "pngSha256": "e25683e72e20e98c2c1be2007c3965110302323b17df5d5a61dea6be5d5ff5bf", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "export_share", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + }, + "path": "browser/screenshots/export_share--tablet-landscape--dark.png", + "sidecarPath": "browser/screenshots/export_share--tablet-landscape--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:59.235Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 87026, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=first_arrival", + "pngSha256": "039a9c8ffd412c9ded55da5359696187483549f23edf14643fdf818d8f5caa4e", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "first_arrival", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/first_arrival--tablet-portrait--light.png", + "sidecarPath": "browser/screenshots/first_arrival--tablet-portrait--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:59.804Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 87594, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=orientation", + "pngSha256": "b027d7674551f0642fa5a2ba75b4add6e0e04ac9d981caffb87f16d074b5c4da", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "orientation", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/orientation--tablet-portrait--light.png", + "sidecarPath": "browser/screenshots/orientation--tablet-portrait--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:00.370Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 88160, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=input", + "pngSha256": "de4aeb1b823198a02cb2f064c8ad725906c2a6abc3d235b306c6657d065470e8", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "input", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/input--tablet-portrait--light.png", + "sidecarPath": "browser/screenshots/input--tablet-portrait--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:00.987Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 88777, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=validation_error", + "pngSha256": "618bbc7c1938b8c9de1805499b689a4e8493cca504bc2989faca582063e6f396", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "validation_error", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/validation_error--tablet-portrait--light.png", + "sidecarPath": "browser/screenshots/validation_error--tablet-portrait--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:01.534Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 89325, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=running", + "pngSha256": "791f507945c05852f2aa5681d6c63c100219f45fbe83d3e4dff2a89b978ab0cc", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "running", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/running--tablet-portrait--light.png", + "sidecarPath": "browser/screenshots/running--tablet-portrait--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:02.086Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 89877, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=partial_result", + "pngSha256": "c365050b7e46e9dc7f7cb465fdf7a7ba00a78503602a3ef086dd40519a5e73ad", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "partial_result", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/partial_result--tablet-portrait--light.png", + "sidecarPath": "browser/screenshots/partial_result--tablet-portrait--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:02.651Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 90442, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=external_wait", + "pngSha256": "a566ba60ebad7fb4398272a3f5aa18e9cce5493b1601db4fbc2410c8bb94901a", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "external_wait", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/external_wait--tablet-portrait--light.png", + "sidecarPath": "browser/screenshots/external_wait--tablet-portrait--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:03.201Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 90992, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=proposal_pending", + "pngSha256": "ac1710692ab9a41581a284f3e897da654e77d992a3c95f1f6334b7dd166a0cdc", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "proposal_pending", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/proposal_pending--tablet-portrait--light.png", + "sidecarPath": "browser/screenshots/proposal_pending--tablet-portrait--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:03.766Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 91557, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=approval", + "pngSha256": "3c92196c2baca41d0ad764b7e26a4f3092b95ee3ecce6d1354c6eb26c5e9b5da", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "approval", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/approval--tablet-portrait--light.png", + "sidecarPath": "browser/screenshots/approval--tablet-portrait--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:04.318Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 92109, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=conflict", + "pngSha256": "ebb48487d93e55557780993039c3fb9ddcf44bd8cd480d9295747a8222616230", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "conflict", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/conflict--tablet-portrait--light.png", + "sidecarPath": "browser/screenshots/conflict--tablet-portrait--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:04.882Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 92673, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=recoverable_failure", + "pngSha256": "b4ec848b8520e4d651723dd6660eeff2ba807bce80c228133b878f8596f7abb4", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "recoverable_failure", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/recoverable_failure--tablet-portrait--light.png", + "sidecarPath": "browser/screenshots/recoverable_failure--tablet-portrait--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:05.449Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 93240, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=reload_resume", + "pngSha256": "0bd885f65cbd66c0fdfe495149e769643a5c6cd12da65edbdfc4dafdc82051a1", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "reload_resume", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/reload_resume--tablet-portrait--light.png", + "sidecarPath": "browser/screenshots/reload_resume--tablet-portrait--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:06.534Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 94324, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=completed_receipt", + "pngSha256": "143ac2e53f9ee78371dd1ec9925f8e9105500a4131a2215f288ae66325ca50eb", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "completed_receipt", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/completed_receipt--tablet-portrait--light.png", + "sidecarPath": "browser/screenshots/completed_receipt--tablet-portrait--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:07.608Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 95399, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=receipt_inspection", + "pngSha256": "e3d0ab3c979c7b047bdb01e88121830e6090a3db10368e6c6c670adadc65d57c", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "receipt_inspection", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/receipt_inspection--tablet-portrait--light.png", + "sidecarPath": "browser/screenshots/receipt_inspection--tablet-portrait--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:08.166Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 95956, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=export_share", + "pngSha256": "873a98293da66e483e0f6e3b033dc0a05c919ab7665cf5f117b9c7a9aceeb225", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "export_share", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/export_share--tablet-portrait--light.png", + "sidecarPath": "browser/screenshots/export_share--tablet-portrait--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:08.801Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 96592, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=first_arrival", + "pngSha256": "3993a349ba426e89b4646044c8f43cd37726540077efd835ad0a1867d0e7014a", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "first_arrival", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/first_arrival--tablet-portrait--dark.png", + "sidecarPath": "browser/screenshots/first_arrival--tablet-portrait--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:09.368Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 97159, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=orientation", + "pngSha256": "8ecb265459e0530d0fce80022e9aab816725a3ca1300e8490dae493a2e56c659", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "orientation", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/orientation--tablet-portrait--dark.png", + "sidecarPath": "browser/screenshots/orientation--tablet-portrait--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:09.938Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 97728, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=input", + "pngSha256": "00de66d4c832feb03791b1a78c9f898b308f0ac117427ac5083dd0918d329f30", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "input", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/input--tablet-portrait--dark.png", + "sidecarPath": "browser/screenshots/input--tablet-portrait--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:10.552Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 98342, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=validation_error", + "pngSha256": "b0aa56fc263738894b59ed27ee104a4d5eacd8f36d40e71585ed4d719fdc9b41", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "validation_error", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/validation_error--tablet-portrait--dark.png", + "sidecarPath": "browser/screenshots/validation_error--tablet-portrait--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:11.103Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 98893, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=running", + "pngSha256": "09c8dd927828db18b262a1be5dd8d4ed5e59b0cdeaed46c2ac37ae8dacbec79d", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "running", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/running--tablet-portrait--dark.png", + "sidecarPath": "browser/screenshots/running--tablet-portrait--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:11.669Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 99459, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=partial_result", + "pngSha256": "44ca11398a3c3441f65e1b35234bb7065740656489fbd24c72443147588cf5b3", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "partial_result", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/partial_result--tablet-portrait--dark.png", + "sidecarPath": "browser/screenshots/partial_result--tablet-portrait--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:12.234Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 100025, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=external_wait", + "pngSha256": "4defc08ac8c08950aa3929f17a8439f046b0747670bdef4a0caee69069f72c77", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "external_wait", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/external_wait--tablet-portrait--dark.png", + "sidecarPath": "browser/screenshots/external_wait--tablet-portrait--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:12.785Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 100576, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=proposal_pending", + "pngSha256": "da751b86a2835a0c4634a4b0989f7b096c4071be8b9d73b23e40f1a84cfd6efc", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "proposal_pending", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/proposal_pending--tablet-portrait--dark.png", + "sidecarPath": "browser/screenshots/proposal_pending--tablet-portrait--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:13.351Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 101141, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=approval", + "pngSha256": "6acc4f0f62947e3c7dd3518a8af50ac9b0731e764171a298f0878f2ddf97bba1", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "approval", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/approval--tablet-portrait--dark.png", + "sidecarPath": "browser/screenshots/approval--tablet-portrait--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:13.901Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 101691, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=conflict", + "pngSha256": "a0f6f83552272d8757bd51960dfc758bfce20b7e7fae7e4256696b5372643ab6", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "conflict", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/conflict--tablet-portrait--dark.png", + "sidecarPath": "browser/screenshots/conflict--tablet-portrait--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:14.451Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 102242, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=recoverable_failure", + "pngSha256": "bd094e04e8bb063231ab0c8ddd54eb165f9d64d4836d5a28eed0b9f490155c28", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "recoverable_failure", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/recoverable_failure--tablet-portrait--dark.png", + "sidecarPath": "browser/screenshots/recoverable_failure--tablet-portrait--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:15.018Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 102808, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=reload_resume", + "pngSha256": "bfcc3dcf8178f532a022ca9fef6f9df57618127a41f56b9ddef77068e197180f", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "reload_resume", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/reload_resume--tablet-portrait--dark.png", + "sidecarPath": "browser/screenshots/reload_resume--tablet-portrait--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:16.100Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 103891, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=completed_receipt", + "pngSha256": "f1d2d63638a606a029ef567e910062dd50d59ede7c39ca3348dcb652f5e2b81c", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "completed_receipt", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/completed_receipt--tablet-portrait--dark.png", + "sidecarPath": "browser/screenshots/completed_receipt--tablet-portrait--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:17.183Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 104974, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=receipt_inspection", + "pngSha256": "c5a06283b7f56a05e43fbd573d8f36162e13ea4f4b2cc8ffbb3f51a349883c92", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "receipt_inspection", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/receipt_inspection--tablet-portrait--dark.png", + "sidecarPath": "browser/screenshots/receipt_inspection--tablet-portrait--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:17.738Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 105529, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=export_share", + "pngSha256": "c188b4fb49b21c4d2741a1b246159655b8acbd086dd4619f53430238de284bd1", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "export_share", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + }, + "path": "browser/screenshots/export_share--tablet-portrait--dark.png", + "sidecarPath": "browser/screenshots/export_share--tablet-portrait--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:18.358Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 106149, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=first_arrival", + "pngSha256": "06aeb36753da9de625432d8e9d2f7e23b54da7b35b350170bf3d0d1297c7af15", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "first_arrival", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/first_arrival--mobile-portrait--light.png", + "sidecarPath": "browser/screenshots/first_arrival--mobile-portrait--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:18.908Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 106698, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=orientation", + "pngSha256": "83c8a75dbb8521cc5ba766be8d48e3ef6f41a4d691aa4f4804b9e83d625074b2", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "orientation", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/orientation--mobile-portrait--light.png", + "sidecarPath": "browser/screenshots/orientation--mobile-portrait--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:19.459Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 107249, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=input", + "pngSha256": "105124b708991a2d20c0650f055c7bcd226118b6cb699f33d6ea5ccd7f9b187c", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "input", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/input--mobile-portrait--light.png", + "sidecarPath": "browser/screenshots/input--mobile-portrait--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:20.041Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 107832, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=validation_error", + "pngSha256": "d0fb96fc3515c56bff68f2f9f1b0a20dfd290a3cbd2c43b3a8980ed0bd77bde7", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "validation_error", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/validation_error--mobile-portrait--light.png", + "sidecarPath": "browser/screenshots/validation_error--mobile-portrait--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:20.591Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 108382, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=running", + "pngSha256": "730f926f9f58badccdabaf08ea5b1eeb4e22e96dc9ded4aad396ea58bd11aae7", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "running", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/running--mobile-portrait--light.png", + "sidecarPath": "browser/screenshots/running--mobile-portrait--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:21.143Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 108933, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=partial_result", + "pngSha256": "634670b5b38cd795f83d125e78dcf652872f8a943fd326cc2deae8055c934d9a", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "partial_result", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/partial_result--mobile-portrait--light.png", + "sidecarPath": "browser/screenshots/partial_result--mobile-portrait--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:21.690Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 109481, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=external_wait", + "pngSha256": "0007bef120712d371f15a1a2b9c1249b197043897a680b29dd7590f73d7f97cc", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "external_wait", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/external_wait--mobile-portrait--light.png", + "sidecarPath": "browser/screenshots/external_wait--mobile-portrait--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:22.242Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 110033, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=proposal_pending", + "pngSha256": "20594ee7577f98359d72e8a3ddcd7c924cce956a04b7b9ce82fcca682374f2c3", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "proposal_pending", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/proposal_pending--mobile-portrait--light.png", + "sidecarPath": "browser/screenshots/proposal_pending--mobile-portrait--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:22.792Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 110583, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=approval", + "pngSha256": "9ac088050f37810c1b246d09d965939c522f48e27d3aa650f6f0d0f042acbf6e", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "approval", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/approval--mobile-portrait--light.png", + "sidecarPath": "browser/screenshots/approval--mobile-portrait--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:23.360Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 111151, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=conflict", + "pngSha256": "61b4dd8bc0826736979277af2b20cbee8c18e753dd9eb9131bd1f1ce206eb8bf", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "conflict", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/conflict--mobile-portrait--light.png", + "sidecarPath": "browser/screenshots/conflict--mobile-portrait--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:23.910Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 111701, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=recoverable_failure", + "pngSha256": "33eb02934c740e40226e1d8ea2b6105ab0598fc70cedd38605efc79ac61571ea", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "recoverable_failure", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/recoverable_failure--mobile-portrait--light.png", + "sidecarPath": "browser/screenshots/recoverable_failure--mobile-portrait--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:24.459Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 112250, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=reload_resume", + "pngSha256": "68af6db9c23cb93169847ff7e2a79fbd0f3cd6ce78ef44bf34d2fd346429f138", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "reload_resume", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/reload_resume--mobile-portrait--light.png", + "sidecarPath": "browser/screenshots/reload_resume--mobile-portrait--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:25.543Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 113333, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=completed_receipt", + "pngSha256": "00d60621a399b2c106de39d746875c185c1125af11032b1b2f49832dcb9855c5", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "completed_receipt", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/completed_receipt--mobile-portrait--light.png", + "sidecarPath": "browser/screenshots/completed_receipt--mobile-portrait--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:26.641Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 114432, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=receipt_inspection", + "pngSha256": "327a8456950f0ec59a14877afeb8ca2bbf8a7fa619e73d0e89092d5af25e8b13", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "receipt_inspection", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/receipt_inspection--mobile-portrait--light.png", + "sidecarPath": "browser/screenshots/receipt_inspection--mobile-portrait--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:27.223Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 115014, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=export_share", + "pngSha256": "75049b15376b22e52e6c504d916d10ecef046518612123915c737068c7df7cd5", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "export_share", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/export_share--mobile-portrait--light.png", + "sidecarPath": "browser/screenshots/export_share--mobile-portrait--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:27.860Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 115650, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=first_arrival", + "pngSha256": "64cb3f851e66cc8632a11d34ad279111d07af55ca21d009778bf5d3a4cf4e92c", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "first_arrival", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/first_arrival--mobile-portrait--dark.png", + "sidecarPath": "browser/screenshots/first_arrival--mobile-portrait--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:28.426Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 116216, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=orientation", + "pngSha256": "1416a658a1ae7729e57e11f4f383776144e6f4fb114041ebb6de11ffc58ca5bf", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "orientation", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/orientation--mobile-portrait--dark.png", + "sidecarPath": "browser/screenshots/orientation--mobile-portrait--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:28.976Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 116767, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=input", + "pngSha256": "0ed732340cb7411d068b7a0445858accb1cc7e02da4b96ed5f4f28495bf5c9f3", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "input", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/input--mobile-portrait--dark.png", + "sidecarPath": "browser/screenshots/input--mobile-portrait--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:29.558Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 117348, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=validation_error", + "pngSha256": "02b66012b2e1948340a0404768cf643d43fc5ac96cdb4e20e568c9c729e1eba9", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "validation_error", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/validation_error--mobile-portrait--dark.png", + "sidecarPath": "browser/screenshots/validation_error--mobile-portrait--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:30.108Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 117898, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=running", + "pngSha256": "63639f19c68a1cf91cb3871eb1ab315a6267310706297affaaf1000a47d3b376", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "running", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/running--mobile-portrait--dark.png", + "sidecarPath": "browser/screenshots/running--mobile-portrait--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:30.661Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 118451, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=partial_result", + "pngSha256": "63ca2fa009666d191d1d9fbe1988febd25e77ef2b4e1c6e5969a50213bff71ea", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "partial_result", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/partial_result--mobile-portrait--dark.png", + "sidecarPath": "browser/screenshots/partial_result--mobile-portrait--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:31.207Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 118998, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=external_wait", + "pngSha256": "d7060668bb00b5ceb1e5c65141cbdd2ed139eb77e42452c6ad43dfa12b43f293", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "external_wait", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/external_wait--mobile-portrait--dark.png", + "sidecarPath": "browser/screenshots/external_wait--mobile-portrait--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:31.742Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 119532, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=proposal_pending", + "pngSha256": "b8095014aa3a980c701960dc828db3c5aeae0ad9ea2f56268c42d306fee688b3", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "proposal_pending", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/proposal_pending--mobile-portrait--dark.png", + "sidecarPath": "browser/screenshots/proposal_pending--mobile-portrait--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:32.293Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 120083, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=approval", + "pngSha256": "824eb84486d8d15179143bad849176e33e04f9eea8a165dd0a6ed325e61b2b09", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "approval", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/approval--mobile-portrait--dark.png", + "sidecarPath": "browser/screenshots/approval--mobile-portrait--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:32.842Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 120632, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=conflict", + "pngSha256": "cd65b2a4aa611cc28bb010aba61fbed152c9232c236151f7643f533f6c0dfe48", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "conflict", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/conflict--mobile-portrait--dark.png", + "sidecarPath": "browser/screenshots/conflict--mobile-portrait--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:33.395Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 121185, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=recoverable_failure", + "pngSha256": "297cbff2271db7580227a88d5868a0aa0ac636d576275b48a88716d5c0eb81a2", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "recoverable_failure", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/recoverable_failure--mobile-portrait--dark.png", + "sidecarPath": "browser/screenshots/recoverable_failure--mobile-portrait--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:33.942Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 121732, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=reload_resume", + "pngSha256": "ed7e01a088b12b21eab979c8297696d4e0abf5632e0adb54ed3c8e883bbb08db", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "reload_resume", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/reload_resume--mobile-portrait--dark.png", + "sidecarPath": "browser/screenshots/reload_resume--mobile-portrait--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:35.026Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 122816, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=completed_receipt", + "pngSha256": "77ebf9cf6e47cfbe581c129f0a96af411d078681a181c94b6fea5c4d5be9acae", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "completed_receipt", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/completed_receipt--mobile-portrait--dark.png", + "sidecarPath": "browser/screenshots/completed_receipt--mobile-portrait--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:36.141Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 123930, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=receipt_inspection", + "pngSha256": "77ed38d44b4017526cb7ae55081800b622a9ba56a9b0f37499c238e785e91e76", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "receipt_inspection", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/receipt_inspection--mobile-portrait--dark.png", + "sidecarPath": "browser/screenshots/receipt_inspection--mobile-portrait--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:36.726Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 124516, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=export_share", + "pngSha256": "dc637181081f3439c9df84b2f608b109f541b88e3eddecaacb88358391828a15", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "export_share", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + }, + "path": "browser/screenshots/export_share--mobile-portrait--dark.png", + "sidecarPath": "browser/screenshots/export_share--mobile-portrait--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:37.358Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 125148, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=first_arrival", + "pngSha256": "5ba09c367a9a263ed026d90f14a4ccf65baefb3ef7477e5f17907fc46512c0f0", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "first_arrival", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/first_arrival--mobile-landscape--light.png", + "sidecarPath": "browser/screenshots/first_arrival--mobile-landscape--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:37.909Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 125699, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=orientation", + "pngSha256": "5ba09c367a9a263ed026d90f14a4ccf65baefb3ef7477e5f17907fc46512c0f0", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "orientation", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/orientation--mobile-landscape--light.png", + "sidecarPath": "browser/screenshots/orientation--mobile-landscape--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:38.459Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 126249, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=input", + "pngSha256": "5ba09c367a9a263ed026d90f14a4ccf65baefb3ef7477e5f17907fc46512c0f0", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "input", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/input--mobile-landscape--light.png", + "sidecarPath": "browser/screenshots/input--mobile-landscape--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:39.044Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 126834, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=validation_error", + "pngSha256": "28efb728c9eb980724c19a19d8050d05b5409d1d7736f2b638ced96f6659f169", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "validation_error", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/validation_error--mobile-landscape--light.png", + "sidecarPath": "browser/screenshots/validation_error--mobile-landscape--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:39.591Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 127381, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=running", + "pngSha256": "7311400a07613abafb2c3ab11a1a51eec8f01e0699a428de092425f7a78448e8", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "running", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/running--mobile-landscape--light.png", + "sidecarPath": "browser/screenshots/running--mobile-landscape--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:40.143Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 127933, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=partial_result", + "pngSha256": "7311400a07613abafb2c3ab11a1a51eec8f01e0699a428de092425f7a78448e8", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "partial_result", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/partial_result--mobile-landscape--light.png", + "sidecarPath": "browser/screenshots/partial_result--mobile-landscape--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:40.692Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 128482, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=external_wait", + "pngSha256": "feb67962e4fb9fe6eff99624d9fba027e4b5916344af223bea8afac701cb6fc0", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "external_wait", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/external_wait--mobile-landscape--light.png", + "sidecarPath": "browser/screenshots/external_wait--mobile-landscape--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:41.248Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 129038, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=proposal_pending", + "pngSha256": "b5a725ff429d63398d151fcbaadb01e4de68e36ee359d2df60c51ef6da9e57a2", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "proposal_pending", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/proposal_pending--mobile-landscape--light.png", + "sidecarPath": "browser/screenshots/proposal_pending--mobile-landscape--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:41.808Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 129598, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=approval", + "pngSha256": "fd651381c53a9ff246aabb08eab87f376aaa54cb2bdeabe303f63d048d782248", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "approval", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/approval--mobile-landscape--light.png", + "sidecarPath": "browser/screenshots/approval--mobile-landscape--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:42.360Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 130150, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=conflict", + "pngSha256": "b5a725ff429d63398d151fcbaadb01e4de68e36ee359d2df60c51ef6da9e57a2", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "conflict", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/conflict--mobile-landscape--light.png", + "sidecarPath": "browser/screenshots/conflict--mobile-landscape--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:42.915Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 130705, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=recoverable_failure", + "pngSha256": "feb67962e4fb9fe6eff99624d9fba027e4b5916344af223bea8afac701cb6fc0", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "recoverable_failure", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/recoverable_failure--mobile-landscape--light.png", + "sidecarPath": "browser/screenshots/recoverable_failure--mobile-landscape--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:43.477Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 131267, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=reload_resume", + "pngSha256": "b5a725ff429d63398d151fcbaadb01e4de68e36ee359d2df60c51ef6da9e57a2", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "reload_resume", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/reload_resume--mobile-landscape--light.png", + "sidecarPath": "browser/screenshots/reload_resume--mobile-landscape--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:44.557Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 132347, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=completed_receipt", + "pngSha256": "b2130d5cd0070f274e5ef06fd22caade9262a448ee0b62fad820c2548e422ee2", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "completed_receipt", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/completed_receipt--mobile-landscape--light.png", + "sidecarPath": "browser/screenshots/completed_receipt--mobile-landscape--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:45.657Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 133447, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=receipt_inspection", + "pngSha256": "c5cd5e0716befd08488b1649133397f95910a59b00a7f3e5cc072a922171c4d9", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "receipt_inspection", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/receipt_inspection--mobile-landscape--light.png", + "sidecarPath": "browser/screenshots/receipt_inspection--mobile-landscape--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:46.241Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 134031, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=export_share", + "pngSha256": "34dae83b0e3251a643b25c4e711e93f3b3030e1f0a6e68565f1726d3e1c3ce03", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "export_share", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/export_share--mobile-landscape--light.png", + "sidecarPath": "browser/screenshots/export_share--mobile-landscape--light.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:46.874Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 134664, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=first_arrival", + "pngSha256": "cf8837825047e06b18035461c8f1ce428170f9f4fe0168aab8533616a8f55874", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "first_arrival", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/first_arrival--mobile-landscape--dark.png", + "sidecarPath": "browser/screenshots/first_arrival--mobile-landscape--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:47.425Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 135215, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=orientation", + "pngSha256": "cf8837825047e06b18035461c8f1ce428170f9f4fe0168aab8533616a8f55874", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "orientation", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/orientation--mobile-landscape--dark.png", + "sidecarPath": "browser/screenshots/orientation--mobile-landscape--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:47.991Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 135781, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=input", + "pngSha256": "cf8837825047e06b18035461c8f1ce428170f9f4fe0168aab8533616a8f55874", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "input", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/input--mobile-landscape--dark.png", + "sidecarPath": "browser/screenshots/input--mobile-landscape--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:48.574Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 136364, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=validation_error", + "pngSha256": "5ab4e710ae41bfbed96d8bae1df8bea53ee9ceac414cb4596da2e24301e04be8", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "validation_error", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/validation_error--mobile-landscape--dark.png", + "sidecarPath": "browser/screenshots/validation_error--mobile-landscape--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:49.125Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 136915, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=running", + "pngSha256": "4462e909f0bc28437bb21a470f330587c90d61cbd33a21c4f68c577306a000b6", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "running", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/running--mobile-landscape--dark.png", + "sidecarPath": "browser/screenshots/running--mobile-landscape--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:49.677Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 137466, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=partial_result", + "pngSha256": "4462e909f0bc28437bb21a470f330587c90d61cbd33a21c4f68c577306a000b6", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "partial_result", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/partial_result--mobile-landscape--dark.png", + "sidecarPath": "browser/screenshots/partial_result--mobile-landscape--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:50.241Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 138031, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=external_wait", + "pngSha256": "c232bf27be0bec2dd9c0860f5a65a0bc8d0fec30929a2ef0662c5adb05ad17a6", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "external_wait", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/external_wait--mobile-landscape--dark.png", + "sidecarPath": "browser/screenshots/external_wait--mobile-landscape--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:50.792Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 138582, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=proposal_pending", + "pngSha256": "49034360bc184960c219e1bed559c1edc3973250837f85d299971e59f645c846", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "proposal_pending", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/proposal_pending--mobile-landscape--dark.png", + "sidecarPath": "browser/screenshots/proposal_pending--mobile-landscape--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:51.342Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 139132, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=approval", + "pngSha256": "103a9728f1471515c753d38a04429f9d34d264b33cfaa90845e6a2c6e96fd840", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "approval", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/approval--mobile-landscape--dark.png", + "sidecarPath": "browser/screenshots/approval--mobile-landscape--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:51.892Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 139682, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=conflict", + "pngSha256": "49034360bc184960c219e1bed559c1edc3973250837f85d299971e59f645c846", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "conflict", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/conflict--mobile-landscape--dark.png", + "sidecarPath": "browser/screenshots/conflict--mobile-landscape--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:52.457Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 140247, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=recoverable_failure", + "pngSha256": "c232bf27be0bec2dd9c0860f5a65a0bc8d0fec30929a2ef0662c5adb05ad17a6", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "recoverable_failure", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/recoverable_failure--mobile-landscape--dark.png", + "sidecarPath": "browser/screenshots/recoverable_failure--mobile-landscape--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:53.010Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 140800, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=reload_resume", + "pngSha256": "49034360bc184960c219e1bed559c1edc3973250837f85d299971e59f645c846", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "reload_resume", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/reload_resume--mobile-landscape--dark.png", + "sidecarPath": "browser/screenshots/reload_resume--mobile-landscape--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:54.093Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 141883, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=completed_receipt", + "pngSha256": "d9f0aadc693b3672dff82360540e1b654c80d308b1bd5ce05602d6dcaece15e2", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "completed_receipt", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/completed_receipt--mobile-landscape--dark.png", + "sidecarPath": "browser/screenshots/completed_receipt--mobile-landscape--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:55.207Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 142997, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=receipt_inspection", + "pngSha256": "9de3f08c5fbae30615667095663c99bf93a7b1081ba747579be3b15e88683a81", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "receipt_inspection", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/receipt_inspection--mobile-landscape--dark.png", + "sidecarPath": "browser/screenshots/receipt_inspection--mobile-landscape--dark.json" + }, + { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:55.791Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 143581, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=export_share", + "pngSha256": "da91f17ea503d24705036e969f810bf6e8291f6e9ef40a2aa7047246df63866d", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "export_share", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + }, + "path": "browser/screenshots/export_share--mobile-landscape--dark.png", + "sidecarPath": "browser/screenshots/export_share--mobile-landscape--dark.json" + } + ], + "startedAt": "2026-07-21T22:26:32.209Z", + "verdict": "BROWSER_CERTIFIED", + "manifestSha256": "87e9f12f43812439026ff506c55628ac22786856f6baaaa8dceed5d15865fa52" +} diff --git a/proof/ease/latest/browser/screenshots/approval--desktop--dark.json b/proof/ease/latest/browser/screenshots/approval--desktop--dark.json new file mode 100644 index 0000000..b5a899a --- /dev/null +++ b/proof/ease/latest/browser/screenshots/approval--desktop--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:03.955Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 31745, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=approval", + "pngSha256": "c6d0cc0da5d1ab2fb17960d09dd214b97c1be7abbc9fc4a1f54d9405c1eafc8c", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "approval", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/latest/browser/screenshots/approval--desktop--dark.png b/proof/ease/latest/browser/screenshots/approval--desktop--dark.png new file mode 100644 index 0000000..75b3530 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/approval--desktop--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/approval--desktop--light.json b/proof/ease/latest/browser/screenshots/approval--desktop--light.json new file mode 100644 index 0000000..e7486b1 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/approval--desktop--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:26:47.715Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 15505, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=approval", + "pngSha256": "3c19b2d45969202bd549380be7ea803229ccc813291943592bccb7d38dd358b8", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "approval", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/latest/browser/screenshots/approval--desktop--light.png b/proof/ease/latest/browser/screenshots/approval--desktop--light.png new file mode 100644 index 0000000..9f7c385 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/approval--desktop--light.png differ diff --git a/proof/ease/latest/browser/screenshots/approval--mobile-landscape--dark.json b/proof/ease/latest/browser/screenshots/approval--mobile-landscape--dark.json new file mode 100644 index 0000000..5d197e4 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/approval--mobile-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:51.342Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 139132, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=approval", + "pngSha256": "103a9728f1471515c753d38a04429f9d34d264b33cfaa90845e6a2c6e96fd840", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "approval", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/latest/browser/screenshots/approval--mobile-landscape--dark.png b/proof/ease/latest/browser/screenshots/approval--mobile-landscape--dark.png new file mode 100644 index 0000000..f74f441 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/approval--mobile-landscape--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/approval--mobile-landscape--light.json b/proof/ease/latest/browser/screenshots/approval--mobile-landscape--light.json new file mode 100644 index 0000000..f28b02d --- /dev/null +++ b/proof/ease/latest/browser/screenshots/approval--mobile-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:41.808Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 129598, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=approval", + "pngSha256": "fd651381c53a9ff246aabb08eab87f376aaa54cb2bdeabe303f63d048d782248", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "approval", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/latest/browser/screenshots/approval--mobile-landscape--light.png b/proof/ease/latest/browser/screenshots/approval--mobile-landscape--light.png new file mode 100644 index 0000000..d8e57db Binary files /dev/null and b/proof/ease/latest/browser/screenshots/approval--mobile-landscape--light.png differ diff --git a/proof/ease/latest/browser/screenshots/approval--mobile-portrait--dark.json b/proof/ease/latest/browser/screenshots/approval--mobile-portrait--dark.json new file mode 100644 index 0000000..34b04a4 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/approval--mobile-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:32.293Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 120083, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=approval", + "pngSha256": "824eb84486d8d15179143bad849176e33e04f9eea8a165dd0a6ed325e61b2b09", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "approval", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/latest/browser/screenshots/approval--mobile-portrait--dark.png b/proof/ease/latest/browser/screenshots/approval--mobile-portrait--dark.png new file mode 100644 index 0000000..3561861 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/approval--mobile-portrait--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/approval--mobile-portrait--light.json b/proof/ease/latest/browser/screenshots/approval--mobile-portrait--light.json new file mode 100644 index 0000000..96c5a09 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/approval--mobile-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:22.792Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 110583, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=approval", + "pngSha256": "9ac088050f37810c1b246d09d965939c522f48e27d3aa650f6f0d0f042acbf6e", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "approval", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/latest/browser/screenshots/approval--mobile-portrait--light.png b/proof/ease/latest/browser/screenshots/approval--mobile-portrait--light.png new file mode 100644 index 0000000..0661a96 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/approval--mobile-portrait--light.png differ diff --git a/proof/ease/latest/browser/screenshots/approval--tablet-landscape--dark.json b/proof/ease/latest/browser/screenshots/approval--tablet-landscape--dark.json new file mode 100644 index 0000000..60bd69c --- /dev/null +++ b/proof/ease/latest/browser/screenshots/approval--tablet-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:54.153Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 81944, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=approval", + "pngSha256": "97545aefb5e8b026880aef18a6188495835163fd704b3a2fc26d0d57fc5377e9", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "approval", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/latest/browser/screenshots/approval--tablet-landscape--dark.png b/proof/ease/latest/browser/screenshots/approval--tablet-landscape--dark.png new file mode 100644 index 0000000..0ade40f Binary files /dev/null and b/proof/ease/latest/browser/screenshots/approval--tablet-landscape--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/approval--tablet-landscape--light.json b/proof/ease/latest/browser/screenshots/approval--tablet-landscape--light.json new file mode 100644 index 0000000..c184f7b --- /dev/null +++ b/proof/ease/latest/browser/screenshots/approval--tablet-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:44.587Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 72378, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=approval", + "pngSha256": "f5455da68f7a2a4daab3507bcbc0f03dcd367f8f314a31b473da9056b561d3d9", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "approval", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/latest/browser/screenshots/approval--tablet-landscape--light.png b/proof/ease/latest/browser/screenshots/approval--tablet-landscape--light.png new file mode 100644 index 0000000..ed09c79 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/approval--tablet-landscape--light.png differ diff --git a/proof/ease/latest/browser/screenshots/approval--tablet-portrait--dark.json b/proof/ease/latest/browser/screenshots/approval--tablet-portrait--dark.json new file mode 100644 index 0000000..5e2b1ad --- /dev/null +++ b/proof/ease/latest/browser/screenshots/approval--tablet-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:13.351Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 101141, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=approval", + "pngSha256": "6acc4f0f62947e3c7dd3518a8af50ac9b0731e764171a298f0878f2ddf97bba1", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "approval", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/latest/browser/screenshots/approval--tablet-portrait--dark.png b/proof/ease/latest/browser/screenshots/approval--tablet-portrait--dark.png new file mode 100644 index 0000000..57b88db Binary files /dev/null and b/proof/ease/latest/browser/screenshots/approval--tablet-portrait--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/approval--tablet-portrait--light.json b/proof/ease/latest/browser/screenshots/approval--tablet-portrait--light.json new file mode 100644 index 0000000..d2285cc --- /dev/null +++ b/proof/ease/latest/browser/screenshots/approval--tablet-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:03.766Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 91557, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=approval", + "pngSha256": "3c92196c2baca41d0ad764b7e26a4f3092b95ee3ecce6d1354c6eb26c5e9b5da", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "approval", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/latest/browser/screenshots/approval--tablet-portrait--light.png b/proof/ease/latest/browser/screenshots/approval--tablet-portrait--light.png new file mode 100644 index 0000000..1338300 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/approval--tablet-portrait--light.png differ diff --git a/proof/ease/latest/browser/screenshots/approval--wide--dark.json b/proof/ease/latest/browser/screenshots/approval--wide--dark.json new file mode 100644 index 0000000..5438417 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/approval--wide--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:34.783Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 62573, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=approval", + "pngSha256": "3b0c2069deb0f48d5bba9cc042079d2ef248c69a127190fe5e98b6fba1655787", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "approval", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/latest/browser/screenshots/approval--wide--dark.png b/proof/ease/latest/browser/screenshots/approval--wide--dark.png new file mode 100644 index 0000000..8290464 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/approval--wide--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/approval--wide--light.json b/proof/ease/latest/browser/screenshots/approval--wide--light.json new file mode 100644 index 0000000..b47b829 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/approval--wide--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:14.040Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 41830, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=approval", + "pngSha256": "f474501a676387b93bf761ac805c99bded731649dc45dd17e8731f30728f3f55", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "approval", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/latest/browser/screenshots/approval--wide--light.png b/proof/ease/latest/browser/screenshots/approval--wide--light.png new file mode 100644 index 0000000..ab6c1a2 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/approval--wide--light.png differ diff --git a/proof/ease/latest/browser/screenshots/completed_receipt--desktop--dark.json b/proof/ease/latest/browser/screenshots/completed_receipt--desktop--dark.json new file mode 100644 index 0000000..93dc84c --- /dev/null +++ b/proof/ease/latest/browser/screenshots/completed_receipt--desktop--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:06.860Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 34650, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=completed_receipt", + "pngSha256": "c88e589107382aa6effe6bcf7f549774f2e93b561264d3df1b5f81963fae7fae", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "completed_receipt", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/latest/browser/screenshots/completed_receipt--desktop--dark.png b/proof/ease/latest/browser/screenshots/completed_receipt--desktop--dark.png new file mode 100644 index 0000000..f1290a2 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/completed_receipt--desktop--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/completed_receipt--desktop--light.json b/proof/ease/latest/browser/screenshots/completed_receipt--desktop--light.json new file mode 100644 index 0000000..e5d253f --- /dev/null +++ b/proof/ease/latest/browser/screenshots/completed_receipt--desktop--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:26:53.328Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 21118, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=completed_receipt", + "pngSha256": "b9962eccf1e1caea4a55956216d6055fd182ab5e4032f8c745ac7ba06bd909dd", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "completed_receipt", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/latest/browser/screenshots/completed_receipt--desktop--light.png b/proof/ease/latest/browser/screenshots/completed_receipt--desktop--light.png new file mode 100644 index 0000000..c5cf849 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/completed_receipt--desktop--light.png differ diff --git a/proof/ease/latest/browser/screenshots/completed_receipt--mobile-landscape--dark.json b/proof/ease/latest/browser/screenshots/completed_receipt--mobile-landscape--dark.json new file mode 100644 index 0000000..c977a58 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/completed_receipt--mobile-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:54.093Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 141883, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=completed_receipt", + "pngSha256": "d9f0aadc693b3672dff82360540e1b654c80d308b1bd5ce05602d6dcaece15e2", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "completed_receipt", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/latest/browser/screenshots/completed_receipt--mobile-landscape--dark.png b/proof/ease/latest/browser/screenshots/completed_receipt--mobile-landscape--dark.png new file mode 100644 index 0000000..c192b42 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/completed_receipt--mobile-landscape--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/completed_receipt--mobile-landscape--light.json b/proof/ease/latest/browser/screenshots/completed_receipt--mobile-landscape--light.json new file mode 100644 index 0000000..454cce4 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/completed_receipt--mobile-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:44.557Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 132347, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=completed_receipt", + "pngSha256": "b2130d5cd0070f274e5ef06fd22caade9262a448ee0b62fad820c2548e422ee2", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "completed_receipt", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/latest/browser/screenshots/completed_receipt--mobile-landscape--light.png b/proof/ease/latest/browser/screenshots/completed_receipt--mobile-landscape--light.png new file mode 100644 index 0000000..e4ef845 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/completed_receipt--mobile-landscape--light.png differ diff --git a/proof/ease/latest/browser/screenshots/completed_receipt--mobile-portrait--dark.json b/proof/ease/latest/browser/screenshots/completed_receipt--mobile-portrait--dark.json new file mode 100644 index 0000000..d286717 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/completed_receipt--mobile-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:35.026Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 122816, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=completed_receipt", + "pngSha256": "77ebf9cf6e47cfbe581c129f0a96af411d078681a181c94b6fea5c4d5be9acae", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "completed_receipt", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/latest/browser/screenshots/completed_receipt--mobile-portrait--dark.png b/proof/ease/latest/browser/screenshots/completed_receipt--mobile-portrait--dark.png new file mode 100644 index 0000000..62dec72 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/completed_receipt--mobile-portrait--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/completed_receipt--mobile-portrait--light.json b/proof/ease/latest/browser/screenshots/completed_receipt--mobile-portrait--light.json new file mode 100644 index 0000000..7cbdb60 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/completed_receipt--mobile-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:25.543Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 113333, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=completed_receipt", + "pngSha256": "00d60621a399b2c106de39d746875c185c1125af11032b1b2f49832dcb9855c5", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "completed_receipt", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/latest/browser/screenshots/completed_receipt--mobile-portrait--light.png b/proof/ease/latest/browser/screenshots/completed_receipt--mobile-portrait--light.png new file mode 100644 index 0000000..2c00faf Binary files /dev/null and b/proof/ease/latest/browser/screenshots/completed_receipt--mobile-portrait--light.png differ diff --git a/proof/ease/latest/browser/screenshots/completed_receipt--tablet-landscape--dark.json b/proof/ease/latest/browser/screenshots/completed_receipt--tablet-landscape--dark.json new file mode 100644 index 0000000..44b1004 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/completed_receipt--tablet-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:56.936Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 84727, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=completed_receipt", + "pngSha256": "70ee9845d565cfe1f3760b0ed7f78c05237c57892047fb82096b43b6b5dd37b1", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "completed_receipt", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/latest/browser/screenshots/completed_receipt--tablet-landscape--dark.png b/proof/ease/latest/browser/screenshots/completed_receipt--tablet-landscape--dark.png new file mode 100644 index 0000000..b88ae58 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/completed_receipt--tablet-landscape--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/completed_receipt--tablet-landscape--light.json b/proof/ease/latest/browser/screenshots/completed_receipt--tablet-landscape--light.json new file mode 100644 index 0000000..ec6d152 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/completed_receipt--tablet-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:47.335Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 75126, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=completed_receipt", + "pngSha256": "db442baadd225a30982e6a97e2a4b0000f341a1d941c7a7a682d7810fcb83129", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "completed_receipt", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/latest/browser/screenshots/completed_receipt--tablet-landscape--light.png b/proof/ease/latest/browser/screenshots/completed_receipt--tablet-landscape--light.png new file mode 100644 index 0000000..024c8cc Binary files /dev/null and b/proof/ease/latest/browser/screenshots/completed_receipt--tablet-landscape--light.png differ diff --git a/proof/ease/latest/browser/screenshots/completed_receipt--tablet-portrait--dark.json b/proof/ease/latest/browser/screenshots/completed_receipt--tablet-portrait--dark.json new file mode 100644 index 0000000..bf89c9b --- /dev/null +++ b/proof/ease/latest/browser/screenshots/completed_receipt--tablet-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:16.100Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 103891, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=completed_receipt", + "pngSha256": "f1d2d63638a606a029ef567e910062dd50d59ede7c39ca3348dcb652f5e2b81c", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "completed_receipt", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/latest/browser/screenshots/completed_receipt--tablet-portrait--dark.png b/proof/ease/latest/browser/screenshots/completed_receipt--tablet-portrait--dark.png new file mode 100644 index 0000000..8864b51 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/completed_receipt--tablet-portrait--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/completed_receipt--tablet-portrait--light.json b/proof/ease/latest/browser/screenshots/completed_receipt--tablet-portrait--light.json new file mode 100644 index 0000000..c4c7a47 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/completed_receipt--tablet-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:06.534Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 94324, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=completed_receipt", + "pngSha256": "143ac2e53f9ee78371dd1ec9925f8e9105500a4131a2215f288ae66325ca50eb", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "completed_receipt", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/latest/browser/screenshots/completed_receipt--tablet-portrait--light.png b/proof/ease/latest/browser/screenshots/completed_receipt--tablet-portrait--light.png new file mode 100644 index 0000000..3dc9b61 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/completed_receipt--tablet-portrait--light.png differ diff --git a/proof/ease/latest/browser/screenshots/completed_receipt--wide--dark.json b/proof/ease/latest/browser/screenshots/completed_receipt--wide--dark.json new file mode 100644 index 0000000..af4dfa7 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/completed_receipt--wide--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:37.710Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 65501, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=completed_receipt", + "pngSha256": "5186f384ddf2c5a67f2cde21fe03d2705436b1b94455c847784d079c8b65bbb0", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "completed_receipt", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/latest/browser/screenshots/completed_receipt--wide--dark.png b/proof/ease/latest/browser/screenshots/completed_receipt--wide--dark.png new file mode 100644 index 0000000..310e92f Binary files /dev/null and b/proof/ease/latest/browser/screenshots/completed_receipt--wide--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/completed_receipt--wide--light.json b/proof/ease/latest/browser/screenshots/completed_receipt--wide--light.json new file mode 100644 index 0000000..0983816 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/completed_receipt--wide--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:16.929Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 44720, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=completed_receipt", + "pngSha256": "9d4e4ba587cf1616788343f7a0c32625f4882273e400dab3158a8e23d66698f7", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "completed_receipt", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/latest/browser/screenshots/completed_receipt--wide--light.png b/proof/ease/latest/browser/screenshots/completed_receipt--wide--light.png new file mode 100644 index 0000000..dfd9dd2 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/completed_receipt--wide--light.png differ diff --git a/proof/ease/latest/browser/screenshots/conflict--desktop--dark.json b/proof/ease/latest/browser/screenshots/conflict--desktop--dark.json new file mode 100644 index 0000000..e45b822 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/conflict--desktop--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:04.535Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 32325, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=conflict", + "pngSha256": "af2b4ed40f0263a81bcadc5c9f83277ea7c38b28c8379445f586ebdae861450b", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "conflict", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/latest/browser/screenshots/conflict--desktop--dark.png b/proof/ease/latest/browser/screenshots/conflict--desktop--dark.png new file mode 100644 index 0000000..edac0d0 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/conflict--desktop--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/conflict--desktop--light.json b/proof/ease/latest/browser/screenshots/conflict--desktop--light.json new file mode 100644 index 0000000..0ac2190 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/conflict--desktop--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:26:49.014Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 16805, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=conflict", + "pngSha256": "7897f016075f11df9f51c52a301f34b0a821a67938ddd5ee377d7acfeb3297b7", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "conflict", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/latest/browser/screenshots/conflict--desktop--light.png b/proof/ease/latest/browser/screenshots/conflict--desktop--light.png new file mode 100644 index 0000000..d6c2944 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/conflict--desktop--light.png differ diff --git a/proof/ease/latest/browser/screenshots/conflict--mobile-landscape--dark.json b/proof/ease/latest/browser/screenshots/conflict--mobile-landscape--dark.json new file mode 100644 index 0000000..0c65fe9 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/conflict--mobile-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:51.892Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 139682, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=conflict", + "pngSha256": "49034360bc184960c219e1bed559c1edc3973250837f85d299971e59f645c846", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "conflict", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/latest/browser/screenshots/conflict--mobile-landscape--dark.png b/proof/ease/latest/browser/screenshots/conflict--mobile-landscape--dark.png new file mode 100644 index 0000000..4ebe044 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/conflict--mobile-landscape--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/conflict--mobile-landscape--light.json b/proof/ease/latest/browser/screenshots/conflict--mobile-landscape--light.json new file mode 100644 index 0000000..5890b7d --- /dev/null +++ b/proof/ease/latest/browser/screenshots/conflict--mobile-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:42.360Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 130150, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=conflict", + "pngSha256": "b5a725ff429d63398d151fcbaadb01e4de68e36ee359d2df60c51ef6da9e57a2", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "conflict", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/latest/browser/screenshots/conflict--mobile-landscape--light.png b/proof/ease/latest/browser/screenshots/conflict--mobile-landscape--light.png new file mode 100644 index 0000000..fce92ae Binary files /dev/null and b/proof/ease/latest/browser/screenshots/conflict--mobile-landscape--light.png differ diff --git a/proof/ease/latest/browser/screenshots/conflict--mobile-portrait--dark.json b/proof/ease/latest/browser/screenshots/conflict--mobile-portrait--dark.json new file mode 100644 index 0000000..efc1c03 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/conflict--mobile-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:32.842Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 120632, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=conflict", + "pngSha256": "cd65b2a4aa611cc28bb010aba61fbed152c9232c236151f7643f533f6c0dfe48", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "conflict", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/latest/browser/screenshots/conflict--mobile-portrait--dark.png b/proof/ease/latest/browser/screenshots/conflict--mobile-portrait--dark.png new file mode 100644 index 0000000..aff7c47 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/conflict--mobile-portrait--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/conflict--mobile-portrait--light.json b/proof/ease/latest/browser/screenshots/conflict--mobile-portrait--light.json new file mode 100644 index 0000000..f47aa36 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/conflict--mobile-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:23.360Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 111151, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=conflict", + "pngSha256": "61b4dd8bc0826736979277af2b20cbee8c18e753dd9eb9131bd1f1ce206eb8bf", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "conflict", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/latest/browser/screenshots/conflict--mobile-portrait--light.png b/proof/ease/latest/browser/screenshots/conflict--mobile-portrait--light.png new file mode 100644 index 0000000..0a938c5 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/conflict--mobile-portrait--light.png differ diff --git a/proof/ease/latest/browser/screenshots/conflict--tablet-landscape--dark.json b/proof/ease/latest/browser/screenshots/conflict--tablet-landscape--dark.json new file mode 100644 index 0000000..c5b1f57 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/conflict--tablet-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:54.717Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 82508, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=conflict", + "pngSha256": "185ae733d261b64b000648ef3a3b64751d263ea80ad0865e5f3d4ccb4b04cf8f", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "conflict", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/latest/browser/screenshots/conflict--tablet-landscape--dark.png b/proof/ease/latest/browser/screenshots/conflict--tablet-landscape--dark.png new file mode 100644 index 0000000..fad44a0 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/conflict--tablet-landscape--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/conflict--tablet-landscape--light.json b/proof/ease/latest/browser/screenshots/conflict--tablet-landscape--light.json new file mode 100644 index 0000000..af00474 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/conflict--tablet-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:45.140Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 72931, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=conflict", + "pngSha256": "c34055192591e8c8714a71a931d8cdab5453710fe0a48af8256fb04d03c82a79", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "conflict", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/latest/browser/screenshots/conflict--tablet-landscape--light.png b/proof/ease/latest/browser/screenshots/conflict--tablet-landscape--light.png new file mode 100644 index 0000000..92d48b2 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/conflict--tablet-landscape--light.png differ diff --git a/proof/ease/latest/browser/screenshots/conflict--tablet-portrait--dark.json b/proof/ease/latest/browser/screenshots/conflict--tablet-portrait--dark.json new file mode 100644 index 0000000..01c7fa1 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/conflict--tablet-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:13.901Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 101691, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=conflict", + "pngSha256": "a0f6f83552272d8757bd51960dfc758bfce20b7e7fae7e4256696b5372643ab6", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "conflict", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/latest/browser/screenshots/conflict--tablet-portrait--dark.png b/proof/ease/latest/browser/screenshots/conflict--tablet-portrait--dark.png new file mode 100644 index 0000000..c71fdf4 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/conflict--tablet-portrait--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/conflict--tablet-portrait--light.json b/proof/ease/latest/browser/screenshots/conflict--tablet-portrait--light.json new file mode 100644 index 0000000..a7682b8 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/conflict--tablet-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:04.318Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 92109, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=conflict", + "pngSha256": "ebb48487d93e55557780993039c3fb9ddcf44bd8cd480d9295747a8222616230", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "conflict", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/latest/browser/screenshots/conflict--tablet-portrait--light.png b/proof/ease/latest/browser/screenshots/conflict--tablet-portrait--light.png new file mode 100644 index 0000000..a25dfc4 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/conflict--tablet-portrait--light.png differ diff --git a/proof/ease/latest/browser/screenshots/conflict--wide--dark.json b/proof/ease/latest/browser/screenshots/conflict--wide--dark.json new file mode 100644 index 0000000..ceeea99 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/conflict--wide--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:35.371Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 63162, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=conflict", + "pngSha256": "c1b4485d6cb04f22e3aee8e8bfc4c4d7d963227b28459cbaa4cea0ed965667af", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "conflict", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/latest/browser/screenshots/conflict--wide--dark.png b/proof/ease/latest/browser/screenshots/conflict--wide--dark.png new file mode 100644 index 0000000..f75fd6d Binary files /dev/null and b/proof/ease/latest/browser/screenshots/conflict--wide--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/conflict--wide--light.json b/proof/ease/latest/browser/screenshots/conflict--wide--light.json new file mode 100644 index 0000000..c2612df --- /dev/null +++ b/proof/ease/latest/browser/screenshots/conflict--wide--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:14.639Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 42429, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=conflict", + "pngSha256": "0a403ecb9708e8a66b0f79c159da3f47e18f4ed1fffc29750531fbc7f289e55d", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "conflict", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/latest/browser/screenshots/conflict--wide--light.png b/proof/ease/latest/browser/screenshots/conflict--wide--light.png new file mode 100644 index 0000000..6f18867 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/conflict--wide--light.png differ diff --git a/proof/ease/latest/browser/screenshots/export_share--desktop--dark.json b/proof/ease/latest/browser/screenshots/export_share--desktop--dark.json new file mode 100644 index 0000000..2c4d154 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/export_share--desktop--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:08.552Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 36342, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=export_share", + "pngSha256": "d99a011a24ae208486ee428f54056667f3225a5b232edf8ff3fa3f019fd77f5d", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "export_share", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/latest/browser/screenshots/export_share--desktop--dark.png b/proof/ease/latest/browser/screenshots/export_share--desktop--dark.png new file mode 100644 index 0000000..8e45655 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/export_share--desktop--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/export_share--desktop--light.json b/proof/ease/latest/browser/screenshots/export_share--desktop--light.json new file mode 100644 index 0000000..84c4f1c --- /dev/null +++ b/proof/ease/latest/browser/screenshots/export_share--desktop--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:26:56.377Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 24167, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=export_share", + "pngSha256": "72c8ddfcc3270616e9adb67abc6c7ba1e1fe7574ce8d82c5263cccb32d9d4eb6", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "export_share", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/latest/browser/screenshots/export_share--desktop--light.png b/proof/ease/latest/browser/screenshots/export_share--desktop--light.png new file mode 100644 index 0000000..1522b72 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/export_share--desktop--light.png differ diff --git a/proof/ease/latest/browser/screenshots/export_share--mobile-landscape--dark.json b/proof/ease/latest/browser/screenshots/export_share--mobile-landscape--dark.json new file mode 100644 index 0000000..bc2fe79 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/export_share--mobile-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:55.791Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 143581, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=export_share", + "pngSha256": "da91f17ea503d24705036e969f810bf6e8291f6e9ef40a2aa7047246df63866d", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "export_share", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/latest/browser/screenshots/export_share--mobile-landscape--dark.png b/proof/ease/latest/browser/screenshots/export_share--mobile-landscape--dark.png new file mode 100644 index 0000000..328cebc Binary files /dev/null and b/proof/ease/latest/browser/screenshots/export_share--mobile-landscape--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/export_share--mobile-landscape--light.json b/proof/ease/latest/browser/screenshots/export_share--mobile-landscape--light.json new file mode 100644 index 0000000..80b84a2 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/export_share--mobile-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:46.241Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 134031, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=export_share", + "pngSha256": "34dae83b0e3251a643b25c4e711e93f3b3030e1f0a6e68565f1726d3e1c3ce03", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "export_share", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/latest/browser/screenshots/export_share--mobile-landscape--light.png b/proof/ease/latest/browser/screenshots/export_share--mobile-landscape--light.png new file mode 100644 index 0000000..b9541ac Binary files /dev/null and b/proof/ease/latest/browser/screenshots/export_share--mobile-landscape--light.png differ diff --git a/proof/ease/latest/browser/screenshots/export_share--mobile-portrait--dark.json b/proof/ease/latest/browser/screenshots/export_share--mobile-portrait--dark.json new file mode 100644 index 0000000..d45645e --- /dev/null +++ b/proof/ease/latest/browser/screenshots/export_share--mobile-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:36.726Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 124516, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=export_share", + "pngSha256": "dc637181081f3439c9df84b2f608b109f541b88e3eddecaacb88358391828a15", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "export_share", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/latest/browser/screenshots/export_share--mobile-portrait--dark.png b/proof/ease/latest/browser/screenshots/export_share--mobile-portrait--dark.png new file mode 100644 index 0000000..b01b61f Binary files /dev/null and b/proof/ease/latest/browser/screenshots/export_share--mobile-portrait--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/export_share--mobile-portrait--light.json b/proof/ease/latest/browser/screenshots/export_share--mobile-portrait--light.json new file mode 100644 index 0000000..f70f710 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/export_share--mobile-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:27.223Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 115014, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=export_share", + "pngSha256": "75049b15376b22e52e6c504d916d10ecef046518612123915c737068c7df7cd5", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "export_share", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/latest/browser/screenshots/export_share--mobile-portrait--light.png b/proof/ease/latest/browser/screenshots/export_share--mobile-portrait--light.png new file mode 100644 index 0000000..299c254 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/export_share--mobile-portrait--light.png differ diff --git a/proof/ease/latest/browser/screenshots/export_share--tablet-landscape--dark.json b/proof/ease/latest/browser/screenshots/export_share--tablet-landscape--dark.json new file mode 100644 index 0000000..4dc3b3b --- /dev/null +++ b/proof/ease/latest/browser/screenshots/export_share--tablet-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:58.588Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 86379, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=export_share", + "pngSha256": "e25683e72e20e98c2c1be2007c3965110302323b17df5d5a61dea6be5d5ff5bf", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "export_share", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/latest/browser/screenshots/export_share--tablet-landscape--dark.png b/proof/ease/latest/browser/screenshots/export_share--tablet-landscape--dark.png new file mode 100644 index 0000000..77436d3 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/export_share--tablet-landscape--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/export_share--tablet-landscape--light.json b/proof/ease/latest/browser/screenshots/export_share--tablet-landscape--light.json new file mode 100644 index 0000000..762a0ce --- /dev/null +++ b/proof/ease/latest/browser/screenshots/export_share--tablet-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:48.986Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 76777, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=export_share", + "pngSha256": "45f741a7bd513bc69a8f7dba2c5b4c07af60223223b46081ed2bbef935a42dbb", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "export_share", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/latest/browser/screenshots/export_share--tablet-landscape--light.png b/proof/ease/latest/browser/screenshots/export_share--tablet-landscape--light.png new file mode 100644 index 0000000..70513ae Binary files /dev/null and b/proof/ease/latest/browser/screenshots/export_share--tablet-landscape--light.png differ diff --git a/proof/ease/latest/browser/screenshots/export_share--tablet-portrait--dark.json b/proof/ease/latest/browser/screenshots/export_share--tablet-portrait--dark.json new file mode 100644 index 0000000..ddb6cf1 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/export_share--tablet-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:17.738Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 105529, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=export_share", + "pngSha256": "c188b4fb49b21c4d2741a1b246159655b8acbd086dd4619f53430238de284bd1", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "export_share", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/latest/browser/screenshots/export_share--tablet-portrait--dark.png b/proof/ease/latest/browser/screenshots/export_share--tablet-portrait--dark.png new file mode 100644 index 0000000..93f4ed6 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/export_share--tablet-portrait--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/export_share--tablet-portrait--light.json b/proof/ease/latest/browser/screenshots/export_share--tablet-portrait--light.json new file mode 100644 index 0000000..0220dc7 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/export_share--tablet-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:08.166Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 95956, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=export_share", + "pngSha256": "873a98293da66e483e0f6e3b033dc0a05c919ab7665cf5f117b9c7a9aceeb225", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "export_share", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/latest/browser/screenshots/export_share--tablet-portrait--light.png b/proof/ease/latest/browser/screenshots/export_share--tablet-portrait--light.png new file mode 100644 index 0000000..15db04f Binary files /dev/null and b/proof/ease/latest/browser/screenshots/export_share--tablet-portrait--light.png differ diff --git a/proof/ease/latest/browser/screenshots/export_share--wide--dark.json b/proof/ease/latest/browser/screenshots/export_share--wide--dark.json new file mode 100644 index 0000000..18be8ce --- /dev/null +++ b/proof/ease/latest/browser/screenshots/export_share--wide--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:39.411Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 67202, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=export_share", + "pngSha256": "2623316cf64d698b65b19d9c45ace488ab6aa46ad1505aab65d8edde862c76cb", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "export_share", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/latest/browser/screenshots/export_share--wide--dark.png b/proof/ease/latest/browser/screenshots/export_share--wide--dark.png new file mode 100644 index 0000000..4820454 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/export_share--wide--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/export_share--wide--light.json b/proof/ease/latest/browser/screenshots/export_share--wide--light.json new file mode 100644 index 0000000..b2b28b0 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/export_share--wide--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:29.215Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 57006, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=export_share", + "pngSha256": "ab15e4369e7601f5c052a3965e948dc2e2ab07f7d62d2f315fe95b41159d1ae0", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "export_share", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/latest/browser/screenshots/export_share--wide--light.png b/proof/ease/latest/browser/screenshots/export_share--wide--light.png new file mode 100644 index 0000000..c9583e4 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/export_share--wide--light.png differ diff --git a/proof/ease/latest/browser/screenshots/external_wait--desktop--dark.json b/proof/ease/latest/browser/screenshots/external_wait--desktop--dark.json new file mode 100644 index 0000000..03a3d5b --- /dev/null +++ b/proof/ease/latest/browser/screenshots/external_wait--desktop--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:02.784Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 30574, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=external_wait", + "pngSha256": "cc760027415b86e8e27ea37b909a623e4c968d26778bbf199cee0a4f56fb544a", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "external_wait", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/latest/browser/screenshots/external_wait--desktop--dark.png b/proof/ease/latest/browser/screenshots/external_wait--desktop--dark.png new file mode 100644 index 0000000..0182bf2 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/external_wait--desktop--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/external_wait--desktop--light.json b/proof/ease/latest/browser/screenshots/external_wait--desktop--light.json new file mode 100644 index 0000000..39df17f --- /dev/null +++ b/proof/ease/latest/browser/screenshots/external_wait--desktop--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:26:45.192Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 12983, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=external_wait", + "pngSha256": "eb3938592250d844857164c8e702e3ff95ef78a0bcc4e10dc00ee1e2b58cd4b7", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "external_wait", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/latest/browser/screenshots/external_wait--desktop--light.png b/proof/ease/latest/browser/screenshots/external_wait--desktop--light.png new file mode 100644 index 0000000..b117e5c Binary files /dev/null and b/proof/ease/latest/browser/screenshots/external_wait--desktop--light.png differ diff --git a/proof/ease/latest/browser/screenshots/external_wait--mobile-landscape--dark.json b/proof/ease/latest/browser/screenshots/external_wait--mobile-landscape--dark.json new file mode 100644 index 0000000..3c56620 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/external_wait--mobile-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:50.241Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 138031, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=external_wait", + "pngSha256": "c232bf27be0bec2dd9c0860f5a65a0bc8d0fec30929a2ef0662c5adb05ad17a6", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "external_wait", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/latest/browser/screenshots/external_wait--mobile-landscape--dark.png b/proof/ease/latest/browser/screenshots/external_wait--mobile-landscape--dark.png new file mode 100644 index 0000000..88356f1 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/external_wait--mobile-landscape--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/external_wait--mobile-landscape--light.json b/proof/ease/latest/browser/screenshots/external_wait--mobile-landscape--light.json new file mode 100644 index 0000000..137719a --- /dev/null +++ b/proof/ease/latest/browser/screenshots/external_wait--mobile-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:40.692Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 128482, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=external_wait", + "pngSha256": "feb67962e4fb9fe6eff99624d9fba027e4b5916344af223bea8afac701cb6fc0", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "external_wait", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/latest/browser/screenshots/external_wait--mobile-landscape--light.png b/proof/ease/latest/browser/screenshots/external_wait--mobile-landscape--light.png new file mode 100644 index 0000000..b38c72e Binary files /dev/null and b/proof/ease/latest/browser/screenshots/external_wait--mobile-landscape--light.png differ diff --git a/proof/ease/latest/browser/screenshots/external_wait--mobile-portrait--dark.json b/proof/ease/latest/browser/screenshots/external_wait--mobile-portrait--dark.json new file mode 100644 index 0000000..e13681e --- /dev/null +++ b/proof/ease/latest/browser/screenshots/external_wait--mobile-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:31.207Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 118998, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=external_wait", + "pngSha256": "d7060668bb00b5ceb1e5c65141cbdd2ed139eb77e42452c6ad43dfa12b43f293", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "external_wait", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/latest/browser/screenshots/external_wait--mobile-portrait--dark.png b/proof/ease/latest/browser/screenshots/external_wait--mobile-portrait--dark.png new file mode 100644 index 0000000..38bf032 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/external_wait--mobile-portrait--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/external_wait--mobile-portrait--light.json b/proof/ease/latest/browser/screenshots/external_wait--mobile-portrait--light.json new file mode 100644 index 0000000..571a625 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/external_wait--mobile-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:21.690Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 109481, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=external_wait", + "pngSha256": "0007bef120712d371f15a1a2b9c1249b197043897a680b29dd7590f73d7f97cc", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "external_wait", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/latest/browser/screenshots/external_wait--mobile-portrait--light.png b/proof/ease/latest/browser/screenshots/external_wait--mobile-portrait--light.png new file mode 100644 index 0000000..cc06264 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/external_wait--mobile-portrait--light.png differ diff --git a/proof/ease/latest/browser/screenshots/external_wait--tablet-landscape--dark.json b/proof/ease/latest/browser/screenshots/external_wait--tablet-landscape--dark.json new file mode 100644 index 0000000..5c4cdff --- /dev/null +++ b/proof/ease/latest/browser/screenshots/external_wait--tablet-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:53.023Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 80814, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=external_wait", + "pngSha256": "f0071a6cc94a36f7bdc97231cbcbfb43f2c1492aa68e4637ac48509e9fab661e", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "external_wait", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/latest/browser/screenshots/external_wait--tablet-landscape--dark.png b/proof/ease/latest/browser/screenshots/external_wait--tablet-landscape--dark.png new file mode 100644 index 0000000..0559df7 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/external_wait--tablet-landscape--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/external_wait--tablet-landscape--light.json b/proof/ease/latest/browser/screenshots/external_wait--tablet-landscape--light.json new file mode 100644 index 0000000..d09f4e0 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/external_wait--tablet-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:43.474Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 71264, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=external_wait", + "pngSha256": "c427f8ea2a923506e7d3ad1a8525157cd88d81f63eb42bdac90223ddb442559b", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "external_wait", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/latest/browser/screenshots/external_wait--tablet-landscape--light.png b/proof/ease/latest/browser/screenshots/external_wait--tablet-landscape--light.png new file mode 100644 index 0000000..37d8439 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/external_wait--tablet-landscape--light.png differ diff --git a/proof/ease/latest/browser/screenshots/external_wait--tablet-portrait--dark.json b/proof/ease/latest/browser/screenshots/external_wait--tablet-portrait--dark.json new file mode 100644 index 0000000..f9f5a99 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/external_wait--tablet-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:12.234Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 100025, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=external_wait", + "pngSha256": "4defc08ac8c08950aa3929f17a8439f046b0747670bdef4a0caee69069f72c77", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "external_wait", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/latest/browser/screenshots/external_wait--tablet-portrait--dark.png b/proof/ease/latest/browser/screenshots/external_wait--tablet-portrait--dark.png new file mode 100644 index 0000000..5cb93e7 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/external_wait--tablet-portrait--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/external_wait--tablet-portrait--light.json b/proof/ease/latest/browser/screenshots/external_wait--tablet-portrait--light.json new file mode 100644 index 0000000..b4876fc --- /dev/null +++ b/proof/ease/latest/browser/screenshots/external_wait--tablet-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:02.651Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 90442, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=external_wait", + "pngSha256": "a566ba60ebad7fb4398272a3f5aa18e9cce5493b1601db4fbc2410c8bb94901a", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "external_wait", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/latest/browser/screenshots/external_wait--tablet-portrait--light.png b/proof/ease/latest/browser/screenshots/external_wait--tablet-portrait--light.png new file mode 100644 index 0000000..0dc4ba9 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/external_wait--tablet-portrait--light.png differ diff --git a/proof/ease/latest/browser/screenshots/external_wait--wide--dark.json b/proof/ease/latest/browser/screenshots/external_wait--wide--dark.json new file mode 100644 index 0000000..6c7b92e --- /dev/null +++ b/proof/ease/latest/browser/screenshots/external_wait--wide--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:33.595Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 61385, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=external_wait", + "pngSha256": "719b4c23c7fb85e2d49e14ed789c64cbdded494625010ff4b09c8a3dd30af6b5", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "external_wait", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/latest/browser/screenshots/external_wait--wide--dark.png b/proof/ease/latest/browser/screenshots/external_wait--wide--dark.png new file mode 100644 index 0000000..203823b Binary files /dev/null and b/proof/ease/latest/browser/screenshots/external_wait--wide--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/external_wait--wide--light.json b/proof/ease/latest/browser/screenshots/external_wait--wide--light.json new file mode 100644 index 0000000..31be9da --- /dev/null +++ b/proof/ease/latest/browser/screenshots/external_wait--wide--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:12.844Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 40634, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=external_wait", + "pngSha256": "c513f779fde386e0ee8eecda6abaaa1b7a32d4ba1ce26cc24b6b4e60f7e3866c", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "external_wait", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/latest/browser/screenshots/external_wait--wide--light.png b/proof/ease/latest/browser/screenshots/external_wait--wide--light.png new file mode 100644 index 0000000..26040dd Binary files /dev/null and b/proof/ease/latest/browser/screenshots/external_wait--wide--light.png differ diff --git a/proof/ease/latest/browser/screenshots/first_arrival--desktop--dark.json b/proof/ease/latest/browser/screenshots/first_arrival--desktop--dark.json new file mode 100644 index 0000000..f172fb7 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/first_arrival--desktop--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:26:59.267Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 27057, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=first_arrival", + "pngSha256": "a57a3c197de2726f943f7141f54a974ec6d3803b1cf19a58a0d16fd1ebfe3f3a", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "first_arrival", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/latest/browser/screenshots/first_arrival--desktop--dark.png b/proof/ease/latest/browser/screenshots/first_arrival--desktop--dark.png new file mode 100644 index 0000000..14dbc66 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/first_arrival--desktop--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/first_arrival--desktop--light.json b/proof/ease/latest/browser/screenshots/first_arrival--desktop--light.json new file mode 100644 index 0000000..92f5fd3 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/first_arrival--desktop--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:26:36.179Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 3970, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=first_arrival", + "pngSha256": "41b27c1fed98be0a51bebc0eedf8851cf5b144826cc6d9caa0a1029e6ac3cf62", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "first_arrival", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/latest/browser/screenshots/first_arrival--desktop--light.png b/proof/ease/latest/browser/screenshots/first_arrival--desktop--light.png new file mode 100644 index 0000000..ed501ec Binary files /dev/null and b/proof/ease/latest/browser/screenshots/first_arrival--desktop--light.png differ diff --git a/proof/ease/latest/browser/screenshots/first_arrival--mobile-landscape--dark.json b/proof/ease/latest/browser/screenshots/first_arrival--mobile-landscape--dark.json new file mode 100644 index 0000000..55c5803 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/first_arrival--mobile-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:46.874Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 134664, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=first_arrival", + "pngSha256": "cf8837825047e06b18035461c8f1ce428170f9f4fe0168aab8533616a8f55874", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "first_arrival", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/latest/browser/screenshots/first_arrival--mobile-landscape--dark.png b/proof/ease/latest/browser/screenshots/first_arrival--mobile-landscape--dark.png new file mode 100644 index 0000000..833b57b Binary files /dev/null and b/proof/ease/latest/browser/screenshots/first_arrival--mobile-landscape--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/first_arrival--mobile-landscape--light.json b/proof/ease/latest/browser/screenshots/first_arrival--mobile-landscape--light.json new file mode 100644 index 0000000..8c94f2f --- /dev/null +++ b/proof/ease/latest/browser/screenshots/first_arrival--mobile-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:37.358Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 125148, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=first_arrival", + "pngSha256": "5ba09c367a9a263ed026d90f14a4ccf65baefb3ef7477e5f17907fc46512c0f0", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "first_arrival", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/latest/browser/screenshots/first_arrival--mobile-landscape--light.png b/proof/ease/latest/browser/screenshots/first_arrival--mobile-landscape--light.png new file mode 100644 index 0000000..29c50ce Binary files /dev/null and b/proof/ease/latest/browser/screenshots/first_arrival--mobile-landscape--light.png differ diff --git a/proof/ease/latest/browser/screenshots/first_arrival--mobile-portrait--dark.json b/proof/ease/latest/browser/screenshots/first_arrival--mobile-portrait--dark.json new file mode 100644 index 0000000..8bd35f2 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/first_arrival--mobile-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:27.860Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 115650, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=first_arrival", + "pngSha256": "64cb3f851e66cc8632a11d34ad279111d07af55ca21d009778bf5d3a4cf4e92c", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "first_arrival", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/latest/browser/screenshots/first_arrival--mobile-portrait--dark.png b/proof/ease/latest/browser/screenshots/first_arrival--mobile-portrait--dark.png new file mode 100644 index 0000000..0a6c771 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/first_arrival--mobile-portrait--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/first_arrival--mobile-portrait--light.json b/proof/ease/latest/browser/screenshots/first_arrival--mobile-portrait--light.json new file mode 100644 index 0000000..b97bb11 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/first_arrival--mobile-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:18.358Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 106149, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=first_arrival", + "pngSha256": "06aeb36753da9de625432d8e9d2f7e23b54da7b35b350170bf3d0d1297c7af15", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "first_arrival", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/latest/browser/screenshots/first_arrival--mobile-portrait--light.png b/proof/ease/latest/browser/screenshots/first_arrival--mobile-portrait--light.png new file mode 100644 index 0000000..fe23599 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/first_arrival--mobile-portrait--light.png differ diff --git a/proof/ease/latest/browser/screenshots/first_arrival--tablet-landscape--dark.json b/proof/ease/latest/browser/screenshots/first_arrival--tablet-landscape--dark.json new file mode 100644 index 0000000..ac0e09e --- /dev/null +++ b/proof/ease/latest/browser/screenshots/first_arrival--tablet-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:49.636Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 77426, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=first_arrival", + "pngSha256": "9deec21252677430894e0df1698bfb5e22b87a9f48d140a09a6cb2fc62aec278", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "first_arrival", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/latest/browser/screenshots/first_arrival--tablet-landscape--dark.png b/proof/ease/latest/browser/screenshots/first_arrival--tablet-landscape--dark.png new file mode 100644 index 0000000..0be9a92 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/first_arrival--tablet-landscape--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/first_arrival--tablet-landscape--light.json b/proof/ease/latest/browser/screenshots/first_arrival--tablet-landscape--light.json new file mode 100644 index 0000000..7b8d646 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/first_arrival--tablet-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:40.053Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 67843, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=first_arrival", + "pngSha256": "cda6d5d64ae2540fe450720bf590d1e2529a65111385ed5cc1de924f35689f44", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "first_arrival", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/latest/browser/screenshots/first_arrival--tablet-landscape--light.png b/proof/ease/latest/browser/screenshots/first_arrival--tablet-landscape--light.png new file mode 100644 index 0000000..a3a52ad Binary files /dev/null and b/proof/ease/latest/browser/screenshots/first_arrival--tablet-landscape--light.png differ diff --git a/proof/ease/latest/browser/screenshots/first_arrival--tablet-portrait--dark.json b/proof/ease/latest/browser/screenshots/first_arrival--tablet-portrait--dark.json new file mode 100644 index 0000000..985d52a --- /dev/null +++ b/proof/ease/latest/browser/screenshots/first_arrival--tablet-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:08.801Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 96592, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=first_arrival", + "pngSha256": "3993a349ba426e89b4646044c8f43cd37726540077efd835ad0a1867d0e7014a", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "first_arrival", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/latest/browser/screenshots/first_arrival--tablet-portrait--dark.png b/proof/ease/latest/browser/screenshots/first_arrival--tablet-portrait--dark.png new file mode 100644 index 0000000..0043383 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/first_arrival--tablet-portrait--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/first_arrival--tablet-portrait--light.json b/proof/ease/latest/browser/screenshots/first_arrival--tablet-portrait--light.json new file mode 100644 index 0000000..27ab22e --- /dev/null +++ b/proof/ease/latest/browser/screenshots/first_arrival--tablet-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:59.235Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 87026, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=first_arrival", + "pngSha256": "039a9c8ffd412c9ded55da5359696187483549f23edf14643fdf818d8f5caa4e", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "first_arrival", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/latest/browser/screenshots/first_arrival--tablet-portrait--light.png b/proof/ease/latest/browser/screenshots/first_arrival--tablet-portrait--light.png new file mode 100644 index 0000000..e343a59 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/first_arrival--tablet-portrait--light.png differ diff --git a/proof/ease/latest/browser/screenshots/first_arrival--wide--dark.json b/proof/ease/latest/browser/screenshots/first_arrival--wide--dark.json new file mode 100644 index 0000000..022713b --- /dev/null +++ b/proof/ease/latest/browser/screenshots/first_arrival--wide--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:29.900Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 57690, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=first_arrival", + "pngSha256": "e967f444bdcff348ced0855448454acf6bb9301c0eb5c600910de6207a7b7dfa", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "first_arrival", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/latest/browser/screenshots/first_arrival--wide--dark.png b/proof/ease/latest/browser/screenshots/first_arrival--wide--dark.png new file mode 100644 index 0000000..a7f0cb4 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/first_arrival--wide--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/first_arrival--wide--light.json b/proof/ease/latest/browser/screenshots/first_arrival--wide--light.json new file mode 100644 index 0000000..e98e97c --- /dev/null +++ b/proof/ease/latest/browser/screenshots/first_arrival--wide--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:09.235Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 37026, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=first_arrival", + "pngSha256": "3de1c119f175c6b30381fc22fd3f9b85807d3cc88a266669648099283e5d5f90", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "first_arrival", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/latest/browser/screenshots/first_arrival--wide--light.png b/proof/ease/latest/browser/screenshots/first_arrival--wide--light.png new file mode 100644 index 0000000..f0cf90b Binary files /dev/null and b/proof/ease/latest/browser/screenshots/first_arrival--wide--light.png differ diff --git a/proof/ease/latest/browser/screenshots/input--desktop--dark.json b/proof/ease/latest/browser/screenshots/input--desktop--dark.json new file mode 100644 index 0000000..ba6cdb7 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/input--desktop--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:00.419Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 28209, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=input", + "pngSha256": "b92eea59ad4126b66be93a505c2f041b1a5f59e92501f8c3fac30a0a7308ef37", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "input", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/latest/browser/screenshots/input--desktop--dark.png b/proof/ease/latest/browser/screenshots/input--desktop--dark.png new file mode 100644 index 0000000..a31f150 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/input--desktop--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/input--desktop--light.json b/proof/ease/latest/browser/screenshots/input--desktop--light.json new file mode 100644 index 0000000..3ce3e38 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/input--desktop--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:26:39.318Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 7109, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=input", + "pngSha256": "b83128bb17f5a55763edae4b6d573a333b37ca38c6fd7f6dcfd2d78c42b14d4b", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "input", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/latest/browser/screenshots/input--desktop--light.png b/proof/ease/latest/browser/screenshots/input--desktop--light.png new file mode 100644 index 0000000..a57ef56 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/input--desktop--light.png differ diff --git a/proof/ease/latest/browser/screenshots/input--mobile-landscape--dark.json b/proof/ease/latest/browser/screenshots/input--mobile-landscape--dark.json new file mode 100644 index 0000000..0c3f452 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/input--mobile-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:47.991Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 135781, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=input", + "pngSha256": "cf8837825047e06b18035461c8f1ce428170f9f4fe0168aab8533616a8f55874", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "input", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/latest/browser/screenshots/input--mobile-landscape--dark.png b/proof/ease/latest/browser/screenshots/input--mobile-landscape--dark.png new file mode 100644 index 0000000..833b57b Binary files /dev/null and b/proof/ease/latest/browser/screenshots/input--mobile-landscape--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/input--mobile-landscape--light.json b/proof/ease/latest/browser/screenshots/input--mobile-landscape--light.json new file mode 100644 index 0000000..ac9d848 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/input--mobile-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:38.459Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 126249, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=input", + "pngSha256": "5ba09c367a9a263ed026d90f14a4ccf65baefb3ef7477e5f17907fc46512c0f0", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "input", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/latest/browser/screenshots/input--mobile-landscape--light.png b/proof/ease/latest/browser/screenshots/input--mobile-landscape--light.png new file mode 100644 index 0000000..29c50ce Binary files /dev/null and b/proof/ease/latest/browser/screenshots/input--mobile-landscape--light.png differ diff --git a/proof/ease/latest/browser/screenshots/input--mobile-portrait--dark.json b/proof/ease/latest/browser/screenshots/input--mobile-portrait--dark.json new file mode 100644 index 0000000..2ad8149 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/input--mobile-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:28.976Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 116767, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=input", + "pngSha256": "0ed732340cb7411d068b7a0445858accb1cc7e02da4b96ed5f4f28495bf5c9f3", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "input", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/latest/browser/screenshots/input--mobile-portrait--dark.png b/proof/ease/latest/browser/screenshots/input--mobile-portrait--dark.png new file mode 100644 index 0000000..b214939 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/input--mobile-portrait--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/input--mobile-portrait--light.json b/proof/ease/latest/browser/screenshots/input--mobile-portrait--light.json new file mode 100644 index 0000000..67ef245 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/input--mobile-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:19.459Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 107249, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=input", + "pngSha256": "105124b708991a2d20c0650f055c7bcd226118b6cb699f33d6ea5ccd7f9b187c", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "input", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/latest/browser/screenshots/input--mobile-portrait--light.png b/proof/ease/latest/browser/screenshots/input--mobile-portrait--light.png new file mode 100644 index 0000000..e8a002a Binary files /dev/null and b/proof/ease/latest/browser/screenshots/input--mobile-portrait--light.png differ diff --git a/proof/ease/latest/browser/screenshots/input--tablet-landscape--dark.json b/proof/ease/latest/browser/screenshots/input--tablet-landscape--dark.json new file mode 100644 index 0000000..ae74ef8 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/input--tablet-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:50.752Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 78543, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=input", + "pngSha256": "3465ebc56542b64ee7bac812862001bf3197ef4ab6eb2bc46fe34eeb7c49634f", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "input", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/latest/browser/screenshots/input--tablet-landscape--dark.png b/proof/ease/latest/browser/screenshots/input--tablet-landscape--dark.png new file mode 100644 index 0000000..449490c Binary files /dev/null and b/proof/ease/latest/browser/screenshots/input--tablet-landscape--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/input--tablet-landscape--light.json b/proof/ease/latest/browser/screenshots/input--tablet-landscape--light.json new file mode 100644 index 0000000..8e2d867 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/input--tablet-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:41.185Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 68976, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=input", + "pngSha256": "b0d40d0833208426b20ce9b4998f82f6de4288fbc65ce5fa7be9c068666191a4", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "input", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/latest/browser/screenshots/input--tablet-landscape--light.png b/proof/ease/latest/browser/screenshots/input--tablet-landscape--light.png new file mode 100644 index 0000000..473379c Binary files /dev/null and b/proof/ease/latest/browser/screenshots/input--tablet-landscape--light.png differ diff --git a/proof/ease/latest/browser/screenshots/input--tablet-portrait--dark.json b/proof/ease/latest/browser/screenshots/input--tablet-portrait--dark.json new file mode 100644 index 0000000..109900f --- /dev/null +++ b/proof/ease/latest/browser/screenshots/input--tablet-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:09.938Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 97728, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=input", + "pngSha256": "00de66d4c832feb03791b1a78c9f898b308f0ac117427ac5083dd0918d329f30", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "input", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/latest/browser/screenshots/input--tablet-portrait--dark.png b/proof/ease/latest/browser/screenshots/input--tablet-portrait--dark.png new file mode 100644 index 0000000..6118cd8 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/input--tablet-portrait--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/input--tablet-portrait--light.json b/proof/ease/latest/browser/screenshots/input--tablet-portrait--light.json new file mode 100644 index 0000000..7e084a8 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/input--tablet-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:00.370Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 88160, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=input", + "pngSha256": "de4aeb1b823198a02cb2f064c8ad725906c2a6abc3d235b306c6657d065470e8", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "input", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/latest/browser/screenshots/input--tablet-portrait--light.png b/proof/ease/latest/browser/screenshots/input--tablet-portrait--light.png new file mode 100644 index 0000000..985c402 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/input--tablet-portrait--light.png differ diff --git a/proof/ease/latest/browser/screenshots/input--wide--dark.json b/proof/ease/latest/browser/screenshots/input--wide--dark.json new file mode 100644 index 0000000..a1ca696 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/input--wide--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:31.207Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 58998, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=input", + "pngSha256": "f9da38328d057bfe72062aaba7b071c0728afd202c78fc1a0fdfc606356e0aec", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "input", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/latest/browser/screenshots/input--wide--dark.png b/proof/ease/latest/browser/screenshots/input--wide--dark.png new file mode 100644 index 0000000..1b0e5f7 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/input--wide--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/input--wide--light.json b/proof/ease/latest/browser/screenshots/input--wide--light.json new file mode 100644 index 0000000..7ec1ebd --- /dev/null +++ b/proof/ease/latest/browser/screenshots/input--wide--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:10.433Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 38223, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=input", + "pngSha256": "f63bda5c27b460ef82088039a9e5d3e098776804ef3a3ac26555de67a6b0a81d", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "input", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/latest/browser/screenshots/input--wide--light.png b/proof/ease/latest/browser/screenshots/input--wide--light.png new file mode 100644 index 0000000..9104eb9 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/input--wide--light.png differ diff --git a/proof/ease/latest/browser/screenshots/orientation--desktop--dark.json b/proof/ease/latest/browser/screenshots/orientation--desktop--dark.json new file mode 100644 index 0000000..e211cf5 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/orientation--desktop--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:26:59.833Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 27623, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=orientation", + "pngSha256": "b66c1507eb0357de6239383543931ff8ef725ae35cef356e2f0c4429c7c67e32", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "orientation", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/latest/browser/screenshots/orientation--desktop--dark.png b/proof/ease/latest/browser/screenshots/orientation--desktop--dark.png new file mode 100644 index 0000000..714178c Binary files /dev/null and b/proof/ease/latest/browser/screenshots/orientation--desktop--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/orientation--desktop--light.json b/proof/ease/latest/browser/screenshots/orientation--desktop--light.json new file mode 100644 index 0000000..627398f --- /dev/null +++ b/proof/ease/latest/browser/screenshots/orientation--desktop--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:26:37.609Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 5399, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=orientation", + "pngSha256": "9a5e78314cb69e20d63993a8f84ca2402de1fd4357664bb082adbe7f0427a7c9", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "orientation", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/latest/browser/screenshots/orientation--desktop--light.png b/proof/ease/latest/browser/screenshots/orientation--desktop--light.png new file mode 100644 index 0000000..de1000e Binary files /dev/null and b/proof/ease/latest/browser/screenshots/orientation--desktop--light.png differ diff --git a/proof/ease/latest/browser/screenshots/orientation--mobile-landscape--dark.json b/proof/ease/latest/browser/screenshots/orientation--mobile-landscape--dark.json new file mode 100644 index 0000000..80061ab --- /dev/null +++ b/proof/ease/latest/browser/screenshots/orientation--mobile-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:47.425Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 135215, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=orientation", + "pngSha256": "cf8837825047e06b18035461c8f1ce428170f9f4fe0168aab8533616a8f55874", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "orientation", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/latest/browser/screenshots/orientation--mobile-landscape--dark.png b/proof/ease/latest/browser/screenshots/orientation--mobile-landscape--dark.png new file mode 100644 index 0000000..833b57b Binary files /dev/null and b/proof/ease/latest/browser/screenshots/orientation--mobile-landscape--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/orientation--mobile-landscape--light.json b/proof/ease/latest/browser/screenshots/orientation--mobile-landscape--light.json new file mode 100644 index 0000000..003c9c8 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/orientation--mobile-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:37.909Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 125699, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=orientation", + "pngSha256": "5ba09c367a9a263ed026d90f14a4ccf65baefb3ef7477e5f17907fc46512c0f0", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "orientation", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/latest/browser/screenshots/orientation--mobile-landscape--light.png b/proof/ease/latest/browser/screenshots/orientation--mobile-landscape--light.png new file mode 100644 index 0000000..29c50ce Binary files /dev/null and b/proof/ease/latest/browser/screenshots/orientation--mobile-landscape--light.png differ diff --git a/proof/ease/latest/browser/screenshots/orientation--mobile-portrait--dark.json b/proof/ease/latest/browser/screenshots/orientation--mobile-portrait--dark.json new file mode 100644 index 0000000..78608a7 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/orientation--mobile-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:28.426Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 116216, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=orientation", + "pngSha256": "1416a658a1ae7729e57e11f4f383776144e6f4fb114041ebb6de11ffc58ca5bf", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "orientation", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/latest/browser/screenshots/orientation--mobile-portrait--dark.png b/proof/ease/latest/browser/screenshots/orientation--mobile-portrait--dark.png new file mode 100644 index 0000000..06dd198 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/orientation--mobile-portrait--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/orientation--mobile-portrait--light.json b/proof/ease/latest/browser/screenshots/orientation--mobile-portrait--light.json new file mode 100644 index 0000000..5be9fe1 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/orientation--mobile-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:18.908Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 106698, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=orientation", + "pngSha256": "83c8a75dbb8521cc5ba766be8d48e3ef6f41a4d691aa4f4804b9e83d625074b2", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "orientation", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/latest/browser/screenshots/orientation--mobile-portrait--light.png b/proof/ease/latest/browser/screenshots/orientation--mobile-portrait--light.png new file mode 100644 index 0000000..f421f85 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/orientation--mobile-portrait--light.png differ diff --git a/proof/ease/latest/browser/screenshots/orientation--tablet-landscape--dark.json b/proof/ease/latest/browser/screenshots/orientation--tablet-landscape--dark.json new file mode 100644 index 0000000..f5741fa --- /dev/null +++ b/proof/ease/latest/browser/screenshots/orientation--tablet-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:50.186Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 77976, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=orientation", + "pngSha256": "c64a1fa435da9c786eac01feead9f08c7f81273cf42664a021bfaa5173771c84", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "orientation", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/latest/browser/screenshots/orientation--tablet-landscape--dark.png b/proof/ease/latest/browser/screenshots/orientation--tablet-landscape--dark.png new file mode 100644 index 0000000..c7e177f Binary files /dev/null and b/proof/ease/latest/browser/screenshots/orientation--tablet-landscape--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/orientation--tablet-landscape--light.json b/proof/ease/latest/browser/screenshots/orientation--tablet-landscape--light.json new file mode 100644 index 0000000..d48ca47 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/orientation--tablet-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:40.620Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 68411, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=orientation", + "pngSha256": "ae495e396bcb9eb5c6378d91035778591bac6917fdb110e8b64713d7f882f57a", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "orientation", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/latest/browser/screenshots/orientation--tablet-landscape--light.png b/proof/ease/latest/browser/screenshots/orientation--tablet-landscape--light.png new file mode 100644 index 0000000..c98c706 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/orientation--tablet-landscape--light.png differ diff --git a/proof/ease/latest/browser/screenshots/orientation--tablet-portrait--dark.json b/proof/ease/latest/browser/screenshots/orientation--tablet-portrait--dark.json new file mode 100644 index 0000000..0a186b7 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/orientation--tablet-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:09.368Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 97159, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=orientation", + "pngSha256": "8ecb265459e0530d0fce80022e9aab816725a3ca1300e8490dae493a2e56c659", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "orientation", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/latest/browser/screenshots/orientation--tablet-portrait--dark.png b/proof/ease/latest/browser/screenshots/orientation--tablet-portrait--dark.png new file mode 100644 index 0000000..49147f5 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/orientation--tablet-portrait--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/orientation--tablet-portrait--light.json b/proof/ease/latest/browser/screenshots/orientation--tablet-portrait--light.json new file mode 100644 index 0000000..b7850f9 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/orientation--tablet-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:59.804Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 87594, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=orientation", + "pngSha256": "b027d7674551f0642fa5a2ba75b4add6e0e04ac9d981caffb87f16d074b5c4da", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "orientation", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/latest/browser/screenshots/orientation--tablet-portrait--light.png b/proof/ease/latest/browser/screenshots/orientation--tablet-portrait--light.png new file mode 100644 index 0000000..48f3291 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/orientation--tablet-portrait--light.png differ diff --git a/proof/ease/latest/browser/screenshots/orientation--wide--dark.json b/proof/ease/latest/browser/screenshots/orientation--wide--dark.json new file mode 100644 index 0000000..54a6e64 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/orientation--wide--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:30.559Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 58349, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=orientation", + "pngSha256": "ed37c21fad900e0c68f4d6e7cc40cd8e5670c5363bced2e55e2ade336d0ff8e5", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "orientation", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/latest/browser/screenshots/orientation--wide--dark.png b/proof/ease/latest/browser/screenshots/orientation--wide--dark.png new file mode 100644 index 0000000..6a737e0 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/orientation--wide--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/orientation--wide--light.json b/proof/ease/latest/browser/screenshots/orientation--wide--light.json new file mode 100644 index 0000000..bc0b302 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/orientation--wide--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:09.837Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 37627, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=orientation", + "pngSha256": "a08b0807c6784d9bcbf850263843e4883a80da887fc293dd4212d410fc626642", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "orientation", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/latest/browser/screenshots/orientation--wide--light.png b/proof/ease/latest/browser/screenshots/orientation--wide--light.png new file mode 100644 index 0000000..d2fc947 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/orientation--wide--light.png differ diff --git a/proof/ease/latest/browser/screenshots/partial_result--desktop--dark.json b/proof/ease/latest/browser/screenshots/partial_result--desktop--dark.json new file mode 100644 index 0000000..dbf55ab --- /dev/null +++ b/proof/ease/latest/browser/screenshots/partial_result--desktop--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:02.203Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 29993, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=partial_result", + "pngSha256": "7fe1598bc3d7aa34ff115c8a37606c3acd20c951196a279ae3cdab1c6a4d6284", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "partial_result", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/latest/browser/screenshots/partial_result--desktop--dark.png b/proof/ease/latest/browser/screenshots/partial_result--desktop--dark.png new file mode 100644 index 0000000..96e5c99 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/partial_result--desktop--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/partial_result--desktop--light.json b/proof/ease/latest/browser/screenshots/partial_result--desktop--light.json new file mode 100644 index 0000000..4a41209 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/partial_result--desktop--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:26:44.010Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 11801, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=partial_result", + "pngSha256": "bc56d7518de84fd388a24105d9745dd08dafb46ad7a310531dcf473435b19690", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "partial_result", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/latest/browser/screenshots/partial_result--desktop--light.png b/proof/ease/latest/browser/screenshots/partial_result--desktop--light.png new file mode 100644 index 0000000..1013e6c Binary files /dev/null and b/proof/ease/latest/browser/screenshots/partial_result--desktop--light.png differ diff --git a/proof/ease/latest/browser/screenshots/partial_result--mobile-landscape--dark.json b/proof/ease/latest/browser/screenshots/partial_result--mobile-landscape--dark.json new file mode 100644 index 0000000..68db8f8 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/partial_result--mobile-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:49.677Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 137466, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=partial_result", + "pngSha256": "4462e909f0bc28437bb21a470f330587c90d61cbd33a21c4f68c577306a000b6", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "partial_result", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/latest/browser/screenshots/partial_result--mobile-landscape--dark.png b/proof/ease/latest/browser/screenshots/partial_result--mobile-landscape--dark.png new file mode 100644 index 0000000..596969d Binary files /dev/null and b/proof/ease/latest/browser/screenshots/partial_result--mobile-landscape--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/partial_result--mobile-landscape--light.json b/proof/ease/latest/browser/screenshots/partial_result--mobile-landscape--light.json new file mode 100644 index 0000000..1a39a02 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/partial_result--mobile-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:40.143Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 127933, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=partial_result", + "pngSha256": "7311400a07613abafb2c3ab11a1a51eec8f01e0699a428de092425f7a78448e8", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "partial_result", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/latest/browser/screenshots/partial_result--mobile-landscape--light.png b/proof/ease/latest/browser/screenshots/partial_result--mobile-landscape--light.png new file mode 100644 index 0000000..8f7b3f3 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/partial_result--mobile-landscape--light.png differ diff --git a/proof/ease/latest/browser/screenshots/partial_result--mobile-portrait--dark.json b/proof/ease/latest/browser/screenshots/partial_result--mobile-portrait--dark.json new file mode 100644 index 0000000..86142e5 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/partial_result--mobile-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:30.661Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 118451, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=partial_result", + "pngSha256": "63ca2fa009666d191d1d9fbe1988febd25e77ef2b4e1c6e5969a50213bff71ea", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "partial_result", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/latest/browser/screenshots/partial_result--mobile-portrait--dark.png b/proof/ease/latest/browser/screenshots/partial_result--mobile-portrait--dark.png new file mode 100644 index 0000000..b411e85 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/partial_result--mobile-portrait--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/partial_result--mobile-portrait--light.json b/proof/ease/latest/browser/screenshots/partial_result--mobile-portrait--light.json new file mode 100644 index 0000000..73ab6c0 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/partial_result--mobile-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:21.143Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 108933, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=partial_result", + "pngSha256": "634670b5b38cd795f83d125e78dcf652872f8a943fd326cc2deae8055c934d9a", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "partial_result", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/latest/browser/screenshots/partial_result--mobile-portrait--light.png b/proof/ease/latest/browser/screenshots/partial_result--mobile-portrait--light.png new file mode 100644 index 0000000..05f5efc Binary files /dev/null and b/proof/ease/latest/browser/screenshots/partial_result--mobile-portrait--light.png differ diff --git a/proof/ease/latest/browser/screenshots/partial_result--tablet-landscape--dark.json b/proof/ease/latest/browser/screenshots/partial_result--tablet-landscape--dark.json new file mode 100644 index 0000000..ef6ae8e --- /dev/null +++ b/proof/ease/latest/browser/screenshots/partial_result--tablet-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:52.468Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 80259, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=partial_result", + "pngSha256": "689ec45c6fcab70ada5969d7bf0c84fe85b0289f54ae25c9c46f478bc99aa1f1", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "partial_result", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/latest/browser/screenshots/partial_result--tablet-landscape--dark.png b/proof/ease/latest/browser/screenshots/partial_result--tablet-landscape--dark.png new file mode 100644 index 0000000..836de23 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/partial_result--tablet-landscape--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/partial_result--tablet-landscape--light.json b/proof/ease/latest/browser/screenshots/partial_result--tablet-landscape--light.json new file mode 100644 index 0000000..00de58c --- /dev/null +++ b/proof/ease/latest/browser/screenshots/partial_result--tablet-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:42.902Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 70692, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=partial_result", + "pngSha256": "45e9c846ceced75e36a03a9063737437741665ec79dd170a5de919f8087b1cd9", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "partial_result", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/latest/browser/screenshots/partial_result--tablet-landscape--light.png b/proof/ease/latest/browser/screenshots/partial_result--tablet-landscape--light.png new file mode 100644 index 0000000..6fe2d42 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/partial_result--tablet-landscape--light.png differ diff --git a/proof/ease/latest/browser/screenshots/partial_result--tablet-portrait--dark.json b/proof/ease/latest/browser/screenshots/partial_result--tablet-portrait--dark.json new file mode 100644 index 0000000..fea9f82 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/partial_result--tablet-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:11.669Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 99459, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=partial_result", + "pngSha256": "44ca11398a3c3441f65e1b35234bb7065740656489fbd24c72443147588cf5b3", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "partial_result", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/latest/browser/screenshots/partial_result--tablet-portrait--dark.png b/proof/ease/latest/browser/screenshots/partial_result--tablet-portrait--dark.png new file mode 100644 index 0000000..608dc95 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/partial_result--tablet-portrait--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/partial_result--tablet-portrait--light.json b/proof/ease/latest/browser/screenshots/partial_result--tablet-portrait--light.json new file mode 100644 index 0000000..a45408c --- /dev/null +++ b/proof/ease/latest/browser/screenshots/partial_result--tablet-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:02.086Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 89877, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=partial_result", + "pngSha256": "c365050b7e46e9dc7f7cb465fdf7a7ba00a78503602a3ef086dd40519a5e73ad", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "partial_result", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/latest/browser/screenshots/partial_result--tablet-portrait--light.png b/proof/ease/latest/browser/screenshots/partial_result--tablet-portrait--light.png new file mode 100644 index 0000000..5e6a719 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/partial_result--tablet-portrait--light.png differ diff --git a/proof/ease/latest/browser/screenshots/partial_result--wide--dark.json b/proof/ease/latest/browser/screenshots/partial_result--wide--dark.json new file mode 100644 index 0000000..2887678 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/partial_result--wide--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:33.012Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 60802, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=partial_result", + "pngSha256": "17e82dd3dccb4905ab4cf0147b6a4d35fbb2aa66b69f996b0d5409c96e28604a", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "partial_result", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/latest/browser/screenshots/partial_result--wide--dark.png b/proof/ease/latest/browser/screenshots/partial_result--wide--dark.png new file mode 100644 index 0000000..649c0ba Binary files /dev/null and b/proof/ease/latest/browser/screenshots/partial_result--wide--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/partial_result--wide--light.json b/proof/ease/latest/browser/screenshots/partial_result--wide--light.json new file mode 100644 index 0000000..ea32d4c --- /dev/null +++ b/proof/ease/latest/browser/screenshots/partial_result--wide--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:12.248Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 40039, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=partial_result", + "pngSha256": "8c3895dede848e0a19c6f32438c1c9583a7fb3820788be15bc35a08448bc6068", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "partial_result", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/latest/browser/screenshots/partial_result--wide--light.png b/proof/ease/latest/browser/screenshots/partial_result--wide--light.png new file mode 100644 index 0000000..dbd0660 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/partial_result--wide--light.png differ diff --git a/proof/ease/latest/browser/screenshots/proposal_pending--desktop--dark.json b/proof/ease/latest/browser/screenshots/proposal_pending--desktop--dark.json new file mode 100644 index 0000000..aee87da --- /dev/null +++ b/proof/ease/latest/browser/screenshots/proposal_pending--desktop--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:03.373Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 31163, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=proposal_pending", + "pngSha256": "885061f0cb3f21682b765941392ca4eff6152eb6dd4a62c5591e3262a6b26f9c", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "proposal_pending", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/latest/browser/screenshots/proposal_pending--desktop--dark.png b/proof/ease/latest/browser/screenshots/proposal_pending--desktop--dark.png new file mode 100644 index 0000000..f748783 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/proposal_pending--desktop--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/proposal_pending--desktop--light.json b/proof/ease/latest/browser/screenshots/proposal_pending--desktop--light.json new file mode 100644 index 0000000..401b8ee --- /dev/null +++ b/proof/ease/latest/browser/screenshots/proposal_pending--desktop--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:26:46.407Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 14197, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=proposal_pending", + "pngSha256": "fcfe2f205e143c39529039db367db3e47a76a85b27a5917ba2fa649f7c269914", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "proposal_pending", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/latest/browser/screenshots/proposal_pending--desktop--light.png b/proof/ease/latest/browser/screenshots/proposal_pending--desktop--light.png new file mode 100644 index 0000000..4acbc0c Binary files /dev/null and b/proof/ease/latest/browser/screenshots/proposal_pending--desktop--light.png differ diff --git a/proof/ease/latest/browser/screenshots/proposal_pending--mobile-landscape--dark.json b/proof/ease/latest/browser/screenshots/proposal_pending--mobile-landscape--dark.json new file mode 100644 index 0000000..d76f89f --- /dev/null +++ b/proof/ease/latest/browser/screenshots/proposal_pending--mobile-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:50.792Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 138582, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=proposal_pending", + "pngSha256": "49034360bc184960c219e1bed559c1edc3973250837f85d299971e59f645c846", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "proposal_pending", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/latest/browser/screenshots/proposal_pending--mobile-landscape--dark.png b/proof/ease/latest/browser/screenshots/proposal_pending--mobile-landscape--dark.png new file mode 100644 index 0000000..4ebe044 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/proposal_pending--mobile-landscape--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/proposal_pending--mobile-landscape--light.json b/proof/ease/latest/browser/screenshots/proposal_pending--mobile-landscape--light.json new file mode 100644 index 0000000..20c74f2 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/proposal_pending--mobile-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:41.248Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 129038, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=proposal_pending", + "pngSha256": "b5a725ff429d63398d151fcbaadb01e4de68e36ee359d2df60c51ef6da9e57a2", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "proposal_pending", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/latest/browser/screenshots/proposal_pending--mobile-landscape--light.png b/proof/ease/latest/browser/screenshots/proposal_pending--mobile-landscape--light.png new file mode 100644 index 0000000..fce92ae Binary files /dev/null and b/proof/ease/latest/browser/screenshots/proposal_pending--mobile-landscape--light.png differ diff --git a/proof/ease/latest/browser/screenshots/proposal_pending--mobile-portrait--dark.json b/proof/ease/latest/browser/screenshots/proposal_pending--mobile-portrait--dark.json new file mode 100644 index 0000000..843589f --- /dev/null +++ b/proof/ease/latest/browser/screenshots/proposal_pending--mobile-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:31.742Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 119532, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=proposal_pending", + "pngSha256": "b8095014aa3a980c701960dc828db3c5aeae0ad9ea2f56268c42d306fee688b3", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "proposal_pending", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/latest/browser/screenshots/proposal_pending--mobile-portrait--dark.png b/proof/ease/latest/browser/screenshots/proposal_pending--mobile-portrait--dark.png new file mode 100644 index 0000000..045868f Binary files /dev/null and b/proof/ease/latest/browser/screenshots/proposal_pending--mobile-portrait--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/proposal_pending--mobile-portrait--light.json b/proof/ease/latest/browser/screenshots/proposal_pending--mobile-portrait--light.json new file mode 100644 index 0000000..c11da3f --- /dev/null +++ b/proof/ease/latest/browser/screenshots/proposal_pending--mobile-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:22.242Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 110033, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=proposal_pending", + "pngSha256": "20594ee7577f98359d72e8a3ddcd7c924cce956a04b7b9ce82fcca682374f2c3", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "proposal_pending", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/latest/browser/screenshots/proposal_pending--mobile-portrait--light.png b/proof/ease/latest/browser/screenshots/proposal_pending--mobile-portrait--light.png new file mode 100644 index 0000000..ea9ffa5 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/proposal_pending--mobile-portrait--light.png differ diff --git a/proof/ease/latest/browser/screenshots/proposal_pending--tablet-landscape--dark.json b/proof/ease/latest/browser/screenshots/proposal_pending--tablet-landscape--dark.json new file mode 100644 index 0000000..ea4430c --- /dev/null +++ b/proof/ease/latest/browser/screenshots/proposal_pending--tablet-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:53.586Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 81376, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=proposal_pending", + "pngSha256": "eeac98cd587fe710bc483d46dcb9fe48e183c223c2c3cebb561a791093b15cc7", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "proposal_pending", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/latest/browser/screenshots/proposal_pending--tablet-landscape--dark.png b/proof/ease/latest/browser/screenshots/proposal_pending--tablet-landscape--dark.png new file mode 100644 index 0000000..28ba364 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/proposal_pending--tablet-landscape--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/proposal_pending--tablet-landscape--light.json b/proof/ease/latest/browser/screenshots/proposal_pending--tablet-landscape--light.json new file mode 100644 index 0000000..a6001a7 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/proposal_pending--tablet-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:44.035Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 71826, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=proposal_pending", + "pngSha256": "44f4b9a1bdd0356c85c0161134fd89782926c8fdf8275fcfaf8ec734a7a464d1", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "proposal_pending", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/latest/browser/screenshots/proposal_pending--tablet-landscape--light.png b/proof/ease/latest/browser/screenshots/proposal_pending--tablet-landscape--light.png new file mode 100644 index 0000000..4809ab2 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/proposal_pending--tablet-landscape--light.png differ diff --git a/proof/ease/latest/browser/screenshots/proposal_pending--tablet-portrait--dark.json b/proof/ease/latest/browser/screenshots/proposal_pending--tablet-portrait--dark.json new file mode 100644 index 0000000..fbf5440 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/proposal_pending--tablet-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:12.785Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 100576, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=proposal_pending", + "pngSha256": "da751b86a2835a0c4634a4b0989f7b096c4071be8b9d73b23e40f1a84cfd6efc", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "proposal_pending", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/latest/browser/screenshots/proposal_pending--tablet-portrait--dark.png b/proof/ease/latest/browser/screenshots/proposal_pending--tablet-portrait--dark.png new file mode 100644 index 0000000..c1a680d Binary files /dev/null and b/proof/ease/latest/browser/screenshots/proposal_pending--tablet-portrait--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/proposal_pending--tablet-portrait--light.json b/proof/ease/latest/browser/screenshots/proposal_pending--tablet-portrait--light.json new file mode 100644 index 0000000..db25bc7 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/proposal_pending--tablet-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:03.201Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 90992, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=proposal_pending", + "pngSha256": "ac1710692ab9a41581a284f3e897da654e77d992a3c95f1f6334b7dd166a0cdc", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "proposal_pending", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/latest/browser/screenshots/proposal_pending--tablet-portrait--light.png b/proof/ease/latest/browser/screenshots/proposal_pending--tablet-portrait--light.png new file mode 100644 index 0000000..57460d7 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/proposal_pending--tablet-portrait--light.png differ diff --git a/proof/ease/latest/browser/screenshots/proposal_pending--wide--dark.json b/proof/ease/latest/browser/screenshots/proposal_pending--wide--dark.json new file mode 100644 index 0000000..50f6154 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/proposal_pending--wide--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:34.186Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 61976, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=proposal_pending", + "pngSha256": "9caf980541cf87aee67952081658ed4134e1058580c920b1b93e39bcd14c61af", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "proposal_pending", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/latest/browser/screenshots/proposal_pending--wide--dark.png b/proof/ease/latest/browser/screenshots/proposal_pending--wide--dark.png new file mode 100644 index 0000000..c4373d5 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/proposal_pending--wide--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/proposal_pending--wide--light.json b/proof/ease/latest/browser/screenshots/proposal_pending--wide--light.json new file mode 100644 index 0000000..e95611f --- /dev/null +++ b/proof/ease/latest/browser/screenshots/proposal_pending--wide--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:13.439Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 41229, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=proposal_pending", + "pngSha256": "20261b18a983cdb0bb59471bfe54ca8393728f613d68f371b624cf59378755a8", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "proposal_pending", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/latest/browser/screenshots/proposal_pending--wide--light.png b/proof/ease/latest/browser/screenshots/proposal_pending--wide--light.png new file mode 100644 index 0000000..280e65c Binary files /dev/null and b/proof/ease/latest/browser/screenshots/proposal_pending--wide--light.png differ diff --git a/proof/ease/latest/browser/screenshots/receipt_inspection--desktop--dark.json b/proof/ease/latest/browser/screenshots/receipt_inspection--desktop--dark.json new file mode 100644 index 0000000..d987a1e --- /dev/null +++ b/proof/ease/latest/browser/screenshots/receipt_inspection--desktop--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:07.982Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 35773, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=receipt_inspection", + "pngSha256": "727fac9157c7dc5aaa50bcd2f247885f4247992f642f808f76b5d5ef27f0d8aa", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "receipt_inspection", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/latest/browser/screenshots/receipt_inspection--desktop--dark.png b/proof/ease/latest/browser/screenshots/receipt_inspection--desktop--dark.png new file mode 100644 index 0000000..2274d87 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/receipt_inspection--desktop--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/receipt_inspection--desktop--light.json b/proof/ease/latest/browser/screenshots/receipt_inspection--desktop--light.json new file mode 100644 index 0000000..cfb84eb --- /dev/null +++ b/proof/ease/latest/browser/screenshots/receipt_inspection--desktop--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:26:55.141Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 22931, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=receipt_inspection", + "pngSha256": "930b571cf7b9d6bfcb680f8363e5cbd0a573d737fd7213cdb07853979d1d07a1", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "receipt_inspection", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/latest/browser/screenshots/receipt_inspection--desktop--light.png b/proof/ease/latest/browser/screenshots/receipt_inspection--desktop--light.png new file mode 100644 index 0000000..5e806b0 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/receipt_inspection--desktop--light.png differ diff --git a/proof/ease/latest/browser/screenshots/receipt_inspection--mobile-landscape--dark.json b/proof/ease/latest/browser/screenshots/receipt_inspection--mobile-landscape--dark.json new file mode 100644 index 0000000..a9b3966 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/receipt_inspection--mobile-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:55.207Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 142997, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=receipt_inspection", + "pngSha256": "9de3f08c5fbae30615667095663c99bf93a7b1081ba747579be3b15e88683a81", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "receipt_inspection", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/latest/browser/screenshots/receipt_inspection--mobile-landscape--dark.png b/proof/ease/latest/browser/screenshots/receipt_inspection--mobile-landscape--dark.png new file mode 100644 index 0000000..910dc7d Binary files /dev/null and b/proof/ease/latest/browser/screenshots/receipt_inspection--mobile-landscape--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/receipt_inspection--mobile-landscape--light.json b/proof/ease/latest/browser/screenshots/receipt_inspection--mobile-landscape--light.json new file mode 100644 index 0000000..da7baf7 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/receipt_inspection--mobile-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:45.657Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 133447, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=receipt_inspection", + "pngSha256": "c5cd5e0716befd08488b1649133397f95910a59b00a7f3e5cc072a922171c4d9", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "receipt_inspection", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/latest/browser/screenshots/receipt_inspection--mobile-landscape--light.png b/proof/ease/latest/browser/screenshots/receipt_inspection--mobile-landscape--light.png new file mode 100644 index 0000000..28fabaf Binary files /dev/null and b/proof/ease/latest/browser/screenshots/receipt_inspection--mobile-landscape--light.png differ diff --git a/proof/ease/latest/browser/screenshots/receipt_inspection--mobile-portrait--dark.json b/proof/ease/latest/browser/screenshots/receipt_inspection--mobile-portrait--dark.json new file mode 100644 index 0000000..de39825 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/receipt_inspection--mobile-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:36.141Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 123930, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=receipt_inspection", + "pngSha256": "77ed38d44b4017526cb7ae55081800b622a9ba56a9b0f37499c238e785e91e76", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "receipt_inspection", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/latest/browser/screenshots/receipt_inspection--mobile-portrait--dark.png b/proof/ease/latest/browser/screenshots/receipt_inspection--mobile-portrait--dark.png new file mode 100644 index 0000000..b9fd576 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/receipt_inspection--mobile-portrait--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/receipt_inspection--mobile-portrait--light.json b/proof/ease/latest/browser/screenshots/receipt_inspection--mobile-portrait--light.json new file mode 100644 index 0000000..d843659 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/receipt_inspection--mobile-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:26.641Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 114432, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=receipt_inspection", + "pngSha256": "327a8456950f0ec59a14877afeb8ca2bbf8a7fa619e73d0e89092d5af25e8b13", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "receipt_inspection", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/latest/browser/screenshots/receipt_inspection--mobile-portrait--light.png b/proof/ease/latest/browser/screenshots/receipt_inspection--mobile-portrait--light.png new file mode 100644 index 0000000..9fba839 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/receipt_inspection--mobile-portrait--light.png differ diff --git a/proof/ease/latest/browser/screenshots/receipt_inspection--tablet-landscape--dark.json b/proof/ease/latest/browser/screenshots/receipt_inspection--tablet-landscape--dark.json new file mode 100644 index 0000000..5d7a7fd --- /dev/null +++ b/proof/ease/latest/browser/screenshots/receipt_inspection--tablet-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:58.024Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 85814, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=receipt_inspection", + "pngSha256": "8e9aa0f71cdfeff60bb2838601ecc3895d85ea42c946aabecfb313ffc9337001", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "receipt_inspection", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/latest/browser/screenshots/receipt_inspection--tablet-landscape--dark.png b/proof/ease/latest/browser/screenshots/receipt_inspection--tablet-landscape--dark.png new file mode 100644 index 0000000..f3aadd7 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/receipt_inspection--tablet-landscape--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/receipt_inspection--tablet-landscape--light.json b/proof/ease/latest/browser/screenshots/receipt_inspection--tablet-landscape--light.json new file mode 100644 index 0000000..9a7e918 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/receipt_inspection--tablet-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:48.420Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 76210, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=receipt_inspection", + "pngSha256": "364929c96cc74e088b83d5d62ffafecb3709247dadb59a1f632334fcfb966561", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "receipt_inspection", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/latest/browser/screenshots/receipt_inspection--tablet-landscape--light.png b/proof/ease/latest/browser/screenshots/receipt_inspection--tablet-landscape--light.png new file mode 100644 index 0000000..c86845f Binary files /dev/null and b/proof/ease/latest/browser/screenshots/receipt_inspection--tablet-landscape--light.png differ diff --git a/proof/ease/latest/browser/screenshots/receipt_inspection--tablet-portrait--dark.json b/proof/ease/latest/browser/screenshots/receipt_inspection--tablet-portrait--dark.json new file mode 100644 index 0000000..4079986 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/receipt_inspection--tablet-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:17.183Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 104974, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=receipt_inspection", + "pngSha256": "c5a06283b7f56a05e43fbd573d8f36162e13ea4f4b2cc8ffbb3f51a349883c92", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "receipt_inspection", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/latest/browser/screenshots/receipt_inspection--tablet-portrait--dark.png b/proof/ease/latest/browser/screenshots/receipt_inspection--tablet-portrait--dark.png new file mode 100644 index 0000000..dc18d39 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/receipt_inspection--tablet-portrait--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/receipt_inspection--tablet-portrait--light.json b/proof/ease/latest/browser/screenshots/receipt_inspection--tablet-portrait--light.json new file mode 100644 index 0000000..ff862fa --- /dev/null +++ b/proof/ease/latest/browser/screenshots/receipt_inspection--tablet-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:07.608Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 95399, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=receipt_inspection", + "pngSha256": "e3d0ab3c979c7b047bdb01e88121830e6090a3db10368e6c6c670adadc65d57c", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "receipt_inspection", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/latest/browser/screenshots/receipt_inspection--tablet-portrait--light.png b/proof/ease/latest/browser/screenshots/receipt_inspection--tablet-portrait--light.png new file mode 100644 index 0000000..afcabf5 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/receipt_inspection--tablet-portrait--light.png differ diff --git a/proof/ease/latest/browser/screenshots/receipt_inspection--wide--dark.json b/proof/ease/latest/browser/screenshots/receipt_inspection--wide--dark.json new file mode 100644 index 0000000..c97ecd8 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/receipt_inspection--wide--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:38.829Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 66620, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=receipt_inspection", + "pngSha256": "ccfb416779fe418e21f16bf0f16cf0ceaa6b86f5deaff08d6edcbf5ed5ad9c51", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "receipt_inspection", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/latest/browser/screenshots/receipt_inspection--wide--dark.png b/proof/ease/latest/browser/screenshots/receipt_inspection--wide--dark.png new file mode 100644 index 0000000..a78607f Binary files /dev/null and b/proof/ease/latest/browser/screenshots/receipt_inspection--wide--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/receipt_inspection--wide--light.json b/proof/ease/latest/browser/screenshots/receipt_inspection--wide--light.json new file mode 100644 index 0000000..4b5c810 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/receipt_inspection--wide--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:28.620Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 56410, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=receipt_inspection", + "pngSha256": "53b00756209876fceffb828d36c8cd0f4b5a9d9da5ec509ab0a4b5a714bbea8b", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "receipt_inspection", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/latest/browser/screenshots/receipt_inspection--wide--light.png b/proof/ease/latest/browser/screenshots/receipt_inspection--wide--light.png new file mode 100644 index 0000000..a8b1c11 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/receipt_inspection--wide--light.png differ diff --git a/proof/ease/latest/browser/screenshots/recoverable_failure--desktop--dark.json b/proof/ease/latest/browser/screenshots/recoverable_failure--desktop--dark.json new file mode 100644 index 0000000..6497cb0 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/recoverable_failure--desktop--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:05.144Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 32935, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=recoverable_failure", + "pngSha256": "bf75db99449a2f5968e7a04d6d4ec454f0873caee1606bf3e8d2580cc784bf97", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "recoverable_failure", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/latest/browser/screenshots/recoverable_failure--desktop--dark.png b/proof/ease/latest/browser/screenshots/recoverable_failure--desktop--dark.png new file mode 100644 index 0000000..cb66395 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/recoverable_failure--desktop--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/recoverable_failure--desktop--light.json b/proof/ease/latest/browser/screenshots/recoverable_failure--desktop--light.json new file mode 100644 index 0000000..cb1e5d0 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/recoverable_failure--desktop--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:26:50.399Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 18189, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=recoverable_failure", + "pngSha256": "c3bc749a87e94946816db6d6361015828ea834d89182539e6fdd21cfc444ef42", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "recoverable_failure", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/latest/browser/screenshots/recoverable_failure--desktop--light.png b/proof/ease/latest/browser/screenshots/recoverable_failure--desktop--light.png new file mode 100644 index 0000000..8b6c35e Binary files /dev/null and b/proof/ease/latest/browser/screenshots/recoverable_failure--desktop--light.png differ diff --git a/proof/ease/latest/browser/screenshots/recoverable_failure--mobile-landscape--dark.json b/proof/ease/latest/browser/screenshots/recoverable_failure--mobile-landscape--dark.json new file mode 100644 index 0000000..67a63de --- /dev/null +++ b/proof/ease/latest/browser/screenshots/recoverable_failure--mobile-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:52.457Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 140247, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=recoverable_failure", + "pngSha256": "c232bf27be0bec2dd9c0860f5a65a0bc8d0fec30929a2ef0662c5adb05ad17a6", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "recoverable_failure", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/latest/browser/screenshots/recoverable_failure--mobile-landscape--dark.png b/proof/ease/latest/browser/screenshots/recoverable_failure--mobile-landscape--dark.png new file mode 100644 index 0000000..88356f1 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/recoverable_failure--mobile-landscape--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/recoverable_failure--mobile-landscape--light.json b/proof/ease/latest/browser/screenshots/recoverable_failure--mobile-landscape--light.json new file mode 100644 index 0000000..790787b --- /dev/null +++ b/proof/ease/latest/browser/screenshots/recoverable_failure--mobile-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:42.915Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 130705, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=recoverable_failure", + "pngSha256": "feb67962e4fb9fe6eff99624d9fba027e4b5916344af223bea8afac701cb6fc0", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "recoverable_failure", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/latest/browser/screenshots/recoverable_failure--mobile-landscape--light.png b/proof/ease/latest/browser/screenshots/recoverable_failure--mobile-landscape--light.png new file mode 100644 index 0000000..b38c72e Binary files /dev/null and b/proof/ease/latest/browser/screenshots/recoverable_failure--mobile-landscape--light.png differ diff --git a/proof/ease/latest/browser/screenshots/recoverable_failure--mobile-portrait--dark.json b/proof/ease/latest/browser/screenshots/recoverable_failure--mobile-portrait--dark.json new file mode 100644 index 0000000..468489a --- /dev/null +++ b/proof/ease/latest/browser/screenshots/recoverable_failure--mobile-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:33.395Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 121185, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=recoverable_failure", + "pngSha256": "297cbff2271db7580227a88d5868a0aa0ac636d576275b48a88716d5c0eb81a2", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "recoverable_failure", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/latest/browser/screenshots/recoverable_failure--mobile-portrait--dark.png b/proof/ease/latest/browser/screenshots/recoverable_failure--mobile-portrait--dark.png new file mode 100644 index 0000000..ef6998d Binary files /dev/null and b/proof/ease/latest/browser/screenshots/recoverable_failure--mobile-portrait--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/recoverable_failure--mobile-portrait--light.json b/proof/ease/latest/browser/screenshots/recoverable_failure--mobile-portrait--light.json new file mode 100644 index 0000000..250ea87 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/recoverable_failure--mobile-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:23.910Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 111701, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=recoverable_failure", + "pngSha256": "33eb02934c740e40226e1d8ea2b6105ab0598fc70cedd38605efc79ac61571ea", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "recoverable_failure", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/latest/browser/screenshots/recoverable_failure--mobile-portrait--light.png b/proof/ease/latest/browser/screenshots/recoverable_failure--mobile-portrait--light.png new file mode 100644 index 0000000..11a59a4 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/recoverable_failure--mobile-portrait--light.png differ diff --git a/proof/ease/latest/browser/screenshots/recoverable_failure--tablet-landscape--dark.json b/proof/ease/latest/browser/screenshots/recoverable_failure--tablet-landscape--dark.json new file mode 100644 index 0000000..449a53d --- /dev/null +++ b/proof/ease/latest/browser/screenshots/recoverable_failure--tablet-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:55.285Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 83075, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=recoverable_failure", + "pngSha256": "bea5876d8e8c9a7258861cbe26467e1360471f94a50ce523c396a8ebf11eaa66", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "recoverable_failure", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/latest/browser/screenshots/recoverable_failure--tablet-landscape--dark.png b/proof/ease/latest/browser/screenshots/recoverable_failure--tablet-landscape--dark.png new file mode 100644 index 0000000..0773d0b Binary files /dev/null and b/proof/ease/latest/browser/screenshots/recoverable_failure--tablet-landscape--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/recoverable_failure--tablet-landscape--light.json b/proof/ease/latest/browser/screenshots/recoverable_failure--tablet-landscape--light.json new file mode 100644 index 0000000..ba718aa --- /dev/null +++ b/proof/ease/latest/browser/screenshots/recoverable_failure--tablet-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:45.700Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 73490, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=recoverable_failure", + "pngSha256": "448d05c1d94a49c8e392082e9ca3fe6e634f29369926105c40f30e82bc0bc899", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "recoverable_failure", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/latest/browser/screenshots/recoverable_failure--tablet-landscape--light.png b/proof/ease/latest/browser/screenshots/recoverable_failure--tablet-landscape--light.png new file mode 100644 index 0000000..6493e27 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/recoverable_failure--tablet-landscape--light.png differ diff --git a/proof/ease/latest/browser/screenshots/recoverable_failure--tablet-portrait--dark.json b/proof/ease/latest/browser/screenshots/recoverable_failure--tablet-portrait--dark.json new file mode 100644 index 0000000..270ca00 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/recoverable_failure--tablet-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:14.451Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 102242, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=recoverable_failure", + "pngSha256": "bd094e04e8bb063231ab0c8ddd54eb165f9d64d4836d5a28eed0b9f490155c28", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "recoverable_failure", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/latest/browser/screenshots/recoverable_failure--tablet-portrait--dark.png b/proof/ease/latest/browser/screenshots/recoverable_failure--tablet-portrait--dark.png new file mode 100644 index 0000000..52b14f4 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/recoverable_failure--tablet-portrait--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/recoverable_failure--tablet-portrait--light.json b/proof/ease/latest/browser/screenshots/recoverable_failure--tablet-portrait--light.json new file mode 100644 index 0000000..ad6c2f3 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/recoverable_failure--tablet-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:04.882Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 92673, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=recoverable_failure", + "pngSha256": "b4ec848b8520e4d651723dd6660eeff2ba807bce80c228133b878f8596f7abb4", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "recoverable_failure", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/latest/browser/screenshots/recoverable_failure--tablet-portrait--light.png b/proof/ease/latest/browser/screenshots/recoverable_failure--tablet-portrait--light.png new file mode 100644 index 0000000..ae34def Binary files /dev/null and b/proof/ease/latest/browser/screenshots/recoverable_failure--tablet-portrait--light.png differ diff --git a/proof/ease/latest/browser/screenshots/recoverable_failure--wide--dark.json b/proof/ease/latest/browser/screenshots/recoverable_failure--wide--dark.json new file mode 100644 index 0000000..ee4dd7b --- /dev/null +++ b/proof/ease/latest/browser/screenshots/recoverable_failure--wide--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:36.007Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 63798, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=recoverable_failure", + "pngSha256": "edb8ab6554ce2c3bf83f9dc4772962dffc83e4669cfb20233966348944e5dda0", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "recoverable_failure", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/latest/browser/screenshots/recoverable_failure--wide--dark.png b/proof/ease/latest/browser/screenshots/recoverable_failure--wide--dark.png new file mode 100644 index 0000000..9676808 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/recoverable_failure--wide--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/recoverable_failure--wide--light.json b/proof/ease/latest/browser/screenshots/recoverable_failure--wide--light.json new file mode 100644 index 0000000..1566946 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/recoverable_failure--wide--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:15.227Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 43018, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=recoverable_failure", + "pngSha256": "210d798bdb3882df8d00af9f4b3ca75b0ffa96a68e62f7dcb816bc7c8f899e4c", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "recoverable_failure", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/latest/browser/screenshots/recoverable_failure--wide--light.png b/proof/ease/latest/browser/screenshots/recoverable_failure--wide--light.png new file mode 100644 index 0000000..a3fc870 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/recoverable_failure--wide--light.png differ diff --git a/proof/ease/latest/browser/screenshots/reload_resume--desktop--dark.json b/proof/ease/latest/browser/screenshots/reload_resume--desktop--dark.json new file mode 100644 index 0000000..1c04552 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/reload_resume--desktop--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:05.730Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 33520, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=reload_resume", + "pngSha256": "d07253685da5c7d3da8c91440fabeaedc97ffda14bb754dc815a38b7850b83ed", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "reload_resume", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/latest/browser/screenshots/reload_resume--desktop--dark.png b/proof/ease/latest/browser/screenshots/reload_resume--desktop--dark.png new file mode 100644 index 0000000..f5cd176 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/reload_resume--desktop--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/reload_resume--desktop--light.json b/proof/ease/latest/browser/screenshots/reload_resume--desktop--light.json new file mode 100644 index 0000000..1ff248f --- /dev/null +++ b/proof/ease/latest/browser/screenshots/reload_resume--desktop--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:26:51.578Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 19369, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=reload_resume", + "pngSha256": "a277adeea31da7eb8d652d2afec26006c3ea2c0b468722cc98ca60be11ce81c3", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "reload_resume", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/latest/browser/screenshots/reload_resume--desktop--light.png b/proof/ease/latest/browser/screenshots/reload_resume--desktop--light.png new file mode 100644 index 0000000..8c3be64 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/reload_resume--desktop--light.png differ diff --git a/proof/ease/latest/browser/screenshots/reload_resume--mobile-landscape--dark.json b/proof/ease/latest/browser/screenshots/reload_resume--mobile-landscape--dark.json new file mode 100644 index 0000000..c85909a --- /dev/null +++ b/proof/ease/latest/browser/screenshots/reload_resume--mobile-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:53.010Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 140800, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=reload_resume", + "pngSha256": "49034360bc184960c219e1bed559c1edc3973250837f85d299971e59f645c846", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "reload_resume", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/latest/browser/screenshots/reload_resume--mobile-landscape--dark.png b/proof/ease/latest/browser/screenshots/reload_resume--mobile-landscape--dark.png new file mode 100644 index 0000000..4ebe044 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/reload_resume--mobile-landscape--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/reload_resume--mobile-landscape--light.json b/proof/ease/latest/browser/screenshots/reload_resume--mobile-landscape--light.json new file mode 100644 index 0000000..5150e86 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/reload_resume--mobile-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:43.477Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 131267, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=reload_resume", + "pngSha256": "b5a725ff429d63398d151fcbaadb01e4de68e36ee359d2df60c51ef6da9e57a2", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "reload_resume", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/latest/browser/screenshots/reload_resume--mobile-landscape--light.png b/proof/ease/latest/browser/screenshots/reload_resume--mobile-landscape--light.png new file mode 100644 index 0000000..fce92ae Binary files /dev/null and b/proof/ease/latest/browser/screenshots/reload_resume--mobile-landscape--light.png differ diff --git a/proof/ease/latest/browser/screenshots/reload_resume--mobile-portrait--dark.json b/proof/ease/latest/browser/screenshots/reload_resume--mobile-portrait--dark.json new file mode 100644 index 0000000..6dbc3f5 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/reload_resume--mobile-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:33.942Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 121732, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=reload_resume", + "pngSha256": "ed7e01a088b12b21eab979c8297696d4e0abf5632e0adb54ed3c8e883bbb08db", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "reload_resume", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/latest/browser/screenshots/reload_resume--mobile-portrait--dark.png b/proof/ease/latest/browser/screenshots/reload_resume--mobile-portrait--dark.png new file mode 100644 index 0000000..4aaecc7 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/reload_resume--mobile-portrait--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/reload_resume--mobile-portrait--light.json b/proof/ease/latest/browser/screenshots/reload_resume--mobile-portrait--light.json new file mode 100644 index 0000000..9a8151b --- /dev/null +++ b/proof/ease/latest/browser/screenshots/reload_resume--mobile-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:24.459Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 112250, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=reload_resume", + "pngSha256": "68af6db9c23cb93169847ff7e2a79fbd0f3cd6ce78ef44bf34d2fd346429f138", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "reload_resume", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/latest/browser/screenshots/reload_resume--mobile-portrait--light.png b/proof/ease/latest/browser/screenshots/reload_resume--mobile-portrait--light.png new file mode 100644 index 0000000..1e7e817 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/reload_resume--mobile-portrait--light.png differ diff --git a/proof/ease/latest/browser/screenshots/reload_resume--tablet-landscape--dark.json b/proof/ease/latest/browser/screenshots/reload_resume--tablet-landscape--dark.json new file mode 100644 index 0000000..7122e87 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/reload_resume--tablet-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:55.852Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 83642, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=reload_resume", + "pngSha256": "4dc645feeb3435a498b3a00b81ede0f3591f763f1db7e572e99d3cf8bd74ef01", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "reload_resume", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/latest/browser/screenshots/reload_resume--tablet-landscape--dark.png b/proof/ease/latest/browser/screenshots/reload_resume--tablet-landscape--dark.png new file mode 100644 index 0000000..03ac935 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/reload_resume--tablet-landscape--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/reload_resume--tablet-landscape--light.json b/proof/ease/latest/browser/screenshots/reload_resume--tablet-landscape--light.json new file mode 100644 index 0000000..3694efe --- /dev/null +++ b/proof/ease/latest/browser/screenshots/reload_resume--tablet-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:46.252Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 74043, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=reload_resume", + "pngSha256": "70647d82506c5492331835fd0833ec8ef629e795caf9c85372dad50414cbf5d5", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "reload_resume", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/latest/browser/screenshots/reload_resume--tablet-landscape--light.png b/proof/ease/latest/browser/screenshots/reload_resume--tablet-landscape--light.png new file mode 100644 index 0000000..9636ab9 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/reload_resume--tablet-landscape--light.png differ diff --git a/proof/ease/latest/browser/screenshots/reload_resume--tablet-portrait--dark.json b/proof/ease/latest/browser/screenshots/reload_resume--tablet-portrait--dark.json new file mode 100644 index 0000000..d372fb6 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/reload_resume--tablet-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:15.018Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 102808, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=reload_resume", + "pngSha256": "bfcc3dcf8178f532a022ca9fef6f9df57618127a41f56b9ddef77068e197180f", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "reload_resume", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/latest/browser/screenshots/reload_resume--tablet-portrait--dark.png b/proof/ease/latest/browser/screenshots/reload_resume--tablet-portrait--dark.png new file mode 100644 index 0000000..9711ce7 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/reload_resume--tablet-portrait--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/reload_resume--tablet-portrait--light.json b/proof/ease/latest/browser/screenshots/reload_resume--tablet-portrait--light.json new file mode 100644 index 0000000..dfc3895 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/reload_resume--tablet-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:05.449Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 93240, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=reload_resume", + "pngSha256": "0bd885f65cbd66c0fdfe495149e769643a5c6cd12da65edbdfc4dafdc82051a1", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "reload_resume", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/latest/browser/screenshots/reload_resume--tablet-portrait--light.png b/proof/ease/latest/browser/screenshots/reload_resume--tablet-portrait--light.png new file mode 100644 index 0000000..d769024 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/reload_resume--tablet-portrait--light.png differ diff --git a/proof/ease/latest/browser/screenshots/reload_resume--wide--dark.json b/proof/ease/latest/browser/screenshots/reload_resume--wide--dark.json new file mode 100644 index 0000000..808de8f --- /dev/null +++ b/proof/ease/latest/browser/screenshots/reload_resume--wide--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:36.594Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 64384, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=reload_resume", + "pngSha256": "9a4b725e5f42a2265cb0eb473e772b0c425a6ec98929561eec78580994bd9926", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "reload_resume", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/latest/browser/screenshots/reload_resume--wide--dark.png b/proof/ease/latest/browser/screenshots/reload_resume--wide--dark.png new file mode 100644 index 0000000..ca5700a Binary files /dev/null and b/proof/ease/latest/browser/screenshots/reload_resume--wide--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/reload_resume--wide--light.json b/proof/ease/latest/browser/screenshots/reload_resume--wide--light.json new file mode 100644 index 0000000..6d40565 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/reload_resume--wide--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:15.815Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 43605, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=reload_resume", + "pngSha256": "00e7c528ba8c859b7362d8b134c89c03ca685fb4093dc672010801c65832d3fd", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "reload_resume", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/latest/browser/screenshots/reload_resume--wide--light.png b/proof/ease/latest/browser/screenshots/reload_resume--wide--light.png new file mode 100644 index 0000000..420f354 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/reload_resume--wide--light.png differ diff --git a/proof/ease/latest/browser/screenshots/running--desktop--dark.json b/proof/ease/latest/browser/screenshots/running--desktop--dark.json new file mode 100644 index 0000000..4345b7e --- /dev/null +++ b/proof/ease/latest/browser/screenshots/running--desktop--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:01.634Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 29424, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=running", + "pngSha256": "25f0953dda7641393995c4fb2cfcf9e0ac8009a13d5bee03824a3138e8be3d8c", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "running", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/latest/browser/screenshots/running--desktop--dark.png b/proof/ease/latest/browser/screenshots/running--desktop--dark.png new file mode 100644 index 0000000..c7ff902 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/running--desktop--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/running--desktop--light.json b/proof/ease/latest/browser/screenshots/running--desktop--light.json new file mode 100644 index 0000000..8734e12 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/running--desktop--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:26:42.812Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 10602, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=running", + "pngSha256": "63971e350a3cfca7d6e0cfae70a27324cfe6efa7d0defd290fb51173da71c9ba", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "running", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/latest/browser/screenshots/running--desktop--light.png b/proof/ease/latest/browser/screenshots/running--desktop--light.png new file mode 100644 index 0000000..f736842 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/running--desktop--light.png differ diff --git a/proof/ease/latest/browser/screenshots/running--mobile-landscape--dark.json b/proof/ease/latest/browser/screenshots/running--mobile-landscape--dark.json new file mode 100644 index 0000000..6d937d4 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/running--mobile-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:49.125Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 136915, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=running", + "pngSha256": "4462e909f0bc28437bb21a470f330587c90d61cbd33a21c4f68c577306a000b6", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "running", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/latest/browser/screenshots/running--mobile-landscape--dark.png b/proof/ease/latest/browser/screenshots/running--mobile-landscape--dark.png new file mode 100644 index 0000000..596969d Binary files /dev/null and b/proof/ease/latest/browser/screenshots/running--mobile-landscape--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/running--mobile-landscape--light.json b/proof/ease/latest/browser/screenshots/running--mobile-landscape--light.json new file mode 100644 index 0000000..e60f605 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/running--mobile-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:39.591Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 127381, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=running", + "pngSha256": "7311400a07613abafb2c3ab11a1a51eec8f01e0699a428de092425f7a78448e8", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "running", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/latest/browser/screenshots/running--mobile-landscape--light.png b/proof/ease/latest/browser/screenshots/running--mobile-landscape--light.png new file mode 100644 index 0000000..8f7b3f3 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/running--mobile-landscape--light.png differ diff --git a/proof/ease/latest/browser/screenshots/running--mobile-portrait--dark.json b/proof/ease/latest/browser/screenshots/running--mobile-portrait--dark.json new file mode 100644 index 0000000..ce2529a --- /dev/null +++ b/proof/ease/latest/browser/screenshots/running--mobile-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:30.108Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 117898, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=running", + "pngSha256": "63639f19c68a1cf91cb3871eb1ab315a6267310706297affaaf1000a47d3b376", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "running", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/latest/browser/screenshots/running--mobile-portrait--dark.png b/proof/ease/latest/browser/screenshots/running--mobile-portrait--dark.png new file mode 100644 index 0000000..2896067 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/running--mobile-portrait--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/running--mobile-portrait--light.json b/proof/ease/latest/browser/screenshots/running--mobile-portrait--light.json new file mode 100644 index 0000000..ea263dd --- /dev/null +++ b/proof/ease/latest/browser/screenshots/running--mobile-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:20.591Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 108382, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=running", + "pngSha256": "730f926f9f58badccdabaf08ea5b1eeb4e22e96dc9ded4aad396ea58bd11aae7", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "running", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/latest/browser/screenshots/running--mobile-portrait--light.png b/proof/ease/latest/browser/screenshots/running--mobile-portrait--light.png new file mode 100644 index 0000000..b4c2f39 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/running--mobile-portrait--light.png differ diff --git a/proof/ease/latest/browser/screenshots/running--tablet-landscape--dark.json b/proof/ease/latest/browser/screenshots/running--tablet-landscape--dark.json new file mode 100644 index 0000000..c7e6ad9 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/running--tablet-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:51.920Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 79710, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=running", + "pngSha256": "2843cb3c783b2632f8033a8b957abace622a5a58dc317cfc00651c78502efad5", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "running", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/latest/browser/screenshots/running--tablet-landscape--dark.png b/proof/ease/latest/browser/screenshots/running--tablet-landscape--dark.png new file mode 100644 index 0000000..058fc3d Binary files /dev/null and b/proof/ease/latest/browser/screenshots/running--tablet-landscape--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/running--tablet-landscape--light.json b/proof/ease/latest/browser/screenshots/running--tablet-landscape--light.json new file mode 100644 index 0000000..f872cfb --- /dev/null +++ b/proof/ease/latest/browser/screenshots/running--tablet-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:42.355Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 70145, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=running", + "pngSha256": "21ea362237822e386d94c236d2dbff012e4203cce654ce3fe84793e78a7e2027", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "running", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/latest/browser/screenshots/running--tablet-landscape--light.png b/proof/ease/latest/browser/screenshots/running--tablet-landscape--light.png new file mode 100644 index 0000000..a01e76f Binary files /dev/null and b/proof/ease/latest/browser/screenshots/running--tablet-landscape--light.png differ diff --git a/proof/ease/latest/browser/screenshots/running--tablet-portrait--dark.json b/proof/ease/latest/browser/screenshots/running--tablet-portrait--dark.json new file mode 100644 index 0000000..a97e501 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/running--tablet-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:11.103Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 98893, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=running", + "pngSha256": "09c8dd927828db18b262a1be5dd8d4ed5e59b0cdeaed46c2ac37ae8dacbec79d", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "running", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/latest/browser/screenshots/running--tablet-portrait--dark.png b/proof/ease/latest/browser/screenshots/running--tablet-portrait--dark.png new file mode 100644 index 0000000..9ac921f Binary files /dev/null and b/proof/ease/latest/browser/screenshots/running--tablet-portrait--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/running--tablet-portrait--light.json b/proof/ease/latest/browser/screenshots/running--tablet-portrait--light.json new file mode 100644 index 0000000..b63e5b8 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/running--tablet-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:01.534Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 89325, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=running", + "pngSha256": "791f507945c05852f2aa5681d6c63c100219f45fbe83d3e4dff2a89b978ab0cc", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "running", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/latest/browser/screenshots/running--tablet-portrait--light.png b/proof/ease/latest/browser/screenshots/running--tablet-portrait--light.png new file mode 100644 index 0000000..13e98a9 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/running--tablet-portrait--light.png differ diff --git a/proof/ease/latest/browser/screenshots/running--wide--dark.json b/proof/ease/latest/browser/screenshots/running--wide--dark.json new file mode 100644 index 0000000..dfc89ce --- /dev/null +++ b/proof/ease/latest/browser/screenshots/running--wide--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:32.430Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 60220, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=running", + "pngSha256": "c929d419ff30ea0d3f103baa537e500b42078200b661bbfa0be4b70b637743f4", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "running", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/latest/browser/screenshots/running--wide--dark.png b/proof/ease/latest/browser/screenshots/running--wide--dark.png new file mode 100644 index 0000000..a89cc6e Binary files /dev/null and b/proof/ease/latest/browser/screenshots/running--wide--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/running--wide--light.json b/proof/ease/latest/browser/screenshots/running--wide--light.json new file mode 100644 index 0000000..5983fc3 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/running--wide--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:11.666Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 39456, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=running", + "pngSha256": "e98b0c2b9d899f354c55d54acc224a99e73aa00e6897f35e07e2059c52b3fbb4", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "running", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/latest/browser/screenshots/running--wide--light.png b/proof/ease/latest/browser/screenshots/running--wide--light.png new file mode 100644 index 0000000..3209de0 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/running--wide--light.png differ diff --git a/proof/ease/latest/browser/screenshots/validation_error--desktop--dark.json b/proof/ease/latest/browser/screenshots/validation_error--desktop--dark.json new file mode 100644 index 0000000..3019cdc --- /dev/null +++ b/proof/ease/latest/browser/screenshots/validation_error--desktop--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:01.060Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 28850, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=validation_error", + "pngSha256": "4b290ccb18c7fa205637ecb774496ea6b829057b725fa39e6efaf617be70dc14", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "validation_error", + "theme": "dark", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/latest/browser/screenshots/validation_error--desktop--dark.png b/proof/ease/latest/browser/screenshots/validation_error--desktop--dark.png new file mode 100644 index 0000000..1a280cc Binary files /dev/null and b/proof/ease/latest/browser/screenshots/validation_error--desktop--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/validation_error--desktop--light.json b/proof/ease/latest/browser/screenshots/validation_error--desktop--light.json new file mode 100644 index 0000000..9a5d50b --- /dev/null +++ b/proof/ease/latest/browser/screenshots/validation_error--desktop--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:26:41.321Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 9112, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=validation_error", + "pngSha256": "f15834e766d0b1a69d2fe550b2f71d7b403900e167219b635fdd1cb7d561ce82", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "validation_error", + "theme": "light", + "viewport": { + "height": 900, + "width": 1440 + } +} diff --git a/proof/ease/latest/browser/screenshots/validation_error--desktop--light.png b/proof/ease/latest/browser/screenshots/validation_error--desktop--light.png new file mode 100644 index 0000000..5adee95 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/validation_error--desktop--light.png differ diff --git a/proof/ease/latest/browser/screenshots/validation_error--mobile-landscape--dark.json b/proof/ease/latest/browser/screenshots/validation_error--mobile-landscape--dark.json new file mode 100644 index 0000000..d1e4b27 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/validation_error--mobile-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:48.574Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 136364, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=validation_error", + "pngSha256": "5ab4e710ae41bfbed96d8bae1df8bea53ee9ceac414cb4596da2e24301e04be8", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "validation_error", + "theme": "dark", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/latest/browser/screenshots/validation_error--mobile-landscape--dark.png b/proof/ease/latest/browser/screenshots/validation_error--mobile-landscape--dark.png new file mode 100644 index 0000000..619277c Binary files /dev/null and b/proof/ease/latest/browser/screenshots/validation_error--mobile-landscape--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/validation_error--mobile-landscape--light.json b/proof/ease/latest/browser/screenshots/validation_error--mobile-landscape--light.json new file mode 100644 index 0000000..fa1e5f1 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/validation_error--mobile-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:39.044Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 126834, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=validation_error", + "pngSha256": "28efb728c9eb980724c19a19d8050d05b5409d1d7736f2b638ced96f6659f169", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "validation_error", + "theme": "light", + "viewport": { + "height": 390, + "width": 844 + } +} diff --git a/proof/ease/latest/browser/screenshots/validation_error--mobile-landscape--light.png b/proof/ease/latest/browser/screenshots/validation_error--mobile-landscape--light.png new file mode 100644 index 0000000..bba9561 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/validation_error--mobile-landscape--light.png differ diff --git a/proof/ease/latest/browser/screenshots/validation_error--mobile-portrait--dark.json b/proof/ease/latest/browser/screenshots/validation_error--mobile-portrait--dark.json new file mode 100644 index 0000000..87f8675 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/validation_error--mobile-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:29.558Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 117348, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=validation_error", + "pngSha256": "02b66012b2e1948340a0404768cf643d43fc5ac96cdb4e20e568c9c729e1eba9", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "validation_error", + "theme": "dark", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/latest/browser/screenshots/validation_error--mobile-portrait--dark.png b/proof/ease/latest/browser/screenshots/validation_error--mobile-portrait--dark.png new file mode 100644 index 0000000..ebf9306 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/validation_error--mobile-portrait--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/validation_error--mobile-portrait--light.json b/proof/ease/latest/browser/screenshots/validation_error--mobile-portrait--light.json new file mode 100644 index 0000000..e1b72bc --- /dev/null +++ b/proof/ease/latest/browser/screenshots/validation_error--mobile-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:20.041Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 107832, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=validation_error", + "pngSha256": "d0fb96fc3515c56bff68f2f9f1b0a20dfd290a3cbd2c43b3a8980ed0bd77bde7", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "validation_error", + "theme": "light", + "viewport": { + "height": 844, + "width": 390 + } +} diff --git a/proof/ease/latest/browser/screenshots/validation_error--mobile-portrait--light.png b/proof/ease/latest/browser/screenshots/validation_error--mobile-portrait--light.png new file mode 100644 index 0000000..1aa1615 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/validation_error--mobile-portrait--light.png differ diff --git a/proof/ease/latest/browser/screenshots/validation_error--tablet-landscape--dark.json b/proof/ease/latest/browser/screenshots/validation_error--tablet-landscape--dark.json new file mode 100644 index 0000000..9d8c25b --- /dev/null +++ b/proof/ease/latest/browser/screenshots/validation_error--tablet-landscape--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:51.356Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 79147, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=validation_error", + "pngSha256": "6d8072d39c0320148f614f001433c913c497f2a6dcaaaa989937f7eec87e0f37", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "validation_error", + "theme": "dark", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/latest/browser/screenshots/validation_error--tablet-landscape--dark.png b/proof/ease/latest/browser/screenshots/validation_error--tablet-landscape--dark.png new file mode 100644 index 0000000..170eb2a Binary files /dev/null and b/proof/ease/latest/browser/screenshots/validation_error--tablet-landscape--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/validation_error--tablet-landscape--light.json b/proof/ease/latest/browser/screenshots/validation_error--tablet-landscape--light.json new file mode 100644 index 0000000..59f3716 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/validation_error--tablet-landscape--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:41.789Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 69579, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=validation_error", + "pngSha256": "f29543f60947f18b05928e27ee5296a8e4504a62f51c1389de625a33f70942a0", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "validation_error", + "theme": "light", + "viewport": { + "height": 768, + "width": 1024 + } +} diff --git a/proof/ease/latest/browser/screenshots/validation_error--tablet-landscape--light.png b/proof/ease/latest/browser/screenshots/validation_error--tablet-landscape--light.png new file mode 100644 index 0000000..d565111 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/validation_error--tablet-landscape--light.png differ diff --git a/proof/ease/latest/browser/screenshots/validation_error--tablet-portrait--dark.json b/proof/ease/latest/browser/screenshots/validation_error--tablet-portrait--dark.json new file mode 100644 index 0000000..b8945bd --- /dev/null +++ b/proof/ease/latest/browser/screenshots/validation_error--tablet-portrait--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:10.552Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 98342, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=validation_error", + "pngSha256": "b0aa56fc263738894b59ed27ee104a4d5eacd8f36d40e71585ed4d719fdc9b41", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "validation_error", + "theme": "dark", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/latest/browser/screenshots/validation_error--tablet-portrait--dark.png b/proof/ease/latest/browser/screenshots/validation_error--tablet-portrait--dark.png new file mode 100644 index 0000000..a23de04 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/validation_error--tablet-portrait--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/validation_error--tablet-portrait--light.json b/proof/ease/latest/browser/screenshots/validation_error--tablet-portrait--light.json new file mode 100644 index 0000000..3e4b3cb --- /dev/null +++ b/proof/ease/latest/browser/screenshots/validation_error--tablet-portrait--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:28:00.987Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 88777, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=validation_error", + "pngSha256": "618bbc7c1938b8c9de1805499b689a4e8493cca504bc2989faca582063e6f396", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "validation_error", + "theme": "light", + "viewport": { + "height": 1024, + "width": 768 + } +} diff --git a/proof/ease/latest/browser/screenshots/validation_error--tablet-portrait--light.png b/proof/ease/latest/browser/screenshots/validation_error--tablet-portrait--light.png new file mode 100644 index 0000000..9a84a09 Binary files /dev/null and b/proof/ease/latest/browser/screenshots/validation_error--tablet-portrait--light.png differ diff --git a/proof/ease/latest/browser/screenshots/validation_error--wide--dark.json b/proof/ease/latest/browser/screenshots/validation_error--wide--dark.json new file mode 100644 index 0000000..f330c93 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/validation_error--wide--dark.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:31.847Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 59637, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=validation_error", + "pngSha256": "41b2287c1e95e283eeb7dbc811812057af8965586314b6348515bab216120403", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "validation_error", + "theme": "dark", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/latest/browser/screenshots/validation_error--wide--dark.png b/proof/ease/latest/browser/screenshots/validation_error--wide--dark.png new file mode 100644 index 0000000..5b255fa Binary files /dev/null and b/proof/ease/latest/browser/screenshots/validation_error--wide--dark.png differ diff --git a/proof/ease/latest/browser/screenshots/validation_error--wide--light.json b/proof/ease/latest/browser/screenshots/validation_error--wide--light.json new file mode 100644 index 0000000..170dd01 --- /dev/null +++ b/proof/ease/latest/browser/screenshots/validation_error--wide--light.json @@ -0,0 +1,27 @@ +{ + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "capturedAt": "2026-07-21T22:27:11.066Z", + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "consoleErrors": 0, + "elapsedMs": 38857, + "failedRequests": 0, + "generatedCandidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "horizontalOverflowPx": 0, + "mojibakeDetected": false, + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "pageUrl": "http://127.0.0.1:43552/?scenario=validation_error", + "pngSha256": "240431908327645e91e5b5dc0598f7fa2c0d0caf7774ba54d227a0cb77d087af", + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.screenshot-proof/v1", + "serverProcess": { + "command": "C:\\nvm4w\\nodejs\\node.exe apps/web/server.mjs", + "pid": 47704 + }, + "state": "validation_error", + "theme": "light", + "viewport": { + "height": 1080, + "width": 1920 + } +} diff --git a/proof/ease/latest/browser/screenshots/validation_error--wide--light.png b/proof/ease/latest/browser/screenshots/validation_error--wide--light.png new file mode 100644 index 0000000..d7215ca Binary files /dev/null and b/proof/ease/latest/browser/screenshots/validation_error--wide--light.png differ diff --git a/proof/ease/latest/candidate.tar.gz b/proof/ease/latest/candidate.tar.gz new file mode 100644 index 0000000..3968fa1 Binary files /dev/null and b/proof/ease/latest/candidate.tar.gz differ diff --git a/proof/ease/latest/manifest.json b/proof/ease/latest/manifest.json new file mode 100644 index 0000000..130ed61 --- /dev/null +++ b/proof/ease/latest/manifest.json @@ -0,0 +1,218 @@ +{ + "base": { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "browserManifestDigest": "87e9f12f43812439026ff506c55628ac22786856f6baaaa8dceed5d15865fa52", + "candidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "checks": { + "browserContractPassed": true, + "browserJourneyPassed": true, + "candidateCommitted": true, + "compileReproducible": true, + "domainBlank": true, + "easeCertificationHonest": true, + "figuredOutContract": true, + "lockCreated": true, + "proofIdentityBound": true, + "proofPassed": true, + "runtimeHashBound": true, + "runtimeNotMisclassified": true, + "runtimeSpecifierPortable": true + }, + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "dependency": "file:vendor/nodekit", + "phases": [ + { + "durationMs": 918, + "name": "scaffold_generation", + "startedAt": "2026-07-21T22:26:21.047Z" + }, + { + "args": [ + "install", + "--prefer-offline", + "--no-audit", + "--no-fund" + ], + "command": "npm", + "durationMs": 2386, + "exitCode": 0, + "failed": false, + "startedAt": "2026-07-21T22:26:21.047Z", + "stderrSha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdoutSha256": "4b2eee44482fed74249b00abf3d2eed03eee4eb7f8825674ad05d39a1989c1c9", + "name": "dependency_installation" + }, + { + "args": [ + "add", + "--all" + ], + "command": "git", + "durationMs": 82, + "exitCode": 0, + "failed": false, + "startedAt": "2026-07-21T22:26:23.434Z", + "stderrSha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdoutSha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "name": "candidate_stage_after_install" + }, + { + "args": [ + "-c", + "user.name=NodeKit", + "-c", + "user.email=nodekit@local", + "commit", + "-m", + "chore: lock generated dependencies" + ], + "command": "git", + "durationMs": 99, + "exitCode": 0, + "failed": false, + "startedAt": "2026-07-21T22:26:23.564Z", + "stderrSha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdoutSha256": "8db7f68bf6483e8009803ae8e16e3c73ed7827007d44f01c40ab9055f0e69fdf", + "name": "candidate_lock_commit" + }, + { + "args": [ + "run", + "compile" + ], + "command": "npm", + "durationMs": 2285, + "exitCode": 0, + "failed": false, + "startedAt": "2026-07-21T22:26:23.703Z", + "stderrSha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdoutSha256": "b88d127adca9c60554f6134f611cb1592fa62e37a2ea0baf5b7e811a479a6095", + "name": "compile" + }, + { + "args": [ + "run", + "check" + ], + "command": "npm", + "durationMs": 929, + "exitCode": 0, + "failed": false, + "startedAt": "2026-07-21T22:26:26.097Z", + "stderrSha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdoutSha256": "6a89ef71c475b033d273d73101e5af462664bb8d9b79315adf49813ca36fc40b", + "name": "deterministic_checks" + }, + { + "args": [ + "run", + "demo" + ], + "command": "npm", + "durationMs": 465, + "exitCode": 0, + "failed": false, + "startedAt": "2026-07-21T22:26:27.026Z", + "stderrSha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdoutSha256": "3710e7fb474bebb01b858271638e7e0529b23491f79c0b4fc35d6b8e5b2d24f9", + "name": "neutral_journey" + }, + { + "args": [ + "run", + "eval" + ], + "command": "npm", + "durationMs": 468, + "exitCode": 0, + "failed": false, + "startedAt": "2026-07-21T22:26:27.491Z", + "stderrSha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdoutSha256": "3a6bb875cde7d14ff0851aa15291f2ae172517591ca045c99287fa087e511d63", + "name": "evaluation" + }, + { + "args": [ + "run", + "proof:browser-contract" + ], + "command": "npm", + "durationMs": 727, + "exitCode": 0, + "failed": false, + "startedAt": "2026-07-21T22:26:27.960Z", + "stderrSha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdoutSha256": "04305364d8398dce86441bea101492ba850da24cd91589b1b589974a80f39b2c", + "name": "browser_contract" + }, + { + "args": [ + "playwright", + "install", + "chromium" + ], + "command": "npx", + "durationMs": 2289, + "exitCode": 0, + "failed": false, + "startedAt": "2026-07-21T22:26:28.686Z", + "stderrSha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdoutSha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "name": "browser_runtime_installation" + }, + { + "args": [ + "run", + "proof:browser" + ], + "command": "npm", + "durationMs": 145521, + "exitCode": 0, + "failed": false, + "startedAt": "2026-07-21T22:26:30.975Z", + "stderrSha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdoutSha256": "f88400c5aa2c51cdf7cc4705b2d1a224d14f0c4d4036eb30e96ee8bd349daa8d", + "name": "rendered_browser_journey" + }, + { + "args": [ + "run", + "proof" + ], + "command": "npm", + "durationMs": 566, + "exitCode": 0, + "failed": false, + "startedAt": "2026-07-21T22:28:56.497Z", + "stderrSha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdoutSha256": "0304f8d75612ba559bdb26bc3dc144c5ec4763cb6e22788598818650853bda9c", + "name": "receipt_completion" + } + ], + "proofDigest": "3629ee7ec9e62c751301ae1179f1434748d4d1d6a41c569cf80451b37623bc35", + "template": "domain-blank-base" + }, + "cacheClass": "warm-or-unknown", + "durationMs": 157768, + "generatedAt": "2026-07-21T22:28:57.763Z", + "nodeVersion": "v22.22.2", + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "operatingSystem": "win32-10.0.26200-x64", + "packageManager": "npm", + "passed": true, + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.ease-proof-run/v1", + "startedAt": "2026-07-21T22:26:19.994Z", + "submissionReady": false, + "submissionBlockers": [ + "developerTimingMatrix", + "freshAgentHeldout", + "freshHumanUsability", + "threeConvexConsumers", + "previewDeployment", + "proofloopEaseVerification" + ], + "verdict": "EASE_NOT_CERTIFIED", + "receiptDigest": "446785bbb41fed78907c6fe8535ec41f1d08e3fba2579a9a151cc9c8187b6b9a" +} diff --git a/proof/ease/latest/proofloop-receipt.json b/proof/ease/latest/proofloop-receipt.json new file mode 100644 index 0000000..e634d30 --- /dev/null +++ b/proof/ease/latest/proofloop-receipt.json @@ -0,0 +1,125 @@ +{ + "schema": "proofloop.receipt/v1", + "schemaVersion": 1, + "receiptId": "ease-ease_e320fe62fdc746a0a286", + "kind": "nodekit-ease-integrity", + "createdAt": "2026-07-21T22:29:06.074Z", + "producer": { + "id": "proofloop", + "version": "0.3.0", + "configHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb" + }, + "subject": { + "type": "run", + "id": "ease_e320fe62fdc746a0a286", + "runId": "ease_e320fe62fdc746a0a286", + "repository": { + "candidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "dirty": false + } + }, + "claim": { + "text": "The supplied NodeKit EaseProof evidence is internally bound; this receipt does not certify ease, human usability, deployment, or submission readiness.", + "boundary": "proxy", + "tier": "local_ready" + }, + "verdict": { + "status": "passed", + "authority": "authoritative", + "decisionMethod": "deterministic_gate", + "decisiveCheckIds": [ + "ease-integrity" + ], + "summary": "Local EaseProof hashes and identities verified." + }, + "checks": [ + { + "id": "ease-integrity", + "status": "passed", + "role": "decisive", + "method": "deterministic", + "summary": "180 screenshot(s), 2 replay artifact(s), and the candidate/timer manifests were checked; Ease certification=false.", + "evidenceRefs": [ + "ease-manifest", + "browser-manifest", + "candidate-archive", + "playwright-trace", + "browser-video" + ] + } + ], + "evidence": [ + { + "id": "ease-manifest", + "kind": "ease-manifest", + "path": "manifest.json", + "sha256": "02e83a39c3de47346d179daab65db80ddaa878c194dc6932e502a177dad5e1a5", + "hashMethod": "raw-bytes-sha256" + }, + { + "id": "browser-manifest", + "kind": "screenshot-manifest", + "path": "browser/screenshot-manifest.json", + "sha256": "7c8014b94973ede0e966d2a696903894fe566fa5809417012abd62c850fa4cf6", + "hashMethod": "raw-bytes-sha256" + }, + { + "id": "candidate-archive", + "kind": "generated-candidate", + "path": "candidate.tar.gz", + "sha256": "6658378e0d04c7dcc9b1cf8561dea0feb16733e29d214f5af685191bedb0a4a5", + "hashMethod": "raw-bytes-sha256" + }, + { + "id": "playwright-trace", + "kind": "playwright-trace", + "path": "browser/playwright-trace.zip", + "sha256": "1e75d01735d50da8c8d4053099869ef40f436266515ad333b438d7e894299cfa", + "hashMethod": "raw-bytes-sha256" + }, + { + "id": "browser-video", + "kind": "browser-video", + "path": "browser/journey.webm", + "sha256": "d10b13c9bb0b7dff932cae2de0abf852ce817dc19b165785d0e8448b15fe2bd6", + "hashMethod": "raw-bytes-sha256" + } + ], + "payload": { + "schema": "nodekit.ease-verification/v1", + "version": 1, + "mode": "inline", + "data": { + "easeCertified": false, + "errors": [], + "warnings": [ + "Evidence integrity may pass, but NodeKit Ease is not certified and submission remains blocked." + ], + "checkedScreenshots": 180, + "runId": "ease_e320fe62fdc746a0a286" + }, + "sha256": "e8633c3d4816be0ed37d9241a73a8dfe30c3425176addb32863128567458c7cf", + "hashMethod": "canonical-json-sha256" + }, + "timing": { + "startedAt": "2026-07-21T22:26:19.994Z", + "completedAt": "2026-07-21T22:28:57.763Z", + "durationMs": 157768 + }, + "privacy": { + "visibility": "private", + "redacted": true, + "externalEgress": false + }, + "extensions": { + "easeCertified": false, + "submissionBlockers": [ + "developerTimingMatrix", + "freshAgentHeldout", + "freshHumanUsability", + "threeConvexConsumers", + "previewDeployment", + "proofloopEaseVerification" + ] + } +} diff --git a/proof/factory-acceptance.json b/proof/factory-acceptance.json index 5b81f4c..130ed61 100644 --- a/proof/factory-acceptance.json +++ b/proof/factory-acceptance.json @@ -1,45 +1,218 @@ { - "durationMs": 37221, - "generatedAt": "2026-07-21T02:59:29.345Z", - "nodeVersion": "v22.22.2", - "nodekitSourceHash": "3c7777258b38b8a501a9e9929b04b8224786168eeb1f2adaf8e5f9f50f67c60e", - "passed": true, - "presets": { - "agentic-rl-research": { - "applicationHash": "6ec029c45898affba0801095ba6b506d0384f152a423ebe7c674aa9abc23b796", - "candidateCommit": "7e4d1c37a2424228361b0038eb4a68a887e19d91", - "checks": { - "candidateCommitted": true, - "compileReproducible": true, - "exactRuntimeInstalled": true, - "proofIdentityBound": true, - "proofPassed": true, - "runtimeHashBound": true, - "runtimeNotMisclassified": true, - "runtimeSpecifierPortable": true - }, - "configHash": "6ec029c45898affba0801095ba6b506d0384f152a423ebe7c674aa9abc23b796", - "dependency": "file:vendor/nodekit", - "proofDigest": "368b74a7e7378fb8bcced76ae05a55bcbeb49f64078ef72b697fbe323576f2a6" + "base": { + "applicationHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "browserManifestDigest": "87e9f12f43812439026ff506c55628ac22786856f6baaaa8dceed5d15865fa52", + "candidateCommit": "2fd4b30957ef794aebc4ce618f804b2b1e25484b", + "checks": { + "browserContractPassed": true, + "browserJourneyPassed": true, + "candidateCommitted": true, + "compileReproducible": true, + "domainBlank": true, + "easeCertificationHonest": true, + "figuredOutContract": true, + "lockCreated": true, + "proofIdentityBound": true, + "proofPassed": true, + "runtimeHashBound": true, + "runtimeNotMisclassified": true, + "runtimeSpecifierPortable": true }, - "smb-lending-fde": { - "applicationHash": "7e667aa5d789a22a9c97664b03ce2cb75087d68c29962e08e5095a257ef798d0", - "candidateCommit": "6c3a2fd298879a35bfdf8fa3df35ff609153d2ab", - "checks": { - "candidateCommitted": true, - "compileReproducible": true, - "exactRuntimeInstalled": true, - "proofIdentityBound": true, - "proofPassed": true, - "runtimeHashBound": true, - "runtimeNotMisclassified": true, - "runtimeSpecifierPortable": true - }, - "configHash": "7e667aa5d789a22a9c97664b03ce2cb75087d68c29962e08e5095a257ef798d0", - "dependency": "file:vendor/nodekit", - "proofDigest": "7be9d6cdf6c144910e07d9b1ea7ec85d7567433572c5eaaeda0279f5cf56daba" - } + "configHash": "429f8396206cb5c8bc014473012beaaee57cb41ba45eb1fea2595a3b9c9881ef", + "dependency": "file:vendor/nodekit", + "phases": [ + { + "durationMs": 918, + "name": "scaffold_generation", + "startedAt": "2026-07-21T22:26:21.047Z" + }, + { + "args": [ + "install", + "--prefer-offline", + "--no-audit", + "--no-fund" + ], + "command": "npm", + "durationMs": 2386, + "exitCode": 0, + "failed": false, + "startedAt": "2026-07-21T22:26:21.047Z", + "stderrSha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdoutSha256": "4b2eee44482fed74249b00abf3d2eed03eee4eb7f8825674ad05d39a1989c1c9", + "name": "dependency_installation" + }, + { + "args": [ + "add", + "--all" + ], + "command": "git", + "durationMs": 82, + "exitCode": 0, + "failed": false, + "startedAt": "2026-07-21T22:26:23.434Z", + "stderrSha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdoutSha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "name": "candidate_stage_after_install" + }, + { + "args": [ + "-c", + "user.name=NodeKit", + "-c", + "user.email=nodekit@local", + "commit", + "-m", + "chore: lock generated dependencies" + ], + "command": "git", + "durationMs": 99, + "exitCode": 0, + "failed": false, + "startedAt": "2026-07-21T22:26:23.564Z", + "stderrSha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdoutSha256": "8db7f68bf6483e8009803ae8e16e3c73ed7827007d44f01c40ab9055f0e69fdf", + "name": "candidate_lock_commit" + }, + { + "args": [ + "run", + "compile" + ], + "command": "npm", + "durationMs": 2285, + "exitCode": 0, + "failed": false, + "startedAt": "2026-07-21T22:26:23.703Z", + "stderrSha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdoutSha256": "b88d127adca9c60554f6134f611cb1592fa62e37a2ea0baf5b7e811a479a6095", + "name": "compile" + }, + { + "args": [ + "run", + "check" + ], + "command": "npm", + "durationMs": 929, + "exitCode": 0, + "failed": false, + "startedAt": "2026-07-21T22:26:26.097Z", + "stderrSha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdoutSha256": "6a89ef71c475b033d273d73101e5af462664bb8d9b79315adf49813ca36fc40b", + "name": "deterministic_checks" + }, + { + "args": [ + "run", + "demo" + ], + "command": "npm", + "durationMs": 465, + "exitCode": 0, + "failed": false, + "startedAt": "2026-07-21T22:26:27.026Z", + "stderrSha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdoutSha256": "3710e7fb474bebb01b858271638e7e0529b23491f79c0b4fc35d6b8e5b2d24f9", + "name": "neutral_journey" + }, + { + "args": [ + "run", + "eval" + ], + "command": "npm", + "durationMs": 468, + "exitCode": 0, + "failed": false, + "startedAt": "2026-07-21T22:26:27.491Z", + "stderrSha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdoutSha256": "3a6bb875cde7d14ff0851aa15291f2ae172517591ca045c99287fa087e511d63", + "name": "evaluation" + }, + { + "args": [ + "run", + "proof:browser-contract" + ], + "command": "npm", + "durationMs": 727, + "exitCode": 0, + "failed": false, + "startedAt": "2026-07-21T22:26:27.960Z", + "stderrSha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdoutSha256": "04305364d8398dce86441bea101492ba850da24cd91589b1b589974a80f39b2c", + "name": "browser_contract" + }, + { + "args": [ + "playwright", + "install", + "chromium" + ], + "command": "npx", + "durationMs": 2289, + "exitCode": 0, + "failed": false, + "startedAt": "2026-07-21T22:26:28.686Z", + "stderrSha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdoutSha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "name": "browser_runtime_installation" + }, + { + "args": [ + "run", + "proof:browser" + ], + "command": "npm", + "durationMs": 145521, + "exitCode": 0, + "failed": false, + "startedAt": "2026-07-21T22:26:30.975Z", + "stderrSha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdoutSha256": "f88400c5aa2c51cdf7cc4705b2d1a224d14f0c4d4036eb30e96ee8bd349daa8d", + "name": "rendered_browser_journey" + }, + { + "args": [ + "run", + "proof" + ], + "command": "npm", + "durationMs": 566, + "exitCode": 0, + "failed": false, + "startedAt": "2026-07-21T22:28:56.497Z", + "stderrSha256": "12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "stdoutSha256": "0304f8d75612ba559bdb26bc3dc144c5ec4763cb6e22788598818650853bda9c", + "name": "receipt_completion" + } + ], + "proofDigest": "3629ee7ec9e62c751301ae1179f1434748d4d1d6a41c569cf80451b37623bc35", + "template": "domain-blank-base" }, - "schemaVersion": "nodekit.factory-acceptance/v1", - "receiptDigest": "3f792e0ddbf7cffe5173b543b96baf3c43a7c81567f746ebfbce1e2cebce9867" + "cacheClass": "warm-or-unknown", + "durationMs": 157768, + "generatedAt": "2026-07-21T22:28:57.763Z", + "nodeVersion": "v22.22.2", + "nodekitCommit": "1036650717c1b8cff76939cf6e2a5bef3c965da9", + "nodekitSourceHash": "784beca380484e6fbd9eda515cfe246c1b5a04448d9e519147067bae320417eb", + "operatingSystem": "win32-10.0.26200-x64", + "packageManager": "npm", + "passed": true, + "runId": "ease_e320fe62fdc746a0a286", + "schemaVersion": "nodekit.ease-proof-run/v1", + "startedAt": "2026-07-21T22:26:19.994Z", + "submissionReady": false, + "submissionBlockers": [ + "developerTimingMatrix", + "freshAgentHeldout", + "freshHumanUsability", + "threeConvexConsumers", + "previewDeployment", + "proofloopEaseVerification" + ], + "verdict": "EASE_NOT_CERTIFIED", + "receiptDigest": "446785bbb41fed78907c6fe8535ec41f1d08e3fba2579a9a151cc9c8187b6b9a" } diff --git a/proof/nodekit-figured-out-presentation.json b/proof/nodekit-figured-out-presentation.json new file mode 100644 index 0000000..7876dcc --- /dev/null +++ b/proof/nodekit-figured-out-presentation.json @@ -0,0 +1,20 @@ +{ + "schemaVersion": "nodekit.presentation-proof/v1", + "artifact": { + "path": "outputs/nodekit-figured-out-proof.pptx", + "bytes": 252024, + "sha256": "603bb034dd4ef8eac28ce37a9f93c56bd1ea80bf7552e3cb139d59dea5b4ab3d", + "slides": 8 + }, + "source": "changes/nodekit-proof-campaign-2026-07-20/presentation/exports/nodekit-proof-campaign-2026-07-20.final.pptx", + "checks": { + "artifactToolImportAndExport": true, + "exactTemplateFidelity": true, + "overflow": false, + "perSlideVisualInspection": true, + "staleFactoryClaimsRemoved": true, + "speakerNotesUpdated": true + }, + "status": "passed", + "generatedAt": "2026-07-21T20:10:33.143Z" +} diff --git a/proof/package-install-verdict.json b/proof/package-install-verdict.json new file mode 100644 index 0000000..f1eada8 --- /dev/null +++ b/proof/package-install-verdict.json @@ -0,0 +1,47 @@ +{ + "schemaVersion": "nodekit.package-install-proof/v1", + "candidateCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "package": "@homenshum/nodekit", + "version": "0.2.1", + "tarball": "proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/package/homenshum-nodekit-0.2.1.tgz", + "tarballSha256": "7893ed5ce41f4454fa03f6d7c33eab6667ea8271de9fa37d878085d3b6cd3882", + "tarballBytes": 139409, + "unpackedSize": 575049, + "fileCount": 161, + "checks": { + "freshConsumerInstall": true, + "packagedCliCreate": true, + "compile": true, + "check": true, + "demo": true, + "eval": true, + "typecheckPublic": true, + "distributionComplete": true + }, + "distributionChecks": { + "postgresAdapter": true, + "postgresTypes": true, + "postgresMigration": true, + "supabaseProfile": true, + "caseflowTypes": true + }, + "supportingEvidence": [ + { + "path": "proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/package/application-identity.json", + "sha256": "8125fde7b20e113849b908bd9eda6418fd5553b375e0fbfd748c0e57b2d22f5a" + }, + { + "path": "proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/package/demo-receipt.json", + "sha256": "eb28e3256b510b41eae8b68559cae715861b89d783e77a5d9777016ba237044c" + }, + { + "path": "proof/ease/candidates/5cc61578b3c1bd5b5c8195b83347b91f8b83242b/0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b/package/eval-receipt.json", + "sha256": "6ba85ab58163b4f386108675685005533548528c7329d7bc07fe93ef22e04bac" + } + ], + "publicationPerformed": false, + "passed": true, + "startedAt": "2026-07-22T08:43:15.000Z", + "generatedAt": "2026-07-22T08:43:48.670Z" +} diff --git a/proof/postgres-conformance.json b/proof/postgres-conformance.json new file mode 100644 index 0000000..394783a --- /dev/null +++ b/proof/postgres-conformance.json @@ -0,0 +1,71 @@ +{ + "schemaVersion": "nodekit.postgres-conformance/v1", + "adapter": "@homenshum/nodekit/adapters/postgres", + "assertions": { + "crossOwnerDenied": true, + "ownerIsolation": true, + "receiptIntegrity": true, + "reloadPreservedState": true, + "sameBaseRaceFailedClosed": true, + "sharedConformancePassed": true + }, + "candidateCommit": "5cc61578b3c1bd5b5c8195b83347b91f8b83242b", + "capabilities": { + "schemaVersion": "nodekit.runtime-capabilities/v1", + "provider": "postgres", + "durableState": true, + "transactions": true, + "optimisticConcurrency": true, + "subscriptions": "polling", + "durableJobs": "external", + "fileStorage": false, + "presence": false, + "scheduledJobs": false, + "localDevelopment": true + }, + "conformance": { + "assertions": { + "activeRunStartIsIdempotent": true, + "canonicalVersionAdvancedOnce": true, + "contentAddressedReceipt": true, + "exceptionStatePreserved": true, + "nextActionOwnerExplicit": true, + "oneAuthoritativeCase": true, + "repeatedCompletionIsIdempotent": true, + "repeatedDecisionIsIdempotent": true, + "staleProposalFailedClosed": true + }, + "capabilityNegotiation": { + "missing": [], + "passed": true, + "provider": "postgres", + "schemaVersion": "nodekit.runtime-capability-negotiation/v1" + }, + "capabilities": { + "schemaVersion": "nodekit.runtime-capabilities/v1", + "provider": "postgres", + "durableState": true, + "transactions": true, + "optimisticConcurrency": true, + "subscriptions": "polling", + "durableJobs": "external", + "fileStorage": false, + "presence": false, + "scheduledJobs": false, + "localDevelopment": true + }, + "passed": true, + "schemaVersion": "nodekit.adapter-conformance/v1" + }, + "generatedAt": "2026-07-22T08:41:03.302Z", + "migration": { + "path": "adapters/postgres/001_caseflow.sql", + "sha256": "63e4cd3af94f5db8204f3c80ad3faadd53ed0f1b8019bae414f3785e6c1051a3" + }, + "nodekitSourceHash": "0ced0adf6e0f719be9a5fabefd69754a79102f39b6b6a54b20daeb60ceba7c0b", + "ownerScope": "isolated-test-identities", + "passed": true, + "postgres": { + "serverVersion": "17.10" + } +} diff --git a/proof/publication-approval.template.json b/proof/publication-approval.template.json new file mode 100644 index 0000000..7260e4d --- /dev/null +++ b/proof/publication-approval.template.json @@ -0,0 +1,9 @@ +{ + "schemaVersion": "nodekit.publication-approval/v1", + "candidateCommit": "0000000000000000000000000000000000000000", + "approved": false, + "approvedBy": "", + "approvedAt": null, + "scopes": [], + "note": "Copy to proof/publication-approval.json only after the named approver explicitly authorizes both npm publication and Convex directory submission for this exact candidate commit." +} diff --git a/proof/submission-manifest.template.json b/proof/submission-manifest.template.json new file mode 100644 index 0000000..82c5bed --- /dev/null +++ b/proof/submission-manifest.template.json @@ -0,0 +1,14 @@ +{ + "schemaVersion": "nodekit.submission-manifest/v1", + "candidateCommit": "0000000000000000000000000000000000000000", + "gates": [ + { "id": "developerTimingMatrix", "passed": false, "evidence": [{ "path": "proof/ease/developer-timing-verdict.json", "sha256": "0000000000000000000000000000000000000000000000000000000000000000" }] }, + { "id": "freshAgentHeldout", "passed": false, "evidence": [{ "path": "proof/ease/fresh-agent-verdict.json", "sha256": "0000000000000000000000000000000000000000000000000000000000000000" }] }, + { "id": "freshHumanUsability", "passed": false, "evidence": [{ "path": "proof/ease/fresh-users-verdict.json", "sha256": "0000000000000000000000000000000000000000000000000000000000000000" }] }, + { "id": "threeConvexConsumers", "passed": false, "evidence": [{ "path": "proof/convex-consumers-verdict.json", "sha256": "0000000000000000000000000000000000000000000000000000000000000000" }] }, + { "id": "previewDeployment", "passed": false, "evidence": [{ "path": "proof/preview-verdict.json", "sha256": "0000000000000000000000000000000000000000000000000000000000000000" }] }, + { "id": "proofloopEaseVerification", "passed": false, "evidence": [{ "path": "proof/proofloop-final.json", "sha256": "0000000000000000000000000000000000000000000000000000000000000000" }] }, + { "id": "packageInstallProof", "passed": false, "evidence": [{ "path": "proof/package-install-verdict.json", "sha256": "0000000000000000000000000000000000000000000000000000000000000000" }] }, + { "id": "publicationApproval", "passed": false, "evidence": [{ "path": "proof/publication-approval.json", "sha256": "0000000000000000000000000000000000000000000000000000000000000000" }] } + ] +} diff --git a/templates/agentic-rl-research/.dockerignore b/reference-apps/agentic-rl-research/.dockerignore similarity index 100% rename from templates/agentic-rl-research/.dockerignore rename to reference-apps/agentic-rl-research/.dockerignore diff --git a/templates/agentic-rl-research/.env.example b/reference-apps/agentic-rl-research/.env.example similarity index 100% rename from templates/agentic-rl-research/.env.example rename to reference-apps/agentic-rl-research/.env.example diff --git a/templates/agentic-rl-research/.gitattributes b/reference-apps/agentic-rl-research/.gitattributes similarity index 100% rename from templates/agentic-rl-research/.gitattributes rename to reference-apps/agentic-rl-research/.gitattributes diff --git a/templates/agentic-rl-research/AGENTS.md b/reference-apps/agentic-rl-research/AGENTS.md similarity index 100% rename from templates/agentic-rl-research/AGENTS.md rename to reference-apps/agentic-rl-research/AGENTS.md diff --git a/templates/agentic-rl-research/Dockerfile b/reference-apps/agentic-rl-research/Dockerfile similarity index 100% rename from templates/agentic-rl-research/Dockerfile rename to reference-apps/agentic-rl-research/Dockerfile diff --git a/templates/agentic-rl-research/README.md b/reference-apps/agentic-rl-research/README.md similarity index 100% rename from templates/agentic-rl-research/README.md rename to reference-apps/agentic-rl-research/README.md diff --git a/templates/agentic-rl-research/adw/workflows/launch.yaml b/reference-apps/agentic-rl-research/adw/workflows/launch.yaml similarity index 100% rename from templates/agentic-rl-research/adw/workflows/launch.yaml rename to reference-apps/agentic-rl-research/adw/workflows/launch.yaml diff --git a/templates/agentic-rl-research/agent/experiment-loop.mjs b/reference-apps/agentic-rl-research/agent/experiment-loop.mjs similarity index 100% rename from templates/agentic-rl-research/agent/experiment-loop.mjs rename to reference-apps/agentic-rl-research/agent/experiment-loop.mjs diff --git a/templates/agentic-rl-research/agent/instructions.md b/reference-apps/agentic-rl-research/agent/instructions.md similarity index 100% rename from templates/agentic-rl-research/agent/instructions.md rename to reference-apps/agentic-rl-research/agent/instructions.md diff --git a/templates/agentic-rl-research/agent/planner/planning-policy.yaml b/reference-apps/agentic-rl-research/agent/planner/planning-policy.yaml similarity index 100% rename from templates/agentic-rl-research/agent/planner/planning-policy.yaml rename to reference-apps/agentic-rl-research/agent/planner/planning-policy.yaml diff --git a/templates/agentic-rl-research/agent/policies/budgets.yaml b/reference-apps/agentic-rl-research/agent/policies/budgets.yaml similarity index 100% rename from templates/agentic-rl-research/agent/policies/budgets.yaml rename to reference-apps/agentic-rl-research/agent/policies/budgets.yaml diff --git a/templates/agentic-rl-research/agent/policies/permissions.yaml b/reference-apps/agentic-rl-research/agent/policies/permissions.yaml similarity index 100% rename from templates/agentic-rl-research/agent/policies/permissions.yaml rename to reference-apps/agentic-rl-research/agent/policies/permissions.yaml diff --git a/templates/agentic-rl-research/agent/skills/founderquest-rl/SKILL.md b/reference-apps/agentic-rl-research/agent/skills/founderquest-rl/SKILL.md similarity index 100% rename from templates/agentic-rl-research/agent/skills/founderquest-rl/SKILL.md rename to reference-apps/agentic-rl-research/agent/skills/founderquest-rl/SKILL.md diff --git a/templates/agentic-rl-research/agent/tools/evaluate-founder-quest.mjs b/reference-apps/agentic-rl-research/agent/tools/evaluate-founder-quest.mjs similarity index 100% rename from templates/agentic-rl-research/agent/tools/evaluate-founder-quest.mjs rename to reference-apps/agentic-rl-research/agent/tools/evaluate-founder-quest.mjs diff --git a/templates/agentic-rl-research/apps/web/public/app.js b/reference-apps/agentic-rl-research/apps/web/public/app.js similarity index 100% rename from templates/agentic-rl-research/apps/web/public/app.js rename to reference-apps/agentic-rl-research/apps/web/public/app.js diff --git a/templates/agentic-rl-research/apps/web/public/index.html b/reference-apps/agentic-rl-research/apps/web/public/index.html similarity index 100% rename from templates/agentic-rl-research/apps/web/public/index.html rename to reference-apps/agentic-rl-research/apps/web/public/index.html diff --git a/templates/agentic-rl-research/apps/web/public/styles.css b/reference-apps/agentic-rl-research/apps/web/public/styles.css similarity index 100% rename from templates/agentic-rl-research/apps/web/public/styles.css rename to reference-apps/agentic-rl-research/apps/web/public/styles.css diff --git a/templates/agentic-rl-research/apps/web/server.mjs b/reference-apps/agentic-rl-research/apps/web/server.mjs similarity index 100% rename from templates/agentic-rl-research/apps/web/server.mjs rename to reference-apps/agentic-rl-research/apps/web/server.mjs diff --git a/templates/agentic-rl-research/evals/deterministic-foundation.json b/reference-apps/agentic-rl-research/evals/deterministic-foundation.json similarity index 100% rename from templates/agentic-rl-research/evals/deterministic-foundation.json rename to reference-apps/agentic-rl-research/evals/deterministic-foundation.json diff --git a/templates/agentic-rl-research/evals/heldout-stability.json b/reference-apps/agentic-rl-research/evals/heldout-stability.json similarity index 100% rename from templates/agentic-rl-research/evals/heldout-stability.json rename to reference-apps/agentic-rl-research/evals/heldout-stability.json diff --git a/templates/agentic-rl-research/evals/protected-reward.json b/reference-apps/agentic-rl-research/evals/protected-reward.json similarity index 100% rename from templates/agentic-rl-research/evals/protected-reward.json rename to reference-apps/agentic-rl-research/evals/protected-reward.json diff --git a/templates/agentic-rl-research/fixtures/tasks/heldout.json b/reference-apps/agentic-rl-research/fixtures/tasks/heldout.json similarity index 100% rename from templates/agentic-rl-research/fixtures/tasks/heldout.json rename to reference-apps/agentic-rl-research/fixtures/tasks/heldout.json diff --git a/templates/agentic-rl-research/fixtures/tasks/train.json b/reference-apps/agentic-rl-research/fixtures/tasks/train.json similarity index 100% rename from templates/agentic-rl-research/fixtures/tasks/train.json rename to reference-apps/agentic-rl-research/fixtures/tasks/train.json diff --git a/templates/agentic-rl-research/fixtures/tasks/validation.json b/reference-apps/agentic-rl-research/fixtures/tasks/validation.json similarity index 100% rename from templates/agentic-rl-research/fixtures/tasks/validation.json rename to reference-apps/agentic-rl-research/fixtures/tasks/validation.json diff --git a/templates/agentic-rl-research/gitignore.template b/reference-apps/agentic-rl-research/gitignore.template similarity index 100% rename from templates/agentic-rl-research/gitignore.template rename to reference-apps/agentic-rl-research/gitignore.template diff --git a/templates/agentic-rl-research/hackathon.yaml b/reference-apps/agentic-rl-research/hackathon.yaml similarity index 100% rename from templates/agentic-rl-research/hackathon.yaml rename to reference-apps/agentic-rl-research/hackathon.yaml diff --git a/templates/agentic-rl-research/nodeagent.yaml b/reference-apps/agentic-rl-research/nodeagent.yaml similarity index 100% rename from templates/agentic-rl-research/nodeagent.yaml rename to reference-apps/agentic-rl-research/nodeagent.yaml diff --git a/templates/agentic-rl-research/nodekit.yaml b/reference-apps/agentic-rl-research/nodekit.yaml similarity index 100% rename from templates/agentic-rl-research/nodekit.yaml rename to reference-apps/agentic-rl-research/nodekit.yaml diff --git a/templates/agentic-rl-research/package.json b/reference-apps/agentic-rl-research/package.json similarity index 100% rename from templates/agentic-rl-research/package.json rename to reference-apps/agentic-rl-research/package.json diff --git a/templates/agentic-rl-research/packs/primary/pack.yaml b/reference-apps/agentic-rl-research/packs/primary/pack.yaml similarity index 100% rename from templates/agentic-rl-research/packs/primary/pack.yaml rename to reference-apps/agentic-rl-research/packs/primary/pack.yaml diff --git a/templates/agentic-rl-research/proof/.gitkeep b/reference-apps/agentic-rl-research/proof/.gitkeep similarity index 100% rename from templates/agentic-rl-research/proof/.gitkeep rename to reference-apps/agentic-rl-research/proof/.gitkeep diff --git a/templates/agentic-rl-research/render.yaml b/reference-apps/agentic-rl-research/render.yaml similarity index 100% rename from templates/agentic-rl-research/render.yaml rename to reference-apps/agentic-rl-research/render.yaml diff --git a/templates/agentic-rl-research/schemas/founderquest-rl-receipt.schema.json b/reference-apps/agentic-rl-research/schemas/founderquest-rl-receipt.schema.json similarity index 100% rename from templates/agentic-rl-research/schemas/founderquest-rl-receipt.schema.json rename to reference-apps/agentic-rl-research/schemas/founderquest-rl-receipt.schema.json diff --git a/templates/agentic-rl-research/scripts/benchmark.mjs b/reference-apps/agentic-rl-research/scripts/benchmark.mjs similarity index 100% rename from templates/agentic-rl-research/scripts/benchmark.mjs rename to reference-apps/agentic-rl-research/scripts/benchmark.mjs diff --git a/templates/agentic-rl-research/scripts/check.mjs b/reference-apps/agentic-rl-research/scripts/check.mjs similarity index 100% rename from templates/agentic-rl-research/scripts/check.mjs rename to reference-apps/agentic-rl-research/scripts/check.mjs diff --git a/templates/agentic-rl-research/scripts/demo.mjs b/reference-apps/agentic-rl-research/scripts/demo.mjs similarity index 100% rename from templates/agentic-rl-research/scripts/demo.mjs rename to reference-apps/agentic-rl-research/scripts/demo.mjs diff --git a/templates/agentic-rl-research/scripts/eval.mjs b/reference-apps/agentic-rl-research/scripts/eval.mjs similarity index 100% rename from templates/agentic-rl-research/scripts/eval.mjs rename to reference-apps/agentic-rl-research/scripts/eval.mjs diff --git a/templates/agentic-rl-research/scripts/lib/application-identity.mjs b/reference-apps/agentic-rl-research/scripts/lib/application-identity.mjs similarity index 100% rename from templates/agentic-rl-research/scripts/lib/application-identity.mjs rename to reference-apps/agentic-rl-research/scripts/lib/application-identity.mjs diff --git a/templates/agentic-rl-research/scripts/lib/proof-bindings.mjs b/reference-apps/agentic-rl-research/scripts/lib/proof-bindings.mjs similarity index 100% rename from templates/agentic-rl-research/scripts/lib/proof-bindings.mjs rename to reference-apps/agentic-rl-research/scripts/lib/proof-bindings.mjs diff --git a/templates/agentic-rl-research/scripts/proof.mjs b/reference-apps/agentic-rl-research/scripts/proof.mjs similarity index 100% rename from templates/agentic-rl-research/scripts/proof.mjs rename to reference-apps/agentic-rl-research/scripts/proof.mjs diff --git a/templates/agentic-rl-research/test/experiment-loop.test.mjs b/reference-apps/agentic-rl-research/test/experiment-loop.test.mjs similarity index 100% rename from templates/agentic-rl-research/test/experiment-loop.test.mjs rename to reference-apps/agentic-rl-research/test/experiment-loop.test.mjs diff --git a/templates/research-loop/.dockerignore b/reference-apps/research-loop/.dockerignore similarity index 100% rename from templates/research-loop/.dockerignore rename to reference-apps/research-loop/.dockerignore diff --git a/templates/research-loop/.env.example b/reference-apps/research-loop/.env.example similarity index 100% rename from templates/research-loop/.env.example rename to reference-apps/research-loop/.env.example diff --git a/templates/research-loop/.gitattributes b/reference-apps/research-loop/.gitattributes similarity index 100% rename from templates/research-loop/.gitattributes rename to reference-apps/research-loop/.gitattributes diff --git a/templates/research-loop/AGENTS.md b/reference-apps/research-loop/AGENTS.md similarity index 100% rename from templates/research-loop/AGENTS.md rename to reference-apps/research-loop/AGENTS.md diff --git a/templates/research-loop/Dockerfile b/reference-apps/research-loop/Dockerfile similarity index 100% rename from templates/research-loop/Dockerfile rename to reference-apps/research-loop/Dockerfile diff --git a/templates/research-loop/README.md b/reference-apps/research-loop/README.md similarity index 100% rename from templates/research-loop/README.md rename to reference-apps/research-loop/README.md diff --git a/templates/research-loop/adw/workflows/launch.yaml b/reference-apps/research-loop/adw/workflows/launch.yaml similarity index 100% rename from templates/research-loop/adw/workflows/launch.yaml rename to reference-apps/research-loop/adw/workflows/launch.yaml diff --git a/templates/research-loop/agent/experiment-loop.mjs b/reference-apps/research-loop/agent/experiment-loop.mjs similarity index 100% rename from templates/research-loop/agent/experiment-loop.mjs rename to reference-apps/research-loop/agent/experiment-loop.mjs diff --git a/templates/research-loop/agent/instructions.md b/reference-apps/research-loop/agent/instructions.md similarity index 100% rename from templates/research-loop/agent/instructions.md rename to reference-apps/research-loop/agent/instructions.md diff --git a/templates/research-loop/agent/planner/planning-policy.yaml b/reference-apps/research-loop/agent/planner/planning-policy.yaml similarity index 100% rename from templates/research-loop/agent/planner/planning-policy.yaml rename to reference-apps/research-loop/agent/planner/planning-policy.yaml diff --git a/templates/research-loop/agent/policies/budgets.yaml b/reference-apps/research-loop/agent/policies/budgets.yaml similarity index 100% rename from templates/research-loop/agent/policies/budgets.yaml rename to reference-apps/research-loop/agent/policies/budgets.yaml diff --git a/templates/research-loop/agent/policies/permissions.yaml b/reference-apps/research-loop/agent/policies/permissions.yaml similarity index 100% rename from templates/research-loop/agent/policies/permissions.yaml rename to reference-apps/research-loop/agent/policies/permissions.yaml diff --git a/templates/research-loop/agent/skills/autoresearch-live/SKILL.md b/reference-apps/research-loop/agent/skills/autoresearch-live/SKILL.md similarity index 100% rename from templates/research-loop/agent/skills/autoresearch-live/SKILL.md rename to reference-apps/research-loop/agent/skills/autoresearch-live/SKILL.md diff --git a/templates/research-loop/agent/subagents/reviewer/agent.yaml b/reference-apps/research-loop/agent/subagents/reviewer/agent.yaml similarity index 100% rename from templates/research-loop/agent/subagents/reviewer/agent.yaml rename to reference-apps/research-loop/agent/subagents/reviewer/agent.yaml diff --git a/templates/research-loop/agent/tools/measure-ngram.mjs b/reference-apps/research-loop/agent/tools/measure-ngram.mjs similarity index 100% rename from templates/research-loop/agent/tools/measure-ngram.mjs rename to reference-apps/research-loop/agent/tools/measure-ngram.mjs diff --git a/templates/research-loop/apps/web/public/app.js b/reference-apps/research-loop/apps/web/public/app.js similarity index 100% rename from templates/research-loop/apps/web/public/app.js rename to reference-apps/research-loop/apps/web/public/app.js diff --git a/templates/research-loop/apps/web/public/index.html b/reference-apps/research-loop/apps/web/public/index.html similarity index 100% rename from templates/research-loop/apps/web/public/index.html rename to reference-apps/research-loop/apps/web/public/index.html diff --git a/templates/research-loop/apps/web/public/styles.css b/reference-apps/research-loop/apps/web/public/styles.css similarity index 100% rename from templates/research-loop/apps/web/public/styles.css rename to reference-apps/research-loop/apps/web/public/styles.css diff --git a/templates/research-loop/apps/web/server.mjs b/reference-apps/research-loop/apps/web/server.mjs similarity index 100% rename from templates/research-loop/apps/web/server.mjs rename to reference-apps/research-loop/apps/web/server.mjs diff --git a/templates/research-loop/backend/filesystem/store.mjs b/reference-apps/research-loop/backend/filesystem/store.mjs similarity index 100% rename from templates/research-loop/backend/filesystem/store.mjs rename to reference-apps/research-loop/backend/filesystem/store.mjs diff --git a/templates/research-loop/evals/deterministic-smoke.json b/reference-apps/research-loop/evals/deterministic-smoke.json similarity index 100% rename from templates/research-loop/evals/deterministic-smoke.json rename to reference-apps/research-loop/evals/deterministic-smoke.json diff --git a/templates/research-loop/fixtures/corpus/train.txt b/reference-apps/research-loop/fixtures/corpus/train.txt similarity index 100% rename from templates/research-loop/fixtures/corpus/train.txt rename to reference-apps/research-loop/fixtures/corpus/train.txt diff --git a/templates/research-loop/fixtures/corpus/validation.txt b/reference-apps/research-loop/fixtures/corpus/validation.txt similarity index 100% rename from templates/research-loop/fixtures/corpus/validation.txt rename to reference-apps/research-loop/fixtures/corpus/validation.txt diff --git a/templates/research-loop/gitignore.template b/reference-apps/research-loop/gitignore.template similarity index 100% rename from templates/research-loop/gitignore.template rename to reference-apps/research-loop/gitignore.template diff --git a/templates/research-loop/hackathon.yaml b/reference-apps/research-loop/hackathon.yaml similarity index 100% rename from templates/research-loop/hackathon.yaml rename to reference-apps/research-loop/hackathon.yaml diff --git a/templates/research-loop/integrations/pi-ai/provider.mjs b/reference-apps/research-loop/integrations/pi-ai/provider.mjs similarity index 100% rename from templates/research-loop/integrations/pi-ai/provider.mjs rename to reference-apps/research-loop/integrations/pi-ai/provider.mjs diff --git a/templates/research-loop/nodeagent.yaml b/reference-apps/research-loop/nodeagent.yaml similarity index 100% rename from templates/research-loop/nodeagent.yaml rename to reference-apps/research-loop/nodeagent.yaml diff --git a/templates/research-loop/nodekit.yaml b/reference-apps/research-loop/nodekit.yaml similarity index 100% rename from templates/research-loop/nodekit.yaml rename to reference-apps/research-loop/nodekit.yaml diff --git a/templates/research-loop/package.json b/reference-apps/research-loop/package.json similarity index 100% rename from templates/research-loop/package.json rename to reference-apps/research-loop/package.json diff --git a/templates/research-loop/packs/primary/pack.yaml b/reference-apps/research-loop/packs/primary/pack.yaml similarity index 100% rename from templates/research-loop/packs/primary/pack.yaml rename to reference-apps/research-loop/packs/primary/pack.yaml diff --git a/templates/research-loop/render.yaml b/reference-apps/research-loop/render.yaml similarity index 100% rename from templates/research-loop/render.yaml rename to reference-apps/research-loop/render.yaml diff --git a/templates/research-loop/schemas/experiment-receipt.schema.json b/reference-apps/research-loop/schemas/experiment-receipt.schema.json similarity index 100% rename from templates/research-loop/schemas/experiment-receipt.schema.json rename to reference-apps/research-loop/schemas/experiment-receipt.schema.json diff --git a/templates/research-loop/scripts/audit-prod.mjs b/reference-apps/research-loop/scripts/audit-prod.mjs similarity index 100% rename from templates/research-loop/scripts/audit-prod.mjs rename to reference-apps/research-loop/scripts/audit-prod.mjs diff --git a/templates/research-loop/scripts/browser-proof.mjs b/reference-apps/research-loop/scripts/browser-proof.mjs similarity index 100% rename from templates/research-loop/scripts/browser-proof.mjs rename to reference-apps/research-loop/scripts/browser-proof.mjs diff --git a/templates/research-loop/scripts/check.mjs b/reference-apps/research-loop/scripts/check.mjs similarity index 100% rename from templates/research-loop/scripts/check.mjs rename to reference-apps/research-loop/scripts/check.mjs diff --git a/templates/research-loop/scripts/demo.mjs b/reference-apps/research-loop/scripts/demo.mjs similarity index 100% rename from templates/research-loop/scripts/demo.mjs rename to reference-apps/research-loop/scripts/demo.mjs diff --git a/templates/research-loop/scripts/eval.mjs b/reference-apps/research-loop/scripts/eval.mjs similarity index 100% rename from templates/research-loop/scripts/eval.mjs rename to reference-apps/research-loop/scripts/eval.mjs diff --git a/templates/research-loop/scripts/lib/friction.mjs b/reference-apps/research-loop/scripts/lib/friction.mjs similarity index 100% rename from templates/research-loop/scripts/lib/friction.mjs rename to reference-apps/research-loop/scripts/lib/friction.mjs diff --git a/templates/research-loop/scripts/live-smoke.mjs b/reference-apps/research-loop/scripts/live-smoke.mjs similarity index 100% rename from templates/research-loop/scripts/live-smoke.mjs rename to reference-apps/research-loop/scripts/live-smoke.mjs diff --git a/templates/research-loop/scripts/phase.mjs b/reference-apps/research-loop/scripts/phase.mjs similarity index 100% rename from templates/research-loop/scripts/phase.mjs rename to reference-apps/research-loop/scripts/phase.mjs diff --git a/templates/research-loop/scripts/proof.mjs b/reference-apps/research-loop/scripts/proof.mjs similarity index 100% rename from templates/research-loop/scripts/proof.mjs rename to reference-apps/research-loop/scripts/proof.mjs diff --git a/templates/research-loop/scripts/timeline.mjs b/reference-apps/research-loop/scripts/timeline.mjs similarity index 100% rename from templates/research-loop/scripts/timeline.mjs rename to reference-apps/research-loop/scripts/timeline.mjs diff --git a/templates/research-loop/test/experiment-loop.test.mjs b/reference-apps/research-loop/test/experiment-loop.test.mjs similarity index 100% rename from templates/research-loop/test/experiment-loop.test.mjs rename to reference-apps/research-loop/test/experiment-loop.test.mjs diff --git a/templates/smb-lending-fde/.dockerignore b/reference-apps/smb-lending-fde/.dockerignore similarity index 100% rename from templates/smb-lending-fde/.dockerignore rename to reference-apps/smb-lending-fde/.dockerignore diff --git a/templates/smb-lending-fde/.env.example b/reference-apps/smb-lending-fde/.env.example similarity index 100% rename from templates/smb-lending-fde/.env.example rename to reference-apps/smb-lending-fde/.env.example diff --git a/templates/smb-lending-fde/.gitattributes b/reference-apps/smb-lending-fde/.gitattributes similarity index 100% rename from templates/smb-lending-fde/.gitattributes rename to reference-apps/smb-lending-fde/.gitattributes diff --git a/templates/smb-lending-fde/AGENTS.md b/reference-apps/smb-lending-fde/AGENTS.md similarity index 100% rename from templates/smb-lending-fde/AGENTS.md rename to reference-apps/smb-lending-fde/AGENTS.md diff --git a/templates/smb-lending-fde/Dockerfile b/reference-apps/smb-lending-fde/Dockerfile similarity index 100% rename from templates/smb-lending-fde/Dockerfile rename to reference-apps/smb-lending-fde/Dockerfile diff --git a/templates/smb-lending-fde/README.md b/reference-apps/smb-lending-fde/README.md similarity index 100% rename from templates/smb-lending-fde/README.md rename to reference-apps/smb-lending-fde/README.md diff --git a/templates/smb-lending-fde/adw/workflows/launch.yaml b/reference-apps/smb-lending-fde/adw/workflows/launch.yaml similarity index 100% rename from templates/smb-lending-fde/adw/workflows/launch.yaml rename to reference-apps/smb-lending-fde/adw/workflows/launch.yaml diff --git a/templates/smb-lending-fde/agent/experiment-loop.mjs b/reference-apps/smb-lending-fde/agent/experiment-loop.mjs similarity index 100% rename from templates/smb-lending-fde/agent/experiment-loop.mjs rename to reference-apps/smb-lending-fde/agent/experiment-loop.mjs diff --git a/templates/smb-lending-fde/agent/instructions.md b/reference-apps/smb-lending-fde/agent/instructions.md similarity index 100% rename from templates/smb-lending-fde/agent/instructions.md rename to reference-apps/smb-lending-fde/agent/instructions.md diff --git a/templates/smb-lending-fde/agent/planner/planning-policy.yaml b/reference-apps/smb-lending-fde/agent/planner/planning-policy.yaml similarity index 100% rename from templates/smb-lending-fde/agent/planner/planning-policy.yaml rename to reference-apps/smb-lending-fde/agent/planner/planning-policy.yaml diff --git a/templates/smb-lending-fde/agent/policies/budgets.yaml b/reference-apps/smb-lending-fde/agent/policies/budgets.yaml similarity index 100% rename from templates/smb-lending-fde/agent/policies/budgets.yaml rename to reference-apps/smb-lending-fde/agent/policies/budgets.yaml diff --git a/templates/smb-lending-fde/agent/policies/permissions.yaml b/reference-apps/smb-lending-fde/agent/policies/permissions.yaml similarity index 100% rename from templates/smb-lending-fde/agent/policies/permissions.yaml rename to reference-apps/smb-lending-fde/agent/policies/permissions.yaml diff --git a/templates/smb-lending-fde/agent/process-graph.mjs b/reference-apps/smb-lending-fde/agent/process-graph.mjs similarity index 100% rename from templates/smb-lending-fde/agent/process-graph.mjs rename to reference-apps/smb-lending-fde/agent/process-graph.mjs diff --git a/templates/smb-lending-fde/agent/runtime/smb-lending-pack-registry.mjs b/reference-apps/smb-lending-fde/agent/runtime/smb-lending-pack-registry.mjs similarity index 100% rename from templates/smb-lending-fde/agent/runtime/smb-lending-pack-registry.mjs rename to reference-apps/smb-lending-fde/agent/runtime/smb-lending-pack-registry.mjs diff --git a/templates/smb-lending-fde/agent/runtime/stable-digest.mjs b/reference-apps/smb-lending-fde/agent/runtime/stable-digest.mjs similarity index 100% rename from templates/smb-lending-fde/agent/runtime/stable-digest.mjs rename to reference-apps/smb-lending-fde/agent/runtime/stable-digest.mjs diff --git a/templates/smb-lending-fde/agent/skills/autoresearch-live/SKILL.md b/reference-apps/smb-lending-fde/agent/skills/autoresearch-live/SKILL.md similarity index 100% rename from templates/smb-lending-fde/agent/skills/autoresearch-live/SKILL.md rename to reference-apps/smb-lending-fde/agent/skills/autoresearch-live/SKILL.md diff --git a/templates/smb-lending-fde/agent/subagents/reviewer/agent.yaml b/reference-apps/smb-lending-fde/agent/subagents/reviewer/agent.yaml similarity index 100% rename from templates/smb-lending-fde/agent/subagents/reviewer/agent.yaml rename to reference-apps/smb-lending-fde/agent/subagents/reviewer/agent.yaml diff --git a/templates/smb-lending-fde/agent/tools/approve-proposal.mjs b/reference-apps/smb-lending-fde/agent/tools/approve-proposal.mjs similarity index 100% rename from templates/smb-lending-fde/agent/tools/approve-proposal.mjs rename to reference-apps/smb-lending-fde/agent/tools/approve-proposal.mjs diff --git a/templates/smb-lending-fde/agent/tools/inspect-lending-file.mjs b/reference-apps/smb-lending-fde/agent/tools/inspect-lending-file.mjs similarity index 100% rename from templates/smb-lending-fde/agent/tools/inspect-lending-file.mjs rename to reference-apps/smb-lending-fde/agent/tools/inspect-lending-file.mjs diff --git a/templates/smb-lending-fde/agent/tools/propose-document-request.mjs b/reference-apps/smb-lending-fde/agent/tools/propose-document-request.mjs similarity index 100% rename from templates/smb-lending-fde/agent/tools/propose-document-request.mjs rename to reference-apps/smb-lending-fde/agent/tools/propose-document-request.mjs diff --git a/templates/smb-lending-fde/agent/validators/document-request-is-missing.mjs b/reference-apps/smb-lending-fde/agent/validators/document-request-is-missing.mjs similarity index 100% rename from templates/smb-lending-fde/agent/validators/document-request-is-missing.mjs rename to reference-apps/smb-lending-fde/agent/validators/document-request-is-missing.mjs diff --git a/templates/smb-lending-fde/agent/validators/human-authority-boundary.mjs b/reference-apps/smb-lending-fde/agent/validators/human-authority-boundary.mjs similarity index 100% rename from templates/smb-lending-fde/agent/validators/human-authority-boundary.mjs rename to reference-apps/smb-lending-fde/agent/validators/human-authority-boundary.mjs diff --git a/templates/smb-lending-fde/agent/validators/receipt-is-secret-free.mjs b/reference-apps/smb-lending-fde/agent/validators/receipt-is-secret-free.mjs similarity index 100% rename from templates/smb-lending-fde/agent/validators/receipt-is-secret-free.mjs rename to reference-apps/smb-lending-fde/agent/validators/receipt-is-secret-free.mjs diff --git a/templates/smb-lending-fde/agent/validators/synthetic-data-only.mjs b/reference-apps/smb-lending-fde/agent/validators/synthetic-data-only.mjs similarity index 100% rename from templates/smb-lending-fde/agent/validators/synthetic-data-only.mjs rename to reference-apps/smb-lending-fde/agent/validators/synthetic-data-only.mjs diff --git a/templates/smb-lending-fde/apps/web/public/app.js b/reference-apps/smb-lending-fde/apps/web/public/app.js similarity index 100% rename from templates/smb-lending-fde/apps/web/public/app.js rename to reference-apps/smb-lending-fde/apps/web/public/app.js diff --git a/templates/smb-lending-fde/apps/web/public/index.html b/reference-apps/smb-lending-fde/apps/web/public/index.html similarity index 100% rename from templates/smb-lending-fde/apps/web/public/index.html rename to reference-apps/smb-lending-fde/apps/web/public/index.html diff --git a/templates/smb-lending-fde/apps/web/public/styles.css b/reference-apps/smb-lending-fde/apps/web/public/styles.css similarity index 100% rename from templates/smb-lending-fde/apps/web/public/styles.css rename to reference-apps/smb-lending-fde/apps/web/public/styles.css diff --git a/templates/smb-lending-fde/apps/web/server.mjs b/reference-apps/smb-lending-fde/apps/web/server.mjs similarity index 100% rename from templates/smb-lending-fde/apps/web/server.mjs rename to reference-apps/smb-lending-fde/apps/web/server.mjs diff --git a/templates/smb-lending-fde/backend/authority/local-only.mjs b/reference-apps/smb-lending-fde/backend/authority/local-only.mjs similarity index 100% rename from templates/smb-lending-fde/backend/authority/local-only.mjs rename to reference-apps/smb-lending-fde/backend/authority/local-only.mjs diff --git a/templates/smb-lending-fde/backend/authority/runtime-policy.mjs b/reference-apps/smb-lending-fde/backend/authority/runtime-policy.mjs similarity index 100% rename from templates/smb-lending-fde/backend/authority/runtime-policy.mjs rename to reference-apps/smb-lending-fde/backend/authority/runtime-policy.mjs diff --git a/templates/smb-lending-fde/backend/filesystem/store.mjs b/reference-apps/smb-lending-fde/backend/filesystem/store.mjs similarity index 100% rename from templates/smb-lending-fde/backend/filesystem/store.mjs rename to reference-apps/smb-lending-fde/backend/filesystem/store.mjs diff --git a/templates/smb-lending-fde/deployment/PRODUCTION_BLOCKED.md b/reference-apps/smb-lending-fde/deployment/PRODUCTION_BLOCKED.md similarity index 100% rename from templates/smb-lending-fde/deployment/PRODUCTION_BLOCKED.md rename to reference-apps/smb-lending-fde/deployment/PRODUCTION_BLOCKED.md diff --git a/templates/smb-lending-fde/evals/deterministic-smoke.json b/reference-apps/smb-lending-fde/evals/deterministic-smoke.json similarity index 100% rename from templates/smb-lending-fde/evals/deterministic-smoke.json rename to reference-apps/smb-lending-fde/evals/deterministic-smoke.json diff --git a/templates/smb-lending-fde/evals/human-authority-boundary.json b/reference-apps/smb-lending-fde/evals/human-authority-boundary.json similarity index 100% rename from templates/smb-lending-fde/evals/human-authority-boundary.json rename to reference-apps/smb-lending-fde/evals/human-authority-boundary.json diff --git a/templates/smb-lending-fde/evals/interrupted-proposal-recovery.json b/reference-apps/smb-lending-fde/evals/interrupted-proposal-recovery.json similarity index 100% rename from templates/smb-lending-fde/evals/interrupted-proposal-recovery.json rename to reference-apps/smb-lending-fde/evals/interrupted-proposal-recovery.json diff --git a/templates/smb-lending-fde/fixtures/heldout/harbor-view-medical-source-packet.json b/reference-apps/smb-lending-fde/fixtures/heldout/harbor-view-medical-source-packet.json similarity index 100% rename from templates/smb-lending-fde/fixtures/heldout/harbor-view-medical-source-packet.json rename to reference-apps/smb-lending-fde/fixtures/heldout/harbor-view-medical-source-packet.json diff --git a/templates/smb-lending-fde/fixtures/primary/bay-hearth-source-packet.json b/reference-apps/smb-lending-fde/fixtures/primary/bay-hearth-source-packet.json similarity index 100% rename from templates/smb-lending-fde/fixtures/primary/bay-hearth-source-packet.json rename to reference-apps/smb-lending-fde/fixtures/primary/bay-hearth-source-packet.json diff --git a/templates/smb-lending-fde/gitignore.template b/reference-apps/smb-lending-fde/gitignore.template similarity index 100% rename from templates/smb-lending-fde/gitignore.template rename to reference-apps/smb-lending-fde/gitignore.template diff --git a/templates/smb-lending-fde/hackathon.yaml b/reference-apps/smb-lending-fde/hackathon.yaml similarity index 100% rename from templates/smb-lending-fde/hackathon.yaml rename to reference-apps/smb-lending-fde/hackathon.yaml diff --git a/templates/smb-lending-fde/integrations/pi-ai/provider.mjs b/reference-apps/smb-lending-fde/integrations/pi-ai/provider.mjs similarity index 100% rename from templates/smb-lending-fde/integrations/pi-ai/provider.mjs rename to reference-apps/smb-lending-fde/integrations/pi-ai/provider.mjs diff --git a/templates/smb-lending-fde/nodeagent.yaml b/reference-apps/smb-lending-fde/nodeagent.yaml similarity index 100% rename from templates/smb-lending-fde/nodeagent.yaml rename to reference-apps/smb-lending-fde/nodeagent.yaml diff --git a/templates/smb-lending-fde/nodekit.yaml b/reference-apps/smb-lending-fde/nodekit.yaml similarity index 100% rename from templates/smb-lending-fde/nodekit.yaml rename to reference-apps/smb-lending-fde/nodekit.yaml diff --git a/templates/smb-lending-fde/package.json b/reference-apps/smb-lending-fde/package.json similarity index 100% rename from templates/smb-lending-fde/package.json rename to reference-apps/smb-lending-fde/package.json diff --git a/templates/smb-lending-fde/packs/primary/pack.yaml b/reference-apps/smb-lending-fde/packs/primary/pack.yaml similarity index 100% rename from templates/smb-lending-fde/packs/primary/pack.yaml rename to reference-apps/smb-lending-fde/packs/primary/pack.yaml diff --git a/templates/smb-lending-fde/proof/README.md b/reference-apps/smb-lending-fde/proof/README.md similarity index 100% rename from templates/smb-lending-fde/proof/README.md rename to reference-apps/smb-lending-fde/proof/README.md diff --git a/templates/smb-lending-fde/schemas/experiment-receipt.schema.json b/reference-apps/smb-lending-fde/schemas/experiment-receipt.schema.json similarity index 100% rename from templates/smb-lending-fde/schemas/experiment-receipt.schema.json rename to reference-apps/smb-lending-fde/schemas/experiment-receipt.schema.json diff --git a/templates/smb-lending-fde/scripts/audit-prod.mjs b/reference-apps/smb-lending-fde/scripts/audit-prod.mjs similarity index 100% rename from templates/smb-lending-fde/scripts/audit-prod.mjs rename to reference-apps/smb-lending-fde/scripts/audit-prod.mjs diff --git a/templates/smb-lending-fde/scripts/benchmark.mjs b/reference-apps/smb-lending-fde/scripts/benchmark.mjs similarity index 100% rename from templates/smb-lending-fde/scripts/benchmark.mjs rename to reference-apps/smb-lending-fde/scripts/benchmark.mjs diff --git a/templates/smb-lending-fde/scripts/browser-proof.mjs b/reference-apps/smb-lending-fde/scripts/browser-proof.mjs similarity index 100% rename from templates/smb-lending-fde/scripts/browser-proof.mjs rename to reference-apps/smb-lending-fde/scripts/browser-proof.mjs diff --git a/templates/smb-lending-fde/scripts/check.mjs b/reference-apps/smb-lending-fde/scripts/check.mjs similarity index 100% rename from templates/smb-lending-fde/scripts/check.mjs rename to reference-apps/smb-lending-fde/scripts/check.mjs diff --git a/templates/smb-lending-fde/scripts/demo.mjs b/reference-apps/smb-lending-fde/scripts/demo.mjs similarity index 100% rename from templates/smb-lending-fde/scripts/demo.mjs rename to reference-apps/smb-lending-fde/scripts/demo.mjs diff --git a/templates/smb-lending-fde/scripts/eval.mjs b/reference-apps/smb-lending-fde/scripts/eval.mjs similarity index 100% rename from templates/smb-lending-fde/scripts/eval.mjs rename to reference-apps/smb-lending-fde/scripts/eval.mjs diff --git a/templates/smb-lending-fde/scripts/lib/application-identity.mjs b/reference-apps/smb-lending-fde/scripts/lib/application-identity.mjs similarity index 100% rename from templates/smb-lending-fde/scripts/lib/application-identity.mjs rename to reference-apps/smb-lending-fde/scripts/lib/application-identity.mjs diff --git a/templates/smb-lending-fde/scripts/lib/candidate.mjs b/reference-apps/smb-lending-fde/scripts/lib/candidate.mjs similarity index 100% rename from templates/smb-lending-fde/scripts/lib/candidate.mjs rename to reference-apps/smb-lending-fde/scripts/lib/candidate.mjs diff --git a/templates/smb-lending-fde/scripts/lib/friction.mjs b/reference-apps/smb-lending-fde/scripts/lib/friction.mjs similarity index 100% rename from templates/smb-lending-fde/scripts/lib/friction.mjs rename to reference-apps/smb-lending-fde/scripts/lib/friction.mjs diff --git a/templates/smb-lending-fde/scripts/live-smoke.mjs b/reference-apps/smb-lending-fde/scripts/live-smoke.mjs similarity index 100% rename from templates/smb-lending-fde/scripts/live-smoke.mjs rename to reference-apps/smb-lending-fde/scripts/live-smoke.mjs diff --git a/templates/smb-lending-fde/scripts/phase.mjs b/reference-apps/smb-lending-fde/scripts/phase.mjs similarity index 100% rename from templates/smb-lending-fde/scripts/phase.mjs rename to reference-apps/smb-lending-fde/scripts/phase.mjs diff --git a/templates/smb-lending-fde/scripts/proof.mjs b/reference-apps/smb-lending-fde/scripts/proof.mjs similarity index 100% rename from templates/smb-lending-fde/scripts/proof.mjs rename to reference-apps/smb-lending-fde/scripts/proof.mjs diff --git a/templates/smb-lending-fde/scripts/timeline.mjs b/reference-apps/smb-lending-fde/scripts/timeline.mjs similarity index 100% rename from templates/smb-lending-fde/scripts/timeline.mjs rename to reference-apps/smb-lending-fde/scripts/timeline.mjs diff --git a/templates/smb-lending-fde/scripts/verify-receipts.mjs b/reference-apps/smb-lending-fde/scripts/verify-receipts.mjs similarity index 100% rename from templates/smb-lending-fde/scripts/verify-receipts.mjs rename to reference-apps/smb-lending-fde/scripts/verify-receipts.mjs diff --git a/templates/smb-lending-fde/test/experiment-loop.test.mjs b/reference-apps/smb-lending-fde/test/experiment-loop.test.mjs similarity index 100% rename from templates/smb-lending-fde/test/experiment-loop.test.mjs rename to reference-apps/smb-lending-fde/test/experiment-loop.test.mjs diff --git a/templates/smb-lending-fde/test/network-boundary.test.mjs b/reference-apps/smb-lending-fde/test/network-boundary.test.mjs similarity index 100% rename from templates/smb-lending-fde/test/network-boundary.test.mjs rename to reference-apps/smb-lending-fde/test/network-boundary.test.mjs diff --git a/templates/smb-lending-fde/test/pack-registry.test.mjs b/reference-apps/smb-lending-fde/test/pack-registry.test.mjs similarity index 100% rename from templates/smb-lending-fde/test/pack-registry.test.mjs rename to reference-apps/smb-lending-fde/test/pack-registry.test.mjs diff --git a/templates/smb-lending-fde/test/process-graph.test.mjs b/reference-apps/smb-lending-fde/test/process-graph.test.mjs similarity index 100% rename from templates/smb-lending-fde/test/process-graph.test.mjs rename to reference-apps/smb-lending-fde/test/process-graph.test.mjs diff --git a/templates/smb-lending-fde/test/runtime-policy.test.mjs b/reference-apps/smb-lending-fde/test/runtime-policy.test.mjs similarity index 100% rename from templates/smb-lending-fde/test/runtime-policy.test.mjs rename to reference-apps/smb-lending-fde/test/runtime-policy.test.mjs diff --git a/schemas/nodekit.assumption.v1.schema.json b/schemas/nodekit.assumption.v1.schema.json new file mode 100644 index 0000000..ee80563 --- /dev/null +++ b/schemas/nodekit.assumption.v1.schema.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/HomenShum/node-platform/blob/main/schemas/nodekit.assumption.v1.schema.json", + "type": "object", + "required": ["schemaVersion", "id", "statement", "scope", "status", "supportingEvidenceIds", "contradictingEvidenceIds"], + "properties": { + "schemaVersion": { "const": "nodekit.assumption/v1" }, + "id": { "$ref": "#/$defs/id" }, + "statement": { "$ref": "#/$defs/text" }, + "scope": { "$ref": "#/$defs/scope" }, + "status": { "enum": ["untested", "supported", "scope-limited", "disproven", "superseded"] }, + "introducedByEventId": { "$ref": "#/$defs/id" }, + "invalidatedByEventId": { "$ref": "#/$defs/id" }, + "supportingEvidenceIds": { "$ref": "#/$defs/ids" }, + "contradictingEvidenceIds": { "$ref": "#/$defs/ids" } + }, + "$defs": { + "id": { "type": "string", "pattern": "^[a-z][a-z0-9._:-]+$" }, + "text": { "type": "string", "minLength": 1 }, + "ids": { "type": "array", "items": { "$ref": "#/$defs/id" }, "uniqueItems": true }, + "scope": { "type": "object", "properties": { "applications": { "$ref": "#/$defs/strings" }, "packages": { "$ref": "#/$defs/strings" }, "taskFamilies": { "$ref": "#/$defs/strings" } }, "minProperties": 1, "additionalProperties": false }, + "strings": { "type": "array", "items": { "$ref": "#/$defs/text" }, "uniqueItems": true } + }, + "additionalProperties": false +} diff --git a/schemas/nodekit.canary-receipt.v1.schema.json b/schemas/nodekit.canary-receipt.v1.schema.json new file mode 100644 index 0000000..0347da2 --- /dev/null +++ b/schemas/nodekit.canary-receipt.v1.schema.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/HomenShum/node-platform/blob/main/schemas/nodekit.canary-receipt.v1.schema.json", + "title": "NodeKit fresh-agent canary receipt", + "type": "object", + "required": ["schemaVersion", "canaryId", "candidateId", "freshContext", "humanReprompts", "substantiveChanges", "protectedEvaluatorUnchanged", "checksPassed", "evidenceRefs", "passed"], + "properties": { + "schemaVersion": { "const": "nodekit.canary-receipt/v1" }, + "canaryId": { "type": "string", "minLength": 1 }, + "candidateId": { "type": "string", "minLength": 1 }, + "freshContext": { "const": true }, + "humanReprompts": { "const": 0 }, + "substantiveChanges": { "const": true }, + "protectedEvaluatorUnchanged": { "const": true }, + "checksPassed": { "const": true }, + "evidenceRefs": { "type": "array", "minItems": 1, "items": { "type": "string", "minLength": 1 }, "uniqueItems": true }, + "passed": { "const": true } + }, + "additionalProperties": false +} diff --git a/schemas/nodekit.design-decision.v1.schema.json b/schemas/nodekit.design-decision.v1.schema.json new file mode 100644 index 0000000..cc15405 --- /dev/null +++ b/schemas/nodekit.design-decision.v1.schema.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/HomenShum/node-platform/blob/main/schemas/nodekit.design-decision.v1.schema.json", + "title": "NodeKit independent frontend direction decision", + "type": "object", + "required": ["schemaVersion", "decisionId", "directionSetId", "candidateIds", "selectedCandidateId", "selectionReasons", "criticReceiptIds", "humanDecision", "independentSelector", "protectedEvaluatorUnchanged", "decidedAt"], + "properties": { + "schemaVersion": { "const": "nodekit.design-decision/v1" }, + "decisionId": { "type": "string", "minLength": 1 }, + "directionSetId": { "type": "string", "minLength": 1 }, + "candidateIds": { "type": "array", "minItems": 3, "maxItems": 3, "items": { "type": "string", "minLength": 1 }, "uniqueItems": true }, + "selectedCandidateId": { "type": "string", "minLength": 1 }, + "selectionReasons": { "type": "array", "minItems": 1, "items": { "type": "string", "minLength": 1 }, "uniqueItems": true }, + "criticReceiptIds": { "type": "array", "items": { "type": "string", "minLength": 1 }, "uniqueItems": true }, + "humanDecision": { "type": "boolean" }, + "independentSelector": { "const": true }, + "protectedEvaluatorUnchanged": { "const": true }, + "decidedAt": { "type": "string", "pattern": "^\\d{4}-\\d{2}-\\d{2}T" } + }, + "additionalProperties": false +} diff --git a/schemas/nodekit.evolution-adoption.v1.schema.json b/schemas/nodekit.evolution-adoption.v1.schema.json new file mode 100644 index 0000000..e1b7a27 --- /dev/null +++ b/schemas/nodekit.evolution-adoption.v1.schema.json @@ -0,0 +1,31 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/HomenShum/node-platform/blob/main/schemas/nodekit.evolution-adoption.v1.schema.json", + "type": "object", + "required": ["schemaVersion", "id", "invariantId", "consumer", "adoptedAtCommit", "evidenceIds", "status"], + "properties": { + "schemaVersion": { "const": "nodekit.evolution-adoption/v1" }, + "id": { "$ref": "#/$defs/id" }, + "invariantId": { "$ref": "#/$defs/id" }, + "consumer": { + "type": "object", + "required": ["repository"], + "properties": { + "repository": { "$ref": "#/$defs/text" }, + "application": { "$ref": "#/$defs/text" }, + "package": { "$ref": "#/$defs/text" } + }, + "additionalProperties": false + }, + "adoptedAtCommit": { "$ref": "#/$defs/sha" }, + "evidenceIds": { "$ref": "#/$defs/ids" }, + "status": { "enum": ["declared", "verified", "drifted", "removed"] } + }, + "$defs": { + "id": { "type": "string", "pattern": "^[a-z][a-z0-9._:-]+$" }, + "text": { "type": "string", "minLength": 1 }, + "ids": { "type": "array", "items": { "$ref": "#/$defs/id" }, "uniqueItems": true }, + "sha": { "type": "string", "pattern": "^[a-f0-9]{40}$" } + }, + "additionalProperties": false +} diff --git a/schemas/nodekit.evolution-event.v1.schema.json b/schemas/nodekit.evolution-event.v1.schema.json new file mode 100644 index 0000000..a4bce0c --- /dev/null +++ b/schemas/nodekit.evolution-event.v1.schema.json @@ -0,0 +1,67 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/HomenShum/node-platform/blob/main/schemas/nodekit.evolution-event.v1.schema.json", + "title": "NodeKit material evolution event", + "type": "object", + "required": ["schemaVersion", "id", "projectId", "repository", "source", "track", "category", "challenge", "resolution", "assumptionIds", "invariantIds", "evidenceIds", "knownLimitations", "interpretation"], + "properties": { + "schemaVersion": { "const": "nodekit.evolution-event/v1" }, + "id": { "$ref": "#/$defs/id" }, + "projectId": { "$ref": "#/$defs/nonEmpty" }, + "repository": { "$ref": "#/$defs/nonEmpty" }, + "source": { + "type": "object", + "required": ["commitSha", "occurredAt"], + "properties": { + "commitSha": { "$ref": "#/$defs/sha" }, + "pullRequest": { "type": "integer", "minimum": 1 }, + "issue": { "type": "integer", "minimum": 1 }, + "occurredAt": { "$ref": "#/$defs/time" } + }, + "additionalProperties": false + }, + "track": { "enum": ["product", "architecture", "harness"] }, + "category": { "enum": ["workflow", "schema", "runtime", "agent", "ui", "evaluation", "security", "release"] }, + "challenge": { "$ref": "#/$defs/nonEmpty" }, + "observedFailure": { "$ref": "#/$defs/nonEmpty" }, + "resolution": { "$ref": "#/$defs/nonEmpty" }, + "assumptionIds": { "$ref": "#/$defs/ids" }, + "invariantIds": { "$ref": "#/$defs/ids" }, + "evidenceIds": { "$ref": "#/$defs/nonEmptyIds" }, + "predecessorIds": { "$ref": "#/$defs/ids" }, + "supersedesIds": { "$ref": "#/$defs/ids" }, + "extractedFromIds": { "$ref": "#/$defs/ids" }, + "modelContext": { + "type": "object", + "properties": { + "requestedRoute": { "$ref": "#/$defs/nonEmpty" }, + "resolvedModel": { "$ref": "#/$defs/nonEmpty" }, + "provider": { "$ref": "#/$defs/nonEmpty" }, + "harnessVersion": { "$ref": "#/$defs/nonEmpty" }, + "promptHash": { "$ref": "#/$defs/hash" } + }, + "additionalProperties": false + }, + "knownLimitations": { "type": "array", "items": { "$ref": "#/$defs/nonEmpty" }, "uniqueItems": true }, + "interpretation": { + "type": "object", + "required": ["status", "reviewedBy", "reviewedAt"], + "properties": { + "status": { "const": "human-reviewed" }, + "reviewedBy": { "$ref": "#/$defs/nonEmpty" }, + "reviewedAt": { "$ref": "#/$defs/time" } + }, + "additionalProperties": false + } + }, + "$defs": { + "nonEmpty": { "type": "string", "minLength": 1 }, + "id": { "type": "string", "pattern": "^[a-z][a-z0-9._:-]+$" }, + "ids": { "type": "array", "items": { "$ref": "#/$defs/id" }, "uniqueItems": true }, + "nonEmptyIds": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/id" }, "uniqueItems": true }, + "sha": { "type": "string", "pattern": "^[a-f0-9]{40}$" }, + "hash": { "type": "string", "pattern": "^[a-f0-9]{64}$" }, + "time": { "type": "string", "pattern": "^\\d{4}-\\d{2}-\\d{2}T" } + }, + "additionalProperties": false +} diff --git a/schemas/nodekit.evolution-evidence.v1.schema.json b/schemas/nodekit.evolution-evidence.v1.schema.json new file mode 100644 index 0000000..30c155c --- /dev/null +++ b/schemas/nodekit.evolution-evidence.v1.schema.json @@ -0,0 +1,29 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/HomenShum/node-platform/blob/main/schemas/nodekit.evolution-evidence.v1.schema.json", + "type": "object", + "required": ["schemaVersion", "id", "kind", "artifactRef", "sha256", "sourceCommit", "generatedAt", "verifiesInvariantIds", "result"], + "properties": { + "schemaVersion": { "const": "nodekit.evolution-evidence/v1" }, + "id": { "$ref": "#/$defs/id" }, + "kind": { "enum": ["test", "benchmark", "code", "trace", "screenshot", "interaction-clip", "artifact", "deployment", "human-review"] }, + "artifactRef": { "$ref": "#/$defs/text" }, + "sha256": { "$ref": "#/$defs/hash" }, + "sourceCommit": { "$ref": "#/$defs/sha" }, + "generatedAt": { "$ref": "#/$defs/time" }, + "command": { "$ref": "#/$defs/text" }, + "environment": { "type": "object", "additionalProperties": { "type": "string" } }, + "verifiesInvariantIds": { "$ref": "#/$defs/ids" }, + "nodeProofReceiptId": { "$ref": "#/$defs/text" }, + "result": { "enum": ["pass", "fail", "partial", "informational"] } + }, + "$defs": { + "id": { "type": "string", "pattern": "^[a-z][a-z0-9._:-]+$" }, + "text": { "type": "string", "minLength": 1 }, + "ids": { "type": "array", "items": { "$ref": "#/$defs/id" }, "uniqueItems": true }, + "hash": { "type": "string", "pattern": "^[a-f0-9]{64}$" }, + "sha": { "type": "string", "pattern": "^[a-f0-9]{40}$" }, + "time": { "type": "string", "pattern": "^\\d{4}-\\d{2}-\\d{2}T" } + }, + "additionalProperties": false +} diff --git a/schemas/nodekit.frontend-benchmark.v1.schema.json b/schemas/nodekit.frontend-benchmark.v1.schema.json new file mode 100644 index 0000000..34229c5 --- /dev/null +++ b/schemas/nodekit.frontend-benchmark.v1.schema.json @@ -0,0 +1,54 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/HomenShum/node-platform/blob/main/schemas/nodekit.frontend-benchmark.v1.schema.json", + "title": "NodeKit Frontend Gym benchmark", + "type": "object", + "required": ["schemaVersion", "benchmarkId", "directionSet", "scores", "pairwiseResults", "criticIndependent", "browserChecksPassed", "accessibilityPassed", "overflowPassed", "majorFindings", "freshUserEvidenceRefs"], + "properties": { + "schemaVersion": { "const": "nodekit.frontend-benchmark/v1" }, + "benchmarkId": { "type": "string", "minLength": 1 }, + "directionSet": { "type": "string", "minLength": 1 }, + "scores": { + "type": "object", + "minProperties": 3, + "additionalProperties": { + "type": "object", + "required": ["primaryJobClarity", "artifactDominance", "workflowHierarchy", "agentLegibility", "reviewSafety", "mobileOperation", "domainAppropriateness", "visualQuality"], + "properties": { + "primaryJobClarity": { "$ref": "#/$defs/score" }, + "artifactDominance": { "$ref": "#/$defs/score" }, + "workflowHierarchy": { "$ref": "#/$defs/score" }, + "agentLegibility": { "$ref": "#/$defs/score" }, + "reviewSafety": { "$ref": "#/$defs/score" }, + "mobileOperation": { "$ref": "#/$defs/score" }, + "domainAppropriateness": { "$ref": "#/$defs/score" }, + "visualQuality": { "$ref": "#/$defs/score" } + }, + "additionalProperties": false + } + }, + "pairwiseResults": { + "type": "array", + "minItems": 3, + "items": { + "type": "object", + "required": ["left", "right", "winner", "evidenceRefs"], + "properties": { + "left": { "type": "string", "minLength": 1 }, + "right": { "type": "string", "minLength": 1 }, + "winner": { "type": "string", "minLength": 1 }, + "evidenceRefs": { "type": "array", "minItems": 1, "items": { "type": "string", "minLength": 1 }, "uniqueItems": true } + }, + "additionalProperties": false + } + }, + "criticIndependent": { "const": true }, + "browserChecksPassed": { "type": "boolean" }, + "accessibilityPassed": { "type": "boolean" }, + "overflowPassed": { "type": "boolean" }, + "majorFindings": { "type": "array", "items": { "type": "string", "minLength": 1 }, "uniqueItems": true }, + "freshUserEvidenceRefs": { "type": "array", "items": { "type": "string", "minLength": 1 }, "uniqueItems": true } + }, + "$defs": { "score": { "type": "number", "minimum": 0, "maximum": 1 } }, + "additionalProperties": false +} diff --git a/schemas/nodekit.frontend-direction-set.v1.schema.json b/schemas/nodekit.frontend-direction-set.v1.schema.json new file mode 100644 index 0000000..972cf07 --- /dev/null +++ b/schemas/nodekit.frontend-direction-set.v1.schema.json @@ -0,0 +1,33 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/HomenShum/node-platform/blob/main/schemas/nodekit.frontend-direction-set.v1.schema.json", + "title": "NodeKit three-direction frontend hypothesis set", + "type": "object", + "required": ["schemaVersion", "directionSetId", "productContractHash", "routeHash", "blind", "requiredViews", "candidates"], + "properties": { + "schemaVersion": { "const": "nodekit.frontend-direction-set/v1" }, + "directionSetId": { "type": "string", "minLength": 1 }, + "productContractHash": { "$ref": "#/$defs/hash" }, + "routeHash": { "$ref": "#/$defs/hash" }, + "blind": { "const": true }, + "requiredViews": { "type": "array", "minItems": 6, "items": { "type": "string", "minLength": 1 }, "uniqueItems": true }, + "candidates": { + "type": "array", + "minItems": 3, + "maxItems": 3, + "items": { + "type": "object", + "required": ["candidateId", "archetype", "status", "viewEvidence"], + "properties": { + "candidateId": { "type": "string", "pattern": "^direction-[abc]$" }, + "archetype": { "enum": ["collaborative-workspace", "artifact-studio", "domain-native"] }, + "status": { "enum": ["planned", "rendered", "evaluated"] }, + "viewEvidence": { "type": "object", "additionalProperties": { "type": "string", "minLength": 1 } } + }, + "additionalProperties": false + } + } + }, + "$defs": { "hash": { "type": "string", "pattern": "^[a-f0-9]{64}$" } }, + "additionalProperties": false +} diff --git a/schemas/nodekit.frontend-route.v1.schema.json b/schemas/nodekit.frontend-route.v1.schema.json new file mode 100644 index 0000000..f825b20 --- /dev/null +++ b/schemas/nodekit.frontend-route.v1.schema.json @@ -0,0 +1,60 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/HomenShum/node-platform/blob/main/schemas/nodekit.frontend-route.v1.schema.json", + "title": "NodeKit evidence-ranked frontend specialist route", + "type": "object", + "required": ["schemaVersion", "role", "status", "selection", "requiredCapabilities", "authority", "budget", "expiry"], + "properties": { + "schemaVersion": { "const": "nodekit.frontend-route/v1" }, + "role": { "const": "frontend-architect" }, + "status": { "enum": ["unprofiled", "provisional", "evidence-ranked", "expired"] }, + "selection": { + "type": "object", + "required": ["strategy", "preferredRoute", "fallbacks", "evidenceRefs"], + "properties": { + "strategy": { "const": "evidence_ranked" }, + "preferredRoute": { "type": ["string", "null"], "minLength": 1 }, + "fallbacks": { "$ref": "#/$defs/strings" }, + "evidenceRefs": { "$ref": "#/$defs/strings" } + }, + "additionalProperties": false + }, + "requiredCapabilities": { "$ref": "#/$defs/nonEmptyStrings" }, + "authority": { + "type": "object", + "required": ["sourceWrite", "proofGateWrite", "productionCredentials", "deployment"], + "properties": { + "sourceWrite": { "const": "proposal_workspace" }, + "proofGateWrite": { "const": "prohibited" }, + "productionCredentials": { "const": "prohibited" }, + "deployment": { "const": "approval_required" } + }, + "additionalProperties": false + }, + "budget": { + "type": "object", + "required": ["maximumCostUsd", "maximumRepairRounds"], + "properties": { + "maximumCostUsd": { "type": "number", "minimum": 0 }, + "maximumRepairRounds": { "type": "integer", "minimum": 0, "maximum": 5 } + }, + "additionalProperties": false + }, + "expiry": { + "type": "object", + "required": ["profiledAt", "reprofileAfterDays", "invalidateOnModelRevision", "invalidateOnToolSurfaceChange"], + "properties": { + "profiledAt": { "type": ["string", "null"], "pattern": "^$|^\\d{4}-\\d{2}-\\d{2}T" }, + "reprofileAfterDays": { "type": "integer", "minimum": 1 }, + "invalidateOnModelRevision": { "const": true }, + "invalidateOnToolSurfaceChange": { "const": true } + }, + "additionalProperties": false + } + }, + "$defs": { + "strings": { "type": "array", "items": { "type": "string", "minLength": 1 }, "uniqueItems": true }, + "nonEmptyStrings": { "type": "array", "minItems": 1, "items": { "type": "string", "minLength": 1 }, "uniqueItems": true } + }, + "additionalProperties": false +} diff --git a/schemas/nodekit.graph-patch.v1.schema.json b/schemas/nodekit.graph-patch.v1.schema.json new file mode 100644 index 0000000..45e0851 --- /dev/null +++ b/schemas/nodekit.graph-patch.v1.schema.json @@ -0,0 +1,62 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/HomenShum/node-platform/blob/main/schemas/nodekit.graph-patch.v1.schema.json", + "title": "NodeKit governed graph patch proposal", + "type": "object", + "required": ["schemaVersion", "patchId", "graphId", "baseVersion", "operations", "evidenceRefs", "contradictionRefs", "proposedBy", "confidence", "validation", "status", "proposedAt"], + "properties": { + "schemaVersion": { "const": "nodekit.graph-patch/v1" }, + "patchId": { "$ref": "#/$defs/nonEmpty" }, + "graphId": { "$ref": "#/$defs/nonEmpty" }, + "baseVersion": { "type": "integer", "minimum": 0 }, + "operations": { + "type": "array", + "minItems": 1, + "items": { + "oneOf": [ + { "type": "object", "required": ["type"], "properties": { "type": { "const": "INSERT" }, "node": { "type": "object" }, "hyperedge": { "type": "object" } }, "additionalProperties": true }, + { "type": "object", "required": ["type", "targetId", "patch"], "properties": { "type": { "const": "UPDATE" }, "targetId": { "$ref": "#/$defs/nonEmpty" }, "patch": { "type": "object" }, "evidenceRefs": { "$ref": "#/$defs/stringList" } }, "additionalProperties": false }, + { "type": "object", "required": ["type", "targetId", "reason"], "properties": { "type": { "const": "DEPRECATE" }, "targetId": { "$ref": "#/$defs/nonEmpty" }, "reason": { "$ref": "#/$defs/nonEmpty" }, "evidenceRefs": { "$ref": "#/$defs/stringList" } }, "additionalProperties": false } + ] + } + }, + "evidenceRefs": { "$ref": "#/$defs/stringList" }, + "contradictionRefs": { "$ref": "#/$defs/stringList" }, + "proposedBy": { + "type": "object", + "required": ["agentId", "modelRoute", "resolvedModel", "harnessVersion"], + "properties": { + "agentId": { "$ref": "#/$defs/nonEmpty" }, + "modelRoute": { "$ref": "#/$defs/nonEmpty" }, + "resolvedModel": { "$ref": "#/$defs/nonEmpty" }, + "harnessVersion": { "$ref": "#/$defs/nonEmpty" } + }, + "additionalProperties": false + }, + "confidence": { "type": "number", "minimum": 0, "maximum": 1 }, + "validation": { + "type": "object", + "required": ["sourceGrounded", "schemaValid", "authorityValid", "conflictFree", "freshnessValid", "errors"], + "properties": { + "sourceGrounded": { "type": "boolean" }, + "schemaValid": { "type": "boolean" }, + "authorityValid": { "type": "boolean" }, + "conflictFree": { "type": "boolean" }, + "freshnessValid": { "type": "boolean" }, + "errors": { "type": "array", "items": { "type": "string" } } + }, + "additionalProperties": false + }, + "status": { "enum": ["pending", "accepted", "rejected", "conflicted", "applied"] }, + "proposedAt": { "$ref": "#/$defs/dateTime" }, + "validatedAt": { "$ref": "#/$defs/dateTime" }, + "appliedAt": { "$ref": "#/$defs/dateTime" }, + "decision": { "type": "object" } + }, + "$defs": { + "nonEmpty": { "type": "string", "minLength": 1 }, + "dateTime": { "type": "string", "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T.*Z$" }, + "stringList": { "type": "array", "items": { "type": "string", "minLength": 1 }, "uniqueItems": true } + }, + "additionalProperties": false +} diff --git a/schemas/nodekit.harness.v1.schema.json b/schemas/nodekit.harness.v1.schema.json new file mode 100644 index 0000000..27cbbad --- /dev/null +++ b/schemas/nodekit.harness.v1.schema.json @@ -0,0 +1,51 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/HomenShum/node-platform/blob/main/schemas/nodekit.harness.v1.schema.json", + "title": "NodeKit Harness Gym configuration", + "type": "object", + "required": ["schemaVersion", "applicationId", "version", "evaluation", "modelIntelligence", "promotion"], + "properties": { + "schemaVersion": { "const": "nodekit.harness/v1" }, + "applicationId": { "type": "string", "minLength": 1 }, + "version": { "type": "string", "pattern": "^h[0-9]+$" }, + "evaluation": { + "type": "object", + "required": ["protectedTaskRoots", "candidateMayModify"], + "properties": { + "protectedTaskRoots": { + "type": "array", + "minItems": 1, + "items": { "type": "string", "minLength": 1 }, + "uniqueItems": true + }, + "candidateMayModify": { + "type": "array", + "items": { "type": "string", "minLength": 1 }, + "uniqueItems": true + } + }, + "additionalProperties": false + }, + "modelIntelligence": { + "type": "object", + "required": ["observationRoot", "cardRoot", "findingsLedger"], + "properties": { + "observationRoot": { "type": "string", "minLength": 1 }, + "cardRoot": { "type": "string", "minLength": 1 }, + "findingsLedger": { "type": "string", "minLength": 1 } + }, + "additionalProperties": false + }, + "promotion": { + "type": "object", + "required": ["automatic", "freshAgentCanary", "proofReceipt"], + "properties": { + "automatic": { "const": false }, + "freshAgentCanary": { "const": true }, + "proofReceipt": { "const": true } + }, + "additionalProperties": false + } + }, + "additionalProperties": false +} diff --git a/schemas/nodekit.invariant-claim.v1.schema.json b/schemas/nodekit.invariant-claim.v1.schema.json new file mode 100644 index 0000000..2323e0e --- /dev/null +++ b/schemas/nodekit.invariant-claim.v1.schema.json @@ -0,0 +1,24 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/HomenShum/node-platform/blob/main/schemas/nodekit.invariant-claim.v1.schema.json", + "type": "object", + "required": ["schemaVersion", "id", "statement", "scope", "enforcement", "verifierRefs", "introducedByEventId", "status"], + "properties": { + "schemaVersion": { "const": "nodekit.invariant-claim/v1" }, + "id": { "$ref": "#/$defs/id" }, + "statement": { "$ref": "#/$defs/text" }, + "scope": { "$ref": "#/$defs/scope" }, + "enforcement": { "enum": ["type-system", "schema", "test", "runtime-gate", "ci", "human-policy"] }, + "verifierRefs": { "$ref": "#/$defs/strings" }, + "introducedByEventId": { "$ref": "#/$defs/id" }, + "supersededByEventId": { "$ref": "#/$defs/id" }, + "status": { "enum": ["proposed", "partially-verified", "verified", "invalidated", "superseded"] } + }, + "$defs": { + "id": { "type": "string", "pattern": "^[a-z][a-z0-9._:-]+$" }, + "text": { "type": "string", "minLength": 1 }, + "strings": { "type": "array", "items": { "$ref": "#/$defs/text" }, "uniqueItems": true }, + "scope": { "type": "object", "properties": { "applications": { "$ref": "#/$defs/strings" }, "packages": { "$ref": "#/$defs/strings" }, "surfaces": { "$ref": "#/$defs/strings" } }, "minProperties": 1, "additionalProperties": false } + }, + "additionalProperties": false +} diff --git a/schemas/nodekit.knowledge-action-receipt.v1.schema.json b/schemas/nodekit.knowledge-action-receipt.v1.schema.json new file mode 100644 index 0000000..2d4367c --- /dev/null +++ b/schemas/nodekit.knowledge-action-receipt.v1.schema.json @@ -0,0 +1,29 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/HomenShum/node-platform/blob/main/schemas/nodekit.knowledge-action-receipt.v1.schema.json", + "title": "NodeKit knowledge action receipt", + "type": "object", + "required": ["schemaVersion", "receiptId", "graphId", "graphVersion", "runId", "caseId", "actorId", "type", "input", "outputRefs", "evidenceRefs", "budget", "status", "occurredAt"], + "properties": { + "schemaVersion": { "const": "nodekit.knowledge-action-receipt/v1" }, + "receiptId": { "$ref": "#/$defs/nonEmpty" }, + "graphId": { "$ref": "#/$defs/nonEmpty" }, + "graphVersion": { "type": "integer", "minimum": 0 }, + "runId": { "type": "string" }, + "caseId": { "type": "string" }, + "actorId": { "type": "string" }, + "type": { "enum": ["GRAPH_RETRIEVE", "EXTERNAL_RESEARCH", "PROPOSE_GRAPH_PATCH", "INSPECT_ARTIFACT", "EXECUTE_TOOL", "REQUEST_APPROVAL", "COMPLETE", "ABSTAIN"] }, + "input": { "type": "object" }, + "outputRefs": { "$ref": "#/$defs/stringList" }, + "evidenceRefs": { "$ref": "#/$defs/stringList" }, + "budget": { "type": "object" }, + "status": { "enum": ["planned", "completed", "failed", "abstained", "awaiting_approval"] }, + "occurredAt": { "$ref": "#/$defs/dateTime" } + }, + "$defs": { + "nonEmpty": { "type": "string", "minLength": 1 }, + "dateTime": { "type": "string", "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T.*Z$" }, + "stringList": { "type": "array", "items": { "type": "string", "minLength": 1 }, "uniqueItems": true } + }, + "additionalProperties": false +} diff --git a/schemas/nodekit.knowledge-graph.v1.schema.json b/schemas/nodekit.knowledge-graph.v1.schema.json new file mode 100644 index 0000000..729a421 --- /dev/null +++ b/schemas/nodekit.knowledge-graph.v1.schema.json @@ -0,0 +1,103 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/HomenShum/node-platform/blob/main/schemas/nodekit.knowledge-graph.v1.schema.json", + "title": "NodeKit governed multimodal knowledge hypergraph", + "type": "object", + "required": ["schemaVersion", "graphId", "version", "authority", "layers", "nodes", "hyperedges", "proposals", "actionReceipts", "evolutionReceipts", "genesis", "createdAt", "updatedAt", "contentHash"], + "properties": { + "schemaVersion": { "const": "nodekit.knowledge-graph/v1" }, + "graphId": { "$ref": "#/$defs/nonEmpty" }, + "version": { "type": "integer", "minimum": 0 }, + "authority": { + "type": "object", + "required": ["canonicalMutation", "destructiveDelete", "oneAuthoritativeGraph"], + "properties": { + "canonicalMutation": { "const": "accepted-patch-only" }, + "destructiveDelete": { "const": false }, + "oneAuthoritativeGraph": { "const": true } + }, + "additionalProperties": false + }, + "layers": { + "type": "array", + "minItems": 6, + "items": { + "type": "object", + "required": ["id", "writableThrough"], + "properties": { + "id": { "enum": ["source", "derived", "working", "proposal", "canonical", "hypothesis"] }, + "writableThrough": { "enum": ["ingest-proposal", "graph-patch"] } + }, + "additionalProperties": false + } + }, + "nodes": { "type": "array", "items": { "$ref": "#/$defs/node" } }, + "hyperedges": { "type": "array", "items": { "$ref": "#/$defs/hyperedge" } }, + "proposals": { "type": "array", "items": { "type": "object" } }, + "actionReceipts": { "type": "array", "items": { "type": "object" } }, + "evolutionReceipts": { "type": "array", "items": { "type": "object" } }, + "genesis": { "type": "object", "required": ["createdAt", "graphId"] }, + "createdAt": { "$ref": "#/$defs/dateTime" }, + "updatedAt": { "$ref": "#/$defs/dateTime" }, + "contentHash": { "$ref": "#/$defs/hash" } + }, + "$defs": { + "nonEmpty": { "type": "string", "minLength": 1 }, + "dateTime": { "type": "string", "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T.*Z$" }, + "hash": { "type": "string", "pattern": "^[a-f0-9]{64}$" }, + "layer": { "enum": ["source", "derived", "working", "proposal", "canonical", "hypothesis"] }, + "stringList": { "type": "array", "items": { "type": "string", "minLength": 1 }, "uniqueItems": true }, + "node": { + "type": "object", + "required": ["id", "kind", "label", "layer", "confidence", "evidenceRefs"], + "properties": { + "id": { "$ref": "#/$defs/nonEmpty" }, + "kind": { "$ref": "#/$defs/nonEmpty" }, + "label": { "$ref": "#/$defs/nonEmpty" }, + "layer": { "$ref": "#/$defs/layer" }, + "confidence": { "type": "number", "minimum": 0, "maximum": 1 }, + "evidenceRefs": { "$ref": "#/$defs/stringList" }, + "properties": { "type": "object" }, + "contentHash": { "$ref": "#/$defs/hash" }, + "sourceUri": { "type": "string", "minLength": 1 }, + "capturedAt": { "$ref": "#/$defs/dateTime" }, + "region": { "type": "object" }, + "timeRange": { "type": "object" }, + "freshness": { "type": "object" }, + "createdAt": { "$ref": "#/$defs/dateTime" }, + "updatedAt": { "$ref": "#/$defs/dateTime" }, + "deprecatedAt": { "$ref": "#/$defs/dateTime" }, + "deprecationReason": { "type": "string" } + }, + "additionalProperties": true + }, + "hyperedge": { + "type": "object", + "required": ["id", "predicate", "layer", "participants", "confidence", "evidenceRefs"], + "properties": { + "id": { "$ref": "#/$defs/nonEmpty" }, + "predicate": { "$ref": "#/$defs/nonEmpty" }, + "layer": { "$ref": "#/$defs/layer" }, + "participants": { + "type": "array", + "minItems": 2, + "items": { + "type": "object", + "required": ["nodeId", "role"], + "properties": { "nodeId": { "$ref": "#/$defs/nonEmpty" }, "role": { "$ref": "#/$defs/nonEmpty" } }, + "additionalProperties": false + } + }, + "confidence": { "type": "number", "minimum": 0, "maximum": 1 }, + "evidenceRefs": { "$ref": "#/$defs/stringList" }, + "properties": { "type": "object" }, + "createdAt": { "$ref": "#/$defs/dateTime" }, + "updatedAt": { "$ref": "#/$defs/dateTime" }, + "deprecatedAt": { "$ref": "#/$defs/dateTime" }, + "deprecationReason": { "type": "string" } + }, + "additionalProperties": true + } + }, + "additionalProperties": false +} diff --git a/schemas/nodekit.knowledge-state.v1.schema.json b/schemas/nodekit.knowledge-state.v1.schema.json new file mode 100644 index 0000000..73e5212 --- /dev/null +++ b/schemas/nodekit.knowledge-state.v1.schema.json @@ -0,0 +1,47 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/HomenShum/node-platform/blob/main/schemas/nodekit.knowledge-state.v1.schema.json", + "title": "NodeKit knowledge environment state", + "type": "object", + "required": ["schemaVersion", "caseId", "runId", "goal", "graphId", "graphVersion", "selectedSubgraphIds", "actionHistory", "currentArtifactIds", "currentTaskIds", "unresolvedQuestionIds", "contradictionIds", "permissions", "budget"], + "properties": { + "schemaVersion": { "const": "nodekit.knowledge-state/v1" }, + "caseId": { "type": "string" }, + "runId": { "type": "string" }, + "goal": { "type": "string" }, + "graphId": { "$ref": "#/$defs/nonEmpty" }, + "graphVersion": { "type": "integer", "minimum": 0 }, + "selectedSubgraphIds": { "$ref": "#/$defs/stringList" }, + "actionHistory": { "type": "array", "items": { "type": "object" } }, + "currentArtifactIds": { "$ref": "#/$defs/stringList" }, + "currentTaskIds": { "$ref": "#/$defs/stringList" }, + "unresolvedQuestionIds": { "$ref": "#/$defs/stringList" }, + "contradictionIds": { "$ref": "#/$defs/stringList" }, + "permissions": { + "type": "object", + "required": ["retrieve", "propose", "apply"], + "properties": { + "retrieve": { "type": "boolean" }, + "propose": { "type": "boolean" }, + "apply": { "type": "boolean" } + }, + "additionalProperties": false + }, + "budget": { + "type": "object", + "required": ["maximumGraphEdits", "maximumSearches", "maximumToolCalls", "maximumTokens"], + "properties": { + "maximumGraphEdits": { "type": "integer", "minimum": 0 }, + "maximumSearches": { "type": "integer", "minimum": 0 }, + "maximumToolCalls": { "type": "integer", "minimum": 0 }, + "maximumTokens": { "type": "integer", "minimum": 0 } + }, + "additionalProperties": false + } + }, + "$defs": { + "nonEmpty": { "type": "string", "minLength": 1 }, + "stringList": { "type": "array", "items": { "type": "string", "minLength": 1 }, "uniqueItems": true } + }, + "additionalProperties": false +} diff --git a/schemas/nodekit.model-capability-card.v1.schema.json b/schemas/nodekit.model-capability-card.v1.schema.json new file mode 100644 index 0000000..edd4446 --- /dev/null +++ b/schemas/nodekit.model-capability-card.v1.schema.json @@ -0,0 +1,94 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/HomenShum/node-platform/blob/main/schemas/nodekit.model-capability-card.v1.schema.json", + "title": "NodeKit evidence-backed model capability card", + "type": "object", + "required": ["schemaVersion", "model", "scope", "evidenceWindow", "strengths", "weaknesses", "bestRoles", "avoidRoles", "requiredScaffolding", "metrics", "confidence", "expiresWhen", "evidenceRefs", "status"], + "properties": { + "schemaVersion": { "const": "nodekit.model-capability-card/v1" }, + "model": { + "type": "object", + "required": ["requestedRoute", "resolvedProvider", "resolvedModel"], + "properties": { + "requestedRoute": { "$ref": "#/$defs/nonEmpty" }, + "resolvedProvider": { "$ref": "#/$defs/nonEmpty" }, + "resolvedModel": { "$ref": "#/$defs/nonEmpty" }, + "modelRevision": { "$ref": "#/$defs/nonEmpty" } + }, + "additionalProperties": false + }, + "scope": { + "type": "object", + "required": ["level", "taskFamilies"], + "properties": { + "level": { "enum": ["ecosystem", "domain", "project"] }, + "applicationId": { "$ref": "#/$defs/nonEmpty" }, + "domainId": { "$ref": "#/$defs/nonEmpty" }, + "taskFamilies": { "$ref": "#/$defs/stringList" } + }, + "allOf": [ + { "if": { "properties": { "level": { "const": "project" } } }, "then": { "required": ["applicationId"] } }, + { "if": { "properties": { "level": { "const": "domain" } } }, "then": { "required": ["domainId"] } } + ], + "additionalProperties": false + }, + "evidenceWindow": { + "type": "object", + "required": ["from", "to", "benchmarkRuns", "taskCount", "harnessVersions"], + "properties": { + "from": { "type": "string", "pattern": "^\\d{4}-\\d{2}-\\d{2}$" }, + "to": { "type": "string", "pattern": "^\\d{4}-\\d{2}-\\d{2}$" }, + "benchmarkRuns": { "type": "integer", "minimum": 0 }, + "taskCount": { "type": "integer", "minimum": 0 }, + "harnessVersions": { "$ref": "#/$defs/stringList" } + }, + "additionalProperties": false + }, + "strengths": { "$ref": "#/$defs/stringList" }, + "weaknesses": { "$ref": "#/$defs/stringList" }, + "bestRoles": { "$ref": "#/$defs/stringList" }, + "avoidRoles": { "$ref": "#/$defs/stringList" }, + "requiredScaffolding": { "$ref": "#/$defs/stringList" }, + "metrics": { + "type": "object", + "properties": { + "briefAdherence": { "$ref": "#/$defs/score" }, + "validToolCalls": { "$ref": "#/$defs/score" }, + "firstPassAcceptance": { "$ref": "#/$defs/score" }, + "repairSuccess": { "$ref": "#/$defs/score" }, + "medianLatencyMs": { "type": "integer", "minimum": 0 }, + "costPerSuccessUsd": { "type": "number", "minimum": 0 } + }, + "additionalProperties": false + }, + "confidence": { + "type": "object", + "required": ["level", "reason"], + "properties": { + "level": { "enum": ["low", "medium", "high"] }, + "reason": { "$ref": "#/$defs/nonEmpty" } + }, + "additionalProperties": false + }, + "expiresWhen": { "$ref": "#/$defs/stringList" }, + "evidenceRefs": { "$ref": "#/$defs/stringList" }, + "status": { "enum": ["provisional", "certified", "expired"] }, + "certificationReceiptId": { "$ref": "#/$defs/nonEmpty" } + }, + "allOf": [ + { + "if": { "properties": { "status": { "const": "certified" } } }, + "then": { "required": ["certificationReceiptId"] } + } + ], + "$defs": { + "nonEmpty": { "type": "string", "minLength": 1 }, + "score": { "type": "number", "minimum": 0, "maximum": 1 }, + "stringList": { + "type": "array", + "items": { "$ref": "#/$defs/nonEmpty" }, + "uniqueItems": true + } + }, + "additionalProperties": false +} diff --git a/schemas/nodekit.model-observation.v1.schema.json b/schemas/nodekit.model-observation.v1.schema.json new file mode 100644 index 0000000..28ac75e --- /dev/null +++ b/schemas/nodekit.model-observation.v1.schema.json @@ -0,0 +1,165 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/HomenShum/node-platform/blob/main/schemas/nodekit.model-observation.v1.schema.json", + "title": "NodeKit evaluated model-run observation", + "type": "object", + "required": [ + "schemaVersion", + "runId", + "applicationId", + "taskId", + "taskFamily", + "model", + "harness", + "budgets", + "cognitive", + "execution", + "artifact", + "efficiency", + "failures", + "evidenceRefs", + "proofReceiptId" + ], + "properties": { + "schemaVersion": { "const": "nodekit.model-observation/v1" }, + "runId": { "$ref": "#/$defs/nonEmpty" }, + "applicationId": { "$ref": "#/$defs/nonEmpty" }, + "taskId": { "$ref": "#/$defs/nonEmpty" }, + "taskFamily": { "$ref": "#/$defs/nonEmpty" }, + "model": { + "type": "object", + "required": ["requestedRoute", "resolvedProvider", "resolvedModel"], + "properties": { + "requestedRoute": { "$ref": "#/$defs/nonEmpty" }, + "resolvedProvider": { "$ref": "#/$defs/nonEmpty" }, + "resolvedModel": { "$ref": "#/$defs/nonEmpty" }, + "modelRevision": { "$ref": "#/$defs/nonEmpty" } + }, + "additionalProperties": false + }, + "harness": { + "type": "object", + "required": ["version", "hash", "toolSurfaceHash", "contextPolicyHash", "skillStackHash"], + "properties": { + "version": { "$ref": "#/$defs/nonEmpty" }, + "hash": { "$ref": "#/$defs/hash" }, + "toolSurfaceHash": { "$ref": "#/$defs/hash" }, + "contextPolicyHash": { "$ref": "#/$defs/hash" }, + "skillStackHash": { "$ref": "#/$defs/hash" } + }, + "additionalProperties": false + }, + "budgets": { + "type": "object", + "properties": { + "maximumTokens": { "type": "integer", "minimum": 0 }, + "maximumCostUsd": { "type": "number", "minimum": 0 }, + "maximumDurationMs": { "type": "integer", "minimum": 0 } + }, + "additionalProperties": false + }, + "cognitive": { + "$ref": "#/$defs/scoreGroup", + "required": ["briefUnderstanding", "decomposition", "constraintRetention", "ambiguityDetection", "referenceUse", "repairReasoning"] + }, + "execution": { + "$ref": "#/$defs/scoreGroup", + "required": ["toolSelection", "validArguments", "toolOrdering", "resultInspection", "recovery", "scopedChanges", "completion"] + }, + "artifact": { + "$ref": "#/$defs/scoreGroup", + "required": ["correctness", "usability", "domainQuality", "evidenceIntegrity"] + }, + "efficiency": { + "type": "object", + "required": ["latencyMs", "toolCalls", "retries"], + "properties": { + "latencyMs": { "type": "integer", "minimum": 0 }, + "tokensIn": { "type": "integer", "minimum": 0 }, + "tokensOut": { "type": "integer", "minimum": 0 }, + "costUsd": { "type": "number", "minimum": 0 }, + "toolCalls": { "type": "integer", "minimum": 0 }, + "retries": { "type": "integer", "minimum": 0 }, + "repairRounds": { "type": "integer", "minimum": 0 } + }, + "additionalProperties": false + }, + "failures": { + "type": "array", + "items": { "$ref": "#/$defs/failure" } + }, + "evidenceRefs": { "$ref": "#/$defs/stringList" }, + "proofReceiptId": { "$ref": "#/$defs/nonEmpty" } + }, + "$defs": { + "nonEmpty": { "type": "string", "minLength": 1 }, + "hash": { "type": "string", "pattern": "^[a-f0-9]{64}$" }, + "score": { "type": "number", "minimum": 0, "maximum": 1 }, + "stringList": { + "type": "array", + "items": { "$ref": "#/$defs/nonEmpty" }, + "uniqueItems": true + }, + "scoreGroup": { + "type": "object", + "properties": { + "briefUnderstanding": { "$ref": "#/$defs/score" }, + "decomposition": { "$ref": "#/$defs/score" }, + "constraintRetention": { "$ref": "#/$defs/score" }, + "ambiguityDetection": { "$ref": "#/$defs/score" }, + "referenceUse": { "$ref": "#/$defs/score" }, + "repairReasoning": { "$ref": "#/$defs/score" }, + "toolSelection": { "$ref": "#/$defs/score" }, + "validArguments": { "$ref": "#/$defs/score" }, + "toolOrdering": { "$ref": "#/$defs/score" }, + "resultInspection": { "$ref": "#/$defs/score" }, + "recovery": { "$ref": "#/$defs/score" }, + "scopedChanges": { "$ref": "#/$defs/score" }, + "completion": { "$ref": "#/$defs/score" }, + "correctness": { "$ref": "#/$defs/score" }, + "usability": { "$ref": "#/$defs/score" }, + "domainQuality": { "$ref": "#/$defs/score" }, + "visualQuality": { "$ref": "#/$defs/score" }, + "evidenceIntegrity": { "$ref": "#/$defs/score" }, + "editability": { "$ref": "#/$defs/score" }, + "exportQuality": { "$ref": "#/$defs/score" } + }, + "additionalProperties": false + }, + "failure": { + "type": "object", + "required": ["failureId", "failureClass", "severity", "model", "taskId", "harnessVersion", "behavior", "expectedBehavior", "probableCause", "evidenceRefs"], + "properties": { + "failureId": { "$ref": "#/$defs/nonEmpty" }, + "failureClass": { + "enum": [ + "BRIEF_MISS", "PRODUCT_TOPOLOGY_MISS", "GENERIC_DASHBOARD_FALLBACK", "GENERIC_FALLBACK", "OVERPLANNING", "UNDERPLANNING", "WRONG_PRIMITIVE", + "LAYOUT_REPETITION", "TEXT_DENSITY", "TOOL_AVOIDANCE", "TOOL_MISUSE", "NO_RESULT_INSPECTION", + "FALSE_COMPLETION", "WEAK_REPAIR", "CONTEXT_DRIFT", "ORCHESTRATION_FAILURE", "UNSUPPORTED_CLAIM", + "COST_INEFFICIENCY", "REPOSITORY_LEGIBILITY_FAILURE", "REFERENCE_DISCOVERY_FAILURE", "UI_OPERABILITY_FAILURE", "MOBILE_TOPOLOGY_FAILURE", "BACKSTAGE_COMPLEXITY_LEAK", + "AUTHORITY_VIOLATION", "STALE_WRITE_FAILURE", "EXPORT_FAILURE", "RECOVERY_FAILURE" + ] + }, + "severity": { "enum": ["P0", "P1", "P2", "P3"] }, + "model": { "$ref": "#/$defs/nonEmpty" }, + "taskId": { "$ref": "#/$defs/nonEmpty" }, + "harnessVersion": { "$ref": "#/$defs/nonEmpty" }, + "behavior": { "$ref": "#/$defs/nonEmpty" }, + "expectedBehavior": { "$ref": "#/$defs/nonEmpty" }, + "probableCause": { "enum": ["model", "prompt", "skill", "tool", "context", "reference", "renderer", "validator", "runtime", "product contract", "unknown"] }, + "evidenceRefs": { "$ref": "#/$defs/stringList" }, + "repair": { + "type": "object", + "properties": { + "candidateSkillId": { "$ref": "#/$defs/nonEmpty" }, + "candidateHarnessId": { "$ref": "#/$defs/nonEmpty" }, + "result": { "enum": ["improved", "unchanged", "regressed"] } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false +} diff --git a/schemas/nodekit.product-design-contract.v1.schema.json b/schemas/nodekit.product-design-contract.v1.schema.json new file mode 100644 index 0000000..b8f18cb --- /dev/null +++ b/schemas/nodekit.product-design-contract.v1.schema.json @@ -0,0 +1,67 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/HomenShum/node-platform/blob/main/schemas/nodekit.product-design-contract.v1.schema.json", + "title": "NodeKit protected product design contract", + "type": "object", + "required": ["schemaVersion", "contractId", "product", "journey", "designIntent", "interfaceHypothesis", "requiredDesktopSurfaces", "requiredMobileSurfaces", "avoid", "requiredStates", "protectedDecisions"], + "properties": { + "schemaVersion": { "const": "nodekit.product-design-contract/v1" }, + "contractId": { "$ref": "#/$defs/nonEmpty" }, + "product": { + "type": "object", + "required": ["targetUser", "primaryJob", "primaryArtifact"], + "properties": { + "targetUser": { "$ref": "#/$defs/nonEmpty" }, + "primaryJob": { "$ref": "#/$defs/nonEmpty" }, + "primaryArtifact": { "$ref": "#/$defs/nonEmpty" } + }, + "additionalProperties": false + }, + "journey": { "$ref": "#/$defs/nonEmptyStrings" }, + "designIntent": { + "type": "object", + "required": ["emotionalTarget", "dominantSurface", "dominantAction", "density"], + "properties": { + "emotionalTarget": { "$ref": "#/$defs/nonEmptyStrings" }, + "dominantSurface": { "$ref": "#/$defs/nonEmpty" }, + "dominantAction": { "$ref": "#/$defs/nonEmpty" }, + "density": { "enum": ["low", "medium", "high"] } + }, + "additionalProperties": false + }, + "interfaceHypothesis": { + "type": "object", + "required": ["artifactDominance", "agentPlacement", "reviewBoundary", "mobileTopology"], + "properties": { + "artifactDominance": { "$ref": "#/$defs/nonEmpty" }, + "agentPlacement": { "$ref": "#/$defs/nonEmpty" }, + "reviewBoundary": { "$ref": "#/$defs/nonEmpty" }, + "mobileTopology": { "$ref": "#/$defs/nonEmpty" } + }, + "additionalProperties": false + }, + "requiredDesktopSurfaces": { "$ref": "#/$defs/nonEmptyStrings" }, + "requiredMobileSurfaces": { "$ref": "#/$defs/nonEmptyStrings" }, + "avoid": { "$ref": "#/$defs/nonEmptyStrings" }, + "requiredStates": { "$ref": "#/$defs/nonEmptyStrings" }, + "protectedDecisions": { + "type": "object", + "required": ["primaryUser", "primaryJob", "canonicalWorkflow", "dataAuthority", "permissionBoundaries", "completionCriteria", "finalVerdict"], + "properties": { + "primaryUser": { "const": "nodekit" }, + "primaryJob": { "const": "nodekit" }, + "canonicalWorkflow": { "const": "nodekit" }, + "dataAuthority": { "const": "nodekit" }, + "permissionBoundaries": { "const": "nodekit" }, + "completionCriteria": { "const": "nodeproof" }, + "finalVerdict": { "const": "nodeproof" } + }, + "additionalProperties": false + } + }, + "$defs": { + "nonEmpty": { "type": "string", "minLength": 1 }, + "nonEmptyStrings": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/nonEmpty" }, "uniqueItems": true } + }, + "additionalProperties": false +} diff --git a/schemas/nodekit.promotion-receipt.v1.schema.json b/schemas/nodekit.promotion-receipt.v1.schema.json new file mode 100644 index 0000000..834763d --- /dev/null +++ b/schemas/nodekit.promotion-receipt.v1.schema.json @@ -0,0 +1,22 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/HomenShum/node-platform/blob/main/schemas/nodekit.promotion-receipt.v1.schema.json", + "title": "NodeKit proof-controlled promotion receipt", + "type": "object", + "required": ["schemaVersion", "promotionId", "candidateId", "kind", "benchmarkVerdictRef", "canaryReceiptRef", "nodeProofReceiptRef", "nodeProofVerified", "rollbackVersion", "automatic", "approvedBy", "promotedHash"], + "properties": { + "schemaVersion": { "const": "nodekit.promotion-receipt/v1" }, + "promotionId": { "type": "string", "minLength": 1 }, + "candidateId": { "type": "string", "minLength": 1 }, + "kind": { "enum": ["skill", "routing", "harness"] }, + "benchmarkVerdictRef": { "type": "string", "minLength": 1 }, + "canaryReceiptRef": { "type": "string", "minLength": 1 }, + "nodeProofReceiptRef": { "type": "string", "minLength": 1 }, + "nodeProofVerified": { "const": true }, + "rollbackVersion": { "type": "string", "minLength": 1 }, + "automatic": { "const": false }, + "approvedBy": { "type": "string", "minLength": 1 }, + "promotedHash": { "type": "string", "pattern": "^[a-f0-9]{64}$" } + }, + "additionalProperties": false +} diff --git a/schemas/nodekit.routing-policy.v1.schema.json b/schemas/nodekit.routing-policy.v1.schema.json new file mode 100644 index 0000000..609911a --- /dev/null +++ b/schemas/nodekit.routing-policy.v1.schema.json @@ -0,0 +1,82 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/HomenShum/node-platform/blob/main/schemas/nodekit.routing-policy.v1.schema.json", + "title": "NodeKit capability-driven routing policy", + "type": "object", + "required": ["schemaVersion", "applicationId", "status", "evidencePrecedence", "routes", "automaticPromotion"], + "properties": { + "schemaVersion": { "const": "nodekit.routing-policy/v1" }, + "applicationId": { "type": "string", "minLength": 1 }, + "status": { "enum": ["provisional", "canary-passed", "promoted", "expired"] }, + "evidencePrecedence": { "const": ["project", "domain", "ecosystem", "unprofiled-fallback"] }, + "routes": { + "type": "array", + "items": { + "type": "object", + "required": ["taskFamily", "requiredCapabilities", "candidates", "fallback", "completion"], + "properties": { + "taskFamily": { "type": "string", "minLength": 1 }, + "requiredCapabilities": { "$ref": "#/$defs/stringList" }, + "candidates": { + "type": "array", + "items": { + "type": "object", + "required": ["requestedRoute", "resolvedProvider", "resolvedModel", "scope", "confidence", "priority", "roleSkills", "domainSkills", "modelAdapters", "guardrails", "evidenceRefs"], + "properties": { + "requestedRoute": { "type": "string", "minLength": 1 }, + "resolvedProvider": { "type": "string", "minLength": 1 }, + "resolvedModel": { "type": "string", "minLength": 1 }, + "scope": { "enum": ["project", "domain", "ecosystem"] }, + "confidence": { "enum": ["low", "medium", "high"] }, + "priority": { "type": "integer", "minimum": 1 }, + "roleSkills": { "$ref": "#/$defs/stringList" }, + "domainSkills": { "$ref": "#/$defs/stringList" }, + "modelAdapters": { "$ref": "#/$defs/stringList" }, + "guardrails": { "$ref": "#/$defs/stringList" }, + "evidenceRefs": { "$ref": "#/$defs/nonEmptyStringList" } + }, + "additionalProperties": false + } + }, + "fallback": { + "type": "object", + "required": ["type", "skill"], + "properties": { + "type": { "const": "deterministic" }, + "skill": { "type": "string", "minLength": 1 } + }, + "additionalProperties": false + }, + "completion": { + "type": "object", + "required": ["requireProposal", "requireArtifactInspection", "directMutation"], + "properties": { + "requireProposal": { "const": true }, + "requireArtifactInspection": { "const": true }, + "directMutation": { "const": false } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + }, + "automaticPromotion": { "const": false }, + "compiledFrom": { + "type": "object", + "required": ["harnessHash", "benchmarkHash", "cardHashes"], + "properties": { + "harnessHash": { "$ref": "#/$defs/hash" }, + "benchmarkHash": { "$ref": "#/$defs/hash" }, + "cardHashes": { "type": "array", "items": { "$ref": "#/$defs/hash" }, "uniqueItems": true } + }, + "additionalProperties": false + } + }, + "$defs": { + "hash": { "type": "string", "pattern": "^[a-f0-9]{64}$" }, + "stringList": { "type": "array", "items": { "type": "string", "minLength": 1 }, "uniqueItems": true }, + "nonEmptyStringList": { "type": "array", "minItems": 1, "items": { "type": "string", "minLength": 1 }, "uniqueItems": true } + }, + "additionalProperties": false +} diff --git a/schemas/nodekit.runtime-capabilities.v1.schema.json b/schemas/nodekit.runtime-capabilities.v1.schema.json new file mode 100644 index 0000000..3bf978f --- /dev/null +++ b/schemas/nodekit.runtime-capabilities.v1.schema.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://nodekit.dev/schemas/nodekit.runtime-capabilities.v1.schema.json", + "title": "NodeKit runtime capabilities", + "type": "object", + "additionalProperties": false, + "required": ["schemaVersion", "provider", "durableState", "transactions", "optimisticConcurrency", "subscriptions", "durableJobs", "fileStorage", "presence", "scheduledJobs", "localDevelopment"], + "properties": { + "schemaVersion": { "const": "nodekit.runtime-capabilities/v1" }, + "provider": { "type": "string", "minLength": 1 }, + "durableState": { "type": "boolean" }, + "transactions": { "type": "boolean" }, + "optimisticConcurrency": { "type": "boolean" }, + "subscriptions": { "enum": ["native", "event-driven", "polling", "snapshot"] }, + "durableJobs": { "enum": ["native", "queue-backed", "external", "in-process"] }, + "fileStorage": { "type": "boolean" }, + "presence": { "type": "boolean" }, + "scheduledJobs": { "type": "boolean" }, + "localDevelopment": { "type": "boolean" } + } +} diff --git a/schemas/nodekit.skill-candidate.v1.schema.json b/schemas/nodekit.skill-candidate.v1.schema.json new file mode 100644 index 0000000..31a90d2 --- /dev/null +++ b/schemas/nodekit.skill-candidate.v1.schema.json @@ -0,0 +1,33 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/HomenShum/node-platform/blob/main/schemas/nodekit.skill-candidate.v1.schema.json", + "title": "NodeKit proposal-only skill candidate", + "type": "object", + "required": ["schemaVersion", "candidateId", "status", "hypothesis", "expectedImpact", "risks", "sourceCluster", "skillFile", "protectedBenchmarkHash", "createdFromEvidence"], + "properties": { + "schemaVersion": { "const": "nodekit.skill-candidate/v1" }, + "candidateId": { "type": "string", "pattern": "^skill-candidate-[a-z0-9-]+$" }, + "status": { "enum": ["proposed", "benchmark-passed", "benchmark-failed", "canary-passed", "promoted", "rejected"] }, + "hypothesis": { "type": "string", "minLength": 1 }, + "expectedImpact": { "type": "string", "minLength": 1 }, + "risks": { "type": "array", "minItems": 1, "items": { "type": "string", "minLength": 1 }, "uniqueItems": true }, + "sourceCluster": { + "type": "object", + "required": ["failureClass", "probableCause", "model", "count", "taskIds", "taskFamilies", "evidenceRefs"], + "properties": { + "failureClass": { "type": "string", "minLength": 1 }, + "probableCause": { "type": "string", "minLength": 1 }, + "model": { "type": "string", "minLength": 1 }, + "count": { "type": "integer", "minimum": 3 }, + "taskIds": { "type": "array", "minItems": 2, "items": { "type": "string", "minLength": 1 }, "uniqueItems": true }, + "taskFamilies": { "type": "array", "minItems": 1, "items": { "type": "string", "minLength": 1 }, "uniqueItems": true }, + "evidenceRefs": { "type": "array", "minItems": 1, "items": { "type": "string", "minLength": 1 }, "uniqueItems": true } + }, + "additionalProperties": false + }, + "skillFile": { "type": "string", "minLength": 1 }, + "protectedBenchmarkHash": { "type": "string", "pattern": "^[a-f0-9]{64}$" }, + "createdFromEvidence": { "const": true } + }, + "additionalProperties": false +} diff --git a/schemas/nodekit.skill-comparison.v1.schema.json b/schemas/nodekit.skill-comparison.v1.schema.json new file mode 100644 index 0000000..49c9653 --- /dev/null +++ b/schemas/nodekit.skill-comparison.v1.schema.json @@ -0,0 +1,40 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/HomenShum/node-platform/blob/main/schemas/nodekit.skill-comparison.v1.schema.json", + "title": "NodeKit controlled with-skill versus without-skill comparison", + "type": "object", + "required": ["schemaVersion", "candidateId", "benchmarkHash", "harnessHash", "resolvedModel", "taskIds", "baseline", "candidate", "protectedEvaluatorUnchanged"], + "properties": { + "schemaVersion": { "const": "nodekit.skill-comparison/v1" }, + "candidateId": { "type": "string", "minLength": 1 }, + "benchmarkHash": { "$ref": "#/$defs/hash" }, + "harnessHash": { "$ref": "#/$defs/hash" }, + "resolvedModel": { "type": "string", "minLength": 1 }, + "taskIds": { "type": "array", "minItems": 2, "items": { "type": "string", "minLength": 1 }, "uniqueItems": true }, + "baseline": { "$ref": "#/$defs/arm" }, + "candidate": { "$ref": "#/$defs/arm" }, + "protectedEvaluatorUnchanged": { "const": true } + }, + "$defs": { + "hash": { "type": "string", "pattern": "^[a-f0-9]{64}$" }, + "score": { "type": "number", "minimum": 0, "maximum": 1 }, + "arm": { + "type": "object", + "required": ["runs", "successRate", "targetFailureRate", "accuracy", "safety", "editability", "exportQuality", "userCompletion", "medianLatencyMs", "costPerSuccessUsd"], + "properties": { + "runs": { "type": "integer", "minimum": 3 }, + "successRate": { "$ref": "#/$defs/score" }, + "targetFailureRate": { "$ref": "#/$defs/score" }, + "accuracy": { "$ref": "#/$defs/score" }, + "safety": { "$ref": "#/$defs/score" }, + "editability": { "$ref": "#/$defs/score" }, + "exportQuality": { "$ref": "#/$defs/score" }, + "userCompletion": { "$ref": "#/$defs/score" }, + "medianLatencyMs": { "type": "integer", "minimum": 0 }, + "costPerSuccessUsd": { "type": "number", "minimum": 0 } + }, + "additionalProperties": false + } + }, + "additionalProperties": false +} diff --git a/schemas/nodekit.skill.v1.schema.json b/schemas/nodekit.skill.v1.schema.json new file mode 100644 index 0000000..39d09d6 --- /dev/null +++ b/schemas/nodekit.skill.v1.schema.json @@ -0,0 +1,40 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/HomenShum/node-platform/blob/main/schemas/nodekit.skill.v1.schema.json", + "title": "NodeKit executable skill", + "type": "object", + "required": ["schemaVersion", "id", "version", "kind", "triggers", "inputs", "requiredTools", "procedure", "constraints", "completionChecks", "failureBehavior", "positiveExamples", "negativeExamples", "expectedToolTraces", "testFixtures", "evidenceRefs"], + "properties": { + "schemaVersion": { "const": "nodekit.skill/v1" }, + "id": { "type": "string", "pattern": "^[a-z0-9][a-z0-9._-]*$" }, + "version": { "type": "integer", "minimum": 1 }, + "kind": { "enum": ["role", "domain", "model-adapter", "guardrail", "recovery", "domain-role"] }, + "triggers": { + "type": "object", + "properties": { + "taskFamilies": { "$ref": "#/$defs/stringList" }, + "failureClasses": { "$ref": "#/$defs/stringList" }, + "models": { "$ref": "#/$defs/stringList" } + }, + "minProperties": 1, + "additionalProperties": false + }, + "inputs": { "$ref": "#/$defs/stringList" }, + "requiredTools": { "$ref": "#/$defs/stringList" }, + "procedure": { "$ref": "#/$defs/nonEmptyStringList" }, + "constraints": { "$ref": "#/$defs/nonEmptyStringList" }, + "completionChecks": { "$ref": "#/$defs/nonEmptyStringList" }, + "failureBehavior": { "$ref": "#/$defs/nonEmptyStringList" }, + "positiveExamples": { "$ref": "#/$defs/nonEmptyStringList" }, + "negativeExamples": { "$ref": "#/$defs/nonEmptyStringList" }, + "expectedToolTraces": { "$ref": "#/$defs/stringList" }, + "testFixtures": { "$ref": "#/$defs/nonEmptyStringList" }, + "evidenceRefs": { "$ref": "#/$defs/nonEmptyStringList" } + }, + "$defs": { + "nonEmpty": { "type": "string", "minLength": 1 }, + "stringList": { "type": "array", "items": { "$ref": "#/$defs/nonEmpty" }, "uniqueItems": true }, + "nonEmptyStringList": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/nonEmpty" }, "uniqueItems": true } + }, + "additionalProperties": false +} diff --git a/schemas/nodekit.submission-manifest.v1.schema.json b/schemas/nodekit.submission-manifest.v1.schema.json new file mode 100644 index 0000000..5c5cd3b --- /dev/null +++ b/schemas/nodekit.submission-manifest.v1.schema.json @@ -0,0 +1,38 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://nodekit.dev/schemas/nodekit.submission-manifest.v1.schema.json", + "title": "NodeKit submission manifest", + "type": "object", + "additionalProperties": false, + "required": ["schemaVersion", "candidateCommit", "gates"], + "properties": { + "schemaVersion": { "const": "nodekit.submission-manifest/v1" }, + "candidateCommit": { "type": "string", "pattern": "^[a-f0-9]{40}$" }, + "gates": { + "type": "array", + "minItems": 8, + "items": { + "type": "object", + "additionalProperties": false, + "required": ["id", "passed", "evidence"], + "properties": { + "id": { "enum": ["developerTimingMatrix", "freshAgentHeldout", "freshHumanUsability", "threeConvexConsumers", "previewDeployment", "proofloopEaseVerification", "packageInstallProof", "publicationApproval"] }, + "passed": { "type": "boolean" }, + "evidence": { + "type": "array", + "minItems": 1, + "items": { + "type": "object", + "additionalProperties": false, + "required": ["path", "sha256"], + "properties": { + "path": { "type": "string", "minLength": 1 }, + "sha256": { "type": "string", "pattern": "^[a-f0-9]{64}$" } + } + } + } + } + } + } + } +} diff --git a/schemas/nodekit.tournament.v1.schema.json b/schemas/nodekit.tournament.v1.schema.json new file mode 100644 index 0000000..9c816f4 --- /dev/null +++ b/schemas/nodekit.tournament.v1.schema.json @@ -0,0 +1,36 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/HomenShum/node-platform/blob/main/schemas/nodekit.tournament.v1.schema.json", + "title": "NodeKit blind controlled tournament", + "type": "object", + "required": ["schemaVersion", "tournamentId", "benchmarkHash", "harnessHash", "candidates", "pairwiseResults", "protectedEvaluatorUnchanged"], + "properties": { + "schemaVersion": { "const": "nodekit.tournament/v1" }, + "tournamentId": { "type": "string", "minLength": 1 }, + "benchmarkHash": { "$ref": "#/$defs/hash" }, + "harnessHash": { "$ref": "#/$defs/hash" }, + "candidates": { "type": "array", "minItems": 2, "items": { "type": "string", "minLength": 1 }, "uniqueItems": true }, + "pairwiseResults": { + "type": "array", + "minItems": 1, + "items": { + "type": "object", + "required": ["taskId", "left", "right", "winner", "criticId", "criticIndependent", "deterministicChecksPassed", "evidenceRefs"], + "properties": { + "taskId": { "type": "string", "minLength": 1 }, + "left": { "type": "string", "minLength": 1 }, + "right": { "type": "string", "minLength": 1 }, + "winner": { "type": "string", "minLength": 1 }, + "criticId": { "type": "string", "minLength": 1 }, + "criticIndependent": { "const": true }, + "deterministicChecksPassed": { "const": true }, + "evidenceRefs": { "type": "array", "minItems": 1, "items": { "type": "string", "minLength": 1 }, "uniqueItems": true } + }, + "additionalProperties": false + } + }, + "protectedEvaluatorUnchanged": { "const": true } + }, + "$defs": { "hash": { "type": "string", "pattern": "^[a-f0-9]{64}$" } }, + "additionalProperties": false +} diff --git a/scripts/aggregate-developer-timing.mjs b/scripts/aggregate-developer-timing.mjs new file mode 100644 index 0000000..c99979d --- /dev/null +++ b/scripts/aggregate-developer-timing.mjs @@ -0,0 +1,39 @@ +import { readdir, readFile, writeFile } from "node:fs/promises"; +import path from "node:path"; +import { evaluateDeveloperTimingMatrix } from "../src/lib/ease-evidence.mjs"; + +const root = path.resolve(process.argv[2] ?? "proof/ease/downloaded"); +const output = path.resolve(process.argv[3] ?? "proof/ease/developer-timing-runs.json"); + +async function findReceipts(directory) { + const entries = await readdir(directory, { withFileTypes: true }); + const nested = await Promise.all(entries.map(async (entry) => { + const absolute = path.join(directory, entry.name); + if (entry.isDirectory()) return findReceipts(absolute); + return entry.name === "developer-timing-run.json" ? [absolute] : []; + })); + return nested.flat(); +} + +const files = await findReceipts(root); +const byRun = new Map(); +let duplicateCopies = 0; +for (const file of files) { + const receipt = JSON.parse(await readFile(file, "utf8")); + if (receipt.schemaVersion !== "nodekit.developer-timing-run/v1") throw new Error(`${file}: unexpected schemaVersion`); + const existing = byRun.get(receipt.runId); + if (existing) { + if (JSON.stringify(existing.receipt) !== JSON.stringify(receipt)) { + throw new Error(`conflicting timing receipts for runId ${receipt.runId}: ${existing.file} and ${file}`); + } + duplicateCopies += 1; + continue; + } + byRun.set(receipt.runId, { file, receipt }); +} +const receipts = [...byRun.values()].map(({ receipt }) => receipt).sort((a, b) => `${a.lane}/${a.cacheClass}/${a.runId}`.localeCompare(`${b.lane}/${b.cacheClass}/${b.runId}`)); +const verdict = evaluateDeveloperTimingMatrix(receipts); +await writeFile(output, `${JSON.stringify(receipts, null, 2)}\n`); +await writeFile(output.replace(/\.json$/, "-verdict.json"), `${JSON.stringify(verdict, null, 2)}\n`); +console.log(JSON.stringify({ files: files.length, uniqueRuns: receipts.length, duplicateCopies, output, ...verdict }, null, 2)); +if (!verdict.passed) process.exitCode = 1; diff --git a/scripts/evaluate-agent-ease.mjs b/scripts/evaluate-agent-ease.mjs new file mode 100644 index 0000000..b581b70 --- /dev/null +++ b/scripts/evaluate-agent-ease.mjs @@ -0,0 +1,54 @@ +import { readdir, readFile, writeFile } from "node:fs/promises"; +import path from "node:path"; + +const root = path.resolve(process.argv[2] ?? "proof/ease/agents"); +const output = path.resolve(process.argv[3] ?? "proof/ease/fresh-agent-verdict.json"); +const requiredTasks = ["research-map", "volunteer-onboarding", "launch-presentation"]; + +const directories = await readdir(root, { withFileTypes: true }).catch(() => []); +const manifests = []; +for (const entry of directories) { + if (!entry.isDirectory()) continue; + const file = path.join(root, entry.name, "manifest.json"); + try { + const value = JSON.parse(await readFile(file, "utf8")); + if (value.schemaVersion === "nodekit.agent-ease-trial/v1") manifests.push(value); + } catch { + // Incomplete or non-trial evidence is not silently promoted. + } +} + +const errors = []; +const selected = []; +for (const taskId of requiredTasks) { + const passing = manifests + .filter((entry) => entry.taskId === taskId && entry.passed === true) + .sort((a, b) => String(b.generatedAt).localeCompare(String(a.generatedAt))); + if (passing.length === 0) { + errors.push(`${taskId}: no passing authentic trial`); + continue; + } + const trial = passing[0]; + if (trial.interventions !== 0 || trial.userReprompts !== 0) errors.push(`${taskId}: intervention or reprompt recorded`); + if (!Array.isArray(trial.substantiveFiles) || trial.substantiveFiles.length === 0) errors.push(`${taskId}: no substantive files`); + if (!trial.checks || !Object.values(trial.checks).every(Boolean)) errors.push(`${taskId}: one or more required checks failed`); + if (!/^[a-f0-9]{40}$/.test(trial.nodekitCommit ?? "")) errors.push(`${taskId}: missing immutable NodeKit commit`); + if (!/^[a-f0-9]{64}$/.test(trial.nodekitSourceHash ?? "")) errors.push(`${taskId}: missing NodeKit source hash`); + selected.push(trial); +} +const identities = new Set(selected.map((entry) => `${entry.nodekitCommit}/${entry.nodekitSourceHash}`)); +if (identities.size > 1) errors.push("held-out trials do not share one immutable NodeKit identity"); +const verdict = { + errors, + nodekitCommit: identities.size === 1 ? [...identities][0].split("/")[0] : null, + nodekitSourceHash: identities.size === 1 ? [...identities][0].split("/")[1] : null, + nodekitIdentity: identities.size === 1 ? [...identities][0] : null, + observedTrials: manifests.length, + passed: errors.length === 0 && selected.length === requiredTasks.length, + requiredTasks, + schemaVersion: "nodekit.fresh-agent-verdict/v1", + selectedRuns: selected.map((entry) => ({ runId: entry.runId, taskId: entry.taskId, receiptSha256: entry.receiptSha256 })), +}; +await writeFile(output, `${JSON.stringify(verdict, null, 2)}\n`); +console.log(JSON.stringify(verdict, null, 2)); +if (!verdict.passed) process.exitCode = 1; diff --git a/scripts/evaluate-ease-evidence.mjs b/scripts/evaluate-ease-evidence.mjs new file mode 100644 index 0000000..5303eb6 --- /dev/null +++ b/scripts/evaluate-ease-evidence.mjs @@ -0,0 +1,22 @@ +import { readFile, writeFile } from "node:fs/promises"; +import path from "node:path"; +import { evaluateDeveloperTimingMatrix, evaluateFreshUserStudy } from "../src/lib/ease-evidence.mjs"; + +const [mode, inputArg, outputArg] = process.argv.slice(2); +if (!mode || !["developer", "humans"].includes(mode)) { + console.error("usage: node scripts/evaluate-ease-evidence.mjs [input.json] [output.json]"); + process.exit(2); +} +const input = path.resolve(inputArg ?? (mode === "humans" ? "proof/ease/fresh-users.json" : "proof/ease/developer-timing-runs.json")); +const output = path.resolve(outputArg ?? (mode === "humans" ? "proof/ease/fresh-users-verdict.json" : "proof/ease/developer-timing-verdict.json")); +let value; +try { + value = JSON.parse(await readFile(input, "utf8")); +} catch (error) { + console.error(`unable to read ${input}: ${error instanceof Error ? error.message : String(error)}`); + process.exit(1); +} +const verdict = mode === "humans" ? evaluateFreshUserStudy(value) : evaluateDeveloperTimingMatrix(Array.isArray(value) ? value : value.runs ?? []); +await writeFile(output, `${JSON.stringify(verdict, null, 2)}\n`, "utf8"); +console.log(JSON.stringify(verdict, null, 2)); +process.exit(verdict.passed ? 0 : 1); diff --git a/scripts/evaluate-submission.mjs b/scripts/evaluate-submission.mjs new file mode 100644 index 0000000..8f531f0 --- /dev/null +++ b/scripts/evaluate-submission.mjs @@ -0,0 +1,31 @@ +import path from "node:path"; +import { writeFile } from "node:fs/promises"; +import { evaluateSubmissionManifest } from "../src/lib/submission-gate.mjs"; + +function parseArguments(argv) { + const options = {}; + const positional = []; + for (let index = 0; index < argv.length; index += 1) { + const argument = argv[index]; + if (!argument.startsWith("--")) { + positional.push(argument); + continue; + } + const name = argument.slice(2); + if (!["repo-root", "manifest", "output"].includes(name)) throw new Error(`unknown option --${name}`); + const value = argv[index + 1]; + if (!value || value.startsWith("--")) throw new Error(`--${name} requires a value`); + options[name] = value; + index += 1; + } + return { options, positional }; +} + +const { options, positional } = parseArguments(process.argv.slice(2)); +const repoRoot = path.resolve(options["repo-root"] ?? positional[0] ?? "."); +const manifest = options.manifest ?? positional[1] ?? "proof/submission-manifest.json"; +const outputPath = options.output ?? "proof/submission-verdict.json"; +const output = await evaluateSubmissionManifest(repoRoot, manifest); +await writeFile(path.resolve(repoRoot, outputPath), `${JSON.stringify(output, null, 2)}\n`); +console.log(JSON.stringify(output, null, 2)); +if (!output.passed) process.exitCode = 1; diff --git a/scripts/prepare-submission.mjs b/scripts/prepare-submission.mjs new file mode 100644 index 0000000..382f5fd --- /dev/null +++ b/scripts/prepare-submission.mjs @@ -0,0 +1,40 @@ +import path from "node:path"; +import { prepareSubmissionManifest } from "../src/lib/submission-preparation.mjs"; + +function parseArguments(argv) { + const options = {}; + const positional = []; + for (let index = 0; index < argv.length; index += 1) { + const argument = argv[index]; + if (!argument.startsWith("--")) { + positional.push(argument); + continue; + } + const name = argument.slice(2); + if (!["candidate", "repo-root", "output"].includes(name)) { + throw new Error(`unknown option --${name}`); + } + const value = argv[index + 1]; + if (!value || value.startsWith("--")) throw new Error(`--${name} requires a value`); + options[name] = value; + index += 1; + } + return { options, positional }; +} + +const { options, positional } = parseArguments(process.argv.slice(2)); +const candidateRef = options.candidate ?? positional[0] ?? "HEAD"; +const repoRoot = path.resolve(options["repo-root"] ?? positional[1] ?? "."); +const outputPath = options.output ?? positional[2] ?? "proof/submission-manifest.json"; + +const result = await prepareSubmissionManifest({ candidateRef, outputPath, repoRoot }); +const passedGates = result.manifest.gates.filter((gate) => gate.passed).length; +console.log(JSON.stringify({ + candidateCommit: result.manifest.candidateCommit, + outputPath: result.outputPath, + passedGates, + requiredGates: result.manifest.gates.length, + sourceChanges: result.sourceChanges, + sourceIsExact: result.sourceIsExact, + submissionReady: passedGates === result.manifest.gates.length && result.sourceIsExact, +}, null, 2)); diff --git a/scripts/run-agent-ease-trial.mjs b/scripts/run-agent-ease-trial.mjs new file mode 100644 index 0000000..68682e3 --- /dev/null +++ b/scripts/run-agent-ease-trial.mjs @@ -0,0 +1,235 @@ +import { createHash, randomUUID } from "node:crypto"; +import { spawnSync } from "node:child_process"; +import { cp, mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { createProject } from "../src/lib/scaffold.mjs"; +import { computeNodeKitSourceHash } from "../src/lib/source-hash.mjs"; +import { reportsWriteBlockage } from "../src/lib/agent-ease-report.mjs"; + +const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); +const args = Object.fromEntries(process.argv.slice(2).map((entry) => { + const [key, ...value] = entry.replace(/^--/, "").split("="); + return [key, value.join("=") || true]; +})); +const taskId = String(args.task ?? "volunteer-onboarding"); +const tasks = JSON.parse(await readFile(path.join(repoRoot, "evals", "ease", "heldout-tasks.json"), "utf8")); +const task = tasks.tasks.find((entry) => entry.id === taskId); +if (!task) throw new Error(`unknown held-out task ${taskId}; available: ${tasks.tasks.map((entry) => entry.id).join(", ")}`); +const runId = String(args.run ?? `agent_${taskId}_${randomUUID().replaceAll("-", "").slice(0, 12)}`); +const packageManager = String(args.packageManager ?? "npm"); +const executor = String(args.executor ?? "native"); +if (!new Set(["native", "docker"]).has(executor)) throw new Error(`unsupported executor ${executor}`); +const evidenceRoot = path.join(repoRoot, "proof", "ease", "agents", runId); +const candidateRoot = path.join(await mkdtemp(path.join(os.tmpdir(), "nodekit-agent-ease-")), "candidate"); +const commandLedger = []; +const trialStartedAt = new Date().toISOString(); +const trialStarted = performance.now(); + +function sha256(value) { return createHash("sha256").update(value).digest("hex"); } +function run(command, commandArgs, cwd, options = {}) { + const startedAt = new Date().toISOString(); + const started = performance.now(); + const childEnv = { ...process.env, CI: "1", ...(options.env ?? {}) }; + for (const key of options.unsetEnv ?? []) delete childEnv[key]; + const result = spawnSync(command, commandArgs, { + cwd, + encoding: "utf8", + env: childEnv, + maxBuffer: 50 * 1024 * 1024, + shell: process.platform === "win32" && new Set(["npm", "npx", "pnpm"]).has(command), + timeout: options.timeout ?? 300_000, + }); + const record = { + args: commandArgs, + command, + durationMs: Math.round(performance.now() - started), + exitCode: result.status, + startedAt, + stderrSha256: sha256(result.stderr ?? ""), + stdoutSha256: sha256(result.stdout ?? ""), + }; + commandLedger.push(record); + return { + error: result.error, + record, + signal: result.signal, + status: result.status, + stderr: result.stderr ?? "", + stdout: result.stdout ?? "", + }; +} +function requirePass(result, label) { + if (result.error || result.status !== 0) throw new Error(`${label} failed\n${result.stdout ?? ""}\n${result.stderr ?? ""}`); +} + +// Pin the NodeKit source identity before the candidate is generated. A long-running +// trial must never inherit a newer commit merely because the parent repository moved +// while the isolated coding agent was working. +const nodekitCommit = run("git", ["rev-parse", "HEAD"], repoRoot).stdout.trim(); +const sourceHash = await computeNodeKitSourceHash(repoRoot); + +await mkdir(path.join(evidenceRoot, "agent"), { recursive: true }); +await mkdir(path.join(evidenceRoot, "candidate"), { recursive: true }); +await writeFile(path.join(evidenceRoot, "agent", "original-prompt.txt"), `${task.goal}\n`); +await writeFile(path.join(evidenceRoot, "agent", "prompt.sha256"), `${sha256(task.goal)}\n`); +await writeFile(path.join(evidenceRoot, "agent", "interventions.json"), "[]\n"); + +const scaffoldStarted = performance.now(); +await createProject({ brief: "Carry one bounded user intention to a reviewed and verified artifact.", git: true, install: false, name: `ease-${taskId}`, packageManager, target: candidateRoot }); +commandLedger.push({ command: "nodekit create", durationMs: Math.round(performance.now() - scaffoldStarted), exitCode: 0, startedAt: trialStartedAt }); +const installArgs = packageManager === "pnpm" ? ["install", "--prefer-offline"] : ["install", "--prefer-offline", "--no-audit", "--no-fund"]; +requirePass(run(packageManager, installArgs, candidateRoot, { timeout: 300_000 }), "dependency installation"); + +const writeProbePath = path.join(candidateRoot, ".nodekit-agent-write-probe"); +await writeFile(writeProbePath, `${runId}\n`); +const writeProbeMatched = (await readFile(writeProbePath, "utf8")) === `${runId}\n`; +await rm(writeProbePath); +if (!writeProbeMatched) throw new Error("candidate write preflight did not round-trip"); +await writeFile(path.join(evidenceRoot, "agent", "environment.json"), `${JSON.stringify({ + outerIsolation: executor === "docker" ? "disposable-container" : "host-codex-sandbox", + inheritedParentThread: false, + requestedSandbox: executor === "docker" ? "danger-full-access-inside-disposable-container" : "workspace-write", + userConfigLoaded: false, + userExecPolicyLoaded: false, + writePreflight: "passed", +}, null, 2)}\n`); + +const sessionPath = path.join(evidenceRoot, "agent", "session.jsonl"); +const finalPath = path.join(evidenceRoot, "agent", "final-report.md"); +const candidateFinalPath = path.join(candidateRoot, ".nodekit-agent-final-report.md"); +const codexWrapper = process.platform === "win32" + ? spawnSync("where.exe", ["codex.cmd"], { encoding: "utf8" }).stdout.split(/\r?\n/).find(Boolean) + : undefined; +const agentCommand = process.platform === "win32" ? process.execPath : "codex"; +const agentPrefixArgs = process.platform === "win32" + ? [path.join(path.dirname(codexWrapper ?? ""), "node_modules", "@openai", "codex", "bin", "codex.js")] + : []; +if (process.platform === "win32" && !codexWrapper) throw new Error("codex.cmd was not found"); +const dockerImage = String(args.dockerImage ?? "nodekit-ease-agent:codex-0.142.5"); +const authPath = path.join(os.homedir(), ".codex", "auth.json"); +const nativeAgentArgs = [...agentPrefixArgs, + "exec", "--ephemeral", "--ignore-user-config", "--ignore-rules", "--sandbox", "workspace-write", "--json", + "--output-last-message", finalPath, "-C", candidateRoot, task.goal, +]; +const dockerAgentArgs = [ + "run", "--rm", + "--mount", `type=bind,source=${candidateRoot},target=/workspace`, + "--mount", `type=bind,source=${authPath},target=/root/.codex/auth.json,readonly`, + "--workdir", "/workspace", + "--env", "CODEX_HOME=/root/.codex", + "--env", "CI=1", + dockerImage, + "codex", "exec", "--ephemeral", "--ignore-user-config", "--ignore-rules", "--sandbox", "danger-full-access", "--json", + "--output-last-message", "/workspace/.nodekit-agent-final-report.md", "-C", "/workspace", task.goal, +]; +const agent = executor === "docker" + ? run("docker", dockerAgentArgs, candidateRoot, { timeout: Number(args.timeoutMs ?? 1_800_000) }) + : run(agentCommand, nativeAgentArgs, candidateRoot, { + timeout: Number(args.timeoutMs ?? 1_800_000), + unsetEnv: [ + "CODEX_INTERNAL_ORIGINATOR_OVERRIDE", + "CODEX_PERMISSION_PROFILE", + "CODEX_SHELL", + "CODEX_THREAD_ID", + ], + }); +if (executor === "docker") { + await writeFile(finalPath, await readFile(candidateFinalPath, "utf8").catch(() => "")); + await rm(candidateFinalPath, { force: true }); +} +await writeFile(sessionPath, agent.stdout ?? ""); +await writeFile(path.join(evidenceRoot, "agent", "stderr.txt"), agent.stderr ?? ""); + +const checks = {}; +for (const [name, command, commandArgs] of [ + ["compile", packageManager, ["run", "compile"]], + ["check", packageManager, ["run", "check"]], + ["demo", packageManager, ["run", "demo"]], + ["eval", packageManager, ["run", "eval"]], + ["browserContract", packageManager, ["run", "proof:browser-contract"]], +]) { + const result = run(command, commandArgs, candidateRoot); + checks[name] = result.status === 0 && !result.error; +} +const browserInstall = packageManager === "pnpm" + ? run("pnpm", ["exec", "playwright", "install", "chromium"], candidateRoot, { timeout: 300_000 }) + : run("npx", ["playwright", "install", "chromium"], candidateRoot, { timeout: 300_000 }); +checks.browserRuntime = browserInstall.status === 0 && !browserInstall.error; +const browser = run(packageManager, ["run", "proof:browser"], candidateRoot, { + env: { NODEKIT_EASE_RUN_ID: runId, NODEKIT_SOURCE_COMMIT: nodekitCommit, NODEKIT_SOURCE_HASH: sourceHash }, + timeout: 300_000, +}); +checks.browserJourney = browser.status === 0 && !browser.error; +const proof = run(packageManager, ["run", "proof"], candidateRoot); +checks.proof = proof.status === 0 && !proof.error; +const endingNodekitCommit = run("git", ["rev-parse", "HEAD"], repoRoot).stdout.trim(); +const endingNodekitSourceHash = await computeNodeKitSourceHash(repoRoot); +checks.nodekitIdentityStable = endingNodekitCommit === nodekitCommit && endingNodekitSourceHash === sourceHash; + +const finalReport = await readFile(finalPath, "utf8").catch(() => ""); +const porcelain = run("git", ["status", "--porcelain=v1"], candidateRoot).stdout; +const changedFiles = porcelain.split(/\r?\n/).filter(Boolean).map((line) => line.slice(3).trim()); +const substantiveFiles = changedFiles.filter((file) => + file !== "package-lock.json" + && !file.startsWith("proof/") + && !file.startsWith(".nodeagent/") + && !file.startsWith("node_modules/")); +const blockedReport = reportsWriteBlockage(finalReport); +checks.agentImplemented = substantiveFiles.length > 0; +checks.agentReportedCompletion = finalReport.length > 0 && !blockedReport; + +const browserEvidenceSource = path.join(candidateRoot, "proof", "ease", runId, "browser"); +const browserEvidenceTarget = path.join(evidenceRoot, "candidate", "browser"); +await cp(browserEvidenceSource, browserEvidenceTarget, { recursive: true }).catch(() => undefined); +await cp( + path.join(candidateRoot, "proof", "browser-certification.json"), + path.join(evidenceRoot, "candidate", "browser-certification.json"), +).catch(() => undefined); + +const diff = run("git", ["diff", "--binary", "HEAD"], candidateRoot).stdout; +await writeFile(path.join(evidenceRoot, "candidate", "diff.patch"), diff); +await writeFile(path.join(evidenceRoot, "candidate", "git-status.txt"), run("git", ["status", "--short"], candidateRoot).stdout); +await writeFile(path.join(evidenceRoot, "candidate", "commit.txt"), `${run("git", ["rev-parse", "HEAD"], candidateRoot).stdout.trim()}\n`); +const identity = await readFile(path.join(candidateRoot, ".nodeagent", "application-identity.json"), "utf8").catch(() => "{}"); +await writeFile(path.join(evidenceRoot, "candidate", "application-identity.json"), identity); +run("git", ["archive", "--format=tar.gz", `--output=${path.join(evidenceRoot, "candidate", "generated-repo.tar.gz")}`, "HEAD"], candidateRoot); + +const sessionLines = (agent.stdout ?? "").split(/\r?\n/).filter(Boolean); +const parsedEvents = sessionLines.map((line) => { try { return JSON.parse(line); } catch { return null; } }).filter(Boolean); +const usageEvents = parsedEvents.filter((entry) => JSON.stringify(entry).includes("token")); +await writeFile(path.join(evidenceRoot, "agent", "token-usage.json"), `${JSON.stringify({ events: usageEvents, note: "Raw Codex JSONL usage-bearing events; cost depends on the configured account and is not inferred." }, null, 2)}\n`); +await writeFile(path.join(evidenceRoot, "commands.jsonl"), commandLedger.map((entry) => JSON.stringify(entry)).join("\n") + "\n"); + +const receipt = { + agentExitCode: agent.status, + agentVersion: executor === "docker" + ? run("docker", ["run", "--rm", dockerImage, "codex", "--version"], repoRoot).stdout.trim() + : run(agentCommand, [...agentPrefixArgs, "--version"], repoRoot).stdout.trim(), + candidateRoot, + changedFiles, + checks, + durationMs: Math.round(performance.now() - trialStarted), + executor, + generatedAt: new Date().toISOString(), + interventions: 0, + endingNodekitCommit, + endingNodekitSourceHash, + nodekitCommit, + nodekitSourceHash: sourceHash, + packageManager, + passed: agent.status === 0 && Object.values(checks).every(Boolean), + promptSha256: sha256(task.goal), + runId, + schemaVersion: "nodekit.agent-ease-trial/v1", + taskId, + trialStartedAt, + userReprompts: 0, + substantiveFiles, +}; +receipt.verdict = receipt.passed ? "PILOT_PASS_NOT_REPEATABILITY_CERTIFIED" : blockedReport ? "PILOT_FAIL_AGENT_BLOCKED" : "PILOT_FAIL"; +receipt.receiptSha256 = sha256(JSON.stringify(receipt)); +await writeFile(path.join(evidenceRoot, "manifest.json"), `${JSON.stringify(receipt, null, 2)}\n`); +console.log(JSON.stringify(receipt, null, 2)); +if (!receipt.passed) process.exitCode = 1; diff --git a/scripts/run-postgres-conformance.mjs b/scripts/run-postgres-conformance.mjs new file mode 100644 index 0000000..c39be36 --- /dev/null +++ b/scripts/run-postgres-conformance.mjs @@ -0,0 +1,128 @@ +import { createHash, randomUUID } from "node:crypto"; +import { execFileSync } from "node:child_process"; +import { readFile, writeFile } from "node:fs/promises"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { Pool } from "pg"; +import { runCaseflowConformance } from "../src/lib/caseflow-conformance.mjs"; +import { contentHash } from "../src/lib/caseflow.mjs"; +import { createPostgresCaseflow } from "../src/adapters/postgres-caseflow.mjs"; +import { computeNodeKitSourceHash } from "../src/lib/source-hash.mjs"; + +const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); +const connectionString = process.env.NODEKIT_POSTGRES_URL; +if (!connectionString) throw new Error("NODEKIT_POSTGRES_URL is required"); + +function option(name) { + const index = process.argv.indexOf(name); + if (index < 0) return null; + const value = process.argv[index + 1]; + if (!value || value.startsWith("--")) throw new Error(`${name} requires a value`); + return value; +} + +function sha256(value) { + return createHash("sha256").update(value).digest("hex"); +} + +const pool = new Pool({ connectionString, max: 8 }); +const migrationPath = path.join(repoRoot, "adapters", "postgres", "001_caseflow.sql"); +const migration = await readFile(migrationPath, "utf8"); +const migrationSha256 = sha256(migration); +const candidateCommit = execFileSync("git", ["rev-parse", "HEAD"], { cwd: repoRoot, encoding: "utf8" }).trim(); +const dirtySource = execFileSync("git", ["status", "--porcelain", "--untracked-files=all"], { cwd: repoRoot, encoding: "utf8" }) + .split(/\r?\n/) + .filter(Boolean) + .map((line) => line.slice(3).replace(/^"|"$/g, "").replaceAll("\\", "/")) + .filter((file) => !/^(?:proof|docs|evolution)\//.test(file)); +if (dirtySource.length > 0 && process.env.NODEKIT_ALLOW_DIRTY_CONFORMANCE !== "true") { + throw new Error(`PostgreSQL conformance requires a clean source candidate; dirty paths: ${dirtySource.join(", ")}`); +} +const nodekitSourceHash = await computeNodeKitSourceHash(repoRoot); +const ownerPrefix = `postgres-conformance-${randomUUID()}`; +const ownerA = `${ownerPrefix}-a`; +const ownerB = `${ownerPrefix}-b`; + +try { + await pool.query(migration); + const conformance = await runCaseflowConformance( + () => createPostgresCaseflow({ pool, ownerId: ownerA }), + { requiredCapabilities: { durableState: true, optimisticConcurrency: true, transactions: true } }, + ); + + const runtimeA = createPostgresCaseflow({ pool, ownerId: ownerA }); + const runtimeB = createPostgresCaseflow({ pool, ownerId: ownerB }); + const ownerASnapshot = await runtimeA.snapshot(); + const ownerBSnapshot = await runtimeB.snapshot(); + const ownerIsolation = ownerASnapshot.cases.length === 1 && ownerBSnapshot.cases.length === 0; + let crossOwnerDenied = false; + try { + await runtimeB.startRun({ caseId: ownerASnapshot.cases[0].caseId, stages: [{ id: "working", label: "Working", owner: "agent" }] }); + } catch (error) { + crossOwnerDenied = /case not found/.test(String(error?.message)); + } + + const raceCase = await runtimeA.createCase({ title: "Same-base race", primaryJob: "Apply exactly one proposal" }); + const raceRun = await runtimeA.startRun({ + caseId: raceCase.caseId, + stages: [ + { id: "working", label: "Working", owner: "agent" }, + { id: "complete", label: "Complete", owner: "system" }, + ], + }); + const raceArtifact = await runtimeA.createArtifact({ caseId: raceCase.caseId, runId: raceRun.runId, title: "Race artifact", content: { value: 1 } }); + const proposalA = await runtimeA.createProposal({ artifactId: raceArtifact.artifactId, baseVersion: 1, patch: { value: 2 } }); + const proposalB = await runtimeA.createProposal({ artifactId: raceArtifact.artifactId, baseVersion: 1, patch: { value: 3 } }); + const race = await Promise.all([ + runtimeA.decideProposal({ proposalId: proposalA.proposalId, decision: "accepted" }), + runtimeA.decideProposal({ proposalId: proposalB.proposalId, decision: "accepted" }), + ]); + const raceStatuses = race.map((entry) => entry.proposal.status).sort(); + const sameBaseRaceFailedClosed = JSON.stringify(raceStatuses) === JSON.stringify(["accepted", "conflicted"]) + && race.every((entry) => entry.artifact.canonicalVersion === 2); + + const reloaded = createPostgresCaseflow({ pool, ownerId: ownerA }); + const reloadedSnapshot = await reloaded.snapshot(); + const reloadPreservedState = reloadedSnapshot.cases.length === 2 + && reloadedSnapshot.artifacts.find((entry) => entry.artifactId === raceArtifact.artifactId)?.canonicalVersion === 2; + const completionReceipt = ownerASnapshot.receipts[0]; + const { receiptHash, receiptId, ...receiptBody } = completionReceipt; + const receiptIntegrity = /^[a-f0-9]{64}$/.test(receiptHash) + && receiptId.startsWith("receipt_") + && contentHash(receiptBody) === receiptHash; + + const assertions = { + crossOwnerDenied, + ownerIsolation, + receiptIntegrity, + reloadPreservedState, + sameBaseRaceFailedClosed, + sharedConformancePassed: conformance.passed, + }; + const serverVersion = (await pool.query("show server_version")).rows[0].server_version; + const verdict = { + schemaVersion: "nodekit.postgres-conformance/v1", + adapter: "@homenshum/nodekit/adapters/postgres", + assertions, + candidateCommit, + capabilities: conformance.capabilities, + conformance, + generatedAt: new Date().toISOString(), + migration: { + path: "adapters/postgres/001_caseflow.sql", + sha256: migrationSha256, + }, + nodekitSourceHash, + ownerScope: "isolated-test-identities", + passed: Object.values(assertions).every(Boolean), + postgres: { + serverVersion, + }, + }; + const output = option("--output"); + if (output) await writeFile(path.resolve(output), `${JSON.stringify(verdict, null, 2)}\n`); + console.log(JSON.stringify(verdict, null, 2)); + if (!verdict.passed) process.exitCode = 1; +} finally { + await pool.end(); +} diff --git a/src/adapters/postgres-caseflow.mjs b/src/adapters/postgres-caseflow.mjs new file mode 100644 index 0000000..faa3b48 --- /dev/null +++ b/src/adapters/postgres-caseflow.mjs @@ -0,0 +1,518 @@ +import { randomUUID } from "node:crypto"; +import { + CASEFLOW_SCHEMA_VERSIONS, + TERMINAL_RUN_STATUSES, + contentHash, +} from "../lib/caseflow.mjs"; +import { runtimeProfiles } from "../lib/runtime-capabilities.mjs"; + +const defaultActor = Object.freeze({ type: "system", id: "nodekit" }); + +function id(prefix) { + return `${prefix}_${randomUUID().replaceAll("-", "")}`; +} + +function iso(value) { + return value instanceof Date ? value.toISOString() : String(value); +} + +function json(value) { + return typeof value === "string" ? JSON.parse(value) : structuredClone(value); +} + +function caseRecord(row) { + return { + caseId: row.case_id, + createdAt: iso(row.created_at), + currentRunId: row.current_run_id, + primaryJob: row.primary_job, + schemaVersion: CASEFLOW_SCHEMA_VERSIONS.case, + status: row.status, + title: row.title, + updatedAt: iso(row.updated_at), + }; +} + +function runRecord(row) { + return { + caseId: row.case_id, + createdAt: iso(row.created_at), + currentStageId: row.current_stage_id, + nextAction: row.next_action, + nextActionOwner: row.next_action_owner, + runId: row.run_id, + schemaVersion: CASEFLOW_SCHEMA_VERSIONS.run, + stages: json(row.stages), + status: row.status, + updatedAt: iso(row.updated_at), + }; +} + +function proposalRecord(row) { + return { + artifactId: row.artifact_id, + baseVersion: row.base_version, + createdAt: iso(row.created_at), + patch: json(row.patch), + proposalId: row.proposal_id, + rationale: row.rationale, + schemaVersion: CASEFLOW_SCHEMA_VERSIONS.proposal, + status: row.status, + }; +} + +function approvalRecord(row) { + if (!row) return null; + return { + approvalId: row.approval_id, + comment: row.comment, + decidedAt: iso(row.decided_at), + decision: row.decision, + proposalId: row.proposal_id, + schemaVersion: CASEFLOW_SCHEMA_VERSIONS.approval, + }; +} + +function exceptionRecord(row) { + return { + code: row.code, + exceptionId: row.exception_id, + message: row.message, + preservedState: json(row.preserved_state), + raisedAt: iso(row.raised_at), + resolution: row.resolution, + ...(row.resolved_at ? { resolvedAt: iso(row.resolved_at) } : {}), + runId: row.run_id, + schemaVersion: CASEFLOW_SCHEMA_VERSIONS.exception, + status: row.status, + }; +} + +function receiptRecord(row) { + return json(row.body); +} + +function eventRecord(row) { + return { + actor: json(row.actor), + aggregateId: row.aggregate_id, + aggregateType: row.aggregate_type, + eventId: row.event_id, + eventType: row.event_type, + occurredAt: iso(row.occurred_at), + payload: json(row.payload), + schemaVersion: CASEFLOW_SCHEMA_VERSIONS.event, + sequence: row.sequence, + }; +} + +async function withTransaction(pool, operation) { + if (typeof pool.connect !== "function") { + throw new Error("PostgreSQL Caseflow requires a pool/client with connect(), query(), and release() transaction support"); + } + const client = await pool.connect(); + try { + await client.query("begin"); + const result = await operation(client); + await client.query("commit"); + return result; + } catch (error) { + await client.query("rollback").catch(() => undefined); + throw error; + } finally { + client.release(); + } +} + +async function emit(client, { actor = defaultActor, aggregateId, aggregateType, eventType, now, ownerId, payload = {} }) { + await client.query("select pg_advisory_xact_lock(hashtextextended($1, 0))", [aggregateId]); + const sequenceResult = await client.query( + "select coalesce(max(sequence), 0)::integer + 1 as sequence from nodekit.events where owner_id = $1 and aggregate_id = $2", + [ownerId, aggregateId], + ); + const event = { + actor: structuredClone(actor), + aggregateId, + aggregateType, + eventId: id("event"), + eventType, + occurredAt: now, + payload: structuredClone(payload), + schemaVersion: CASEFLOW_SCHEMA_VERSIONS.event, + sequence: sequenceResult.rows[0].sequence, + }; + await client.query( + `insert into nodekit.events + (event_id, owner_id, aggregate_type, aggregate_id, sequence, event_type, actor, payload, occurred_at) + values ($1, $2, $3, $4, $5, $6, $7::jsonb, $8::jsonb, $9)`, + [event.eventId, ownerId, aggregateType, aggregateId, event.sequence, eventType, JSON.stringify(event.actor), JSON.stringify(event.payload), now], + ); + return event; +} + +async function loadArtifact(client, ownerId, artifactId) { + const artifactResult = await client.query( + "select * from nodekit.artifacts where owner_id = $1 and artifact_id = $2", + [ownerId, artifactId], + ); + if (artifactResult.rowCount !== 1) throw new Error(`artifact not found: ${artifactId}`); + const row = artifactResult.rows[0]; + const versions = await client.query( + "select * from nodekit.artifact_versions where artifact_id = $1 order by version", + [artifactId], + ); + return { + artifactId: row.artifact_id, + caseId: row.case_id, + canonicalVersion: row.canonical_version, + createdAt: iso(row.created_at), + kind: row.kind, + runId: row.run_id, + schemaVersion: CASEFLOW_SCHEMA_VERSIONS.artifact, + title: row.title, + updatedAt: iso(row.updated_at), + versions: versions.rows.map((version) => ({ + content: json(version.content), + contentHash: version.content_hash, + createdAt: iso(version.created_at), + ...(version.proposal_id ? { proposalId: version.proposal_id } : {}), + version: version.version, + })), + }; +} + +/** + * Create an owner-scoped PostgreSQL implementation of NodeKit Caseflow. + * `pool` is compatible with node-postgres Pool: it must expose query() and connect(). + */ +export function createPostgresCaseflow({ pool, ownerId, clock = () => new Date().toISOString() } = {}) { + if (!pool || typeof pool.query !== "function") throw new Error("PostgreSQL Caseflow requires a query-capable pool"); + if (!String(ownerId ?? "").trim()) throw new Error("PostgreSQL Caseflow requires an ownerId"); + const owner = String(ownerId).trim(); + + async function createCase({ title, primaryJob, actor }) { + if (!String(title ?? "").trim()) throw new Error("case title is required"); + if (!String(primaryJob ?? "").trim()) throw new Error("case primaryJob is required"); + return withTransaction(pool, async (client) => { + const now = clock(); + const row = (await client.query( + `insert into nodekit.cases + (case_id, owner_id, title, primary_job, status, current_run_id, created_at, updated_at) + values ($1, $2, $3, $4, 'ready', null, $5, $5) returning *`, + [id("case"), owner, String(title).trim(), String(primaryJob).trim(), now], + )).rows[0]; + const record = caseRecord(row); + await emit(client, { actor, aggregateId: record.caseId, aggregateType: "case", eventType: "case.created", now, ownerId: owner, payload: record }); + return record; + }); + } + + async function startRun({ caseId, stages, actor }) { + if (!Array.isArray(stages) || stages.length === 0) throw new Error("run stages are required"); + return withTransaction(pool, async (client) => { + const caseResult = await client.query( + "select * from nodekit.cases where owner_id = $1 and case_id = $2 for update", + [owner, caseId], + ); + if (caseResult.rowCount !== 1) throw new Error(`case not found: ${caseId}`); + const caseRow = caseResult.rows[0]; + if (caseRow.current_run_id) { + const current = await client.query( + "select * from nodekit.runs where owner_id = $1 and run_id = $2", + [owner, caseRow.current_run_id], + ); + if (current.rowCount === 1 && !TERMINAL_RUN_STATUSES.includes(current.rows[0].status)) return runRecord(current.rows[0]); + } + const normalizedStages = stages.map((stage, index) => ({ + id: String(stage.id ?? `stage-${index + 1}`), + label: String(stage.label ?? stage.id ?? `Stage ${index + 1}`), + owner: stage.owner ?? "system", + status: index === 0 ? "active" : "pending", + })); + const now = clock(); + const runId = id("run"); + const row = (await client.query( + `insert into nodekit.runs + (run_id, owner_id, case_id, status, current_stage_id, next_action, next_action_owner, stages, created_at, updated_at) + values ($1, $2, $3, 'active', $4, $5, $6, $7::jsonb, $8, $8) returning *`, + [runId, owner, caseId, normalizedStages[0].id, normalizedStages[0].label, normalizedStages[0].owner, JSON.stringify(normalizedStages), now], + )).rows[0]; + await client.query( + "update nodekit.cases set current_run_id = $1, status = 'in_progress', updated_at = $2 where owner_id = $3 and case_id = $4", + [runId, now, owner, caseId], + ); + const record = runRecord(row); + await emit(client, { actor, aggregateId: runId, aggregateType: "run", eventType: "run.started", now, ownerId: owner, payload: record }); + await emit(client, { actor, aggregateId: runId, aggregateType: "run", eventType: "stage.entered", now, ownerId: owner, payload: { stageId: record.currentStageId } }); + return record; + }); + } + + async function enterStage({ runId, stageId, nextAction, nextActionOwner, actor }) { + return withTransaction(pool, async (client) => { + const result = await client.query( + "select * from nodekit.runs where owner_id = $1 and run_id = $2 for update", + [owner, runId], + ); + if (result.rowCount !== 1) throw new Error(`run not found: ${runId}`); + const current = runRecord(result.rows[0]); + if (TERMINAL_RUN_STATUSES.includes(current.status)) throw new Error(`run is terminal: ${current.status}`); + const targetIndex = current.stages.findIndex((stage) => stage.id === stageId); + if (targetIndex < 0) throw new Error(`stage not found: ${stageId}`); + const stages = current.stages.map((stage, index) => ({ + ...stage, + status: index < targetIndex ? "completed" : index === targetIndex ? "active" : "pending", + })); + const now = clock(); + const action = nextAction ?? stages[targetIndex].label; + const actionOwner = nextActionOwner ?? stages[targetIndex].owner; + const row = (await client.query( + `update nodekit.runs set current_stage_id = $1, next_action = $2, next_action_owner = $3, + stages = $4::jsonb, updated_at = $5 where owner_id = $6 and run_id = $7 returning *`, + [stageId, action, actionOwner, JSON.stringify(stages), now, owner, runId], + )).rows[0]; + await emit(client, { actor, aggregateId: runId, aggregateType: "run", eventType: "stage.entered", now, ownerId: owner, payload: { nextAction: action, nextActionOwner: actionOwner, stageId } }); + return runRecord(row); + }); + } + + async function createArtifact({ caseId, runId, kind = "generic", title = "Artifact", content, actor }) { + return withTransaction(pool, async (client) => { + const references = await client.query( + `select + exists(select 1 from nodekit.cases where owner_id = $1 and case_id = $2) as has_case, + exists(select 1 from nodekit.runs where owner_id = $1 and run_id = $3 and case_id = $2) as has_run`, + [owner, caseId, runId], + ); + if (!references.rows[0].has_case) throw new Error(`case not found: ${caseId}`); + if (!references.rows[0].has_run) throw new Error(`run not found: ${runId}`); + const now = clock(); + const artifactId = id("artifact"); + await client.query( + `insert into nodekit.artifacts + (artifact_id, owner_id, case_id, run_id, kind, title, canonical_version, created_at, updated_at) + values ($1, $2, $3, $4, $5, $6, 1, $7, $7)`, + [artifactId, owner, caseId, runId, kind, String(title), now], + ); + await client.query( + `insert into nodekit.artifact_versions (artifact_id, version, content, content_hash, created_at) + values ($1, 1, $2::jsonb, $3, $4)`, + [artifactId, JSON.stringify(content), contentHash(content), now], + ); + await emit(client, { actor, aggregateId: artifactId, aggregateType: "artifact", eventType: "artifact.created", now, ownerId: owner, payload: { artifactId, version: 1 } }); + return loadArtifact(client, owner, artifactId); + }); + } + + async function createProposal({ artifactId, baseVersion, patch, rationale = "", actor }) { + return withTransaction(pool, async (client) => { + const artifact = await client.query( + "select * from nodekit.artifacts where owner_id = $1 and artifact_id = $2 for update", + [owner, artifactId], + ); + if (artifact.rowCount !== 1) throw new Error(`artifact not found: ${artifactId}`); + if (baseVersion !== artifact.rows[0].canonical_version) { + throw new Error(`proposal base version ${baseVersion} is stale; canonical version is ${artifact.rows[0].canonical_version}`); + } + const now = clock(); + const proposalId = id("proposal"); + const row = (await client.query( + `insert into nodekit.proposals + (proposal_id, owner_id, artifact_id, base_version, patch, rationale, status, created_at) + values ($1, $2, $3, $4, $5::jsonb, $6, 'pending', $7) returning *`, + [proposalId, owner, artifactId, baseVersion, JSON.stringify(patch), String(rationale), now], + )).rows[0]; + const record = proposalRecord(row); + await emit(client, { actor, aggregateId: proposalId, aggregateType: "proposal", eventType: "proposal.created", now, ownerId: owner, payload: record }); + return record; + }); + } + + async function decideProposal({ proposalId, decision, actor, comment = "" }) { + if (!new Set(["accepted", "rejected"]).has(decision)) throw new Error("decision must be accepted or rejected"); + return withTransaction(pool, async (client) => { + const now = clock(); + const proposalBefore = await client.query( + "select * from nodekit.proposals where owner_id = $1 and proposal_id = $2", + [owner, proposalId], + ); + if (proposalBefore.rowCount !== 1) throw new Error(`proposal not found: ${proposalId}`); + const result = (await client.query( + "select * from nodekit.apply_proposal($1, $2, $3, $4, $5, $6, $7)", + [owner, proposalId, decision, id("approval"), String(comment), contentHash(json(proposalBefore.rows[0].patch)), now], + )).rows[0]; + const proposal = proposalRecord((await client.query( + "select * from nodekit.proposals where owner_id = $1 and proposal_id = $2", + [owner, proposalId], + )).rows[0]); + const approval = approvalRecord((await client.query( + "select * from nodekit.approvals where owner_id = $1 and proposal_id = $2", + [owner, proposalId], + )).rows[0]); + const artifact = await loadArtifact(client, owner, proposal.artifactId); + if (!result.reused) { + if (proposal.status === "accepted") { + await emit(client, { actor, aggregateId: artifact.artifactId, aggregateType: "artifact", eventType: "artifact.version_created", now, ownerId: owner, payload: { proposalId, version: artifact.canonicalVersion } }); + } + const payload = proposal.status === "conflicted" + ? { canonicalVersion: artifact.canonicalVersion } + : { approvalId: approval.approvalId }; + await emit(client, { actor, aggregateId: proposalId, aggregateType: "proposal", eventType: `proposal.${proposal.status}`, now, ownerId: owner, payload }); + } + return { approval, artifact, proposal, reused: result.reused }; + }); + } + + async function raiseException({ runId, code, message, preservedState = {}, actor }) { + return withTransaction(pool, async (client) => { + const run = await client.query( + "select * from nodekit.runs where owner_id = $1 and run_id = $2 for update", + [owner, runId], + ); + if (run.rowCount !== 1) throw new Error(`run not found: ${runId}`); + const now = clock(); + const exceptionId = id("exception"); + const row = (await client.query( + `insert into nodekit.exceptions + (exception_id, owner_id, run_id, code, message, preserved_state, status, resolution, raised_at) + values ($1, $2, $3, $4, $5, $6::jsonb, 'open', null, $7) returning *`, + [exceptionId, owner, runId, String(code ?? "unknown"), String(message ?? "An exception occurred."), JSON.stringify(preservedState), now], + )).rows[0]; + await client.query( + "update nodekit.runs set status = 'blocked', next_action = 'Resolve exception', next_action_owner = 'user', updated_at = $1 where owner_id = $2 and run_id = $3", + [now, owner, runId], + ); + await emit(client, { actor, aggregateId: runId, aggregateType: "run", eventType: "exception.raised", now, ownerId: owner, payload: { code: row.code, exceptionId } }); + return exceptionRecord(row); + }); + } + + async function resolveException({ exceptionId, resolution, nextAction, nextActionOwner, actor }) { + return withTransaction(pool, async (client) => { + const existing = await client.query( + "select * from nodekit.exceptions where owner_id = $1 and exception_id = $2 for update", + [owner, exceptionId], + ); + if (existing.rowCount !== 1) throw new Error(`exception not found: ${exceptionId}`); + if (existing.rows[0].status !== "open") throw new Error("exception is already resolved"); + const now = clock(); + const resolved = (await client.query( + `update nodekit.exceptions set status = 'resolved', resolution = $1, resolved_at = $2 + where owner_id = $3 and exception_id = $4 returning *`, + [String(resolution ?? "resolved"), now, owner, exceptionId], + )).rows[0]; + const runRow = (await client.query( + `update nodekit.runs set status = 'active', next_action = $1, next_action_owner = $2, updated_at = $3 + where owner_id = $4 and run_id = $5 returning *`, + [nextAction ?? "Continue run", nextActionOwner ?? "system", now, owner, resolved.run_id], + )).rows[0]; + await emit(client, { actor, aggregateId: resolved.run_id, aggregateType: "run", eventType: "exception.resolved", now, ownerId: owner, payload: { exceptionId, resolution: resolved.resolution } }); + return { exception: exceptionRecord(resolved), run: runRecord(runRow) }; + }); + } + + async function completeRun({ runId, actor }) { + return withTransaction(pool, async (client) => { + const runResult = await client.query( + "select * from nodekit.runs where owner_id = $1 and run_id = $2 for update", + [owner, runId], + ); + if (runResult.rowCount !== 1) throw new Error(`run not found: ${runId}`); + const current = runRecord(runResult.rows[0]); + if (current.status === "completed") { + const receipt = await client.query( + "select * from nodekit.receipts where owner_id = $1 and run_id = $2", + [owner, runId], + ); + if (receipt.rowCount !== 1) throw new Error("completed run is missing its receipt"); + return { receipt: receiptRecord(receipt.rows[0]), run: current, reused: true }; + } + if (TERMINAL_RUN_STATUSES.includes(current.status)) throw new Error(`run is terminal: ${current.status}`); + const open = await client.query( + "select 1 from nodekit.exceptions where owner_id = $1 and run_id = $2 and status = 'open' limit 1", + [owner, runId], + ); + if (open.rowCount > 0) throw new Error("run has unresolved exceptions"); + const now = clock(); + const stages = current.stages.map((stage) => ({ ...stage, status: "completed" })); + const completedRow = (await client.query( + `update nodekit.runs set status = 'completed', next_action = 'Review receipt', next_action_owner = 'user', + stages = $1::jsonb, updated_at = $2 where owner_id = $3 and run_id = $4 returning *`, + [JSON.stringify(stages), now, owner, runId], + )).rows[0]; + await client.query( + "update nodekit.cases set status = 'completed', updated_at = $1 where owner_id = $2 and case_id = $3", + [now, owner, current.caseId], + ); + await emit(client, { actor, aggregateId: runId, aggregateType: "run", eventType: "run.completed", now, ownerId: owner }); + const artifactIds = (await client.query( + "select artifact_id from nodekit.artifacts where owner_id = $1 and run_id = $2 order by created_at, artifact_id", + [owner, runId], + )).rows.map((row) => row.artifact_id); + const proposalIds = artifactIds.length === 0 ? [] : (await client.query( + "select proposal_id from nodekit.proposals where owner_id = $1 and artifact_id = any($2::text[]) order by created_at, proposal_id", + [owner, artifactIds], + )).rows.map((row) => row.proposal_id); + const aggregateIds = [runId, ...artifactIds, ...proposalIds]; + const eventIds = (await client.query( + "select event_id from nodekit.events where owner_id = $1 and aggregate_id = any($2::text[]) order by occurred_at, event_id", + [owner, aggregateIds], + )).rows.map((row) => row.event_id); + const receiptBody = { + artifactIds, + caseId: current.caseId, + eventIds, + generatedAt: now, + proposalIds, + runId, + schemaVersion: CASEFLOW_SCHEMA_VERSIONS.receipt, + status: "completed", + }; + const receipt = { ...receiptBody, receiptId: id("receipt"), receiptHash: contentHash(receiptBody) }; + await client.query( + `insert into nodekit.receipts (receipt_id, owner_id, run_id, receipt_hash, body, generated_at) + values ($1, $2, $3, $4, $5::jsonb, $6)`, + [receipt.receiptId, owner, runId, receipt.receiptHash, JSON.stringify(receipt), now], + ); + await emit(client, { actor, aggregateId: runId, aggregateType: "run", eventType: "receipt.created", now, ownerId: owner, payload: { receiptHash: receipt.receiptHash, receiptId: receipt.receiptId } }); + return { receipt, run: runRecord(completedRow), reused: false }; + }); + } + + async function snapshot() { + const [cases, runs, artifacts, proposals, approvals, exceptions, receipts, events] = await Promise.all([ + pool.query("select * from nodekit.cases where owner_id = $1 order by created_at, case_id", [owner]), + pool.query("select * from nodekit.runs where owner_id = $1 order by created_at, run_id", [owner]), + pool.query("select * from nodekit.artifacts where owner_id = $1 order by created_at, artifact_id", [owner]), + pool.query("select * from nodekit.proposals where owner_id = $1 order by created_at, proposal_id", [owner]), + pool.query("select * from nodekit.approvals where owner_id = $1 order by decided_at, approval_id", [owner]), + pool.query("select * from nodekit.exceptions where owner_id = $1 order by raised_at, exception_id", [owner]), + pool.query("select * from nodekit.receipts where owner_id = $1 order by generated_at, receipt_id", [owner]), + pool.query("select * from nodekit.events where owner_id = $1 order by occurred_at, event_id", [owner]), + ]); + return { + approvals: approvals.rows.map(approvalRecord), + artifacts: await Promise.all(artifacts.rows.map((row) => loadArtifact(pool, owner, row.artifact_id))), + cases: cases.rows.map(caseRecord), + events: events.rows.map(eventRecord), + exceptions: exceptions.rows.map(exceptionRecord), + proposals: proposals.rows.map(proposalRecord), + receipts: receipts.rows.map(receiptRecord), + runs: runs.rows.map(runRecord), + }; + } + + return { + capabilities: runtimeProfiles.postgres, + completeRun, + createArtifact, + createCase, + createProposal, + decideProposal, + enterStage, + raiseException, + resolveException, + snapshot, + startRun, + }; +} diff --git a/src/adapters/postgres.d.mts b/src/adapters/postgres.d.mts new file mode 100644 index 0000000..56aa47b --- /dev/null +++ b/src/adapters/postgres.d.mts @@ -0,0 +1,22 @@ +import type { CaseflowRuntime } from "../caseflow.mjs"; + +export interface PostgreSqlQueryResult> { + rowCount: number | null; + rows: Row[]; +} + +export interface PostgreSqlClient { + query>(text: string, values?: unknown[]): Promise>; + release(): void; +} + +export interface PostgreSqlPool { + query>(text: string, values?: unknown[]): Promise>; + connect(): Promise; +} + +export function createPostgresCaseflow(options: { + pool: PostgreSqlPool; + ownerId: string; + clock?: () => string; +}): CaseflowRuntime; diff --git a/src/adapters/postgres.mjs b/src/adapters/postgres.mjs new file mode 100644 index 0000000..cfed9a9 --- /dev/null +++ b/src/adapters/postgres.mjs @@ -0,0 +1 @@ +export { createPostgresCaseflow } from "./postgres-caseflow.mjs"; diff --git a/src/caseflow.d.mts b/src/caseflow.d.mts new file mode 100644 index 0000000..1b3b28e --- /dev/null +++ b/src/caseflow.d.mts @@ -0,0 +1,209 @@ +export type NodeKitActor = { + id: string; + type: "agent" | "human" | "policy" | "system" | string; +}; + +export type NodeKitStageOwner = "agent" | "external" | "reviewer" | "system" | "user" | string; +export type NodeKitRunStatus = "active" | "blocked" | "cancelled" | "completed" | "failed_safely"; +export type NodeKitProposalStatus = "accepted" | "conflicted" | "pending" | "rejected"; + +export interface NodeKitStage { + id: string; + label: string; + owner: NodeKitStageOwner; + status: "active" | "completed" | "pending"; +} + +export interface NodeKitCase { + caseId: string; + createdAt: string; + currentRunId: string | null; + primaryJob: string; + schemaVersion: "nodekit.case/v1"; + status: "completed" | "in_progress" | "ready"; + title: string; + updatedAt: string; +} + +export interface NodeKitRun { + caseId: string; + createdAt: string; + currentStageId: string; + nextAction: string; + nextActionOwner: NodeKitStageOwner; + runId: string; + schemaVersion: "nodekit.run/v1"; + stages: NodeKitStage[]; + status: NodeKitRunStatus; + updatedAt: string; +} + +export interface NodeKitArtifactVersion { + content: T; + contentHash: string; + createdAt: string; + proposalId?: string; + version: number; +} + +export interface NodeKitArtifact { + artifactId: string; + caseId: string; + canonicalVersion: number; + createdAt: string; + kind: string; + runId: string; + schemaVersion: "nodekit.artifact/v1"; + title: string; + updatedAt: string; + versions: NodeKitArtifactVersion[]; +} + +export interface NodeKitProposal { + artifactId: string; + baseVersion: number; + createdAt: string; + patch: T; + proposalId: string; + rationale: string; + schemaVersion: "nodekit.proposal/v1"; + status: NodeKitProposalStatus; +} + +export interface NodeKitApproval { + approvalId: string; + comment: string; + decidedAt: string; + decision: "accepted" | "rejected"; + proposalId: string; + schemaVersion: "nodekit.approval/v1"; +} + +export interface NodeKitException { + code: string; + exceptionId: string; + message: string; + preservedState: T; + raisedAt: string; + resolution: string | null; + resolvedAt?: string; + runId: string; + schemaVersion: "nodekit.exception/v1"; + status: "open" | "resolved"; +} + +export interface NodeKitReceipt { + artifactIds: string[]; + caseId: string; + eventIds: string[]; + generatedAt: string; + proposalIds: string[]; + receiptHash: string; + receiptId: string; + runId: string; + schemaVersion: "nodekit.receipt/v1"; + status: "completed"; +} + +export interface NodeKitEvent { + actor: NodeKitActor; + aggregateId: string; + aggregateType: string; + eventId: string; + eventType: string; + occurredAt: string; + payload: T; + schemaVersion: "nodekit.caseflow-event/v1"; + sequence: number; +} + +export interface NodeKitCaseflowSnapshot { + approvals: NodeKitApproval[]; + artifacts: NodeKitArtifact[]; + cases: NodeKitCase[]; + events: NodeKitEvent[]; + exceptions: NodeKitException[]; + proposals: NodeKitProposal[]; + receipts: NodeKitReceipt[]; + runs: NodeKitRun[]; +} + +export interface RuntimeCapabilities { + schemaVersion: "nodekit.runtime-capabilities/v1"; + provider: string; + durableState: boolean; + transactions: boolean; + optimisticConcurrency: boolean; + subscriptions: "event-driven" | "native" | "polling" | "snapshot"; + durableJobs: "external" | "in-process" | "native" | "queue-backed"; + fileStorage: boolean; + presence: boolean; + scheduledJobs: boolean; + localDevelopment: boolean; +} + +export type MaybePromise = Promise | T; + +export interface CaseflowRuntime { + capabilities: RuntimeCapabilities; + createCase(input: { title: string; primaryJob: string; actor?: NodeKitActor }): MaybePromise; + startRun(input: { caseId: string; stages: Array>; actor?: NodeKitActor }): MaybePromise; + enterStage(input: { runId: string; stageId: string; nextAction?: string; nextActionOwner?: NodeKitStageOwner; actor?: NodeKitActor }): MaybePromise; + createArtifact(input: { caseId: string; runId: string; kind?: string; title?: string; content: T; actor?: NodeKitActor }): MaybePromise>; + createProposal(input: { artifactId: string; baseVersion: number; patch: T; rationale?: string; actor?: NodeKitActor }): MaybePromise>; + decideProposal(input: { proposalId: string; decision: "accepted" | "rejected"; comment?: string; actor?: NodeKitActor }): MaybePromise<{ + approval: NodeKitApproval; + artifact: NodeKitArtifact; + proposal: NodeKitProposal; + reused: boolean; + }>; + raiseException(input: { runId: string; code?: string; message?: string; preservedState?: T; actor?: NodeKitActor }): MaybePromise>; + resolveException(input: { exceptionId: string; resolution?: string; nextAction?: string; nextActionOwner?: NodeKitStageOwner; actor?: NodeKitActor }): MaybePromise<{ + exception: NodeKitException; + run: NodeKitRun; + }>; + completeRun(input: { runId: string; actor?: NodeKitActor }): MaybePromise<{ + receipt: NodeKitReceipt; + run: NodeKitRun; + reused: boolean; + }>; + snapshot(): MaybePromise; +} + +export interface CaseflowConformanceVerdict { + assertions: Record; + capabilities: RuntimeCapabilities; + capabilityNegotiation: { + missing: Array<{ actual: unknown; expected: unknown; name: string }>; + passed: boolean; + provider: string; + schemaVersion: "nodekit.runtime-capability-negotiation/v1"; + }; + passed: boolean; + schemaVersion: "nodekit.adapter-conformance/v1"; +} + +export const CASEFLOW_SCHEMA_VERSIONS: Readonly<{ + approval: "nodekit.approval/v1"; + artifact: "nodekit.artifact/v1"; + case: "nodekit.case/v1"; + event: "nodekit.caseflow-event/v1"; + exception: "nodekit.exception/v1"; + proposal: "nodekit.proposal/v1"; + receipt: "nodekit.receipt/v1"; + run: "nodekit.run/v1"; + stage: "nodekit.stage/v1"; +}>; + +export const TERMINAL_RUN_STATUSES: readonly NodeKitRunStatus[]; +export function contentHash(value: unknown): string; +export function createMemoryCaseflow(options?: { clock?: () => string }): CaseflowRuntime; +export function runCaseflowConformance( + createRuntime: () => MaybePromise, + options?: { requiredCapabilities?: Partial }, +): Promise; +export function negotiateRuntimeCapabilities( + offered: RuntimeCapabilities, + required?: Partial, +): CaseflowConformanceVerdict["capabilityNegotiation"]; +export const runtimeProfiles: Readonly>; diff --git a/src/caseflow.mjs b/src/caseflow.mjs new file mode 100644 index 0000000..5530458 --- /dev/null +++ b/src/caseflow.mjs @@ -0,0 +1,11 @@ +export { + CASEFLOW_SCHEMA_VERSIONS, + TERMINAL_RUN_STATUSES, + contentHash, + createMemoryCaseflow, +} from "./lib/caseflow.mjs"; +export { runCaseflowConformance } from "./lib/caseflow-conformance.mjs"; +export { + negotiateRuntimeCapabilities, + runtimeProfiles, +} from "./lib/runtime-capabilities.mjs"; diff --git a/src/cli.mjs b/src/cli.mjs index 0817040..9de0391 100755 --- a/src/cli.mjs +++ b/src/cli.mjs @@ -1,6 +1,6 @@ #!/usr/bin/env node import { spawn } from "node:child_process"; -import { mkdir, writeFile } from "node:fs/promises"; +import { mkdir, readFile, writeFile } from "node:fs/promises"; import path from "node:path"; import { fileURLToPath } from "node:url"; import { renderDashboard } from "./lib/dashboard.mjs"; @@ -9,11 +9,63 @@ import { pathExists } from "./lib/files.mjs"; import { checkRepository, commandFor } from "./lib/repo-check.mjs"; import { loadRegistry, validateRegistry } from "./lib/registry.mjs"; import { adoptProject, createProject, recordSetupEvent } from "./lib/scaffold.mjs"; +import { + compileModelIntelligence, + diagnoseModelFailures, + initializeHarness, + writeModelBaseline, +} from "./lib/model-intelligence.mjs"; +import { + benchmarkSkillCandidate, + compileRoutingPolicy, + evaluateTournament, + harnessStatus, + promoteSkillCandidate, + proposeSkillCandidates, + rejectSkillCandidate, + reviewSkillCandidate, + rollbackHarness, + verifyCanary, +} from "./lib/harness-gym.mjs"; import { importUnderstandAnythingCodeGraph, queryUnderstandAnythingCodeGraph, readUnderstandAnythingCodeGraph, } from "./lib/understand-anything.mjs"; +import { + applyGraphPatch, + benchmarkKnowledgeRetrieval, + decideGraphPatch, + diffKnowledgeGraph, + initializeKnowledgeGraph, + inspectKnowledgeGaps, + proposeGraphPatch, + queryKnowledgeGraph, + readKnowledgeGraph, + recordKnowledgeAction, + replayKnowledgeGraph, + validateGraphPatch, +} from "./lib/knowledge-evolution.mjs"; +import { proposeHarnessKnowledgePatch } from "./lib/harness-knowledge.mjs"; +import { + compileFrontendPlan, + createFrontendDirections, + createFrontendRepairPlan, + evaluateFrontendTournament, + initializeFrontendHarness, + verifyFrontendCanary, +} from "./lib/frontend-specialist.mjs"; +import { + buildEvolutionDocs, + checkEvolutionMateriality, + diffEvolutionLedger, + draftEvolutionEvent, + initializeEvolutionLedger, + proposeEvolutionKnowledgePatch, + queryEvolutionLedger, + recordEvolutionRecord, + verifyEvolutionLedger, +} from "./lib/evolution-ledger.mjs"; const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); @@ -48,7 +100,7 @@ function printHelp() { console.log(`NodeKit Usage: - nodekit create --name --brief [--preset research-loop|smb-lending-fde|agentic-rl-research] + nodekit create --name --brief [--provider openrouter] [--model openai/gpt-4o-mini] [--backend filesystem] [--nodekit-specifier ] [--sponsors ] [--package-manager npm|pnpm] @@ -64,7 +116,51 @@ Usage: nodekit ecosystem check [--workspace ] [--json] nodekit dashboard [--workspace ] [--write] [--out ] nodekit graph import [--repo-root ] [--graph-dir ] [--repo-id ] [--commit ] [--json] - nodekit graph query [--repo-root ] [--limit ] [--json] + nodekit graph init [--repo-root ] [--graph-id ] [--json] + nodekit graph ingest --input [--repo-root ] [--json] + nodekit graph inspect [--repo-root ] [--json] + nodekit graph query [--repo-root ] [--limit ] [--code] [--json] + nodekit graph gaps [--repo-root ] [--json] + nodekit graph research [--repo-root ] [--run-id ] [--json] + nodekit graph propose --patch [--repo-root ] [--json] + nodekit graph validate --patch [--repo-root ] [--json] + nodekit graph apply --patch --approved-by [--reason ] [--repo-root ] [--json] + nodekit graph diff --from [--to ] [--repo-root ] [--json] + nodekit graph replay --version [--out ] [--repo-root ] [--json] + nodekit graph benchmark --cases [--repo-root ] [--json] + nodekit graph harness-sync [--repo-root ] [--json] + nodekit frontend init [--repo-root ] [--json] + nodekit frontend plan --contract [--route ] [--repo-root ] [--json] + nodekit frontend directions --plan [--repo-root ] [--json] + nodekit frontend benchmark --manifest [--repo-root ] [--json] + nodekit frontend repair --benchmark [--repo-root ] [--json] + nodekit frontend canary --receipt [--repo-root ] [--json] + nodekit evolution init [--repo-root ] [--json] + nodekit evolution draft --id --track --category --challenge --resolution --reviewed-by + nodekit evolution record --file [--repo-root ] [--json] + nodekit evolution verify [--repo-root ] [--json] + nodekit evolution query [--track ] [--since ] [--invariant ] [--repo-root ] [--json] + nodekit evolution diff --from --to [--repo-root ] [--json] + nodekit evolution materiality --from --to [--repo-root ] [--json] + nodekit evolution build-docs [--repo-root ] [--json] + nodekit evolution sync-graph [--graph-path ] [--repo-root ] [--json] + nodekit harness init [--repo-root ] [--json] + nodekit models baseline [--repo-root ] [--json] + nodekit models profile [--repo-root ] [--json] + nodekit models inspect [--repo-root ] [--json] + nodekit models diagnose [--repo-root ] [--json] + nodekit skills propose [--repo-root ] [--json] + nodekit skills review --candidate [--repo-root ] [--json] + nodekit skills benchmark --candidate --comparison [--repo-root ] [--json] + nodekit skills promote --candidate --canary --proof-receipt --approved-by + nodekit skills reject --candidate --reason + nodekit routing compile [--repo-root ] [--json] + nodekit routing canary --receipt [--repo-root ] [--json] + nodekit harness tournament --manifest [--repo-root ] [--json] + nodekit harness baseline|inspect|diagnose|propose|benchmark|canary|review|promote + nodekit harness status [--repo-root ] [--json] + nodekit harness gate [--repo-root ] [--json] + nodekit harness rollback [--repo-root ] [--json] nodekit certify [--repo-root ] [--json]`); } @@ -256,7 +352,6 @@ async function runCreate(parsed) { name: parsed.options.name ?? path.basename(path.resolve(target)), nodekitSpecifier, packageManager: parsed.options["package-manager"], - preset: parsed.options.preset, provider: parsed.options.provider, researchMs: parsed.options["research-ms"] === undefined ? undefined : Number(parsed.options["research-ms"]), secretRef: parsed.options["secret-ref"], @@ -267,9 +362,7 @@ async function runCreate(parsed) { const compiled = await compileAgentDefinition(result.target); await recordSetupEvent(result.target, "compile_completed", { configHash: compiled.definition.configHash }, Date.now() - compileStarted); if (localProof) { - const scripts = ["smb-lending-fde", "agentic-rl-research"].includes(result.preset) - ? ["demo.mjs", "eval.mjs", "benchmark.mjs", "proof.mjs"] - : ["demo.mjs", "eval.mjs", "proof.mjs"]; + const scripts = ["demo.mjs", "eval.mjs", "proof.mjs"]; for (const script of scripts) { await new Promise((resolve, reject) => { const child = spawn(process.execPath, [path.join(result.target, "scripts", script)], { @@ -406,6 +499,28 @@ async function runGraphQuery(parsed) { const query = parsed.positional.slice(2).join(" "); if (!query) throw new Error("graph query requires search terms"); const repoRoot = path.resolve(String(parsed.options["repo-root"] ?? process.cwd())); + if (!parsed.options.code) { + try { + const graph = await readKnowledgeGraph(repoRoot, { graphPath: parsed.options["graph-path"] }); + const output = queryKnowledgeGraph(graph, query, { limit: parsed.options.limit }); + if (parsed.options.json) console.log(JSON.stringify(output, null, 2)); + else { + console.log(`KNOWLEDGE GRAPH ${output.graphId}@v${output.graphVersion}`); + for (const { entity, score } of output.results) console.log(` ${score} ${entity.label ?? entity.predicate} (${entity.kind ?? "hyperedge"}:${entity.layer})`); + } + await recordKnowledgeAction(repoRoot, { + type: "GRAPH_RETRIEVE", + runId: parsed.options["run-id"], + caseId: parsed.options["case-id"], + actorId: parsed.options["actor-id"] ?? "nodekit-cli", + input: { query, limit: parsed.options.limit ?? 12 }, + outputRefs: output.results.map((entry) => entry.entity.id), + }, { graphPath: parsed.options["graph-path"] }); + return; + } catch (error) { + if (!String(error.message).includes("knowledge graph is missing")) throw error; + } + } const snapshot = await readUnderstandAnythingCodeGraph(repoRoot, { snapshotPath: parsed.options["snapshot-path"], }); @@ -420,6 +535,453 @@ async function runGraphQuery(parsed) { for (const { node, score } of output.matched) console.log(` ${score} ${node.name} (${node.type})`); } +async function readJsonInput(repoRoot, candidate, label) { + if (!candidate) throw new Error(`${label} file is required`); + const root = path.resolve(repoRoot); + const absolute = path.resolve(root, String(candidate)); + const relative = path.relative(root, absolute); + if (relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) throw new Error(`${label} file must stay inside the repository`); + try { + return JSON.parse(await readFile(absolute, "utf8")); + } catch (error) { + throw new Error(`${label} file is invalid JSON: ${relative}: ${error.message}`); + } +} + +async function runGraphInit(parsed) { + const repoRoot = repoRootFrom(parsed); + const graph = await initializeKnowledgeGraph(repoRoot, { + graphId: parsed.options["graph-id"], + graphPath: parsed.options["graph-path"], + }); + const output = { passed: true, graphId: graph.graphId, graphVersion: graph.version, contentHash: graph.contentHash }; + if (parsed.options.json) console.log(JSON.stringify(output, null, 2)); + else console.log(`INITIALIZED ${graph.graphId}@v${graph.version} ${graph.contentHash}`); +} + +async function runGraphInspect(parsed) { + const graph = await readKnowledgeGraph(repoRootFrom(parsed), { graphPath: parsed.options["graph-path"] }); + const output = { + schemaVersion: "nodekit.knowledge-inspection/v1", + graphId: graph.graphId, + graphVersion: graph.version, + contentHash: graph.contentHash, + nodes: graph.nodes.length, + hyperedges: graph.hyperedges.length, + layers: Object.fromEntries(graph.layers.map((layer) => [layer.id, graph.nodes.filter((node) => node.layer === layer.id).length + graph.hyperedges.filter((edge) => edge.layer === layer.id).length])), + patches: Object.fromEntries(["pending", "accepted", "rejected", "conflicted", "applied"].map((status) => [status, graph.proposals.filter((patch) => patch.status === status).length])), + actionReceipts: graph.actionReceipts.length, + evolutionReceipts: graph.evolutionReceipts.length, + authority: graph.authority, + }; + if (parsed.options.json) console.log(JSON.stringify(output, null, 2)); + else console.log(`KNOWLEDGE ${output.graphId}@v${output.graphVersion} ${output.nodes} nodes ${output.hyperedges} hyperedges ${output.patches.pending} pending patches`); +} + +function proposalActor(parsed) { + return { + agentId: String(parsed.options["agent-id"] ?? "nodekit-cli"), + modelRoute: String(parsed.options["model-route"] ?? "deterministic"), + resolvedModel: String(parsed.options["resolved-model"] ?? "none"), + harnessVersion: String(parsed.options["harness-version"] ?? "h0"), + }; +} + +async function runGraphIngest(parsed) { + const repoRoot = repoRootFrom(parsed); + const input = await readJsonInput(repoRoot, parsed.options.input, "graph ingest input"); + const patch = await proposeGraphPatch(repoRoot, { + operations: [ + ...(input.nodes ?? []).map((node) => ({ type: "INSERT", node })), + ...(input.hyperedges ?? []).map((hyperedge) => ({ type: "INSERT", hyperedge })), + ], + evidenceRefs: input.evidenceRefs ?? [], + contradictionRefs: input.contradictionRefs ?? [], + proposedBy: input.proposedBy ?? proposalActor(parsed), + confidence: input.confidence ?? 1, + }, { graphPath: parsed.options["graph-path"] }); + const output = { passed: true, proposalOnly: true, patch }; + if (parsed.options.json) console.log(JSON.stringify(output, null, 2)); + else console.log(`PROPOSED INGEST ${patch.patchId} (${patch.operations.length} operations); canonical graph unchanged`); +} + +async function runGraphPropose(parsed) { + const repoRoot = repoRootFrom(parsed); + const input = await readJsonInput(repoRoot, parsed.options.patch, "graph patch"); + const patch = await proposeGraphPatch(repoRoot, { + ...input, + proposedBy: input.proposedBy ?? proposalActor(parsed), + }, { graphPath: parsed.options["graph-path"] }); + if (parsed.options.json) console.log(JSON.stringify({ passed: true, patch }, null, 2)); + else console.log(`PROPOSED ${patch.patchId}@v${patch.baseVersion}; validate then apply with explicit approval`); +} + +async function runGraphValidate(parsed) { + const patchId = parsed.options.patch; + if (!patchId) throw new Error("graph validate requires --patch "); + const patch = await validateGraphPatch(repoRootFrom(parsed), String(patchId), { graphPath: parsed.options["graph-path"] }); + const passed = patch.validation.errors.length === 0 && Object.entries(patch.validation).filter(([key]) => key !== "errors").every(([, value]) => value); + if (parsed.options.json) console.log(JSON.stringify({ passed, patch }, null, 2)); + else { + console.log(`GRAPH PATCH ${passed ? "VALID" : "BLOCKED"} ${patch.patchId}`); + for (const error of patch.validation.errors) console.log(` ${error}`); + } + if (!passed) process.exitCode = 1; +} + +async function runGraphApply(parsed) { + const patchId = parsed.options.patch; + const principalId = parsed.options["approved-by"]; + if (!patchId || !principalId) throw new Error("graph apply requires --patch and --approved-by "); + const repoRoot = repoRootFrom(parsed); + let graph = await readKnowledgeGraph(repoRoot, { graphPath: parsed.options["graph-path"] }); + let patch = graph.proposals.find((entry) => entry.patchId === patchId); + if (!patch) throw new Error(`graph patch not found: ${patchId}`); + if (patch.status === "pending" || patch.status === "conflicted") { + patch = await validateGraphPatch(repoRoot, String(patchId), { graphPath: parsed.options["graph-path"] }); + if (patch.status !== "pending") throw new Error(`graph patch is ${patch.status}; create a rebased proposal`); + patch = await decideGraphPatch(repoRoot, String(patchId), { + decision: "accept", + principalId: String(principalId), + reason: parsed.options.reason, + graphPath: parsed.options["graph-path"], + }); + } + const output = await applyGraphPatch(repoRoot, String(patchId), { graphPath: parsed.options["graph-path"] }); + if (parsed.options.json) console.log(JSON.stringify({ passed: true, ...output }, null, 2)); + else console.log(`APPLIED ${patchId} v${output.receipt.fromVersion}->v${output.receipt.toVersion} ${output.receipt.receiptId}`); +} + +async function runGraphGaps(parsed) { + const graph = await readKnowledgeGraph(repoRootFrom(parsed), { graphPath: parsed.options["graph-path"] }); + const output = inspectKnowledgeGaps(graph); + if (parsed.options.json) console.log(JSON.stringify(output, null, 2)); + else console.log(`GAPS unresolved=${output.unresolved.length} unsupported=${output.unsupported.length} stale=${output.staleEvidence.length} pending=${output.pendingPatches.length}`); +} + +async function runGraphResearch(parsed) { + const query = parsed.positional.slice(2).join(" "); + if (!query) throw new Error("graph research requires a typed knowledge-gap query"); + const receipt = await recordKnowledgeAction(repoRootFrom(parsed), { + type: "EXTERNAL_RESEARCH", + runId: parsed.options["run-id"], + caseId: parsed.options["case-id"], + actorId: parsed.options["actor-id"] ?? "nodekit-cli", + input: { query, gapIds: String(parsed.options["gap-ids"] ?? "").split(",").filter(Boolean) }, + budget: { maximumSearches: Number(parsed.options["max-searches"] ?? 1) }, + status: "planned", + }, { graphPath: parsed.options["graph-path"] }); + if (parsed.options.json) console.log(JSON.stringify({ passed: true, receipt }, null, 2)); + else console.log(`PLANNED EXTERNAL_RESEARCH ${receipt.receiptId}; results require evidence anchors and a graph patch`); +} + +async function runGraphDiff(parsed) { + if (parsed.options.from === undefined) throw new Error("graph diff requires --from "); + const graph = await readKnowledgeGraph(repoRootFrom(parsed), { graphPath: parsed.options["graph-path"] }); + const output = diffKnowledgeGraph(graph, parsed.options.from, parsed.options.to ?? graph.version); + if (parsed.options.json) console.log(JSON.stringify(output, null, 2)); + else console.log(`GRAPH DIFF v${output.fromVersion}->v${output.toVersion}: ${output.patchIds.length} patches ${output.operations.length} operations`); +} + +async function runGraphReplay(parsed) { + if (parsed.options.version === undefined) throw new Error("graph replay requires --version "); + const repoRoot = repoRootFrom(parsed); + const graph = await readKnowledgeGraph(repoRoot, { graphPath: parsed.options["graph-path"] }); + const output = replayKnowledgeGraph(graph, parsed.options.version); + if (parsed.options.out) { + const root = path.resolve(repoRoot); + const destination = path.resolve(root, String(parsed.options.out)); + const relation = path.relative(root, destination); + if (relation === ".." || relation.startsWith(`..${path.sep}`) || path.isAbsolute(relation)) throw new Error("graph replay output must stay inside the repository"); + await mkdir(path.dirname(destination), { recursive: true }); + await writeFile(destination, `${JSON.stringify(output, null, 2)}\n`, "utf8"); + } + if (parsed.options.json) console.log(JSON.stringify(output, null, 2)); + else console.log(`REPLAYED ${output.graphId}@v${output.version} ${output.nodes.length} nodes ${output.hyperedges.length} hyperedges`); +} + +async function runGraphBenchmark(parsed) { + const repoRoot = repoRootFrom(parsed); + const cases = await readJsonInput(repoRoot, parsed.options.cases, "graph benchmark cases"); + const graph = await readKnowledgeGraph(repoRoot, { graphPath: parsed.options["graph-path"] }); + const output = benchmarkKnowledgeRetrieval(graph, Array.isArray(cases) ? cases : cases.cases, { limit: parsed.options.limit }); + if (parsed.options.json) console.log(JSON.stringify(output, null, 2)); + else console.log(`GRAPH BENCHMARK flat=${output.results.flat.averageRecall.toFixed(3)} static=${output.results.staticGraph.averageRecall.toFixed(3)} evolving=${output.results.evolvingGraph.averageRecall.toFixed(3)}`); +} + +async function runGraphHarnessSync(parsed) { + const output = await proposeHarnessKnowledgePatch(repoRootFrom(parsed), { + graphPath: parsed.options["graph-path"], + agentId: parsed.options["agent-id"], + }); + if (parsed.options.json) console.log(JSON.stringify({ passed: true, proposalOnly: true, ...output }, null, 2)); + else if (output.unchanged) console.log(`HARNESS KNOWLEDGE UNCHANGED (${output.observationCount} observations)`); + else console.log(`PROPOSED HARNESS KNOWLEDGE ${output.patch.patchId} (${output.patch.operations.length} operations); canonical graph unchanged`); +} + +async function runFrontendInit(parsed) { + const output = await initializeFrontendHarness(repoRootFrom(parsed)); + printStructured({ ...output, passed: true }, parsed, (value) => `INITIALIZED Frontend Gym at ${value.frontendRoot}; preferred route remains unprofiled`); +} + +async function runFrontendPlan(parsed) { + const output = await compileFrontendPlan(repoRootFrom(parsed), requireOption(parsed, "contract"), parsed.options.route); + printStructured(output, parsed, (value) => `PLANNED ${value.plan.planId}; 3 directions required; route ${value.plan.routeStatus}; deployment unauthorized`); +} + +async function runFrontendDirections(parsed) { + const output = await createFrontendDirections(repoRootFrom(parsed), requireOption(parsed, "plan")); + printStructured(output, parsed, (value) => `CREATED ${value.directionSet.directionSetId}: collaborative workspace, artifact studio, and domain-native hypotheses`); +} + +async function runFrontendBenchmark(parsed) { + const output = await evaluateFrontendTournament(repoRootFrom(parsed), requireOption(parsed, "manifest")); + printStructured(output, parsed, (value) => `FRONTEND TOURNAMENT ${value.decisive ? "DECISIVE" : "BLOCKED"}: provisional ${value.decision.selectedCandidateId}; promotion unauthorized`); + if (!output.decisive) process.exitCode = 1; +} + +async function runFrontendRepair(parsed) { + const output = await createFrontendRepairPlan(repoRootFrom(parsed), requireOption(parsed, "benchmark")); + printStructured(output, parsed, (value) => `REPAIR ${value.repair.repairId}: maximum ${value.repair.maximumRounds} bounded rounds; prior implementation preserved`); +} + +async function runFrontendCanary(parsed) { + const output = await verifyFrontendCanary(repoRootFrom(parsed), requireOption(parsed, "receipt")); + printStructured(output, parsed, (value) => `FRONTEND CANARY ${value.passed ? "PASS" : "BLOCKED"}`); + if (!output.passed) process.exitCode = 1; +} + +function optionList(parsed, name) { + const value = parsed.options[name]; + if (value === undefined || value === true || String(value).trim() === "") return []; + return String(value).split(",").map((entry) => entry.trim()).filter(Boolean); +} + +async function runEvolutionInit(parsed) { + const output = await initializeEvolutionLedger(repoRootFrom(parsed)); + printStructured({ ...output, passed: true }, parsed, (value) => `INITIALIZED Evolution Ledger at ${value.evolutionRoot}`); +} + +async function runEvolutionDraft(parsed) { + const output = await draftEvolutionEvent(repoRootFrom(parsed), { + id: requireOption(parsed, "id"), + repository: parsed.options.repository, + projectId: parsed.options["project-id"], + commitSha: parsed.options.commit, + pullRequest: parsed.options.pr, + track: requireOption(parsed, "track"), + category: requireOption(parsed, "category"), + challenge: requireOption(parsed, "challenge"), + observedFailure: parsed.options.failure, + resolution: requireOption(parsed, "resolution"), + reviewedBy: requireOption(parsed, "reviewed-by"), + assumptionIds: optionList(parsed, "assumptions"), + invariantIds: optionList(parsed, "invariants"), + evidenceIds: optionList(parsed, "evidence"), + knownLimitations: optionList(parsed, "limitations"), + }); + printStructured(output, parsed, (value) => `DRAFTED ${value.event.id}; record remains separate from canonical history`); +} + +async function runEvolutionRecord(parsed) { + const output = await recordEvolutionRecord(repoRootFrom(parsed), requireOption(parsed, "file")); + printStructured(output, parsed, (value) => `${value.duplicate ? "UNCHANGED" : "RECORDED"} ${value.record.id}`); +} + +async function runEvolutionVerify(parsed) { + const output = await verifyEvolutionLedger(repoRootFrom(parsed)); + printStructured(output, parsed, (value) => `EVOLUTION ${value.passed ? "PASS" : "BLOCKED"}: ${value.counts.events} events, ${value.counts.invariants} invariants, ${value.counts.adoptions} adoptions`); + if (!output.passed) process.exitCode = 1; +} + +async function runEvolutionQuery(parsed) { + const output = await queryEvolutionLedger(repoRootFrom(parsed), { track: parsed.options.track, since: parsed.options.since, invariantId: parsed.options.invariant }); + printStructured(output, parsed, (value) => `EVOLUTION QUERY: ${value.events.length} events, ${value.invariants.length} invariants, ${value.evidence.length} evidence records`); +} + +async function runEvolutionDiff(parsed) { + const output = await diffEvolutionLedger(repoRootFrom(parsed), requireOption(parsed, "from"), requireOption(parsed, "to")); + printStructured(output, parsed, (value) => `EVOLUTION DIFF ${value.from}..${value.to}: ${value.events.length} material events`); +} + +async function runEvolutionMateriality(parsed) { + const output = await checkEvolutionMateriality(repoRootFrom(parsed), requireOption(parsed, "from"), requireOption(parsed, "to")); + printStructured(output, parsed, (value) => `EVOLUTION MATERIALITY ${value.passed ? "PASS" : "BLOCKED"}: ${value.materialFiles.length} material files, ${value.events.length} recorded events`); + if (!output.passed) process.exitCode = 1; +} + +async function runEvolutionBuildDocs(parsed) { + const output = await buildEvolutionDocs(repoRootFrom(parsed)); + printStructured(output, parsed, (value) => `BUILT ${value.output} with ${value.adoptionMap.length} adoption records`); +} + +async function runEvolutionSyncGraph(parsed) { + const output = await proposeEvolutionKnowledgePatch(repoRootFrom(parsed), { graphPath: parsed.options["graph-path"] }); + printStructured({ ...output, proposalOnly: true }, parsed, (value) => `PROPOSED EVOLUTION KNOWLEDGE ${value.patch.patchId}; approval is still required`); +} + +function repoRootFrom(parsed) { + return path.resolve(String(parsed.options["repo-root"] ?? process.cwd())); +} + +async function runHarnessInit(parsed) { + const root = repoRootFrom(parsed); + const output = await initializeHarness(root); + if (parsed.options.json) console.log(JSON.stringify({ ...output, passed: true }, null, 2)); + else { + console.log(`INITIALIZED Harness Gym for ${output.applicationId}`); + console.log(` ${output.created.length} files created; existing files preserved`); + console.log(" Frontend Gym initialized with an unprofiled evidence-ranked route and three-direction contract"); + console.log(" automatic promotion disabled; no model capability claims were created"); + } +} + +async function runModelsBaseline(parsed) { + const { receipt, output } = await writeModelBaseline(repoRootFrom(parsed)); + if (parsed.options.json) console.log(JSON.stringify({ ...receipt, output, passed: true }, null, 2)); + else { + console.log(`BASELINED ${receipt.applicationId}: ${receipt.observationCount} observations, ${receipt.capabilityCardCount} cards`); + console.log(` status ${receipt.status}; provider calls 0; routing not certified`); + console.log(` receipt ${output}`); + } +} + +async function runModelsProfile(parsed) { + const compiled = await compileModelIntelligence(repoRootFrom(parsed)); + const output = { ...compiled.registry, passed: true }; + if (parsed.options.json) console.log(JSON.stringify(output, null, 2)); + else console.log(`PROFILED ${output.applicationId}: ${output.observations} observations, ${output.cards.length} evidence-backed cards (${output.status})`); +} + +async function runModelsInspect(parsed) { + const compiled = await compileModelIntelligence(repoRootFrom(parsed), { write: false }); + const output = { + applicationId: compiled.harness.applicationId, + harnessVersion: compiled.harness.version, + harnessHash: compiled.resolved.harnessHash, + benchmarkHash: compiled.resolved.benchmarkHash, + status: compiled.registry.status, + observationCount: compiled.observations.length, + cards: compiled.cards.map((card) => ({ + confidence: card.confidence, + model: card.model, + scope: card.scope, + status: card.status, + })), + routingCertified: false, + automaticPromotion: false, + }; + if (parsed.options.json) console.log(JSON.stringify(output, null, 2)); + else { + console.log(`${output.applicationId} Harness ${output.harnessVersion}: ${output.status}`); + console.log(` observations ${output.observationCount}; cards ${output.cards.length}`); + console.log(" routing uncertified; automatic promotion disabled"); + for (const card of output.cards) console.log(` ${card.scope.level} ${card.model.resolvedProvider}/${card.model.resolvedModel}: ${card.status}, ${card.confidence.level} confidence`); + } +} + +async function runModelsDiagnose(parsed) { + const compiled = await compileModelIntelligence(repoRootFrom(parsed), { write: false }); + const clusters = diagnoseModelFailures(compiled.observations); + const output = { + schemaVersion: "nodekit.model-diagnosis/v1", + applicationId: compiled.harness.applicationId, + observationCount: compiled.observations.length, + clusters, + skillCandidates: clusters.filter((cluster) => cluster.skillCandidateEligible).length, + passed: true, + }; + if (parsed.options.json) console.log(JSON.stringify(output, null, 2)); + else { + console.log(`DIAGNOSED ${output.applicationId}: ${clusters.length} failure clusters, ${output.skillCandidates} eligible for skill-candidate review`); + for (const cluster of clusters) console.log(` ${cluster.count}x ${cluster.failureClass} (${cluster.probableCause}) ${cluster.model}${cluster.skillCandidateEligible ? " [candidate threshold met]" : ""}`); + } +} + +function requireOption(parsed, name) { + const value = parsed.options[name]; + if (value === undefined || value === true || String(value).trim() === "") throw new Error(`--${name} is required`); + return String(value); +} + +function printStructured(output, parsed, textSummary) { + if (parsed.options.json) console.log(JSON.stringify(output, null, 2)); + else console.log(textSummary(output)); +} + +async function runSkillsPropose(parsed) { + const output = await proposeSkillCandidates(repoRootFrom(parsed)); + printStructured(output, parsed, (value) => `PROPOSED ${value.candidates.length} evidence-backed skill candidates; none promoted`); +} + +async function runSkillsReview(parsed) { + const output = await reviewSkillCandidate(repoRootFrom(parsed), requireOption(parsed, "candidate")); + printStructured(output, parsed, (value) => `REVIEWED ${value.candidate.candidateId}: ${value.candidate.status} (${value.skill.id}@${value.skill.version})`); +} + +async function runSkillsBenchmark(parsed) { + const output = await benchmarkSkillCandidate( + repoRootFrom(parsed), + requireOption(parsed, "candidate"), + requireOption(parsed, "comparison"), + ); + printStructured(output, parsed, (value) => `BENCHMARK ${value.passed ? "PASS" : "FAIL"} ${value.candidateId}; meaningful improvement ${value.meaningfulImprovement}`); + if (!output.passed) process.exitCode = 1; +} + +async function runSkillsPromote(parsed) { + const output = await promoteSkillCandidate(repoRootFrom(parsed), requireOption(parsed, "candidate"), { + approvedBy: requireOption(parsed, "approved-by"), + canaryPath: requireOption(parsed, "canary"), + proofPath: requireOption(parsed, "proof-receipt"), + }); + printStructured(output, parsed, (value) => `PROMOTED ${value.promotion.candidateId} to ${value.nextVersion}; rollback ${value.promotion.rollbackVersion}`); +} + +async function runSkillsReject(parsed) { + const output = await rejectSkillCandidate(repoRootFrom(parsed), requireOption(parsed, "candidate"), requireOption(parsed, "reason")); + printStructured(output, parsed, (value) => `REJECTED ${value.candidateId}: ${value.reason}`); +} + +async function runRoutingCompile(parsed) { + const output = await compileRoutingPolicy(repoRootFrom(parsed)); + printStructured(output, parsed, (value) => `COMPILED provisional routing policy with ${value.routes.length} task-family routes; promotion not authorized`); +} + +async function runRoutingCanary(parsed) { + const output = await verifyCanary(repoRootFrom(parsed), requireOption(parsed, "receipt")); + printStructured(output, parsed, (value) => `CANARY PASS ${value.canaryId} for ${value.candidateId}`); +} + +async function runHarnessTournament(parsed) { + const output = await evaluateTournament(repoRootFrom(parsed), requireOption(parsed, "manifest")); + printStructured(output, parsed, (value) => `TOURNAMENT ${value.tournamentId}: ${value.decisive ? `provisional winner ${value.winner}` : "no decisive winner"}; promotion not authorized`); +} + +async function runHarnessStatus(parsed) { + const output = await harnessStatus(repoRootFrom(parsed)); + printStructured(output, parsed, (value) => `HARNESS ${value.version}: ${value.observations} observations, ${value.capabilityCards} cards, ${value.skillCandidates.length} candidates; routing uncertified`); +} + +async function runHarnessRollback(parsed) { + const output = await rollbackHarness(repoRootFrom(parsed)); + printStructured(output, parsed, (value) => `ROLLED BACK ${value.from} -> ${value.to}; version history preserved`); +} + +async function runHarnessGate(parsed) { + const output = await harnessStatus(repoRootFrom(parsed)); + const checks = { + activeVersionPromoted: /^h[1-9]\d*$/.test(output.version), + automaticPromotionDisabled: output.automaticPromotion === false, + benchmarkBound: typeof output.benchmarkHash === "string" && output.benchmarkHash.length === 64, + noOpenCandidates: output.skillCandidates.every((entry) => !["proposed", "reviewed", "benchmark-passed"].includes(entry.status)), + routingCertified: output.routingCertified === true, + }; + const result = { ...output, checks, passed: Object.values(checks).every(Boolean), schemaVersion: "nodekit.harness-gate/v1" }; + printStructured(result, parsed, (value) => `HARNESS GATE ${value.passed ? "PASS" : "BLOCKED"} ${value.version}`); + if (!result.passed) process.exitCode = 1; +} + async function main() { const parsed = parseArgs(process.argv.slice(2)); const [first, second] = parsed.positional; @@ -477,10 +1039,214 @@ async function main() { await runGraphImport(parsed); return; } + if (first === "graph" && second === "init") { + await runGraphInit(parsed); + return; + } + if (first === "graph" && second === "ingest") { + await runGraphIngest(parsed); + return; + } + if (first === "graph" && second === "inspect") { + await runGraphInspect(parsed); + return; + } if (first === "graph" && second === "query") { await runGraphQuery(parsed); return; } + if (first === "graph" && second === "gaps") { + await runGraphGaps(parsed); + return; + } + if (first === "graph" && second === "research") { + await runGraphResearch(parsed); + return; + } + if (first === "graph" && second === "propose") { + await runGraphPropose(parsed); + return; + } + if (first === "graph" && second === "validate") { + await runGraphValidate(parsed); + return; + } + if (first === "graph" && second === "apply") { + await runGraphApply(parsed); + return; + } + if (first === "graph" && second === "diff") { + await runGraphDiff(parsed); + return; + } + if (first === "graph" && second === "replay") { + await runGraphReplay(parsed); + return; + } + if (first === "graph" && second === "benchmark") { + await runGraphBenchmark(parsed); + return; + } + if (first === "graph" && second === "harness-sync") { + await runGraphHarnessSync(parsed); + return; + } + if (first === "frontend" && second === "init") { + await runFrontendInit(parsed); + return; + } + if (first === "frontend" && second === "plan") { + await runFrontendPlan(parsed); + return; + } + if (first === "frontend" && second === "directions") { + await runFrontendDirections(parsed); + return; + } + if (first === "frontend" && second === "benchmark") { + await runFrontendBenchmark(parsed); + return; + } + if (first === "frontend" && second === "repair") { + await runFrontendRepair(parsed); + return; + } + if (first === "frontend" && second === "canary") { + await runFrontendCanary(parsed); + return; + } + if (first === "evolution" && second === "init") { + await runEvolutionInit(parsed); + return; + } + if (first === "evolution" && second === "draft") { + await runEvolutionDraft(parsed); + return; + } + if (first === "evolution" && second === "record") { + await runEvolutionRecord(parsed); + return; + } + if (first === "evolution" && second === "verify") { + await runEvolutionVerify(parsed); + return; + } + if (first === "evolution" && second === "query") { + await runEvolutionQuery(parsed); + return; + } + if (first === "evolution" && second === "diff") { + await runEvolutionDiff(parsed); + return; + } + if (first === "evolution" && second === "materiality") { + await runEvolutionMateriality(parsed); + return; + } + if (first === "evolution" && second === "build-docs") { + await runEvolutionBuildDocs(parsed); + return; + } + if (first === "evolution" && second === "sync-graph") { + await runEvolutionSyncGraph(parsed); + return; + } + if (first === "harness" && second === "init") { + await runHarnessInit(parsed); + return; + } + if (first === "models" && second === "baseline") { + await runModelsBaseline(parsed); + return; + } + if (first === "models" && second === "profile") { + await runModelsProfile(parsed); + return; + } + if (first === "models" && second === "inspect") { + await runModelsInspect(parsed); + return; + } + if (first === "models" && second === "diagnose") { + await runModelsDiagnose(parsed); + return; + } + if (first === "harness" && second === "baseline") { + await runModelsBaseline(parsed); + return; + } + if (first === "harness" && second === "inspect") { + await runModelsInspect(parsed); + return; + } + if (first === "harness" && second === "diagnose") { + await runModelsDiagnose(parsed); + return; + } + if (first === "harness" && second === "propose") { + await runSkillsPropose(parsed); + return; + } + if (first === "harness" && second === "benchmark") { + await runSkillsBenchmark(parsed); + return; + } + if (first === "harness" && second === "canary") { + await runRoutingCanary(parsed); + return; + } + if (first === "harness" && second === "review") { + await runSkillsReview(parsed); + return; + } + if (first === "harness" && second === "promote") { + await runSkillsPromote(parsed); + return; + } + if (first === "skills" && second === "propose") { + await runSkillsPropose(parsed); + return; + } + if (first === "skills" && second === "review") { + await runSkillsReview(parsed); + return; + } + if (first === "skills" && second === "benchmark") { + await runSkillsBenchmark(parsed); + return; + } + if (first === "skills" && second === "promote") { + await runSkillsPromote(parsed); + return; + } + if (first === "skills" && second === "reject") { + await runSkillsReject(parsed); + return; + } + if (first === "routing" && second === "compile") { + await runRoutingCompile(parsed); + return; + } + if (first === "routing" && second === "canary") { + await runRoutingCanary(parsed); + return; + } + if (first === "harness" && second === "tournament") { + await runHarnessTournament(parsed); + return; + } + if (first === "harness" && second === "status") { + await runHarnessStatus(parsed); + return; + } + if (first === "harness" && second === "gate") { + await runHarnessGate(parsed); + return; + } + if (first === "harness" && second === "rollback") { + await runHarnessRollback(parsed); + return; + } throw new Error(`unknown command: ${parsed.positional.join(" ")}`); } diff --git a/src/factory-acceptance.mjs b/src/factory-acceptance.mjs index 85f5ef9..7176d79 100644 --- a/src/factory-acceptance.mjs +++ b/src/factory-acceptance.mjs @@ -1,153 +1,250 @@ -import { createHash } from "node:crypto"; +import { createHash, randomUUID } from "node:crypto"; import { spawnSync } from "node:child_process"; -import { lstat, mkdir, mkdtemp, readFile, readdir, rm, writeFile } from "node:fs/promises"; +import { cp, mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import { fileURLToPath } from "node:url"; import { compileAgentDefinition } from "./lib/agent-definition.mjs"; import { createProject } from "./lib/scaffold.mjs"; +import { computeNodeKitSourceHash } from "./lib/source-hash.mjs"; +import { requiredSubmissionGates } from "./lib/submission-gate.mjs"; const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); -const presets = ["agentic-rl-research", "smb-lending-fde"]; +const packageManager = process.env.NODEKIT_PACKAGE_MANAGER ?? "npm"; +const runId = process.env.NODEKIT_EASE_RUN_ID ?? `ease_${randomUUID().replaceAll("-", "").slice(0, 20)}`; +const cacheClass = process.env.NODEKIT_CACHE_CLASS ?? "warm-or-unknown"; -function digest(value) { - return createHash("sha256").update(JSON.stringify(value)).digest("hex"); +if (!["cold", "warm", "warm-or-unknown"].includes(cacheClass)) { + throw new Error(`unsupported NODEKIT_CACHE_CLASS ${cacheClass}; expected cold, warm, or warm-or-unknown`); } -function canonicalBytes(content) { - if (content.includes(0)) return content; - try { - const text = new TextDecoder("utf-8", { fatal: true }).decode(content); - return Buffer.from(text.replace(/\r\n?/g, "\n"), "utf8"); - } catch { - return content; - } -} - -async function nodeKitSourceHash() { - const packageJson = await readJson(path.join(repoRoot, "package.json")); - const entries = ["package.json", ...(packageJson.files ?? [])]; - const files = []; - const visit = async (absolute) => { - const metadata = await lstat(absolute); - if (metadata.isSymbolicLink()) throw new Error(`NodeKit distribution cannot contain symlinks: ${absolute}`); - if (metadata.isDirectory()) { - const children = await readdir(absolute); - children.sort(); - for (const child of children) await visit(path.join(absolute, child)); - return; - } - const content = canonicalBytes(await readFile(absolute)); - files.push({ - digest: createHash("sha256").update(content).digest("hex"), - path: path.relative(repoRoot, absolute).replaceAll("\\", "/"), - }); - }; - for (const relative of entries) await visit(path.join(repoRoot, relative)); - return digest(files.sort((left, right) => left.path.localeCompare(right.path))); +function digest(value) { + return createHash("sha256").update(JSON.stringify(value)).digest("hex"); } -function runNpm(cwd, script) { - const result = spawnSync("npm", ["run", script], { +function runCommand(cwd, command, args, { env = {}, timeout = 180_000 } = {}) { + const startedAt = new Date().toISOString(); + const started = performance.now(); + const result = spawnSync(command, args, { cwd, encoding: "utf8", - env: { ...process.env, CI: "1" }, - shell: process.platform === "win32", - timeout: 120_000, + env: { ...process.env, CI: "1", ...env }, + shell: process.platform === "win32" && new Set(["npm", "npx", "pnpm"]).has(command), + timeout, }); - if (result.error) throw result.error; - if (result.status !== 0) { - throw new Error(`npm run ${script} failed in ${cwd}\n${result.stdout ?? ""}\n${result.stderr ?? ""}`); - } + const phase = { + args, + command, + durationMs: Math.round(performance.now() - started), + exitCode: result.status, + failed: Boolean(result.error) || result.status !== 0, + startedAt, + stderrSha256: digest(result.stderr ?? ""), + stdoutSha256: digest(result.stdout ?? ""), + }; + if (result.error) throw Object.assign(result.error, { phase }); + if (result.status !== 0) throw Object.assign(new Error(`${command} ${args.join(" ")} failed in ${cwd}\n${result.stdout ?? ""}\n${result.stderr ?? ""}`), { phase }); + return { phase, stdout: result.stdout ?? "" }; +} + +function runPackageScript(cwd, script, options) { + return runCommand(cwd, packageManager, ["run", script], options); } async function readJson(file) { return JSON.parse(await readFile(file, "utf8")); } -async function acceptPreset(root, preset) { - const target = path.join(root, preset); - const created = await createProject({ - brief: `Factory acceptance for ${preset}`, +async function currentGitCommit(cwd) { + return runCommand(cwd, "git", ["rev-parse", "HEAD"]).stdout.trim(); +} + +async function acceptBase(root, sourceIdentity) { + const phases = []; + const target = path.join(root, "domain-blank-base"); + const isolatedCache = path.join(root, `${packageManager}-cache`); + const scaffoldStarted = performance.now(); + await createProject({ + brief: "Carry one bounded user intention to a reviewed and verified artifact.", git: true, - install: true, - name: `factory-${preset}`, - packageManager: "npm", - preset, + install: false, + name: "factory-domain-blank-base", + packageManager, target, }); + phases.push({ durationMs: Math.round(performance.now() - scaffoldStarted), name: "scaffold_generation", startedAt: new Date().toISOString() }); + + const installArgs = packageManager === "pnpm" + ? ["install", "--prefer-offline", "--store-dir", isolatedCache] + : ["install", "--prefer-offline", "--no-audit", "--no-fund", "--cache", isolatedCache]; + if (cacheClass === "warm") { + phases.push({ ...runCommand(target, packageManager, installArgs, { timeout: 300_000 }).phase, measured: false, name: "cache_priming" }); + await rm(path.join(target, "node_modules"), { force: true, recursive: true }); + } + phases.push({ ...runCommand(target, packageManager, installArgs, { timeout: 300_000 }).phase, name: "dependency_installation" }); + phases.push({ ...runCommand(target, "git", ["add", "--all"]).phase, name: "candidate_stage_after_install" }); + const status = runCommand(target, "git", ["status", "--porcelain"]).stdout.trim(); + if (status) { + phases.push({ + ...runCommand(target, "git", ["-c", "user.name=NodeKit", "-c", "user.email=nodekit@local", "commit", "-m", "chore: lock generated dependencies"]).phase, + name: "candidate_lock_commit", + }); + } + const candidateCommit = await currentGitCommit(target); + + phases.push({ ...runPackageScript(target, "compile").phase, name: "compile" }); const compiled = await compileAgentDefinition(target); - for (const script of ["compile", "check", "demo", "eval", "benchmark", "proof"]) { - runNpm(target, script); + for (const [script, name] of [["check", "deterministic_checks"], ["demo", "neutral_journey"], ["eval", "evaluation"], ["proof:browser-contract", "browser_contract"]]) { + phases.push({ ...runPackageScript(target, script).phase, name }); } - const [packageJson, packageLock, proof, currentIdentity, resolvedDefinition] = await Promise.all([ + const browserInstall = packageManager === "pnpm" + ? runCommand(target, "pnpm", ["exec", "playwright", "install", "chromium"], { timeout: 300_000 }) + : runCommand(target, "npx", ["playwright", "install", "chromium"], { timeout: 300_000 }); + phases.push({ ...browserInstall.phase, name: "browser_runtime_installation" }); + phases.push({ + ...runPackageScript(target, "proof:browser", { + env: { + NODEKIT_EASE_RUN_ID: runId, + NODEKIT_SOURCE_COMMIT: sourceIdentity.commit, + NODEKIT_SOURCE_HASH: sourceIdentity.hash, + }, + timeout: 300_000, + }).phase, + name: "rendered_browser_journey", + }); + phases.push({ ...runPackageScript(target, "proof").phase, name: "receipt_completion" }); + + const [packageJson, lock, proof, browserContract, browserJourney, currentIdentity, resolvedDefinition, figuredOut, experience] = await Promise.all([ readJson(path.join(target, "package.json")), - readJson(path.join(target, "package-lock.json")), + readFile(path.join(target, packageManager === "pnpm" ? "pnpm-lock.yaml" : "package-lock.json"), "utf8").then(() => true).catch(() => false), readJson(path.join(target, "proof", "release-proof.json")), + readJson(path.join(target, "proof", "browser-contract.json")), + readJson(path.join(target, "proof", "browser-certification.json")), readJson(path.join(target, ".nodeagent", "application-identity.json")), readJson(path.join(target, ".nodeagent", "resolved-definition.json")), + readFile(path.join(target, "docs", "FIGURED_OUT.md"), "utf8"), + readFile(path.join(target, "product", "EXPERIENCE.yaml"), "utf8"), ]); - const dependency = packageJson.devDependencies?.["@homenshum/nodekit"]; - const installedNodeKitLink = packageLock.packages?.["node_modules/@homenshum/nodekit"]; - const installedNodeKit = packageLock.packages?.["vendor/nodekit"]; + const dependency = packageJson.dependencies?.["@homenshum/nodekit"] ?? packageJson.devDependencies?.["@homenshum/nodekit"]; const checks = { - candidateCommitted: /^[a-f0-9]{40}$/.test(created.candidateCommit ?? ""), + browserContractPassed: browserContract.passed === true, + browserJourneyPassed: browserJourney.passed === true, + candidateCommitted: /^[a-f0-9]{40}$/.test(candidateCommit), compileReproducible: currentIdentity.applicationHash === compiled.definition.applicationHash, - exactRuntimeInstalled: installedNodeKit?.version === "0.2.0", - proofIdentityBound: proof.applicationHash === currentIdentity.applicationHash - && proof.configHash === currentIdentity.configHash, + domainBlank: !/lending|research-loop|founderquest/i.test(`${figuredOut}\n${experience}`), + easeCertificationHonest: browserJourney.certified === true + && browserJourney.missingStates.length === 0 + && browserJourney.coveredStates.length === browserJourney.requiredStates.length, + figuredOutContract: figuredOut.includes("Case -> Run -> Stage -> Artifact -> Proposal -> Approval -> Receipt"), + lockCreated: lock === true, + proofIdentityBound: proof.applicationHash === currentIdentity.applicationHash && proof.configHash === currentIdentity.configHash, proofPassed: proof.passed === true && proof.releaseReady === false, runtimeHashBound: currentIdentity.identity?.files?.some((file) => file.path === "vendor/nodekit/src/cli.mjs"), - runtimeNotMisclassified: Object.values(resolvedDefinition.discovered ?? {}).flat() - .every((entry) => !String(entry).startsWith("vendor/")), - runtimeSpecifierPortable: dependency === "file:vendor/nodekit" - && installedNodeKitLink?.link === true - && installedNodeKitLink?.resolved === "vendor/nodekit" - && !path.isAbsolute(String(installedNodeKitLink?.resolved ?? "")), + runtimeNotMisclassified: Object.values(resolvedDefinition.discovered ?? {}).flat().every((entry) => !String(entry).startsWith("vendor/")), + runtimeSpecifierPortable: dependency === "file:vendor/nodekit", }; - if (!Object.values(checks).every(Boolean)) { - throw new Error(`${preset} factory acceptance failed: ${JSON.stringify(checks)}`); - } + if (!Object.values(checks).every(Boolean)) throw new Error(`domain-blank base factory acceptance failed: ${JSON.stringify(checks)}`); return { applicationHash: currentIdentity.applicationHash, - candidateCommit: created.candidateCommit, + browserManifestDigest: browserJourney.manifestSha256, + candidateCommit, checks, configHash: currentIdentity.configHash, dependency, + phases, proofDigest: digest(proof), + timingEvidence: { + cacheClass, + cacheIsolated: cacheClass === "cold" || cacheClass === "warm", + firstMeaningfulPaintMs: browserJourney.firstMeaningfulPaintMs, + horizontalOverflowPx: Math.max(0, ...browserJourney.screenshots.map((entry) => entry.horizontalOverflowPx ?? 0)), + neutralJourneyMs: browserJourney.milestones.find((entry) => entry.name === "receipt_reload_confirmed")?.elapsedMs, + reloadPreserved: browserJourney.journeyAssertions?.receiptSurvivedReload === true, + serverReadinessMs: browserJourney.phases.find((entry) => entry.name === "server_readiness")?.durationMs, + }, + target, + template: "domain-blank-base", }; } -const started = Date.now(); +const startedAt = new Date().toISOString(); +const started = performance.now(); const temporaryRoot = await mkdtemp(path.join(os.tmpdir(), "nodekit-factory-acceptance-")); let receipt; try { - const results = {}; - for (const preset of presets) { - console.log(`FACTORY ACCEPTANCE ${preset}`); - results[preset] = await acceptPreset(temporaryRoot, preset); - } + console.log(`FACTORY ACCEPTANCE domain-blank-base (${packageManager})`); + const sourceIdentity = { commit: await currentGitCommit(repoRoot), hash: await computeNodeKitSourceHash(repoRoot) }; + const result = await acceptBase(temporaryRoot, sourceIdentity); + const evidenceSource = path.join(result.target, "proof", "ease", runId); + const evidenceTarget = path.join(repoRoot, "proof", "ease", "latest"); + await rm(evidenceTarget, { force: true, recursive: true }); + await mkdir(evidenceTarget, { recursive: true }); + await cp(evidenceSource, evidenceTarget, { recursive: true }); + runCommand(result.target, "git", ["archive", "--format=tar.gz", `--output=${path.join(evidenceTarget, "candidate.tar.gz")}`, "HEAD"]); + + const durationMs = Math.round(performance.now() - started); receipt = { - durationMs: Date.now() - started, + base: { ...result, target: undefined }, + cacheClass, + cacheIsolated: result.timingEvidence.cacheIsolated, + durationMs, generatedAt: new Date().toISOString(), nodeVersion: process.version, - nodekitSourceHash: await nodeKitSourceHash(), + nodekitCommit: sourceIdentity.commit, + nodekitSourceHash: sourceIdentity.hash, + operatingSystem: `${os.platform()}-${os.release()}-${os.arch()}`, + packageManager, passed: true, - presets: results, - schemaVersion: "nodekit.factory-acceptance/v1", + runId, + schemaVersion: "nodekit.ease-proof-run/v1", + startedAt, + submissionReady: false, + submissionBlockers: [...requiredSubmissionGates], + verdict: "EASE_NOT_CERTIFIED", }; receipt.receiptDigest = digest(receipt); await mkdir(path.join(repoRoot, "proof"), { recursive: true }); - await writeFile( - path.join(repoRoot, "proof", "factory-acceptance.json"), - `${JSON.stringify(receipt, null, 2)}\n`, - ); + await writeFile(path.join(repoRoot, "proof", "factory-acceptance.json"), `${JSON.stringify(receipt, null, 2)}\n`); + await writeFile(path.join(evidenceTarget, "manifest.json"), `${JSON.stringify(receipt, null, 2)}\n`); + if (cacheClass === "cold" || cacheClass === "warm") { + const phase = (name) => result.phases.find((entry) => entry.name === name); + const timingReceipt = { + apiKeysRequired: 0, + cacheClass, + cacheIsolated: result.timingEvidence.cacheIsolated, + consoleErrors: 0, + failedCommands: result.phases.filter((entry) => entry.failed === true).length, + generatedAt: receipt.generatedAt, + horizontalOverflowPx: result.timingEvidence.horizontalOverflowPx, + lane: `${os.platform() === "win32" ? "windows" : os.platform() === "darwin" ? "macos" : "ubuntu"}/${packageManager}`, + manualDecisions: 0, + measurements: { + compileMs: phase("compile")?.durationMs, + dependencyInstallationMs: phase("dependency_installation")?.durationMs, + firstMeaningfulPaintMs: result.timingEvidence.firstMeaningfulPaintMs, + neutralJourneyMs: result.timingEvidence.neutralJourneyMs, + scaffoldGenerationMs: phase("scaffold_generation")?.durationMs, + serverReadinessMs: result.timingEvidence.serverReadinessMs, + totalMs: durationMs, + }, + nodeVersion: receipt.nodeVersion, + nodekitCommit: sourceIdentity.commit, + nodekitSourceHash: sourceIdentity.hash, + operatingSystem: receipt.operatingSystem, + packageManager, + receiptProduced: result.checks.proofPassed, + reloadPreserved: result.timingEvidence.reloadPreserved, + runId, + schemaVersion: "nodekit.developer-timing-run/v1", + sourceEdits: 0, + }; + timingReceipt.receiptSha256 = digest(timingReceipt); + await writeFile(path.join(repoRoot, "proof", "ease", "developer-timing-run.json"), `${JSON.stringify(timingReceipt, null, 2)}\n`); + await writeFile(path.join(evidenceTarget, "developer-timing-run.json"), `${JSON.stringify(timingReceipt, null, 2)}\n`); + } console.log(JSON.stringify(receipt, null, 2)); } finally { - if (process.env.NODEKIT_KEEP_ACCEPTANCE !== "1") { - await rm(temporaryRoot, { force: true, recursive: true }); - } else { - console.log(`FACTORY ACCEPTANCE WORKSPACES ${temporaryRoot}`); - } + if (process.env.NODEKIT_KEEP_ACCEPTANCE !== "1") await rm(temporaryRoot, { force: true, recursive: true }); + else console.log(`FACTORY ACCEPTANCE WORKSPACES ${temporaryRoot}`); } diff --git a/src/index.d.mts b/src/index.d.mts new file mode 100644 index 0000000..c4e9169 --- /dev/null +++ b/src/index.d.mts @@ -0,0 +1 @@ +export * from "./caseflow.mjs"; diff --git a/src/index.mjs b/src/index.mjs new file mode 100644 index 0000000..5530458 --- /dev/null +++ b/src/index.mjs @@ -0,0 +1,11 @@ +export { + CASEFLOW_SCHEMA_VERSIONS, + TERMINAL_RUN_STATUSES, + contentHash, + createMemoryCaseflow, +} from "./lib/caseflow.mjs"; +export { runCaseflowConformance } from "./lib/caseflow-conformance.mjs"; +export { + negotiateRuntimeCapabilities, + runtimeProfiles, +} from "./lib/runtime-capabilities.mjs"; diff --git a/src/lib/agent-ease-report.mjs b/src/lib/agent-ease-report.mjs new file mode 100644 index 0000000..61c3fc9 --- /dev/null +++ b/src/lib/agent-ease-report.mjs @@ -0,0 +1,12 @@ +const WRITE_BLOCKAGE_PATTERNS = [ + /\bno repository files were changed\b/i, + /\bplease restart with write access\b/i, + /\b(?:the )?workspace is read-only\b/i, + /\b(?:unable|failed) to (?:edit|modify|write)(?: to)? (?:the )?(?:repository|workspace|files?)\b/i, + /\bblocked (?:from|by) (?:editing|writing|the sandbox|permissions?)\b/i, +]; + +export function reportsWriteBlockage(report) { + const text = String(report ?? ""); + return WRITE_BLOCKAGE_PATTERNS.some((pattern) => pattern.test(text)); +} diff --git a/src/lib/caseflow-conformance.mjs b/src/lib/caseflow-conformance.mjs new file mode 100644 index 0000000..a4c0159 --- /dev/null +++ b/src/lib/caseflow-conformance.mjs @@ -0,0 +1,62 @@ +/** + * Provider-neutral Caseflow conformance. An adapter may return values directly + * or as promises; the suite observes behavior rather than storage mechanics. + */ +export async function runCaseflowConformance(createRuntime, { requiredCapabilities = { optimisticConcurrency: true, transactions: true } } = {}) { + const runtime = await createRuntime(); + const work = await runtime.createCase({ title: "Adapter conformance", primaryJob: "Preserve one reviewed artifact" }); + const run = await runtime.startRun({ + caseId: work.caseId, + stages: [ + { id: "working", label: "Prepare", owner: "agent" }, + { id: "review", label: "Review", owner: "user" }, + { id: "complete", label: "Complete", owner: "system" }, + ], + }); + const reusedRun = await runtime.startRun({ + caseId: work.caseId, + stages: [ + { id: "working", label: "Prepare", owner: "agent" }, + { id: "review", label: "Review", owner: "user" }, + { id: "complete", label: "Complete", owner: "system" }, + ], + }); + const artifact = await runtime.createArtifact({ caseId: work.caseId, runId: run.runId, title: "Artifact", content: { value: 1 } }); + const accepted = await runtime.createProposal({ artifactId: artifact.artifactId, baseVersion: 1, patch: { value: 2 } }); + const stale = await runtime.createProposal({ artifactId: artifact.artifactId, baseVersion: 1, patch: { value: 99 } }); + const firstDecision = await runtime.decideProposal({ proposalId: accepted.proposalId, decision: "accepted" }); + const repeatedDecision = await runtime.decideProposal({ proposalId: accepted.proposalId, decision: "accepted" }); + const staleDecision = await runtime.decideProposal({ proposalId: stale.proposalId, decision: "accepted" }); + const raised = await runtime.raiseException({ runId: run.runId, code: "conformance_pause", message: "Exercise recovery", preservedState: { canonicalVersion: 2 } }); + const recovered = await runtime.resolveException({ exceptionId: raised.exceptionId, resolution: "Resume", nextAction: "Verify", nextActionOwner: "system" }); + await runtime.enterStage({ runId: run.runId, stageId: "complete" }); + const completed = await runtime.completeRun({ runId: run.runId }); + const repeatedCompletion = await runtime.completeRun({ runId: run.runId }); + const snapshot = await runtime.snapshot(); + + const assertions = { + activeRunStartIsIdempotent: reusedRun.runId === run.runId, + canonicalVersionAdvancedOnce: firstDecision.artifact.canonicalVersion === 2, + contentAddressedReceipt: /^[a-f0-9]{64}$/.test(completed.receipt.receiptHash), + exceptionStatePreserved: raised.preservedState.canonicalVersion === 2, + nextActionOwnerExplicit: recovered.run.nextActionOwner === "system", + oneAuthoritativeCase: snapshot.cases.length === 1 && snapshot.cases[0].status === "completed", + repeatedCompletionIsIdempotent: repeatedCompletion.reused === true + && repeatedCompletion.receipt.receiptId === completed.receipt.receiptId + && repeatedCompletion.receipt.receiptHash === completed.receipt.receiptHash, + repeatedDecisionIsIdempotent: repeatedDecision.reused === true + && repeatedDecision.approval.approvalId === firstDecision.approval.approvalId + && repeatedDecision.artifact.canonicalVersion === 2, + staleProposalFailedClosed: staleDecision.proposal.status === "conflicted" + && staleDecision.artifact.versions.at(-1).content.value === 2, + }; + return { + assertions, + capabilityNegotiation: negotiateRuntimeCapabilities(runtime.capabilities, requiredCapabilities), + capabilities: runtime.capabilities, + passed: Object.values(assertions).every(Boolean) + && negotiateRuntimeCapabilities(runtime.capabilities, requiredCapabilities).passed, + schemaVersion: "nodekit.adapter-conformance/v1", + }; +} +import { negotiateRuntimeCapabilities } from "./runtime-capabilities.mjs"; diff --git a/src/lib/caseflow.mjs b/src/lib/caseflow.mjs new file mode 100644 index 0000000..44a83b1 --- /dev/null +++ b/src/lib/caseflow.mjs @@ -0,0 +1,318 @@ +import { createHash, randomUUID } from "node:crypto"; +import { runtimeProfiles } from "./runtime-capabilities.mjs"; + +export const CASEFLOW_SCHEMA_VERSIONS = Object.freeze({ + approval: "nodekit.approval/v1", + artifact: "nodekit.artifact/v1", + case: "nodekit.case/v1", + event: "nodekit.caseflow-event/v1", + exception: "nodekit.exception/v1", + proposal: "nodekit.proposal/v1", + receipt: "nodekit.receipt/v1", + run: "nodekit.run/v1", + stage: "nodekit.stage/v1", +}); + +export const TERMINAL_RUN_STATUSES = Object.freeze([ + "cancelled", + "completed", + "failed_safely", +]); + +function clone(value) { + return structuredClone(value); +} + +function id(prefix) { + return `${prefix}_${randomUUID().replaceAll("-", "")}`; +} + +function canonical(value) { + if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`; + if (value && typeof value === "object") { + return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${canonical(value[key])}`).join(",")}}`; + } + return JSON.stringify(value); +} + +export function contentHash(value) { + return createHash("sha256").update(canonical(value)).digest("hex"); +} + +export function createMemoryCaseflow({ clock = () => new Date().toISOString() } = {}) { + const state = { + approvals: new Map(), + artifacts: new Map(), + cases: new Map(), + events: [], + exceptions: new Map(), + proposals: new Map(), + receipts: new Map(), + runs: new Map(), + }; + + function requireRecord(collection, recordId, label) { + const value = collection.get(recordId); + if (!value) throw new Error(`${label} not found: ${recordId}`); + return value; + } + + function emit(aggregateType, aggregateId, eventType, payload = {}, actor = { type: "system", id: "nodekit" }) { + const event = { + actor, + aggregateId, + aggregateType, + eventId: id("event"), + eventType, + occurredAt: clock(), + payload: clone(payload), + schemaVersion: CASEFLOW_SCHEMA_VERSIONS.event, + sequence: state.events.filter((entry) => entry.aggregateId === aggregateId).length + 1, + }; + state.events.push(event); + return event; + } + + function createCase({ title, primaryJob, actor }) { + if (!String(title ?? "").trim()) throw new Error("case title is required"); + if (!String(primaryJob ?? "").trim()) throw new Error("case primaryJob is required"); + const createdAt = clock(); + const record = { + caseId: id("case"), + createdAt, + currentRunId: null, + primaryJob: String(primaryJob).trim(), + schemaVersion: CASEFLOW_SCHEMA_VERSIONS.case, + status: "ready", + title: String(title).trim(), + updatedAt: createdAt, + }; + state.cases.set(record.caseId, record); + emit("case", record.caseId, "case.created", record, actor); + return clone(record); + } + + function startRun({ caseId, stages, actor }) { + const caseRecord = requireRecord(state.cases, caseId, "case"); + if (!Array.isArray(stages) || stages.length === 0) throw new Error("run stages are required"); + if (caseRecord.currentRunId) { + const current = state.runs.get(caseRecord.currentRunId); + if (current && !TERMINAL_RUN_STATUSES.includes(current.status)) return clone(current); + } + const normalizedStages = stages.map((stage, index) => ({ + id: String(stage.id ?? `stage-${index + 1}`), + label: String(stage.label ?? stage.id ?? `Stage ${index + 1}`), + owner: stage.owner ?? "system", + status: index === 0 ? "active" : "pending", + })); + const createdAt = clock(); + const run = { + caseId, + createdAt, + currentStageId: normalizedStages[0].id, + nextAction: normalizedStages[0].label, + nextActionOwner: normalizedStages[0].owner, + runId: id("run"), + schemaVersion: CASEFLOW_SCHEMA_VERSIONS.run, + stages: normalizedStages, + status: "active", + updatedAt: createdAt, + }; + state.runs.set(run.runId, run); + Object.assign(caseRecord, { currentRunId: run.runId, status: "in_progress", updatedAt: createdAt }); + emit("run", run.runId, "run.started", run, actor); + emit("run", run.runId, "stage.entered", { stageId: run.currentStageId }, actor); + return clone(run); + } + + function enterStage({ runId, stageId, nextAction, nextActionOwner, actor }) { + const run = requireRecord(state.runs, runId, "run"); + if (TERMINAL_RUN_STATUSES.includes(run.status)) throw new Error(`run is terminal: ${run.status}`); + const targetIndex = run.stages.findIndex((stage) => stage.id === stageId); + if (targetIndex < 0) throw new Error(`stage not found: ${stageId}`); + run.stages = run.stages.map((stage, index) => ({ + ...stage, + status: index < targetIndex ? "completed" : index === targetIndex ? "active" : "pending", + })); + Object.assign(run, { + currentStageId: stageId, + nextAction: nextAction ?? run.stages[targetIndex].label, + nextActionOwner: nextActionOwner ?? run.stages[targetIndex].owner, + updatedAt: clock(), + }); + emit("run", runId, "stage.entered", { nextAction: run.nextAction, nextActionOwner: run.nextActionOwner, stageId }, actor); + return clone(run); + } + + function createArtifact({ caseId, runId, kind, title, content, actor }) { + requireRecord(state.cases, caseId, "case"); + requireRecord(state.runs, runId, "run"); + const createdAt = clock(); + const artifact = { + artifactId: id("artifact"), + caseId, + canonicalVersion: 1, + createdAt, + kind: kind ?? "generic", + runId, + schemaVersion: CASEFLOW_SCHEMA_VERSIONS.artifact, + title: String(title ?? "Artifact"), + updatedAt: createdAt, + versions: [{ content: clone(content), contentHash: contentHash(content), createdAt, version: 1 }], + }; + state.artifacts.set(artifact.artifactId, artifact); + emit("artifact", artifact.artifactId, "artifact.created", { artifactId: artifact.artifactId, version: 1 }, actor); + return clone(artifact); + } + + function createProposal({ artifactId, baseVersion, patch, rationale, actor }) { + const artifact = requireRecord(state.artifacts, artifactId, "artifact"); + if (baseVersion !== artifact.canonicalVersion) { + throw new Error(`proposal base version ${baseVersion} is stale; canonical version is ${artifact.canonicalVersion}`); + } + const createdAt = clock(); + const proposal = { + artifactId, + baseVersion, + createdAt, + patch: clone(patch), + proposalId: id("proposal"), + rationale: String(rationale ?? ""), + schemaVersion: CASEFLOW_SCHEMA_VERSIONS.proposal, + status: "pending", + }; + state.proposals.set(proposal.proposalId, proposal); + emit("proposal", proposal.proposalId, "proposal.created", proposal, actor); + return clone(proposal); + } + + function decideProposal({ proposalId, decision, actor, comment = "" }) { + if (!new Set(["accepted", "rejected"]).has(decision)) throw new Error("decision must be accepted or rejected"); + const proposal = requireRecord(state.proposals, proposalId, "proposal"); + const artifact = requireRecord(state.artifacts, proposal.artifactId, "artifact"); + if (proposal.status !== "pending") { + const existingApproval = [...state.approvals.values()].find((entry) => entry.proposalId === proposalId); + const repeatedDecisionMatches = existingApproval?.decision === decision + && (proposal.status === decision || (proposal.status === "conflicted" && decision === "accepted")); + if (!repeatedDecisionMatches) throw new Error(`proposal is already ${proposal.status}`); + return { approval: clone(existingApproval), artifact: clone(artifact), proposal: clone(proposal), reused: true }; + } + const decidedAt = clock(); + const approval = { + approvalId: id("approval"), + comment: String(comment), + decidedAt, + decision, + proposalId, + schemaVersion: CASEFLOW_SCHEMA_VERSIONS.approval, + }; + state.approvals.set(approval.approvalId, approval); + if (decision === "accepted" && proposal.baseVersion !== artifact.canonicalVersion) { + proposal.status = "conflicted"; + emit("proposal", proposalId, "proposal.conflicted", { canonicalVersion: artifact.canonicalVersion }, actor); + return { approval: clone(approval), artifact: clone(artifact), proposal: clone(proposal), reused: false }; + } + proposal.status = decision; + if (decision === "accepted") { + const nextVersion = artifact.canonicalVersion + 1; + artifact.canonicalVersion = nextVersion; + artifact.updatedAt = decidedAt; + artifact.versions.push({ + content: clone(proposal.patch), + contentHash: contentHash(proposal.patch), + createdAt: decidedAt, + proposalId, + version: nextVersion, + }); + emit("artifact", artifact.artifactId, "artifact.version_created", { proposalId, version: nextVersion }, actor); + } + emit("proposal", proposalId, `proposal.${proposal.status}`, { approvalId: approval.approvalId }, actor); + return { approval: clone(approval), artifact: clone(artifact), proposal: clone(proposal), reused: false }; + } + + function raiseException({ runId, code, message, preservedState, actor }) { + const run = requireRecord(state.runs, runId, "run"); + const exception = { + code: String(code ?? "unknown"), + exceptionId: id("exception"), + message: String(message ?? "An exception occurred."), + preservedState: clone(preservedState ?? {}), + raisedAt: clock(), + resolution: null, + runId, + schemaVersion: CASEFLOW_SCHEMA_VERSIONS.exception, + status: "open", + }; + state.exceptions.set(exception.exceptionId, exception); + Object.assign(run, { status: "blocked", nextAction: "Resolve exception", nextActionOwner: "user", updatedAt: clock() }); + emit("run", runId, "exception.raised", { code: exception.code, exceptionId: exception.exceptionId }, actor); + return clone(exception); + } + + function resolveException({ exceptionId, resolution, nextAction, nextActionOwner, actor }) { + const exception = requireRecord(state.exceptions, exceptionId, "exception"); + if (exception.status !== "open") throw new Error("exception is already resolved"); + exception.status = "resolved"; + exception.resolution = String(resolution ?? "resolved"); + exception.resolvedAt = clock(); + const run = requireRecord(state.runs, exception.runId, "run"); + Object.assign(run, { status: "active", nextAction: nextAction ?? "Continue run", nextActionOwner: nextActionOwner ?? "system", updatedAt: clock() }); + emit("run", run.runId, "exception.resolved", { exceptionId, resolution: exception.resolution }, actor); + return { exception: clone(exception), run: clone(run) }; + } + + function completeRun({ runId, actor }) { + const run = requireRecord(state.runs, runId, "run"); + if (run.status === "completed") { + const receipt = [...state.receipts.values()].find((entry) => entry.runId === runId); + if (!receipt) throw new Error("completed run is missing its receipt"); + return { receipt: clone(receipt), run: clone(run), reused: true }; + } + if (TERMINAL_RUN_STATUSES.includes(run.status)) throw new Error(`run is terminal: ${run.status}`); + if ([...state.exceptions.values()].some((entry) => entry.runId === runId && entry.status === "open")) { + throw new Error("run has unresolved exceptions"); + } + run.status = "completed"; + run.nextAction = "Review receipt"; + run.nextActionOwner = "user"; + run.updatedAt = clock(); + run.stages = run.stages.map((stage) => ({ ...stage, status: "completed" })); + const caseRecord = requireRecord(state.cases, run.caseId, "case"); + Object.assign(caseRecord, { status: "completed", updatedAt: clock() }); + emit("run", runId, "run.completed", {}, actor); + const artifactIds = [...state.artifacts.values()].filter((entry) => entry.runId === runId).map((entry) => entry.artifactId); + const proposalIds = [...state.proposals.values()].filter((entry) => artifactIds.includes(entry.artifactId)).map((entry) => entry.proposalId); + const receiptBody = { + artifactIds, + caseId: run.caseId, + eventIds: state.events.filter((entry) => entry.aggregateId === runId || artifactIds.includes(entry.aggregateId) || proposalIds.includes(entry.aggregateId)).map((entry) => entry.eventId), + generatedAt: clock(), + proposalIds, + runId, + schemaVersion: CASEFLOW_SCHEMA_VERSIONS.receipt, + status: "completed", + }; + const receipt = { ...receiptBody, receiptId: id("receipt"), receiptHash: contentHash(receiptBody) }; + state.receipts.set(receipt.receiptId, receipt); + emit("run", runId, "receipt.created", { receiptHash: receipt.receiptHash, receiptId: receipt.receiptId }, actor); + return { receipt: clone(receipt), run: clone(run), reused: false }; + } + + function snapshot() { + return clone(Object.fromEntries(Object.entries(state).map(([key, value]) => [key, value instanceof Map ? [...value.values()] : value]))); + } + + return { + capabilities: runtimeProfiles.memory, + completeRun, + createArtifact, + createCase, + createProposal, + decideProposal, + enterStage, + raiseException, + resolveException, + snapshot, + startRun, + }; +} diff --git a/src/lib/contracts.mjs b/src/lib/contracts.mjs index 961d972..cc2601d 100644 --- a/src/lib/contracts.mjs +++ b/src/lib/contracts.mjs @@ -1,6 +1,18 @@ export const CONTRACT_VERSIONS = Object.freeze({ application: "nodeagent.application/v1", + canaryReceipt: "nodekit.canary-receipt/v1", event: "nodeagent.event/v1", + harness: "nodekit.harness/v1", + modelCapabilityCard: "nodekit.model-capability-card/v1", + modelObservation: "nodekit.model-observation/v1", + promotionReceipt: "nodekit.promotion-receipt/v1", + routingPolicy: "nodekit.routing-policy/v1", + runtimeCapabilities: "nodekit.runtime-capabilities/v1", + skill: "nodekit.skill/v1", + skillCandidate: "nodekit.skill-candidate/v1", + skillComparison: "nodekit.skill-comparison/v1", + submissionManifest: "nodekit.submission-manifest/v1", + tournament: "nodekit.tournament/v1", pack: "nodeagent.pack/v1", repository: "nodekit.repo/v1", trace: "nodeagent.trace/v1", @@ -8,6 +20,18 @@ export const CONTRACT_VERSIONS = Object.freeze({ export const CONTRACT_SCHEMA_FILES = Object.freeze({ application: "nodeagent.application.v1.schema.json", + canaryReceipt: "nodekit.canary-receipt.v1.schema.json", + harness: "nodekit.harness.v1.schema.json", + modelCapabilityCard: "nodekit.model-capability-card.v1.schema.json", + modelObservation: "nodekit.model-observation.v1.schema.json", + promotionReceipt: "nodekit.promotion-receipt.v1.schema.json", + routingPolicy: "nodekit.routing-policy.v1.schema.json", + runtimeCapabilities: "nodekit.runtime-capabilities.v1.schema.json", + skill: "nodekit.skill.v1.schema.json", + skillCandidate: "nodekit.skill-candidate.v1.schema.json", + skillComparison: "nodekit.skill-comparison.v1.schema.json", + submissionManifest: "nodekit.submission-manifest.v1.schema.json", + tournament: "nodekit.tournament.v1.schema.json", pack: "nodeagent.pack.v1.schema.json", repository: "nodekit.schema.json", }); diff --git a/src/lib/ease-evidence.mjs b/src/lib/ease-evidence.mjs new file mode 100644 index 0000000..e6153d7 --- /dev/null +++ b/src/lib/ease-evidence.mjs @@ -0,0 +1,116 @@ +const timingFields = [ + "scaffoldGenerationMs", + "dependencyInstallationMs", + "compileMs", + "serverReadinessMs", + "firstMeaningfulPaintMs", + "neutralJourneyMs", + "totalMs", +]; + +const requiredLanes = [ + "windows/npm", "windows/pnpm", + "ubuntu/npm", "ubuntu/pnpm", + "macos/npm", "macos/pnpm", +]; + +function median(values) { + const sorted = [...values].sort((a, b) => a - b); + if (sorted.length === 0) return null; + const middle = Math.floor(sorted.length / 2); + return sorted.length % 2 ? sorted[middle] : (sorted[middle - 1] + sorted[middle]) / 2; +} + +function percentile(values, fraction) { + const sorted = [...values].sort((a, b) => a - b); + if (sorted.length === 0) return null; + return sorted[Math.ceil(sorted.length * fraction) - 1]; +} + +export function evaluateFreshUserStudy(study) { + const errors = []; + const participants = Array.isArray(study?.participants) ? study.participants : []; + if (!/^[a-f0-9]{40}$/.test(study?.nodekitCommit ?? "")) errors.push("study requires an immutable NodeKit commit"); + if (!/^[a-f0-9]{64}$/.test(study?.nodekitSourceHash ?? "")) errors.push("study requires a NodeKit source hash"); + if (study?.instruction !== "Use this app to complete the job shown on screen.") errors.push("study instruction changed"); + if (participants.length < 5) errors.push("at least five fresh participants are required"); + if (new Set(participants.map((entry) => entry.participantId)).size !== participants.length) errors.push("participant IDs must be unique"); + const numericFields = ["firstMeaningfulActionMs", "neutralJourneyMs", "wrongTurns", "helpRequests", "singleEaseQuestion", "p0P1Failures"]; + for (const participant of participants) { + if (participant.fresh !== true) errors.push(`${participant.participantId ?? "unknown"}: participant is not marked fresh`); + if (participant.consentRecorded !== true) errors.push(`${participant.participantId ?? "unknown"}: consent is not recorded`); + if (!/^\d{4}-\d{2}-\d{2}T/.test(participant.sessionStartedAt ?? "")) errors.push(`${participant.participantId ?? "unknown"}: invalid sessionStartedAt`); + if (!/^\d{4}-\d{2}-\d{2}T/.test(participant.sessionCompletedAt ?? "")) errors.push(`${participant.participantId ?? "unknown"}: invalid sessionCompletedAt`); + if (!Array.isArray(participant.evidenceRefs) || participant.evidenceRefs.length === 0) errors.push(`${participant.participantId ?? "unknown"}: screenshot or recording evidence is required`); + for (const field of numericFields) if (!Number.isFinite(participant[field]) || participant[field] < 0) errors.push(`${participant.participantId ?? "unknown"}: invalid ${field}`); + for (const field of ["completed", "assisted", "canExplainOutcome", "locatedFinalArtifact", "locatedUnresolvedIssues"]) { + if (typeof participant[field] !== "boolean") errors.push(`${participant.participantId ?? "unknown"}: missing ${field}`); + } + } + const thresholds = study?.thresholds ?? {}; + const unassistedCompletions = participants.filter((entry) => entry.completed === true && entry.assisted === false).length; + const medianFirstMeaningfulActionMs = median(participants.map((entry) => entry.firstMeaningfulActionMs).filter(Number.isFinite)); + const medianNeutralJourneyMs = median(participants.map((entry) => entry.neutralJourneyMs).filter(Number.isFinite)); + const medianSingleEaseQuestion = median(participants.map((entry) => entry.singleEaseQuestion).filter(Number.isFinite)); + const p0P1Failures = participants.reduce((sum, entry) => sum + (Number.isFinite(entry.p0P1Failures) ? entry.p0P1Failures : 0), 0); + const checks = { + participantCount: participants.length >= 5, + unassistedCompletion: unassistedCompletions >= thresholds.minimumUnassistedCompletions, + firstMeaningfulAction: medianFirstMeaningfulActionMs !== null && medianFirstMeaningfulActionMs <= thresholds.maximumMedianFirstMeaningfulActionMs, + neutralJourney: medianNeutralJourneyMs !== null && medianNeutralJourneyMs <= thresholds.maximumMedianNeutralJourneyMs, + singleEaseQuestion: medianSingleEaseQuestion !== null && medianSingleEaseQuestion >= thresholds.minimumMedianSingleEaseQuestion, + noP0P1Failures: p0P1Failures <= thresholds.maximumP0P1Failures, + }; + return { + schemaVersion: "nodekit.fresh-user-verdict/v1", + nodekitCommit: study?.nodekitCommit ?? null, + nodekitSourceHash: study?.nodekitSourceHash ?? null, + nodekitIdentity: study?.nodekitCommit && study?.nodekitSourceHash ? `${study.nodekitCommit}/${study.nodekitSourceHash}` : null, + passed: errors.length === 0 && Object.values(checks).every(Boolean), + errors, + checks, + metrics: { participantCount: participants.length, unassistedCompletions, medianFirstMeaningfulActionMs, medianNeutralJourneyMs, medianSingleEaseQuestion, p0P1Failures }, + }; +} + +export function evaluateDeveloperTimingMatrix(receipts) { + const errors = []; + const cells = {}; + const identities = new Set(); + for (const run of receipts) { + if (!/^[a-f0-9]{40}$/.test(run.nodekitCommit ?? "")) errors.push(`${run.runId ?? "unknown"}: missing immutable NodeKit commit`); + if (!/^[a-f0-9]{64}$/.test(run.nodekitSourceHash ?? "")) errors.push(`${run.runId ?? "unknown"}: missing NodeKit source hash`); + if (/^[a-f0-9]{40}$/.test(run.nodekitCommit ?? "") && /^[a-f0-9]{64}$/.test(run.nodekitSourceHash ?? "")) identities.add(`${run.nodekitCommit}/${run.nodekitSourceHash}`); + } + if (identities.size !== 1) errors.push(`developer timing receipts must share one immutable NodeKit identity; found ${identities.size}`); + for (const lane of requiredLanes) { + cells[lane] = {}; + for (const cacheClass of ["cold", "warm"]) { + const runs = receipts.filter((entry) => entry.lane === lane && entry.cacheClass === cacheClass); + if (runs.length < 5) errors.push(`${lane}/${cacheClass}: requires five runs, found ${runs.length}`); + if (cacheClass === "cold" && runs.some((entry) => entry.cacheIsolated !== true)) errors.push(`${lane}/cold: every cache must be isolated`); + for (const run of runs) { + for (const field of timingFields) if (!Number.isFinite(run.measurements?.[field]) || run.measurements[field] < 0) errors.push(`${run.runId ?? "unknown"}: invalid ${field}`); + if (run.failedCommands !== 0 || run.manualDecisions !== 0 || run.apiKeysRequired !== 0 || run.sourceEdits !== 0 || run.consoleErrors !== 0 || run.horizontalOverflowPx !== 0 || run.receiptProduced !== true || run.reloadPreserved !== true) { + errors.push(`${run.runId ?? "unknown"}: zero-intervention journey invariants failed`); + } + } + cells[lane][cacheClass] = Object.fromEntries(timingFields.map((field) => { + const values = runs.map((entry) => entry.measurements?.[field]).filter(Number.isFinite); + return [field, { + samples: values.length, + median: median(values), + minimum: values.length ? Math.min(...values) : null, + maximum: values.length ? Math.max(...values) : null, + q1: values.length >= 4 ? percentile(values, 0.25) : null, + q3: values.length >= 4 ? percentile(values, 0.75) : null, + p95: values.length >= 20 ? percentile(values, 0.95) : null, + p95Eligible: values.length >= 20, + }]; + })); + } + } + const nodekitIdentity = identities.size === 1 ? [...identities][0] : null; + const [nodekitCommit = null, nodekitSourceHash = null] = nodekitIdentity?.split("/") ?? []; + return { schemaVersion: "nodekit.developer-timing-verdict/v1", nodekitCommit, nodekitSourceHash, nodekitIdentity, passed: errors.length === 0, errors, requiredRuns: 60, observedRuns: receipts.length, cells }; +} diff --git a/src/lib/evolution-ledger.mjs b/src/lib/evolution-ledger.mjs new file mode 100644 index 0000000..8cc97cf --- /dev/null +++ b/src/lib/evolution-ledger.mjs @@ -0,0 +1,384 @@ +import { createHash } from "node:crypto"; +import { execFileSync } from "node:child_process"; +import { mkdir, readFile, readdir, writeFile } from "node:fs/promises"; +import path from "node:path"; +import { pathExists, readJson } from "./files.mjs"; +import { proposeGraphPatch, readKnowledgeGraph } from "./knowledge-evolution.mjs"; +import { validateSchema } from "./schema-validation.mjs"; + +export const EVOLUTION_EVENT_SCHEMA = "nodekit.evolution-event/v1"; +export const EVOLUTION_RECORD_TYPES = Object.freeze({ + [EVOLUTION_EVENT_SCHEMA]: { directory: "events", schema: "nodekit.evolution-event.v1.schema.json", plural: "events" }, + "nodekit.assumption/v1": { directory: "assumptions", schema: "nodekit.assumption.v1.schema.json", plural: "assumptions" }, + "nodekit.invariant-claim/v1": { directory: "invariants", schema: "nodekit.invariant-claim.v1.schema.json", plural: "invariants" }, + "nodekit.evolution-evidence/v1": { directory: "evidence", schema: "nodekit.evolution-evidence.v1.schema.json", plural: "evidence" }, + "nodekit.evolution-adoption/v1": { directory: "adoptions", schema: "nodekit.evolution-adoption.v1.schema.json", plural: "adoptions" }, +}); + +function digest(value) { + return createHash("sha256").update(value).digest("hex"); +} + +function canonical(value) { + if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`; + if (value && typeof value === "object") return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${canonical(value[key])}`).join(",")}}`; + return JSON.stringify(value); +} + +function now() { + return new Date().toISOString(); +} + +function resolveInside(repoRoot, relative, label) { + const root = path.resolve(repoRoot); + const target = path.resolve(root, String(relative)); + const relation = path.relative(root, target); + if (relation === ".." || relation.startsWith(`..${path.sep}`) || path.isAbsolute(relation)) { + throw new Error(`${label} must stay inside the repository: ${relative}`); + } + return target; +} + +function git(repoRoot, args, { allowFailure = false } = {}) { + try { + return execFileSync("git", args, { cwd: repoRoot, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim(); + } catch (error) { + if (allowFailure) return null; + throw error; + } +} + +function commitExists(repoRoot, commitSha) { + try { + execFileSync("git", ["cat-file", "-e", `${commitSha}^{commit}`], { cwd: repoRoot, stdio: "ignore" }); + return true; + } catch { + return false; + } +} + +async function jsonFiles(directory) { + if (!(await pathExists(directory))) return []; + const output = []; + async function visit(current) { + const entries = await readdir(current, { withFileTypes: true }); + entries.sort((left, right) => left.name.localeCompare(right.name)); + for (const entry of entries) { + const target = path.join(current, entry.name); + if (entry.isDirectory()) await visit(target); + else if (entry.name.endsWith(".json")) output.push(target); + } + } + await visit(directory); + return output; +} + +async function readLedger(repoRoot) { + const root = path.join(repoRoot, "evolution"); + const ledger = { events: [], assumptions: [], invariants: [], evidence: [], adoptions: [], filesById: new Map() }; + for (const definition of Object.values(EVOLUTION_RECORD_TYPES)) { + for (const file of await jsonFiles(path.join(root, definition.directory))) { + const value = await readJson(file); + ledger[definition.plural].push(value); + ledger.filesById.set(value.id, file); + } + } + return ledger; +} + +export async function initializeEvolutionLedger(repoRoot) { + const root = path.resolve(repoRoot); + const evolutionRoot = path.join(root, "evolution"); + const directories = ["events/product", "events/architecture", "events/harness", "assumptions", "invariants", "evidence", "adoptions", "drafts", "projections", "artifacts"]; + for (const directory of directories) await mkdir(path.join(evolutionRoot, directory), { recursive: true }); + const manifestPath = path.join(evolutionRoot, "ledger.json"); + if (!(await pathExists(manifestPath))) { + await writeFile(manifestPath, `${JSON.stringify({ + schemaVersion: "nodekit.evolution-ledger/v1", + authority: { canonicalRecords: "human-reviewed-only", mutation: "append-or-supersede", delete: "prohibited" }, + materiality: ["primary-user-workflow", "public-contract", "architectural-ownership", "security-authority", "proof-requirement", "model-routing", "harness-behavior", "benchmark-conclusion", "downstream-guarantee"], + recordSchemas: Object.keys(EVOLUTION_RECORD_TYPES), + }, null, 2)}\n`); + } + return { evolutionRoot, manifestPath }; +} + +export async function draftEvolutionEvent(repoRoot, input) { + const root = path.resolve(repoRoot); + await initializeEvolutionLedger(root); + const commitSha = input.commitSha ?? git(root, ["rev-parse", "HEAD"]); + const event = { + schemaVersion: EVOLUTION_EVENT_SCHEMA, + id: input.id ?? `evt:${String(input.track ?? "architecture")}:${digest(canonical(input)).slice(0, 12)}`, + projectId: input.projectId ?? "nodekit", + repository: input.repository ?? "HomenShum/node-platform", + source: { commitSha, ...(input.pullRequest ? { pullRequest: Number(input.pullRequest) } : {}), occurredAt: input.occurredAt ?? now() }, + track: input.track ?? "architecture", + category: input.category ?? "runtime", + challenge: input.challenge, + ...(input.observedFailure ? { observedFailure: input.observedFailure } : {}), + resolution: input.resolution, + assumptionIds: input.assumptionIds ?? [], + invariantIds: input.invariantIds ?? [], + evidenceIds: input.evidenceIds ?? [], + knownLimitations: input.knownLimitations ?? [], + interpretation: { status: "human-reviewed", reviewedBy: input.reviewedBy, reviewedAt: input.reviewedAt ?? now() }, + }; + const findings = await validateSchema("nodekit.evolution-event.v1.schema.json", event, "evolution event draft"); + if (findings.length > 0) throw new Error(`evolution event draft validation failed:\n${findings.join("\n")}`); + const output = path.join(root, "evolution", "drafts", `${event.id.replaceAll(":", "-")}.json`); + await writeFile(output, `${JSON.stringify(event, null, 2)}\n`); + return { event, output }; +} + +export async function recordEvolutionRecord(repoRoot, recordFile) { + const root = path.resolve(repoRoot); + await initializeEvolutionLedger(root); + const source = resolveInside(root, recordFile, "evolution record"); + const record = await readJson(source); + const definition = EVOLUTION_RECORD_TYPES[record.schemaVersion]; + if (!definition) throw new Error(`unsupported evolution record schema: ${record.schemaVersion}`); + const findings = await validateSchema(definition.schema, record, `evolution record ${record.id ?? source}`); + if (findings.length > 0) throw new Error(`evolution record validation failed:\n${findings.join("\n")}`); + if (record.schemaVersion === EVOLUTION_EVENT_SCHEMA && record.interpretation?.status !== "human-reviewed") { + throw new Error("canonical evolution events require human-reviewed interpretation"); + } + const subtype = record.schemaVersion === EVOLUTION_EVENT_SCHEMA ? `${definition.directory}/${record.track}` : definition.directory; + const output = path.join(root, "evolution", subtype, `${record.id.replaceAll(":", "-")}.json`); + if (await pathExists(output)) { + const existing = await readJson(output); + if (canonical(existing) !== canonical(record)) throw new Error(`evolution records are immutable; supersede instead of overwriting ${record.id}`); + return { duplicate: true, output, record }; + } + await mkdir(path.dirname(output), { recursive: true }); + await writeFile(output, `${JSON.stringify(record, null, 2)}\n`); + return { duplicate: false, output, record }; +} + +function hasCycle(events) { + const links = new Map(events.map((event) => [event.id, [...(event.predecessorIds ?? []), ...(event.supersedesIds ?? [])]])); + const visiting = new Set(); + const visited = new Set(); + function visit(id) { + if (visiting.has(id)) return true; + if (visited.has(id)) return false; + visiting.add(id); + for (const next of links.get(id) ?? []) if (links.has(next) && visit(next)) return true; + visiting.delete(id); + visited.add(id); + return false; + } + return [...links.keys()].some(visit); +} + +const SECRET_PATTERN = /(sk-[a-z0-9_-]{16,}|api[_-]?key\s*[:=]\s*["']?[a-z0-9_-]{12,}|-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----)/i; + +async function evidenceBytes(repoRoot, artifactRef) { + if (artifactRef.startsWith("git:")) { + const match = /^git:([a-f0-9]{40}):(.+)$/.exec(artifactRef); + if (!match) throw new Error(`invalid git artifactRef: ${artifactRef}`); + return execFileSync("git", ["show", `${match[1]}:${match[2]}`], { cwd: repoRoot }); + } + const relative = artifactRef.startsWith("file:") ? artifactRef.slice(5) : artifactRef; + return readFile(resolveInside(repoRoot, relative, "evolution evidence")); +} + +export async function verifyEvolutionLedger(repoRoot) { + const root = path.resolve(repoRoot); + const ledger = await readLedger(root); + const issues = []; + const warnings = []; + const all = [...ledger.events, ...ledger.assumptions, ...ledger.invariants, ...ledger.evidence, ...ledger.adoptions]; + const byId = new Map(); + for (const record of all) { + const definition = EVOLUTION_RECORD_TYPES[record.schemaVersion]; + if (!definition) { issues.push(`unsupported schema ${record.schemaVersion} for ${record.id}`); continue; } + const findings = await validateSchema(definition.schema, record, record.id ?? definition.plural); + issues.push(...findings); + if (byId.has(record.id)) issues.push(`duplicate evolution id: ${record.id}`); + byId.set(record.id, record); + if (SECRET_PATTERN.test(JSON.stringify(record))) issues.push(`possible secret in evolution record ${record.id}`); + } + const ids = new Set(byId.keys()); + const requireRefs = (owner, refs, expected) => { + for (const id of refs ?? []) { + const target = byId.get(id); + if (!target) issues.push(`${owner} references missing ${id}`); + else if (expected && !expected.includes(target.schemaVersion)) issues.push(`${owner} references ${id} with unexpected schema ${target.schemaVersion}`); + } + }; + for (const event of ledger.events) { + if (!commitExists(root, event.source.commitSha)) issues.push(`${event.id} source commit does not exist: ${event.source.commitSha}`); + requireRefs(event.id, event.assumptionIds, ["nodekit.assumption/v1"]); + requireRefs(event.id, event.invariantIds, ["nodekit.invariant-claim/v1"]); + requireRefs(event.id, event.evidenceIds, ["nodekit.evolution-evidence/v1"]); + requireRefs(event.id, event.predecessorIds, [EVOLUTION_EVENT_SCHEMA]); + requireRefs(event.id, event.supersedesIds, [EVOLUTION_EVENT_SCHEMA]); + if (event.modelContext && !(event.modelContext.requestedRoute && event.modelContext.resolvedModel && event.modelContext.provider)) { + issues.push(`${event.id} modelContext requires requestedRoute, resolvedModel, and provider together`); + } + } + if (hasCycle(ledger.events)) issues.push("evolution predecessor/supersession graph is circular"); + for (const assumption of ledger.assumptions) { + requireRefs(assumption.id, assumption.supportingEvidenceIds, ["nodekit.evolution-evidence/v1"]); + requireRefs(assumption.id, assumption.contradictingEvidenceIds, ["nodekit.evolution-evidence/v1"]); + if (["disproven", "superseded"].includes(assumption.status) && assumption.contradictingEvidenceIds.length === 0) issues.push(`${assumption.id} is ${assumption.status} without contradicting evidence`); + } + for (const invariant of ledger.invariants) { + requireRefs(invariant.id, [invariant.introducedByEventId], [EVOLUTION_EVENT_SCHEMA]); + if (invariant.status === "verified" && invariant.verifierRefs.length === 0) issues.push(`${invariant.id} is verified without a verifier`); + if (invariant.status === "verified" && !ledger.evidence.some((evidence) => evidence.result === "pass" && evidence.verifiesInvariantIds.includes(invariant.id))) { + issues.push(`${invariant.id} is verified without passing evidence`); + } + } + for (const evidence of ledger.evidence) { + requireRefs(evidence.id, evidence.verifiesInvariantIds, ["nodekit.invariant-claim/v1"]); + if (!commitExists(root, evidence.sourceCommit)) issues.push(`${evidence.id} source commit does not exist: ${evidence.sourceCommit}`); + try { + const bytes = await evidenceBytes(root, evidence.artifactRef); + const actual = digest(bytes); + if (actual !== evidence.sha256) issues.push(`${evidence.id} hash mismatch: expected ${evidence.sha256}, got ${actual}`); + if (SECRET_PATTERN.test(bytes.toString("utf8"))) issues.push(`${evidence.id} artifact may contain a secret`); + } catch (error) { + issues.push(`${evidence.id} evidence cannot be read: ${error.message}`); + } + if (evidence.kind === "benchmark" && !(evidence.environment?.benchmarkIdentity && evidence.environment?.sampleSize)) issues.push(`${evidence.id} benchmark evidence requires benchmarkIdentity and sampleSize`); + if (evidence.kind === "screenshot" && !(evidence.environment?.viewport && evidence.environment?.candidateIdentity)) issues.push(`${evidence.id} screenshot evidence requires viewport and candidateIdentity`); + if (["deployment", "interaction-clip"].includes(evidence.kind) && !evidence.nodeProofReceiptId) warnings.push(`${evidence.id} should reference a NodeProof receipt`); + } + for (const adoption of ledger.adoptions) { + requireRefs(adoption.id, [adoption.invariantId], ["nodekit.invariant-claim/v1"]); + requireRefs(adoption.id, adoption.evidenceIds, ["nodekit.evolution-evidence/v1"]); + if (adoption.status === "verified" && adoption.evidenceIds.length === 0) issues.push(`${adoption.id} verified adoption requires consumer-side evidence`); + const invariant = byId.get(adoption.invariantId); + if (invariant?.status === "superseded" && ["declared", "verified"].includes(adoption.status)) warnings.push(`${adoption.id} still adopts superseded invariant ${adoption.invariantId}`); + } + for (const invariant of ledger.invariants) { + const adopters = ledger.adoptions.filter((adoption) => adoption.invariantId === invariant.id && adoption.status === "verified"); + if (invariant.scope.applications?.length > adopters.length && invariant.status === "verified") warnings.push(`${invariant.id} scope names more applications than verified adoptions`); + } + return { + schemaVersion: "nodekit.evolution-verdict/v1", + counts: { events: ledger.events.length, assumptions: ledger.assumptions.length, invariants: ledger.invariants.length, evidence: ledger.evidence.length, adoptions: ledger.adoptions.length }, + issues: [...new Set(issues)], + warnings: [...new Set(warnings)], + passed: issues.length === 0, + }; +} + +export async function queryEvolutionLedger(repoRoot, { track, since, invariantId } = {}) { + const ledger = await readLedger(path.resolve(repoRoot)); + const events = ledger.events.filter((event) => (!track || event.track === track) && (!since || Date.parse(event.source.occurredAt) >= Date.parse(since)) && (!invariantId || event.invariantIds.includes(invariantId))); + return { + events, + assumptions: invariantId ? ledger.assumptions.filter((assumption) => events.some((event) => event.assumptionIds.includes(assumption.id))) : ledger.assumptions, + invariants: invariantId ? ledger.invariants.filter((invariant) => invariant.id === invariantId) : ledger.invariants, + evidence: ledger.evidence.filter((evidence) => !invariantId || evidence.verifiesInvariantIds.includes(invariantId)), + adoptions: ledger.adoptions.filter((adoption) => !invariantId || adoption.invariantId === invariantId), + }; +} + +export async function diffEvolutionLedger(repoRoot, from, to) { + const root = path.resolve(repoRoot); + if (!/^[a-f0-9]{7,40}$/.test(from) || !/^[a-f0-9]{7,40}$/.test(to)) throw new Error("evolution diff requires git commit identifiers"); + const commits = new Set(git(root, ["rev-list", `${from}..${to}`]).split(/\r?\n/).filter(Boolean)); + const ledger = await readLedger(root); + const events = ledger.events.filter((event) => commits.has(event.source.commitSha)); + return { schemaVersion: "nodekit.evolution-diff/v1", from, to, commits: commits.size, events }; +} + +const MATERIAL_PATHS = [ + /^(?:src|schemas|templates\/base|harness)\//, + /^(?:nodekit\.yaml|ownership\.yaml)$/, + /^\.github\/workflows\//, +]; + +export async function checkEvolutionMateriality(repoRoot, from, to) { + const root = path.resolve(repoRoot); + if (!/^[a-f0-9]{7,40}$/.test(from) || !/^[a-f0-9]{7,40}$/.test(to)) { + throw new Error("evolution materiality requires git commit identifiers"); + } + const changedFiles = git(root, ["diff", "--name-only", `${from}..${to}`]).split(/\r?\n/).filter(Boolean).map((file) => file.replaceAll("\\", "/")); + const materialFiles = changedFiles.filter((file) => MATERIAL_PATHS.some((pattern) => pattern.test(file))); + const diff = await diffEvolutionLedger(root, from, to); + const passed = materialFiles.length === 0 || diff.events.length > 0; + return { + schemaVersion: "nodekit.evolution-materiality-verdict/v1", + from, + to, + changedFiles, + materialFiles, + events: diff.events, + passed, + reason: passed + ? materialFiles.length === 0 ? "No material NodeKit surfaces changed." : "Material changes are linked to at least one human-reviewed evolution event." + : "Material NodeKit surfaces changed without a human-reviewed evolution event sourced from this commit range.", + }; +} + +export async function buildEvolutionDocs(repoRoot) { + const root = path.resolve(repoRoot); + const ledger = await readLedger(root); + const byEvidence = new Map(ledger.evidence.map((record) => [record.id, record])); + const byInvariant = new Map(ledger.invariants.map((record) => [record.id, record])); + const lines = ["# NodeKit Evolution Ledger", "", "Canonical JSON records remain authoritative. This projection explains why material system guarantees exist.", ""]; + for (const track of ["product", "architecture", "harness"]) { + lines.push(`## ${track[0].toUpperCase()}${track.slice(1)} evolution`, ""); + for (const event of ledger.events.filter((entry) => entry.track === track).sort((a, b) => a.source.occurredAt.localeCompare(b.source.occurredAt))) { + lines.push(`### ${event.challenge}`, "", `- Event: \`${event.id}\``, `- Source: \`${event.source.commitSha}\``, `- Resolution: ${event.resolution}`); + if (event.observedFailure) lines.push(`- Observed failure: ${event.observedFailure}`); + if (event.invariantIds.length) lines.push(`- Invariants: ${event.invariantIds.map((id) => `\`${id}\` (${byInvariant.get(id)?.status ?? "missing"})`).join(", ")}`); + lines.push(`- Evidence: ${event.evidenceIds.map((id) => `\`${id}\` (${byEvidence.get(id)?.result ?? "missing"})`).join(", ")}`); + if (event.knownLimitations.length) lines.push(`- Known limitations: ${event.knownLimitations.join("; ")}`); + lines.push(""); + } + } + const output = path.join(root, "evolution", "projections", "EVOLUTION.md"); + await mkdir(path.dirname(output), { recursive: true }); + await writeFile(output, `${lines.join("\n")}\n`); + const adoptionMap = ledger.adoptions.map((adoption) => ({ invariantId: adoption.invariantId, consumer: adoption.consumer, status: adoption.status, evidenceIds: adoption.evidenceIds })); + await writeFile(path.join(root, "evolution", "projections", "adoption-map.json"), `${JSON.stringify(adoptionMap, null, 2)}\n`); + return { adoptionMap, output }; +} + +export async function proposeEvolutionKnowledgePatch(repoRoot, { graphPath } = {}) { + const root = path.resolve(repoRoot); + const verdict = await verifyEvolutionLedger(root); + if (!verdict.passed) throw new Error(`evolution ledger must verify before graph projection:\n${verdict.issues.join("\n")}`); + const ledger = await readLedger(root); + const graph = await readKnowledgeGraph(root, { graphPath }); + const existing = new Set([...graph.nodes, ...graph.hyperedges].map((entity) => entity.id)); + const timestamp = now(); + const operations = []; + const evidenceNodeIds = new Map(); + for (const evidence of ledger.evidence) { + const id = `evolution:${evidence.id}`; + evidenceNodeIds.set(evidence.id, id); + if (!existing.has(id)) operations.push({ type: "INSERT", node: { id, kind: "evidence", label: evidence.id, layer: "source", confidence: evidence.result === "pass" ? 1 : 0.7, evidenceRefs: [], contentHash: evidence.sha256, sourceUri: evidence.artifactRef, capturedAt: evidence.generatedAt, metadata: { evolutionRecordId: evidence.id, sourceCommit: evidence.sourceCommit, result: evidence.result } } }); + } + const records = [...ledger.events, ...ledger.assumptions, ...ledger.invariants, ...ledger.adoptions]; + for (const record of records) { + const id = `evolution:${record.id}`; + if (existing.has(id)) continue; + const refs = record.evidenceIds ?? record.supportingEvidenceIds ?? (record.schemaVersion === "nodekit.invariant-claim/v1" ? ledger.evidence.filter((evidence) => evidence.verifiesInvariantIds.includes(record.id)).map((evidence) => evidence.id) : []); + const grounded = refs.map((ref) => evidenceNodeIds.get(ref)).filter(Boolean); + if (grounded.length === 0) continue; + operations.push({ type: "INSERT", node: { id, kind: record.schemaVersion.split("/")[0].replace("nodekit.", ""), label: record.statement ?? record.challenge ?? record.id, layer: record.schemaVersion === "nodekit.evolution-adoption/v1" ? "canonical" : "derived", confidence: record.status === "verified" ? 1 : 0.8, evidenceRefs: grounded, metadata: record } }); + } + for (const event of ledger.events) { + const participants = [event.id, ...event.assumptionIds, ...event.invariantIds].map((id, index) => ({ nodeId: `evolution:${id}`, role: index === 0 ? "event" : id.startsWith("asm") ? "challenged-assumption" : "introduced-invariant" })).filter((participant) => operations.some((operation) => operation.node?.id === participant.nodeId) || existing.has(participant.nodeId)); + const edgeId = `evolution:causal:${event.id}`; + if (participants.length >= 2 && !existing.has(edgeId)) operations.push({ type: "INSERT", hyperedge: { id: edgeId, predicate: "evolution-causal-chain", layer: "derived", participants, confidence: 1, evidenceRefs: event.evidenceIds.map((id) => evidenceNodeIds.get(id)).filter(Boolean), createdAt: timestamp } }); + } + if (operations.length === 0) throw new Error("evolution ledger has no new evidence-grounded records to propose"); + const patch = await proposeGraphPatch(root, { + graphId: graph.graphId, + baseVersion: graph.version, + operations, + evidenceRefs: [...evidenceNodeIds.values()], + contradictionRefs: [], + proposedBy: { agentId: "nodekit-evolution-ledger", modelRoute: "deterministic", resolvedModel: "none", harnessVersion: "evolution-v1" }, + confidence: 1, + }, { graphPath }); + return { patch, verdict }; +} diff --git a/src/lib/frontend-specialist.mjs b/src/lib/frontend-specialist.mjs new file mode 100644 index 0000000..afd3baf --- /dev/null +++ b/src/lib/frontend-specialist.mjs @@ -0,0 +1,258 @@ +import { createHash } from "node:crypto"; +import { mkdir, readFile, writeFile } from "node:fs/promises"; +import path from "node:path"; +import { stringify as stringifyYaml } from "yaml"; +import { pathExists, readJson, readYaml } from "./files.mjs"; +import { validateSchema } from "./schema-validation.mjs"; + +export const FRONTEND_ROUTE_SCHEMA = "nodekit.frontend-route/v1"; +export const PRODUCT_DESIGN_CONTRACT_SCHEMA = "nodekit.product-design-contract/v1"; + +export const FRONTEND_REQUIRED_VIEWS = Object.freeze([ + "desktop-first-arrival", + "desktop-active-workflow", + "desktop-proposal-review", + "mobile-primary-artifact", + "mobile-agent", + "mobile-review", +]); + +export const FRONTEND_EVALUATION_DIMENSIONS = Object.freeze([ + "primaryJobClarity", + "artifactDominance", + "workflowHierarchy", + "agentLegibility", + "reviewSafety", + "mobileOperation", + "domainAppropriateness", + "visualQuality", +]); + +export const FRONTEND_REQUIRED_STATES = Object.freeze([ + "first_arrival", + "loading", + "populated", + "exception", + "proposal", + "conflict", + "failed_safe", + "completed", + "mobile", +]); + +export const FRONTEND_REQUIRED_GUARDRAILS = Object.freeze([ + "generic_kpi_dashboard", + "accounting_schema_as_interface", + "decorative_chat", + "all_workflow_states_expanded", + "hidden_data_freshness", +]); + +function canonical(value) { + if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`; + if (value && typeof value === "object") return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${canonical(value[key])}`).join(",")}}`; + return JSON.stringify(value); +} + +function hash(value) { + return createHash("sha256").update(canonical(value)).digest("hex"); +} + +async function validateOrThrow(schema, value, label) { + const findings = await validateSchema(schema, value, label); + if (findings.length > 0) throw new Error(`${label} validation failed:\n${findings.join("\n")}`); +} + +function resolveInside(repoRoot, relative, label) { + const root = path.resolve(repoRoot); + const target = path.resolve(root, String(relative)); + const relation = path.relative(root, target); + if (relation === ".." || relation.startsWith(`..${path.sep}`) || path.isAbsolute(relation)) { + throw new Error(`${label} must stay inside the repository: ${relative}`); + } + return target; +} + +export async function initializeFrontendHarness(repoRoot) { + const root = path.resolve(repoRoot); + const frontendRoot = path.join(root, "harness", "frontend"); + const directories = ["product-packets", "design-directions", "anti-references", "benchmark-tasks", "receipts", "repairs"]; + for (const directory of directories) await mkdir(path.join(frontendRoot, directory), { recursive: true }); + const routePath = path.join(frontendRoot, "route.yaml"); + if (!(await pathExists(routePath))) { + const route = { + schemaVersion: FRONTEND_ROUTE_SCHEMA, + role: "frontend-architect", + status: "unprofiled", + selection: { strategy: "evidence_ranked", preferredRoute: null, fallbacks: [], evidenceRefs: [] }, + requiredCapabilities: ["repository-scale coding", "image input", "browser tool use", "responsive implementation", "screenshot-driven repair"], + authority: { sourceWrite: "proposal_workspace", proofGateWrite: "prohibited", productionCredentials: "prohibited", deployment: "approval_required" }, + budget: { maximumCostUsd: 8, maximumRepairRounds: 2 }, + expiry: { profiledAt: null, reprofileAfterDays: 30, invalidateOnModelRevision: true, invalidateOnToolSurfaceChange: true }, + }; + await writeFile(routePath, stringifyYaml(route), "utf8"); + } + return { frontendRoot, routePath, requiredViews: FRONTEND_REQUIRED_VIEWS }; +} + +export async function compileFrontendPlan(repoRoot, contractFile, routeFile = "harness/frontend/route.yaml") { + const root = path.resolve(repoRoot); + const contractPath = resolveInside(root, contractFile, "product contract"); + const routePath = resolveInside(root, routeFile, "frontend route"); + const contract = await readYaml(contractPath); + const route = await readYaml(routePath); + await validateOrThrow("nodekit.product-design-contract.v1.schema.json", contract, "product design contract"); + await validateOrThrow("nodekit.frontend-route.v1.schema.json", route, "frontend route"); + const missingStates = FRONTEND_REQUIRED_STATES.filter((state) => !contract.requiredStates.includes(state)); + if (missingStates.length) throw new Error(`product design contract is missing required states: ${missingStates.join(", ")}`); + const missingGuardrails = FRONTEND_REQUIRED_GUARDRAILS.filter((guardrail) => !contract.avoid.includes(guardrail)); + if (missingGuardrails.length) throw new Error(`product design contract is missing anti-pattern guardrails: ${missingGuardrails.join(", ")}`); + if (route.selection.preferredRoute && route.selection.evidenceRefs.length === 0) { + throw new Error("a preferred frontend route requires evidenceRefs; model reputation is not routing evidence"); + } + if (route.status === "evidence-ranked" && !route.selection.preferredRoute) { + throw new Error("an evidence-ranked route must identify its preferredRoute"); + } + const protectedContract = { + primaryUser: contract.product.targetUser, + primaryJob: contract.product.primaryJob, + primaryArtifact: contract.product.primaryArtifact, + journey: contract.journey, + protectedDecisions: contract.protectedDecisions, + }; + const plan = { + schemaVersion: "nodekit.frontend-plan/v1", + planId: `frontend-plan-${hash({ contract, route }).slice(0, 12)}`, + contractPath: path.relative(root, contractPath).replaceAll("\\", "/"), + routePath: path.relative(root, routePath).replaceAll("\\", "/"), + productContractHash: hash(contract), + routeHash: hash(route), + routeStatus: route.status, + requestedRoute: route.selection.preferredRoute, + resolvedRoute: null, + protectedContract, + requiredViews: FRONTEND_REQUIRED_VIEWS, + directionCount: 3, + finalVerdictAuthority: "nodeproof", + implementationAuthority: "proposal_workspace", + deploymentAuthorized: false, + }; + const output = path.join(root, "harness", "frontend", "receipts", `${plan.planId}.json`); + await mkdir(path.dirname(output), { recursive: true }); + await writeFile(output, `${JSON.stringify(plan, null, 2)}\n`); + return { contract, output, plan, route }; +} + +export async function createFrontendDirections(repoRoot, planFile) { + const root = path.resolve(repoRoot); + const planPath = resolveInside(root, planFile, "frontend plan"); + const plan = await readJson(planPath); + if (plan.schemaVersion !== "nodekit.frontend-plan/v1") throw new Error("frontend directions require a nodekit.frontend-plan/v1 receipt"); + const directionSet = { + schemaVersion: "nodekit.frontend-direction-set/v1", + directionSetId: `directions-${hash(plan).slice(0, 12)}`, + productContractHash: plan.productContractHash, + routeHash: plan.routeHash, + blind: true, + requiredViews: [...FRONTEND_REQUIRED_VIEWS], + candidates: [ + { candidateId: "direction-a", archetype: "collaborative-workspace", status: "planned", viewEvidence: {} }, + { candidateId: "direction-b", archetype: "artifact-studio", status: "planned", viewEvidence: {} }, + { candidateId: "direction-c", archetype: "domain-native", status: "planned", viewEvidence: {} }, + ], + }; + await validateOrThrow("nodekit.frontend-direction-set.v1.schema.json", directionSet, "frontend direction set"); + const output = path.join(root, "harness", "frontend", "design-directions", `${directionSet.directionSetId}.json`); + await mkdir(path.dirname(output), { recursive: true }); + await writeFile(output, `${JSON.stringify(directionSet, null, 2)}\n`); + return { directionSet, output }; +} + +function assertRenderedDirections(directionSet) { + for (const candidate of directionSet.candidates) { + if (!['rendered', 'evaluated'].includes(candidate.status)) throw new Error(`${candidate.candidateId} is not rendered`); + for (const view of directionSet.requiredViews) { + if (!candidate.viewEvidence[view]) throw new Error(`${candidate.candidateId} is missing ${view} evidence`); + } + } +} + +export async function evaluateFrontendTournament(repoRoot, benchmarkFile) { + const root = path.resolve(repoRoot); + const benchmarkPath = resolveInside(root, benchmarkFile, "frontend benchmark"); + const benchmark = await readJson(benchmarkPath); + await validateOrThrow("nodekit.frontend-benchmark.v1.schema.json", benchmark, "frontend benchmark"); + const directionPath = resolveInside(root, benchmark.directionSet, "direction set"); + const directionSet = await readJson(directionPath); + await validateOrThrow("nodekit.frontend-direction-set.v1.schema.json", directionSet, "frontend direction set"); + assertRenderedDirections(directionSet); + const candidateIds = directionSet.candidates.map((candidate) => candidate.candidateId); + if (Object.keys(benchmark.scores).length !== candidateIds.length || candidateIds.some((id) => !benchmark.scores[id])) { + throw new Error("frontend benchmark must score every direction exactly once"); + } + for (const result of benchmark.pairwiseResults) { + if (![result.left, result.right].includes(result.winner)) throw new Error("pairwise winner must be one of the compared candidates"); + if (!candidateIds.includes(result.left) || !candidateIds.includes(result.right)) throw new Error("pairwise result references an unknown direction"); + } + const wins = Object.fromEntries(candidateIds.map((id) => [id, 0])); + for (const result of benchmark.pairwiseResults) wins[result.winner] += 1; + const average = (id) => FRONTEND_EVALUATION_DIMENSIONS.reduce((sum, key) => sum + benchmark.scores[id][key], 0) / FRONTEND_EVALUATION_DIMENSIONS.length; + const ranked = [...candidateIds].sort((left, right) => wins[right] - wins[left] || average(right) - average(left) || left.localeCompare(right)); + const decisive = benchmark.browserChecksPassed && benchmark.accessibilityPassed && benchmark.overflowPassed && benchmark.majorFindings.length === 0; + const decision = { + schemaVersion: "nodekit.design-decision/v1", + decisionId: `design-decision-${hash({ benchmark, ranked }).slice(0, 12)}`, + directionSetId: directionSet.directionSetId, + candidateIds, + selectedCandidateId: ranked[0], + selectionReasons: [`pairwise_wins_${wins[ranked[0]]}`, `mean_score_${average(ranked[0]).toFixed(4)}`, "artifact_and_review_contract_preserved"], + criticReceiptIds: [...new Set(benchmark.pairwiseResults.flatMap((result) => result.evidenceRefs))], + humanDecision: false, + independentSelector: true, + protectedEvaluatorUnchanged: true, + decidedAt: new Date().toISOString(), + }; + await validateOrThrow("nodekit.design-decision.v1.schema.json", decision, "design decision"); + const output = path.join(root, "harness", "frontend", "receipts", `${decision.decisionId}.json`); + await writeFile(output, `${JSON.stringify({ ...decision, decisive, promotionAuthorized: false, freshUserEvidenceRefs: benchmark.freshUserEvidenceRefs }, null, 2)}\n`); + return { benchmark, decision, decisive, output, promotionAuthorized: false, ranked, wins }; +} + +export async function createFrontendRepairPlan(repoRoot, benchmarkFile) { + const root = path.resolve(repoRoot); + const benchmark = await readJson(resolveInside(root, benchmarkFile, "frontend benchmark")); + await validateOrThrow("nodekit.frontend-benchmark.v1.schema.json", benchmark, "frontend benchmark"); + const route = await readYaml(path.join(root, "harness", "frontend", "route.yaml")); + await validateOrThrow("nodekit.frontend-route.v1.schema.json", route, "frontend route"); + const repair = { + schemaVersion: "nodekit.frontend-repair/v1", + repairId: `frontend-repair-${hash(benchmark).slice(0, 12)}`, + benchmarkId: benchmark.benchmarkId, + maximumRounds: route.budget.maximumRepairRounds, + findings: benchmark.majorFindings, + writeScope: "proposal_workspace", + preservePriorImplementation: true, + proofGateMutable: false, + completionClaimAllowed: false, + }; + const output = path.join(root, "harness", "frontend", "repairs", `${repair.repairId}.json`); + await writeFile(output, `${JSON.stringify(repair, null, 2)}\n`); + return { output, repair }; +} + +export async function verifyFrontendCanary(repoRoot, receiptFile) { + const root = path.resolve(repoRoot); + const receipt = await readJson(resolveInside(root, receiptFile, "frontend canary")); + const checks = { + exactCandidate: typeof receipt.candidateId === "string" && receipt.candidateId.length > 0, + exactCommit: /^[a-f0-9]{40}$/.test(receipt.commitSha ?? ""), + requestedAndResolvedModel: Boolean(receipt.requestedRoute && receipt.resolvedProvider && receipt.resolvedModel), + freshIdentity: receipt.freshIdentity === true, + renderedJourney: receipt.renderedJourney === true, + screenshotsBound: Array.isArray(receipt.screenshotEvidenceRefs) && receipt.screenshotEvidenceRefs.length >= FRONTEND_REQUIRED_VIEWS.length, + nodeProofPassed: receipt.nodeProofPassed === true, + majorFindingsClosed: Array.isArray(receipt.majorFindings) && receipt.majorFindings.length === 0, + humanApproval: typeof receipt.approvedBy === "string" && receipt.approvedBy.length > 0, + }; + return { schemaVersion: "nodekit.frontend-canary-verdict/v1", checks, passed: Object.values(checks).every(Boolean), receipt }; +} diff --git a/src/lib/harness-gym.mjs b/src/lib/harness-gym.mjs new file mode 100644 index 0000000..9a06a3a --- /dev/null +++ b/src/lib/harness-gym.mjs @@ -0,0 +1,379 @@ +import { createHash } from "node:crypto"; +import { cp, mkdir, readFile, writeFile } from "node:fs/promises"; +import path from "node:path"; +import { stringify as stringifyYaml } from "yaml"; +import { compileModelIntelligence, diagnoseModelFailures } from "./model-intelligence.mjs"; +import { pathExists, readJson, readYaml } from "./files.mjs"; +import { validateSchema } from "./schema-validation.mjs"; + +function canonical(value) { + if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`; + if (value && typeof value === "object") return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${canonical(value[key])}`).join(",")}}`; + return JSON.stringify(value); +} + +function hash(value) { + return createHash("sha256").update(canonical(value)).digest("hex"); +} + +function slug(value) { + return String(value).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, ""); +} + +function candidateRoot(repoRoot, candidateId) { + if (!/^skill-candidate-[a-z0-9-]+$/.test(candidateId)) throw new Error(`invalid candidate id: ${candidateId}`); + return path.join(repoRoot, "harness", "candidates", candidateId); +} + +async function validateOrThrow(schema, value, label) { + const errors = await validateSchema(schema, value, label); + if (errors.length > 0) throw new Error(`${label} validation failed:\n${errors.join("\n")}`); +} + +function skillFromCluster(candidateId, cluster) { + const skillId = `guardrail.${slug(cluster.failureClass)}`; + return { + schemaVersion: "nodekit.skill/v1", + id: skillId, + version: 1, + kind: "guardrail", + triggers: { + taskFamilies: cluster.taskFamilies, + failureClasses: [cluster.failureClass], + models: [cluster.model], + }, + inputs: ["task", "artifact", "execution_trace"], + requiredTools: [], + procedure: [ + `Check for ${cluster.failureClass} before reporting completion`, + "Inspect the cited tool results and artifact evidence", + "Return a bounded proposal or typed failure instead of mutating canonical state", + ], + constraints: [ + "Do not modify protected tasks, decisive judges, or proof thresholds", + "Do not claim success when a completion assertion is unsupported", + ], + completionChecks: [ + `${cluster.failureClass} is absent from the evaluated result`, + "Evidence references and canonical-state protections remain intact", + ], + failureBehavior: [ + `Emit ${cluster.failureClass} with evidence when the guardrail cannot recover`, + "Preserve the previous canonical artifact and request bounded review", + ], + positiveExamples: [`Candidate ${candidateId} inspects evidence before producing a reviewable proposal`], + negativeExamples: [cluster.failureClass], + expectedToolTraces: ["artifact inspection before completion"], + testFixtures: cluster.taskIds, + evidenceRefs: cluster.evidenceRefs, + }; +} + +export async function proposeSkillCandidates(repoRoot) { + const compiled = await compileModelIntelligence(repoRoot, { write: false }); + const clusters = diagnoseModelFailures(compiled.observations).filter((cluster) => cluster.skillCandidateEligible); + const proposed = []; + for (const cluster of clusters) { + const candidateId = `skill-candidate-${slug(cluster.failureClass)}-${hash({ model: cluster.model, taskIds: cluster.taskIds }).slice(0, 10)}`; + const root = candidateRoot(repoRoot, candidateId); + const skill = skillFromCluster(candidateId, cluster); + const candidate = { + schemaVersion: "nodekit.skill-candidate/v1", + candidateId, + status: "proposed", + hypothesis: `A focused ${cluster.failureClass} guardrail will reduce the repeated failure without changing the model, tools, protected tasks, or decisive evaluator.`, + expectedImpact: `Reduce ${cluster.failureClass} on ${cluster.taskFamilies.join(", ")} while holding safety, correctness, editability, export, cost, and latency within the protected comparison thresholds.`, + risks: ["The guardrail may over-constrain valid behavior", "Additional inspection may increase latency or cost"], + sourceCluster: { + failureClass: cluster.failureClass, + probableCause: cluster.probableCause, + model: cluster.model, + count: cluster.count, + taskIds: cluster.taskIds, + taskFamilies: cluster.taskFamilies, + evidenceRefs: cluster.evidenceRefs, + }, + skillFile: "skill.yaml", + protectedBenchmarkHash: compiled.resolved.benchmarkHash, + createdFromEvidence: true, + }; + await validateOrThrow("nodekit.skill.v1.schema.json", skill, "generated skill"); + await validateOrThrow("nodekit.skill-candidate.v1.schema.json", candidate, "generated skill candidate"); + await mkdir(root, { recursive: true }); + if (!(await pathExists(path.join(root, "candidate.json")))) { + await writeFile(path.join(root, "candidate.json"), `${JSON.stringify(candidate, null, 2)}\n`); + await writeFile(path.join(root, "skill.yaml"), stringifyYaml(skill)); + await writeFile(path.join(root, "hypothesis.yaml"), stringifyYaml({ schemaVersion: "nodekit.skill-hypothesis/v1", hypothesis: candidate.hypothesis, evidenceRefs: cluster.evidenceRefs })); + await writeFile(path.join(root, "expected-impact.yaml"), stringifyYaml({ schemaVersion: "nodekit.expected-impact/v1", expectedImpact: candidate.expectedImpact })); + await writeFile(path.join(root, "risks.yaml"), stringifyYaml({ schemaVersion: "nodekit.candidate-risks/v1", risks: candidate.risks })); + } else { + Object.assign(candidate, await readJson(path.join(root, "candidate.json"))); + Object.assign(skill, await readYaml(path.join(root, candidate.skillFile))); + } + proposed.push({ candidate, skill, root }); + } + return { applicationId: compiled.harness.applicationId, benchmarkHash: compiled.resolved.benchmarkHash, candidates: proposed }; +} + +export async function reviewSkillCandidate(repoRoot, candidateId) { + const root = candidateRoot(repoRoot, candidateId); + const candidate = await readJson(path.join(root, "candidate.json")); + const skill = await readYaml(path.join(root, candidate.skillFile)); + await validateOrThrow("nodekit.skill-candidate.v1.schema.json", candidate, "skill candidate"); + await validateOrThrow("nodekit.skill.v1.schema.json", skill, "skill"); + return { candidate, skill, candidateHash: hash(candidate), skillHash: hash(skill), root }; +} + +function acceptableIncrease(candidate, baseline) { + if (baseline === 0) return candidate === 0; + return candidate <= baseline * 1.25; +} + +export async function benchmarkSkillCandidate(repoRoot, candidateId, comparisonPath) { + const reviewed = await reviewSkillCandidate(repoRoot, candidateId); + const compiled = await compileModelIntelligence(repoRoot, { write: false }); + const comparison = await readJson(path.resolve(comparisonPath)); + await validateOrThrow("nodekit.skill-comparison.v1.schema.json", comparison, "skill comparison"); + if (comparison.candidateId !== candidateId) throw new Error("comparison candidateId does not match requested candidate"); + if (comparison.benchmarkHash !== reviewed.candidate.protectedBenchmarkHash || comparison.benchmarkHash !== compiled.resolved.benchmarkHash) { + throw new Error("protected benchmark hash changed during skill comparison"); + } + if (comparison.harnessHash !== compiled.resolved.harnessHash) throw new Error("harness hash changed during skill comparison"); + const nonRegression = { + accuracy: comparison.candidate.accuracy >= comparison.baseline.accuracy, + safety: comparison.candidate.safety >= comparison.baseline.safety, + editability: comparison.candidate.editability >= comparison.baseline.editability, + exportQuality: comparison.candidate.exportQuality >= comparison.baseline.exportQuality, + userCompletion: comparison.candidate.userCompletion >= comparison.baseline.userCompletion, + latency: acceptableIncrease(comparison.candidate.medianLatencyMs, comparison.baseline.medianLatencyMs), + cost: acceptableIncrease(comparison.candidate.costPerSuccessUsd, comparison.baseline.costPerSuccessUsd), + }; + const meaningfulImprovement = comparison.candidate.successRate > comparison.baseline.successRate + && comparison.candidate.targetFailureRate < comparison.baseline.targetFailureRate; + const passed = meaningfulImprovement && Object.values(nonRegression).every(Boolean); + const verdict = { + schemaVersion: "nodekit.skill-benchmark-verdict/v1", + candidateId, + benchmarkHash: comparison.benchmarkHash, + harnessHash: comparison.harnessHash, + meaningfulImprovement, + nonRegression, + passed, + comparisonHash: hash(comparison), + }; + reviewed.candidate.status = passed ? "benchmark-passed" : "benchmark-failed"; + await writeFile(path.join(reviewed.root, "candidate.json"), `${JSON.stringify(reviewed.candidate, null, 2)}\n`); + await cp(path.resolve(comparisonPath), path.join(reviewed.root, "comparison.json")); + await writeFile(path.join(reviewed.root, "benchmark-verdict.json"), `${JSON.stringify(verdict, null, 2)}\n`); + return verdict; +} + +export async function evaluateTournament(repoRoot, manifestPath) { + const tournament = await readJson(path.resolve(manifestPath)); + await validateOrThrow("nodekit.tournament.v1.schema.json", tournament, "tournament"); + const compiled = await compileModelIntelligence(repoRoot, { write: false }); + if (tournament.benchmarkHash !== compiled.resolved.benchmarkHash || tournament.harnessHash !== compiled.resolved.harnessHash) { + throw new Error("tournament is not bound to the current protected benchmark and harness"); + } + const wins = Object.fromEntries(tournament.candidates.map((candidate) => [candidate, 0])); + for (const result of tournament.pairwiseResults) { + if (!tournament.candidates.includes(result.left) || !tournament.candidates.includes(result.right)) throw new Error("pairwise result references an unknown candidate"); + if (![result.left, result.right].includes(result.winner)) throw new Error("pairwise winner must be one of the compared candidates"); + if ([result.left, result.right].includes(result.criticId)) throw new Error("candidate cannot serve as its own decisive critic"); + wins[result.winner] += 1; + } + const ranked = Object.entries(wins).sort((left, right) => right[1] - left[1] || left[0].localeCompare(right[0])); + const decisive = ranked.length > 1 && ranked[0][1] > ranked[1][1]; + const verdict = { + schemaVersion: "nodekit.tournament-verdict/v1", + tournamentId: tournament.tournamentId, + tournamentHash: hash(tournament), + wins, + winner: decisive ? ranked[0][0] : null, + decisive, + promotionAuthorized: false, + }; + const root = path.join(repoRoot, "harness", "tournaments", tournament.tournamentId); + await mkdir(root, { recursive: true }); + await cp(path.resolve(manifestPath), path.join(root, "manifest.json")); + await writeFile(path.join(root, "verdict.json"), `${JSON.stringify(verdict, null, 2)}\n`); + return verdict; +} + +const scopePriority = { project: 3, domain: 2, ecosystem: 1 }; +const confidencePriority = { high: 3, medium: 2, low: 1 }; + +export async function compileRoutingPolicy(repoRoot) { + const compiled = await compileModelIntelligence(repoRoot, { write: false }); + const cards = compiled.cards.filter((card) => card.status !== "expired"); + const byFamily = new Map(); + for (const card of cards) { + for (const family of card.scope.taskFamilies) { + const entries = byFamily.get(family) ?? []; + entries.push(card); + byFamily.set(family, entries); + } + } + const routes = [...byFamily.entries()].sort(([left], [right]) => left.localeCompare(right)).map(([taskFamily, entries]) => { + const sorted = entries.sort((left, right) => + scopePriority[right.scope.level] - scopePriority[left.scope.level] + || confidencePriority[right.confidence.level] - confidencePriority[left.confidence.level] + || (right.metrics.firstPassAcceptance ?? 0) - (left.metrics.firstPassAcceptance ?? 0) + || left.model.resolvedModel.localeCompare(right.model.resolvedModel)); + return { + taskFamily, + requiredCapabilities: [...new Set(sorted.flatMap((card) => card.strengths))].sort(), + candidates: sorted.map((card, index) => ({ + requestedRoute: card.model.requestedRoute, + resolvedProvider: card.model.resolvedProvider, + resolvedModel: card.model.resolvedModel, + scope: card.scope.level, + confidence: card.confidence.level, + priority: index + 1, + roleSkills: card.bestRoles, + domainSkills: [], + modelAdapters: [], + guardrails: card.requiredScaffolding, + evidenceRefs: card.evidenceRefs, + })), + fallback: { type: "deterministic", skill: "nodekit.unprofiled-safe-fallback" }, + completion: { requireProposal: true, requireArtifactInspection: true, directMutation: false }, + }; + }); + const policy = { + schemaVersion: "nodekit.routing-policy/v1", + applicationId: compiled.harness.applicationId, + status: "provisional", + evidencePrecedence: ["project", "domain", "ecosystem", "unprofiled-fallback"], + routes, + automaticPromotion: false, + compiledFrom: { + harnessHash: compiled.resolved.harnessHash, + benchmarkHash: compiled.resolved.benchmarkHash, + cardHashes: cards.map(hash).sort(), + }, + }; + await validateOrThrow("nodekit.routing-policy.v1.schema.json", policy, "routing policy"); + const root = path.join(repoRoot, ".nodekit", "harness"); + await mkdir(root, { recursive: true }); + await writeFile(path.join(root, "resolved-routing-policy.json"), `${JSON.stringify(policy, null, 2)}\n`); + await writeFile(path.join(root, "routing-decision-receipt.json"), `${JSON.stringify({ + schemaVersion: "nodekit.routing-decision-receipt/v1", + applicationId: policy.applicationId, + policyHash: hash(policy), + routeCount: policy.routes.length, + evidencePrecedence: policy.evidencePrecedence, + provisional: true, + promotionAuthorized: false, + }, null, 2)}\n`); + return policy; +} + +export async function verifyCanary(repoRoot, canaryPath) { + const canary = await readJson(path.resolve(canaryPath)); + await validateOrThrow("nodekit.canary-receipt.v1.schema.json", canary, "canary receipt"); + const output = path.join(repoRoot, "harness", "receipts", "canaries", `${canary.canaryId}.json`); + await mkdir(path.dirname(output), { recursive: true }); + await cp(path.resolve(canaryPath), output); + return canary; +} + +function skillDirectory(kind) { + if (kind === "role") return "roles"; + if (kind === "domain" || kind === "domain-role") return "domains"; + if (kind === "model-adapter") return "models"; + if (kind === "recovery") return "recovery"; + return "guardrails"; +} + +export async function promoteSkillCandidate(repoRoot, candidateId, { canaryPath, proofPath, approvedBy }) { + if (!approvedBy) throw new Error("explicit approvedBy identity is required; automatic promotion is prohibited"); + const reviewed = await reviewSkillCandidate(repoRoot, candidateId); + const verdict = await readJson(path.join(reviewed.root, "benchmark-verdict.json")); + if (verdict.passed !== true) throw new Error("skill benchmark has not passed"); + const canary = await verifyCanary(repoRoot, canaryPath); + if (canary.candidateId !== candidateId) throw new Error("canary candidateId mismatch"); + const proof = await readJson(path.resolve(proofPath)); + if (proof.schemaVersion !== "nodeproof.integrity-receipt/v1" || proof.passed !== true || proof.integrityVerified !== true || proof.candidateId !== candidateId) { + throw new Error("a matching verified NodeProof integrity receipt is required"); + } + const currentPath = path.join(repoRoot, "harness", "versions", "current.json"); + const current = await readJson(currentPath); + const previousManifest = await readJson(path.join(repoRoot, "harness", "versions", current.version, "manifest.json")); + const nextNumber = Number(String(current.version).replace(/^h/, "")) + 1; + const nextVersion = `h${nextNumber}`; + const destination = path.join(repoRoot, "harness", "skills", skillDirectory(reviewed.skill.kind), `${reviewed.skill.id}.yaml`); + await mkdir(path.dirname(destination), { recursive: true }); + await writeFile(destination, stringifyYaml(reviewed.skill)); + const activeSkills = [...new Set([...(previousManifest.activeSkills ?? []), path.relative(repoRoot, destination).replaceAll("\\", "/")])].sort(); + const promotion = { + schemaVersion: "nodekit.promotion-receipt/v1", + promotionId: `promotion-${candidateId}-${nextVersion}`, + candidateId, + kind: "skill", + benchmarkVerdictRef: path.relative(repoRoot, path.join(reviewed.root, "benchmark-verdict.json")).replaceAll("\\", "/"), + canaryReceiptRef: path.relative(repoRoot, path.resolve(canaryPath)).replaceAll("\\", "/"), + nodeProofReceiptRef: path.relative(repoRoot, path.resolve(proofPath)).replaceAll("\\", "/"), + nodeProofVerified: true, + rollbackVersion: current.version, + automatic: false, + approvedBy, + promotedHash: reviewed.skillHash, + }; + await validateOrThrow("nodekit.promotion-receipt.v1.schema.json", promotion, "promotion receipt"); + const versionRoot = path.join(repoRoot, "harness", "versions", nextVersion); + await mkdir(versionRoot, { recursive: true }); + await writeFile(path.join(versionRoot, "manifest.json"), `${JSON.stringify({ schemaVersion: "nodekit.harness-version/v1", version: nextVersion, previousVersion: current.version, status: "promoted", activeSkills, promotionReceiptId: promotion.promotionId }, null, 2)}\n`); + await writeFile(currentPath, `${JSON.stringify({ schemaVersion: "nodekit.harness-current/v1", version: nextVersion }, null, 2)}\n`); + const receiptPath = path.join(repoRoot, "harness", "receipts", "skill-promotions", `${promotion.promotionId}.json`); + await writeFile(receiptPath, `${JSON.stringify(promotion, null, 2)}\n`); + reviewed.candidate.status = "promoted"; + await writeFile(path.join(reviewed.root, "candidate.json"), `${JSON.stringify(reviewed.candidate, null, 2)}\n`); + const compiledRoot = path.join(repoRoot, ".nodekit", "harness"); + await mkdir(compiledRoot, { recursive: true }); + await writeFile(path.join(compiledRoot, "resolved-skill-stack.json"), `${JSON.stringify({ schemaVersion: "nodekit.resolved-skill-stack/v1", harnessVersion: nextVersion, activeSkills, promotionReceiptId: promotion.promotionId }, null, 2)}\n`); + return { promotion, nextVersion, receiptPath }; +} + +export async function rejectSkillCandidate(repoRoot, candidateId, reason) { + if (!String(reason ?? "").trim()) throw new Error("rejection reason is required"); + const reviewed = await reviewSkillCandidate(repoRoot, candidateId); + reviewed.candidate.status = "rejected"; + await writeFile(path.join(reviewed.root, "candidate.json"), `${JSON.stringify(reviewed.candidate, null, 2)}\n`); + const verdict = { schemaVersion: "nodekit.skill-rejection/v1", candidateId, reason: String(reason).trim(), candidateHash: reviewed.candidateHash }; + await writeFile(path.join(reviewed.root, "rejection.json"), `${JSON.stringify(verdict, null, 2)}\n`); + return verdict; +} + +export async function rollbackHarness(repoRoot) { + const currentPath = path.join(repoRoot, "harness", "versions", "current.json"); + const current = await readJson(currentPath); + const manifest = await readJson(path.join(repoRoot, "harness", "versions", current.version, "manifest.json")); + if (!manifest.previousVersion) throw new Error(`${current.version} has no previous version to roll back to`); + const previous = await readJson(path.join(repoRoot, "harness", "versions", manifest.previousVersion, "manifest.json")); + await writeFile(currentPath, `${JSON.stringify({ schemaVersion: "nodekit.harness-current/v1", version: manifest.previousVersion }, null, 2)}\n`); + const compiledRoot = path.join(repoRoot, ".nodekit", "harness"); + await mkdir(compiledRoot, { recursive: true }); + await writeFile(path.join(compiledRoot, "resolved-skill-stack.json"), `${JSON.stringify({ schemaVersion: "nodekit.resolved-skill-stack/v1", harnessVersion: manifest.previousVersion, activeSkills: previous.activeSkills ?? [], rolledBackFrom: current.version }, null, 2)}\n`); + const receipt = { schemaVersion: "nodekit.harness-rollback/v1", from: current.version, to: manifest.previousVersion, preservedVersions: true }; + await writeFile(path.join(repoRoot, "harness", "receipts", `rollback-${current.version}-to-${manifest.previousVersion}.json`), `${JSON.stringify(receipt, null, 2)}\n`); + return receipt; +} + +export async function harnessStatus(repoRoot) { + const compiled = await compileModelIntelligence(repoRoot, { write: false }); + const current = await readJson(path.join(repoRoot, "harness", "versions", "current.json")); + const proposed = await proposeSkillCandidates(repoRoot); + return { + schemaVersion: "nodekit.harness-status/v1", + applicationId: compiled.harness.applicationId, + version: current.version, + harnessHash: compiled.resolved.harnessHash, + benchmarkHash: compiled.resolved.benchmarkHash, + observations: compiled.observations.length, + capabilityCards: compiled.cards.length, + skillCandidates: proposed.candidates.map(({ candidate }) => ({ id: candidate.candidateId, status: candidate.status })), + routingCertified: false, + automaticPromotion: false, + }; +} diff --git a/src/lib/harness-knowledge.mjs b/src/lib/harness-knowledge.mjs new file mode 100644 index 0000000..880d4a5 --- /dev/null +++ b/src/lib/harness-knowledge.mjs @@ -0,0 +1,106 @@ +import { createHash } from "node:crypto"; +import { compileModelIntelligence } from "./model-intelligence.mjs"; +import { proposeGraphPatch, readKnowledgeGraph } from "./knowledge-evolution.mjs"; + +function canonical(value) { + if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`; + if (value && typeof value === "object") return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${canonical(value[key])}`).join(",")}}`; + return JSON.stringify(value); +} + +function hash(value) { + return createHash("sha256").update(canonical(value)).digest("hex"); +} + +function safe(value) { + return String(value).toLowerCase().replace(/[^a-z0-9_.:/-]+/g, "-").replace(/-+/g, "-"); +} + +export async function proposeHarnessKnowledgePatch(repoRoot, { + graphPath, + agentId = "nodekit:harness-knowledge-compiler", +} = {}) { + const graph = await readKnowledgeGraph(repoRoot, { graphPath }); + const compiled = await compileModelIntelligence(repoRoot, { write: false }); + const existing = new Set([...graph.nodes, ...graph.hyperedges].map((entity) => entity.id)); + const operations = []; + const evidenceRefs = []; + + function insertNode(node) { + if (existing.has(node.id)) return; + existing.add(node.id); + operations.push({ type: "INSERT", node }); + } + + function insertHyperedge(hyperedge) { + if (existing.has(hyperedge.id)) return; + existing.add(hyperedge.id); + operations.push({ type: "INSERT", hyperedge }); + } + + for (const observation of compiled.observations) { + const observationHash = hash(observation); + const evidenceId = `evidence:model-observation:${safe(observation.runId)}@${observationHash.slice(0, 12)}`; + evidenceRefs.push(evidenceId); + insertNode({ + id: evidenceId, + kind: "evidence", + label: `Evaluated model observation ${observation.runId}`, + layer: "source", + confidence: 1, + evidenceRefs: [], + contentHash: observationHash, + sourceUri: `nodekit:model-observation:${observation.runId}`, + capturedAt: observation.observedAt ?? new Date(0).toISOString(), + properties: { proofReceiptId: observation.proofReceiptId, evidenceRefs: observation.evidenceRefs }, + }); + + const taskId = `task:${safe(observation.applicationId)}:${safe(observation.taskId)}`; + const modelId = `model:${safe(observation.model.resolvedProvider)}:${safe(observation.model.resolvedModel)}`; + const harnessId = `harness:${safe(observation.applicationId)}:${safe(observation.harness.version)}@${observation.harness.hash.slice(0, 12)}`; + insertNode({ id: taskId, kind: "task", label: `${observation.taskFamily}: ${observation.taskId}`, layer: "derived", confidence: 1, evidenceRefs: [evidenceId], properties: { applicationId: observation.applicationId, taskFamily: observation.taskFamily } }); + insertNode({ id: modelId, kind: "model", label: `${observation.model.resolvedProvider}/${observation.model.resolvedModel}`, layer: "derived", confidence: 1, evidenceRefs: [evidenceId], properties: observation.model }); + insertNode({ id: harnessId, kind: "harness", label: `Harness ${observation.harness.version}`, layer: "derived", confidence: 1, evidenceRefs: [evidenceId], properties: observation.harness }); + + const participants = [ + { nodeId: evidenceId, role: "observation" }, + { nodeId: taskId, role: "task" }, + { nodeId: modelId, role: "model" }, + { nodeId: harnessId, role: "harness" }, + ]; + for (const failure of observation.failures) { + const failureId = `failure:${safe(observation.applicationId)}:${safe(failure.failureId)}`; + insertNode({ id: failureId, kind: "failure", label: failure.failureClass, layer: "derived", confidence: 1, evidenceRefs: [evidenceId], properties: failure }); + participants.push({ nodeId: failureId, role: "failure" }); + } + insertHyperedge({ + id: `hyperedge:model-run:${safe(observation.runId)}@${observationHash.slice(0, 12)}`, + predicate: "model-performed-task-under-harness", + layer: "derived", + participants, + confidence: 1, + evidenceRefs: [evidenceId], + properties: { + cognitive: observation.cognitive, + execution: observation.execution, + artifact: observation.artifact, + efficiency: observation.efficiency, + }, + }); + } + + if (operations.length === 0) return { unchanged: true, patch: null, observationCount: compiled.observations.length }; + const patch = await proposeGraphPatch(repoRoot, { + operations, + evidenceRefs, + contradictionRefs: [], + proposedBy: { + agentId, + modelRoute: "deterministic-compiler", + resolvedModel: "none", + harnessVersion: compiled.harness.version, + }, + confidence: 1, + }, { graphPath }); + return { unchanged: false, patch, observationCount: compiled.observations.length }; +} diff --git a/src/lib/knowledge-evolution.mjs b/src/lib/knowledge-evolution.mjs new file mode 100644 index 0000000..396b9db --- /dev/null +++ b/src/lib/knowledge-evolution.mjs @@ -0,0 +1,670 @@ +import { createHash } from "node:crypto"; +import { mkdir, readFile, writeFile } from "node:fs/promises"; +import path from "node:path"; +import { normalizePath, pathExists } from "./files.mjs"; + +export const KNOWLEDGE_GRAPH_SCHEMA = "nodekit.knowledge-graph/v1"; +export const KNOWLEDGE_PATCH_SCHEMA = "nodekit.graph-patch/v1"; +export const KNOWLEDGE_ACTION_SCHEMA = "nodekit.knowledge-action-receipt/v1"; +export const KNOWLEDGE_STATE_SCHEMA = "nodekit.knowledge-state/v1"; + +export const KNOWLEDGE_LAYERS = Object.freeze([ + "source", + "derived", + "working", + "proposal", + "canonical", + "hypothesis", +]); + +const DEFAULT_GRAPH_PATH = ".nodeagent/knowledge/graph.json"; +const PATCH_STATUSES = new Set(["pending", "accepted", "rejected", "conflicted", "applied"]); +const OPERATION_TYPES = new Set(["INSERT", "UPDATE", "DEPRECATE"]); + +function canonical(value) { + if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`; + if (value && typeof value === "object") { + return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${canonical(value[key])}`).join(",")}}`; + } + return JSON.stringify(value); +} + +function digest(value) { + return createHash("sha256").update(canonical(value)).digest("hex"); +} + +function now() { + return new Date().toISOString(); +} + +function nonEmpty(value) { + return typeof value === "string" && value.trim().length > 0; +} + +function clone(value) { + return structuredClone(value); +} + +function containedPath(repoRoot, candidate = DEFAULT_GRAPH_PATH) { + const root = path.resolve(repoRoot); + const absolute = path.resolve(root, String(candidate)); + const relative = path.relative(root, absolute); + if (relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) { + throw new Error(`knowledge graph path must stay inside the repository: ${candidate}`); + } + return { absolute, relative: normalizePath(relative) }; +} + +function graphPayload(graph) { + const copy = clone(graph); + delete copy.contentHash; + return copy; +} + +function graphHash(graph) { + return digest(graphPayload(graph)); +} + +export function createMemoryKnowledgeGraphAdapter(initialGraph) { + let current = initialGraph ? clone(initialGraph) : null; + return { + id: "memory", + capabilities: { + atomicCompareAndSwap: true, + durable: false, + nativeHypergraphTraversal: false, + reactiveSubscriptions: false, + }, + async readGraph() { + if (!current) throw new Error("memory knowledge graph is not initialized"); + return clone(current); + }, + async initializeGraph(graph) { + if (current) return clone(current); + const errors = validateKnowledgeGraphDocument(graph); + if (errors.length) throw new Error(`memory knowledge graph validation failed:\n${errors.join("\n")}`); + current = clone(graph); + return clone(current); + }, + async compareAndSwap(expectedVersion, nextGraph) { + if (!current) throw new Error("memory knowledge graph is not initialized"); + if (current.version !== expectedVersion) return { applied: false, actualVersion: current.version }; + const errors = validateKnowledgeGraphDocument(nextGraph); + if (errors.length) throw new Error(`memory knowledge graph validation failed:\n${errors.join("\n")}`); + current = clone(nextGraph); + return { applied: true, actualVersion: current.version, graph: clone(current) }; + }, + async exportDocument() { + if (!current) throw new Error("memory knowledge graph is not initialized"); + return `${JSON.stringify(current, null, 2)}\n`; + }, + }; +} + +export function createFileKnowledgeGraphAdapter(repoRoot, { graphPath } = {}) { + return { + id: "file", + capabilities: { + atomicCompareAndSwap: false, + durable: true, + nativeHypergraphTraversal: false, + reactiveSubscriptions: false, + }, + readGraph: () => readKnowledgeGraph(repoRoot, { graphPath }), + initializeGraph: (options = {}) => initializeKnowledgeGraph(repoRoot, { ...options, graphPath }), + async compareAndSwap(expectedVersion, nextGraph) { + const current = await readKnowledgeGraph(repoRoot, { graphPath }); + if (current.version !== expectedVersion) return { applied: false, actualVersion: current.version }; + await persistKnowledgeGraph(repoRoot, clone(nextGraph), { graphPath }); + return { applied: true, actualVersion: nextGraph.version, graph: clone(nextGraph) }; + }, + async exportDocument() { + return `${JSON.stringify(await readKnowledgeGraph(repoRoot, { graphPath }), null, 2)}\n`; + }, + }; +} + +function canonicalStateHash(graph) { + return digest({ + graphId: graph.graphId, + version: graph.version, + nodes: graph.nodes, + hyperedges: graph.hyperedges, + }); +} + +function entityMap(graph) { + return new Map([...graph.nodes, ...graph.hyperedges].map((entity) => [entity.id, entity])); +} + +function patchIdFor(patch) { + return `patch_${digest({ + baseVersion: patch.baseVersion, + contradictionRefs: patch.contradictionRefs ?? [], + evidenceRefs: patch.evidenceRefs ?? [], + graphId: patch.graphId, + operations: patch.operations, + proposedBy: patch.proposedBy, + }).slice(0, 20)}`; +} + +function validateEvidenceNode(node, label) { + const errors = []; + if (!nonEmpty(node.contentHash) || !/^[a-f0-9]{64}$/.test(node.contentHash)) { + errors.push(`${label} evidence anchor requires a sha256 contentHash`); + } + if (!nonEmpty(node.sourceUri)) errors.push(`${label} evidence anchor requires sourceUri`); + if (!nonEmpty(node.capturedAt) || Number.isNaN(Date.parse(node.capturedAt))) { + errors.push(`${label} evidence anchor requires capturedAt`); + } + if (node.region) { + for (const field of ["x", "y", "width", "height"]) { + if (!Number.isFinite(node.region[field]) || node.region[field] < 0) errors.push(`${label} region.${field} must be non-negative`); + } + } + if (node.timeRange && (!Number.isFinite(node.timeRange.startMs) || !Number.isFinite(node.timeRange.endMs) || node.timeRange.startMs < 0 || node.timeRange.endMs < node.timeRange.startMs)) { + errors.push(`${label} timeRange must be ordered non-negative milliseconds`); + } + return errors; +} + +function validateNode(node, label = "node") { + const errors = []; + if (!node || typeof node !== "object" || Array.isArray(node)) return [`${label} must be an object`]; + if (!nonEmpty(node.id)) errors.push(`${label}.id is required`); + if (!nonEmpty(node.kind)) errors.push(`${label}.kind is required`); + if (!nonEmpty(node.label)) errors.push(`${label}.label is required`); + if (!KNOWLEDGE_LAYERS.includes(node.layer)) errors.push(`${label}.layer is invalid`); + if (!Number.isFinite(node.confidence) || node.confidence < 0 || node.confidence > 1) errors.push(`${label}.confidence must be between 0 and 1`); + if (!Array.isArray(node.evidenceRefs)) errors.push(`${label}.evidenceRefs must be an array`); + if (node.kind === "evidence") errors.push(...validateEvidenceNode(node, label)); + return errors; +} + +function validateHyperedge(edge, label = "hyperedge") { + const errors = []; + if (!edge || typeof edge !== "object" || Array.isArray(edge)) return [`${label} must be an object`]; + if (!nonEmpty(edge.id)) errors.push(`${label}.id is required`); + if (!nonEmpty(edge.predicate)) errors.push(`${label}.predicate is required`); + if (!KNOWLEDGE_LAYERS.includes(edge.layer)) errors.push(`${label}.layer is invalid`); + if (!Array.isArray(edge.participants) || edge.participants.length < 2) errors.push(`${label}.participants requires at least two entries`); + for (const [index, participant] of (edge.participants ?? []).entries()) { + if (!nonEmpty(participant?.nodeId) || !nonEmpty(participant?.role)) errors.push(`${label}.participants[${index}] requires nodeId and role`); + } + if (!Number.isFinite(edge.confidence) || edge.confidence < 0 || edge.confidence > 1) errors.push(`${label}.confidence must be between 0 and 1`); + if (!Array.isArray(edge.evidenceRefs)) errors.push(`${label}.evidenceRefs must be an array`); + return errors; +} + +export function validateKnowledgeGraphDocument(graph) { + const errors = []; + if (!graph || typeof graph !== "object" || Array.isArray(graph)) return ["knowledge graph must be an object"]; + if (graph.schemaVersion !== KNOWLEDGE_GRAPH_SCHEMA) errors.push(`schemaVersion must be ${KNOWLEDGE_GRAPH_SCHEMA}`); + if (!nonEmpty(graph.graphId)) errors.push("graphId is required"); + if (!Number.isInteger(graph.version) || graph.version < 0) errors.push("version must be a non-negative integer"); + for (const collection of ["nodes", "hyperedges", "proposals", "actionReceipts", "evolutionReceipts"]) { + if (!Array.isArray(graph[collection])) errors.push(`${collection} must be an array`); + } + if (!graph.authority || graph.authority.canonicalMutation !== "accepted-patch-only") { + errors.push("authority.canonicalMutation must be accepted-patch-only"); + } + const ids = new Set(); + for (const [index, node] of (graph.nodes ?? []).entries()) { + errors.push(...validateNode(node, `nodes[${index}]`)); + if (ids.has(node?.id)) errors.push(`duplicate entity id ${node.id}`); + ids.add(node?.id); + } + for (const [index, edge] of (graph.hyperedges ?? []).entries()) { + errors.push(...validateHyperedge(edge, `hyperedges[${index}]`)); + if (ids.has(edge?.id)) errors.push(`duplicate entity id ${edge.id}`); + ids.add(edge?.id); + } + for (const [index, edge] of (graph.hyperedges ?? []).entries()) { + for (const participant of edge.participants ?? []) if (!ids.has(participant.nodeId)) errors.push(`hyperedges[${index}] references missing node ${participant.nodeId}`); + } + if (nonEmpty(graph.contentHash) && graph.contentHash !== graphHash(graph)) errors.push("contentHash does not match graph content"); + return errors; +} + +export async function initializeKnowledgeGraph(repoRoot, { + graphId = `knowledge:${path.basename(path.resolve(repoRoot))}`, + graphPath, + write = true, +} = {}) { + const resolved = containedPath(repoRoot, graphPath); + if (await pathExists(resolved.absolute)) return readKnowledgeGraph(repoRoot, { graphPath }); + const createdAt = now(); + const graph = { + schemaVersion: KNOWLEDGE_GRAPH_SCHEMA, + graphId, + version: 0, + authority: { + canonicalMutation: "accepted-patch-only", + destructiveDelete: false, + oneAuthoritativeGraph: true, + }, + layers: KNOWLEDGE_LAYERS.map((id) => ({ id, writableThrough: id === "source" ? "ingest-proposal" : "graph-patch" })), + nodes: [], + hyperedges: [], + proposals: [], + actionReceipts: [], + evolutionReceipts: [], + genesis: { createdAt, graphId }, + createdAt, + updatedAt: createdAt, + }; + graph.contentHash = graphHash(graph); + if (write) { + await mkdir(path.dirname(resolved.absolute), { recursive: true }); + await writeFile(resolved.absolute, `${JSON.stringify(graph, null, 2)}\n`, "utf8"); + } + return graph; +} + +export async function readKnowledgeGraph(repoRoot, { graphPath } = {}) { + const resolved = containedPath(repoRoot, graphPath); + if (!(await pathExists(resolved.absolute))) throw new Error(`knowledge graph is missing: ${resolved.relative}; run nodekit graph init`); + let graph; + try { + graph = JSON.parse(await readFile(resolved.absolute, "utf8")); + } catch (error) { + throw new Error(`knowledge graph is invalid JSON: ${resolved.relative}: ${error.message}`); + } + const errors = validateKnowledgeGraphDocument(graph); + if (errors.length) throw new Error(`knowledge graph validation failed:\n${errors.join("\n")}`); + return graph; +} + +async function persistKnowledgeGraph(repoRoot, graph, { graphPath } = {}) { + graph.updatedAt = now(); + graph.contentHash = graphHash(graph); + const resolved = containedPath(repoRoot, graphPath); + await mkdir(path.dirname(resolved.absolute), { recursive: true }); + await writeFile(resolved.absolute, `${JSON.stringify(graph, null, 2)}\n`, "utf8"); + return graph; +} + +export function validateGraphPatchShape(patch) { + const errors = []; + if (!patch || typeof patch !== "object" || Array.isArray(patch)) return ["graph patch must be an object"]; + if (patch.schemaVersion !== KNOWLEDGE_PATCH_SCHEMA) errors.push(`schemaVersion must be ${KNOWLEDGE_PATCH_SCHEMA}`); + if (!nonEmpty(patch.patchId)) errors.push("patchId is required"); + if (!nonEmpty(patch.graphId)) errors.push("graphId is required"); + if (!Number.isInteger(patch.baseVersion) || patch.baseVersion < 0) errors.push("baseVersion must be a non-negative integer"); + if (!Array.isArray(patch.operations) || patch.operations.length === 0) errors.push("operations must be non-empty"); + if (!Array.isArray(patch.evidenceRefs)) errors.push("evidenceRefs must be an array"); + if (!Array.isArray(patch.contradictionRefs)) errors.push("contradictionRefs must be an array"); + if (!patch.proposedBy || !["agentId", "modelRoute", "resolvedModel", "harnessVersion"].every((key) => nonEmpty(patch.proposedBy[key]))) { + errors.push("proposedBy requires agentId, modelRoute, resolvedModel, and harnessVersion"); + } + if (!Number.isFinite(patch.confidence) || patch.confidence < 0 || patch.confidence > 1) errors.push("confidence must be between 0 and 1"); + if (!PATCH_STATUSES.has(patch.status)) errors.push("status is invalid"); + for (const [index, operation] of (patch.operations ?? []).entries()) { + if (!OPERATION_TYPES.has(operation?.type)) errors.push(`operations[${index}].type is invalid`); + if (operation?.type === "INSERT") { + if (Boolean(operation.node) === Boolean(operation.hyperedge)) errors.push(`operations[${index}] INSERT requires exactly one node or hyperedge`); + if (operation.node) errors.push(...validateNode(operation.node, `operations[${index}].node`)); + if (operation.hyperedge) errors.push(...validateHyperedge(operation.hyperedge, `operations[${index}].hyperedge`)); + } + if (operation?.type === "UPDATE" && (!nonEmpty(operation.targetId) || !operation.patch || typeof operation.patch !== "object" || Array.isArray(operation.patch))) { + errors.push(`operations[${index}] UPDATE requires targetId and object patch`); + } + if (operation?.type === "DEPRECATE" && (!nonEmpty(operation.targetId) || !nonEmpty(operation.reason))) { + errors.push(`operations[${index}] DEPRECATE requires targetId and reason`); + } + } + return errors; +} + +export async function proposeGraphPatch(repoRoot, input, { graphPath, write = true } = {}) { + const graph = await readKnowledgeGraph(repoRoot, { graphPath }); + const patch = { + schemaVersion: KNOWLEDGE_PATCH_SCHEMA, + graphId: input.graphId ?? graph.graphId, + baseVersion: input.baseVersion ?? graph.version, + operations: clone(input.operations ?? []), + evidenceRefs: [...new Set(input.evidenceRefs ?? [])], + contradictionRefs: [...new Set(input.contradictionRefs ?? [])], + proposedBy: clone(input.proposedBy ?? {}), + confidence: input.confidence ?? 0, + validation: { + sourceGrounded: false, + schemaValid: false, + authorityValid: false, + conflictFree: false, + freshnessValid: false, + errors: [], + }, + status: "pending", + proposedAt: input.proposedAt ?? now(), + }; + patch.patchId = input.patchId ?? patchIdFor(patch); + const errors = validateGraphPatchShape(patch); + if (errors.length) throw new Error(`graph patch proposal is invalid:\n${errors.join("\n")}`); + if (graph.proposals.some((entry) => entry.patchId === patch.patchId)) throw new Error(`graph patch already exists: ${patch.patchId}`); + graph.proposals.push(patch); + if (write) await persistKnowledgeGraph(repoRoot, graph, { graphPath }); + return patch; +} + +function evidenceRefsForOperation(operation, patch) { + const entity = operation.node ?? operation.hyperedge; + return [...new Set([...(patch.evidenceRefs ?? []), ...(entity?.evidenceRefs ?? []), ...(operation.evidenceRefs ?? [])])]; +} + +function validatePatchAgainstGraph(graph, patch) { + const errors = validateGraphPatchShape(patch); + const entities = entityMap(graph); + const inserts = new Map(); + for (const operation of patch.operations) { + if (operation.type === "INSERT") { + const entity = operation.node ?? operation.hyperedge; + if (entities.has(entity.id) || inserts.has(entity.id)) errors.push(`INSERT target already exists: ${entity.id}`); + inserts.set(entity.id, entity); + } + } + const available = new Map([...entities, ...inserts]); + for (const [index, operation] of patch.operations.entries()) { + if (operation.type === "INSERT" && operation.hyperedge) { + for (const participant of operation.hyperedge.participants) if (!available.has(participant.nodeId)) errors.push(`operations[${index}] references missing participant ${participant.nodeId}`); + } + if (["UPDATE", "DEPRECATE"].includes(operation.type)) { + const target = entities.get(operation.targetId); + if (!target) errors.push(`${operation.type} target does not exist: ${operation.targetId}`); + if (target?.layer === "source") errors.push(`${operation.type} cannot mutate immutable source entity ${operation.targetId}`); + if (operation.type === "UPDATE" && ("id" in operation.patch || "createdAt" in operation.patch)) errors.push(`UPDATE cannot change identity fields for ${operation.targetId}`); + } + const entity = operation.node ?? operation.hyperedge; + const refs = evidenceRefsForOperation(operation, patch); + const selfGroundedSource = operation.type === "INSERT" && entity?.kind === "evidence" && validateEvidenceNode(entity, entity.id).length === 0; + const refsGrounded = refs.length > 0 && refs.every((reference) => { + const evidence = available.get(reference); + return evidence?.kind === "evidence" && evidence.layer === "source" && !evidence.deprecatedAt; + }); + if (!selfGroundedSource && !refsGrounded) errors.push(`operations[${index}] is not grounded in immutable source evidence`); + } + for (const reference of patch.contradictionRefs) if (!available.has(reference)) errors.push(`contradiction reference does not exist: ${reference}`); + const freshnessErrors = [...available.values()] + .filter((entity) => evidenceRefsForOperation({ node: entity }, patch).includes(entity.id)) + .filter((entity) => entity.freshness?.expiresAt && Date.parse(entity.freshness.expiresAt) <= Date.now()) + .map((entity) => `evidence is stale: ${entity.id}`); + errors.push(...freshnessErrors); + const shapeErrors = validateGraphPatchShape(patch); + return { + sourceGrounded: !errors.some((entry) => entry.includes("grounded") || entry.includes("evidence is stale")), + schemaValid: shapeErrors.length === 0, + authorityValid: patch.graphId === graph.graphId && graph.authority.canonicalMutation === "accepted-patch-only", + conflictFree: patch.baseVersion === graph.version && !errors.some((entry) => entry.includes("already exists") || entry.includes("does not exist")), + freshnessValid: freshnessErrors.length === 0, + errors: [...new Set(errors)], + }; +} + +export async function validateGraphPatch(repoRoot, patchId, { graphPath, write = true } = {}) { + const graph = await readKnowledgeGraph(repoRoot, { graphPath }); + const patch = graph.proposals.find((entry) => entry.patchId === patchId); + if (!patch) throw new Error(`graph patch not found: ${patchId}`); + if (["rejected", "applied"].includes(patch.status)) throw new Error(`graph patch cannot be validated from status ${patch.status}`); + patch.validation = validatePatchAgainstGraph(graph, patch); + patch.validatedAt = now(); + if (patch.baseVersion !== graph.version) patch.status = "conflicted"; + else if (patch.status === "conflicted") patch.status = "pending"; + if (write) await persistKnowledgeGraph(repoRoot, graph, { graphPath }); + return clone(patch); +} + +export async function decideGraphPatch(repoRoot, patchId, { + decision, + principalId, + reason, + graphPath, +} = {}) { + if (!nonEmpty(principalId)) throw new Error("graph patch decision requires principalId"); + if (!["accept", "reject"].includes(decision)) throw new Error("graph patch decision must be accept or reject"); + const graph = await readKnowledgeGraph(repoRoot, { graphPath }); + const patch = graph.proposals.find((entry) => entry.patchId === patchId); + if (!patch) throw new Error(`graph patch not found: ${patchId}`); + if (patch.status !== "pending") throw new Error(`graph patch decision requires pending status, got ${patch.status}`); + patch.validation = validatePatchAgainstGraph(graph, patch); + if (decision === "accept" && (!Object.entries(patch.validation).filter(([key]) => key !== "errors").every(([, value]) => value) || patch.validation.errors.length)) { + throw new Error(`graph patch failed validation:\n${patch.validation.errors.join("\n")}`); + } + patch.status = decision === "accept" ? "accepted" : "rejected"; + patch.decision = { decision, principalId, reason: String(reason ?? ""), decidedAt: now() }; + await persistKnowledgeGraph(repoRoot, graph, { graphPath }); + return clone(patch); +} + +function applyOperation(graph, operation, timestamp) { + if (operation.type === "INSERT") { + const entity = clone(operation.node ?? operation.hyperedge); + entity.createdAt ??= timestamp; + entity.updatedAt ??= timestamp; + if (operation.node) graph.nodes.push(entity); + else graph.hyperedges.push(entity); + return; + } + const collections = [graph.nodes, graph.hyperedges]; + const collection = collections.find((entries) => entries.some((entry) => entry.id === operation.targetId)); + const index = collection.findIndex((entry) => entry.id === operation.targetId); + if (operation.type === "UPDATE") { + collection[index] = { ...collection[index], ...clone(operation.patch), updatedAt: timestamp }; + return; + } + collection[index] = { + ...collection[index], + deprecatedAt: timestamp, + deprecationReason: operation.reason, + updatedAt: timestamp, + }; +} + +export async function applyGraphPatch(repoRoot, patchId, { graphPath } = {}) { + const graph = await readKnowledgeGraph(repoRoot, { graphPath }); + const patch = graph.proposals.find((entry) => entry.patchId === patchId); + if (!patch) throw new Error(`graph patch not found: ${patchId}`); + if (patch.status !== "accepted") throw new Error(`only accepted graph patches can apply; got ${patch.status}`); + if (patch.baseVersion !== graph.version) { + patch.status = "conflicted"; + await persistKnowledgeGraph(repoRoot, graph, { graphPath }); + throw new Error(`stale graph patch baseVersion ${patch.baseVersion}; canonical version is ${graph.version}`); + } + const validation = validatePatchAgainstGraph(graph, patch); + if (validation.errors.length) throw new Error(`accepted graph patch no longer validates:\n${validation.errors.join("\n")}`); + const beforeHash = canonicalStateHash(graph); + const appliedAt = now(); + for (const operation of patch.operations) applyOperation(graph, operation, appliedAt); + const fromVersion = graph.version; + graph.version += 1; + patch.status = "applied"; + patch.appliedAt = appliedAt; + const receipt = { + schemaVersion: "nodekit.graph-evolution-receipt/v1", + receiptId: `graph_receipt_${digest({ patchId, fromVersion, appliedAt }).slice(0, 20)}`, + graphId: graph.graphId, + patchId, + fromVersion, + toVersion: graph.version, + beforeHash, + afterHash: canonicalStateHash(graph), + operations: clone(patch.operations), + evidenceRefs: clone(patch.evidenceRefs), + decision: clone(patch.decision), + appliedAt, + }; + graph.evolutionReceipts.push(receipt); + await persistKnowledgeGraph(repoRoot, graph, { graphPath }); + return { graph: clone(graph), receipt: clone(receipt) }; +} + +export async function recordKnowledgeAction(repoRoot, action, { graphPath } = {}) { + const graph = await readKnowledgeGraph(repoRoot, { graphPath }); + const allowed = new Set(["GRAPH_RETRIEVE", "EXTERNAL_RESEARCH", "PROPOSE_GRAPH_PATCH", "INSPECT_ARTIFACT", "EXECUTE_TOOL", "REQUEST_APPROVAL", "COMPLETE", "ABSTAIN"]); + if (!allowed.has(action?.type)) throw new Error(`unsupported knowledge action: ${action?.type}`); + const receipt = { + schemaVersion: KNOWLEDGE_ACTION_SCHEMA, + receiptId: action.receiptId ?? `knowledge_action_${digest({ action, graphVersion: graph.version, at: now() }).slice(0, 20)}`, + graphId: graph.graphId, + graphVersion: graph.version, + runId: String(action.runId ?? ""), + caseId: String(action.caseId ?? ""), + actorId: String(action.actorId ?? ""), + type: action.type, + input: clone(action.input ?? {}), + outputRefs: [...new Set(action.outputRefs ?? [])], + evidenceRefs: [...new Set(action.evidenceRefs ?? [])], + budget: clone(action.budget ?? {}), + status: action.status ?? "completed", + occurredAt: action.occurredAt ?? now(), + }; + graph.actionReceipts.push(receipt); + await persistKnowledgeGraph(repoRoot, graph, { graphPath }); + return receipt; +} + +function normalizeTerms(query) { + return String(query ?? "").toLowerCase().split(/[^a-z0-9_/-]+/).filter((term) => term.length > 1); +} + +function entityScore(entity, terms) { + if (!terms.length) return 1; + const text = canonical({ + id: entity.id, + kind: entity.kind, + label: entity.label, + predicate: entity.predicate, + properties: entity.properties, + participants: entity.participants, + }).toLowerCase(); + return terms.reduce((score, term) => score + (text.includes(term) ? (String(entity.label ?? entity.predicate ?? "").toLowerCase().includes(term) ? 8 : 2) : 0), 0); +} + +export function queryKnowledgeGraph(graph, query, { limit = 12, layers = ["canonical", "working", "source", "hypothesis", "derived"] } = {}) { + const terms = normalizeTerms(query); + const allowedLayers = new Set(layers); + const entities = [...graph.nodes, ...graph.hyperedges] + .filter((entity) => allowedLayers.has(entity.layer) && !entity.deprecatedAt) + .map((entity) => ({ entity, score: entityScore(entity, terms) })) + .filter((entry) => entry.score > 0) + .sort((left, right) => right.score - left.score || left.entity.id.localeCompare(right.entity.id)) + .slice(0, Math.max(1, Math.min(Number(limit) || 12, 100))); + const selectedIds = new Set(entities.map((entry) => entry.entity.id)); + const supportingHyperedges = graph.hyperedges.filter((edge) => !edge.deprecatedAt && edge.participants.some((participant) => selectedIds.has(participant.nodeId))); + return { + schemaVersion: "nodekit.knowledge-query/v1", + graphId: graph.graphId, + graphVersion: graph.version, + query: String(query ?? ""), + results: entities, + supportingHyperedges, + }; +} + +export function inspectKnowledgeGaps(graph, { at = Date.now() } = {}) { + const unresolved = graph.nodes.filter((node) => !node.deprecatedAt && ["question", "contradiction", "gap"].includes(node.kind)); + const unsupported = graph.nodes.filter((node) => !node.deprecatedAt && node.kind !== "evidence" && node.layer !== "hypothesis" && (node.evidenceRefs?.length ?? 0) === 0); + const staleEvidence = graph.nodes.filter((node) => node.kind === "evidence" && node.freshness?.expiresAt && Date.parse(node.freshness.expiresAt) <= at); + const pendingPatches = graph.proposals.filter((patch) => ["pending", "conflicted"].includes(patch.status)); + return { + schemaVersion: "nodekit.knowledge-gaps/v1", + graphId: graph.graphId, + graphVersion: graph.version, + pendingPatches, + staleEvidence, + unresolved, + unsupported, + }; +} + +export function diffKnowledgeGraph(graph, fromVersion, toVersion = graph.version) { + const from = Number(fromVersion); + const to = Number(toVersion); + if (!Number.isInteger(from) || !Number.isInteger(to) || from < 0 || to < from || to > graph.version) throw new Error(`invalid graph diff range ${fromVersion}..${toVersion}`); + const receipts = graph.evolutionReceipts.filter((receipt) => receipt.fromVersion >= from && receipt.toVersion <= to); + return { + schemaVersion: "nodekit.knowledge-diff/v1", + graphId: graph.graphId, + fromVersion: from, + toVersion: to, + patchIds: receipts.map((receipt) => receipt.patchId), + operations: receipts.flatMap((receipt) => receipt.operations.map((operation) => ({ patchId: receipt.patchId, ...operation }))), + receipts: receipts.map(({ operations, ...receipt }) => receipt), + }; +} + +export function replayKnowledgeGraph(graph, targetVersion = graph.version) { + const target = Number(targetVersion); + if (!Number.isInteger(target) || target < 0 || target > graph.version) throw new Error(`invalid replay target version ${targetVersion}`); + const replayed = { + ...clone(graph), + version: 0, + nodes: [], + hyperedges: [], + proposals: [], + actionReceipts: graph.actionReceipts.filter((receipt) => receipt.graphVersion <= target), + evolutionReceipts: [], + contentHash: "", + }; + for (const receipt of graph.evolutionReceipts.filter((entry) => entry.toVersion <= target).sort((a, b) => a.toVersion - b.toVersion)) { + for (const operation of receipt.operations) applyOperation(replayed, operation, receipt.appliedAt); + replayed.version = receipt.toVersion; + replayed.evolutionReceipts.push(clone(receipt)); + } + replayed.proposals = graph.proposals.filter((patch) => patch.status === "applied" && patch.appliedAt && graph.evolutionReceipts.some((receipt) => receipt.patchId === patch.patchId && receipt.toVersion <= target)); + replayed.contentHash = graphHash(replayed); + return replayed; +} + +export function benchmarkKnowledgeRetrieval(graph, cases, { limit = 8 } = {}) { + if (!Array.isArray(cases) || cases.length === 0) throw new Error("knowledge benchmark requires non-empty cases"); + const profiles = { + flat: KNOWLEDGE_LAYERS, + staticGraph: ["source", "canonical"], + evolvingGraph: ["source", "derived", "working", "canonical", "hypothesis"], + }; + const results = {}; + for (const [profile, layers] of Object.entries(profiles)) { + const evaluations = cases.map((entry) => { + const output = queryKnowledgeGraph(graph, entry.query, { limit, layers }); + const returned = new Set(output.results.map((result) => result.entity.id)); + const expected = [...new Set(entry.expectedEntityIds ?? [])]; + const hits = expected.filter((id) => returned.has(id)); + return { caseId: entry.caseId, expected, returned: [...returned], recall: expected.length ? hits.length / expected.length : 1 }; + }); + results[profile] = { + averageRecall: evaluations.reduce((sum, entry) => sum + entry.recall, 0) / evaluations.length, + cases: evaluations, + }; + } + return { + schemaVersion: "nodekit.knowledge-benchmark/v1", + graphId: graph.graphId, + graphVersion: graph.version, + ablations: { + insert: graph.evolutionReceipts.some((receipt) => receipt.operations.some((operation) => operation.type === "INSERT")), + update: graph.evolutionReceipts.some((receipt) => receipt.operations.some((operation) => operation.type === "UPDATE")), + deprecate: graph.evolutionReceipts.some((receipt) => receipt.operations.some((operation) => operation.type === "DEPRECATE")), + externalResearch: graph.actionReceipts.some((receipt) => receipt.type === "EXTERNAL_RESEARCH"), + }, + results, + }; +} + +export function createKnowledgeState(graph, input = {}) { + return { + schemaVersion: KNOWLEDGE_STATE_SCHEMA, + caseId: String(input.caseId ?? ""), + runId: String(input.runId ?? ""), + goal: String(input.goal ?? ""), + graphId: graph.graphId, + graphVersion: graph.version, + selectedSubgraphIds: [...new Set(input.selectedSubgraphIds ?? [])], + actionHistory: clone(input.actionHistory ?? graph.actionReceipts), + currentArtifactIds: [...new Set(input.currentArtifactIds ?? [])], + currentTaskIds: [...new Set(input.currentTaskIds ?? [])], + unresolvedQuestionIds: graph.nodes.filter((node) => node.kind === "question" && !node.deprecatedAt).map((node) => node.id), + contradictionIds: graph.nodes.filter((node) => node.kind === "contradiction" && !node.deprecatedAt).map((node) => node.id), + permissions: clone(input.permissions ?? { retrieve: true, propose: true, apply: false }), + budget: clone(input.budget ?? { maximumGraphEdits: 10, maximumSearches: 3, maximumToolCalls: 20, maximumTokens: 20_000 }), + }; +} diff --git a/src/lib/model-intelligence.mjs b/src/lib/model-intelligence.mjs new file mode 100644 index 0000000..ea52d6f --- /dev/null +++ b/src/lib/model-intelligence.mjs @@ -0,0 +1,369 @@ +import { createHash } from "node:crypto"; +import { mkdir, readFile, readdir, writeFile } from "node:fs/promises"; +import path from "node:path"; +import { stringify as stringifyYaml } from "yaml"; +import { pathExists, readYaml } from "./files.mjs"; +import { initializeFrontendHarness } from "./frontend-specialist.mjs"; +import { validateSchema } from "./schema-validation.mjs"; + +export const MODEL_FAILURE_CLASSES = Object.freeze([ + "BRIEF_MISS", + "PRODUCT_TOPOLOGY_MISS", + "GENERIC_DASHBOARD_FALLBACK", + "GENERIC_FALLBACK", + "OVERPLANNING", + "UNDERPLANNING", + "WRONG_PRIMITIVE", + "LAYOUT_REPETITION", + "TEXT_DENSITY", + "TOOL_AVOIDANCE", + "TOOL_MISUSE", + "NO_RESULT_INSPECTION", + "FALSE_COMPLETION", + "WEAK_REPAIR", + "CONTEXT_DRIFT", + "ORCHESTRATION_FAILURE", + "UNSUPPORTED_CLAIM", + "COST_INEFFICIENCY", + "REPOSITORY_LEGIBILITY_FAILURE", + "REFERENCE_DISCOVERY_FAILURE", + "UI_OPERABILITY_FAILURE", + "MOBILE_TOPOLOGY_FAILURE", + "BACKSTAGE_COMPLEXITY_LEAK", + "AUTHORITY_VIOLATION", + "STALE_WRITE_FAILURE", + "EXPORT_FAILURE", + "RECOVERY_FAILURE", +]); + +export const MODEL_FAILURE_CAUSES = Object.freeze([ + "model", + "prompt", + "skill", + "tool", + "context", + "reference", + "renderer", + "validator", + "runtime", + "product contract", + "unknown", +]); + +const HARNESS_DIRECTORIES = [ + "models/cards/ecosystem", + "models/cards/domain", + "models/cards/project", + "models/comparisons", + "skills/roles", + "skills/domains", + "skills/models", + "skills/guardrails", + "skills/recovery", + "tasks/development", + "tasks/validation", + "tasks/heldout", + "tasks/adversarial", + "tournaments", + "candidates", + "versions/h0", + "receipts/capability-cards", + "receipts/skill-promotions", + "receipts/routing-promotions", + "receipts/canaries", +]; + +function canonical(value) { + if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`; + if (value && typeof value === "object") { + return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${canonical(value[key])}`).join(",")}}`; + } + return JSON.stringify(value); +} + +function hash(value) { + return createHash("sha256").update(canonical(value)).digest("hex"); +} + +function resolveWithin(repoRoot, relative, label) { + const root = path.resolve(repoRoot); + const target = path.resolve(root, String(relative)); + const relation = path.relative(root, target); + if (relation === ".." || relation.startsWith(`..${path.sep}`) || path.isAbsolute(relation)) { + throw new Error(`${label} must stay within the repository: ${relative}`); + } + return target; +} + +async function writeIfMissing(target, content, created) { + if (await pathExists(target)) return; + await mkdir(path.dirname(target), { recursive: true }); + await writeFile(target, content, "utf8"); + created.push(target); +} + +async function applicationIdFor(repoRoot) { + const manifest = await readYaml(path.join(repoRoot, "nodeagent.yaml")); + const applicationId = manifest?.application?.id; + if (!applicationId) throw new Error("nodeagent.yaml must declare application.id before harness init"); + return applicationId; +} + +export async function initializeHarness(repoRoot) { + const resolvedRoot = path.resolve(repoRoot); + if (!(await pathExists(path.join(resolvedRoot, "nodekit.yaml")))) { + throw new Error("harness init requires a NodeKit repository; run nodekit create or nodekit adopt first"); + } + const applicationId = await applicationIdFor(resolvedRoot); + const harnessRoot = path.join(resolvedRoot, "harness"); + const created = []; + for (const relative of HARNESS_DIRECTORIES) await mkdir(path.join(harnessRoot, relative), { recursive: true }); + await mkdir(path.join(resolvedRoot, ".qa", "models", "observations"), { recursive: true }); + + const harness = { + schemaVersion: "nodekit.harness/v1", + applicationId, + version: "h0", + evaluation: { + protectedTaskRoots: ["harness/tasks/validation", "harness/tasks/heldout", "harness/tasks/adversarial"], + candidateMayModify: ["harness/skills", "harness/models/routing-matrix.yaml"], + }, + modelIntelligence: { + observationRoot: ".qa/models/observations", + cardRoot: "harness/models/cards", + findingsLedger: "harness/models/findings.jsonl", + }, + promotion: { automatic: false, freshAgentCanary: true, proofReceipt: true }, + }; + + await writeIfMissing(path.join(harnessRoot, "harness.yaml"), stringifyYaml(harness), created); + await writeIfMissing( + path.join(harnessRoot, "HARNESS.md"), + "# NodeKit Harness Gym\n\nThis directory is application-specific and evidence driven. Record requested and resolved model identities, keep protected tasks outside candidate write scope, and never promote a skill or route without controlled comparison, a fresh-agent canary, and NodeProof receipt.\n\n`nodekit models baseline` validates observations and compiles an evidence registry. It does not call a provider or claim that an unobserved model is capable.\n", + created, + ); + await writeIfMissing(path.join(harnessRoot, "models", "findings.jsonl"), "", created); + await writeIfMissing( + path.join(harnessRoot, "models", "routing-matrix.yaml"), + stringifyYaml({ schemaVersion: "nodekit.routing-matrix/v0", status: "provisional-empty", routes: [] }), + created, + ); + await writeIfMissing( + path.join(harnessRoot, "versions", "h0", "manifest.json"), + `${JSON.stringify({ schemaVersion: "nodekit.harness-version/v1", version: "h0", status: "baseline-unmeasured", activeSkills: [] }, null, 2)}\n`, + created, + ); + await writeIfMissing( + path.join(harnessRoot, "versions", "current.json"), + `${JSON.stringify({ schemaVersion: "nodekit.harness-current/v1", version: "h0" }, null, 2)}\n`, + created, + ); + await writeIfMissing( + path.join(resolvedRoot, ".qa", "models", "README.md"), + "# Model observations\n\nPlace evaluated `nodekit.model-observation/v1` JSON or JSONL records in `observations/`. Raw observations are evidence, not routing policy.\n", + created, + ); + for (const taskClass of ["development", "validation", "heldout", "adversarial"]) { + await writeIfMissing( + path.join(harnessRoot, "tasks", taskClass, "index.json"), + `${JSON.stringify({ schemaVersion: "nodekit.task-index/v1", taskClass, protected: taskClass !== "development", tasks: [] }, null, 2)}\n`, + created, + ); + } + + const frontend = await initializeFrontendHarness(resolvedRoot); + return { + applicationId, + created: created.map((file) => path.relative(resolvedRoot, file).replaceAll("\\", "/")), + frontend, + harnessRoot, + }; +} + +async function filesUnder(root, extensions) { + if (!(await pathExists(root))) return []; + const files = []; + async function visit(directory) { + const entries = await readdir(directory, { withFileTypes: true }); + entries.sort((left, right) => left.name.localeCompare(right.name)); + for (const entry of entries) { + const target = path.join(directory, entry.name); + if (entry.isDirectory()) await visit(target); + else if (extensions.has(path.extname(entry.name).toLowerCase())) files.push(target); + } + } + await visit(root); + return files; +} + +async function recordsFrom(file) { + const content = await readFile(file, "utf8"); + if (path.extname(file).toLowerCase() === ".jsonl") { + return content.split(/\r?\n/).map((line) => line.trim()).filter(Boolean).map((line, index) => { + try { + return JSON.parse(line); + } catch (error) { + throw new Error(`${file}:${index + 1} is not valid JSON: ${error.message}`); + } + }); + } + if ([".yaml", ".yml"].includes(path.extname(file).toLowerCase())) return [await readYaml(file)]; + return [JSON.parse(content)]; +} + +async function loadValidatedRecords(root, extensions, schema, label) { + const records = []; + const errors = []; + for (const file of await filesUnder(root, extensions)) { + for (const [index, value] of (await recordsFrom(file)).entries()) { + const recordLabel = `${label} ${file}${path.extname(file) === ".jsonl" ? `:${index + 1}` : ""}`; + const findings = await validateSchema(schema, value, recordLabel); + if (findings.length > 0) errors.push(...findings); + else records.push({ file, value }); + } + } + return { errors, records }; +} + +export async function compileModelIntelligence(repoRoot, { write = true } = {}) { + const resolvedRoot = path.resolve(repoRoot); + const harnessPath = path.join(resolvedRoot, "harness", "harness.yaml"); + if (!(await pathExists(harnessPath))) throw new Error("Harness Gym is not initialized; run nodekit harness init"); + const harness = await readYaml(harnessPath); + const errors = await validateSchema("nodekit.harness.v1.schema.json", harness, "harness"); + const observations = await loadValidatedRecords( + resolveWithin(resolvedRoot, harness.modelIntelligence?.observationRoot ?? ".qa/models/observations", "observationRoot"), + new Set([".json", ".jsonl"]), + "nodekit.model-observation.v1.schema.json", + "observation", + ); + const cards = await loadValidatedRecords( + resolveWithin(resolvedRoot, harness.modelIntelligence?.cardRoot ?? "harness/models/cards", "cardRoot"), + new Set([".json", ".yaml", ".yml"]), + "nodekit.model-capability-card.v1.schema.json", + "capability card", + ); + errors.push(...observations.errors, ...cards.errors); + if (errors.length > 0) throw new Error(`model intelligence validation failed:\n${errors.join("\n")}`); + + const observationValues = observations.records.map((entry) => entry.value); + const cardValues = cards.records.map((entry) => entry.value); + for (const card of cardValues.filter((entry) => entry.scope.level === "project")) { + if (card.scope.applicationId !== harness.applicationId) { + errors.push(`project capability card applicationId ${card.scope.applicationId} does not match harness applicationId ${harness.applicationId}`); + continue; + } + const matching = observationValues.filter((observation) => + observation.applicationId === card.scope.applicationId && + observation.model.requestedRoute === card.model.requestedRoute && + observation.model.resolvedProvider === card.model.resolvedProvider && + observation.model.resolvedModel === card.model.resolvedModel && + (!card.model.modelRevision || observation.model.modelRevision === card.model.modelRevision) && + card.scope.taskFamilies.includes(observation.taskFamily) && + card.evidenceWindow.harnessVersions.includes(observation.harness.version) + ); + const taskCount = new Set(matching.map((observation) => observation.taskId)).size; + if (matching.length < card.evidenceWindow.benchmarkRuns) { + errors.push(`project capability card for ${card.model.resolvedModel} claims ${card.evidenceWindow.benchmarkRuns} runs but only ${matching.length} matching observations exist`); + } + if (taskCount < card.evidenceWindow.taskCount) { + errors.push(`project capability card for ${card.model.resolvedModel} claims ${card.evidenceWindow.taskCount} tasks but only ${taskCount} matching tasks exist`); + } + const availableEvidence = new Set(matching.flatMap((observation) => [observation.proofReceiptId, ...observation.evidenceRefs])); + for (const reference of card.evidenceRefs) { + if (!availableEvidence.has(reference)) errors.push(`project capability card evidence is not present in matching observations: ${reference}`); + } + } + if (errors.length > 0) throw new Error(`model intelligence evidence validation failed:\n${errors.join("\n")}`); + const harnessHash = hash(harness); + const benchmarkHash = hash(observationValues.map((entry) => ({ + applicationId: entry.applicationId, + taskFamily: entry.taskFamily, + taskId: entry.taskId, + proofReceiptId: entry.proofReceiptId, + }))); + const registry = { + schemaVersion: "nodekit.model-registry/v1", + applicationId: harness.applicationId, + status: observationValues.length === 0 ? "unmeasured" : cardValues.length === 0 ? "observed-unprofiled" : "profiled", + observations: observationValues.length, + cards: cardValues, + models: [...new Set(observationValues.map((entry) => `${entry.model.resolvedProvider}/${entry.model.resolvedModel}`))].sort(), + requestedRoutes: [...new Set(observationValues.map((entry) => entry.model.requestedRoute))].sort(), + harnessHash, + benchmarkHash, + routingCertified: false, + automaticPromotion: false, + }; + const resolved = { + schemaVersion: "nodekit.resolved-harness/v1", + ...harness, + harnessHash, + benchmarkHash, + observationCount: observationValues.length, + capabilityCardCount: cardValues.length, + }; + if (write) { + const outputRoot = path.join(resolvedRoot, ".nodekit", "harness"); + await mkdir(outputRoot, { recursive: true }); + await writeFile(path.join(outputRoot, "resolved-harness.json"), `${JSON.stringify(resolved, null, 2)}\n`); + await writeFile(path.join(outputRoot, "model-registry.json"), `${JSON.stringify(registry, null, 2)}\n`); + await writeFile(path.join(outputRoot, "harness-hash.txt"), `${harnessHash}\n`); + await writeFile(path.join(outputRoot, "benchmark-hash.txt"), `${benchmarkHash}\n`); + } + return { cards: cardValues, harness, observations: observationValues, registry, resolved }; +} + +export function diagnoseModelFailures(observations) { + const clusters = new Map(); + for (const observation of observations) { + for (const failure of observation.failures) { + const key = `${failure.failureClass}|${failure.probableCause}|${observation.model.resolvedProvider}/${observation.model.resolvedModel}`; + const cluster = clusters.get(key) ?? { + failureClass: failure.failureClass, + probableCause: failure.probableCause, + model: `${observation.model.resolvedProvider}/${observation.model.resolvedModel}`, + count: 0, + taskIds: new Set(), + taskFamilies: new Set(), + severities: new Set(), + evidenceRefs: new Set(), + }; + cluster.count += 1; + cluster.taskIds.add(observation.taskId); + cluster.taskFamilies.add(observation.taskFamily); + cluster.severities.add(failure.severity); + for (const reference of failure.evidenceRefs) cluster.evidenceRefs.add(reference); + clusters.set(key, cluster); + } + } + return [...clusters.values()].map((cluster) => ({ + ...cluster, + taskIds: [...cluster.taskIds].sort(), + taskFamilies: [...cluster.taskFamilies].sort(), + severities: [...cluster.severities].sort(), + evidenceRefs: [...cluster.evidenceRefs].sort(), + skillCandidateEligible: cluster.count >= 3 && cluster.taskIds.size >= 2, + })).sort((left, right) => right.count - left.count || left.failureClass.localeCompare(right.failureClass)); +} + +export async function writeModelBaseline(repoRoot) { + const compiled = await compileModelIntelligence(repoRoot); + const receipt = { + schemaVersion: "nodekit.model-baseline-receipt/v1", + applicationId: compiled.harness.applicationId, + harnessVersion: compiled.harness.version, + harnessHash: compiled.resolved.harnessHash, + benchmarkHash: compiled.resolved.benchmarkHash, + observationCount: compiled.observations.length, + capabilityCardCount: compiled.cards.length, + status: compiled.registry.status, + providerCallsMade: 0, + capabilityClaimsCertified: false, + routingCertified: false, + }; + const output = path.join(repoRoot, "harness", "receipts", "capability-cards", "baseline.json"); + await mkdir(path.dirname(output), { recursive: true }); + await writeFile(output, `${JSON.stringify(receipt, null, 2)}\n`); + return { compiled, output, receipt }; +} diff --git a/src/lib/runtime-capabilities.mjs b/src/lib/runtime-capabilities.mjs new file mode 100644 index 0000000..114726f --- /dev/null +++ b/src/lib/runtime-capabilities.mjs @@ -0,0 +1,86 @@ +export const RUNTIME_CAPABILITY_VERSION = "nodekit.runtime-capabilities/v1"; + +export const runtimeProfiles = Object.freeze({ + memory: Object.freeze({ + schemaVersion: RUNTIME_CAPABILITY_VERSION, + provider: "memory", + durableState: false, + transactions: true, + optimisticConcurrency: true, + subscriptions: "snapshot", + durableJobs: "in-process", + fileStorage: false, + presence: false, + scheduledJobs: false, + localDevelopment: true, + }), + convex: Object.freeze({ + schemaVersion: RUNTIME_CAPABILITY_VERSION, + provider: "convex", + durableState: true, + transactions: true, + optimisticConcurrency: true, + subscriptions: "native", + durableJobs: "native", + fileStorage: true, + presence: true, + scheduledJobs: true, + localDevelopment: true, + }), + postgres: Object.freeze({ + schemaVersion: RUNTIME_CAPABILITY_VERSION, + provider: "postgres", + durableState: true, + transactions: true, + optimisticConcurrency: true, + subscriptions: "polling", + durableJobs: "external", + fileStorage: false, + presence: false, + scheduledJobs: false, + localDevelopment: true, + }), + supabase: Object.freeze({ + schemaVersion: RUNTIME_CAPABILITY_VERSION, + provider: "supabase", + durableState: true, + transactions: true, + optimisticConcurrency: true, + subscriptions: "event-driven", + durableJobs: "queue-backed", + fileStorage: true, + presence: true, + scheduledJobs: true, + localDevelopment: true, + }), +}); + +export const coreCaseflowRequirements = Object.freeze({ + durableState: true, + optimisticConcurrency: true, + transactions: true, +}); + +export function negotiateRuntimeCapabilities(offered, required = coreCaseflowRequirements) { + const missing = []; + for (const [name, expectation] of Object.entries(required)) { + const actual = offered?.[name]; + if (Array.isArray(expectation)) { + if (!expectation.includes(actual)) missing.push({ actual, expected: expectation, name }); + } else if (actual !== expectation) missing.push({ actual, expected: expectation, name }); + } + return { + missing, + passed: missing.length === 0, + provider: offered?.provider ?? "unknown", + schemaVersion: "nodekit.runtime-capability-negotiation/v1", + }; +} + +export function requireRuntimeCapabilities(offered, required = coreCaseflowRequirements) { + const result = negotiateRuntimeCapabilities(offered, required); + if (!result.passed) { + throw new Error(`runtime ${result.provider} is missing required capabilities: ${result.missing.map((entry) => `${entry.name}=${JSON.stringify(entry.expected)}`).join(", ")}`); + } + return result; +} diff --git a/src/lib/scaffold.mjs b/src/lib/scaffold.mjs index 9626f12..da0ae41 100644 --- a/src/lib/scaffold.mjs +++ b/src/lib/scaffold.mjs @@ -1,16 +1,11 @@ import { spawn } from "node:child_process"; -import { chmod, cp, mkdir, readFile, readdir, rm, stat, writeFile } from "node:fs/promises"; +import { chmod, cp, mkdir, readFile, readdir, stat, writeFile } from "node:fs/promises"; import path from "node:path"; import { fileURLToPath } from "node:url"; import { pathExists } from "./files.mjs"; const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", ".."); -const PRESETS = Object.freeze({ - "agentic-rl-research": "agentic-rl-research", - "research-loop": "research-loop", - "smb-lending-fde": "smb-lending-fde", -}); -const defaultTemplateRoot = path.join(packageRoot, "templates", PRESETS["research-loop"]); +const defaultTemplateRoot = path.join(packageRoot, "templates", "base"); const pluginSkillsRoot = path.join(packageRoot, "plugins", "nodekit", "skills"); const projectedSkillNames = ["nodekit-launch", "nodekit-present", "nodekit-qa"]; const vendoredNodeKitSpecifier = "file:vendor/nodekit"; @@ -33,9 +28,8 @@ async function isEmpty(directory) { return (await readdir(directory)).length === 0; } -function normalizedSponsors(options = {}, presetName = options.preset) { - const defaults = presetName === "agentic-rl-research" ? [] : ["pi-ai"]; - return [...new Set([...defaults, ...(options.sponsors ?? [])].map(slugify).filter(Boolean))]; +function normalizedSponsors(options = {}) { + return [...new Set((options.sponsors ?? []).map(slugify).filter(Boolean))]; } function substitutions(options) { @@ -45,7 +39,10 @@ function substitutions(options) { ? vendoredNodeKitSpecifier : options.nodekitSpecifier, ).replaceAll("\\", "/"); - const sponsors = normalizedSponsors(options, options.preset); + const nodekitRuntimeImport = usesVendoredNodeKitRuntime(options.nodekitSpecifier) + ? "../vendor/nodekit/src/lib/caseflow.mjs" + : "@homenshum/nodekit/caseflow"; + const sponsors = normalizedSponsors(options); return { "__APP_NAME__": slug, "__APP_TITLE__": titleCase(slug), @@ -53,6 +50,7 @@ function substitutions(options) { "__BRIEF_JSON__": JSON.stringify(options.brief ?? "Build a measurable, proof-carrying agent workflow."), "__BRIEF_TEXT__": String(options.brief ?? "Build a measurable, proof-carrying agent workflow.").replaceAll(/\s+/g, " ").trim(), "__NODEKIT_SPECIFIER_JSON__": JSON.stringify(nodekitSpecifier), + "__NODEKIT_RUNTIME_IMPORT__": nodekitRuntimeImport, "__PI_PACKAGE__": options.piPackage ?? "0.80.10", "__PROVIDER_ID__": options.provider ?? "openrouter", "__MODEL_ID__": options.model ?? "openai/gpt-4o-mini", @@ -79,39 +77,6 @@ async function vendorNodeKitRuntime(target) { await chmod(path.join(destination, "src", "cli.mjs"), 0o755); } -function resolvePreset(preset) { - const normalized = preset ?? "research-loop"; - const templateName = PRESETS[normalized]; - if (!templateName) throw new Error(`unknown preset ${normalized}; available: ${Object.keys(PRESETS).join(", ")}`); - return { name: normalized, root: path.join(packageRoot, "templates", templateName) }; -} - -async function applyPresetTemplate(preset, target, values) { - if (preset.name !== "agentic-rl-research") { - await copyTemplate(preset.root, target, values); - return; - } - - // The reference research loop carries a deliberately live-capable Pi adapter. - // FounderQuest-RL is a clean-room replay-only lab, so remove those semantics - // before its authored offline contract overlays the generic starter. - await copyTemplate(defaultTemplateRoot, target, values); - for (const relative of [ - "agent/tools/measure-ngram.mjs", - "agent/skills/autoresearch-live", - "agent/subagents", - "fixtures/corpus", - "integrations/pi-ai", - "evals/deterministic-smoke.json", - "schemas/experiment-receipt.schema.json", - "scripts/live-smoke.mjs", - "scripts/browser-proof.mjs", - ]) { - await rm(path.join(target, relative), { force: true, recursive: true }); - } - await copyTemplate(preset.root, target, values); -} - function replaceTokens(value, values) { let output = value; for (const [token, replacement] of Object.entries(values)) output = output.replaceAll(token, replacement); @@ -175,26 +140,25 @@ function runGit(args, cwd, { capture = false } = {}) { }); } -export async function createProject(options) { +async function scaffoldProject(options) { const target = path.resolve(options.target); if (!(await isEmpty(target))) { throw new Error(`target is not empty: ${target}`); } - const preset = resolvePreset(options.preset); const startedAt = new Date().toISOString(); const packageManager = options.packageManager ?? "npm"; if (!new Set(["npm", "pnpm"]).has(packageManager)) { throw new Error(`unsupported package manager ${packageManager}; available: npm, pnpm`); } const launchStartedAt = options.launchStartedAt && Number.isFinite(Date.parse(options.launchStartedAt)) ? options.launchStartedAt : startedAt; - const values = substitutions({ ...options, preset: preset.name, target }); + const values = substitutions({ ...options, target }); await mkdir(target, { recursive: true }); - await applyPresetTemplate(preset, target, values); + await copyTemplate(defaultTemplateRoot, target, values); await projectCodingAgentSkills(target, values); if (usesVendoredNodeKitRuntime(options.nodekitSpecifier)) { await vendorNodeKitRuntime(target); } - const sponsors = normalizedSponsors(options, preset.name); + const sponsors = normalizedSponsors(options); for (const sponsor of sponsors.filter((entry) => entry !== "pi-ai")) { const integrationRoot = path.join(target, "integrations", sponsor); await mkdir(integrationRoot, { recursive: true }); @@ -215,9 +179,9 @@ export async function createProject(options) { name: "implementation_completed", }, ], - nodekitVersion: "0.2.0", + nodekitVersion: "0.2.1", packageManager, - preset: preset.name, + foundation: "domain-blank-base", repairLoops: 0, schemaVersion: "nodekit.build-friction/v1", }; @@ -253,7 +217,14 @@ export async function createProject(options) { ], target); candidateCommit = (await runGit(["rev-parse", "HEAD"], target, { capture: true })).trim(); } - return { candidateCommit, name: values.__APP_NAME__, packageManager, preset: preset.name, target }; + return { candidateCommit, name: values.__APP_NAME__, packageManager, target }; +} + +export async function createProject(options) { + if (options.preset !== undefined) { + throw new Error("nodekit create does not accept --preset; create the domain-blank figured-out base and let the coding agent specialize it from the user's real job"); + } + return scaffoldProject(options); } export async function recordSetupEvent(target, name, detail = {}, durationMs) { @@ -298,14 +269,11 @@ export async function adoptProject(options) { "nodekit:eval": "node scripts/eval.mjs", "nodekit:inspect": "nodekit inspect --repo-root .", "nodekit:proof": "node scripts/proof.mjs", - "nodekit:smoke:pi": "node scripts/live-smoke.mjs", }; for (const [name, command] of Object.entries(scripts)) { if (packageJson.scripts[name] && packageJson.scripts[name] !== command) collisions.push(`package.json#scripts.${name}`); else packageJson.scripts[name] = command; } - if (!packageJson.dependencies["@earendil-works/pi-ai"]) packageJson.dependencies["@earendil-works/pi-ai"] = "0.80.10"; - else if (packageJson.dependencies["@earendil-works/pi-ai"] !== "0.80.10") collisions.push("package.json#dependencies.@earendil-works/pi-ai"); if (!packageJson.devDependencies["@homenshum/nodekit"]) packageJson.devDependencies["@homenshum/nodekit"] = JSON.parse(values.__NODEKIT_SPECIFIER_JSON__); await writeFile(packagePath, `${JSON.stringify(packageJson, null, 2)}\n`); const receipt = { diff --git a/src/lib/source-hash.mjs b/src/lib/source-hash.mjs new file mode 100644 index 0000000..3ecfd2a --- /dev/null +++ b/src/lib/source-hash.mjs @@ -0,0 +1,37 @@ +import { createHash } from "node:crypto"; +import { lstat, readFile, readdir } from "node:fs/promises"; +import path from "node:path"; + +function digest(value) { + return createHash("sha256").update(JSON.stringify(value)).digest("hex"); +} + +function canonicalBytes(content) { + if (content.includes(0)) return content; + try { + const text = new TextDecoder("utf-8", { fatal: true }).decode(content); + return Buffer.from(text.replace(/\r\n?/g, "\n"), "utf8"); + } catch { + return content; + } +} + +export async function computeNodeKitSourceHash(repoRoot) { + const packageJson = JSON.parse(await readFile(path.join(repoRoot, "package.json"), "utf8")); + const entries = ["package.json", ...(packageJson.files ?? [])]; + const files = []; + const visit = async (absolute) => { + const metadata = await lstat(absolute); + if (metadata.isSymbolicLink()) throw new Error(`NodeKit distribution cannot contain symlinks: ${absolute}`); + if (metadata.isDirectory()) { + const children = await readdir(absolute); + children.sort(); + for (const child of children) await visit(path.join(absolute, child)); + return; + } + const content = canonicalBytes(await readFile(absolute)); + files.push({ digest: createHash("sha256").update(content).digest("hex"), path: path.relative(repoRoot, absolute).replaceAll("\\", "/") }); + }; + for (const relative of entries) await visit(path.join(repoRoot, relative)); + return digest(files.sort((left, right) => left.path.localeCompare(right.path))); +} diff --git a/src/lib/submission-gate.mjs b/src/lib/submission-gate.mjs new file mode 100644 index 0000000..91f57cd --- /dev/null +++ b/src/lib/submission-gate.mjs @@ -0,0 +1,117 @@ +import { createHash } from "node:crypto"; +import { execFileSync } from "node:child_process"; +import { readFile } from "node:fs/promises"; +import path from "node:path"; +import { validateSchema } from "./schema-validation.mjs"; + +export const requiredSubmissionGates = Object.freeze([ + "developerTimingMatrix", + "freshAgentHeldout", + "freshHumanUsability", + "threeConvexConsumers", + "previewDeployment", + "proofloopEaseVerification", + "packageInstallProof", + "publicationApproval", +]); + +function sha256(bytes) { + return createHash("sha256").update(bytes).digest("hex"); +} + +function git(repoRoot, args) { + return execFileSync("git", args, { cwd: repoRoot, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim(); +} + +export function evidenceCandidateCommit(value) { + if (typeof value?.candidateCommit === "string") return value.candidateCommit; + if (typeof value?.nodekitCommit === "string") return value.nodekitCommit; + if (typeof value?.subject?.repository?.candidateCommit === "string") return value.subject.repository.candidateCommit; + if (typeof value?.nodekitIdentity === "string") return value.nodekitIdentity.split("/")[0]; + return null; +} + +export function evidenceContractPasses(gateId, value) { + switch (gateId) { + case "developerTimingMatrix": return value?.schemaVersion === "nodekit.developer-timing-verdict/v1" && value.passed === true && value.observedRuns >= 60; + case "freshAgentHeldout": return value?.schemaVersion === "nodekit.fresh-agent-verdict/v1" && value.passed === true && value.selectedRuns?.length === 3; + case "freshHumanUsability": return value?.schemaVersion === "nodekit.fresh-user-verdict/v1" && value.passed === true && value.metrics?.participantCount >= 5; + case "threeConvexConsumers": return value?.schemaVersion === "nodekit.convex-consumers-verdict/v1" && value.passed === true && value.qualifyingConsumers === 3; + case "previewDeployment": return value?.schemaVersion === "nodekit.preview-verdict/v1" && value.passed === true && value.freshIdentity === true && value.exportReopenPassed === true && value.cleanupPassed === true; + case "proofloopEaseVerification": return value?.verdict?.status === "passed" && value?.extensions?.easeCertified === true; + case "packageInstallProof": { + const requiredChecks = ["freshConsumerInstall", "packagedCliCreate", "compile", "check", "demo", "eval"]; + return value?.schemaVersion === "nodekit.package-install-proof/v1" + && value.passed === true + && requiredChecks.every((check) => value?.checks?.[check] === true) + && /^[a-f0-9]{64}$/.test(value?.tarballSha256 ?? ""); + } + case "publicationApproval": return value?.schemaVersion === "nodekit.publication-approval/v1" && value.approved === true && Array.isArray(value.scopes) && value.scopes.includes("npm-publish") && value.scopes.includes("convex-directory-submit") && typeof value.approvedBy === "string" && value.approvedBy.length > 0; + default: return false; + } +} + +export async function evaluateSubmissionManifest(repoRoot, manifestPath = "proof/submission-manifest.json") { + const absolute = path.resolve(repoRoot, manifestPath); + const manifest = JSON.parse(await readFile(absolute, "utf8")); + const schemaErrors = await validateSchema("nodekit.submission-manifest.v1.schema.json", manifest, manifestPath); + if (schemaErrors.length > 0) throw new Error(schemaErrors.join("\n")); + const errors = []; + const root = path.resolve(repoRoot); + try { + git(root, ["cat-file", "-e", `${manifest.candidateCommit}^{commit}`]); + git(root, ["merge-base", "--is-ancestor", manifest.candidateCommit, "HEAD"]); + const postCandidateChanges = git(root, ["diff", "--name-only", `${manifest.candidateCommit}..HEAD`]).split(/\r?\n/).filter(Boolean); + const disallowed = postCandidateChanges.filter((file) => !/^(?:proof|docs|evolution)\//.test(file.replaceAll("\\", "/"))); + if (disallowed.length > 0) errors.push(`candidateCommit is stale; source changed afterward: ${disallowed.join(", ")}`); + const dirtyPaths = git(root, ["status", "--porcelain", "--untracked-files=all"]) + .split(/\r?\n/) + .filter(Boolean) + .map((line) => line.slice(3).replace(/^"|"$/g, "").replaceAll("\\", "/")); + const dirtySource = dirtyPaths.filter((file) => !/^(?:proof|docs|evolution)\//.test(file)); + if (dirtySource.length > 0) errors.push(`working tree contains uncommitted source changes: ${dirtySource.join(", ")}`); + } catch { + errors.push(`candidateCommit is not an ancestor of repository HEAD: ${manifest.candidateCommit}`); + } + const ids = manifest.gates.map((gate) => gate.id); + for (const id of requiredSubmissionGates) { + if (ids.filter((candidate) => candidate === id).length !== 1) errors.push(`${id}: required exactly once`); + } + const evidenceOwners = new Map(); + for (const gate of manifest.gates) { + if (gate.passed !== true) errors.push(`${gate.id}: not passed`); + let contractEvidence = false; + for (const evidence of gate.evidence) { + const priorOwner = evidenceOwners.get(evidence.path); + if (priorOwner && priorOwner !== gate.id) errors.push(`${gate.id}: evidence path is reused by ${priorOwner}: ${evidence.path}`); + evidenceOwners.set(evidence.path, gate.id); + const evidencePath = path.resolve(repoRoot, evidence.path); + if (!evidencePath.startsWith(`${path.resolve(repoRoot)}${path.sep}`)) { + errors.push(`${gate.id}: evidence escapes repository: ${evidence.path}`); + continue; + } + try { + const bytes = await readFile(evidencePath); + if (sha256(bytes) !== evidence.sha256) errors.push(`${gate.id}: evidence hash mismatch: ${evidence.path}`); + try { + const value = JSON.parse(bytes.toString("utf8")); + const boundCommit = evidenceCandidateCommit(value); + if (boundCommit && boundCommit !== manifest.candidateCommit) errors.push(`${gate.id}: evidence is bound to ${boundCommit}, expected ${manifest.candidateCommit}: ${evidence.path}`); + if (boundCommit === manifest.candidateCommit && evidenceContractPasses(gate.id, value)) contractEvidence = true; + } catch { + // Binary and supporting evidence are allowed, but cannot be the decisive gate verdict. + } + } catch { + errors.push(`${gate.id}: evidence missing: ${evidence.path}`); + } + } + if (!contractEvidence) errors.push(`${gate.id}: no exact-candidate decisive verdict satisfies the gate contract`); + } + return { + candidateCommit: manifest.candidateCommit, + errors, + passed: errors.length === 0, + schemaVersion: "nodekit.submission-verdict/v1", + submissionReady: errors.length === 0, + }; +} diff --git a/src/lib/submission-preparation.mjs b/src/lib/submission-preparation.mjs new file mode 100644 index 0000000..86b93d7 --- /dev/null +++ b/src/lib/submission-preparation.mjs @@ -0,0 +1,106 @@ +import { createHash } from "node:crypto"; +import { execFileSync } from "node:child_process"; +import { mkdir, readFile, writeFile } from "node:fs/promises"; +import path from "node:path"; +import { + evidenceCandidateCommit, + evidenceContractPasses, + requiredSubmissionGates, +} from "./submission-gate.mjs"; + +const ZERO_HASH = "0".repeat(64); + +export const canonicalSubmissionEvidence = Object.freeze([ + Object.freeze({ id: "developerTimingMatrix", path: "proof/ease/developer-timing-verdict.json" }), + Object.freeze({ id: "freshAgentHeldout", path: "proof/ease/fresh-agent-verdict.json" }), + Object.freeze({ id: "freshHumanUsability", path: "proof/ease/fresh-users-verdict.json" }), + Object.freeze({ id: "threeConvexConsumers", path: "proof/convex-consumers-verdict.json" }), + Object.freeze({ id: "previewDeployment", path: "proof/preview-verdict.json" }), + Object.freeze({ id: "proofloopEaseVerification", path: "proof/proofloop-final.json" }), + Object.freeze({ id: "packageInstallProof", path: "proof/package-install-verdict.json" }), + Object.freeze({ id: "publicationApproval", path: "proof/publication-approval.json" }), +]); + +function sha256(bytes) { + return createHash("sha256").update(bytes).digest("hex"); +} + +function git(repoRoot, args) { + return execFileSync("git", args, { + cwd: repoRoot, + encoding: "utf8", + stdio: ["ignore", "pipe", "pipe"], + }).trim(); +} + +function changedSourcePaths(repoRoot, candidateCommit) { + const committed = git(repoRoot, ["diff", "--name-only", `${candidateCommit}..HEAD`]) + .split(/\r?\n/) + .filter(Boolean); + const working = git(repoRoot, ["status", "--porcelain=v1", "--untracked-files=all"]) + .split(/\r?\n/) + .filter(Boolean) + .map((line) => line.slice(3).split(" -> ").at(-1)); + return [...new Set([...committed, ...working])] + .map((file) => file.replaceAll("\\", "/")) + .filter((file) => !/^(?:proof|docs|evolution)\//.test(file)); +} + +export function resolveCandidateCommit(repoRoot, candidateRef = "HEAD") { + const root = path.resolve(repoRoot); + const commit = git(root, ["rev-parse", "--verify", `${candidateRef}^{commit}`]).toLowerCase(); + if (!/^[a-f0-9]{40}$/.test(commit)) throw new Error(`Unable to resolve candidate commit: ${candidateRef}`); + return commit; +} + +export async function prepareSubmissionManifest({ + repoRoot, + candidateRef = "HEAD", + outputPath = "proof/submission-manifest.json", +} = {}) { + const root = path.resolve(repoRoot ?? "."); + const candidateCommit = resolveCandidateCommit(root, candidateRef); + const sourceChanges = changedSourcePaths(root, candidateCommit); + const sourceIsExact = sourceChanges.length === 0; + const configuredGateIds = canonicalSubmissionEvidence.map((gate) => gate.id); + if (configuredGateIds.join("\n") !== requiredSubmissionGates.join("\n")) { + throw new Error("canonical submission evidence paths drifted from the required gate contract"); + } + const gates = []; + + for (const gate of canonicalSubmissionEvidence) { + const absoluteEvidencePath = path.resolve(root, gate.path); + let digest = ZERO_HASH; + let passed = false; + try { + const bytes = await readFile(absoluteEvidencePath); + digest = sha256(bytes); + const value = JSON.parse(bytes.toString("utf8")); + passed = sourceIsExact + && evidenceCandidateCommit(value) === candidateCommit + && evidenceContractPasses(gate.id, value); + } catch { + passed = false; + } + gates.push({ + id: gate.id, + passed, + evidence: [{ path: gate.path, sha256: digest }], + }); + } + + const manifest = { + schemaVersion: "nodekit.submission-manifest/v1", + candidateCommit, + gates, + }; + const absoluteOutputPath = path.resolve(root, outputPath); + await mkdir(path.dirname(absoluteOutputPath), { recursive: true }); + await writeFile(absoluteOutputPath, `${JSON.stringify(manifest, null, 2)}\n`); + return { + manifest, + outputPath: path.relative(root, absoluteOutputPath).replaceAll("\\", "/"), + sourceChanges, + sourceIsExact, + }; +} diff --git a/templates/base/.dockerignore b/templates/base/.dockerignore new file mode 100644 index 0000000..47f7f3e --- /dev/null +++ b/templates/base/.dockerignore @@ -0,0 +1,5 @@ +node_modules +.git +.data +.env* +proof diff --git a/templates/base/.env.example b/templates/base/.env.example new file mode 100644 index 0000000..5da743f --- /dev/null +++ b/templates/base/.env.example @@ -0,0 +1,3 @@ +# The deterministic demo requires no secrets. +# Add provider credentials only after the domain workflow and budget are approved. +NODEKIT_OPTIONAL_MODEL_KEY= diff --git a/templates/base/.gitattributes b/templates/base/.gitattributes new file mode 100644 index 0000000..6313b56 --- /dev/null +++ b/templates/base/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf diff --git a/templates/base/AGENTS.md b/templates/base/AGENTS.md new file mode 100644 index 0000000..53e6ece --- /dev/null +++ b/templates/base/AGENTS.md @@ -0,0 +1,33 @@ +# __APP_TITLE__ coding-agent entrypoint + +Read these files in order before changing the product: + +1. `docs/FIGURED_OUT.md` +2. `docs/KNOWLEDGE_EVOLUTION.md` +3. `docs/EVOLUTION.md` +4. `product/BRIEF.md` +5. `product/USER_JOURNEY.md` +6. `product/SERVICE_BLUEPRINT.md` +7. `product/EXPERIENCE.yaml` +8. `nodeagent.yaml` + +Keep the application blank in domain until the real user workflow is researched. Preserve the universal `Case -> Run -> Stage -> Artifact -> Proposal -> Approval -> Receipt` behavior and the in-memory deterministic demonstration. + +- Do not let generated work overwrite a canonical artifact without a base-version check and decision. +- Keep the primary artifact above chat, tools, and traces in the information hierarchy. +- Make the current stage, next action, and next-action owner visible. +- Preserve valid state through cancellation, conflicts, and failures. +- Never put secrets in source, manifests, browser bundles, logs, or receipts. +- Ask before paid activation, production writes, deployment, or publication. +- Run `npm run compile`, `npm run check`, `npm run eval`, and `npm run proof` after lifecycle changes. + +When the real workflow needs durable cross-run knowledge, initialize the NodeKit Knowledge Evolution Plane rather than accumulating scattered prompt notes: + +- Retrieve existing graph knowledge before broad external research. +- Research only typed gaps or contradictions. +- Anchor every durable claim to immutable source evidence. +- Propose INSERT, UPDATE, or DEPRECATE operations; never mutate the canonical graph directly. +- Require validation and explicit approval before a graph version advances. +- Use `ABSTAIN` when required evidence is missing. + +Material product, architecture, or harness changes require a reviewed Evolution Ledger record with immutable evidence and scoped adoption. A generating or frontend model cannot define its own judge, proof threshold, or completion verdict. diff --git a/templates/base/Dockerfile b/templates/base/Dockerfile new file mode 100644 index 0000000..c2238d6 --- /dev/null +++ b/templates/base/Dockerfile @@ -0,0 +1,10 @@ +FROM node:22-alpine +WORKDIR /app +COPY package*.json ./ +COPY vendor ./vendor +RUN npm install --omit=dev +COPY . . +ENV HOST=0.0.0.0 +ENV PORT=4173 +EXPOSE 4173 +CMD ["node", "apps/web/server.mjs"] diff --git a/templates/base/README.md b/templates/base/README.md new file mode 100644 index 0000000..46a0d97 --- /dev/null +++ b/templates/base/README.md @@ -0,0 +1,29 @@ +# __APP_TITLE__ + +__BRIEF_TEXT__ + +This is NodeKit's domain-blank base application. Its domain is intentionally unspecified; its product behavior is already figured out. + +```text +Case -> Run -> Stage -> Artifact -> Proposal -> Approval -> Receipt +``` + +## Quickstart + +```bash +npm install +npm run compile +npm run demo +npm run check +npm run dev +``` + +Open `http://127.0.0.1:4173`. The deterministic demonstration requires no account, provider key, database, or network connection. + +`npm run proof:browser-contract` checks the live HTTP/source-DOM contract. It is deliberately not called browser certification. `npm run proof:browser` drives a real Playwright journey and emits candidate-bound screenshots and metadata; install Chromium with `npx playwright install chromium` when running that certification outside CI. + +## Specialize the application + +Start with `docs/FIGURED_OUT.md` and the files in `product/`. Replace the neutral copy, artifact renderer, guided stages, domain tools, validators, and fixtures only after researching the real user journey. Do not replace the proposal, approval, version-conflict, safe-failure, or receipt semantics. + +Convex is the preferred first managed backend. The browser consumes NodeKit view models, not Convex documents, so another conforming backend can implement the same observable lifecycle later. diff --git a/templates/base/adw/workflows/launch.yaml b/templates/base/adw/workflows/launch.yaml new file mode 100644 index 0000000..753c5af --- /dev/null +++ b/templates/base/adw/workflows/launch.yaml @@ -0,0 +1,4 @@ +schemaVersion: nodekit.workflow/v1 +name: figured-out-application +steps: [research, specialize, compile, deterministic-demo, conformance, browser-proof, deployment-proof] +gates: [figured-out-contract, tests, accessibility, artifact-proof, receipt-verification] diff --git a/templates/base/agent/instructions.md b/templates/base/agent/instructions.md new file mode 100644 index 0000000..5c7ba3d --- /dev/null +++ b/templates/base/agent/instructions.md @@ -0,0 +1,3 @@ +# Domain-blank agent + +Help complete the current case. Work only through typed proposals against the current artifact version. Never treat generated content as canonical until a user or explicit policy accepts it. Explain consequences in plain language, preserve unresolved uncertainty, and leave the next action and its owner explicit. diff --git a/templates/base/agent/workflow.mjs b/templates/base/agent/workflow.mjs new file mode 100644 index 0000000..386608c --- /dev/null +++ b/templates/base/agent/workflow.mjs @@ -0,0 +1,54 @@ +import { createMemoryCaseflow } from "__NODEKIT_RUNTIME_IMPORT__"; + +export const stages = Object.freeze([ + { id: "intake", label: "Confirm the intended outcome", owner: "user" }, + { id: "working", label: "Prepare a proposal", owner: "agent" }, + { id: "review", label: "Review the proposed change", owner: "user" }, + { id: "complete", label: "Verify and export", owner: "system" }, +]); + +export function createGuidedDemo(options = {}) { + const runtime = createMemoryCaseflow(options); + + function start() { + const work = runtime.createCase({ + primaryJob: __BRIEF_JSON__, + title: "__APP_TITLE__ case", + }); + const run = runtime.startRun({ caseId: work.caseId, stages }); + const artifact = runtime.createArtifact({ + caseId: work.caseId, + content: { summary: "The last approved result remains here.", status: "baseline" }, + kind: "structured-document", + runId: run.runId, + title: "Primary artifact", + }); + return { artifact, case: work, run }; + } + + function propose({ artifactId, runId }) { + runtime.enterStage({ runId, stageId: "working" }); + const proposal = runtime.createProposal({ + artifactId, + baseVersion: runtime.snapshot().artifacts.find((entry) => entry.artifactId === artifactId).canonicalVersion, + patch: { + summary: "A bounded, reviewable result is ready. Replace this with the researched domain artifact.", + status: "proposed", + }, + rationale: "Demonstrate proposal-before-mutation without assuming a domain.", + }); + runtime.enterStage({ runId, stageId: "review", nextAction: "Approve or reject the proposed change", nextActionOwner: "user" }); + return proposal; + } + + function decide({ proposalId, runId, decision }) { + const result = runtime.decideProposal({ proposalId, decision }); + if (decision === "accepted" && result.proposal.status === "accepted") { + runtime.enterStage({ runId, stageId: "complete" }); + return { ...result, ...runtime.completeRun({ runId }) }; + } + return result; + } + + return { decide, propose, runtime, start }; +} diff --git a/templates/base/apps/web/public/app.js b/templates/base/apps/web/public/app.js new file mode 100644 index 0000000..ac199b3 --- /dev/null +++ b/templates/base/apps/web/public/app.js @@ -0,0 +1,61 @@ +const elements = Object.fromEntries([...document.querySelectorAll("[id]")].map((element) => [element.id, element])); +let state; + +async function api(path, options = {}) { + const response = await fetch(path, { headers: { "content-type": "application/json" }, ...options }); + const payload = await response.json(); + if (!response.ok) throw new Error(payload.error ?? `HTTP ${response.status}`); + return payload; +} + +function render() { + const pending = state.proposal?.status === "pending"; + const latest = state.artifact.versions.at(-1); + const scenario = state.presentation?.id ?? "first_arrival"; + document.body.dataset.scenario = scenario; + document.querySelector(".status").dataset.status = state.run.status; + elements["state-kind"].textContent = String(state.presentation?.kind ?? "status").replaceAll("_", " "); + elements["state-title"].textContent = state.presentation?.title ?? "Case status"; + elements["state-message"].textContent = state.presentation?.message ?? "The case state is available."; + elements["state-banner"].dataset.kind = state.presentation?.kind ?? "status"; + elements["case-name"].textContent = state.case.title; + elements["status-label"].textContent = state.run.status.replaceAll("_", " "); + elements["next-owner"].textContent = state.run.nextActionOwner; + elements["current-action"].textContent = state.run.nextAction; + elements["artifact-version"].textContent = `v${state.artifact.canonicalVersion}`; + elements["artifact-body"].innerHTML = `

${escapeText(latest.content.summary ?? JSON.stringify(latest.content))}

Canonical hash ${latest.contentHash.slice(0, 16)}`; + elements.progress.innerHTML = state.run.stages.map((stage) => `
${escapeText(stage.label)}
`).join(""); + elements.proposal.innerHTML = pending + ? `Proposed change

${escapeText(state.proposal.patch.summary)}

Based on artifact v${state.proposal.baseVersion} · ${escapeText(state.proposal.rationale)}` + : `${state.proposal ? `Proposal ${escapeText(state.proposal.status)}.` : "Nothing pending."}`; + const openException = state.exceptions?.find((entry) => entry.status === "open"); + elements.exception.hidden = !openException; + elements.exception.innerHTML = openException ? `${escapeText(openException.code.replaceAll("_", " "))}

${escapeText(openException.message)}

Preserved artifact v${escapeText(openException.preservedState.artifactVersion ?? "current")}` : ""; + const inspectingReceipt = ["receipt_inspection", "export_share"].includes(scenario) && state.receipt; + elements["receipt-detail"].hidden = !inspectingReceipt; + elements["receipt-detail"].innerHTML = inspectingReceipt ? `${scenario === "export_share" ? "Portable proof bundle" : "Receipt contents"}
Receipt
${escapeText(state.receipt.receiptHash)}
Artifacts
${state.receipt.artifactIds.length}
Proposals
${state.receipt.proposalIds.length}
Events
${state.receipt.eventIds.length}
` : ""; + elements["primary-input"].hidden = !["first_arrival", "orientation", "input", "validation_error"].includes(scenario); + elements.reset.hidden = ["first_arrival", "orientation", "input", "validation_error"].includes(scenario); + elements.outcome.setAttribute("aria-invalid", scenario === "validation_error" ? "true" : "false"); + elements["input-help"].textContent = scenario === "validation_error" ? "A concrete outcome is required before work can start." : "Saved locally in this deterministic demonstration."; + elements.propose.disabled = pending || state.run.status === "completed"; + elements.approve.disabled = !pending; + elements.reject.disabled = !pending; + elements.completion.hidden = state.run.status !== "completed"; + elements["receipt-id"].textContent = state.receipt ? `Receipt ${state.receipt.receiptHash.slice(0, 16)}` : "No receipt yet"; + elements.events.innerHTML = state.events.slice(-8).reverse().map((event) => `
  • ${escapeText(event.eventType)}${escapeText(event.occurredAt)}
  • `).join(""); +} + +function escapeText(value) { const node = document.createElement("span"); node.textContent = String(value ?? ""); return node.innerHTML; } +async function act(path, body = {}) { elements.error.hidden = true; try { state = await api(path, { method: "POST", body: JSON.stringify(body) }); render(); } catch (error) { elements.error.textContent = error.message; elements.error.hidden = false; } } +elements.reset.addEventListener("click", () => act("/api/reset")); +elements.propose.addEventListener("click", () => act("/api/propose")); +elements.approve.addEventListener("click", () => act("/api/decide", { decision: "accepted" })); +elements.reject.addEventListener("click", () => act("/api/decide", { decision: "rejected" })); +elements["primary-input"].addEventListener("submit", (event) => { event.preventDefault(); if (!elements.outcome.value.trim()) { elements.error.textContent = "Add a concrete outcome before continuing."; elements.error.hidden = false; elements.outcome.setAttribute("aria-invalid", "true"); } else { elements.error.hidden = true; elements.outcome.setAttribute("aria-invalid", "false"); } }); +const requestedScenario = new URL(window.location.href).searchParams.get("scenario"); +const existingState = await api("/api/state"); +state = requestedScenario && existingState.presentation?.id !== requestedScenario + ? await api("/api/scenario", { method: "POST", body: JSON.stringify({ id: requestedScenario }) }) + : existingState; +render(); diff --git a/templates/base/apps/web/public/index.html b/templates/base/apps/web/public/index.html new file mode 100644 index 0000000..b48c049 --- /dev/null +++ b/templates/base/apps/web/public/index.html @@ -0,0 +1,75 @@ + + + + + + + __APP_TITLE__ | Guided case + + + + +
    + N/ __APP_TITLE__ +
    Case ready
    +
    +
    +
    +
    +

    GUIDED CASE

    +

    One clear job.
    Carried all the way through.

    +

    __BRIEF_TEXT__

    +
    + +
    + +
    + +
    + ORIENTATION +
    Ready for your direction

    Confirm the outcome before the run begins.

    +
    + +
    + + +
    +
    + +
    + Saved locally in this deterministic demonstration. +
    +

    PRIMARY ARTIFACT

    Primary artifact

    v1
    +
    Current actionConfirm the intended outcome
    +
    + +
    + + +
    +
    +
    Blank in domain. Figured out in behavior.No receipt yet
    + + + diff --git a/templates/base/apps/web/public/styles.css b/templates/base/apps/web/public/styles.css new file mode 100644 index 0000000..24099e6 --- /dev/null +++ b/templates/base/apps/web/public/styles.css @@ -0,0 +1,16 @@ +:root { color-scheme: light; --ink:#171817; --muted:#676a66; --paper:#f3f0e8; --panel:#fffdf7; --line:#d4d0c4; --lime:#d8ff72; --violet:#6656e8; --soft:#ebe7ff; font-family:Inter,ui-sans-serif,system-ui,sans-serif; } +* { box-sizing:border-box; } body { margin:0; min-height:100vh; color:var(--ink); background:var(--paper); } button { font:inherit; } button:focus-visible,a:focus-visible,[tabindex]:focus-visible,summary:focus-visible { outline:3px solid var(--violet); outline-offset:3px; } +.skip-link { position:fixed; left:1rem; top:-4rem; padding:.75rem 1rem; background:var(--ink); color:white; z-index:20; }.skip-link:focus{top:1rem} header { height:64px; padding:0 clamp(1rem,4vw,4rem); display:flex; align-items:center; justify-content:space-between; border-bottom:1px solid var(--line); background:rgba(243,240,232,.92); position:sticky; top:0; backdrop-filter:blur(12px); z-index:10; } +.brand { color:inherit; text-decoration:none; font-weight:850; letter-spacing:-.04em; }.brand span{margin-left:.45rem}.status{display:flex;align-items:center;gap:.55rem;color:var(--muted);font:.72rem ui-monospace,monospace;text-transform:uppercase}.status i{width:8px;height:8px;border-radius:50%;background:#7d8179;box-shadow:0 0 0 4px #7d81791c}.status[data-status="active"] i{background:#42a85a;box-shadow:0 0 0 4px #42a85a1c}.status[data-status="blocked"] i{background:#c98718;box-shadow:0 0 0 4px #c9871824}.status[data-status="completed"] i{background:#6656e8;box-shadow:0 0 0 4px #6656e824} +main{width:min(1500px,100%);margin:auto;padding:0 clamp(1rem,4vw,4rem) 4rem}.orientation{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:end;gap:1.5rem;padding:clamp(2rem,4vw,3.75rem) 0 1rem}.eyebrow{margin:0 0 .65rem;color:var(--muted);font:750 .68rem/1.2 ui-monospace,monospace;letter-spacing:.15em}h1{margin:0;max-width:900px;font-size:clamp(2.8rem,5.4vw,5.4rem);line-height:.92;letter-spacing:-.07em}h1 em{font-family:Georgia,serif;font-weight:400;color:var(--violet)}.lede{max-width:680px;margin:.85rem 0 0;color:var(--muted);font-size:1.02rem;line-height:1.45}.button{min-height:44px;border:1px solid var(--ink);padding:.7rem 1rem;background:transparent;font-weight:750;cursor:pointer}.button:disabled{opacity:.4;cursor:not-allowed}.primary,.approve{background:var(--lime);box-shadow:3px 3px 0 var(--ink)}.secondary{background:var(--panel)} +.progress{display:grid;grid-template-columns:repeat(4,1fr);border:1px solid var(--ink);background:var(--ink);gap:1px}.step{min-height:58px;padding:.7rem 1rem;background:var(--panel);display:flex;align-items:center;gap:.7rem;font-size:.78rem;font-weight:700}.step i{width:12px;height:12px;border:1px solid var(--ink);border-radius:50%}.step.completed i{background:var(--ink)}.step.active{background:var(--lime)}.step.active i{background:var(--violet)}.error{margin-top:1rem;padding:1rem;border:1px solid #b33;background:#fff0ed;color:#812} +.state-banner{margin-top:.75rem;padding:.85rem 1.1rem;border:1px solid var(--line);background:var(--panel);display:flex;align-items:flex-start;gap:1rem}.state-banner[data-kind="running"]{border-color:var(--violet);background:var(--soft)}.state-banner[data-kind="waiting"],.state-banner[data-kind="partial"]{border-color:#9b7412;background:#fff6d8}.state-banner[data-kind="error"],.state-banner[data-kind="failure"],.state-banner[data-kind="conflict"]{border-color:#b33;background:#fff0ed}.state-banner[data-kind="complete"],.state-banner[data-kind="receipt_inspection"],.state-banner[data-kind="export_share"]{border-color:#43862b;background:var(--lime)}.state-kind{min-width:7.5rem;padding:.3rem .45rem;background:var(--ink);color:var(--panel);font:750 .65rem ui-monospace,monospace;text-transform:uppercase;letter-spacing:.08em}.state-banner p{margin:.3rem 0 0;color:var(--muted);line-height:1.4} +.workspace{display:grid;grid-template-columns:220px minmax(0,1fr) 360px;margin-top:1rem;border:1px solid var(--ink);background:var(--panel);min-height:520px}.workspace nav{padding:1.4rem;border-right:1px solid var(--line);display:flex;flex-direction:column;gap:.35rem}.workspace nav>a{padding:.7rem;color:var(--muted);text-decoration:none;font-size:.84rem}.workspace nav>a.active{background:var(--ink);color:white}.case-meta{margin-top:auto;border-top:1px solid var(--line);padding-top:1rem;display:flex;flex-direction:column;gap:.4rem}.case-meta span{color:var(--muted);font-size:.7rem;text-transform:uppercase}.artifact{padding:clamp(1.5rem,3vw,2.5rem);min-width:0}.section-title{display:flex;justify-content:space-between;align-items:start}.section-title h2,aside h2{margin:0;font-size:clamp(1.7rem,3vw,2.5rem);letter-spacing:-.05em}.version{border:1px solid var(--line);padding:.35rem .5rem;font:.72rem ui-monospace,monospace}.current-action{margin:2rem 0 1.2rem;padding:1rem;background:var(--soft);display:flex;flex-direction:column;gap:.35rem}.current-action span{color:var(--muted);font-size:.68rem;text-transform:uppercase}.artifact-body{min-height:260px;border:1px solid var(--line);background:white;padding:clamp(1.5rem,4vw,3rem);display:flex;flex-direction:column;justify-content:center}.artifact-body p{font-family:Georgia,serif;font-size:clamp(1.5rem,3vw,2.5rem);line-height:1.25}.artifact-body small{color:var(--muted);font-family:ui-monospace,monospace}.completion{margin-top:1rem;padding:1rem;background:var(--lime)}.completion p{margin:.3rem 0 0} +.primary-input{margin:0 0 1.2rem;padding:1rem;border:1px solid var(--line);background:var(--panel)}.primary-input label{display:block;margin-bottom:.55rem;font-size:.78rem;font-weight:750}.primary-input>div{display:flex;gap:.6rem}.primary-input input{min-width:0;flex:1;padding:.75rem;border:1px solid var(--ink);background:var(--panel);color:var(--ink);font:inherit}.primary-input input[aria-invalid="true"]{border-color:#b33;box-shadow:0 0 0 2px #b333}.primary-input small{display:block;margin-top:.5rem;color:var(--muted)} +aside{border-left:1px solid var(--ink);padding:1.5rem;background:#f1eeff}.tabs{display:flex;border-bottom:1px solid #c8c1ef;margin:-.4rem 0 2rem}.tab{border:0;background:transparent;padding:.7rem;font-weight:750;color:var(--muted)}.tab.active{color:var(--ink);border-bottom:2px solid var(--ink)}aside>p:not(.eyebrow){color:var(--muted);line-height:1.5}.proposal{min-height:130px;margin:1.2rem 0;padding:1rem;border:1px solid #c8c1ef;background:rgba(255,255,255,.6)}.proposal p{line-height:1.45}.proposal small{color:var(--muted)}.actions{display:grid;grid-template-columns:1fr 1fr;gap:.6rem}.actions .primary{grid-column:1/-1}details{margin-top:2rem;border-top:1px solid #c8c1ef;padding-top:1rem}summary{cursor:pointer;font-weight:750}details ol{padding-left:1rem}details li{display:flex;flex-direction:column;gap:.25rem;margin:.8rem 0;font-size:.72rem}details li span{color:var(--muted);font-family:ui-monospace,monospace}footer{padding:1.2rem clamp(1rem,4vw,4rem);border-top:1px solid var(--line);display:flex;justify-content:space-between;gap:1rem;color:var(--muted);font-size:.72rem} +.exception,.receipt-detail{margin:1rem 0;padding:1rem;border:1px solid #b33;background:#fff0ed}.exception p{line-height:1.4}.exception small{color:var(--muted)}.receipt-detail{border-color:#43862b;background:rgba(216,255,114,.35)}.receipt-detail dl{display:grid;grid-template-columns:auto 1fr;gap:.5rem;margin-bottom:0}.receipt-detail dt{color:var(--muted);font-size:.72rem;text-transform:uppercase}.receipt-detail dd{margin:0;min-width:0;overflow-wrap:anywhere;font: .7rem ui-monospace,monospace} +@media(max-width:1000px){.workspace{grid-template-columns:170px 1fr}.workspace aside{grid-column:1/-1;border-left:0;border-top:1px solid var(--ink)}.progress{grid-template-columns:repeat(2,1fr)}} +@media(max-width:640px){header{height:58px}.orientation{grid-template-columns:1fr;padding:1rem 0 .75rem}.orientation .button{justify-self:start}h1{font-size:2.55rem}.lede{margin-top:.65rem;font-size:.92rem;line-height:1.4}.progress{grid-template-columns:repeat(2,1fr)}.step{min-height:48px;padding:.5rem;font-size:.7rem}.state-banner{flex-direction:row;margin-top:.6rem;padding:.65rem;gap:.65rem}.state-kind{min-width:0}.workspace{grid-template-columns:1fr;margin-top:.6rem}.workspace nav{grid-row:2;border-right:0;border-bottom:1px solid var(--line);display:grid;grid-template-columns:repeat(3,1fr);padding:1rem}.workspace nav .eyebrow,.workspace nav>strong,.case-meta{grid-column:1/-1}.workspace nav>a{text-align:center;padding:.45rem}.artifact{grid-row:1;padding:1rem}.primary-input>div{flex-direction:column}.artifact-body{min-height:220px;padding:1.3rem}aside{grid-row:3;padding:1.1rem}footer{flex-direction:column}} +@media(max-height:500px) and (min-width:641px){header{height:44px}.orientation{padding:.5rem 0 .4rem;gap:.5rem}.orientation h1{max-width:none;font-size:1.9rem;line-height:.9;letter-spacing:-.05em}.orientation h1 br{display:none}.orientation h1 em{margin-left:.35rem}.orientation .eyebrow,.orientation .lede{display:none}.progress{grid-template-columns:repeat(4,1fr);margin-top:.15rem}.step{min-height:38px;padding:.35rem;font-size:.66rem}.state-banner{margin-top:.3rem;padding:.4rem .6rem;align-items:center}.state-banner p{display:none}.workspace{margin-top:.3rem}.artifact{padding:.5rem 1rem}.primary-input{margin-bottom:.5rem;padding:.5rem}.primary-input label{margin-bottom:.3rem}} +@media(prefers-color-scheme:dark){:root{color-scheme:dark;--ink:#f3f1e9;--muted:#aaa99f;--paper:#111310;--panel:#191b17;--line:#383b34;--lime:#b8e85b;--violet:#a99cff;--soft:#29263a}header{background:rgba(17,19,16,.92)}.brand{color:var(--ink)}.primary,.approve{color:#111313;box-shadow:3px 3px 0 #777d70}.secondary{color:var(--ink)}.workspace nav>a.active{background:#f3f1e9;color:#171817}.artifact-body{background:#131511}aside{background:#201e2b}.proposal{background:rgba(0,0,0,.18)}.completion{color:#111313}.error{background:#341c18;color:#ffb7aa}.state-banner[data-kind="waiting"],.state-banner[data-kind="partial"]{color:#211806}.state-banner[data-kind="waiting"] p,.state-banner[data-kind="partial"] p{color:#5e4a18}.state-banner[data-kind="error"],.state-banner[data-kind="failure"],.state-banner[data-kind="conflict"]{color:#3e100c}.state-banner[data-kind="error"] p,.state-banner[data-kind="failure"] p,.state-banner[data-kind="conflict"] p{color:#6c3a34}.state-banner[data-kind="complete"],.state-banner[data-kind="receipt_inspection"],.state-banner[data-kind="export_share"]{color:#111313}.state-banner[data-kind="complete"] p,.state-banner[data-kind="receipt_inspection"] p,.state-banner[data-kind="export_share"] p{color:#31431b}} +@media(prefers-reduced-motion:reduce){*{scroll-behavior:auto!important;transition:none!important}} diff --git a/templates/base/apps/web/server.mjs b/templates/base/apps/web/server.mjs new file mode 100644 index 0000000..0423607 --- /dev/null +++ b/templates/base/apps/web/server.mjs @@ -0,0 +1,156 @@ +import { createServer } from "node:http"; +import { readFile } from "node:fs/promises"; +import path from "node:path"; +import { createGuidedDemo } from "../../agent/workflow.mjs"; + +const port = Number(process.env.PORT ?? 4173); +const host = process.env.HOST ?? "127.0.0.1"; +const publicRoot = path.resolve("apps", "web", "public"); +const types = { ".css": "text/css; charset=utf-8", ".html": "text/html; charset=utf-8", ".js": "text/javascript; charset=utf-8" }; +let demo; +let current; +let presentation; + +function reset() { + demo = createGuidedDemo(); + current = demo.start(); + presentation = { + id: "first_arrival", + kind: "orientation", + message: "Confirm the outcome before the run begins.", + title: "Ready for your direction", + }; + return view(); +} + +function view() { + const snapshot = demo.runtime.snapshot(); + return { + artifact: snapshot.artifacts[0], + approvals: snapshot.approvals, + case: snapshot.cases[0], + events: snapshot.events, + exceptions: snapshot.exceptions, + presentation, + proposal: snapshot.proposals.at(-1) ?? null, + receipt: snapshot.receipts.at(-1) ?? null, + run: snapshot.runs[0], + }; +} + +function setPresentation(id, kind, title, message) { + presentation = { id, kind, message, title }; +} + +function prepareProposal() { + return demo.propose({ artifactId: current.artifact.artifactId, runId: current.run.runId }); +} + +function loadScenario(id) { + reset(); + if (id === "first_arrival") return view(); + if (id === "orientation") { + setPresentation(id, "orientation", "One clear job", "Review the intended outcome, required inputs, and completion promise."); + } else if (id === "input") { + setPresentation(id, "input", "Describe the outcome", "The case will not start until its required outcome is present."); + } else if (id === "validation_error") { + setPresentation(id, "error", "Outcome required", "Add a concrete outcome so the system can create a bounded case."); + } else if (id === "running") { + demo.runtime.enterStage({ runId: current.run.runId, stageId: "working", nextAction: "Prepare the bounded proposal", nextActionOwner: "agent" }); + setPresentation(id, "running", "Work is in progress", "The canonical artifact remains unchanged while the agent prepares a proposal."); + } else if (id === "partial_result") { + demo.runtime.enterStage({ runId: current.run.runId, stageId: "working", nextAction: "Validate the partial result", nextActionOwner: "system" }); + setPresentation(id, "partial", "Partial result preserved", "A checkpoint is visible, but it is not yet a proposal or canonical output."); + } else if (id === "external_wait") { + demo.runtime.raiseException({ runId: current.run.runId, code: "external_review_wait", message: "Waiting for the external reviewer.", preservedState: { artifactVersion: 1 } }); + setPresentation(id, "waiting", "Waiting on an external reviewer", "The last valid artifact is preserved. No action is required from you yet."); + } else if (id === "proposal_pending" || id === "approval") { + prepareProposal(); + setPresentation(id, "review", id === "approval" ? "Your approval is required" : "Proposal ready for review", "Compare the bounded change with the canonical artifact before deciding."); + } else if (id === "conflict") { + const winner = demo.runtime.createProposal({ artifactId: current.artifact.artifactId, baseVersion: 1, patch: { summary: "A newer approved result.", status: "accepted" }, rationale: "Create the newer canonical version." }); + const stale = demo.runtime.createProposal({ artifactId: current.artifact.artifactId, baseVersion: 1, patch: { summary: "A stale competing result.", status: "proposed" }, rationale: "Exercise stale-write protection." }); + demo.runtime.decideProposal({ proposalId: winner.proposalId, decision: "accepted" }); + demo.runtime.decideProposal({ proposalId: stale.proposalId, decision: "accepted" }); + demo.runtime.enterStage({ runId: current.run.runId, stageId: "review", nextAction: "Resolve the version conflict", nextActionOwner: "user" }); + setPresentation(id, "conflict", "Conflict contained", "The stale proposal was not applied. Canonical version 2 remains intact."); + } else if (id === "recoverable_failure") { + demo.runtime.raiseException({ runId: current.run.runId, code: "source_unavailable", message: "A required source could not be reached.", preservedState: { artifactVersion: 1 } }); + setPresentation(id, "failure", "Failed safely", "The last valid artifact is preserved. Retry the unavailable source or continue with a warning."); + } else if (id === "reload_resume") { + prepareProposal(); + setPresentation(id, "resume", "Run resumed after reload", "The pending proposal and next action survived reload without restarting the case."); + } else if (["completed_receipt", "receipt_inspection", "export_share"].includes(id)) { + const proposal = prepareProposal(); + demo.decide({ decision: "accepted", proposalId: proposal.proposalId, runId: current.run.runId }); + const copy = id === "receipt_inspection" + ? ["Receipt inspection", "Review the bound artifact, proposal, event, and receipt identifiers."] + : id === "export_share" + ? ["Ready to export and share", "The canonical artifact and its content-addressed receipt travel together."] + : ["Completion verified", "The canonical artifact and content-addressed receipt are ready."]; + setPresentation(id, id === "completed_receipt" ? "complete" : id, copy[0], copy[1]); + } else { + throw new Error(`unknown browser scenario: ${id}`); + } + return view(); +} + +function send(response, status, body, contentType = "application/json; charset=utf-8") { + response.writeHead(status, { "cache-control": "no-store", "content-type": contentType }); + response.end(contentType.startsWith("application/json") ? JSON.stringify(body) : body); +} + +async function body(request) { + const chunks = []; + for await (const chunk of request) chunks.push(chunk); + return chunks.length ? JSON.parse(Buffer.concat(chunks).toString("utf8")) : {}; +} + +async function api(request, response, url) { + if (request.method === "GET" && url.pathname === "/api/health") return send(response, 200, { + application: "__APP_NAME__", + certificationRunId: process.env.NODEKIT_BROWSER_RUN_ID ?? null, + serverPid: process.pid, + status: "ok", + }); + if (request.method === "GET" && url.pathname === "/api/state") return send(response, 200, view()); + if (request.method === "POST" && url.pathname === "/api/reset") return send(response, 200, reset()); + if (request.method === "POST" && url.pathname === "/api/scenario") { + const input = await body(request); + return send(response, 200, loadScenario(input.id)); + } + if (request.method === "POST" && url.pathname === "/api/propose") { + if (view().proposal?.status === "pending") throw new Error("review the current proposal first"); + prepareProposal(); + setPresentation("proposal_pending", "review", "Proposal ready for review", "Compare the bounded change with the canonical artifact before deciding."); + return send(response, 200, view()); + } + if (request.method === "POST" && url.pathname === "/api/decide") { + const input = await body(request); + const proposal = view().proposal; + if (!proposal) throw new Error("create a proposal first"); + demo.decide({ decision: input.decision, proposalId: proposal.proposalId, runId: current.run.runId }); + if (input.decision === "accepted") setPresentation("completed_receipt", "complete", "Completion verified", "The canonical artifact and content-addressed receipt are ready."); + return send(response, 200, view()); + } + return false; +} + +reset(); +const server = createServer(async (request, response) => { + try { + const url = new URL(request.url, `http://${request.headers.host ?? host}`); + if (url.pathname.startsWith("/api/")) { + if (await api(request, response, url) !== false) return; + return send(response, 404, { error: "not found" }); + } + const relative = url.pathname === "/" ? "index.html" : url.pathname.slice(1); + if (relative.includes("..") || path.isAbsolute(relative)) return send(response, 400, "bad path", "text/plain"); + const file = path.join(publicRoot, relative); + send(response, 200, await readFile(file), types[path.extname(file)] ?? "application/octet-stream"); + } catch (error) { + if (error.code === "ENOENT") return send(response, 404, "not found", "text/plain"); + send(response, 400, { error: error.message }); + } +}); +server.listen(port, host, () => console.log(`__APP_TITLE__ running at http://${host}:${port}`)); diff --git a/templates/base/docs/EVOLUTION.md b/templates/base/docs/EVOLUTION.md new file mode 100644 index 0000000..0369a7d --- /dev/null +++ b/templates/base/docs/EVOLUTION.md @@ -0,0 +1,7 @@ +# Evolution discipline + +Record an Evolution Ledger event when work changes a primary workflow, public contract, authority boundary, proof requirement, model route, harness behavior, benchmark conclusion, or downstream guarantee. + +The reasoning chain is limitation -> immutable evidence -> scoped assumption -> invariant -> verifier -> verified adoption. Agents may draft this chain but may not certify their own interpretation, proof, or downstream adoption. + +Run `nodekit evolution verify` before treating ledger history as Harness Gym or Knowledge Evolution input. diff --git a/templates/base/docs/FIGURED_OUT.md b/templates/base/docs/FIGURED_OUT.md new file mode 100644 index 0000000..0961005 --- /dev/null +++ b/templates/base/docs/FIGURED_OUT.md @@ -0,0 +1,37 @@ +# Figured-out contract + +This application is blank in domain and figured out in behavior. + +The user must be able to enter with one intention, understand the next step without training, move through a bounded progression, review generated work before it becomes canonical, recover without losing valid work, and leave with one clear artifact plus proof of completion. + +## Stable product grammar + +```text +Case -> Run -> Stage -> Artifact -> Proposal -> Approval -> Receipt +``` + +- One primary job. +- One obvious starting action. +- One guided progression. +- One canonical output. +- One explicit recovery path. +- One verifiable completion state. + +Infrastructure stays backstage. The main experience exposes progress, consequences, decisions, and the current owner of the next action. Agent activity, tool traces, retries, and provider details remain available but secondary. + +## Clean-state invariants + +- A proposal is never the canonical artifact. +- Every proposal names the artifact version it inspected. +- A stale proposal conflicts instead of overwriting newer work. +- A failed run preserves the last valid artifact. +- Every run ends as completed, awaiting review, blocked, conflicted, cancelled, or failed safely. +- Every completion creates a content-addressed receipt. + +## Specialization boundary + +Change the domain job, guided questions, stages, artifact type, tools, validators, fixtures, and renderer. Do not remove version safety, approval, explicit next-action ownership, exception recovery, or proof. + +When specialization requires durable cross-run knowledge, use `docs/KNOWLEDGE_EVOLUTION.md`. +Persistent knowledge follows the same safety grammar as artifacts: retrieve, propose, validate, +approve, apply, and retain a receipt. diff --git a/templates/base/docs/KNOWLEDGE_EVOLUTION.md b/templates/base/docs/KNOWLEDGE_EVOLUTION.md new file mode 100644 index 0000000..76d18ad --- /dev/null +++ b/templates/base/docs/KNOWLEDGE_EVOLUTION.md @@ -0,0 +1,16 @@ +# Knowledge evolution + +This domain-blank application can add persistent project knowledge without changing its Caseflow +authority model. + +```bash +npx nodekit graph init --repo-root . +npx nodekit graph inspect --repo-root . +``` + +The graph is optional until the researched workflow requires cross-run knowledge. Once enabled, +agents retrieve before broad search, represent missing knowledge as typed gaps, anchor durable +claims to immutable sources, and submit graph patches for validation and explicit approval. + +The graph never writes directly to application artifacts, Caseflow state, source code, benchmarks, +or proof receipts. `DEPRECATE` preserves history; destructive deletion is not supported. diff --git a/templates/base/evals/figured-out-journey.json b/templates/base/evals/figured-out-journey.json new file mode 100644 index 0000000..68bac84 --- /dev/null +++ b/templates/base/evals/figured-out-journey.json @@ -0,0 +1,5 @@ +{ + "id": "figured-out-journey", + "schemaVersion": "nodekit.eval/v1", + "assertions": ["explicit-next-action", "proposal-before-mutation", "receipt-created"] +} diff --git a/templates/base/evals/stale-proposal-conflict.json b/templates/base/evals/stale-proposal-conflict.json new file mode 100644 index 0000000..43c526d --- /dev/null +++ b/templates/base/evals/stale-proposal-conflict.json @@ -0,0 +1,5 @@ +{ + "id": "stale-proposal-conflict", + "schemaVersion": "nodekit.eval/v1", + "assertions": ["stale-proposal-cannot-overwrite", "canonical-version-preserved"] +} diff --git a/templates/base/fixtures/corpus/validation.txt b/templates/base/fixtures/corpus/validation.txt new file mode 100644 index 0000000..8582102 --- /dev/null +++ b/templates/base/fixtures/corpus/validation.txt @@ -0,0 +1 @@ +This neutral fixture exists so compiled identity and drift detection remain deterministic. diff --git a/templates/base/gitignore.template b/templates/base/gitignore.template new file mode 100644 index 0000000..50e32dd --- /dev/null +++ b/templates/base/gitignore.template @@ -0,0 +1,7 @@ +node_modules/ +.nodeagent/ +.data/ +.env +.env.local +proof/*.json +!proof/build-friction.json diff --git a/templates/base/hackathon.yaml b/templates/base/hackathon.yaml new file mode 100644 index 0000000..33dfe4c --- /dev/null +++ b/templates/base/hackathon.yaml @@ -0,0 +1,18 @@ +schemaVersion: nodekit.brief/v1 +application: __APP_NAME__ +problem: __BRIEF_JSON__ +outcome: + - Carry one user intention through a guided case to a reviewable artifact. + - Keep agent work provisional until an explicit approval applies it. + - Preserve the last canonical version when work conflicts or fails. + - Emit a content-addressed completion receipt. +sponsors: +__SPONSORS_YAML__ +constraints: + domainBlank: true + noKeyDemo: true + productionWrites: false +approvals: + paidResourceActivation: human + productionDeployment: human + publicSubmission: human diff --git a/templates/base/integrations/convex/README.md b/templates/base/integrations/convex/README.md new file mode 100644 index 0000000..c4020c8 --- /dev/null +++ b/templates/base/integrations/convex/README.md @@ -0,0 +1,14 @@ +# Convex managed profile + +Convex is NodeKit's preferred first managed backend, but the neutral demo stays on memory so it works without an account. + +Before enabling Convex: + +1. research the real workflow and owner/auth boundary; +2. install only the required Convex features and Components; +3. keep authentication in application-owned wrapper functions; +4. map NodeKit Caseflow semantics to transactional mutations and reactive queries; +5. run the same adapter conformance suite; +6. collect browser and deployment proof from the exact tested commit. + +Do not create a parallel conversation, workflow, workpool, RAG, presence, or streaming subsystem when the corresponding Convex Component satisfies the requirement. diff --git a/templates/base/integrations/convex/capability-plan.yaml b/templates/base/integrations/convex/capability-plan.yaml new file mode 100644 index 0000000..543c74d --- /dev/null +++ b/templates/base/integrations/convex/capability-plan.yaml @@ -0,0 +1,24 @@ +schemaVersion: nodekit.capability-plan/v1 +status: available-after-workflow-research +provider: convex +authority: + authentication: host-application + lifecycle: convex-caseflow-adapter +requiredSemantics: + - proposal-before-mutation + - base-version-check + - owner-scoped-wrapper + - reactive-case-progress + - immutable-receipt +candidateComponents: + agent: optional + workflow: optional + workpool: optional + persistent-streaming: optional + rag: optional + presence: optional +proofRequired: + - adapter-conformance + - authenticated-owner-scope + - browser-journey + - exact-commit-deployment diff --git a/templates/base/nodeagent.yaml b/templates/base/nodeagent.yaml new file mode 100644 index 0000000..56b20de --- /dev/null +++ b/templates/base/nodeagent.yaml @@ -0,0 +1,38 @@ +schemaVersion: nodeagent.application/v1 +application: + id: __APP_NAME__ + name: __APP_TITLE__ + purpose: __BRIEF_JSON__ +authoring: + directory: ./agent +runtime: + engine: nodekit-caseflow + profile: replay +provider: + adapter: deterministic-fixture + model: + provider: local + id: nodekit-guided-demo-v1 + secretRef: NODEKIT_OPTIONAL_MODEL_KEY +backend: + adapter: memory +contracts: + event: nodeagent.event/v1 + trace: nodeagent.trace/v1 +orchestration: + lifecycle: case-run-stage-artifact-proposal-approval-receipt + oneAuthoritativeBackend: true +packs: + - ./packs/primary/pack.yaml +policies: + tools: deny-by-default + proposalBeforeMutation: true + preserveCanonicalStateOnFailure: true + requireBaseVersion: true + requireReceipt: true +evaluations: + required: + - figured-out-journey + - stale-proposal-conflict +secrets: + - envRef: NODEKIT_OPTIONAL_MODEL_KEY diff --git a/templates/base/nodekit.yaml b/templates/base/nodekit.yaml new file mode 100644 index 0000000..57674d2 --- /dev/null +++ b/templates/base/nodekit.yaml @@ -0,0 +1,31 @@ +schemaVersion: nodekit.repo/v1 +registryMode: external +repository: local/__APP_NAME__ +lifecycle: experimental +support: active +role: agent-application +commandProfile: application +canonicalFor: [] +consumes: + - nodeplatform.repo-contract + - nodeagent.agent-run + - proofloop.certification +commands: + dev: { script: dev, mode: service } + demo: { script: demo, mode: finite } + doctor: { script: doctor, mode: finite } + check: { script: check, mode: finite } + proof: { script: proof, mode: finite } +noKey: + status: certified + command: npm run demo + externalAccountsRequired: 0 + disclosure: The neutral Caseflow demonstration uses an in-memory runtime and no network. +environment: + contractVersion: nodeplatform.env/v1 + status: aligned +proof: + command: npm run proof + receiptSchema: nodekit.proof-receipt/v1 +contractDeclarations: [] +architectureExceptions: [] diff --git a/templates/base/package.json b/templates/base/package.json new file mode 100644 index 0000000..0139fb1 --- /dev/null +++ b/templates/base/package.json @@ -0,0 +1,27 @@ +{ + "name": "__APP_NAME__", + "version": "0.1.0", + "private": true, + "type": "module", + "engines": { "node": ">=22.19.0" }, + "scripts": { + "compile": "nodekit compile --repo-root .", + "inspect": "nodekit inspect --repo-root .", + "doctor": "nodekit doctor --repo-root .", + "dev": "node apps/web/server.mjs", + "demo": "node scripts/demo.mjs", + "eval": "node scripts/eval.mjs", + "check": "node scripts/check.mjs", + "proof": "node scripts/proof.mjs", + "proof:browser-contract": "node scripts/browser-proof.mjs", + "proof:browser": "node scripts/browser-certify.mjs", + "audit:prod": "node scripts/audit-prod.mjs" + }, + "dependencies": { + "@homenshum/nodekit": __NODEKIT_SPECIFIER_JSON__ + }, + "devDependencies": { + "@axe-core/playwright": "4.12.1", + "playwright": "1.61.1" + } +} diff --git a/templates/base/packs/primary/pack.yaml b/templates/base/packs/primary/pack.yaml new file mode 100644 index 0000000..0d6fa3e --- /dev/null +++ b/templates/base/packs/primary/pack.yaml @@ -0,0 +1,14 @@ +schemaVersion: nodeagent.pack/v1 +id: guided-case +version: 1.0.0 +description: Domain-blank Caseflow lifecycle demonstration. +tools: [] +artifacts: + - primary-artifact +validators: + - proposal-before-mutation + - stale-proposal-conflicts +policies: [] +evals: + - ../../evals/figured-out-journey.json + - ../../evals/stale-proposal-conflict.json diff --git a/templates/base/product/AUDIENCE.md b/templates/base/product/AUDIENCE.md new file mode 100644 index 0000000..130af5b --- /dev/null +++ b/templates/base/product/AUDIENCE.md @@ -0,0 +1,3 @@ +# Audience + +The initial audience is a person with a real outcome to complete, not an operator who wants to configure models and infrastructure. Research the actual audience before replacing this placeholder. Record their vocabulary, anxiety, constraints, accessibility needs, and evidence threshold. diff --git a/templates/base/product/BRIEF.md b/templates/base/product/BRIEF.md new file mode 100644 index 0000000..dd3cb1d --- /dev/null +++ b/templates/base/product/BRIEF.md @@ -0,0 +1,13 @@ +# Product brief + +## Primary job + +__BRIEF_TEXT__ + +## Product promise + +Carry the user from intent to one reviewed, durable, verifiable result without exposing the underlying agent and backend plumbing. + +## Completion + +The case is complete only when a canonical artifact exists, every material proposal has a decision, unresolved limitations are visible, and a receipt has been issued. diff --git a/templates/base/product/DESIGN.md b/templates/base/product/DESIGN.md new file mode 100644 index 0000000..1adef67 --- /dev/null +++ b/templates/base/product/DESIGN.md @@ -0,0 +1,3 @@ +# Design direction + +The artifact is primary, the agent is adjacent, progress is always visible, pending decisions are collected, transient activity is separated, and advanced traces are secondary. Desktop may use three coordinated regions; mobile must stack or tab them instead of shrinking the desktop layout. diff --git a/templates/base/product/EXPERIENCE.yaml b/templates/base/product/EXPERIENCE.yaml new file mode 100644 index 0000000..6a971e3 --- /dev/null +++ b/templates/base/product/EXPERIENCE.yaml @@ -0,0 +1,11 @@ +schemaVersion: nodekit.experience/v1 +primaryJob: __BRIEF_JSON__ +startAction: Start guided case +canonicalOutput: reviewed artifact +stages: + - { id: intake, title: Confirm the intended outcome, owner: user } + - { id: working, title: Prepare a proposal, owner: agent } + - { id: review, title: Review the proposed change, owner: user } + - { id: complete, title: Verify and export, owner: system } +terminalStates: [completed, awaiting_review, blocked, conflicted, cancelled, failed_safely] +requiredSurfaces: [orientation, progress, current_action, artifact, proposal_review, exception, completion, receipt] diff --git a/templates/base/product/SERVICE_BLUEPRINT.md b/templates/base/product/SERVICE_BLUEPRINT.md new file mode 100644 index 0000000..4d9e310 --- /dev/null +++ b/templates/base/product/SERVICE_BLUEPRINT.md @@ -0,0 +1,10 @@ +# Service blueprint + +| Stage | User sees | NodeKit handles backstage | +|---|---|---| +| Orient | Job, time, inputs, safe starting action | Case identity and policy | +| Intake | One necessary choice at a time | Validation and continuous state | +| Working | Milestones and current owner | Agent execution, queues, retries | +| Review | Artifact, diff, evidence, decision | Proposal validation and version check | +| Recovery | What failed, what survived, next choices | Exception isolation and resumability | +| Completion | Canonical result, warnings, export, proof | Receipt, hashes, version history | diff --git a/templates/base/product/TASTE.md b/templates/base/product/TASTE.md new file mode 100644 index 0000000..c07f631 --- /dev/null +++ b/templates/base/product/TASTE.md @@ -0,0 +1,3 @@ +# Taste contract + +Prefer calm confidence, legible hierarchy, plain language, restrained motion, strong focus states, real empty/error/long-content states, and visible consequences. Do not present an empty dashboard, a chat-only product, decorative telemetry, arbitrary percentages, or a success state without a usable artifact. diff --git a/templates/base/product/USER_JOURNEY.md b/templates/base/product/USER_JOURNEY.md new file mode 100644 index 0000000..f02d555 --- /dev/null +++ b/templates/base/product/USER_JOURNEY.md @@ -0,0 +1,15 @@ +# User journey + +```text +Orient +-> confirm the intended outcome +-> start a bounded run +-> observe meaningful progress +-> inspect the primary artifact +-> review the proposed change +-> approve or reject +-> verify completion +-> reopen, revise, or export +``` + +At every state, the interface answers: what happened, what is happening, who owns the next action, and what happens afterward. diff --git a/templates/base/proof/README.md b/templates/base/proof/README.md new file mode 100644 index 0000000..94e8867 --- /dev/null +++ b/templates/base/proof/README.md @@ -0,0 +1,3 @@ +# Proof outputs + +Generated receipts live here. Do not commit secrets, production data, or unsupported claims. diff --git a/templates/base/render.yaml b/templates/base/render.yaml new file mode 100644 index 0000000..d37ca63 --- /dev/null +++ b/templates/base/render.yaml @@ -0,0 +1,6 @@ +services: + - type: web + name: __APP_NAME__ + runtime: docker + healthCheckPath: /api/health + autoDeploy: false diff --git a/templates/base/scripts/audit-prod.mjs b/templates/base/scripts/audit-prod.mjs new file mode 100644 index 0000000..e8470a4 --- /dev/null +++ b/templates/base/scripts/audit-prod.mjs @@ -0,0 +1,3 @@ +import { spawn } from "node:child_process"; +const child = spawn("npm", ["audit", "--omit=dev"], { shell: process.platform === "win32", stdio: "inherit" }); +await new Promise((resolve, reject) => { child.on("error", reject); child.on("exit", (code) => code === 0 ? resolve() : reject(new Error(`npm audit exited ${code}`))); }); diff --git a/templates/base/scripts/browser-certify.mjs b/templates/base/scripts/browser-certify.mjs new file mode 100644 index 0000000..1450f1c --- /dev/null +++ b/templates/base/scripts/browser-certify.mjs @@ -0,0 +1,295 @@ +import { createHash, randomUUID } from "node:crypto"; +import { execFileSync, spawn } from "node:child_process"; +import { copyFile, mkdir, readFile, rm, stat, writeFile } from "node:fs/promises"; +import path from "node:path"; +import AxeBuilder from "@axe-core/playwright"; +import { chromium } from "playwright"; + +const startedAt = new Date().toISOString(); +const started = performance.now(); +const runId = process.env.NODEKIT_EASE_RUN_ID ?? `ease_${randomUUID().replaceAll("-", "").slice(0, 20)}`; +const port = Number(process.env.NODEKIT_BROWSER_PORT ?? 43000 + (process.pid % 1000)); +const baseUrl = `http://127.0.0.1:${port}`; +const evidenceRoot = path.resolve("proof", "ease", runId); +const browserRoot = path.join(evidenceRoot, "browser"); +const screenshotRoot = path.join(evidenceRoot, "browser", "screenshots"); +const identity = JSON.parse(await readFile(path.resolve(".nodeagent", "application-identity.json"), "utf8")); +const generatedCandidateCommit = execFileSync("git", ["rev-parse", "HEAD"], { encoding: "utf8" }).trim(); +const nodekitCommit = process.env.NODEKIT_SOURCE_COMMIT ?? null; +const nodekitSourceHash = process.env.NODEKIT_SOURCE_HASH ?? null; +const requiredStates = [ + "first_arrival", "orientation", "input", "validation_error", "running", "partial_result", + "external_wait", "proposal_pending", "approval", "conflict", "recoverable_failure", + "reload_resume", "completed_receipt", "receipt_inspection", "export_share", +]; +const viewports = [ + { id: "desktop", width: 1440, height: 900 }, + { id: "wide", width: 1920, height: 1080 }, + { id: "tablet-landscape", width: 1024, height: 768 }, + { id: "tablet-portrait", width: 768, height: 1024 }, + { id: "mobile-portrait", width: 390, height: 844 }, + { id: "mobile-landscape", width: 844, height: 390 }, +]; +const screenshots = []; +const globalConsole = []; +const globalNetwork = []; +const accessibilityViolations = []; +const phases = []; +const milestones = []; +const evidenceArtifacts = []; +const journeyAssertions = { + approvalApplied: false, + proposalVisible: false, + receiptSurvivedReload: false, + receiptVisible: false, +}; +let firstMeaningfulPaintMs = null; + +function sha256(bytes) { + return createHash("sha256").update(bytes).digest("hex"); +} + +function mark(name, phaseStarted) { + phases.push({ at: new Date().toISOString(), durationMs: Math.round(performance.now() - phaseStarted), name }); +} + +function milestone(name) { + if (!milestones.some((entry) => entry.name === name)) milestones.push({ at: new Date().toISOString(), elapsedMs: Math.round(performance.now() - started), name }); +} + +async function recordArtifact(id, absolutePath) { + const bytes = await readFile(absolutePath); + evidenceArtifacts.push({ byteSize: (await stat(absolutePath)).size, id, path: path.relative(evidenceRoot, absolutePath).replaceAll("\\", "/"), sha256: sha256(bytes) }); +} + +async function waitForServer() { + const phaseStarted = performance.now(); + for (let attempt = 0; attempt < 100; attempt += 1) { + try { + const response = await fetch(`${baseUrl}/api/health`); + const health = response.ok ? await response.json() : null; + if (health?.certificationRunId === runId && health?.serverPid === server.pid) { + mark("server_readiness", phaseStarted); + return; + } + } catch {} + await new Promise((resolve) => setTimeout(resolve, 100)); + } + throw new Error("Playwright certification server did not become ready"); +} + +async function capture(page, state, viewport, theme, observations) { + const relativePng = path.join("browser", "screenshots", `${state}--${viewport.id}--${theme}.png`); + const absolutePng = path.join(evidenceRoot, relativePng); + const bytes = await page.screenshot({ path: absolutePng }); + const horizontalOverflowPx = await page.evaluate(() => Math.max(0, document.documentElement.scrollWidth - window.innerWidth)); + const renderedText = await page.locator("body").innerText(); + const sidecar = { + applicationHash: identity.applicationHash, + capturedAt: new Date().toISOString(), + configHash: identity.configHash, + consoleErrors: observations.consoleErrors.length, + elapsedMs: Math.round(performance.now() - started), + failedRequests: observations.failedRequests.length, + generatedCandidateCommit, + horizontalOverflowPx, + mojibakeDetected: /(?:Â|Ã|â€)/.test(renderedText), + nodekitCommit, + nodekitSourceHash, + pageUrl: page.url(), + pngSha256: sha256(bytes), + runId, + schemaVersion: "nodekit.screenshot-proof/v1", + serverProcess: { command: `${process.execPath} apps/web/server.mjs`, pid: server.pid }, + state, + theme, + viewport: { height: viewport.height, width: viewport.width }, + }; + const sidecarPath = absolutePng.replace(/\.png$/i, ".json"); + await writeFile(sidecarPath, `${JSON.stringify(sidecar, null, 2)}\n`); + screenshots.push({ + ...sidecar, + path: relativePng.replaceAll("\\", "/"), + sidecarPath: path.relative(evidenceRoot, sidecarPath).replaceAll("\\", "/"), + }); +} + +await mkdir(screenshotRoot, { recursive: true }); +await mkdir(path.join(browserRoot, "video"), { recursive: true }); +const server = spawn(process.execPath, [path.resolve("apps", "web", "server.mjs")], { + env: { ...process.env, NODEKIT_BROWSER_RUN_ID: runId, PORT: String(port) }, + stdio: ["ignore", "pipe", "pipe"], +}); +let browser; +let passed = false; +let error = null; +try { + await waitForServer(); + browser = await chromium.launch({ headless: true }); + for (const viewport of viewports) { + for (const theme of ["light", "dark"]) { + const canonicalJourney = viewport.id === "desktop" && theme === "light"; + const context = await browser.newContext({ + colorScheme: theme, + ...(canonicalJourney ? { recordVideo: { dir: path.join(browserRoot, "video"), size: { height: 900, width: 1440 } } } : {}), + reducedMotion: viewport.id === "mobile-portrait" ? "reduce" : "no-preference", + viewport: { height: viewport.height, width: viewport.width }, + }); + if (canonicalJourney) await context.tracing.start({ screenshots: true, snapshots: true, sources: true }); + const page = await context.newPage(); + const video = canonicalJourney ? page.video() : null; + const observations = { consoleErrors: [], failedRequests: [] }; + page.on("console", (message) => { + if (message.type() === "error") observations.consoleErrors.push({ text: message.text(), type: message.type() }); + }); + page.on("requestfailed", (request) => observations.failedRequests.push({ error: request.failure()?.errorText ?? "unknown", url: request.url() })); + if (canonicalJourney) { + const resetResponse = await fetch(`${baseUrl}/api/reset`, { method: "POST" }); + if (!resetResponse.ok) throw new Error(`canonical journey reset failed with HTTP ${resetResponse.status}`); + milestone("canonical_state_reset"); + await page.goto(baseUrl, { waitUntil: "networkidle" }); + await page.locator("#case-title").waitFor(); + firstMeaningfulPaintMs = Math.round(performance.now() - started); + milestone("first_meaningful_paint"); + milestone("neutral_case_visible"); + await page.locator("#propose").click(); + await page.locator("#proposal strong").waitFor(); + journeyAssertions.proposalVisible = true; + milestone("proposal_visible"); + await page.locator("#approve").click(); + await page.locator("#completion").waitFor({ state: "visible" }); + journeyAssertions.approvalApplied = true; + journeyAssertions.receiptVisible = (await page.locator("#receipt-id").innerText()).startsWith("Receipt "); + milestone("approval_applied"); + milestone("receipt_visible"); + const receiptBeforeReload = await page.locator("#receipt-id").innerText(); + await page.reload({ waitUntil: "networkidle" }); + journeyAssertions.receiptSurvivedReload = (await page.locator("#receipt-id").innerText()) === receiptBeforeReload; + milestone("receipt_reload_confirmed"); + } + for (const state of requiredStates) { + await page.goto(`${baseUrl}/?scenario=${state}`, { waitUntil: "networkidle" }); + await page.locator("#case-title").waitFor(); + await page.locator(`body[data-scenario="${state}"]`).waitFor(); + if (firstMeaningfulPaintMs === null) firstMeaningfulPaintMs = Math.round(performance.now() - started); + if (state === "first_arrival") { + const inputBox = await page.locator("#primary-input").boundingBox(); + const actionBox = await page.locator("#primary-input button").boundingBox(); + const visibleInputHeight = inputBox ? Math.max(0, Math.min(inputBox.y + inputBox.height, viewport.height) - Math.max(inputBox.y, 0)) : 0; + if (!inputBox || !actionBox || visibleInputHeight < Math.min(88, inputBox.height) + || actionBox.y < 0 || actionBox.y + actionBox.height > viewport.height) { + throw new Error(`primary action is not visible on first arrival for ${viewport.id}/${theme}: ${JSON.stringify({ actionBox, inputBox, viewport, visibleInputHeight })}`); + } + if (await page.locator("#reset").isVisible()) { + throw new Error(`reset control distracts from the first action for ${viewport.id}/${theme}`); + } + } + if (state === "validation_error") { + await page.locator("#outcome").fill(""); + await page.locator("#primary-input button").click(); + await page.locator("#error").waitFor({ state: "visible" }); + } + if (state === "approval") await page.locator("#approve").focus(); + if (state === "receipt_inspection" || state === "export_share") { + await page.locator("#receipt-detail").waitFor({ state: "visible" }); + if (viewport.id === "mobile-landscape" || viewport.id === "mobile-portrait") await page.locator("#receipt-detail").scrollIntoViewIfNeeded(); + } + if (state === "recoverable_failure" || state === "external_wait") await page.locator("#exception").waitFor({ state: "visible" }); + if (state === "conflict") { + const copy = await page.locator("#proposal").innerText(); + if (!copy.includes("conflicted")) throw new Error(`conflict state was not visible for ${viewport.id}/${theme}`); + } + if (viewport.id === "desktop" && theme === "light") { + const axe = await new AxeBuilder({ page }).analyze(); + accessibilityViolations.push(...axe.violations + .filter((violation) => violation.impact === "serious" || violation.impact === "critical") + .map((violation) => ({ + help: violation.help, + id: violation.id, + impact: violation.impact, + nodes: violation.nodes.map((node) => node.target), + state, + }))); + } + await capture(page, state, viewport, theme, observations); + if (state === "reload_resume") { + const proposalBeforeReload = await page.locator("#proposal").innerText(); + await page.reload({ waitUntil: "networkidle" }); + const proposalAfterReload = await page.locator("#proposal").innerText(); + if (!proposalBeforeReload.includes("Proposed change") || proposalAfterReload !== proposalBeforeReload) { + throw new Error(`proposal did not survive reload for ${viewport.id}/${theme}`); + } + } + if (state === "completed_receipt") { + const receiptBeforeReload = await page.locator("#receipt-id").innerText(); + await page.reload({ waitUntil: "networkidle" }); + const receiptAfterReload = await page.locator("#receipt-id").innerText(); + if (!receiptBeforeReload.startsWith("Receipt ") || receiptAfterReload !== receiptBeforeReload) { + throw new Error(`receipt did not survive reload for ${viewport.id}/${theme}`); + } + } + } + globalConsole.push(...observations.consoleErrors.map((entry) => ({ ...entry, theme, viewport: viewport.id }))); + globalNetwork.push(...observations.failedRequests.map((entry) => ({ ...entry, theme, viewport: viewport.id }))); + if (canonicalJourney) await context.tracing.stop({ path: path.join(browserRoot, "playwright-trace.zip") }); + await context.close(); + if (canonicalJourney && video) { + const recordedVideo = await video.path(); + await copyFile(recordedVideo, path.join(browserRoot, "journey.webm")); + await rm(path.join(browserRoot, "video"), { force: true, recursive: true }); + await recordArtifact("playwright-trace", path.join(browserRoot, "playwright-trace.zip")); + await recordArtifact("browser-video", path.join(browserRoot, "journey.webm")); + } + await fetch(`${baseUrl}/api/reset`, { method: "POST" }); + } + } + passed = screenshots.length === viewports.length * 2 * requiredStates.length + && accessibilityViolations.length === 0 + && evidenceArtifacts.length === 2 + && Object.values(journeyAssertions).every(Boolean) + && screenshots.every((entry) => entry.consoleErrors === 0 && entry.failedRequests === 0 && entry.horizontalOverflowPx === 0 && entry.mojibakeDetected === false); +} catch (caught) { + error = caught instanceof Error ? caught.stack ?? caught.message : String(caught); +} finally { + await browser?.close(); + server.kill(); +} + +const coveredStates = [...new Set(screenshots.map((entry) => entry.state))]; +const missingStates = requiredStates.filter((state) => !coveredStates.includes(state)); +const manifest = { + accessibilityViolations, + applicationHash: identity.applicationHash, + certified: passed && missingStates.length === 0 && Boolean(nodekitCommit) && Boolean(nodekitSourceHash), + configHash: identity.configHash, + consoleErrors: globalConsole, + coveredStates, + durationMs: Math.round(performance.now() - started), + error, + evidenceArtifacts, + firstMeaningfulPaintMs, + generatedAt: new Date().toISOString(), + generatedCandidateCommit, + journeyAssertions, + milestones, + missingStates, + networkFailures: globalNetwork, + nodekitCommit, + nodekitSourceHash, + passed, + phases, + requiredStates, + runId, + schemaVersion: "nodekit.browser-certification/v1", + serverProcess: { command: `${process.execPath} apps/web/server.mjs`, pid: server.pid }, + screenshots, + startedAt, + verdict: passed && missingStates.length === 0 ? "BROWSER_CERTIFIED" : "BROWSER_JOURNEY_PASS_COVERAGE_INCOMPLETE", +}; +manifest.manifestSha256 = sha256(Buffer.from(JSON.stringify(manifest))); +await writeFile(path.join(evidenceRoot, "browser", "console.jsonl"), globalConsole.map((entry) => JSON.stringify(entry)).join("\n") + (globalConsole.length ? "\n" : "")); +await writeFile(path.join(evidenceRoot, "browser", "network.jsonl"), globalNetwork.map((entry) => JSON.stringify(entry)).join("\n") + (globalNetwork.length ? "\n" : "")); +await writeFile(path.join(evidenceRoot, "browser", "screenshot-manifest.json"), `${JSON.stringify(manifest, null, 2)}\n`); +await writeFile(path.resolve("proof", "browser-certification.json"), `${JSON.stringify(manifest, null, 2)}\n`); +console.log(JSON.stringify(manifest, null, 2)); +if (!passed) process.exitCode = 1; diff --git a/templates/base/scripts/browser-proof.mjs b/templates/base/scripts/browser-proof.mjs new file mode 100644 index 0000000..49a0a6a --- /dev/null +++ b/templates/base/scripts/browser-proof.mjs @@ -0,0 +1,42 @@ +import { spawn } from "node:child_process"; +import { randomUUID } from "node:crypto"; +import { mkdir, writeFile } from "node:fs/promises"; +import path from "node:path"; +import { recordFriction } from "./lib/friction.mjs"; + +const port = 42731; +const runId = `browser_contract_${randomUUID()}`; +const child = spawn(process.execPath, [path.resolve("apps/web/server.mjs")], { + env: { ...process.env, NODEKIT_BROWSER_RUN_ID: runId, PORT: String(port) }, + stdio: ["ignore", "pipe", "pipe"], +}); +try { + let ready = false; + for (let attempt = 0; attempt < 40; attempt += 1) { + try { + const response = await fetch(`http://127.0.0.1:${port}/api/health`); + const health = response.ok ? await response.json() : null; + ready = health?.certificationRunId === runId && health?.serverPid === child.pid; + if (ready) break; + } catch {} + await new Promise((resolve) => setTimeout(resolve, 100)); + } + if (!ready) throw new Error("browser-proof server did not become ready"); + const page = await fetch(`http://127.0.0.1:${port}/`).then((response) => response.text()); + const state = await fetch(`http://127.0.0.1:${port}/api/state`).then((response) => response.json()); + const assertions = { + artifactPrimary: page.indexOf("Primary artifact") < page.indexOf("Agent activity"), + currentActionVisible: page.includes("Current action") && Boolean(state.run.nextAction), + mobileContractPresent: page.includes("viewport") && page.includes("review-tab"), + proposalBoundaryVisible: page.includes("Review proposal"), + semanticLandmarks: /]/.test(page) && /]/.test(page), + }; + const receipt = { assertions, generatedAt: new Date().toISOString(), note: "Structural live HTTP and source-DOM contract only. This is not rendered-browser certification.", passed: Object.values(assertions).every(Boolean), schemaVersion: "nodekit.browser-contract/v1" }; + await mkdir("proof", { recursive: true }); + await writeFile(path.resolve("proof", "browser-contract.json"), `${JSON.stringify(receipt, null, 2)}\n`); + await recordFriction(receipt.passed ? "browser_contract_completed" : "browser_contract_failed", assertions); + console.log(JSON.stringify(receipt, null, 2)); + if (!receipt.passed) process.exitCode = 1; +} finally { + child.kill(); +} diff --git a/templates/base/scripts/check.mjs b/templates/base/scripts/check.mjs new file mode 100644 index 0000000..151f6a5 --- /dev/null +++ b/templates/base/scripts/check.mjs @@ -0,0 +1,15 @@ +import { spawnSync } from "node:child_process"; +import path from "node:path"; +import { recordFriction } from "./lib/friction.mjs"; + +const started = Date.now(); +const nodekit = path.resolve("node_modules", "@homenshum", "nodekit", "src", "cli.mjs"); +for (const [command, args] of [[process.execPath, [nodekit, "compile", "--repo-root", ".", "--check"]], [process.execPath, ["--test"]]]) { + const result = spawnSync(command, args, { stdio: "inherit" }); + if (result.error) throw result.error; + if (result.status !== 0) { + await recordFriction("tests_failed", { command, status: result.status }, Date.now() - started); + throw new Error(`${command} exited ${result.status}`); + } +} +await recordFriction("tests_passed", { compileHashCurrent: true }, Date.now() - started); diff --git a/templates/base/scripts/demo.mjs b/templates/base/scripts/demo.mjs new file mode 100644 index 0000000..bc9ead6 --- /dev/null +++ b/templates/base/scripts/demo.mjs @@ -0,0 +1,28 @@ +import { mkdir, writeFile } from "node:fs/promises"; +import path from "node:path"; +import { createGuidedDemo } from "../agent/workflow.mjs"; +import { recordFriction } from "./lib/friction.mjs"; + +const started = Date.now(); +const demo = createGuidedDemo(); +const initial = demo.start(); +const proposal = demo.propose({ artifactId: initial.artifact.artifactId, runId: initial.run.runId }); +const completed = demo.decide({ decision: "accepted", proposalId: proposal.proposalId, runId: initial.run.runId }); +const snapshot = demo.runtime.snapshot(); +const receipt = { + assertions: { + canonicalVersionAdvanced: completed.artifact.canonicalVersion === 2, + explicitNextAction: snapshot.runs[0].nextActionOwner === "user", + proposalAccepted: completed.proposal.status === "accepted", + receiptCreated: Boolean(completed.receipt.receiptHash), + }, + receipt: completed.receipt, + schemaVersion: "nodekit.figured-out-demo/v1", + snapshot, +}; +receipt.passed = Object.values(receipt.assertions).every(Boolean); +await mkdir("proof", { recursive: true }); +await writeFile(path.resolve("proof", "demo-receipt.json"), `${JSON.stringify(receipt, null, 2)}\n`); +await recordFriction(receipt.passed ? "deterministic_demo_passed" : "deterministic_demo_failed", receipt.assertions, Date.now() - started); +console.log(JSON.stringify(receipt, null, 2)); +if (!receipt.passed) process.exitCode = 1; diff --git a/templates/base/scripts/eval.mjs b/templates/base/scripts/eval.mjs new file mode 100644 index 0000000..8ddebab --- /dev/null +++ b/templates/base/scripts/eval.mjs @@ -0,0 +1,26 @@ +import { mkdir, writeFile } from "node:fs/promises"; +import path from "node:path"; +import { createGuidedDemo } from "../agent/workflow.mjs"; +import { recordFriction } from "./lib/friction.mjs"; + +const started = Date.now(); +const demo = createGuidedDemo(); +const initial = demo.start(); +const first = demo.runtime.createProposal({ artifactId: initial.artifact.artifactId, baseVersion: 1, patch: { value: "first" } }); +const stale = demo.runtime.createProposal({ artifactId: initial.artifact.artifactId, baseVersion: 1, patch: { value: "stale" } }); +demo.runtime.decideProposal({ proposalId: first.proposalId, decision: "accepted" }); +const conflict = demo.runtime.decideProposal({ proposalId: stale.proposalId, decision: "accepted" }); +const raised = demo.runtime.raiseException({ runId: initial.run.runId, code: "fixture_exception", message: "Exercise safe recovery", preservedState: { artifactVersion: 2 } }); +const recovered = demo.runtime.resolveException({ exceptionId: raised.exceptionId, resolution: "Fixture resolved", nextAction: "Continue", nextActionOwner: "agent" }); +const assertions = { + canonicalStatePreserved: conflict.artifact.canonicalVersion === 2 && conflict.artifact.versions.at(-1).content.value === "first", + exceptionPreservedState: raised.preservedState.artifactVersion === 2, + recoveryOwnerExplicit: recovered.run.nextActionOwner === "agent", + staleProposalConflicted: conflict.proposal.status === "conflicted", +}; +const receipt = { assertions, generatedAt: new Date().toISOString(), passed: Object.values(assertions).every(Boolean), schemaVersion: "nodekit.eval-receipt/v1" }; +await mkdir("proof", { recursive: true }); +await writeFile(path.resolve("proof", "eval-receipt.json"), `${JSON.stringify(receipt, null, 2)}\n`); +await recordFriction(receipt.passed ? "eval_passed" : "eval_failed", assertions, Date.now() - started); +console.log(JSON.stringify(receipt, null, 2)); +if (!receipt.passed) process.exitCode = 1; diff --git a/templates/base/scripts/lib/friction.mjs b/templates/base/scripts/lib/friction.mjs new file mode 100644 index 0000000..15b7593 --- /dev/null +++ b/templates/base/scripts/lib/friction.mjs @@ -0,0 +1,15 @@ +import { readFile, writeFile } from "node:fs/promises"; +import path from "node:path"; + +export async function recordFriction(name, detail = {}, durationMs) { + const file = path.resolve("proof", "build-friction.json"); + let receipt; + try { + receipt = JSON.parse(await readFile(file, "utf8")); + } catch { + receipt = { events: [], repairLoops: 0, schemaVersion: "nodekit.build-friction/v1" }; + } + if (name.endsWith("_failed")) receipt.repairLoops = (receipt.repairLoops ?? 0) + 1; + receipt.events.push({ at: new Date().toISOString(), detail, ...(durationMs === undefined ? {} : { durationMs }), name }); + await writeFile(file, `${JSON.stringify(receipt, null, 2)}\n`); +} diff --git a/templates/base/scripts/proof.mjs b/templates/base/scripts/proof.mjs new file mode 100644 index 0000000..ae9a0d3 --- /dev/null +++ b/templates/base/scripts/proof.mjs @@ -0,0 +1,48 @@ +import { mkdir, readFile, writeFile } from "node:fs/promises"; +import path from "node:path"; +import { recordFriction } from "./lib/friction.mjs"; + +async function readJson(relative, required = true) { + try { return JSON.parse(await readFile(path.resolve(relative), "utf8")); } + catch (error) { if (!required && error.code === "ENOENT") return null; throw new Error(`missing or invalid ${relative}`); } +} + +const [demo, evaluation, browserContract, browserJourney, identity] = await Promise.all([ + readJson("proof/demo-receipt.json"), + readJson("proof/eval-receipt.json"), + readJson("proof/browser-contract.json", false), + readJson("proof/browser-certification.json", false), + readJson(".nodeagent/application-identity.json"), +]); +const checks = { + browserContractPassed: browserContract === null ? null : browserContract.passed === true, + browserJourneyPassed: browserJourney === null ? null : browserJourney.passed === true, + browserCertified: browserJourney === null ? null : browserJourney.certified === true, + deterministicDemo: demo.passed === true, + deterministicEvaluation: evaluation.passed === true, + identityBound: typeof identity.applicationHash === "string" && typeof identity.configHash === "string", + secretFree: !/(?:sk-[A-Za-z0-9_-]{12,}|-----BEGIN [A-Z ]+PRIVATE KEY-----)/.test(JSON.stringify({ browserContract, browserJourney, demo, evaluation })), +}; +const suppliedBrowserEvidencePassed = checks.browserContractPassed !== false + && checks.browserJourneyPassed !== false; +const localReady = checks.deterministicDemo + && checks.deterministicEvaluation + && checks.identityBound + && checks.secretFree + && suppliedBrowserEvidencePassed; +const receipt = { + applicationHash: identity.applicationHash, + checks, + configHash: identity.configHash, + generatedAt: new Date().toISOString(), + level: checks.browserCertified === true && localReady ? "browser-certified" : "local-ready", + missingReleaseGates: [...(checks.browserCertified === true ? [] : ["browserCertification"]), "deployment", "freshAgentHeldout", "freshHumanUsability", "threeConvexConsumers"], + passed: localReady, + releaseReady: false, + schemaVersion: "nodekit.proof-receipt/v1", +}; +await mkdir("proof", { recursive: true }); +await writeFile(path.resolve("proof", "release-proof.json"), `${JSON.stringify(receipt, null, 2)}\n`); +await recordFriction(receipt.passed ? "proof_passed" : "proof_failed", checks); +console.log(JSON.stringify(receipt, null, 2)); +if (!receipt.passed) process.exitCode = 1; diff --git a/templates/base/test/workflow.test.mjs b/templates/base/test/workflow.test.mjs new file mode 100644 index 0000000..6e943d0 --- /dev/null +++ b/templates/base/test/workflow.test.mjs @@ -0,0 +1,13 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import { createGuidedDemo } from "../agent/workflow.mjs"; + +test("generated base keeps proposals provisional until approval", () => { + const demo = createGuidedDemo(); + const initial = demo.start(); + const proposal = demo.propose({ artifactId: initial.artifact.artifactId, runId: initial.run.runId }); + assert.equal(demo.runtime.snapshot().artifacts[0].canonicalVersion, 1); + const completed = demo.decide({ decision: "accepted", proposalId: proposal.proposalId, runId: initial.run.runId }); + assert.equal(completed.artifact.canonicalVersion, 2); + assert.match(completed.receipt.receiptHash, /^[a-f0-9]{64}$/); +}); diff --git a/test/caseflow.test.mjs b/test/caseflow.test.mjs new file mode 100644 index 0000000..8137293 --- /dev/null +++ b/test/caseflow.test.mjs @@ -0,0 +1,96 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import { CASEFLOW_SCHEMA_VERSIONS, createMemoryCaseflow } from "../src/lib/caseflow.mjs"; +import { runCaseflowConformance } from "../src/lib/caseflow-conformance.mjs"; +import { negotiateRuntimeCapabilities, runtimeProfiles } from "../src/lib/runtime-capabilities.mjs"; + +test("memory runtime passes the provider-neutral adapter conformance suite", async () => { + const result = await runCaseflowConformance(() => createMemoryCaseflow()); + assert.equal(result.passed, true); + assert.equal(result.capabilities.provider, "memory"); +}); + +test("runtime capability negotiation is provider-native and fails closed", () => { + assert.equal(negotiateRuntimeCapabilities(runtimeProfiles.convex).passed, true); + assert.equal(negotiateRuntimeCapabilities(runtimeProfiles.postgres).passed, true); + assert.equal(negotiateRuntimeCapabilities(runtimeProfiles.supabase).passed, true); + const memoryProduction = negotiateRuntimeCapabilities(runtimeProfiles.memory); + assert.equal(memoryProduction.passed, false); + assert.equal(memoryProduction.missing.some((entry) => entry.name === "durableState"), true); +}); + +test("memory caseflow carries one guided transaction to a content-addressed receipt", () => { + let tick = 0; + const runtime = createMemoryCaseflow({ clock: () => `2026-07-21T00:00:${String(tick++).padStart(2, "0")}.000Z` }); + const work = runtime.createCase({ title: "Neutral case", primaryJob: "Produce one reviewable artifact" }); + const run = runtime.startRun({ + caseId: work.caseId, + stages: [ + { id: "intake", label: "Confirm the intended outcome", owner: "user" }, + { id: "working", label: "Prepare a proposal", owner: "agent" }, + { id: "review", label: "Review the proposal", owner: "user" }, + { id: "complete", label: "Verify completion", owner: "system" }, + ], + }); + const artifact = runtime.createArtifact({ caseId: work.caseId, runId: run.runId, title: "Primary artifact", content: { state: "baseline" } }); + runtime.enterStage({ runId: run.runId, stageId: "working" }); + const proposal = runtime.createProposal({ artifactId: artifact.artifactId, baseVersion: 1, patch: { state: "reviewed result" }, rationale: "Complete the bounded case." }); + runtime.enterStage({ runId: run.runId, stageId: "review" }); + const decided = runtime.decideProposal({ proposalId: proposal.proposalId, decision: "accepted" }); + runtime.enterStage({ runId: run.runId, stageId: "complete" }); + const completed = runtime.completeRun({ runId: run.runId }); + + assert.equal(runtime.capabilities.provider, "memory"); + assert.equal(decided.artifact.canonicalVersion, 2); + assert.equal(completed.receipt.schemaVersion, CASEFLOW_SCHEMA_VERSIONS.receipt); + assert.match(completed.receipt.receiptHash, /^[a-f0-9]{64}$/); + assert.equal(runtime.snapshot().cases[0].status, "completed"); +}); + +test("memory caseflow fails stale proposals closed and preserves the canonical artifact", () => { + const runtime = createMemoryCaseflow(); + const work = runtime.createCase({ title: "Concurrency case", primaryJob: "Preserve human-authoritative state" }); + const run = runtime.startRun({ caseId: work.caseId, stages: [{ id: "review", label: "Review", owner: "user" }] }); + const artifact = runtime.createArtifact({ caseId: work.caseId, runId: run.runId, title: "Artifact", content: { value: 1 } }); + const first = runtime.createProposal({ artifactId: artifact.artifactId, baseVersion: 1, patch: { value: 2 } }); + const stale = runtime.createProposal({ artifactId: artifact.artifactId, baseVersion: 1, patch: { value: 99 } }); + runtime.decideProposal({ proposalId: first.proposalId, decision: "accepted" }); + const result = runtime.decideProposal({ proposalId: stale.proposalId, decision: "accepted" }); + + assert.equal(result.proposal.status, "conflicted"); + assert.equal(result.artifact.canonicalVersion, 2); + assert.deepEqual(result.artifact.versions.at(-1).content, { value: 2 }); +}); + +test("memory caseflow makes exception recovery and next-action ownership explicit", () => { + const runtime = createMemoryCaseflow(); + const work = runtime.createCase({ title: "Recovery case", primaryJob: "Recover without losing valid work" }); + const run = runtime.startRun({ caseId: work.caseId, stages: [{ id: "working", label: "Work", owner: "agent" }] }); + const raised = runtime.raiseException({ runId: run.runId, code: "source_unavailable", message: "A source could not be reached", preservedState: { completedItems: 2 } }); + assert.equal(runtime.snapshot().runs[0].status, "blocked"); + assert.equal(runtime.snapshot().runs[0].nextActionOwner, "user"); + const resolved = runtime.resolveException({ exceptionId: raised.exceptionId, resolution: "Use the attached source", nextAction: "Resume preparation", nextActionOwner: "agent" }); + assert.equal(resolved.run.status, "active"); + assert.equal(resolved.run.nextActionOwner, "agent"); +}); + +test("memory caseflow retries decisions and completion without duplicate writes", () => { + const runtime = createMemoryCaseflow(); + const work = runtime.createCase({ title: "Retry safety", primaryJob: "Apply once" }); + const run = runtime.startRun({ caseId: work.caseId, stages: [{ id: "work", label: "Work", owner: "agent" }] }); + const reusedRun = runtime.startRun({ caseId: work.caseId, stages: [{ id: "ignored", label: "Ignored", owner: "system" }] }); + assert.equal(reusedRun.runId, run.runId); + const artifact = runtime.createArtifact({ caseId: work.caseId, runId: run.runId, title: "Result", content: { value: 1 } }); + const proposal = runtime.createProposal({ artifactId: artifact.artifactId, baseVersion: 1, patch: { value: 2 } }); + const first = runtime.decideProposal({ proposalId: proposal.proposalId, decision: "accepted" }); + const repeated = runtime.decideProposal({ proposalId: proposal.proposalId, decision: "accepted" }); + assert.equal(first.reused, false); + assert.equal(repeated.reused, true); + assert.equal(repeated.approval.approvalId, first.approval.approvalId); + assert.equal(runtime.snapshot().artifacts[0].versions.length, 2); + const completed = runtime.completeRun({ runId: run.runId }); + const retriedCompletion = runtime.completeRun({ runId: run.runId }); + assert.equal(retriedCompletion.reused, true); + assert.equal(retriedCompletion.receipt.receiptId, completed.receipt.receiptId); + assert.equal(runtime.snapshot().receipts.length, 1); +}); diff --git a/test/ease-evidence.test.mjs b/test/ease-evidence.test.mjs new file mode 100644 index 0000000..e08429e --- /dev/null +++ b/test/ease-evidence.test.mjs @@ -0,0 +1,78 @@ +import test from "node:test"; +import assert from "node:assert/strict"; +import { evaluateDeveloperTimingMatrix, evaluateFreshUserStudy } from "../src/lib/ease-evidence.mjs"; + +test("fresh-user evidence fails closed without five real participants", () => { + const verdict = evaluateFreshUserStudy({ + instruction: "Use this app to complete the job shown on screen.", + participants: [], + thresholds: { minimumUnassistedCompletions: 4, maximumMedianFirstMeaningfulActionMs: 30000, maximumMedianNeutralJourneyMs: 180000, minimumMedianSingleEaseQuestion: 6, maximumP0P1Failures: 0 }, + }); + assert.equal(verdict.passed, false); + assert.match(verdict.errors.join("\n"), /five fresh participants/); +}); + +test("developer timing matrix fails closed until all sixty isolated trials exist", () => { + const verdict = evaluateDeveloperTimingMatrix([]); + assert.equal(verdict.passed, false); + assert.equal(verdict.requiredRuns, 60); + assert.match(verdict.errors.join("\n"), /share one immutable NodeKit identity/); + assert.equal(verdict.cells["windows/npm"].cold.totalMs.p95, null); + assert.equal(verdict.cells["windows/npm"].cold.totalMs.p95Eligible, false); +}); + +test("developer timing evidence cannot mix otherwise valid revisions", () => { + const lanes = ["windows/npm", "windows/pnpm", "ubuntu/npm", "ubuntu/pnpm", "macos/npm", "macos/pnpm"]; + const runs = lanes.flatMap((lane) => ["cold", "warm"].flatMap((cacheClass) => Array.from({ length: 5 }, (_, index) => ({ + apiKeysRequired: 0, + cacheClass, + cacheIsolated: true, + consoleErrors: 0, + failedCommands: 0, + horizontalOverflowPx: 0, + lane, + manualDecisions: 0, + measurements: { scaffoldGenerationMs: 1, dependencyInstallationMs: 1, compileMs: 1, serverReadinessMs: 1, firstMeaningfulPaintMs: 1, neutralJourneyMs: 1, totalMs: 7 }, + nodekitCommit: index === 0 && lane === "windows/npm" && cacheClass === "cold" ? "b".repeat(40) : "a".repeat(40), + nodekitSourceHash: "c".repeat(64), + receiptProduced: true, + reloadPreserved: true, + runId: `${lane}/${cacheClass}/${index}`, + sourceEdits: 0, + })))); + const verdict = evaluateDeveloperTimingMatrix(runs); + assert.equal(verdict.passed, false); + assert.match(verdict.errors.join("\n"), /found 2/); +}); + +test("fresh-user evidence requires exact revision, consent, timestamps, and session artifacts", () => { + const participants = Array.from({ length: 5 }, (_, index) => ({ + participantId: `participant-${index + 1}`, + fresh: true, + consentRecorded: index !== 0, + sessionStartedAt: "2026-07-22T00:00:00Z", + sessionCompletedAt: "2026-07-22T00:02:00Z", + evidenceRefs: index === 0 ? [] : [`proof/humans/${index + 1}.png`], + firstMeaningfulActionMs: 1000, + neutralJourneyMs: 120000, + wrongTurns: 0, + helpRequests: 0, + singleEaseQuestion: 7, + p0P1Failures: 0, + completed: true, + assisted: false, + canExplainOutcome: true, + locatedFinalArtifact: true, + locatedUnresolvedIssues: true, + })); + const verdict = evaluateFreshUserStudy({ + nodekitCommit: "a".repeat(40), + nodekitSourceHash: "b".repeat(64), + instruction: "Use this app to complete the job shown on screen.", + participants, + thresholds: { minimumUnassistedCompletions: 4, maximumMedianFirstMeaningfulActionMs: 30000, maximumMedianNeutralJourneyMs: 180000, minimumMedianSingleEaseQuestion: 6, maximumP0P1Failures: 0 }, + }); + assert.equal(verdict.passed, false); + assert.match(verdict.errors.join("\n"), /consent is not recorded/); + assert.match(verdict.errors.join("\n"), /screenshot or recording evidence is required/); +}); diff --git a/test/ease-proof.test.mjs b/test/ease-proof.test.mjs new file mode 100644 index 0000000..0303dce --- /dev/null +++ b/test/ease-proof.test.mjs @@ -0,0 +1,96 @@ +import assert from "node:assert/strict"; +import { readFile } from "node:fs/promises"; +import path from "node:path"; +import test from "node:test"; +import { reportsWriteBlockage } from "../src/lib/agent-ease-report.mjs"; +import { requiredSubmissionGates } from "../src/lib/submission-gate.mjs"; + +test("EaseProof keeps browser contracts distinct from certification", async () => { + const proof = await readFile(path.resolve("templates", "base", "scripts", "proof.mjs"), "utf8"); + const contract = await readFile(path.resolve("templates", "base", "scripts", "browser-proof.mjs"), "utf8"); + const browser = await readFile(path.resolve("templates", "base", "scripts", "browser-certify.mjs"), "utf8"); + assert.match(contract, /not rendered-browser certification/); + assert.match(contract, /health\?\.certificationRunId === runId/); + assert.match(contract, /health\?\.serverPid === child\.pid/); + assert.match(proof, /browserContractPassed/); + assert.match(proof, /browserCertified/); + assert.match(browser, /nodekit\.screenshot-proof\/v1/); + assert.match(browser, /missingStates/); + assert.match(browser, /AxeBuilder/); + assert.match(browser, /accessibilityViolations\.length === 0/); + assert.match(browser, /playwright-trace\.zip/); + assert.match(browser, /journey\.webm/); + assert.match(browser, /receipt_reload_confirmed/); + assert.match(browser, /canonical_state_reset/); + assert.match(browser, /health\?\.certificationRunId === runId/); + assert.match(browser, /health\?\.serverPid === server\.pid/); + assert.match(browser, /NODEKIT_BROWSER_RUN_ID: runId/); + assert.match(browser, /serverProcess/); + assert.match(proof, /suppliedBrowserEvidencePassed/); + assert.match(proof, /checks\.browserJourneyPassed !== false/); + for (const state of [ + "first_arrival", "orientation", "input", "validation_error", "running", "partial_result", + "external_wait", "proposal_pending", "approval", "conflict", "recoverable_failure", + "reload_resume", "completed_receipt", "receipt_inspection", "export_share", + ]) assert.match(browser, new RegExp(`\\b${state}\\b`)); + assert.match(browser, /requiredStates\.length/); +}); + +test("generated UI scenarios are backed by caseflow conflict, exception, and receipt records", async () => { + const server = await readFile(path.resolve("templates", "base", "apps", "web", "server.mjs"), "utf8"); + assert.match(server, /decideProposal\(\{ proposalId: stale\.proposalId, decision: "accepted" \}\)/); + assert.match(server, /raiseException\(\{ runId: current\.run\.runId/); + assert.match(server, /"receipt_inspection", "export_share"/); + assert.match(server, /\/api\/scenario/); + assert.match(server, /certificationRunId: process\.env\.NODEKIT_BROWSER_RUN_ID \?\? null/); + assert.match(server, /serverPid: process\.pid/); +}); + +test("fresh-agent recorder preserves process fields and prohibits routine reprompts", async () => { + const harness = await readFile(path.resolve("scripts", "run-agent-ease-trial.mjs"), "utf8"); + assert.match(harness, /stdout: result\.stdout \?\? ""/); + assert.match(harness, /--ephemeral/); + assert.match(harness, /--ignore-user-config/); + assert.match(harness, /--ignore-rules/); + assert.match(harness, /CODEX_PERMISSION_PROFILE/); + assert.match(harness, /candidate write preflight did not round-trip/); + assert.match(harness, /nodekit-ease-agent:codex-0\.142\.5/); + assert.match(harness, /target=\/root\/\.codex\/auth\.json,readonly/); + assert.match(harness, /danger-full-access-inside-disposable-container/); + assert.match(harness, /"--sandbox", "danger-full-access"/); + assert.match(harness, /interventions: 0/); + assert.match(harness, /nodekitCommit,/); + assert.match(harness, /nodekitSourceHash: sourceHash/); + assert.ok(harness.indexOf('const nodekitCommit = run("git"') < harness.indexOf("await createProject")); + assert.ok(harness.indexOf("const sourceHash = await computeNodeKitSourceHash") < harness.indexOf("await createProject")); + assert.match(harness, /checks\.nodekitIdentityStable = endingNodekitCommit === nodekitCommit && endingNodekitSourceHash === sourceHash/); + assert.match(harness, /userReprompts: 0/); + assert.match(harness, /checks\.agentImplemented = substantiveFiles\.length > 0/); + assert.match(harness, /checks\.agentReportedCompletion/); + assert.match(harness, /PILOT_FAIL_AGENT_BLOCKED/); + assert.match(harness, /candidateRoot, "proof", "ease", runId, "browser"/); +}); + +test("fresh-agent completion detection distinguishes domain blockers from write blockage", () => { + assert.equal(reportsWriteBlockage("Added tests for blocked missing-document packets."), false); + assert.equal(reportsWriteBlockage("The workflow surfaces blocked cases for coordinator review."), false); + assert.equal(reportsWriteBlockage("No repository files were changed because the workspace is read-only."), true); + assert.equal(reportsWriteBlockage("I was blocked from writing by permissions."), true); +}); + +test("submission remains fail-closed while external EaseProof gates are open", async () => { + const factory = await readFile(path.resolve("src", "factory-acceptance.mjs"), "utf8"); + assert.match(factory, /submissionReady: false/); + assert.doesNotMatch(factory, /submissionBlockers: \["browserStateCoverage"/); + assert.match(factory, /submissionBlockers: \[\.\.\.requiredSubmissionGates\]/); + assert.deepEqual(requiredSubmissionGates, [ + "developerTimingMatrix", + "freshAgentHeldout", + "freshHumanUsability", + "threeConvexConsumers", + "previewDeployment", + "proofloopEaseVerification", + "packageInstallProof", + "publicationApproval", + ]); +}); diff --git a/test/evolution-ledger.test.mjs b/test/evolution-ledger.test.mjs new file mode 100644 index 0000000..cb77005 --- /dev/null +++ b/test/evolution-ledger.test.mjs @@ -0,0 +1,120 @@ +import assert from "node:assert/strict"; +import { createHash } from "node:crypto"; +import { execFileSync } from "node:child_process"; +import { mkdtemp, mkdir, readFile, writeFile } from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; +import test from "node:test"; +import { + buildEvolutionDocs, + checkEvolutionMateriality, + initializeEvolutionLedger, + proposeEvolutionKnowledgePatch, + queryEvolutionLedger, + recordEvolutionRecord, + verifyEvolutionLedger, +} from "../src/lib/evolution-ledger.mjs"; +import { initializeKnowledgeGraph } from "../src/lib/knowledge-evolution.mjs"; + +function git(root, args) { + return execFileSync("git", args, { cwd: root, encoding: "utf8" }).trim(); +} + +async function fixture() { + const root = await mkdtemp(path.join(os.tmpdir(), "nodekit-evolution-")); + git(root, ["init"]); + git(root, ["config", "user.email", "nodekit@example.com"]); + git(root, ["config", "user.name", "NodeKit Test"]); + await writeFile(path.join(root, "verifier.txt"), "proposal-before-mutation verified\n"); + git(root, ["add", "verifier.txt"]); + git(root, ["commit", "-m", "test invariant"]); + const commit = git(root, ["rev-parse", "HEAD"]); + const bytes = await readFile(path.join(root, "verifier.txt")); + const sha256 = createHash("sha256").update(bytes).digest("hex"); + await initializeEvolutionLedger(root); + const records = [ + { schemaVersion: "nodekit.evolution-evidence/v1", id: "evd:test", kind: "test", artifactRef: "file:verifier.txt", sha256, sourceCommit: commit, generatedAt: new Date().toISOString(), command: "node --test", environment: { platform: process.platform }, verifiesInvariantIds: ["inv:test"], nodeProofReceiptId: "proof:test", result: "pass" }, + { schemaVersion: "nodekit.assumption/v1", id: "asm:test", statement: "Direct mutation was safe", scope: { applications: ["fixture"] }, status: "disproven", introducedByEventId: "evt:test", invalidatedByEventId: "evt:test", supportingEvidenceIds: [], contradictingEvidenceIds: ["evd:test"] }, + { schemaVersion: "nodekit.invariant-claim/v1", id: "inv:test", statement: "Agent writes remain proposals until approval", scope: { applications: ["fixture"] }, enforcement: "runtime-gate", verifierRefs: ["verifier.txt"], introducedByEventId: "evt:test", status: "verified" }, + { schemaVersion: "nodekit.evolution-event/v1", id: "evt:test", projectId: "fixture", repository: "local/fixture", source: { commitSha: commit, occurredAt: new Date().toISOString() }, track: "architecture", category: "runtime", challenge: "Direct mutation corrupted canonical state", observedFailure: "A stale agent write replaced newer work", resolution: "Introduced proposal validation and approval", assumptionIds: ["asm:test"], invariantIds: ["inv:test"], evidenceIds: ["evd:test"], knownLimitations: [], interpretation: { status: "human-reviewed", reviewedBy: "reviewer", reviewedAt: new Date().toISOString() } }, + { schemaVersion: "nodekit.evolution-adoption/v1", id: "adp:test", invariantId: "inv:test", consumer: { repository: "local/fixture", application: "fixture" }, adoptedAtCommit: commit, evidenceIds: ["evd:test"], status: "verified" }, + ]; + await mkdir(path.join(root, "inputs")); + for (const record of records) { + const file = path.join(root, "inputs", `${record.id.replace(":", "-")}.json`); + await writeFile(file, `${JSON.stringify(record, null, 2)}\n`); + await recordEvolutionRecord(root, path.relative(root, file)); + } + return { commit, records, root }; +} + +test("Evolution Ledger verifies causal records, immutable evidence, and consumer adoption", async () => { + const { root } = await fixture(); + const verdict = await verifyEvolutionLedger(root); + assert.equal(verdict.passed, true, verdict.issues.join("\n")); + assert.deepEqual(verdict.counts, { events: 1, assumptions: 1, invariants: 1, evidence: 1, adoptions: 1 }); + const query = await queryEvolutionLedger(root, { invariantId: "inv:test" }); + assert.equal(query.events.length, 1); + assert.equal(query.adoptions[0].status, "verified"); +}); + +test("Evolution Ledger detects evidence drift and refuses canonical overwrite", async () => { + const { records, root } = await fixture(); + await writeFile(path.join(root, "verifier.txt"), "drifted\n"); + const verdict = await verifyEvolutionLedger(root); + assert.equal(verdict.passed, false); + assert.match(verdict.issues.join("\n"), /hash mismatch/); + const event = records.find((record) => record.schemaVersion === "nodekit.evolution-event/v1"); + event.resolution = "silently overwritten"; + const input = path.join(root, "inputs", "changed-event.json"); + await writeFile(input, `${JSON.stringify(event, null, 2)}\n`); + await assert.rejects(() => recordEvolutionRecord(root, path.relative(root, input)), /immutable/); +}); + +test("verified evolution history generates projections and only proposes Knowledge Evolution changes", async () => { + const { root } = await fixture(); + const docs = await buildEvolutionDocs(root); + assert.match(await readFile(docs.output, "utf8"), /proposal validation and approval/i); + await initializeKnowledgeGraph(root, { graphId: "fixture-evolution" }); + const { patch } = await proposeEvolutionKnowledgePatch(root); + assert.equal(patch.status, "pending"); + assert.equal(patch.operations.some((operation) => operation.node?.kind === "evidence"), true); + const graph = JSON.parse(await readFile(path.join(root, ".nodeagent", "knowledge", "graph.json"), "utf8")); + assert.equal(graph.version, 0); + assert.equal(graph.nodes.length, 0); +}); + +test("materiality gate blocks unrecorded system changes and accepts a reviewed event in range", async () => { + const { commit: before, root } = await fixture(); + await mkdir(path.join(root, "src")); + await writeFile(path.join(root, "src", "runtime.mjs"), "export const version = 2;\n"); + git(root, ["add", "src/runtime.mjs"]); + git(root, ["commit", "-m", "material runtime change"]); + const after = git(root, ["rev-parse", "HEAD"]); + const blocked = await checkEvolutionMateriality(root, before, after); + assert.equal(blocked.passed, false); + assert.deepEqual(blocked.materialFiles, ["src/runtime.mjs"]); + + const event = { + schemaVersion: "nodekit.evolution-event/v1", + id: "evt:material-runtime-change", + projectId: "fixture", + repository: "local/fixture", + source: { commitSha: after, occurredAt: new Date().toISOString() }, + track: "architecture", + category: "runtime", + challenge: "Runtime contract changed", + resolution: "Recorded the reviewed material change", + assumptionIds: [], + invariantIds: [], + evidenceIds: ["evd:test"], + knownLimitations: [], + interpretation: { status: "human-reviewed", reviewedBy: "reviewer", reviewedAt: new Date().toISOString() }, + }; + const input = path.join(root, "inputs", "material-event.json"); + await writeFile(input, `${JSON.stringify(event, null, 2)}\n`); + await recordEvolutionRecord(root, path.relative(root, input)); + const passed = await checkEvolutionMateriality(root, before, after); + assert.equal(passed.passed, true); + assert.equal(passed.events[0].id, event.id); +}); diff --git a/test/factory.test.mjs b/test/factory.test.mjs index b991997..884b9f7 100644 --- a/test/factory.test.mjs +++ b/test/factory.test.mjs @@ -25,8 +25,14 @@ test("create emits a parseable, reproducible application from multiline input", target, }); const packageJson = JSON.parse(await readFile(path.join(target, "package.json"), "utf8")); - assert.equal(packageJson.devDependencies["@homenshum/nodekit"], "file:D:/work/node-platform"); - assert.equal(packageJson.dependencies["@earendil-works/pi-ai"], "0.80.10"); + assert.equal(packageJson.dependencies["@homenshum/nodekit"], "file:D:/work/node-platform"); + assert.equal(packageJson.dependencies?.["@earendil-works/pi-ai"], undefined); + assert.equal(await readFile(path.join(target, "docs", "FIGURED_OUT.md"), "utf8").then((value) => value.includes("Case -> Run -> Stage -> Artifact -> Proposal -> Approval -> Receipt")), true); + assert.equal(await readFile(path.join(target, "product", "SERVICE_BLUEPRINT.md"), "utf8").then(Boolean), true); + assert.match( + await readFile(path.join(target, "integrations", "convex", "capability-plan.yaml"), "utf8"), + /status: available-after-workflow-research/, + ); assert.equal( JSON.parse(await readFile(path.join(target, "proof", "build-friction.json"), "utf8")).packageManager, "pnpm", @@ -51,7 +57,7 @@ test("create emits a parseable, reproducible application from multiline input", assert.match(await readFile(path.join(target, ".gitattributes"), "utf8"), /\* text=auto eol=lf/); const dockerfile = await readFile(path.join(target, "Dockerfile"), "utf8"); const renderBlueprint = await readFile(path.join(target, "render.yaml"), "utf8"); - assert.match(dockerfile, /@earendil-works\/pi-ai@0\.80\.10/); + assert.match(dockerfile, /node:22-alpine/); assert.match(renderBlueprint, /name: fresh-app/); assert.match(renderBlueprint, /healthCheckPath: \/api\/health/); assert.equal(`${dockerfile}\n${renderBlueprint}`.includes("__"), false); @@ -69,7 +75,24 @@ test("create emits a parseable, reproducible application from multiline input", first.definition.applicationHash, ); assert.equal(first.manifest.application.purpose.includes("second line"), true); - assert.equal(inspectAgentDefinition(second).secrets[0].name, "OPENROUTER_API_KEY"); + assert.equal(inspectAgentDefinition(second).secrets[0].name, "NODEKIT_OPTIONAL_MODEL_KEY"); +}); + +test("primary create rejects every domain preset without suggesting a narrow alternative", async (t) => { + const root = await mkdtemp(path.join(os.tmpdir(), "nodekit-no-presets-")); + t.after(() => rm(root, { force: true, recursive: true })); + await assert.rejects( + () => createProject({ git: false, install: false, name: "not-a-preset", preset: "research-loop", target: path.join(root, "bad") }), + /does not accept --preset/, + ); +}); + +test("the published package and CLI expose only the blank figured-out factory", async () => { + const packageJson = JSON.parse(await readFile(path.join(process.cwd(), "package.json"), "utf8")); + assert.equal(packageJson.files.includes("reference-apps"), false); + + const { stdout } = await execFileAsync(process.execPath, [path.join(process.cwd(), "src", "cli.mjs"), "help"]); + assert.doesNotMatch(stdout, /reference create|--preset/i); }); test("compiled hash detects fixture drift and literal secrets fail closed", async (t) => { @@ -212,13 +235,12 @@ test("default projects vendor the exact NodeKit runtime without polluting capabi git: false, install: false, name: "portable-runtime", - preset: "agentic-rl-research", target: root, }); const packageJson = JSON.parse(await readFile(path.join(root, "package.json"), "utf8")); const vendoredPackage = JSON.parse(await readFile(path.join(root, "vendor", "nodekit", "package.json"), "utf8")); - assert.equal(packageJson.devDependencies["@homenshum/nodekit"], "file:vendor/nodekit"); + assert.equal(packageJson.dependencies["@homenshum/nodekit"], "file:vendor/nodekit"); assert.equal(vendoredPackage.name, "@homenshum/nodekit"); assert.equal(vendoredPackage.version, "0.2.1"); @@ -239,11 +261,10 @@ test("default projects vendor the exact NodeKit runtime without polluting capabi install: false, name: "blank-specifier-runtime", nodekitSpecifier: " ", - preset: "agentic-rl-research", target: blankSpecifierTarget, }); const blankSpecifierPackage = JSON.parse(await readFile(path.join(blankSpecifierTarget, "package.json"), "utf8")); - assert.equal(blankSpecifierPackage.devDependencies["@homenshum/nodekit"], "file:vendor/nodekit"); + assert.equal(blankSpecifierPackage.dependencies["@homenshum/nodekit"], "file:vendor/nodekit"); }); test("create --local-proof emits the deterministic receipt in one CLI workflow", async (t) => { @@ -265,42 +286,6 @@ test("create --local-proof emits the deterministic receipt in one CLI workflow", assert.equal(receipt.releaseReady, false); }); -test("agentic RL preset creates an offline FounderQuest lab with protected heldout evaluation", async (t) => { - const root = await mkdtemp(path.join(os.tmpdir(), "nodekit-agentic-rl-")); - const target = path.join(root, "lab"); - t.after(() => rm(root, { force: true, recursive: true })); - await createProject({ - git: false, - install: false, - name: "FounderQuest RL", - preset: "agentic-rl-research", - target, - }); - - const packageJson = JSON.parse(await readFile(path.join(target, "package.json"), "utf8")); - assert.equal(packageJson.dependencies?.["@earendil-works/pi-ai"], undefined); - assert.doesNotMatch(await readFile(path.join(target, ".env.example"), "utf8"), /OPENROUTER|API_KEY/); - await assert.rejects(readFile(path.join(target, "integrations", "pi-ai", "provider.mjs"), "utf8"), /ENOENT/); - assert.equal(await readFile(path.join(target, "fixtures", "tasks", "train.json"), "utf8").then(Boolean), true); - assert.equal(await readFile(path.join(target, "fixtures", "tasks", "validation.json"), "utf8").then(Boolean), true); - assert.equal(await readFile(path.join(target, "fixtures", "tasks", "heldout.json"), "utf8").then(Boolean), true); - - const compiled = await compileAgentDefinition(target); - assert.equal(compiled.manifest.provider.adapter, "deterministic-fixture"); - assert.equal(compiled.manifest.runtime.profile, "replay-only"); - assert.equal(compiled.definition.discovered.integrations.length, 0); - - for (const script of ["demo.mjs", "eval.mjs", "benchmark.mjs", "proof.mjs"]) { - await execFileAsync(process.execPath, [path.join(target, "scripts", script)], { cwd: target }); - } - const benchmark = JSON.parse(await readFile(path.join(target, "proof", "agentic-rl-benchmark.json"), "utf8")); - const proof = JSON.parse(await readFile(path.join(target, "proof", "release-proof.json"), "utf8")); - assert.equal(benchmark.assertions.heldoutProtected, true); - assert.equal(benchmark.assertions.unsafeActionRejected, true); - assert.equal(proof.passed, true); - assert.equal(proof.releaseReady, false); -}); - test("adopt is additive, runnable, and reports collisions", async (t) => { const root = await mkdtemp(path.join(os.tmpdir(), "nodekit-adopt-")); t.after(() => rm(root, { force: true, recursive: true })); @@ -324,7 +309,7 @@ test("adopt is additive, runnable, and reports collisions", async (t) => { ); assert.equal(result.collisions.includes("agent/instructions.md"), true); assert.equal(result.collisions.includes(".claude/skills/nodekit-present/SKILL.md"), true); - assert.equal(await readFile(path.join(root, "backend", "filesystem", "store.mjs"), "utf8").then(Boolean), true); + assert.equal(await readFile(path.join(root, "agent", "workflow.mjs"), "utf8").then(Boolean), true); }); test("a fresh no-key Git candidate reaches an honest local-ready proof", async (t) => { @@ -347,105 +332,11 @@ test("a fresh no-key Git candidate reaches an honest local-ready proof", async ( assert.equal(receipt.releaseReady, false); assert.equal(receipt.applicationHash, compiled.definition.applicationHash); assert.equal(receipt.configHash, compiled.definition.configHash); - assert.deepEqual(receipt.missingReleaseGates, ["livePi", "browserQa", "deployment"]); -}); - -test("agentic RL preset creates an offline FounderQuest lab with protected heldout evaluation", async (t) => { - const root = await mkdtemp(path.join(os.tmpdir(), "nodekit-agentic-rl-")); - const target = path.join(root, "lab"); - t.after(() => rm(root, { force: true, recursive: true })); - await createProject({ - git: true, - install: false, - name: "FounderQuest RL", - preset: "agentic-rl-research", - target, - }); - - const packageJson = JSON.parse(await readFile(path.join(target, "package.json"), "utf8")); - assert.equal(packageJson.dependencies?.["@earendil-works/pi-ai"], undefined); - assert.doesNotMatch(await readFile(path.join(target, ".env.example"), "utf8"), /OPENROUTER|API_KEY/); - await assert.rejects(readFile(path.join(target, "integrations", "pi-ai", "provider.mjs"), "utf8"), /ENOENT/); - for (const split of ["train", "validation", "heldout"]) { - assert.equal(await readFile(path.join(target, "fixtures", "tasks", `${split}.json`), "utf8").then(Boolean), true); - } - - const compiled = await compileAgentDefinition(target); - assert.equal(compiled.manifest.provider.adapter, "deterministic-fixture"); - assert.equal(compiled.manifest.runtime.profile, "replay-only"); - assert.equal(compiled.definition.discovered.integrations.length, 0); - for (const script of ["demo.mjs", "eval.mjs", "benchmark.mjs", "proof.mjs"]) { - await execFileAsync(process.execPath, [path.join(target, "scripts", script)], { cwd: target }); - } - const benchmark = JSON.parse(await readFile(path.join(target, "proof", "agentic-rl-benchmark.json"), "utf8")); - const proof = JSON.parse(await readFile(path.join(target, "proof", "release-proof.json"), "utf8")); - assert.equal(benchmark.assertions.heldoutProtected, true); - assert.equal(benchmark.assertions.unsafeActionRejected, true); - assert.equal(proof.passed, true); - assert.equal(proof.releaseReady, false); - - await writeFile(path.join(target, "agent", "instructions.md"), "drift after proof\n"); - await execFileAsync("git", ["add", "agent/instructions.md"], { cwd: target }); - await execFileAsync("git", [ - "-c", "user.name=NodeKit", - "-c", "user.email=nodekit@local", - "commit", "-m", "test: introduce stale compiled identity", - ], { cwd: target }); - await assert.rejects( - () => execFileAsync(process.execPath, [path.join(target, "scripts", "proof.mjs")], { cwd: target }), - /compiled application identity is stale/, - ); -}); - -test("the SMB lending FDE preset produces a clean-room human-authority proof", async (t) => { - const root = await mkdtemp(path.join(os.tmpdir(), "nodekit-smb-lending-fde-")); - t.after(() => rm(root, { force: true, recursive: true })); - await createProject({ - brief: "Map a synthetic SMB lending file without making a credit decision.", - git: true, - install: false, - name: "casca-fde-deployment-lab", - preset: "smb-lending-fde", - target: root, - }); - const compiled = await compileAgentDefinition(root); - for (const script of ["demo.mjs", "eval.mjs", "benchmark.mjs", "proof.mjs"]) { - await execFileAsync(process.execPath, [path.join(root, "scripts", script)], { cwd: root }); - } - const demo = JSON.parse(await readFile(path.join(root, "proof", "demo-receipt.json"), "utf8")); - const evaluation = JSON.parse(await readFile(path.join(root, "proof", "eval-receipt.json"), "utf8")); - const proof = JSON.parse(await readFile(path.join(root, "proof", "release-proof.json"), "utf8")); - const instructions = await readFile(path.join(root, "agent", "instructions.md"), "utf8"); - - assert.equal(demo.schemaVersion, "nodekit.smb-lending-receipt/v1"); - assert.equal(demo.documents.find((document) => document.id === "operating-bank-statements-q2").status, "requested"); - assert.deepEqual(demo.packRegistry.toolIds, [ - "lending.inspect-file", - "lending.propose-document-request", - "lending.approve-proposal", + assert.deepEqual(receipt.missingReleaseGates, [ + "browserCertification", + "deployment", + "freshAgentHeldout", + "freshHumanUsability", + "threeConvexConsumers", ]); - assert.deepEqual(demo.packRegistry.receiptValidation.validatorIds, ["receipt-is-secret-free"]); - assert.equal( - await readFile(path.join(root, "test", "pack-registry.test.mjs"), "utf8").then((value) => value.includes("fails closed")), - true, - ); - assert.equal(evaluation.passed, true); - assert.equal(proof.passed, true); - assert.equal(proof.applicationHash, compiled.definition.applicationHash); - assert.match(proof.receiptVerification.candidateCommit, /^[a-f0-9]{40}$/); - assert.match(instructions, /Never make, recommend, approve, decline, or simulate a credit decision/); - - await writeFile(path.join(root, "agent", "instructions.md"), "drift after proof\n"); - await execFileAsync("git", ["add", "agent/instructions.md"], { cwd: root }); - await execFileAsync("git", [ - "-c", "user.name=NodeKit", - "-c", "user.email=nodekit@local", - "commit", "-m", "test: introduce stale compiled identity", - ], { cwd: root }); - await assert.rejects( - () => execFileAsync(process.execPath, [path.join(root, "scripts", "proof.mjs")], { cwd: root }), - ); - const staleProof = JSON.parse(await readFile(path.join(root, "proof", "release-proof.json"), "utf8")); - assert.equal(staleProof.checks.receiptsVerified, false); - assert.match(staleProof.receiptVerification.error, /compiled application identity is stale/); }); diff --git a/test/frontend-specialist.test.mjs b/test/frontend-specialist.test.mjs new file mode 100644 index 0000000..ea19309 --- /dev/null +++ b/test/frontend-specialist.test.mjs @@ -0,0 +1,108 @@ +import assert from "node:assert/strict"; +import { mkdtemp, mkdir, readFile, writeFile } from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; +import test from "node:test"; +import { stringify as stringifyYaml } from "yaml"; +import { + FRONTEND_REQUIRED_STATES, + FRONTEND_REQUIRED_VIEWS, + compileFrontendPlan, + createFrontendDirections, + createFrontendRepairPlan, + evaluateFrontendTournament, + initializeFrontendHarness, + verifyFrontendCanary, +} from "../src/lib/frontend-specialist.mjs"; + +async function fixture() { + const root = await mkdtemp(path.join(os.tmpdir(), "nodekit-frontend-")); + await mkdir(path.join(root, "harness"), { recursive: true }); + const initialized = await initializeFrontendHarness(root); + const contract = { + schemaVersion: "nodekit.product-design-contract/v1", + contractId: "contract-verified-business", + product: { targetUser: "salon_owner", primaryJob: "understand_verified_profit", primaryArtifact: "verified_business_brief" }, + journey: ["orient", "connect_sources", "reconcile", "review_exceptions", "verify", "act", "export"], + designIntent: { emotionalTarget: ["trustworthy", "calm", "operational"], dominantSurface: "verified_brief", dominantAction: "resolve_next_exception", density: "medium" }, + interfaceHypothesis: { artifactDominance: "brief occupies the primary stage", agentPlacement: "agent remains adjacent", reviewBoundary: "proposals remain distinct from canonical state", mobileTopology: "explicit artifact, review, and agent modes" }, + requiredDesktopSurfaces: ["navigation", "primary_artifact", "agent_review_rail", "current_action", "data_freshness"], + requiredMobileSurfaces: ["today", "review", "business", "sources", "sticky_action"], + avoid: ["generic_kpi_dashboard", "accounting_schema_as_interface", "decorative_chat", "all_workflow_states_expanded", "hidden_data_freshness"], + requiredStates: [...FRONTEND_REQUIRED_STATES], + protectedDecisions: { primaryUser: "nodekit", primaryJob: "nodekit", canonicalWorkflow: "nodekit", dataAuthority: "nodekit", permissionBoundaries: "nodekit", completionCriteria: "nodeproof", finalVerdict: "nodeproof" }, + }; + const contractPath = path.join(root, "harness", "frontend", "product-packets", "verified-business.yaml"); + await writeFile(contractPath, stringifyYaml(contract)); + return { contract, contractPath, initialized, root }; +} + +test("frontend planning protects product decisions and refuses reputation-only routing", async () => { + const { contractPath, initialized, root } = await fixture(); + const output = await compileFrontendPlan(root, path.relative(root, contractPath)); + assert.equal(output.plan.finalVerdictAuthority, "nodeproof"); + assert.equal(output.plan.directionCount, 3); + assert.equal(output.plan.requestedRoute, null); + + const route = await readFile(initialized.routePath, "utf8"); + await writeFile(initialized.routePath, route.replace("status: unprofiled", "status: evidence-ranked").replace("preferredRoute: null", "preferredRoute: moonshotai/kimi-k3")); + await assert.rejects(() => compileFrontendPlan(root, path.relative(root, contractPath)), /requires evidenceRefs/); +}); + +test("Frontend Gym requires three materially distinct rendered directions and blind independent selection", async () => { + const { contractPath, root } = await fixture(); + const { plan, output: planOutput } = await compileFrontendPlan(root, path.relative(root, contractPath)); + const { directionSet, output: directionOutput } = await createFrontendDirections(root, path.relative(root, planOutput)); + assert.deepEqual(directionSet.candidates.map((candidate) => candidate.archetype), ["collaborative-workspace", "artifact-studio", "domain-native"]); + for (const candidate of directionSet.candidates) { + candidate.status = "rendered"; + candidate.viewEvidence = Object.fromEntries(FRONTEND_REQUIRED_VIEWS.map((view) => [view, `proof/${candidate.candidateId}/${view}.png`])); + } + await writeFile(directionOutput, `${JSON.stringify(directionSet, null, 2)}\n`); + const score = (offset) => Object.fromEntries(["primaryJobClarity", "artifactDominance", "workflowHierarchy", "agentLegibility", "reviewSafety", "mobileOperation", "domainAppropriateness", "visualQuality"].map((key, index) => [key, Math.min(1, 0.7 + offset + index * 0.005)])); + const benchmark = { + schemaVersion: "nodekit.frontend-benchmark/v1", + benchmarkId: "frontend-gym-1", + directionSet: path.relative(root, directionOutput).replaceAll("\\", "/"), + scores: { "direction-a": score(0), "direction-b": score(0.05), "direction-c": score(0.1) }, + pairwiseResults: [ + { left: "direction-a", right: "direction-b", winner: "direction-b", evidenceRefs: ["critic-1"] }, + { left: "direction-a", right: "direction-c", winner: "direction-c", evidenceRefs: ["critic-2"] }, + { left: "direction-b", right: "direction-c", winner: "direction-c", evidenceRefs: ["critic-3"] }, + ], + criticIndependent: true, + browserChecksPassed: true, + accessibilityPassed: true, + overflowPassed: true, + majorFindings: [], + freshUserEvidenceRefs: ["fresh-user-1"], + }; + const benchmarkPath = path.join(root, "harness", "frontend", "benchmark.json"); + await writeFile(benchmarkPath, `${JSON.stringify(benchmark, null, 2)}\n`); + const result = await evaluateFrontendTournament(root, path.relative(root, benchmarkPath)); + assert.equal(result.decision.selectedCandidateId, "direction-c"); + assert.equal(result.promotionAuthorized, false); + assert.equal(result.decisive, true); + assert.equal(plan.deploymentAuthorized, false); +}); + +test("frontend repair is bounded and a canary requires exact model, screenshots, NodeProof, and human approval", async () => { + const { root } = await fixture(); + const benchmark = { + schemaVersion: "nodekit.frontend-benchmark/v1", + benchmarkId: "blocked-ui", + directionSet: "harness/frontend/design-directions/unused.json", + scores: { a: Object.fromEntries(["primaryJobClarity", "artifactDominance", "workflowHierarchy", "agentLegibility", "reviewSafety", "mobileOperation", "domainAppropriateness", "visualQuality"].map((key) => [key, 0.5])), b: Object.fromEntries(["primaryJobClarity", "artifactDominance", "workflowHierarchy", "agentLegibility", "reviewSafety", "mobileOperation", "domainAppropriateness", "visualQuality"].map((key) => [key, 0.5])), c: Object.fromEntries(["primaryJobClarity", "artifactDominance", "workflowHierarchy", "agentLegibility", "reviewSafety", "mobileOperation", "domainAppropriateness", "visualQuality"].map((key) => [key, 0.5])) }, + pairwiseResults: [{ left: "a", right: "b", winner: "a", evidenceRefs: ["e1"] }, { left: "a", right: "c", winner: "a", evidenceRefs: ["e2"] }, { left: "b", right: "c", winner: "b", evidenceRefs: ["e3"] }], + criticIndependent: true, browserChecksPassed: false, accessibilityPassed: true, overflowPassed: true, majorFindings: ["PRODUCT_TOPOLOGY_MISS"], freshUserEvidenceRefs: [], + }; + const benchmarkPath = path.join(root, "harness", "frontend", "blocked.json"); + await writeFile(benchmarkPath, `${JSON.stringify(benchmark, null, 2)}\n`); + const { repair } = await createFrontendRepairPlan(root, path.relative(root, benchmarkPath)); + assert.equal(repair.maximumRounds, 2); + assert.equal(repair.preservePriorImplementation, true); + + const canaryPath = path.join(root, "harness", "frontend", "canary.json"); + await writeFile(canaryPath, `${JSON.stringify({ candidateId: "direction-c", commitSha: "a".repeat(40), requestedRoute: "frontend", resolvedProvider: "provider", resolvedModel: "model", freshIdentity: true, renderedJourney: true, screenshotEvidenceRefs: FRONTEND_REQUIRED_VIEWS, nodeProofPassed: true, majorFindings: [], approvedBy: "human-reviewer" }, null, 2)}\n`); + assert.equal((await verifyFrontendCanary(root, path.relative(root, canaryPath))).passed, true); +}); diff --git a/test/harness-gym.test.mjs b/test/harness-gym.test.mjs new file mode 100644 index 0000000..89713f6 --- /dev/null +++ b/test/harness-gym.test.mjs @@ -0,0 +1,196 @@ +import assert from "node:assert/strict"; +import { mkdtemp, mkdir, readFile, rm, writeFile } from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; +import test from "node:test"; +import { + benchmarkSkillCandidate, + compileRoutingPolicy, + evaluateTournament, + promoteSkillCandidate, + proposeSkillCandidates, + reviewSkillCandidate, + rollbackHarness, +} from "../src/lib/harness-gym.mjs"; +import { compileModelIntelligence, initializeHarness } from "../src/lib/model-intelligence.mjs"; +import { createProject } from "../src/lib/scaffold.mjs"; + +const HASH = "b".repeat(64); + +function failure(id, taskId) { + return { + failureId: id, + failureClass: "NO_RESULT_INSPECTION", + severity: "P1", + model: "exact-model-1", + taskId, + harnessVersion: "h0", + behavior: "Declared completion before inspecting the result", + expectedBehavior: "Inspect the result before completion", + probableCause: "model", + evidenceRefs: [`proof/${id}.json`], + }; +} + +function observation(run, taskId, failureId) { + return { + schemaVersion: "nodekit.model-observation/v1", + runId: run, + applicationId: "gym-lab", + taskId, + taskFamily: "bounded-repair", + model: { requestedRoute: "provider/alias", resolvedProvider: "provider", resolvedModel: "exact-model-1", modelRevision: "r1" }, + harness: { version: "h0", hash: HASH, toolSurfaceHash: HASH, contextPolicyHash: HASH, skillStackHash: HASH }, + budgets: { maximumTokens: 1000, maximumCostUsd: 1, maximumDurationMs: 10000 }, + cognitive: { briefUnderstanding: 0.8, decomposition: 0.8, constraintRetention: 0.8, ambiguityDetection: 0.8, referenceUse: 0.8, repairReasoning: 0.8 }, + execution: { toolSelection: 0.8, validArguments: 0.8, toolOrdering: 0.8, resultInspection: 0.2, recovery: 0.7, scopedChanges: 0.9, completion: 0.7 }, + artifact: { correctness: 0.8, usability: 0.8, domainQuality: 0.8, evidenceIntegrity: 0.8 }, + efficiency: { latencyMs: 1000, costUsd: 0.1, toolCalls: 3, retries: 0 }, + failures: [failure(failureId, taskId)], + evidenceRefs: [`proof/${failureId}.json`], + proofReceiptId: `receipt-${run}`, + }; +} + +async function preparedGym(t) { + const root = await mkdtemp(path.join(os.tmpdir(), "nodekit-gym-")); + t.after(() => rm(root, { force: true, recursive: true })); + await createProject({ git: false, install: false, name: "gym-lab", target: root }); + await initializeHarness(root); + const observations = [ + observation("run-1", "task-1", "f-1"), + observation("run-2", "task-1", "f-2"), + observation("run-3", "task-2", "f-3"), + ]; + for (const entry of observations) { + await writeFile(path.join(root, ".qa", "models", "observations", `${entry.runId}.json`), `${JSON.stringify(entry, null, 2)}\n`); + } + return root; +} + +test("skill compiler proposes only evidence-threshold candidates with executable contracts", async (t) => { + const root = await preparedGym(t); + const proposed = await proposeSkillCandidates(root); + assert.equal(proposed.candidates.length, 1); + const candidateId = proposed.candidates[0].candidate.candidateId; + const reviewed = await reviewSkillCandidate(root, candidateId); + assert.equal(reviewed.candidate.sourceCluster.count, 3); + assert.equal(reviewed.candidate.sourceCluster.taskIds.length, 2); + assert.equal(reviewed.skill.kind, "guardrail"); + assert.equal(reviewed.skill.positiveExamples.length > 0, true); + assert.equal(reviewed.skill.negativeExamples.length > 0, true); + assert.equal(reviewed.skill.completionChecks.length > 0, true); + assert.equal(reviewed.skill.failureBehavior.length > 0, true); +}); + +test("skill benchmark holds model harness and protected benchmark fixed and rejects regressions", async (t) => { + const root = await preparedGym(t); + const proposed = await proposeSkillCandidates(root); + const candidateId = proposed.candidates[0].candidate.candidateId; + const compiled = await compileModelIntelligence(root, { write: false }); + const comparison = { + schemaVersion: "nodekit.skill-comparison/v1", + candidateId, + benchmarkHash: compiled.resolved.benchmarkHash, + harnessHash: compiled.resolved.harnessHash, + resolvedModel: "provider/exact-model-1", + taskIds: ["task-1", "task-2"], + baseline: { runs: 3, successRate: 0.5, targetFailureRate: 0.5, accuracy: 0.8, safety: 1, editability: 0.8, exportQuality: 0.8, userCompletion: 0.8, medianLatencyMs: 1000, costPerSuccessUsd: 0.1 }, + candidate: { runs: 3, successRate: 0.8, targetFailureRate: 0.1, accuracy: 0.8, safety: 1, editability: 0.8, exportQuality: 0.8, userCompletion: 0.8, medianLatencyMs: 1100, costPerSuccessUsd: 0.11 }, + protectedEvaluatorUnchanged: true, + }; + const comparisonPath = path.join(root, "comparison.json"); + await writeFile(comparisonPath, `${JSON.stringify(comparison, null, 2)}\n`); + const passed = await benchmarkSkillCandidate(root, candidateId, comparisonPath); + assert.equal(passed.passed, true); + + comparison.candidate.safety = 0.9; + await writeFile(comparisonPath, `${JSON.stringify(comparison, null, 2)}\n`); + const failed = await benchmarkSkillCandidate(root, candidateId, comparisonPath); + assert.equal(failed.passed, false); + assert.equal(failed.nonRegression.safety, false); +}); + +test("routing compiler honors project evidence and remains provisional", async (t) => { + const root = await preparedGym(t); + const card = { + schemaVersion: "nodekit.model-capability-card/v1", + model: { requestedRoute: "provider/alias", resolvedProvider: "provider", resolvedModel: "exact-model-1", modelRevision: "r1" }, + scope: { level: "project", applicationId: "gym-lab", taskFamilies: ["bounded-repair"] }, + evidenceWindow: { from: "2026-07-01", to: "2026-07-22", benchmarkRuns: 3, taskCount: 2, harnessVersions: ["h0"] }, + strengths: ["bounded typed repair"], + weaknesses: ["result inspection"], + bestRoles: ["bounded-editor"], + avoidRoles: ["proof-authority"], + requiredScaffolding: ["inspect-tool-result"], + metrics: { briefAdherence: 0.8, validToolCalls: 0.8, firstPassAcceptance: 0.7, repairSuccess: 0.8, medianLatencyMs: 1000, costPerSuccessUsd: 0.1 }, + confidence: { level: "medium", reason: "Three controlled runs across two tasks" }, + expiresWhen: ["model revision changes", "harness changes"], + evidenceRefs: ["proof/f-1.json", "proof/f-2.json", "proof/f-3.json"], + status: "provisional", + }; + const cardRoot = path.join(root, "harness", "models", "cards", "project"); + await mkdir(cardRoot, { recursive: true }); + await writeFile(path.join(cardRoot, "model.json"), `${JSON.stringify(card, null, 2)}\n`); + const policy = await compileRoutingPolicy(root); + assert.equal(policy.status, "provisional"); + assert.equal(policy.automaticPromotion, false); + assert.equal(policy.routes[0].candidates[0].scope, "project"); + assert.equal(policy.routes[0].fallback.type, "deterministic"); + assert.equal(policy.routes[0].completion.directMutation, false); +}); + +test("tournament rejects self-judging and never authorizes promotion", async (t) => { + const root = await preparedGym(t); + const compiled = await compileModelIntelligence(root, { write: false }); + const tournament = { + schemaVersion: "nodekit.tournament/v1", + tournamentId: "tournament-1", + benchmarkHash: compiled.resolved.benchmarkHash, + harnessHash: compiled.resolved.harnessHash, + candidates: ["candidate-a", "candidate-b"], + pairwiseResults: [ + { taskId: "task-1", left: "candidate-a", right: "candidate-b", winner: "candidate-b", criticId: "independent-critic", criticIndependent: true, deterministicChecksPassed: true, evidenceRefs: ["proof/pair-1.json"] }, + ], + protectedEvaluatorUnchanged: true, + }; + const file = path.join(root, "tournament.json"); + await writeFile(file, `${JSON.stringify(tournament, null, 2)}\n`); + const verdict = await evaluateTournament(root, file); + assert.equal(verdict.winner, "candidate-b"); + assert.equal(verdict.promotionAuthorized, false); + + tournament.pairwiseResults[0].criticId = "candidate-b"; + await writeFile(file, `${JSON.stringify(tournament, null, 2)}\n`); + await assert.rejects(() => evaluateTournament(root, file), /cannot serve as its own decisive critic/); +}); + +test("manual promotion requires benchmark canary and NodeProof and remains reversible", async (t) => { + const root = await preparedGym(t); + const proposed = await proposeSkillCandidates(root); + const candidateId = proposed.candidates[0].candidate.candidateId; + const compiled = await compileModelIntelligence(root, { write: false }); + const comparison = { + schemaVersion: "nodekit.skill-comparison/v1", candidateId, + benchmarkHash: compiled.resolved.benchmarkHash, harnessHash: compiled.resolved.harnessHash, + resolvedModel: "provider/exact-model-1", taskIds: ["task-1", "task-2"], protectedEvaluatorUnchanged: true, + baseline: { runs: 3, successRate: 0.4, targetFailureRate: 0.6, accuracy: 0.8, safety: 1, editability: 0.8, exportQuality: 0.8, userCompletion: 0.8, medianLatencyMs: 1000, costPerSuccessUsd: 0.1 }, + candidate: { runs: 3, successRate: 0.8, targetFailureRate: 0.1, accuracy: 0.8, safety: 1, editability: 0.8, exportQuality: 0.8, userCompletion: 0.8, medianLatencyMs: 1000, costPerSuccessUsd: 0.1 }, + }; + const comparisonPath = path.join(root, "comparison.json"); + await writeFile(comparisonPath, `${JSON.stringify(comparison, null, 2)}\n`); + await benchmarkSkillCandidate(root, candidateId, comparisonPath); + const canaryPath = path.join(root, "canary.json"); + await writeFile(canaryPath, `${JSON.stringify({ schemaVersion: "nodekit.canary-receipt/v1", canaryId: "canary-1", candidateId, freshContext: true, humanReprompts: 0, substantiveChanges: true, protectedEvaluatorUnchanged: true, checksPassed: true, evidenceRefs: ["proof/canary.json"], passed: true }, null, 2)}\n`); + const proofPath = path.join(root, "nodeproof.json"); + await writeFile(proofPath, `${JSON.stringify({ schemaVersion: "nodeproof.integrity-receipt/v1", candidateId, passed: true, integrityVerified: true }, null, 2)}\n`); + + await assert.rejects(() => promoteSkillCandidate(root, candidateId, { canaryPath, proofPath }), /approvedBy/); + const promoted = await promoteSkillCandidate(root, candidateId, { canaryPath, proofPath, approvedBy: "human-reviewer" }); + assert.equal(promoted.nextVersion, "h1"); + assert.equal(promoted.promotion.automatic, false); + assert.equal(promoted.promotion.rollbackVersion, "h0"); + const rollback = await rollbackHarness(root); + assert.deepEqual({ from: rollback.from, to: rollback.to }, { from: "h1", to: "h0" }); + assert.equal(JSON.parse(await readFile(path.join(root, "harness", "versions", "current.json"), "utf8")).version, "h0"); +}); diff --git a/test/knowledge-evolution.test.mjs b/test/knowledge-evolution.test.mjs new file mode 100644 index 0000000..e76f190 --- /dev/null +++ b/test/knowledge-evolution.test.mjs @@ -0,0 +1,289 @@ +import assert from "node:assert/strict"; +import { execFile } from "node:child_process"; +import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; +import test from "node:test"; +import { promisify } from "node:util"; +import { + applyGraphPatch, + benchmarkKnowledgeRetrieval, + createMemoryKnowledgeGraphAdapter, + createKnowledgeState, + decideGraphPatch, + diffKnowledgeGraph, + initializeKnowledgeGraph, + inspectKnowledgeGaps, + proposeGraphPatch, + queryKnowledgeGraph, + readKnowledgeGraph, + recordKnowledgeAction, + replayKnowledgeGraph, + validateGraphPatch, +} from "../src/lib/knowledge-evolution.mjs"; +import { validateSchema } from "../src/lib/schema-validation.mjs"; +import { createProject } from "../src/lib/scaffold.mjs"; +import { initializeHarness } from "../src/lib/model-intelligence.mjs"; +import { proposeHarnessKnowledgePatch } from "../src/lib/harness-knowledge.mjs"; + +const actor = { + agentId: "agent:test", + modelRoute: "deterministic", + resolvedModel: "fixture", + harnessVersion: "h0", +}; +const execFileAsync = promisify(execFile); + +const evidence = { + id: "evidence:paper", + kind: "evidence", + label: "EvoGraph-R1 paper", + layer: "source", + confidence: 1, + evidenceRefs: [], + contentHash: "a".repeat(64), + sourceUri: "https://arxiv.org/pdf/2607.12764", + capturedAt: "2026-07-21T00:00:00.000Z", + freshness: { checkedAt: "2026-07-21T00:00:00.000Z", expiresAt: "2027-07-21T00:00:00.000Z" }, +}; + +async function rootFor(t) { + const root = await mkdtemp(path.join(os.tmpdir(), "nodekit-knowledge-")); + t.after(() => rm(root, { force: true, recursive: true })); + await initializeKnowledgeGraph(root, { graphId: "knowledge:test" }); + return root; +} + +async function proposeValidateAcceptApply(root, input, principalId = "human:reviewer") { + const patch = await proposeGraphPatch(root, { proposedBy: actor, confidence: 0.95, ...input }); + const validated = await validateGraphPatch(root, patch.patchId); + assert.deepEqual(validated.validation.errors, []); + await decideGraphPatch(root, patch.patchId, { decision: "accept", principalId, reason: "reviewed fixture" }); + return applyGraphPatch(root, patch.patchId); +} + +test("initializes a backend-neutral six-layer graph with proposal-only authority", async (t) => { + const root = await rootFor(t); + const graph = await readKnowledgeGraph(root); + assert.equal(graph.schemaVersion, "nodekit.knowledge-graph/v1"); + assert.equal(graph.authority.canonicalMutation, "accepted-patch-only"); + assert.equal(graph.authority.destructiveDelete, false); + assert.deepEqual(graph.layers.map((layer) => layer.id), ["source", "derived", "working", "proposal", "canonical", "hypothesis"]); + assert.match(graph.contentHash, /^[a-f0-9]{64}$/); + assert.deepEqual(await validateSchema("nodekit.knowledge-graph.v1.schema.json", graph, "graph"), []); + + const persisted = JSON.parse(await readFile(path.join(root, ".nodeagent", "knowledge", "graph.json"), "utf8")); + assert.equal(persisted.graphId, "knowledge:test"); + + const adapter = createMemoryKnowledgeGraphAdapter(graph); + assert.equal((await adapter.readGraph()).version, 0); + const stale = await adapter.compareAndSwap(1, graph); + assert.deepEqual(stale, { applied: false, actualVersion: 0 }); + assert.match(await adapter.exportDocument(), /nodekit\.knowledge-graph\/v1/); +}); + +test("ingests immutable multimodal evidence only through an accepted patch", async (t) => { + const root = await rootFor(t); + const proposed = await proposeGraphPatch(root, { + operations: [{ type: "INSERT", node: evidence }], + evidenceRefs: [], + contradictionRefs: [], + proposedBy: actor, + confidence: 1, + }); + let graph = await readKnowledgeGraph(root); + assert.equal(graph.nodes.length, 0, "a proposal must not mutate canonical graph state"); + assert.equal(proposed.status, "pending"); + + const validated = await validateGraphPatch(root, proposed.patchId); + assert.equal(validated.validation.sourceGrounded, true); + assert.deepEqual(await validateSchema("nodekit.graph-patch.v1.schema.json", validated, "patch"), []); + await decideGraphPatch(root, proposed.patchId, { decision: "accept", principalId: "human:reviewer" }); + const { receipt } = await applyGraphPatch(root, proposed.patchId); + graph = await readKnowledgeGraph(root); + assert.equal(graph.version, 1); + assert.equal(graph.nodes[0].id, evidence.id); + assert.match(receipt.beforeHash, /^[a-f0-9]{64}$/); + assert.match(receipt.afterHash, /^[a-f0-9]{64}$/); + assert.notEqual(receipt.beforeHash, receipt.afterHash); +}); + +test("supports source-grounded hyperedges, retrieval, gaps, actions, replay, and benchmark", async (t) => { + const root = await rootFor(t); + await proposeValidateAcceptApply(root, { + operations: [{ type: "INSERT", node: evidence }], + evidenceRefs: [], + contradictionRefs: [], + }); + const claim = { + id: "claim:governed-evolution", + kind: "claim", + label: "Canonical graph changes require governed patches", + layer: "canonical", + confidence: 0.95, + evidenceRefs: [evidence.id], + properties: { owner: "NodeKit", status: "verified" }, + }; + const task = { + id: "task:harness-gym", + kind: "task", + label: "Harness Gym graph-assisted canary", + layer: "working", + confidence: 0.8, + evidenceRefs: [evidence.id], + }; + const relationship = { + id: "hyperedge:evidence-claim-task", + predicate: "supports-under-task", + layer: "canonical", + participants: [ + { nodeId: evidence.id, role: "source" }, + { nodeId: claim.id, role: "claim" }, + { nodeId: task.id, role: "task" }, + ], + confidence: 0.95, + evidenceRefs: [evidence.id], + }; + await proposeValidateAcceptApply(root, { + operations: [ + { type: "INSERT", node: claim }, + { type: "INSERT", node: task }, + { type: "INSERT", hyperedge: relationship }, + ], + evidenceRefs: [evidence.id], + contradictionRefs: [], + }); + await recordKnowledgeAction(root, { + type: "EXTERNAL_RESEARCH", + runId: "run:1", + caseId: "case:1", + actorId: "agent:test", + input: { query: "dynamic hypergraph" }, + outputRefs: [evidence.id], + evidenceRefs: [evidence.id], + status: "completed", + }); + + const graph = await readKnowledgeGraph(root); + const query = queryKnowledgeGraph(graph, "governed harness task"); + assert.ok(query.results.some((entry) => entry.entity.id === task.id)); + assert.ok(query.supportingHyperedges.some((edge) => edge.id === relationship.id)); + assert.equal(inspectKnowledgeGaps(graph).unsupported.length, 0); + const state = createKnowledgeState(graph, { caseId: "case:1", runId: "run:1", goal: "Improve harness" }); + assert.equal(state.schemaVersion, "nodekit.knowledge-state/v1"); + assert.deepEqual(await validateSchema("nodekit.knowledge-state.v1.schema.json", state, "state"), []); + + const replayed = replayKnowledgeGraph(graph, 1); + assert.equal(replayed.version, 1); + assert.deepEqual(replayed.nodes.map((node) => node.id), [evidence.id]); + const diff = diffKnowledgeGraph(graph, 0, 2); + assert.equal(diff.patchIds.length, 2); + assert.equal(diff.operations.length, 4); + const benchmark = benchmarkKnowledgeRetrieval(graph, [{ caseId: "case:query", query: "governed canonical graph", expectedEntityIds: [claim.id] }]); + assert.equal(benchmark.results.evolvingGraph.averageRecall, 1); + assert.equal(benchmark.ablations.insert, true); + assert.equal(benchmark.ablations.externalResearch, true); +}); + +test("blocks ungrounded, stale, and direct source mutations while retaining deprecated history", async (t) => { + const root = await rootFor(t); + const ungrounded = await proposeGraphPatch(root, { + operations: [{ type: "INSERT", node: { id: "claim:unsupported", kind: "claim", label: "Unsupported", layer: "canonical", confidence: 0.5, evidenceRefs: [] } }], + evidenceRefs: [], contradictionRefs: [], proposedBy: actor, confidence: 0.5, + }); + const invalid = await validateGraphPatch(root, ungrounded.patchId); + assert.ok(invalid.validation.errors.some((error) => error.includes("not grounded"))); + await assert.rejects(() => decideGraphPatch(root, ungrounded.patchId, { decision: "accept", principalId: "human:reviewer" }), /failed validation/); + + await proposeValidateAcceptApply(root, { operations: [{ type: "INSERT", node: evidence }], evidenceRefs: [], contradictionRefs: [] }); + const sourceUpdate = await proposeGraphPatch(root, { + operations: [{ type: "UPDATE", targetId: evidence.id, patch: { label: "Changed source" }, evidenceRefs: [evidence.id] }], + evidenceRefs: [evidence.id], contradictionRefs: [], proposedBy: actor, confidence: 1, + }); + const sourceInvalid = await validateGraphPatch(root, sourceUpdate.patchId); + assert.ok(sourceInvalid.validation.errors.some((error) => error.includes("immutable source"))); + + const claim = { id: "claim:old", kind: "claim", label: "Old claim", layer: "canonical", confidence: 0.8, evidenceRefs: [evidence.id] }; + await proposeValidateAcceptApply(root, { operations: [{ type: "INSERT", node: claim }], evidenceRefs: [evidence.id], contradictionRefs: [] }); + await proposeValidateAcceptApply(root, { + operations: [{ type: "DEPRECATE", targetId: claim.id, reason: "superseded", evidenceRefs: [evidence.id] }], + evidenceRefs: [evidence.id], contradictionRefs: [], + }); + const graph = await readKnowledgeGraph(root); + assert.equal(graph.nodes.find((node) => node.id === claim.id).deprecationReason, "superseded"); + assert.equal(queryKnowledgeGraph(graph, "old claim").results.length, 0); +}); + +test("rejects stale accepted proposals instead of overwriting a newer graph version", async (t) => { + const root = await rootFor(t); + const first = await proposeGraphPatch(root, { operations: [{ type: "INSERT", node: evidence }], evidenceRefs: [], contradictionRefs: [], proposedBy: actor, confidence: 1 }); + const secondEvidence = { ...evidence, id: "evidence:second", label: "Second source", contentHash: "b".repeat(64) }; + const second = await proposeGraphPatch(root, { operations: [{ type: "INSERT", node: secondEvidence }], evidenceRefs: [], contradictionRefs: [], proposedBy: actor, confidence: 1 }); + await validateGraphPatch(root, first.patchId); + await decideGraphPatch(root, first.patchId, { decision: "accept", principalId: "human:reviewer" }); + await applyGraphPatch(root, first.patchId); + const stale = await validateGraphPatch(root, second.patchId); + assert.equal(stale.status, "conflicted"); + await assert.rejects(() => applyGraphPatch(root, second.patchId), /only accepted/); + assert.equal((await readKnowledgeGraph(root)).nodes.length, 1); +}); + +test("projects evaluated Harness Gym observations as a proposal, never as an automatic promotion", async (t) => { + const root = await mkdtemp(path.join(os.tmpdir(), "nodekit-harness-knowledge-")); + t.after(() => rm(root, { force: true, recursive: true })); + await createProject({ git: false, install: false, name: "knowledge-gym", target: root }); + await initializeHarness(root); + await initializeKnowledgeGraph(root, { graphId: "knowledge:gym" }); + const hash = "c".repeat(64); + const observation = { + schemaVersion: "nodekit.model-observation/v1", + runId: "run-graph-1", + applicationId: "knowledge-gym", + taskId: "task-graph-1", + taskFamily: "graph-retrieval", + model: { requestedRoute: "provider/alias", resolvedProvider: "provider", resolvedModel: "exact-model" }, + harness: { version: "h0", hash, toolSurfaceHash: hash, contextPolicyHash: hash, skillStackHash: hash }, + budgets: { maximumTokens: 1000, maximumCostUsd: 1, maximumDurationMs: 10000 }, + cognitive: { briefUnderstanding: 0.8, decomposition: 0.8, constraintRetention: 0.8, ambiguityDetection: 0.8, referenceUse: 0.8, repairReasoning: 0.8 }, + execution: { toolSelection: 0.8, validArguments: 0.8, toolOrdering: 0.8, resultInspection: 0.3, recovery: 0.7, scopedChanges: 0.9, completion: 0.7 }, + artifact: { correctness: 0.8, usability: 0.8, domainQuality: 0.8, evidenceIntegrity: 0.8 }, + efficiency: { latencyMs: 1000, toolCalls: 2, retries: 0 }, + failures: [{ + failureId: "failure-graph-1", failureClass: "NO_RESULT_INSPECTION", severity: "P1", model: "exact-model", + taskId: "task-graph-1", harnessVersion: "h0", behavior: "Stopped before inspecting graph results", + expectedBehavior: "Inspect graph results", probableCause: "model", evidenceRefs: ["proof/run-graph-1.json"], + }], + evidenceRefs: ["proof/run-graph-1.json"], + proofReceiptId: "receipt-run-graph-1", + }; + await writeFile(path.join(root, ".qa", "models", "observations", "run-graph-1.json"), `${JSON.stringify(observation, null, 2)}\n`); + const projection = await proposeHarnessKnowledgePatch(root); + assert.equal(projection.unchanged, false); + assert.equal(projection.patch.status, "pending"); + assert.ok(projection.patch.operations.some((operation) => operation.hyperedge?.predicate === "model-performed-task-under-harness")); + const graph = await readKnowledgeGraph(root); + assert.equal(graph.nodes.length, 0, "Harness projection cannot mutate canonical graph without approval"); + const validated = await validateGraphPatch(root, projection.patch.patchId); + assert.deepEqual(validated.validation.errors, []); +}); + +test("CLI exposes the complete safe graph lifecycle", async (t) => { + const root = await mkdtemp(path.join(os.tmpdir(), "nodekit-knowledge-cli-")); + t.after(() => rm(root, { force: true, recursive: true })); + const cli = path.resolve("src", "cli.mjs"); + const run = async (...args) => JSON.parse((await execFileAsync(process.execPath, [cli, ...args, "--repo-root", root, "--json"])).stdout); + const initialized = await run("graph", "init", "--graph-id", "knowledge:cli"); + assert.equal(initialized.graphVersion, 0); + const inputPath = path.join(root, "evidence.json"); + await writeFile(inputPath, `${JSON.stringify({ nodes: [evidence] }, null, 2)}\n`); + const ingest = await run("graph", "ingest", "--input", "evidence.json"); + assert.equal(ingest.proposalOnly, true); + const validated = await run("graph", "validate", "--patch", ingest.patch.patchId); + assert.equal(validated.passed, true); + const applied = await run("graph", "apply", "--patch", ingest.patch.patchId, "--approved-by", "human:cli-reviewer"); + assert.equal(applied.receipt.toVersion, 1); + const queried = await run("graph", "query", "EvoGraph paper"); + assert.equal(queried.results[0].entity.id, evidence.id); + const inspected = await run("graph", "inspect"); + assert.equal(inspected.nodes, 1); +}); diff --git a/test/model-intelligence.test.mjs b/test/model-intelligence.test.mjs new file mode 100644 index 0000000..9473336 --- /dev/null +++ b/test/model-intelligence.test.mjs @@ -0,0 +1,184 @@ +import assert from "node:assert/strict"; +import { mkdtemp, mkdir, readFile, rm, writeFile } from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; +import test from "node:test"; +import { + compileModelIntelligence, + diagnoseModelFailures, + initializeHarness, + MODEL_FAILURE_CAUSES, + MODEL_FAILURE_CLASSES, + writeModelBaseline, +} from "../src/lib/model-intelligence.mjs"; +import { createProject } from "../src/lib/scaffold.mjs"; +import { validateSchema } from "../src/lib/schema-validation.mjs"; + +const HASH = "a".repeat(64); + +function observation(overrides = {}) { + const taskId = overrides.taskId ?? "task-1"; + return { + schemaVersion: "nodekit.model-observation/v1", + runId: overrides.runId ?? "run-1", + applicationId: "model-lab", + taskId, + taskFamily: overrides.taskFamily ?? "bounded-repair", + model: { + requestedRoute: "openrouter/free", + resolvedProvider: "example-provider", + resolvedModel: "exact-model-1", + modelRevision: "2026-07-21", + }, + harness: { + version: "h0", + hash: HASH, + toolSurfaceHash: HASH, + contextPolicyHash: HASH, + skillStackHash: HASH, + }, + budgets: { maximumTokens: 1000, maximumCostUsd: 0, maximumDurationMs: 10000 }, + cognitive: { + briefUnderstanding: 0.8, + decomposition: 0.8, + constraintRetention: 0.9, + ambiguityDetection: 0.7, + referenceUse: 0.8, + repairReasoning: 0.7, + }, + execution: { + toolSelection: 0.8, + validArguments: 0.9, + toolOrdering: 0.8, + resultInspection: 0.5, + recovery: 0.7, + scopedChanges: 0.9, + completion: 0.8, + }, + artifact: { correctness: 0.8, usability: 0.8, domainQuality: 0.7, evidenceIntegrity: 0.9 }, + efficiency: { latencyMs: 1000, toolCalls: 3, retries: 0, costUsd: 0 }, + failures: overrides.failures ?? [], + evidenceRefs: ["proof/run-1.json"], + proofReceiptId: "receipt-1", + }; +} + +function capabilityCard() { + return { + schemaVersion: "nodekit.model-capability-card/v1", + model: { + requestedRoute: "openrouter/free", + resolvedProvider: "example-provider", + resolvedModel: "exact-model-1", + modelRevision: "2026-07-21", + }, + scope: { level: "project", applicationId: "model-lab", taskFamilies: ["bounded-repair"] }, + evidenceWindow: { from: "2026-07-01", to: "2026-07-21", benchmarkRuns: 1, taskCount: 1, harnessVersions: ["h0"] }, + strengths: ["bounded typed changes"], + weaknesses: ["inconsistent result inspection"], + bestRoles: ["bounded-editor"], + avoidRoles: ["final-proof-authority"], + requiredScaffolding: ["inspect-tool-result"], + metrics: { briefAdherence: 0.8, validToolCalls: 0.9, medianLatencyMs: 1000, costPerSuccessUsd: 0 }, + confidence: { level: "low", reason: "One controlled run; provisional only" }, + expiresWhen: ["model revision changes", "harness major version changes"], + evidenceRefs: ["proof/run-1.json"], + status: "provisional", + }; +} + +test("model observation and capability-card schemas require exact resolved identity", async () => { + assert.deepEqual(await validateSchema("nodekit.model-observation.v1.schema.json", observation(), "observation"), []); + assert.deepEqual(await validateSchema("nodekit.model-capability-card.v1.schema.json", capabilityCard(), "card"), []); + const invalid = observation(); + delete invalid.model.resolvedModel; + assert.match( + (await validateSchema("nodekit.model-observation.v1.schema.json", invalid, "observation")).join("\n"), + /resolvedModel/, + ); + assert.equal(MODEL_FAILURE_CLASSES.includes("FALSE_COMPLETION"), true); + assert.equal(MODEL_FAILURE_CLASSES.includes("AUTHORITY_VIOLATION"), true); + assert.equal(MODEL_FAILURE_CLASSES.includes("PRODUCT_TOPOLOGY_MISS"), true); + assert.equal(MODEL_FAILURE_CLASSES.includes("MOBILE_TOPOLOGY_FAILURE"), true); + assert.equal(MODEL_FAILURE_CAUSES.includes("product contract"), true); +}); + +test("harness init is additive and baseline fails closed to an unmeasured registry", async (t) => { + const root = await mkdtemp(path.join(os.tmpdir(), "nodekit-model-intelligence-")); + t.after(() => rm(root, { force: true, recursive: true })); + await createProject({ git: false, install: false, name: "model-lab", target: root }); + assert.equal(await readFile(path.join(root, "README.md"), "utf8").then((text) => text.includes("harness/models")), false); + + const first = await initializeHarness(root); + const second = await initializeHarness(root); + assert.equal(first.created.includes("harness/harness.yaml"), true); + assert.match(await readFile(first.frontend.routePath, "utf8"), /status: unprofiled/); + assert.equal(second.created.length, 0); + const { receipt } = await writeModelBaseline(root); + assert.equal(receipt.status, "unmeasured"); + assert.equal(receipt.providerCallsMade, 0); + assert.equal(receipt.capabilityClaimsCertified, false); + assert.equal(receipt.routingCertified, false); +}); + +test("compiler validates observations and cards and keeps requested aliases separate from resolved models", async (t) => { + const root = await mkdtemp(path.join(os.tmpdir(), "nodekit-model-registry-")); + t.after(() => rm(root, { force: true, recursive: true })); + await createProject({ git: false, install: false, name: "model-lab", target: root }); + await initializeHarness(root); + await writeFile(path.join(root, ".qa", "models", "observations", "run-1.json"), `${JSON.stringify(observation(), null, 2)}\n`); + const cardRoot = path.join(root, "harness", "models", "cards", "project"); + await mkdir(cardRoot, { recursive: true }); + await writeFile(path.join(cardRoot, "exact-model-1.json"), `${JSON.stringify(capabilityCard(), null, 2)}\n`); + + const compiled = await compileModelIntelligence(root); + assert.equal(compiled.registry.status, "profiled"); + assert.deepEqual(compiled.registry.requestedRoutes, ["openrouter/free"]); + assert.deepEqual(compiled.registry.models, ["example-provider/exact-model-1"]); + assert.equal(compiled.registry.routingCertified, false); + assert.match(await readFile(path.join(root, ".nodekit", "harness", "harness-hash.txt"), "utf8"), /^[a-f0-9]{64}\n$/); +}); + +test("compiler rejects escaped evidence roots and unsupported project-card claims", async (t) => { + const root = await mkdtemp(path.join(os.tmpdir(), "nodekit-model-integrity-")); + t.after(() => rm(root, { force: true, recursive: true })); + await createProject({ git: false, install: false, name: "model-lab", target: root }); + await initializeHarness(root); + const harnessPath = path.join(root, "harness", "harness.yaml"); + const originalHarness = await readFile(harnessPath, "utf8"); + await writeFile(harnessPath, originalHarness.replace("observationRoot: .qa/models/observations", "observationRoot: ../outside")); + await assert.rejects(() => compileModelIntelligence(root, { write: false }), /observationRoot must stay within the repository/); + await writeFile(harnessPath, originalHarness); + + await writeFile(path.join(root, ".qa", "models", "observations", "run-1.json"), `${JSON.stringify(observation(), null, 2)}\n`); + const unsupported = capabilityCard(); + unsupported.evidenceWindow.benchmarkRuns = 2; + const cardRoot = path.join(root, "harness", "models", "cards", "project"); + await mkdir(cardRoot, { recursive: true }); + await writeFile(path.join(cardRoot, "unsupported.json"), `${JSON.stringify(unsupported, null, 2)}\n`); + await assert.rejects(() => compileModelIntelligence(root, { write: false }), /claims 2 runs but only 1 matching observations exist/); +}); + +test("failure diagnosis only marks repeated multi-brief findings as skill candidates", () => { + const failure = (id, taskId) => ({ + failureId: id, + failureClass: "NO_RESULT_INSPECTION", + severity: "P1", + model: "exact-model-1", + taskId, + harnessVersion: "h0", + behavior: "Declared completion before inspecting the tool result", + expectedBehavior: "Inspect the tool result before completion", + probableCause: "model", + evidenceRefs: [`proof/${id}.json`], + }); + const observations = [ + observation({ runId: "run-1", taskId: "task-1", failures: [failure("f-1", "task-1")] }), + observation({ runId: "run-2", taskId: "task-1", failures: [failure("f-2", "task-1")] }), + observation({ runId: "run-3", taskId: "task-2", failures: [failure("f-3", "task-2")] }), + ]; + const [cluster] = diagnoseModelFailures(observations); + assert.equal(cluster.count, 3); + assert.deepEqual(cluster.taskIds, ["task-1", "task-2"]); + assert.equal(cluster.skillCandidateEligible, true); +}); diff --git a/test/postgres-adapter.test.mjs b/test/postgres-adapter.test.mjs new file mode 100644 index 0000000..7718a88 --- /dev/null +++ b/test/postgres-adapter.test.mjs @@ -0,0 +1,31 @@ +import assert from "node:assert/strict"; +import { readFile } from "node:fs/promises"; +import path from "node:path"; +import test from "node:test"; +import { createPostgresCaseflow } from "@homenshum/nodekit/adapters/postgres"; + +test("PostgreSQL adapter is available through the supported package entry point", () => { + assert.equal(typeof createPostgresCaseflow, "function"); + assert.throws(() => createPostgresCaseflow(), /query-capable pool/); + assert.throws(() => createPostgresCaseflow({ pool: { query() {} } }), /ownerId/); +}); + +test("PostgreSQL migration defines the complete owner-scoped Caseflow record set", async () => { + const sql = await readFile(path.resolve("adapters/postgres/001_caseflow.sql"), "utf8"); + for (const table of ["cases", "runs", "artifacts", "artifact_versions", "proposals", "approvals", "exceptions", "receipts", "events"]) { + assert.match(sql, new RegExp(`create table if not exists nodekit\\.${table}`)); + } + assert.match(sql, /owner_id text not null/); + assert.match(sql, /create or replace function nodekit\.apply_proposal/); + assert.match(sql, /proposal_row\.base_version <> artifact_row\.canonical_version/); + assert.match(sql, /approval_row\.decision <> requested_decision/); +}); + +test("Supabase profile extends the complete portable record set with owner RLS", async () => { + const sql = await readFile(path.resolve("adapters/supabase/001_profile.sql"), "utf8"); + for (const table of ["cases", "runs", "artifacts", "artifact_versions", "proposals", "approvals", "exceptions", "receipts", "events"]) { + assert.match(sql, new RegExp(`alter table nodekit\\.${table} enable row level security`)); + } + assert.match(sql, /owner_id = auth\.uid\(\)::text/); + assert.match(sql, /pg_publication_tables/); +}); diff --git a/test/public-api.test.mjs b/test/public-api.test.mjs new file mode 100644 index 0000000..ff016b1 --- /dev/null +++ b/test/public-api.test.mjs @@ -0,0 +1,19 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import { + CASEFLOW_SCHEMA_VERSIONS, + createMemoryCaseflow, + runCaseflowConformance, + runtimeProfiles, +} from "@homenshum/nodekit/caseflow"; +import { createPostgresCaseflow } from "@homenshum/nodekit/adapters/postgres"; + +test("published Caseflow entry point exposes the supported portable contract", async () => { + assert.equal(CASEFLOW_SCHEMA_VERSIONS.case, "nodekit.case/v1"); + assert.equal(runtimeProfiles.memory.optimisticConcurrency, true); + const verdict = await runCaseflowConformance(() => createMemoryCaseflow()); + assert.equal(verdict.passed, true); + assert.equal(verdict.assertions.staleProposalFailedClosed, true); + assert.equal(verdict.assertions.contentAddressedReceipt, true); + assert.equal(typeof createPostgresCaseflow, "function"); +}); diff --git a/test/submission-cli.test.mjs b/test/submission-cli.test.mjs new file mode 100644 index 0000000..5deb564 --- /dev/null +++ b/test/submission-cli.test.mjs @@ -0,0 +1,54 @@ +import assert from "node:assert/strict"; +import { execFileSync, spawnSync } from "node:child_process"; +import { mkdtemp, readFile, writeFile } from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; +import test from "node:test"; +import { fileURLToPath } from "node:url"; + +const repositoryRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); + +function git(root, args) { + return execFileSync("git", args, { cwd: root, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim(); +} + +async function createRepository() { + const root = await mkdtemp(path.join(os.tmpdir(), "nodekit-submission-cli-")); + git(root, ["init"]); + git(root, ["config", "user.email", "nodekit@example.com"]); + git(root, ["config", "user.name", "NodeKit Test"]); + await writeFile(path.join(root, "candidate.txt"), "candidate\n"); + git(root, ["add", "candidate.txt"]); + git(root, ["commit", "-m", "candidate"]); + return root; +} + +test("submission preparation accepts explicit named options", async () => { + const root = await createRepository(); + const output = "proof/named-options-manifest.json"; + const result = spawnSync(process.execPath, [ + path.join(repositoryRoot, "scripts", "prepare-submission.mjs"), + "--candidate", "HEAD", + "--repo-root", root, + "--output", output, + ], { cwd: repositoryRoot, encoding: "utf8" }); + assert.equal(result.status, 0, result.stderr); + const summary = JSON.parse(result.stdout); + assert.equal(summary.candidateCommit, git(root, ["rev-parse", "HEAD"])); + assert.equal(summary.outputPath, output); + assert.equal(summary.submissionReady, false); + const manifest = JSON.parse(await readFile(path.join(root, output), "utf8")); + assert.equal(manifest.candidateCommit, summary.candidateCommit); + assert.equal(manifest.gates.length, 8); +}); + +test("submission scripts fail clearly on unknown options", () => { + for (const script of ["prepare-submission.mjs", "evaluate-submission.mjs"]) { + const result = spawnSync(process.execPath, [path.join(repositoryRoot, "scripts", script), "--wat"], { + cwd: repositoryRoot, + encoding: "utf8", + }); + assert.notEqual(result.status, 0); + assert.match(result.stderr, /unknown option --wat/); + } +}); diff --git a/test/submission-gate.test.mjs b/test/submission-gate.test.mjs new file mode 100644 index 0000000..ec0b829 --- /dev/null +++ b/test/submission-gate.test.mjs @@ -0,0 +1,127 @@ +import assert from "node:assert/strict"; +import { mkdtemp, mkdir, cp, readFile, writeFile } from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; +import test from "node:test"; +import { createHash } from "node:crypto"; +import { execFileSync } from "node:child_process"; +import { evaluateSubmissionManifest, requiredSubmissionGates } from "../src/lib/submission-gate.mjs"; + +const digest = (value) => createHash("sha256").update(value).digest("hex"); + +test("submission gate requires all evidence, hashes, and explicit approval", async () => { + const root = await mkdtemp(path.join(os.tmpdir(), "nodekit-submission-")); + await mkdir(path.join(root, "schemas"), { recursive: true }); + await mkdir(path.join(root, "proof"), { recursive: true }); + await cp(path.resolve("schemas", "nodekit.submission-manifest.v1.schema.json"), path.join(root, "schemas", "nodekit.submission-manifest.v1.schema.json")); + execFileSync("git", ["init"], { cwd: root, stdio: "ignore" }); + execFileSync("git", ["config", "user.email", "nodekit@example.com"], { cwd: root }); + execFileSync("git", ["config", "user.name", "NodeKit Test"], { cwd: root }); + await writeFile(path.join(root, "candidate.txt"), "candidate\n"); + execFileSync("git", ["add", "candidate.txt", "schemas"], { cwd: root }); + execFileSync("git", ["commit", "-m", "candidate"], { cwd: root, stdio: "ignore" }); + const candidateCommit = execFileSync("git", ["rev-parse", "HEAD"], { cwd: root, encoding: "utf8" }).trim(); + const verdicts = { + developerTimingMatrix: { schemaVersion: "nodekit.developer-timing-verdict/v1", nodekitCommit: candidateCommit, passed: true, observedRuns: 60 }, + freshAgentHeldout: { schemaVersion: "nodekit.fresh-agent-verdict/v1", nodekitCommit: candidateCommit, passed: true, selectedRuns: [{}, {}, {}] }, + freshHumanUsability: { schemaVersion: "nodekit.fresh-user-verdict/v1", nodekitCommit: candidateCommit, passed: true, metrics: { participantCount: 5 } }, + threeConvexConsumers: { schemaVersion: "nodekit.convex-consumers-verdict/v1", candidateCommit, passed: true, qualifyingConsumers: 3 }, + previewDeployment: { schemaVersion: "nodekit.preview-verdict/v1", candidateCommit, passed: true, freshIdentity: true, exportReopenPassed: true, cleanupPassed: true }, + proofloopEaseVerification: { subject: { repository: { candidateCommit } }, verdict: { status: "passed" }, extensions: { easeCertified: true } }, + packageInstallProof: { + schemaVersion: "nodekit.package-install-proof/v1", + candidateCommit, + passed: true, + tarballSha256: "a".repeat(64), + checks: { freshConsumerInstall: true, packagedCliCreate: true, compile: true, check: true, demo: true, eval: true }, + }, + publicationApproval: { schemaVersion: "nodekit.publication-approval/v1", candidateCommit, approved: true, approvedBy: "owner", scopes: ["npm-publish", "convex-directory-submit"] }, + }; + const gates = []; + for (const id of requiredSubmissionGates) { + const relative = `proof/${id}.json`; + const bytes = Buffer.from(`${JSON.stringify(verdicts[id])}\n`); + await writeFile(path.join(root, relative), bytes); + gates.push({ id, passed: true, evidence: [{ path: relative, sha256: digest(bytes) }] }); + } + const manifest = { + schemaVersion: "nodekit.submission-manifest/v1", + candidateCommit, + gates, + }; + await writeFile(path.join(root, "proof", "submission-manifest.json"), JSON.stringify(manifest)); + assert.equal((await evaluateSubmissionManifest(root)).submissionReady, true); + manifest.gates.at(-1).passed = false; + await writeFile(path.join(root, "proof", "submission-manifest.json"), JSON.stringify(manifest)); + const blocked = await evaluateSubmissionManifest(root); + assert.equal(blocked.submissionReady, false); + assert.match(blocked.errors.join("\n"), /publicationApproval: not passed/); +}); + +test("submission gate rejects stale source revisions and cross-gate evidence reuse", async () => { + const root = await mkdtemp(path.join(os.tmpdir(), "nodekit-submission-stale-")); + await mkdir(path.join(root, "schemas"), { recursive: true }); + await mkdir(path.join(root, "proof"), { recursive: true }); + await cp(path.resolve("schemas", "nodekit.submission-manifest.v1.schema.json"), path.join(root, "schemas", "nodekit.submission-manifest.v1.schema.json")); + execFileSync("git", ["init"], { cwd: root, stdio: "ignore" }); + execFileSync("git", ["config", "user.email", "nodekit@example.com"], { cwd: root }); + execFileSync("git", ["config", "user.name", "NodeKit Test"], { cwd: root }); + await writeFile(path.join(root, "candidate.txt"), "candidate\n"); + execFileSync("git", ["add", "candidate.txt", "schemas"], { cwd: root }); + execFileSync("git", ["commit", "-m", "candidate"], { cwd: root, stdio: "ignore" }); + const candidateCommit = execFileSync("git", ["rev-parse", "HEAD"], { cwd: root, encoding: "utf8" }).trim(); + await mkdir(path.join(root, "src")); + await writeFile(path.join(root, "src", "changed.mjs"), "export default true;\n"); + execFileSync("git", ["add", "src/changed.mjs"], { cwd: root }); + execFileSync("git", ["commit", "-m", "source changed"], { cwd: root, stdio: "ignore" }); + const evidence = Buffer.from(`${JSON.stringify({ schemaVersion: "nodekit.developer-timing-verdict/v1", nodekitCommit: candidateCommit, passed: true, observedRuns: 60 })}\n`); + await writeFile(path.join(root, "proof", "shared.json"), evidence); + const manifest = { + schemaVersion: "nodekit.submission-manifest/v1", + candidateCommit, + gates: requiredSubmissionGates.map((id) => ({ id, passed: true, evidence: [{ path: "proof/shared.json", sha256: digest(evidence) }] })), + }; + await writeFile(path.join(root, "proof", "submission-manifest.json"), JSON.stringify(manifest)); + const verdict = await evaluateSubmissionManifest(root); + assert.equal(verdict.submissionReady, false); + assert.match(verdict.errors.join("\n"), /candidateCommit is stale/); + assert.match(verdict.errors.join("\n"), /evidence path is reused/); +}); + +test("submission gate rejects uncommitted source and incomplete package proof", async () => { + const root = await mkdtemp(path.join(os.tmpdir(), "nodekit-submission-dirty-")); + await mkdir(path.join(root, "schemas"), { recursive: true }); + await mkdir(path.join(root, "proof"), { recursive: true }); + await mkdir(path.join(root, "src"), { recursive: true }); + await cp(path.resolve("schemas", "nodekit.submission-manifest.v1.schema.json"), path.join(root, "schemas", "nodekit.submission-manifest.v1.schema.json")); + execFileSync("git", ["init"], { cwd: root, stdio: "ignore" }); + execFileSync("git", ["config", "user.email", "nodekit@example.com"], { cwd: root }); + execFileSync("git", ["config", "user.name", "NodeKit Test"], { cwd: root }); + await writeFile(path.join(root, "candidate.txt"), "candidate\n"); + execFileSync("git", ["add", "candidate.txt", "schemas"], { cwd: root }); + execFileSync("git", ["commit", "-m", "candidate"], { cwd: root, stdio: "ignore" }); + const candidateCommit = execFileSync("git", ["rev-parse", "HEAD"], { cwd: root, encoding: "utf8" }).trim(); + const verdicts = { + developerTimingMatrix: { schemaVersion: "nodekit.developer-timing-verdict/v1", nodekitCommit: candidateCommit, passed: true, observedRuns: 60 }, + freshAgentHeldout: { schemaVersion: "nodekit.fresh-agent-verdict/v1", nodekitCommit: candidateCommit, passed: true, selectedRuns: [{}, {}, {}] }, + freshHumanUsability: { schemaVersion: "nodekit.fresh-user-verdict/v1", nodekitCommit: candidateCommit, passed: true, metrics: { participantCount: 5 } }, + threeConvexConsumers: { schemaVersion: "nodekit.convex-consumers-verdict/v1", candidateCommit, passed: true, qualifyingConsumers: 3 }, + previewDeployment: { schemaVersion: "nodekit.preview-verdict/v1", candidateCommit, passed: true, freshIdentity: true, exportReopenPassed: true, cleanupPassed: true }, + proofloopEaseVerification: { subject: { repository: { candidateCommit } }, verdict: { status: "passed" }, extensions: { easeCertified: true } }, + packageInstallProof: { schemaVersion: "nodekit.package-install-proof/v1", candidateCommit, passed: true, tarballSha256: "a".repeat(64), checks: {} }, + publicationApproval: { schemaVersion: "nodekit.publication-approval/v1", candidateCommit, approved: true, approvedBy: "owner", scopes: ["npm-publish", "convex-directory-submit"] }, + }; + const gates = []; + for (const id of requiredSubmissionGates) { + const relative = `proof/${id}.json`; + const bytes = Buffer.from(`${JSON.stringify(verdicts[id])}\n`); + await writeFile(path.join(root, relative), bytes); + gates.push({ id, passed: true, evidence: [{ path: relative, sha256: digest(bytes) }] }); + } + await writeFile(path.join(root, "proof", "submission-manifest.json"), JSON.stringify({ schemaVersion: "nodekit.submission-manifest/v1", candidateCommit, gates })); + await writeFile(path.join(root, "src", "dirty.mjs"), "export default true;\n"); + const verdict = await evaluateSubmissionManifest(root); + assert.equal(verdict.submissionReady, false); + assert.match(verdict.errors.join("\n"), /working tree contains uncommitted source changes/); + assert.match(verdict.errors.join("\n"), /packageInstallProof: no exact-candidate decisive verdict/); +}); diff --git a/test/submission-preparation.test.mjs b/test/submission-preparation.test.mjs new file mode 100644 index 0000000..88ae707 --- /dev/null +++ b/test/submission-preparation.test.mjs @@ -0,0 +1,107 @@ +import assert from "node:assert/strict"; +import { createHash } from "node:crypto"; +import { execFileSync } from "node:child_process"; +import { mkdtemp, mkdir, readFile, writeFile } from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; +import test from "node:test"; +import { + canonicalSubmissionEvidence, + prepareSubmissionManifest, +} from "../src/lib/submission-preparation.mjs"; + +function git(root, args) { + return execFileSync("git", args, { cwd: root, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim(); +} + +async function createRepository(prefix) { + const root = await mkdtemp(path.join(os.tmpdir(), prefix)); + git(root, ["init"]); + git(root, ["config", "user.email", "nodekit@example.com"]); + git(root, ["config", "user.name", "NodeKit Test"]); + await writeFile(path.join(root, "candidate.txt"), "candidate\n"); + git(root, ["add", "candidate.txt"]); + git(root, ["commit", "-m", "candidate"]); + return { root, candidateCommit: git(root, ["rev-parse", "HEAD"]) }; +} + +function exactVerdicts(candidateCommit) { + return { + developerTimingMatrix: { schemaVersion: "nodekit.developer-timing-verdict/v1", nodekitCommit: candidateCommit, passed: true, observedRuns: 60 }, + freshAgentHeldout: { schemaVersion: "nodekit.fresh-agent-verdict/v1", nodekitCommit: candidateCommit, passed: true, selectedRuns: [{}, {}, {}] }, + freshHumanUsability: { schemaVersion: "nodekit.fresh-user-verdict/v1", nodekitCommit: candidateCommit, passed: true, metrics: { participantCount: 5 } }, + threeConvexConsumers: { schemaVersion: "nodekit.convex-consumers-verdict/v1", candidateCommit, passed: true, qualifyingConsumers: 3 }, + previewDeployment: { schemaVersion: "nodekit.preview-verdict/v1", candidateCommit, passed: true, freshIdentity: true, exportReopenPassed: true, cleanupPassed: true }, + proofloopEaseVerification: { subject: { repository: { candidateCommit } }, verdict: { status: "passed" }, extensions: { easeCertified: true } }, + packageInstallProof: { + schemaVersion: "nodekit.package-install-proof/v1", + candidateCommit, + passed: true, + tarballSha256: "a".repeat(64), + checks: { freshConsumerInstall: true, packagedCliCreate: true, compile: true, check: true, demo: true, eval: true }, + }, + publicationApproval: { schemaVersion: "nodekit.publication-approval/v1", candidateCommit, approved: true, approvedBy: "owner", scopes: ["npm-publish", "convex-directory-submit"] }, + }; +} + +async function writeVerdicts(root, verdicts) { + for (const gate of canonicalSubmissionEvidence) { + await mkdir(path.dirname(path.join(root, gate.path)), { recursive: true }); + await writeFile(path.join(root, gate.path), `${JSON.stringify(verdicts[gate.id])}\n`); + } +} + +test("submission preparation emits eight distinct fail-closed gates when evidence is missing", async () => { + const { root, candidateCommit } = await createRepository("nodekit-prepare-missing-"); + const result = await prepareSubmissionManifest({ repoRoot: root }); + assert.equal(result.manifest.candidateCommit, candidateCommit); + assert.equal(result.manifest.gates.length, 8); + assert.equal(new Set(result.manifest.gates.map((gate) => gate.evidence[0].path)).size, 8); + assert.equal(result.manifest.gates.every((gate) => gate.passed === false), true); + assert.equal(result.manifest.gates.every((gate) => gate.evidence[0].sha256 === "0".repeat(64)), true); + assert.deepEqual( + JSON.parse(await readFile(path.join(root, "proof", "submission-manifest.json"), "utf8")), + result.manifest, + ); +}); + +test("submission preparation passes only exact-candidate decisive evidence", async () => { + const { root, candidateCommit } = await createRepository("nodekit-prepare-pass-"); + await writeVerdicts(root, exactVerdicts(candidateCommit)); + const result = await prepareSubmissionManifest({ repoRoot: root, candidateRef: candidateCommit }); + assert.equal(result.sourceIsExact, true); + assert.equal(result.manifest.gates.every((gate) => gate.passed === true), true); + for (const gate of result.manifest.gates) { + const bytes = await readFile(path.join(root, gate.evidence[0].path)); + assert.equal(gate.evidence[0].sha256, createHash("sha256").update(bytes).digest("hex")); + } +}); + +test("submission preparation never upgrades stale, invalid, or unapproved evidence", async () => { + const { root, candidateCommit } = await createRepository("nodekit-prepare-blocked-"); + const verdicts = exactVerdicts(candidateCommit); + verdicts.developerTimingMatrix.nodekitCommit = "a".repeat(40); + verdicts.publicationApproval.approved = false; + verdicts.publicationApproval.approvedBy = ""; + verdicts.publicationApproval.scopes = []; + await writeVerdicts(root, verdicts); + await writeFile(path.join(root, canonicalSubmissionEvidence.find((gate) => gate.id === "freshAgentHeldout").path), "not json\n"); + const result = await prepareSubmissionManifest({ repoRoot: root }); + const byId = Object.fromEntries(result.manifest.gates.map((gate) => [gate.id, gate])); + assert.equal(byId.developerTimingMatrix.passed, false); + assert.equal(byId.freshAgentHeldout.passed, false); + assert.equal(byId.publicationApproval.passed, false); + assert.match(byId.freshAgentHeldout.evidence[0].sha256, /^[a-f0-9]{64}$/); + assert.notEqual(byId.freshAgentHeldout.evidence[0].sha256, "0".repeat(64)); +}); + +test("submission preparation blocks otherwise passing evidence while source is dirty", async () => { + const { root, candidateCommit } = await createRepository("nodekit-prepare-dirty-"); + await writeVerdicts(root, exactVerdicts(candidateCommit)); + await mkdir(path.join(root, "src")); + await writeFile(path.join(root, "src", "uncommitted.mjs"), "export default true;\n"); + const result = await prepareSubmissionManifest({ repoRoot: root }); + assert.equal(result.sourceIsExact, false); + assert.deepEqual(result.sourceChanges, ["src/uncommitted.mjs"]); + assert.equal(result.manifest.gates.every((gate) => gate.passed === false), true); +}); diff --git a/test/types/caseflow-consumer.ts b/test/types/caseflow-consumer.ts new file mode 100644 index 0000000..9575243 --- /dev/null +++ b/test/types/caseflow-consumer.ts @@ -0,0 +1,23 @@ +import { + CASEFLOW_SCHEMA_VERSIONS, + createMemoryCaseflow, + runCaseflowConformance, + type NodeKitArtifact, +} from "@homenshum/nodekit/caseflow"; +import { + createPostgresCaseflow, + type PostgreSqlPool, +} from "@homenshum/nodekit/adapters/postgres"; + +const memory = createMemoryCaseflow(); +const created = memory.createCase({ title: "Typed case", primaryJob: "Prove the public API" }); +void created; +void CASEFLOW_SCHEMA_VERSIONS.case; +void runCaseflowConformance(() => memory); + +declare const pool: PostgreSqlPool; +const postgres = createPostgresCaseflow({ pool, ownerId: "owner_123" }); +void postgres.snapshot(); + +declare const artifact: NodeKitArtifact<{ value: number }>; +artifact.versions.at(-1)?.content.value satisfies number | undefined; diff --git a/tsconfig.public.json b/tsconfig.public.json new file mode 100644 index 0000000..37178fa --- /dev/null +++ b/tsconfig.public.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "allowJs": false, + "exactOptionalPropertyTypes": true, + "module": "NodeNext", + "moduleResolution": "NodeNext", + "noEmit": true, + "skipLibCheck": false, + "strict": true, + "target": "ES2022" + }, + "include": [ + "src/**/*.d.ts", + "src/**/*.d.mts", + "test/types/**/*.ts" + ] +}