release: v0.10.0 - #151
Merged
Merged
Conversation
…back via sqlx Any)
…ke tests
- sqlx 0.8 with default-features=false; adds `any` and `macros` on top of
the design-doc feature list: the Any driver and the migrate!() macro are
otherwise not compiled in.
- migrations/0001_init.sql: 7 tables per design/persistence.md §3.2, with one
deviation proven by smoke test — timestamp columns are TEXT carrying
fixed-width RFC 3339 UTC strings, because the Any driver has no chrono
Type impls and SQLite refuses String decode from TIMESTAMP-typed columns.
- src/store/mod.rs: SqlxStore::connect (PG/SQLite URL discrimination),
connect_default (sqlite://{config_dir}/review.db?mode=rwc),
new_in_memory (max_connections(1)), migrate (embedded via migrate!()).
SQLite pools get WAL / foreign_keys / busy_timeout pragmas.
- Verification point A/D smoke tests: schema creation + idempotent re-run,
`?` placeholder INSERT/SELECT round trip, timestamp round trip with
ORDER BY correctness. PG side is #[ignore]d behind DATABASE_URL.
- traits.rs: ConfigStore — whole-set load/replace for git_platforms and
llm_providers (aligned with UiStateFile semantics), legacy gitlab via the
app_settings 'gitlab' JSON row, arbitrary app_settings JSON get/upsert,
and config_tables_empty (the §6.1 one-shot import trigger).
- rows.rs: GitPlatformConfig / LLMConfig / PersistedGitlabConfig codecs.
This is the enc: boundary: token / webhook_secret / webhook_signing_secret
/ api_key are ChaCha20-Poly1305 encrypted at rest (LLM api_key newly inside
the boundary — 0.9 persisted it plaintext); empty stays empty; values
without the enc: prefix read back as legacy plaintext. LLM list order is
round-tripped via a position marker in raw JSON (first entry is the
fallback primary provider).
- sqlx.rs: SqlxStore impl; ? placeholders, no RETURNING, RFC 3339 TEXT
timestamps, replace-in-transaction semantics.
- SqlxStore now carries the secrets key: connect() resolves it via
resolve_ui_state_path + key_path_for, connect_default(config_dir) uses
{config_dir}/secrets.key, new_in_memory() uses an ephemeral key.
- Migration deviation (verified by smoke test): git_platforms.enabled is
INTEGER 0/1 instead of BOOLEAN — the Any driver cannot decode SQLite
columns declared BOOLEAN (only Null/Int4/Integer/Float/Blob/Text pass the
SQLite→Any type mapping).
…e-test findings - §3.1 布尔行: BOOLEAN 证伪, DDL 改 INTEGER 0/1 (Any 无法解码 SQLite Bool 声明列) - §3.2 enabled 列同步改 INTEGER (时间戳 TEXT 化与 §11 验证点 A 勾选为杜衡并行修订)
Startup sequence (design/persistence.md §6.1, strict order, cli/app.rs):
1. bootstrap_database(): REVIEW_DISABLE_DB=1 bypasses to 0.9 behaviour
(warn); DATABASE_URL set-but-unreachable is a hard startup error, never a
silent SQLite fallback (§9); no DATABASE_URL → embedded SQLite at
{config_dir}/review.db. migrate() failure propagates (exit non-zero).
2. (placeholder comment) §5.3 interrupted sweep — 梁序 step 4.
3. import_ui_state_into_db(): triggers only when all three config tables
are empty AND ui-state.toml exists; the whole import is ONE transaction
(ConfigStore::save_ui_state), renamed to ui-state.toml.migrated only
after every table is written; any error keeps the file and falls back to
the file replay path.
4. load_and_apply_ui_state_from_db(): DB rows reassemble into a UiStateFile
and go through the SAME replay_payload + apply_ui_config path, so
env precedence and masked projections are literally shared with the file
path. Empty DB → Ok(false) → file replay fallback.
PUT /config (§6.2): state.db set → ConfigStore::save_ui_state (single tx);
else the 0.9 file path. UiStateFile::from_applied env filtering is reused
unchanged — env/CLI values are never persisted anywhere. Persist failure
returns 500, same as a file-write failure today.
AppState gains pub db: Option<Arc<SqlxStore>> (None default; the sync
AppState::new() test path is untouched).
Tests: import happy path (plaintext-LLM-key legacy file → tables populated,
file renamed, all four secret columns enc: at rest, DB replay ≡ file
replay); env precedence matrix against the DB source (env LLM wins
wholesale; gitlab env is fallback-only / DB wins when set); failed import
(dup UNIQUE name) rolls back completely and keeps the file;
put_config_persists_to_db_instead_of_file; REVIEW_DISABLE_DB flag parsing.
…covery
0.10.0 persistence step 4 (design/persistence.md §5):
- store/traits.rs: ReviewStore — create / mark_started / fill_source_meta /
complete (terminal, single tx: reviews UPDATE + expert_reports replace) /
mark_cancelled / mark_retry / mark_interrupted (startup sweep).
- store/sqlx.rs: SqlxStore impl; store/rows.rs: TaskEntry⇄reviews and
ReviewOutput.reports⇄expert_reports codecs (state strings reuse the API
projection mapping task_status_str so DB/SSE vocabulary cannot drift).
- server/task_queue.rs: TaskStore gains db: Option<Arc<dyn ReviewStore>> +
set_db; write-through on create_with_request/start/fill_source_meta/
update(terminal)/delete/retry per §5.2; set_progress stays memory-only;
the cancelled early-return writes nothing. DB writes are synchronously
awaited AFTER the in-memory lock is released; failures log and continue,
terminal writes retry once. db=None is exactly 0.9 behaviour.
- cli/app.rs: after migrate, mark_interrupted() sweeps stale
pending/running rows to failed('interrupted: server restarted') and the
DB handle is injected into the task store.
- reaper untouched (memory-only).
Tests: full lifecycle row assertions, expert_reports split, failing-store
injection (path unblocked, terminal retried exactly once), interrupted
sweep state coverage, codec round-trip, cancel/retry write-through,
db=None 0.9 parity. fmt/clippy/test green (1592 passed, 0 failed).
0.10.0 persistence step 5 (design/persistence.md §8.1): - store/traits.rs: ReviewStore gains the read side — ReviewListQuery (handler-normalized params), list_reviews() -> (entries, total), get_review() -> Option<TaskEntry>. - store/sqlx.rs: list pagination via ORDER BY created_at DESC, task_id DESC (deterministic tiebreak) + LIMIT/OFFSET, COUNT(*) under the same WHERE; status/q/project/repository/date_from/date_to filters; q keeps the 0.9 case-insensitive literal-substring semantics over source_meta TEXT (LOWER(...) LIKE LOWER(?) ESCAPE '\', LIKE wildcards escaped). - store/rows.rs: REVIEW_COLUMNS + ReviewRowTuple + From tuple -> ReviewRow; review_from_row decodes DB rows back to TaskEntry, so the three API projection functions (task_to_status / build_review_detail / build_review_list_item) keep their signatures and the response shape is byte-compatible with 0.9 (timestamps, duration_ms included). - handlers.rs: list_reviews / get_review switch on AppState::db — Some: DB query (get_review overlays live progress/expert_name from memory for in-flight tasks; a memory-only task whose create write-through failed is still served instead of 404); None (REVIEW_DISABLE_DB=1 / tests): the 0.9 in-memory path, unchanged. Tests (src/server/api/review/tests.rs): (a) pagination/status/q/project/ repository/date semantics vs 0.9 + item/top-level key-set parity against the memory path; (b) live progress/expert_name overlay for in-flight tasks, pure-history detail from DB, 404 for unknown; (c) db=None fallback for list+get; (d) empty DB and out-of-range page boundaries. fmt/clippy/test green (1596 passed, 0 failed).
…ions
0.10.0 persistence step 6a (design/persistence.md §7.1):
- store/traits.rs: DiscussionStore — upsert_note (idempotent on
(platform, project, mr_iid, note_id), edits update body/author) and
list_notes (created_at, note_id ascending — the append-only order the
step-6b context renderer relies on). store/rows.rs: row codecs;
store/sqlx.rs: SqlxStore impl (? placeholders, ON CONFLICT upsert).
- server/gitlab/hooks.rs: handle_note_hook gains db: Option<Arc<SqlxStore>>;
ingestion runs after parse, BEFORE the command check (command notes are
discussion history too). Skips: non-note payloads, non-MR noteables,
system notes. MR iid falls back to the object_attributes.url tail;
platform defaults to 'default'; author prefers user.username.
- Self-echo guard (§7.1): (a) notes starting with the published report
prefix — extracted as publisher::REVIEW_REPORT_PREFIX ("# CodeReview
Board\n\n", previously inline in lib.rs publish_review); (b) notes
authored by the service's own GitLab user id, resolved lazily per
platform via GET /user (new Client::for_instance) and cached for the
process lifetime (only successes cached; failures retry next note).
/review and /describe command notes always ingest (user intent).
- handler.rs passes AppState::db through both note-hook call sites.
db=None keeps exact 0.9 behaviour; ingestion failures are logged, never
fail the hook.
Tests: ingestion field fidelity (incl. GitLab legacy '… UTC' timestamp
format), platform name + iid URL fallback, redelivery dedup, edit-in-place,
non-MR / system note skips, self-echo guards (a)+(b) with command
exception, db=None 0.9 parity, store-level list ordering. fmt/clippy/test
green (1605 passed, 0 failed).
….10.0 §7.2) DB-first (mr_discussions from §7.1 webhook ingestion) with a GitLab discussions-API fallback that back-fills the DB; the rendered section is prefix-stable (ordered by created_at/note_id, fixed 2000-char body cap), sha256-recorded into review_contexts, capped at 128 KiB, and attached to MRInfo.discussion_context between the fixed MR context and the diff. Every failure path (no DB, API down, empty, oversized) degrades to the 0.9 prompt; self-echo guards (report prefix, own user id, system notes) match webhook ingestion while /review /describe command notes are kept.
…-comment tab (§8.3) raw_comment now resolves aggregated.markdown → consolidated.assessment.tl_dr, filtering empty strings to None, so team reviews without an aggregator report no longer render an empty full-comment tab.
…-up) SqlxStore records its BackendKind (postgresql/sqlite) at connect time from the URL discrimination in design/persistence.md §4.3, exposed via backend_kind(). GET /api/v1/system/health gains a read-only storage_backend field: "postgresql" / "sqlite" / "disabled" (no DB attached — REVIEW_DISABLE_DB=1, tests, embedded use), for the frontend config page.
…0.10.0 wrap-up) The config page Advanced card gains a permanently-disabled row displaying the persistence backend in use (PostgreSQL / SQLite / disabled), sourced from the storage_backend field added to GET /api/v1/system/health in d0a8c82. The service layer normalizes the one snake_case key to storageBackend and validates it against the known kinds; a health-check failure or an older server simply hides the row (fail-silent). Also drops an unused catch binding in Configuration.vue flagged by eslint (no-unused-vars).
…ore layer sqlx 0.8.6's Any driver passes statement text through verbatim — it does NOT translate `?` to $1..$n (no placeholder/rewrite logic anywhere in sqlx-core-0.8.6/src/any/), so every bound DML failed on real PostgreSQL with 42601 syntax error (0.10.0 E2E). The earlier assumption baked into src/store/mod.rs and design/persistence.md §3.1 was wrong. - src/store/placeholders.rs: lexer-based rewriter; `?` inside single-quoted literals (incl. '' escapes), double-quoted identifiers, -- line comments and nested /* */ block comments stays literal. Covered by unit tests for mixed literals, escaped quotes, comments, consecutive/double-digit placeholders, and unterminated input. - src/store/mod.rs: adapt_sql(kind, sql) / SqlxStore::sql() — the single adaptation point; PG rewrites (borrowing when there is nothing to rewrite), SQLite passes the text through unchanged. Module doc and the ignored PG smoke test corrected. - src/store/sqlx.rs: every statement (ConfigStore / ReviewStore / DiscussionStore, incl. tx helpers, the interrupted sweep, the dynamic list pagination and both upserts) now passes through adapt_sql once. - design/persistence.md: §3.1 placeholder row and verification point A corrected; Migrator is unaffected because the Any migrate path delegates to the real underlying driver. Verified on postgres:16-alpine: migrate_on_postgres_smoke green, server health reports storage_backend=postgresql, ui-state.toml import fills git_platforms/llm_providers/app_settings (secrets enc:-prefixed), the restart sweep flips pending/running to failed/interrupted, and review list pagination/filters return correct pages.
…e durations F1 (PG release blocker): llm_providers.temperature was REAL, which PG parses as float4, while the store binds/decodes f64 — read-back failed with 'mismatched types: f64 is not compatible with SQL type REAL'. The failure chain was silent: ui-state import succeeded (file renamed to .migrated), but the restart DB replay died on decrypting/decoding, leaving GET /config empty while /health stayed green. SQLite's REAL is 8 bytes, which is why unit tests and the SQLite E2E never saw it. 0.10.0 is unreleased with no databases to migrate, so the column simply becomes DOUBLE PRECISION (float8); SQLite maps that to the same 8-byte REAL affinity. Decode stays f64. A new ignored PG test (llm_providers_temperature_round_trip_on_postgres) gates the exact read path, and cleans up its enc: rows so shared scratch databases are not polluted with foreign-key ciphertext. migrations/0001_init.sql and design/persistence.md document why REAL is banned for float columns. No other float columns exist in the schema (audited: temperature is the only one). F2: TaskEntry::duration_ms wrapped to ~2^64 when a hand-seeded row has created_at later than completed_at (negative i64 cast to u64). All four millisecond-span projections (duration_ms, elapsed_ms, and the two inline SSE elapsed calculations) now go through one millis_between helper: saturating_sub on timestamps, clamped at 0. Unit test covers inverted spans and clock skew. Verified on postgres:16-alpine: both ignored store tests green; server boot imports ui-state.toml, restart replays from the DB, and GET /config is byte-identical across the restart (gitPlatforms/gitlab/llm intact, temperature 0.3); an inverted-timestamp review row reports durationMs 0.
…lumns 0.10.0 E2E-A 观察点 4:reviews 表的 project/repository 物化列有值、但 列表 API 响应 project:null。语义设计是「物化列供过滤、投影走 source_meta JSON」(design/persistence.md §5.2,写穿同步维护),两者一致 时无问题;但 fill_source_meta 的 UPDATE 失败只记日志不重试 (task_queue.rs),加上手工种子/遗留行不经过 codec,漂移行客观存在—— 于是出现「?project=X 过滤命中、行里 project 却显示空」的自相矛盾。 rows.rs review_from_row 之前把已 SELECT 出来的物化列直接丢弃。现在 decode source_meta 后,对空白(None/纯空白,口径同 fill_source_meta 的 is_blank)的 project/repository 用物化列回炉;JSON 非空值永远是主 源,列只做兜底,不臆造值(列也为空则保持 None)。 附实证(隔离 server + 手插漂移行):修复前过滤 total=2 而漂移行 project=null;修复后漂移行 project='grp/proj',与过滤语义一致;正常行 响应逐字段不变(status/project/repository/result.consolidated.assessment 全保留)。 测试: - store::rows::tests ×3 — 列回炉 / JSON 主源优先 / 不臆造空值(codec 层 钉住语义)。 - api::review::tests::list_reviews_db_projection_values_complete — 用真实 写穿路径(record_task_started → record_task_outcome,即 webhook 所用 helper)建行,断言 DB 路径列表项的 status/project/repository/branch/ author/duration/内嵌 assessment 全部带值,并与 0.9 内存路径逐值对齐 (既有 (a) 只比键集合,project:null 漂移能漏过)。 - api::review::tests::list_reviews_db_drifted_row_projects_materialized_column — API 层钉住漂移行:过滤命中什么,投影就显示什么。 注:status 在两层投影里都是非 Option 字符串(来自 reviews.state 列解 码),DB 路径不可能产出缺 status 的列表项;state 解码失败是整列 500 而非字段缺失。
E2E 实测两个静态服务缺陷: 1. 前端 SPA 由裸 ServeDir serve,没有 not-found 回退——直接打开或刷新 /history、/config 等 history-mode 子路由返回 ServeDir 的裸 404;从根 路径进站再客户端跳转则正常,所以只有深链接/刷新中招。 2. static_dir() 按 CWD 找 ./frontend/dist,从非仓库根目录启动时静默 退化成 "Dashboard coming soon" 占位页,无任何日志,排查全靠猜。 修复: - ServeDir 挂上 axum 回退 handler(get + 闭包):文件不存在时,对「非 /api/ 前缀、且末段不含扩展名」的 GET 路径重新读盘 serve index.html (200 + no-cache, must-revalidate,与 / 的缓存契约一致——它就是 index.html)。不缓存到内存:原地升级会在 server 运行期间替换 dist, 内存副本会引用已消失的 hashed chunk(正是 cache-control 修复过的 白屏缺陷)。 - 用 ServeDir::fallback 而非 not_found_service:后者用 SetStatus 把 回退响应强制改写为 404,深链接会拿到「200 的 body + 404 的 status」 (实测复现)。handler 自己按路径裁决:深链接 200,/api/ 与带扩展名 的文件请求显式 404——未匹配的 API 路由和缺失 hashed asset 的 404 契约均不变(HTML 200 会把部署事故藏成白屏)。 - static_dir() 两个候选路径都不存在时打 WARN,带当前 CWD 和占位页 后果说明;回退到占位页的逻辑本身不变。 测试: - 单测(router.rs mod spa_deep_link):决策函数三组——客户端路由回退、 /api/ 保持 404、带扩展名文件保持 404。 - 集成测试(tests/server/frontend.rs):tempdir 假 dist + 真实 spawn server,GET /history、/config、/reviews/42 均 200 且 body 为 index.html、带 no-cache;GET /api/v1/definitely-not-a-route 与 /assets/missing-00000000.js 保持 404。 - cargo test 全绿:lib 1502 passed,各 test target 58/31/47/4 passed,0 failed;cargo fmt --check 干净。
… under 0.10.0 Fixed
Formalizes the previously untracked Dockerfile.local as a tracked contributor-facing build path: in-container Rust builder (macOS arm64 has no cross toolchain), COPY migrations for sqlx::migrate! (0.10.0), and APT_MIRROR build-arg instead of a hardcoded mirror.
docs/migration-0.10.md covers backup, upgrade paths, the automatic first-boot migration chain, post-upgrade verification, and troubleshooting; states plainly that downgrading to 0.9.x is not supported. CHANGELOG 0.10.0 opens with the same one-way-upgrade declaration; README and docs index link to the guide.
…-clipping The project el-tag had no max-width, so slugs wider than the 140px column were cut off by the cell's overflow:hidden with no ellipsis or way to read the full name. Cap the tag at the cell width with the same truncation recipe Element Plus uses internally, add a hover tooltip with the full slug, widen project/status/score columns slightly, and give the MR title column back some of its excess min-width.
… parsing (RENG-26) The lead-overview response was parsed with strict serde_yaml_ng on the raw LLM output. LLMs commonly wrap the document in a ```yaml fence — a backtick at line start is a reserved YAML indicator, so the scanner aborts with 'found character that cannot start any token' and the global context was silently dropped for the whole expert pass. Parse the response through a layered fallback instead: strict parse, then parse after stripping code fences and normalizing tab indentation (illegal in YAML), then parse the first fenced YAML block only. Reuses the shared output-parser helpers (clean_yaml / extract_first_fenced_yaml) already used by the adjudicator and verifier. Full parse failure still degrades to no global context, unchanged.
…exists The lead-overview context gather treated MRInfo.project_path as a local filesystem path, but for webhook/API-triggered reviews it is the provider slug (group/project) and the server never clones the repository. Every first review of a repo with no local cache logged "failed to gather project context: Repository path does not exist: <slug>" and degraded to an empty ProjectContext. gather_lead_project_context now only invokes the git-backed gatherer when the path is an existing directory; otherwise (and on gather failure) it falls back to a partial context built from the reviewed diff's file list, so first-time reviews still get a real file tree without new network I/O. RENG-25
…RENG-27) The History author column always showed the MR creator (e.g. the GitLab root account 'Administrator') instead of the person who actually wrote the commits. Author resolution now prefers the head commit's author and falls back to the MR creator, per the 2026-09-04 decision: - Webhook parse: object_attributes.last_commit.author.name now wins over object_attributes.author.name, then the trigger user. - GitLab fetch_mr_info: best-effort GET /repository/commits/<head_sha> resolves author_name into the new MRInfo.commit_author; any failure degrades to None and never fails the review. GitHub path unchanged. - source_meta_from_mr_info: author_name = commit_author, falling back to pr_author; blank commit authors are treated as absent. No schema change: author_name lives in the reviews.source_meta JSON column, so existing history rows are untouched and only new records pick up the fix.
# Conflicts: # src/team/orchestrator/pipeline.rs
GET /api/v1/reviews/history returned 400 'Cannot parse task_id' because
no such route exists: the request is captured by /{task_id} and fails
UUID path-parameter validation. Routing is correct (axum 0.8 has no
registration-order issue here) and every in-tree caller — frontend
services/reviews.ts and docs/rest-api.md — already uses GET /reviews,
so there is nothing to reorder or rewire.
Pin the contract instead of expanding the API surface:
- docs/rest-api.md: GET /reviews is documented as the sole history
list endpoint; GET /reviews/:task_id now documents the 400 for
non-UUID task_id alongside the existing 404.
- tests/server/reviews.rs: reviews_history_subpath_is_not_a_route
locks the semantics end-to-end (list 200 envelope; /reviews/history
400 naming task_id).
The adjudication pass assumed MRInfo.project_path is a local filesystem path, but for webhook/API-triggered reviews it is the provider slug (group/project) and the server never clones the repository. Every file load failed with "not readable from the local checkout" (INFO, one per file), no finding was actually adjudicated, yet the pipeline summary still logged "examined N findings". Adjudicating against the diff patch alone is not a safe substitute: a unified diff carries only the changed regions ±3 context lines, so the full-file ground-truth check the pass exists for (defensive code far from the hunk) is unsatisfiable, and judging against patch-only content would risk fail-closed drops on missing data. The only verdict safe under patch-only ground truth is Confirmed, which is behaviorally identical to keeping the finding. So skip explicitly instead: when no local checkout directory exists and there are candidates at or above the threshold, emit one WARN naming the reason and the number of findings that pass through unadjudicated (kept unchanged, fail-open), and make no LLM calls. The per-file skip inside a real checkout (e.g. files deleted by the MR) is elevated from INFO to WARN with the kept-finding count, and the pipeline summary now says "candidates" instead of the misleading "examined". CLI local reviews (real checkout) are unchanged. Follow-up (not implemented): full adjudication for server-side reviews does not require cloning — the provider raw-file API (GitLab /projects/:id/repository/files/:path/raw?ref=<head_sha>) can supply ground truth on demand, but threading the provider client into the provider-agnostic team layer is an architecture change of its own. RENG-25
The status column was 100px wide, but cell padding stacks (16px from cellStyle on the td plus Element Plus' default 12px on .cell), leaving only ~44px of content width. The default .cell word-break: break-all then split "已完成" into "已完/成". Widen the column to 108px to match the history table (8aa1740), wrap the badge + label in a flex cell, and apply the same truncation recipe so over-long labels (e.g. ja "キャンセル済み") ellipsize instead of wrapping or being hard-clipped.
…ct, RENG-30 dashboard wrap under 0.10.0
…erministic CI (Linux) failed with left=...524657Z vs right=...524657367Z: Utc::now() returns nanoseconds on Linux (clock_gettime) but only microseconds on macOS (gettimeofday), so the strict equality assertion was platform-flaky. Root cause is the test, not the store: encode_ts deliberately stores timestamps as fixed-width RFC 3339 at microsecond precision (SecondsFormat::Micros) — the documented codec contract shared by the SQLite and Postgres backends via the Any driver's TEXT path. Use a deterministic nanosecond timestamp as input, assert the input carries sub-microsecond digits (so the test can't silently degenerate), and assert the round-trip equals the micro-truncated value.
CodeQL flags cleartext logging of sensitive information where test assert failure messages interpolate the api_key variable. The values are fake keys, but the rule matches the pattern regardless. Assert conditions are unchanged; messages now describe the expectation without printing the key value.
The rsa 0.9.10 crate enters Cargo.lock only as an optional dependency of sqlx-mysql, which sqlx 0.8 locks in regardless of feature activation. The workspace builds sqlx with default-features = false and no "mysql" feature, so neither sqlx-mysql nor rsa is ever compiled (verified with `cargo tree --all-features --target all -i rsa`, which prints nothing, and by regenerating the lockfile from scratch, where rsa reappears and therefore cannot be pruned while sqlx remains a dependency). The Marvin attack requires a chosen-ciphertext oracle against RSA private-key decryption. review-engine performs no RSA operations at all (SQLite/PostgreSQL only; no MySQL code path exists), and upstream provides no patched rsa release, so ignoring is the only available remediation. The ignore must be revisited if a fixed rsa lands or if sqlx's mysql feature is ever enabled.
… audit ignore fixes under 0.10.0
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Review Engine 0.10.0 — a major upgrade centered on persistent storage. One-way migration: the first 0.10.x boot creates the database, applies embedded migrations, and imports
ui-state.tomlin one transaction; downgrading to 0.9.x is not supported. Back up the config directory before upgrading (seedocs/migration-0.10.md).Highlights
src/store/module on sqlx 0.8'sAnypool;DATABASE_URLset → PostgreSQL, unset → embedded SQLite. 7 tables, migrations embedded viasqlx::migrate!()and applied at startup.TaskStorewrites through to the DB; History APIs read from the DB, no longer bounded by the 30-minute in-memory reaper. Startup sweep fails interrupted tasks.ui-state.tomlinto the DB via a one-shot transactional import; all credentials storedenc:-encrypted (ChaCha20-Poly1305).REVIEW_DISABLE_DB=1restores full 0.9 behaviour.mr_discussions(idempotent upsert, self-echo guard) and injected into expert prompts.GET /api/v1/system/healthgainsstorage_backend(postgresql/sqlite/disabled).Fixed (incl. E2E-found release blockers)
?→$nplaceholder rewriting (sqlxAnydoes not translate; every bound statement failed on PG with42601).llm_providers.temperaturedeclared float8 on PG (f64 bind/decode mismatch broke config read-back).durationMswrap-around guard; History list projection self-consistency.ServeDir::fallbackservesindex.html); diagnosable static-dir miss.GlobalReviewContextYAML parsing (RENG-26); History author column shows the commit author (RENG-27);/api/v1/reviewspinned as sole history list endpoint (RENG-29); Dashboard recent-reviews status stays on one line (RENG-30).Full details in CHANGELOG.md
[0.10.0].Verification
cargo fmt --check/cargo clippy --all-targets --all-features/cargo test/cargo build— see CIcargo audit— see CI (audit.yml)Known issues
webhookSecretmasking alignment, no deep DB probe in/system/healthyet.