Skip to content

Commit 7e705b6

Browse files
feat(ce-compound): learnings name their retirement condition (EveryInc#1772)
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
1 parent 943432a commit 7e705b6

13 files changed

Lines changed: 245 additions & 1 deletion

File tree

‎docs/guides/ce-compound-refresh.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ With no hint, the skill clusters the store and recommends a starting area. Inter
7676
- A learning still names `app/models/auth_token.rb` after the file became `session_token.rb`
7777
- The recommended fix is now an anti-pattern
7878
- Two learnings describe the same problem from different months and have started to disagree
79+
- A workaround learning whose upstream bug was fixed, with nothing saying the workaround can go
7980
- A pattern doc whose supporting learnings no longer back the rule
8081
- Code that disappeared quarters ago, with the learning still sitting there
8182
- `_archived/` folders that pollute search and nobody reads
@@ -102,6 +103,8 @@ The skill investigates each doc against the tree, then looks at the set (overlap
102103

103104
Each doc gets a specific action and an evidence bar. Age alone is not staleness, and typos are not a reason to edit. When a doc's claims about current mechanics disagree with the code, the doc changes. Guidance with independent support is different: if the implementation stops satisfying it, the skill preserves the guidance and reports the conflict as a potential product regression. It never edits or adjudicates product code.
104105

106+
A learning can declare a `retire_when` condition: the outside change, such as an upstream bug fix, that would retire it. The skill checks a declared condition and treats a met one as contradicting evidence, not age. When repo code still carries the workaround the learning explains, the learning stays and the report recommends removing that code. A condition the skill cannot check, for example because the tracker is unreachable, is noted in the report and does not change the learning on its own.
107+
105108
### Two modes
106109

107110
**Interactive** (default) applies Keep, Update, and obvious Consolidate directly. It asks before a Replace, a Split, a Delete that fails the auto-delete gate, or a Consolidate whose canonical doc is not obvious.

‎docs/guides/ce-compound.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ The track determines section order and frontmatter fields.
109109

110110
`problem_type`, `severity`, and `resolution_type` are closed enums. `component` and `root_cause` are open vocabulary, and the category directories are a default layout, not a mandate. When `docs/solutions/` already holds learnings, the classifier samples their frontmatter and directory names and reuses what the corpus already uses for the area. It falls back to the schema's suggested values only for an empty corpus or an uncovered area. Repos with their own documentation vocabulary keep it, so their existing retrieval still finds the new doc.
111111

112+
Some guidance holds only while something outside the repo holds: an open upstream bug, a tool or platform version, a dependency's behavior. Such a learning also records `retire_when`, which names the change that would retire it and how to check for it without changing the repo, so `ce-compound-refresh` can tell when it stops applying. Most learnings have no such condition and omit the field.
113+
112114
### Overlap, discoverability, and grounding
113115

114116
The Related Docs Finder scores overlap with existing `docs/solutions/` content across five dimensions: problem statement, root cause, solution approach, referenced files, prevention rules.
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
---
2+
title: Learning Retirement Condition - Plan
3+
type: feat
4+
date: 2026-09-23
5+
artifact_contract: ce-unified-plan/v1
6+
artifact_readiness: implementation-ready
7+
product_contract_source: ce-plan-bootstrap
8+
execution: code
9+
---
10+
11+
# Learning Retirement Condition - Plan
12+
13+
## Goal Capsule
14+
15+
- **Objective:** A learning can declare the outside change that would retire it. `ce-compound` sets the field on new captures whose guidance depends on an outside condition, the bun workaround learning declares it now, and `ce-compound-refresh` checks a declared condition when it audits the learning.
16+
- **Authority:** the user's request, then the project's active instructions (`AGENTS.md`: conditions not procedures, the skill-authoring standard, byte-mirrored support files), then this plan.
17+
- **Execution profile:** one native session. Prose edits to skill references, one learning, two guides, and one test file.
18+
- **Stop conditions:** stop and report if the change needs bytes in either `SKILL.md` body (both sit within 40 bytes of the 8,000-byte budget in `tests/codex-skill-prompt-budget.test.ts`). Stop if the field cannot stay optional without breaking an existing learning.
19+
- **Tail ownership:** the calling pipeline owns commit, push, PR, and CI.
20+
21+
---
22+
23+
## Product Contract
24+
25+
### Summary
26+
27+
Add an optional `retire_when` field to the learning frontmatter schema. `ce-compound` sets it only when the guidance depends on an outside condition. `ce-compound-refresh` checks a declared condition and treats a met one as contradicting evidence, not age. The bun workaround learning gets its condition in the same change.
28+
29+
### Problem Frame
30+
31+
arXiv 2609.12039 ("Reality Is the Final Verifier") argues that a lesson is a scoped rule that must retire when counterevidence arrives, and that knowledge bases rot when nothing says when a rule stops applying. CE learnings record scope (`applies_when`) and cause (`root_cause`), but nothing a later refresh can find that says when the guidance ends. `docs/solutions/developer-experience/bun-parallel-worker-loses-subprocess-exit.md` explains the TimeoutError re-run in `scripts/run-tests.ts` by citing open upstream bugs oven-sh/bun#34069 and #41024. Its retirement condition exists only as the last Prevention bullet, which no refresh check reads.
32+
33+
### Requirements
34+
35+
**Capture**
36+
37+
- R1. The learning schema offers an optional `retire_when` field on both tracks. It names the outside change that would retire the learning and a check for it that runs without changing the repo.
38+
- R2. A learning sets `retire_when` only when its guidance holds just while an outside condition holds (an open upstream bug, a tool or platform version, a dependency's behavior). Other learnings omit it.
39+
40+
**Refresh**
41+
42+
- R3. When a learning declares `retire_when`, the refresh checks whether the condition is now met and records the evidence in the report, not in the learning. It never infers a condition for a learning that declares none.
43+
- R4. A met condition is contradicting evidence, classified through the existing outcomes. When repo code still carries the workaround the learning explains, the doc stays (Keep, or Update where the evidence contradicts a claim it makes), and removing the workaround is the recommended action in that doc's report entry, never applied.
44+
- R5. A condition the refresh cannot check falls under the existing "Unverifiable is not false" rule.
45+
46+
**Repo example**
47+
48+
- R6. The bun workaround learning declares its retirement condition.
49+
50+
**Contract hygiene**
51+
52+
- R7. Existing learnings without the field stay valid. Schema, prose schema, template, and validation rules change together, byte-identical across `ce-compound` and `ce-compound-refresh`.
53+
- R8. The user guides for both skills describe the field and the refresh check.
54+
55+
### Scope Boundaries
56+
57+
- No new refresh outcome or report counter, and no status-like field such as a retired state or a last-checked date. "Retire" already names a `CONCEPTS.md` action in the refresh report.
58+
- No edits to either `SKILL.md` body.
59+
- No edits to the learnings-researcher prompt copies in `ce-plan`, `ce-ideate`, `ce-optimize`, and `ce-code-review`. They read frontmatter fields as-is.
60+
61+
#### Deferred to Follow-Up Work
62+
63+
- Off-enum values already in the corpus: `resolution_type: workaround` in the bun learning and `install_strategy` in `docs/solutions/integrations/native-plugin-install-strategy.md`. A corpus-wide enum check would catch both.
64+
- Backfilling `retire_when` on other learnings that cite an upstream bug.
65+
66+
---
67+
68+
## Planning Contract
69+
70+
### Key Technical Decisions
71+
72+
- KTD1. **A frontmatter field, not a body section.** A refresh can grep a field across the store. The schema already reaches every writer: the Full-mode Context Analyzer, Full assembly, Lightweight, and refresh's Replace successors (which may not invent fields outside the schema).
73+
- KTD2. **Optional on both tracks.** (session-settled: user-directed — chosen over a required field: existing learnings stay valid) The field goes under `optional_fields`, because the bun learning is bug-track and workaround learnings such as `docs/solutions/skill-design/harness-agent-gate-workaround.md` are knowledge-track.
74+
- KTD3. **Name `retire_when`, one quoted string.** It parallels `applies_when`. Its value states the retiring change, not the holding condition, and a check a refresh can run without changing the repo. A verification that needs a repo change (such as removing the workaround and watching CI) belongs in the learning's body as the step before removal. A top-level string gets the existing parser-safety check for unquoted ` #` and `: `, which array items skip. A compound condition such as the bun one fits in one string.
75+
- KTD4. **The capture rule lives in the schema and the template.** The schema description states when to set the field. Each track template carries one optional `retire_when` line whose placeholder says to omit it when no outside change applies. `references/assembly.md` and `references/lightweight.md` already validate against the schema, so they need no copy of the rule.
76+
- KTD5. **The orchestrator checks the condition; classify routes the result.** `references/investigate.md` adds the check and assigns it to the orchestrator, because investigation subagents see only the three verbatim clauses and would hit permission prompts on tracker queries. `references/classify.md` adds one judgment rule for a met condition. The refresh never changes product code, and a met condition is a reason to verify the removal, not proof it is safe (`docs/solutions/skill-design/harness-agent-gate-workaround.md`). So when the workaround is still in repo code, the doc stays, and removing the workaround is the recommended action in the doc's report entry (non-interactive: under **Recommended**), the same routing the named-guidance rule uses. The auto-delete gate is unchanged.
77+
- KTD6. **Pin the smallest falsifiable units.** In `tests/compound-support-files.test.ts`, assert that the parsed schema has `retire_when` under `optional_fields` and not under `required_fields`, that the template has one `retire_when:` line per track, and that the refresh `investigate.md` and `classify.md` name `retire_when`. The existing byte-equality test covers the refresh copies.
78+
79+
### Assumptions
80+
81+
- A declared condition's check may need the network (tracker state). When it is unreachable, R5 applies and the network stays outside correctness.
82+
- No Consolidate or Split flow edit. Only the bun learning carries the field after this change, so how a merge carries it can wait for a real case.
83+
- The bun learning's `resolution_type: workaround` stays as-is in this change (see Deferred).
84+
85+
### Sources
86+
87+
- `skills/ce-compound-refresh/references/classify.md`: the "Unverifiable is not false" rule and the descriptive-drift rule the new rule sits beside.
88+
- `skills/ce-compound-refresh/references/report.md`: Recommended already covers everything that never runs unattended.
89+
- `docs/solutions/skill-design/bound-contradiction-checks-to-named-guidance.md`: bound a refresh check to what the learning names, and pin the paragraph that owns the rule.
90+
- `docs/solutions/conventions/verify-externally-attributed-constraints-at-the-source.md`: check upstream claims at the primary source.
91+
- oven-sh/bun#34069 and oven-sh/bun#41024: both open on 2026-09-23.
92+
93+
---
94+
95+
## Implementation Units
96+
97+
### U1. Schema, prose schema, and template carry `retire_when`
98+
99+
- **Goal:** Offer the optional field and state when to set it.
100+
- **Requirements:** R1, R2, R7. KTD1-KTD4.
101+
- **Dependencies:** none.
102+
- **Files:** `skills/ce-compound/references/schema.yaml`, `skills/ce-compound/references/yaml-schema.md`, `skills/ce-compound/assets/resolution-template.md`, their byte-identical copies under `skills/ce-compound-refresh/`, `tests/compound-support-files.test.ts`.
103+
- **Approach:**
104+
1. Add `retire_when` to `optional_fields` in `schema.yaml` with a description that states R2's condition. Add a `validation_rules` entry saying the field is optional on both tracks and is one string.
105+
2. Mirror both in `yaml-schema.md`: an "Optional Fields (both tracks)" bullet and a numbered validation rule.
106+
3. Add one optional `retire_when:` line to each track's frontmatter block in the template.
107+
4. Copy the three files byte-for-byte into `ce-compound-refresh`.
108+
- **Patterns to follow:** the `framework_version` threading (schema block, validation rule, prose section) and the YAML-safety pins in `tests/compound-support-files.test.ts`.
109+
- **Test scenarios:**
110+
- Parsed `schema.yaml` has `optional_fields.retire_when` with a description, and `required_fields` has no `retire_when`.
111+
- The template contains exactly two lines that start with `retire_when:`, one per track.
112+
- The existing drift test passes, which proves the refresh copies match.
113+
- **Verification:** the focused tests pass. Schema, prose schema, and template name the field with the same meaning.
114+
115+
### U2. Refresh checks and classifies by `retire_when`
116+
117+
- **Goal:** The refresh checks a declared condition and routes a met one.
118+
- **Requirements:** R3, R4, R5. KTD5.
119+
- **Dependencies:** U1.
120+
- **Files:** `skills/ce-compound-refresh/references/investigate.md`, `skills/ce-compound-refresh/references/classify.md`, `tests/compound-support-files.test.ts`.
121+
- **Approach:**
122+
1. In `investigate.md`, add the declared-condition check as its own short paragraph after the dimensions. The orchestrator runs it through whatever interface the condition's source exposes, records the evidence and date or that it could not check, and never infers a condition.
123+
2. In `classify.md`, add one judgment rule next to the descriptive-drift rule. It covers R4 and cites "Unverifiable is not false" for R5.
124+
- **Patterns to follow:** the named-guidance check (#1265), which bounded a refresh check to what the learning names.
125+
- **Test scenarios:**
126+
- `investigate.md` names `retire_when`.
127+
- `classify.md` names `retire_when`.
128+
- Deleting either rule fails its assertion.
129+
- **Verification:** both references state the rule once. Neither `SKILL.md` changes.
130+
131+
### U3. The bun learning declares its retirement condition
132+
133+
- **Goal:** The repo's concrete example carries the field.
134+
- **Requirements:** R6.
135+
- **Dependencies:** U1.
136+
- **Files:** `docs/solutions/developer-experience/bun-parallel-worker-loses-subprocess-exit.md`.
137+
- **Approach:**
138+
1. Add a quoted `retire_when` naming both upstream issues closed as fixed in a released bun that CI's `bun-version: latest` installs, checked on the issue tracker and bun's releases.
139+
2. Rewrite the last Prevention bullet to point at the field and to name the verification before removal: repeated green CI runs with the TimeoutError re-run removed from `scripts/run-tests.ts`.
140+
3. Add `last_updated: 2026-09-23`.
141+
- **Test expectation:** none -- a learning doc. Run the bundled `validate-frontmatter.py` and `validate-doc-claims.py` against it.
142+
- **Verification:** both validators exit 0.
143+
144+
### U4. Guides
145+
146+
- **Goal:** User guides describe the field and the check.
147+
- **Requirements:** R8.
148+
- **Dependencies:** U1, U2.
149+
- **Files:** `docs/guides/ce-compound.md`, `docs/guides/ce-compound-refresh.md`.
150+
- **Approach:** One or two sentences in each: the frontmatter paragraph of the ce-compound guide, and the outcomes section of the refresh guide. Add no blockquotes, because `tests/ce-setup-instruction-file-offers.test.ts` parses the ce-compound guide's blockquotes.
151+
- **Test expectation:** none -- user docs.
152+
- **Verification:** the guides match the skill references.
153+
154+
---
155+
156+
## Verification Contract
157+
158+
| Gate | Command or check | Applies to |
159+
|---|---|---|
160+
| Focused tests | `bun test tests/compound-support-files.test.ts tests/pipeline-review-contract.test.ts tests/codex-skill-prompt-budget.test.ts tests/frontmatter-validator.test.ts` | U1, U2 |
161+
| Full suite | `bun run test` | all |
162+
| Release metadata | `bun run release:validate` | all |
163+
| Learning validators | `python3 skills/ce-compound/scripts/validate-frontmatter.py <doc>` and `python3 skills/ce-compound/scripts/validate-doc-claims.py <doc>` | U3 |
164+
| Behavioral eval | `bun run test:skill-eval-cell` on Claude and Codex with a fixture learning whose condition is checkable offline and met, so the R4 path fires; or the exact skip reason when no host CLI is on PATH | U2 |
165+
166+
## Definition of Done
167+
168+
- R1-R8 hold, and each unit's verification passes.
169+
- Neither `SKILL.md` body changed, and the budget test passes.
170+
- The refresh copies of the three support files are byte-identical to the `ce-compound` copies.
171+
- No dead-end edits remain in the diff.

‎docs/solutions/developer-experience/bun-parallel-worker-loses-subprocess-exit.md‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
---
22
title: "A bun test worker that loses one subprocess exit turns the rest of its file into exact-timeout failures"
33
date: 2026-09-11
4+
last_updated: 2026-09-23
45
category: developer-experience
56
module: test-suite
67
problem_type: test_failure
@@ -13,6 +14,7 @@ symptoms:
1314
root_cause: dependency_bug
1415
resolution_type: workaround
1516
severity: medium
17+
retire_when: "oven-sh/bun#34069 and oven-sh/bun#41024 are closed as fixed in a released bun, which CI installs through bun-version: latest; check both issues and bun's release notes"
1618
tags:
1719
- bun
1820
- bun-test-parallel
@@ -61,4 +63,4 @@ Two consequences shape the fix. The wedge lives in the worker's event loop state
6163
- Keep every failure in an affected file a `TimeoutError`. On 2026-09-18 (`main`) and 2026-09-20 (PR 1748) the red runs carried 19 `TimeoutError`s plus about ten `ENOENT: ... lstat '/tmp/ce-work-repo-template-*/repo'`: the workspace harness's cached repo template had gone missing mid-file, every later `makeRepo` threw `ENOENT`, and that one non-timeout error kind blocked the re-run. `seedTemplate` now reseeds when the cached directory is gone. What removes the directory is not established.
6264
- Do not raise timeouts or add `retry` for this signature; both re-run inside the same wedged worker.
6365
- Keep the re-run inside the package `test` script so CI and local runs stay the same command, as `AGENTS.md` already requires for `--parallel`.
64-
- Check the bun issue before touching this: when it is fixed and CI runs a bun with the fix, the re-run pass becomes dead weight and can be removed.
66+
- The re-run pass retires under this doc's `retire_when` condition. A closed issue is not proof on its own: bun 1.4.0 fixed the common case while this wedge persisted. Before removing the re-run from `scripts/run-tests.ts`, confirm that repeated CI runs of `bun run test` stay green without it. Remove everything that exists only to feed or describe the re-run in the same change, across code, tests, and docs; a search for `bun#34069`, `lost-child-exit`, and `rerunCandidates` is the place to start.

‎skills/ce-compound-refresh/assets/resolution-template.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ symptoms:
2323
root_cause: [corpus value, else schema suggested default]
2424
resolution_type: [schema enum]
2525
severity: [schema enum]
26+
retire_when: [optional; the outside change that would retire this doc and how to check for it, else omit this line]
2627
tags: [keyword-one, keyword-two]
2728
---
2829

@@ -69,6 +70,7 @@ component: [corpus value, else schema suggested default]
6970
severity: [schema enum]
7071
applies_when:
7172
- [Condition where this applies]
73+
retire_when: [optional; the outside change that would retire this doc and how to check for it, else omit this line]
7274
tags: [keyword-one, keyword-two]
7375
---
7476

0 commit comments

Comments
 (0)