Skip to content

feat(page)!: page-as-extension direct rewrite - dispatcher, 38 client-bodied verbs, catalog-derived CLI (#269) - #284

Merged
omridevk merged 8 commits into
registry-cleanup-232from
page-chain-269
Aug 7, 2026
Merged

feat(page)!: page-as-extension direct rewrite - dispatcher, 38 client-bodied verbs, catalog-derived CLI (#269)#284
omridevk merged 8 commits into
registry-cleanup-232from
page-chain-269

Conversation

@omridevk

@omridevk omridevk commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Stacked on #275 -> #268; retarget after they merge.

The page-as-extension DIRECT rewrite chain (#255 epic): page stops being a special case. Every page verb is a defineTool().client(body) declaration in @conciv/extension-page, dispatched by name through the registry over one wire, gated through the same decide() path as every extension.

Scope map

Dispatcher, final formpackages/page/src/page-tool-dispatcher.ts: instance-scoped name -> __clientExecute lookup off client tools passed as an argument to makeDomPageDriver/startPagePlane; per-call ClientToolCtx (document, lazy target(locator) with mirror-on-resolve per declaration meta, non-throwing resolve, addRef/resetRefs, console reader); zero module-level state. page-driver.ts is dispatcher-only; DOM_HANDLERS/page-handlers.ts are gone.

Final wirePageQuerySchema is {requestId, name, input} (protocol page-types.ts); no kind enum, no field-bag, no legacy union. The bus timeout budget peeks input.timeout.

All 38 verbs — 37 declarations in packages/extensions/page/src/shared/defs.ts, browser bodies in src/client/bodies.ts calling @conciv/page primitives (react bridge, snapshot/refs, dehydrate, render tracker stay put). effect carries as the documented stub, catalog-honest. The 38th (ext) died with the vocabulary. locate is server-wraps-client (P6): the client body returns raw frames, the server half (src/server.ts) symbolicates via symbolicateFrames threaded into mount deps. Core always mounts the page server extension; the embed always prepends the page client extension, so every host keeps page capability with no per-host wiring.

One registration ratchetisRegistryDeclared covers client-bodied tools (binding + meta/outputSchema); registration reads mount SURVIVORS (createInstances drops a throwing __client(); the driver collects tools off router.options.context.instances). The spike's registerExtensionClientTools shape is unified into registerExtensionTools.

Vocabulary deletedPAGE_QUERY_KINDS, PageQueryInputSchema, MUTATING_KINDS/MIRROR_KINDS + helpers, page.run (rpc + contract), ext kind, PageVerbMap/definePageVerbs/page-verb-registry.ts/bindExtensionPageVerbs, scopedPageCaller, BUILTIN_PAGE_TOOLS. Widget action cards and running titles read labels/icons from the declaration metadata (@conciv/extension-page/defs); mutating/mirror/journal are meta-driven everywhere (journal entries now carry full tool names, e.g. page.fill).

CLI re-derives (P3)rpc.registry.catalog (signatures incl. JSON-schema inputs) + rpc.registry.call replace rpc.page.run; conciv tools page/conciv tools react build their command trees at runtime from the catalog (packages/cli/src/page.ts, wireToolCommand).

decide() on every surface — chat: conciv_page calls with a mutating verb (per registry meta) prompt through the run gate; code-mode: page tools are registry capabilities, already gated by gatedToolRun; CLI: the blanket conciv tools* Bash allow is gone, replaced by catalog-derived read-only allows, so a mutating CLI page command surfaces the Approve/Deny card.

TanStack (P8) — the 14 pageVerbs are .client() tools (tanstack.detect ... tanstack.back, src/shared/verb-defs.ts with real zod result schemas mirroring framework-types); the framework adapter reaches its own browser tools through server.tools.call (registry threaded into mount deps); callPageVerb/PageCaller died. ServerApi now carries page.call (raw browser ask), tools.call (registry), and symbolicate.

conciv_open (noted in #275's report): verified NOT redundant as a surface — it is the chat-facing tool the system prompts name, while registry open serves code-mode/rpc. The duplicate execution path IS gone: conciv_open now executes through registry.call('open', ...).

Grep proofs (all zero)

grep -rn "DOM_HANDLERS\|PAGE_QUERY_KINDS\|PageVerbMap\|page\.run\|dispatchExtVerb\|scopedPageCaller\|BUILTIN_PAGE_TOOLS\|callPageVerb\|pagespike\|PageQueryInputSchema\|MUTATING_KINDS\|MIRROR_KINDS\|definePageVerbs\|registerExtensionPageVerbs" \
  --include='*.ts' --include='*.tsx' --include='*.mdx' packages apps e2e   # -> no matches

Verification

  • turbo run typecheck build --filter='!@conciv/extension-whiteboard' — 107/107 green
  • turbo run test --concurrency=1 (VITEST_MAX_FORKS=1, whiteboard excluded per repo rule) — green; bootNormal (page-plane.it) AND bootConnect (page-dispatch-boot.it) ITs green against the prebuilt embed; the moved page behavior suite runs in packages/extensions/page/test in a real browser; whiteboard verifies in CI
  • pnpm lint, pnpm format:check — green
  • fallow audit --changed-since origin/registry-cleanup-232 — verdict pass, zero INTRODUCED

Closes #269

🤖 Generated with Claude Code

omridevk added a commit that referenced this pull request Aug 6, 2026
Its mutating tool call now round-trips through callToolApproved
(dce879f), pushing it near the 60s vitest default on loaded CI
runners (PR #275 shard-4, PR #284 shard-4). Bump only this test to
120000ms; fixture size and other tests are untouched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
omridevk added a commit that referenced this pull request Aug 7, 2026
…242) (#268)

* feat(extensions): whiteboard tools declare through the tool registry (#242)

All twenty-two whiteboard defs gain outputSchema, catalog meta with honest
per-tool mutating flags, and declared errors where a real failure mode exists
(INVALID_SVG, DIAGRAM_TOO_LARGE, EXPORT_TIMEOUT, COMMIT_TIMEOUT,
COMMENT_NOT_FOUND, PIN_NOT_FOUND), so they auto-register into the one tool
registry at mount and appear in catalog.list() with real summaries under the
whiteboard category. Server handlers throw those codes as toolError instead of
bare Error, a new IT proves the catalog carries every declaration with its
mutating flag, and AGENTS.md records the standing rule that the whiteboard
suite is CI-only, never run locally.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(extensions): approve mutating whiteboard tool calls in tests and honest anchor output schema

The registry move flagged thirteen whiteboard tools mutating, which routes
their code-mode calls through the permission gate (gatedToolRun awaits
gate.decide, ASK_TIMEOUT_MS 120s). The ITs still called them through the
non-approving caller, so every first mutating call hung past the 60s test
timeout on CI shard-5. Mutating calls now go through callToolApproved,
matching the tanstack precedent. anchor.resolve's catalog output schema stops
promising anchor/candidates/diff on every status (the handler returns bare
{status:'orphaned'} for an unparseable anchor), and the catalog IT's
declared-errors oracle now covers every declared code, so dropping or
misspelling DIAGRAM_TOO_LARGE, EXPORT_TIMEOUT, COMMIT_TIMEOUT,
COMMENT_NOT_FOUND or PIN_NOT_FOUND fails the suite.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test(whiteboard): raise timeout on the many-subpath svg capping test

Its mutating tool call now round-trips through callToolApproved
(dce879f), pushing it near the 60s vitest default on loaded CI
runners (PR #275 shard-4, PR #284 shard-4). Bump only this test to
120000ms; fixture size and other tests are untouched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test(whiteboard): strengthen the tool-catalog IT oracle to set-equality and real output shapes

Set-equality on catalog names (a 23rd tool that skips declaration now fails
instead of silently passing), per-tool exact declared-error sets (extras and
missing both fail; the other 11 tools assert zero errors), a per-tool output
JSON-schema shape table derived from each def.ts outputSchema (catches
catalog drift instead of one substring check on canvas.svg), and one
executable error-path case (comment.read on a nonexistent cid surfaces
COMMENT_NOT_FOUND through the real code-mode call path).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
omridevk and others added 8 commits August 7, 2026 10:26
…re, extension-page verbs, CLI catalog derivation

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…tension-page browser suite, bootConnect embed IT

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ibuted failures

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…d never crosses the wire, CLI honors declared positionals and fails structurally offline

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… drift guard, honest narrow inputs, dom stale refs, verb behavior coverage

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…actories

The page and tanstack extensions declare tools through local factories
(pageTool, verbTool) whose defineTool name is a template literal, so the
hardened guard harvested nothing from them. The harvest now detects such
a factory from the source itself (prefix read from the template, spec
field from the interpolation) and harvests its call sites.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.

page-as-extension: the direct rewrite chain - dispatcher, all 38 verbs, vocabulary deletion, CLI re-derive

1 participant