summaryrefslogtreecommitdiff
path: root/docs/backend-repair-contract.md
diff options
context:
space:
mode:
authorSomhairle H. Marisol <[email protected]>2026-09-18 08:27:41 +0800
committerSomhairle H. Marisol <[email protected]>2026-09-18 08:27:41 +0800
commit088735b948d46896b8af30efcb0a2dc5d362b97f (patch)
tree0dcab0d5ebc65309267a42d7cda827e9fdd866e7 /docs/backend-repair-contract.md
parentbf6681eb29ac8b0c80ca17b2b5869f7de3da1198 (diff)
downloadstrategy-lab-088735b948d46896b8af30efcb0a2dc5d362b97f.tar.gz
docs(release): 全周期交接文档入库(含 ui-shadcn 迁移交付说明)
[变更性质] 纯文档提交,无运行时逻辑。 [文档内容] 补齐此前各轮未入库的交接/验收文档:backend-auth/backend-domain/ domain-authorization-user(认证与授权域)、etf-recovery-release- handoff(ETF 修复 + ops 演练定稿与生产部署命令)、recovery-* 系列、 frontend/parent-ui-findings(UI 迁移上下文)、worker/integration 等, 以及本轮 docs/ui-shadcn-handoff.md(shadcn-svelte 迁移交接,含 Chart.svelte 契约、runes $state 踩坑记录与 375/768/1440 验证证据)。 [更新方案] 按主题分文;每份文档只记录可复现的命令、验证结果与语义边界, 不导出密钥或生产敏感路径。 [影响范围] 文档渠道:后续 leader/client 审阅入口;与代码提交一一对应便于回溯。
Diffstat (limited to 'docs/backend-repair-contract.md')
-rw-r--r--docs/backend-repair-contract.md34
1 files changed, 34 insertions, 0 deletions
diff --git a/docs/backend-repair-contract.md b/docs/backend-repair-contract.md
new file mode 100644
index 0000000..2b02703
--- /dev/null
+++ b/docs/backend-repair-contract.md
@@ -0,0 +1,34 @@
+# Backend repair contract (mandatory for all repair workers)
+
+Prior broad edits introduced cascading signature errors. Parent stopped the old backend process. Repair in narrow file ownership; other workers may be editing their own files. Do NOT edit outside your allocation. Do not weaken tests, delete API handlers, or change language/framework.
+
+## Fixed signatures
+- state.rs exports `pub type Cx = axum::extract::State<std::sync::Arc<AppState>>;`
+- AppState owns cfg:Config, db:tokio::sync::Mutex<rusqlite::Connection>, run_sem/fetch_sem Arc<Semaphore> (preserve other needed state).
+- `impl AppState { pub async fn with_db<R>(&self, f: impl FnOnce(&mut rusqlite::Connection) -> R) -> R { let mut db = self.db.lock().await; f(&mut db) } }`.
+- This returns exactly R after await. If closure returns Result<T,E>, use `.await?`; if closure returns number/unit, `.await` only. Never await inside database closure. Keep transactions and atomic operations inside ONE closure. No nested locks.
+- Handler argument is `cx: Cx` (State extractor), not a bare Arc alias. Helper taking `&Cx` receives `&cx`. Background jobs may use Arc and create `State(cx.clone())` only where necessary. All handlers futures must be Send.
+- util::now_iso() -> String is SYNCHRONOUS. hash/random token helpers synchronous unless they actually await.
+- error::AppError supports new(StatusCode,code,message), bad(code,message), unauthorized(message), forbidden(message), not_found(message), conflict(code,message), internal(message), with_details(Value), with_code(code). One inherent impl, one IntoResponse, one From<rusqlite::Error>, one From<std::io::Error>, one From<serde_json::Error>. Never duplicate From impl elsewhere.
+- `auth::make_admin_token` existing admin call sites need coherent async Result signature; auth/admin worker owns both.
+- Router Axum0.8 paths use `{id}`, NEVER `:id` (runtime panic).
+- Do not change actual JSON HTTP contract: SPEC.md + RELEASE_SCOPE.md authoritative.
+
+## Worker partition
+CORE: config.rs state.rs error.rs util.rs main.rs store.rs worker.rs Cargo.toml (+ core tests). Main integrates call signatures but not other module sources.
+AUTH: auth.rs admin.rs (+ auth tests inside module).
+DOMAIN: projects.rs datasets.rs runs.rs ai.rs jobs.rs (+ domain tests inside module).
+Read other files freely. Compile often, filter diagnostics for OWN modules; leave other-worker diagnostics alone. Final shared compile gate performed by parent.
+
+## Parent findings to address
+- main.rs origin substring match is exploitable. Compare exact canonical origin (scheme+host+port), reject `https://allowed.example.evil.invalid`. Empty canonical allows exact local origin only. No trust of X-Forwarded host.
+- Serve frontend/dist static SPA with index.html fallback for UI only; /api unknown returns JSON 404 (not SPA).
+- Instrument search current stub must connect to actual worker catalog command; no fake successful empty list. Docker worker fetch/search with bounded time and resource.
+- Concurrent fetch duplicate cache key must serialize/recheck. request/cache key and manifest hash are distinct columns/concepts.
+- All raw+normalized referenced artifacts retained; worker manifest fields must be checked against live worker code.
+- Read-only nonroot container UID65534 needs writable output (parent job dir0700, output mode0777 inside; no secrets). Input/data are read-only. Stdout/stderr bounded and drained; timeout/cancel container cleanup real.
+- Finished run state only succeeded if worker JSON status says succeeded. Failed/cancelled state must not be overwritten by racing completion.
+- Durable queued jobs recheck account active, dataset ownership/readiness, per-user budget.
+- Invitation email binding, one-time transaction, no role escalation; disable/reset revoke sessions; last admin protection.
+
+Parent independent API tests live at scripts/qa_api.py, including substring CSRF and admin/private owner separation. READ these gates and make them genuinely pass. Parent prepares isolated QA environment, do not read any secrets outside project.