Skip to content
Merged
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
45 changes: 42 additions & 3 deletions scripts/grain_tag.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,19 @@ 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 -- #12719 and #13830 INTERACT: `lane myia-po-2023:CoursIA. Énoncé
# réécrit ...` parsed as the lane `myia-po-2023:CoursIA. Énoncé`. The `.` is
# admitted by the token class (hostnames need it) and `É` is admitted as a
# continuation initial (`À-ÖØ-Þ`), so the period is no longer TRAILING and
# #12719's `rstrip(".")` cannot see it. Since a `[DELIVERED]` marker re-marks
# the claim while its PR is OPEN (#12320), the declaring lane was blocked on
# its OWN claim by a lane token that matches nothing -- the 4th variant of the
# self-block class (#12145, #12719, #13830). A `.` followed by whitespace is
# prose punctuation, never part of `<machine>:<workspace>`; a `.` INSIDE the
# token (a hostname, `Foo.Bar`) is followed by a non-space and survives. The
# negative lookbehind refuses the continuation, so the token ends at the
# period and #12719's strip applies exactly as before.
_LANE_RE = re.compile(
r"lane\s*:?\s+"
# #13830 V2 -- union of #13869 (full Latin-1 + Latin Extended-A `À-ſ`)
Expand All @@ -213,7 +226,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 @@ -246,6 +259,11 @@ def _strip_title_hashes(flat: str) -> str:
# #13830 -- the accent tolerance added to `_LANE_RE` applies here too:
# the fallback lane token for marker comments that omit `lane` must accept the
# same workspace names. Same widening of body class and continuation initial.
#
# #15864 -- the sentence-period guard above moves here too, for the same
# reason the bare-date guard did: a marker that omits the literal `lane`
# keyword can equally end its token with a period followed by prose
# (`[DELIVERED] myia-po-2023:CoursIA. Énoncé ...`).
_LANE_FALLBACK_RE = re.compile(
# #13830 V2 -- twin of `_LANE_RE`: the workspace class widens from
# `[A-Za-z][A-Za-z0-9._-]*` to `[A-Za-zÀ-ÖØ-öø-ÿĀ-ſ][A-Za-zÀ-ÖØ-öø-ÿĀ-ſ0-9._-]*`
Expand All @@ -257,7 +275,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 Expand Up @@ -540,6 +558,12 @@ class admits `.` (hostnames need it), so `lane myia-po-2023:CoursIA.`
a lane that matches nothing. No cluster lane ends in a period, so the
residue is stripped here; `lane_marker_residues` reports it so the organ
can surface the malformed form instead of silently reinterpreting it.

#15864 -- and when prose FOLLOWS the period, the regex itself now stops
at it (see `_LANE_RE`): `lane myia-po-2023:CoursIA. Énoncé réécrit` yields
the same `myia-po-2023:CoursIA.` here, which this strip then cleans. The
two halves are complementary -- the lookbehind refuses the accented
continuation, this strip removes the period it stopped at.
"""
if not body:
return None
Expand All @@ -561,6 +585,18 @@ class admits `.` (hostnames need it), so `lane myia-po-2023:CoursIA.`
m_kw = _LANE_RE.search(flat_line)
if m_bare is not None and (m_kw is None or m_bare.start() < m_kw.start(1)):
return m_bare.group(1).rstrip(".")
# #15864 x #15918 -- a position tie falls through to the primary, but
# the primary that #15918 kept in scope is the BODY match `m`. When
# the body is silent (#10395 Variante 1 -- `marker_line` is the
# human-stated intent searched on its own), that fallthrough hit
# `if m:` with m=None and returned None even though the marker line
# names its lane in keyworded form (`lane X. Énoncé ...`, the
# #15864 phantom). The tie's primary IS m_kw here: return it instead
# of the absent body match. Both real callers are unaffected -- they
# pass the marker line as (part of) the body, so m is non-None
# whenever m_kw is, and this branch stays dead for them.
if m is None and m_kw is not None:
return m_kw.group(1).rstrip(".")
if m:
return m.group(1).rstrip(".")
return None
Expand All @@ -587,7 +623,10 @@ def lane_marker_residues(marker_line: str | None) -> list[str]:
token, which the continuation guard refused and the declaring lane
did not intend as part of the lane;
* `"trailing-period:<token>"` -- the token ended in a sentence period
that `extract_lane` strips before comparing.
that `extract_lane` strips before comparing. Since #15864 this also
covers the period that PROSE follows (`lane X. Énoncé ...`): the
refused continuation leaves the period trailing, so the writer sees
why the rest of their sentence was not read as part of the lane.
"""
if not marker_line:
return []
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 @@ -507,6 +507,38 @@ def test_check_clear_when_only_my_lane(capsys):
assert "CLEAR" in out


def test_delivered_marker_sentence_period_is_not_a_phantom_lane(capsys):
"""#15864 -- a lane's own `[DELIVERED]` marker must not become a SECOND lane.

Reproduced from issue #15674 (2026-09-12). The DELIVERED body ends with
French prose right after the lane -- `lane myia-po-2023:CoursIA. Énoncé
réécrit ...` -- and the sentence period plus the accented capital word were
swallowed into the lane token (`myia-po-2023:CoursIA. Énoncé`, a lane that
matches nothing). The guard then blocked the declaring lane on its OWN
claim, and the refusal named a lane that does not exist. Measured
end-to-end on the guard's own entry point: pre-fix this returns a non-zero
verdict, post-fix CLEAR.
"""
p = payload(
comment(
"[CLAIMED] lane myia-po-2023:CoursIA — 2026-09-12T09:0xZ — paths: "
"MyIA.AI.Notebooks/GenAI/RAG-et-Memoire-Semantique/05-Stockage-Vectoriel.ipynb",
"2026-09-12T09:13:07Z",
),
comment(
"[DELIVERED] PR #15733 — lane myia-po-2023:CoursIA. Énoncé réécrit en "
"deux gestes (chercher inchangé + recenser via "
"`client2.count(count_filter=...)`), le piège top-10 nommé dans "
"l'énoncé. Exéc complète 12.2s, validate 17 cells PASS.",
"2026-09-12T09:17:24Z",
),
)
rc = clc._run_check(p, "myia-po-2023:CoursIA")
out = capsys.readouterr().out
assert "myia-po-2023:CoursIA. Énoncé" not in out
assert rc == 0


def test_check_no_paths_returns_exit_2_and_not_scoped(capsys):
# #12322 -- when the caller does NOT pass `--paths` AND has no scoped
# active claim of their own, the call cannot prove disjointness from any
Expand Down
68 changes: 68 additions & 0 deletions scripts/tests/test_grain_tag.py
Original file line number Diff line number Diff line change
Expand Up @@ -1122,6 +1122,74 @@ def test_lane_ascii_control_unchanged():
assert g == "myia-po-2026:CoursIA"


# --- #15864: sentence period + accented continuation (#12719 x #13830) ------
#
# The two earlier fixes interact. `_LANE_RE` admits `.` in the token class
# (#12719, hostnames) and admits an accented uppercase as a continuation
# initial (#13830). Together, `lane myia-po-2023:CoursIA. Énoncé réécrit ...`
# parsed the lane as `myia-po-2023:CoursIA. Énoncé` -- a token that matches no
# lane, so `check_lane_claim` blocked the declaring lane on its OWN claim.
#
# The real-world trigger is the lane's own `[DELIVERED]` marker, which
# re-marks the claim while the PR is OPEN (#12320) and is followed by French
# prose that naturally starts with an accented capital word.

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


def test_lane_sentence_period_stops_the_token_primary():
# Acceptance #15864-1: the real marker from issue #15674 parses to the
# BARE lane, not to `...CoursIA. Énoncé`.
assert gt.extract_lane(_REAL_DELIVERED_15864) == "myia-po-2023:CoursIA"


def test_lane_sentence_period_stops_the_token_fallback():
# Acceptance #15864-2: the twin carries the same case, or the defect stays
# whole in the copy (#12145 discipline).
line = "lane myia-po-2023:CoursIA. Énoncé réécrit"
assert gt.extract_lane("no lane keyword here", marker_line=line) == "myia-po-2023:CoursIA"


def test_lane_sentence_period_parse_grain_tag_same_lane():
# Acceptance #15864-3: single-reader discipline (#9485) -- `parse_grain_tag`
# reads `_LANE_RE` directly and must not diverge from `extract_lane`.
body = "Grain: MED/notebook-python — lane myia-po-2023:CoursIA. Énoncé réécrit"
assert gt.parse_grain_tag(body)["lane"] == "myia-po-2023:CoursIA"
assert gt.extract_lane(body) == "myia-po-2023:CoursIA"


def test_lane_sentence_period_residue_witnessed():
# Acceptance #15864-4: the malformed form is REPORTED, not silently
# reinterpreted (#12719 acceptance 4). The writer learns that the text
# after their period was not read as part of the lane.
residues = gt.lane_marker_residues(_REAL_DELIVERED_15864)
assert any(r.startswith("trailing-period:") for r in residues), residues


def test_lane_period_is_the_discriminator():
# The two halves of the pair, side by side. Without a period the accented
# continuation is a legitimate workspace word (#13830, preserved); with a
# period it is prose.
assert gt.extract_lane("lane myia-x:W Énoncé") == "myia-x:W Énoncé"
assert gt.extract_lane("lane myia-x:W. Énoncé") == "myia-x:W"


def test_lane_inner_period_still_inside_the_token():
# Non-regression #12719: a period followed by a NON-space is part of the
# token (hostnames, `Foo.Bar`). Only `.` + whitespace ends the lane.
assert gt.extract_lane("lane myia-x:A.B") == "myia-x:A.B"
assert gt.extract_lane("lane host.example:Baz Qux") == "host.example:Baz Qux"


def test_lane_period_after_accented_workspace():
# #13830 and #12719 at once: accented workspace, period, accented prose.
# The residue is stripped, the ghost token never forms.
body = "[CLAIMED] lane myia-ai-01:LivresAgités. Épisode suivant"
assert gt.extract_lane(body, marker_line=body) == "myia-ai-01:LivresAgités"


_REAL_RECONCILIATION_15918 = (
Expand Down
Loading