18 KiB
phase, plan, subsystem, tags, requires, provides, affects, actuals, plan_head_before, plan_head_after, tech-stack, key-files, key-decisions, patterns-established, requirements-completed, coverage, duration, completed, status
| phase | plan | subsystem | tags | requires | provides | affects | actuals | plan_head_before | plan_head_after | tech-stack | key-files | key-decisions | patterns-established | requirements-completed | coverage | duration | completed | status | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 11-jobs-realtime-and-search-infrastructure | 01 | infra |
|
|
|
|
|
718a35caba |
6c1f94e |
|
|
|
|
|
|
37min | 2026-09-29 | complete |
Phase 11 Plan 01: conga job framework on River Summary
River v0.47.0 on the shared pool via riverdatabasesql.NewWithPgxListener (3-5 ms LISTEN pickup against a 30 s poll), transactional conga.Manager.Dispatch into a PHP-shaped summer_jobs record, workers in serve and queue:work, queue:clear, plus the lagoon.OnDatabase and after-commit seams.
Performance
- Duration: 37 min
- Started: 2026-09-29T12:48:56Z
- Completed: 2026-09-29T13:26:12Z
- Tasks: 3
- Files modified: 50 (38 in summercms.go, 12 in fonoteka.go)
Accomplishments
lagoon.Migratenow runs thesummercms.congaset: River's schema pinned at version 7 andsummer_jobswith the exact apparatus columns plus the internalriver_job_id BIGINT.congapackage:Dispatchwrites the row with status 1 (IN_PROGRESS, as PHP does) and enqueues on the same*sql.Tx. The full JobManager surface keeps PHP semantics: raw column updates, CancelJob vs StopJob split, and the final-attempt ERROR rule with panic recovery.- One River client type. Workers use
NewWithPgxListenerwith aMaxConns 1 / MinConns 0listener pool; the timed test proves LISTEN pickup (about 5 ms) and a poll-only control proves the test is not measuring poll latency. servestarts the worker unlessqueue.work_in_serve: false.queue:work --queue ...andqueue:clear [queue]exist in the app binary and assummerdelegates.make:jobscaffolds aconga.Job.lagoon.OnDatabasecloses the Boot-order gap: fonoteka's slug, artist-resolver and credential-encryption callbacks now register underserve.lagoon.Transaction/lagoon.AfterCommitand thelagoon:after_commitcallback are ready for plan 11-05.
Task Commits
summercms.go:
- Task 1: transactional dispatch picked up through LISTEN -
0bc5c77(feat); toolchain-line restore05ba88a(fix) - Task 2: outcomes, cancellation, serve/queue:work/queue:clear -
b319e7c(feat) - Task 3: OnDatabase and after-commit transactions -
6c1f94e(feat)
fonoteka.go:
- Task 1 -
3d4895d(chore: tidy + allow-lists); toolchain-line restore0b9a2a5(fix) - Task 2 -
e0b1b0d(feat: regenerated main.go, config/queue.yaml) - Task 3 -
36fc473(fix: Boot registers hooks through lagoon.OnDatabase)
Files Created/Modified
modules/lagoon/queue_migrations.go:QueueMigrations,QueueHistoryID,JobsTable,RiverSchemaVersionmodules/lagoon/ondatabase.go:OnDatabaseplus the per-app hook queue drained byPublishmodules/lagoon/transaction.go:Transaction,AfterCommit,AfterCommitCallback(lagoon:after_commit)modules/lagoon/connection.go:Publishdrains the hooks;gormFromSQLinstalls the after-commit callbackmodules/conga/*.go: Manager, Record/Status, Job adapter, client config, worker, commandsmodules/surf/serve.go: in-process worker start/stopinternal/build/build.go,internal/build/stubs/artifacts.tmpl,internal/build/artifact.go: generated main registersconga.RuntimeCommands; themake:jobstub is aconga.Jobcmd/summer/main.go,cmd/summer/runtime.go:queue:work(forwards repeatable--queue) andqueue:cleardelegates- READMEs: new
modules/conga/README.md, root modules table row,modules/lagoon/README.md,modules/surf/README.md - fonoteka.go:
config/queue.yaml, regeneratedmain.go,plugin.goBoot fix, parity allow-lists, go.mod/go.sum tidy
Decisions Made
See key-decisions in the frontmatter. In short:
- Registration closes only while a worker runs.
- Worker lifetime is tied to
Stop, not the start ctx. - An app without jobs gets an idle worker.
OnDatabasetreats a published*gorm.DBas ready.- The River v7 table set was read from
pg_tables.
TDD Gate Compliance
Tasks 2 and 3 are tdd="true". For each, the RED run was observed and recorded before the implementation. The tests compiled against stubs that returned "not implemented", so they failed on their assertions. gsd-tools check tdd-red-evidence returned RED_EVIDENCE_OK for:
TestJobManagerOperations,TestAttemptOutcomes,TestCancelJob,TestQueueClear,TestQueueWorkCommandandTestStartServeWorker(Task 2)TestOnDatabaseAfterActivateandTestTransactionAfterCommit(Task 3)
TestHooksRegisterWhenDatabasePublishedAfterBoot failed against the HEAD plugin.go and passed with the fix.
Gate violation, flagged: there are no separate test(11-01) RED commits. Tests and implementation landed together in feat/fix commits because the project CLAUDE.md requires go vet and go test ./... to be green at every commit. The CLAUDE.md rule was treated as the higher-priority constraint.
Deviations from Plan
Auto-fixed Issues
1. [Rule 3 - Blocking] River cannot start a client with no workers
- Found during: Task 2 (TestStartServeWorker, TestQueueWorkCommand)
- Issue:
river.NewClientfails with "at least one Worker must be added" for an app without jobs, soserveandqueue:workwould fail. The plan's flagged assumption (c) says they must start and idle. - Fix: When no job is registered,
StartWorkeradds an internalsummercms_conga_idleworker that nothing inserts. - Files modified: modules/conga/worker.go
- Committed in:
b319e7c
2. [Rule 1 - Bug] go mod tidy dropped the toolchain go1.27.0 line
- Found during: Task 2 (reading
ensureToolchainin internal/build) - Issue: The scaffolder keeps
toolchain go1.27.0in every module. The Task 1 tidy removed it fromexamples/hello(app and three plugins) and fromfonoteka.go/go.mod. The same tidy also picked up pre-existing indirect-requirement drift in the example plugin modules. - Fix: Restored the toolchain line in all five files and kept the tidy requirement updates.
- Files modified: examples/hello/go.mod, examples/hello/plugins/{base,greeter,optional}/go.mod, ../fonoteka.go/go.mod
- Committed in:
05ba88a(summercms.go), 0b9a2a5 (fonoteka.go)
3. [Rule 1 - Bug] OnDatabase must accept a published *gorm.DB alone
- Found during: Task 3
- Issue: Existing fonoteka test harnesses publish only
*gorm.DBbeforeparty.Activate. With the plan's literal "both handles published" check,RegisterHookswould have been queued forever in those tests, and the slug and encryption hooks would have stopped firing. - Fix: A published
*gorm.DBcounts as ready, and the pool comes fromgdb.DB()when no*sql.DBis published. This is covered by thegorm_only_published_runs_nowsubtest. - Files modified: modules/lagoon/ondatabase.go
- Committed in:
6c1f94e
4. [Rule 1 - Bug] SIGTERM would hard-cancel running jobs
- Found during: Task 1/2 (River
Startdocs: cancelling the start ctx is a hard stop) - Issue:
serveandqueue:workpass a signal ctx, so a SIGTERM would have cancelled every running job's ctx beforeStopran. - Fix:
StartWorkerstarts River withcontext.WithoutCancel(ctx).Worker.Stopdoes the graceful stop and falls back toStopAndCancelwhen its own ctx expires. - Files modified: modules/conga/worker.go
- Committed in:
0bc5c77
5. [Plan wording] Registration closes while a worker runs, not "once a client has been built"
- The insert-only client is built lazily on the first Dispatch and carries no Workers bundle, so it does not depend on the job registry. Closing registration only while a worker exists lets
StartWorkerregister plugin jobs even after an earlier Dispatch, and lets a worker restart afterStop.
6. [Additions] Small extra exported surface
conga.Worker.Queues()is used by thequeue:workandserveoutput.lagoon.AfterCommitCallbackis the name constant of thelagoon:after_commitcallback.- The unexported
WorkerOptions.retryPolicytest knob keeps retry tests fast. - All of these are documented in the module READMEs.
7. [Process] Commits on master
gsd-tools git.base-branch --is-protected masterreports true. The project config setsgit.branching_strategy: "none", every earlier plan committed directly to master, and the orchestrator ran this as a sequential executor on the main working tree. So the plan was committed on master in both repositories, with no branch created.
Total deviations: 4 auto-fixed (1 blocking, 3 bugs), plus 3 documented plan-interpretation or process notes. Impact on plan: All fixes were needed for correct boot, shutdown and green builds. No scope creep.
Issues Encountered
- The shell aliases
rmandcpto interactive mode, which stalled two background commands. They were rerun with/bin/rm -fand/bin/cp -f. GOWORK=offbuilds ofexamples/hellofail on the toolchain line. This was already true before the plan. Workspace-mode builds, the project's norm, are green.fonoteka.go/parity/parity_test.go,cmd/summer/main.goandcmd/summer/runtime.gohad gofmt drift at HEAD. It is out of scope and was left untouched.
Flagged assumptions (planner, edge probe)
- (a) Two Dispatch calls get distinct ids (SERIAL), and River row locking keeps one job on one worker at a time. This was not load-tested; plan 11-07 can add a concurrency case.
- (b) Dispatch is not idempotent. Each call creates a new job, as in PHP. Confirmed by design.
- (c) Confirmed by test:
queue:workandservestart and idle with no registered jobs, andqueue:clearon an empty queue printsCleared 0 jobs.
User Setup Required
None. config/queue.yaml notes that a PgBouncer in front of Postgres must use session pooling (or be bypassed) for the worker's LISTEN connection.
Next Phase Readiness
- Plan 11-02 (scheduler): the worker client is the place to attach
PeriodicJobs. - Plan 11-03 (broadcasts): it can use
conga.Manager.Enqueueon the write's transaction andlagoon.OnDatabasefor callbacks. - Plan 11-05 (search sync):
lagoon.Transaction/AfterCommitare ready. Existinggdb.Transactioncall sites (such asalbum_write_service.go) still need migrating in Phase 12. - Plan 11-07: it should add branch coverage for
client.gosettings parsing,selectQueues,Worker.Stop's hard-stop fallback and theEnqueuenon-transaction path.
Self-Check: PASSED
- All 15 key created files exist on disk.
- summercms.go commits
0bc5c77,05ba88a,b319e7cand6c1f94eexist, and so do fonoteka.go commits 3d4895d, 0b9a2a5, e0b1b0d and 36fc473. - The RED stub files were removed.
go vet ./... && go test ./...passed in summercms.go, and the vet and test commands for all three modules passed in fonoteka.go, after the final task.
Phase: 11-jobs-realtime-and-search-infrastructure Completed: 2026-09-29