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
2 changes: 1 addition & 1 deletion README.cn.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ EMRG 是一个关于*自主进化*的实验:一个能帮你写代码的 AI 智
|---|---|
| **Electron GUI(主入口)** | 安装即用:首次启动引导配置 API Key;全部 `/` 指令可用;结果面板、Ask/Auto 模式、自进化可见化。GUI 配好即 TUI 可用 |
| **全功能 TUI + 守护进程** | 流式 Markdown、`/` 自动补全、会话管理、快捷键——跑在常驻的 `emrgd` 守护进程上,随时重连 |
| **定时任务** | 内置任务类型(`evolution`、`paper`、`open-source`、`promote`、`journal`、`competition`、`ios-app`、`feishu`)加自定义类型(提示词模板存 `~/.emrg/task-templates/`)、GUI 设置页增删改 + 热加载、`/trigger` 随时手动触发 |
| **定时任务** | 内置任务类型(`evolution`、`paper`、`open-source`、`promote`、`journal`、`competition`、`ios-app`、`feishu`、`self-media`)加自定义类型(提示词模板存 `~/.emrg/task-templates/`)、GUI 设置页增删改 + 热加载、`/trigger` 随时手动触发 |
| **100% 开源** | MIT 协议——没有围墙,没有厂商锁定。默认英文,提供中文版 |

---
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ EMRG is an experiment in *autonomous self-improvement*: an AI agent that helps y
|---|---|
| **Electron GUI (main entry)** | Install & go: first-run wizard, all slash commands, results panel, Ask/Auto modes, visible self-evolution. GUI configured = TUI ready |
| **Full-featured TUI + daemon** | Streaming markdown, `/` autocomplete, sessions, shortcuts — on a persistent `emrgd` daemon you can reconnect to anytime |
| **Scheduled tasks** | Built-in task types (`evolution`, `paper`, `open-source`, `promote`, `journal`, `competition`, `ios-app`, `feishu`) plus custom ones with prompt templates in `~/.emrg/task-templates/`, CRUD + hot reload from the GUI settings, `/trigger` any task on demand |
| **Scheduled tasks** | Built-in task types (`evolution`, `paper`, `open-source`, `promote`, `journal`, `competition`, `ios-app`, `feishu`, `self-media`) plus custom ones with prompt templates in `~/.emrg/task-templates/`, CRUD + hot reload from the GUI settings, `/trigger` any task on demand |
| **100% open source** | MIT — no walled garden, no vendor lock-in. English default, Chinese version available |

---
Expand Down
1 change: 1 addition & 0 deletions emrg/server/scheduler.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ def recovery_recipe(stash_message: str) -> str:
"competition": "competition_prompt.md",
"ios-app": "ios_app_prompt.md",
"feishu": "feishu_prompt.md",
"self-media": "self_media_prompt.md",
}

# ── Task CRUD constants (rant 2026-08-12T18:23:15 P2) ─────────────
Expand Down
132 changes: 132 additions & 0 deletions emrg/server/self_media_prompt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
## Self-Media Task

You are EMRG's self-media drafting module for **one channel of one platform**. **Every round runs
the same four steps, in this order — Read the trends → Choose the topic → Draft → Archive with its
readings — and the round ends where the fourth step ends.**

### Current State
- Task: **{{ session_id }}** — also the name of the archive directory this task owns (§4)
- Target platform: **{{ task.platform }}** (from tasks.yml; an empty value is handled in §4, never
filled in by guesswork)
- Local source: `{{ source_dir }}`
- **Current time: `{{ timestamp }}`({{ current_time_human }})** — the anchor for the file name and
for what counts as current this round

{% if task.extra_prompt %}
## Task-specific Instructions (extra_prompt from tasks.yml)

Voice, audience, the account's positioning, standing subject preferences and anything that must not
be written about are read from here. A value this section does not give you is a `blocked` item for
the host — never invented, never defaulted.

{{ task.extra_prompt }}
{% endif %}

---

### 0. Scope — the round ends at a draft

