Commit Graph

734 Commits

Author SHA1 Message Date
Jakub Zych
6c1f94e57c feat(11-01): add lagoon.OnDatabase and after-commit transactions
- OnDatabase runs a callback once the database is published (now, or when
  lagoon.Publish runs), so GORM callbacks registered at Boot also install
  under serve, where Boot runs before the database is opened
- Transaction runs AfterCommit callbacks in order after a successful commit;
  nested calls are savepoints whose callbacks drop with them
- the lagoon:after_commit GORM callback flushes single-statement AfterCommit
  work after GORM's own commit; outside a transaction it runs immediately
2026-09-29 15:25:51 +02:00
Jakub Zych
b319e7cc61 feat(11-01): run job workers in serve and queue:work, add queue:clear
- Manager gains the apparatus JobManager surface: StartJob, UpdateJobState,
  UpdateMetadata, FailJob, CancelJob (is_canceled + STOPPED + River JobCancel),
  StopJob (STOPPED only), CheckIfCanceled and GetMetadata, all raw column
  writes so updated_at is untouched
- serve starts the in-process worker unless queue.work_in_serve is false and
  stops it on shutdown; an app without jobs gets an idle worker
- queue:work runs a foreground worker with repeatable --queue filters;
  queue:clear deletes available, scheduled and retryable jobs of one queue
- the generated main appends conga.RuntimeCommands; summer delegates
  queue:work and queue:clear; make:job scaffolds a conga.Job
2026-09-29 15:20:14 +02:00
Jakub Zych
05ba88a54a fix(11-01): restore the toolchain line that go mod tidy dropped
The scaffolder's ensureToolchain keeps 'toolchain go1.27.0' in every
module; the River tidy in the previous commit removed it from the example
app and its plugins.
2026-09-29 15:10:19 +02:00
Jakub Zych
0bc5c77097 feat(11-01): add the conga job framework on River with transactional dispatch
- Pin River v0.47.0 (riverdatabasesql, rivertype) and tidy the example modules
- lagoon.Migrate runs the summercms.conga set: River schema v7 and summer_jobs
  with the apparatus columns plus an internal river_job_id link
- conga.Manager.Dispatch writes the summer_jobs row (status IN_PROGRESS) and the
  River job on the caller's *sql.Tx; a rollback leaves neither
- conga.Job wraps typed job functions so plugins never import River
- conga.StartWorker runs one client on riverdatabasesql.NewWithPgxListener with
  a single-connection LISTEN pool; the final failed attempt sets ERROR
