Roadmap — Features in Planning¶
This section contains implementation plans for features that have been fully researched and architecturally specified but are not yet implemented. Each page captures the design intent, API surface, error model, and key decisions so that implementation can start without re-doing the research.
These are living design documents — they reflect the current best thinking at the time of writing. API surface may change slightly during implementation as new constraints are discovered.
Planned features¶
| Feature | Package | Status | Summary |
|---|---|---|---|
| Stream — FlatMap | stream |
Awaiting use case | FlatMap sub-stream variant (semaphore pool, unordered merge; main IO driver now covered by ports.IOPort + 1→N adapters) — deferred until a concrete driver appears. Also records will-not-implement decisions (RetryWithBackoff). GroupBy/Switch routing SHIPPED in stream/route.go; CombineLatest5+ resolved via nested composition (stream guide) |
ZeroMQ REQ/REP Adapter for api/rest |
adapters/zeromqrest |
Design draft | Spun out of D-0006 — Protocol-Native Capabilities's Phase 3 (adapter plumbing only — the capability mechanism itself stays in that doc). New REQ/REP-based api/rest adapter, the FIRST non-HTTP REST-eligible transport (synchronous + transport-stateless guardrail) — mirrors reqreply's ZeroMQ framing conventions ([status, headers, body]) with a NEW headers frame for HeaderParam/ResponseHeaderParam; reuses adapters/zeromq's FramedSocket pattern (zero new ZMQ library dependency); CookieParam support and Method's dispatch role are open questions |
| AMQP 0.9.1 Adapter | adapters/amqp |
Design complete | PUB/SUB + Request/Reply over RabbitMQ — exchange/queue topology, Ack/Nack, ReplyTo/CorrelationId RPC, structured errors, observer integration |
| TCP Adapter | adapters/tcp |
Design complete | Request/Reply + streaming over raw TCP — pluggable FramedConn framing, built-in length-prefix framer, stdlib-only, no CGO |
| WebSocket — Deferred | adapters/websocket |
Deferred | Phases 1+2 SHIPPED (server adapters + DuplexPort + SocketPattern; client dial adapters with auto-reconnect + gap SocketErrors + session generations; chi variants via swapHandler delegation; RegisterSocket AsyncAPI channel). Remaining: ConnectionObserver extension, dynamic subprotocol negotiation. Will-not-implement: MQTT-over-WS, universal StreamPattern, outbound queueing across gaps |
| Redis — Phase 2: Pub/Sub | adapters/redis |
Design draft | SubscribeAdapter (SourcePort) + PublishAdapter (SinkPort) via existing EventPattern; new narrow PubSubCommands interface + fake; at-most-once semantics documented loudly (offline subscribers LOSE messages — closer to ZeroMQ than MQTT); PSUBSCRIBE glob derivation from {var} templates; reuses RecordSubscribe/RecordPublish (no new stats extension). Redis Streams = Phase 3 candidate; still deferred from Phase 1: DelAdapter, CachePattern spec rendering. Per-var key codecs SHIPPED — see Redis Cache Adapter |
| Webhook Adapter | adapters/webhook |
Design complete — Phase 0 prerequisite identified | ReceiveAdapter (SourcePort) + DeliverAdapter (SinkPort) — HMAC-SHA256 signature verification/signing over raw payload bytes, the one mechanism generic nethttp/chi can't do today (the declarative .HandleMW/.ClientMW security mechanisms run after body decode). Body-preserving verify middleware wraps a small, additive nethttp.IngestAdapterOptions.Middleware hook (a Phase 0 prerequisite, since nethttp.Handler was removed by middleware-workflow-simplification); handle.EncodeRequest gives raw bytes to sign before rest.CallWithTransport. Reuses RESTPattern (OpenAPI) + optional EventPattern (AsyncAPI) unchanged; retry-with-backoff mirrors adapters/websocket's reconnect loop; no new stats extension (SecurityObserver/Observer reused) |
| Fuzz & Benchmark Testing Infrastructure | validate, codex, format |
Design complete | Internal quality initiative, no new API. Fuzz targets for every hand-rolled string/byte parser (9 validate regex constraints, codex.HexColor's byte-level hex parser, JSON/TOML format-boundary decode) — zero fuzz targets exist today. Benchmarks for hot paths (Struct encode/decode, SliceOf/StringMap, Refine chains, format.JSON round trip) with b.ReportAllocs() — zero benchmarks exist today. New CI fuzz job (-fuzztime=30s per target); benchstat-comparable baseline for future regression checks |
| Dynamic Port Rebinding (Hot-Swap Adapters) | ports |
Design draft | Live adapter cutover on running ports without process restart. Phase 1 scopes explicit BindRef/Rebind/Unbind on single-binding ports (IOPort, DuplexPort, LatestPort) with generation-based lifecycle tracking, typed slog.LogValuer errors, and observer reporting on all transition paths. Cross-referenced with MCP and Ports Declarative Middleware's "L11": this doc's motivating "credential rollover" scenario is ALREADY solved for ports by that doc's per-call middleware attachment (zero mechanism from this doc needed there) — Rebind here is scoped to swapping the underlying TRANSPORT ADAPTER itself (broker failover, endpoint rotation, phased migration), a different concern. REST/events/reqreply's immutable RouteHandle/ChannelHandle middleware hot-swap remains an acknowledged gap in both docs, not yet designed. |
| Adapter Dispatch-Core Unification | adapters/mqtt, adapters/mqtt5, adapters/zeromq |
Idea / investigation only — Phase 1 is an AUDIT, not a redesign | Spun out of a retrospective discussion on whether this project's bottom-up design produced clean fundamentals or accumulated "layers of cope." Confirmed via direct citation (not re-derived) from d-0001's "Lessons Learned" and d-0007's "Post-ship regression": api/rest's dispatch already converged to ONE reflection-based path (adapters/nethttp/serve.go's ServeOne, reflecting into RouteHandle's own generic methods) — the reference pattern — but api/events' Subscribe dispatch had THREE independently-maintained reflection implementations, and the PRIMARY, documented Client.Attach workflow silently never ran bound middleware for an entire rollout phase, undetected until a new example exercised that exact entry point. Phase 1 scope is AUDIT ONLY: catalog the exact dispatch-implementation count/location per capability/adapter/pattern (events Publish and reqreply Call/Serve are UNAUDITED — unknown whether they share Subscribe's shape) — no redesign commitment yet. Confirms D-0008 — Declarative Router Groups is structurally immune to this regression class (pure api/*-layer spec-assembly, zero adapter dispatch involvement). |
| Strongly-Typed Router Groups | api/rest, api/events, api/reqreply |
Idea / investigation only — no design committed yet | Spun out of D-0008 — Declarative Router Groups's Phase C deferred-item review (item 3): a strongly-typed Router[Req,Resp] that only ever groups SAME-typed leaves, as a PARALLEL construct to d-0008's existing heterogeneous Router — would let bound-class middleware.BoundMiddleware[Req,Resp,...] attach at the Router level, which d-0008's type-erased Router structurally cannot support. Two rejected alternatives documented (a reflection-based runtime type-check; folding a type parameter onto d-0008's existing Router) before concluding this needs its own, independently-designed type. No concrete driver confirmed yet; open questions only (naming/coexistence, whether Mount/Group even make sense for a same-typed Router, whether a simpler non-Router mechanism would do). |
| MCP and Ports Declarative Middleware | api/mcp, adapters/mcpgo, ports, middleware, stats |
Design draft — not yet implemented | Spun out of the now-DELETED declarative-middleware.md (its REST/events/reqreply content was fully superseded by D-0001/D-0002/D-0003/D-0004; only MCP/ports remained open). This round REDESIGNS that moved content to align with the ACTUAL shipped Middleware[In,Out]/two-phase declare-dispatch pattern (the old sketch predated it) — confirmed BOTH api/mcp.Tool[In,Out] (NewTool+.Register) and ports.File/Cache/Dir (NewFile/NewCache) already have a genuine declare-once value a .Use() can attach to, building directly on D-0003's own existing ports feasibility analysis. ports.SQL confirmed metadata-only (no handle to attach to) — scoped out as a permanent limit case. Carries forward L5/L11/L12/L14 (MCP Resources/Prompts coverage; cross-reference with Dynamic Port Rebinding; MCP/ports observer fan-out via stats.NewFanout; Layer 3/forge spun out to Forge Pipeline Middleware) verbatim, resolved. middleware.ContextField[V] already shipped, available to both with no further design work. |
| Middleware Workflow Simplification | middleware, api/rest, adapters/nethttp, adapters/chi |
GRADUATED to docs/design/ | Fully implemented and shipped, including old-door removal (Handler/Register/SSEHandler/RegisterSSE deleted from both adapters/nethttp and adapters/chi; Serve/ServeOne/ServeSSE are the sole server-side entry points) — moved to Design Documents per this section's graduation bar (establishes a pattern followed by REST + the ports.Pattern binding layer). |
| WebSocket Declarative Middleware | adapters/websocket, ports |
Idea only — no code written | Spun out of checking whether REST req/resp, SSE, and WebSocket had the same duplication gap as pub/sub's Observability[T]. REST/SSE confirmed to have NO gap (nethttp.Observability is the ONLY implementation, reused unchanged by adapters/chi, covers SSE for free). WebSocket's situation is DIFFERENT IN KIND: confirmed zero .HandleMW/.ClientMW/.SubscribeMW/.PublishMW/.Use( anywhere in adapters/websocket — it's built on ports.DuplexPort/SocketPattern, not api/events.Channel's builder, so there is no spec object to attach middleware to at all. This doc scopes (does NOT pre-decide) whether WebSocket should gain a general-purpose declarative middleware mechanism — 4 candidate directions sketched, including the honest null hypothesis that the current Options.Observer/ErrorFrame design already suffices. Not implemented — investigation/planning only. |
| Feature — sealed, per-adapter capability interfaces | api/events, internal/middleware, stats, adapters/mqtt, adapters/mqtt5, adapters/zeromq |
GRADUATED to docs/design/ | Sealed Capability mechanism (QoS/Retained/HWM/Conflate) + CapabilitySpec/CheckCapabilityCoverage spec-rendering + stats.CapabilityObserver + Handler Disposition (stats.Disposition) + stats.DispositionObserver, all SHIPPED across adapters/mqtt/mqtt5/zeromq (events) and adapters/mqtt5/zeromq (reqreply) — moved to Design Documents. Address/TopicAddress shipped as standalone additive types; the full Channel[Addr,T] retrofit and its ~300-call-site migration remain DEFERRED until a real Address-needing adapter (AMQP) exists to consume it. |
| SSE Resume & Retry Policy | adapters/nethttp, adapters/chi, api/rest |
Findings only — no proposal, no driver yet | Spun out from SSE Client Consumption's "Out of scope" (that roadmap doc's Phase 1 shipped and was removed after a review-go-codex pass confirmed zero gaps against its committed scope — see docs/design/d-0001-rest-middleware-workflow-simplification.md's addendum for the design lineage) once investigation showed BOTH Last-Event-ID resume and pluggable retry policy are genuinely client+SERVER features, not client-only follow-ons — confirmed via code that the SSE writer never emits id:/event:/retry: protocol fields anywhere today (only bare data: lines), and stream.BroadcastHub has no past-event buffer for any replay mechanism. Sketches two INDEPENDENT sub-features: (A) Last-Event-ID resume — server assigns+writes per-event id:, buffers recent events, replays from an incoming Last-Event-ID header; client tracks+resends the last-seen id on every reconnect. (B) pluggable retry policy — TWO DISTINCT halves: a client-side RetryPolicy func for go-codex's own Consume/CallSSEAdapter, and a server-side retry: field emission that spec-compliant browser EventSource clients honor natively, independent of go-codex's client machinery entirely. Cross-references webhook-adapter.md's identical-shaped RetryPolicy idea as a precedent, while flagging the semantics differ (SSE reconnect is naturally infinite; webhook delivery is finite/give-up-eventually) — explicitly does NOT assume one shared type serves both without investigation. Flags 2 further out-of-scope items: a persistent/durable cross-restart event buffer, and SSE's event: field for multiplexed event types (related, but separate features, not expanded here). |
api/reqreply.Client Registry |
api/reqreply |
Phase 0 SHIPPED — registry + CallRaw implemented and tested (api/reqreply/client.go, adapters/mqtt5, adapters/zeromq); unblocks Phase 1 |
Phase 0 of a 3-phase reqreply design (split from one combined doc once all 3 phases reached "design complete" — see Forward/Bounce and Dead-Letter Replay). Spun out of the rest.Client registry investigation (shipped, see docs/features/rest-redirects.md): reqreply.Client was the only api/* Client with no registry. Registry shape: map[string]*registeredRoute keyed by Topic, no routesOrder (reqreply's lookups are always exact-string), DuplicateTopicTypeMismatchError + a lazily-surfaced poisoned-entry marker for collision detection REST's own first-registered-wins policy doesn't need. A CENTRALIZED adapter↔registry bridge (new ClientTransport.CallRaw method; adapters stay registry-unaware, resolve+decode happens in api/reqreply.Client itself) — a deliberate divergence from REST's adapter-side resolution (ClientAwareTransport/MatchRedirectRoute), explained in the doc's own "Adapter↔registry bridge" section. CallRaw now implements ALL 3 reply legs (ordinary/Forward/ErrorPattern) — the Forward-signal leg shipped as part of Forward/Bounce (Phase 1). Doc stays in docs/roadmap/ (not deleted/promoted yet) since Phase 2 still cross-references its design rationale. |
api/reqreply Forward/Bounce |
api/reqreply, adapters/mqtt5, adapters/zeromq |
Phase 1 + Phase 1.5 SHIPPED — Forward/ForwardError + structured errors + the chain-following loop implemented and tested (api/reqreply/forward.go, api/reqreply/client.go, adapters/mqtt5, adapters/zeromq); runnable demo at examples/reqreply-api/demo_forward.go (+ demoForwardWithProperties); unblocked Phase 2, which has ALSO shipped |
A handler can signal "route X should handle this instead" within the same correlation-ID round trip; the client republishes and auto-follows to the target's typed response (free Req/Resp, unlike HTTP 307/308). New wire signal mirroring each adapter's existing ErrorPattern discriminator (mqtt5 reserved ContentType+User Property; zeromq a 3rd/4th status frame) — no new envelope type; BOTH adapters' server-publish AND client-recognize sides (9 total insertion points) implement it, skipping ErrorPattern/DeadLetter entirely for a Forward. Resolution is CENTRALIZED in api/reqreply.Client.followForward via a chain-following loop built on Phase 0's single-hop CallRaw method — registry lookup, a client-side fail-fast ForwardSecuritySchemeMismatchError scheme-name check (going further than REST's own "let the target reject it" precedent), MaxForwards chain-depth capping (mirrors rest.ClientCallOptions.MaxRedirects), and ErrorPattern-matched-reply decode via the resolved target RouteHandle.DecodeErrorFor (reusing Phase 0's own ForwardRawErrorReply{Code, Body} extraction). ClientTransport.CallAsync was REMOVED from the interface entirely — Client.CallAsync now wraps Client.Call directly, reusing the SAME loop (confirmed structurally necessary: the prior per-adapter-goroutine design could never let api/reqreply.Client intercept a chained forward before the Future resolved). ports.Pattern/CallWithTransport bindings stay registry-free by design (verified, no new code needed) — a Forward there surfaces unwrapped, same as rest.CallWithTransport's own precedent. User-facing docs (docs/features/reqreply-forward.md/docs/guides/reqreply-forward.md) published together with this implementation, following rest-redirects.md's own "write them together, in the commit that ships the full feature" precedent. Phase 1.5 (THIS doc's own responsibility, documented in its own "Addendum" section) has ALSO SHIPPED — CallRaw/Forward gained a properties map[string]string parameter/field, unblocking Dead-Letter Replay's own Phase 2.0/2.1; a genuine write-side gap (the forward-reply publish initially dropped fwd.Properties onto the wire) was found and fixed during implementation, via a reserved prefixed MQTT5 User Property key. |
api/reqreply Dead-Letter Replay |
api/reqreply |
Phase 2 (2.0 + 2.1) SHIPPED — Client.ReplayDeadLetter/ReplayDeadLetterAsync implemented and tested (api/reqreply/dead_letter_replay.go, adapters/mqtt5, adapters/zeromq); runnable demo at examples/reqreply-api/demo_dead_letter_replay.go |
An ops/replay tool resolves a DeadLetterEnvelope's SourceTopic against the registry (Phase 0) and reissues the original request via Client.ReplayDeadLetter/ReplayDeadLetterAsync. Replays Payload verbatim (never re-decodes — DeadLetter fires on decode-failure too, so re-decoding would fail identically). Replay does its OWN loop-free hop-1 dispatch (own UnrecognizedDeadLetterSourceError, no security check — its unconditional security-scheme check would otherwise reject replay against any secured route, Replay's primary use case), handing off to Phase 1's unchanged chain-following loop (factored into Client.followForwardFromHandle to accept an already-resolved handle) only from hop 2 onward (a genuine forward-chain continuation) — fixing a MaxForwards accounting asymmetry for free too. DeadLetterEnvelope gained a Properties map[string]string field (Phase 2.0), capturing the original request's wire-level properties at dead-letter time (mqtt5 real capture via propertyVarsFromUserProperties; zeromq confirmed no-op) — making a header-delivered credential recoverable at replay time, closing a LATENT gap CallRaw itself (Phase 0) had, shared cross-cuttingly with Forward/Bounce's own Phase 1.5 (CallRaw/ForwardError.Properties, shipped first, as the design required) — confirmed api/events.DeadLetterEnvelope is a separate, unrelated type, so this stayed reqreply-scoped. New, distinct UnrecognizedDeadLetterSourceError (not a reuse of Phase 1's UnrecognizedForwardError, for caller clarity). ReplayDeadLetterAsync's FutureFactory resolves via the registry directly (NOT CallAsync's route-value-based path — a distinct detail). No ports.Pattern binding equivalent (Client-level-only method). A real end-to-end test (adapters/mqtt5) proves a header-delivered credential survives dead-letter capture AND replay over the real wire — the single most important proof the whole wire-level properties addendum exists for. Documents an operational caution against looping unconditional replay against a permanently-broken message. |
| Forge Pipeline Middleware | forge, stats |
Idea only — no driver, spun out from Declarative Middleware's "L14" finding | forge.Registry/pipeline functions (Layer 3) were never checked against the Declarative Middleware design. Forge already has its own parallel cross-cutting mechanism (Registry.WithObserver/stats.PipelineObserver, an explicit builder API by design) and no security/authorization concept at all. Three open directions carried forward unresolved: document Registry.WithObserver as already adequate; actually extend middleware.Middleware to forge via a decorator-shaped Fn (mirrors ports.File); or simply narrow Declarative Middleware's "every boundary" claim to Layer 2 only. No concrete driver identified yet — deferred to a future dedicated design pass. |
| Typed State Machine Contracts & Runtime | state, stream, forge |
Design draft | Codec-validated state payload + typed transition graph + runtime reducer so state can progress with explicit, forward-safe rules. Phase 1 includes both contract surface and runtime execution helper (no external FSM dependency). |
| Vector Store Adapter (RAG retrieval) | adapters/vectorstore, adapters/openai (embeddings) |
Awaiting use case | Placeholder for RAG's retrieval half — a narrow VectorStore interface (mirrors adapters/redis.Commands) for native vector DBs (Qdrant/Weaviate/Pinecone/Redis-search), plus the adapters/openai embeddings endpoint the (now-shipped) LLM Integration feature deferred. Notes what already works with ZERO new code today: pgvector similarity search via the existing generic adapters/sql.QueryEachAdapter, and embedding vectors via codex.SliceOf(codex.Float32()). Not yet designed in detail — captured as a reminder pending a concrete driving use case. |
| OpenAI Multimodal Content | api/llm, adapters/openai |
Awaiting use case | Placeholder for image/audio/video input support — today adapters/openai's wire shape is text/JSON-only (chatMessage.Content is always a plain string), with no path to OpenAI's multimodal content array (image_url/input_audio parts). Sketches a new llm.CallOpt for attaching media alongside the typed request codec and the chatMessage.Content union-type change needed on the adapter side. Not yet designed in detail — captured as a reminder pending a concrete driving use case. |
PartialFrom — Diff Two Structs Into a Patch |
codex |
Idea only | Follow-on to the now-SHIPPED Partial/Patch Struct Codec (codex.PartialField/PartialStruct, see docs/concepts/codec.md's matching subsection and .github/instructions/go-codex.instructions.md) — auto-derive a patch by diffing a base/previous T against an updated T, populating only the CHANGED fields. Not scoped, no use case yet; the open question is per-field equality without reflection (many real F types aren't comparable in Go's strict sense). |
codex.OptionalMutable[T]: A Mutable Cell That May Start Unset |
codex, stats |
Design draft — Mutable[T] now shipped, ready to implement |
Mutable[T]'s revalidating, hot-reloadable Set + Maybe[T]'s "may start unset" lifecycle, combined into a standalone THIRD type (not a modification of either) — for an optional feature's rotating credential that might not exist until the feature is enabled. Reuses Mutable[T]'s codex.ReloadObserver unchanged; Get() never panics (returns zero value if never Set, mirroring Maybe[T]'s precedent, not Immutable[T]'s panic-before-first-Set). Was explicitly sequenced AFTER Mutable[T] ships — that dependency is now satisfied — promoted from a one-line flagged idea once the need surfaced twice. |
Mutable[T]/Cacheable[T] — Subscribe-to-Changes |
codex |
Idea only — no driver | Of the 3 ideas this doc originally captured, 2 are now RESOLVED with no code change: native Builder security wiring (SecurityFunc/CredentialFunc are already plain closures — a caller composes Mutable[T]/Cacheable[T] via .Get() with zero new API, proven in examples/mutable-security-keys and documented in docs/concepts/codec.md's "Composing with adapters" subsection), and folding adapters/nethttp.NewCachingCredentialFunc onto a container (redirected to ports.RefreshingCacheable[T], not Mutable[T] — revisit after that ships). Only a "subscribe to changes" push-notification mechanism (distinct from ReloadObserver/InvalidateObserver, which only cover observability) remains open, with no driver and a genuinely open design space (channel-sub vs. callback vs. stream.Stream[T]). |
Maybe[Nullable[T]] (Three-State Fields) |
codex |
Idea only | A three-state field (absent/present-null/present-value — the RFC 7396 JSON Merge Patch shape) via Maybe[Nullable[T]] — VERIFIED to already compose for free with zero new code (MaybeField("x", codex.Nullable(inner), ...) on a Maybe[*T] field genuinely distinguishes all three states today); what's left is documentation-only (a worked example + test). (This doc's other idea — a public codex.Codec[Maybe[T]] — has SHIPPED as codex.MaybeCodec[T], see docs/concepts/codec.md's "Maybe[T]: definitive presence tracking" subsection.) |
Cacheable[T]: Redis-Backed Sibling |
adapters/redis, codex, ports |
Design draft — codex.Cacheable[T] now shipped, ready to implement |
Follow-on to the now-SHIPPED in-process codex.Cacheable[T] (see docs/concepts/codec.md's Cacheable[T] subsection and codex/cacheable.go) — a adapters/redis.Cacheable[T] sibling with the same TTL/Invalidate() shape but a ctx-aware, fallible Get(ctx) (T, bool, error) backed by an actual Redis entry via ports.Cache, so a value's storage is pluggable (in-memory or Redis) without changing how callers read it. Also captures a "generic multi-key Cacheable" idea as a no-driver-yet note. |
ports.RefreshingCacheable[T] |
ports, codex, stats |
Design complete — codex.Cacheable[T] now shipped, ready to implement |
Auto-refresh wrapper COMPOSING codex.Cacheable[T] (not modifying it) — GetOrRefresh(ctx) (T, error) single-flights a caller-supplied RefreshFunc[T] when stale, mirroring adapters/nethttp.NewCachingCredentialFunc's existing hand-rolled single-flight/TTL pattern but generalized beyond HTTP credentials. Stale-while-revalidate is the default failure policy (WithFailHardOnRefreshError opts into fail-hard). Its Cacheable[T] dependency (previous roadmap row) has now shipped; a forward-looking note flags it as a candidate to later compose either the in-memory or Redis-backed Cacheable[T] sibling interchangeably. |
| Streaming Walk Adapter for Files & Directories | adapters/file |
Idea only | Follow-on to the now-SHIPPED Directory Listing Port (ports.Dir/DirEntry, see docs/features/ports.md) — sketches a ports.SourceAdapter-shaped adapter that WALKS/streams ports.DirEntry values (recursive tree enumeration and/or continuous change-watching) instead of ports.Dir.List's one-shot slice. Not designed: the central open question is a real dependency/architecture fork — stdlib-only polling (extends adapters/file.WatchAdapter's existing approach) vs. event-based OS-native notifications (would require the FIRST external dependency in adapters/file, most likely fsnotify/fsnotify) — plus one-shot walk vs. continuous watch scope, and whether modify/delete events (not just creations) are needed. |
| MQTT5 Capability Extensions | adapters/mqtt5 |
Design draft — Message Expiry ready to implement; Shared Subscriptions has an open decision | Spun out of docs/design/d-0006-protocol-native-capabilities.md's §6 feature survey and §9 (Phase 8) review. Two never-implemented Capability candidates: MessageExpiry(seconds uint32) (Publish-side only, mirrors Retained's shape exactly — one-line adapter.go change, no open questions) and SharedSubscription(group string) ($share/group/topic competing-consumers — genuinely blocked on an open design decision, since MQTT5 encodes it as a topic-FILTER-STRING prefix, not a WireAttributes field; presents 2 real options, neither pre-decided). |
| Design-Doc Compaction & Workflow-Model Narrative | docs/design, docs/concepts |
IMPLEMENTED | Spun out of docs/design/d-0006-protocol-native-capabilities.md's §9 (Phase 8) review. Scope widened at execution time: confirmed d-0006-protocol-native-capabilities.md was D-0006's own rework (not a separately-promotable doc), so it was merged wholesale into D-0006 as §9 and deleted, after a full repo-wide reference sweep (~100 files). Also added the capability-declaration step to docs/concepts/declaring-apis-and-ports.md and a new "Interface inventory" section to docs/concepts/ports-and-adapters.md. See the doc's own Learnings section for the full executed scope. |
| Request/Event Correlation ID | stats (+ every adapter) |
Idea only | A first-class, cross-cutting correlation ID for logs/metrics/support, spun out of reviewing D-0004 — ReqReply Workflow Simplification's wire-level corrID. Confirmed via code: no such concept exists anywhere today — stats.Observer's own methods (RecordRequest/RecordSubscribe/RecordPublish) take no ctx at all, so they structurally can't read one even if ctx-stored. Proposes stats.WithRequestID/RequestIDFromContext/NewRequestID, mirroring WithObserver/ObserverFromContext exactly. Key design question resolved: this ID must stay DELIBERATELY SEPARATE from TraceObserver's distributed trace/span ID and from reqreply's own wire-level corrID — three distinct concepts, not one. The clean connection to tracing is already validated by TraceObserver.StartSpan(ctx, ...)'s EXISTING signature (it already receives ctx): if the request ID is set into ctx BEFORE StartSpan is called, a tracer implementation can read it back internally and attach it as a span attribute — zero interface change needed, mirroring OpenTelemetry's own Baggage/attribute practice. Whether Observer needs a NEW additive RequestIDObserver extension (mirroring DispositionObserver's pattern) is flagged, not locked — needs a throwaway prototype. ports.File/Cache/SQL/Dir coverage is explicitly flagged open (same structural point as d-0006-protocol-native-capabilities.md's Review-7), not force-included. |
| Idea: Codec-Defined HATEOAS | api/rest, codex |
Idea only — brainstorm transcript, not a formal design | Explores a declarative, codec-derived HATEOAS (_links) mechanism for api/rest, building on the now-shipped codex.Template[T]/codex.Param/MergedParam[T]/NewParam[T] primitives (the same build+match/merge engine underneath rest.PathParam/events.TopicParam/reqreply.TopicParam/ports.FilePathParam). Sketches a Route → Resource → Affordance → Link conceptual core and a port.Follow(ctx, link) call shape, deliberately deferring the wire representation (HAL, JSON:API, Siren, plain _links) as a swappable renderer layered on top of the internal relationship. No API surface committed — captured as a starting point for a future formal roadmap plan. |
| Declarative-Workflow Walkthrough & Tutorial Skills | .github/skills/ (docs/process only, no Go code) |
Design draft | Plans docs/design/d-0006-protocol-native-capabilities.md's §9 (Phase 8) closing items: a joint, human-in-the-loop walkthrough declaring one api/rest/api/events/api/reqreply API end-to-end (struct+codec → route/channel → capability requirement → adapter attach → run), documenting every friction point as a Learnings entry; then 3 per-API live tutorial skills (tutorial-api-{rest,events,reqreply}) informed by those findings, each teaching the same workflow shape against the existing flagship examples rather than new demo code. No Go API surface changes — a substantial friction finding spins into its own follow-on roadmap doc instead of being folded in silently. |
Deferred — not planned for immediate implementation¶
| Feature | Package | Why deferred |
|---|---|---|
zeromq.CallDealerStream |
adapters/zeromq |
Requires adding correlation-ID frames ([seq_bytes, payload]) to DEALER framing AND matching changes to ServeRouter — protocol-level breaking change, not a standalone stream bridge addition. The sequential CallStream (REQ socket) covers most use cases. |
How to read these plans¶
Each plan page covers:
- Motivation — why this feature belongs in go-codex
- Scope decisions — what is in and out of Phase 1
- API surface — exact type signatures, options structs, interface definitions
- Structured errors — every new error type with
slog.LogValuerattributes - Observer integration — which observer hooks fire and when
- AsyncAPI / OpenAPI spec — how the feature integrates with spec generation
- Files to create — concrete file list with responsibilities
- Usage sketch — end-to-end code example