From eec2b3947d6506c35e36ce283c4f6e8c4567f27b Mon Sep 17 00:00:00 2001 From: EMRG Evolution Date: Thu, 22 Oct 2026 03:18:37 +0800 Subject: [PATCH] emrg: the promote task reads what is addressed to it, and counts what it publishes --- emrg/server/promote_prompt.md | 18 +++++++++-- tests/test_prompt_templates.py | 59 ++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 3 deletions(-) diff --git a/emrg/server/promote_prompt.md b/emrg/server/promote_prompt.md index a9a2cf2b..8a12cee6 100644 --- a/emrg/server/promote_prompt.md +++ b/emrg/server/promote_prompt.md @@ -211,7 +211,19 @@ if a platform forbids automated publishing, comply or mark the platform blocked. #### Step 1 — Find topics (recon) -Search for topics related to {{ project.name }}: +Recon has two directions, and **the inbound one comes first**: read what is already waiting on this account before looking for anything new. + +```bash +# the threads this account opened elsewhere — its own open pull requests and issues +gh search prs --author @me --state open --limit 20 --json repository,number,title,updatedAt +gh search issues --author @me --state open --limit 20 --json repository,number,title,updatedAt +# then the newest comment on each one that is not this account's own +gh api repos///issues//comments --jq '.[-1] | "\(.user.login) @ \(.created_at): \(.body)"' +``` + +A maintainer who asks for a rebase, a test, clearer wording, or an answer has addressed a **request to this account**, and carrying it out is the round's first work — ahead of any new participation. A thread left waiting while the same account opens another costs twice from one cause: the request stands unanswered, and there is one more thread to keep up with. A request this round cannot carry out does not drop out of sight — it enters `promotion tracking` as the to-do, with the link and what was asked (§4.1). + +Then look outward. Search for topics related to {{ project.name }}: ```bash # Reddit example: search topics related to the project's domain @@ -230,7 +242,7 @@ curl -s "https://hn.algolia.com/api/v1/search?query=&tags=story" 1. **Read the full discussion**: open the original post with the browser harness, read all replies (not just the OP), understand the context, existing viewpoints, and the asker's real concern. 2. **Research related material**: verify third-party projects/terms/background involved in the discussion first (docs / repos / official sites) — no unfounded claims. -3. **Verify locally**: if the discussion involves technical claims (performance, API, behavior), **write a test script / run a local verification before replying** — technical facts in your reply must be verified, not from memory or reasoning alone. +3. **Verify locally**: if the discussion involves technical claims (performance, API, behavior), **write a test script / run a local verification before replying** — technical facts in your reply must be verified, not from memory or reasoning alone. **A count is such a claim**: how many tests a file holds, how many files a directory has, how many commits, versions, users or bytes — each is a number a command produced in this round, and it is quoted from that output. A count that no command produced, one carried over from an earlier round included, is taken out rather than softened, because a wrong count reads exactly like a verified one and the reader has no way to tell them apart. 4. **Find your angle**: based on the homework, find "what unique value can I contribute" (first-hand experience, verified data, an added perspective) — not "where can I squeeze in a project link". 5. **If homework is too costly or time is limited → skip that discussion** (record it in this round's closing summary as a `promotion opportunities` entry, join when you can do it properly) rather than posting a low-quality reply. @@ -322,7 +334,7 @@ Every field below is REPLACED every round — this is a snapshot, not a log: - promotion target: - promotion log: - promotion opportunities: -- promotion tracking: +- promotion tracking: - last learned: - homework record: - flagged/negative: diff --git a/tests/test_prompt_templates.py b/tests/test_prompt_templates.py index 88a23fb8..ca8c8192 100644 --- a/tests/test_prompt_templates.py +++ b/tests/test_prompt_templates.py @@ -2124,3 +2124,62 @@ def test_the_published_contribution_pin_answers_both_ways(): f"removing {phrase!r} did not make exactly that phrase read as missing, so this " "pin would not notice its own rule being deleted" ) + + +# --------------------------------------------------------------------------- the inbound half +# +# A round of the promotion task is written as four outward steps: find a topic, join it, +# follow the threads it started, hand the feedback on. Every list such a round carries is +# therefore a list of what it sent out, and nothing in the template answered the opposite +# question — what has someone else asked this account to do, and is it still waiting. +# +# Measured cost, on a pull request this account had opened in another project: a maintainer +# asked for a rebase in a comment, no round read it, and the request sat unanswered for nine +# days while later rounds opened new threads instead. Carrying it out took minutes. +# +# The second half is the same defect one level down. A changelog entry published in that pull +# request said a test file held "7 tests"; the file had held six since the branch was created, +# and no command had counted anything. Step 2 already requires technical claims to be verified +# locally, and a count does not read as a claim, so it passed under a rule written for prose. +# +# Pinned as verbatim phrases for the reason the pin above states: the file and the *render* +# are two carriers, and a rewrite that keeps the surrounding paragraphs while dropping these +# leaves every keyword in place. + +PROMOTE_INBOUND_PHRASES = ( + "the inbound one comes first", + "addressed a **request to this account**", + "it enters `promotion tracking` as the to-do", + "requests addressed to this account that it has not answered yet", + "**A count is such a claim**", + "A count that no command produced", +) + + +def test_the_promote_prompt_reads_what_is_asked_of_it_and_counts_what_it_publishes(): + """Both halves of a round the template was missing: inbound work, and measured numbers. + + What this measures and what it does not: it pins that the two rules are still **stated**. + Whether a round ran the reading is that round's closing summary — not something a text + assertion can answer, so this guard has no verdict on behaviour. + """ + text = _PROMOTE_TEMPLATE.read_text(encoding="utf-8") + missing = _missing_phrases(text, PROMOTE_INBOUND_PHRASES) + assert not missing, ( + "the promote template no longer states what its four outward steps leave out: " + f"{missing}. Without the first, every list a round carries is one-directional and a " + "maintainer's request can wait indefinitely while the round finds new topics; without " + "the second, a published count is a number nothing counted." + ) + + +def test_the_inbound_pin_answers_both_ways(): + """The control: removing one phrase makes exactly that phrase read as missing.""" + text = _PROMOTE_TEMPLATE.read_text(encoding="utf-8") + for phrase in PROMOTE_INBOUND_PHRASES: + stripped = text.replace(phrase, "") + assert stripped != text, f"{phrase!r} is pinned but absent, so this arm measured nothing" + assert _missing_phrases(stripped, PROMOTE_INBOUND_PHRASES) == [phrase], ( + f"removing {phrase!r} did not make exactly that phrase read as missing, so this " + "pin would not notice its own rule being deleted" + )