- TestListenPickupLatency: 30s poll, pickup under 1s; poll-only control 2s miss
2026-09-29 15:02:04 +02:00
Jakub Zych
718a35caba docs(11): create phase plan 2026-09-29 14:34:06 +02:00
Jakub Zych
d9b951fe29 docs(11): map phase patterns 2026-09-29 13:40:53 +02:00
Jakub Zych
a1ed5b3539 docs(11): reword SC-1 for River's single-client pgx listener split 2026-09-29 13:38:46 +02:00
Jakub Zych
9dabc6c5a1 docs(phase-11): add validation strategy 2026-09-29 12:32:56 +02:00
Jakub Zych
c82950c2a6 docs(11): research phase domain 2026-09-29 12:32:02 +02:00
Jakub Zych
0bbbce6d27 docs(10.1): record code review disposition 2026-09-29 10:10:45 +02:00
Jakub Zych
c0d16eccc1 docs(10.1): add code review fix report 2026-09-29 10:10:44 +02:00
Jakub Zych
719ed719b4 fix(10.1): WR-06 honour the widget field's context on the action route
widgetAction derives the form from the request (create without record_id,
update with one) and answers 404 when the field's context hides the
widget on that form, reusing contextAllows as the save path does. An
update-only action can no longer run with a nil record through a direct
POST.
2026-09-29 10:00:08 +02:00
Jakub Zych
7b72bf4be4 fix(10.1): WR-05 drop widgets the admin may not run from the form schema
formSchema now filters type: widget fields by the action's permissions,
the same D-12 filtering listSchema applies to toolbarActions, so an admin
without the action permission no longer gets a button that always
answers 403, and the action name is not revealed. The filtered fields are
a new slice, so the cached schema is never modified.
2026-09-29 09:58:27 +02:00
Jakub Zych
0bdb6ebcac fix(10.1): WR-04 judge action fill values by their JSON encoding
isJSONScalar now encodes each value and keeps it only when the encoding
is a string, number, boolean or null, so a named scalar whose MarshalJSON
writes an array or object, NaN and the infinities are dropped. writeJSON
encodes into a buffer before the status line, so an encode failure is a
logged 500 with the generic envelope instead of a 200 with a truncated
body.
2026-09-29 09:56:44 +02:00
Jakub Zych
7333f450ad fix(10.1): WR-03 walk the whole partial view model before rendering
refusedViewModel compared only the top-level type with the controller's
model. It now walks the type through pointers, slices, arrays, maps,
struct fields and the results of exported methods, and the values held
in interface-typed members, refusing the controller's model, any other
GORM model (TableName, a gorm tag, gorm.Model, gorm.DeletedAt) and
html/template's trusted content types anywhere in that structure.
2026-09-29 09:54:35 +02:00
Jakub Zych
5bbb0ada05 fix(10.1): WR-01 keep a late schema response from restyling the current view
loadControllerAssets no longer activates stylesheets. activateStyles,
called only by the router, records the controller on screen, and new
links are created disabled unless they belong to it, so a list or form
schema that resolves after its view was left adds its links disabled
instead of switching plugin CSS to the wrong controller. Rebuilt the
embedded admin dist.
2026-09-29 09:50:05 +02:00
Jakub Zych
849a9ffe3f fix(10.1): WR-02 disable plugin stylesheets on screens that are not controller views
The router now drives stylesheet activation after every confirmed
navigation: the list, create and record routes enable their controller's
links, and settings, login, not-found and a controller whose schema has
not arrived yet enable none. Rebuilt the embedded admin dist.
2026-09-29 09:47:52 +02:00
Jakub Zych
e60e69745e fix(10.1): CR-01 answer a value that does not fit its column with a 422
lagoon.Fill now returns a *lagoon.FillTypeError naming the key when a
requested value cannot be stored in its column (a fraction, exponent or
overflow for an integer field, or a value of the wrong type). The admin
save path maps it to a validation_failed 422 on that field instead of a
500 CapabilityError; genuine capability failures keep the 500.
2026-09-29 09:44:14 +02:00
Jakub Zych
74e30c9878 test(10.1): persist human verification items as UAT 2026-09-29 03:37:38 +02:00
Jakub Zych
29433f3c40 docs(10.1): record code review disposition 2026-09-29 03:29:52 +02:00
Jakub Zych
9f6b5a5706 docs(10.1): add code review report 2026-09-29 03:29:41 +02:00
Jakub Zych
613491f674 docs(10.1-04): record plan progress, decisions and ADMIN-07 in state 2026-09-29 03:15:16 +02:00
Jakub Zych
0ddf7f8f68 docs(10.1-04): complete unit tests, gate and security evidence plan 2026-09-29 03:14:47 +02:00
Jakub Zych
bbceeb957f docs(10.1-04): record the Phase 10.1 security review and validation map
- 10.1-SECURITY-REVIEW.md: T-10.1-01 to T-10.1-22 and T-10.1-SC with
  mitigation, test or gate stage, observed result and 23 removal checks
- 10.1-VALIDATION.md: every plan task mapped to its command, all green
  under check-phase10.1.sh --all; nyquist_compliant and wave 0 complete
- Phase 10 deferred item for the parity failures marked resolved
2026-09-29 03:13:18 +02:00
Jakub Zych
02df0a8a15 feat(10.1-04): add the fail-closed Phase 10.1 gate
scripts/check-phase10.1.sh: --self-test, --go, --security, --postgres,
--spa, --openapi, --dist, --hygiene, --evidence and --all.

- phase101_detect refuses failed, skipped, zero-test, non-JSON and
  build-failed runs and required tests that did not pass
- hygiene_101 refuses HTML-string parsers in admin/src, network, cookie
  or storage access in application plugin asset JS, and script, style or
  inline handler markup in application partial templates; each rule is
  proven by its own self-test plant
- --evidence requires a review row and, for every high threat, a named
  test and a removal check row
2026-09-29 03:13:08 +02:00
Jakub Zych
9aeb0e156b fix(10.1-04): drop the stale parity allow-list from the Phase 10 gate
TestMigrateSeedsCanonicalGenres and TestSchemaMatchesPHPSnapshot pass
since fonoteka.go 21c0f12, and the detector refuses an allow-listed
failure that passes, so check-phase10.sh --go failed. The gate now
allow-lists nothing.
2026-09-29 03:13:08 +02:00
Jakub Zych
11c4e5466b test(10.1-04): bring the SPA extension point under Vitest
- WidgetField: skeleton and aria-busy, the 5000 ms whenDefined timeout,
  script failure, attributes only, fill-values and locale sync, one POST
  while busy, fill-key-only patching, danger toasts, unmount
