3 Commits

Author SHA1 Message Date
Alex Dunmow
04991295cf feat(abi,blocks): ninjatpl rendering as a host capability (powered blocks + tag/filter callbacks)
pongo2 stays host-side and is never compiled into a guest; plugins render
templates by handing the host a {template, data} pair, and the host calls
back into the free guest instance for plugin-declared tags/filters.

ABI (additive, buf-breaking clean):
- RenderBlockResponse gains a `powered` PoweredBlock{template, data_json};
  a block returns EITHER html OR powered.
- New hooks HOOK_RENDER_TAG (10) / HOOK_APPLY_FILTER (11) with
  RenderTag{Request,Response} and ApplyFilter{Request,Response}.
- PluginManifest gains repeated declared_tags / declared_filters (31/32).

Guest SDK (core/blocks, core/plugin/wasmguest):
- blocks.PoweredBlock(template, data) / DecodePoweredBlock: NUL-sentinel
  marker so BlockFunc's string signature is unchanged (smallest additive
  change — no ripple to existing blocks or the host guest-side).
- blocks.RegisterTag / RegisterFilter (+ RenderContext = context.Context)
  write a package-level registry; runRegister resets it per registration
  for deterministic DESCRIBE + dispatch.
- DESCRIBE emits declared_tags/filters; dispatch handles RENDER_TAG /
  APPLY_FILTER (fn errors → response.error; panics → AbiError INTERNAL,
  instance stays callable).

Docs: core/docs/wasm-abi.md gains the render-as-a-host-capability model,
the powered-block flow (re-entrancy-free), the plugin API, and the
HOST-SIDE CONTRACT the cms phase implements.

Tests: unit round-trips for powered/RENDER_TAG/APPLY_FILTER (dispatch +
error + unknown + panic + per-guest registry isolation) plus a real
wazero round-trip through the compiled fixture module. Verified no
guest-reachable package imports pongo2 (go list -deps on the wasip1
fixture build is clean).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 02:36:39 +08:00
Alex Dunmow
bec3a43f55 feat(wasmguest): guest capability stubs over host calls (WO-WZ-003)
Every CoreServices interface (core/plugin/deps.go) now has a guest-side stub
that marshals to the WO-WZ-001 capability messages and dispatches through the
generic host_call transport, so plugin service code compiles and runs
unchanged against content.Content, settings.Settings, plugin.PluginBridge, etc.

- core/plugin/wasmguest/caps/: one file per family (17 families, 38 methods),
  a var _ <iface> = (*stub)(nil) compile proof each, and NewCoreServices(call)
  assembling them. The transport is injected (CallFunc) so marshaling is
  natively testable; the wasm shim binds it to CallHost, DESCRIBE probes pass
  nil (capability calls fail cleanly instead of nil-panicking).
- Error mapping wraps AbiError with <family>.<method> context and maps
  DEADLINE_EXCEEDED onto context.DeadlineExceeded.
- RAGService.RegisterContentFetcher stays guest-side (RAGStub) for
  HOOK_RAG_FETCH dispatch; Query/OnContentChanged marshal out. dispatch.go and
  describe.go now source fetchers from the caps RAG stub.
- caps_roundtrip_test.go: fake transport + 76 deterministic golden payloads
  (family_method_{req,resp}.pb) covering 100% of families, plus error-mapping,
  deadline, nil-transport, and guest-side-fetcher tests. WO-WZ-006 replays the
  same goldens to prevent host/guest drift.
- Acceptance: testdata/fixture Load hook calls deps.Content/Settings/Bridge
  unchanged (compiles for wasip1); caps_wasmhost_test.go drives it end-to-end
  through a real wazero module + fake host_call table.
- Disposition table in docs/wasm-abi.md: every member stub | host-side
  (Pool, Interceptors, AppURL/MediaPath, CoreServiceBindings host-side).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 14:23:58 +08:00
Alex Dunmow
9bbc793563 test(wasmguest): reactor fixture plugin + throwaway wazero host (WO-WZ-002)
testdata/fixture is the acceptance fixture: one block, one template, one
admin page, plus the two-line reactor boilerplate main. wasmhost_test.go
compiles it with GOOS=wasip1 GOARCH=wasm -buildmode=c-shared (15.7 MiB,
the WO-WZ-012 memory-budget baseline) and drives it through wazero
exactly per wasm-abi.md: _initialize as the start function, request
bytes through bn_alloc, DESCRIBE returning a decodable manifest,
same-instance block+template renders, a panicking BlockFunc surfacing as
ABI_ERROR_CODE_INTERNAL with the instance still callable, and a
non-bn_alloc request pointer rejected as DECODE.

wazero v1.12.0 joins go.mod as a test-only dependency (approved).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 13:53:39 +08:00