Implement DDD architecture with domain layer, use cases, and infrastructure#1
Open
marcuspat wants to merge 61 commits into
Open
Implement DDD architecture with domain layer, use cases, and infrastructure#1marcuspat wants to merge 61 commits into
marcuspat wants to merge 61 commits into
Conversation
Introduce docs/adr/ with 15 Architecture Decision Records covering language, GUI, HTTP client, async runtime, serialization, error handling, layering, TDD, persistence, observability, domain typing, concurrency, build profiles, module layout, and configuration. Introduce docs/ddd/ with 12 Domain-Driven Design documents covering ubiquitous language, domain overview, bounded contexts, context map, aggregates, entities/value objects, domain services, application services, repositories, domain events, anti-corruption layers, and an implementation roadmap. These describe the target architecture for the full implementation; existing code is unchanged. https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
The .claude/worktrees/ tree is created by the harness when an isolated sub-agent is dispatched. It is ephemeral and must not be tracked. https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
Removes the TypeScript prototype residue under src/, tests/, and scripts/ that the Rust crate does not consume, plus the dead `src/main_broken.rs` and `src/test_main.rs` files. Drops the corresponding `[[bin]]` entry for `test_requester` from Cargo.toml. This satisfies milestone M0 in docs/ddd/12-implementation-roadmap.md and ADR-0014 (source-tree layout aligned with bounded contexts). `cargo check` remains green with the same two pre-existing unused-var warnings in `src/main.rs` that will be addressed in M2. https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
Creates the bounded-context module tree from ADR-0014:
- src/domain/{http,history,collections,settings,secrets}/mod.rs
- src/app/mod.rs
- src/infrastructure/{http,persistence,config,secrets}/mod.rs
- src/ui/mod.rs
Each new mod.rs carries a `//!` doc-comment naming its responsibility
and the milestone in which its first contents are scheduled to land.
The modules are otherwise empty stubs.
`src/lib.rs` declares the new modules and keeps the existing
`http_types` module plus the `HttpMethod`, `HttpRequest`, `HttpResponse`
re-exports so external integration tests (`tests/integration_tests.rs`,
`tests/error_handling_tests.rs`, `tests/gui_utils.rs`) keep compiling
through the M2 split.
`cargo check` remains green; the same two pre-existing unused-var
warnings in `src/main.rs` will be cleaned up in M2.
https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
Splits the legacy `src/http_types.rs` into per-concept files under
`src/domain/http/`:
- `method.rs` — pure-domain `HttpMethod` (no `reqwest` dependency)
- `url.rs` — validated `Url` newtype rejecting non-http(s)
schemes and missing hosts
- `headers.rs` — `HeaderName` (case-insensitive equality, casing
preserved), `HeaderValue` (rejects CR/LF/NUL),
`Headers` insertion-ordered multi-map
- `status.rs` — `StatusCode(u16)` (100..=599) and `StatusClass`
- `body.rs` — `RequestBody`/`ResponseBody`/`MultipartPart`
- `request.rs` — `HttpRequest` value-object cluster
- `response.rs` — `HttpResponse` with `chrono::Duration`
(serialised as integer ms)
- `error.rs` — `UrlError`, `StatusCodeError`, `HeaderNameError`,
`HeaderValueError`, `RequestError`
All value objects derive `Debug, Clone, Serialize, Deserialize`, and
`PartialEq, Eq, Hash` where meaningful. Smart constructors return
typed errors via `thiserror`. `Url`, `HeaderName`, `HeaderValue`, and
`StatusCode` round-trip as plain JSON strings/numbers via
`#[serde(into = ..., try_from = ...)]`.
Per ADR-0011, `From<HttpMethod> for reqwest::Method`,
`TryFrom<&Url> for reqwest::Url`, `build_reqwest_request`, and
`into_domain_response` move into the new
`src/infrastructure/http/conversions.rs`. The `domain/` tree no longer
mentions `reqwest`.
Each value object ships unit tests plus `proptest!` round-trip
properties; the suite has 49 library tests passing.
`src/main.rs` is rewired through the new types: the GUI continues to
hold raw text, and converts to a domain `HttpRequest` via the new
`build_domain_request` shim at the moment "Send" is clicked. The two
pre-existing unused-var warnings (`response`, `ctx`) are cleaned up;
seven new tests cover the build-domain-request happy/sad paths.
`src/http_types.rs` becomes a thin re-export shim of the new domain
types so external crates that imported the legacy path keep working.
Cargo.toml additions: `async-trait = "0.1"`,
`tokio-util = { version = "0.7", features = ["rt"] }`, and
`zeroize = "1.7"` (dev-dep). The bogus `cargo-nextest = "0.9"`
dev-dependency (a CLI tool, never a library dep) is removed; the
comment explains the trait-resolution overflow it caused.
Deviations from the brief, all forced by pre-existing brokenness:
1. Every file under `tests/` (`integration_tests*.rs`,
`error_handling_tests.rs`, `gui_utils.rs`, `tests/common/**`)
referenced unresolved symbols (`use http_types::...` instead of
`use requester::http_types::...`, `requester::main::RequesterApp`
which never existed, `proptest::Arbitrary` impls that the crate
never provided, methods on `TestMockServer`/`TestHelpers` that
were never declared) and never compiled. They have been deleted
rather than migrated; the spirit of the original `http_types.rs`
tests is preserved by the new per-module test suites in
`src/domain/http/*.rs`.
2. `benches/` is left untouched per brief; another agent owns it.
It does not currently compile and `cargo fmt --check` reports
formatting drift in those files. Both are pre-existing.
https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
Flips the status badges for milestones M0/M1/M2 from [Planned] to [Implemented] in `docs/ddd/12-implementation-roadmap.md` and appends an "Acceptance Notes" subsection summarising commit hashes, key files, test counts, and the validation-gate outcomes. https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
CancellationToken lives in tokio_util::sync, not tokio_util::rt. Required by the M3 HttpEngine port whose execute() takes a CancellationToken parameter. https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
Imports the .github/workflows/ci.yml and release.yml from the parallel CI agent's worktree branch (worktree-agent-a532cb10741f1b0d6). ci.yml runs build, fmt, clippy, test, and bench-compile on every push and PR, on ubuntu-latest with the apt packages required for egui. release.yml fires on v*.*.* tags and produces stripped binaries for Linux x86_64, macOS aarch64, and Windows x86_64, attached to a draft GitHub release. The bench-compile step is currently expected to fail until the bench suites are rewritten against the new domain::http value objects (the parallel agent wrote them against the pre-M2 HashMap<String, String> header shape). That rewrite is queued for after M3 lands. https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
Lands the M3 HTTP engine port + first ACL implementation. * `domain::http::engine::HttpEngine` — async trait the rest of the crate depends on; takes a domain `HttpRequest` and a `tokio_util::sync::CancellationToken`, returns a domain `HttpResponse` or a typed `RequestError`. Re-exported at the crate root so `requester::HttpEngine` resolves. * `infrastructure::http::ReqwestEngine` — concrete adapter wrapping a shared `reqwest::Client`. Translates errors through a centralised `translate_error` (timeout / decode / TLS / network / other) so `reqwest::Error` never leaks past the ACL boundary, and races every await against the cancellation token via `tokio::select!` (biased, so cancellation wins ties). Wall-clock duration is captured at send start and threaded into `into_domain_response`. Bug fix: `tokio-util = "0.7"` does not expose a `sync` cargo feature (`CancellationToken` lives under the `rt` feature already, which we had); the spurious `sync` feature would have failed to resolve. Drop it to keep `cargo build` green. Per the M3 brief the GUI continues to use the inline send code in `src/main.rs`; rewiring the application service onto the new port is M4 work to avoid re-introducing the synchronous `block_on` smell. https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
Programmable in-process HttpEngine test double, gated behind
cfg(any(test, feature = "testing")) so it never ships in release
artefacts. Use cases under test inject &MockHttpEngine in place of
ReqwestEngine; tests queue MockResponse expectations
(Respond, Fail, Hang) and assert against executed_requests()
afterwards.
Hang cooperates with CancellationToken so tests for cancellation
paths do not need a real network -- pair it with a short-deadline
cancel.cancel() and assert RequestError::Cancelled. A missing
expectation surfaces as RequestError::Other("MockHttpEngine: no
expectation queued") so over-stubbing failures are obvious.
Inline unit tests cover request capture, FIFO dispatch of multiple
expectations, typed-error propagation, and the cancellation handshake.
https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
Snapshot of in-progress M3 work captured to keep the working tree
clean while the M3 agent continues iterating. Adds:
* src/infrastructure/http/mock_engine.rs — programmable in-process
HttpEngine for use-case unit tests, gated under cfg(any(test,
feature = "testing")).
* tests/http_engine_wiremock.rs — wiremock-backed integration tests
covering 200/4xx/5xx, body echo, header round-trip, network
failure, and cancellation.
* src/infrastructure/http/mod.rs — module declarations and
re-exports for the above.
The M3 agent will land the final acceptance commit (roadmap badge
flip + Acceptance Notes) once its validation gates are green.
https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
Flips the M3 heading badge to [Implemented] and appends an "Acceptance Notes -- M3" section to docs/ddd/12-implementation-roadmap.md covering: commits landed, what shipped (HttpEngine port, ReqwestEngine adapter with typed error translation and tokio::select! cancellation, MockHttpEngine test double, wiremock integration test suite), test counts (70 total: 56 lib + 7 bin + 7 integration), validation-gate outcomes, and deferrals. The most important deferral is the GUI rewiring -- per the M3 brief, src/main.rs continues to use the inline send code; switching it onto SendRequest + HttpEngine is M4 work so the synchronous block_on smell can be retired alongside the runtime + worker changes rather than re-introduced here. Notes for the M4 agent are captured at the end of the section (token-clone discipline, who stamps the duration, the with_client escape hatch). https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
Introduce the M4 single-shot send use case that owns an `Arc<dyn HttpEngine>` and propagates `CancellationToken` through to the engine. Held behind a trait-object so the worker channel keeps a single concrete type when the engine is swapped (real, mock, future caching wrapper). M5 will inject `HistoryRecorder` here so each call also writes one history entry; the constructor and call sites are shaped to make that change a one-line addition rather than a refactor. Re-export `SendRequest` from the crate root so the GUI binary (and any future bins) can `use requester::SendRequest`. The `AppRuntime` / `AppCommand` / `AppEvent` re-exports land in the next commit alongside the worker harness itself. Tests: three `#[tokio::test]`s on `SendRequest` covering the happy path, an engine failure that bubbles, and a `Hang` expectation cancelled mid-flight that resolves to `RequestError::Cancelled`. https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
Introduce the GUI ↔ worker glue described in ADR-0012. `AppRuntime`
owns the multi-threaded tokio runtime, an unbounded
`tokio::sync::mpsc` for GUI → worker dispatch, and a `std::sync::mpsc`
for worker → GUI events (egui is sync; `try_recv` is the idiom that
fits the immediate-mode frame loop). After every event is posted the
worker invokes a caller-supplied repaint closure so the GUI thread
wakes up to drain the channel.
`AppCommand::{Send, Cancel}` and `AppEvent::{SendStarted,
SendCompleted}` form the wire protocol. The worker keeps an
`Arc<Mutex<HashMap<u64, CancellationToken>>>` of in-flight sends:
`Send` inserts a token and `tokio::spawn`s the work, `Cancel` looks
up by id and calls `.cancel()`. Unknown ids are a debug-logged no-op
so the GUI cannot wedge the worker by racing Cancel against
completion.
Closed channels (worker has shut down, GUI has dropped the receiver)
are logged and dropped — neither side may panic the other. The
runtime is held in a `_runtime` field that drops last, aborting any
in-flight tasks at shutdown.
Tests: four sync-channel `#[test]`s exercising the success
round-trip, a cancel arriving during a `MockResponse::Hang` (asserts
`RequestError::Cancelled` within 500 ms), a cancel for an unknown id
(no events emitted), and two interleaved sends with distinct ids
completing independently. All four use the same channel path the GUI
will, so the harness is exercised exactly as it ships.
https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
Replace the inline `execute_http_request` and the synchronous send
shim with the M4 worker harness. `RequesterApp::new` now constructs
an `Arc<dyn HttpEngine>` (a `ReqwestEngine`), wraps it in
`SendRequest`, and starts an `AppRuntime` whose repaint closure is
`ctx.request_repaint()`. The GUI thread never `.await`s and never
calls `block_on` — every send goes through `AppCommand::Send` and
every completion arrives as an `AppEvent` drained by
`try_recv_event` at the top of each frame.
UI changes (per the M4 spec, intentionally minimal):
- "Send Request" is greyed out via `add_enabled` while a request is
in flight, so a second click cannot dispatch a duplicate id.
- A "Cancel" button appears next to "Send" while a request is in
flight; clicking it dispatches `AppCommand::Cancel { id }` for the
currently-running request.
- A "Sending…" italic label sits next to the buttons during flight,
the egui idiom for a spinner stand-in.
- The response area now stores a `ResponsePane` (an `Ok(HttpResponse)`
/ `Err(String)` pair). On `RequestError::Cancelled` the user sees
"Error: request was cancelled" — the typed-error message from
`RequestError`.
Logging: `tracing_subscriber::fmt::try_init()` replaces the
unconditional `init()` so embedding scenarios that pre-install a
subscriber don't panic.
Tests: the binary's seven `build_domain_request` unit tests are
unchanged in behaviour; `default_state` now also asserts the new
`runtime`/`in_flight`/`next_id` fields. A new
`tests/concurrency_smoke.rs` integration test boots an `AppRuntime`
with `MockResponse::Hang`, dispatches Send/Cancel via the same sync
channel the GUI uses, and asserts a `SendCompleted{Cancelled}` event
within 500 ms (observed latency: ~2 ms locally).
Cargo: `MockHttpEngine` is gated behind `feature = "testing"`. A
self-dev-dependency entry enables that feature only for integration
tests in `tests/`, so the release binary never carries the mock
while `cargo test` keeps working out of the box.
https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
Flip the M4 heading to **[Implemented]** and append the Acceptance Notes paragraph: commit hashes, what shipped (SendRequest, AppRuntime, GUI rewire, Cancel button, integration test), test counts (78 total — 63 lib + 7 bin + 1 + 7 integration), the explicit deferral of `HistoryRecorder` injection to M5, and notes for M5/M6/M7 about the cleanest injection points. https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
WIP snapshot of the M5 agent's domain-layer work, captured to keep
the working tree clean while the agent continues. Adds:
* src/domain/ports.rs — cross-cutting Clock + IdGenerator traits.
* src/domain/history/{entry,query,repository,recorder,retention}.rs
— bounded-context domain types and traits.
* src/domain/http/error.rs — minor extension (likely deriving
Serialize/Deserialize on RequestError per the M5 brief option).
Infrastructure adapters (JsonlHistoryRepository, DataDirectories,
SystemClock, UuidV4Generator) and the SendRequest/GUI rewire will
land in subsequent commits from the agent.
https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
The M5 history agent landed the domain layer (HistoryEntry, query, recorder, repository, retention, RetentionPolicy, plus the cross-cutting Clock / IdGenerator ports) but did not get to the infrastructure adapters (SystemClock, UuidV4Generator, JsonlHistoryRepository, DirectoriesProvider) or the GUI wiring before the session ended. The WIP snapshot in 583d495 left lib.rs re-exporting symbols that don't exist yet, breaking the build. This commit makes the build green again so the branch is a clean checkpoint a future M5 attempt can build on: - Cargo.toml: enable uuid `serde` feature so HistoryEntryId(Uuid) can derive Serialize/Deserialize. - src/lib.rs: drop the dangling `infrastructure::clock` and `infrastructure::persistence::{DataDirectories, DirectoriesProvider, JsonlHistoryRepository}` re-exports. Domain re-exports and the `Clock`/`IdGenerator` ports remain. - src/domain/history/query.rs, retention.rs, repository.rs: minor clippy fixes (struct-init instead of mut-then-reassign, std::io::Error::other). - benches/http_performance.rs: rustfmt drift. - docs/ddd/12-implementation-roadmap.md: mark M5 as [Partial / Deferred] with a clear breakdown of what landed and what remains, so the next attempt has a precise starting point. Validation gates after the wrap-up: - cargo build --lib --bin requester --tests: clean - cargo test --lib --bin requester --tests: 104 passed (89 lib + 7 bin + 1 concurrency_smoke + 7 wiremock) - cargo clippy --lib --bin requester --tests -- -D warnings: clean - rustfmt --check on every src/**/*.rs: clean https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
Implements the production adapters for the `Clock` and `IdGenerator` domain ports landed alongside the M5 history domain layer. Both adapters are zero-sized so they cost nothing to embed in `Arc<…>` shared across the worker and GUI. Test doubles `FakeClock` (mutex-wrapped wall-clock with set/advance) and `SequentialIdGenerator` (atomic counter producing `Uuid::from_u128`) are gated behind `cfg(any(test, feature = "testing"))` so they're available to internal `#[cfg(test)]` modules and to the self-dev-dependency integration crates without leaking into release binaries. `src/lib.rs` re-exports `SystemClock` and `UuidV4Generator` at the crate root so the GUI bootstrap site can `use requester::SystemClock` without reaching into module internals. https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
Introduces the OS data-directory port that every persistence adapter will share starting with M5's `JsonlHistoryRepository`. The production adapter resolves the path via the `directories` crate (qualifier `dev`, organization `requester`, application `requester`), matching XDG / Application Support / %APPDATA% conventions per ADR-0009. `DirectoriesProvider::at` is an explicit-path escape hatch for tests and local overrides, and `InMemoryDataDirectories` (gated behind `cfg(any(test, feature = "testing"))`) is the test-only double that wraps a `tempfile::TempDir`. `directories = "5"` is the only new runtime dependency. The trait itself is `Send + Sync` so it can flow through `Arc<dyn …>` across worker tasks without bound churn. https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
Implements `HistoryRepository` against append-only JSONL shards rooted
under `<data_dir>/history/`:
- One file per UTC date (`YYYY-MM-DD.jsonl`) keyed off `entry.sent_at`.
- A separate `tombstones.jsonl` of deleted ids; `list`/`get` skip
tombstoned entries without rewriting the original shard line, which
preserves the append-only invariant.
- An in-memory `HashMap<HistoryEntryId, EntryLocator { date, line }>`
rebuilt at `open(...)` by streaming every shard once. `get` is then
O(line-seek-in-shard); `list` walks shards newest-first, applies
`HistoryQuery::matches`, and stops at `effective_limit`.
All file IO runs inside `tokio::task::spawn_blocking` so no syscall
ever blocks a tokio worker thread; a single `tokio::sync::Mutex`
serialises every mutating step (and is briefly held by reads to clone
the shard-list / tombstones snapshot before they release the lock and
dive into shard files).
Retention pruning is intentionally manual in M5 — the auto-prune
scheduler waits for M8's domain-events flow.
Five `tempfile`-backed unit tests cover the core contract: round-trip
append/list/get/delete; `delete(unknown)` → `NotFound`; `limit`
honoured; `get(unknown)` → `None`; entries spanning two UTC dates
land in two shard files.
https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
`SendRequest::execute` now invokes the injected `HistoryService` after the engine returns, so every send (success or failure) produces exactly one persisted entry. The user-visible result mirrors the engine's result; persistence failures are logged at WARN and swallowed so an on-disk hiccup never derails a send. The new constructor signature is `SendRequest::new(engine, history)`. A `without_history(engine)` helper wires `NoopHistoryService` for tests that don't exercise persistence; existing call sites in `app::runtime` and `tests::concurrency_smoke` migrate to that helper without semantic change. `AppRuntime` gains: - `AppCommand::ListHistory(HistoryQuery)` and `AppCommand::Recall(id)` for the new History panel. - `AppEvent::HistoryListed(Vec<HistoryEntrySummary>)` and `AppEvent::Recalled(Box<Option<HistoryEntry>>)` (boxed to keep the enum size bounded). - `AppRuntime::spawn_with_history(send, repo, repaint)` which carries an `Arc<dyn HistoryRepository>` for the worker to fan out reads on. The original `spawn(send, repaint)` still works; without a repo `ListHistory` / `Recall` are debug-logged no-ops so the GUI keeps rendering even if persistence is unavailable. `RequesterApp::new` constructs the full chain — `DirectoriesProvider` + `JsonlHistoryRepository` + `SystemClock` + `UuidV4Generator` + `HistoryRecorder` + `SendRequest` — and falls back to `NoopHistoryService` (logged) if the OS data dir is unavailable. The GUI panel that consumes the new events lands in the next commit. https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
Introduces `src/ui/history_panel.rs` — a presentational right-side `SidePanel` that renders `HistoryEntrySummary` rows (method tag, status colour-coded by class, duration in ms, truncated URL) and emits a `HistoryPanelAction` per frame. The host translates that intent into `AppCommand::ListHistory` / `AppCommand::Recall` so the panel itself stays free of `AppRuntime` knowledge. `RequesterApp` gains: - `history_summaries: Vec<HistoryEntrySummary>` and a `persistence_enabled` flag. - A `populate_from_recalled` helper that overwrites the request fields (method, URL, headers, body) from the recalled `HistoryEntry`. - `drain_worker_events` now buffers events into a local `Vec` before matching, then auto-issues a `ListHistory(most_recent(50))` after every `SendCompleted` so the panel reflects fresh entries without the user clicking Refresh. - The constructor primes the panel with the on-disk contents immediately after opening the JSONL repository. Two new unit tests cover the panel's pure helpers (`method_label_covers_every_method`, `status_color_thresholds`) plus a `populate_from_recalled` test that asserts URL / method / body / headers all get rewritten. https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
Two new integration test files exercise the full M5 surface:
- `tests/history_persistence.rs` (5 tests) — drives
`JsonlHistoryRepository` against a `tempfile::TempDir`:
* `entries_survive_a_restart` — drop the repo, reopen, list still
returns all 5 entries.
* `entries_spanning_two_dates_produce_two_shards` — confirms the
sharding key is `entry.sent_at.date_naive()` (UTC).
* `fake_clock_drives_shard_split` — `FakeClock::advance` across
midnight produces two on-disk shards.
* `concurrent_appends_yield_distinct_ids_and_no_torn_lines` —
8 `tokio::spawn`'d recorders ⇒ 8 distinct ids on disk.
* `tombstones_survive_restart` — `delete` then reopen ⇒ entry
stays gone.
- `tests/send_request_records_history.rs` (3 tests) — wires
`SendRequest` to `MockHttpEngine` + a real `JsonlHistoryRepository`
and asserts success / network failure / cancellation each produce
exactly one persisted entry with the expected `HistoryOutcome`.
`src/lib.rs` re-exports the test-only doubles
(`FakeClock`, `SequentialIdGenerator`, `InMemoryDataDirectories`)
behind `cfg(any(test, feature = "testing"))` so the integration
tests can reach them via `requester::*` rather than hand-spelling
deep paths.
https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
Flips the M5 heading from `[Partial / Deferred]` to `[Implemented]` and replaces the deferred-state notes with a fresh `Acceptance Notes — M5` section listing commit hashes, what shipped (infrastructure adapters, wiring into `SendRequest` / `AppRuntime`, GUI history panel + Recall flow, integration tests), the on-disk JSONL layout with a sample shard line, the final 135-test count breakdown, and the deferrals M6 / M7 / M8 should inherit (notably: `Arc<dyn DataDirectories>` is the sharing primitive every future persistence adapter should accept). https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
Captures the early M6 agent output (Settings repository trait + Theme value object) to keep the working tree clean while the agent continues. The aggregate root, change enum, infrastructure adapter, use case, and GUI panel are still being written. https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
Land the singleton Settings aggregate, its SettingsVersion tag,
the Theme + HistoryRetention value objects, the SettingsChange
command, and the SettingsRepository / SettingsError persistence
port. The aggregate enforces its own invariants (timeout in
1..=600_000 ms); every editable field round-trips through serde
so a future JSON adapter can persist it as one atomic file.
Surface the new types at the crate root so the application
layer and the GUI can `use requester::{Settings, SettingsChange,
SettingsRepository, ...}` without reaching into module internals.
https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
UpdateSettings is the only path the GUI uses to mutate Settings. Holds an Arc<dyn SettingsRepository> + Arc<RwLock<Settings>> cache: each execute call clones the cache, applies the SettingsChange (invariant check), persists via the repository, and only then swaps the new value back into the cache. Errors leave the cache untouched so the GUI never observes a state that disagrees with disk. shared_cache() exposes the underlying Arc<RwLock<Settings>> so future use cases (e.g. SendRequest folding in default headers) can read the freshest snapshot without going through the service. https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
Single-file JSON adapter for SettingsRepository, storing the aggregate at <data_dir>/settings.json. Writes go through NamedTempFile + persist() so a half-written file is never observable; all I/O runs inside tokio::task::spawn_blocking and a small write-side Mutex serialises concurrent save calls. The load path applies a static migration chain (empty in M6, since current version is 1) and writes the migrated form back to disk after a successful version bump, so a crash mid-migration cannot replay the chain forever. A future on-disk version (file newer than build) surfaces as SettingsError::Migration rather than silently downgrading. Promote tempfile from dev-deps to a normal dependency now that the runtime adapter consumes NamedTempFile. https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
SendRequest::with_settings(engine, history, Arc<RwLock<Settings>>) is the new constructor; the previous SendRequest::new and without_history paths stay untouched. execute() now snapshots the cache once at the top, folds in every default header whose name does not already appear on the request (request wins on a case-insensitive name match), and wraps the engine call in tokio::time::timeout(settings.default_timeout(), …). An elapsed timeout maps to RequestError::Timeout. The engine still owns connect/read timeouts at the reqwest layer; this is the wall-clock cap above that. Three new unit tests cover header folding, request-overrides- default precedence, and the 50 ms timeout against MockResponse::Hang completing inside a 500 ms budget. https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
Flip the M6 heading to [Implemented] and append an Acceptance Notes section listing the six implementation commits, what each shipped, the per-target validation outcomes, the final test counts (193 total), and a sample default settings.json payload. Document the M7 handoff notes: template-rendered request headers should beat Settings::default_headers (matching the ad-hoc-GUI precedence), UpdateSettings::shared_cache() is the supported sharing primitive, and the collection adapter should consume the same Arc<dyn DataDirectories>. https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
Introduces the Secret Vault bounded context: SecretRef (opaque UUID handle, transparent serde), SecretValue (zeroizing wrapper with no Serialize/Display impls, constant-time PartialEq), and the SecretVault async trait + SecretError taxonomy. Promotes zeroize to a direct dependency and pulls in keyring for the upcoming adapter. https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
KeyringSecretVault wraps the keyring crate behind tokio::spawn_blocking since every call is synchronous and may prompt the user. Maps keyring errors onto the SecretError taxonomy. Entry names are "requester:<uuid>" under the service "com.requester.app". InMemorySecretVault is the test double everything in tests/ uses so cargo test never touches the OS keychain. Feature-gated behind testing. https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
Introduces the Collections bounded context domain layer: VariableName (C-identifier grammar) + VariableValue (Literal / FromEnv / FromSecret), AuthCredential (None / Bearer / ApiKey / Basic — carries SecretRef only, plaintext never), RequestTemplate + TemplateName + TemplateId, the Collection aggregate root with rename / add-template / remove-template / rename-template / set-variable / unset-variable methods that bump updated_at, and CollectionName with case-insensitive equality. Adds base64 as a direct dep so AuthCredential::Basic can be rendered. https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
Stores one JSON file per collection under <data_dir>/collections/<uuid>.json plus an index.json carrying ordered CollectionSummary entries. Writes go through tempfile::NamedTempFile::persist for atomic rename, with a single tokio::sync::Mutex serialising the index rewrite. Save enforces case-insensitive name uniqueness; delete is idempotent and rewrites the index before unlinking the file. https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
SaveTemplate takes plaintext credentials at the boundary (AuthSpec), writes each to the SecretVault to mint a SecretRef, swaps in the refs to build an AuthCredential, then loads/mutates/saves the parent Collection. Rolls back any vault writes if the collection save fails. RunTemplate resolves a saved template through SimpleRenderer (with SecretVault for FromSecret + AuthCredential lookups), then dispatches through SendRequest so history records the run alongside ad-hoc sends. Manage Collection family covers CreateCollection, RenameCollection, DeleteCollection (with cascade-delete-secrets opt-in), DeleteTemplate, SetVariable, UnsetVariable. AppRuntime gains spawn_full plus AppCommand/AppEvent variants for every collections operation. RunTemplate reuses SendCompleted so one event listener handles both ad-hoc and template sends. https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
Wires the M7 collections + secret-vault stack into the GUI: - src/ui/collections_panel.rs: left-side sidebar listing collections with per-row Run / Edit / Delete buttons. Mutually exclusive with the Settings panel (both want the left edge). FromSecret variables render as "***REDACTED*** (linked)" — plaintext is never displayed. - src/ui/template_editor.rs: inline editor for the selected template. Plaintext credential input uses TextEdit::password(true); on Save the buffer is moved into a SecretValue (consumed) and the draft is cleared so plaintext does not linger across frames. - src/main.rs: bootstraps JsonCollectionRepository + KeyringSecretVault + the manage_collections use cases at startup, hands them to the new AppRuntime::spawn_full constructor, and surfaces CollectionsListed / CollectionSaved / CollectionDeleted / TemplateSaved / TemplateDeleted events into the sidebar cache. Run-template completions reuse the existing SendCompleted plumbing. https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
tests/collections_persistence.rs covers JsonCollectionRepository round-trip (save/list/get/delete), case-insensitive name uniqueness, reopen-restores-index, and — the load-bearing assertion — that SaveTemplate + a Bearer credential never leaks the plaintext token into any on-disk JSON file (collection or index). A separate test also covers a manually-built bearer credential. tests/secret_vault.rs covers InMemorySecretVault round-trip + unknown ref + delete. The KeyringSecretVault round-trip is #[ignore]d behind RUSTREQUESTER_RUN_KEYRING_TESTS so CI sandboxes without a Secret Service / Keychain still pass. https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
tests/template_run_records_history.rs wires every M7 piece together against a TempDir: JsonCollectionRepository, InMemorySecretVault, a real JsonlHistoryRepository, and a MockHttpEngine. The test: - Saves a Bearer-secured template through SaveTemplate. - Asserts the on-disk collection JSON does not contain the plaintext. - Runs the template through RunTemplate. - Verifies the engine saw Authorization: Bearer <token>. - Verifies the history shard recorded HistoryOutcome::Success. - Documents (and asserts) that the history shard *does* carry the rendered plaintext because history is intentionally a complete record of what was sent — the redaction guarantee is about the collection file, not history. Also folds in rustfmt + clippy fixes across the M7 modules: derives Default on AuthCredential / AuthKind, drops a redundant local in JsonCollectionRepository::save, allows the intentionally-large TemplateEditorAction enum variant, and trims an unnecessary mut on a renderer call. https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
Flips the M7 heading to [Implemented] and appends the M7 Acceptance Notes section: commit hashes, what shipped per layer, test counts (279 total, 1 ignored keyring), validation-gate outcomes, a sample collection JSON snippet showing the SecretRef where a token used to live, the redaction-proof grep one-liner, and notes for M8 (the new collection events are shaped to feed DomainEvent subscribers) and M9 (keyring platform footprint, performance and a11y follow-ups). https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
Introduce the M8 event surface as a typed, in-process signal: every state transition the domain catalogue lists (history, collections, templates, settings, secrets, retention) gains a `DomainEvent` variant plus an `OutcomeClass` enum for `RequestSent`. The publisher port is async and object-safe so use cases can hold an `Arc<dyn EventPublisher>`. Pair with a `RedactionPolicy` trait + `DefaultRedactionPolicy` so publishers expose header *names* only; values are dropped at the boundary. The default policy strips Authorization, Cookie, Set-Cookie, Proxy-Authorization, X-Api-Key, and any name ending in -token / -secret. `DomainEvent` is deliberately not `Serialize`/`Deserialize` (transient in-process signal, not a persisted log) and does not derive `PartialEq` (the `SettingsChanged` payload would force a deep compare on every assertion). https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
Introduce the two default adapters behind the `EventPublisher` port: * `BroadcastEventPublisher` wraps `tokio::sync::broadcast` so any number of subscribers can independently receive every event. Lagged subscribers get `RecvError::Lagged` and skip; we tolerate that drop and log a single `warn!` per skip on the receiver side. `publish` silently absorbs the "no subscribers" send error so a use case never blocks on slow consumers. * `NoopEventPublisher` is the zero-cost default for use cases that don't care about emission (and the default for unit tests). * `CapturingEventPublisher` (under `cfg(any(test, feature = "testing"))`) records every published event so use-case unit tests can assert "after `execute`, exactly one `X` event landed". Scaffold `RetentionScheduler` alongside so the rest of the M8 wiring can reference it; the implementation is fleshed out in a follow-up commit. The handle owns a `CancellationToken` that fires on `Drop` for clean GUI shutdown. Surface the new types through `lib.rs` re-exports so the GUI bootstrap and integration tests can reach them via `use requester::…` without spelunking module internals. https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
Every M5–M7 use case now takes an optional `Arc<dyn EventPublisher>`
(builder-style `with_publisher`; defaults to `NoopEventPublisher` so
existing call sites compile untouched) and publishes the corresponding
`DomainEvent` *after* its repository write returns `Ok`. The mapping
matches the doc-10 catalogue:
* `SendRequest::execute` — `RequestSent` (header *names* only via the
configured `RedactionPolicy`) plus `HistoryEntryRecorded` once the
recorder confirms persistence. Outcome class is mapped from the
engine result through a small `classify_outcome` helper.
* `CreateCollection`, `RenameCollection`, `SetVariable` — `CollectionSaved`.
* `UnsetVariable` — `SecretRevoked` (if the binding was `FromSecret`
and the vault delete succeeded) then `CollectionSaved`.
* `DeleteCollection` — one `SecretRevoked` per successfully-revoked
vault entry, then `CollectionDeleted`.
* `DeleteTemplate` — `SecretRevoked` (if applicable) then
`TemplateDeleted`.
* `SaveTemplate` — `SecretRotated` per freshly-written secret, then
`TemplateSaved`, then `CollectionSaved` (the parent's `updated_at`
changed). Rollback paths emit nothing — the canary test asserts it.
* `UpdateSettings::execute` — `SettingsChanged { snapshot }`.
The `UpdateSettings` cache write-guard is now scoped tightly so it
cannot live across the `publish().await` — that previously made the
returned future non-`Send`.
Unit tests added against `CapturingEventPublisher` for every use case
above, plus a `RequestSent` redaction test that scans the published
event for an Authorization-canary plaintext and confirms it's absent.
https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
The egui render loop is sync and polls a `std::sync::mpsc` once per frame; the M8 domain-events surface is async and runs on `tokio::sync::broadcast`. The bridge sits between them. * Adds `AppEvent::Domain(Box<DomainEvent>)` — strictly additive, no existing variant changes shape. * Adds `crate::ui::event_bridge::EventBridge`: a `tokio::spawn`ed task that subscribes to the publisher and forwards every event onto the GUI sync channel. Owns a `CancellationToken` so GUI shutdown can stop it cleanly (also triggered via `Drop`). * Lagged subscribers log a single `warn!` and skip; channel closure exits the loop. `AppRuntime` now exposes `tokio_handle()`, `event_sender()` and `repaint_hook()` so external subscribers (the bridge and the retention scheduler) can plug into the same executor and wake egui in the same way the worker does. `RequesterApp::new` builds a `BroadcastEventPublisher`, hands it to every use case via `with_publisher(...)`, then spawns the bridge and a `RetentionScheduler` against the same handle. The GUI continues to react to the worker-emitted `AppEvent`s for command-result payloads (`SendCompleted` carries the `HttpResponse`); the bridge surfaces domain events to the GUI for cross-cutting reactions (re-list history on `HistoryEntryRecorded`, log `RetentionPurged`). https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
Land the M8 retention scheduler that subscribes to the broadcast
publisher and runs `RetentionPolicy::purge` on a debounced timer.
* `RetentionScheduler::spawn` returns a handle whose `CancellationToken`
fires on `Drop` so GUI shutdown cancels the task cleanly.
* The task `select!`s over the broadcast subscriber, a
`tokio::time::Sleep` reset on every `HistoryEntryRecorded`, and the
cancel token. The actual purge runs in a detached `tokio::spawn` so
the subscriber loop keeps consuming events; we serialise scheduling
but not execution.
* `HistoryRetention` maps onto `DefaultRetentionPolicy`:
* `Forever` → no-op policy
* `Off` → zero-second keep window (everything purged)
* `Days{n}` → `keep_for = days(n)`
* Successful purges with `removed > 0` publish
`DomainEvent::RetentionPurged { removed, older_than, at }`.
* `RequesterApp::new` spawns one scheduler against the same publisher
and tokio handle as the event bridge.
Re-format previously-edited files per rustfmt 2021.
https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
Flip the M8 heading from [Planned] to [Implemented] and append the Acceptance Notes section listing commit hashes, what shipped, test counts (320 passing, 1 ignored), the per-use-case event-emission table, the redaction-canary attestation, and the logging-cadence guidance for the M9 polish pass. https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
Add `#[tracing::instrument(skip_all, fields(...))]` spans to every public async use-case entry point and every repository/engine adapter trait method, with non-sensitive fields only (ids, method, url, plus the new `SettingsChange::kind()` classifier). Each use case now emits exactly one `tracing::info!` on success and `tracing::warn!` on every distinct failure branch. The event bus and its subscribers remain silent per-event (already debug/trace), so there is no double-logging between use-case info logs and the in-process DomainEvent fan-out. `src/main.rs` now installs `tracing_subscriber::EnvFilter` keyed on `RUSTREQUESTER_LOG` (matches ADR-0010), defaulting to `info,requester=debug` so an unconfigured release build is quiet for foreign crates and chatty for our own modules. https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
Capture median timings for every benchmark group in http_performance.rs and ui_performance.rs as the regression baseline referenced in ADR-0013. Measurement was taken with `cargo bench ... -- --quick --noplot` on a single-host VM (rustc 1.94.1, Linux 6.18.5); the env / methodology lines are committed alongside the numbers so a future regression can be evaluated against the same shape of run. CI continues to compile benches only (`cargo bench --no-run`); full runs stay manual. https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
Per ADR-0013, the release profile sets `strip = true` so the shipped binary contains no symbols. To keep panics from a released build symbolicable, each platform job now produces a sibling debug-info bundle alongside the stripped binary: - Linux: `objcopy --only-keep-debug` produces `requester.debug`, then strip + `--add-gnu-debuglink` so gdb/addr2line can find it. - macOS: `dsymutil` produces `requester.dSYM` (a bundle directory) before the strip step; uploaded tar-gzipped. - Windows: cargo already emits a `.pdb` alongside the binary; the workflow now uploads it. The release draft body documents which artefact to pair with which debugger so a downstream user knows how to feed a panic into addr2line / lldb / WinDbg. https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
The pre-M9 README described a hypothetical pre-M3 architecture with `HttpClient.ts`, `RequesterApp.ts`, async-response placeholders, and no history/collections/settings — none of which exist any more. The rewrite covers the actual state of the binary as of v0.1.0-alpha: - Tech stack accurate to current `Cargo.toml`. - Feature list scoped to what `src/` actually ships (cancellation, history, collections + templates + keychain-backed credentials, settings, domain events with redaction). - Honest "what doesn't work yet" section (no import/export, no proxy, no streaming, no WS/SSE/gRPC, no multi-window). - Quick-start: `cargo run --release` for the GUI; `RUSTREQUESTER_RUN_KEYRING_TESTS=1` opts the ignored keyring test into a run on a desktop with a real Secret Service / KWallet / Keychain / Credential Manager. - Architecture pointer to docs/README.md, docs/adr/, docs/ddd/ rather than re-describing each layer. - Pointer to benches/BASELINE.md for the M9 perf baseline. The new README is ~110 lines vs. the previous ~765. https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
- Delete FINAL_EVIDENCE_REPORT.md and qa_pipeline_report.md. These predate this branch's work and the claims inside (e.g. "84.8% SWE-Bench solve rate") have no bearing on this codebase. - Delete src/http_types.rs (the M2 backwards-compat shim that just re-exported domain::http types) and drop the `pub use http_types::*` re-export from src/lib.rs. The crate root re-export of HttpMethod / HttpRequest / HttpResponse now comes directly from `domain::http`. Verified no compile-time references remain via grep. - Rewrite CLAUDE.md from scratch. The previous version described a hypothetical Node/React/PostgreSQL CLI that doesn't exist. The new version is ~75 lines covering: what Requester actually is, the four-layer architecture, bounded-context module paths, the validation gates, the hard rules (no force-push, no plaintext secrets on disk, no logging header values, no reqwest outside infrastructure/http/), and pointers to the roadmap and ADR-0010. https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
The persistence strategy (ADR-0009), the bounded-context module layout (ADR-0014), and the configuration / secrets handling (ADR-0015) all describe choices that have been implemented and exercised by 320+ tests across M5–M7. Their statuses lagged the code; this commit flips them to Accepted and updates the index table in docs/adr/README.md to match. https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
The CI workflow's clippy step still runs `--all-targets` (preferred: the bench-compile gate catches domain-type drift before it reaches a release). The new bench step is scoped to the two named benches so that adding a stray `*.rs` under `benches/` doesn't silently start running on every PR — they must be opted in by name. A short comment in the workflow records the intent so a future reader knows why these two steps look slightly different in scope. https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
Inventory every interactive widget Requester ships and document its keyboard story in docs/accessibility.md, including the known gaps that v0.1.0-alpha deliberately does not solve (no screen-reader output via egui, no persistent focus ring, no skip-links, no AT-SPI integration). The doc names the widgets that would either need a non-egui re-implementation or upstream egui/accesskit work to fully address. Close the easy paper-cuts: - `src/ui/collections_panel.rs`: add `on_hover_text` to the create- collection `+`, the per-row expand arrow (state-aware label), the `+ New template`, the per-variable `x`, and the `set` buttons. - `src/ui/settings_panel.rs`: add `on_hover_text` to the per-header `x` and the `Add` defaults button. - `src/ui/template_editor.rs`: add `on_hover_text` to the per-row header `x`. - `src/main.rs`: add `on_hover_text` to the per-header `x` and the Cancel button. No widget was refactored; this commit only closes obvious labelling gaps. Tests stay at 320 passing. https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
`cargo fmt --all` produces slightly different output than the per-file `rustfmt --edition 2021` invocation the contributor gates use; this commit reconciles the workspace so both gates pass. Also fix a pre-existing `clippy::identity_op` lint in benches/ui_performance.rs (`1 * 1024` → `1024`) so `cargo clippy --all-targets -- -D warnings` is green workspace-wide. No behaviour change. https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
The previous commit picked up a handful of `*.profraw` coverage files that cargo's coverage instrumentation scatters at the workspace root. They are large (>10 MB combined) and have no value in version control. Remove them and extend `.gitignore` so a future coverage run doesn't repeat the slip. https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
Flip the M9 heading to **[Implemented]** and append a full acceptance notes section listing commit hashes, what shipped per polish item (1–7), ADR statuses flipped (0009 / 0014 / 0015), test counts (320 passing, 1 ignored — unchanged vs. M8, M9 is polish), validation-gate outcomes, and a pointer to the benches/BASELINE.md regression baseline. This closes the last functional milestone before the `v0.1.0-alpha` tag. https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
…lpha - docs/VALIDATION_REPORT.md: full command-by-command record of all five validation gates (fmt, clippy, build, 320-test suite, bench compile) with exact inputs and outputs captured at release time. - docs/USE_CASE_GUIDE.md: user-facing guide explaining who Requester is for, what each feature does, comparison table vs Postman/Bruno, data locations, and quick-start commands. - README.md: pin test count to 320 (not "320+"), remove reference to non-existent rust-toolchain.toml, add links to both new docs. https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
The validation report proved the build/test gates; this adds proof that every user-facing use case actually works, with captured input/output. - examples/acceptance_demo.rs: runnable end-to-end demo of all six use cases (send request via real reqwest engine against a localhost stub, history persist/query/delete, settings round-trip, secret redaction on disk, template rendering with vault resolution, vault put/get/delete). Formatted and clippy-clean. - docs/ACCEPTANCE_REPORT.md: verbatim captured output of the demo, the observed OS-keychain behaviour (and why it is gated), proof the benches measure (not just compile), and a coverage matrix mapping each use case to its automated tests. - README.md: link the acceptance report and the runnable demo command. Reproduce: cargo run --features testing --example acceptance_demo https://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU
|
| GitGuardian id | GitGuardian status | Secret | Commit | Filename | |
|---|---|---|---|---|---|
| 32700439 | Triggered | Bearer Token | fb20b17 | benches/http_performance.rs | View secret |
| 32750164 | Triggered | Generic Password | ecbe53c | src/domain/collections/renderer.rs | View secret |
🛠 Guidelines to remediate hardcoded secrets
- Understand the implications of revoking this secret by investigating where it is used in your code.
- Replace and store your secrets safely. Learn here the best practices.
- Revoke and rotate these secrets.
- If possible, rewrite git history. Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.
To avoid such incidents in the future consider
- following these best practices for managing and storing secrets including API keys and other credentials
- install secret detection on pre-commit to catch secret before it leaves your machine and ease remediation.
🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.
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
This PR implements a complete Domain-Driven Design (DDD) architecture for Requester, restructuring the codebase from a mixed UI/HTTP prototype into a layered, bounded-context-aligned system. The change includes comprehensive domain modeling, application services (use cases), infrastructure adapters, and supporting documentation.
Key Changes
Architecture & Documentation
docs/ddd/01-12): Complete strategic and tactical design covering ubiquitous language, bounded contexts, aggregates, entities, value objects, domain services, application services, repositories, domain events, and anti-corruption layersdocs/adr/0001-0015): 15 ADRs documenting technology choices (Rust, egui, reqwest, tokio, serde) and architectural patterns (layered architecture, TDD, concurrency model, storage strategy)docs/ddd/12-implementation-roadmap.md): Sequenced plan mapping target model to current code across 9 milestones (M0–M9)VALIDATION_REPORT.mdandACCEPTANCE_REPORT.mddocumenting build gates, test results, and use case demonstrationsDomain Layer (
src/domain/)Collectionaggregate root,RequestTemplateentity,VariableName/VariableValuevalue objects,AuthCredential,CollectionRepositoryport,TemplateRendererdomain serviceHistoryEntryaggregate,HistoryEntryId,HistoryQueryfilter,HistoryRepositoryport,HistoryRecorderserviceHttpRequest,HttpResponse,HttpMethod,HttpStatus,HeaderMap,Body,HttpEngineport,HttpErrortypes,RedactionserviceSecretRef,SecretValue,SecretVaultportSettingsaggregate,SettingsChangevalue object,SettingsRepositoryport,ThemeenumClocktrait for time injection,EventPublishertrait for domain eventsDomainEventenum covering collection mutations, template saves, history recording, and settings changesApplication Layer (
src/app/)SendRequest,RunTemplate,SaveTemplate,CreateCollection,RenameCollection,DeleteCollection,DeleteTemplate,SetVariable,UnsetVariable,UpdateSettingsruntime.rs): Owns tokio runtime, dispatch channels (GUI→worker async_mpsc, worker→GUI sync_mpsc), and in-flight cancellation token mapevent_bus.rs): In-process event bus adapters wrapping tokio::sync::broadcastretention_scheduler.rs): Debounced subscriber applying retention policies to historyClockdependencyInfrastructure Layer (
src/infrastructure/)ReqwestEngine(reqwest-based HTTP client),MockEngine(testing double), conversion adaptersJsonCollectionsRepository,JsonlHistoryRepository,JsonSettingsRepository,DataDirconfigurationKeyringVault(OS keyring integration),InMemoryVault(testing double)UI Layer (
src/ui/)collections_panel.rs,history_panel.rs,settings_panel.rs,template_editor.rsevent_bridge.rs): Glues GUI to application layer viaAppCommanddispatchTesting & Examples
collections_persistence.rs,history_persistence.rs,settings_persistence.rs,http_engine_wiremock.rs,event_bus_integration.rs,send_request_records_history.rs, `sendhttps://claude.ai/code/session_01RS6AV6MgSQeUF7Vux2kYuU