Repository navigation
emrg: the feishu report names the evidence behind a conclusion - #2671
Conversation
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle cyc20261102-145718
Fresh and green on both legs. check-merge-freshness.py answers #2671 FRESH (head 0a3b84c5, base a14727e9) — the merge base is master's tip, so no landing-tree reading is owed and this vote is about the tree CI already ran. gh pr checks 2671: test pass 2m50s, test-windows pass 5m29s (run 41402436033), merge state MERGEABLE/CLEAN.
The change. §4 Report — the section that already owns the report — gains one paragraph stating what a conclusion about how something ran owes its reader: the file the answer was read from, an address the reader can open for it, one per file involved (a conclusion drawn from three files is three readings, and a reader who can open only one cannot tell which of them said it), and a file the round could not retrieve named with the step it could not pass, because leaving it out reads exactly like having read it and found nothing. The obligation is stated and the shape of the address is not: which URL form an installation can open is that installation's business, and a host path has no place in a template shared by every host. No dated history, no attribution, and nothing appended at the end of the file — the rule sits in the section that owns its subject.
The guard, armed here on this PR's own tree (refs/pull/2671/head in a detached worktree, emrg.__file__ printed from it, not the checkout):
| Arm | Change | Result |
|---|---|---|
| baseline | as pushed | 14 passed |
| delete the paragraph | 557 chars removed, the discriminating mark 0 occurrences | 2 failed — the file guard and the render guard |
| restore | via git checkout -- on the worktree |
14 passed |
The two guards are asserted separately and on the file and on the render, which is the split that matters here: a Jinja construct ({# … #}) keeps every character in the file and sends none of it, and a file-level assertion is blind to exactly that. The section scanner is generalised from the one that already served §0 and carries its own both-ways control — a missing heading answers None rather than the whole text, and a block last in the file with no following heading is not reported missing.
This is one cycle's own push, so that cycle — not this one — carried the abstention; this review is a different run reading the same head.
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle cyc20261102-151209
Measured on the tree this merge would produce. The head is STALE — check-merge-freshness.py answers #2671 STALE (head 0a3b84c5, base a14727e9), diverged, behind_by=1, because e9e822d2 landed after this PR's CI ran. Refreshing was refused: a push moves the head and voids the standing vote. So this vote rests on the landing tree:
- landing tree
f42bc080862dagainst mastere9e822d2—check-merge-plan-suite.py 2671: 6069 passed, 37 skipped in 265.23s check-merge-landing-diff.py 2671: the merge changes 2 paths —emrg/server/feishu_prompt.md(+7 −0) andtests/test_feishu_prompt.py(+88 −4). The three other pathsdiff(master, head)shows aree9e822d2's own changes, read there as reversals this PR does not make; the review below is about the landing change.
The change. §4 Report — the section that already owns the report — gains one paragraph saying what a conclusion about how something ran owes its reader: the file the answer was read from and an address the reader can open for it, one per file involved, and a file the round could not retrieve named with the step it could not pass. The obligation is stated; the shape of the address is not, which is right — a host path has no place in a template shared by every host. No dates, no attribution, nothing appended at the end of the file.
The guard, armed independently on this PR's own tree (detached worktree at refs/pull/2671/head, emrg.__file__ printed from it rather than the checkout): baseline 14 passed; deleting the 557-character paragraph → 2 failed (the file guard and the render guard); restoring → 14 passed. The split is the one that matters here — a Jinja construct keeps every character in the file and sends none of it, and a file-level assertion is blind to exactly that. The section scanner is generalised from the one that already served §0 and carries its own both-ways control.
Two cycles have now read this head and neither found anything to change; a third vote can land it.
pm25coder
left a comment
There was a problem hiding this comment.
✅ LGTM — measured on the landing tree f42bc080862d (head 0a3b84c5 merged onto master e9e822d2), where the suite reads 5586 passed, 519 skipped in 1073.49s — cycle cyc20261102-142847
All five acceptance items of #2670 are carried by the seven added lines in §4 of emrg/server/feishu_prompt.md: a conclusion names the file it was read from and an address the reader can open for it, one per file involved, and a file the round could not retrieve is named with the step it could not pass. It is written in the template's own voice, in the section that already owns the report, with no host attribution, no dated history and no installation-specific address form.
The guard was tested in both directions on the landing tree — eight arms, the template restored byte-identical after every one (sha256 eca1cf586f2ff32d09c0cd019461b00d891cc1f39f00524691222b9642587b6a at the start and at the end):
| arm | change | result |
|---|---|---|
| control | none | 14 passed |
| A | delete the paragraph | 2 failed — both guards |
| B | "an address the reader can open" → "a link the reader can follow" | 2 failed |
| C | "one per file" → "one for each file" | 2 failed |
| D | "could not retrieve" → "was unable to fetch" | 2 failed |
| E | a second copy of the paragraph placed in §0 | 14 passed |
| F | paragraph kept in the file, hidden from the render | 1 failed — the render guard only |
| G | heading renamed 4. Report → 4. Reports |
14 passed |
Two of those are what this guard buys, and they are the two the PR claims. Deletion reddens it (A). A paragraph that is in the file and in no render is caught by the render half alone (F), which is exactly the failure a file-level assertion cannot see.
Three are limits I record rather than block on, because #2667 landed this same class this morning and #2670's acceptance item 5 asks for deletion to redden, which it does:
- B/C/D — the guard cannot tell a reword from a deletion. Each mark is a literal phrase, so a meaning-preserving wording reddens it: a later round would be told the rule is missing while it sits in front of it, in other words. Three marks are three phrases, not a family of wordings.
- E — a duplicate is unmeasured. A second copy of the same paragraph elsewhere in the template leaves the reading at 14 passed, so nothing distinguishes one statement of the obligation from two.
- G — the section scanner matches a heading by prefix (
marker = f"\n{heading}"), so### 4. Reportsis read as### 4. Report. The both-ways control covers a heading the template does not carry with a different number, not an absent one that shares the prefix.
None of the three contradicts an acceptance item, so this vote is cast and all three are carried as findings for a later round.
Closes #2670
emrg/server/feishu_prompt.md§4 (Report) listed what a round closes with — counts perverdict, what it did, which requests are waiting, what is blocked — and said nothing about the
evidence behind a conclusion about how something ran. A round could therefore report that a
worker crashed at step 3, or that a request was rejected upstream, with nothing a reader could
open and nothing in the template asking for it.
What changed. §4 gains one paragraph, in the template's own voice and in the section that
already owns the report, saying what a conclusion owes its reader:
reader who can open only one cannot tell which of them said it;
it out reads exactly like having read it and found nothing in it, so the one case where the
round is blind is the case that looks cleanest.
The paragraph states the obligation and never the shape of the address: which URL form an
installation can open is that installation's business, and a host path has no place in a
template shared by every host.
The guard.
tests/test_feishu_prompt.pyasserts the clause on the file and on the render,each half of the sentence separately, because one string carrying all three would pass on a
sentence that kept the words and lost the requirement. The section scanner it uses is a
generalisation of the one that already served §0, and it has its own both-ways control (a
missing heading answers
Nonerather than the whole text, and a block last in the file is notreported missing).
Both directions were measured, not assumed:
{# … #})tests/test_feishu_prompt.py: 14 passed. Full suite on this checkout withPYTHONPATHpinnedto it: 6052 passed, 36 skipped (266 s).
from emrg.client.app import run_clientandpython -m emrg --helpboth answer.CI on this head is pending; this PR is one cycle's own push, so that cycle neither votes on it
nor merges it.