Records builds every model's table and associations; models should declare their own schema and Db should only build #742

Closed
opened 2026-09-26 14:43:12 +00:00 by jared · 1 comment
Owner

Problem

Query::Records at plugins/os-sdlc/lib/os_sdlc/query/records.rb builds an anonymous Sequel::Model class per table via Base.Model(@db[table]). It mixes in each concept's ClassMethods and InstanceMethods. It wires associations for Agent, Handoff, Dispatch, Implementation, CorrectionRound, StepResult, EdgeSpend, and other concepts. About 30 call sites use it; five of those sit inside model/. Records therefore knows the configuration of every model in the system, not just how to build one.

Approach

The intended shape, per the user on 2026-09-26: each model declares its own table and associations, the way an ActiveRecord model does. A Db-namespaced adapter receives a model and knows how to build and populate it. The adapter knows nothing about specific models.

Required work:

  1. Rename Query::Records to describe an adapter that builds a model it is given, not one that knows every model — check: the new name contains no model-specific list.
  2. Move the class under plugins/os-sdlc/lib/os_sdlc/adapter/ in the Db namespace.
  3. Move each concept's association wiring (Agent, Handoff, Dispatch, Implementation, CorrectionRound, StepResult, EdgeSpend, and the rest) out of Records and onto the concept's own model declaration.
  4. Update the "Sequel stays inside model/" sentence in .claude/rules/os-sdlc-architecture.md to match the new adapter location.
  5. Open an ADR that amends or supersedes ADR-0167 (table-backed object is a Sequel::Model; registry Models.for(db)) to record the new adapter/model split. Do not carry out the rename without this ADR.

Rejected: leaving Records as-is under query/ — that keeps one object holding every model's association knowledge, the problem this ticket exists to remove.

Acceptance criteria

  1. Given a concept's model file, when it declares its table and associations, then the adapter holds no concept-specific association code for that concept.
  2. Given the renamed adapter class, when read, then its name and location communicate "build and populate a given model," not "know every model."
  3. Given .claude/rules/os-sdlc-architecture.md, when read after this change, then the "Sequel stays inside model/" sentence matches where Sequel code now lives.
  4. Given ADR-0167, when this change ships, then a new ADR amends or supersedes it and is referenced from the affected files.

Owner sketch

class Db::ModelAdapter
  def build(model_class)
  end
end

Non-goals

Do not do this work now. Ship the Gate/Brief consolidation and the Dispatch/Implementation/Map/Project role-morph batch first; this ticket is deferred until both land.

Origin

  • Trigger: user review of query/records.rb during the ADR-0222 rename follow-up, 2026-09-26.
  • Improvised this session: none.
  • Chain: Records builds every model's association wiring ← query/records.rb mixes in each concept's ClassMethods/InstanceMethods and wires Agent/Handoff/Dispatch/etc. associations directly ← DESIGN, ADR-0167 established the Records/Models.for(db) table-backed-object registry before the model/adapter split was proposed.
  • Root candidate: this ticket.
  • Where: plugins/os-sdlc/lib/os_sdlc/query/records.rb
  • Session: d0c72a0b-34c7-4dae-bf3b-7400ef21ad4a
  • Transcript: not available in this invocation
