Bind implementations to a map name through Map::Catalog #868

Closed
opened 2026-10-01 14:26:05 +00:00 by jared · 3 comments
Owner

Problem

ADR-0258 decision 5 makes implementations.map_path a map NAME, so a reroutes row and the default map use the same kind of value. Today the column holds a file path. Implementation#map loads it with Map::Loader.load(map_path) (model/implementation/targeting.rb:6), and Brief.for finds brief: files with File.dirname(implementation.map_path) (presenter/brief.rb:6). A name has no directory, so both break.

Reproduce

n/a: design change, nothing to reproduce.

Approach

Add Map::Catalog, the one object that resolves a map name to a file in a maps directory. Catalog.default uses the plugin maps/ directory. Tests bind the catalog to tests/fixtures/maps through a test-support seam (no ENV override).

os-sdlc-implementation-open SESSION TICKET NAME takes a map name (e.g. poodr-ticket-implementation). WorkOrder#map_path and args_holder.rb map_path! carry the name; the usage text shows a name. Implementation#map loads Map::Catalog.default.load(map_path). Brief.for passes Map::Catalog.default.dir as map_dir. open.rb:18 loads through the catalog. Bump SCHEMA_VERSION (ADR-0129/0155) because existing rows hold paths.

The column name stays map_path (ADR-0258 keeps it). The open.rb:48 basename test still works with a name; the headers ticket replaces it with a map-level flag.

Required work:

  1. Add the catalog that resolves a map name — check: present class Catalog in plugins/os-sdlc/lib/os_sdlc/**/*.rb
  2. Load the implementation map through the catalog — check: absent Map::Loader.load(map_path) in plugins/os-sdlc/lib/os_sdlc/model/implementation/targeting.rb
  3. Resolve brief files from the catalog directory — check: absent File.dirname(implementation.map_path) in plugins/os-sdlc/lib/os_sdlc/presenter/brief.rb
  4. Show a map name in the implementation-open usage text — check: absent maps/toy.yaml in plugins/os-sdlc/lib/os_sdlc/parser/dispatch_commands/args_holder.rb

Rejected: keep a path and derive the name with File.basename — ADR-0258 decision 5 stores a name, and a drafts directory (slice 4) needs a resolver, not a path.

Acceptance criteria

  1. Given the map name poodr-ticket-implementation, when implementation-open runs, then the implementation row stores poodr-ticket-implementation and its map is the one loaded from maps/poodr-ticket-implementation.yaml.
  2. Given an unknown map name, when implementation-open runs, then it fails loudly and names the missing map file.
  3. Given a node with a brief: path, when the brief is composed for an implementation bound by name, then the brief file resolves relative to the catalog maps directory.

Owner sketch

class OsSdlc::Map::Catalog
  def self.default
  def initialize(dir)
  def dir
  def path_for(name)
  def load(name)
end

Origin

  • Trigger: user ruling on the map reroute design, 2026-10-01 (ADR-0258)
  • Improvised this session: none
  • Chain: a run cannot change maps ← a run binds one map by file path for life ← DESIGN (docs/adr/0169, docs/adr/0258)
  • Root candidate: #859
  • Where: plugins/os-sdlc/lib/os_sdlc/model/implementation/targeting.rb
  • Session: 55a56279-83ee-47d5-a21e-ee67f9e28abd
  • Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os/55a56279-83ee-47d5-a21e-ee67f9e28abd.jsonl

Parent: #859

## Problem ADR-0258 decision 5 makes `implementations.map_path` a map NAME, so a reroutes row and the default map use the same kind of value. Today the column holds a file path. `Implementation#map` loads it with `Map::Loader.load(map_path)` (model/implementation/targeting.rb:6), and `Brief.for` finds `brief:` files with `File.dirname(implementation.map_path)` (presenter/brief.rb:6). A name has no directory, so both break. ## Reproduce n/a: design change, nothing to reproduce. ## Approach Add `Map::Catalog`, the one object that resolves a map name to a file in a maps directory. `Catalog.default` uses the plugin `maps/` directory. Tests bind the catalog to `tests/fixtures/maps` through a test-support seam (no ENV override). `os-sdlc-implementation-open SESSION TICKET NAME` takes a map name (e.g. `poodr-ticket-implementation`). `WorkOrder#map_path` and `args_holder.rb` `map_path!` carry the name; the usage text shows a name. `Implementation#map` loads `Map::Catalog.default.load(map_path)`. `Brief.for` passes `Map::Catalog.default.dir` as `map_dir`. `open.rb:18` loads through the catalog. Bump `SCHEMA_VERSION` (ADR-0129/0155) because existing rows hold paths. The column name stays `map_path` (ADR-0258 keeps it). The `open.rb:48` basename test still works with a name; the headers ticket replaces it with a map-level flag. Required work: 1. Add the catalog that resolves a map name — check: present `class Catalog` in `plugins/os-sdlc/lib/os_sdlc/**/*.rb` 2. Load the implementation map through the catalog — check: absent `Map::Loader.load(map_path)` in `plugins/os-sdlc/lib/os_sdlc/model/implementation/targeting.rb` 3. Resolve brief files from the catalog directory — check: absent `File.dirname(implementation.map_path)` in `plugins/os-sdlc/lib/os_sdlc/presenter/brief.rb` 4. Show a map name in the implementation-open usage text — check: absent `maps/toy.yaml` in `plugins/os-sdlc/lib/os_sdlc/parser/dispatch_commands/args_holder.rb` Rejected: keep a path and derive the name with `File.basename` — ADR-0258 decision 5 stores a name, and a drafts directory (slice 4) needs a resolver, not a path. ## Acceptance criteria 1. Given the map name `poodr-ticket-implementation`, when implementation-open runs, then the implementation row stores `poodr-ticket-implementation` and its map is the one loaded from `maps/poodr-ticket-implementation.yaml`. 2. Given an unknown map name, when implementation-open runs, then it fails loudly and names the missing map file. 3. Given a node with a `brief:` path, when the brief is composed for an implementation bound by name, then the brief file resolves relative to the catalog maps directory. ## Owner sketch ```ruby class OsSdlc::Map::Catalog def self.default def initialize(dir) def dir def path_for(name) def load(name) end ``` ## Origin - Trigger: user ruling on the map reroute design, 2026-10-01 (ADR-0258) - Improvised this session: none - Chain: a run cannot change maps ← a run binds one map by file path for life ← DESIGN (docs/adr/0169, docs/adr/0258) - Root candidate: #859 - Where: plugins/os-sdlc/lib/os_sdlc/model/implementation/targeting.rb - Session: 55a56279-83ee-47d5-a21e-ee67f9e28abd - Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os/55a56279-83ee-47d5-a21e-ee67f9e28abd.jsonl Parent: #859
Author
Owner

