Skip to content

app-showcase seed writes data: URIs into an image field, so ADR-0104's migration gate can never attest on a fresh datastore #4891

Description

@xuyushun441-sys

What happens

Every boot of examples/app-showcase on a brand-new empty database:

[value-shape] cover has an invalid image value: Expected an opaque sys_file id — accepted for now
  (ADR-0104 warn-first; run `os migrate files-to-references --apply` to migrate this deployment and enforce)

WARN [migration] NOT attesting 'adr-0104-file-references' on this new datastore: this boot already wrote
  10 value(s) that the migration's own contract rejects (showcase_task.cover: Expected an opaque sys_file id).
  The store was created empty, but it is no longer empty and what it now holds contradicts the claim —
  the gate stays open (warn-first). Fix the data, then run `os migrate files-to-references --apply` to
  close it on real evidence (ADR-0104).

Why it happens

showcase_task.cover is Field.image() (src/data/objects/task.object.ts:76),
and ADR-0104 requires an opaque sys_file id as its value.

The seed instead stores an inline SVG data URI
(src/data/seed/index.ts, placeholderCover()):

return `data:image/svg+xml,${encodeURIComponent(svg)}`;

10 task rows carry one.

The helper's own docblock explains the motivation, and it is a good one:

Task cover seeds used to point at picsum.photos, which renders as a wall of
broken images in offline/restricted-network environments (Gallery, All Views).
A data: URI needs no network at all…

So this is a sound fix for the offline-images problem that happens to collide
head-on with ADR-0104's value contract. Both constraints are legitimate; the seed
currently satisfies one by violating the other.

Why it's worth fixing rather than tolerating

The interesting consequence is not the warning — it is the second line. ADR-0104's
attestation is designed to close automatically on a new store, on the
reasoning that a store created empty has no legacy values to migrate. The
showcase's own seed falsifies that in the same boot, so:

  • a fresh install of the reference app can never auto-attest
    adr-0104-file-references; the gate stays open on day one,
  • the printed remedy (os migrate files-to-references --apply) is aimed at
    migrating legacy deployments, and here it would be run against data our own
    seeder just wrote,
  • anyone copying the showcase's image-seeding pattern inherits the same stuck gate.

It also means the warn-first gate is permanently noisy in the app most likely to
be someone's first ObjectStack experience, which erodes the signal for the
deployments the gate actually exists to catch.

Suggested direction

Seed the cover through the storage service so cover holds a real sys_file
id — keeping the offline property that motivated the data URI (the SVG bytes can
be registered as a sys_file at seed time; no network needed), and letting a
fresh store attest cleanly.

If seeding real sys_file rows is out of scope for the example, the alternative
is to drop cover from the seed and demonstrate the Gallery cover with an
explicit upload step — but that loses coverage the seed is deliberately providing,
so the first option seems better.

Related to #4888 (other showcase declarations that don't survive to runtime),
though this one is seed data rather than a declaration and its blast radius is
the platform migration gate.

Repro

cd examples/app-showcase
node ../../packages/cli/bin/run.js dev --ui --seed-admin -p 3000 -d file:/tmp/fresh.db
# both lines appear during boot, on an empty DB

Observed on main @ 0e96e46. Found while browser-sweeping showcase + Studio for #4879.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions