Skip to content

The issue Lifecycle as data, checked for termination - #59

Merged
don-petry merged 2 commits into
mainfrom
slice-0/issue-lifecycle-data
Oct 15, 2026
Merged

don-petry merged 2 commits into
mainfrom
slice-0/issue-lifecycle-data

Conversation

@don-petry

@don-petry don-petry commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Part of the epic "Epic: slice 0, foundations. The repo holds the line, its context is tested, and the platform facts are recorded".

Declares the issue Lifecycle of design §4.1 as data beside the pull request's, and proves that it terminates. The design merged in the pull request "Design the issue Lifecycle: triage, Readiness, the hand-over to a pull request, and its bounds", through the merge queue, after a third, narrow check that found no blocker.

What changes

  • lifecycle/issue.json: nine States (new, triage, ready, in_progress, in_review, held, not_served, done, declined), with done and declined terminal, and 65 transitions. The 32 arrows drawn, two of them made by the Factory or a person alike; every arrow the prose under the diagram declares ("Any State but done may move to declined…"); and eight the derivation table allows that neither the diagram nor the prose states, listed below.
  • Bounds: max_attempts on ready to in_progress and back; max_unmerged_closes on in_progress or in_review back to ready or triage; max_runs_per_item on triage to ready. The Lifecycle schema accepts max_unmerged_closes, and its number, one ("starting at one"), is in config/defaults.json beside the other limits.
  • The States only a person may leave are data: person_only_exits, accepted by the schema. The pull request's Lifecycle lists held; the issue's lists held and not_served, which for an issue is not terminal. The check refuses a transition out of one of them by the Factory or a Lens, and a Lifecycle that has held and does not list it. "Only a person, or the platform acting for one" is kept: the pull request's Lifecycle releases a Hold to verifying by the platform.
  • tools/check_lifecycle.py: run(root) checks every Lifecycle under lifecycle/, requires one for each Work item the schema names, and requires each bound to be a limit in config/defaults.json. It validates each against the Lifecycle schema first, and reports malformed data rather than raising. check(lc) checks one. Until now make verify loaded the pull request's alone.
  • The counter-example: with max_attempts taken off, the Factory can cycle from ready to in_progress and back, and the check rejects it. The same holds for max_unmerged_closes. It is a test of the check, as the pull request's Lifecycle counter-examples are, because a Lifecycle with no bound is still valid against the schema.

Not here: the Kernel's derivation and Admission for an issue, and issues in the simulator. Those are the three stories that follow.

A known gap. Deleting max_runs_per_item from triage to ready passes every check today. Its cycle goes back through ready to triage, which a person makes, and the check counts only cycles made wholly by the Factory. That belongs to the story "Lifecycle check: bounds in the data for every cycle with no person in it" (number 38).

Open questions

  1. Eight transitions the derivation table allows that neither the diagram nor the prose states. All are declared here:
    • new, triage, ready, held and not_served to done, by a person: row 1 ranks first, so merging a linked pull request ends the issue from any State.
    • ready to in_review, by a person who opens their own pull request that is not a draft (rows 6 and 8).
    • triage to held by the Factory when the Run ceiling is reached (row 4, "The Kernel holds the issue"). The design draws triage to held only as a person's act.
    • new to triage by a person with write access who adds factory:triage (I-41).
  2. in_review is stale "as its pull request". The schema takes one number, so in_review: 30 borrows the stale time of verifying, the State a pull request enters when marked ready.
  3. An outsider's edit that lapses acceptance is declared human. So is the content change behind ready to triage, which is made by a writer, since an outsider's edit lapses acceptance. The trial script used platform for it; no check result depends on the choice.
  4. Closing and merging are declared human, as in the pull request's Lifecycle, though GitHub carries out a commit's closing keyword.

For the Kernel stories

  • in_progress to ready by the Factory is declared twice, with different bounds: max_attempts when an opening Run ends having opened nothing, and max_unmerged_closes when a linked pull request closes unmerged. The Kernel must tell the two causes apart from the facts, and count each against its own bound.
  • Row 4 derives held from conditions that can clear with no person acting: the Run ceiling is counted over a window, so held can end when Runs age out of it. That contradicts held in person_only_exits. It needs a simulator test in the story for issues in the simulator (number 43).

