Stop the onboarding checklist counting a field no player reads

"Default Content" is persisted by the device route, snapshotted and restored by the settings layer,
offered in the device form in five languages — and read by nothing. Grep the whole tree and it
appears only in those places, the schema, and this checklist. It is absent from assemblePayload,
from every socket payload, and from all four players.

Counting it as "content assigned" therefore told the operator their screen was set up while the
screen itself went on showing "waiting for content" — the checklist confirming the one thing it
exists to confirm, incorrectly. It now counts only a playlist or a layout, both of which really do
put something on a display.

An existing test asserted the opposite ("any of the three ways of assigning counts"). It encoded the
same false premise, so it is replaced by one that pins the corrected behaviour along with the
evidence for it. The column and the form field are left alone — whether to implement or remove the
feature is a product decision, and this change only stops the checklist making a claim on its
behalf.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uaeo9MvzKoyXuN6ZsbhtkL
This commit is contained in:
Claude 2026-07-30 22:41:06 -05:00
parent 9458af95fd
commit a310d7d5b6
2 changed files with 19 additions and 3 deletions

View file

@ -24,7 +24,12 @@ export function computeSteps({ devices = [], content = [], playlists = [] } = {}
const hasPlaylist = playlists.length > 0;
// "On screen" is the only step that cannot be faked by creating an object and walking away:
// some screen has to actually be pointed at something.
const isAssigned = devices.some((d) => d.playlist_id || d.default_content_id || d.layout_id);
// default_content_id is deliberately NOT counted. No player reads it — grep the whole tree and
// it appears only in this checklist, the device route, the settings snapshot and the schema —
// so counting it ticked "content assigned" for a screen that goes on showing "waiting for
// content". A checklist that lies about the one thing it is there to confirm is worse than no
// checklist. The field itself is left alone; that is a separate decision.
const isAssigned = devices.some((d) => d.playlist_id || d.layout_id);
const steps = [
{

View file

@ -61,13 +61,24 @@ test('THE POINT: it is only finished when something is actually ON a screen', as
assert.equal(done.nextIndex, -1);
});
test('any of the three ways of assigning counts', async () => {
for (const d of [{ id: 'x', playlist_id: 'p' }, { id: 'x', default_content_id: 'c' }, { id: 'x', layout_id: 'l' }]) {
test('assigning a playlist or a layout counts', async () => {
for (const d of [{ id: 'x', playlist_id: 'p' }, { id: 'x', layout_id: 'l' }]) {
const s = GS.computeSteps({ devices: [d], content: [{ id: 'c' }], playlists: [{ id: 'p' }] });
assert.equal(s.complete, true, `${Object.keys(d).join(',')} should count as assigned`);
}
});
test('default_content_id does NOT count, because no player reads it', async () => {
// This test previously asserted the opposite, and it was wrong. Grep the whole tree and
// default_content appears only in this checklist, the device form, the settings snapshot, the
// schema and the devices route — never in a socket payload, in assemblePayload, or in any of the
// four players. Setting it changes nothing on the screen, so counting it told the operator
// "content assigned" while their display went on showing "waiting for content". A checklist that
// lies about the one thing it exists to confirm is worse than no checklist.
const s = GS.computeSteps({ devices: [{ id: 'x', default_content_id: 'c' }], content: [{ id: 'c' }], playlists: [{ id: 'p' }] });
assert.equal(s.complete, false, 'a screen with only default_content is not actually showing anything');
});
test('steps stay in dependency order — never sent somewhere unusable', async () => {
// Content before a screen exists is not wrong, but it cannot be PUT anywhere, so the next
// action must remain the screen.