Problem
Views are Go Render() functions registered at compile time. An object whose type lives in an external app has no view compiled into the binary, so it falls
back to Go's %v and prints internal state (RuntimeObject carrier) instead of a readable value.
Structure of external types is already runtime-discoverable (blueprints), but presentation is not — there is no way to render a type the binary was not built
with.
Examples
Known type (view compiled in):
file at glitzy-whack:/Users/grzegorz/.../the_way_luv_goes.mp3
External type (no view):
&{0x14000739ec0 [{ObjectID 0x14000b8d920} {Format 0x140008014e0} ...] map[Album:4 Artist:3 ...] }
Possible directions
- Generic fallback view for blueprint-backed objects
A blueprint-decoded object already carries its full schema: type name, field names, field order. A single generic view can render any such object as
type{Field: value, ...}, delegating each field value to the existing view machinery (fields are astral primitives, which all render correctly).
Requires a fallback hook in the view lookup — the per-type registry can't match external type names, so unmatched blueprint-backed objects route to the generic
view instead of %v.
Fixes every current and future external type at once. Output is uniform, not custom — but readable. Also remains the safety net for any other direction (when a
custom view is missing or out of date).
- Enriched client build (xcaddy pattern)
Today the client doesn't render at all — it requests out=render and copies text the node already rendered. So this direction has two parts:
- Client-side rendering — the client requests binary output, decodes objects locally, and renders them with its own compiled-in views. Importing an app's
package registers both its Go types (decode works) and its views (rendering works), so one import covers both.
- Build tooling — a script takes a list of external view packages, generates a temporary aggregator file with blank imports (same shape as the existing all/
import packages), and builds a custom client binary. Same model as xcaddy building Caddy with plugins.
Gives full-fidelity custom views per app. Costs: apps must publish views as importable Go modules, the binary needs a rebuild per app set, and version skew
between binary and running apps shows up as decode errors or missing views (which fall back to direction 1).
Problem
Views are Go Render() functions registered at compile time. An object whose type lives in an external app has no view compiled into the binary, so it falls
back to Go's %v and prints internal state (RuntimeObject carrier) instead of a readable value.
Structure of external types is already runtime-discoverable (blueprints), but presentation is not — there is no way to render a type the binary was not built
with.
Examples
Known type (view compiled in):
External type (no view):
Possible directions
A blueprint-decoded object already carries its full schema: type name, field names, field order. A single generic view can render any such object as
type{Field: value, ...}, delegating each field value to the existing view machinery (fields are astral primitives, which all render correctly).
Requires a fallback hook in the view lookup — the per-type registry can't match external type names, so unmatched blueprint-backed objects route to the generic
view instead of %v.
Fixes every current and future external type at once. Output is uniform, not custom — but readable. Also remains the safety net for any other direction (when a
custom view is missing or out of date).
Today the client doesn't render at all — it requests out=render and copies text the node already rendered. So this direction has two parts:
package registers both its Go types (decode works) and its views (rendering works), so one import covers both.
import packages), and builds a custom client binary. Same model as xcaddy building Caddy with plugins.
Gives full-fidelity custom views per app. Costs: apps must publish views as importable Go modules, the binary needs a rebuild per app set, and version skew
between binary and running apps shows up as decode errors or missing views (which fall back to direction 1).