Toolbelt glue absorption — map: what the toolbelt absorbs from duplicated consumer glue #1
Labels
No labels
needs-info
needs-triage
ready-for-agent
ready-for-human
wayfinder:grilling
wayfinder:map
wayfinder:prototype
wayfinder:research
wayfinder:task
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
ginjiruu/toolbelt#1
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Destination
A decision/implementation spec for what the toolbelt absorbs from the duplicated consumer glue — delivered as a
docs/plans/collection, one plan per ticket. Consumers (go-template, ttrpg) stay out of scope until the map is done. The map ends when every ticket is closed and the plans are written; reshaping theexample/app onto the absorbed surfaces is deliberately deferred to a separate post-map clean rewrite where the challenges are reassessed against every surface at once.Notes
CONTEXT.mdglossary (composition root, authorizer, Feature, Field).example/app hand-wrote it AND consumers duplicate it.docs/plans/, written at resolution and linked from the ticket. The map ends when every ticket is closed; the plans are the deliverable, not example code. No toolbelt or example code lands with a ticket — all implementation (toolbelt API changes, dead-helper deletions, shim shrink, example reshape) is deferred to a separate post-map clean rewrite ofexample/where the absorbed surfaces land together and challenges are reassessed./grilling+/domain-modeling(use/prototypewhen a concrete API shape helps); AFK →/researchsubagent on a throwawayresearch/<name>branch.ListObjectstype:idfolds into the authz-absorption ticket; sqlctypes:manywas resolved as a research ticket (see Decisions).Decisions so far
package cmd); atoolbelt/cmdparent-command factory preserves the raw cobra API. Graduated into a design ticket.types:manyis not a sqlc annotation; usesqlc.slice('ids'); no fork, no workaround; the only affected query is consumer-side (go-template).toolbelt/authzpolicy package;type:idstrip inScope;ScopeFunc+ListRelations+Enforceabsorbed; user refs codegen-owned; no write interface (ridesembeddedfga.Server); narrowBackendinterface. Deliverable (plan + example reshape) taken to a new session.config.New(Options{...})instance-owned object (globalsFields/ConfigEnvPrefix/AppName/AppVersiondeleted; funcs become methods); KCL single source of truth,kcl-generateemits registry + bootstrap (var App = config.New(...)),internal/config100% generated;BuildLoggermoves totoolbelt/app;cfg.Validate()absorbs KCL validation;cfg.Fields()exposes completion data fortoolbelt/cmd. Plan:docs/plans/config-bootstrap.md; example reshape + consumer migration deferred (example reshape in a later session). Service-owned defaults graduated to #12.toolbelt/appowns the whole bootstrap:New(ctx, cfg *config.Config, opts)reads platform fields internally and builds the logger (exportedBuildLogger(level, format)); the startup summary moves intoNew;Component.Configbecomes the*config.Configobject. Features self-populate atRegister—authn.New(opts)loses its six OIDC fields + logger arg and readsc.Config/c.Logger;app.goshrinks toNewKanbanApp(ctx, config.App)+ feature wiring. FlatConfigstruct,NewConfig(), and all fiveBuildLoggercopies die. Plan:docs/plans/app-bootstrap.md; example reshape deferred with the config ticket. Field ownership coordinates with #12.f3cbcd0(sub-tickets #15–#18 under parent spec #14): each service package ships its ownschema.kfragment (db/,embeddednats/,embeddedfga/,app/);kcl-generateresolves fragments viakcl.mod(sharedkclmodpackage;-universalflag removed) instead of string-merging a universal schema; apps compose by native import under canonical keys (schema Config(app.Config)); Go const defaults dropped (embeddednats.SetupNATStakes the store dir positionally); ttrpg consumer break recorded. Lessons: directive correlation became file-aware for multi-file merges;kcl.Runmust receive path deps as explicit external packages.NewRootCommand(cfg, RootOptions{Short, Long, PreInit})(Use/Version from cfg; validation structural via generatedcfg.Validate(), no callback),AddConfigCommand(cfg, root)full group (KCL-aware by default: blank-importconfig/kcl,.kgate; no save callback),Pager(),Serve(cfg, ServeOptions{Options app.Options})(logger build/sync + startup summary absorbed intoapp.Newper plan #4),embeddedoidc.DevSetupwith options. Int-v/-vv/-vvvladder only (ttrpg bool-vnot a compat constraint); per-appExecute()stays; factory owns flag storage. Plan:docs/plans/cmd-bootstrap.md; implementation lands with the #3/#4 reshapes. Codegen follow-up graduated to #23.var ModelFS embed.FS/var MigrationsFS embed.FS); all shim helpers die (ModelFS(),Migrations(),ModDir,EmbeddedModData/EmbeddedModules/ModelFiles,EmbeddedFGAModelFS— usage audit found the latter three dead everywhere).db.NewDatabasewrapper absorbed as a variadic functional-options API in theembeddedfgaidiom:appdb.NewDatabase(ctx, dbPath, fsys, opts ...Option)withWithMigrationsDir(default"migrations") +WithLogger. Embed-dir convention codified by option defaults + loud startup failure, not KCL (schemas declare config values, embed dirs are Go-source layout — nothing to validate). Plan:docs/plans/embed-shims.md; no code lands (deferred to the post-map example rewrite).can_convention: a relation startingcan_is a capability action (1:1 with an affordance, reads "can ");owner/memberare composition roles, never actions.fga-generateemitsActions()+ActionsForUser()(same set annotated{Rel, Allowed, Reason}from oneListRelationsbatch), dependency-free — no app-side computeActions, no glue.Actioncarries no Href/Method (renderer's rel→widget map owns routes); permission-only, state conditionals stay in the renderer (declarative state verification graduated to #24). Authz seam closes #2's open point:authz.ListRelationshides rawopenfgav1; generatedRelationListerstays dependency-free. Read model = per-board KV buckets, actions recompute per render. Board model grows per-action relations, seeded to show granularity. Plan:docs/plans/hateoas-actions.md; implementation deferred to the post-map rewrite.Not yet specified
sqlc.sliceguidance needs codifying in the toolbelt (docs/example) even though no toolbelt example usesINtoday.sqlc-gen-zombiezenreproducibility gap (unpinned plugin) and two unrelated upstream plugin bugs surfaced by the sqlc research — candidate task, not yet ticketed.can_relations — the linking convention (which operation ↔ which relation) is unsettled.example/(graduate on discovery).Out of scope
GO_TEMPLATEprefix; stale fork of go-template) — a consumer bug, not an absorption decision.ResolveDBPath/ResolveTupleFilePath(config reexport ticket) — fails the example-proves-it boundary (not hand-written in the example);toolbelt/db.NewDatabasealready resolves paths viafilepath.Abs.example/clean rewrite (reshaping the app onto all absorbed surfaces) — a separate post-map effort, not part of this map; it is where every ticket's deferred implementation lands together and the challenges are reassessed.