Parse uppercase CHANGE tags in handoff items so the auto-commit includes refactorer files #906

Closed
opened 2026-10-02 15:28:59 +00:00 by jared · 3 comments
Owner

Parse uppercase CHANGE tags in handoff items so the auto-commit includes refactorer files [capture]

Problem

The Runner auto-commit takes its file list from handoff items. Commit::Item strips only lowercase tags. The refactorer writes CHANGE: <path> :: ... items with an uppercase tag. The parser reads CHANGE: as the path. A file that only a refactorer item names is never staged. A new untracked file in that state breaks the committed tree.

Observed

Implementation 6 (#897) reached implementation_complete. The Runner committed d93e4600. commit.paths listed 10 files. commit.dirty_paths listed plugins/os-sdlc/lib/os_sdlc/model/map/source.rb. That file was new and untracked. The code in d93e4600 references Map::Source. The commit alone fails the suite. The user committed the file by hand as e43b1864.

The handoff rows of implementation 6 show the cause.

  • Handoff 34 (refactorer) names source.rb only in CHANGE: plugins/os-sdlc/lib/os_sdlc/model/map/source.rb :: new Map::Source = ....
  • Handoff 30 (programmer) lists plain paths, for example plugins/os-sdlc/lib/os_sdlc/model/map/changed_error.rb. Those paths were committed.
  • Implementation 7 (#900): the test-writer listed plugins/os-sdlc/tests/model/dispatch/work_order_test.rb as a plain path. It was in commit.paths.
plugins/os-sdlc/lib/os_sdlc/parser/implementation/commit/item.rb:7
TAG = /\A[a-z_]+:\s*/
plugins/os-sdlc/lib/os_sdlc/parser/implementation/commit/item.rb:23
@token ||= @entry.strip.sub(TAG, "").split(/\s/).first.to_s.gsub(DECORATION, "")

"CHANGE: plugins/.../source.rb :: ...".sub(/\A[a-z_]+:\s*/, "") does not match, because CHANGE is uppercase. The first token is CHANGE:, which becomes CHANGE: after DECORATION removal. It is not in changed_paths. ListedPaths#call & changed_paths (query/implementation/commit/listed_paths.rb:12, command/implementation/commit.rb:34) drops it. commit.rb:27 then puts the real file in dirty_paths: changed_paths - paths.

The refactorer agent definition prescribes this format (plugins/os-sdlc/agents/refactorer.md:44): CHANGE: <path> :: <refactoring or NO-REFACTOR-NEEDED>.

Reproduce

ruby -e 'puts "CHANGE: plugins/os-sdlc/lib/os_sdlc/model/map/source.rb :: new Map::Source".strip.sub(/\A[a-z_]+:\s*/, "").split(/\s/).first'

Output: CHANGE:

Approach

Give the refactorer the same items grammar as the programmer and the test-writer: each item is one changed file path, with no tag. The parser stays as it is. Then the commit path list has one grammar for every writer role, and no parser heuristic gets wider.

  • In plugins/os-sdlc/agents/refactorer.md, items lists every file the refactorer changed, as a plain path. items: [] means no refactoring.
  • The RATIONALE: and LENS: lines move to disclosures, as in agents/programmer.md.
  • The NO-REFACTOR-NEEDED result goes in summary.
  • A test reads the handoff example in agents/refactorer.md and sends each item to Implementation::Commit::Item#path. Each item must give its path. This test is red now: the example item CHANGE: lib/widget.rb :: ... gives CHANGE:.

Required work:

  1. Remove the CHANGE: item tag from the refactorer handoff — check: absent CHANGE: in plugins/os-sdlc/agents/refactorer.md
  2. Move rationale and lens lines to disclosures — check: present disclosures: in plugins/os-sdlc/agents/refactorer.md

Rejected:

  • Widen Item::TAG to /\A[A-Za-z_]+:\s*/. The parser then accepts a second grammar that a prompt owns. Judge items such as TEST: <path> :: <name> (test-reviewer) also start to resolve to paths, so a judge item can stage a file. This adds a heuristic for one emitter (feedback-remove-habits-dont-backfill-heuristics).
  • Stage every dirty path. A dirty path can be user work that is not part of the implementation (overlapping_paths and dirty_paths exist to protect that work, ADR-0171).
  • Read paths only from writer-role handoffs. This changes ADR-0171 decision 2 and needs a user ruling.

Acceptance criteria

  1. Given the handoff example in agents/refactorer.md, when each item goes to Implementation::Commit::Item#path, then each item gives a repository-relative path.
  2. Given a refactorer handoff whose items name only a new untracked file, when the implementation completes, then commit.paths contains that file and commit.dirty_paths does not.
  3. Given agents/refactorer.md, when searched, then no CHANGE: item prefix remains, and the RATIONALE: and LENS: lines are under disclosures.

Owner sketch

summary: "Extracted Widget::Pricer to remove a behavior-selecting conditional."
disclosures:
  - "RATIONALE: the green diff added a second pricing branch inside Widget#reprice."
items:
  - lib/widget.rb
  - lib/widget/pricer.rb

Origin

  • Trigger: /os-sdlc:implement 897, implementation 6
  • Improvised this session: manual commit e43b1864 ("fix(os-sdlc): #897 add Map::Source, omitted from the Runner commit d93e4600")
  • Chain: new file source.rb in dirty_paths not paths ← ListedPaths keeps only tokens that Item parses as a path ← Item::TAG matches lowercase tags only (plugins/os-sdlc/lib/os_sdlc/parser/implementation/commit/item.rb:7) ← refactorer items use the uppercase CHANGE: tag (plugins/os-sdlc/agents/refactorer.md:44; handoff 34 in .sdlc/pipeline.db) ← DESIGN: ADR-0171 auto-commit takes paths from handoff items, and the item grammar has two disagreeing owners (agent prompt and parser).
  • Root candidate: none
  • Where: plugins/os-sdlc/lib/os_sdlc/parser/implementation/commit/item.rb
  • Session: 8e234876-f10b-45d6-8783-ad61b3747f10
  • Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os/8e234876-f10b-45d6-8783-ad61b3747f10.jsonl

VERDICT: CREATE — observed in a complete in-policy run (#897 implementation 6); Item::TAG at item.rb:7 matches only lowercase tags while refactorer.md:44 prescribes uppercase CHANGE:, so the auto-commit silently omits refactorer files.

# Parse uppercase CHANGE tags in handoff items so the auto-commit includes refactorer files [capture] ## Problem The Runner auto-commit takes its file list from handoff `items`. `Commit::Item` strips only lowercase tags. The refactorer writes `CHANGE: <path> :: ...` items with an uppercase tag. The parser reads `CHANGE:` as the path. A file that only a refactorer item names is never staged. A new untracked file in that state breaks the committed tree. ## Observed Implementation 6 (#897) reached `implementation_complete`. The Runner committed d93e4600. `commit.paths` listed 10 files. `commit.dirty_paths` listed `plugins/os-sdlc/lib/os_sdlc/model/map/source.rb`. That file was new and untracked. The code in d93e4600 references `Map::Source`. The commit alone fails the suite. The user committed the file by hand as e43b1864. The handoff rows of implementation 6 show the cause. - Handoff 34 (refactorer) names `source.rb` only in `CHANGE: plugins/os-sdlc/lib/os_sdlc/model/map/source.rb :: new Map::Source = ...`. - Handoff 30 (programmer) lists plain paths, for example `plugins/os-sdlc/lib/os_sdlc/model/map/changed_error.rb`. Those paths were committed. - Implementation 7 (#900): the test-writer listed `plugins/os-sdlc/tests/model/dispatch/work_order_test.rb` as a plain path. It was in `commit.paths`. ``` plugins/os-sdlc/lib/os_sdlc/parser/implementation/commit/item.rb:7 TAG = /\A[a-z_]+:\s*/ plugins/os-sdlc/lib/os_sdlc/parser/implementation/commit/item.rb:23 @token ||= @entry.strip.sub(TAG, "").split(/\s/).first.to_s.gsub(DECORATION, "") ``` `"CHANGE: plugins/.../source.rb :: ...".sub(/\A[a-z_]+:\s*/, "")` does not match, because `CHANGE` is uppercase. The first token is `CHANGE:`, which becomes `CHANGE:` after DECORATION removal. It is not in `changed_paths`. `ListedPaths#call & changed_paths` (query/implementation/commit/listed_paths.rb:12, command/implementation/commit.rb:34) drops it. `commit.rb:27` then puts the real file in `dirty_paths: changed_paths - paths`. The refactorer agent definition prescribes this format (`plugins/os-sdlc/agents/refactorer.md:44`): `CHANGE: <path> :: <refactoring or NO-REFACTOR-NEEDED>.` ## Reproduce ``` ruby -e 'puts "CHANGE: plugins/os-sdlc/lib/os_sdlc/model/map/source.rb :: new Map::Source".strip.sub(/\A[a-z_]+:\s*/, "").split(/\s/).first' ``` Output: `CHANGE:` ## Approach Give the refactorer the same `items` grammar as the programmer and the test-writer: each item is one changed file path, with no tag. The parser stays as it is. Then the commit path list has one grammar for every writer role, and no parser heuristic gets wider. - In `plugins/os-sdlc/agents/refactorer.md`, `items` lists every file the refactorer changed, as a plain path. `items: []` means no refactoring. - The `RATIONALE:` and `LENS:` lines move to `disclosures`, as in `agents/programmer.md`. - The `NO-REFACTOR-NEEDED` result goes in `summary`. - A test reads the handoff example in `agents/refactorer.md` and sends each item to `Implementation::Commit::Item#path`. Each item must give its path. This test is red now: the example item `CHANGE: lib/widget.rb :: ...` gives `CHANGE:`. Required work: 1. Remove the `CHANGE:` item tag from the refactorer handoff — check: absent `CHANGE:` in `plugins/os-sdlc/agents/refactorer.md` 2. Move rationale and lens lines to disclosures — check: present `disclosures:` in `plugins/os-sdlc/agents/refactorer.md` Rejected: - Widen `Item::TAG` to `/\A[A-Za-z_]+:\s*/`. The parser then accepts a second grammar that a prompt owns. Judge items such as `TEST: <path> :: <name>` (test-reviewer) also start to resolve to paths, so a judge item can stage a file. This adds a heuristic for one emitter (feedback-remove-habits-dont-backfill-heuristics). - Stage every dirty path. A dirty path can be user work that is not part of the implementation (`overlapping_paths` and `dirty_paths` exist to protect that work, ADR-0171). - Read paths only from writer-role handoffs. This changes ADR-0171 decision 2 and needs a user ruling. ## Acceptance criteria 1. Given the handoff example in `agents/refactorer.md`, when each item goes to `Implementation::Commit::Item#path`, then each item gives a repository-relative path. 2. Given a refactorer handoff whose `items` name only a new untracked file, when the implementation completes, then `commit.paths` contains that file and `commit.dirty_paths` does not. 3. Given `agents/refactorer.md`, when searched, then no `CHANGE:` item prefix remains, and the `RATIONALE:` and `LENS:` lines are under `disclosures`. ## Owner sketch ```yaml summary: "Extracted Widget::Pricer to remove a behavior-selecting conditional." disclosures: - "RATIONALE: the green diff added a second pricing branch inside Widget#reprice." items: - lib/widget.rb - lib/widget/pricer.rb ``` ## Origin - Trigger: /os-sdlc:implement 897, implementation 6 - Improvised this session: manual commit e43b1864 ("fix(os-sdlc): #897 add Map::Source, omitted from the Runner commit d93e4600") - Chain: new file `source.rb` in `dirty_paths` not `paths` ← `ListedPaths` keeps only tokens that Item parses as a path ← `Item::TAG` matches lowercase tags only (`plugins/os-sdlc/lib/os_sdlc/parser/implementation/commit/item.rb:7`) ← refactorer items use the uppercase `CHANGE:` tag (`plugins/os-sdlc/agents/refactorer.md:44`; handoff 34 in `.sdlc/pipeline.db`) ← DESIGN: ADR-0171 auto-commit takes paths from handoff items, and the item grammar has two disagreeing owners (agent prompt and parser). - Root candidate: none - Where: plugins/os-sdlc/lib/os_sdlc/parser/implementation/commit/item.rb - Session: 8e234876-f10b-45d6-8783-ad61b3747f10 - Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os/8e234876-f10b-45d6-8783-ad61b3747f10.jsonl VERDICT: CREATE — observed in a complete in-policy run (#897 implementation 6); Item::TAG at item.rb:7 matches only lowercase tags while refactorer.md:44 prescribes uppercase CHANGE:, so the auto-commit silently omits refactorer files.
Author
Owner

PREDICTED RULING: REVISE

a. No. The user would not accept "widen Item::TAG to [A-Za-z_]".

b. Reasons:

  • feedback-remove-habits-dont-backfill-heuristics: the wider regex adds a parser heuristic for one emitter. The only current tag test (modified: lib/widget.rb) pins a format that no agent emits.
  • feedback-lens-request-is-a-design-symptom / feedback-adr-conflict-is-a-symptom: the Origin chain names the root, "two disagreeing owners" of the item grammar. The fix must remove one owner, not teach the parser the second grammar.
  • ADR-0171 decision 2: the git intersection is the safety net. With uppercase tags stripped, judge items such as TEST: <path> :: <name> (test-reviewer) also resolve to paths. A judge item can then stage a file.

c. They would call the regex a patch on the symptom. Research done: the item formats in agents/*.md. programmer.md and test-writer.md use plain paths in items and put LENS in disclosures. Only refactorer.md tags a writer item. No lib code reads CHANGE:. No test reads agent prompts.

d. Give the refactorer the programmer's grammar: plain changed paths in items, RATIONALE/LENS in disclosures, NO-REFACTOR-NEEDED in summary. Add a red test: the refactorer.md example items must parse through Commit::Item#path. The parser and ADR-0171 do not change. The body is edited to match.

e. Not read: pipeline.db handoff 34, the contract-auditor brief assembly (it may quote refactorer items), the map node instructions for the refactorer, and ADR-0050/0135 on handoff item kinds.

PREDICTED RULING: REVISE a. No. The user would not accept "widen Item::TAG to [A-Za-z_]". b. Reasons: - feedback-remove-habits-dont-backfill-heuristics: the wider regex adds a parser heuristic for one emitter. The only current tag test (`modified: lib/widget.rb`) pins a format that no agent emits. - feedback-lens-request-is-a-design-symptom / feedback-adr-conflict-is-a-symptom: the Origin chain names the root, "two disagreeing owners" of the item grammar. The fix must remove one owner, not teach the parser the second grammar. - ADR-0171 decision 2: the git intersection is the safety net. With uppercase tags stripped, judge items such as `TEST: <path> :: <name>` (test-reviewer) also resolve to paths. A judge item can then stage a file. c. They would call the regex a patch on the symptom. Research done: the item formats in agents/*.md. programmer.md and test-writer.md use plain paths in `items` and put LENS in `disclosures`. Only refactorer.md tags a writer item. No lib code reads `CHANGE:`. No test reads agent prompts. d. Give the refactorer the programmer's grammar: plain changed paths in `items`, RATIONALE/LENS in `disclosures`, NO-REFACTOR-NEEDED in `summary`. Add a red test: the refactorer.md example items must parse through Commit::Item#path. The parser and ADR-0171 do not change. The body is edited to match. e. Not read: pipeline.db handoff 34, the contract-auditor brief assembly (it may quote refactorer items), the map node instructions for the refactorer, and ADR-0050/0135 on handoff item kinds.
Author
Owner

Work started on branch ticket-906 through /os-sdlc:implement (poodr-ticket-implementation map).

Work started on branch ticket-906 through /os-sdlc:implement (poodr-ticket-implementation map).
Author
Owner

Resolution

Done: refactorer.md handoff now writes plain changed paths in items (programmer grammar); RATIONALE/LENS lines go to disclosures; NO-REFACTOR-NEEDED goes in summary. Commit::Item parser and ADR-0171 unchanged, so judge-agent tags still never become commit paths.

Evidence: Implementation 21 complete, 8 dispatches all pass, suite gate exit 0. New test tests/parser/implementation/commit/item_test.rb reads the refactorer.md example through Commit::Item#path. Commits 04fbc824, 7bef0c4e on main.

Follow-ups: none

## Resolution **Done:** refactorer.md handoff now writes plain changed paths in items (programmer grammar); RATIONALE/LENS lines go to disclosures; NO-REFACTOR-NEEDED goes in summary. Commit::Item parser and ADR-0171 unchanged, so judge-agent tags still never become commit paths. **Evidence:** Implementation 21 complete, 8 dispatches all pass, suite gate exit 0. New test tests/parser/implementation/commit/item_test.rb reads the refactorer.md example through Commit::Item#path. Commits 04fbc824, 7bef0c4e on main. **Follow-ups:** none
jared closed this issue 2026-10-02 19:55:26 +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#906
No description provided.