Skip to content

fix: declare host compatibility by capability, not a wrong minHost - #15

Merged
cchacs merged 3 commits into
mainfrom
pullfrog/6-declare-correct-host-floor
Oct 1, 2026
Merged

cchacs merged 3 commits into
mainfrom
pullfrog/6-declare-correct-host-floor

Conversation

@pullfrog

@pullfrog pullfrog Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

Fixes #6.

What this does

Replaces the manifest's host version declaration with a capability check that can tell users which component is actually too old.

Note this reverses a prior attempt: commit 7e9f52c ("Updated minHost to 1.14.8") is already on main. This PR removes the field rather than replacing it with a different number. If you expected minHost to end up with a value, this is the part to push back on — reasoning below.

Why minHost is removed instead of corrected

minHost was 1.14.8, which is a Split Screen plugin version. Per plugin-spec §4.1, minHost means the minimum Host (core) version — so the field described a core compatibility level that the number says nothing about.

No core version can replace it honestly:

  • feedBack's VERSION has read 0.3.0-alpha.2 since ec1157ac (2026-08-10) — five weeks before renderFrameAt() landed in f7c761c (2026-09-17T02:36:07Z) — and f7c761c's own parent be5f27a reports the same string. main still reads it today.
  • feedBack publishes no releases and no tags, so anything above 0.3.0-alpha.2 names a release that does not exist.

So 0.3.0-alpha.2 would admit ~5 weeks of hosts that cannot export, and any higher value is fiction. feedBack #102 declines to pin a floor for this API and proposes publishing a traceable baseline version instead — this PR follows that, and CLAUDE.md records when to restore the field.

It also buys little today: minHost is advisory-only in the current host. I grepped all of feedBack's static/js/plugin-loader.js for minHost|min_host|minimum|requireHost — zero matches, and no version-gating code path exists in the module. A wrong value would not have warned anyone; it would only mislead a future enforcing host.

The enforcement that replaces it

frameDriverProblem() in screen.js checks the two requirements separately and names the failing one:

Situation Message
Host lacks highway.renderFrameAt names feedBack commit f7c761c; does not blame Split Screen
Split Screen active, bridge incomplete names the missing hooks and Split Screen 1.14.8; notes the host is fine
Up to date proceeds

Previously one message named both at once ("Update the host and Splitscreen plugin"), so a stale Split Screen was indistinguishable from a stale host.

Requiring the whole bridge is substantive, not cosmetic. Split Screen's renderFrameAt() starts with if (!active || !_offlineRenderActive || ...) return false (screen.js:684 in that repo), so beginOfflineRender() is a precondition of painting any frame. A build exposing renderFrameAt without it could never export — it died on frame 0 with "The selected visualization is not ready for offline rendering", which says nothing about the cause. That case is now reported up front by name. To be precise about what this does not fix: it did not produce a corrupted video before, since Split Screen's own guard returned false.

Attribution of the bridge

Split Screen's v1.14.20 release notes describe the hooks as "deterministic offline-render hooks added in 2301dd5 and completed in 87e3622a." This PR cites both and takes the floor as 1.14.8, where 87e3622a set the manifest and whose parent 7cd0ec5 is manifest 1.14.7 with no beginOfflineRender. (I'd initially written that 2301dd5 did not introduce the bridge — upstream's own wording corrects that.)

Other changes

  • README.md — requirements split into four independent sections (feedBack host / Split Screen / WebCodecs / FFmpeg). Drops highway.getCanvas(), which screen.js never calls.
  • CLAUDE.md — host contract updated with the verified history; also clears a stale "Known gap" note about Staff View that survived PR Fix Staff View notation never appearing in exported video #4 (fb03961) merging. Unrelated to Declare the correct minimum host version #6 but it was actively wrong in a file I was editing; easy to drop if you'd rather split it.
  • tests/js/screen.test.js + a CI step — screen.js has no exports, so the test extracts the gate from the real source by brace matching (the same idiom feedBack core uses in tests/js/highway_external_frame_driver.test.js) and drives it directly. Verified by mutation: disabling the core check fails 3 tests, removing the splitActive guard fails 2.

Verification

