REPOSITORY / ScuroNeko/Laniakea

Wiki

KNOWLEDGE REPOSITORY
10
Framework Backlog
ScuroNeko edited this page 2026-05-20 13:28:29 +03:00

Framework Backlog

This page tracks framework-level backlog items that are about missing concepts in the library itself, not just missing documentation.

Done

[1.0.0]

Major — closed before 1.0.0 tag

  • M1. BotPayloadType* are var, must be constbot.go:50-59. Public sentinels are user-mutable globals. KeyboardButtonStyle* in keyboard.go:10-17 already uses const; match the pattern.
  • M2. Observer method naming asymmetryobserver.go:147-157. OnReceiveUpdateOnUpdateReceived; OnHandledUpdateOnUpdateHandled to match UpdateReceivedEvent / UpdateHandledEvent and the rest of the OnX pattern. Breaking after 1.0.
  • M3. Uploader returns ad-hoc error string instead of *ResponseErrortgapi/uploader_api.go:183. tgapi/api.go:258-292 returns *ResponseError; uploader must do the same so errors.As(err, &tgapi.ResponseError{}) works for upload paths too.
  • M4. BotOptsFileJSON is missing PollTimeoutbot_opts_loader.go:35-46, plus FromBytes/ToBytes mapping. File round-trip silently drops PollTimeout.
  • M5. Stale Bot.Updates godocmethods.go:11-44. Claims "30-second timeout" and "empty slice if none"; in reality timeout is bot.pollTimeout and the function returns nil on error.
  • M6. Self-contradicting NewRandomDraftProvider godocdrafts.go:50-59. Says "cryptographically secure random numbers" but uses math/rand/v2 (the underlying generator type correctly notes it is not crypto-secure).
  • M7. Draft.Delete godoc says "internal method"drafts.go:190-201. Method is exported; either rewrite the godoc with a public-intent description or unexport.
  • M8. Russian comments in production codemsg_handler.go:28, tgapi/uploader_api.go:181.
  • M9. MessageContext.Error godoc references unexported helpermsg_context.go:540. Rewrite to describe the centralized handler error path and IsUserError gating.
  • M10. Scene and SceneSession mix exported fields with settersScene.PluginName unexported; SceneSession.Data unexported, use accessor helpers.
  • M11. Constant-time compare for webhook secretbot_webhook.go. Use subtle.ConstantTimeCompare.

Minor — closed before 1.0.0 tag

  • Strip // Internal helper … godoc from unexported funcs.
  • Plugin.AddCommand godoc references unexported field .command.
  • Runner builder: Onetime/TimeoutEvery/Async; Once() removed.
  • Typo in webhook error string: "MaxConnections must between 1 and 100" → "must be between".
  • RunWebhookWithContext inline errors.New(...)Err* sentinels.
  • tgapi.UpdateTypeManagedBot missing godoc.
  • Bot.GetAPI, Bot.GetUploader, InlineKeyboard.GetMaxRow missing godoc.
  • Bot.L10n godoc says "Returns empty string if translation not found"; actually returns the key.
  • Bot.handle panic recovery only logs — emit ErrorEvent so observers see panics.
  • handleCallback vs handleMessage plugin-logger assignment asymmetry — aligned.
  • SetCallbackData godoc: zero BotPayloadType behavior documented explicitly.
  • commands.go empty case CommandValueAny: merged with default.
  • Bot.SetDebug does not call configMutable — noted in godoc.

Tests added after the fixes

  • BotOptsFileJSON round-trip for PollTimeout.
  • Uploader 4xx/429 surfaces *tgapi.ResponseError.
  • Bot.handle panic → observer receives ErrorEvent.
  • Webhook /status with wrong SecretToken returns 404, constant-time-compare smoke test.
  • Table-driven parseCommand cases for /cmd@botname stripping.

[1.0.0-rc.14] Webhook runtime model

Current state:

  • The repository already exposes low-level Telegram webhook setup APIs through tgapi, including SetWebhook(...), DeleteWebhook(...), GetWebhookInfo(...), and uploader-based certificate upload support.
  • The framework now exposes first-class bot-level webhook runtime entry points through RunWebhookWithContext(...) and RunWebhook(...).
  • Webhook delivery now uses the same internal update queue, worker pool, runner startup model, and single-use runtime contract as polling.
  • The webhook runtime behavior, security model, and polling-transition requirements are now documented in the main docs and wiki.

Why this matters:

  • Telegram webhook transport support at the API-client layer was not enough on its own; users still needed a framework-owned runtime path to make webhook delivery feel equivalent to polling.
  • A first-class runtime mode keeps worker scheduling, lifecycle behavior, configuration rules, and shutdown semantics aligned across both ingress models.
  • The framework also needs an explicit transition story when users move a deployment back from webhook delivery to polling.

