An acknowledgement can carry its own deadline - #247
Merged
Merged
Conversation
A directive is a promise about the future. Some of those promises are permanent -- "this page is that history" will be true for as long as the page exists. Others are not: #58 asks for "reference a proposed decision you want to circle back to", and "circle back" has a horizon the acknowledgement does not. So it outlives the intent that produced it, silently, because its whole job is to silence a check. That is the failure directives exist to prevent, one level up. <!-- inactive-ok: ADR-028 until 2026-10-01 — revisit when the API settles --> After that date `find` does not return the directive at all. That function is the one place directives are read -- ten call sites go through it -- so dropping it there makes "behaves as if the annotation isn't even there" true everywhere without a single consumer knowing the feature exists. `until` is parsed by the same parser that reads the name and the scope suffix, so it is part of the shape rather than a feature of one word, and it is lifted out of `args` because it is a modifier and not one of the things the directive names. The date is inclusive: good on the 1st, gone on the 2nd. Two loudness rules follow from what this is for. What expired is reported, as its own lint finding rather than folded into "no longer apply" -- those are different facts, and inert must not mean invisible. And a date that cannot be read leaves the directive LIVE and is reported: dropping a suppression over a typo breaks a build for a reason the message would not explain, but a typo silently meaning "forever" is the exact rot this stops. Deferred, with the reason recorded: a version milestone (`until 1.0.0`, the other half of #58) needs a project-version concept luria does not have -- __init__ carries the package's version, not the record's, and an adopter's record is not luria. It parses in the same slot when one exists. Nothing here uses `until` yet, deliberately: every acknowledgement in this record is permanent, and inventing a deadline to demo the feature would be writing down a decision nobody made. Fired once before trusting it: a planted `until 2026-09-01` on docs/adopting.md reported "docs/adopting.md:1: `inactive-ok: ADR-010` expired 2026-09-01 — check the naming story again after 1.0", then reverted. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YSRQzpm4kAWQSaKWD6V9x2
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #58 — the date half. The version half is deferred with the reason written down; see below.
Summary
A directive is a promise about the future. Some are permanent —
inactive-ok-file: ADR-010 — this page is that historywill be true for as long as the page exists. Others are not, and #58 names the shape: reference a proposed decision that you want to circle back to. "Circle back" has a horizon; the acknowledgement doesn't. So it outlives the intent that produced it, silently, because its entire job is to silence a check.That is the failure directives exist to prevent, reproduced one level up.
After that date
luriabehaves as if the directive were never written. The date is inclusive — good on the 1st, gone on the 2nd, because somebody writing it means the last day it holds.Three things that make it this shape
It is part of the syntax, not a feature of one directive.
untilis read by the same parser that reads the name and the scope suffix, so every directive gets it — ADR-008's "a third directive is a name, not a new syntax", kept for a modifier too. A test walksinactive-ok,unexempt,target-okandurl-okto hold that.An expired directive is dropped at
find. That is the one place directives are read — ten call sites go through it — so dropping it there makes "behaves as if the annotation isn't even there" true everywhere without a single consumer knowing the feature exists. The expiry is lifted out ofargson the way, because it is a modifier rather than one of the things the directive names; leaving it in would make every consumer that validates arguments reportuntilas an unknown one.Inert must not mean invisible.
luria lintreports what expired as its ownexpired-directivesfinding — file, directive, date, and the author's own reason:Separate from "no longer apply", because they are different facts: a stale acknowledgement means the subject moved under it (the document went Active), an expired one did its job and ran out of the time its author gave it. A check that starts failing again with the acknowledgement still sitting above it is a puzzle, not a report.
A date it can't read leaves the directive live, and says so.
until nextweekis a typo. Dropping a suppression over one breaks a build for a reason the failure message would not explain; keeping it and naming the typo says what to fix without breaking anything. The third option — a typo quietly meaning "forever" — is the exact rot this stops.What's deferred, and why
until 1.0.0, the version-milestone flavour you raised by pointing at #189. Right idea for a deadline that's about a release rather than a calendar, and not shippable yet: it needs to know which version, and luria has no concept of the project's own version at all.luria/__init__.pycarries the package's version, not the record's, and an adopter's record isn't luria. #189's own sunset is a prose string for exactly that reason.Deferred rather than declined — when a project-version source exists,
until 1.0.0parses in the same slot and the only new code is the comparison. The ADR records that so the next person doesn't re-derive it.A relative duration (
for 2w, how you first phrased it) is declined outright, on the anchor: a duration is measured from something, and a comment carries no reliable "written on". Git says when the line last moved, which isn't when the promise was made — a reformat would silently extend every deadline in the repo. The absolute date is the anchor, written where the reader can see it. "Two weeks" stays the way to think about it; a helper that does the arithmetic is a convenience this doesn't need to decide.Nothing here uses it yet, deliberately
Every acknowledgement currently in this record is permanent — this paragraph is about that decision, this page is that history. Giving one an invented deadline to demonstrate the feature would be writing down a decision nobody made, which is a different thing from the dogfooding #245 needed, where the record genuinely wanted the new behaviour and the old one was broken. The first real use is the next provisional acknowledgement somebody writes.
Changelog
record/changelog.d/claude-directive-ttl.mdDevlog
docs-generatejobVerification
Test-first, 15 cases in
tests/test_directive_ttl.pywritten against a parser that didn't have the field yet, watched fail, then implemented. They pin the boundary in both directions (live on the date, gone the day after), the partition (findandfind_expirednever both return the same directive), uniformity across four directive names, and that a malformed expiry keeps the codes it names so whatever it was excusing stays excused.Fired once on a real file before trusting the report — planted
<!-- inactive-ok: ADR-010 until 2026-09-01 — … -->at the top ofdocs/adopting.md, ranluria lint, got the line quoted above, reverted. The unit tests build their own fixtures; this is the one that proves the corpus sweep reaches a real document.The clock is injected.
find(..., as_of=...)defaults to today and followsadr_pending's existing convention. A test that depends on the wall clock passes until the day it doesn't.python -m pytest tests -q→ 1116 passed, 33 skipped (1101 + 15), with no existing test modified — a directive withoutuntilparses exactly as before, which is the upgrade-safety claim and is pinned directly.luria lintclean.🤖 Generated with Claude Code
https://claude.ai/code/session_01YSRQzpm4kAWQSaKWD6V9x2
Generated by Claude Code