From 789d715d7d3c069496adc56aa880e61839546866 Mon Sep 17 00:00:00 2001 From: wujinnuhuo Date: Mon, 3 Aug 2026 12:55:01 +0800 Subject: [PATCH 01/24] feat: focus dashboard on collection workflow --- apps/web/templates/dashboard.html | 74 +++++++++++++++------------- tests/web/test_missions_workbench.py | 18 ++++--- 2 files changed, 51 insertions(+), 41 deletions(-) diff --git a/apps/web/templates/dashboard.html b/apps/web/templates/dashboard.html index 6e73736..92417b4 100644 --- a/apps/web/templates/dashboard.html +++ b/apps/web/templates/dashboard.html @@ -1,51 +1,57 @@ {% extends "base.html" %} -{% block title %}Dashboard{% endblock %} +{% block title %}采集工作台{% endblock %} {% block content %} -

Personal evidence operations

-

Reality Workbench

-

先处理现实观察、最强异议和最小下一步;来源、条目和运行数量只在需要排障时展开,不能替代需求判断。

+

需求挖掘 · 最小可用闭环

+

采集工作台

+

从定义信源开始,采集公开内容,保留原文上下文,再把值得研究的内容送入需求分析。这里不替你联系任何人,也不把评论当作已被验证的需求。

-
-
Today: reality-facing work
-

Loading the current evidence, objection, and smallest next action…

-
+
+
+
1. 定义信源
+

添加一个你要持续关注的平台、社区或内容入口,并配置采集频率与边界。

+ Define a source +
+
+
2. 配置并执行采集
+

为选定信源固定采集配置,先预览,再由你决定是否执行有限范围的公开采集。

+ Open collection workbench +
+
+
3. 审阅证据候选
+

只有带原文与上下文的内容才能进入需求判断;接受、忽略或标记待查都保留理由。

+ Review collected evidence +
+
-

Start from reality

-
- Record an observation - Plan a bounded mission - Review sources and configuration -
+
+
最近采集到的证据候选
+

Loading…

+
-
What this workbench does not claim
-

A collected comment, an Agent proposal, an ontology relationship, or a finished Feature is not proof of demand, payment, retention, or profit. Each must remain traceable to observations, counterevidence, and a next reality action.

+
这一步的完成标准
+

你能独立完成一次:创建信源 → 固定配置 → 预览或执行采集 → 查看原始上下文 → 做出证据处置。需求假设与产品定义在此之后再进入下一步。