Verification

  • make verify: 124 tests (5 expected to fail), 7 checks, all ok.
  • python3 -m tools.mutants: 20 of 20 killed.
  • Each new test in tests/test_tools.py failed before the change it tests: the counter-example for max_attempts, the planted transition from not_served to ready by the Factory, and a Lifecycle that is a JSON list or has a transition missing by.

Closes #18

🤖 Generated with Claude Code

View guided diff Turn on auto-fix

don-petry and others added 2 commits October 14, 2026 17:18
Slice 0 declares the issue Lifecycle of design section 4.1, merged by the pull
request "Design the issue Lifecycle: triage, Readiness, the hand-over to a pull
request, and its bounds" (number 22), and proves that it terminates.

- lifecycle/issue.json: nine States, two terminal, 65 transitions. The 32
  arrows drawn, two of them made by the Factory or a person alike; every arrow
  the prose under the diagram declares; and five the derivation table allows
  that the text does not state, named in the pull request as open questions.
- Bounds: max_attempts on ready to in_progress and back; max_unmerged_closes on
  in_progress or in_review back to ready or triage; max_runs_per_item on
  triage to ready. The Lifecycle schema accepts max_unmerged_closes, and its
  number, one, is in config/defaults.json beside the others.
- tools/check_lifecycle.py: run(root) checks every file under lifecycle/,
  requires one for each Work item the schema names, and requires each bound to
  be a limit in config/defaults.json. check(lc) checks one Lifecycle.
- The counter-example: with max_attempts taken off, the Factory can cycle from
  ready to in_progress and back, and the check says so. The same for
  max_unmerged_closes.

Not here: the Kernel's derivation and Admission for an issue, and issues in the
simulator. The check still cannot see max_runs_per_item, whose cycle passes
through an edit.

make verify: 121 tests (5 expected to fail), 7 checks. python3 -m tools.mutants: 20 of 20 killed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… bad data

From the review of the previous commit, which found no blocker.

- An issue's not_served is not terminal, and nothing stopped the Factory
  leaving it: a transition from not_served to ready by the Factory passed.
  The States only a person, or the platform acting for one, may leave are now
  data: person_only_exits in each Lifecycle, accepted by the schema. The pull
  request's lists held; the issue's lists held and not_served. The check
  refuses a Lifecycle that has held and does not list it.
- tools/check_lifecycle.py validates each Lifecycle against its schema before
  checking it, and reports a file that is not a Lifecycle, a transition with a
  field missing, or a configuration or schema it cannot use, instead of
  raising.

Unchanged: only a transition by the Factory counts toward an unbounded cycle.
That is the story "Lifecycle check: bounds in the data for every cycle with no
person in it".

make verify: 124 tests (5 expected to fail), 7 checks. python3 -m tools.mutants: 20 of 20 killed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@don-petry
don-petry requested a review from a team as a code owner October 14, 2026 22:28
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@coderabbitai

coderabbitai Bot commented Oct 14, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 14 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: Repository: combsmith/software-factory/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 279570d9-17f8-48c5-bafe-97dc908476f6
📥 Commits

Reviewing files that changed from the base of the PR and between 1f4ed08 and 38dc4b3.

📒 Files selected for processing: 10
  • README.md
  • config/defaults.json
  • lifecycle/AGENTS.md
  • lifecycle/issue.json
  • lifecycle/pull_request.json
  • schemas/config.schema.json
  • schemas/lifecycle.schema.json
  • tests/test_tools.py
  • tools/check_examples.py
  • tools/check_lifecycle.py
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
0.0% Coverage on New Code (required ≥ 80%)

See analysis details on SonarQube Cloud

@don-petry
don-petry added this pull request to the merge queue Oct 15, 2026
Merged via the queue into main with commit 464fd29 Oct 15, 2026
8 of 9 checks passed
@don-petry
don-petry deleted the slice-0/issue-lifecycle-data branch October 20, 2026 17:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

The issue Lifecycle as data, checked for termination

1 participant