What is now present:

  • BotWebhookOpts, NewBotWebhookOpts(), and fluent helpers for webhook-specific configuration.
  • RunWebhookWithContext(...) and RunWebhook(...) as bot-owned runtime entry points.
  • Shared queued update dispatch, worker-pool delivery, runner startup, and single-use run semantics between polling and webhook modes.
  • Default fallback from webhook AllowedUpdates to the bot-level update type configuration.
  • Request validation for webhook path shape and TLS file count before remote webhook setup.
  • Explicit remote webhook teardown through CloseWebhook() or low-level tgapi.DeleteWebhook(...) when switching a deployment from webhook delivery back to polling.
  • Regression coverage for queue delivery, runner startup, single-use behavior, body-size rejection, path and TLS validation, and status-endpoint auth behavior.

Practical target:

  • Treat webhook delivery as a first-class framework runtime mode rather than leaving it as only a low-level tgapi transport primitive.

[1.0.0-rc.13] Observability Model

Current state:

  • The framework now exposes a first-class Observer model with typed runtime events and a panic-safe dispatch path through the bot runtime.
  • Observer hooks now cover update receipt and completion, command and payload handlers, generic update handlers, scene handler lifecycle, scene transitions, policy checks, runner completion, polling retries, and centralized error routing.
  • Instrumentation remains best-effort and does not introduce a second execution pipeline or require application data to be threaded through observer callbacks.

Why this matters:

  • Logging alone is useful, but it does not create a stable runtime-facing observability contract for metrics, tracing adapters, or structured event collection.
  • Framework-owned hooks make runtime behavior visible without forcing users to patch internal routing paths or duplicate instrumentation logic around commands, payloads, scenes, and polling.
  • A typed event model preserves clarity in godoc and tests while keeping observer integration lightweight.

What is now present:

  • Observer as the public instrumentation interface.
  • Typed events for update, handler, scene, policy, runner, polling, and error flows.
  • Safe event dispatch through safeEmitEvent(...), including panic recovery.
  • Runtime emission across command, payload, generic update, and scene flows.
  • Policy evaluation events through PolicyCheckedEvent.
  • Runner and polling observability through RunnerFinishedEvent, PollingRetryEvent, and observer ErrorEvents.
  • Regression coverage for observer configuration, command and payload lifecycle events, scene lifecycle and transitions, update-handler errors, callback decode failures, runner completion, polling retries, and policy checks.

Practical target:

  • Treat observability as a first-class framework capability instead of leaving instrumentation to ad-hoc logging and external wrappers.

[1.0.0-rc.13] Authorization and Policy Model

Current state:

  • The framework now exposes Policy[T] as a first-class reusable authorization rule that runs against the normalized MessageContext and injected app data.
  • Policies integrate with the existing execution model through RequirePolicy(...), so authorization stays on the middleware path instead of introducing a second routing pipeline.
  • Bot-level and plugin-level registration helpers now make policy usage explicit in configuration.

Why this matters:

  • Access checks are a common requirement in Telegram bots, but ad-hoc middleware alone does not create a stable framework concept.
  • Reusable policy helpers make chat-type restrictions, admin checks, and callback restrictions visible and composable instead of spreading them across handler internals.
  • Keeping policy execution on the existing middleware path preserves the framework's current runtime model while still giving authorization its own language.

What is now present:

  • Policy[T] as the public authorization abstraction.
  • RequirePolicy(...) for adapting policies into blocking middleware.
  • Bot.UsePolicy(...) and Plugin.UsePolicy(...) for registration ergonomics.
  • Built-in Telegram-aware helpers such as RequirePrivateChat(...), RequireGroupChat(...), RequireSupergroupChat(...), RequireChatAdmin(...), RequireChatCreator(...), RequireBotAdmin(...), and RequireCallbackFromUser(...).
  • Composition helpers AllPolicies(...), AnyPolicy(...), and NotPolicy(...).
  • Extended MessageContext normalization for Chat and ChatID, plus regression coverage for policy and normalization behavior.

Practical target:

  • Treat authorization rules as a reusable framework concept instead of leaving access control as only an ad-hoc middleware pattern.

[1.0.0-rc.13] User-Facing vs Internal Error Model

Current state:

  • The framework still keeps one centralized handler error path, but it now supports explicit classification between user-visible and internal-only errors.
  • Unclassified returned errors remain user-visible for backward compatibility.
  • Internal-only failures can now be logged without automatically sending their raw text back to the user.