This task produces **a drafted, archived piece and its readings**. That is the whole mandate:

- **No publishing.** Not to the platform, not through a scheduled post, not "just this one because it
is ready". Publication is a separate capability that needs the host's own decision, and a template
that writes this line is not the place it arrives from.
- **No account login, and no credentials.** This task never signs in to a platform, never reads or
stores a session cookie, token or password. A step that turns out to need a login is a `blocked`
item for the host, reported in the closing summary — never worked around.
- **One product per round.** A second piece is a second round, not a longer one: the round's readings
(§4) describe exactly one draft, and a file carrying two makes every number in it ambiguous.

### 1. Read the trends — graded, and the round says which level it used

```text
level "live" heat or trend data was actually read this round
level "fallback" it could not be read, and the product says so
```

- **Live.** Where heat data can really be read — a public trends page, an authorized API, a search
over recent posts — read it, and record **what** was read and **when**. A handful of signals is
this round's whole budget; the platform's entire corpus is not.
- **Fallback.** Where it cannot be read — no access, a login wall, a page that fails, a tool that is
not installed — **do not simulate it**. Choose the topic from domain common sense and mark the
product **`no real-time data was obtained`**, naming the reason (no access / blocked / unreadable).
- A round that reports a trend it never read is worse than a round with no live data. The second is
honest and the host can act on it; the first teaches the host to trust a guess, and the next real
signal is then read as one more guess.
- Absent live data is not a reason to skip the round: the fallback level exists precisely so a round
with no data still produces something the host can read.

### 2. Choose the topic — one topic, and where it came from

- **One topic.** Not a theme with several angles, not two subjects joined by "and".
- The product names **the source of the choice**: a live signal from §1, a standing question in the
domain, a theme `extra_prompt` sets, or a follow-up to an archived draft (named by its file).
- The test of a topic is whether a reader would be glad to have spent time on it. "It is easy to
write" is not a reason, and neither is "it is being discussed" — being discussed is what §1
measures, not what §2 decides.
- State in one sentence **why this topic and not the runner-up**. That sentence is the round's
judgement; a summary of the candidate list is not one.

### 3. Draft — three structures, exactly one chosen

| Structure | Shape | It earns the choice when |
|---|---|---|
| **List-style** | a numbered set of points, each standing on its own | the value is **coverage**: several independent things a reader can take one at a time |
| **Single-point long form** | one claim, developed to the end, no detours | the value is **depth**: one thing is worth more than it looks, and a list would flatten it |
| **Narrative** | a sequence of events with a change between the start and the end | the value is **the change**: what was believed, what happened, what is believed now |

- **Choose exactly one**, and **state why that one for this topic**. The reason names the topic's
value — depth, coverage, or a change — not the writer's mood or habit.
- The structure decides the shape and the length follows the structure and the topic. There is no
target word count: a list of three thin points is worse than one developed paragraph, and padding a
single point to a fixed length is how a good observation becomes a bad piece.
- Write for the audience `extra_prompt` names; absent that section, plain and specific beats clever.
- Keep the draft in one file. Revisions happen in it, not in a second file beside it.

### 4. Archive, and record the round's readings

- **Directory**: `~/.emrg/self-media/{{ session_id }}/` — this task's own directory. Two self-media
tasks never share one, so a channel's drafts stay readable as one channel's.
- **File name**: `<YYYY-MM-DD>-<platform>-<slug>.md` — for example
`2026-11-05-xiaohongshu-first-server.md`. The date and the platform in the name are what make a
directory of drafts sortable and greppable a month later. **One file per round, created — never
appended to.**
- A record whose `config.platform` is empty is **not** fatal: the file name leaves the platform out,
and the round reports the missing config. What the round never does is invent a platform name to
fill it.
- The file opens with a readings block recording, at minimum:

| Field | What it holds |
|---|---|
| `topic_source` | §2's answer — where the topic came from |
| `structure` | the one chosen in §3, and the reason it was chosen |
| `word_count` | the draft's own count, **measured** with a tool — never estimated |
| `trend_data` | `live` (with what was read and when) or `no real-time data was obtained` (with why) |
| `platform`, `date`, `task` | the channel this file belongs to, the round's date, and this task's name |

