Skip to content

feat(webui): forward port the Mini App contract and accept the newer manifest keys - #261

Merged
Fectivnfy112357 merged 4 commits into
webuifrom
feat/miniapp-contract-forward-port
Oct 27, 2026
Merged

Fectivnfy112357 merged 4 commits into
webuifrom
feat/miniapp-contract-forward-port

Conversation

@Fectivnfy112357

@Fectivnfy112357 Fectivnfy112357 commented Oct 26, 2026 •

Copy link
Copy Markdown

Closes #252.

Change

The Mini App contract moves to the generation the desktop client ships, minus its window block, and
the plugin package reader stops rejecting a package written for that generation. Scope and user
stories: #251. Decisions: packages/webui/docs/adr/0020-webui-miniapp-view-and-runtime-seams.md.

Three commits: the ADR 0020 revision this ticket implements (docs(webui): settle ADR 0020 for hosting Mini Apps in the WebUI, previously local-only), the port itself, and a rename that keeps the
reader suite under test/unit instead of beside the module.

  • packages/shared/src/miniapp-surface.ts is now a contiguous copy of the shipped file's leading part
    (shipped lines 1-964 of 1267): the start/page-open split, detached and presentationRevision,
    the loopback token header, the composer block, and the whole agent block with its nine run events
    and their mapping from runtime failure codes. The window block is the omitted tail.
  • One private helper comes out of that tail. The agent parsers above use isPlainRecord nine times
    and the shipped file declares it inside the window block (shipped line 1156), so it is kept at the
    seam between the copied part and the omission. It is the one local divergence a future copy has to
    reconcile, and ADR 0020 now records it.
  • The reader accepts the three keys the newer generation declares: surface.window accepted and
    ignored, agentAccess and agentConcurrency validated and preserved on the contribution so the
    agent service can enforce them when it lands. Both allowlists stay strict — an unknown top-level
    key still fails as MINIAPP_MANIFEST_SCHEMA_INVALID, and an unknown key beside surface.window
    still fails as MINIAPP_SURFACE_INVALID.
  • No runtime behaviour changes: the service still does not start for this client's owner kind, and
    nothing reads the agent fields yet.
  • ADR 0020's contract-baseline section is corrected from measurement: the shipped leading part is a
    strict superset of the old 54 lines but not a line-identical copy of them, and the omitted tail is
    not purely window code.

Validation

  • pnpm typecheck — passed
  • pnpm typecheck:webui — passed (server, client and test projects, plus the host-event catalogue check), rerun at the branch head
  • pnpm run test:capabilities — 235 files, 5811 passed, 18 skipped
  • pnpm build — passed
  • node scripts/source-inventory.mjs --write then pnpm check:source — 5451 files, passed
  • New reader suite packages/local-runtime-v2/test/unit/plugin-system/miniapp-manifest.test.ts,
    registered in test/vitest-suites.json: fails 13 of 16 cases against the previous reader, passes
    16 of 16 here
  • NOT RUN: pnpm verify in full, including the test:webui-browser and release-package steps. The
    change is content-only and touches no client behaviour and no export path, so those gates are left
    to CI.

Publication and contribution checks

  • I have permission to contribute these changes under the existing licenses applicable to the changed files/packages; imported material and its provenance are identified and existing notices are preserved.
  • No credentials, account data, real user content, internal source history or private review material is included.
  • Added/removed source files were reviewed before regenerating release/public-source.json; new tests are declared in test/vitest-suites.json where applicable.
  • Shared English/Chinese documentation and capability/verification records are updated where applicable. Mock/offline results are not described as live-service acceptance.

Maintainer handoff

Publication scope or license changes (if any): none.

Shared-source port: not needed — every changed file is already published in this repository, and the
contract content comes from this repository's own shipped generation.

Record the decisions the grilling settled, and shrink the open list to the two
implementation details that remain.

Taken: the shipped contract minus the window block; the loopback credential guard
with readiness proving it is in the path; the postMessage bridge, with the
origin-local shim recorded as rejected; the agent capability in scope, since parity
with the desktop client's page is the acceptance standard; the connector's frames
moving onto the IPC channel that already carries lifecycle, leaving stdout and
stderr as logs; the workspace panel's miniapp tab as the inline home, opened from
the launcher's tile and listed in the catalogue's apps area; the standalone route
with a service-held presentation lease; the agent open call mapped onto the session
rail; one proxy listener per Mini App with a per-plugin port; and single-client
ownership, where the second client reports the owner and refuses to start.

