Releases: DonaldMurillo/gofastr
v0.8.1 — README rework
v0.8.1 — README rework
A docs-only patch. No code changes.
Changed
- New "Why this exists" section — GoFastr is framed as a personal project first, with the motivations behind it: solidifying web-tech foundations, attacking UI generation from a compiled-language angle (the author's background is Node), working in a compiled language, skipping the convention-vs-configuration choice, building something large with AI, and making a framework that's AI-first on both the authoring and the consuming side.
- Removed the framework comparison from the README — the PocketBase / Encore / Wasp / Supabase / FastAPI name-drops and the
comparison.mdlink. (Thecomparison.mdfile is left on disk for now.) - Kiln demoted to a brief experimental mention — the ~60-line section became one paragraph linking to
kiln.md, and it's no longer centerstage (dropped from "Built with GoFastr" and the install block; repo-layout line marked(experimental)). - Migrated the README-only Kiln detail into
kiln.md(plan-gated destructive ops, full tool surface, Claude Code MCP wiring, HTTP tool-call example) so nothing was lost. - "dogfooded" → "runs on itself"; fixed the opaque "Walkthrough: the v2 read/write surface" heading → "Walkthrough: the read/write API".
Full details in CHANGELOG.md.
v0.8.0 — native gofastr export (SSG) + site repositioning
v0.8.0 — native gofastr export (SSG) + site repositioning
Added
gofastr export— native static-site generation. The framework now exports a deploy-ready static site itself, replacing the brokenwget --mirrorcrawl used for the Pages deploy. The crawl baked cache-bust?v=<hash>queries into on-disk module filenames; the static host stripped the query, every split runtime module 404'd, and all client interactivity silently died (theme toggle, command palette, copy, widgets).App.ExportStatic(ctx, dir, basePath)drives the app in-process, enumerates every declared route, renders each through the SSG path, and dumps all/__gofastrassets with query-free filenames.- Subpath base path (
--export-base /<repo>) for GitHub Pages project URLs; the builder prefixes every root-absolute URL and bakes the prefix intoruntime.js. - Runtime static-mode — a
data-fui-staticmarker (stamped only at export time) no-ops every server-backed dispatch so disabled actions read as intentionally inactive, with a dismissible "run locally"ui.Banner. Client-only features (theme, copy, signals) are unaffected. ui.CodeBlock.Scroll+ui.HighlightLines(framework/ui)./examplesMeridian row shows the real, fullexamples/meridian/gofastr.ymlblueprint — embedded at build time, drift-guarded by a test, in a scrollable copyable block.- New
framework/docs/content/static-export.mdguide;pages.ymlnow runssite --export _site --export-base /gofastr.
Fixed
- Sticky sidebars were silently broken site-wide.
body { overflow-x: hidden }forcedoverflow-ytoauto, turning<body>into a scroll container so everyposition: stickydescendant (docs + components sidebars, TOC, step-rail) anchored to the non-scrolling body and scrolled away. Guard moved tohtml. - Components-page sidebar wouldn't pin even after the above — the framework's sidebar
<nav>wrapper is the grid column but didn't pass its height to theSectionMenuwidget; it's now a flex column. - Header brand read as a doubled version (
λ gofastr v0.x dev) — dropped the static tag; the badge shows one version (devlocally,v0.8.0tagged).
Changed
- Homepage + getting-started repositioned to lead with screens + blueprints (new "One file, a real app" section).
- Kiln marked experimental across the site + docs.
Also in this release (post-tag hardening, included)
These landed after the v0.8.0 tag was first cut, so the tag was moved to the commit that includes them before this Release was published:
- Homepage a11y — the new "real app" section's screen-mock URL failed color-contrast and its Meridian link failed link-in-text-block, in both color schemes; fixed (axe gate green).
- Island SSE push-drop race —
core-ui/islandServeSSEflushed response headers before subscribing, so a client that pushed immediately on connect had its update silently dropped (theTestServeSSECI timeout). Subscribe now precedes the header flush. - Supply-chain hardening — every CI action is now SHA-pinned to an immutable commit (floating tags can no longer swap code into runs), and
mainis branch-protected with required CI checks.
Full details in CHANGELOG.md.
v0.6.1 — docs-site fixes (version-from-tag, sidebar drawer)
Patch release fixing two examples/site (docs site) bugs. No breaking changes, no framework API changes.
Fixed
- The site version is stamped from the deployment's git tag instead of a hand-bumped constant that had drifted to
0.4.0. Injected at build via-ldflags "-X main.siteVersion=$(git describe --tags --abbrev=0)"(wired intodev-watch.sh,make build-examples, and the Pages workflow). The deployed site now always matches the tag it was built from. - The Sidebar showcase's mobile nav drawer now opens. At < 900px,
/components/sidebarrendered a hamburger wired toui-sidebar-drawer, but that drawer was never mounted — the button silently did nothing. It's now mounted (page-scoped, sharing the showcase config), with a contract test (TestE2E_Sidebar_HamburgerOpensDrawer).
Pin a version (go get …@v0.6.1). Full notes: CHANGELOG.md.
v0.6.0 — blueprint is a generator, not a source of truth (BREAKING)
Reframes the blueprint as a generator, not a source of truth. gofastr generate now scaffolds owned Go in an idiomatic, module-root layout you read, edit, and commit — no quarantined gen/ directory and no // Code generated … DO NOT EDIT. header. Re-running the generator is add-only and never clobbers your edits.
BREAKING
-
gofastr generatescaffolds into the module root, notgen/. A blueprint scaffoldsmain.go+entities/+blueprint/at the module root, as owned Go with no generated header. Writes are conflict-skip: a re-run adds new files but never overwrites hand-edited code —--forceto overwrite. The blueprint is an on-ramp; once scaffolded the generated Go is the source of truth and the running app doesn't need thegofastr.yml.- Migration: pass
--out=gen(orapp.output_dir: gen) to keep the old layout; build/run withgo run .instead ofgo run ./gen.examples/ecommercescaffolds into an ownedapp/subpackage.
- Migration: pass
-
gofastr migrate diffremoved. It applied a blueprint directly onto a live database, treating the blueprint as authoritative over the running schema. Code generation and schema migration are separate concerns.- Migration: use
gofastr migrate generate <name>to emit a reviewable, versioned migration, thengofastr migrate up; additive columns also converge on boot viaAutoMigrate.migrate generatestill accepts--from=<blueprint.yml>as an opt-in schema source.
- Migration: use
Full notes: see CHANGELOG.md. Pin a version (go get …@v0.6.0).
v0.5.1 — readiness reporting + repository truth patch
Patch release with no breaking changes.\n\n- App.Start now reports readiness only after the listener binds.\n- Startup output includes the resolved listening address and correct API-prefixed entity URLs.\n- Bind failures no longer print a false readiness banner.\n- Security, roadmap, Make, coverage, and architecture status surfaces now match v0.5.x.\n- Obsolete worktree scripts were removed.\n- repolint now guards supported-version drift and retired build-script paths.\n\nVerification: make lint and SHORT=1 ./scripts/test-all.sh.
v0.5.0 — first-contact remediation (BREAKING auth)
The first-contact release: an adversarially-verified 10-dimension audit
(2026-06-09) found the engine strong but the first-touch surface broken —
the README quickstart failed verbatim, the flagship example shipped
insecure, RBAC was unreachable from the blueprint, and CI was red on every
release tag. Everything below closes those findings. Contains a BREAKING
auth change (see below); pin a version (go get …@v0.5.0).
Changed
- BREAKING:
battery/authfails closed on an emptyJWTSecretin
production. AnAuthManagerwithDevMode=falseand noJWTSecret
now makesInitreturn an error (the app refuses to boot) instead of
warning and continuing — an empty HMAC key yields forgeable,
restart-unstable JWTs. The error names the remedy (set
AuthConfig.JWTSecret, orDevMode: truefor local HTTP). The
blueprint path rejectsapp.authwithdev_mode: falseand no
jwt_secretatgofastr validate/generatetime, so a generated app
can't be built into the broken state. Migration: set
AuthConfig.JWTSecretfrom your secret store (most prod apps already
do);DevModeis unchanged and still mints a per-process secret.
Added
-
Blueprint
access:key — per-operation RBAC fromgofastr.yml. An
optionalaccess:map on an entity (read/create/update/
delete, each a permission string) threads through
EntityDeclaration.Accessinto the generatedregister.goas
framework.AccessControl{…}, where the existing CRUD chokepoint enforces
it fail-closed (403). Fully additive: blueprints without the key produce
byte-identical output. Closes the audit's "one leg of the secure-by-default
triad is unreachable from the primary declaration format". Also re-exports
framework.AccessDeclarationfor symmetry withEntityDeclaration. -
gofastr validate <blueprint.yml>. Parse + full blueprint validation
(including the module/go.mod coherence check, a render pass, and an
entity-name check that rejects names whose generated Go identifier would
not compile, e.g.2fa_tokens) without generating; exit 0/1 with
agent-friendly file:line + remedy diagnostics.--from,--config, and
--outongofastr generatenow also accept the space form
(--from x.yml), which previously silently did nothing. -
unscoped-piilint — CLAUDE.md hard rule #6 in the toolchain. Flags
any auto-exposed entity (CRUD default-on ormcp: true) with PII-shaped
fields and noowner_field/access/multi_tenant. Enabling
app.authalone does not suppress it — the session middleware is
pass-through for anonymous requests (an adversarial review proved the
original auth-suppression wrong by anonymously reading user emails from
a generated example app). Error fromgofastr validate, prominent
warning fromgofastr generate, finding ingofastr audit lint.
Running it against the repo's own examples found and fixed shipped
exposures inblog,lms,real-estate,portfolio, and
project-manager— each now demonstrates a scoping pattern (RBAC-gated
staff rosters, public-catalog reads with gated writes, lead-capture
forms with open create and gated reads). -
Executable-README CI gate.
cmd/gofastr/readme_quickstart_test.go
extracts the README's quickstart blocks and runs them for real: blueprint
→ generate → build → boot →GET /posts200; plus drift gates (no
"unpublished"/replace-directive guidance anywhere in the embedded docs,
README blueprint relations must resolve). The audit found this single
gate would have caught five of its eight confirmed findings. -
App.OnReady(func(addr string))— lifecycle hook that fires after
the listener has actually bound (and is skipped on start failure).
Generated apps now print their startup banner from it, so a migrate
failure can no longer print "Server starting" and then exit 1. -
Trust surface.
SECURITY.md(private vulnerability reporting,
honest v0.x support policy),CONTRIBUTING.md(truthful prereqs: Docker,
Chrome, the test-isolation rules), and low-ceremony issue templates. -
Docs:
comparison.mdandtutorial-blueprint-app.md. An honest
head-to-head (PocketBase / Encore.go / Wasp / hand-rolled Gin+sqlc,
weaknesses included) and the missing thesis tutorial (blueprint →
generate → secure with auth +owner_field+access:→ customize in
plain Go → deploy), every step executed end-to-end before shipping.
Plus a README forexamples/ecommerce. -
In-package Postgres coverage for
framework/crud. The SQL-generation
core had 66 sqlite-only test files; a focused testcontainers suite now
exercises the representative paths (filters, sort, offset+COUNT, cursor
keyset walk, batch rollback, upsert, eager-load include, soft-delete,
owner+tenant fail-closed) against real Postgres. -
First tests for
framework/owner(14): fail-closed verified at the
HTTP gate (anonymous → 401 with no extractor), last-call-wins override
warns, OwnerField-unset is inert, forged clientuser_idoverridden,
race-checked. Two layer-level fail-open contracts (ApplyOwnerScope,
InjectOwnerwithout an extractor) are pinned with tests and comments
documenting thatrequireScopeupstream is the actual boundary. -
Common-mistakes callouts completed and gated. The docs claimed every
topic ends with one; 21 of 60 didn't. Real, code-verified callouts added
to the 15 guide docs (including entity-declarations, "the heart of the
model"); 6 data/index docs exempted with reasons; the claim text now
matches reality andTestGuideDocsEndWithCommonMistakesenforces it. -
Security ledger fully re-verified. All 103 SECURITY_FINDINGS.md rows
re-checked against current code: 102fixed(each with the mitigation
cited AND a named pinning test run and observed passing), 1accepted
(#58, an intentional accepted-risk documented in code), 0 unverified or
open. A guard test pins header-count == row-count and valid status
tokens (fixed/open/needs-verification/accepted). -
Coverage floors are a CI gate.
scripts/coverage-floors.shfails the
blocking job if any claimed package drops ~2 points below its measured
coverage. COVERAGE_NOTES.md now separates own-package numbers from the
full-suite-overlay numbers the old 100% claims were quoting.
Fixed
-
SECURITY:
EntityConfig.MaxListLimitcould be bypassed on two list
paths. The cursor path never consulted the entity cap (asked for ≤3,
served up to 100), and an oversized?limiton the offset/stream path
silently fell back to the default page size (20) — exceeding any cap
below 20. Both were hidden behind the security tests' auto-skip-on-500
heuristic; converting those skips to hard failures (an audit
recommendation) exposed them immediately. Oversized?limitnow clamps
to the effective cap on every path (listLimitCapshared by offset,
stream, and cursor), with regression tests un-skipped and green. -
crud security tests fail instead of skip on server errors. The
skip-on-redacted-500 heuristic (and its "SQLite can't run $N
placeholders" rationale, which was false — the 500s were fixtures
missingTable) is gone across the suite. -
battery/queuetests are deterministic. Thirteen sleep-based
assertions replaced with Close-drain semantics, boundedwaitFor
polling, and an unexported clock seam for lease/visibility expiry —
stable under-race -count=3; no production behavior change. -
Doc staleness found while verifying the new callouts:
widgets.md
described awidget.Mountreturn value and bootstrap route that don't
exist (rewritten aroundMountRuntime/RuntimeTag);
ui-new-components.mdcited three nonexistent drift gates (corrected to
the real ones);ui-getting-started.mdhad a non-compiling
DBFromContextsnippet and listedAPIPrefixas roadmap (it shipped).
cursor-pagination.mdnow documents the per-entity cap. -
Boot-time auto-migrate now adds missing columns.
AutoMigratedid
CREATE TABLE IF NOT EXISTSonly, while deploy.md claimed
"create tables, add columns" — adding a field to an existing entity
broke the next boot. It now reuses the existing schema-diff machinery
and applies the additive changes only (drops/renames/retypes stay
behindmigrate diff's destructive gate); required new columns are
added nullable, column adds run before index DDL, and a racing replica
re-reads live columns on the lock-holding transaction and no-ops.
Also fixes Postgres live-schema readers to case-fold unquoted table
names (mixed-case entities were mis-reported as missing every boot). -
Light color scheme now passes WCAG AA — and the axe gate can no
longer be platform-blind. The "Linux-only" CI axe failures were real:
Linux Chrome defaults toprefers-color-scheme: light, so CI audited
the light palette that Dark-mode dev Macs never did. Light primary/
accent/code-comment tokens retoned (worst offender was 1.71:1, now all
≥4.6:1), the framework'sDefaultThemestatus tones darkened so any
light-theme Badge/Tag chip passes AA, thegofastr theme initscaffold
updated to match, andTestAxe_AllPagesAreCleannow scans every page
under BOTH forced schemes. The browser-e2e CI job is blocking again. -
Generated auth honors
dev_modeand validates it strictly. The
generator hardcodedDevMode: true; the blueprint key now works, with
a deliberate default oftrue(production cookie posture —
__Host-session+Secure— never round-trips on the plain HTTP a
fresh app serves) announced in the generated code, thegofastr generateoutput, and the docs.dev_mode: yesis a hard error, not a
silent coercion to prod mode.auth.CSRFis deliberately not mounted
(it would 403 the JSON/MCP surface); the gap and the
SameSite=Strictmitigation are documented. -
**Docs-site copy drift caught by visual review.*...
v0.4.0 — blueprint-only declarations + ecommerce flagship (BREAKING)
The blueprint becomes GoFastr's single declaration format: the legacy
entities/*.json path is removed (BREAKING), and a declaration-driven
flagship (examples/ecommerce) proves one gofastr.yml → SQL + REST + OpenAPI
- MCP + UI end to end.
Added
- Declaration-driven flagship example —
examples/ecommerce. A complete
storefront (five related entities, screens, nav, custom endpoints, seed data,
and a theme) declared once ingofastr.ymland emitted as runnable Go by
gofastr generate --from=gofastr.yml(the generatedgen/is gitignored).
flagship_test.goregenerates, builds, and runs it to prove every surface —
SQL schema, REST CRUD, OpenAPI, the 25-tool MCP surface, and the
server-rendered UI — is live with zero hand-written application code. See
examples/ecommerce/BUILD_JOURNAL.md.
Fixed
gofastr generatenow gofmt's its generated Go. Blueprint output is run
throughgo/formatbefore being written, so the emitted package is clean and
stable across regenerations (no more spurious diffs on re-generate).gofastr generate --fromre-run no longer refuses to cleanmain.go. The
output-dir cleaner now ownsmain.go(the blueprint emitsgen/main.go), so
regenerating over an existinggen/succeeds instead of erroring with
"refusing to clean — contains unknown entry".
Removed
-
BREAKING: the legacy
entities/*.jsondeclaration format is gone. The
gofastr.ymlblueprint is now the single declaration format — it decodes into
the sameEntityDeclarationshape and additionally emitsmain.go, screens,
and stubs, so the JSON-file path was a strict subset. Removed:- Framework API:
App.EntityFromFile,App.EntitiesFromDir,
App.GroupEntitiesFromDir,framework.LoadEntityDeclaration,
framework.LoadEntityDeclarations. (TheEntityDeclaration/
FieldDeclarationtypes and.Config()remain — they are the in-memory
shape the blueprint loader decodes entities into.) - CLI:
gofastr generate entity <name>andgofastr new entity <name>(both
now print a removal notice and exit non-zero); the--entities=<dir>flag
ongofastr generate,gofastr migrate generate, andgofastr migrate diff. gofastr generateno longer defaults to "scanentities/and generate." It
requires--from=<blueprint.yml>(or agofastr.codegen.ymlextension
config). Auto-discovery ofgofastr.ymlis intentionally not done — that
filename is also thegofastr initisolation config.
Migration: declare entities in a
gofastr.ymlblueprint and run
gofastr generate --from=gofastr.yml, or declare them in Go via
app.Entity(name, framework.EntityConfig{…})(unchanged).gofastr migrate generate <name> --from=<blueprint.yml>andgofastr migrate diff --from=<blueprint.yml>replace the old--entities=<dir>form. The
gofastr.codegen.ymlextension protocol andcodegenpackage are unchanged.Follow-up (kiln is experimental):
kiln freezestill writes
entities/*.jsonas its own snapshot artifact; emitting agofastr.yml
blueprint directly is tracked for a later pass. - Framework API:
v0.3.3 — assessment backlog C
The four larger features held back from v0.3.2, each additive and
backward-compatible. The OAuth token store passed the mandatory dual-model
security audit (see AI_TEST_AUDIT.md).
Added
- Typed schemas for custom
entity.Endpoint. New optional
InputSchema/OutputSchema([]schema.Field) fields. When set, the OpenAPI
spec emits a typedrequestBody/200response and the generated MCP tool
advertises a typed input schema, instead of a shapeless{type:object}. A
single helper (openapi.EndpointInputSchema) feeds both the OpenAPI and MCP
paths. Endpoints with no schema render exactly as before. - OAuth2 token store + transparent refresh (
battery/auth). A new
OAuthTokenStoreinterface + AES-GCM-sealedSQLOAuthTokenStorepersists
{access, refresh, expiry}per(user, provider);RefreshOAuthToken/
ValidOAuthTokenrefresh transparently on/near expiry via the provider's
refresh grant (Google + GitHub). OAuth login now persists the refresh token
(previously discarded) when a store is wired. Opt-in — login is unchanged
with no store configured.EncryptionKeyis required (fails closed); the
userIDpassed to refresh/valid must be the authenticated principal. - Cron-expression scheduling in the queue Scheduler.
Scheduler.Cron(spec)
fires on a standard 5-field cron expression (plus@daily/@hourly/… shortcuts),
alongside the existingEvery(interval). Reusesframework/cron(now exposing
Parse/Schedule.Next) — no second cron parser. Interval schedules are unchanged. - Request context in i18n-rendering
framework/uicomponents.RepeaterConfig,
LightboxConfig,StepWizardConfig,PasswordInputConfiggain an optional
Ctxfield so their localizable strings resolve the request's locale instead
of always rendering the default. NilCtxpreserves today's behavior.
v0.3.2 — assessment backlog B
A developer-experience patch from the same whole-framework assessment that
drove v0.3.1 — twenty DX improvements and small features, all with tests and
shipped docs. No BREAKING changes; everything is additive.
Added
App.WithSeed(func(ctx) error)— register seed funcs that run AFTER
auto-migration (tables exist) and before the listener binds, fixing the
first-run "no such table" footgun.framework.DBFromContext(ctx)/WithDBContext+ an auto-wired
DBContextMiddleware— screens reach the app's*sql.DBfrom the request
context instead of a package-level global handle.access.GetRoles(ctx)(and theframework.GetRolesfacade) — the reader
half of the role-context seam, for role-based UI branching.PluginGetAs[T]— typed plugin lookup mirroring the existingGetAs[T].- Typed interactive effects —
Confirm/AfterText/AfterDisable/ScrollTo/
PushStatebuilders incore-ui/interactive, replacing hand-written
data-fui-*attribute strings. ListOptions.NestedFilters— in-processListAll/CountAllnow apply the
same?author.name=aliceEXISTS-subquery nested filters the HTTP path does.RedisQueue.Start(ctx, interval)— background reclaim ticker recovers jobs
stranded by a crashed worker, matchingDBQueue.Batterywrappers for cache/search/storage (NewBattery) with clean
lifecycle shutdown of background goroutines.gofastr harness creds add|list|delete— store credentials in the
encrypted credstore;gofastr --helpnow lists theharness/agentssubcommands.audit_log.tenant_id— a nullable column (idempotentADD COLUMN) stamped
from the request tenant, so multi-tenant audit trails are scopeable.
Changed
- The OpenAPI spec advertises
?fields=(projection) and?trashed=query
parameters so SDK generators and agents can see them. - Auto-CRUD registration pre-flights entity/screen path collisions with an
actionable diagnostic (names the entity, the colliding path, and the fixes)
instead of the opaque ServeMux/foods/llm.md conflictspanic. - Queue
Queue/Browsable/Replayableinterface assertions moved into source
files (fail at build, not test-link). - The
agents.mdsnippet validator now understands interface methods and
non-Newconstructors (e.g.embed.Open→Index), so it stops
false-flagging correct interface APIs while still catching fictional methods.
Documentation
- New
queue.mdandtestkit.mdreference pages;battery/embednow
shipsagents.go/agents.mdso semantic search is discoverable to agents. - Documented the typed list/get hooks (
OnBeforeList/OnAfterList/OnBeforeGet/
OnAfterGet) and a consolidated hook-skip matrix. - Security docstrings on the unscoped
softdelete.Restore/ForceDelete/WithTrashed
helpers; "Common mistakes" sections (form-module, api-versioning); deeper
observability docs;GetRoles/PluginGetAsdocs; and stale-claim fixes.
v0.3.1 — assessment backlog A
A correctness and developer-experience patch from a whole-framework
assessment. No BREAKING changes. Twenty fixes, all with regression tests;
the recurring theme was converting silent wrong answers into correct
behavior or loud errors.
Security
- Codegen and blueprints no longer drop
OwnerField.renderEntityRegistration
emitted every scope flag exceptOwnerField, and the blueprint YAML allow-list
rejectedowner_fieldoutright — so generated/blueprinted apps silently lost the
per-user row scoping the docs hard-warn about. Both paths now preserve it. - Streaming list can no longer bypass
AfterListredaction.?stream=true
skipped include resolution and theAfterListhook; anAfterListredactor would
have been silently bypassed, leaking the fields it exists to hide. An explicit
stream with?include=or a registeredAfterListhook is now refused with400;
an auto-stream (very largelimit) falls back to the buffered path so redaction
always runs. GOFASTR_HARNESS_MACHINE_KEYno longer silently downgrades. Only a raw 32-byte
value was accepted; a hex or base64 key failed the length check and fell through to
the default passphrase with no warning. The env var now decodes raw-32/hex-64/base64
and errors loudly on an unparseable or wrong-length value.- The OpenAPI spec advertises
401/403on RBAC-gated, batch, and SSE operations.
EntityConfig.Accessis folded into the gated flag and403is added alongside
401, so generated SDKs/agents see the real auth contract instead of treating
RBAC-gated routes (and_batch/_events) as public. - The
UpsertOneDO-NOTHING fallbackSELECTnow applies tenant/owner/soft-delete
scope (defense-in-depth;upsertPreflightalready guarded the row).
Fixed
updated_atis restamped on every UPDATE and bulk update. It previously froze
at its creation value because the field loop skips all auto-generate columns; cache
invalidation and change detection silently saw stale timestamps. Clients still
cannot forge it.ADD COLUMNfor a required field with no default no longer emitsNOT NULL.
That DDL fails on a populated table (Postgres and old SQLite); the column is now
added nullable with the deferral noted in the change summary (matches the kiln path).App.InTxjoins an ambient transaction already in the context (e.g. when called
from a CRUD hook) instead of silently opening a second independent transaction and
breaking atomicity.- DSL
after(cursor)is wired intoBuildDSLQuery. It was parsed and discarded,
so DSL pagination always returned page 1. Composite-cursor/unknown-field entities now
return a clear error instead of no-oping. - LiveSearch debounce works. The emitted attribute (
data-fui-rpc-debounce) did not
match what the runtime reads (…-ms); debounce was silently ignored. - Widget dismiss closes its
EventSources instead of leaking a live server SSE
connection on every modal open/close. - Signal ARIA is text-mode only.
role=status/aria-liveis no longer applied to
attribute- or html-mode signal nodes (invalid ARIA + live-region spam on island swaps). - Carousel timers and the toc
IntersectionObserverare torn down on SPA navigation
instead of leaking for the session. RedisQueueimplementsBrowsable(ListJobs/Statsover the dead-letter list),
so the admin queue page works on the most common non-DB production backend.- Scheduler enqueue failures log via
sloginstead offmt.Printf, surfacing
otherwise-invisible job loss to the log battery/observability. MemoryQueuehandler timeout is configurable viaWithHandlerTimeout(default
unchanged at 30s) so long jobs aren't silently cancelled and dead-lettered.- Per-page Open Graph/meta beats the global default. Per-screen SEO is emitted before
the sitewideWithOpenGraphtags, so first-match crawlers honour the page override. gofastr new entityandgenerate entityagree on table naming (singular
snake_case, matching the framework default) so migrations target one table.- Built-in harness profiles are embedded (
go:embed, on-disk-wins fallback), so
gofastr harness --frameworkworks for an installed binary outside the source tree.
Documentation
- Corrected the
access.Policyinterface in the docs from a non-existent 3-arg
Can(ctx, permission, resource)to the real 2-argCan(ctx, permission)(custom
policies following the docs failed to compile), and documented that per-record
decisions are made viaOwnerFieldscoping orBefore*hooks. A compile-time
assertion now pins the doc to the interface. The Go interface is unchanged. - Documented the streaming/
AfterListexclusivity,App.InTxambient-tx joining,
theADD COLUMNNOT NULLdeferral, and corrected the staleupdated_athook
comment inmigrate.go.