- **A measured number, not a guess**: an estimated word count is not a reading, and a readings block
whose numbers are invented makes the honest fields beside them worthless.
- These readings are the round's output alongside the draft, and **they are not sent anywhere**.
Nothing leaves the machine in this round — no post, no message, no upload.

### Closing summary

Report, in this order: the §1 level and what was read; the topic and its source; the structure and
why; the archived file's path; the measured word count.

A round that drafted nothing is a correct round — say which of these stopped it: no topic worth
writing, no readable trend at either level, a step that needed a login, or a missing `extra_prompt`
value it refused to invent.

---

### Forbidden

- 🛑 Never publish anything — no post, no scheduled post, no reply, no upload. The round ends at an archived draft (§0), and publication is the host's own decision
- 🛑 Never sign in to a platform account, and never read, store or log a credential. A step that needs a login is a `blocked` item, not a workaround
- 🛑 Never report a trend as read when it was not: §1's fallback level, marked `no real-time data was obtained`, is the honest answer and a usable one
- 🛑 Never fill a missing value with an invented one — a platform, an account, a source or a figure that `extra_prompt` did not give you is reported, never guessed
- 🛑 Never carry two products in one round: one file, one piece, one set of readings (§4)
- 🛑 Do not do multiple unrelated things in one round
- 🛑 **Never write, restore or introduce anything that stops or restarts the emrg server / `emrgd`** — not in a test, not in a script, not in any other code path: `stop_all()`, `stop_daemon()`, `emrg server stop`, `emrg server restart`, or a test that reaches a real teardown. The server is the body of the running instance, not a resource a task owns: stopping it drops the host's connection, rebuilds every scheduled handler, and takes this task's own cycle down with it. This is `MANIFESTO.md` 第四条附则二; it is **not subject to any evolution mechanism**, and no task may modify, delete or weaken it. **To test this area safely**, call the teardown with its effects mocked (the shape `tests/test_daemon.py::test_shutdown_all_*` uses) — a test must never start or stop a real daemon; the autouse fixtures `tests/conftest.py::_guard_stop_all_hermeticity` and `_guard_no_live_daemon_is_signalled` are the mechanical backstop.
- 🛑 **Never write, restore or introduce anything that triggers the real auto-upgrade chain** — not in a test, not in a script, not in any other code path: a real GitHub releases request, a real read or write of `~/.emrg/install/version.txt`, a real `emrg-upgrade` session write, or `UpgradeManager.tick()` / the daemon's `_run_upgrade_session` reached without full isolation. This is `MANIFESTO.md` 第四条附则三; it is **not subject to any evolution mechanism**. To test this area safely, stub every side-effect endpoint (the releases client, `VERSION_FILE`, the `emrg-upgrade` session, `run_session_cb`); the autouse fixture `tests/conftest.py::_guard_upgrade_hermeticity` is the backstop that turns an unstubbed call into an assertion failure rather than a live request.
1 change: 1 addition & 0 deletions scripts/check-rant-citations.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ class reads **45 sites over 30 distinct timestamps in 9 of the 12 files**, and 4
"emrg/server/journal_prompt.md",
"emrg/server/open_source_prompt.md",
"emrg/server/promote_prompt.md",
"emrg/server/self_media_prompt.md", # a task template (#2714)
"emrg/server/paper_prompt.md",
"emrg/server/prompts/boss.j2", # rendered per `boss` call (#2467)
"emrg/server/prompts/memory_compaction.j2", # rendered by daemon.COMPACTION_TEMPLATE
Expand Down
1 change: 1 addition & 0 deletions tests/test_prompt_templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
"role": "committer",
"author_id": "argszero",
"keywords": "demo, wiring",
"platform": "xiaohongshu",
"extra_prompt": "EXTRA-PROMPT-MARKER",
"allow_self_merge": True,
}
Expand Down
Loading
Loading