@easysociable/cli 0.1.3 → 0.1.5

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 (58) hide show
  1. package/README.md +13 -10
  2. package/content-engine/scripts/starter.mjs +3 -1
  3. package/content-engine/scripts/workbench.mjs +13 -8
  4. package/content-engine/starter/CLAUDE.md +217 -128
  5. package/content-engine/starter/engine.json +3 -0
  6. package/content-engine/starter/needs/README.md +38 -0
  7. package/content-engine/starter/needs/_index.md +31 -0
  8. package/content-engine/starter/needs/_template.md +24 -0
  9. package/content-engine/starter/needs/entries/README.md +5 -0
  10. package/content-engine/starter/products/README.md +38 -0
  11. package/content-engine/starter/products/_index.md +24 -0
  12. package/content-engine/starter/products/_template.md +56 -0
  13. package/content-engine/starter/products/entries/P-001-easysociable.md +150 -0
  14. package/content-engine/starter/profiles/_index.md +6 -3
  15. package/content-engine/starter/profiles/default/README.md +4 -1
  16. package/content-engine/starter/profiles/default/boundaries.md +14 -0
  17. package/content-engine/starter/profiles/default/expression.md +18 -0
  18. package/content-engine/starter/profiles/default/lanes.md +12 -0
  19. package/content-engine/starter/profiles/default/stories.md +15 -0
  20. package/content-engine/starter/profiles/default/voice.md +14 -0
  21. package/content-engine/starter/recommendations/README.md +21 -0
  22. package/content-engine/starter/recommendations/_index.md +21 -0
  23. package/content-engine/starter/recommendations/_template.md +44 -0
  24. package/content-engine/starter/runs/_template/brief.md +1 -1
  25. package/content-engine/starter/runs/_template/pack.md +15 -7
  26. package/content-engine/starter/runs/_template/packet.md +6 -0
  27. package/content-engine/starter/swipe/_template.md +3 -1
  28. package/content-engine/starter/topics/README.md +4 -8
  29. package/content-engine/starter/topics/_index.md +12 -5
  30. package/content-engine/starter/topics/_template-item.md +64 -51
  31. package/content-engine/starter/topics/scoring.md +55 -4
  32. package/content-engine/workbench/build.py +315 -97
  33. package/content-engine/workbench/serve.py +0 -0
  34. package/content-engine/workbench/template.html +172 -94
  35. package/dist/easysociable.js +191 -133
  36. package/dist/easysociable.js.map +1 -1
  37. package/package.json +13 -14
  38. package/skills/ONBOARDING.md +14 -15
  39. package/skills/easysociable-content/SKILL.md +100 -19
  40. package/skills/easysociable-content/workflows/INTERVIEW.md +70 -0
  41. package/skills/easysociable-content/workflows/NEEDS.md +82 -0
  42. package/skills/easysociable-slideshow/SKILL.md +79 -45
  43. package/skills/easysociable-slideshow/references/ARC.md +44 -0
  44. package/skills/easysociable-slideshow/references/CONTENT_PACK.md +34 -6
  45. package/skills/easysociable-slideshow/workflows/CREATE_RUN.md +24 -5
  46. package/skills/easysociable-slideshow/workflows/OUT_OF_SCOPE.md +3 -0
  47. package/skills/easysociable-template/SKILL.md +13 -16
  48. package/skills/easysociable-template/references/CATALOG_METADATA.md +21 -18
  49. package/skills/easysociable-template/scripts/generate-theme-from-hex.mjs +0 -0
  50. package/skills/easysociable-template/workflows/FILL_AND_RENDER.md +4 -83
  51. package/skills/easysociable-template/workflows/OUT_OF_SCOPE.md +3 -2
  52. package/content-engine/starter/discovery/README.md +0 -18
  53. package/content-engine/starter/discovery/_index.md +0 -27
  54. package/content-engine/starter/discovery/_template.md +0 -41
  55. package/content-engine/starter/topics/WATCHLIST.md +0 -65
  56. package/content-engine/starter/topics/watchlists/README.md +0 -10
  57. package/content-engine/starter/topics/watchlists/_shared.md +0 -14
  58. package/content-engine/starter/topics/watchlists/default.md +0 -6
package/README.md CHANGED
@@ -13,22 +13,22 @@ Requires Node.js 20+. Content Engine workbench also needs `python3`.
13
13
  ## Quick start
