Skip to content

fix(widget-dev): read a newer node's session view without its unknown top-level fields - #678

Merged
mrgoonie merged 2 commits into
mainfrom
fix/673-tolerant-dev-session-view
Oct 8, 2026
Merged

mrgoonie merged 2 commits into
mainfrom
fix/673-tolerant-dev-session-view

Conversation

@mrgoonie

@mrgoonie mrgoonie commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #673

Problem

An older desktop app connected to a newer node on another machine parsed the widget dev session view with a strict
schema, so one new optional field (for example stopCode, #662/#665) made it refuse the whole view and lose the session.

Change

Follows the directory-entry precedent (#431, #438):

  • packages/contracts/src/node-view-read.ts: readNodeView(schema, candidate) drops unknown top-level fields and never
    passes them on. Every known field is still validated with all its bounds. The nested objects (activation, latest,
    running, lastBuild, placed) stay strict, because they carry activation, approval and reach state. The drop is
    returned as an UnreadListingFields note. Field names are reported only when they are plain identifiers; other
    fields are only counted.
  • packages/conversation-client/src/api.ts: the four widget dev session reads (widgetDevSession, start, rebuild,
    stop) go through one readDevSession and return WidgetDevSessionRead, which carries unreadFields when
    something was dropped.
  • What was dropped is stated, never hidden. The status line beside the widget frame and the /develop card outcome
    add "This node is newer than this app, so some of what it says about this session is not shown. Update the app to
    see everything." (EN and VI).
  • Requests (widgetDevSessionCreateSchema) and the node's own widgetDevSessionViewSchema stay strict. Runtime files
    and sessions.json are not touched, so this does not overlap with widget dev: removing a snapshot can race a build or install that reuses the same digest #670.
  • Docs: docs/open-interfaces(.vi).md adds a Reading a session view rule. The "keep the same build" advice now
    names the cases that still refuse: an app built before this change, a newer field inside a nested object, and a
    value the app does not know in a field it knows (a new stopReason, status or activation state). The rule also
    says approval or reach state is never added as a new top-level field, since an older app would drop it.
  • Official docs: docs(api): a newer node's dev session view is read without unknown fields clarkcant-web#146.

Not done here, tracked in #679: the other strict reads below, the optional item 4 (the client reads the node's
version and says older or newer up front), unknown values in known fields, the /develop card that shows failed with
raw ZodError text after the node started the session, and making the top-level authoring rule enforceable.

Other strict node→client reads, not changed here

api.ts also parses these with z.strictObject (or unions of strict objects), so they have the same skew exposure.
They are listed for a follow-up rather than changed: inboxResponseSchema, inboxSummarySchema, noticeSchema,
noticeOperationResponseSchema, effectReconcileResponseSchema, waitingItemSchema, memoryListSchema,
suggestionsResponseSchema, composerSuggestionsResponseSchema, attachmentRefSchema,
conversationDeleteResultSchema, appIntentDecisionSchema, widgetDevFolderForgetResultSchema, and the widget-sdk
browserTokenWireSchema and jobSnapshotWireSchema. readNodeView covers a plain top-level object directly; unions
and inbox items need their own decision about which fields bind.

Tests

  • packages/contracts/test/node-view-read.spec.ts:
    • a view carrying a future fooCode parses, with fooCode dropped and reported;
    • names that are not plain identifiers are counted, not named;
    • known bounds still refuse;
    • an unknown field inside activation (active and awaiting-approval) or running.delta still refuses;
    • the node schema and the create request still reject unknown fields.
  • packages/conversation-client/test/widget-dev-session-read.spec.ts: GatewayClient.widgetDevSession and
    startWidgetDevSession read a view with fooCode from a stubbed node. A nested unknown field rejects. The develop
    outcome states the drop in EN and VI.
  • Fails before the fix: with the src changes reverted, the client spec fails with a ZodError on the fooCode view,
    and the contracts spec fails with readNodeView is not a function.

Verification

  • focused vitest: 4 files, 26 tests pass (round 2 adds a render test for the status-line sentence; replacing it with
    false && fails that test)
  • pnpm typecheck: pass
  • pnpm invariants: all 15 checks pass
  • eslint on the changed files: clean
  • pnpm verify: exit 0, 586 files and 8022 tests passed
  • node tools/check-invariants.mjs --fix-manifest: no manifest change (open-interfaces is not in docs/manifest.json)

… top-level fields, and say what was left out

An older desktop app connected to a newer node on another machine refused the whole widget dev session view when the node added an optional field such as stopCode. The client now reads the view with readNodeView: unknown top-level fields are dropped and never passed on, the status line and the develop card say the node is newer, every known field keeps its bounds, and nested objects carrying activation, approval and reach state stay strict. Requests and the node's own schema stay strict.

Fixes #673
…ed, and test the status line's newer-node sentence

A new value in a field the app knows (a new stopReason, status or activation state) still refuses the view, so the same-build advice covers it too. lastBuild and placed carry build and placement state, not approval or reach. Approval or reach state is never added as a new top-level field, which an older app would drop. The status report is drawn by its own component so the sentence can be rendered in a test.
@mrgoonie

mrgoonie commented Oct 8, 2026

Copy link
Copy Markdown
Contributor Author

Review attestation: ready to merge at e33eeae6687b3599d55c5522a76b8bcce76a57ad, reviewed by agent:code-reviewer.

A push to this PR makes this attestation stale; the new head needs its own review.

@mrgoonie
mrgoonie enabled auto-merge (squash) October 8, 2026 07:38
@mrgoonie
mrgoonie merged commit e6a81b0 into main Oct 8, 2026
23 checks passed
@mrgoonie
mrgoonie deleted the fix/673-tolerant-dev-session-view branch October 8, 2026 07:47
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.

Client reads of node views: tolerate unknown fields so an older desktop app keeps working with a newer remote node

1 participant