- PartialHost and partialNodes: exhaustive tag, attribute and URL
  allowlist, depth and node caps, skeleton sizes, empty, failure,
  busy refetch and ?id= only with a record
- PartialField, ListToolbar, ListView, registry, formState, FormField and
  FormView: group labels, registered toolbar actions, header refetch
  rules, form context provision and asset loading
2026-09-29 02:59:13 +02:00
Jakub Zych
6b0ac15086 fix(10.1-04): refuse percent-encoded dot segments in plugin asset URLs
The URL parser resolves %2e%2e like .., so /{base}/assets/%2e%2e/api/...
passed assetAllowed and would load from outside the asset prefix. A
segment is now a dot segment after decoding %2e, in any case.

- tests/app/pluginAssets.test.ts covers the URL check, loadScript,
  loadStyles, activateStyles and loadControllerAssets
- modules/boardwalk/dist rebuilt
2026-09-29 02:52:14 +02:00
Jakub Zych
7eed4acd87 test(10.1-04): cover the Phase 10.1 extension point Go code
- acme fixture plugin under modules/cabana/testdata/extension (gadgets
  controller, header and form partials, lookup widget, JS and CSS)
- TestPhase101FormExtensionSchema, TestPhase101PartialSchema and
  TestPhase101Toolbar: every widget, partial and toolbar boot rule
- TestPhase101PartialSanitizer: tag, attribute and URL allowlist, escaping,
  per-request trans, size/node/depth caps and the view-model guard
- TestPhase101Assets: exact-key asset hits, revalidation, SPA fall-through,
  boot path checks and ?v= schema URLs
- TestPhase101Actions (PostgreSQL): scoping, fill filter, strict body,
  action permission, error mapping, CSRF header, toolbar and partial routes
- TestPhase101BoardwalkExports: ContentType and SetSecurityHeaders
2026-09-29 02:48:12 +02:00
Jakub Zych
c3c547c394 docs(10.1-03): record plan decisions in state 2026-09-29 02:29:12 +02:00
Jakub Zych
0f6c10c4e3 docs(10.1-03): record plan progress in state and roadmap 2026-09-29 02:28:56 +02:00
Jakub Zych
29336a58ce docs(10.1-03): complete Albums extension proof plan 2026-09-29 02:28:40 +02:00
Jakub Zych
c3efbc3428 fix(10.1-03): fill numeric model fields from JSON numbers
- lagoon.Fill converts a json.Number (from a UseNumber decoder, as cabana's
  save path uses) into integer, unsigned and float fields; a fraction or an
  overflow is an error
- before this, saving a type: number field into an *int column was a 500
- README documents the conversion
2026-09-29 02:24:25 +02:00
Jakub Zych
9d23ac5832 docs(11.2): capture phase context
Four repos (sm-summercms-app, vue-summercms-app, sm-summercms-plugin,
sm-newsletter-plugin), Nuxt 4 static site in EN and PL embedded in the
binary, double opt-in signup with honeypot, consent and neutral
responses, and a standalone sending-ready subscribers table.
2026-09-29 02:24:10 +02:00
Jakub Zych
720ee5796b docs(10.1-02): record plan progress in state and roadmap 2026-09-29 02:15:44 +02:00
Jakub Zych
55c47ca3bc docs(10.1-02): complete SPA extension seams plan 2026-09-29 02:15:15 +02:00
Jakub Zych
484eeb989b docs(roadmap): insert phase 11.2 ready-to-share website and newsletter plugin
Adds Phase 11.2 after 11.1: the sm-summercms-app root with a
vue-summercms-app website, an sm-newsletter-plugin stub ported from
Golem15.Newsletter with double opt-in signup, and the 11.1 docs served
at /docs. Records the sm- repo naming convention.
2026-09-29 02:14:35 +02:00
Jakub Zych
a5e7dac10f feat(10.1-02): run registered toolbar actions and scope plugin CSS per controller
- ListToolbar renders server-filtered actions after delete as outline buttons, busy during their POST
- ListView posts {} to toolbar/{action}, toasts, reloads the list and refetches the header partial
- Lists load controller assets too, so other controllers' stylesheet links are disabled on every open
- Vite dev server proxies {prefix}/assets to summer serve; dist rebuilt
2026-09-29 02:13:26 +02:00
Jakub Zych
9df9fae930 feat(10.1-02): render header and form partials through an allowlisted node renderer
- partialNodes rebuilds the server node tree with h() under the server's tag, attribute and URL lists
- PartialHost owns the skeleton, empty and failure states and keeps nodes visible on refetch
- type: partial is a valueless group-labelled field rendered on create and update
- ListView shows headerPartial above the list card and refetches it after bulk delete
- summer-partial and summer-stats style kit in main.css, documented in the cabana README; dist rebuilt
2026-09-29 02:10:18 +02:00
Jakub Zych
107d820109 feat(10.1-02): mount plugin widget elements and run their actions from the form
- pluginAssets loads controller scripts and stylesheets from {base}/assets/ only, once per URL
- WidgetField mounts the custom element with attributes only and posts summer-action through the typed client
- Only declared fill keys returned by the server are patched; the form turns dirty and nothing saves
- widget is a registered valueless type rendered on create and update, labelled as a group
- backend::lang.extension strings in en and pl; embedded dist rebuilt
2026-09-29 02:04:34 +02:00
Jakub Zych
c3b76b1afb docs(10.1-01): record plan progress in state and roadmap 2026-09-28 23:54:29 +02:00
Jakub Zych
2325d80ecb docs(10.1-01): complete framework Go extension point plan 2026-09-28 23:54:08 +02:00
Jakub Zych
771d2ccce0 feat(10.1-01): render header and form partials into an allowlisted node tree
- fields.yaml type: partial with a bare path name and config_list.yaml
  headerPartial resolve to {ConfigDir}/_{name}.htm, parsed at boot; the
  controller must implement pact.AdminPartialData
