Files
summercms/.planning/quick/261006-seq-fix-nullable-pointer-scalars-rendering-a/261006-seq-SUMMARY.md

3.8 KiB

phase, plan, subsystem, tags, status, requires, provides, affects, tech-stack, key-files, decisions, metrics, actuals, plan_head_before, plan_head_after
phase plan subsystem tags status requires provides affects tech-stack key-files decisions metrics actuals plan_head_before plan_head_after
quick-261006-seq 01 cabana
cabana
ml
hydration
nullable
reflect
complete
hostScalarString dereferences pointer host columns at any depth; nil becomes empty string
Any mltext/mlmarkdown field backed by a nullable pointer GORM column (Journal Categories -> Description)
added patterns
reflect pointer walk before stringifying projected host values
created modified
modules/cabana/ml_nullable_test.go
modules/cabana/field_ml.go
Fix lives in cabana hostScalarString, not plugin YAML; save path (liftMLValues -> lagoon.Fill) left unchanged and only pinned by the round-trip test
String kinds return raw text via reflect.Value.String(), bypassing any String() method on named string types
duration completed
~5 min 2026-10-06
tokens tasks commits
2400 2 2
8e22e5f25b 4f7e69fd2b

Quick 261006-seq Plan 01: Fix nullable pointer scalars rendering in ML hydration Summary

hostScalarString now walks pointers through reflect, so an mltext field on a nullable *string column hydrates to "" or to its text. Before the fix it showed "<nil>" or a heap address such as 0x36b66873bb0.

Tasks

Task Name Commit Files
1 Round-trip regression for a *string mltext field, then dereference pointers in hostScalarString 90b87d6 modules/cabana/field_ml.go, modules/cabana/ml_nullable_test.go
2 Unit tests for hostScalarString and DB-free hydrateMLRecord over pointer scalars 4f7e69f modules/cabana/ml_nullable_test.go

What changed

  • modules/cabana/field_ml.go: hostScalarString returns "" for a nil interface. It then dereferences pointers at any depth and returns "" for a nil pointer at any depth. String kinds return their raw text, including named string types and bypassing their String() methods. Byte slices are decoded as text. Every other kind goes through fmt.Sprint of the dereferenced value, never of the pointer. A doc comment was added and reflect is now imported.
  • modules/cabana/ml_nullable_test.go:
    • TestMLNullablePointerHost is a Postgres round trip with mlNullablePost.Description *string. It covers a nil host, a cleared value (stored as a pointer to "") and a filled value (the host text is stored and Polish goes through TranslationWriter).
    • TestHostScalarString has 19 table rows.
    • TestMLHydrationPointerHost calls hydrateMLRecord directly with a nil tx.

TDD evidence

RED: before the fix, TestMLNullablePointerHost failed with show description.en = "<nil>" and save description.en = "0x36b66873bb0". GREEN: it passes after the fix, and TestMLHydration, TestMLNestedSave and TestML still pass.

Verification

  • go vet ./... is clean at both commits.
  • go test ./... -count=1 is green at both commits, including the testcontainers cabana tests.
  • gofmt -l modules/cabana prints nothing.
  • The two commits touch only modules/cabana/field_ml.go and modules/cabana/ml_nullable_test.go. They change no README, docs or .planning files and carry no co-author trailers.

Deviations from Plan

None. The plan was executed as written. One small addition: the file-local nullableSlug type has a String() method that returns "decorated". The named-string rows therefore show that the raw stored text is returned, not the Stringer output.

Threat Flags

None. T-quick-261006-seq-01 is mitigated: tests assert that no hydrated value starts with 0x or contains <nil>.

Self-Check: PASSED

  • FOUND: modules/cabana/field_ml.go
  • FOUND: modules/cabana/ml_nullable_test.go
  • FOUND: 90b87d6
  • FOUND: 4f7e69f