os-backlog validate-template refuses Required-work checks that are not typed present/absent #847

Closed
opened 2026-09-30 17:02:28 +00:00 by jared · 6 comments
Owner

Problem

ADR-0255 makes a Required-work check a typed present/absent text check. os-sdlc enforces it. os-backlog validate-template does not. It accepts any backticked span after check:.

So a ticket with a shell or slash-command check passes os-backlog, gets ready-for-agent, and then implementation-open refuses it. ADR-0209 says the two validators must change together. This ticket is the os-backlog part of #843. It is a separate ticket because an os-sdlc implementation takes one target.

plugins/os-backlog/lib/backlog/required_work.rb:10  BACKTICKED_COMMAND = /`[^`]+`/
plugins/os-backlog/lib/backlog/required_work.rb:22-23  def checks_without_command ... !tail.match?(BACKTICKED_COMMAND)
plugins/os-backlog/lib/backlog/heading_content_issues.rb:57  "Required work check has no backticked command: #{item}"
plugins/os-sdlc/lib/os_sdlc/parser/ticket_shape/required_work.rb:12  CHECK_FORM = /\A\s*(present|absent)\s+`([^`]+)`\s+in\s+`([^`]+)`\s*\z/
plugins/os-sdlc/lib/os_sdlc/parser/ticket_shape.rb:103  "Required work check is not present/absent `<text>` in `<glob>`: #{item.text}"

Reproduce

An agent-ready ticket whose Required-work item ends with a shell check passes os-backlog validate-template N --agent-ready. os-sdlc then refuses the same item. The os-sdlc part is #843.

Approach

Backlog::RequiredWork refuses every check that is not present `<text>` in `<glob>` or absent `<text>` in `<glob>` . The refusal text matches os-sdlc: Required work check is not present/absent `<text>` in `<glob>`: <item>.

Keep CHECK_TAIL. Keep the placement rule: the first check: in an item's lines is the item's check. Keep the agent-ready-only gate. Add no runtime dependency from os-backlog on os-sdlc. Copy the form into Backlog::RequiredWork (ADR-0190, ADR-0209 and ADR-0255 decision 3 keep the two validators duplicated and in lockstep).

The test-writer rewrites the cases in plugins/os-backlog/tests/template_validator_required_work_check_test.rb that pin the old message and the old pass cases. validate-template is the public seam; the acceptance criteria test through it. After merge, the orchestrator adds the History row to docs/implementation-status/os-backlog.md and runs bin/refresh-plugins. These are not Required work (ADR-0255 decision 5).

Required work:

  1. In plugins/os-backlog/lib/backlog/required_work.rb, replace BACKTICKED_COMMAND with a CHECK_FORM copy of the os-sdlc form — check: absent BACKTICKED_COMMAND in plugins/os-backlog/lib/**/*.rb
  2. Rename checks_without_command to checks_not_typed; its only caller is heading_content_issues.rb:57 — check: absent checks_without_command in plugins/os-backlog/lib/**/*.rb
  3. In plugins/os-backlog/lib/backlog/heading_content_issues.rb, make unrunnable_check_issues emit the os-sdlc refusal text — check: present Required work check is not present/absent in plugins/os-backlog/lib/**/*.rb

Rejected:

  • A unit test of RequiredWork#checks_not_typed in required_work_test.rb. It repeats the validate-template cases one layer down (lens principle 7: test the public seam).
  • History row and bin/refresh-plugins as Required-work items. They are not file changes inside the target that a pipeline agent makes (ADR-0255 decision 5).
  • One shared grammar (a gem, or os-backlog calling os-sdlc). ADR-0255 kept the lockstep on 2026-09-30. A change needs a user ruling.
  • Copy the os-sdlc Item and Check values into os-backlog. os-backlog only refuses; it never evaluates polarity, text or glob.

Sketch:

class Backlog::RequiredWork
  CHECK_FORM = /\A\s*(present|absent)\s+`([^`]+)`\s+in\s+`([^`]+)`\s*\z/

  def checks_not_typed
    items.select { |lines| (tail = lines.join[CHECK_TAIL, 1]) && !tail.match?(CHECK_FORM) }
         .map { |lines| lines.first.sub(NUMBER_PREFIX, "").strip }
  end
end

Acceptance criteria

  1. Given an agent-ready ticket with the check absent `CLI::` in `lib/**/*.rb` , when validate-template runs, then it reports no Required-work issue.
  2. Given an agent-ready ticket whose Required-work item names the shell command grep -q X lib/a.rb as its check, when validate-template runs, then it refuses with the new text and names the item.
  3. Given an agent-ready ticket whose Required-work item names the slash command /os-adr:find foo as its check, when validate-template runs, then it refuses with the new text and names the item.
  4. Given an agent-ready ticket with a free-text check that has no backticks, when validate-template runs, then it refuses with the new text and names the item.
  5. Given a typed check written in the middle of an item line, when validate-template runs, then it reads the check as it does today.

Owner sketch

class Backlog::RequiredWork
  def checks_not_typed end
end

class Backlog::HeadingContentIssues
  def unrunnable_check_issues(required_work) end
end

Changes since filing (verified 2026-10-02 on main 7bef0c4e)

  • No commit has touched either lib file since #821 (433b0445). Paths and lines are still correct.
  • #856 (forgejo-api) did not touch this code.
  • The Retired tests section is removed. The test file is not retired. Its cases change; the test-writer rewrites them through the acceptance criteria.
  • Required work cut to file changes with typed checks; the History row and bin/refresh-plugins moved to orchestrator work; the extra required_work_test.rb case dropped (predicted ruling, 2026-10-02).
  • ADR-0239 duplication of the two validators is Superseded by ADR-0224. The duplication is still the status quo.

Blast radius

  • Production: plugins/os-backlog/lib/backlog/required_work.rb, plugins/os-backlog/lib/backlog/heading_content_issues.rb.
  • Tests: template_validator_required_work_check_test.rb (rewrite).
  • Docs: one History row in docs/implementation-status/os-backlog.md.
  • ADRs: 0209 (validators change together), 0244 decision 5, 0255. None conflicts.
  • Behavior change: open ready-for-agent tickets with shell checks now fail validate-template. This is correct. os-sdlc already refuses them.
  • Run bin/refresh-plugins after the change.

Origin

  • Trigger: /os-sdlc:implement #843, implementation 7, test-reviewer objection 6
  • Improvised this session: #843 split by target; the os-backlog part moved here
  • Chain: test-review loop does not converge ← os-backlog test outside the os-sdlc gate target ← #843 spans two targets ← DESIGN: one target per implementation
  • Root candidate: this ticket
  • Where: plugins/os-backlog/lib/backlog/required_work.rb; plugins/os-backlog/lib/backlog/heading_content_issues.rb
  • Session: fc9f2295-3034-4595-9344-2d94ad4e3ee1
  • Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os/fc9f2295-3034-4595-9344-2d94ad4e3ee1.jsonl

Blocked by: none. #843 shipped (ADR-0255); the os-sdlc text is at plugins/os-sdlc/lib/os_sdlc/parser/ticket_shape.rb:103.

VERDICT: CREATE — user-filed; os-backlog passes checks that os-sdlc refuses (ADR-0209) (ticket-skeptic, 2026-09-30).

## Problem ADR-0255 makes a Required-work check a typed `present`/`absent` text check. os-sdlc enforces it. os-backlog `validate-template` does not. It accepts any backticked span after `check:`. So a ticket with a shell or slash-command check passes os-backlog, gets `ready-for-agent`, and then implementation-open refuses it. ADR-0209 says the two validators must change together. This ticket is the os-backlog part of #843. It is a separate ticket because an os-sdlc implementation takes one target. ``` plugins/os-backlog/lib/backlog/required_work.rb:10 BACKTICKED_COMMAND = /`[^`]+`/ plugins/os-backlog/lib/backlog/required_work.rb:22-23 def checks_without_command ... !tail.match?(BACKTICKED_COMMAND) plugins/os-backlog/lib/backlog/heading_content_issues.rb:57 "Required work check has no backticked command: #{item}" plugins/os-sdlc/lib/os_sdlc/parser/ticket_shape/required_work.rb:12 CHECK_FORM = /\A\s*(present|absent)\s+`([^`]+)`\s+in\s+`([^`]+)`\s*\z/ plugins/os-sdlc/lib/os_sdlc/parser/ticket_shape.rb:103 "Required work check is not present/absent `<text>` in `<glob>`: #{item.text}" ``` ## Reproduce An agent-ready ticket whose Required-work item ends with a shell check passes `os-backlog validate-template N --agent-ready`. os-sdlc then refuses the same item. The os-sdlc part is #843. ## Approach `Backlog::RequiredWork` refuses every check that is not ``present `<text>` in `<glob>` `` or ``absent `<text>` in `<glob>` ``. The refusal text matches os-sdlc: ``Required work check is not present/absent `<text>` in `<glob>`: <item>``. Keep `CHECK_TAIL`. Keep the placement rule: the first `check:` in an item's lines is the item's check. Keep the agent-ready-only gate. Add no runtime dependency from os-backlog on os-sdlc. Copy the form into `Backlog::RequiredWork` (ADR-0190, ADR-0209 and ADR-0255 decision 3 keep the two validators duplicated and in lockstep). The test-writer rewrites the cases in `plugins/os-backlog/tests/template_validator_required_work_check_test.rb` that pin the old message and the old pass cases. `validate-template` is the public seam; the acceptance criteria test through it. After merge, the orchestrator adds the History row to `docs/implementation-status/os-backlog.md` and runs `bin/refresh-plugins`. These are not Required work (ADR-0255 decision 5). Required work: 1. In `plugins/os-backlog/lib/backlog/required_work.rb`, replace `BACKTICKED_COMMAND` with a `CHECK_FORM` copy of the os-sdlc form — check: absent `BACKTICKED_COMMAND` in `plugins/os-backlog/lib/**/*.rb` 2. Rename `checks_without_command` to `checks_not_typed`; its only caller is `heading_content_issues.rb:57` — check: absent `checks_without_command` in `plugins/os-backlog/lib/**/*.rb` 3. In `plugins/os-backlog/lib/backlog/heading_content_issues.rb`, make `unrunnable_check_issues` emit the os-sdlc refusal text — check: present `Required work check is not present/absent` in `plugins/os-backlog/lib/**/*.rb` Rejected: - A unit test of `RequiredWork#checks_not_typed` in `required_work_test.rb`. It repeats the validate-template cases one layer down (lens principle 7: test the public seam). - History row and `bin/refresh-plugins` as Required-work items. They are not file changes inside the target that a pipeline agent makes (ADR-0255 decision 5). - One shared grammar (a gem, or os-backlog calling os-sdlc). ADR-0255 kept the lockstep on 2026-09-30. A change needs a user ruling. - Copy the os-sdlc `Item` and `Check` values into os-backlog. os-backlog only refuses; it never evaluates polarity, text or glob. Sketch: ```ruby class Backlog::RequiredWork CHECK_FORM = /\A\s*(present|absent)\s+`([^`]+)`\s+in\s+`([^`]+)`\s*\z/ def checks_not_typed items.select { |lines| (tail = lines.join[CHECK_TAIL, 1]) && !tail.match?(CHECK_FORM) } .map { |lines| lines.first.sub(NUMBER_PREFIX, "").strip } end end ``` ## Acceptance criteria 1. Given an agent-ready ticket with the check ``absent `CLI::` in `lib/**/*.rb` ``, when `validate-template` runs, then it reports no Required-work issue. 2. Given an agent-ready ticket whose Required-work item names the shell command `grep -q X lib/a.rb` as its check, when `validate-template` runs, then it refuses with the new text and names the item. 3. Given an agent-ready ticket whose Required-work item names the slash command `/os-adr:find foo` as its check, when `validate-template` runs, then it refuses with the new text and names the item. 4. Given an agent-ready ticket with a free-text check that has no backticks, when `validate-template` runs, then it refuses with the new text and names the item. 5. Given a typed check written in the middle of an item line, when `validate-template` runs, then it reads the check as it does today. ## Owner sketch ```ruby class Backlog::RequiredWork def checks_not_typed end end class Backlog::HeadingContentIssues def unrunnable_check_issues(required_work) end end ``` ## Changes since filing (verified 2026-10-02 on main 7bef0c4e) - No commit has touched either lib file since #821 (433b0445). Paths and lines are still correct. - #856 (forgejo-api) did not touch this code. - The Retired tests section is removed. The test file is not retired. Its cases change; the test-writer rewrites them through the acceptance criteria. - Required work cut to file changes with typed checks; the History row and `bin/refresh-plugins` moved to orchestrator work; the extra `required_work_test.rb` case dropped (predicted ruling, 2026-10-02). - ADR-0239 duplication of the two validators is Superseded by ADR-0224. The duplication is still the status quo. ## Blast radius - Production: `plugins/os-backlog/lib/backlog/required_work.rb`, `plugins/os-backlog/lib/backlog/heading_content_issues.rb`. - Tests: `template_validator_required_work_check_test.rb` (rewrite). - Docs: one History row in `docs/implementation-status/os-backlog.md`. - ADRs: 0209 (validators change together), 0244 decision 5, 0255. None conflicts. - Behavior change: open ready-for-agent tickets with shell checks now fail `validate-template`. This is correct. os-sdlc already refuses them. - Run `bin/refresh-plugins` after the change. ## Origin - Trigger: /os-sdlc:implement #843, implementation 7, test-reviewer objection 6 - Improvised this session: #843 split by target; the os-backlog part moved here - Chain: test-review loop does not converge ← os-backlog test outside the os-sdlc gate target ← #843 spans two targets ← DESIGN: one target per implementation - Root candidate: this ticket - Where: plugins/os-backlog/lib/backlog/required_work.rb; plugins/os-backlog/lib/backlog/heading_content_issues.rb - Session: fc9f2295-3034-4595-9344-2d94ad4e3ee1 - Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os/fc9f2295-3034-4595-9344-2d94ad4e3ee1.jsonl Blocked by: none. #843 shipped (ADR-0255); the os-sdlc text is at `plugins/os-sdlc/lib/os_sdlc/parser/ticket_shape.rb:103`. VERDICT: CREATE — user-filed; os-backlog passes checks that os-sdlc refuses (ADR-0209) (ticket-skeptic, 2026-09-30).
Author
Owner