- html/template render against a curated view model, then x/net/html
  ParseFragment and a tag, attribute and URL allowlist with 64 KiB, 2000-node
  and depth-32 caps; the model type and trusted template types are refused
- GET .../partials/{name} with optional ?id= loaded through the form scope
- golang.org/x/net becomes a direct requirement (D-18), no new module
2026-09-28 23:52:50 +02:00
Jakub Zych
8b1cb244de feat(10.1-01): serve controller JS/CSS and run registered toolbar actions
- boardwalk exports ContentType and SetSecurityHeaders
- pact.AdminClientAssets files are read and hashed at boot and served by exact
  key under {prefix}/assets/{vendor}/{plugin}/ with nosniff, CSP, CORP,
  no-cache and an ETag; a miss falls through to the SPA
- list and form schemas carry assets URLs with a ?v= hash
- toolbar.buttons resolves create, delete and registered actions after decode;
  toolbarActions is permission-filtered per admin
- POST .../toolbar/{action} behind requireAjax and action permissions
2026-09-28 23:41:17 +02:00
Jakub Zych
f9281949a6 feat(10.1-01): run registered widget actions through a cabana-owned route
- pact: AdminClientAssets, AdminAction, AdminActionInput, AdminActionResult,
  HasAdminActions and AdminPartialData contracts
- fields.yaml type: widget with widget, action and fill keys; boot checks the
  plugin tag prefix, the registered action and writable scalar fill fields
- POST .../widgets/{field} behind requireAjax, controller and action
  permissions, scoped non-locking record read and a server-side fill filter
- typed OpenAPI operation, inventories and an acme conformance case
2026-09-28 23:35:00 +02:00
Jakub Zych
9b98d8409f docs(10.1): record D-18/D-19, resolve research questions, add pattern map
Plan checker iteration 1 flagged unresolved research questions and a
missing decision note for golang.org/x/net/html. D-18 approves x/net/html
for the partial sanitizer; D-19 fixes the Discogs widget fill to
[year, format]. STATE marks the phase ready to execute.
2026-09-28 22:44:34 +02:00
Jakub Zych
ccdc014078 docs(10.1): create phase plans for the runtime admin extension point
Four plans: framework Go contracts and routes, framework SPA hosts,
Albums proof in fonoteka.go, and unit tests with the phase gate.
Adds ADMIN-07 to REQUIREMENTS.md and fills the Phase 10.1 roadmap goal,
success criteria and plan list.
2026-09-28 22:25:50 +02:00
Jakub Zych
04c587a5a3 docs(11.1): UI design contract 2026-09-28 22:25:34 +02:00
Jakub Zych
bf61acada1 docs(11.1): insert documentation phase with context and validation strategy 2026-09-28 20:22:59 +02:00
Jakub Zych
09c1ade9a9 docs(11.1): research documentation phase domain 2026-09-28 18:20:42 +02:00