Skip to content

Commit d287174

Browse files
igerberclaude
andcommitted
docs(v4): give param-value rows a real lifecycle (review feedback)
param-value rows now share the symbol lifecycle instead of being schema-only: planned -> shimmed -> removed statuses, test_ref required at shim/removal (value behavior - old spelling warns then rejects, new spelling accepted - lives in that suite), and full due-gate coverage (overdue and early-removal). No reality probe (accepted values are not introspectable). Negative fixtures added for both the schema rule and the due gate; spec section 11 updated in the same diff. Closes the M-086 park-silently gap. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017eVKbBqzenDKa5idwtexQa
1 parent a2a28a7 commit d287174

2 files changed

Lines changed: 50 additions & 9 deletions

File tree

docs/v4-design.md

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -451,7 +451,9 @@ state). `deprecated_in`/`removed_in`/`new` are required-present but nullable;
451451
versions match `\d+\.\d+(\.\d+)?`.
452452

453453
**Kinds.** `param` (constructor or method parameter), `param-value` (accepted
454-
value spelling; schema-checked only), `class`, `field` (results attribute),
454+
value spelling; full symbol lifecycle and due gate but NO reality probe -
455+
accepted values are not introspectable, so behavioral enforcement lives in
456+
the row's `test_ref` suite), `class`, `field` (results attribute),
455457
`function`, `alias` (top-level export alias), `default-flip` (same param, new
456458
default), `env-default` (environment-variable-resolved default),
457459
`warning-retirement` (a warning message scheduled to disappear), `behavior`
@@ -468,8 +470,10 @@ never a legal absence.
468470

469471
**Status lifecycle.** Symbol kinds (param/class/field/function): `planned` ->
470472
`shimmed` -> `removed`; the `shimmed` stop may be skipped only when the row's
471-
deprecation rides a parent row (stated in `notes`, e.g. [M-011]). Non-symbol
472-
kinds (alias/default-flip/env-default/warning-retirement/behavior): `planned`
473+
deprecation rides a parent row (stated in `notes`, e.g. [M-011]).
474+
`param-value` rows follow the same lifecycle (with `test_ref` required at
475+
`shimmed`/`removed` and due-gate coverage). Remaining non-symbol kinds
476+
(alias/default-flip/env-default/warning-retirement/behavior): `planned`
473477
or `evaluate` -> `done`. Terminal rows (`removed`/`done`) keep asserting
474478
forever - a removed symbol resurrecting is a test failure.
475479

@@ -492,7 +496,11 @@ forever - a removed symbol resurrecting is a test failure.
492496
"on" expects True, "off" expects False).
493497
- `warning-retirement`: `snippet` present in the `code_refs` file (`done`:
494498
absent).
495-
- `param-value`/`behavior`: schema + `code_refs` existence only.
499+
- `param-value`: schema + due gate + `test_ref` existence at
500+
`shimmed`/`removed`; no reality probe (value behavior asserted in the
501+
`test_ref` suite: old spelling warns at shim, rejected at removal, new
502+
spelling accepted).
503+
- `behavior`: schema + `code_refs` existence only.
496504
- Release gate (all kinds): once `diff_diff.__version__` reaches a row's
497505
`removed_in` (symbol/alias rows), flip version (`deprecated_in` on
498506
default-flip/warning-retirement/behavior rows), or `decision_due`

