Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 23 additions & 2 deletions scripts/grain_tag.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,27 @@ def _strip_title_hashes(flat: str) -> str:
# body class widens to the corresponding lowercase range (`à-öø-ÿ` + ſ) so the
# maximal-munch `(?![A-Za-z0-9._À-ſ-])` keeps working with the new characters.
# Tested in `test_lane_workspace_accented` + the existing #12145 / #12719 set.
# #15864 -- a PROSE PERIOD followed by an accented continuation word is the
# fourth variant of the self-blocking class (#12145 space-workspace, #12719
# bare date, #13830 accents, now #12719 x #13830). The founder marker of
# issue #15674:
#
# [DELIVERED] PR #15733 — lane myia-po-2023:CoursIA. Énoncé réécrit ...
#
# The workspace class admits `.` (hostnames need it, #12719), so it eats
# `CoursIA.`; the continuation then legitimately admits `Énoncé` (#13830
# accented uppercase) and the read token becomes `myia-po-2023:CoursIA.
# Énoncé` -- a lane that exists nowhere, which check_lane_claim used to
# block the declaring lane on (its OWN [DELIVERED] re-marker, #12320).
#
# Rule: a `.` followed by whitespace is PROSE PUNCTUATION, never part of a
# `<machine>:<workspace>` token; a `.` INSIDE a token (hostname, `Foo.Bar`)
# is followed by a non-blank and survives. Implemented by a `(?<!\.)`
# lookbehind at the head of the continuation clause: after a token-ending
# period every continuation iteration is refused, the capture stops at
# `...CoursIA.` and the existing #12719 `rstrip(".")` in extract_lane /
# parse_grain_tag lands the bare lane. `lane_marker_residues` keeps
# declaring the malformed form (`trailing-period:`).
_LANE_RE = re.compile(
r"lane\s*:?\s+"
# #13830 V2 -- union of #13869 (full Latin-1 + Latin Extended-A `À-ſ`)
Expand All @@ -213,7 +234,7 @@ def _strip_title_hashes(flat: str) -> str:
# `test_lane_workspace_accented` + the existing #12145 / #12719 set
# + the #13869 comparative table (LivresAgités / Cours×IA / Łódź).
r"([A-Za-z0-9._-]+:[A-Za-zÀ-ÖØ-öø-ÿĀ-ſ0-9._-]+"
r"(?:[ \t]+(?!\d{4}-\d{2}-\d{2})(?-i:[A-Z0-9À-ÖØ-ÞĀ-ſ])[A-Za-z0-9._À-ÖØ-öø-ÿĀ-ſ-]*(?![A-Za-z0-9._À-ÖØ-öø-ÿĀ-ſ-])(?![:@])){0,3})",
r"(?:(?<!\.)[ \t]+(?!\d{4}-\d{2}-\d{2})(?-i:[A-Z0-9À-ÖØ-ÞĀ-ſ])[A-Za-z0-9._À-ÖØ-öø-ÿĀ-ſ-]*(?![A-Za-z0-9._À-ÖØ-öø-ÿĀ-ſ-])(?![:@])){0,3})",
re.IGNORECASE,
)

Expand Down Expand Up @@ -257,7 +278,7 @@ def _strip_title_hashes(flat: str) -> str:
# The twin MUST move with the primary or the founder's class of bug
# (#12145) re-opens on the fallback only.
r"\b(myia-[A-Za-z0-9._-]+:[A-Za-zÀ-ÖØ-öø-ÿĀ-ſ][A-Za-zÀ-ÖØ-öø-ÿĀ-ſ0-9._-]*"
r"(?:[ \t]+(?!\d{4}-\d{2}-\d{2})(?-i:[A-Z0-9À-ÖØ-ÞĀ-ſ])[A-Za-z0-9._À-ÖØ-öø-ÿĀ-ſ-]*(?![A-Za-z0-9._À-ÖØ-öø-ÿĀ-ſ-])(?![:@])){0,3})"
r"(?:(?<!\.)[ \t]+(?!\d{4}-\d{2}-\d{2})(?-i:[A-Z0-9À-ÖØ-ÞĀ-ſ])[A-Za-z0-9._À-ÖØ-öø-ÿĀ-ſ-]*(?![A-Za-z0-9._À-ÖØ-öø-ÿĀ-ſ-])(?![:@])){0,3})"
)

# `prev` (case-insensitive), optional colon, whitespace, then the SAME
Expand Down
32 changes: 32 additions & 0 deletions scripts/tests/test_check_lane_claim.py
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,38 @@ def test_check_no_paths_claim_in_prose_returns_exit_2_not_scoped(capsys):
assert "myia-po-2025:CoursIA-2" in captured.out


