os-sdlc: Project becomes an ActiveModel object (no behavior change) #1672
Labels
No labels
P0
P1
P2
P3
bug
category/code-change
category/cross-repo
category/eval-run
category/investigation
category/no-test-target
category/owner-ruling
category/prose-artifact
category/recurring-chore
category/structure
category/umbrella
crc-refactor
create
delete
enhancement
filed-by/agent
filed-by/user
frozen
lint-rule
needs-info
needs-triage
next
plugin/cc-architect
plugin/os
plugin/os-adr
plugin/os-aidd-lint
plugin/os-backlog
plugin/os-context
plugin/os-doc-hygiene
plugin/os-sdlc
plugin/os-vault
project/cc-os
ready-for-agent
ready-for-human
recurring
review
rule-proposal
update
waiting
wayfinder:grilling
wayfinder:map
wayfinder:map
wayfinder:research
wayfinder:task
wayfinder:task
wontfix
worklist/deviations
worklist/lint-rule
worklist/new-implement-build
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
jared/cc-os#1672
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
OsSdlc::Project(plugins/os-sdlc/lib/os_sdlc/project.rb) isData.define(:name, :root, :tracker, :targets, :selected). The project is persisted as a JSON snapshot in theimplementations.project_configcolumn. The next ticket makes the project hold connections (connect(:db)and a lazyrepo). A frozenDataobject cannot hold them. ADR-0312 acceptedactivemodelas a direct dependency of os-sdlc.Approach
Projectbecomes a Ruby class that includesActiveModel::ModelandActiveModel::Attributes. The five fields become attributes.gem "activemodel"goes intoplugins/os-sdlc/Gemfile, andGemfile.lockis updated.require "active_model"andActiveModel.eager_load!are loaded asplugins/os-backlog/lib/backlog.rb:9-12does it.Project::Target,Project::Suite, andProject::LintstayData. The language is Ruby. The suite command isruby test/all.rb, run fromplugins/os-sdlc.Kept behavior. The existing suite pins these items, so they are not red-round criteria. The new class must keep each one.
name,root,tracker,targets, andselectedare==, areeql?, and have equalhash(test/project_json_round_trip_test.rb:13,test/project_nested_code_test_lint_test.rb:105;Crc::Census::Settingsholds a project inside aData).Project.newtakes the same keywords with the same defaults:targetsdefaults to[Project::Target.new(name: name)], androot,tracker, andselecteddefault to nil.Project::Unconfigured < Projectstill loads and constructs.project.with(...)returns a changed copy and leaves the receiver unchanged (call sitesproject.rb:12,project.rb:23,project/config.rb:17,project/config.rb:71).bound_toreturns the same result as before.case project; in Project[root:]matches (lib/os_sdlc/boot/commands.rb:164), sodeconstruct_keysmust exist.to_jsonandto_snapshot_hemit exactly the key set{name, tracker, targets: {<target name> => {path, test_path, test_command, test_framework, lint_config, tracker}}}throughProject::Snapshot.rootandselectednever enter the snapshot. A legacy snapshot with notargetskey still loads. Do not use the JSON serializer of ActiveModel.project.rbthat name.sdlc/project.yamland--target NAMEstay verbatim.Required work:
gem "activemodel"inplugins/os-sdlc/GemfileActiveModel::Attributesinplugins/os-sdlc/lib/os_sdlc/project.rbData.define(:name, :root, :tracker, :targets, :selected)inplugins/os-sdlc/lib/os_sdlc/project.rbAcceptance criteria
Project.new(name: "x").frozen?is false, and a test sets an instance variable on aProjectand reads it back.Project.new(name: "x", tracker: "t").attributesreturns a Hash with exactly the keys"name","root","tracker","targets", and"selected", with the given values.Project.new(name: "x")answersvalid?with true and has an emptyerrorsobject.Owner sketch
OsSdlc::Projectowns its five attributes, their defaults, value equality, the changed copy (with), pattern-match keys (deconstruct_keys), and the delegation of its snapshot toProject::Snapshot. This ticket adds no object and moves no responsibility.Out of scope
connect,repo, the Thor entry, the removal ofBoot, and new validations. These are the next tickets of the same change.Origin
Trigger: n/a: design ticket named by the user, not a run that showed a fault
Improvised this session: none
Chain: the next ticket cannot hold
connect(:db)and a lazyrepoon Project ← Project is a frozenData.define← ADR-0312 (DESIGN) acceptedactivemodelas a direct dependencySession: a48658d5-b566-40ce-8257-ebeabf56f7e1
Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os-plugins-os-sdlc/a48658d5-b566-40ce-8257-ebeabf56f7e1.jsonl
Where: plugins/os-sdlc/lib/os_sdlc/project.rb (OsSdlc::Project)
Root candidate: none found; searched Project, ActiveModel, project.rb, Data.define, project_config, connect(:db)
Approved by: Jared named and approved this ticket in session on 2026-11-10.
VERDICT: CREATE
REASON: No open ticket converts OsSdlc::Project from Data to an ActiveModel class. #586 and #1200 name the same file or subsystem but a different fault.
Work started on branch ticket-1672 through /os-sdlc:implement (map poodr-ticket-implementation).
Resolution
Done: OsSdlc::Project is an ActiveModel class (ActiveModel::Model + ActiveModel::Attributes) with the five fields as attributes. It is not frozen. Value equality, with, deconstruct_keys, the keyword constructor, and the snapshot key set are kept. activemodel is a direct gem in plugins/os-sdlc/Gemfile (ADR-0312). active_model loads only with Project and with no eager load.
Evidence: Implementation 270 complete on branch ticket-1672: commits
7262854fcandcd1fe70bd. All gates passed (suite, survivors after one survivor-test round, contract-auditor). Full suite after the load fix: 3916 runs, 8055 assertions, 0 failures. Load of lib/os_sdlc plus Project: 0.17 s before, 0.22 s after.Follow-ups: none: the next pieces of the same change (project.connect and lazy project.repo; Thor entry; removal of Boot) are worked in this session and get their own tickets.