@easysociable/cli 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/README.md +4 -1
  2. package/dist/easysociable.js +301 -2829
  3. package/dist/easysociable.js.map +1 -1
  4. package/package.json +17 -12
  5. package/skills/ONBOARDING.md +3 -1
  6. package/skills/easysociable-content/INSTALL.md +60 -0
  7. package/skills/easysociable-content/SKILL.md +107 -96
  8. package/skills/easysociable-content/fixtures/golden-run-en/draft.md +13 -0
  9. package/skills/easysociable-content/fixtures/golden-run-en/idea.md +26 -0
  10. package/skills/easysociable-content/fixtures/golden-run-en/pack-tiktok.json +11 -0
  11. package/skills/easysociable-content/platforms/claude.md +1 -2
  12. package/skills/easysociable-content/platforms/codex.md +1 -2
  13. package/skills/easysociable-content/platforms/hermes.md +1 -2
  14. package/skills/easysociable-content/platforms/openclaw.md +1 -2
  15. package/skills/easysociable-content/references/CLI.md +25 -12
  16. package/skills/easysociable-content/references/CONTRACT_SYNC.md +42 -0
  17. package/skills/easysociable-content/references/MCP.md +7 -12
  18. package/skills/easysociable-content/references/PAGE_PACK.md +128 -5
  19. package/skills/easysociable-content/references/PLATFORM_GUIDE.md +36 -0
  20. package/skills/easysociable-content/references/RUN_LAYOUT.md +73 -0
  21. package/skills/easysociable-content/references/TROUBLESHOOTING.md +11 -9
  22. package/skills/easysociable-content/references/VAULT.md +32 -12
  23. package/skills/easysociable-content/references/VOICE.md +16 -0
  24. package/skills/easysociable-content/scripts/contracts.mjs +183 -0
  25. package/skills/easysociable-content/scripts/export-contracts.mjs +76 -0
  26. package/skills/easysociable-content/scripts/fixture.test.mjs +24 -0
  27. package/skills/easysociable-content/scripts/package-skill.mjs +49 -0
  28. package/skills/easysociable-content/scripts/setup.mjs +127 -83
  29. package/skills/easysociable-content/scripts/setup.test.mjs +49 -38
  30. package/skills/easysociable-content/scripts/starter.mjs +50 -48
  31. package/skills/easysociable-content/scripts/validate-export.mjs +42 -0
  32. package/skills/easysociable-content/scripts/validate-pack.mjs +48 -0
  33. package/skills/easysociable-content/scripts/validate-run.mjs +100 -0
  34. package/skills/easysociable-content/scripts/validate.test.mjs +101 -0
  35. package/skills/easysociable-content/scripts/workbench.mjs +135 -0
  36. package/skills/easysociable-content/starter/CLAUDE.md +756 -0
  37. package/skills/easysociable-content/starter/accounts/README.md +9 -0
  38. package/skills/easysociable-content/starter/accounts/_index.md +31 -0
  39. package/skills/easysociable-content/starter/accounts/overrides/README.md +3 -0
  40. package/skills/easysociable-content/starter/discovery/README.md +18 -0
  41. package/skills/easysociable-content/starter/discovery/_index.md +27 -0
  42. package/skills/easysociable-content/starter/discovery/_template.md +41 -0
  43. package/skills/easysociable-content/starter/engine.json +19 -0
  44. package/skills/easysociable-content/starter/exports/README.md +28 -0
  45. package/skills/easysociable-content/starter/inbox/README.md +9 -0
  46. package/skills/easysociable-content/starter/inbox/_index.md +24 -0
  47. package/skills/easysociable-content/starter/inbox/entries/README.md +3 -0
  48. package/skills/easysociable-content/starter/platforms/README.md +19 -0
  49. package/skills/easysociable-content/starter/platforms/ig.md +69 -0
  50. package/skills/easysociable-content/starter/platforms/instagram.md +5 -0
  51. package/skills/easysociable-content/starter/platforms/linkedin.md +72 -0
  52. package/skills/easysociable-content/starter/platforms/threads.md +69 -0
  53. package/skills/easysociable-content/starter/platforms/tiktok.md +71 -0
  54. package/skills/easysociable-content/starter/platforms/x.md +78 -0
  55. package/skills/easysociable-content/starter/platforms/xiaohongshu.md +19 -0
  56. package/skills/easysociable-content/starter/profiles/_index.md +13 -0
  57. package/skills/easysociable-content/starter/profiles/default/README.md +11 -0
  58. package/skills/easysociable-content/starter/profiles/default/audience.md +38 -0
  59. package/skills/easysociable-content/starter/profiles/default/pillars.md +33 -0
  60. package/skills/easysociable-content/starter/profiles/default/voice.md +24 -0
  61. package/skills/easysociable-content/starter/published/_index.md +20 -0
  62. package/skills/easysociable-content/starter/raw/README.md +4 -0
  63. package/skills/easysociable-content/starter/raw/_index.md +12 -0
  64. package/skills/easysociable-content/starter/runs/README.md +22 -0
  65. package/skills/easysociable-content/starter/runs/_index.md +41 -0
  66. package/skills/easysociable-content/starter/runs/_template/brief.md +66 -0
  67. package/skills/easysociable-content/starter/runs/_template/draft.md +11 -0
  68. package/skills/easysociable-content/starter/runs/_template/editor.md +18 -0
  69. package/skills/easysociable-content/starter/runs/_template/feedback.md +15 -0
  70. package/skills/easysociable-content/starter/runs/_template/idea.md +27 -0
  71. package/skills/easysociable-content/starter/runs/_template/pack.md +47 -0
  72. package/skills/easysociable-content/starter/runs/_template/packet.md +25 -0
  73. package/skills/easysociable-content/starter/runs/_template/rubric.md +19 -0
  74. package/skills/easysociable-content/starter/swipe/_index.md +27 -0
  75. package/skills/easysociable-content/starter/swipe/_template.md +34 -0
  76. package/skills/easysociable-content/starter/topics/README.md +25 -0
  77. package/skills/easysociable-content/starter/topics/WATCHLIST.md +65 -0
  78. package/skills/easysociable-content/starter/topics/_index.md +44 -0
  79. package/skills/easysociable-content/starter/topics/_template-item.md +90 -0
  80. package/skills/easysociable-content/starter/topics/items/README.md +4 -0
  81. package/skills/easysociable-content/starter/topics/scoring.md +201 -0
  82. package/skills/easysociable-content/starter/topics/watchlists/README.md +10 -0
  83. package/skills/easysociable-content/starter/topics/watchlists/_shared.md +14 -0
  84. package/skills/easysociable-content/starter/topics/watchlists/default.md +6 -0
  85. package/skills/easysociable-content/starter/wiki/README.md +13 -0
  86. package/skills/easysociable-content/starter/wiki/atoms/_index.md +23 -0
  87. package/skills/easysociable-content/starter/wiki/atoms/_template.md +31 -0
  88. package/skills/easysociable-content/starter/wiki/claims/_index.md +26 -0
  89. package/skills/easysociable-content/starter/wiki/claims/_template.md +38 -0
  90. package/skills/easysociable-content/starter/workbench/README.md +17 -0
  91. package/skills/easysociable-content/workbench/build.py +1074 -0
  92. package/skills/easysociable-content/workbench/serve.py +53 -0
  93. package/skills/easysociable-content/workbench/template.html +2119 -0
  94. package/skills/easysociable-content/workflows/ANALYSIS_AND_COVERAGE.md +14 -5
  95. package/skills/easysociable-content/workflows/CONTENT_EXPORT.md +16 -11
  96. package/skills/easysociable-content/workflows/GETTING_STARTED.md +40 -0
  97. package/skills/easysociable-content/workflows/MCP_FIRST.md +3 -8
  98. package/skills/easysociable-content/workflows/OUT_OF_SCOPE.md +18 -0
  99. package/skills/easysociable-content/workflows/PACK_AUTHORING.md +42 -0
  100. package/skills/easysociable-content/workflows/SWIPE_CAPTURE.md +18 -0
  101. package/skills/easysociable-content/workflows/TOPIC_DRAFT.md +28 -4
  102. package/skills/easysociable-content/workflows/WORKING_DRAFT.md +17 -8
  103. package/skills/easysociable-slideshow/SKILL.md +20 -13
  104. package/skills/easysociable-slideshow/references/VAULT.md +13 -11
  105. package/skills/easysociable-template/scripts/generate-theme-from-hex.mjs +0 -0
  106. package/skills/easysociable-content/workflows/DEVICE_TASKS.md +0 -5
  107. package/skills/easysociable-content/workflows/EXPERIMENTS.md +0 -6
  108. package/skills/easysociable-content/workflows/PUBLICATION.md +0 -5