tests/test_v4_matrix.py

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@
6161
"behavior",
6262
}
6363
KINDS = SYMBOL_KINDS | NONSYMBOL_KINDS
64+
# param-value rows share the symbol LIFECYCLE (planned -> shimmed -> removed, test_ref at
65+
# shim/removal, due-gated) but skip reality probes - accepted values are not introspectable;
66+
# their behavioral enforcement lives in the row's test_ref suite.
67+
LIFECYCLE_KINDS = SYMBOL_KINDS | {"param-value"}
6468
SYMBOL_STATUSES = {"planned", "shimmed", "removed"}
6569
NONSYMBOL_STATUSES = {"planned", "evaluate", "done"}
6670
WARNING_VALUES = {"FutureWarning", "DeprecationWarning"}
@@ -188,7 +192,7 @@ def validate_schema(rows):
188192
if kind not in KINDS:
189193
errors.append(f"{rid}: unknown kind '{kind}'")
190194
continue
191-
legal = SYMBOL_STATUSES if kind in SYMBOL_KINDS else NONSYMBOL_STATUSES
195+
legal = SYMBOL_STATUSES if kind in LIFECYCLE_KINDS else NONSYMBOL_STATUSES
192196
if status not in legal:
193197
errors.append(
194198
f"{rid}: status '{status}' illegal for kind '{kind}' (legal: {sorted(legal)})"
@@ -199,7 +203,7 @@ def validate_schema(rows):
199203
errors.append(f"{rid}: {vfield}={val!r} does not match \\d+.\\d+(.\\d+)?")
200204
if row.get("warning") is not None and row["warning"] not in WARNING_VALUES:
201205
errors.append(f"{rid}: warning={row['warning']!r} not in {sorted(WARNING_VALUES)}")
202-
if kind in SYMBOL_KINDS and status in ("shimmed", "removed") and not row.get("test_ref"):
206+
if kind in LIFECYCLE_KINDS and status in ("shimmed", "removed") and not row.get("test_ref"):
203207
errors.append(
204208
f"{rid}: status '{status}' requires a test_ref (dedicated behavioral test)"
205209
)
@@ -447,7 +451,7 @@ def collect_due_problems(rows, current):
447451
f"still in the future - early removal/flip breaks the shim window promise"
448452
)
449453
if removed_in and current >= _version_tuple(removed_in):
450-
if kind in SYMBOL_KINDS and status != "removed":
454+
if kind in LIFECYCLE_KINDS and status != "removed":
451455
problems.append(f"{rid}: removed_in {removed_in} is due but status is '{status}'")
452456
if kind == "alias" and status != "done":
453457
problems.append(f"{rid}: removed_in {removed_in} is due but status is '{status}'")
@@ -469,7 +473,7 @@ def collect_due_problems(rows, current):
469473
"(new surface not shipped?)"
470474
)
471475
if (
472-
kind in SYMBOL_KINDS
476+
kind in LIFECYCLE_KINDS
473477
and row.get("warning")
474478
and row.get("deprecated_in")
475479
and current >= _version_tuple(row["deprecated_in"])
@@ -585,6 +589,22 @@ def test_all_membership_helper_semantics():
585589
"removed_in 4.0 is due",
586590
(3, 9, 0),
587591
),
592+
# overdue param-value removal: value migrations are due-gated like symbol rows
593+
(
594+
{
595+
"id": "M-906",
596+
"kind": "param-value",
597+
"old": "diff_diff:WooldridgeDiDResults.aggregate[type]=event",
598+
"new": "diff_diff:WooldridgeDiDResults.aggregate[type]=event_study",
599+
"deprecated_in": "3.9",
600+
"removed_in": "4.0",
601+
"status": "shimmed",
602+
"phase": 5,
603+
},
604+
(4, 0, 0),
605+
"removed_in 4.0 is due",
606+
(3, 9, 0),
607+
),
588608
# EARLY removal: row flipped to removed while removed_in is still in the future
589609
(
590610
{
@@ -606,6 +626,7 @@ def test_all_membership_helper_semantics():
606626
"overdue-introduce-only-alias",
607627
"overdue-env-default-decision",
608628
"overdue-removal",
629+
"overdue-param-value-removal",
609630
"early-removal-before-schedule",
610631
],
611632
)
@@ -625,7 +646,12 @@ def test_row_matches_reality(row, monkeypatch):
625646
rid, kind, status = row["id"], row["kind"], row["status"]
626647
old, new = row["old"], row["new"]
627648

628-
if kind in ("param-value", "behavior"):
649+
if kind == "param-value":
650+
pytest.skip(
651+
"no reality probe (accepted values are not introspectable); lifecycle is "
652+
"schema+due-gate enforced and value behavior lives in the row's test_ref suite"
653+
)
654+
if kind == "behavior":
629655
pytest.skip("schema-checked kind; flipped manually, swept at the cut")
630656

631657
if kind in ("param", "class", "function"):
@@ -849,6 +875,12 @@ def _schema_errors_for(text):
849875
"code_refs must be a non-empty list",
850876
),
851877
(lambda t: t.replace(" group: fixture\n", ""), "missing required field"),
878+
(
879+
lambda t: t.replace(" kind: param\n", " kind: param-value\n").replace(
880+
" status: planned\n", " status: shimmed\n"
881+
),
882+
"requires a test_ref",
883+
),
852884
(
853885
lambda t: t.replace(" kind: param\n", " kind: behavior\n").replace(
854886
" status: planned\n", " status: done\n"
@@ -866,6 +898,7 @@ def _schema_errors_for(text):
866898
"dotted-locator-on-removable",
867899
"empty-code-refs",
868900
"missing-group",
901+
"param-value-shimmed-without-test-ref",
869902
"behavior-done-without-test-ref",
870903
],
871904
)

0 commit comments

Comments
 (0)