Application foundation ¶
The application foundation covers DEV-01, DEV-02, DEV-03, DEV-05, DEV-06 and DEV-07: project/app scaffolding, settings, registration and lifecycle, routing, management commands, checks and local rebuild/restart. Its public APIs remain experimental. The separate reusable-package battery (DEV-04), database inspection/shell tools (DEV-08), and independent release validation retain their own acceptance criteria in the inventory.
Projects and upgrades ¶
cicadel startproject DIRECTORY example.com/project creates an ordinary Go module,
manage.go, typed project settings, explicit project/apps.go, root routes,
compiled components, embedded CSS and request tests. startapp NAME [DIRECTORY]
creates model, view, route, migration and test files. Names and module paths are
validated before writing. Existing destinations are never intentionally replaced.
startpackage is the independently distributable-app scaffold.
Keep models in apps/notes/models.go, split them into multiple files in that Go
package, or move them to apps/notes/models/ with package models and import that
package explicitly. Do not keep duplicate model declarations in both layouts.
There is no runtime discovery. Register owned models with ModelOf in the app’s Models() list. makemigrations and generatequeries consume that same registry. Handwritten SQL also belongs to recorded history with reviewed historical metadata.
Neither startup nor reload calls AutoMigrate or applies migrations.
For upgrades, pin the intended framework version with go get, run go mod tidy,
read release notes, and generate a reference project in a new directory. Compare
it with your project and merge deliberate changes. Generation does not rewrite
existing settings, models, components or migration history. Run the matching
go tool cicadel ui build, ui check --json and project tests before deployment.
See the upgrade policy and model migrations.
Configuration and registration ¶
Settings precedence is defaults, typed project assignments, then explicit
environment overrides. Every operational field has a documented override in
operations. Invalid values fail without printing their contents.
settings.LoadWith accepts an injected lookup for tests; it never changes the
process environment. Loaded and constructed applications own their allowed-host
slices. Settings formatting and structured logging redact DatabaseURL; directly
logging that field or serializing arbitrary application secrets bypasses this
protection.
Package settings are ordinary typed constructor options, passed explicitly from
project/apps.go. Validate them in the constructor or a registered check; do not
introduce a shared untyped registry or mutate another app’s settings. A new factory
call must allocate fresh lifecycle state for each test or application.
App descriptors are copied at construction. Registration rejects duplicate labels, missing dependencies, cycles, incompatible manifest versions and contribution collisions. Dependency ordering is stable. Startup runs checks before acquiring resources. Successful hooks and a failing hook’s returned cleanup run in reverse order on failure. Successful startup is idempotent; closed applications cannot restart. Close follows HTTP drain and attempts every cleanup even when one fails. Hooks must honor context deadlines and must not recursively call Start or Close.
Routes use typed urls.Route values and standard HTTP handlers. urls.Include
applies namespaces and prefixes; reversal validates parameters. App middleware
wraps only that app’s routes, in declaration order. See the application API
and installable lifecycle contracts.
Management commands ¶
go run manage.go help lists built-in, project and namespaced app commands in
sorted order. help COMMAND or COMMAND --help shows its description without
running checks, startup hooks or the command. Add descriptions through
App.CommandHelp or Manager.CommandHelp; descriptions must reference registered
commands. Project names are lowercase identifiers with optional hyphens and
underscores; app commands remain app:command. Collisions are errors.
Runtime commands execute after checks/startup and receive cleanup on success or
failure. Explicit App.BuildCommands run without database validation or runtime
hooks. Constructors and the generated project’s Build function still run before
Manager.Run; a project that opens a database during construction needs its
configuration even for help. Offline tools should use a separate construction
path when that distinction matters.
Custom commands receive an unmodified argument slice and context. Use Go’s
flag.NewFlagSet(name, flag.ContinueOnError), choose an explicit output writer,
and return management.UsageError{Err: err} for invalid flags or arguments.
Handle flag.ErrHelp as success. Return ordinary errors for execution failures.
Do not call os.Exit inside commands, because it bypasses lifecycle cleanup.
management.ExitCode(err) defines 0 for success/help, 2 for usage errors, 130 for
context cancellation, and 1 for execution/configuration failure. Generated
manage.go and the framework CLI use it. Existing entry points can adopt the same
helper. The exit status of go run itself is Go’s launcher status; build an
executable to observe the application’s exact status directly.
Structured system and deployment checks ¶
Legacy App.Checks continue to return errors. New App.SystemChecks register
SystemCheck{Deployment: bool, Run: func(context.Context) []CheckMessage}. Each
message has a local ID, severity (info, warning, error), concise Message and
optional Hint. IDs must be lowercase contribution identifiers and unique within
one check invocation. The framework qualifies them as app:check:id and rejects
invalid results. Diagnostics are ordered by app dependencies, sorted check names,
and the check’s returned message order. Keep messages and hints free of secrets.
Ordinary startup fails on error diagnostics; warnings and information do not block startup. Deployment-only checks run only on explicit request. Checks must be read-only, must honor cancellation, and must not start background services.
go run manage.go check
go run manage.go check --deploy --fail-level warning
go run manage.go check --deploy --json--deploy additionally checks debug mode, secure cookies and explicit allowed
hosts, and runs package deployment checks. --json emits a diagnostic array,
including on check failure. --fail-level selects the minimum failing severity;
it defaults to error. Database provider requirements are included in management
checks. These checks cannot certify a reverse proxy, external TLS, backups or
production infrastructure; those remain deployment responsibilities.
Development rebuild and restart ¶
Install the matching CLI and the pinned CSS compiler once, then run:
go tool cicadel ui css install
CICADEL_DEBUG=true CICADEL_SECURE_COOKIES=false go tool cicadel devdev --dir DIRECTORY --interval 300ms --shutdown-timeout 10s watches a project
containing go.mod and manage.go. It compiles components and configured CSS, builds
an executable outside the project, and runs it with runserver. The same project
configuration and environment apply to every generation. It does not install
build tools implicitly. Applications using another CSS system retain their own
build process when no ui.css.json is configured.
Content hashes detect creation, deletion and same-size changes, including embedded assets. Two stable observations debounce edits. A source change during a successful build causes another build before that binary is served. A failed build reports compiler/source diagnostics and leaves the working server alive. Fixing the source triggers a new build. The old server receives an interrupt only after a replacement build succeeds; it is reaped before the replacement starts. Shutdown escalates to kill after the configured timeout. Interrupt and SIGTERM stop the supervisor and its child. A server that exits, including on a port conflict, returns an error with its output instead of repeatedly spawning failing processes.
Hidden files/directories, bin, dist, vendor, node_modules and nested Go modules are
excluded. The project root is resolved to its real path; symlinks within the
source tree are rejected with a diagnostic. Put generated runtime logs and
artifacts in an excluded output directory. Changes to another module or environment
require restarting the supervisor. This is an executable restart workflow; it does
not preserve process memory, inject browser refresh scripts or provide in-process
hot replacement. Production remains go build and ordinary process supervision.
Acceptance evidence ¶
settings: every operational override, malformed-value secrecy, copied host configuration, redacted formatted/structured diagnostics and fuzz boundaries.- Root application tests: route composition, dependency ordering, descriptor isolation, partial-start cleanup, cancellation, readiness, HTTP shutdown and structured check validation/deployment selection.
management: command collisions, offline/runtime lifecycle, deterministic help, check flags, severity thresholds, usage status and cancellation status.internal/devserver: real executable rebuild, continued HTTP service after a compiler failure, recovery after fixing source, content-hash boundaries and forced shutdown/reaping of an unresponsive child.internal/scaffold: generated project/app tests and an outside-source-tree executable exercise help, JSON checks, deployment rejection and exact exit codes; existing generated-consumer tests verify component and package integration.
The applicable repository workflows verify generated files, PostgreSQL integration, race checks, vet, fuzz targets, vulnerability scans and builds, including nested modules. External security review and beta adoption are not claimed by these tests.
Local verification on 2026-11-02 used Go 1.26.10 and PostgreSQL 18.4. The required
commands from ci.yml, accounts.yml, formsets.yml and
migration-installables.yml passed, including strict PostgreSQL/race coverage,
all eight configured fuzz runs, generated-source checks, nested-module vet and
vulnerability scans. Release CLI builds passed for Linux and macOS on amd64 and
arm64. The root suite was rerun after the final watcher fixes. These results cover
local checks; they do not claim an external review or a published release.