Work started on branch ticket-868 (os-sdlc pipeline, map poodr-ticket-implementation).

Work started on branch ticket-868 (os-sdlc pipeline, map poodr-ticket-implementation).
Author
Owner

Implementation 20 ended implementation_failed (verdict:error at contract-auditor dispatch 173). Cause: the contract-auditor correction (name-only Catalog#path_for, test-support seam binding the catalog to tests/fixtures/maps, migration of path-passing test support) needs changes to existing tests, which no role in poodr-ticket-implementation may make. Under-decomposed for the pipeline, recoverable. Finishing by hand in worktree ticket-868 per the escalation procedure; gates replicated (suite, lint, required-work, independent review). Earlier: implementation 19 escalated on a test-writer defect (payload.node on a Hash); suite-check also hit an environment defect, fixed in 38652664 (Gemfile.lock BUNDLED WITH 4.0.11 -> 4.0.22, from #858).

Implementation 20 ended implementation_failed (verdict:error at contract-auditor dispatch 173). Cause: the contract-auditor correction (name-only Catalog#path_for, test-support seam binding the catalog to tests/fixtures/maps, migration of path-passing test support) needs changes to existing tests, which no role in poodr-ticket-implementation may make. Under-decomposed for the pipeline, recoverable. Finishing by hand in worktree ticket-868 per the escalation procedure; gates replicated (suite, lint, required-work, independent review). Earlier: implementation 19 escalated on a test-writer defect (payload.node on a Hash); suite-check also hit an environment defect, fixed in 38652664 (Gemfile.lock BUNDLED WITH 4.0.11 -> 4.0.22, from #858).
Author
Owner

Resolution

Done: Map::Catalog resolves map names; implementations.map_path holds a name; Implementation#map, Brief.for and implementation-open load through Catalog.default; usage text and implement/responsibility-refactor skills pass names; test-support seam binds the catalog to tests/fixtures/maps (thread-local, no ENV); SCHEMA_VERSION 26. Deviations: implementation 19 escalated on a test-writer defect (payload.node on a Hash), fixed by hand and re-opened as implementation 20; suite-check hit an env defect from #858 (Gemfile.lock BUNDLED WITH 4.0.11), fixed in 38652664; implementation 20 failed verdict:error because the test-support migration is not routable in the map; HAND-FINISHED (sonnet worker + independent opus review PASS).

Evidence: main a2875763 (feature), 38652664 (lockfile), ca494959 (History row); bin/test on main: 9 suites passed; rubocop clean on changed lib files; all four Required-work checks verified present/absent.

Follow-ups: none captured. Dropped: four review NITs on the two new tests (identity fixture_map helper, StandardError in assert_raises, self-derived expected values) - cosmetic; A10 catalog injection at targeting.rb - lens said JUSTIFIED by the ticket Approach.

## Resolution **Done:** Map::Catalog resolves map names; implementations.map_path holds a name; Implementation#map, Brief.for and implementation-open load through Catalog.default; usage text and implement/responsibility-refactor skills pass names; test-support seam binds the catalog to tests/fixtures/maps (thread-local, no ENV); SCHEMA_VERSION 26. Deviations: implementation 19 escalated on a test-writer defect (payload.node on a Hash), fixed by hand and re-opened as implementation 20; suite-check hit an env defect from #858 (Gemfile.lock BUNDLED WITH 4.0.11), fixed in 38652664; implementation 20 failed verdict:error because the test-support migration is not routable in the map; HAND-FINISHED (sonnet worker + independent opus review PASS). **Evidence:** main a2875763 (feature), 38652664 (lockfile), ca494959 (History row); bin/test on main: 9 suites passed; rubocop clean on changed lib files; all four Required-work checks verified present/absent. **Follow-ups:** none captured. Dropped: four review NITs on the two new tests (identity fixture_map helper, StandardError in assert_raises, self-derived expected values) - cosmetic; A10 catalog injection at targeting.rb - lens said JUSTIFIED by the ticket Approach.
jared closed this issue 2026-10-01 16:07:56 +00:00
jared referenced this issue from a commit 2026-10-01 18:24:32 +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.

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