## Problem Query::Records at plugins/os-sdlc/lib/os_sdlc/query/records.rb builds an anonymous Sequel::Model class per table via `Base.Model(@db[table])`. It mixes in each concept's ClassMethods and InstanceMethods. It wires associations for Agent, Handoff, Dispatch, Implementation, CorrectionRound, StepResult, EdgeSpend, and other concepts. About 30 call sites use it; five of those sit inside model/. Records therefore knows the configuration of every model in the system, not just how to build one. ## Approach The intended shape, per the user on 2026-09-26: each model declares its own table and associations, the way an ActiveRecord model does. A Db-namespaced adapter receives a model and knows how to build and populate it. The adapter knows nothing about specific models. Required work: 1. Rename Query::Records to describe an adapter that builds a model it is given, not one that knows every model — check: the new name contains no model-specific list. 2. Move the class under plugins/os-sdlc/lib/os_sdlc/adapter/ in the Db namespace. 3. Move each concept's association wiring (Agent, Handoff, Dispatch, Implementation, CorrectionRound, StepResult, EdgeSpend, and the rest) out of Records and onto the concept's own model declaration. 4. Update the "Sequel stays inside model/" sentence in .claude/rules/os-sdlc-architecture.md to match the new adapter location. 5. Open an ADR that amends or supersedes ADR-0167 (table-backed object is a Sequel::Model; registry Models.for(db)) to record the new adapter/model split. Do not carry out the rename without this ADR. Rejected: leaving Records as-is under query/ — that keeps one object holding every model's association knowledge, the problem this ticket exists to remove. ## Acceptance criteria 1. Given a concept's model file, when it declares its table and associations, then the adapter holds no concept-specific association code for that concept. 2. Given the renamed adapter class, when read, then its name and location communicate "build and populate a given model," not "know every model." 3. Given .claude/rules/os-sdlc-architecture.md, when read after this change, then the "Sequel stays inside model/" sentence matches where Sequel code now lives. 4. Given ADR-0167, when this change ships, then a new ADR amends or supersedes it and is referenced from the affected files. ## Owner sketch ```ruby class Db::ModelAdapter def build(model_class) end end ``` ## Non-goals Do not do this work now. Ship the Gate/Brief consolidation and the Dispatch/Implementation/Map/Project role-morph batch first; this ticket is deferred until both land. ## Origin - Trigger: user review of query/records.rb during the ADR-0222 rename follow-up, 2026-09-26. - Improvised this session: none. - Chain: Records builds every model's association wiring ← query/records.rb mixes in each concept's ClassMethods/InstanceMethods and wires Agent/Handoff/Dispatch/etc. associations directly ← DESIGN, ADR-0167 established the Records/Models.for(db) table-backed-object registry before the model/adapter split was proposed. - Root candidate: this ticket. - Where: plugins/os-sdlc/lib/os_sdlc/query/records.rb - Session: d0c72a0b-34c7-4dae-bf3b-7400ef21ad4a - Transcript: not available in this invocation
Author
Owner

Resolution

Done: Each table-backed concept root (model/agent.rb, step_result.rb, handoff.rb, dispatch.rb, correction_round.rb, violations.rb, edge_spend.rb, objection.rb, implementation.rb) declares its table and associations. Query::Records became OsSdlc::Db::Models at adapter/db/models.rb: a generic builder, Db::Models.for(db)[Concept], with no concept-specific code. All lib and test callers updated. Rules sentence now places Sequel in model/ and adapter/db/. ADR-0227 amends ADR-0167.

Evidence: Commits 5cd826ba, 2dec1f8f, 896c319e, 897abb03, de45398f on branch crc-pass-os-sdlc. Suite: 1429 runs, 0 failures, 0 errors. bin/lint-inward-dependencies 73 -> 72.

Follow-ups: none (remaining model->adapter references to Db::Models in four model files are covered by #741)

## Resolution **Done:** Each table-backed concept root (model/agent.rb, step_result.rb, handoff.rb, dispatch.rb, correction_round.rb, violations.rb, edge_spend.rb, objection.rb, implementation.rb) declares its table and associations. Query::Records became OsSdlc::Db::Models at adapter/db/models.rb: a generic builder, Db::Models.for(db)[Concept], with no concept-specific code. All lib and test callers updated. Rules sentence now places Sequel in model/ and adapter/db/. ADR-0227 amends ADR-0167. **Evidence:** Commits 5cd826ba, 2dec1f8f, 896c319e, 897abb03, de45398f on branch crc-pass-os-sdlc. Suite: 1429 runs, 0 failures, 0 errors. bin/lint-inward-dependencies 73 -> 72. **Follow-ups:** none (remaining model->adapter references to Db::Models in four model files are covered by #741)
jared closed this issue 2026-09-26 21:54:06 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
jared/cc-os#742
No description provided.