Skip to content

[codepress#5858 3a-0] EditorHost.subscribe - #15

Merged
lightninglu10 merged 1 commit into
codepress-mainfrom
ve-3a0-editorhost-subscribe
Aug 22, 2026
Merged

[codepress#5858 3a-0] EditorHost.subscribe#15
lightninglu10 merged 1 commit into
codepress-mainfrom
ve-3a0-editorhost-subscribe

Conversation

@lightninglu10

@lightninglu10lightninglu10 commented Aug 22, 2026

Copy link
Copy Markdown

Item 3a-0 of https://github.com/quantfive/codepress/issues/5858 ("Video editor v1: ship the editor and connect it to the CodePress agent").

Why

The published EditorHost is pull-only (capabilities, load, resolveMedia, submitEdit, transcript?, navigation?, notify?). HostEditorController.replaceAuthoritativeSnapshot() is the right entry point for an out-of-band revision and EmbeddedEditorHostRuntime already subscribes to it, but nothing on the public contract reaches it. So the only way a host could push an agent's (or another user's) revision into a mounted editor was to swap the host prop — a full remount that resets the playhead, clears undo, and rebuilds the media library.

This is the prerequisite for the in-editor agent loop in CodePress (#5858 item 2c).

What

  • EditorHost gains an optional subscribe?(listener: (snapshot: EmbeddedEditorSnapshot) => void): () => void.
  • FreeCutEditorSurface wires it to the runtime's controller right after constructing the runtime, and returns the unsubscribe from the mount effect's cleanup. A pushed snapshot travels the existing receiving path — the same one a submitEdit result takes.
  • packages/freecut-editor/src/index.d.ts (hand-written, copied verbatim into dist) declares the same optional method; @quantfive/freecut-editor-surface bumps to 0.3.3.
  • Tests: an out-of-band snapshot pushed through subscribe lands in the timeline stores with the same DOM node and a single load() (no remount), and the listener is dropped on unmount.

applySnapshotToStores is unchanged — it still resets playhead/undo on an authoritative snapshot. Known and accepted for now; out of scope here.

Publishing 0.3.3 to npm is not part of this PR — a maintainer publishes after merge.

The published EditorHost was pull-only, so the only way to push an
agent's or another user's revision into a mounted editor was to swap the
host prop — a full remount. Add an optional subscribe port: the surface
wires it to the runtime controller's replaceAuthoritativeSnapshot, the
same path a submitted edit's result takes, and returns the unsubscribe
from the mount effect's cleanup.
@lightninglu10

Copy link
Copy Markdown
Author

Canonical QA Report

Verifier revision binding

base: 47e2db3 (origin/staging)
head: aca9613
git rev-parse HEAD -> aca9613
git status --porcelain -> (empty)

Gate results

#GateResultEvidence
1Head binding✅ PASSnpm run qa:binding -- --check → "OK: clean tree, head descends from base"
2Type check✅ PASSnpm run check → "Found no warnings, lint errors, or type errors in 2445 files"
3Lint✅ PASSnpm run lint (after build:editor-surface) → no findings
4Unit tests❌ FAIL (inherited)npm run test:run → 45 failed / 637 passed files, 584 failed / 4280 passed tests — byte-identical failure set at base (see below). Focused: vp test run src/features/editor/host/editor-surface.test.tsx → 3 passed (3)
5Build✅ PASSnpm run build → built in 6.30s
6Feature boundaries✅ PASS1675 files scanned, no cross-feature imports outside deps/*
7Deps contract boundaries✅ PASS161 deps files, 297 contract files
8Legacy lib imports✅ PASSno unauthorized @/lib/* imports
9Deps wrapper health✅ PASS46 pass-through wrappers, 0 unused
10Unused exports❌ FAIL (inherited)129 findings, 119 allowlisted, 10 new, 3 stale — identical counts and identical entries at base; none in the changed files
11Unused class members❌ FAIL (inherited)83 findings, 83 allowlisted, 0 new, 2 stale — identical at base
12Changed-health✅ PASS5 changed files, base origin/staging; introduced dead_code=0, complexity=0, duplication=0
13Edge budgets✅ PASSall edges within budget
14Provenance/reproducibility✅ PASSnpm run verify:provenance → source/license/notices/dependency(51+16)/asset(5 roots) verified
15Editor-surface package build✅ PASSnpm run build:editor-surface → built in 8.69s; dist/index.d.ts:343 carries the new subscribe?
16Installed consumer smoke✅ PASSnpm run package:editor-surfaceartifacts/freecut-editor-surface-0.3.3.tgz sha256 9f0e3da0b32f4fae67b6d3d6004b06f8c89a42bbe082b0e161535fa02194cdd7; npm run test:editor-surface:consumer -- --artifact <tgz> → 1 passed (1), installed @quantfive/freecut-editor-surface@0.3.3
17Headless contract tests (Node)✅ PASSnpm run headless:test:node → 43 pass / 0 fail
18Browser QA✅ PASSnpm run qa:browser -- --skip-build → system Chrome 151.0.7922.172; harness ready, renderFrame ok, frame width matches, PNG >1KB
19Full headless browser suite✅ PASSnpm run headless:test:chrome → all headless checks + 19 edit-operation contracts + lifecycle HTTP/CLI passed. Also npm run headless:test:media → portable media/audio contract passed
20Redaction✅ PASSnpm run check:qa-redaction → gate pass + fail-closed self-test 6 pass / 0 fail
+Publish guards✅ PASSnpm run test:publish-editor-surface-guards → 7 pass / 0 fail

Inherited baseline failures

  • Gate 4 (unit tests): FAIL at base 47e2db3c — jsdom without localStorage (opaque origin) breaks the zustand persist middleware across the suite. Reproduced at the base SHA in a separate worktree: base = 45 failed / 637 passed files, 584 failed / 4278 passed tests; head = 45 failed / 637 passed files, 584 failed / 4280 passed tests. The delta is exactly the two tests this PR adds; no new failing file or test. The one touched test file (src/features/editor/host/editor-surface.test.tsx) installs the same hoisted localStorage stub the neighbouring runtime.test.tsx already uses, so it passes in both environments.
  • Gate 10 (unused exports): FAIL at base 47e2db3c with "129 findings, 119 allowlisted, 10 new, 3 stale" — identical at head. All 10 new entries are in properties-sidebar, timeline-motion, keyframes, preview, timeline and composition-runtime; none is touched by this PR. Allowlists were not edited.
  • Gate 11 (unused class members): FAIL at base 47e2db3c with "83 findings, 83 allowlisted, 0 new, 2 stale, 0 changed counts" — identical at head. Allowlists were not edited.

Environment blockers

  • None. Gates 18–19 ran against system Chrome 151.0.7922.172.

Visual / browser artifacts

  • artifacts/qa/browser-aca961335d98/manifest.json (+ frame.png, harness.png, console.log)

Overall

✅ PASS — PENDING JUDGE (gates 4, 10 and 11 fail identically at base 47e2db3c; disclosed above, reproduced at base, not caused by this PR)

No merge until trusted judge PASS.

@lightninglu10

Copy link
Copy Markdown
Author

@codepress /judge-verification can you judge this verification?

@codepress-dev

Copy link
Copy Markdown

👋 Hey @lightninglu10! Spawning a verification judge session to help with your request. View session

@codepress-dev

Copy link
Copy Markdown

@Terminal@lightninglu10

Dispatched by judge-verification.

Work the Path to merge below. Reuse healthy exact-head verification environments; do not redeploy or restart unless stale or unhealthy.

Continue only when there is meaningful work to clear the blocker. If the path is already satisfied, the same blocker remains, or another judge pass would add no useful evidence, stop and report that instead of submitting another verification report.

Verification Judgment — PR #15 (staging)

Verdict: ❌ NOT READY TO MERGE

PR Head SHA:aca961335d9856fe6e5059950646f6ca0bbf9d2a

Summary: The supplied report is missing. The linked PR comment contains only a request to start judging and no canonical report head, verification marker, or evidence rows. Safety of the live head therefore cannot be established; the verification report must be reposted and rerun for this exact head.

Revision binding

  • Classification: TRUE STALE EVIDENCE
  • Report head:missing
  • Live PR head:aca961335d9856fe6e5059950646f6ca0bbf9d2a
  • Audited checkout: clean detached checkout at aca961335d9856fe6e5059950646f6ca0bbf9d2a; git status --porcelain was empty
  • Concrete evidence:gh pr view 15 reports live head aca961335d9856fe6e5059950646f6ca0bbf9d2a and base staging; the linked comment 5382343977 was created at 2026-08-22T20:04:57Z and contains only the judge request, with neither a canonical full-head field nor a codepress-verify-result marker
  • Evidence gap: No verification report was supplied for this judgment, so no report revision, clean verifier provenance, or behavioral evidence can be audited.
  • Rows carried forward: none
  • Rows reverified at live head: none
  • Rows still requiring verification: 1, 2, 3, 4, 5
  • Publication timing: The linked comment is a trigger, not a report; its timestamp cannot establish report/head ordering or verifier checkout provenance.

Diff trigger inventory

SurfaceLocationCoverage
Public optional EditorHost.subscribe contractsrc/features/editor/host/contract.ts:299-302; packages/freecut-editor/src/index.d.ts:3431
Out-of-band snapshot delivery into the mounted editor and storessrc/features/editor/host/editor-surface.tsx:39-49; src/features/editor/host/controller.ts:456-462; src/features/editor/host/runtime.ts:116-1192
Backward-compatible host without subscribesrc/features/editor/host/editor-surface.tsx:46; src/features/editor/host/editor-surface.test.tsx:103-1423
Subscription cleanup during surface teardown or host replacementsrc/features/editor/host/editor-surface.tsx:34-59; src/features/editor/components/editor.tsx:528-5374
Published package version, copied declaration, and installed-consumer surfacepackages/freecut-editor/package.json:3; vite.editor-package.config.ts:84-87; scripts/test-editor-surface-consumer.mjs:100-1525

Per-item

#Verifier saidJudgmentEvidence
1BLOCKED⚠️ BLOCKEDNo report row or evidence establishes that the source and published declaration expose the same optional subscribe contract.
2BLOCKED⚠️ BLOCKEDNo report row or real trigger evidence shows a host-pushed snapshot reaching the controller, timeline stores, and mounted editor without a remount or extra load().
3BLOCKED⚠️ BLOCKEDNo report row or evidence covers the negative branch in which an existing host omits subscribe and the surface still mounts normally.
4BLOCKED⚠️ BLOCKEDNo report row or evidence proves the returned unsubscribe is called on unmount and host replacement, with no stale listener remaining.
5BLOCKED⚠️ BLOCKEDNo report row or installed-consumer evidence proves the 0.3.3 package build and copied declaration remain consumable.

Overall status check

  • Verifier said: BLOCKED
  • Result: NOT READY TO MERGE

Path to merge

RowClassOwnerAction that clears it
1verification-gapverifierRepost a complete report rerun at the live SHA with the public source contract and copied package declaration evidence.
2verification-gapverifierExercise a real host-backed surface, push an authoritative snapshot through subscribe, and record the resulting store state, DOM identity, and load() count.
3verification-gapverifierExercise a host implementation without subscribe and record successful initial surface mount and initial load.
4verification-gapverifierExercise surface unmount and host replacement, then record unsubscribe calls and the absence of stale snapshot delivery.
5verification-gapverifierBuild/package 0.3.3, install the tarball in the consumer smoke fixture, and record the installed version and passing consumer result.

Becomes ✅ READY TO MERGE when: Rows 1, 2, 3, 4, and 5 are covered by a newly posted verification report rerun for aca961335d9856fe6e5059950646f6ca0bbf9d2a, with no blocking row remaining.

Dispatch: yes

What stood up

  • The live PR head was independently resolved and audited in a clean detached checkout.
  • The diff inventory identifies the contract, delivery, compatibility, lifecycle, and installed-package surfaces that the missing report must cover.

@lightninglu10

Copy link
Copy Markdown
Author

@codepress /judge-verification can you judge this verification?

Local Verification — PR #15 (FreeCut, base staging)

PR Head SHA:aca961335d9856fe6e5059950646f6ca0bbf9d2a

Verifier revision binding

  • git rev-parse HEAD -> aca961335d9856fe6e5059950646f6ca0bbf9d2a
  • git status --porcelain -> (empty)

Captured from npm run qa:binding -- --check at report time: "OK: clean tree, head descends from base"; base 47e2db3c2d5a6148cb9137f3a2a21836322568a7 (origin/staging). The head is unchanged from the judged head — no commit was made for this rerun.

Note on the prior judgment: a canonical QA report for this exact head was already posted (comment 5381952676, 2026-08-22T18:32:15Z), but it used the FreeCut docs/qa/canonical-report.md template, which carries no codepress-verify-result marker and no **PR Head SHA:** field. The judge was linked to the trigger comment (5382343977) and correctly found no bindable report. This report restates the same head in the CodePress verifier format and adds the behavioral evidence rows 1–5 asked for.

Diff trigger inventory

SurfaceLocationRow
Public optional EditorHost.subscribe contractsrc/features/editor/host/contract.ts:302; packages/freecut-editor/src/index.d.ts:3431
Out-of-band snapshot delivery into the mounted editor and storessrc/features/editor/host/editor-surface.tsx:39-49; controller.ts:456-462; runtime.ts:116-1192
Backward-compatible host without subscribesrc/features/editor/host/editor-surface.tsx:463
Subscription cleanup on teardown and host replacementsrc/features/editor/host/editor-surface.tsx:34-594
Published version, copied declaration, installed-consumer surfacepackages/freecut-editor/package.json:3; vite.editor-package.config.ts:84-875

How rows 1–4 were exercised

All four rows were exercised against the real published artifact, not the repo source: npm run package:editor-surface produced artifacts/freecut-editor-surface-0.3.3.tgz (sha256 9f0e3da0b32f4fae67b6d3d6004b06f8c89a42bbe082b0e161535fa02194cdd7, reproduced byte-identical on two independent runs), which was installed with npm install --ignore-scripts --no-package-lock --no-save <tgz> into a fresh fixture. The probe renders the real FreeCutEditorSurface → real LoadedEditor tree → real EmbeddedEditorHostRuntime → real zustand stores. Nothing inside the package is mocked or stubbed; the only test doubles are the EditorHost implementations, which are the caller-supplied port the contract exists for.

Negative control (anti-vacuity). The identical probe file and the identical TypeScript consumer file were run a second time against a tarball built from the base commit 47e2db3c (freecut-editor-surface-0.3.2.tgz, sha256 f8c185d25065a0827302661af4011aef43e83de9d8de301b4c94886bfc87764d). Rows 2/4a/4b fail there and row 3 still passes — so the probes detect exactly this PR's behavior and are not passing vacuously.

Behavioral contract results

#Effect that must be observedRequired evidenceResultHow it was observed
1Source contract and published declaration expose the same optionalsubscribe, and the declaration is load-bearing for consumersinstalled-artifact typecheck✅ PASSByte-identical signature at three layers: src/features/editor/host/contract.ts:302, packages/freecut-editor/src/index.d.ts:343, and the installednode_modules/@quantfive/freecut-editor-surface/dist/index.d.ts:343 — all subscribe?(listener: (snapshot: EmbeddedEditorSnapshot) => void): () => void. Real tsc@7.0.2 --strict --noEmit against the installed declaration: a host withoutsubscribe and a host with it both compile (exit=0), and the call site must be defensive (subscribe?.(...)). Load-bearing, not any: a subscribe returning voidTS2322: Type 'void' is not assignable to type '() => void' (exit=1); a listener typed (snapshot: string)TS2345: Type 'EmbeddedEditorSnapshot' is not assignable to type 'string' (exit=1). Base control: the same positive file against installed 0.3.2 → TS2353: 'subscribe' does not exist in type 'EditorHost' (exit=1)
2A snapshot pushed through subscribe reaches the mounted editor and stores with no remount and no extra load()live execution against installed package✅ PASSProbe ROW 2: mounted the real editor tree from installed 0.3.3, captured the [data-freecut-editor-surface="host"] node, pushed revision 7 carrying a text item PUSHED_BY_AGENT_MARKER. Observed: same surface node = true (identical DOM node instance → never torn down), load calls = 1, subscribe calls = 1, rendered marker nodes = 1 — the pushed item is rendered by the real timeline tree, having gone through replaceAuthoritativeSnapshot → controller listener → applySnapshotToStores. The marker was asserted absent before the push. Base control: fails (subscribe never called)
3An existing host that omits subscribe still mounts the full editor unchangedlive execution against installed package✅ PASSProbe ROW 3: asserted 'subscribe' in host === false and host.subscribe === undefined, then mounted from installed 0.3.3 → full tree renders (≥2 toolbars, Preview area region, Timeline, properties-clip-panel-host), load calls = 1, and unmount runs the cleanup path without throwing. Base control: also passes (correct — this is the pre-existing behavior this PR must not regress)
4The returned unsubscribe runs on unmount and on host replacement, leaving no stale listener able to deliverlive execution against installed package✅ PASSProbe ROW 4a (unmount): unsubscribe calls after unmount = 1, listeners = 0. Probe ROW 4b (host replacement): after rerender with a second host — old-host unsubscribes = 1, old listeners = 0, new listeners = 1, new host subscribe called exactly once; a stale push through the replaced host reached nobody (marker asserted absent) while a push through the live host rendered. Base control: both fail
5The 0.3.3 package builds, the declaration is copied, and the tarball is consumable as an installed dependencypackaged + installed consumer run✅ PASSnpm run package:editor-surfaceversion 0.3.3, sha256 9f0e3da0…94cdd7, identical across two runs (deterministic). npm run test:editor-surface:consumer -- --artifact artifacts/freecut-editor-surface-0.3.3.tgz → fresh temp fixture, verifyInstalledPackage name+version assertions pass, Test Files 1 passed (1) / Tests 1 passed (1), "installed and tested @quantfive/freecut-editor-surface@0.3.3". The independent probe fixture separately confirmed installed version 0.3.3 and the copied dist/index.d.ts

Probe run (installed 0.3.3): Test Files 1 passed (1) / Tests 4 passed (4).
Negative control (installed 0.3.2 from base): Test Files 1 failed (1) / Tests 3 failed | 1 passed (4) — failing exactly rows 2, 4a, 4b.

Committed in-repo focused tests at this head: vp test run src/features/editor/host/editor-surface.test.tsxTests 3 passed (3) (no-subscribe mount; pushed snapshot without remount; unsubscribe on unmount).

Repo gate set (FreeCut docs/qa gates 1–20, unchanged head)

17 of 20 gates plus the publish guards pass; full evidence table in comment 5381952676. Three gates fail identically at base 47e2db3c and are disclosed there, reproduced at the base SHA in a separate clean worktree, with no allowlist, skip list, or config edited:

  • Gate 4 unit tests: base 45 failed files / 584 failed tests; head 45 failed files / 584 failed tests. Head passes 4280 vs base 4278 — the delta is exactly the two tests this PR adds. Cause: jsdom opaque origin leaves localStorage undefined, which the zustand persist middleware captures at import time.
  • Gate 10 unused exports: "129 findings, 119 allowlisted, 10 new, 3 stale" — identical at base and head; none of the 10 is in a file this PR touches.
  • Gate 11 unused class members: "83 findings, 83 allowlisted, 0 new, 2 stale" — identical at base and head.

Scope note

applySnapshotToStores still resets playhead/undo when an authoritative snapshot is adopted. That is pre-existing behavior on the receiving path, explicitly out of scope for this item, and unchanged here — it is a known limitation, not a regression introduced by this PR.

Overall: ✅ PASS — PENDING JUDGE

Rows 1–5 are each covered by live execution against the installed published artifact, each with a base-commit negative control. Head unchanged at aca961335d9856fe6e5059950646f6ca0bbf9d2a. PR remains DRAFT.

@codepress-dev

Copy link
Copy Markdown

👋 Hey @lightninglu10! Spawning a verification judge session to help with your request. View session

@lightninglu10

Copy link
Copy Markdown
Author

@codepress /judge-verification can you judge this verification?

Local Verification — FreeCut PR #15 (base staging)

PR Head SHA:aca961335d9856fe6e5059950646f6ca0bbf9d2a

Verifier revision binding

  • git rev-parse HEAD -> aca961335d9856fe6e5059950646f6ca0bbf9d2a
  • git status --porcelain -> (empty)

Captured from npm run qa:binding -- --check: "OK: clean tree, head descends from base"; base 47e2db3c2d5a6148cb9137f3a2a21836322568a7 (origin/staging). The head is unchanged from the previously judged head — no commit was made for this rerun.

Report history: the first report for this exact head (comment 5381952676) used FreeCut's own docs/qa/canonical-report.md template, which carries no anchored result marker and no **PR Head SHA:** field, so it was not bindable. The second (comment 5382396097) added the binding but used sentence-case section headers, which the report parser rejects. This version fixes the headers to the exact-case forms the parser requires. The head, the artifact, and the evidence below are unchanged across all three.

Diff Trigger Inventory

SurfaceLocationRow
Public optional EditorHost.subscribe contractsrc/features/editor/host/contract.ts:302; packages/freecut-editor/src/index.d.ts:3431
Out-of-band snapshot delivery into the mounted editor and storessrc/features/editor/host/editor-surface.tsx:39-49; controller.ts:456-462; runtime.ts:116-1192
Backward-compatible host without subscribesrc/features/editor/host/editor-surface.tsx:463
Subscription cleanup on teardown and host replacementsrc/features/editor/host/editor-surface.tsx:34-594
Published version, copied declaration, installed-consumer surfacepackages/freecut-editor/package.json:3; vite.editor-package.config.ts:84-875

How rows 1-4 were exercised

All four rows were exercised against the real published artifact, not the repo source. npm run package:editor-surface produced artifacts/freecut-editor-surface-0.3.3.tgz (sha256 9f0e3da0b32f4fae67b6d3d6004b06f8c89a42bbe082b0e161535fa02194cdd7, reproduced byte-identical on two independent runs), which was installed with npm install --ignore-scripts --no-package-lock --no-save <tgz> into a fresh fixture. The probe renders the real FreeCutEditorSurface -> real LoadedEditor tree -> real EmbeddedEditorHostRuntime -> real zustand stores. Nothing inside the package is mocked or stubbed; the only test doubles are the EditorHost implementations, which are the caller-supplied port the contract exists for.

Negative control (anti-vacuity). The identical probe file and the identical TypeScript consumer file were run a second time against a tarball built from the base commit 47e2db3c (freecut-editor-surface-0.3.2.tgz, sha256 f8c185d25065a0827302661af4011aef43e83de9d8de301b4c94886bfc87764d). Rows 2/4a/4b fail there and row 3 still passes, so the probes detect exactly this PR's behavior and are not passing vacuously.

Verification Contract Results

#Effect that must be observedRequired evidenceResultHow it was observed
1Source contract and published declaration expose the same optionalsubscribe, and the declaration is load-bearing for consumersinstalled-artifact typecheck✅ PASSByte-identical signature at three layers: src/features/editor/host/contract.ts:302, packages/freecut-editor/src/index.d.ts:343, and the installednode_modules/@quantfive/freecut-editor-surface/dist/index.d.ts:343 — all subscribe?(listener: (snapshot: EmbeddedEditorSnapshot) => void): () => void. Real tsc@7.0.2 --strict --noEmit against the installed declaration: a host withoutsubscribe and a host with it both compile (exit=0), and the call site must be defensive (subscribe?.(...)). Load-bearing, not any: a subscribe returning void -> TS2322: Type 'void' is not assignable to type '() => void' (exit=1); a listener typed (snapshot: string) -> TS2345: Type 'EmbeddedEditorSnapshot' is not assignable to type 'string' (exit=1). Base control: the same positive file against installed 0.3.2 -> TS2353: 'subscribe' does not exist in type 'EditorHost' (exit=1)
2A snapshot pushed through subscribe reaches the mounted editor and stores with no remount and no extra load()live execution against installed package✅ PASSProbe ROW 2: mounted the real editor tree from installed 0.3.3, captured the [data-freecut-editor-surface="host"] node, pushed revision 7 carrying a text item PUSHED_BY_AGENT_MARKER. Observed same surface node = true (identical DOM node instance, so never torn down), load calls = 1, subscribe calls = 1, rendered marker nodes = 1 — the pushed item is rendered by the real timeline tree, having gone through replaceAuthoritativeSnapshot -> controller listener -> applySnapshotToStores. The marker was asserted absent before the push. Base control: fails, subscribe never called
3An existing host that omits subscribe still mounts the full editor unchangedlive execution against installed package✅ PASSProbe ROW 3: asserted 'subscribe' in host === false and host.subscribe === undefined, then mounted from installed 0.3.3 -> full tree renders (at least 2 toolbars, Preview area region, Timeline, properties-clip-panel-host), load calls = 1, and unmount runs the cleanup path without throwing. Base control: also passes, which is correct — this is the pre-existing behavior this PR must not regress
4The returned unsubscribe runs on unmount and on host replacement, leaving no stale listener able to deliverlive execution against installed package✅ PASSProbe ROW 4a (unmount): unsubscribe calls after unmount = 1, listeners = 0. Probe ROW 4b (host replacement): after rerender with a second host — old-host unsubscribes = 1, old listeners = 0, new listeners = 1, new host subscribe called exactly once; a stale push through the replaced host reached nobody (marker asserted absent) while a push through the live host rendered. Base control: both fail
5The 0.3.3 package builds, the declaration is copied, and the tarball is consumable as an installed dependencypackaged plus installed consumer run✅ PASSnpm run package:editor-surface -> version 0.3.3, sha256 9f0e3da0...94cdd7, identical across two runs (deterministic). npm run test:editor-surface:consumer -- --artifact artifacts/freecut-editor-surface-0.3.3.tgz -> fresh temp fixture, verifyInstalledPackage name and version assertions pass, Test Files 1 passed (1) / Tests 1 passed (1), "installed and tested @quantfive/freecut-editor-surface@0.3.3". The independent probe fixture separately confirmed installed version 0.3.3 and the copied dist/index.d.ts

Probe run (installed 0.3.3): Test Files 1 passed (1) / Tests 4 passed (4).
Negative control (installed 0.3.2 from base): Test Files 1 failed (1) / Tests 3 failed | 1 passed (4) — failing exactly rows 2, 4a, 4b.

Committed in-repo focused tests at this head: vp test run src/features/editor/host/editor-surface.test.tsx -> Tests 3 passed (3) (no-subscribe mount; pushed snapshot without remount; unsubscribe on unmount).

Repo gate set (FreeCut docs/qa gates 1-20, unchanged head)

17 of 20 gates plus the publish guards pass; the full evidence table is in comment 5381952676. Three gates fail identically at base 47e2db3c, reproduced at the base SHA in a separate clean worktree, with no allowlist, skip list, or config edited:

  • Gate 4 unit tests: base 45 failed files / 584 failed tests; head 45 failed files / 584 failed tests. Head passes 4280 vs base 4278 — the delta is exactly the two tests this PR adds. Cause: jsdom opaque origin leaves localStorage undefined, which the zustand persist middleware captures at import time.
  • Gate 10 unused exports: "129 findings, 119 allowlisted, 10 new, 3 stale" — identical at base and head; none of the 10 is in a file this PR touches.
  • Gate 11 unused class members: "83 findings, 83 allowlisted, 0 new, 2 stale" — identical at base and head.

Scope and disclosure

applySnapshotToStores still resets playhead/undo when an authoritative snapshot is adopted. That is pre-existing behavior on the receiving path, explicitly out of scope for this item, and unchanged here — a known limitation, not a regression introduced by this PR.

Rows 3 and 4b are evidenced by an ad-hoc probe held outside the repo, because the dispatch required the head to stay unchanged. If those branches should be covered by tests that live in the PR, that requires a new commit and can be added on request.

Overall: ✅ PASS — PENDING JUDGE

@codepress-dev

Copy link
Copy Markdown

👋 Hey @lightninglu10! Spawning a verification judge session to help with your request. View session

@codepress-dev

Copy link
Copy Markdown

@Terminal@lightninglu10

Dispatched by judge-verification.

This judgment is READY TO MERGE at the current head. If the PR is still a draft, transition it to ready for review now with the guarded readiness transaction: codepress-policy ready-for-review --repo . --pr 15 (or node packages/delivery-policy/dist/cli.js ready-for-review --repo . --pr 15 when the CLI is not on PATH). It re-validates current-head QA and judge evidence, reconciles the delivery-status marker, and marks the PR ready so the native reviewer can dispatch. Do not call gh pr ready directly, and do not merge; merge authorization still requires the reviewer gate.

Verification Judgment — PR #15 (local)

Verdict: ✅ READY TO MERGE

PR Head SHA:aca961335d9856fe6e5059950646f6ca0bbf9d2a

Summary: The current PR head is safe to merge based on the diff and the exact-head verification evidence. The optional host subscription contract is wired into the mounted editor's existing authoritative-snapshot path, backward compatibility is preserved, teardown and host replacement remove listeners, and the published package surface is covered.

Revision binding

  • Classification: SAME-SHA LATE BINDING
  • Report head:aca961335d9856fe6e5059950646f6ca0bbf9d2a
  • Live PR head:aca961335d9856fe6e5059950646f6ca0bbf9d2a
  • Audited checkout: clean detached checkout at aca961335d9856fe6e5059950646f6ca0bbf9d2a
  • Concrete evidence: GitHub reports PR [codepress#5858 3a-0] EditorHost.subscribe #15 head aca961335d9856fe6e5059950646f6ca0bbf9d2a on base staging. Report comment capture: git rev-parse HEAD -> aca9613; git status --porcelain -> (empty). Judge checkout capture: git rev-parse HEAD -> aca9613; git status --porcelain -> (empty).
  • Evidence gap: none
  • Rows carried forward: none
  • Rows reverified at live head: none
  • Rows still requiring verification: none
  • Publication timing: Report comment 5382396097 was created at 2026-08-22T20:17:38Z; the live PR lookup at audit time still returned the same head. The head commit's GitHub timestamp is 2026-08-22T18:29:59Z.

Diff trigger inventory

SurfaceLocationCoverage
Public optional EditorHost.subscribe contractsrc/features/editor/host/contract.ts; packages/freecut-editor/src/index.d.ts1, 5
Out-of-band snapshot delivery into the mounted editor and storessrc/features/editor/host/editor-surface.tsx; existing controller.ts and runtime.ts bridge2
Backward-compatible host without subscribesrc/features/editor/host/editor-surface.tsx optional call site3
Subscription cleanup on unmount and host replacementsrc/features/editor/host/editor-surface.tsx effect cleanup4
Published version, copied declaration, and installed-consumer surfacepackages/freecut-editor/package.json; vite.editor-package.config.ts5

Per-item

#Verifier saidJudgmentEvidence
1PASS✅ PASSThe diff adds the same optional subscribe?(listener: (snapshot: EmbeddedEditorSnapshot) => void): () => void contract to the source and package declaration. The installed 0.3.3 consumer checks accept hosts with and without the member, reject a void unsubscribe and a wrong listener type, and the 0.3.2 control rejects the member.
2PASS✅ PASSThe installed-artifact probe pushed a new snapshot after mount and observed the marker appear in the real editor/store path, with the marker absent before the push, the same surface DOM node, one load(), and one subscription. The diff routes the callback through replaceAuthoritativeSnapshot, which the existing runtime listener applies to stores.
3PASS✅ PASSThe installed-artifact probe mounted a host with no subscribe property and observed the full editor tree, one load(), and clean unmount. The diff uses the optional call, so the pre-existing host shape remains valid.
4PASS✅ PASSThe installed-artifact probe observed one unsubscribe on unmount and one unsubscribe on host replacement, zero old-host listeners, one new-host listener, and no delivery from the stale host while the live host still delivered. The effect cleanup is keyed to host, covering both teardown paths.
5PASS✅ PASSThe report built and installed @quantfive/freecut-editor-surface@0.3.3, confirmed the copied declaration, passed the installed consumer test, and reproduced an identical tarball hash across two builds. The package version and declaration-copy path are present in the audited diff.

Overall status check

  • Verifier said: PASS
  • Result: READY TO MERGE

What stood up

  • The focused installed-artifact probe has a base-commit negative control: snapshot delivery and both cleanup paths fail on 0.3.2 while the backward-compatible mount still passes.
  • The three reported repository-gate failures are reproduced as identical base/head findings, and the unused-symbol findings do not touch this diff. They therefore do not establish a regression in the inventoried contract.

@lightninglu10
lightninglu10 marked this pull request as ready for review August 22, 2026 20:24
@codepress-reviewer-dev

codepress-reviewer-devBot commented Aug 22, 2026

Copy link
Copy Markdown

Review of aca96133 failed before a valid GitHub review could be posted. No review decision was submitted. Failure class: ValueError. Retry the automation after correcting the failure.

View automation run

@lightninglu10
lightninglu10 changed the base branch from staging to codepress-mainAugust 22, 2026 20:25
lightninglu10 added a commit that referenced this pull request Aug 22, 2026
#15 publishes 0.3.3 when it merges, and the OIDC publish workflow's
already-published guard refuses a second publish at the same version. This PR
therefore has to carry its own version for its surface changes (the
ripple_delete capability, the transcript cut action, and the optional
requestTranscription port) to reach the registry.
@codepress-dev

Copy link
Copy Markdown

@Terminal@lightninglu10

Dispatched by judge-verification.

This judgment is READY TO MERGE at the current head. If the PR is still a draft, transition it to ready for review now with the guarded readiness transaction: codepress-policy ready-for-review --repo . --pr 15 (or node packages/delivery-policy/dist/cli.js ready-for-review --repo . --pr 15 when the CLI is not on PATH). It re-validates current-head QA and judge evidence, reconciles the delivery-status marker, and marks the PR ready so the native reviewer can dispatch. Do not call gh pr ready directly, and do not merge; merge authorization still requires the reviewer gate.

Verification Judgment — PR #15 (local)

Verdict: ✅ READY TO MERGE

PR Head SHA:aca961335d9856fe6e5059950646f6ca0bbf9d2a

Summary: The current PR head is safe to merge based on the audited diff and exact-head verification evidence. The optional host subscription is wired into the existing authoritative-snapshot path, hosts without the optional method remain supported, cleanup covers unmount and host replacement, and the published package surface is covered.

Revision binding

  • Classification: SAME-SHA LATE BINDING
  • Report head:aca961335d9856fe6e5059950646f6ca0bbf9d2a
  • Live PR head:aca961335d9856fe6e5059950646f6ca0bbf9d2a
  • Audited checkout: clean detached checkout at aca961335d9856fe6e5059950646f6ca0bbf9d2a
  • Concrete evidence:gh pr view 15 reports live head aca961335d9856fe6e5059950646f6ca0bbf9d2a on base staging. Report capture: git rev-parse HEAD -> aca961335d9856fe6e5059950646f6ca0bbf9d2a; git status --porcelain -> (empty). Judge checkout capture: git rev-parse HEAD -> aca961335d9856fe6e5059950646f6ca0bbf9d2a; git status --porcelain -> (empty); git diff --check -> exit 0. Its diff from base 47e2db3c2d5a6148cb9137f3a2a21836322568a7 contains only the five reported paths.
  • Evidence gap: none
  • Rows carried forward: none
  • Rows reverified at live head: none
  • Rows still requiring verification: none
  • Publication timing: Report comment 5382413548 was created at 2026-08-22T20:21:57Z; the live PR lookup during this audit still returned the same head. The head commit was created at 2026-08-22T18:29:59Z, so the report is late-bound to the unchanged live head. Publication timing supports ordering only; verifier cleanliness is established by the captured status output and the judge's clean detached checkout.

Diff trigger inventory

SurfaceLocationCoverage
Public optional EditorHost.subscribe contractsrc/features/editor/host/contract.ts:288-302; packages/freecut-editor/src/index.d.ts:336-3431, 5
Out-of-band snapshot delivery into the mounted editor and storessrc/features/editor/host/editor-surface.tsx:34-59; src/features/editor/host/controller.ts:451-462; src/features/editor/host/runtime.ts:116-1192
Backward-compatible host without subscribesrc/features/editor/host/editor-surface.tsx:463
Subscription cleanup on unmount and host replacementsrc/features/editor/host/editor-surface.tsx:34-594
Published package version, copied declaration, and installed-consumer surfacepackages/freecut-editor/package.json:3; vite.editor-package.config.ts:84-87; scripts/test-editor-surface-consumer.mjs5

Per-item

#Verifier saidJudgmentEvidence
1PASS✅ PASSThe diff adds the same optional subscribe?(listener: (snapshot: EmbeddedEditorSnapshot) => void): () => void signature to the source and package declaration. The installed 0.3.3 consumer typecheck accepts hosts with and without the member, rejects a void unsubscribe and a wrong listener type, and the 0.3.2 control rejects the member.
2PASS✅ PASSThe real installed 0.3.3 artifact probe pushed a snapshot after mount and observed its marker absent before the push and rendered afterward through the controller/runtime store path. It observed the same surface DOM node, one load(), and one subscription; the 0.3.2 negative control did not subscribe and failed this behavior.
3PASS✅ PASSThe installed-artifact probe mounted a host with no subscribe property, rendered the full editor surface, called load() once, and unmounted cleanly. The optional call site preserves the existing host shape.
4PASS✅ PASSThe installed-artifact probe observed one unsubscribe on surface unmount and one on host replacement, zero listeners on the old host, one on the new host, no delivery from the stale host, and delivery from the live host. The effect cleanup is keyed to host, covering both lifecycle paths.
5PASS✅ PASSThe report built and installed @quantfive/freecut-editor-surface@0.3.3, confirmed the copied declaration, passed the installed consumer test, and reproduced an identical tarball hash across two builds. The package version and declaration-copy path are present in the audited diff.

Overall status check

  • Verifier said: PASS
  • Result: READY TO MERGE

What stood up

  • The installed-artifact probe has a base-commit negative control: snapshot delivery and both cleanup paths fail on 0.3.2 while the backward-compatible mount still passes.
  • The three reported repository-gate failures are reproduced as identical base/head findings, and the unused-symbol findings do not touch this diff; they do not establish a regression in the inventoried contract.

@lightninglu10
lightninglu10 marked this pull request as draft August 22, 2026 20:40
@lightninglu10
lightninglu10 marked this pull request as ready for review August 22, 2026 20:41
@codepress-reviewer-dev

Copy link
Copy Markdown

Starting initial review of aca96133 from baseline 47e2db3c... 👀

View automation run

@codepress-reviewer-devcodepress-reviewer-devBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved.

The change is the right shape: it extends the existing host boundary with an optional push channel and routes updates through the established authoritative-snapshot controller. The source and published declarations match, and the effect cleanup correctly owns the host listener lifecycle.

One nonblocking follow-up is worth addressing: a host that emits during subscribe (or immediately after it is registered) can update the controller before the runtime's mountStores() listener exists. The runtime then mounts from its original snapshot and drops that early revision. Have the runtime read the controller's current snapshot when mounting, or establish the runtime listener before registering the host callback, and cover an immediately-emitting host in a regression test.

Reviewed `47e2db3c`...`aca96133`. Diff collection and GitHub posting were handled by the automation.

View automation run

@codepress-reviewer-dev

Copy link
Copy Markdown

Review head aca961335d9856fe6e5059950646f6ca0bbf9d2a; round 1; 5 reviewed file(s), 0 pending/skipped file(s). 1 subsystem record(s) retained; 0 omitted.

View automation run

@lightninglu10
lightninglu10 merged commit 0f62cc1 into codepress-mainAug 22, 2026
3 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@lightninglu10