def test_prose_period_delivered_remarker_does_not_block_owning_lane(capsys):
"""#15864 -- full-flow regression: the phantom-lane self-block.

The founder payload of issue #15674: a canonical [CLAIMED] plus the lane's
own [DELIVERED] whose prose after the lane token starts with an accented
uppercase word (`CoursIA. Énoncé ...`). Pre-fix, the [DELIVERED] re-marker
parsed as the phantom lane `myia-po-2023:CoursIA. Énoncé` -- a DIFFERENT
key -- and `lane_claim_required` blocked PR #15733 on its own delivery
(gate verdict FAIL, 8 reruns). Post-fix the re-marker parses to the
declaring lane and the check is CLEAR for it (exit 0), still blocking for
any OTHER lane.
"""
claimed = (
"[CLAIMED] lane myia-po-2023:CoursIA — 2026-09-12T09:0xZ — paths: "
"MyIA.AI.Notebooks/GenAI/RAG-et-Memoire-Semantique/05-Stockage-Vectoriel.ipynb"
)
delivered = (
"[DELIVERED] PR #15733 — lane myia-po-2023:CoursIA. Énoncé réécrit en "
"deux gestes (chercher inchangé + recenser), le piège top-10 nommé "
"dans l'énoncé. Exéc complète 12.2s, validate 17 cells PASS."
)
p = payload(
comment(claimed, "2026-09-12T09:17:24Z", author="myia-po-2023"),
comment(delivered, "2026-09-12T20:41:00Z", author="myia-po-2023"),
number=15674,
)
# Acceptance #15864-4: CLEAR (exit 0) for the declaring lane.
assert clc._run_check(p, "myia-po-2023:CoursIA") == 0
captured = capsys.readouterr()
assert "myia-po-2023:CoursIA. Énoncé" not in captured.out + captured.err


def test_parse_unattributed_marker():
# Marker present but no `lane` keyword -> lane None (surfaced, not guessed).
ev = clc.parse_claim_event(comment(
Expand Down
56 changes: 56 additions & 0 deletions scripts/tests/test_grain_tag.py
Original file line number Diff line number Diff line change
Expand Up @@ -1102,6 +1102,62 @@ def test_lane_workspace_rejects_multiplication_sign():
assert g == "myia-x:Cours", "× (U+00D7) must not be admitted as a letter"


# --- #15864: prose period + accented continuation = phantom lane -----------
#
# Founder marker of issue #15674 (4th variant of the self-blocking class:
# #12145 x #12719 x #13830). The workspace class eats the sentence period
# (`CoursIA.`), the #13830 accented-continuation then admits `Énoncé`, and
# the read token `myia-po-2023:CoursIA. Énoncé` blocked the declaring lane
# on its OWN [DELIVERED] re-marker. The `(?<!\.)` lookbehind at the head of
# the continuation clause makes the period a sentence boundary: the capture
# stops at `...CoursIA.` and the #12719 rstrip lands the bare lane.

_FOUNDER_MARKER_15864 = (
"[DELIVERED] PR #15733 — lane myia-po-2023:CoursIA. Énoncé réécrit en deux "
"gestes (chercher inchangé + recenser), le piège top-10 nommé dans "
"l'énoncé. Exéc complète 12.2s, validate 17 cells PASS."
)


def test_lane_prose_period_then_accented_word_not_swallowed():
# Acceptance #15864-1: the real #15674 marker parses to the BARE lane.
assert gt.extract_lane(_FOUNDER_MARKER_15864, marker_line=_FOUNDER_MARKER_15864) == "myia-po-2023:CoursIA"


def test_lane_prose_period_fallback_twin_moves():
# Acceptance #15864-2: the fallback regex (marker line WITHOUT the literal
# `lane` keyword) renders the same verdict -- the twin MUST move (#12145).
no_keyword = _FOUNDER_MARKER_15864.replace("lane ", "", 1)
assert gt.extract_lane("no lane keyword here", marker_line=no_keyword) == "myia-po-2023:CoursIA"


def test_lane_prose_period_parse_grain_tag_no_divergence():
# Acceptance #15864-3: parse_grain_tag (direct _LANE_RE read) and
# extract_lane agree on the same body.
body = "Grain: MED/notebook — lane myia-po-2023:CoursIA. Énoncé réécrit."
assert gt.parse_grain_tag(body)["lane"] == gt.extract_lane(body) == "myia-po-2023:CoursIA"


def test_lane_prose_period_after_multiword_continuation():
# The sentence boundary also holds AFTER a continuation word: the period
# at the end of `Code.` refuses the next word, the rstrip cleans the tail.
assert gt.extract_lane("[CLAIMED] lane myia-po-2025:Microsoft VS Code. Suite de prose") == "myia-po-2025:Microsoft VS Code"


def test_lane_internal_dot_does_not_make_a_boundary():
# A `.` INSIDE the token (hostname, `Foo.Bar`) is followed by a non-blank
# and survives: the continuation after `A.B` is still admitted.
assert gt.extract_lane("[CLAIMED] lane myia-po-2023:A.B Suite -- paths") == "myia-po-2023:A.B Suite"
assert gt.extract_lane("[CLAIMED] lane myia.host.example:Baz -- paths") == "myia.host.example:Baz"


def test_lane_marker_residues_witness_the_prose_period():
# The witness keeps declaring the malformed form instead of silently
# reinterpreting it (acceptance: `trailing-period:` on the founder line).
no_keyword = _FOUNDER_MARKER_15864.replace("lane ", "", 1)
assert gt.lane_marker_residues(no_keyword) == ["trailing-period:myia-po-2023:CoursIA."]


def test_lane_workspace_rejects_division_sign():
# U+00F7 (÷) is NOT a letter; the union class skips it (U+00F7 sits in
# the gap between ö and ø of `à-öø-ÿ`, before `Ā-ſ`). The token
Expand Down
Loading