The client's Mini App work is one business, with three crossings one line wide.

CONTEXT.md follows: the bridge no longer describes a window handle, the view records
that both presentations belong to the client, and the presentation lease is defined.
…manifest keys

The Mini App contract moves to the generation the desktop client ships, minus its
window block, and the package reader stops rejecting a package built for that
generation.

The contract file is now a contiguous copy of the shipped file's leading part
(shipped lines 1-964 of 1267): the start/page-open split, detached and
presentationRevision, the loopback token header, the composer block, and the whole
agent block with its nine run events and their mapping from runtime failure codes.
The window block is the omitted tail: the argv surface id, the surface.window
whitelist and the page's getState/update/focus handle.

One private helper has to come out of that tail. The agent parsers above use
isPlainRecord nine times, and the shipped file declares it at line 1156, inside the
window block, so it is kept at the seam between the copied part and the omission.
It is the one local divergence a future copy has to reconcile.

The reader accepts the three keys the newer generation declares. surface.window is
accepted and ignored, because the window block is not part of this contract and the
value therefore has no effect. agentAccess (only session.run, deduplicated) and
agentConcurrency (an integer from 1 to MINIAPP_AGENT_MAX_CONCURRENCY) are validated
and preserved on the contribution, so the agent service can enforce them when it
lands. Both allowlists stay strict: an unknown top-level key still fails as
MINIAPP_MANIFEST_SCHEMA_INVALID, and an unknown key beside surface.window still
fails as MINIAPP_SURFACE_INVALID.

No runtime behaviour changes: the service still does not start for this client's
owner kind, and nothing reads the agent fields yet.

ADR 0020's contract-baseline section is corrected from measurement: the shipped
leading part is a strict superset of the old 54 lines but not a line-identical copy
of them, and the omitted tail is not purely window code.

Verified: pnpm typecheck; pnpm typecheck:webui; pnpm run test:capabilities (235
files, 5811 passed, 18 skipped); pnpm build; pnpm check:source after
node scripts/source-inventory.mjs --write. The new reader suite fails 13 of 16
cases against the previous reader and passes 16 of 16 here.
@Fectivnfy112357 Fectivnfy112357 added enhancement New feature or request miniapp WebUI Mini App hosting programme (ADR 0020) labels Oct 26, 2026
Unit suites for this package live under packages/local-runtime-v2/test, not beside
the module they exercise, so the reader suite goes to test/unit/plugin-system/.
Registered at its new path in test/vitest-suites.json and the source inventory is
regenerated for the rename.

Verified at the new path: pnpm typecheck; pnpm run test:capabilities (235 files,
5811 passed, 18 skipped); pnpm check:source.
Findings from the independent review of #261.

The two reader rejection codes this branch introduced were unmapped in the failure
diagnostic layer: `PREPARATION_REASON_BY_HOST_CODE` carries every other MINIAPP_* reader
code, so a package rejected for `agentAccess` or `agentConcurrency` fell through to
`INTERNAL_ERROR` with no reason code and no hint — a worse answer than the
`WORKSPACE_PLUGIN_INVALID` it got before the branch started accepting those keys. Both now
map to `MINIAPP_MANIFEST_INVALID`, the reason the other manifest rejections use, so the
catalog entry that already exists supplies the message.

Two test gaps the review found are closed: `agentConcurrency` without `agentAccess` is
asserted accepted (1, 4 and 32, the documented no-effect case and both bounds), and the
changed surface message is pinned rather than left to drift.

Two ADR sentences were wrong about facts: the omitted tail declares the window state and
patch contracts, not a page handle — `getState`/`update`/`focus` live in the preload and
appear nowhere in the reference file — and the agent fields are validated and preserved
here, not enforced.

Verified on this branch: pnpm typecheck; pnpm run test:capabilities (5814 passed, 18
skipped); pnpm check:source.
@Fectivnfy112357

Copy link
Copy Markdown
Author

