diff --git a/CHANGES.md b/CHANGES.md
index 281ad59..444a948 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -25,6 +25,22 @@ Each entry has a permanent anchor, `#vMAJOR.MINOR.PATCH`, and a matching git
tag, so a project can link to the exact release it was checked with. Headings
may be reworded; anchors never change.
+
+## 1.0.1: an archive checks a record against its section's spec (template schema 2)
+
+**For every project:**
+- **Closed entries can be archived in full.** Inside a role's `archive.md`,
+ a record is now checked against the spec whose `in` names the section it
+ sits under, when a template declares one; otherwise against the first
+ matching spec, as before. So a project can add, to an adapted delta
+ template, a second `D-` record spec with `in="Closed"`, and archive closed
+ entries with the fields a closed entry has. Before, every archived `D-`
+ record was checked against the Open spec and needed `Waits on`. Outside an
+ archive nothing changes, so a full record cannot sit under Closed in
+ `delta.md` and be counted as open.
+
+**Updating documents:** nothing to do. A pin of `"1.0"` stays valid.
+
## 1.0.0: first public release (template schema 2)
diff --git a/skill/tools/ipq.py b/skill/tools/ipq.py
index 85b7dbb..9241193 100644
--- a/skill/tools/ipq.py
+++ b/skill/tools/ipq.py
@@ -50,7 +50,7 @@
sys.modules.setdefault("ipq", sys.modules[__name__]) # so ipq_visuals shares this module when run as a script
import ipq_visuals as visuals # noqa: E402
-__version__ = "1.0.0"
+__version__ = "1.0.1"
SKILL = Path(__file__).resolve().parents[1]
TEMPLATES = SKILL / "templates"
@@ -451,8 +451,15 @@ def rel(self, path):
def parse_records(doc, specs):
"""Find heading records, condensed records, and table records in `doc`."""
- def spec_for(id, kind):
- return next((s for s in specs if s.kind == kind and s.matches(id)), None)
+ def spec_for(id, kind, section=None):
+ found = [s for s in specs if s.kind == kind and s.matches(id)]
+ if section is not None and is_archive(doc):
+ # An archive holds finished records in full, under the section that names their
+ # finished state, so a spec declared for that section wins over the working one.
+ placed = [s for s in found if section in s.sections]
+ if placed:
+ return placed[0]
+ return found[0] if found else None
for h in doc.headings:
if h.level < 3:
@@ -460,7 +467,7 @@ def spec_for(id, kind):
m = RECORD_HEADING.match(plain(h.text).strip())
if not m or not re.search(r"[\d-]", m.group(1)):
continue
- spec = spec_for(m.group(1), "record")
+ spec = spec_for(m.group(1), "record", doc.section_of(h.line))
if not spec:
continue
r = Record(m.group(1), "heading", spec, doc.section_of(h.line), h.line, m.group(2))
diff --git a/tests/test_ipq.py b/tests/test_ipq.py
index eb8d0e2..f27f14f 100644
--- a/tests/test_ipq.py
+++ b/tests/test_ipq.py
@@ -378,6 +378,29 @@ def adapt(self, role, old, new):
self.assertIn(old, text)
p.write_text(text.replace(old, new, 1))
+ def test_an_archive_uses_the_spec_for_its_section(self):
+ """A closed entry archived in full answers to a Closed record spec, not the Open one.
+ The preference holds only inside an archive; delta.md keeps the first matching spec."""
+ upstream = (REPO / "skill" / "templates" / "delta.md").read_text()
+ open_line = next(l for l in upstream.splitlines() if l.startswith('')
+ self.config(history={"delta": "archive"})
+ with open(self.docs / "delta.md", "a") as f:
+ f.write("\n## Closed\n\n- **D-009** Retention was undecided — settled in the prd"
+ " · found 2026-09-01 · closed 2026-09-10\n")
+ (self.docs / "delta").mkdir()
+ (self.docs / "delta" / "archive.md").write_text(
+ "# Acme — Delta archive\n\n**Answers:** the full text of closed entries.\n\n"
+ "## Summary\n\nClosed entries.\n\n## Contents\n\n## Closed\n\n"
+ '\n### D-009 — Retention was undecided\n\n'
+ "- **Owner:** product\n- **Found:** 2026-09-01\n- **Closed:** 2026-09-10\n"
+ "- **Canonical source:** [R-CORE-01](../prd.md#r-core-01)\n")
+ self.fix()
+ archived = [f for f in self.findings() if f[1].name == "archive.md" and f[0] == "error"]
+ self.assertEqual(archived, [], archived)
+ self.assertNotIn(("error", "fields"), self.rules())
+
def test_adapted_template_adds_a_section_and_a_field(self):
self.adapt("backlog", "## Blocked\n", "## Readiness track\n\n\n## Blocked\n")
self.adapt("backlog", "Blocked by; Done when*", "Blocked by; Track; Done when*")