Body revised 2026-10-02 after blast-radius re-check on main b80613ec: typed CHECK_FORM copy, checks_not_typed rename, test rewrite (not retired), blast radius added.

Body revised 2026-10-02 after blast-radius re-check on main b80613ec: typed CHECK_FORM copy, checks_not_typed rename, test rewrite (not retired), blast radius added.
Author
Owner

PREDICTED RULING: REVISE

a. No. The design is right; the Required-work list is not.

b. Reasons:

  • Keep the copied CHECK_FORM: ADR-0190, ADR-0209 and ADR-0255 decision 3 (Accepted, 2026-09-30) keep the two validators duplicated and in lockstep. ADR-0239 is Superseded, but those three still hold.
  • Items 4-5 (History row, bin/refresh-plugins) break ADR-0255 decision 5: Required work is only file changes inside the target that a pipeline agent makes.
  • Items 2-5 have no typed check; recent tickets (#906, #914) give every item one.
  • The extra required_work_test.rb case tests one layer under the validate-template seam (lens principle 7; dedup batches #666-#698).

c. Lazy: the user wrote ADR-0255 decision 5 two days ago, and this ticket breaks it. Research done: read both RequiredWork classes, Check.parse, ADR-0025/0190/0209/0224/0239/0244/0255, lens architecture A12, and three recent ticket bodies. A shared gem or os-backlog calling os-sdlc reverses Accepted ADRs, so per feedback-os-sdlc-runs-afk-decide-and-report the ADR-preserving option wins.

d. Same code change. Three items, one typed check each (each fails on main 7bef0c4e): absent BACKTICKED_COMMAND, absent checks_without_command, present the os-sdlc refusal text. Test rewrite goes to the test-writer through the ACs. History row and refresh are orchestrator work. A Rejected list records the alternatives. Body updated; validate-template --agent-ready exits 0.

e. Not read: lens-tactics, lens-testing, lens-cases; the #843 transcript; the full ADR list for later cross-plugin rulings. No new ADR is needed. A shared grammar would need a user ruling.

PREDICTED RULING: REVISE a. No. The design is right; the Required-work list is not. b. Reasons: - Keep the copied CHECK_FORM: ADR-0190, ADR-0209 and ADR-0255 decision 3 (Accepted, 2026-09-30) keep the two validators duplicated and in lockstep. ADR-0239 is Superseded, but those three still hold. - Items 4-5 (History row, bin/refresh-plugins) break ADR-0255 decision 5: Required work is only file changes inside the target that a pipeline agent makes. - Items 2-5 have no typed check; recent tickets (#906, #914) give every item one. - The extra `required_work_test.rb` case tests one layer under the validate-template seam (lens principle 7; dedup batches #666-#698). c. Lazy: the user wrote ADR-0255 decision 5 two days ago, and this ticket breaks it. Research done: read both RequiredWork classes, Check.parse, ADR-0025/0190/0209/0224/0239/0244/0255, lens architecture A12, and three recent ticket bodies. A shared gem or os-backlog calling os-sdlc reverses Accepted ADRs, so per feedback-os-sdlc-runs-afk-decide-and-report the ADR-preserving option wins. d. Same code change. Three items, one typed check each (each fails on main 7bef0c4e): absent BACKTICKED_COMMAND, absent checks_without_command, present the os-sdlc refusal text. Test rewrite goes to the test-writer through the ACs. History row and refresh are orchestrator work. A Rejected list records the alternatives. Body updated; validate-template --agent-ready exits 0. e. Not read: lens-tactics, lens-testing, lens-cases; the #843 transcript; the full ADR list for later cross-plugin rulings. No new ADR is needed. A shared grammar would need a user ruling.
Author
Owner

Work started 2026-10-02 on branch ticket-847 (os-sdlc pipeline, map poodr-ticket-implementation, target os-backlog).

Work started 2026-10-02 on branch ticket-847 (os-sdlc pipeline, map poodr-ticket-implementation, target os-backlog).
Author
Owner

Follow-up dropped by ticket-skeptic (VERDICT: DROP): mirrored typed-check fixtures across os-backlog and os-sdlc — speculative hardening; #847 already fixed the drift with per-plugin tests, the duplication is deliberate (ADR-0190/0209/0255), no new drift observed. File again if drift recurs.

Follow-up dropped by ticket-skeptic (VERDICT: DROP): mirrored typed-check fixtures across os-backlog and os-sdlc — speculative hardening; #847 already fixed the drift with per-plugin tests, the duplication is deliberate (ADR-0190/0209/0255), no new drift observed. File again if drift recurs.
Author
Owner

Resolution

Done: os-backlog validate-template now refuses any Required-work check that is not typed (present|absent <text> in <glob>), with the same refusal text as os-sdlc. Backlog::RequiredWork#checks_not_typed replaces checks_without_command; HeadingContentIssues only renders the refusal.

Evidence: Commits dadb4ce9 (feat, implementation 24: 13 dispatches, suite green on second suite-check, contract-auditor pass) and 1b7b96cd (History row) merged to main. Changed: plugins/os-backlog/lib/backlog/required_work.rb, heading_content_issues.rb, tests/template_validator_required_work_check_test.rb.

Follow-ups: none — parity-fixture follow-up dropped by ticket-skeptic (comment above)

## Resolution **Done:** os-backlog validate-template now refuses any Required-work check that is not typed (present|absent `<text>` in `<glob>`), with the same refusal text as os-sdlc. Backlog::RequiredWork#checks_not_typed replaces checks_without_command; HeadingContentIssues only renders the refusal. **Evidence:** Commits dadb4ce9 (feat, implementation 24: 13 dispatches, suite green on second suite-check, contract-auditor pass) and 1b7b96cd (History row) merged to main. Changed: plugins/os-backlog/lib/backlog/required_work.rb, heading_content_issues.rb, tests/template_validator_required_work_check_test.rb. **Follow-ups:** none — parity-fixture follow-up dropped by ticket-skeptic (comment above)
jared closed this issue 2026-10-02 20:13:14 +00:00
Author
Owner

Owner ruling 2026-10-07 (session 87a712ed): the duplicated check rule is a pain point for #969 (os-backlog gem, ADR-0279). No direct shared owner across the os-sdlc/os-backlog boundary.

Owner ruling 2026-10-07 (session 87a712ed): the duplicated check rule is a pain point for #969 (os-backlog gem, ADR-0279). No direct shared owner across the os-sdlc/os-backlog boundary.
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#847
No description provided.