14
14
 
15
15
  ```bash
16
- # Visual skills + MCP for Codex / Claude Code
16
+ # Default skills + MCP for Codex / Claude Code (template + slideshow)
17
17
  easysociable install --yes
18
-
19
- # Local content engine (markdown + workbench) — no Agent host required
20
- easysociable engine init --root /workspace --yes
21
- easysociable engine serve --engine /workspace/Content Engine --no-open
22
18
  ```
23
19
 
24
- Then tell the Agent to follow `Content Engine/CLAUDE.md`.
25
-
26
- Optional pointer skill for hosts that discover work via SKILL.md:
20
+ Default install does **not** include `easysociable-content`. Add it when you want the Content Engine pointer skill:
27
21
 
28
22
  ```bash
29
23
  easysociable install --skills content,template,slideshow --yes
24
+ easysociable engine init --root /workspace --yes
25
+ easysociable engine serve --engine /workspace/Content Engine --no-open
30
26
  ```
31
27
 
28
+ Then tell the Agent to follow `Content Engine/CLAUDE.md`. The starter includes
29
+ separate `products/` (owned offers) and `recommendations/` (third-party review
30
+ subjects) libraries; neither is a substitute for `needs/`.
31
+
32
32
  Lazy-auth guidance lives in the package at `skills/ONBOARDING.md`.
33
33
 
34
34
  ## Content Engine
@@ -36,12 +36,15 @@ Lazy-auth guidance lives in the package at `skills/ONBOARDING.md`.
36
36
  | Command | Purpose |
37
37
  |---------|---------|
38
38
  | `easysociable engine init [--root] [--yes] [--serve]` | Scaffold starter (never overwrites) |
39
- | `easysociable engine serve [--engine] [--no-open]` | Build + start workbench on `:8765` |
40
- | `easysociable engine build [--engine]` | Rebuild `workbench/index.html` |
39
+ | `easysociable engine serve [--engine] [--no-open]` | Sync workbench runtime from CLI, rebuild, start on `:8765` |
40
+ | `easysociable engine build [--engine]` | Sync workbench runtime from CLI, then rebuild `workbench/index.html` |
41
41
  | `easysociable engine detect` | Find engines under search roots |
42
42
  | `easysociable engine validate --root …` | Starter completeness |
43
43
  | `easysociable engine validate --run …` | Run / pack checks before render |
44
44
 
45
+ The Workbench exposes Products and Recommendations as separate read-only
46
+ indexes. Topics can cite either one without forcing a product connection.
47
+
45
48
  Default `--root` / `--engine` parent is `/workspace` when that directory exists, otherwise the current working directory.
46
49
 
47
50
  ## Authentication
@@ -11,7 +11,9 @@ export const ENGINE_PATHS = {
11
11
  runs: 'runs',
12
12
  exports: 'exports',
13
13
  topics: 'topics',
14
- discovery: 'discovery',
14
+ needs: 'needs',
15
+ products: 'products',
16
+ recommendations: 'recommendations',
15
17
  swipe: 'swipe',
16
18
  profiles: 'profiles',
17
19
  inbox: 'inbox',
@@ -30,21 +30,26 @@ export function portInUse(port = WORKBENCH_PORT) {
30
30
  })
31
31
  }
32
32
 