node --check screen.js, node --test tests/js/screen.test.js (9 pass), python -m pytest -q tests/ (7 pass), and every step in custom-checks.yml all pass locally.

Follow-ups (deliberately not in this PR)

Pullfrog  | View workflow run | via Pullfrog | Using big-pickle (free) | 𝕏

Issue #6. The manifest declared minHost 1.14.8, which is a Split Screen
plugin version, while the plugin spec defines minHost as the minimum *Host
(core)* version -- so the field claimed a core compatibility level the
number says nothing about.

No core version can honestly replace it: feedBack's VERSION file has read
0.3.0-alpha.2 since 2026-08-10, five weeks before highway.renderFrameAt()
landed in f7c761c, and feedBack publishes no releases or tags. So 0.3.0-alpha.2
admits hosts that cannot export and anything higher names a release that does
not exist. feedBack issue #102 says the same and proposes publishing a
traceable baseline version. minHost is also advisory-only in the current
host (no reference to it in static/js/plugin-loader.js).

Remove the field and enforce the requirement by capability instead. The
pre-flight check now names the side that is actually missing: the
always-required highway.renderFrameAt() (feedBack f7c761c), or Split Screen's
beginOfflineRender()/renderFrameAt()/endOfflineRender() bridge (Split Screen
1.14.8) when a split layout is being exported. Previously one message named
both at once, so a stale Split Screen was indistinguishable from a stale host.

Requiring the whole bridge is substantive, not cosmetic: Split Screen's
renderFrameAt() returns false unless beginOfflineRender() has run, so a
partial bridge always failed on frame 0 with a misleading 'visualization is
not ready' error. It is now reported up front, by name.

Also: document the two host floors separately from WebCodecs and FFmpeg in
README.md and CLAUDE.md, drop the getCanvas() dependency that screen.js never
used, correct CLAUDE.md's bridge attribution to Split Screen's own release
wording (added in 2301dd5, completed in 87e3622a), and clear the Staff View
'known gap' note now that PR #4 has merged.

screen.js has no exports, so tests/js/screen.test.js extracts the gate from the
real source and drives it directly; custom-checks.yml runs it.
@pullfrog
pullfrog Bot requested a review from cchacs October 1, 2026 19:11
@codacy-production

codacy-production Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

Not up to standards ⛔

🔴 Issues 1 critical · 1 high

Alerts:
⚠ 2 issues (≤ 0 issues of at least minor severity)

Results:
2 new issues

Category Results
Security 1 critical
1 high

View in Codacy

🟢 Metrics 39 complexity · 0 duplication

Metric Results
Complexity 39
Duplication 0

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

Comment thread CLAUDE.md Resolved
Comment thread tests/js/screen.test.js Resolved
@pullfrog

pullfrog Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor Author

Addressed both findings from the kilo-code review in commit 61d967a; both threads are fixed, pushed, and resolved.

1. CLAUDE.md line citations (real off-by-one). Verified against the file: getSongInfo() is at screen.js:282, the audioUrl fallback chain at :290, and the setInterval poll at :391 — all three were one line low. Corrected, including the Mount lifecycle section the reviewer didn't enumerate. getSections() at :230 was already right, so I left it.