Why this matters:

  • A single unified error path is useful, but not every failure should become a user reply.
  • Infrastructure faults, invariant violations, and internal routing failures are often important for operators while being noisy or misleading for users.
  • Without classification, framework users either leak internal errors into chat UX or are forced to bypass the centralized path entirely.

What is now present:

  • AsUserError(...) and AsInternalError(...) to classify returned handler errors explicitly.
  • IsUserError(...) and IsInternalError(...) for framework-side inspection.
  • Updated centralized MessageContext.Error(...) behavior that still logs all errors but suppresses the automatic user reply for internal-only failures.
  • Regression coverage for both message and callback flows.

Practical target:

  • Keep the centralized handler error path intact while making user-facing and operator-facing failure handling meaningfully distinct.

[1.0.0-rc.13] Configuration Freeze Model

Current state:

  • The framework now treats bot configuration as structurally complete once the first runtime entry point begins: Run(), RunWithContext(...), or RunWebhookWithContext(...).
  • Late bot-level mutation attempts no longer partially apply after runtime startup.
  • Plugin registration and runtime configuration boundaries are now documented and tested as explicit framework behavior.

Why this matters:

  • Runtime mutation of prefixes, middleware, payload policy, localization, runners, or shared dependencies is difficult to reason about safely.
  • The framework already had real commit points like AddPlugins(...); this work makes those boundaries explicit instead of leaving them as implementation details.

What is now present:

  • Bot-level configuration mutators now ignore late calls after runtime start.
  • Lifecycle and configuration-freeze rules are documented as a first-class concept.
  • Regression tests now lock down ignored late mutations.

Practical target:

  • Make startup, runtime, and configuration ownership predictable enough to rely on as a stable framework contract.

[1.0.0-rc.13] Update Schema Contract

Current state:

  • Update normalization already existed, but it is now described and tested as an explicit framework-level contract.
  • Routing categories and MessageContext population guarantees are now treated as a first-class part of the public model.

Why this matters:

  • Handler code needs to know which MessageContext fields are safe to rely on for each update path.
  • Without a formal contract, update handling remains understandable only by reading implementation details.

What is now present:

  • A documented routing model for command flow, payload flow, and generic update handlers.
  • Explicit MessageContext field comments for update-backed, callback-backed, and message-backed contexts.
  • Table-driven regression coverage for the normalized update contract, including callback target semantics and non-command update flows.

Practical target:

  • Make update routing and MessageContext guarantees explicit enough to serve as a stable 1.0 public contract.

[1.0.0-rc.12] Conversation / Scene Model

Current state:

  • The framework is strong at handling a single update through commands, payloads, middleware, and update handlers.
  • It already has useful lower-level building blocks such as MessageContext, drafts, payload routing, plugins, and update handlers.
  • It now provides an implemented initial scene model for long-lived user interaction flows: scenes can be registered in plugins, entered through MessageContext, persisted through SessionStore, and routed before normal command handling.

Why this matters:

  • Many Telegram bots quickly move beyond isolated commands and need stateful multi-step flows.
  • Real bots often need concepts like "wait for the user's next message", "user is currently on step 3 of 5", or "button press moves the user to the next scene state".
  • Without a scene model, library users end up building their own mini-framework on top of Laniakea.

What is already present:

  • Active-scene routing before normal command flow.
  • Per-user, per-chat, and per-user-chat session scopes.
  • Explicit scene entry and exit through MessageContext.
  • Step handlers, scene-local commands, and OnMessage(...).
  • In-memory session storage by default, plus the SessionStore interface for custom persistence.

What is still missing or not yet settled:

  • Scene-local payload routing.
  • A clear decision on whether any additional public scene-inspection API is needed.
  • Further extensions beyond the current message-driven scene model.

Current API direction:

  • Scene, SceneContext, SceneSession, and SessionStore.
  • Plugin.NewScene(...) and Plugin.AddScene(...).
  • MessageContext.EnterScene(...), EnterSceneStep(...), and ExitScene(...).
  • SceneContext.Stay(), Next(...), Exit(), Pass(), BindData(...), and SaveData(...).
  • Storage-backed per-user or per-chat state with a clean interface for custom persistence.

Important design constraints:

  • This should be additive and optional.
  • It should not replace plugins, commands, or handlers as the normal framework entry points.
  • It should work with existing middleware and MessageContext instead of introducing a second incompatible execution model.

Practical target:

  • Extend the current first-class scene model where it adds clear value.
  • Keep both step-based forms and mode-based chat flows supported without forcing users to build custom routing layers around active sessions.

[1.0.0-rc.12] Typed Handler Input Model

Current state:

  • Commands and payloads currently expose parsed text through ctx.Text and ctx.Args.
  • CommandArg provides basic argument validation and shape checks.
  • Handlers still do most non-trivial parsing manually.

Why this matters:

  • As bots grow, handlers often start with repetitive ctx.Args parsing boilerplate.
  • Validation logic tends to spread across handlers instead of living in one predictable binding layer.
  • The current model is simple and honest, but it does not help enough once commands become more structured.

What is missing:

  • A first-class way to bind command or payload arguments into a typed Go value.
  • A framework-level pattern for conversion errors and validation errors beyond raw string handling.
  • A low-friction way to move from positional arguments to a structured input object.

Possible API direction:

  • A lightweight binding API such as ctx.BindArgs(&input).
  • Or explicit typed command registration such as NewCommandTyped(...).
  • Positional mapping into structs, optional fields, basic conversion support, and integration with current validation flow.
  • Unified binding and validation failures routed through the current centralized error path.

Example of the kind of user code this should enable:

type BanInput struct {
	UserID int
	Reason string
}

func ban(ctx *laniakea.MessageContext, db *App) error {
	var input BanInput
	if err := ctx.BindArgs(&input); err != nil {
		return err
	}
	return db.Ban(input.UserID, input.Reason)
}

Important design constraints:

  • Avoid a reflection-heavy, magical subsystem.
  • Keep the current ctx.Args model as the minimal baseline.
  • Treat typed binding as an ergonomic layer on top of the current command model, not a replacement for it.

Practical target:

  • Remove repetitive parsing boilerplate while preserving the framework's explicit, Go-like feel.

[1.0.0-rc.12] Request Context / Cancellation Model

Current state:

  • RunWithContext(...) and RunWebhookWithContext(...) control bot runtime lifecycle and graceful shutdown.
  • tgapi already supports context-aware methods.
  • Regular handlers do not receive a first-class request-scoped context.Context.

Why this matters:

  • Handler business logic often needs cancellation-aware database calls, HTTP calls, or downstream service calls.
  • The framework already has a good runtime cancellation story, but it does not flow naturally into user code inside handlers.
  • In modern Go APIs, context.Context is a standard part of operational correctness.

What is missing:

  • A clean request-scoped context that follows each update through handler execution.
  • A standard way for application code to stop work when the bot is shutting down or the update processing context is canceled.
  • A direct bridge between bot lifecycle control and service-layer cancellation.

Possible API direction:

  • Prefer a non-breaking approach by exposing context through MessageContext, for example ctx.Context().
  • Build the context from the update-processing lifecycle so it is meaningful during graceful shutdown.
  • Make it natural to pass that context into database methods, HTTP clients, and tgapi.WithContext(...) calls.

Why this should probably not be a signature change:

  • Changing handler signatures to accept context.Context directly would be a public breaking change.
  • A MessageContext accessor would preserve compatibility while still giving handlers an idiomatic Go cancellation path.

Practical target:

  • Let handler code participate naturally in cancellation and graceful shutdown without forcing users to invent their own context plumbing.

Related pages:

Ideas

Items in this section are intentionally speculative. They may become real backlog work later, or they may remain design notes if the current framework model continues to be sufficient.

Service layer and dependency graph model

Current state:

  • SetAppData(...) already gives the framework a simple shared dependency model through the generic bot type parameter.
  • That model is intentionally lightweight and works well for bots that only need a small set of long-lived shared dependencies.

Why this is only an idea for now:

  • The repository does not currently show strong evidence that a heavier service container or scoped dependency model is necessary.
  • A framework-owned dependency graph would add API and lifecycle complexity, so it should only move into the active backlog if repeated real-world usage shows that AppData is not enough.

What this could mean later if it becomes necessary:

  • A first-class service registry beyond a single shared AppData value.
  • Optional validation of required services before runtime startup.
  • Clearer lifecycle or scoping rules for shared framework-managed services.

Plugin composition contract

Current state:

  • Plugins are already more than a loose command bag: registration via AddPlugins(...) snapshots plugin state, treats registration as a commit point, and documents post-registration mutation as unsupported.
  • This gives the framework a meaningful baseline contract around plugin ownership and immutability at runtime.

Why this is only an idea for now:

  • The current repository state does not yet show strong pressure for a richer framework-level plugin dependency or capability model.
  • Additional composition contracts would add API surface and validation rules, so they should remain optional design work until repeated real-world plugin interactions justify them.

What this could mean later if it becomes necessary:

  • Explicit plugin dependencies.
  • Shared capability declarations or requirements between plugins.
  • A framework-level composition model for validating or coordinating plugin relationships.