33
- export function ensureWorkbench(engine) {
33
+ /** `update: false` = init (missing only). `update: true` = build/serve refresh from package. */
34
+ export function ensureWorkbench(engine, { update = false } = {}) {
34
35
  const destDir = join(engine, 'workbench')
35
36
  mkdirSync(destDir, { recursive: true })
36
37
  const copied = []
38
+ const updated = []
37
39
  const skipped = []
38
40
  for (const name of WORKBENCH_FILES) {
39
41
  const dest = join(destDir, name)
40
- if (existsSync(dest)) {
42
+ const from = join(WORKBENCH_SRC, name)
43
+ if (existsSync(dest) && !update) {
41
44
  skipped.push(name)
42
45
  continue
43
46
  }
44
- copyFileSync(join(WORKBENCH_SRC, name), dest)
45
- copied.push(name)
47
+ const existed = existsSync(dest)
48
+ copyFileSync(from, dest)
49
+ if (existed) updated.push(name)
50
+ else copied.push(name)
46
51
  }
47
- return { destDir, copied, skipped }
52
+ return { destDir, copied, updated, skipped }
48
53
  }
49
54
 
50
55
  export function buildWorkbench(engine) {
@@ -68,7 +73,7 @@ export async function startWorkbench(engine, { noOpen = false } = {}) {
68
73
  if (!python) {
69
74
  return { ok: false, code: 'PYTHON3_REQUIRED', message: 'python3 is required for the workbench. Install Python 3 and retry.' }
70
75
  }
71
- ensureWorkbench(engine)
76
+ ensureWorkbench(engine, { update: true })
72
77
  const built = buildWorkbench(engine)
73
78
  if (!built.ok) return built
74
79
  const url = WORKBENCH_URL
@@ -112,9 +117,9 @@ async function main() {
112
117
  if (!options.engine) throw new Error('--engine is required')
113
118
  const engine = resolve(options.engine)
114
119
  if (options.command === 'build') {
115
- ensureWorkbench(engine)
120
+ const workbench = ensureWorkbench(engine, { update: true })
116
121
  const built = buildWorkbench(engine)
117
- output(built, options.json)
122
+ output({ workbench, ...built }, options.json)
118
123
  if (!built.ok) process.exitCode = 2
119
124
  return
120
125
  }
@@ -9,7 +9,6 @@ This folder is the local **Content Engine** for EasySociable.
9
9
 
10
10
  - Folder tree: **English** (`Content Engine/`). Do not ask language.
11
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
12
  - **开一单:** do **not** silent-default platform — **ask**; multi-select → **N runs**
14
13
  - Do not invent metrics, customers, or quotes the user did not provide
15
14
 
@@ -18,10 +17,17 @@ This folder is the local **Content Engine** for EasySociable.
18
17
  1. Read **`accounts/_index.md`** for the handle (default: first active row)
19
18
  2. Get `profile_id`, `voice`, `platforms`, `default_platform`, `overrides`
20
19
  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.
20
+ 4. Voice: if account `voice` is `profile` or empty → **`profiles/<profile_id>/voice.md`** (Identity + **Experience** + **Values** + Persona); else load the explicit path. Never invent bio facts not in Identity / Experience.
21
+ `boundaries.md`: missing → **hard-gate fail**. Write empty starter (`## Not covering` / `## Never claim` headings), then interview; **never invent boundaries**.
22
+ `stories.md` / `expression.md`: missing → `profile: partial`, continue.
23
+ Do not invent bio / stories / values absent from those files. Topics listed in `boundaries.md` → stop; do not write.
22
24
  5. If `overrides` is **non-empty**, read that path (e.g. `accounts/overrides/<handle>.md`) and apply last
23
25
  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
26
  7. Catalog filter: swipe/atoms/claims where `platforms` matches **and** (`profiles` is `*` **or** contains `profile_id`)
27
+ 8. If a Topic or draft mentions an offer, load `products/_index.md` and the
28
+ cited `products/entries/P-*.md`. Never invent product pricing, entitlements,
29
+ proof, or outcomes. Product fit is optional; educational content may have no
30
+ product.
25
31
 
26
32
  ## File map
27
33
 
@@ -31,21 +37,26 @@ This folder is the local **Content Engine** for EasySociable.
31
37
  | `profiles/_index.md` | Profile registry |
32
38
  | `profiles/<id>/pillars.md` | Topic pillars (what to talk about) |
33
39
  | `profiles/<id>/audience.md` | Readers + platform emphasis |
34
- | `profiles/<id>/voice.md` | Identity + tone |
40
+ | `profiles/<id>/voice.md` | Identity + Experience + Values + Persona |
41
+ | `profiles/<id>/stories.md` | Story inventory (cite or none) |
42
+ | `profiles/<id>/boundaries.md` | Creator must-avoid (Not covering / Never claim) |
43
+ | `profiles/<id>/expression.md` | Expression likes / dislikes |
44
+ | `profiles/<id>/lanes.md` | Publishing lanes; distinct from pillars |
35
45
  | `accounts/_index.md` | handle → profile, platforms, voice, overrides |
36
46
  | `accounts/overrides/<handle>.md` | Optional account deltas |
37
47
  | `platforms/*.md` | Platform craft only |
38
48
  | `inbox/_index.md` | Capture registry |
39
49
  | `inbox/entries/C-*.md` | Per-capture working notes |
50
+ | `needs/_index.md` | Audience-quote registry |
51
+ | `needs/entries/N-*.md` | Per-need quote + sightings |
52
+ | `products/_index.md` | Owned product registry and lifecycle |
53
+ | `products/entries/P-*.md` | Owned product facts, boundaries, pricing and claims |
54
+ | `recommendations/_index.md` | Third-party tool/product registry and Profile associations |
55
+ | `recommendations/R-*.md` | Review subject facts, evidence and disclosure |
40
56
  | `raw/_index.md` | Authoritative source registry |
41
57
  | `raw/*.md` | Source bodies |
42
58
  | `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) |
59
+ | `topics/items/T-*.md` | Opportunity card (human half) + Score/Trace appendix |
49
60
  | `topics/scoring.md` | Scout gate + opportunity five-dim (context/audience/conflict/insight/evidence) |
50
61
  | `swipe/_index.md` | Structures (+ `profiles` column) |
51
62
  | `wiki/atoms/_index.md` | Atoms (+ `profiles`) |
@@ -60,22 +71,23 @@ This folder is the local **Content Engine** for EasySociable.
60
71
  | User intent | Action |
61
72
  |-------------|--------|
62
73
  | 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) |
74
+ | 记需求 / 用户原话 / 评论导出 | **§ Needs** only |
75
+ | **扫需求选题** | **§ Topics** from `needs/` where `status=captured`; max 5 `produce` |
66
76
  | 扫 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 |
77
+ | 生成 topic (no source specified) | **§ Topics** — offer captured needs or ask which explicit input to use |
69
78
  | 链接直接选题 / 把 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-) |
79
+ | 采访我 / 完善人设 / interview me / who am I as a creator / 建立 profile | **§ Interview** — persist after each dimension; no slideshow |
80
+ | 开一单 / 用 T-xxx 开一单 | **Hard gate first**, then **§ Open a run** — **ask platform(s)** if unset; multi-select → **N runs** (bind `topic_id` if from T-) |
71
81
  | Quick draft | Resolve profile; selection; draft in chat |
72
82
  | 收成 swipe/atom/claim | **§ Library ingest** after approval |
73
83
  | 选题后同意进 inbox | Capture that URL/C-link then optional library offer |
74
84
  | Post URL after ship | feedback + **§ Published index** |
75
85
  | 复盘 / 批准晋级 | Claims promote + published index |
76
86
  | Update pillars/audience | Edit **`profiles/<id>/…`** |
87
+ | 更新产品 / 产品库 | Edit **`products/`**; keep dates and source of truth |
88
+ | 推荐 / 评测第三方产品 | Edit **`recommendations/`**; record evidence and disclosure |
77
89
  | Update account binding | Edit **`accounts/_index.md`** |
78
- | 出图 | **§ PagePack + render** |
90
+ | 出图 | **Hard gate first**, then **§ PagePack + render**. Fail → list missing files; no handoff unless user says **强制继续** / **force continue** |
79
91
 
80
92
  ---
81
93
 
@@ -171,72 +183,91 @@ Optional: if user also says「同时选题/打分」, run **§ Topics** for this
171
183
 
172
184
  ---
173
185
 
174
- ## § Discovery (watchlist signal analysis)
186
+ ---
187
+
188
+ ## § Products (offer facts)
189
+
190
+ ### When
175
191
 
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.
192
+ User defines, updates, prices, retires, or asks about a product or offer.
177
193
 
178
- ### Flow
194
+ ### Role
195
+
196
+ Products are the **supply-side fact layer**. They are not audience needs,
197
+ external discoveries, or content opportunities.
179
198
 
180
199
  ```text
181
- watchlist signal → Discovery → Topic (only when explicitly promoted) → Brief
200
+ Need (demand evidence) + Profile (identity) + optional Product (offer facts)
201
+ → Topic (editorial judgment)
202
+ → Run
182
203
  ```
183
204
 
184
- ### Discovery rules
205
+ ### Rules
185
206
 
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.
207
+ - Read the cited `products/entries/P-*.md` before using product facts in a
208
+ Topic, brief, draft, CTA, or product answer.
209
+ - A Topic may cite `product_ids: none`; do not force a product connection.
210
+ - A Topic may cite `recommendation_ids: none`; recommendations are third-party
211
+ review subjects, not owned products.
212
+ - `product_fit` describes relevance, not a readiness verdict. Product fit never
213
+ replaces `need_ids`, evidence, audience, or opportunity scoring.
214
+ - Do not turn a product promise into a Need quote. Do not turn a Need quote
215
+ into a product capability.
216
+ - Keep exact pricing and entitlements in the dated product entry. Do not copy
217
+ stale prices into other files.
218
+ - Do not invent proof, customers, outcomes, metrics, or testimonials.
192
219
 
193
- ### Discovery status
220
+ ### Product fit
194
221
 
195
- `unread` → `read` → `saved` | `promoted` | `rejected`
222
+ Use one of:
196
223
 
197
- ### Discovery Card
224
+ | fit | Meaning | Typical CTA |
225
+ |-----|---------|-------------|
226
+ | `direct` | The Topic directly addresses a problem the product solves | `educate` / `invite` / `sell` |
227
+ | `adjacent` | The Topic builds context but is not a product pitch | `educate` / `mention` |
228
+ | `none` | No honest product connection is needed | `none` |
198
229
 
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
- ```
230
+ Product fit is written on the Topic human card and inherited by the Brief.
231
+ For third-party reviews, use `recommendation_ids`, not `product_ids`. Load the
232
+ recommendation entry and disclose affiliate, sponsorship, or other commercial
233
+ relationships before drafting.
207
234
 
208
- ### Stop rules
235
+ ### Topic generation modes
209
236
 
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.
237
+ Use the smallest honest mode:
238
+
239
+ | mode | Required inputs |
240
+ |------|-----------------|
241
+ | `demand` | Needs + Profile |
242
+ | `demand_to_offer` | Needs + Product + Profile |
243
+ | `demand_to_review` | Needs + Recommendation + Profile |
244
+ | `review` | Recommendation + Profile + evidence |
245
+ | `offer_education` | Product + Profile + mechanism |
246
+ | `profile_thesis` | Profile + Pillar |
247
+
248
+ Needs are the default source. Product or Recommendation presence never proves
249
+ audience demand and never raises a Topic to `ready` by itself.
215
250
 
216
251
  ---
217
252
 
218
253
  ## § Topics (Opportunity evaluation)
219
254
 
220
255
  **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.
256
+ **Principle:** Topics are not titles. They are a judgment card the content owner can read in one minute. Five-dim scores are a gate in the appendix, not the reading surface.
222
257
 
223
258
  ### Layout
224
259
 
225
260
  | Path | Role |
226
261
  |------|------|
227
262
  | `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
263
  | `topics/_index.md` | Queue: one row per topic, **`item`** link, **`src_*` engagement** |
232
- | `topics/items/T-YYYYMMDD-XX.md` | Content Opportunity (structured fields + scores) |
264
+ | `topics/items/T-YYYYMMDD-XX.md` | Decision card (summary + judgment) + Score/Trace/Constraints (appendix) |
233
265
  | `topics/_template-item.md` | Template |
234
266
 
235
267
  ### Defaults
236
268
 
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`
269
+ - Scout first: `purpose` = `discard` | `library` | `produce`; `scout_label` = `demand` | `ally` | `foil` | `craft_only`. Needs default `scout_label=demand`.
270
+ - Opportunity dims (produce only): `context` `audience` `conflict` `insight` `evidence` (0–10) → `opp_score`. Reasons live in the readable judgment layer, not in a score-table note column.
240
271
  - `ready` requires **opp_score ≥ 7.5 and audience ≥ 6**
241
272
  - `src_likes` / `src_replies` / `src_bookmarks` (+ views if known) + `engagement_at` on **index** when source is external
242
273
  - Do **not** fold `src_*` into opp_score; among **ready** only, prefer replies/bookmarks over views
@@ -246,85 +277,57 @@ watchlist signal → Discovery → Topic (only when explicitly promoted) → Bri
246
277
  | User intent | inbox/raw | topics |
247
278
  |-------------|-----------|--------|
248
279
  | `记一下` + URL | **Yes** (default) | Only if also asked to score as topic |
249
- | `按 watchlist 选题` | **Default NO** | Run **§ Discovery** first; create Discovery records, not Topics directly |
280
+ | `扫需求选题` | **Default NO** | Scout + score ≤5 produce from `needs/` `status=captured`; `source_type=need`; set `need_ids`; **never** put `N-` in `capture_id`. After a `produce` T- is written, set those `N-` to `promoted` and append the T- on `topic_ids` (index + entry). Discard / library: leave `captured` or `retired` |
250
281
  | `扫 inbox 选题` | Existing only | Scout + score ≤5 produce; set `capture_id`; skip captures already linked to a T- |
251
282
  | `把 C-xxx 做成选题` | Keep | Create T- with `capture_id` (still Scout + opportunity score) |
252
283
  | `链接直接选题` | Default NO unless user agrees capture | Create T- + src engagement |
253
284
  | `把 T-xx 收进 inbox` | Yes for that source | Backfill `capture_id` on T- |
254
285
 
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
286
  ### Create topic steps
258
287
 
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.
288
+ **Input gate:** A Topic must have an explicit input. Accept: user idea/brief, Capture `C-…`, Need `N-…`, Product `P-…`, Recommendation `R-…`, or a profile thesis. If the user only says “generate a topic” without naming a source:
289
+
290
+ 1. If needs exist with `status=captured`, offer **扫需求选题**.
291
+ 2. Otherwise ask which explicit input to use.
260
292
 
261
293
  1. Hard filters (`scoring.md`); else skip.
262
294
  2. **Scout gate** — set `purpose` + `scout_label`; if `discard`, do not create T-.
263
295
  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.
296
+ a. Write the **decision card** first (`topics/_template-item.md` upper half): 结论, 状态原因, 适合谁, 核心判断, 下一步, then the readable judgment sections: 要解决的问题, 受众当前相信什么, 我们准备讲哪一刀, 为什么现在, 看完后的改变, 依据, 当前缺口, 建议形式. Keep the summary short; no enum lists, heat, or weighted arithmetic in the readable layer.
297
+ b. Score all 5 opportunity dims (0–10) → `opp_score`. Put **numbers only** in `## Score`. Do not repeat the card as table notes.
298
+ c. Fill `## Trace` (ids, src heat) and `## Constraints` (writer must-nots). Write `topics/items/T-….md`.
299
+ 4. If `library`: optional light item with `status: library`, or capture offer only. Source needs stay `captured` (or `retired` if the user discards).
300
+ 5. Add row on `topics/_index.md` (`item` + `src_*` when external). `one_liner` = the card title sentence.
301
+ 6. If this T- was `produce` from `N-` ids: update each `needs/_index.md` row + entry to `status=promoted` and `topic_ids` containing this T-. Skip if no T- was written (`discard`).
302
+ 7. Output the **Decision Card** in chat — same shape as the readable layer, not a score walkthrough.
270
303
 
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.
304
+ **Important:** 「他们现在相信什么」must name the false belief. If you cannot, conflict cannot score above 5. Brief inherits the readable judgment layer, not the Score table.
272
305
 
273
306
  ### Opportunity Card
274
307
 
308
+ Paste the decision summary and readable judgment sections. Do not walk five scores in the main body.
309
+
275
310
  ```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 / 入库
311
+ 📋 选题判断卡 T-YYYYMMDD-XX
312
+ 【结论】 ready | backlog | parked
313
+ 【状态原因】 …
314
+ 【适合谁】 …
315
+ 【核心判断】 …
316
+ 【下一步】 开始生成 / 补需求 / 补证据 / 修改角度 / 放弃
317
+ 【要解决的问题】 …
318
+ 【他们现在相信什么】 以为 X → 其实 Y
319
+ 【我们准备讲哪一刀】 …
320
+ 【为什么现在值得讲】 …
321
+ 【看完后的改变】 之前 … → 之后 …
322
+ 【依据】 …
323
+ 【当前缺口】 …
324
+ 【建议形式】 …
294
325
  ```
295
326
 
296
327
  ### Topic → run
297
328
 
298
329
  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
330
 
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
331
  ---
329
332
 
330
333
  ## § Swipe / atom library
@@ -338,12 +341,91 @@ On `用 T-xxx 开一单`: **ask platform(s)** if unset (multi-select OK). Create
338
341
 
339
342
  ---
340
343
 
344
+ ## § Interview
345
+
346
+ Triggers: 采访我 / 完善人设 / interview me / who am I as a creator / 建立 profile.
347
+
348
+ Follow `easysociable-content/workflows/INTERVIEW.md` for questions. Persist after each dimension; do not wait until the end.
349
+
350
+ - Append `## Experience` / `## Values` to existing `voice.md` if those headings are missing. Do not rewrite filled Identity rows.
351
+ - Missing `boundaries.md` → write empty starter, then interview. **Never invent boundaries.**
352
+ - Do not scan the watchlist. Do not create a slideshow. Do not ingest needs in this section.
353
+
354
+ ### Hard gate (refuse 出图 / PagePack / `create_run`)
355
+
356
+ Identity **only** Role / What I build / Point of view — no `(fill)`, non-empty. Public handle MAY stay `(fill)`. `boundaries.md` exists with Not covering **and** Never claim non-empty. If `needs/` exists, also `needs/_index.md` ≥ 3 `N-` rows; if `needs/` is absent, skip that count.
357
+
358
+ Fail → list missing files; stay here. User must say **强制继续** / **force continue**. Optional check:
359
+
360
+ ```bash
361
+ easysociable engine validate --root . --require-knowledge
362
+ ```
363
+
364
+ ### Completion
365
+
366
+ Hard gate vs first-session (`profile: partial`) vs complete seven dimensions (`profile: complete`) — same table as INTERVIEW.md. Experience / Values / expression are not the hard gate. Needs hard gate is ≥ **3** original quotes; first-session **target** is **5**.
367
+
368
+ ---
369
+
370
+ ## § Needs (audience quotes)
371
+
372
+ Triggers: 记需求 / 用户原话 / 评论导出 / capture audience quotes / user needs.
373
+ 扫需求选题 → **§ Topics** (this section only writes `N-` records).
374
+
375
+ Follow `easysociable-content/workflows/NEEDS.md` when this engine's copy of the workflow is the newer one; this section wins if both exist.
376
+
377
+ ### Layout
378
+
379
+ | Path | Role |
380
+ |------|------|
381
+ | `needs/_index.md` | Quote registry |
382
+ | `needs/entries/N-YYYYMMDD-XX.md` | Verbatim quote + sightings + optional gloss |
383
+ | `needs/_template.md` | Entry shape |
384
+
385
+ ### Status machine (only these words)
386
+
387
+ ```text
388
+ captured → just written, or still captured after a synonym merge (frequency ≥ 1)
389
+ promoted → and only when a T- exists whose need_ids contain this N-; topic_ids lists that T-
390
+ retired → user discarded
391
+ ```
392
+
393
+ - Never `clustered`. Merge is not a status.
394
+ - Never mark `promoted` without that `T-`. `topic_ids` non-empty ≡ `promoted`.
395
+ - After § Topics writes a `produce` T- from this `N-`, flip this row + entry to `promoted` and append the T- on `topic_ids`. Discard / library stay `captured` or `retired`.
396
+ - Scan input: `status=captured` (`topic_ids` empty). Max 5 `produce`.
397
+ - Quote cell = verbatim. Gloss is optional and separate.
398
+ - Do not invent quotes. Do not treat watchlist hooks as needs.
399
+ - `N-` never goes in topic `capture_id`.
400
+
401
+ ### Ingest
402
+
403
+ 1. New entry from `_template.md` + row at top of `_index.md`.
404
+ 2. Chat card:
405
+
406
+ ```text
407
+ ✅ Need N-YYYYMMDD-XX
408
+ 【Quote】 …
409
+ 【Source】 comment | dm | consult | faq | student | other
410
+ 【Frequency】 1
411
+ 【Stored】 needs/_index + needs/entries/N-….md
412
+ 【Merge】 new | merged into N-… (frequency now k, status still captured)
413
+ ```
414
+
415
+ 3. Synonym merge: propose → user yes → same `N-` stays `captured`, `frequency += 1`, append sighting. Retire a mistaken duplicate.
416
+
417
+ Hard gate: `needs/_index.md` ≥ **3** `N-` rows once `needs/` exists (empty index fails). Target **5**. Prefer scanning needs before inbox.
418
+
419
+ ---
420
+
341
421
  ## § Open a run
342
422
 
343
423
  ### When
344
424
 
345
425
  User wants a tracked job: "开一单", "open a run", "按完整流程", or a multi-step post with gates.
346
426
 
427
+ **Hard gate first** (same as § Interview). Fail → list missing files; do not create `runs/<slug>/` unless **强制继续** / **force continue**.
428
+
347
429
  ### Model (one platform = one Run)
348
430
 
349
431
  - **One folder = one platform draft** (native rewrite).
@@ -438,13 +520,15 @@ The brief inherits reasoning from the Topic Opportunity and focuses on **what to
438
520
 
439
521
  **Step 1 — Inherit from Topic Opportunity**
440
522
 
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)
523
+ When the run is from T-xxx, copy the **readable judgment layer** into `brief.md` `## Inherited`:
524
+ - `context` ← 「为什么现在值得讲」
525
+ - `audience` ← 「适合谁」
526
+ - `conflict` ← 「他们现在相信什么」
527
+ - `insight` ← 「我们准备讲哪一刀」
528
+ - `outcome` ← 「看完后的改变」
529
+ - `evidence` ← 「凭什么这么判断」 + 「当前缺口」
446
530
 
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.
531
+ Sharpen these for the specific platform/angle — do not re-derive from scratch. If T-xxx is a legacy five-section score sheet, derive the readable judgment layer before proceeding. Do not inherit the Score table as prose.
448
532
 
449
533
  **Step 2 — Walk the decision tree**
450
534
 
@@ -625,25 +709,29 @@ Planned platform needs multi-page images (linkedin / ig / tiktok), not pure X te
625
709
 
626
710
  ### Preconditions
627
711
 
712
+ 0. **Hard gate** (same as § Interview). Fail → list missing files; no slideshow/template handoff unless **强制继续** / **force continue**.
628
713
  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.
714
+ 2. Per-platform `page_count` / `deck_hint` follows the **arc**, not a five-family catalog.
715
+ 3. Prefer a swipe whose beats are an argument; else name a one-line arc from the brief.
716
+ 4. **Forbidden default:** Cover → Point → Steps → Recap → CTA (those are P0 family names, not a pack recipe).
631
717
 
632
718
  ### Agent steps
633
719
 
634
720
  1. Write **`runs/<slug>/pack.md`** (from `_template/pack.md`; `pack-<platform>.md` OK):
635
721
  - this run is already one platform
722
+ - **Arc** one-liner + recipe **before** Pages
636
723
  - slots, language, pillar, swipe_id, claim_id
724
+ - page 1 `role` is not automatically `cover`
637
725
  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.
726
+ 3. Hand off to **easysociable-slideshow**: it picks templates by platform + canvas + slot shape, then opens Studio. Topology lock means a wrong arc needs a **new Run**, not slot rewrite.
727
+ 4. **Render / Download:** pixels leave the product from Studio Download (not CLI render). After the user downloads, copy files under **`exports/<slug>/`** if needed; set pack `job_id`, `export_path`, status `done`; set `runs/_index` `pack`/`export` to yes. If slideshow skill is **not** available: status `not_requested` / blocked — **do not fake images**.
728
+ 5. User **图 OK** or revise slots (same topology) / new Run (different arc).
642
729
 
643
730
  ### Pack Card
644
731
 
645
732
  ```text
646
733
  📑 Pack RUN-… platform=linkedin|tiktok|ig
734
+ 【Arc】 one-liner / recipe
647
735
  【Pages】 N
648
736
  【deck_hint】 …
649
737
  【Outline】 P1… Pn
@@ -659,6 +747,8 @@ Planned platform needs multi-page images (linkedin / ig / tiktok), not pure X te
659
747
  - Invent metrics in slots
660
748
  - Skip SHIP before quality pack
661
749
  - Store binaries inside `runs/` (use **exports/**)
750
+ - Default Cover → Point → Steps → Recap → CTA
751
+ - Skip the Arc line and fill P0 family names as a tutorial
662
752
 
663
753
  ---
664
754
 
@@ -725,13 +815,12 @@ Reply 批准 … / 否决 / 再观察
725
815
 
726
816
  ### Self-check (Topic / Opportunity)
727
817
 
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
818
+ - [ ] Decision card filled (结论 / 状态原因 / 适合谁 / 核心判断 / 下一步)
819
+ - [ ] Readable judgment filled (问题 / 当前相信什么 / 我们的角度 / 为什么现在 / 看完后的改变 / 依据 / 缺口 / 形式)
820
+ - [ ] False belief named in 「他们现在相信什么」 (not just a conflict score)
821
+ - [ ] Readable sections are concise; heat and arithmetic only under `## 评分与追踪附录`
822
+ - [ ] Score table is numbers only; opp + aud + verdict line present
823
+ - [ ] Opportunity Card in chat matches the human half (not a five-score walkthrough)
735
824
 
736
825
  ### Self-check (Run)
737
826