2. block("const SPLIT_BRIDGE =") silently swallowing frameDriverProblem (real, and worse than a style nit). Reproduced it: SPLIT_BRIDGE is an array literal, so the first { after the signature is the opening brace of frameDriverProblem further down, and the extracted "constant" spans lines 251–275 — the whole function. The rebuilt new Function body therefore declared frameDriverProblem twice, legal only because new Function bodies are sloppy mode. Replaced brace-matching for the constant with a statement() helper that slices to the terminating semicolon; block() is retained for the function, which is what it was actually suited to.

The interesting part is what the reviewer's framing missed: the suite was already proving it reads the shipped source, so the duplicate declaration wasn't making it test the wrong code — it was a latent fragility. So I verified the fix by mutation rather than by the green run:

Mutation Result
core check → if (false) 3 of 9 red
if (splitActive) → if (true) 2 of 9 red
SPLIT_BRIDGE → ['renderFrameAt'] 2 of 9 red

The third is new to this PR and is the one that matters for this fix — it only goes red because the constant is now genuinely extracted and evaluated. Also green: node --check screen.js, node --test tests/js/screen.test.js (9 pass), python -m pytest -q tests/ (7 pass), plus every step in custom-checks.yml (manifest paths, JS syntax, no-print-in-routes).

One note for you, not blocking: this PR still takes 0.2.2 while PR #14 targets 0.3.0 and rewrites the compositor, so one of the two will need adjusting at merge time. I left that as-is since it's unrelated to this review.

Task list (9/9 completed)
  • Checkout PR fix: declare host compatibility by capability, not a wrong minHost #15 branch
  • Fetch and read review comments (review 5384580279)
  • Verify each finding against actual code
  • Implement sound fixes for actionable findings
  • Test / review diff, commit
  • Push branch
  • Reply to each relevant thread once
  • Resolve threads with fixes on remote
  • Fresh re-read of comments, then report progress

Pullfrog  | View workflow run | via Pullfrog | Using big-pickle (free) | 𝕏

@kilo-code-bot

kilo-code-bot Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Both findings from the previous review are resolved in the latest commits: CLAUDE.md now references screen.js:282, :290, and :391 (verified against HEAD), and the test helper extracts SPLIT_BRIDGE with a new statement() helper that slices to the terminating semicolon, so it no longer bleeds into frameDriverProblem. The incremental diff introduces no new defects.

Files Reviewed (2 files)
  • CLAUDE.md - 0 issues
  • tests/js/screen.test.js - 0 issues
Previous Review Summary (commit 8143e86)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit 8143e86)

Status: 2 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 0
SUGGESTION 2
Issue Details (click to expand)

SUGGESTION

File Line Issue
CLAUDE.md 17 Updated screen.js line references are off by one (282/290/391, not 281/289/390)
tests/js/screen.test.js 34 block("const SPLIT_BRIDGE = ") brace-matches into frameDriverProblem, producing a duplicate function declaration; extraction is silently wrong
Files Reviewed (7 files)
  • .github/workflows/custom-checks.yml - 0 issues
  • CHANGELOG.md - 0 issues
  • CLAUDE.md - 1 issue
  • README.md - 0 issues
  • plugin.json - 0 issues
  • screen.js - 0 issues
  • tests/js/screen.test.js - 1 issue

The capability gate itself is sound: the core check and the Split Screen bridge check are correctly separated, the error path restores exporting/button state, and the newly non-optional split.beginOfflineRender() / split.endOfflineRender() calls are safe because frameDriverProblem() already guarantees all three hooks are functions whenever splitActive is true.

Fix these issues in Kilo Cloud


Reviewed by free · Input: 0 · Output: 0 · Cached: 0

…colon

CLAUDE.md cited screen.js lines 281/289/390; the code is at 282/290/391.
230 was already correct.

block("const SPLIT_BRIDGE =") brace-matched an array literal, so it
swallowed frameDriverProblem along with it and the rebuilt body declared
that function twice -- legal only because new Function bodies are sloppy
mode. Extract the constant to its terminating semicolon instead.

cchacs commented Oct 1, 2026

Copy link
Copy Markdown
Collaborator

@pullfrog this PR now conflicts with main. Please merge main into this branch (no rebase or force-push), resolve the conflicts keeping both sides' intent, and keep CHANGELOG.md entries under [Unreleased]. Re-run node --check screen.js, node --test tests/js/screen.test.js and python -m pytest -q tests/ before pushing. Please don't merge it.

