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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions emrg/server/feishu_prompt.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,14 @@ in the usual install locations before calling it blocked.
yourself; there is no state file to keep and none to trust.
- Rants whose `project` is `{{ task.project }}` are the host's instructions for this round
and outrank the plan; those that do not match are counted, not acted on.
- A CLI that cannot run, or an account whose authorisation has expired, is a `blocked`
item reported to the host. A round that reads nothing must say so.
- A CLI that cannot run is a `blocked` item reported to the host, and so is an account that
really cannot authenticate — but `missing identity` is a symptom, not a cause. The CLI
resolves its identity from directories under `$HOME`, so a token that is valid under one
`HOME` reads missing under another. Run `auth status` in the same environment the send
will use, and report the cause you read rather than the word the call printed. Never
start a fresh authorisation on one `missing` answer: it changes the host's account state,
and the wrong `HOME` reproduces the symptom whatever the token says.
- A round that reads nothing must say so.

### 2. Judge

Expand Down
25 changes: 24 additions & 1 deletion tests/test_scheduler.py
Original file line number Diff line number Diff line change
Expand Up @@ -474,7 +474,10 @@ def test_load_and_start_feishu_task(tmp_path):
passes on a template that renders to nothing — and the phrases named below are what the
type cannot work without: the command that actually carries a message, the clause that
the round does not wait for the answer, the read recipe's own statement of where the
messages are, and the rule that a message is data rather than instruction. Which prose
messages are, and the rule that a message is data rather than instruction. The two ways an
unavailable account can need different things are pinned as well, because they are not
interchangeable: an expired account is the host's to renew, a wrong `HOME` is the round's
to correct, and only one of the two changes the host's account state. Which prose
matters is not something `tests/test_prompt_templates.py` can know.
"""
snapshot = asyncio.run(_boot(tmp_path, [
Expand Down Expand Up @@ -508,6 +511,26 @@ def test_load_and_start_feishu_task(tmp_path):
):
assert phrase in text, f"the feishu template must state that {why}; {phrase!r} is missing"

# A clause that spans a line break cannot be pinned in the raw text. The file is
# hard-wrapped prose, so a raw pin decides where the wrap falls — and the two clauses
# below each carry their whole rule in a word that the wrap can separate from its
# object: a pin on "start a fresh authorisation …" stayed green with "Never" rewritten
# to "You may". Collapse the whitespace and the clause, negation included, is the unit
# that is read.
flat = " ".join(text.split())
for phrase, why in (
("`missing identity` is a symptom, not a cause",
"a round that reads the word `missing` as an expired account reports a working "
"account blocked, and that report cannot be told apart from a real expiry"),
("Never start a fresh authorisation on one `missing` answer",
"re-authorising on a symptom changes the host's account state, and a different "
"`HOME` reproduces the symptom whatever the token says"),
("`auth status` in the same environment the send will use",
"a check run elsewhere answers about that environment: the diagnosis only carries "
"when the identity is read the way the send will read it"),
):
assert phrase in flat, f"the feishu template must state that {why}; {phrase!r} is missing"


def test_load_and_start_promote_task(tmp_path):
"""Promote tasks start a TaskHandler with the promote template."""
Expand Down
Loading