Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
07eb7d5
docs(roadmap): draft FEAT-LOOP-2026-0249 changelog fragments
Nov 2, 2026
fd38d43
chore(roadmap): activate FEAT-LOOP-2026-0249
Nov 2, 2026
850b541
chore(loop): FEAT-LOOP-2026-0249 build pin recorded
Nov 2, 2026
6550f6e
chore(loop): FEAT-LOOP-2026-0249 plan baseline snapshot
Nov 2, 2026
223c086
feat: T01 — walking skeleton: the writer, the setting and the oracle
Nov 2, 2026
a61078f
feat: T02 — one reader for all three guards
Nov 2, 2026
88816ab
feat: T03 — collate fragments at release
Nov 2, 2026
1057d0e
feat: T04 — PR-time fragment check
Nov 2, 2026
3443a23
feat: T05 — ids migrate follows fragment traces
Nov 2, 2026
8be26de
chore(loop): gate 1 preexisting gate failure attributed to FEAT-LOOP-…
Nov 2, 2026
915e05c
test(caller-check): drop append_entry from the ratchet baseline
Nov 2, 2026
0a409ee
chore(loop): FEAT-LOOP-2026-0249 build pin recorded
Nov 2, 2026
6d8e19b
feat: T06 — skills, rules and docs call the writer
Nov 2, 2026
2dbecf2
feat: T07 — this repository opts in
Nov 2, 2026
13533c4
chore(loop): gate 1 broad run clean
Nov 2, 2026
2d8f716
chore(loop): FEAT-LOOP-2026-0249/G1-CLOSE blocked (judge-editing term…
Nov 2, 2026
dfbed42
fix: release() reports the fragments it removes (FEAT-LOOP-2026-0249)
Nov 3, 2026
0d4d14a
chore(loop): FEAT-LOOP-2026-0249/G1-CLOSE judge edits acknowledged, u…
Nov 3, 2026
9045539
chore(loop): FEAT-LOOP-2026-0249 build pin recorded
Nov 3, 2026
c51318c
chore(loop): gate 1 broad run clean
Nov 3, 2026
e649037
feat: G1-CLOSE — terminal close
Nov 3, 2026
9180eda
chore(loop): FEAT-LOOP-2026-0249/G1-CLOSE judge cost fold
Nov 3, 2026
3c3832b
chore(loop): gate 1 awaiting_review
Nov 3, 2026
69c6364
chore(loop): FEAT-LOOP-2026-0249/G1-CLOSE terminal flips
Nov 3, 2026
93517e0
chore(loop): FEAT-LOOP-2026-0249 stage ratchet lesson, record final e…
Nov 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
### T01#1

- **criterion:** The oracle's clause-1 class fails on HEAD and passes after, including its `single`-mode
- **oracle:** python3 -m unittest tests.test_changelog_fragments.TestClause1ConcurrentPrsDoNotConflict -b
- **kind:** `narrow`
- **state:** `pass`
- **proved_at_sha:** `c51318c37302fe6c6ff925b2fe34a8cc6dbce69e`
- **attempt:** `1`
- **covers:** specfuse/loop/changelog_fragments.py, specfuse/loop/layout.py, specfuse/loop/arm_eval.py, tests/test_changelog_fragments.py

### T01#2

- **criterion:** Clause 5's writer test passes: with no `changelog.mode` key, `add` leaves no `changelog.d/`
- **oracle:** python3 -m unittest tests.test_changelog_fragments.TestClause5NoModeKeyBehavesAsBefore.test_add_without_mode_edits_changelog_md -b
- **kind:** `narrow`
- **state:** `pass`
- **proved_at_sha:** `c51318c37302fe6c6ff925b2fe34a8cc6dbce69e`
- **attempt:** `1`
- **covers:** specfuse/loop/changelog_fragments.py, specfuse/loop/layout.py, specfuse/loop/arm_eval.py, tests/test_changelog_fragments.py

### T01#3

- **criterion:** `python3 -m unittest tests.test_judge_path_registry tests.test_changelog_release_wiring -b` passes.
- **oracle:** python3 -m unittest tests.test_judge_path_registry tests.test_changelog_release_wiring -b
- **kind:** `narrow`
- **state:** `pass`
- **proved_at_sha:** `c51318c37302fe6c6ff925b2fe34a8cc6dbce69e`
- **attempt:** `1`
- **covers:** specfuse/loop/changelog_fragments.py, specfuse/loop/layout.py, specfuse/loop/arm_eval.py, tests/test_changelog_fragments.py

### T02#1

- **criterion:** The oracle's clause-2 class is unskipped, fails on HEAD and passes after, for all three guards.
- **oracle:** python3 -m unittest tests.test_changelog_fragments.TestClause2GuardsReadFragments -b
- **kind:** `narrow`
- **state:** `pass`
- **proved_at_sha:** `c51318c37302fe6c6ff925b2fe34a8cc6dbce69e`
- **attempt:** `1`
- **covers:** specfuse/loop/closing_requirements.py, specfuse/loop/lint_closing.py, specfuse/loop/loop.py, specfuse/loop/arm_eval.py, tests/test_changelog_fragments.py

### T02#2

- **criterion:** `grep -c "parse_changelog" specfuse/loop/lint_closing.py` is 0: the check goes through the helper.
- **oracle:** grep -c parse_changelog specfuse/loop/lint_closing.py
- **kind:** `narrow`
- **state:** `pass`
- **proved_at_sha:** `c51318c37302fe6c6ff925b2fe34a8cc6dbce69e`
- **attempt:** `1`
- **covers:** specfuse/loop/closing_requirements.py, specfuse/loop/lint_closing.py, specfuse/loop/loop.py, specfuse/loop/arm_eval.py, tests/test_changelog_fragments.py

### T02#3

- **criterion:** `python3 -m unittest tests.test_terminal_auto_close_changelog tests.test_load_bearing_close_lint tests.test_judge_path_registry -b` passes.
- **oracle:** python3 -m unittest tests.test_terminal_auto_close_changelog tests.test_load_bearing_close_lint tests.test_judge_path_registry -b
- **kind:** `narrow`
- **state:** `pass`
- **proved_at_sha:** `c51318c37302fe6c6ff925b2fe34a8cc6dbce69e`
- **attempt:** `1`
- **covers:** specfuse/loop/closing_requirements.py, specfuse/loop/lint_closing.py, specfuse/loop/loop.py, specfuse/loop/arm_eval.py, tests/test_changelog_fragments.py

### T03#1

- **criterion:** The oracle's clause-3 class is unskipped, fails on HEAD and passes after, driving `release()`
- **oracle:** python3 -m unittest tests.test_changelog_fragments.TestClause3CollateAtRelease -b
- **kind:** `narrow`
- **state:** `pass`
- **proved_at_sha:** `c51318c37302fe6c6ff925b2fe34a8cc6dbce69e`
- **attempt:** `1`
- **covers:** specfuse/loop/changelog_fragments.py, scripts/bump_version.py, tests/test_changelog_fragments.py

### T03#2

- **criterion:** Clause 5's release test passes: with no fragments, `release()` output is byte-identical to
- **oracle:** python3 -m unittest tests.test_changelog_fragments.TestClause5NoModeKeyBehavesAsBefore.test_release_without_fragments_unchanged -b
- **kind:** `narrow`
- **state:** `pass`
- **proved_at_sha:** `c51318c37302fe6c6ff925b2fe34a8cc6dbce69e`
- **attempt:** `1`
- **covers:** specfuse/loop/changelog_fragments.py, scripts/bump_version.py, tests/test_changelog_fragments.py

### T03#3

- **criterion:** `python3 -m unittest tests.test_changelog_release_wiring tests.test_changelog_append_only -b` passes.
- **oracle:** python3 -m unittest tests.test_changelog_release_wiring tests.test_changelog_append_only -b
- **kind:** `narrow`
- **state:** `pass`
- **proved_at_sha:** `c51318c37302fe6c6ff925b2fe34a8cc6dbce69e`
- **attempt:** `1`
- **covers:** specfuse/loop/changelog_fragments.py, scripts/bump_version.py, tests/test_changelog_fragments.py

### T04#1

- **criterion:** The oracle's clause-4 class is unskipped, fails on HEAD and passes after: wrong class, bad
- **oracle:** python3 -m unittest tests.test_changelog_fragments.TestClause4CheckGate -b
- **kind:** `narrow`
- **state:** `pass`
- **proved_at_sha:** `c51318c37302fe6c6ff925b2fe34a8cc6dbce69e`
- **attempt:** `1`
- **covers:** specfuse/loop/changelog_fragments.py, .specfuse/verification.yml, tests/test_changelog_fragments.py

### T04#2

- **criterion:** `python3 -m specfuse.loop.changelog_fragments check` exits zero on this repository.
- **oracle:** python3 -m specfuse.loop.changelog_fragments check
- **kind:** `narrow`
- **state:** `pass`
- **proved_at_sha:** `c51318c37302fe6c6ff925b2fe34a8cc6dbce69e`
- **attempt:** `1`
- **covers:** specfuse/loop/changelog_fragments.py, .specfuse/verification.yml, tests/test_changelog_fragments.py

### T05#1

- **criterion:** A test in `tests/test_ids_migrate_all.py` fails on HEAD and passes after: an unkeyed
- **oracle:** python3 -m unittest tests.test_ids_migrate_all -b
- **kind:** `narrow`
- **state:** `pass`
- **proved_at_sha:** `c51318c37302fe6c6ff925b2fe34a8cc6dbce69e`
- **attempt:** `1`
- **covers:** specfuse/loop/ids.py, tests/test_ids_migrate_all.py

### T05#2

- **criterion:** `python3 -m unittest tests.test_ids_migrate tests.test_ids_migrate_all -b` passes.
- **oracle:** python3 -m unittest tests.test_ids_migrate tests.test_ids_migrate_all -b
- **kind:** `narrow`
- **state:** `pass`
- **proved_at_sha:** `c51318c37302fe6c6ff925b2fe34a8cc6dbce69e`
- **attempt:** `1`
- **covers:** specfuse/loop/ids.py, tests/test_ids_migrate_all.py

### T06#1

- **criterion:** `grep -c "changelog_fragments add" plugins/specfuse/skills/fix-bug/SKILL.md` is at least 1.
- **oracle:** grep -c 'changelog_fragments add' plugins/specfuse/skills/fix-bug/SKILL.md
- **kind:** `narrow`
- **state:** `pass`
- **proved_at_sha:** `c51318c37302fe6c6ff925b2fe34a8cc6dbce69e`
- **attempt:** `1`
- **covers:** plugins/specfuse/skills/fix-bug/SKILL.md, .specfuse/skills/fix-bug/SKILL.md, .specfuse/rules/close-discipline.md, specfuse/loop/data/rules/close-discipline.md, docs/lifecycles.md, specfuse/loop/data/docs/lifecycles.md

### T06#2

- **criterion:** `grep -c "changelog_fragments add" .specfuse/rules/close-discipline.md` is at least 1.
- **oracle:** grep -c 'changelog_fragments add' .specfuse/rules/close-discipline.md
- **kind:** `narrow`
- **state:** `pass`
- **proved_at_sha:** `c51318c37302fe6c6ff925b2fe34a8cc6dbce69e`
- **attempt:** `1`
- **covers:** plugins/specfuse/skills/fix-bug/SKILL.md, .specfuse/skills/fix-bug/SKILL.md, .specfuse/rules/close-discipline.md, specfuse/loop/data/rules/close-discipline.md, docs/lifecycles.md, specfuse/loop/data/docs/lifecycles.md

### T06#3

- **criterion:** `diff .specfuse/rules/close-discipline.md specfuse/loop/data/rules/close-discipline.md` is empty.
- **oracle:** diff .specfuse/rules/close-discipline.md specfuse/loop/data/rules/close-discipline.md
- **kind:** `narrow`
- **state:** `pass`
- **proved_at_sha:** `c51318c37302fe6c6ff925b2fe34a8cc6dbce69e`
- **attempt:** `1`
- **covers:** plugins/specfuse/skills/fix-bug/SKILL.md, .specfuse/skills/fix-bug/SKILL.md, .specfuse/rules/close-discipline.md, specfuse/loop/data/rules/close-discipline.md, docs/lifecycles.md, specfuse/loop/data/docs/lifecycles.md

### T06#4

- **criterion:** `python3 -m unittest tests.test_plugin_prose_cross_refs -b` passes.
- **oracle:** python3 -m unittest tests.test_plugin_prose_cross_refs -b
- **kind:** `narrow`
- **state:** `pass`
- **proved_at_sha:** `c51318c37302fe6c6ff925b2fe34a8cc6dbce69e`
- **attempt:** `1`
- **covers:** plugins/specfuse/skills/fix-bug/SKILL.md, .specfuse/skills/fix-bug/SKILL.md, .specfuse/rules/close-discipline.md, specfuse/loop/data/rules/close-discipline.md, docs/lifecycles.md, specfuse/loop/data/docs/lifecycles.md

### T07#1

- **criterion:** A test fails on HEAD and passes after: on this repository `python3 -m specfuse.loop.layout get
- **oracle:** python3 -m unittest tests.test_changelog_fragments.RepoOptInTests -b
- **kind:** `narrow`
- **state:** `pass`
- **proved_at_sha:** `c51318c37302fe6c6ff925b2fe34a8cc6dbce69e`
- **attempt:** `1`
- **covers:** .specfuse/layout.yml, tests/test_changelog_fragments.py

### T07#2

- **criterion:** `python3 -m unittest tests.test_changelog_schema tests.test_terminal_auto_close_changelog tests.test_changelog_fragments -b` passes.
- **oracle:** python3 -m unittest tests.test_changelog_schema tests.test_terminal_auto_close_changelog tests.test_changelog_fragments -b
- **kind:** `narrow`
- **state:** `pass`
- **proved_at_sha:** `c51318c37302fe6c6ff925b2fe34a8cc6dbce69e`
- **attempt:** `1`
- **covers:** .specfuse/layout.yml, tests/test_changelog_fragments.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
---
gate: 1
status: passed
feature_oracle: "python3 -m unittest tests.test_changelog_fragments -v -b"
cost_budget_usd: 26.00
baseline:
sha: 0a409ee618fd5ec5425ce8b8e8b40ac9654fee0e
probed_at: 2026-11-02T16:42:08.087200+00:00
tree: 12064a6191eeb830a9826d911a8543fbaba3da75:16339b18c202f8359cc4b4b153715fa6895a079e:1ccc3c2339db27f65e69ab179e57fa95b5890a80:27968248a450a842ccd187b7dce0fb0ded934866:2e29b2f7e073a0cbe290f01d3b397e1cdd10c81c:3472f7e05a2871a017bb583f5374d42e34db8ee1:42796ea0e0a6c03811b0e4bc9dbd3b5ee62dd2e4:4acc6dfc5e8cece358c167d876515eba4a39a500:67eeb758efbde89fb518954b0688723c7727b6b9:6c679295e2c653c7a11f14b3c522196f71302111:834d9d9c16094a386cd9b5a6f9ced9a3a9b4e7fb:ba9407c8db4db0875eafe248590b4ea99fc19601:bbd1aea514612d76722a18c7aece7c767cf1a076:c23b62e87f1ae3890c6f9e3d8c7ca7ec9225ed81:caffe2d63ceacde448842a91bc7a12c206a67523:e3b0de29b81a6c8e0abeacb3898519b98014b9ef:fa2f4f88657587b6e3b560977858810846b1d366:fb566fdbeba19af285d03c14d675b03bdc228c9f
entry_sha: 3443a23c22cf48eac997f12f7c9288e001cb22e9
source: attributed:FEAT-LOOP-2026-0249/T06
failing: []
broad_run:
tree: 0a000cd909475090c4edce24b4c49328b181ecce:12064a6191eeb830a9826d911a8543fbaba3da75:1ccc3c2339db27f65e69ab179e57fa95b5890a80:27968248a450a842ccd187b7dce0fb0ded934866:2e29b2f7e073a0cbe290f01d3b397e1cdd10c81c:3472f7e05a2871a017bb583f5374d42e34db8ee1:3cc7c83166be41e9e7ce443f464a64a013c95e9c:42796ea0e0a6c03811b0e4bc9dbd3b5ee62dd2e4:4acc6dfc5e8cece358c167d876515eba4a39a500:6a393dd51a83928fca749bc5db3b5dc8af45c24e:834d9d9c16094a386cd9b5a6f9ced9a3a9b4e7fb:bbd1aea514612d76722a18c7aece7c767cf1a076:c23b62e87f1ae3890c6f9e3d8c7ca7ec9225ed81:caffe2d63ceacde448842a91bc7a12c206a67523:e3b0de29b81a6c8e0abeacb3898519b98014b9ef:e883a6ff3b3d7e83913db0a22f910837f8165dca:f738b5b5afd022f9a508f73b69476da2a2c9a8fd:fa2f4f88657587b6e3b560977858810846b1d366
ran_at: 2026-11-03T13:06:22.647664+00:00
ok: true
failing: []
---

# Gate 1 — concurrent PRs stop conflicting on the changelog, and no entry gets lost

## Definition of done

**Stated as an outcome.** In temporary git repositories driven through the real code:

1. Two branches each run `add` in `fragments` mode, and the second merges clean with real
`git merge` after the first. Control: the same flow in `single` mode conflicts.
2. A feature whose entry exists only as a fragment passes `close-k`, `specfuse lint --closing`
and the terminal auto-close guard. A feature with no entry in either form fails all three.
3. A release leaves every fragment in the new section, the section parses clean, and no
consumed fragment remains. A malformed fragment refuses the release with the tree untouched.
4. `check` exits non-zero naming a malformed fragment, and zero on a valid or absent directory.
5. A project with no `changelog.mode` key behaves as before: `add` edits `CHANGELOG.md`, and a
release with no fragments produces the same output as today.

The `feature_oracle` proves 1–5. It is red on the current tree;
`FEAT-LOOP-2026-0249/T01` is the walking skeleton that creates it and turns clause 1 and clause 5's writer
half green, with every other class `unittest.skip` naming its owner (T02–T04). A skip
surviving to the close is a hollow oracle.

## Arming discipline (see `.specfuse/rules/planning-discipline.md`)

- **One reader, one writer.** All three guards go through one helper; skills and rules call `add`.
- **Positive controls.** Clause 1 pairs with the conflicting `single` flow, clause 2 with the
no-entry failure.
- **Temp repos only.** Fixture commits set `-c commit.gpgsign=false`.
- **Expected self-halt.** `loop.py`, `closing_requirements.py`, `lint_closing.py` and shipped
rules are judge surfaces; acknowledge the stop with your own reason and `/unblock-wu`.

## Reflection notes

<Written by the human at review time.>
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<!--
Copyright 2026 Specfuse Contributors
Licensed under the Apache License, Version 2.0. See LICENSE.
-->

# Lessons staged for promotion — FEAT-LOOP-2026-0249

`autonomy_default: auto`, so `FEAT-LOOP-2026-0249/G1-CLOSE` may not append to
`.specfuse/LEARNINGS.md` directly (`assert_learnings_staged_under_auto`). Nothing generalizes
beyond what the plan already cites: the feature's two borrowed lessons
(`[FEAT-LOOP-2026-0058/G1-CLOSE]`, `[FEAT-LOOP-2026-0023/G1-CLOSE]`) both held, so the close
staged no new entry. The entry below was added by the operator's session at wrap, after the close.

## Entries

- [FEAT-LOOP-2026-0249/T01] **A unit that gives a function its first production caller must
drop that function from the caller-check ratchet's `BASELINE` in the same unit.** T01 made
`append_entry` a real caller target, which shrank the baseline, and
`test_the_baseline_has_not_silently_shrunk` went red. Its narrow tier did not select that
repo-wide test, so T01 passed and the failure surfaced at T06's baseline probe, attributed to
T06, as a `preexisting_gate_failure` halt that needed a human. Rule: when a unit wires up
previously uncalled code, name the ratchet update in its acceptance and run
`tests.test_caller_check_ratchet` in its verification; a green narrow tier does not cover
repo-wide ratchets.
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
{
"gates": [
{
"gate": 1,
"work_units": [
{
"goal": "T01 \u2014 walking skeleton: the writer, the setting and the oracle",
"id": "FEAT-LOOP-2026-0249/T01",
"planned_cost_usd": 3.0,
"type": "implementation"
},
{
"goal": "T02 \u2014 one reader for all three guards",
"id": "FEAT-LOOP-2026-0249/T02",
"planned_cost_usd": 3.5,
"type": "implementation"
},
{
"goal": "T03 \u2014 collate fragments at release",
"id": "FEAT-LOOP-2026-0249/T03",
"planned_cost_usd": 3.0,
"type": "implementation"
},
{
"goal": "T04 \u2014 PR-time fragment check",
"id": "FEAT-LOOP-2026-0249/T04",
"planned_cost_usd": 1.5,
"type": "implementation"
},
{
"goal": "T05 \u2014 ids migrate follows fragment traces",
"id": "FEAT-LOOP-2026-0249/T05",
"planned_cost_usd": 1.5,
"type": "implementation"
},
{
"goal": "T06 \u2014 skills, rules and docs call the writer",
"id": "FEAT-LOOP-2026-0249/T06",
"planned_cost_usd": 2.0,
"type": "implementation"
},
{
"goal": "T07 \u2014 this repository opts in",
"id": "FEAT-LOOP-2026-0249/T07",
"planned_cost_usd": 1.0,
"type": "implementation"
},
{
"goal": "G1-CLOSE \u2014 terminal close",
"id": "FEAT-LOOP-2026-0249/G1-CLOSE",
"planned_cost_usd": 5.0,
"type": "close"
}
]
}
]
}
Loading
Loading