The two Codacy findings on screen.js (generic object injection on split?.[name], where name comes from the constant SPLIT_BRIDGE) and tests/js/screen.test.js (new Function over the repo's own source) are false positives; no code change is needed for them.


Generated by Claude Code

@pullfrog

pullfrog Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor Author

Merged main into this branch (merge commit 3dfc41a, no rebase, no force-push) and resolved all five conflicting files. Both sides' intent is preserved:

  • screen.js — kept main's export-session transport (streaming encoder sink, session mux with the 409 audio_required retry, audio upload, encoder backpressure, compositor object with updateTimeline/composeFrame/dispose) and this branch's capability gate. The two conflict sites resolve to the branch's unguarded split.beginOfflineRender() / split.endOfflineRender() rather than main's optional chaining — frameDriverProblem() already requires all three hooks whenever a split layout is exported, which is the whole point of the change. main's refactor of createCompositor (returning an object, not a bare function) is why the branch side's composeFrame = await … line could not survive.
  • plugin.json — 0.2.2 → 0.3.1. main already bumped to 0.3.0, and neither version is released (both sides' entries live under [Unreleased]), so this branch's user-visible bump lands on top of main's instead of reverting it. This was the one judgement call: taking 0.2.2 would have been a downgrade, taking 0.3.0 would have dropped this branch's bump entirely. Happy to change it if you'd rather sequence the releases differently.
  • CHANGELOG.md — one [Unreleased] section holding all 11 entries, ordered Added → Changed → Removed → Fixed (4 from main under Added, 4 from main plus this branch's 1 under Changed, this branch's Removed/Fixed).
  • README.md — main's progress-reporting paragraph and FEEDBACK_PUBLIC_ORIGIN section kept, ahead of this branch's four-part ## Requirements breakdown (which supersedes main's flatter ## Host integration list).
  • CLAUDE.md — main's new "Encoder pipeline and transport" section kept; this branch's corrections kept (issue #102 deliberately does not pin a floor; Staff View is a fix, not a known gap). All screen.js line citations renumbered for the merged file (getSongInfo 589, audio fallback 597, getSections 402, mount poll 789).

Verification (all green): node --check screen.js, node --test tests/js/screen.test.js (9 pass), python -m pytest -q tests/ (33 pass). I also ran the remaining steps from custom-checks.yml — manifest path validation and the print-call rejection in routes.py — for completeness.

As requested, I did not merge the PR, and the two Codacy findings were left alone.

Two pre-existing doc inaccuracies I noticed but did not touch, since they are outside the conflict:

  1. CLAUDE.md's Testing section still says "No JS test harness exists in this repo yet", which this branch's tests/js/screen.test.js and CI step now contradict.
  2. CLAUDE.md still describes getSections() as being "for HUD text", but main put #player-hud in skipChrome, so updateTimeline() now returns before reaching that call and the exported video carries no HUD text at all.
Task list (7/7 completed)
  • Checkout PR fix: declare host compatibility by capability, not a wrong minHost #15 branch and fetch main
  • Merge origin/main into branch (no rebase)
  • Resolve conflicts keeping both sides' intent; keep CHANGELOG.md under [Unreleased]
  • Verify: node --check screen.js
  • Verify: node --test tests/js/screen.test.js
  • Verify: python -m pytest -q tests/
  • Commit and push merge

Pullfrog  | View workflow run | via Pullfrog | Using big-pickle (free) | 𝕏

Resolve the conflicts with main (PR #14, perf: pipeline encoding, cache
compositor pixels, stream exports) without dropping either side's intent.

screen.js keeps main's export-session transport (streaming encoder sink,
session mux, audio upload, encoder backpressure, compositor object) and this
branch's capability gate, so the Split Screen bridge calls stay unguarded:
frameDriverProblem() already requires all three hooks whenever a split layout
is exported, which is the point of the change.

plugin.json moves 0.2.2 -> 0.3.1. main already bumped to 0.3.0 and neither
version is released (both sets of entries sit under [Unreleased]), so this
branch's user-visible bump lands on top of main's instead of reverting it.

CHANGELOG.md keeps both sides' entries in one [Unreleased] section, ordered
Added, Changed, Removed, Fixed. README.md keeps main's progress-reporting and
FEEDBACK_PUBLIC_ORIGIN sections ahead of this branch's four-part Requirements
breakdown. CLAUDE.md keeps main's encoder-pipeline section and this branch's
corrections (issue #102 does not pin a floor; the Staff View note is a fix, not
a gap), with screen.js line citations renumbered for the merged file.
@cchacs
cchacs merged commit a7bad12 into main Oct 1, 2026
3 of 4 checks passed
@cchacs
cchacs deleted the pullfrog/6-declare-correct-host-floor branch October 11, 2026 10:08
Sign up for free to 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.

Declare the correct minimum host version

1 participant