daemon: store/ query layer — tasks + settings (prep for the download.add vertical slice)

The read/write surface the dispatcher handlers need, so download.add
persists and download.list / download.get project real rows when the
engine lands.

- store/tasks — TaskRow (1:1 with the schema), insert / get / remove /
  set_state / count, and list(filter, sort, offset, limit) that does
  all the WHERE / ORDER BY / LIMIT in SQL (M1 DoD: a 1000-row list
  never materialised client-side). Filter covers states / category /
  queue / case-insensitive filename+url substring / date range; sort
  is a whitelisted column + direction with NULLs last, default
  newest-first; the enum spellings in a state IN (...) come from
  proto::to_string, never from user text. to_summary() projects a row
  onto proto::TaskSummary including the flattened error block when the
  task failed / retry_wait / auto-paused.
- store/settings — key -> JSON-text with a built-in default table
  mirroring Settings.schema.json / ADR 0012; get_raw / set_raw /
  overrides plus typed get_int / get_string / get_string_array for the
  governor config and saveTo.allowedRoots. Full settings.get/set wire
  projection lands with those handlers.
- veloxd_store now links velox::proto + nlohmann_json for the
  projection.

Test veloxd.store_tasks: insert/get round trip, PK duplicate rejected,
the error-block projection, list total+paging+sort+every filter,
set_state pause_reason clear-on-unpause, remove, and settings default
vs override. ASan+UBSan and TSan clean; 34 daemon/cli tests green.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Upd9WhG9oppieig5nRDLig
This commit is contained in:
2026-09-10 19:43:13 +04:00
co-authored by Claude Sonnet 5
parent ab479e7885
commit 6787784936
7 changed files with 754 additions and 1 deletions
+48
View File
@@ -0,0 +1,48 @@
#pragma once
// Read/write access to the `settings` table (key -> JSON-text value). The table starts
// empty; a key with no row falls back to the built-in default that mirrors
// Settings.schema.json. Typed helpers cover what the daemon reads internally (governor
// config, allowed roots); the full settings.get / settings.set projection onto the wire
// `Settings` object lands with those handlers.
#include <cstdint>
#include <map>
#include <optional>
#include <string>
#include <string_view>
#include <vector>
#include "store/sqlite.hpp"
namespace velox::daemon::store {
class Settings {
public:
explicit Settings(Db& db) : db_(db) {}
// Raw JSON text for one key: the stored row, or the built-in default, or nullopt if
// the key is unknown to the daemon entirely.
DbResult<std::optional<std::string>> get_raw(std::string_view key);
// Replace one key's value. `json_text` must be a valid JSON document; the caller
// (settings.set handler) validates it against the schema first.
DbResult<void> set_raw(std::string_view key, std::string_view json_text);
// Every stored override (not merged with defaults).
DbResult<std::map<std::string, std::string>> overrides();
// Typed convenience over get_raw + the defaults. A malformed stored value falls back
// to the default rather than throwing.
std::int64_t get_int(std::string_view key);
std::string get_string(std::string_view key);
std::vector<std::string> get_string_array(std::string_view key);
// The built-in default JSON for `key`, or nullopt if unknown.
static std::optional<std::string_view> default_for(std::string_view key);
private:
Db& db_;
};
} // namespace velox::daemon::store