WORK-533
ID:WORK-533Status:ready

Declare join tables in tag modules

Move the schema↔engine join tables out of RuneConfig and into the tag modules that own them, with config referencing the declaration rather than owning it. This is what makes WORK-534 possible: createContentModelSchema needs the gating facts at construction time, and today it has none.

Priority:highComplexity:complexMilestone:v0.31.0Source:SPEC-125
claude/transform-package-refactor-7mxi8a View source

Criteria completion

Criteria completion: 0 of 8 (0%) checked; tracking started on Sep 6, no incremental history yet0%25%50%75%100%Sep 6Sep 6

Tracking started Sep 6 — check back for trends.

Branches 1
claude/transform-package-refactor-7mxi8a current ready
main ready
History 1
  1. 127194f
    Created (ready)by bjornolofandersson

Why this direction and not the other

The module graph decides it. The dependency direction is config → tags, uniformly: core's config.ts imports four sentinels from tag modules (BREADCRUMB_AUTO_SENTINEL, NAV_AUTO_SENTINEL, PAGINATION_AUTO_SENTINEL, XREF_RUNE_MARKER); no core tag imports config, and no plugin tag imports its config — 0 of 9.

Having tags import config instead would invert that and cycle in core, and since createContentModelSchema runs at module scope the tag would observe coreConfig uninitialised — a crash, not a subtle bug.

Scope

  • sections — ~50 runes across core and nine plugins
  • mediaSlots — a handful
  • frameTarget — 2 (Figure, Showcase)

modifiers does not move. The gates that read it (cover, content-place) key on modifiers the schema already declares as author-facing attributes, so that gate is already schema-side.

Acceptance Criteria

  • sections, mediaSlots and frameTarget are declared in their tag modules and referenced from config
  • createContentModelSchema receives them, so the facts are available at construction — even though narrowing itself lands in WORK-534
  • The engine's read path is unchanged: it still reads config.sections, config.mediaSlots, config.frameTarget
  • No new import direction anywhere; config → tags still holds and no cycle is introduced in core or in any plugin
  • frameTarget is added to the guarded identity set, closing the gap WORK-528 deliberately left
  • Transform output is byte-identical — this is a pure relocation
  • refrakt contracts --check passes with no regeneration
  • npm run build and the full repo suite pass

Approach

Sequenced after the audit items so the migration moves final values. Doing it first would mean editing the same ~50 places twice.

Relocation only — no value changes. Any correction that surfaces mid-migration belongs back in WORK-529WORK-531 (or a follow-up), not folded in here, so that "output is byte-identical" stays a usable check.

The frameTarget move also collapses frame applicability to a single source. Without it, a theme could add frameTarget: 'self' to a rune whose narrowed schema rejects frame= — config granting what the schema forbids, the same divergence inverted. See SPEC-125 Phase 2 for the three resolutions considered and why moving it won.

Blocked by

  • WORK-529
  • WORK-530
  • WORK-531

References

  • SPEC-125 — Phase 2, Where the applicability data lives
  • ADR-028 — the governing decision