Independent review, done by a separate read-only reviewer agent (coding-team-reviewer profile) against
this branch's merge base with webui, with the shipped generation available for diffing. Its report and my
response; the fix is in 79e92a5a.

Verdict from the reviewer

Changes requested — one blocking item, rest clean.

Blocking (fixed in 79e92a5a)

The two new reader rejection codes fell out of the failure-diagnostic mapping.
MINIAPP_AGENT_ACCESS_INVALID and MINIAPP_AGENT_CONCURRENCY_INVALID were absent from
PREPARATION_REASON_BY_HOST_CODE (miniapp-actions.ts:415) and from MINIAPP_FAILURE_CATALOG
(agent-extension/src/miniapp-diagnostics.ts:20), while every other MINIAPP_* reader code has an entry.

I confirmed it myself: grep -c over both files returned 0. The path is
validation.ts → workspaceCandidateReasonCode (preserves any MINIAPP_* code) → publication.ts
(MINIAPP_PREPARATION_FAILED with that reason code) → miniapp-actions.ts lookup → undefined →
INTERNAL_ERROR ("Mini App action failed inside the Host", no reason code, no hint). So a manifest
declaring a bad agentAccess would have failed with a worse answer than the
WORKSPACE_PLUGIN_INVALID it got before this branch accepted the key — and the commit's "no runtime
behaviour changes" was therefore false for the rejection path it created.

Both codes now map to MINIAPP_MANIFEST_INVALID, the reason the other manifest rejections use, so the
existing catalog entry supplies the message without a new one.

Should-fix (fixed in 79e92a5a)

  • agentConcurrency without agentAccess — the documented "no effect" case — had no test, and neither
    bound of the accepted range was asserted, only the rejections. Now 1, 4 and 32 are asserted
    accepted with agentAccess absent.
  • The changed surface message was unpinned, so a regression to the old text would have been invisible.
    The rejection case now matches the message, not only the code.

Nits (fixed in 79e92a5a)

  • ADR 0020 said the omitted tail contains "the page's getState/update/focus handle". Those
    identifiers appear nowhere in the reference file — they are the preload's bridge namespace. The
    sentence now names what the tail actually declares: the argv surface id, the surface.window
    whitelist, and the MiniAppWindowState/MiniAppWindowPatch contracts with their parsers.
  • ADR 0020 said the agent fields "are enforced". They are validated and preserved here; enforcement
    arrives with the agent service. Corrected.

Two further nits were left as observations rather than changes: MINIAPP_AGENT_MAX_CONCURRENCY sits in
miniapp/contracts.ts where the shipped generation keeps it under service/miniapp/agent/contracts.ts
(right for a repository with no agent/ module yet, and a second small divergence from the shipped
layout), and the surface rejection message is unchanged for a non-object surface.

What the reviewer confirmed as correct

  • packages/shared/src/miniapp-surface.ts:1-964 is byte-identical to the shipped leading part; no
    leakage of MINIAPP_SURFACE_*, MiniAppSurfaceWindow, MiniAppWindow* anywhere in the file.
  • isPlainRecord is character-identical to shipped 1156-1160, is a hoisted function declaration, and is
    the only one of the tail's 33 top-level declarations referenced above the cut.
  • readAgentAccess/readAgentConcurrency are byte-identical to the shipped functions.
  • The allowlist is still strict: literal Sets, no spread of the manifest, surface still exactly
    { path: string } on the contribution.
  • No consumer of the changed response types exists; only MiniAppSurfaceSummary is imported and it is
    unchanged.
  • Scope: no dependency or lockfile change, release/public-source.json differs by exactly the added test
    path, and the "13 of 16 fail against the previous reader" claim checks out arithmetically.

Boundary

The reviewer ran no builds or tests (its worktree had no dependencies), so its verdict rests on reading.
The gates quoted in the PR body are mine, re-run at 79e92a5a: pnpm typecheck,
pnpm run test:capabilities (5814 passed, 18 skipped), pnpm check:source.

@Fectivnfy112357
Fectivnfy112357 merged commit 79e92a5 into webui Oct 27, 2026
5 of 8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request miniapp WebUI Mini App hosting programme (ADR 0020)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant