Ticket sections have no shared model, and pipeline roles work from silos of the ticket #643

Closed
opened 2026-09-23 13:49:56 +00:00 by jared · 4 comments
Owner

Problem

  • Every pipeline role receives the full ticket verbatim (brief_facts.rb:62 ticket_brief = implementations.ticket_body), but each charter narrows attention or disclaims ownership, so the union leaves gaps. On #638 three required Approach-only items stayed undone and every role passed.
  • Silo lines: behavior-verifier.md:15 ("test-reviewer owns test relevance, contract-auditor owns design…"), contract-auditor.md:16 ("Do not re-evaluate missing ticket behaviors; behavior-verifier owns that."), contract-auditor.md:20, test-writer.md:36 ("Test exactly those criteria."), test-reviewer.md:14 ("cover each case in ticket_brief"), test-reviewer.md:19, reviewer.md:13, change-auditor.md:42.
  • The sections have no shared model. Two templates: capture.md (Owner sketch/Cases/Approach) and ticket.md (Slice/Acceptance criteria/Non-goals). AC and Cases mean the same thing (given/when/then observable behavior). No document states how Approach, Cases/AC, Owner sketch relate. Authors move Approach rules into Cases to get them enforced (#638 Case 8 grep for Dir.pwd; #627 Cases 6-7 config facts).
  • Nobody owns ticket alignment. Only change-auditor has a single "contradicts itself" judgment; TicketShape checks presence only.

Decided (user, 2026-09-23)

  • Ticket model, in order: Problem (why; root cause for bugs) → Approach (decided design + list of required work / structural checks; stays malleable, the user often pushes back) → Acceptance criteria (observable given/when/then; the name "Cases" is retired in favor of "Acceptance criteria") → Owner sketch (class/method names).
  • One consistent template and validator set for capture, decompose, and modification.
  • Remove instruction silos: every role gets one shared goal ("the ticket is done when every acceptance criterion passes and every required Approach item is in the diff"), keeps one owned part, and reports gaps outside its part in its handoff instead of ignoring or fixing them.
  • Alignment is checked early: change-auditor (first node; its output already reaches later nodes) checks that each Approach required item maps to an acceptance criterion or a structural check and that none contradict, emits one work list every later role receives, and fails → escalates to the human before any test exists. contract-auditor checks the diff against the work list (the original "Option A").
  • Open questions: exact template headings; whether Problem/Approach are mandatory at capture; open-ticket migration (bulk edit vs dual-accept parser).

Blast radius

  • Duplicated literal "Cases" parsers: os-backlog ticket_body_checker.rb:5,70-74 (check_cases_listed, @name == "Cases") and os-sdlc ticket_shape.rb:6,16,39-41; ADR-0190 records the duplication as deliberate. Must change in lockstep.
  • Other code: template_validator.rb:13-16; TicketShape callers gate_commands.rb:52, dispatch_commands.rb:107, brief_facts.rb:53, open_implementation.rb:36,42.
  • Tests: os-backlog template_validator_test.rb, template_validator_optional_sections_test.rb, support/template_validator_fixtures.rb, support/template_validator_stub_tracker.rb, checkout_test.rb; os-sdlc open_implementation_ticket_shape_test.rb, open_implementation_retired_tests_test.rb, open_implementation_retires_file_entries_test.rb.
  • Prose: capture.md:47-73, ticket.md:4-8, body-style.md:5, ticket-decompose/SKILL.md:51, os-sdlc implement/SKILL.md:54, reference/spec-and-ticket-layer.md:70, lint-rule-ticket-template.md:113, agent charters.
  • Data: 13 open tickets use ## Cases: #643, #642, #641, #640, #639, #637, #629, #628, #626, #625, #604, #597, #593. In-flight implementations keep their snapshot body (ADR-0146).
  • ADRs: 0190 and 0202 amend/supersede; 0146, 0169, 0170 context.
  • Risk: removing silo lines changes verdict semantics (who fails on missing behavior); routing in the map (plugins/os-sdlc/maps/poodr-ticket-implementation.yaml) must be checked with the charters.

Observed (original, #638 impl 13)

On ticket #638 (implementation 13, 2026-09-23), the ## Approach section required three things: rewrite tests/support/in_process_runner_cli.rb to call Pipeline::CLI.start instead of its own command table; convert four forking test files to in-process; pass the Invocation through the Thor start config, not a global. None of the three was done. The programmer instead set a class-level global, OsSdlc::Pipeline::CLI.default_invocation, in bin/os-sdlc-runner. test-writer, test-reviewer, programmer, and behavior-verifier all passed the round. The run then escalated on ticket #629 (the verifier cannot see bin/) before contract-auditor ran, so no gate ever compared the diff to Approach.

New Ticket #645 (code roots), new Ticket #646 (validator gap), #644 (test-writer/test-reviewer loop), #629, #638, #639-#642.

Origin

  • Trigger: implementation round 13 on ticket #638, 2026-09-23; rewritten 2026-09-23 after root-cause analysis in session d1aa78d1
  • Improvised this session: none
  • Chain: Approach-only work goes undone ← no gate checks the diff against Approach ← no shared ticket model ← DESIGN: Backlog::TemplateValidator only checks Approach is non-empty; instruction silos across charters
  • Root candidate: this ticket
  • Where: plugins/os-backlog/lib/backlog/template_validator.rb, plugins/os-sdlc/agents/*.md
  • Session: d1aa78d1-c840-471e-bdd0-2be657c7d950
  • Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os/d1aa78d1-c840-471e-bdd0-2be657c7d950.jsonl
## Problem - Every pipeline role receives the full ticket verbatim (`brief_facts.rb:62` ticket_brief = implementations.ticket_body), but each charter narrows attention or disclaims ownership, so the union leaves gaps. On #638 three required Approach-only items stayed undone and every role passed. - Silo lines: behavior-verifier.md:15 ("test-reviewer owns test relevance, contract-auditor owns design…"), contract-auditor.md:16 ("Do not re-evaluate missing ticket behaviors; behavior-verifier owns that."), contract-auditor.md:20, test-writer.md:36 ("Test exactly those criteria."), test-reviewer.md:14 ("cover each case in ticket_brief"), test-reviewer.md:19, reviewer.md:13, change-auditor.md:42. - The sections have no shared model. Two templates: capture.md (Owner sketch/Cases/Approach) and ticket.md (Slice/Acceptance criteria/Non-goals). AC and Cases mean the same thing (given/when/then observable behavior). No document states how Approach, Cases/AC, Owner sketch relate. Authors move Approach rules into Cases to get them enforced (#638 Case 8 grep for Dir.pwd; #627 Cases 6-7 config facts). - Nobody owns ticket alignment. Only change-auditor has a single "contradicts itself" judgment; TicketShape checks presence only. ## Decided (user, 2026-09-23) - Ticket model, in order: Problem (why; root cause for bugs) → Approach (decided design + list of required work / structural checks; stays malleable, the user often pushes back) → Acceptance criteria (observable given/when/then; the name "Cases" is retired in favor of "Acceptance criteria") → Owner sketch (class/method names). - One consistent template and validator set for capture, decompose, and modification. - Remove instruction silos: every role gets one shared goal ("the ticket is done when every acceptance criterion passes and every required Approach item is in the diff"), keeps one owned part, and reports gaps outside its part in its handoff instead of ignoring or fixing them. - Alignment is checked early: change-auditor (first node; its output already reaches later nodes) checks that each Approach required item maps to an acceptance criterion or a structural check and that none contradict, emits one work list every later role receives, and fails → escalates to the human before any test exists. contract-auditor checks the diff against the work list (the original "Option A"). - Open questions: exact template headings; whether Problem/Approach are mandatory at capture; open-ticket migration (bulk edit vs dual-accept parser). ## Blast radius - Duplicated literal "Cases" parsers: os-backlog `ticket_body_checker.rb:5,70-74` (`check_cases_listed`, `@name == "Cases"`) and os-sdlc `ticket_shape.rb:6,16,39-41`; ADR-0190 records the duplication as deliberate. Must change in lockstep. - Other code: template_validator.rb:13-16; TicketShape callers gate_commands.rb:52, dispatch_commands.rb:107, brief_facts.rb:53, open_implementation.rb:36,42. - Tests: os-backlog template_validator_test.rb, template_validator_optional_sections_test.rb, support/template_validator_fixtures.rb, support/template_validator_stub_tracker.rb, checkout_test.rb; os-sdlc open_implementation_ticket_shape_test.rb, open_implementation_retired_tests_test.rb, open_implementation_retires_file_entries_test.rb. - Prose: capture.md:47-73, ticket.md:4-8, body-style.md:5, ticket-decompose/SKILL.md:51, os-sdlc implement/SKILL.md:54, reference/spec-and-ticket-layer.md:70, lint-rule-ticket-template.md:113, agent charters. - Data: 13 open tickets use `## Cases`: #643, #642, #641, #640, #639, #637, #629, #628, #626, #625, #604, #597, #593. In-flight implementations keep their snapshot body (ADR-0146). - ADRs: 0190 and 0202 amend/supersede; 0146, 0169, 0170 context. - Risk: removing silo lines changes verdict semantics (who fails on missing behavior); routing in the map (`plugins/os-sdlc/maps/poodr-ticket-implementation.yaml`) must be checked with the charters. ## Observed (original, #638 impl 13) On ticket #638 (implementation 13, 2026-09-23), the `## Approach` section required three things: rewrite `tests/support/in_process_runner_cli.rb` to call `Pipeline::CLI.start` instead of its own command table; convert four forking test files to in-process; pass the `Invocation` through the Thor `start` config, not a global. None of the three was done. The programmer instead set a class-level global, `OsSdlc::Pipeline::CLI.default_invocation`, in `bin/os-sdlc-runner`. test-writer, test-reviewer, programmer, and behavior-verifier all passed the round. The run then escalated on ticket #629 (the verifier cannot see `bin/`) before contract-auditor ran, so no gate ever compared the diff to Approach. ## Related New Ticket #645 (code roots), new Ticket #646 (validator gap), #644 (test-writer/test-reviewer loop), #629, #638, #639-#642. ## Origin - Trigger: implementation round 13 on ticket #638, 2026-09-23; rewritten 2026-09-23 after root-cause analysis in session d1aa78d1 - Improvised this session: none - Chain: Approach-only work goes undone ← no gate checks the diff against Approach ← no shared ticket model ← DESIGN: Backlog::TemplateValidator only checks Approach is non-empty; instruction silos across charters - Root candidate: this ticket - Where: plugins/os-backlog/lib/backlog/template_validator.rb, plugins/os-sdlc/agents/*.md - Session: d1aa78d1-c840-471e-bdd0-2be657c7d950 - Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os/d1aa78d1-c840-471e-bdd0-2be657c7d950.jsonl
jared changed title from Required work written only in ## Approach passes every pipeline gate undone to Ticket sections have no shared model, and pipeline roles work from silos of the ticket 2026-09-23 16:46:03 +00:00
Author
Owner

Rewritten 2026-09-23 after root-cause analysis in session d1aa78d1: the Approach gap is one symptom of missing ticket model + instruction silos. Original body preserved in the session scratchpad.

Rewritten 2026-09-23 after root-cause analysis in session d1aa78d1: the Approach gap is one symptom of missing ticket model + instruction silos. Original body preserved in the session scratchpad.
Author
Owner

Decision memo: open questions in #643 (design review claude-opus-5-5, 2026-09-23)

Facts found: REQUIRED_SECTIONS = ["Slice", "Acceptance criteria", "Non-goals"] (template_validator.rb:13) makes any body with ## Acceptance criteria a ticket attempt, which then demands Slice and Non-goals (#628 hits this today). All 13 open tickets use ## Context, not ## Problem; #639 and #641 are ready-for-agent.

(a) Headings: one template (capture.md); ticket.md retired

## Problem              always     (absorbs Context, Slice)
## Observed / Reproduce optional   bugs
## Approach             ready      decided design + "Required work:" numbered items, optional `check:` command
## Acceptance criteria  ready      numbered given/when/then
## Owner sketch         ready      one ```ruby fence
## Non-goals / Retired tests       optional
## Origin               always

B: keep two templates sharing only the four model names. A, 85%: the ticket decided one template; two templates keep the Slice/Non-goals tier trap.

(b) Problem mandatory from capture (template rule); Approach mandatory only at ready-for-agent

MODEL_SECTIONS = ["Problem", "Approach", "Acceptance criteria", "Owner sketch"].freeze
# ready-for-agent: all MODEL_SECTIONS required, whatever the body shape

B: both mandatory at every tier. A, 70%: ADR-0092 keeps capture cheap; Approach stays malleable until an agent runs the ticket.

(c) Dual-accept: one alias table in both parsers, removed as the last unit of #643

LEGACY_NAMES = { "Acceptance criteria" => ["Cases"], "Problem" => ["Context"] }.freeze

B: keep aliases, file a removal follow-up. A, 70%: removal is two lines with a clear condition (no open body matches ^## (Cases|Context)$). The Context → Problem alias is needed or #639/#641 fail implementation-open before migration.

(d) Work list travels by the existing enrich-by-agent-type mechanism (ADR-0169)

test-reviewer:     enrich: [..., change-auditor]
behavior-verifier: enrich: [ticket_brief, code_changes, change-auditor]
refactorer:        enrich: [..., change-auditor]
contract-auditor:  enrich: [ticket_brief, gate_results, change-auditor]
change-auditor:    edges: { fail: escalate }

B: a new named fact work_list in brief_facts.rb. A, 80%: map-only change; Map::Validator already checks enrich names.
Item format: WORK: R<n> <item>; proof: AC <k> | check ; owner: <role>, CONFLICT: ..., UNMAPPED: ....

(e) Silo lines: each charter gets the shared goal sentence and keeps one owned part; gaps outside it are reported as GAP: items

The ticket is done when every acceptance criterion passes and every required Approach item is in the diff. Your part is below. Report a gap outside your part as a GAP: item. Do not ignore it and do not fix it.
contract-auditor.md:16 "Do not re-evaluate missing ticket behaviors" → check the diff against every WORK: item; missing = WORK-MISSING: (fail if refactorer can add it production-only, else error). change-auditor gains "## 4. Align".

(f) #646 is absorbed

return capture_result unless agent_ready?(labels) || checker.ticket_attempt?

B: separate one-line fix first. A, 85%: same method is rewritten here; the #646 repro becomes the first failing test.

Taking all recommendations. ADR amends 0092, 0093, 0161, 0170, 0190, 0202.

## Decision memo: open questions in #643 (design review claude-opus-5-5, 2026-09-23) Facts found: `REQUIRED_SECTIONS = ["Slice", "Acceptance criteria", "Non-goals"]` (`template_validator.rb:13`) makes any body with `## Acceptance criteria` a ticket attempt, which then demands Slice and Non-goals (#628 hits this today). All 13 open tickets use `## Context`, not `## Problem`; #639 and #641 are ready-for-agent. ### (a) Headings: one template (`capture.md`); `ticket.md` retired ``` ## Problem always (absorbs Context, Slice) ## Observed / Reproduce optional bugs ## Approach ready decided design + "Required work:" numbered items, optional `check:` command ## Acceptance criteria ready numbered given/when/then ## Owner sketch ready one ```ruby fence ## Non-goals / Retired tests optional ## Origin always ``` B: keep two templates sharing only the four model names. **A, 85%**: the ticket decided one template; two templates keep the Slice/Non-goals tier trap. ### (b) Problem mandatory from capture (template rule); Approach mandatory only at ready-for-agent ```ruby MODEL_SECTIONS = ["Problem", "Approach", "Acceptance criteria", "Owner sketch"].freeze # ready-for-agent: all MODEL_SECTIONS required, whatever the body shape ``` B: both mandatory at every tier. **A, 70%**: ADR-0092 keeps capture cheap; Approach stays malleable until an agent runs the ticket. ### (c) Dual-accept: one alias table in both parsers, removed as the last unit of #643 ```ruby LEGACY_NAMES = { "Acceptance criteria" => ["Cases"], "Problem" => ["Context"] }.freeze ``` B: keep aliases, file a removal follow-up. **A, 70%**: removal is two lines with a clear condition (no open body matches `^## (Cases|Context)$`). The `Context → Problem` alias is needed or #639/#641 fail `implementation-open` before migration. ### (d) Work list travels by the existing enrich-by-agent-type mechanism (ADR-0169) ```yaml test-reviewer: enrich: [..., change-auditor] behavior-verifier: enrich: [ticket_brief, code_changes, change-auditor] refactorer: enrich: [..., change-auditor] contract-auditor: enrich: [ticket_brief, gate_results, change-auditor] change-auditor: edges: { fail: escalate } ``` B: a new named fact `work_list` in brief_facts.rb. **A, 80%**: map-only change; `Map::Validator` already checks enrich names. Item format: `WORK: R<n> <item>; proof: AC <k> | check `<cmd>`; owner: <role>`, `CONFLICT: ...`, `UNMAPPED: ...`. ### (e) Silo lines: each charter gets the shared goal sentence and keeps one owned part; gaps outside it are reported as `GAP:` items > The ticket is done when every acceptance criterion passes and every required Approach item is in the diff. Your part is below. Report a gap outside your part as a `GAP:` item. Do not ignore it and do not fix it. contract-auditor.md:16 "Do not re-evaluate missing ticket behaviors" → check the diff against every `WORK:` item; missing = `WORK-MISSING:` (fail if refactorer can add it production-only, else error). change-auditor gains "## 4. Align". ### (f) #646 is absorbed ```ruby return capture_result unless agent_ready?(labels) || checker.ticket_attempt? ``` B: separate one-line fix first. **A, 85%**: same method is rewritten here; the #646 repro becomes the first failing test. Taking all recommendations. ADR amends 0092, 0093, 0161, 0170, 0190, 0202.
Author
Owner

Decision memo: migration scope (2026-09-23)

A scan of all 167 open issues found 102 bodies with a legacy heading: 9 with ## Cases (#597 #604 #625 #626 #637 #639 #640 #641 #642) and 93 with only ## Context (#133–#649, most from the old capture template, many in the frozen backlog).

A (chosen): migrate the 13 named tickets and the tickets filed after this redesign (#644–#649): ## Cases→## Acceptance criteria, ## Context→## Problem. Remove the Cases alias from both parsers. Keep LEGACY_NAMES = { "Problem" => ["Context"] } in both parsers.
B: rename headings in all 102 bodies and remove both aliases.

Recommendation: A, 70%. The authorization covered the 13 tickets, not a 100-ticket sweep of the frozen backlog. The Context alias costs one table entry. A capture-tier body is not required to have Problem, so the only effect of the alias is that a legacy ticket passes implementation-open once it gains Approach, Acceptance criteria and Owner sketch. Removing the Context alias becomes a follow-up, done when the frozen backlog is closed or migrated.

## Decision memo: migration scope (2026-09-23) A scan of all 167 open issues found 102 bodies with a legacy heading: 9 with `## Cases` (#597 #604 #625 #626 #637 #639 #640 #641 #642) and 93 with only `## Context` (#133–#649, most from the old capture template, many in the frozen backlog). **A (chosen):** migrate the 13 named tickets and the tickets filed after this redesign (#644–#649): `## Cases`→`## Acceptance criteria`, `## Context`→`## Problem`. Remove the `Cases` alias from both parsers. Keep `LEGACY_NAMES = { "Problem" => ["Context"] }` in both parsers. **B:** rename headings in all 102 bodies and remove both aliases. Recommendation: **A, 70%**. The authorization covered the 13 tickets, not a 100-ticket sweep of the frozen backlog. The Context alias costs one table entry. A capture-tier body is not required to have Problem, so the only effect of the alias is that a legacy ticket passes implementation-open once it gains Approach, Acceptance criteria and Owner sketch. Removing the Context alias becomes a follow-up, done when the frozen backlog is closed or migrated.
jared closed this issue 2026-09-23 18:50:08 +00:00
Author
Owner

Resolution

Done: Shared ticket model shipped in both parsers (Problem, Approach, Acceptance criteria, Owner sketch); change-auditor aligns Approach Required work to acceptance criteria and emits the work list (ADR-0209). Review fixes 2026-09-23: Cases and Context aliases removed with a re-decompose message, sections end only at ## in all three parsers, stale Cases sentence removed from six charters, Required work list enforced by both parsers and change-auditor. #646 absorbed.

Evidence: main commits 7f056949..f375143b (build), 51ddc591 (Cases alias drop), c57f4e49 7157c5ce 3e6204a9 a88ae085 e9a872fd 7e4207a1 (review fixes); os-sdlc suite 1430 runs 0 failures, os-backlog 244 runs 0 failures on 7e4207a1; #639-#642 migrated with Required work lists and pass both parsers.

Follow-ups: none

## Resolution **Done:** Shared ticket model shipped in both parsers (Problem, Approach, Acceptance criteria, Owner sketch); change-auditor aligns Approach Required work to acceptance criteria and emits the work list (ADR-0209). Review fixes 2026-09-23: Cases and Context aliases removed with a re-decompose message, sections end only at ## in all three parsers, stale Cases sentence removed from six charters, Required work list enforced by both parsers and change-auditor. #646 absorbed. **Evidence:** main commits 7f056949..f375143b (build), 51ddc591 (Cases alias drop), c57f4e49 7157c5ce 3e6204a9 a88ae085 e9a872fd 7e4207a1 (review fixes); os-sdlc suite 1430 runs 0 failures, os-backlog 244 runs 0 failures on 7e4207a1; #639-#642 migrated with Required work lists and pass both parsers. **Follow-ups:** none
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#643
No description provided.