{% endblock %} diff --git a/tests/web/test_missions_workbench.py b/tests/web/test_missions_workbench.py index afa0bda..3f61c63 100644 --- a/tests/web/test_missions_workbench.py +++ b/tests/web/test_missions_workbench.py @@ -42,17 +42,21 @@ async def test_missions_workbench_exposes_an_explicit_bounded_mission_flow(clien assert "Lease" in page -async def test_dashboard_prioritizes_reality_action_and_objection_over_volume_metrics(client): +async def test_dashboard_prioritizes_collection_workbench_over_validation_tasks(client): response = await client.get("/") assert response.status_code == 200 page = response.text - assert 'id="today-workspace"' in page - assert "/api/today" in page - assert "Strongest objection" in page - assert "Next reality action" in page - assert "Record an observation" in page - assert "Plan a bounded mission" in page + assert 'id="collection-workbench"' in page + assert "Open collection workbench" in page + assert "Define a source" in page + assert "Review collected evidence" in page + assert "/missions" in page + assert "/sources/create" in page + assert "/api/evidence-inbox" in page + assert "/api/today" not in page + assert "Strongest objection" not in page + assert "Next reality action" not in page assert "Success Rate (24h)" not in page assert "Items Collected" not in page From 4e4d8729ee351538d4c78d42a213d8b170d5e126 Mon Sep 17 00:00:00 2001 From: wujinnuhuo Date: Mon, 3 Aug 2026 13:42:46 +0800 Subject: [PATCH 02/24] feat: build collection control room interface --- apps/web/static/style.css | 72 ++++++---------- apps/web/templates/base.html | 59 +++++++------- apps/web/templates/missions/workbench.html | 95 ++++++++++++---------- tests/web/test_missions_workbench.py | 5 ++ 4 files changed, 109 insertions(+), 122 deletions(-) diff --git a/apps/web/static/style.css b/apps/web/static/style.css index 1ef0e0c..b364082 100644 --- a/apps/web/static/style.css +++ b/apps/web/static/style.css @@ -1,49 +1,23 @@ -/* SourceOS — minimal custom styles on top of Pico.css */ - -.status-active { color: var(--pico-color-green-400); font-weight: bold; } -.status-paused { color: var(--pico-color-amber-400); } -.status-error { color: var(--pico-color-red-400); font-weight: bold; } -.status-discovered { color: var(--pico-color-blue-400); } -.status-fetched { color: var(--pico-color-green-400); } -.status-failed { color: var(--pico-color-red-400); } -.status-success { color: var(--pico-color-green-400); } -.status-queued { color: var(--pico-color-grey-400); } -.status-running { color: var(--pico-color-amber-400); } - -.badge { - display: inline-block; - padding: 0.1rem 0.5rem; - background: var(--pico-color-grey-100); - border-radius: 4px; - font-size: 0.8rem; - font-weight: 500; -} - -.error-code { - font-family: monospace; - font-size: 0.85rem; - background: var(--pico-color-red-50); - padding: 0.1rem 0.4rem; - border-radius: 3px; -} - -.markdown-body { - max-width: 800px; - line-height: 1.7; -} -.markdown-body img { max-width: 100%; } -.markdown-body pre { overflow-x: auto; } - -.workbench-heading, .section-heading { display: flex; align-items: start; justify-content: space-between; gap: 1rem; } -.workbench-heading > div, .section-heading > div { max-width: 48rem; } -.workbench-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr)); gap: 1rem; } -.eyebrow { margin-bottom: .25rem; color: var(--pico-primary); font-size: .75rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; } -.config-status { margin: 1rem 0 0; font-size: .9rem; } -.execution-warning { align-self: end; padding: .85rem 1rem; border-left: 3px solid var(--pico-color-amber-400); background: var(--pico-card-background-color); } -.message-error { border-left: 4px solid var(--pico-color-red-400); } -.message-success { border-left: 4px solid var(--pico-color-green-400); } -.run-metrics { display: flex; flex-wrap: wrap; gap: .75rem 1.25rem; margin: 1rem 0; } -.run-metrics span { font-size: .9rem; } -.failure-detail { color: var(--pico-color-red-400); } -.evidence-list { display: grid; gap: .75rem; } -@media (max-width: 600px) { .workbench-heading, .section-heading { flex-direction: column; } } +/* SourceOS control room — operator-facing collection workspace */ +:root { --ink: #20251f; --ink-2: #313830; --canvas: #d9d7cf; --paper: #f5f4ef; --panel: #fffefa; --line: #d8d6ce; --muted: #777b73; --green: #215442; --green-soft: #dceae1; --amber: #c49439; --red: #a94432; --red-soft: #f6e5dd; --shadow: 0 12px 24px rgba(30, 37, 31, .06); --pico-font-family: "Avenir Next", "PingFang SC", "Noto Sans SC", sans-serif; --pico-border-radius: 14px; --pico-primary: var(--green); --pico-primary-background: var(--green); --pico-primary-border: var(--green); --pico-form-element-border-color: var(--line); --pico-form-element-background-color: #fffefa; --pico-card-background-color: var(--panel); } +* { box-sizing: border-box; } +body { margin: 0; background: var(--canvas); color: var(--ink); font-size: 14px; } +.app-shell { display: grid; grid-template-columns: 308px minmax(0, 1fr); min-height: 100vh; max-width: 1848px; margin: 40px auto; overflow: hidden; border-radius: 22px; background: var(--paper); box-shadow: 0 26px 60px rgba(30, 37, 31, .16); } +.app-sidebar { display: flex; flex-direction: column; padding: 22px 18px 25px; background: var(--ink); color: #edf0ea; } +.brand-lockup { display: flex; align-items: center; gap: 12px; padding: 0 10px 27px; color: #f8f8f4; text-decoration: none; font-size: 20px; } +.brand-lockup small { margin-left: 3px; color: #9da49b; font-size: 12px; font-weight: 500; } +.brand-mark { display: grid; width: 40px; height: 40px; place-items: center; border: 2px solid #798179; border-radius: 11px; font-size: 13px; font-weight: 800; letter-spacing: -1px; } +.primary-nav, .secondary-nav { display: grid; gap: 4px; } +.app-sidebar nav a { display: flex; align-items: center; gap: 14px; min-height: 52px; padding: 0 13px; border-radius: 10px; color: #c4c9c0; font-size: 16px; font-weight: 650; text-decoration: none; } +.app-sidebar nav a span { color: #9da49b; font-size: 24px; line-height: 1; }.app-sidebar nav a b { min-width: 24px; margin-left: auto; padding: 2px 7px; border-radius: 999px; background: #394139; color: #d8ddd6; font-size: 12px; text-align: center; } +.app-sidebar nav a.active, .app-sidebar nav a:hover { background: #f2f5ef; color: #213128; }.app-sidebar nav a.active span { color: var(--green); }.app-sidebar nav a.active b { background: #d5e7dc; color: var(--green); } +.nav-label { margin: 26px 13px 9px; color: #7f877e; font-size: 12px; }.weekly-budget { margin: auto 10px 0; padding-top: 22px; border-top: 1px solid #3a4039; color: #eef0ec; }.weekly-budget strong { font-size: 12px; }.weekly-budget small { color: #9da49b; }.budget-track { height: 6px; margin: 12px 0 8px; overflow: hidden; border-radius: 999px; background: #4b544a; }.budget-track i { display: block; width: 41%; height: 100%; border-radius: inherit; background: #8fab97; } +.app-frame { min-width: 0; }.topbar { display: flex; align-items: center; justify-content: space-between; min-height: 80px; padding: 0 28px; border-bottom: 1px solid var(--line); background: #fbfaf6; }.topbar p { margin: 0; color: var(--muted); font-size: 16px; }.topbar strong { color: var(--ink); }.topbar-actions { display: flex; align-items: center; gap: 20px; color: var(--muted); }.command-search { display: flex; align-items: center; width: 362px; margin: 0; padding: 0 14px; border: 1px solid var(--line); border-radius: 12px; background: #fffefa; }.command-search span { margin-right: 10px; color: #92978f; white-space: nowrap; }.command-search input { height: 48px; margin: 0; padding: 0; border: 0; box-shadow: none; background: transparent; font-size: 14px; }.command-search input:focus { box-shadow: none; }.operator-avatar { display: grid; width: 40px; height: 40px; place-items: center; border-radius: 50%; background: var(--green); color: #fff; } +.app-content { padding: 23px 28px 32px; }.collection-heading { display: flex; align-items: center; justify-content: space-between; gap: 20px; margin-bottom: 22px; }.collection-heading h1 { margin: 0 0 5px; font-size: 31px; letter-spacing: -.04em; }.collection-heading p:not(.eyebrow) { margin: 0; color: var(--muted); font-size: 16px; }.eyebrow { margin: 0 0 5px; color: var(--green); font-size: 12px; font-weight: 800; letter-spacing: .04em; }.collection-heading a[role=button] { margin: 0; } +article, .collection-panel > section { margin: 0; border: 1px solid var(--line); border-radius: 16px; background: var(--panel); box-shadow: var(--shadow); }article > header { display: flex; justify-content: space-between; align-items: center; min-height: 66px; padding: 0 18px; border-bottom: 1px solid var(--line); font-size: 17px; }article > header small { color: var(--muted); font-size: 13px; font-weight: 500; }.cold-water-notice { display: grid; grid-template-columns: auto 1fr auto; gap: 18px; align-items: center; margin-bottom: 20px; padding: 15px 20px; border-color: #e3b9aa; background: var(--red-soft); box-shadow: none; color: #4c423e; }.cold-water-notice strong { color: var(--red); }.cold-water-notice a { color: var(--red); font-weight: 750; text-decoration: none; white-space: nowrap; } +#collection-layout { display: grid; grid-template-columns: 280px minmax(430px, 1fr) 320px; gap: 18px; align-items: start; }.collection-panel { display: grid; gap: 18px; min-width: 0; }.collection-panel article { padding-bottom: 18px; }.collection-panel article > :not(header) { margin-right: 18px; margin-left: 18px; }.collection-panel h2 { margin-top: 17px; font-size: 19px; }.collection-panel p { line-height: 1.6; }.source-library-link { width: calc(100% - 36px); margin-top: 20px !important; } +label { color: #62675f; font-size: 13px; font-weight: 700; } input, textarea, select { margin-top: 7px; margin-bottom: 14px; border-color: var(--line); border-radius: 10px; box-shadow: none; font-size: 15px; } textarea { line-height: 1.48; } button, [role=button] { min-height: 42px; border-radius: 10px; font-size: 14px; font-weight: 750; } button:active, [role=button]:active { transform: translateY(1px); } +.app-content button:not(.secondary):not(.outline), .app-content [role=button]:not(.secondary):not(.outline) { background: var(--green); border-color: var(--green); color: #fff; } .app-content button.secondary, .app-content [role=button].secondary { background: #e9ece7; border-color: var(--line); color: #334038; } .app-content button.outline, .app-content [role=button].outline, .app-content button.secondary.outline, .app-content [role=button].secondary.outline { background: transparent; border-color: #bfc5ba; color: #334038; } +#mission-editor .grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }#mission-editor .grid:has(#request-limit) { grid-template-columns: repeat(3, minmax(0, 1fr)); }#mission-editor .grid:has(#time-budget) { grid-template-columns: repeat(3, minmax(0, 1fr)); }.config-status { padding: 11px 13px; border-radius: 9px; background: #eff2ed; color: #526256; font-size: 13px; }.execution-warning { padding: 13px; border-left: 3px solid var(--amber); border-radius: 8px; background: #f7f1df; }.triage-actions { display: flex; flex-wrap: wrap; gap: 8px; }.run-metrics { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; }.run-metrics span { padding: 9px; border-radius: 8px; background: #f1f0eb; color: var(--muted); font-size: 12px; }.run-metrics strong { display: block; color: var(--ink); font-size: 15px; }.failure-detail { color: var(--red); }.message-error { margin-bottom: 14px; padding: 10px 14px; border-left: 4px solid var(--red); }.message-success { margin-bottom: 14px; padding: 10px 14px; border-left: 4px solid var(--green); }.evidence-list { display: grid; gap: 8px; }.evidence-list article { box-shadow: none; }.evidence-list article > header { min-height: 46px; font-size: 14px; }.rail-history, .rail-inbox { border: 0 !important; background: transparent !important; box-shadow: none !important; }.rail-history > .section-heading, .rail-inbox > .section-heading { padding: 0 0 10px; }.rail-history > p, .rail-inbox > p { color: var(--muted); font-size: 12px; }.rail-history .evidence-list article, .rail-inbox .evidence-list article { padding-bottom: 12px; }.badge { display: inline-block; padding: 3px 7px; border-radius: 999px; background: var(--green-soft); color: var(--green); font-size: 11px; font-weight: 800; }.error-code { font-family: monospace; background: var(--red-soft); padding: 2px 5px; border-radius: 3px; }.markdown-body { max-width: 800px; line-height: 1.7; }.markdown-body img { max-width: 100%; }.markdown-body pre { overflow-x: auto; } +@media (max-width: 1280px) { .app-shell { margin: 0; max-width: none; border-radius: 0; }.app-sidebar { width: 248px; }.app-shell { grid-template-columns: 248px minmax(0, 1fr); } #collection-layout { grid-template-columns: 230px minmax(360px, 1fr); }.operation-rail { grid-column: 1 / -1; grid-template-columns: repeat(3, 1fr); display: grid; }.command-search { width: 260px; } } +@media (max-width: 820px) { .app-shell { display: block; }.app-sidebar { display: none; }.topbar { padding: 0 16px; }.topbar-actions small, .command-search { display: none; }.app-content { padding: 20px 16px; }.collection-heading { align-items: flex-start; flex-direction: column; }.cold-water-notice { grid-template-columns: 1fr; gap: 7px; }.cold-water-notice a { white-space: normal; } #collection-layout, .operation-rail { display: grid; grid-template-columns: 1fr; }.collection-panel { gap: 14px; } #mission-editor .grid, #mission-editor .grid:has(#request-limit), #mission-editor .grid:has(#time-budget) { grid-template-columns: 1fr; } } diff --git a/apps/web/templates/base.html b/apps/web/templates/base.html index 7218493..7eb2a43 100644 --- a/apps/web/templates/base.html +++ b/apps/web/templates/base.html @@ -8,36 +8,33 @@ -
- -
- -
- {% block content %}{% endblock %} -
- -
- SourceOS v0.1.0 — 证据驱动的现实需求发现与产品验证工作台 -
+
+ +
+
+

{% block breadcrumb %}现实采集 / 采集控制台{% endblock %}

+
本周剩余 4h 20m
+
+
{% block content %}{% endblock %}
+
+
diff --git a/apps/web/templates/missions/workbench.html b/apps/web/templates/missions/workbench.html index 4b88a82..d133414 100644 --- a/apps/web/templates/missions/workbench.html +++ b/apps/web/templates/missions/workbench.html @@ -2,86 +2,93 @@ {% block title %}Mission Workbench{% endblock %} {% block content %} -
+
-

Evidence acquisition

-

Mission Workbench

-

把一个现实问题约束成可复核的采集任务。这里产出的是证据候选,不是已经成立的需求。

+

现实采集 / 采集控制台

+

现实采集工作台

+

管理“为什么采、从哪里采、采到什么程度”,而不是启动一个黑箱爬虫。

- Create source + 导入材料
-
+
冷水提醒采集量不是证据强度。重复的同源评论不会自动变成独立需求。检查信源组合 →
+ +
+ +
-
2. Pin a collection configuration
-

A mission always records an immutable configuration version. Adjusting this form creates a new version; it never rewrites an old run.

+
任务定义草稿 · 自动保存
+

固定采集配置

+

每次采集都固定一版配置。修改会创建新版本,不会改写既有运行记录。

-
-
-
+ -
-
3. Define a bounded reality question
+
+
本次采集要回答的问题
-
-
+