mirror of
https://github.com/Imbad0202/academic-research-skills.git
synced 2026-09-14 13:51:17 +08:00
305884b252
* feat(reviewer): #610 formal AR<n> arithmetic-receipt grammar for the methodology seat
Adds the mandatory ## Arithmetic Receipts section to the methodology
sprint Phase 2 card: one AR<n> receipt per attempted recomputation under
the four bounded procedures, or a no_recomputable_statistics attestation.
Grammar forces the two adjudicated baseline drop points (unstated-tail
both-values display for p_from_test_statistic; rounding_interval +
nearest_achievable for GRIM/GRIMMER) plus df_identity naming, closed
enums, and bidirectional mismatch<->W<n> linkage.
- canonical source: new methodology-receipt fragment, spliced into
scoring-phase2 by the sync checker (shared text single-sourced),
content lock re-pinned; slot table row 'scoring + receipts'
- methodology_reviewer_agent.md: composed Phase 2 mirror + Step 4a item 8
- check_phase_conformance.py: methodology-only RECEIPT-* gate,
RECEIPT-SECTION-FORBIDDEN on other seats (58 new mutation tests)
- statistical_reporting_standards.md §8: four procedure summaries,
three new arithmetic red-flag rows, covered/reporting-only/
direct-threshold/not_computable classification
- sprint_contract_protocol.md §5.1 + receipt diagnostics rows
- epistemic-status declaration leads every new surface: receipts prove
auditability, never arithmetic truth; human adjudication decides
Phase 1, other seats, DA/synthesis grammars, fixtures, and
dispatch_e4_panel.py are untouched.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016oZptTA3LpqeXWSxynzSz9
* docs(reviewer): #610 receipt attestation honest-claim wording (round-1 partial)
Adjudication of codex round-1 P1-1: the attestation is declaration-only;
the checker cannot verify manuscript applicability (extraction surface is
explicitly out of #610 scope, spec §2). Prompt now states the boundary;
canonical + mirror edited together, content lock re-pinned. Remaining
round-1 fixes tracked in the PR checklist comment.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016oZptTA3LpqeXWSxynzSz9
* fix(reviewer): #610 receipt gate round-1 adjudicated fixes (fence-transparent read, decoration-agnostic aborts, enum defrift lint)
Implements the 10-item adjudicated fix list from PR #644 dual-track round 1:
1. Both-tails rule requires VALUES: each tail label needs a digit in its own
;-segment of derived_value_or_range, NFKC-folded, hyphen variants folded.
2. Attestation honesty: checker prints the declaration-only
[RECEIPT-ATTESTATION] advisory; CHANGELOG / protocol 5.1 / statistical 8
aligned to mandatory-declaration-adjudicated-honesty wording.
3. Fence-aware receipt parsing via _lines_with_fence_state: fenced receipt
content is READ (no #637-family false abort) and a fenced AR block beside
an unfenced attestation is SEEN and aborts; both directions pinned.
4. Decoration-agnostic field detection (_is_dissent_field_shaped
construction) for receipt fields and the Arithmetic Receipt
back-reference; decorated machine lines abort loudly.
5. Back-reference value must be exactly AR<n> to EOL/next pipe.
6. ## Arithmetic Receipts must be the card's final H2 (all three surfaces
agree on "final section").
7. RECEIPT_SPLICE_ANCHOR bytes join canonical_digest; lock re-pinned.
8. New check_receipt_enum_sync.py pins the four closed enums across
fragment / checker frozensets / spec 4 (CI + manifest wired, 91 entries).
9. GRIMMER direct tests (spec 5.3 worked case).
10. P3 batch: conditional-field loop driven from _PROCEDURE_FIELDS,
CHANGELOG counts corrected (91 conformance / 7 sync mutation tests),
8.5 inclusion-criterion sentence, prompt "vanish" overstatement fixed,
H2 case-sensitivity documented.
Verification: full spec-consistency lint battery green locally; 91-entry
pytest manifest green (498 conformance + 32 sync + 9 enum-lint tests).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D3QRa5VKhLqmcdYPGZiXVm
* fix(reviewer): #610 receipt gate round-2 adjudicated fixes (comment visibility, preamble guard, cell/entity detection)
Implements the adjudicated round-2 dual-track findings (codex 3P1+5P2,
security 2P1+4P2+3P3, converging on the HTML-comment channel):
- Shared _lines_with_hidden_state walker (mirrors _raw_dissent_span's
comment/paragraph bookkeeping): commented-out machine lines, attestation
lines, AR headings, and back-references abort; commented prose tolerated.
- Preamble guard: canonical machine lines outside every ### AR<n>
subsection (other than the attestation) abort instead of parking
un-inspected past the enum/linkage gates.
- Shape detection runs per GFM cell on non-canonical lines and
HTML-unescapes entity colons; CJK cell prose pinned tolerated.
- Backref declared-vs-parsed per-cell equality: a malformed declaration
cannot ride beside a canonical one.
- _lines_with_fence_state yields display form: indented-fence content
dedented per CommonMark (false abort removed, hiding direction pinned).
- Tail labels gain letter boundaries; a label needs only to share its
;-segment with a digit (either order). Fused-label and zh-TW-label
behaviors pinned as declared boundaries. Segment scan now single-pass.
- Receipt field bold is balanced-or-absent (half-bold aborts, both ways).
- Terminal rule counts unfenced 1-3-space-indented ## lines as headings.
- Enum lint spec parse scoped to §4 with pre-§4 decoy mutation test.
- Fragment/mirror/§5.1 name the tolerated decorations and the abort
surface, warn against field-name-leading prose; lock re-pinned.
- Declared-boundary comment on check_receipt_section_forbidden.
Conformance suite 407 → 522 collected; enum lint 11; 91-entry manifest and
spec-consistency battery green locally.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D3QRa5VKhLqmcdYPGZiXVm
* fix(reviewer): #610 receipt gate round-3 adjudicated fixes (comment-free zone, inline spans, display-fold order)
Implements the adjudicated round-3 dual-track findings (codex 2P1+3P2+1P3,
security 0P1+2P2+1P3 with a 9/9 round-2 closure sweep; three items shared):
- The receipt section is a comment-free zone: any unfenced <!--/--> aborts,
closing the paragraph-inline `prose <!--` laundering channel the block
visibility model deliberately cannot read. Supersedes the round-2
commented-prose tolerance (machine section, no legitimate comment use);
fenced occurrences stay literal and exempt. Fragment/mirror instruct it.
- The Review Body backref walk tracks paragraph-inline comment spans with
an ordered token scan; a back-reference inside a live span aborts and is
never credited. Spans die at paragraph boundaries (blank line, heading),
pinned both directions.
- _display_fold: HTML-unescape BEFORE the NFKC fold, so fullwidth-colon
entities (:) decode+fold to `:` for fields and backrefs.
- _cell_split_form: link destinations, inline code spans, and escaped
pipes are blanked before GFM cell splitting — a pipe-bearing URL in
receipt-section prose no longer false-aborts; the later-cell attack
still does.
- code_indented state (paragraph-aware) in _lines_with_hidden_state: an
indented-code back-reference aborts instead of earning linkage credit; a
4-space paragraph-continuation backref stays live.
- Tail-rule wording unified to the enforced either-order segment rule
(§5.1 + [RECEIPT-TAILS] diagnostic); shape-scan abort message
neutralized (no false "commented-out" claim on partially-rendering
opener lines).
Conformance suite 522 → 533 collected; fragment lock re-pinned; 91-entry
manifest and focused lints green locally.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D3QRa5VKhLqmcdYPGZiXVm
* fix(reviewer): #610 receipt gate round-4 adjudicated fixes (display-form parity, CommonMark code spans, span edges)
Implements the adjudicated round-4 dual-track findings (codex 2P1+2P2,
security 1P1+1P2, four distinct items over two root causes; both tracks
6/6 CLOSED on round-3):
- Display-form parity (round-3 regression): canonical backref parsing and
the declaration count run on the same _cell_split_form display line, so
a code-span declaration can never be credited over the rendered field
beside it.
- _blank_code_spans: equal-run CommonMark code-span scanner replaces the
unequal-delimiter regex, restoring the later-cell attack abort and the
legitimate equal-tick tolerance.
- _strip_inline_comment_spans yields the rendered remainder per line:
content behind an opener is stripped (opener-line pipe trick dies),
content after a closing --> is parsed live (rendered backref credited,
spurious one aborts).
- Indented-code lines no longer open a paragraph, so line 2 of a code
block is still code; code spans are blanked before comment-token
scanning; any ATX heading level ends an inline span.
Conformance suite 533 → 543 collected; canonical lock unchanged; 91-entry
manifest and focused lints green locally.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D3QRa5VKhLqmcdYPGZiXVm
* fix(reviewer): #610 receipt gate round-5 fix (hidden declaration beside visible content aborts)
Closes the last open dual-track finding (codex round-5 P2; security track
already MERGE-READY at 8119c7f with 0 P1/P2):
_strip_inline_comment_spans now returns the hidden text alongside the
rendered remainder, and a back-reference declaration inside the hidden
span aborts even when the line also carries visible content — silently
dropping it re-opened the hidden-declaration channel one
partial-visibility case at a time. Hidden prose beside a visible
declaration stays harmless; the round-4 opener-line pin now aborts at the
hiding site (paragraph-inline diagnostic) instead of downstream.
Conformance suite 543 → 545 collected; canonical lock unchanged; 91-entry
manifest green locally.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D3QRa5VKhLqmcdYPGZiXVm
* fix(reviewer): #610 receipt gate round-6 fix (per-span hidden-segment checks)
Closes the single codex round-6 P2: hidden inline-comment segments are
kept one entry per span instead of joined, so a harmless prose span can
no longer prefix-shield a declaration hidden in the next span, and
fragments across spans can no longer synthesize a phantom declaration
into a false abort. Both directions pinned.
Conformance suite 545 → 547 collected; canonical lock unchanged; 91-entry
manifest green locally.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D3QRa5VKhLqmcdYPGZiXVm
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
204 lines
6.8 KiB
Python
204 lines
6.8 KiB
Python
#!/usr/bin/env python3
|
|
"""Defrift lint for the #610 arithmetic-receipt closed enums.
|
|
|
|
The receipt grammar spells its four closed enums in three places: the
|
|
`methodology-receipt` canonical prompt fragment (what the reviewer is told),
|
|
the conformance checker's frozensets (what is enforced), and the #610 spec §4
|
|
(what was decided). This lint parses the backtick enum lists out of the
|
|
fragment and the spec and asserts set equality against the checker constants,
|
|
so a value added or renamed on one surface fails CI until every surface moves
|
|
in the same commit. The spec does not define a `tail_convention` enum, so that
|
|
comparison is fragment-vs-checker only.
|
|
|
|
Usage: python scripts/check_receipt_enum_sync.py [--root REPO_ROOT]
|
|
Exit 0 in sync, 1 drift, 2 missing file or unparseable surface.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import re
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
import check_phase_conformance as phase # noqa: E402
|
|
import check_reviewer_sprint_prompt_sync as sync # noqa: E402
|
|
|
|
CANONICAL_REL = sync.CANONICAL_REL
|
|
SPEC_REL = "docs/design/2026-08-02-610-statistical-recompute-baseline-spec.md"
|
|
_BACKTICK_NAME_RE = re.compile(r"`([a-z0-9_]+)`")
|
|
_ANGLE_ENUM_RES = {
|
|
"procedure_id": re.compile(r"`procedure_id: <([^>]+)>`"),
|
|
"status": re.compile(r"`status: <([^>]+)>`"),
|
|
"tail_convention": re.compile(r"`tail_convention: <([^>]+)>`"),
|
|
}
|
|
_FRAGMENT_REASONS_RE = re.compile(
|
|
r"closed v1 enum: (?P<names>.*?)\. Every other status"
|
|
)
|
|
CHECKER_ENUMS = {
|
|
"procedure_id": frozenset(phase._RECEIPT_PROCEDURES),
|
|
"status": frozenset(phase._RECEIPT_STATUSES),
|
|
"not_computable_reason": frozenset(phase._NOT_COMPUTABLE_REASONS),
|
|
"tail_convention": frozenset(phase._TAIL_CONVENTIONS),
|
|
}
|
|
|
|
|
|
def _read(root: Path, rel: str) -> str:
|
|
path = root / rel
|
|
try:
|
|
return path.read_text(encoding="utf-8")
|
|
except OSError as exc:
|
|
print(f"ERROR: required file unreadable: {rel}: {exc}",
|
|
file=sys.stderr)
|
|
raise SystemExit(2)
|
|
|
|
|
|
def _fragment_enums(fragment: str, errors: list[str]) -> dict[str, set[str]]:
|
|
enums: dict[str, set[str]] = {}
|
|
for name, pattern in _ANGLE_ENUM_RES.items():
|
|
matches = pattern.findall(fragment)
|
|
if len(matches) != 1:
|
|
errors.append(
|
|
f"fragment: expected exactly one `{name}: <...>` enum "
|
|
f"spelling, found {len(matches)}"
|
|
)
|
|
continue
|
|
enums[name] = {value.strip() for value in matches[0].split("|")}
|
|
reasons = _FRAGMENT_REASONS_RE.search(fragment)
|
|
if reasons is None:
|
|
errors.append(
|
|
"fragment: the closed v1 not_computable_reason sentence "
|
|
"('closed v1 enum: ... . Every other status') was not found"
|
|
)
|
|
else:
|
|
enums["not_computable_reason"] = set(
|
|
_BACKTICK_NAME_RE.findall(reasons.group("names"))
|
|
)
|
|
return enums
|
|
|
|
|
|
def _spec_section4_lines(
|
|
spec_lines: list[str], errors: list[str]
|
|
) -> list[str]:
|
|
"""The lines of spec §4 only, so a worked example or historical block
|
|
elsewhere in the document can never satisfy — or mask drift in — the
|
|
normative enum definitions (#610 round-2)."""
|
|
starts = [
|
|
index for index, line in enumerate(spec_lines)
|
|
if line.startswith("## 4. ")
|
|
]
|
|
if len(starts) != 1:
|
|
errors.append(
|
|
f"spec: expected exactly one '## 4. ' heading, found "
|
|
f"{len(starts)}"
|
|
)
|
|
return []
|
|
end = next(
|
|
(
|
|
index for index in range(starts[0] + 1, len(spec_lines))
|
|
if spec_lines[index].startswith("## ")
|
|
),
|
|
len(spec_lines),
|
|
)
|
|
return spec_lines[starts[0]:end]
|
|
|
|
|
|
def _spec_table_cell_enum(
|
|
spec_lines: list[str], field: str, errors: list[str]
|
|
) -> set[str] | None:
|
|
rows = [
|
|
line for line in spec_lines
|
|
if line.startswith(f"| `{field}` |")
|
|
]
|
|
if len(rows) != 1:
|
|
errors.append(
|
|
f"spec §4: expected exactly one `| `{field}` |` table row, "
|
|
f"found {len(rows)}"
|
|
)
|
|
return None
|
|
cells = rows[0].split("|")
|
|
return set(_BACKTICK_NAME_RE.findall(cells[2]))
|
|
|
|
|
|
def _spec_reason_enum(spec_lines: list[str], errors: list[str]) -> set[str]:
|
|
starts = [
|
|
index for index, line in enumerate(spec_lines)
|
|
if "closed v1 enum" in line
|
|
]
|
|
if len(starts) != 1:
|
|
errors.append(
|
|
"spec §4: expected exactly one 'closed v1 enum' sentence in "
|
|
f"§4, found {len(starts)}"
|
|
)
|
|
return set()
|
|
names: set[str] = set()
|
|
for line in spec_lines[starts[0] + 1:]:
|
|
if match := re.fullmatch(r"- `([a-z0-9_]+)`", line.strip()):
|
|
names.add(match.group(1))
|
|
elif line.strip() and names:
|
|
break
|
|
return names
|
|
|
|
|
|
def check(root: Path) -> list[str]:
|
|
errors: list[str] = []
|
|
canonical_text = _read(root, CANONICAL_REL)
|
|
fragments = {
|
|
match.group("name"): match.group("body")
|
|
for match in sync.MARKER_RE.finditer(canonical_text)
|
|
}
|
|
fragment = fragments.get("methodology-receipt")
|
|
if fragment is None:
|
|
print("ERROR: methodology-receipt fragment missing from "
|
|
f"{CANONICAL_REL}", file=sys.stderr)
|
|
raise SystemExit(2)
|
|
spec_lines = _spec_section4_lines(
|
|
_read(root, SPEC_REL).splitlines(), errors
|
|
)
|
|
|
|
fragment_enums = _fragment_enums(fragment, errors)
|
|
spec_enums = {
|
|
"procedure_id": _spec_table_cell_enum(
|
|
spec_lines, "procedure_id", errors
|
|
),
|
|
"status": _spec_table_cell_enum(spec_lines, "status", errors),
|
|
"not_computable_reason": _spec_reason_enum(spec_lines, errors),
|
|
}
|
|
|
|
for name, checker_values in CHECKER_ENUMS.items():
|
|
for surface, values in (
|
|
("fragment", fragment_enums.get(name)),
|
|
("spec §4", spec_enums.get(name)),
|
|
):
|
|
if values is None:
|
|
continue # tail_convention has no spec enum; parse errors
|
|
# for the other absences are already recorded above.
|
|
if values != checker_values:
|
|
missing = sorted(checker_values - values)
|
|
extra = sorted(values - checker_values)
|
|
errors.append(
|
|
f"{name}: {surface} enum drifted from the checker "
|
|
f"frozenset (missing={missing}, extra={extra})"
|
|
)
|
|
return errors
|
|
|
|
|
|
def main(argv: list[str] | None = None) -> int:
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument(
|
|
"--root", type=Path,
|
|
default=Path(__file__).resolve().parents[1],
|
|
)
|
|
args = parser.parse_args(argv)
|
|
errors = check(args.root)
|
|
for error in errors:
|
|
print(f"ERROR: {error}", file=sys.stderr)
|
|
if errors:
|
|
return 1
|
|
print("RECEIPT-ENUM-SYNC: PASS (4 enums, 3 surfaces)")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|