@@ -0,0 +1,756 @@
1
+ # Content Engine — Agent operating manual
2
+
3
+ This folder is the local **Content Engine** for EasySociable.
4
+
5
+ **Human interface:** conversation only. The user does **not** edit markdown by hand. Observatory UI: `workbench/` at `http://127.0.0.1:8765/workbench/`.
6
+ **Agent interface:** you create/update files; always return a structured result card in chat. After markdown writes, run `python3 workbench/build.py` (leave `serve.py` running if already up).
7
+
8
+ ## Defaults (v1)
9
+
10
+ - Folder tree: **English** (`Content Engine/`). Do not ask language.
11
+ - Default **account:** first **active** handle in `accounts/_index.md` (starter: `your-handle` — replace it)
12
+ - Default **platform:** `x` (watchlist / catalog filter when unset)
13
+ - **开一单:** do **not** silent-default platform — **ask**; multi-select → **N runs**
14
+ - Do not invent metrics, customers, or quotes the user did not provide
15
+
16
+ ### Resolve account → profile (required)
17
+
18
+ 1. Read **`accounts/_index.md`** for the handle (default: first active row)
19
+ 2. Get `profile_id`, `voice`, `platforms`, `default_platform`, `overrides`
20
+ 3. Load **`profiles/<profile_id>/pillars.md`** + **`audience.md`** (core + current platform section)
21
+ 4. Voice: if account `voice` is `profile` or empty → **`profiles/<profile_id>/voice.md`** (includes **Identity** + tone); else load the explicit path. Never invent bio facts not in Identity.
22
+ 5. If `overrides` is **non-empty**, read that path (e.g. `accounts/overrides/<handle>.md`) and apply last
23
+ 6. Craft: **always** load `platforms/<platform>.md` for the active platform (see `platforms/README.md` for id → file; `instagram` → `ig.md`). Missing file → stop and tell the user; do not invent craft rules.
24
+ 7. Catalog filter: swipe/atoms/claims where `platforms` matches **and** (`profiles` is `*` **or** contains `profile_id`)
25
+
26
+ ## File map
27
+
28
+ | Path | Role |
29
+ |------|------|
30
+ | `CLAUDE.md` | Operating manual |
31
+ | `profiles/_index.md` | Profile registry |
32
+ | `profiles/<id>/pillars.md` | Topic pillars (what to talk about) |
33
+ | `profiles/<id>/audience.md` | Readers + platform emphasis |
34
+ | `profiles/<id>/voice.md` | Identity + tone |
35
+ | `accounts/_index.md` | handle → profile, platforms, voice, overrides |
36
+ | `accounts/overrides/<handle>.md` | Optional account deltas |
37
+ | `platforms/*.md` | Platform craft only |
38
+ | `inbox/_index.md` | Capture registry |
39
+ | `inbox/entries/C-*.md` | Per-capture working notes |
40
+ | `raw/_index.md` | Authoritative source registry |
41
+ | `raw/*.md` | Source bodies |
42
+ | `topics/_index.md` | Topic queue (one row per topic + `item` + src engagement) |
43
+ | `topics/items/T-*.md` | Content Opportunity (structured fields + scores) |
44
+ | `topics/WATCHLIST.md` | Watchlist router (per-profile files) |
45
+ | `discovery/_index.md` | Analyzed watchlist signals and promotion status |
46
+ | `discovery/D-*.md` | Readable Source Digest, source limits, and workflow state |
47
+ | `topics/watchlists/<profile_id>.md` | Profile-scoped radar handles |
48
+ | `topics/watchlists/_shared.md` | Shared radar (`*`, all profiles) |
49
+ | `topics/scoring.md` | Scout gate + opportunity five-dim (context/audience/conflict/insight/evidence) |
50
+ | `swipe/_index.md` | Structures (+ `profiles` column) |
51
+ | `wiki/atoms/_index.md` | Atoms (+ `profiles`) |
52
+ | `wiki/claims/_index.md` | Claims (+ `profiles`) |
53
+ | `runs/_index.md` | Job queue (one row per **platform run**) |
54
+ | `runs/<slug>/` | One platform draft → pack → feedback |
55
+ | `published/_index.md` | One row per **shipped URL** (one run → usually one row) |
56
+ | `exports/<slug>/` | Rendered images for that run (optional `<platform>/` subfolder) |
57
+
58
+ ## Routing
59
+
60
+ | User intent | Action |
61
+ |-------------|--------|
62
+ | capture / 记一下 (+ link) | **§ Capture** only — **not** topics by default |
63
+ | 加 watchlist / 更新 WATCHLIST | Edit `topics/watchlists/<profile>.md` or `_shared.md` (**ask profile** if unset; set **`platform`**) |
64
+ | 按 watchlist 选题 / 扫 list 选题 | **§ Discovery** — **ask profile if unset**; filter **`platform`** (default `x`); analyze signals before promoting Topics |
65
+ | 扫 TikTok/LinkedIn/… watchlist | Same; only rows where `platform` matches (after profile resolve) |
66
+ | 扫 inbox 选题 | **§ Topics** — from captures without topic yet (max 5) |
67
+ | 扫 watchlist / 发现内容 | **§ Discovery** — create Discovery records; do not create Topics directly |
68
+ | 生成 topic (no source specified) | **§ Topics** — ask for the input/source; never choose an existing Discovery silently |
69
+ | 链接直接选题 / 把 C-xxx 做成选题 | **§ Topics** — create T- (may link capture) |
70
+ | 开一单 / 用 T-xxx 开一单 | **§ Open a run** — **ask platform(s)** if unset; multi-select → **N runs** (bind `topic_id` if from T-) |
71
+ | Quick draft | Resolve profile; selection; draft in chat |
72
+ | 收成 swipe/atom/claim | **§ Library ingest** after approval |
73
+ | 选题后同意进 inbox | Capture that URL/C-link then optional library offer |
74
+ | Post URL after ship | feedback + **§ Published index** |
75
+ | 复盘 / 批准晋级 | Claims promote + published index |
76
+ | Update pillars/audience | Edit **`profiles/<id>/…`** |
77
+ | Update account binding | Edit **`accounts/_index.md`** |
78
+ | 出图 | **§ PagePack + render** |
79
+
80
+ ---
81
+
82
+ ## § Capture (inbox + raw indexes)
83
+
84
+ ### When
85
+
86
+ User stores an idea, link, competitor structure, or source — not a pure Q&A.
87
+
88
+ ### Always write (scheme B)
89
+
90
+ 1. **`inbox/entries/C-YYYYMMDD-XX.md`** — working notes (our understanding; not necessarily full source text)
91
+ 2. **Row at top of `inbox/_index.md`** — registry (id, type, one_liner, profile, entry path, raw path, library, status)
92
+
93
+ Id: `C-YYYYMMDD-XX` sequential per day (`01`, `02`, …).
94
+ Default `profile`: current account’s `profile_id`.
95
+
96
+ ### Also write raw when
97
+
98
+ - URL worth revisiting, multi-step structure, media, or user says important
99
+
100
+ Then:
101
+
102
+ 1. Body: `raw/YYYY-MM-DD-short-slug.md` (authoritative text/structure/media links)
103
+ 2. Row in **`raw/_index.md`** with `capture_id`
104
+ 3. Set inbox row `raw` column to that file
105
+
106
+ ### Do not
107
+
108
+ - Use deprecated `inbox/capture.md` (removed)
109
+ - Silent-write swipe/atoms/claims
110
+ - Treat capture as a finished run
111
+ - Reply “saved” without **Capture Card**
112
+ - Invent facts not in the user message / source
113
+
114
+ ### entry file (minimal shape)
115
+
116
+ ```markdown
117
+ ---
118
+ id: C-YYYYMMDD-XX
119
+ date: YYYY-MM-DD
120
+ type: structure | link | insight | product | other
121
+ profile: default
122
+ ---
123
+
124
+ # C-… — short title
125
+
126
+ ## One-liner
127
+ ## User intent / source
128
+ ## Key points (our notes)
129
+ ## Links (raw if any)
130
+ ## Status
131
+ ```
132
+
133
+ ### Capture Card (chat)
134
+
135
+ ```text
136
+ ✅ Captured [C-YYYYMMDD-XX]
137
+
138
+ 【One-liner】 …
139
+ 【Type】 …
140
+ 【Profile】 (resolved profile_id)
141
+ 【Key points】
142
+ - …
143
+
144
+ 【Stored】
145
+ - inbox/_index: yes
146
+ - entry: inbox/entries/C-….md
147
+ - raw: no | raw/….md + raw/_index
148
+
149
+ 【Refine】
150
+ A) OK as stored
151
+ B) Refine notes
152
+ ```
153
+
154
+ ### Library offer (required if type is structure or link)
155
+
156
+ ```text
157
+ 【入库?(需点头)】
158
+ 1) 不入库 — index library=none
159
+ 2) 收成 swipe
160
+ 3) 收成 atom (type: hook|reframe|proof|process|cta|other)
161
+ 4) 收成 claim
162
+ 5) 组合 …
163
+
164
+ Reply number(s) or 不入库.
165
+ ```
166
+
167
+ On approval: create catalog files per **§ Library ingest**, set inbox `library` column (e.g. `swipe:S2`).
168
+ Path A (capture): **no** engagement gates on *your* posts.
169
+
170
+ Optional: if user also says「同时选题/打分」, run **§ Topics** for this capture.
171
+
172
+ ---
173
+
174
+ ## § Discovery (watchlist signal analysis)
175
+
176
+ **Role:** Understand external signals faithfully before deciding whether they become our content opportunities. Discovery is not a Topic queue and does not generate publish-ready copy.
177
+
178
+ ### Flow
179
+
180
+ ```text
181
+ watchlist signal → Discovery → Topic (only when explicitly promoted) → Brief
182
+ ```
183
+
184
+ ### Discovery rules
185
+
186
+ 1. Create one `D-YYYYMMDD-XX` record for each meaningful watchlist signal; discard noise before analysis.
187
+ 2. Write `## Source Digest` as a short, natural-language note:
188
+ one sentence, what happened, the author's argument, supporting material, native language, and limits.
189
+ 3. Do not add our audience, product, thesis, ownership, or candidate Topic to a new Discovery.
190
+ 4. Keep source engagement as context only; it is not evidence for our claims.
191
+ 5. When a user explicitly promotes a Discovery, create a Topic and run the Topic opportunity evaluation there.
192
+
193
+ ### Discovery status
194
+
195
+ `unread` → `read` → `saved` | `promoted` | `rejected`
196
+
197
+ ### Discovery Card
198
+
199
+ ```text
200
+ 🔎 Discovery D-YYYYMMDD-XX
201
+ 【Source】 @handle · url · platform
202
+ 【Source digest】 what happened … · author argues … · support …
203
+ 【Limits】 what the source does not establish …
204
+ 【Status】 unread | read | saved | promoted | rejected
205
+ 【Next】 none / revisit / promote to Topic
206
+ ```
207
+
208
+ ### Stop rules
209
+
210
+ - Do not create a Topic merely because a source has high engagement.
211
+ - Do not force every Discovery into the current product wedge.
212
+ - Do not present a source summary as our own claim.
213
+ - Do not generate candidate opportunities inside Discovery.
214
+ - Do not use 5C as the default reading format.
215
+
216
+ ---
217
+
218
+ ## § Topics (Opportunity evaluation)
219
+
220
+ **Role:** Evaluate **Content Opportunities** — whether a topic deserves to exist as content. Does not replace capture or runs.
221
+ **Principle:** Topics are not titles. They are opportunities with a context, audience, conflict, insight, and evidence potential.
222
+
223
+ ### Layout
224
+
225
+ | Path | Role |
226
+ |------|------|
227
+ | `topics/scoring.md` | Scout → opportunity **15/20/25/25/15** (context/audience/conflict/insight/evidence); ready = opp≥**7.5** **and** aud≥**6** |
228
+ | `topics/WATCHLIST.md` | Watchlist router |
229
+ | `topics/watchlists/<profile_id>.md` | Per-profile radar (**scheme B**) |
230
+ | `topics/watchlists/_shared.md` | Shared rows for all profiles (`*`) |
231
+ | `topics/_index.md` | Queue: one row per topic, **`item`** link, **`src_*` engagement** |
232
+ | `topics/items/T-YYYYMMDD-XX.md` | Content Opportunity (structured fields + scores) |
233
+ | `topics/_template-item.md` | Template |
234
+
235
+ ### Defaults
236
+
237
+ - Max **5** new **`produce`** topics per promotion session; watchlist discovery itself has no Topic output cap
238
+ - Scout first: `purpose` = `discard` | `library` | `produce`; `scout_label` = `demand` | `ally` | `foil` | `craft_only`
239
+ - Opportunity dims (produce only): `context` `audience` `conflict` `insight` `evidence` (0–10 + note each) → `opp_score`
240
+ - `ready` requires **opp_score ≥ 7.5 and audience ≥ 6**
241
+ - `src_likes` / `src_replies` / `src_bookmarks` (+ views if known) + `engagement_at` on **index** when source is external
242
+ - Do **not** fold `src_*` into opp_score; among **ready** only, prefer replies/bookmarks over views
243
+
244
+ ### Signal paths
245
+
246
+ | User intent | inbox/raw | topics |
247
+ |-------------|-----------|--------|
248
+ | `记一下` + URL | **Yes** (default) | Only if also asked to score as topic |
249
+ | `按 watchlist 选题` | **Default NO** | Run **§ Discovery** first; create Discovery records, not Topics directly |
250
+ | `扫 inbox 选题` | Existing only | Scout + score ≤5 produce; set `capture_id`; skip captures already linked to a T- |
251
+ | `把 C-xxx 做成选题` | Keep | Create T- with `capture_id` (still Scout + opportunity score) |
252
+ | `链接直接选题` | Default NO unless user agrees capture | Create T- + src engagement |
253
+ | `把 T-xx 收进 inbox` | Yes for that source | Backfill `capture_id` on T- |
254
+
255
+ After a Discovery batch, report **unread / read / saved / promoted / rejected** counts. Topic promotion is explicit and happens after Discovery, not during the scan.
256
+
257
+ ### Create topic steps
258
+
259
+ **Input gate:** A Topic must have an explicit input. Accept a user idea/brief, a URL, a Capture ID, or a Discovery ID named by the user. If the user only says “generate a topic” without naming a source, ask which input to use; do not select from the Discovery index, watchlist, or inbox by preference.
260
+
261
+ 1. Hard filters (`scoring.md`); else skip.
262
+ 2. **Scout gate** — set `purpose` + `scout_label`; if `discard`, do not create T-.
263
+ 3. If `produce` (or user forces score):
264
+ a. Fill **structured opportunity fields**: context (what_changed, why_now), audience (who, current_belief, icp_checks), conflict (old_model, new_reality, belief_gap), insight (potential_insight, why_nonobvious), evidence (available, type, gaps).
265
+ b. Score all 5 opportunity dims (0–10 + note each) → `opp_score`.
266
+ c. Write `topics/items/T-….md`.
267
+ 4. If `library`: optional light item with `status: library`, or capture offer only.
268
+ 5. Add row on `topics/_index.md` (`item` + `src_*` when external).
269
+ 6. Output **Opportunity Card** in chat.
270
+
271
+ **Important:** the structured fields (current_belief, belief_gap, potential_insight) are not decorative — they are the raw material the Brief Engine inherits. If you cannot articulate a belief gap, the conflict score cannot be above 5.
272
+
273
+ ### Opportunity Card
274
+
275
+ ```text
276
+ 📋 Opportunity T-YYYYMMDD-XX
277
+ 【Opportunity】 … (one sentence — what's the content opportunity)
278
+ 【Source】 watchlist @handle (profile … · platform …) | inbox C-… | user | url
279
+ 【Scout】 purpose … · label … · note …
280
+ 【Src engagement】 likes … · replies … · bookmarks … (@ time)
281
+
282
+ 【Context】 what changed (1 line) → score …
283
+ 【Audience】 who + ICP check (1 line) → score …
284
+ 【Conflict】 current belief → reality (1 line) → score …
285
+ 【Insight】 non-obvious perspective (1 line) → score …
286
+ 【Evidence】 available + gaps (1 line) → score …
287
+ 【Opp score】 … · audience floor …
288
+
289
+ 【Profile fit】 pillar … · audience pain …
290
+ 【Verdict】 ready | backlog | parked | library
291
+ 【Weak dims】 (any dim < 6 → name it + suggest: research / reframe / discard)
292
+ 【Suggested selection】 swipe/atoms/claim (none OK)
293
+ 【Next】 开一单 / 改 angle 重打分 / 研究 evidence / 放弃 / 收进 inbox / 入库
294
+ ```
295
+
296
+ ### Topic → run
297
+
298
+ On `用 T-xxx 开一单`: **ask platform(s)** if unset (multi-select OK). Create **one run per platform**, same `topic_id`, prefill selection per platform, mark topic `promoted` + list run ids on `topics/_index` `run` column.
299
+
300
+ ### Watchlist (scheme B — per profile)
301
+
302
+ **Layout:** `topics/WATCHLIST.md` (router) + `topics/watchlists/<profile_id>.md` + `topics/watchlists/_shared.md`.
303
+
304
+ **Resolve profile before scan or add:**
305
+
306
+ 1. User gives **profile_id** → use it.
307
+ 2. User gives **account** → `accounts/_index` → `profile_id`.
308
+ 3. **Neither → ask** (list profiles from `profiles/_index`; do **not** silently default).
309
+
310
+ **Load set:** active rows in `_shared.md` ∪ `watchlists/<profile_id>.md`.
311
+ **Platform filter:** user-specified, else default **`x`**; `全部平台` = all platforms in the set.
312
+ **Always set `platform` on each row.** Same brand on two networks → two rows.
313
+ **Shared across all profiles** → `_shared.md` (ids `#S-01…`). Two profiles only → copy row into both files.
314
+ **Add handle:** write to the chosen profile file (or `_shared` if user says shared / 全 profile).
315
+
316
+ ### Do not
317
+
318
+ - Auto-dump watchlist scans into inbox
319
+ - Skip Scout gate or opportunity scoring on `produce` topics
320
+ - Skip structured opportunity fields (context/audience/conflict/insight/evidence) on `produce`
321
+ - Skip per-dimension notes on `produce`
322
+ - Exceed 5 **produce** topics per scan
323
+ - Mark `ready` on high `src_*` or high opp_score alone (need audience ≥ 6 too)
324
+ - Score conflict > 5 without articulating the `current_belief` and `belief_gap` in writing
325
+ - Scan wrong network for a row (e.g. X tools on a `tiktok` handle)
326
+ - Silently assume profile when user did not name profile or account
327
+
328
+ ---
329
+
330
+ ## § Swipe / atom library
331
+
332
+ - **Catalog:** `swipe/_index.md` / `wiki/atoms/_index.md` are the selection entrypoints (Approach B).
333
+ - **Hit-status:** `trial` | `working` | `dead` (not claim epistemology).
334
+ - **Hits cache:** index columns `n` `win` `loss` from **our** `published.result`.
335
+ - **Use history:** `published/_index.md` (columns `swipe` / `atoms`). **Do not** put an Evidence table in swipe/atom files.
336
+ - **New item:** template + index row; default `trial`, n=win=loss=0.
337
+ - **From capture / run:** only via **§ Library ingest**. Never silent seed from runs.
338
+
339
+ ---
340
+
341
+ ## § Open a run
342
+
343
+ ### When
344
+
345
+ User wants a tracked job: "开一单", "open a run", "按完整流程", or a multi-step post with gates.
346
+
347
+ ### Model (one platform = one Run)
348
+
349
+ - **One folder = one platform draft** (native rewrite).
350
+ - **Same Topic, different platforms** → **N runs**, shared `topic_id` — **not** one folder with many platform sections.
351
+ - `idea.md`: `primary_platform` = this run’s platform; `platforms` = **same single value** (column kept for index compat).
352
+ - `draft.md`: copy for **this platform only**.
353
+ - Image deck: **`pack.md`** (or `pack-<platform>.md`); pixels under **`exports/<slug>/`**.
354
+ - Ship → one `published/_index` row for this `run_id`.
355
+ - Cross-platform progress lives on the **Topic** (`run` column lists all RUN ids), not inside one run.
356
+
357
+ ### Platform gate (required before folder create)
358
+
359
+ 1. User already named platform(s) → use them.
360
+ 2. Else **ask** (multi-select OK). Offer account’s `platforms` from `accounts/_index` (plus others user names).
361
+ 3. **Do not** silently default to `x` when opening a run (default `x` still applies to other flows, e.g. watchlist filter).
362
+ 4. **N platforms selected → N runs** (same topic/goal/pillar; independent selection/draft/ship).
363
+ 5. Slug: include platform when N>1 or always OK — e.g. `2026-08-26-angle-x`, `…-linkedin`.
364
+
365
+ ### Load platform craft (required — every run, every draft stage)
366
+
367
+ For **each** run, before brief / packet / draft / editor / rubric / pack:
368
+
369
+ 1. Resolve craft file via `platforms/README.md` (`x` → `x.md`, `instagram`/`ig` → `ig.md`, …).
370
+ 2. **Read** that file end-to-end. Apply length, hooks, anti-slop, quality checklist.
371
+ 3. Also load profile `audience.md` **core + this platform’s section**.
372
+ 4. Cite the craft path in `brief.md` / `packet.md` (not hardcoded `platforms/x`).
373
+ 5. **Do not** paste another platform’s copy unchanged. Sibling runs each re-read their own craft file.
374
+ 6. If the craft file is missing → ask user / create stub; **do not draft** from memory.
375
+
376
+ ### State machine (do not skip)
377
+
378
+ ```text
379
+ RUN_OPEN → BRIEF_WAIT → DRAFTED → EDIT_WAIT
380
+ → SCORED → SHIP|HOLD → READY_TO_POST
381
+ → PUBLISHED feedback
382
+ ```
383
+
384
+ Also mirror high-level status on **`runs/_index.md`**:
385
+ `open` → `brief` → `draft` → `scored` → `ready` → `published` | `hold` | `killed`.
386
+ (`partial` = **legacy** only; do not use for new multi-platform planning.)
387
+
388
+ Human gates (chat only):
389
+
390
+ 1. **简报 OK** / Brief OK
391
+ 2. **编辑 OK** / Editor OK
392
+ 3. After SHIP: user posts **this** platform, then URL + result → feedback + published row
393
+ 4. If images: **图 OK** after render (when skill available)
394
+
395
+ ### Folder setup
396
+
397
+ 1. Resolve platform list (gate above). For **each** platform:
398
+ 2. **Load** `platforms/<platform>.md` (required; see above).
399
+ 3. Create `runs/YYYY-MM-DD-short-slug[-platform]/` from `runs/_template/`.
400
+ 4. Fill `idea.md` (pillar, **platform**, **selection ids**, optional `topic_id`).
401
+ 5. **Append row** on `runs/_index.md` (`status=open`).
402
+ 6. If from **T-…**: prefill from topic; mark topic `promoted`; append all new run ids to `topics/_index` `run`.
403
+ 7. Never leave production only in chat when in run mode.
404
+
405
+ ### Selection inside a run (required step; **none allowed**)
406
+
407
+ Before brief body, evaluate catalogs (Approach B + **profile + this run’s platform**).
408
+
409
+ Filter every catalog row:
410
+
411
+ - `platforms` includes this run’s platform (or `*`)
412
+ - `profiles` is `*` **or** includes current `profile_id`
413
+ - Swipe/atom: drop `dead`. Claim: drop `retired`.
414
+
415
+ Rank:
416
+
417
+ - Swipe/atom: `working` before `trial`; then higher `win/n` (if n=0, treat as unranked trial).
418
+ - Claim: **do not** rank by `hypothesis` vs `supported`. Rank remaining by `win/n`. `hypothesis` is valid to pick. `weakened` only if the user wants that tension.
419
+
420
+ Then: swipe 0–1 → atoms by role → claim 0–1. **`none` always allowed** with why.
421
+ Sibling runs for the same topic may pick different swipe/atoms (native rewrite).
422
+
423
+ **Write the pick into `idea.md` (this is the citation).** Chat card alone does not count.
424
+
425
+ | Field | Required form |
426
+ |-------|----------------|
427
+ | `swipe_id` | `none` or `S1` (one id) |
428
+ | `atoms` | `none` or `hook:A-… cta:A-…` (real `A-` ids) |
429
+ | `claim_id` | `none` or catalog `C-…` — **not** the `hypothesis:` one-off line |
430
+
431
+ If the user later changes the pick, **overwrite** those three fields. Do **not** bump catalog `n`/`win`/`loss` here — that is ship-only (§ Catalog evidence).
432
+
433
+ **引用** = how many **runs** currently name this id in `idea.md` (killed runs still count unless selection was cleared). The workbench derives it by scanning runs; it is not a column the agent increments. Observatory numbers update on `workbench/build.py`, not at the moment of chat.
434
+
435
+ ### Brief Engine (decision tree → quality gate → stop rule)
436
+
437
+ The brief inherits reasoning from the Topic Opportunity and focuses on **what to say and how**.
438
+
439
+ **Step 1 — Inherit from Topic Opportunity**
440
+
441
+ When the run is from T-xxx, copy the topic's structured fields into `brief.md` `## Inherited`:
442
+ - `context` (from topic's what_changed / why_now)
443
+ - `audience` (from topic's who / current_belief)
444
+ - `conflict` (from topic's belief_gap)
445
+ - `insight` (from topic's potential_insight)
446
+
447
+ Sharpen these for the specific platform/angle — do not re-derive from scratch. If T-xxx has no structured fields (legacy topic), derive them before proceeding.
448
+
449
+ **Step 2 — Walk the decision tree**
450
+
451
+ ```text
452
+ 1. Read inherited context/conflict/insight
453
+ 2. Can you state a thesis in one sentence?
454
+ → No → reframe or stop
455
+ 3. Can you name specific evidence?
456
+ → No evidence at all → stop, suggest research
457
+ → Weak but mechanism is provable → continue
458
+ 4. What's the angle? (How does this conflict become content?)
459
+ 5. What changes for the reader after consuming this?
460
+ → "They'll learn something" is not specific enough
461
+ → Name the belief before and after
462
+ 6. → Fill brief → Self-score → Gate
463
+ ```
464
+
465
+ **Step 3 — Fill brief.md**
466
+
467
+ Fill Claim (core_thesis + supporting_claims), Evidence (sources, type, quality), Communication (angle, format, hook, structure), Outcome (belief, action, emotion), Constraints, Selection.
468
+
469
+ **Step 4 — Brief quality gate (self-scoring)**
470
+
471
+ Score `brief.md` quality gate (6 × 1–5, max 30):
472
+
473
+ | # | Gate | Question |
474
+ |---|------|----------|
475
+ | 1 | Thesis clarity | Can the thesis be one sentence a stranger understands? |
476
+ | 2 | Evidence strength | Is there at least one specific, named source or mechanism? |
477
+ | 3 | Angle originality | Is this angle different from what's already in the conversation? |
478
+ | 4 | Structure coherence | Does the narrative flow from hook → conflict → thesis → proof → outcome? |
479
+ | 5 | Outcome specificity | Can you name the belief change in before/after form? |
480
+ | 6 | Tension preservation | Does the brief still carry the topic's conflict, or did it dilute? |
481
+
482
+ | Result | Action |
483
+ |--------|--------|
484
+ | **≥ 24** (PASS) | Present Brief Card to human for OK |
485
+ | **20–23** (REVISE) | Flag weak dimensions, self-revise once, re-score |
486
+ | **< 20** (STOP) | Trigger stop rule |
487
+
488
+ Update `brief.md` frontmatter: `gate_score`, `gate_pass`.
489
+
490
+ **Step 5 — Stop rule**
491
+
492
+ When the brief cannot pass the quality gate:
493
+
494
+ ```text
495
+ ⚠️ Weak Brief — RUN-…
496
+ 【Failing gates】 gate … score … · gate … score …
497
+ 【Reason】 one line why
498
+ 【Suggestion】 reframe angle / research evidence / kill topic
499
+ Reply: 用 reframe / 研究 / 换 topic / 强制继续
500
+ ```
501
+
502
+ The agent may reject a brief. "强制继续" overrides the gate (user decision). The stop rule is not a hard block — it is structured feedback that makes weakness visible.
503
+
504
+ ### Packet
505
+
506
+ After brief OK: short `packet.md` — pillar slice + voice + audience (**core + this platform**) + **craft notes distilled from the loaded `platforms/<platform>.md`** + **thesis sentence from brief**.
507
+
508
+ ### Writer → Editor
509
+
510
+ - Writer **re-reads** `platforms/<platform>.md`, then fills `draft.md` (**this platform only**) + full text in chat.
511
+ - Editor → `editor.md`; check against that craft file’s quality bar; REVISE until APPROVE or user override.
512
+
513
+ ### Rubric gate
514
+
515
+ Score `rubric.md` (6 × 0–2, max 12).
516
+
517
+ - **≥ 8 → SHIP** — 发布包 in chat; `runs/_index` → `ready`.
518
+ - **< 8 → HOLD**.
519
+ - Carousel/slideshow platforms: after SHIP → **§ PagePack + render**.
520
+ - Pure X text: no multi-page pack.
521
+
522
+ ### Required chat cards (run mode)
523
+
524
+ **Open card** (one card per run; if N>1, list all opened)
525
+
526
+ ```text
527
+ ✅ Run opened RUN-YYYYMMDD-slug
528
+ 【Account】 (from accounts/_index)
529
+ 【Profile】 (resolved profile_id)
530
+ 【Voice】 profile | path
531
+ 【Platform】 x | linkedin | … → craft `platforms/<file>.md` loaded
532
+ 【Topic】 none | T-… (sibling runs: RUN-…, RUN-…)
533
+ 【Goal】 …
534
+ 【Pillar】 P?
535
+ 【Selection】 (profile + platform; drop dead/retired; prefer working; none OK)
536
+ swipe: S? | none — why
537
+ atoms: …
538
+ claim: C? | none — why
539
+ 【Index】 runs/_index updated
540
+ 【State】 open → next: brief
541
+ ```
542
+
543
+ **Brief card** (after quality gate passes)
544
+
545
+ ```text
546
+ 📋 Brief RUN-…
547
+ 【Inherited from T-…】
548
+ Context: …
549
+ Conflict: current belief → reality
550
+ Insight: …
551
+
552
+ 【Thesis】 one sentence
553
+ 【Evidence】 type · quality (weak/moderate/strong)
554
+ 【Angle】 …
555
+ 【Outcome】 belief / action / emotion
556
+ 【Gate】 Thesis … · Evi … · Angle … · Struct … · Outcome … · Tension … = …/30 ✓
557
+ 【Selection】 swipe … · atoms … · claim …
558
+ Reply 简报 OK / 改 …
559
+ ```
560
+
561
+ **Draft / Editor / Rubric** — as before; draft is single-platform and must follow the loaded craft file.
562
+
563
+ **Publish pack** (SHIP)
564
+
565
+ ```text
566
+ 📦 READY_TO_POST RUN-…
567
+ 【Platform】 x | …
568
+ 【Copy】 …
569
+ 【Pack】 n/a | ready | blocked
570
+ 【After post】 URL + win/flat/loss → feedback + published row
571
+ ```
572
+
573
+ ### Feedback
574
+
575
+ `feedback.md` + **§ Published index** on ship.
576
+ Update `runs/_index` → `published`.
577
+ Then **§ Catalog evidence**. Never auto-`supported` / auto-`working`; never silent catalog ingest.
578
+
579
+ ### § Published index (Step 6)
580
+
581
+ On confirmed ship for this run:
582
+
583
+ 1. Row on **`published/_index.md`** (date, **platform**, url, run, pillar, swipe, atoms, claim, result, notes).
584
+ 2. Optional stub.
585
+ 3. `runs/_index` status → `published`.
586
+ 4. **§ Catalog evidence** for every cited swipe / atom / claim id (skip `none`).
587
+ 5. Weekly 复盘: start from **published** `_index`. Same topic’s other platforms = other runs / other published rows.
588
+
589
+ ### § Catalog evidence (required on ship)
590
+
591
+ Hits cache lives on the **index row** (`n` `win` `loss`). This is **not** 引用. 引用 is run selection in `idea.md` (already true once the run exists). `n`/`win`/`loss` only move **on ship**.
592
+
593
+ **Swipe / atom — no Evidence file.**
594
+ The ledger is the **published row** (`swipe` / `atoms` / `result` / `notes`). Why a post flopped stays in `feedback.md` or that published `notes` cell. Do **not** add `## Evidence` to swipe/atom bodies.
595
+
596
+ **Claim — Evidence table in the claim file.**
597
+ That table answers “does the sentence still hold?” (`vs claim`), which is not `result`.
598
+
599
+ For each cited id:
600
+
601
+ 1. `n += 1`
602
+ 2. `win += 1` if `result=win`; `loss += 1` if `result=loss`; `flat`/`unknown` bump `n` only.
603
+ 3. Sync file frontmatter `status` if you change index status (only after user 批准).
604
+ 4. **Claim only:** append the run to the claim file **Evidence** table (`result` + **vs claim**).
605
+
606
+ Propose status change (user must 批准; never auto):
607
+
608
+ | Object | Propose | Gate |
609
+ |--------|---------|------|
610
+ | swipe / atom | `trial` → `working` | `n≥2` AND `win≥2` AND `win>loss` |
611
+ | swipe / atom | → `dead` | user says stop, **or** `n≥3` AND `win=0` AND `loss≥2` |
612
+ | claim | → `supported` | ≥3 runs **support the proposition** (not merely `result=win`) AND user 批准 |
613
+ | claim | → `weakened` / `retired` | user 批准; hits do not flip epistemology |
614
+
615
+ Single win never promotes. n=1 is recorded, not celebrated.
616
+ ---
617
+
618
+ ## § PagePack + render (Step 7)
619
+
620
+ Bridge to **easysociable-slideshow** (when skill exists). Vault owns pack slots; product owns templates + pixels.
621
+
622
+ ### When
623
+
624
+ Planned platform needs multi-page images (linkedin / ig / tiktok), not pure X text.
625
+
626
+ ### Preconditions
627
+
628
+ 1. Run exists; brief OK; editor OK; **rubric SHIP**.
629
+ 2. Per-platform `page_count` / `deck_hint` sensible.
630
+ 3. Prefer swipe with page roles; else explicit roles from brief.
631
+
632
+ ### Agent steps
633
+
634
+ 1. Write **`runs/<slug>/pack.md`** (from `_template/pack.md`; `pack-<platform>.md` OK):
635
+ - this run is already one platform
636
+ - slots, language, pillar, swipe_id, claim_id
637
+ 2. **Pack Card** in chat — user may `改 pack:…`.
638
+ 3. **Render:**
639
+ - If slideshow skill available: render → files under **`exports/<slug>/`** → set pack `job_id`, `export_path`, status `done`; set `runs/_index` `pack`/`export` to yes.
640
+ - If **not** available: status `not_requested` / blocked — **do not fake images**.
641
+ 4. User **图 OK** or revise slots and re-render.
642
+
643
+ ### Pack Card
644
+
645
+ ```text
646
+ 📑 Pack RUN-… platform=linkedin|tiktok|ig
647
+ 【Pages】 N
648
+ 【deck_hint】 …
649
+ 【Outline】 P1… Pn
650
+ 【export_path】 exports/<slug>/
651
+ 【Render】 done | blocked (no skill) | failed
652
+ ```
653
+
654
+ ### Do not
655
+
656
+ - Cram multiple platforms into one run / one pack
657
+ - Pack for pure X text
658
+ - Private template AST — public slots only
659
+ - Invent metrics in slots
660
+ - Skip SHIP before quality pack
661
+ - Store binaries inside `runs/` (use **exports/**)
662
+
663
+ ---
664
+
665
+ ## § Library ingest — swipe / atoms / claims (strict)
666
+
667
+ **Runs never auto-enter catalogs.** Ship → feedback + published + **§ Catalog evidence** if ids already used. New catalog rows still need user approval.
668
+
669
+ ### Two ingest paths
670
+
671
+ | Path | When | Your-post metrics? |
672
+ |------|------|--------------------|
673
+ | **A. Capture offer** | structure/link capture → user picks 入库 options | **No** (source heat is prior, not our n/win) |
674
+ | **B. Post-publish** | In `published/_index` + gates → Agent proposes → user approves | **Yes** |
675
+
676
+ ### Hard bans
677
+
678
+ | Forbidden |
679
+ |-----------|
680
+ | Unpublished run → new swipe/atom/claim |
681
+ | Silent seed while scaffolding |
682
+ | Catalog write without user approval |
683
+ | Single win → claim `supported` or swipe/atom `working` |
684
+ | Mixing claim epistemology with swipe/atom hit-status |
685
+ | `## Evidence` table in a swipe or atom file |
686
+ | Auto-changing `status` when bumping `n`/`win`/`loss` |
687
+
688
+ ### Engagement gates (path B only)
689
+
690
+ At least one of: **replies ≥ 2** | bookmarks ≥ 5 | likes ≥ 10 | user override “这条可收”.
691
+ These gates are for **new ingest**, not for promoting `working` (that uses our `win`/`loss` counts).
692
+
693
+ ### After user approval
694
+
695
+ | Ask | Create |
696
+ |-----|--------|
697
+ | 收成 swipe | `swipe/` + index, `trial`, n=win=loss=0 |
698
+ | 收成 atom | `wiki/atoms/`, set `type`, `trial`, n=win=loss=0 |
699
+ | 收成 claim | `wiki/claims/`, `hypothesis`, n=win=loss=0 |
700
+
701
+ ### § Claims promote
702
+
703
+ - Epistemic Evidence on existing claims when used (separate from hits).
704
+ - `supported` only if ≥3 runs support the **proposition** **and** user 批准.
705
+ - A `win` that does not actually support the sentence does **not** count toward `supported`.
706
+
707
+ ### 复盘 card
708
+
709
+ ```text
710
+ 📈 Review
711
+ 【Run】 …
712
+ 【Result】 …
713
+ 【Hits updated】 swipe S? n/win/loss … · atoms … · claim …
714
+ 【Hit-status propose】 none | S?/A-… trial→working | →dead
715
+ 【Path B ingest】 not eligible | eligible → propose swipe/atom/claim
716
+ 【Claim promote】 none | suggest C-… hypothesis→supported|weakened
717
+ Reply 批准 … / 否决 / 再观察
718
+ ```
719
+
720
+ ### Self-check (Capture)
721
+
722
+ - [ ] `inbox/_index.md` row + `inbox/entries/C-….md`
723
+ - [ ] `raw/` file + `raw/_index.md` only if criteria matched
724
+ - [ ] Capture Card + library offer when structure/link
725
+
726
+ ### Self-check (Topic / Opportunity)
727
+
728
+ - [ ] Structured opportunity fields filled (context/audience/conflict/insight/evidence) on `produce`
729
+ - [ ] `current_belief` and `belief_gap` articulated in writing (not just a score)
730
+ - [ ] `potential_insight` stated — what's non-obvious
731
+ - [ ] 5-dim scores with per-dim notes
732
+ - [ ] `opp_score` weighted correctly; `audience` floor checked
733
+ - [ ] Weak dims (< 6) flagged with suggestion
734
+ - [ ] Opportunity Card output in chat
735
+
736
+ ### Self-check (Run)
737
+
738
+ - [ ] Folder exists under `runs/` with template files filled
739
+ - [ ] `runs/_index.md` row created/updated
740
+ - [ ] Platform gate done; one run per selected platform; `primary_platform` = that platform
741
+ - [ ] `platforms/<platform>.md` **read** before brief/draft/editor; cited in brief + packet
742
+ - [ ] Brief `## Inherited` populated from Topic Opportunity (not re-derived)
743
+ - [ ] Brief decision tree walked; quality gate scored (6 × 1–5)
744
+ - [ ] Brief gate ≥ 24 before presenting to human (or stop rule triggered)
745
+ - [ ] No skip of brief / editor / rubric when user asked for full run
746
+ - [ ] HOLD never presented as final publish pack
747
+ - [ ] Image pack only for this run’s platform; exports under `exports/<slug>/`
748
+ - [ ] Each ship → published row; this run → `published`
749
+ - [ ] `idea.md` has `swipe_id` / `atoms` / `claim_id` matching the Open card (not only chat)
750
+ - [ ] Cited swipe/atom/claim index `n`/`win`/`loss` bumped **on ship only**; status unchanged unless user 批准
751
+ - [ ] Swipe/atom: no Evidence section; claim Evidence table updated if claim used
752
+ - [ ] Selection dropped `dead`/`retired`; did not treat claim `supported` as a hit-rank
753
+ - [ ] Same topic multi-platform = sibling runs sharing `topic_id`, not one multi-section folder
754
+ - [ ] Packet includes thesis sentence from brief
755
+ - [ ] No invented metrics
756
+ - [ ] No cross-platform paste without re-applying that platform’s craft file