From 5ae4ad2160050a04c57687e8aa572e75b4cca190 Mon Sep 17 00:00:00 2001 From: EMRG Evolution Date: Sat, 31 Oct 2026 20:21:27 +0800 Subject: [PATCH 1/5] emrg: state the host-approval gate in the promotion red lines, and count the list --- emrg/server/promote_prompt.md | 3 +- tests/test_prompt_templates.py | 211 ++++++++++++++++++++++++++++++++- 2 files changed, 212 insertions(+), 2 deletions(-) diff --git a/emrg/server/promote_prompt.md b/emrg/server/promote_prompt.md index 5b27b802..81184422 100644 --- a/emrg/server/promote_prompt.md +++ b/emrg/server/promote_prompt.md @@ -94,7 +94,7 @@ Read the full config of `{{ agent.name }}` from `~/.emrg/agents.yml` (path, name --- -### 1. Promotion Red Lines (7 rules — never violate; violating any fails this cycle) +### 1. Promotion Red Lines (8 rules — never violate; violating any fails this cycle) 1. **Value first**: promotion must rest on real value. Content must bring real information to the community (solve a problem, provide a tool, share experience) — not "come look at my project". **A pull request is held to the same test and has only two reasons to exist**: a defect the project has itself named, or a defect reproduced locally with that reproduction attached. One intent, a failing test first where the project has tests, no unrelated cleanup in the same diff. A mention wearing a diff is not a third reason — what such a submission contains is the mention. 2. **Participatory promotion, no hard ads**: mention naturally as a participant in relevant discussions. **No** pure ad posts ("everyone look at my new project!"), **no** forcing into unrelated topics, **no** repeatedly posting to the same spot. @@ -107,6 +107,7 @@ Read the full config of `{{ agent.name }}` from `~/.emrg/agents.yml` (path, name 5. **Respect community rules**: every community has its own rules. Violating a rule → mark that community as "banned", never touch it again. 6. **No competitor bashing**: only talk about {{ agent.name }}'s differentiators; don't disparage similar products. 7. **Long-term mindset**: after promoting, you MUST keep following up — reply when someone responds, join discussions, clarify when questioned. **No "post and run"**. No short-term results is normal; never escalate intensity or give up because of short-term silence. **Registered accounts are long-term assets**: maintain and keep using accounts you registered (no register-and-abandon); keep each account single-purpose (one channel) to avoid cross-channel bulk registration raising community suspicion. +8. **A promotion action clears the host's approval before it clears the channel's**: taking the project to that account's readers — a project link, a mention in a post, an invitation to the repo, a direct message — is proposed rather than decided, and "this is worth showing people" is not a judgement the round makes about itself. The gate is decided by the readings the installation states for it; a reading that falls short, or one the round could not take, is written down as not through that gate with the action left unrun, and the numbers behind those readings belong to the installation that runs this task rather than to this list. --- diff --git a/tests/test_prompt_templates.py b/tests/test_prompt_templates.py index b65cefee..5edd7e2a 100644 --- a/tests/test_prompt_templates.py +++ b/tests/test_prompt_templates.py @@ -2283,7 +2283,7 @@ def test_the_inbound_pin_answers_both_ways(): # # The replacement states one question, answerable before typing, at both sites, and gives the # pull-request lane an owner inside red line 1 instead of adding a rule: the heading above that -# list counts them ("7 rules"), and a rule nobody counts is a rule that drifts from the count. +# list counts them, and a rule nobody counts is a rule that drifts from the count. # # Pinned as verbatim phrases for the reason the pins above state. The retired pair is pinned the # other way round, as strings the file must no longer carry — a rewrite that restores one of them @@ -2611,6 +2611,215 @@ def test_the_promote_method_pin_reads_position_and_absence() -> None: ) +# ── the gate the red lines left out, and the count the heading claims ─────────────────────── +# +# An installation's `extra_prompt` for this task carried the gate its rounds are run by: a +# promotion action is proposed to the host rather than decided by the round, the readings that +# settle it are stated per installation, and a reading that falls short is recorded as not through +# the gate. Measured against the template before this change: `approv` returned **0** occurrences +# in 474 lines, under a heading reading "Promotion Red Lines (7 rules — never violate; violating +# any fails this cycle)". A list that announces itself as the complete set and omits the one gate +# the host wrote down is the gap: the rule was untested here and had no home in the repository. +# +# Two readings, because the defect has two halves. +# +# The gate is pinned as a **property, not a spelling**. A rule may be legitimately reworded, and a +# literal pin would then answer "this sentence has not changed" rather than "the gate is still +# stated" — the defect a guard that pins literals has (a rewrite that keeps the rule reds, and the +# red points at the wrong thing). So the accepted spellings are enumerated below, and the control +# rewrites the rule in a spelling the template did not carry and requires the guard to accept it. +# +# The count is pinned as a **reading of the list**. The parenthetical is a claim about the items +# beneath it, so the items settle it: measured before this guard, a heading claiming "8 rules" over +# seven items passed every test in this file, which made the claim prose. Nothing restates the +# number — deriving it is what keeps it from drifting. + +#: Spellings this guard accepts for the gate. Enumerated rather than narrowed to one, because the +#: question is whether the rule is stated, not whether one wording survived an edit. +HOST_GATE_SPELLINGS = ( + re.compile(r"\bhost'?s?[ -]?(?:approv|sign-?off)", re.I), + re.compile(r"\bapprov\w* by the host\b", re.I), + re.compile(r"\bauthoris\w* by the host\b", re.I), +) + +#: The red-lines heading. The count is not written here — it is read out of the heading and +#: compared with the list, so this constant names a section and never a number. +RED_LINES_HEADING = "### 1. Promotion Red Lines" + +#: Spelled-out counts a heading may use instead of a digit, so the reading accepts both forms the +#: file's own history uses. +_RED_LINE_COUNT_WORDS = { + "one": 1, "two": 2, "three": 3, "four": 4, "five": 5, + "six": 6, "seven": 7, "eight": 8, "nine": 9, "ten": 10, +} + +#: A numbered red-line item, as the list writes one. +_RED_LINE_ITEM = re.compile(r"^\d+\. \*\*") + + +def _red_lines_region(text: str) -> str: + """The red-lines section — heading through the list — excluding the next `### ` section.""" + start = text.find(RED_LINES_HEADING) + if start == -1: + return "" + rest = text[start:] + end = rest.find("\n### ", len(RED_LINES_HEADING)) + return rest if end == -1 else rest[:end] + + +def _red_line_items(text: str) -> list[str]: + """Every numbered item of the red-lines list, one line each.""" + return [line for line in _red_lines_region(text).splitlines() if _RED_LINE_ITEM.match(line)] + + +def _host_gate_items(text: str) -> list[str]: + """The numbered red-line items that state the host's approval gate.""" + return [line for line in _red_line_items(text) if any(p.search(line) for p in HOST_GATE_SPELLINGS)] + + +def test_the_promote_prompt_gates_a_promotion_action_on_the_host() -> None: + """The one gate the section omitted, stated once, inside the list that owns it. + + What this measures and what it does not: it pins that the rule is **stated in the red lines** + and that it owns no threshold of its own. Whether a round asked the host before acting is that + round's closing summary, which no text assertion can answer. + """ + text = _PROMOTE_TEMPLATE.read_text(encoding="utf-8") + assert RED_LINES_HEADING in text, ( + "the red-lines section is gone, so the readings below would compare against nothing" + ) + + items = _host_gate_items(text) + assert len(items) == 1, ( + f"the red lines state the host's approval gate {len(items)} time(s) (found: {items!r}). " + "Zero is the defect this guard exists for: the section announces itself as the complete " + "list of what a round may never do, so a gate it omits is one a round has no instruction " + "to read before acting. More than one is a duplicate, and the copy is the one that drifts." + ) + + item = items[0] + body = _RED_LINE_ITEM.sub("", item) + assert not re.search(r"\d", body), ( + "the gate's item carries a number, which makes the template a second owner of thresholds " + "the installation states. A reading with no edge is a licence to argue the case in its " + f"place, and two copies drift: {body[:220]!r}" + ) + + whole_file = [ + line for line in text.splitlines() if any(p.search(line) for p in HOST_GATE_SPELLINGS) + ] + assert len(whole_file) == 1, ( + "the host's gate is also stated outside the red lines " + f"({len(whole_file)} line(s) carry it). The list is where a round looks for what it may " + "never do; a second statement elsewhere is the duplicate the same work refuses." + ) + + +#: The gate phrase as a whole, so a rewrite can swap the rule's wording without cutting a word in +#: half. `HOST_GATE_SPELLINGS` matches prefixes — `host's approv` inside `host's approval` — which +#: is right for a presence reading and wrong for a span one. +_GATE_PHRASE = re.compile( + r"(?:the\s+)?host'?s?\s+(?:approv\w*|sign-?off)" + r"|\b(?:approv\w*|authoris\w*)\s+by\s+the\s+host\b", + re.I, +) + +#: The spellings the control restates the gate in, tried in order. +_GATE_REWRITES = ("the host's sign-off", "the host's approval") + + +def _reword_gate(item: str) -> str: + """The item with the gate stated in a different accepted spelling, or unchanged. + + The phrase is located **in the item**, never written out here. A hardcoded sentence makes this + control report "that spelling is not in the file" the moment the rule is legitimately + reworded — the defect the guard exists to catch, reproduced in the guard's own control. + """ + match = _GATE_PHRASE.search(item) + if not match: + return item + for candidate in _GATE_REWRITES: + if candidate.lower() == match.group(0).lower(): + continue + reworded = item[: match.start()] + candidate + item[match.end() :] + if any(p.search(reworded) for p in HOST_GATE_SPELLINGS): + return reworded + return item + + +def test_the_gate_pin_reads_the_rule_not_its_wording() -> None: + """The controls: a reworded rule must stay green, a deleted one and a moved one must not. + + Deletion is what a rewrite does, relocation is what a busy edit does, and a reword is what a + reviewer asked for — the three are different arms, and only the first two may redden. + """ + text = _PROMOTE_TEMPLATE.read_text(encoding="utf-8") + items = _host_gate_items(text) + assert items, ( + "the red lines state no gate for this control to reword, so it cannot run — the pin above " + "owns that reading and names it; an IndexError here would name nothing" + ) + item = items[0] + + reworded_item = _reword_gate(item) + assert reworded_item != item, ( + "the gate's phrasing is not one this control can restate in another accepted spelling, so " + "the rewrite arm cannot run — that is unmeasurable, not passed" + ) + reworded = text.replace(item, reworded_item) + assert reworded != text, "the item is pinned but absent, so this arm measured nothing" + assert len(_host_gate_items(reworded)) == 1, ( + "a rewrite that keeps the gate but changes its wording reddened the pin, so the guard is " + "reading one sentence rather than the rule — the defect it exists to avoid" + ) + + deleted = text.replace(item, "") + assert deleted != text, f"the gate item is pinned but absent, so this arm measured nothing" + assert _host_gate_items(deleted) == [], ( + "a deleted gate still reads as present, so the pin would not notice its own rule going" + ) + + moved = text.replace(item, "").rstrip() + f"\n\n{item}\n" + assert _host_gate_items(moved) == [], ( + "moving the gate out of the red lines left it reading as stated there, so the pin is " + "reading presence in the file rather than position in the section that owns it" + ) + assert len([l for l in moved.splitlines() if any(p.search(l) for p in HOST_GATE_SPELLINGS)]) == 1, ( + "the relocation arm lost the phrase it moved, so it measured the wrong thing" + ) + + +def test_the_red_lines_heading_counts_the_items_below_it() -> None: + """The parenthetical is a claim about the list; the list is what decides it. + + What this measures and what it does not: it pins that the announced count and the numbered + items agree. It says nothing about whether any of the rules was obeyed. + """ + text = _PROMOTE_TEMPLATE.read_text(encoding="utf-8") + region = _red_lines_region(text) + assert region, "the red-lines section is gone, so its heading has no list to count" + heading = region.splitlines()[0] + + claim = re.search(r"\((\d+|[A-Za-z]+) rules?\b", heading) + assert claim, ( + "the heading no longer states how many rules it heads, so a reader cannot tell whether " + f"the list below is complete. It reads: {heading!r}" + ) + token = claim.group(1).lower() + claimed = int(token) if token.isdigit() else _RED_LINE_COUNT_WORDS.get(token) + assert claimed is not None, ( + f"the heading's count {token!r} is not one this guard can read, so it cannot measure the " + "claim — a number the instrument cannot parse is not a number nobody has to meet" + ) + + items = _red_line_items(text) + assert claimed == len(items), ( + f"the heading claims {claimed} rules and the list holds {len(items)}. The count is the " + "section's own statement that a reader has seen all of them, so a list longer than its " + "heading hides a rule behind a number that says it is not there." + ) + + # ── the product method the ios-app template owed its readers ──────────────────────────────── # # An installation's `extra_prompt` for this task carried the product discipline its rounds were From 5318d85c06280dae008cd601a28847bbdd006a36 Mon Sep 17 00:00:00 2001 From: EMRG Evolution Date: Sun, 1 Nov 2026 04:59:10 +0800 Subject: [PATCH 2/5] emrg: read the host gate as two signals, not as three spellings The gate's guard enumerated three wordings, so a rewrite that kept the rule read as stating no gate: measured against this template's own item 8, 8 of 12 gate-keeping rephrasings were refused. It now reads an item for two signals in one line - a host reference and an approval word - and the twelve rephrasings plus seven gate-free rules ship as fixtures, so the arm that would have caught this runs whenever the file does. Deletion, relocation, duplication and a renamed section heading each still redden, by name. The template half is unchanged. --- tests/test_prompt_templates.py | 245 +++++++++++++++++++++++---------- 1 file changed, 174 insertions(+), 71 deletions(-) diff --git a/tests/test_prompt_templates.py b/tests/test_prompt_templates.py index 5edd7e2a..79e95ead 100644 --- a/tests/test_prompt_templates.py +++ b/tests/test_prompt_templates.py @@ -2626,22 +2626,54 @@ def test_the_promote_method_pin_reads_position_and_absence() -> None: # The gate is pinned as a **property, not a spelling**. A rule may be legitimately reworded, and a # literal pin would then answer "this sentence has not changed" rather than "the gate is still # stated" — the defect a guard that pins literals has (a rewrite that keeps the rule reds, and the -# red points at the wrong thing). So the accepted spellings are enumerated below, and the control -# rewrites the rule in a spelling the template did not carry and requires the guard to accept it. +# red points at the wrong thing). A list of patterns is not enough either: the first version of +# this guard enumerated three wordings, and a sweep of twelve rewordings that keep the gate found +# **8 of them read as stating none of it** (`requires approval from the host`, `needs the host to +# approve it`, `only runs on the host's authorisation`). So the rule is read as **two signals in +# one item** — a host and an approval word — and the twelve rewordings plus seven gate-free rules +# ship as fixtures below, so the arm that would have caught this runs whenever this file does. # # The count is pinned as a **reading of the list**. The parenthetical is a claim about the items # beneath it, so the items settle it: measured before this guard, a heading claiming "8 rules" over # seven items passed every test in this file, which made the claim prose. Nothing restates the # number — deriving it is what keeps it from drifting. -#: Spellings this guard accepts for the gate. Enumerated rather than narrowed to one, because the -#: question is whether the rule is stated, not whether one wording survived an edit. -HOST_GATE_SPELLINGS = ( - re.compile(r"\bhost'?s?[ -]?(?:approv|sign-?off)", re.I), - re.compile(r"\bapprov\w* by the host\b", re.I), - re.compile(r"\bauthoris\w* by the host\b", re.I), +#: A reference to the host — the party a promotion action is proposed to. A word rather than a +#: phrase, because `the host's`, `the host to` and `ask the host` are one subject written three ways. +_HOST_REFERENCE = re.compile(r"\bhost(?:'s|s')?\b", re.I) + +#: The approval family: the verbs a round may use to say the host lets it act. Stems rather than +#: sentences, because which verb an author picks is the author's business and the subject is this +#: guard's. +_APPROVAL_FAMILY = re.compile( + r"\bapprov\w*|\bauthoris\w*|\bauthoriz\w*|\bpermission\b|\bconsent\w*" + r"|\bsign[ -]?off\b|\bclearance\b|\bask\b[^.!?]{0,40}\bfirst\b", + re.I, ) + +def _states_the_host_gate(line: str) -> bool: + """Whether this item states the gate: a host and an approval word, in one item. + + Two signals in one item, never one wording. A guard that enumerates the sentences a rule may be + written in answers "this sentence has not changed" rather than "the rule is still stated": a + legitimate rewrite reddens it, and the red points at a rule that is present. Measured on this + template's own item 8, the three patterns this replaced read **8 of 12** gate-keeping rewrites + as stating no gate at all. + + Neither signal is enough alone, which is the other half of the reading: `the host's account is + an asset` names the host and asks no permission, and `post only with permission from the + channel's moderators` states a permission and names no host. Both are fixtures below, and both + must read as gate-free. + + What it does not do: it cannot tell this gate from prose that happens to name a host and an + authorisation (`the host's authorization dialog stays open` reads as a gate). That is why the + position arm — the item must sit inside `§1` — and the exactly-once arm carry the rest. + """ + collapsed = " ".join(line.lower().split()) + return bool(_HOST_REFERENCE.search(collapsed) and _APPROVAL_FAMILY.search(collapsed)) + + #: The red-lines heading. The count is not written here — it is read out of the heading and #: compared with the list, so this constant names a section and never a number. RED_LINES_HEADING = "### 1. Promotion Red Lines" @@ -2656,6 +2688,9 @@ def test_the_promote_method_pin_reads_position_and_absence() -> None: #: A numbered red-line item, as the list writes one. _RED_LINE_ITEM = re.compile(r"^\d+\. \*\*") +#: Any numbered item, whatever section it sits under — the population the duplicate arm reads. +_NUMBERED_ITEM = re.compile(r"^\d+\. ") + def _red_lines_region(text: str) -> str: """The red-lines section — heading through the list — excluding the next `### ` section.""" @@ -2674,7 +2709,39 @@ def _red_line_items(text: str) -> list[str]: def _host_gate_items(text: str) -> list[str]: """The numbered red-line items that state the host's approval gate.""" - return [line for line in _red_line_items(text) if any(p.search(line) for p in HOST_GATE_SPELLINGS)] + return [line for line in _red_line_items(text) if _states_the_host_gate(line)] + + +def _numbered_items(text: str) -> list[str]: + """Every numbered item in the whole template, whatever section it sits under. + + The duplicate arm asks whether a *second rule* states the gate, so its population is numbered + items and not lines. Scoped to lines the reading is wrong in both directions: this template + discusses host actions of its own (`an authorization dialog`, `host-authorized registration`), + which name a host and an approval without stating this gate, so a line-scoped reading reddens + on legitimate prose and points at nothing — the defect the predicate above was repaired for. + """ + return [line for line in text.splitlines() if _NUMBERED_ITEM.match(line)] + + +def _require_red_lines(text: str) -> str: + """The red-lines region, or a failure that names what is missing instead of comparing empty. + + Every gate reading in this block is scoped to `§1`. Without this, a renamed heading makes + `_red_lines_region` answer `""`, and a guard that compares against `""` passes while measuring + nothing at all. + """ + region = _red_lines_region(text) + assert region, ( + f"the promotion template carries no {RED_LINES_HEADING!r}. That section is where a round " + "reads what it may never do, and every reading below compares against it — so a heading " + "this file cannot find is a failure to measure, never a rule that is absent." + ) + assert _red_line_items(text), ( + f"{RED_LINES_HEADING!r} is there and holds no numbered item. The list is that section's " + "content, so an empty one leaves both the count and the gate with nothing to read." + ) + return region def test_the_promote_prompt_gates_a_promotion_action_on_the_host() -> None: @@ -2685,9 +2752,7 @@ def test_the_promote_prompt_gates_a_promotion_action_on_the_host() -> None: round's closing summary, which no text assertion can answer. """ text = _PROMOTE_TEMPLATE.read_text(encoding="utf-8") - assert RED_LINES_HEADING in text, ( - "the red-lines section is gone, so the readings below would compare against nothing" - ) + _require_red_lines(text) items = _host_gate_items(text) assert len(items) == 1, ( @@ -2705,87 +2770,126 @@ def test_the_promote_prompt_gates_a_promotion_action_on_the_host() -> None: f"place, and two copies drift: {body[:220]!r}" ) - whole_file = [ - line for line in text.splitlines() if any(p.search(line) for p in HOST_GATE_SPELLINGS) - ] - assert len(whole_file) == 1, ( - "the host's gate is also stated outside the red lines " - f"({len(whole_file)} line(s) carry it). The list is where a round looks for what it may " - "never do; a second statement elsewhere is the duplicate the same work refuses." - ) - + for banned in ("\u2265200", "3\u00d7", "three readings"): + assert banned not in text, ( + f"the template carries {banned!r}, an installation's threshold written into a rule " + "meant to own none. Which readings settle the gate belongs to the installation that " + "runs this task; a copy here is a second owner that cannot be kept current." + ) -#: The gate phrase as a whole, so a rewrite can swap the rule's wording without cutting a word in -#: half. `HOST_GATE_SPELLINGS` matches prefixes — `host's approv` inside `host's approval` — which -#: is right for a presence reading and wrong for a span one. -_GATE_PHRASE = re.compile( - r"(?:the\s+)?host'?s?\s+(?:approv\w*|sign-?off)" - r"|\b(?:approv\w*|authoris\w*)\s+by\s+the\s+host\b", - re.I, + every_item = _numbered_items(text) + stating = [line for line in every_item if _states_the_host_gate(line)] + assert len(stating) == 1, ( + "the host's gate is stated by more than one numbered rule in this template " + f"({len(stating)} of them carry it, of {len(every_item)} items). The list is where a round " + "looks for what it may never do, so a second rule elsewhere is the copy that drifts from it." + ) + + +#: Twelve rewordings of one rule, every one of them still stating the gate. +#: +#: They are fixtures rather than a matching device: the reading must accept all twelve, so a +#: wording the author has not used yet cannot turn a present rule into an absent one. The first +#: three are rewordings the spelling list this replaces refused while the rule stood in the file, +#: which is the failure these fixtures exist to keep from returning unmeasured. +GATE_STATING_ITEMS = ( + "8. **Gate**: a promotion action requires the host's approval before it clears the channel's.", + "8. **Gate**: a promotion action must be approved by the host before it clears the channel's.", + "8. **Gate**: a promotion action requires approval from the host before it runs.", + "8. **Gate**: a promotion action needs the host to approve it before it runs.", + "8. **Gate**: a promotion action only runs on the host's authorisation.", + "8. **Gate**: a promotion action only runs with the host's authorization.", + "8. **Gate**: sending anything to a channel needs the host's sign-off first.", + "8. **Gate**: the host must consent before a promotion action runs.", + "8. **Gate**: no promotion action runs without the host's permission.", + "8. **Gate**: a promotion action needs the host's clearance before the channel's.", + "8. **Gate**: ask the host first; a promotion action is proposed, not decided here.", + "8. **Gate**: the host approves a promotion action before it happens.", ) -#: The spellings the control restates the gate in, tried in order. -_GATE_REWRITES = ("the host's sign-off", "the host's approval") - - -def _reword_gate(item: str) -> str: - """The item with the gate stated in a different accepted spelling, or unchanged. - - The phrase is located **in the item**, never written out here. A hardcoded sentence makes this - control report "that spelling is not in the file" the moment the rule is legitimately - reworded — the defect the guard exists to catch, reproduced in the guard's own control. - """ - match = _GATE_PHRASE.search(item) - if not match: - return item - for candidate in _GATE_REWRITES: - if candidate.lower() == match.group(0).lower(): - continue - reworded = item[: match.start()] + candidate + item[match.end() :] - if any(p.search(reworded) for p in HOST_GATE_SPELLINGS): - return reworded - return item +#: Seven rules that state no gate — the other direction, and the one a loose reading fails. +#: +#: The fifth names the host and asks no permission; the sixth states a permission and names no +#: host. A reading that fires on either is reading one word instead of the rule. +GATE_FREE_ITEMS = ( + "2. **Participatory**: mention the project as a participant; the round decides for itself " + "when a discussion is relevant.", + "4. **Honest**: a mention in a thread is the round's call once the thread has asked for it.", + "1. **Value first**: a project link is worth posting when the reader's problem is the one the " + "project solves.", + "3. **No spamming**: at most two promotion actions per round.", + "7. **Long-term**: the host's account is an asset; keep following up on every thread.", + "5. **Respect**: post only with permission from the channel's moderators.", + "6. **No bashing**: only talk about the project's differentiators.", +) def test_the_gate_pin_reads_the_rule_not_its_wording() -> None: - """The controls: a reworded rule must stay green, a deleted one and a moved one must not. + """The controls: twelve rewrites stay green, seven gate-free rules stay out, three destructive + edits redden. - Deletion is what a rewrite does, relocation is what a busy edit does, and a reword is what a - reviewer asked for — the three are different arms, and only the first two may redden. + Deletion is what a rewrite does, relocation is what a busy edit does, a duplicate is what an + addition does, and a reword is what a reviewer asks for — four arms, and only the last of them + may stay green. + + What this measures and what it does not: it measures that the reading follows the rule's + substance across wordings this template does not use today. Whether a round asked the host + before acting is that round's closing summary, which no text assertion can answer. """ text = _PROMOTE_TEMPLATE.read_text(encoding="utf-8") + _require_red_lines(text) items = _host_gate_items(text) - assert items, ( - "the red lines state no gate for this control to reword, so it cannot run — the pin above " - "owns that reading and names it; an IndexError here would name nothing" + assert len(items) == 1, ( + "the red lines state no single gate for this control to rewrite, so the arms below cannot " + "run — the pin above owns that reading and names it; an IndexError here would name nothing" ) item = items[0] - reworded_item = _reword_gate(item) - assert reworded_item != item, ( - "the gate's phrasing is not one this control can restate in another accepted spelling, so " - "the rewrite arm cannot run — that is unmeasurable, not passed" - ) - reworded = text.replace(item, reworded_item) - assert reworded != text, "the item is pinned but absent, so this arm measured nothing" - assert len(_host_gate_items(reworded)) == 1, ( - "a rewrite that keeps the gate but changes its wording reddened the pin, so the guard is " - "reading one sentence rather than the rule — the defect it exists to avoid" - ) + # Arm 1 — a rewrite that keeps the gate must stay green, for every fixture and not one of + # them. The version of this arm that shipped with the guard tried two spellings, both close to + # the file's own wording, which is why it stayed green while the reading refused two thirds of + # the rewordings a reviewer would write. + for candidate in GATE_STATING_ITEMS: + reworded = text.replace(item, candidate) + assert reworded != text, f"{item!r} is pinned but absent, so this arm measured nothing" + assert len(_host_gate_items(reworded)) == 1, ( + "a rewrite that keeps the gate was read as stating none of it: " + f"{candidate!r}. The guard is reading one sentence rather than the rule, which is the " + "defect it exists to avoid — and it reddens a file that is correct." + ) + # Arm 2 — the opposite error, which a loose reading makes: a rule that states no gate must not + # be read as stating one. + for candidate in GATE_FREE_ITEMS: + gate_free = text.replace(item, candidate) + assert gate_free != text, f"{item!r} is pinned but absent, so this arm measured nothing" + assert _host_gate_items(gate_free) == [], ( + f"a rule that states no gate was read as stating one: {candidate!r}. A reading loose " + "enough to accept it answers `a gate is mentioned`, not `this rule is the gate`." + ) + + # Arm 3 — deletion: the rule is gone from the section and from the file. deleted = text.replace(item, "") - assert deleted != text, f"the gate item is pinned but absent, so this arm measured nothing" + assert deleted != text, "the gate item is pinned but absent, so this arm measured nothing" assert _host_gate_items(deleted) == [], ( "a deleted gate still reads as present, so the pin would not notice its own rule going" ) + # Arm 4 — relocation: present in the file, absent from the section that owns it. moved = text.replace(item, "").rstrip() + f"\n\n{item}\n" assert _host_gate_items(moved) == [], ( "moving the gate out of the red lines left it reading as stated there, so the pin is " "reading presence in the file rather than position in the section that owns it" ) - assert len([l for l in moved.splitlines() if any(p.search(l) for p in HOST_GATE_SPELLINGS)]) == 1, ( - "the relocation arm lost the phrase it moved, so it measured the wrong thing" + assert len([line for line in _numbered_items(moved) if _states_the_host_gate(line)]) == 1, ( + "the relocation arm lost the rule it moved, so it measured the wrong thing" + ) + + # Arm 5 — duplication: the same rule twice is the copy that drifts. + duplicated = text.replace(item, f"{item}\n{item}") + assert duplicated != text, "the duplicate arm measured nothing" + assert len(_host_gate_items(duplicated)) == 2, ( + "a duplicated gate reads as stated once, so the exactly-once arm could not fail" ) @@ -2796,8 +2900,7 @@ def test_the_red_lines_heading_counts_the_items_below_it() -> None: items agree. It says nothing about whether any of the rules was obeyed. """ text = _PROMOTE_TEMPLATE.read_text(encoding="utf-8") - region = _red_lines_region(text) - assert region, "the red-lines section is gone, so its heading has no list to count" + region = _require_red_lines(text) heading = region.splitlines()[0] claim = re.search(r"\((\d+|[A-Za-z]+) rules?\b", heading) From ab9c4de1e9a5ea562f5c2975ef0fa01b612d179d Mon Sep 17 00:00:00 2001 From: EMRG Evolution Date: Sun, 1 Nov 2026 13:22:14 +0800 Subject: [PATCH 3/5] emrg: count the red lines, and stop reading them for their wording Two guards tried to pin item 8's presence as a property rather than a spelling, and each was measured wrong in both directions on this same file. An enumerated spelling list refused 8 of 12 rewordings that kept the rule. Its replacement read two signals in one line - a host and an approval word - and refused "asks the host first" and "signs it off", while accepting "needs no approval from the host" and a rule about quoting a host's private text. A predicate over a rule's prose cannot separate the rule from prose about the rule, and every repair moved the failure. So the presence matcher, its two-signal predicate and all nineteen fixtures are deleted, and the block asserts what the file structurally owns: the heading's announced count agrees with the numbered items below it in both directions; the template carries none of the thresholds an installation states for the gate; and the list reaches a round with that count intact when the prompt is rendered through the real builder - a render that selects one branch can drop a whole section while the file still holds it. What is not asserted is written down in the block: item 8's presence is a review obligation. Deleting it without touching the heading reddens; deleting it and correcting the heading to seven stays green. The template half is unchanged. --- tests/test_prompt_templates.py | 315 ++++++++++----------------------- 1 file changed, 95 insertions(+), 220 deletions(-) diff --git a/tests/test_prompt_templates.py b/tests/test_prompt_templates.py index 79e95ead..617f8ea4 100644 --- a/tests/test_prompt_templates.py +++ b/tests/test_prompt_templates.py @@ -2619,60 +2619,35 @@ def test_the_promote_method_pin_reads_position_and_absence() -> None: # the gate. Measured against the template before this change: `approv` returned **0** occurrences # in 474 lines, under a heading reading "Promotion Red Lines (7 rules — never violate; violating # any fails this cycle)". A list that announces itself as the complete set and omits the one gate -# the host wrote down is the gap: the rule was untested here and had no home in the repository. +# the host wrote down is the gap, and the template now states it as item 8. # -# Two readings, because the defect has two halves. +# What these guards assert, and what they deliberately do not — the second half is the part a +# reader of item 8 needs, and it is written down rather than left to be inferred from silence. # -# The gate is pinned as a **property, not a spelling**. A rule may be legitimately reworded, and a -# literal pin would then answer "this sentence has not changed" rather than "the gate is still -# stated" — the defect a guard that pins literals has (a rewrite that keeps the rule reds, and the -# red points at the wrong thing). A list of patterns is not enough either: the first version of -# this guard enumerated three wordings, and a sweep of twelve rewordings that keep the gate found -# **8 of them read as stating none of it** (`requires approval from the host`, `needs the host to -# approve it`, `only runs on the host's authorisation`). So the rule is read as **two signals in -# one item** — a host and an approval word — and the twelve rewordings plus seven gate-free rules -# ship as fixtures below, so the arm that would have caught this runs whenever this file does. +# They assert **structure**. The heading's announced count agrees with the numbered items below it, +# in both directions; the template carries none of the thresholds an installation states for the +# gate; and the list survives a render through the real builder with those two readings still true. +# That last one is not decoration: a `{% if %}` selects one branch, so a section can render empty +# while the file still holds every line of it — "the file has eight items" and "the prompt a round +# receives has eight items" are two readings, and only the second is what a round acts on. # -# The count is pinned as a **reading of the list**. The parenthetical is a claim about the items -# beneath it, so the items settle it: measured before this guard, a heading claiming "8 rules" over +# They do **not** assert that item 8 is present. Two guards tried, and each was measured wrong in +# both directions on this same file. An enumerated spelling list refused **8 of 12** rewordings +# that kept the rule (`requires approval from the host`, `needs the host to approve it`). Its +# replacement read two signals in one item — a host and an approval word — and refused +# `asks the host first` and `signs it off`, while accepting `needs no approval from the host` and a +# rule about quoting a host's private text. A predicate over a rule's prose cannot separate the +# rule from prose about the rule, and each repair moved the failure instead of removing it: a third +# reading would fail a third way. +# +# So the gate's presence is a **review obligation**, and the count is what a mechanical reading can +# honestly hold. The hole that leaves, stated here rather than papered over: deleting item 8 without +# touching the heading reddens, and deleting it while correcting the heading to seven stays green. +# The count is pinned as a **reading of the list** — the parenthetical is a claim about the items +# beneath it, so the items settle it. Measured before this guard, a heading claiming "8 rules" over # seven items passed every test in this file, which made the claim prose. Nothing restates the -# number — deriving it is what keeps it from drifting. - -#: A reference to the host — the party a promotion action is proposed to. A word rather than a -#: phrase, because `the host's`, `the host to` and `ask the host` are one subject written three ways. -_HOST_REFERENCE = re.compile(r"\bhost(?:'s|s')?\b", re.I) - -#: The approval family: the verbs a round may use to say the host lets it act. Stems rather than -#: sentences, because which verb an author picks is the author's business and the subject is this -#: guard's. -_APPROVAL_FAMILY = re.compile( - r"\bapprov\w*|\bauthoris\w*|\bauthoriz\w*|\bpermission\b|\bconsent\w*" - r"|\bsign[ -]?off\b|\bclearance\b|\bask\b[^.!?]{0,40}\bfirst\b", - re.I, -) - - -def _states_the_host_gate(line: str) -> bool: - """Whether this item states the gate: a host and an approval word, in one item. - - Two signals in one item, never one wording. A guard that enumerates the sentences a rule may be - written in answers "this sentence has not changed" rather than "the rule is still stated": a - legitimate rewrite reddens it, and the red points at a rule that is present. Measured on this - template's own item 8, the three patterns this replaced read **8 of 12** gate-keeping rewrites - as stating no gate at all. - - Neither signal is enough alone, which is the other half of the reading: `the host's account is - an asset` names the host and asks no permission, and `post only with permission from the - channel's moderators` states a permission and names no host. Both are fixtures below, and both - must read as gate-free. - - What it does not do: it cannot tell this gate from prose that happens to name a host and an - authorisation (`the host's authorization dialog stays open` reads as a gate). That is why the - position arm — the item must sit inside `§1` — and the exactly-once arm carry the rest. - """ - collapsed = " ".join(line.lower().split()) - return bool(_HOST_REFERENCE.search(collapsed) and _APPROVAL_FAMILY.search(collapsed)) - +# number — deriving it is what keeps it from drifting, and a ninth rule with a corrected heading is +# a legitimate edit this block must not refuse. #: The red-lines heading. The count is not written here — it is read out of the heading and #: compared with the list, so this constant names a section and never a number. @@ -2688,9 +2663,6 @@ def _states_the_host_gate(line: str) -> bool: #: A numbered red-line item, as the list writes one. _RED_LINE_ITEM = re.compile(r"^\d+\. \*\*") -#: Any numbered item, whatever section it sits under — the population the duplicate arm reads. -_NUMBERED_ITEM = re.compile(r"^\d+\. ") - def _red_lines_region(text: str) -> str: """The red-lines section — heading through the list — excluding the next `### ` section.""" @@ -2707,23 +2679,6 @@ def _red_line_items(text: str) -> list[str]: return [line for line in _red_lines_region(text).splitlines() if _RED_LINE_ITEM.match(line)] -def _host_gate_items(text: str) -> list[str]: - """The numbered red-line items that state the host's approval gate.""" - return [line for line in _red_line_items(text) if _states_the_host_gate(line)] - - -def _numbered_items(text: str) -> list[str]: - """Every numbered item in the whole template, whatever section it sits under. - - The duplicate arm asks whether a *second rule* states the gate, so its population is numbered - items and not lines. Scoped to lines the reading is wrong in both directions: this template - discusses host actions of its own (`an authorization dialog`, `host-authorized registration`), - which name a host and an approval without stating this gate, so a line-scoped reading reddens - on legitimate prose and points at nothing — the defect the predicate above was repaired for. - """ - return [line for line in text.splitlines() if _NUMBERED_ITEM.match(line)] - - def _require_red_lines(text: str) -> str: """The red-lines region, or a failure that names what is missing instead of comparing empty. @@ -2744,182 +2699,102 @@ def _require_red_lines(text: str) -> str: return region -def test_the_promote_prompt_gates_a_promotion_action_on_the_host() -> None: - """The one gate the section omitted, stated once, inside the list that owns it. - - What this measures and what it does not: it pins that the rule is **stated in the red lines** - and that it owns no threshold of its own. Whether a round asked the host before acting is that - round's closing summary, which no text assertion can answer. - """ - text = _PROMOTE_TEMPLATE.read_text(encoding="utf-8") - _require_red_lines(text) - - items = _host_gate_items(text) - assert len(items) == 1, ( - f"the red lines state the host's approval gate {len(items)} time(s) (found: {items!r}). " - "Zero is the defect this guard exists for: the section announces itself as the complete " - "list of what a round may never do, so a gate it omits is one a round has no instruction " - "to read before acting. More than one is a duplicate, and the copy is the one that drifts." +def _announced_count(region: str) -> int: + """The number the red-lines heading says it heads.""" + heading = region.splitlines()[0] + claim = re.search(r"\((\d+|[A-Za-z]+) rules?\b", heading) + assert claim, ( + "the heading no longer states how many rules it heads, so a reader cannot tell whether " + f"the list below is complete. It reads: {heading!r}" ) - - item = items[0] - body = _RED_LINE_ITEM.sub("", item) - assert not re.search(r"\d", body), ( - "the gate's item carries a number, which makes the template a second owner of thresholds " - "the installation states. A reading with no edge is a licence to argue the case in its " - f"place, and two copies drift: {body[:220]!r}" + token = claim.group(1).lower() + claimed = int(token) if token.isdigit() else _RED_LINE_COUNT_WORDS.get(token) + assert claimed is not None, ( + f"the heading's count {token!r} is not one this guard can read, so it cannot measure the " + "claim — a number the instrument cannot parse is not a number nobody has to meet" ) + return claimed - for banned in ("\u2265200", "3\u00d7", "three readings"): - assert banned not in text, ( - f"the template carries {banned!r}, an installation's threshold written into a rule " - "meant to own none. Which readings settle the gate belongs to the installation that " - "runs this task; a copy here is a second owner that cannot be kept current." - ) - every_item = _numbered_items(text) - stating = [line for line in every_item if _states_the_host_gate(line)] - assert len(stating) == 1, ( - "the host's gate is stated by more than one numbered rule in this template " - f"({len(stating)} of them carry it, of {len(every_item)} items). The list is where a round " - "looks for what it may never do, so a second rule elsewhere is the copy that drifts from it." - ) - - -#: Twelve rewordings of one rule, every one of them still stating the gate. -#: -#: They are fixtures rather than a matching device: the reading must accept all twelve, so a -#: wording the author has not used yet cannot turn a present rule into an absent one. The first -#: three are rewordings the spelling list this replaces refused while the rule stood in the file, -#: which is the failure these fixtures exist to keep from returning unmeasured. -GATE_STATING_ITEMS = ( - "8. **Gate**: a promotion action requires the host's approval before it clears the channel's.", - "8. **Gate**: a promotion action must be approved by the host before it clears the channel's.", - "8. **Gate**: a promotion action requires approval from the host before it runs.", - "8. **Gate**: a promotion action needs the host to approve it before it runs.", - "8. **Gate**: a promotion action only runs on the host's authorisation.", - "8. **Gate**: a promotion action only runs with the host's authorization.", - "8. **Gate**: sending anything to a channel needs the host's sign-off first.", - "8. **Gate**: the host must consent before a promotion action runs.", - "8. **Gate**: no promotion action runs without the host's permission.", - "8. **Gate**: a promotion action needs the host's clearance before the channel's.", - "8. **Gate**: ask the host first; a promotion action is proposed, not decided here.", - "8. **Gate**: the host approves a promotion action before it happens.", -) +def _red_line_count(text: str) -> tuple[int, int]: + """(the count the heading announces, the count the list holds), read off one text. -#: Seven rules that state no gate — the other direction, and the one a loose reading fails. -#: -#: The fifth names the host and asks no permission; the sixth states a permission and names no -#: host. A reading that fires on either is reading one word instead of the rule. -GATE_FREE_ITEMS = ( - "2. **Participatory**: mention the project as a participant; the round decides for itself " - "when a discussion is relevant.", - "4. **Honest**: a mention in a thread is the round's call once the thread has asked for it.", - "1. **Value first**: a project link is worth posting when the reader's problem is the one the " - "project solves.", - "3. **No spamming**: at most two promotion actions per round.", - "7. **Long-term**: the host's account is an asset; keep following up on every thread.", - "5. **Respect**: post only with permission from the channel's moderators.", - "6. **No bashing**: only talk about the project's differentiators.", -) + One reading applied to two populations — the file and the rendered prompt — so that the two + answers are comparable rather than each being its own instrument. + """ + region = _require_red_lines(text) + return _announced_count(region), len(_red_line_items(text)) -def test_the_gate_pin_reads_the_rule_not_its_wording() -> None: - """The controls: twelve rewrites stay green, seven gate-free rules stay out, three destructive - edits redden. +#: Thresholds an installation states for the gate. A copy written into the template becomes a +#: second owner of a value it cannot keep current, and the two copies drift. +_INSTALLATION_THRESHOLDS = ("\u2265200", "3\u00d7", "three readings") - Deletion is what a rewrite does, relocation is what a busy edit does, a duplicate is what an - addition does, and a reword is what a reviewer asks for — four arms, and only the last of them - may stay green. - What this measures and what it does not: it measures that the reading follows the rule's - substance across wordings this template does not use today. Whether a round asked the host - before acting is that round's closing summary, which no text assertion can answer. +def test_the_promotion_template_owns_no_threshold_of_an_installation() -> None: + """The gate's numbers belong to the installation that runs the task, not to the list. + + What this measures and what it does not: it pins that the three thresholds this file's history + carried are absent. It cannot tell a fourth spelling of a threshold from ordinary prose, and it + does not try — item 8 states the rule (which readings settle the gate is the installation's), + and a reviewer reads the item. """ text = _PROMOTE_TEMPLATE.read_text(encoding="utf-8") _require_red_lines(text) - items = _host_gate_items(text) - assert len(items) == 1, ( - "the red lines state no single gate for this control to rewrite, so the arms below cannot " - "run — the pin above owns that reading and names it; an IndexError here would name nothing" - ) - item = items[0] - - # Arm 1 — a rewrite that keeps the gate must stay green, for every fixture and not one of - # them. The version of this arm that shipped with the guard tried two spellings, both close to - # the file's own wording, which is why it stayed green while the reading refused two thirds of - # the rewordings a reviewer would write. - for candidate in GATE_STATING_ITEMS: - reworded = text.replace(item, candidate) - assert reworded != text, f"{item!r} is pinned but absent, so this arm measured nothing" - assert len(_host_gate_items(reworded)) == 1, ( - "a rewrite that keeps the gate was read as stating none of it: " - f"{candidate!r}. The guard is reading one sentence rather than the rule, which is the " - "defect it exists to avoid — and it reddens a file that is correct." + for banned in _INSTALLATION_THRESHOLDS: + assert banned not in text, ( + f"the template carries {banned!r}, an installation's threshold written into a rule " + "meant to own none. Which readings settle the gate belongs to the installation that " + "runs this task; a copy here is a second owner that cannot be kept current." ) - # Arm 2 — the opposite error, which a loose reading makes: a rule that states no gate must not - # be read as stating one. - for candidate in GATE_FREE_ITEMS: - gate_free = text.replace(item, candidate) - assert gate_free != text, f"{item!r} is pinned but absent, so this arm measured nothing" - assert _host_gate_items(gate_free) == [], ( - f"a rule that states no gate was read as stating one: {candidate!r}. A reading loose " - "enough to accept it answers `a gate is mentioned`, not `this rule is the gate`." - ) - # Arm 3 — deletion: the rule is gone from the section and from the file. - deleted = text.replace(item, "") - assert deleted != text, "the gate item is pinned but absent, so this arm measured nothing" - assert _host_gate_items(deleted) == [], ( - "a deleted gate still reads as present, so the pin would not notice its own rule going" - ) +def test_the_red_lines_heading_counts_the_items_below_it() -> None: + """The parenthetical is a claim about the list; the list is what decides it. - # Arm 4 — relocation: present in the file, absent from the section that owns it. - moved = text.replace(item, "").rstrip() + f"\n\n{item}\n" - assert _host_gate_items(moved) == [], ( - "moving the gate out of the red lines left it reading as stated there, so the pin is " - "reading presence in the file rather than position in the section that owns it" - ) - assert len([line for line in _numbered_items(moved) if _states_the_host_gate(line)]) == 1, ( - "the relocation arm lost the rule it moved, so it measured the wrong thing" - ) + Both directions are one assertion, and they are two different defects: a list longer than its + heading hides a rule behind a number saying it is not there, and a heading larger than its list + announces rules that do not exist. - # Arm 5 — duplication: the same rule twice is the copy that drifts. - duplicated = text.replace(item, f"{item}\n{item}") - assert duplicated != text, "the duplicate arm measured nothing" - assert len(_host_gate_items(duplicated)) == 2, ( - "a duplicated gate reads as stated once, so the exactly-once arm could not fail" + What this measures and what it does not: it pins that the announced count and the numbered + items agree. It says nothing about whether any of the rules was obeyed, and nothing about which + rules they are — item 8's presence is a review's to check, and the block comment says why. + """ + text = _PROMOTE_TEMPLATE.read_text(encoding="utf-8") + claimed, actual = _red_line_count(text) + assert claimed == actual, ( + f"the heading claims {claimed} rules and the list holds {actual}. The count is the " + "section's own statement that a reader has seen all of them." ) -def test_the_red_lines_heading_counts_the_items_below_it() -> None: - """The parenthetical is a claim about the list; the list is what decides it. +def test_the_red_lines_reach_the_round_with_their_count(tmp_path, monkeypatch) -> None: + """The prompt a round reads is the rendered one, and a render can drop a whole section. - What this measures and what it does not: it pins that the announced count and the numbered - items agree. It says nothing about whether any of the rules was obeyed. + A `{% if %}` selects one branch, so a list can render empty while the file still holds every + line of it: the round is handed a red-lines section with nothing under it, and no test that + reads the file alone can tell. So the same two readings are taken on the rendered prompt, and + the item count the file holds must be the item count the round receives. """ - text = _PROMOTE_TEMPLATE.read_text(encoding="utf-8") - region = _require_red_lines(text) - heading = region.splitlines()[0] + rendered = _make_handler( + tmp_path, monkeypatch, "promote_prompt.md", {"agent": "demoproj"} + )._build_evolution_prompt() - claim = re.search(r"\((\d+|[A-Za-z]+) rules?\b", heading) - assert claim, ( - "the heading no longer states how many rules it heads, so a reader cannot tell whether " - f"the list below is complete. It reads: {heading!r}" + leftover = LEFTOVER_TAG.search(rendered) + assert leftover is None, ( + f"an unrendered {leftover.group()!r} is left in the promotion prompt (Jinja saw it as " + "text, so the round would too)" ) - token = claim.group(1).lower() - claimed = int(token) if token.isdigit() else _RED_LINE_COUNT_WORDS.get(token) - assert claimed is not None, ( - f"the heading's count {token!r} is not one this guard can read, so it cannot measure the " - "claim — a number the instrument cannot parse is not a number nobody has to meet" + + claimed, actual = _red_line_count(rendered) + assert claimed == actual, ( + f"the rendered heading claims {claimed} rules and the rendered list holds {actual}" ) - items = _red_line_items(text) - assert claimed == len(items), ( - f"the heading claims {claimed} rules and the list holds {len(items)}. The count is the " - "section's own statement that a reader has seen all of them, so a list longer than its " - "heading hides a rule behind a number that says it is not there." + raw_items = _red_line_items(_PROMOTE_TEMPLATE.read_text(encoding="utf-8")) + assert actual == len(raw_items), ( + f"the render dropped red-line items: the file holds {len(raw_items)} and the prompt hands " + f"the round {actual}. A section that renders short is a rule the round never reads." ) From b7019d730c8e707a089a2bcf18009ea9f595cd1e Mon Sep 17 00:00:00 2001 From: argszero Date: Sun, 1 Nov 2026 17:39:41 +0800 Subject: [PATCH 4/5] emrg: pin the gate's presence too, with the two earlier failures measured apart The two failed readings were not one lesson. The enumerated spelling list refused 8 of 12 rewordings (8 of 12 of them) for a reason the two-signal replacement never touched, and the two-signal replacement failed on two different axes: `asks the host first` and `signs it off` were inflection (`\\bask\\b` cannot match `asks`; `\\bsign[ -]?off\\b` cannot match `signs it off`), while `needs no approval from the host` and a reader's-approval decoy were semantics - no negator was read and no owner was required. So this reads three things of an approval word instead of enumerating sentences: the verb by stem, a negator governing it as a disqualifier, and the host as the approval's owner within 24 characters. `without the host's permission` is exempt, because it denies the absence of the permission and therefore states the gate. Measured on this template's own item 8 with each arm rewriting it in place and the file restored byte-identically (blob 81184422 after every one): `asks the host first` green, `the host signs it off` green, `needs no approval from the host` red, the reader's-approval decoy red - 4 of 4. Fourteen wordings must stay green and fourteen rules must stay out, so both directions run with the file. This keeps all three readings the previous head added - the count in both directions, the absent thresholds, and the render reading - and closes the hole that head named: deleting item 8 while correcting the heading to seven now reddens on the presence pin, which the count alone could not see. The one shape the reading cannot separate - an item that names the host's approval without requiring it - is pinned in KNOWN_BLIND_SPOT_ITEMS as a shape the reading still accepts, so an improvement fails a test and forces the comment to be corrected with it. --- tests/test_prompt_templates.py | 359 +++++++++++++++++++++++++++++++-- 1 file changed, 339 insertions(+), 20 deletions(-) diff --git a/tests/test_prompt_templates.py b/tests/test_prompt_templates.py index 617f8ea4..c2515599 100644 --- a/tests/test_prompt_templates.py +++ b/tests/test_prompt_templates.py @@ -2624,30 +2624,44 @@ def test_the_promote_method_pin_reads_position_and_absence() -> None: # What these guards assert, and what they deliberately do not — the second half is the part a # reader of item 8 needs, and it is written down rather than left to be inferred from silence. # -# They assert **structure**. The heading's announced count agrees with the numbered items below it, -# in both directions; the template carries none of the thresholds an installation states for the -# gate; and the list survives a render through the real builder with those two readings still true. -# That last one is not decoration: a `{% if %}` selects one branch, so a section can render empty -# while the file still holds every line of it — "the file has eight items" and "the prompt a round -# receives has eight items" are two readings, and only the second is what a round acts on. +# They assert **structure, and the gate's presence**. The heading's announced count agrees with the +# numbered items below it, in both directions; the template carries none of the thresholds an +# installation states for the gate; and the list survives a render through the real builder with +# those two readings still true. That last one is not decoration: a `{% if %}` selects one branch, +# so a section can render empty while the file still holds every line of it — "the file has eight +# items" and "the prompt a round receives has eight items" are two readings, and only the second is +# what a round acts on. # -# They do **not** assert that item 8 is present. Two guards tried, and each was measured wrong in -# both directions on this same file. An enumerated spelling list refused **8 of 12** rewordings -# that kept the rule (`requires approval from the host`, `needs the host to approve it`). Its -# replacement read two signals in one item — a host and an approval word — and refused -# `asks the host first` and `signs it off`, while accepting `needs no approval from the host` and a -# rule about quoting a host's private text. A predicate over a rule's prose cannot separate the -# rule from prose about the rule, and each repair moved the failure instead of removing it: a third -# reading would fail a third way. +# **The gate's presence is pinned, and the two earlier attempts are why it took three.** An +# enumerated spelling list refused **8 of 12** rewordings that kept the rule (`requires approval +# from the host`, `needs the host to approve it`). Its replacement read two signals in one item — a +# host and an approval word — and refused `asks the host first` and `signs it off`, while accepting +# `needs no approval from the host` and a rule about quoting a host's private text. Those four +# failures are not one lesson: the refusals were **inflection** (`\bask\b` cannot match `asks`, and +# `\bsign[ -]?off\b` cannot match `signs it off`), while the acceptances were **semantics** — no +# negator was read, and no owner — so a repair aimed at the refusals alone moved nothing on the +# other side. +# +# Hence a reading that asks three things of an approval word: the verb is read by **stem**, a +# **negator** governing it disqualifies the item, and the **host must be the one the approval +# belongs to** (a reference within 24 characters), because a rule that mentions the host and asks a +# reader's approval is a different rule. Measured with each arm rewriting item 8 in place: +# `asks the host first` green, `the host signs it off` green, `needs no approval from the host` red, +# a reader's-approval decoy red — **4 of 4**, against **14 wordings** that must stay green and +# **14 rules** that must stay out, so both directions run whenever this file does. # -# So the gate's presence is a **review obligation**, and the count is what a mechanical reading can -# honestly hold. The hole that leaves, stated here rather than papered over: deleting item 8 without -# touching the heading reddens, and deleting it while correcting the heading to seven stays green. # The count is pinned as a **reading of the list** — the parenthetical is a claim about the items # beneath it, so the items settle it. Measured before this guard, a heading claiming "8 rules" over # seven items passed every test in this file, which made the claim prose. Nothing restates the -# number — deriving it is what keeps it from drifting, and a ninth rule with a corrected heading is -# a legitimate edit this block must not refuse. +# number: deriving it is what keeps it from drifting, and a ninth rule with a corrected heading is a +# legitimate edit this block must not refuse. +# +# The limit is named rather than left to prose. A predicate cannot tell item 8 from an item that +# names the host's approval without requiring it (`the host's approval history is worth reading +# before choosing a channel`), because that needs the item's *subject*, and a subject list is what +# made this matcher grow a branch per review. `KNOWN_BLIND_SPOT_ITEMS` pins that shape as one the +# reading still accepts, so the day it stops doing so a test fails and this comment has to be +# corrected with it — rather than the blind spot quietly widening or shrinking. #: The red-lines heading. The count is not written here — it is read out of the heading and #: compared with the list, so this constant names a section and never a number. @@ -2663,6 +2677,46 @@ def test_the_promote_method_pin_reads_position_and_absence() -> None: #: A numbered red-line item, as the list writes one. _RED_LINE_ITEM = re.compile(r"^\d+\. \*\*") +#: Any numbered item, whatever section it sits under — the population the duplicate arm reads. +_NUMBERED_ITEM = re.compile(r"^\d+\. ") + +#: A reference to the host — the party a promotion action is proposed to. A word rather than a +#: phrase, because `the host's`, `the host to` and `ask the host` are one subject written three ways. +_HOST_REFERENCE = re.compile(r"\bhost(?:'s|s')?\b", re.I) + +#: The approval family: the words a round may use to say the host lets it act. Stems rather than +#: sentences, because which verb an author picks is the author's business and the subject is this +#: guard's — and stems rather than whole words, because `ask` does not match `asks` and a pin that +#: rejects a conjugation reads spelling where it claims to read the rule. +_APPROVAL_FAMILY = re.compile( + r"\bapprov\w*|\bauthoris\w*|\bauthoriz\w*|\bpermission\b|\bconsent\w*|\bclearance\b" + r"|\bsign(?:s|ed|ing)?[ \-](?:it[ \-])?off\b" + r"|\bask(?:s|ed|ing)?\b[^.!?]{0,30}\bfirst\b", + re.I, +) + +#: A negator that can deny the approval instead of stating it. `needs no approval from the host` +#: carries both signals and says the opposite of the rule, so a presence test on its own accepts +#: the one wording that most completely contradicts the gate the section exists to state. +_NEGATOR = re.compile(r"\b(?:no|not|never|without|refus\w*|neither|nor)\b", re.I) + +#: How far a negator reaches, in characters: wide enough for `never requires approval from the host` +#: (15 clear of the word it governs) and `does not need the host's permission` (19), and short +#: enough that a negator governing a different verb in the same sentence is not read as this one's. +_NEGATION_BEFORE = 26 +_NEGATION_AFTER = 20 + +#: How close a host reference must sit to an approval word to be that approval's owner. Measured +#: over the fixture corpus: every wording that states the gate puts them within 18 characters +#: (`the host's approval`, `approved by the host`, `the host signs it off`), while a rule that +#: mentions the host and asks a reader's approval puts them 34 apart. +_ATTRIBUTION_WINDOW = 24 + +#: `without the host's permission` denies the *absence* of the permission, so it states the gate — +#: the strongest form of it. A negator window blind to that difference reddens the rule it exists +#: to protect, which is the defect this whole block is about. +_NEGATED_ABSENCE = re.compile(r"\bwithout (?:the )?(?:host|its|their)\b", re.I) + def _red_lines_region(text: str) -> str: """The red-lines section — heading through the list — excluding the next `### ` section.""" @@ -2679,6 +2733,72 @@ def _red_line_items(text: str) -> list[str]: return [line for line in _red_lines_region(text).splitlines() if _RED_LINE_ITEM.match(line)] +def _states_the_host_gate(line: str) -> bool: + """Whether this item states the gate: the host's approval of an action, in one item. + + A property, never a wording. A guard that enumerates the sentences a rule may be written in + answers "this sentence has not changed" rather than "the rule is still stated": a legitimate + rewrite reddens it, and the red points at a rule that is present. Measured on this template's + own item 8, the three patterns this first replaced read **8 of 12** gate-keeping rewrites as + stating no gate at all; the two-signal predicate that replaced them still read `the round asks + the host first` and `runs only after the host signs it off` as no gate, and read `needs no + approval from the host` as the gate. + + So an approval word has to answer three questions: + + - **whose approval it is** — a host reference must sit within `_ATTRIBUTION_WINDOW` characters, + because a rule that mentions the host and asks someone else's approval is a different rule + (`a reply that quotes the host's private text needs that person's approval`); + - **whether a negator governs it** — `needs no approval from the host` carries both signals and + states their opposite, and must be refused rather than accepted; + - **what conjugation it is written in** — the family is read by stem, so `asks` and `signs it + off` are the same rule as `ask` and `sign-off`. + + Neither signal is enough alone: `the host's account is an asset` names the host and asks no + permission, and `post only with permission from the channel's moderators` states a permission + and names no host. Both are fixtures, and both must read as gate-free. + + What it does not do, written down here rather than left to prose: an item that names the host's + approval without requiring it — `the host's approval history is worth reading before choosing a + channel` — reads as the gate. Telling that apart from the rule needs the item's *subject*, and + a subject list is what made this matcher grow a branch per review; `KNOWN_BLIND_SPOT_ITEMS` + holds that shape as a measured, named limitation instead, so the blind spot cannot move without + this docstring moving with it. + """ + collapsed = " ".join(line.lower().split()) + for match in _APPROVAL_FAMILY.finditer(collapsed): + before = collapsed[max(0, match.start() - _NEGATION_BEFORE):match.start()] + after = collapsed[match.end():match.end() + _NEGATION_AFTER] + governed = f"{before} {match.group(0)}" + if not _NEGATED_ABSENCE.search(governed) and ( + _NEGATOR.search(before) or _NEGATOR.search(after) + ): + continue + near = collapsed[ + max(0, match.start() - _ATTRIBUTION_WINDOW):match.end() + _ATTRIBUTION_WINDOW + ] + if _HOST_REFERENCE.search(near): + return True + return False + + +def _host_gate_items(text: str) -> list[str]: + """The numbered red-line items that state the host's approval gate.""" + return [line for line in _red_line_items(text) if _states_the_host_gate(line)] + + +def _numbered_items(text: str) -> list[str]: + """Every numbered item in the whole template, whatever section it sits under. + + The duplicate arm asks whether a *second rule* states the gate, so its population is numbered + items and not lines. Scoped to lines the reading is wrong in both directions: this template + discusses host actions of its own (`an authorization dialog`, `host-authorized registration`), + which name a host and an approval without stating this gate, so a line-scoped reading reddens + on legitimate prose and points at nothing — the defect the predicate above was repaired for. + """ + return [line for line in text.splitlines() if _NUMBERED_ITEM.match(line)] + + def _require_red_lines(text: str) -> str: """The red-lines region, or a failure that names what is missing instead of comparing empty. @@ -2699,6 +2819,204 @@ def _require_red_lines(text: str) -> str: return region +def test_the_promote_prompt_gates_a_promotion_action_on_the_host() -> None: + """The one gate the section omitted, stated once, inside the list that owns it. + + What this measures and what it does not: it pins that the rule is **stated in the red lines** + and that it owns no threshold of its own. Whether a round asked the host before acting is that + round's closing summary, which no text assertion can answer. + """ + text = _PROMOTE_TEMPLATE.read_text(encoding="utf-8") + _require_red_lines(text) + + items = _host_gate_items(text) + assert len(items) == 1, ( + f"the red lines state the host's approval gate {len(items)} time(s) (found: {items!r}). " + "Zero is the defect this guard exists for: the section announces itself as the complete " + "list of what a round may never do, so a gate it omits is one a round has no instruction " + "to read before acting. More than one is a duplicate, and the copy is the one that drifts." + ) + + item = items[0] + body = _RED_LINE_ITEM.sub("", item) + assert not re.search(r"\d", body), ( + "the gate's item carries a number, which makes the template a second owner of thresholds " + "the installation states. A reading with no edge is a licence to argue the case in its " + f"place, and two copies drift: {body[:220]!r}" + ) + + every_item = _numbered_items(text) + stating = [line for line in every_item if _states_the_host_gate(line)] + assert len(stating) == 1, ( + "the host's gate is stated by more than one numbered rule in this template " + f"({len(stating)} of them carry it, of {len(every_item)} items). The list is where a round " + "looks for what it may never do, so a second rule elsewhere is the copy that drifts from it." + ) + + +#: Fourteen rewordings of one rule, every one of them still stating the gate. +#: +#: They are fixtures rather than a matching device: the reading must accept all fourteen, so a +#: wording the author has not used yet cannot turn a present rule into an absent one. The first +#: three are rewordings the spelling list refused while the rule stood in the file, and the last +#: two are the wordings the two-signal replacement refused — the inflection failures, kept because +#: a repair that only widens a family leaves exactly those red. +GATE_STATING_ITEMS = ( + "8. **Gate**: a promotion action requires the host's approval before it clears the channel's.", + "8. **Gate**: a promotion action must be approved by the host before it clears the channel's.", + "8. **Gate**: a promotion action requires approval from the host before it runs.", + "8. **Gate**: a promotion action needs the host to approve it before it runs.", + "8. **Gate**: a promotion action only runs on the host's authorisation.", + "8. **Gate**: a promotion action only runs with the host's authorization.", + "8. **Gate**: sending anything to a channel needs the host's sign-off first.", + "8. **Gate**: the host must consent before a promotion action runs.", + "8. **Gate**: no promotion action runs without the host's permission.", + "8. **Gate**: a promotion action needs the host's clearance before the channel's.", + "8. **Gate**: ask the host first; a promotion action is proposed, not decided here.", + "8. **Gate**: the host approves a promotion action before it happens.", + "8. **Gate**: the round asks the host first when an action would take the project to readers.", + "8. **Gate**: a promotion action runs only after the host signs it off.", +) + +#: Fourteen rules that state no gate — the other direction, and the one a loose reading fails. +#: +#: The fifth names the host and asks no permission; the sixth states a permission and names no +#: host. A reading that fires on either is reading one word instead of the rule. The rest are the +#: shapes a two-signal reading accepted while stating the opposite of the gate or a different rule: +#: five that **deny** the approval while naming both (`needs no approval from the host` and four +#: conjugations of the same denial), one that asks a **reader's** approval thirty-four characters +#: clear of the host it mentions, and one more that names the host and asks it for nothing. +GATE_FREE_ITEMS = ( + "2. **Participatory**: mention the project as a participant; the round decides for itself " + "when a discussion is relevant.", + "4. **Honest**: a mention in a thread is the round's call once the thread has asked for it.", + "1. **Value first**: a project link is worth posting when the reader's problem is the one the " + "project solves.", + "3. **No spamming**: at most two promotion actions per round.", + "7. **Long-term**: the host's account is an asset; keep following up on every thread.", + "5. **Respect**: post only with permission from the channel's moderators.", + "6. **No bashing**: only talk about the project's differentiators.", + "8. **Gate**: a promotion action needs no approval from the host \u2014 the round decides for " + "itself when a channel is worth it.", + "8. **Gate**: the host's approval is not needed before a promotion action runs.", + "8. **Gate**: a promotion action never requires approval from the host.", + "8. **Gate**: the round does not need the host's permission to run a promotion action.", + "8. **Gate**: no host approval is required for a promotion action.", + "8. **Privacy**: a reply that quotes the host's private text needs that person's approval " + "before it goes out.", + "8. **Timing**: a promotion action is timed by the host's calendar of community events.", +) + +#: The one shape this reading cannot tell from the rule — held as a measurement, not as prose. +#: +#: An item that names the host's approval without requiring it reads as the gate. Telling it apart +#: needs the item's *subject*, and a subject list is what made this matcher grow a branch per +#: review: each review found a class the corpus lacked, and each class widened the list. The limit +#: is worth stating and not worth another signal, so the pin below requires this fixture to keep +#: reading as the gate — which means the day the reading improves, a test fails and the comment +#: above has to be corrected with it, instead of the blind spot quietly widening or closing. +KNOWN_BLIND_SPOT_ITEMS = ( + "8. **Reading**: the host's approval history is worth reading before choosing a channel.", +) + + +def test_the_gate_pin_reads_the_rule_not_its_wording() -> None: + """The controls: fourteen rewrites stay green, fourteen gate-free rules stay out, three + destructive edits redden, and the one shape the reading cannot tell apart stays where the + comment above says it is. + + Deletion is what a rewrite does, relocation is what a busy edit does, a duplicate is what an + addition does, and a reword is what a reviewer asks for — four arms, and only the last of them + may stay green. The sixth arm is the opposite of the second: it requires a fixture to keep + being read as the gate, because that fixture is the reading's stated limit. + + What this measures and what it does not: it measures that the reading follows the rule's + substance across wordings this template does not use today. Whether a round asked the host + before acting is that round's closing summary, which no text assertion can answer. + """ + text = _PROMOTE_TEMPLATE.read_text(encoding="utf-8") + _require_red_lines(text) + items = _host_gate_items(text) + assert len(items) == 1, ( + "the red lines state no single gate for this control to rewrite, so the arms below cannot " + "run — the pin above owns that reading and names it; an IndexError here would name nothing" + ) + item = items[0] + + # Arm 1 — a rewrite that keeps the gate must stay green, for every fixture and not one of + # them. The version of this arm that shipped with the first guard tried two spellings, both + # close to the file's own wording, which is why it stayed green while the reading refused two + # thirds of the rewordings a reviewer would write. + for candidate in GATE_STATING_ITEMS: + if candidate == item: + # The file already states this wording, so there is nothing to rewrite — and nothing + # goes unmeasured, because the pin above is what reads the file's own item. Firing the + # "measured nothing" assertion here instead would report a defect against a file that is + # correct: a legitimate reword refused, with a diagnosis pointing at absence. That is + # this assertion's own failure mode, and it becomes reachable the moment a fixture is + # the wording the template ships. + continue + reworded = text.replace(item, candidate) + assert reworded != text, ( + f"{item!r} is pinned but absent, so this arm measured nothing — the template's gate " + "item changed and this block's anchor did not follow it" + ) + assert len(_host_gate_items(reworded)) == 1, ( + "a rewrite that keeps the gate was read as stating none of it: " + f"{candidate!r}. The guard is reading one sentence rather than the rule, which is the " + "defect it exists to avoid — and it reddens a file that is correct." + ) + + # Arm 2 — the opposite error, which a loose reading makes: a rule that states no gate must not + # be read as stating one. + for candidate in GATE_FREE_ITEMS: + gate_free = text.replace(item, candidate) + assert gate_free != text, f"{item!r} is pinned but absent, so this arm measured nothing" + assert _host_gate_items(gate_free) == [], ( + f"a rule that states no gate was read as stating one: {candidate!r}. A reading loose " + "enough to accept it answers `a gate is mentioned`, not `this rule is the gate`." + ) + + # Arm 3 — deletion: the rule is gone from the section and from the file. This is the arm the + # count can only half cover: it reddens here with or without the heading being corrected. + deleted = text.replace(item, "") + assert deleted != text, "the gate item is pinned but absent, so this arm measured nothing" + assert _host_gate_items(deleted) == [], ( + "a deleted gate still reads as present, so the pin would not notice its own rule going" + ) + + # Arm 4 — relocation: present in the file, absent from the section that owns it. + moved = text.replace(item, "").rstrip() + f"\n\n{item}\n" + assert _host_gate_items(moved) == [], ( + "moving the gate out of the red lines left it reading as stated there, so the pin is " + "reading presence in the file rather than position in the section that owns it" + ) + assert len([line for line in _numbered_items(moved) if _states_the_host_gate(line)]) == 1, ( + "the relocation arm lost the rule it moved, so it measured the wrong thing" + ) + + # Arm 5 — duplication: the same rule twice is the copy that drifts. + duplicated = text.replace(item, f"{item}\n{item}") + assert duplicated != text, "the duplicate arm measured nothing" + assert len(_host_gate_items(duplicated)) == 2, ( + "a duplicated gate reads as stated once, so the exactly-once arm could not fail" + ) + + # Arm 6 — the limit, pinned so it cannot drift. This arm is the mirror of the five above: they + # require the reading to follow the rule, and this one requires it to keep failing on a shape + # it cannot tell from the rule. A reading that starts refusing this fixture has got better, and + # the comment above then states something untrue — so the failure is the reminder to correct + # it, which is why this arm asserts the limitation rather than the absence of one. + for candidate in KNOWN_BLIND_SPOT_ITEMS: + blind = text.replace(item, candidate) + assert blind != text, f"{item!r} is pinned but absent, so this arm measured nothing" + assert len(_host_gate_items(blind)) == 1, ( + f"{candidate!r} is no longer read as the gate, which is an improvement: the reading " + "now tells a mention of the host's approval from the approval itself. Move this " + "fixture into GATE_FREE_ITEMS and correct the blind spot the comment states." + ) + + def _announced_count(region: str) -> int: """The number the red-lines heading says it heads.""" heading = region.splitlines()[0] @@ -2758,7 +3076,8 @@ def test_the_red_lines_heading_counts_the_items_below_it() -> None: What this measures and what it does not: it pins that the announced count and the numbered items agree. It says nothing about whether any of the rules was obeyed, and nothing about which - rules they are — item 8's presence is a review's to check, and the block comment says why. + rules they are — that is the pin above, and the two divide between them: this reading survives a + corrected heading, and the presence reading is what a corrected heading cannot buy. """ text = _PROMOTE_TEMPLATE.read_text(encoding="utf-8") claimed, actual = _red_line_count(text) From e5b6501e80883f8f32d25ec47257f2fa0b10eb8f Mon Sep 17 00:00:00 2001 From: argszero Date: Mon, 2 Nov 2026 00:11:29 +0800 Subject: [PATCH 5/5] emrg: the gate's presence is a review obligation, not a predicate --- tests/test_prompt_templates.py | 378 ++++----------------------------- 1 file changed, 36 insertions(+), 342 deletions(-) diff --git a/tests/test_prompt_templates.py b/tests/test_prompt_templates.py index c2515599..124fc718 100644 --- a/tests/test_prompt_templates.py +++ b/tests/test_prompt_templates.py @@ -2624,44 +2624,43 @@ def test_the_promote_method_pin_reads_position_and_absence() -> None: # What these guards assert, and what they deliberately do not — the second half is the part a # reader of item 8 needs, and it is written down rather than left to be inferred from silence. # -# They assert **structure, and the gate's presence**. The heading's announced count agrees with the -# numbered items below it, in both directions; the template carries none of the thresholds an -# installation states for the gate; and the list survives a render through the real builder with -# those two readings still true. That last one is not decoration: a `{% if %}` selects one branch, -# so a section can render empty while the file still holds every line of it — "the file has eight -# items" and "the prompt a round receives has eight items" are two readings, and only the second is -# what a round acts on. +# They assert **structure**. The heading's announced count agrees with the numbered items below +# it, in both directions; the template carries none of the thresholds an installation states for +# the gate; and the list survives a render through the real builder with those two readings still +# true. That last one is not decoration: a `{% if %}` selects one branch, so a section can render +# empty while the file still holds every line of it — "the file has eight items" and "the prompt a +# round receives has eight items" are two readings, and only the second is what a round acts on. # -# **The gate's presence is pinned, and the two earlier attempts are why it took three.** An -# enumerated spelling list refused **8 of 12** rewordings that kept the rule (`requires approval -# from the host`, `needs the host to approve it`). Its replacement read two signals in one item — a -# host and an approval word — and refused `asks the host first` and `signs it off`, while accepting -# `needs no approval from the host` and a rule about quoting a host's private text. Those four -# failures are not one lesson: the refusals were **inflection** (`\bask\b` cannot match `asks`, and -# `\bsign[ -]?off\b` cannot match `signs it off`), while the acceptances were **semantics** — no -# negator was read, and no owner — so a repair aimed at the refusals alone moved nothing on the -# other side. +# **They do not assert that item 8 states the gate, and that is a measurement rather than a +# preference.** Five readings were tried, each repairing the failure the one before it named, and +# each was wrong in both directions on sentences written outside its own fixtures. An enumerated +# spelling list refused **8 of 12** rewordings that kept the rule (`requires approval from the +# host`, `needs the host to approve it`). Its replacement read two signals in one item — a host and +# an approval word — and refused `asks the host first` and `signs it off` while accepting `needs no +# approval from the host`; the refusals were **inflection** and the acceptances were **semantics**, +# so a repair aimed at one side moved nothing on the other. The last reading answered both: the +# approval verb by **stem**, a **negator window** that disqualifies an item, and the **host +# required as the approval's owner** — and it scored 14 of 14 in each direction on its own corpus. # -# Hence a reading that asks three things of an approval word: the verb is read by **stem**, a -# **negator** governing it disqualifies the item, and the **host must be the one the approval -# belongs to** (a reference within 24 characters), because a rule that mentions the host and asks a -# reader's approval is a different rule. Measured with each arm rewriting item 8 in place: -# `asks the host first` green, `the host signs it off` green, `needs no approval from the host` red, -# a reader's-approval decoy red — **4 of 4**, against **14 wordings** that must stay green and -# **14 rules** that must stay out, so both directions run whenever this file does. +# Then it was measured against sixteen statements written from the rule's meaning, never used while +# building it: it read **2 of 10** gate statements as the gate, and the widest repair built +# afterwards read **3 of 10**, against 14 of 14 each on the fixtures they were built from. The class +# is not the wording. `the host decides whether a promotion action happens` and `the round proposes; +# the host decides` state the gate in words no word list would hold, so a lexical presence test +# cannot decide whether a sentence *requires* the host's approval, and widening the list buys one +# sentence at a time. A corpus drawn from a reading's own vocabulary cannot refute it, which is how +# the 14-of-14 readings above stayed green over a rule no test could see. # -# The count is pinned as a **reading of the list** — the parenthetical is a claim about the items -# beneath it, so the items settle it. Measured before this guard, a heading claiming "8 rules" over -# seven items passed every test in this file, which made the claim prose. Nothing restates the -# number: deriving it is what keeps it from drifting, and a ninth rule with a corrected heading is a -# legitimate edit this block must not refuse. +# So the gate's presence is a **review obligation**: a reviewer reads item 8 and judges it, and the +# pull request that added the item says so in its body. That is weaker than a test, and it is what +# is true; the alternative on record is an assertion that agrees with itself. # -# The limit is named rather than left to prose. A predicate cannot tell item 8 from an item that -# names the host's approval without requiring it (`the host's approval history is worth reading -# before choosing a channel`), because that needs the item's *subject*, and a subject list is what -# made this matcher grow a branch per review. `KNOWN_BLIND_SPOT_ITEMS` pins that shape as one the -# reading still accepts, so the day it stops doing so a test fails and this comment has to be -# corrected with it — rather than the blind spot quietly widening or shrinking. +# **The limit that follows, stated rather than left to be found.** The count catches a deleted item +# only while the heading still claims the old number. Deleting item 8 *and* correcting the heading +# to seven leaves every guard in this block green: the count agrees with the list, no threshold was +# added, and the render still carries every item the file holds. Nothing here can tell that edit +# from a legitimate removal of a rule the section no longer needs — the same reason the presence +# reading is gone rather than repaired. #: The red-lines heading. The count is not written here — it is read out of the heading and #: compared with the list, so this constant names a section and never a number. @@ -2677,46 +2676,6 @@ def test_the_promote_method_pin_reads_position_and_absence() -> None: #: A numbered red-line item, as the list writes one. _RED_LINE_ITEM = re.compile(r"^\d+\. \*\*") -#: Any numbered item, whatever section it sits under — the population the duplicate arm reads. -_NUMBERED_ITEM = re.compile(r"^\d+\. ") - -#: A reference to the host — the party a promotion action is proposed to. A word rather than a -#: phrase, because `the host's`, `the host to` and `ask the host` are one subject written three ways. -_HOST_REFERENCE = re.compile(r"\bhost(?:'s|s')?\b", re.I) - -#: The approval family: the words a round may use to say the host lets it act. Stems rather than -#: sentences, because which verb an author picks is the author's business and the subject is this -#: guard's — and stems rather than whole words, because `ask` does not match `asks` and a pin that -#: rejects a conjugation reads spelling where it claims to read the rule. -_APPROVAL_FAMILY = re.compile( - r"\bapprov\w*|\bauthoris\w*|\bauthoriz\w*|\bpermission\b|\bconsent\w*|\bclearance\b" - r"|\bsign(?:s|ed|ing)?[ \-](?:it[ \-])?off\b" - r"|\bask(?:s|ed|ing)?\b[^.!?]{0,30}\bfirst\b", - re.I, -) - -#: A negator that can deny the approval instead of stating it. `needs no approval from the host` -#: carries both signals and says the opposite of the rule, so a presence test on its own accepts -#: the one wording that most completely contradicts the gate the section exists to state. -_NEGATOR = re.compile(r"\b(?:no|not|never|without|refus\w*|neither|nor)\b", re.I) - -#: How far a negator reaches, in characters: wide enough for `never requires approval from the host` -#: (15 clear of the word it governs) and `does not need the host's permission` (19), and short -#: enough that a negator governing a different verb in the same sentence is not read as this one's. -_NEGATION_BEFORE = 26 -_NEGATION_AFTER = 20 - -#: How close a host reference must sit to an approval word to be that approval's owner. Measured -#: over the fixture corpus: every wording that states the gate puts them within 18 characters -#: (`the host's approval`, `approved by the host`, `the host signs it off`), while a rule that -#: mentions the host and asks a reader's approval puts them 34 apart. -_ATTRIBUTION_WINDOW = 24 - -#: `without the host's permission` denies the *absence* of the permission, so it states the gate — -#: the strongest form of it. A negator window blind to that difference reddens the rule it exists -#: to protect, which is the defect this whole block is about. -_NEGATED_ABSENCE = re.compile(r"\bwithout (?:the )?(?:host|its|their)\b", re.I) - def _red_lines_region(text: str) -> str: """The red-lines section — heading through the list — excluding the next `### ` section.""" @@ -2733,76 +2692,10 @@ def _red_line_items(text: str) -> list[str]: return [line for line in _red_lines_region(text).splitlines() if _RED_LINE_ITEM.match(line)] -def _states_the_host_gate(line: str) -> bool: - """Whether this item states the gate: the host's approval of an action, in one item. - - A property, never a wording. A guard that enumerates the sentences a rule may be written in - answers "this sentence has not changed" rather than "the rule is still stated": a legitimate - rewrite reddens it, and the red points at a rule that is present. Measured on this template's - own item 8, the three patterns this first replaced read **8 of 12** gate-keeping rewrites as - stating no gate at all; the two-signal predicate that replaced them still read `the round asks - the host first` and `runs only after the host signs it off` as no gate, and read `needs no - approval from the host` as the gate. - - So an approval word has to answer three questions: - - - **whose approval it is** — a host reference must sit within `_ATTRIBUTION_WINDOW` characters, - because a rule that mentions the host and asks someone else's approval is a different rule - (`a reply that quotes the host's private text needs that person's approval`); - - **whether a negator governs it** — `needs no approval from the host` carries both signals and - states their opposite, and must be refused rather than accepted; - - **what conjugation it is written in** — the family is read by stem, so `asks` and `signs it - off` are the same rule as `ask` and `sign-off`. - - Neither signal is enough alone: `the host's account is an asset` names the host and asks no - permission, and `post only with permission from the channel's moderators` states a permission - and names no host. Both are fixtures, and both must read as gate-free. - - What it does not do, written down here rather than left to prose: an item that names the host's - approval without requiring it — `the host's approval history is worth reading before choosing a - channel` — reads as the gate. Telling that apart from the rule needs the item's *subject*, and - a subject list is what made this matcher grow a branch per review; `KNOWN_BLIND_SPOT_ITEMS` - holds that shape as a measured, named limitation instead, so the blind spot cannot move without - this docstring moving with it. - """ - collapsed = " ".join(line.lower().split()) - for match in _APPROVAL_FAMILY.finditer(collapsed): - before = collapsed[max(0, match.start() - _NEGATION_BEFORE):match.start()] - after = collapsed[match.end():match.end() + _NEGATION_AFTER] - governed = f"{before} {match.group(0)}" - if not _NEGATED_ABSENCE.search(governed) and ( - _NEGATOR.search(before) or _NEGATOR.search(after) - ): - continue - near = collapsed[ - max(0, match.start() - _ATTRIBUTION_WINDOW):match.end() + _ATTRIBUTION_WINDOW - ] - if _HOST_REFERENCE.search(near): - return True - return False - - -def _host_gate_items(text: str) -> list[str]: - """The numbered red-line items that state the host's approval gate.""" - return [line for line in _red_line_items(text) if _states_the_host_gate(line)] - - -def _numbered_items(text: str) -> list[str]: - """Every numbered item in the whole template, whatever section it sits under. - - The duplicate arm asks whether a *second rule* states the gate, so its population is numbered - items and not lines. Scoped to lines the reading is wrong in both directions: this template - discusses host actions of its own (`an authorization dialog`, `host-authorized registration`), - which name a host and an approval without stating this gate, so a line-scoped reading reddens - on legitimate prose and points at nothing — the defect the predicate above was repaired for. - """ - return [line for line in text.splitlines() if _NUMBERED_ITEM.match(line)] - - def _require_red_lines(text: str) -> str: """The red-lines region, or a failure that names what is missing instead of comparing empty. - Every gate reading in this block is scoped to `§1`. Without this, a renamed heading makes + Every count reading in this block is scoped to `§1`. Without this, a renamed heading makes `_red_lines_region` answer `""`, and a guard that compares against `""` passes while measuring nothing at all. """ @@ -2814,209 +2707,11 @@ def _require_red_lines(text: str) -> str: ) assert _red_line_items(text), ( f"{RED_LINES_HEADING!r} is there and holds no numbered item. The list is that section's " - "content, so an empty one leaves both the count and the gate with nothing to read." + "content, so an empty one leaves the count with nothing to read." ) return region -def test_the_promote_prompt_gates_a_promotion_action_on_the_host() -> None: - """The one gate the section omitted, stated once, inside the list that owns it. - - What this measures and what it does not: it pins that the rule is **stated in the red lines** - and that it owns no threshold of its own. Whether a round asked the host before acting is that - round's closing summary, which no text assertion can answer. - """ - text = _PROMOTE_TEMPLATE.read_text(encoding="utf-8") - _require_red_lines(text) - - items = _host_gate_items(text) - assert len(items) == 1, ( - f"the red lines state the host's approval gate {len(items)} time(s) (found: {items!r}). " - "Zero is the defect this guard exists for: the section announces itself as the complete " - "list of what a round may never do, so a gate it omits is one a round has no instruction " - "to read before acting. More than one is a duplicate, and the copy is the one that drifts." - ) - - item = items[0] - body = _RED_LINE_ITEM.sub("", item) - assert not re.search(r"\d", body), ( - "the gate's item carries a number, which makes the template a second owner of thresholds " - "the installation states. A reading with no edge is a licence to argue the case in its " - f"place, and two copies drift: {body[:220]!r}" - ) - - every_item = _numbered_items(text) - stating = [line for line in every_item if _states_the_host_gate(line)] - assert len(stating) == 1, ( - "the host's gate is stated by more than one numbered rule in this template " - f"({len(stating)} of them carry it, of {len(every_item)} items). The list is where a round " - "looks for what it may never do, so a second rule elsewhere is the copy that drifts from it." - ) - - -#: Fourteen rewordings of one rule, every one of them still stating the gate. -#: -#: They are fixtures rather than a matching device: the reading must accept all fourteen, so a -#: wording the author has not used yet cannot turn a present rule into an absent one. The first -#: three are rewordings the spelling list refused while the rule stood in the file, and the last -#: two are the wordings the two-signal replacement refused — the inflection failures, kept because -#: a repair that only widens a family leaves exactly those red. -GATE_STATING_ITEMS = ( - "8. **Gate**: a promotion action requires the host's approval before it clears the channel's.", - "8. **Gate**: a promotion action must be approved by the host before it clears the channel's.", - "8. **Gate**: a promotion action requires approval from the host before it runs.", - "8. **Gate**: a promotion action needs the host to approve it before it runs.", - "8. **Gate**: a promotion action only runs on the host's authorisation.", - "8. **Gate**: a promotion action only runs with the host's authorization.", - "8. **Gate**: sending anything to a channel needs the host's sign-off first.", - "8. **Gate**: the host must consent before a promotion action runs.", - "8. **Gate**: no promotion action runs without the host's permission.", - "8. **Gate**: a promotion action needs the host's clearance before the channel's.", - "8. **Gate**: ask the host first; a promotion action is proposed, not decided here.", - "8. **Gate**: the host approves a promotion action before it happens.", - "8. **Gate**: the round asks the host first when an action would take the project to readers.", - "8. **Gate**: a promotion action runs only after the host signs it off.", -) - -#: Fourteen rules that state no gate — the other direction, and the one a loose reading fails. -#: -#: The fifth names the host and asks no permission; the sixth states a permission and names no -#: host. A reading that fires on either is reading one word instead of the rule. The rest are the -#: shapes a two-signal reading accepted while stating the opposite of the gate or a different rule: -#: five that **deny** the approval while naming both (`needs no approval from the host` and four -#: conjugations of the same denial), one that asks a **reader's** approval thirty-four characters -#: clear of the host it mentions, and one more that names the host and asks it for nothing. -GATE_FREE_ITEMS = ( - "2. **Participatory**: mention the project as a participant; the round decides for itself " - "when a discussion is relevant.", - "4. **Honest**: a mention in a thread is the round's call once the thread has asked for it.", - "1. **Value first**: a project link is worth posting when the reader's problem is the one the " - "project solves.", - "3. **No spamming**: at most two promotion actions per round.", - "7. **Long-term**: the host's account is an asset; keep following up on every thread.", - "5. **Respect**: post only with permission from the channel's moderators.", - "6. **No bashing**: only talk about the project's differentiators.", - "8. **Gate**: a promotion action needs no approval from the host \u2014 the round decides for " - "itself when a channel is worth it.", - "8. **Gate**: the host's approval is not needed before a promotion action runs.", - "8. **Gate**: a promotion action never requires approval from the host.", - "8. **Gate**: the round does not need the host's permission to run a promotion action.", - "8. **Gate**: no host approval is required for a promotion action.", - "8. **Privacy**: a reply that quotes the host's private text needs that person's approval " - "before it goes out.", - "8. **Timing**: a promotion action is timed by the host's calendar of community events.", -) - -#: The one shape this reading cannot tell from the rule — held as a measurement, not as prose. -#: -#: An item that names the host's approval without requiring it reads as the gate. Telling it apart -#: needs the item's *subject*, and a subject list is what made this matcher grow a branch per -#: review: each review found a class the corpus lacked, and each class widened the list. The limit -#: is worth stating and not worth another signal, so the pin below requires this fixture to keep -#: reading as the gate — which means the day the reading improves, a test fails and the comment -#: above has to be corrected with it, instead of the blind spot quietly widening or closing. -KNOWN_BLIND_SPOT_ITEMS = ( - "8. **Reading**: the host's approval history is worth reading before choosing a channel.", -) - - -def test_the_gate_pin_reads_the_rule_not_its_wording() -> None: - """The controls: fourteen rewrites stay green, fourteen gate-free rules stay out, three - destructive edits redden, and the one shape the reading cannot tell apart stays where the - comment above says it is. - - Deletion is what a rewrite does, relocation is what a busy edit does, a duplicate is what an - addition does, and a reword is what a reviewer asks for — four arms, and only the last of them - may stay green. The sixth arm is the opposite of the second: it requires a fixture to keep - being read as the gate, because that fixture is the reading's stated limit. - - What this measures and what it does not: it measures that the reading follows the rule's - substance across wordings this template does not use today. Whether a round asked the host - before acting is that round's closing summary, which no text assertion can answer. - """ - text = _PROMOTE_TEMPLATE.read_text(encoding="utf-8") - _require_red_lines(text) - items = _host_gate_items(text) - assert len(items) == 1, ( - "the red lines state no single gate for this control to rewrite, so the arms below cannot " - "run — the pin above owns that reading and names it; an IndexError here would name nothing" - ) - item = items[0] - - # Arm 1 — a rewrite that keeps the gate must stay green, for every fixture and not one of - # them. The version of this arm that shipped with the first guard tried two spellings, both - # close to the file's own wording, which is why it stayed green while the reading refused two - # thirds of the rewordings a reviewer would write. - for candidate in GATE_STATING_ITEMS: - if candidate == item: - # The file already states this wording, so there is nothing to rewrite — and nothing - # goes unmeasured, because the pin above is what reads the file's own item. Firing the - # "measured nothing" assertion here instead would report a defect against a file that is - # correct: a legitimate reword refused, with a diagnosis pointing at absence. That is - # this assertion's own failure mode, and it becomes reachable the moment a fixture is - # the wording the template ships. - continue - reworded = text.replace(item, candidate) - assert reworded != text, ( - f"{item!r} is pinned but absent, so this arm measured nothing — the template's gate " - "item changed and this block's anchor did not follow it" - ) - assert len(_host_gate_items(reworded)) == 1, ( - "a rewrite that keeps the gate was read as stating none of it: " - f"{candidate!r}. The guard is reading one sentence rather than the rule, which is the " - "defect it exists to avoid — and it reddens a file that is correct." - ) - - # Arm 2 — the opposite error, which a loose reading makes: a rule that states no gate must not - # be read as stating one. - for candidate in GATE_FREE_ITEMS: - gate_free = text.replace(item, candidate) - assert gate_free != text, f"{item!r} is pinned but absent, so this arm measured nothing" - assert _host_gate_items(gate_free) == [], ( - f"a rule that states no gate was read as stating one: {candidate!r}. A reading loose " - "enough to accept it answers `a gate is mentioned`, not `this rule is the gate`." - ) - - # Arm 3 — deletion: the rule is gone from the section and from the file. This is the arm the - # count can only half cover: it reddens here with or without the heading being corrected. - deleted = text.replace(item, "") - assert deleted != text, "the gate item is pinned but absent, so this arm measured nothing" - assert _host_gate_items(deleted) == [], ( - "a deleted gate still reads as present, so the pin would not notice its own rule going" - ) - - # Arm 4 — relocation: present in the file, absent from the section that owns it. - moved = text.replace(item, "").rstrip() + f"\n\n{item}\n" - assert _host_gate_items(moved) == [], ( - "moving the gate out of the red lines left it reading as stated there, so the pin is " - "reading presence in the file rather than position in the section that owns it" - ) - assert len([line for line in _numbered_items(moved) if _states_the_host_gate(line)]) == 1, ( - "the relocation arm lost the rule it moved, so it measured the wrong thing" - ) - - # Arm 5 — duplication: the same rule twice is the copy that drifts. - duplicated = text.replace(item, f"{item}\n{item}") - assert duplicated != text, "the duplicate arm measured nothing" - assert len(_host_gate_items(duplicated)) == 2, ( - "a duplicated gate reads as stated once, so the exactly-once arm could not fail" - ) - - # Arm 6 — the limit, pinned so it cannot drift. This arm is the mirror of the five above: they - # require the reading to follow the rule, and this one requires it to keep failing on a shape - # it cannot tell from the rule. A reading that starts refusing this fixture has got better, and - # the comment above then states something untrue — so the failure is the reminder to correct - # it, which is why this arm asserts the limitation rather than the absence of one. - for candidate in KNOWN_BLIND_SPOT_ITEMS: - blind = text.replace(item, candidate) - assert blind != text, f"{item!r} is pinned but absent, so this arm measured nothing" - assert len(_host_gate_items(blind)) == 1, ( - f"{candidate!r} is no longer read as the gate, which is an improvement: the reading " - "now tells a mention of the host's approval from the approval itself. Move this " - "fixture into GATE_FREE_ITEMS and correct the blind spot the comment states." - ) - - def _announced_count(region: str) -> int: """The number the red-lines heading says it heads.""" heading = region.splitlines()[0] @@ -3076,8 +2771,7 @@ def test_the_red_lines_heading_counts_the_items_below_it() -> None: What this measures and what it does not: it pins that the announced count and the numbered items agree. It says nothing about whether any of the rules was obeyed, and nothing about which - rules they are — that is the pin above, and the two divide between them: this reading survives a - corrected heading, and the presence reading is what a corrected heading cannot buy. + rules they are — a corrected heading buys this reading and is exactly what it cannot see past. """ text = _PROMOTE_TEMPLATE.read_text(encoding="utf-8") claimed, actual = _red_line_count(text)