From 4da4b3b7bae269d391344b79e028add9c01952d6 Mon Sep 17 00:00:00 2001 From: leynos Date: Thu, 21 May 2026 00:59:51 +0200 Subject: [PATCH 1/7] Draft startup composition hardening plan Replace the existing backend 3.5.5 execution-plan draft with an approval-gated plan focused on deterministic user-state startup-mode composition. Record the helper seams, behavioural regression scope, documentation requirements, validation gates, Wyvern reconnaissance, and Firecrawl research that should guide the later implementation pass. --- ...d-3-5-5-harden-startup-mode-composition.md | 1167 +++++++---------- 1 file changed, 496 insertions(+), 671 deletions(-) diff --git a/docs/execplans/backend-3-5-5-harden-startup-mode-composition.md b/docs/execplans/backend-3-5-5-harden-startup-mode-composition.md index 8128cfab..5fe75537 100644 --- a/docs/execplans/backend-3-5-5-harden-startup-mode-composition.md +++ b/docs/execplans/backend-3-5-5-harden-startup-mode-composition.md @@ -1,768 +1,593 @@ -# Harden startup-mode composition in state\_builders.rs (roadmap 3.5.5) +# Harden startup-mode composition for user-state ports (roadmap 3.5.5) This ExecPlan (execution plan) is a living document. The sections -`Constraints`, `Tolerances`, `Risks`, `Progress`, `Surprises & -Discoveries`, `Decision Log`, and `Outcomes & Retrospective` must be kept up -to date as work proceeds. +`Constraints`, `Tolerances`, `Risks`, `Progress`, `Surprises & Discoveries`, +`Decision Log`, and `Outcomes & Retrospective` must be kept up to date as work +proceeds. -Status: DRAFT +Status: DRAFT (awaiting approval before implementation) -This plan covers roadmap item 3.5.5 only: -`Harden backend/src/server/state_builders.rs startup-mode composition with -explicit helper seams and regression assertions so DB-present versus -fixture-fallback adapter selection remains deterministic as user-state -wiring evolves.` +This plan covers roadmap item 3.5.5 only. It must be explicitly approved before +any implementation work begins. ## Purpose / big picture -`backend/src/server/state_builders.rs` is the composition root for all -HTTP-facing domain ports. It inspects `ServerConfig.db_pool` (an -`Option`) and, for each port pair, branches between a DB-backed -adapter and a fixture fallback. This branching logic is the single most -important wiring decision the backend makes at startup: every port the -HTTP layer consumes is resolved here, and a mistake means production -traffic silently hits fixture stubs or, conversely, test harnesses hit -real persistence. - -Today, the module has grown through successive roadmap items (3.5.2, 3.5.3, -3.5.4) and now contains several builder patterns: a generic -`build_service_pair` helper, a `build_idempotent_pair!` macro, and -standalone match-based builders for login/users, profile/interests, -catalogue, offline bundles, walk sessions, and enrichment provenance. The -builders follow the same intent but use inconsistent mechanisms, and the -final coverage split therefore keeps fixture-mode composition checks in -`backend/tests/state_builders_composition_unit.rs` while the DB-present -matrix is exercised at the HTTP boundary by -`backend/tests/startup_mode_composition_bdd.rs`. - -Existing integration and behaviour-driven development (BDD) tests exercise -state composition indirectly -through HTTP-level flows. They confirm that the wired ports produce correct -responses, but they do not assert the composition decision itself. If a -future wiring change accidentally swaps a DB port for a fixture or vice -versa, the HTTP-level tests might still pass (fixtures return plausible -data) and the error would only surface in production. - -After this change: - -- `state_builders.rs` exposes well-defined internal helper seams that each - resolve a single port pair, making the branching decision testable in - isolation. -- `backend/tests/state_builders_composition_unit.rs` contains fixture-mode - regression assertions that prove every port in `HttpStatePorts` and - `HttpStateExtraPorts` is wired to the expected fallback adapter when no - DB pool is configured. -- A new BDD suite exercises the full startup-mode composition matrix at the - HTTP boundary with embedded PostgreSQL, covering happy, unhappy, and edge - paths for the complete port set. -- The `build_service_pair`, `build_idempotent_pair!`, and standalone builder - functions are refactored (where beneficial) to share a consistent internal - seam pattern that future port additions can follow without diverging. - -Observable success criteria: - -- Running `cargo test --test state_builders_composition_unit` exercises the - fixture-mode composition checks for all 16 ports. -- Running `make test` exercises the new BDD suite at the HTTP level using - embedded PostgreSQL, covering: fixture-fallback happy path, DB-present - happy path, DB-present schema-loss unhappy path, and at least one edge - case proving validation contracts remain stable across startup modes. -- `docs/wildside-backend-architecture.md` records the design decision - for the state-builder hardening approach. -- `docs/backend-roadmap.md` marks only 3.5.5 as done after all gates pass. -- `make check-fmt`, `make lint`, and `make test` succeed, with log files - retained. +Roadmap item 3.5.5 requires hardening +`backend/src/server/state_builders.rs`, the backend composition root that +chooses concrete HTTP-facing port implementations during startup. The current +selection rule is simple: when `ServerConfig.db_pool` is present, HTTP state +should use DB-backed adapters; when it is absent, HTTP state should use fixture +fallbacks. As more user-state wiring is added, that rule must remain +deterministic and visible in tests. + +After the implementation, a developer can inspect one explicit user-state +composition helper in `backend/src/server/state_builders.rs` and see how +login, users, profile, interests, preferences, and route-annotation ports are +selected. Running the startup-mode unit and behavioural suites will prove that +DB-present mode does not silently fall back to fixtures, and fixture-fallback +mode still preserves the existing local contracts. + +Observable success means: + +- `backend/src/server/state_builders.rs` has explicit helper seams for + user-state composition rather than separate ad hoc port selection calls. +- Existing public HTTP state construction remains source-compatible. +- DB-present startup mode uses DB-backed user-state adapters for every + user-state port included in this item. +- Fixture-fallback startup mode uses deterministic fixture implementations + when no DB pool is configured. +- Regression assertions fail before or during the helper-seam work if a + DB-present path accidentally serves fixture identity data. +- `rstest` unit tests and `rstest-bdd` behavioural tests cover happy paths, + unhappy DB-schema-loss paths, and mode-stability edge cases. +- `make check-fmt`, `make lint`, and `make test` pass with captured `/tmp` + logs before any implementation commit is considered complete. ## Constraints -- Scope is roadmap item 3.5.5 only. Do not implement roadmap items 3.5.6 - or any other unrelated work in this change. -- Preserve hexagonal boundaries: - - domain owns port traits and domain errors; - - outbound owns Diesel SQL and row mapping; - - inbound handlers consume ports only; - - `state_builders.rs` is a composition root sitting in the server module, - which is allowed to know both domain ports and outbound adapters. -- Preserve fixture fallback when `config.db_pool` is `None`. The fixture - path must remain fully functional for test harnesses. -- Keep endpoint contracts stable for every existing HTTP endpoint. This - change is about composition hardening, not endpoint behaviour changes. -- Do not add new migrations. -- Do not add new external dependencies. -- Do not change public HTTP API signatures. -- Use `rstest` for unit and integration coverage and `rstest-bdd` for - behavioural coverage. -- Use `pg-embedded-setup-unpriv` for DB-backed local tests. -- Keep Markdown style consistent with repository docs standards - (en-GB-oxendict, 80-column wrapping, sentence-case headings). -- Adhere to the repository's 400-line file size limit. Keep - `state_builders.rs` focused on composition logic; composition regression - checks belong in sibling integration tests rather than an in-module test - block. +- Do not implement this plan until the user approves it. +- Scope is roadmap item 3.5.5 only. Do not complete roadmap item 3.5.6, which + is reserved for the broader full startup matrix and revision-conflict + expansion. +- Preserve hexagonal architecture invariants from the + `$hexagonal-architecture` skill and `docs/wildside-backend-architecture.md`: + domain code owns ports, outbound adapters implement driven ports, inbound + handlers consume injected ports, and the composition root wires concrete + adapters without moving persistence details into handlers or domain modules. +- Keep persistence details confined to outbound adapters. The plan may adjust + startup wiring in `backend/src/server/state_builders.rs`, but it must not add + Diesel queries, schema handling, or table-specific logic to inbound HTTP + handlers. +- Preserve the current public shape of `build_http_state` unless a later + approved plan revision explicitly allows an interface change. +- Do not add new external dependencies. Public Firecrawl research found the + existing local stack sufficient: `rstest` for fixture and table-driven tests, + `rstest-bdd` for behaviour scenarios, and `proptest` only if the + implementation introduces a true input-space invariant. +- Use `leta` for symbol navigation and refactoring. Use text search only for + documentation, configuration, literals, and feature files. +- Use `rstest` fixtures for new unit/regression tests. Use `rstest-bdd` for + externally observable startup behaviour, following the scenario-binding and + fixture conventions in `docs/developers-guide.md` and + `docs/rstest-bdd-users-guide.md`. +- Do not run format, lint, or tests in parallel. Capture long command output + with `tee` into `/tmp` logs. +- Commit after each coherent implementation milestone, but only after the + milestone gates pass. +- Run `coderabbit review --agent` after each major milestone and clear all + concerns before moving to the next milestone. +- If `docs/users-guide.md` remains absent, record that no user-guide update was + possible and update the nearest relevant user-facing document only if + behaviour changes are actually visible to users. ## Tolerances (exception triggers) -- Scope tolerance: if implementation requires changes to more than 12 files - or roughly 1,200 net lines of code, stop and split follow-up work. -- Interface tolerance: if any public HTTP API signature must change, stop - and escalate. -- Refactoring tolerance: if refactoring the existing builder helpers - requires changing more than three existing integration test files beyond - import adjustments, stop and escalate because the refactoring blast - radius is too large for this item. -- Dependency tolerance: if a new crate is required, stop and escalate. -- Test tolerance: if focused tests or full gates still fail after three - repair loops, stop and capture evidence. -- Environment tolerance: if embedded PostgreSQL cannot start after - verifying `/dev/null`, `PG_TEST_BACKEND`, and required helper tooling, - stop and record the failure details. -- File-size tolerance: if `state_builders.rs` exceeds 400 lines after - adding the test module, extract the tests to a separate file and - document the split in the decision log. +- Scope tolerance: if implementation needs changes outside + `backend/src/server/state_builders.rs`, the startup-mode BDD suite, + user-state startup tests, architecture/developer docs, and roadmap closure + notes, stop and document the extra scope before proceeding. +- Churn tolerance: if the implementation exceeds 10 files or 600 net lines, + stop and split the work into a smaller follow-up plan. +- Interface tolerance: if a public API signature, route contract, domain port + signature, or persisted schema must change, stop and request approval. +- Dependency tolerance: if a new crate, service, or tool dependency seems + necessary, stop and present alternatives. +- Behaviour tolerance: if DB-present mode cannot be made to hard-fail rather + than fallback when a required user-state schema is missing, stop and document + the conflict. +- Validation tolerance: if any required gate fails after three consecutive fix + attempts, stop with the current logs and a root-cause summary. +- Architecture tolerance: if satisfying a test requires an inbound module to + import outbound persistence code directly, stop and redesign the seam. +- Agent-team tolerance: sub-agents may perform reconnaissance only unless a + later approved implementation pass assigns disjoint write ownership. ## Risks -- Risk: the internal helper seams may need visibility changes (`pub(crate)` - or `pub(super)`) that could have rippled into test files that formerly - included `state_builders.rs` via `#[path]` directives. +- Risk: `HttpStateExtraPorts::default()` is fixture-first and could mask + missing explicit composition if `build_http_state` stops wiring a port. Severity: medium. - Likelihood: high. - Mitigation: Resolved in Stage B: all - `#[path = "../src/server/state_builders.rs"]` includes in the test tree - were removed; tests now import symbols from - `backend::test_support::server`. - -- Risk: composition assertions could drift into type-introspection seams that - leak infrastructure concerns into the domain. + Likelihood: medium. + Mitigation: keep `build_http_state` explicitly constructing all user-state + and extra ports, and add assertions that inspect endpoint behaviour rather + than relying only on type construction. + +- Risk: the `Option` branch is repeated across helper functions, so a + future port addition could accidentally use a fixture in DB-present mode. Severity: high. Likelihood: medium. - Mitigation: keep the tests behavioural. The fixture-mode composition test - exercises each port through a lightweight operation, and the DB-present - matrix is verified through HTTP-level BDD scenarios. - -- Risk: the 400-line file limit may be reached if composition tests drift - back into `state_builders.rs`. - Severity: low. - Likelihood: high. - Mitigation: keep composition checks in - `backend/tests/state_builders_composition_unit.rs` and leave - `state_builders.rs` as production wiring only. - -- Risk: embedded PostgreSQL tests may fail for environmental reasons - unrelated to the feature (known `/dev/null` and missing-tool issues). + Mitigation: introduce a user-state helper seam that groups related port + selection in one place and add regression tests that prove DB identity data + is returned in DB-present mode. + +- Risk: route submission is composed in `backend/src/server/mod.rs`, not in + `backend/src/server/state_builders.rs`, so route-submission parity can drift + from the HTTP state matrix. + Severity: medium. + Likelihood: medium. + Mitigation: keep this plan focused on user-state composition, but preserve + existing route-submission assertions in the startup-mode BDD suite and avoid + changing `build_route_submission_service` unless required by a failed test. + +- Risk: embedded PostgreSQL tests can be skipped or flaky when local database + setup fails. Severity: medium. Likelihood: medium. - Mitigation: use repo-standard `pg-embedded-setup-unpriv` helpers, retain - logs, and apply the known `/dev/null` and tool-installation repairs - documented in the project notes store before treating failures as feature - regressions. + Mitigation: use existing `pg-embedded-setup-unpriv` helpers, preserve + explicit skip diagnostics, and rely on the full `make test` gate before + closure. -- Risk: inconsistent builder patterns may resist unification without - changing external behaviour. +- Risk: over-hardening could duplicate the broader matrix planned for 3.5.6. Severity: medium. Likelihood: medium. - Mitigation: prefer additive seam extraction over rewriting existing - builders. If a builder already works and is tested through HTTP-level - flows, wrap it with an explicit-seam helper rather than rewriting it. + Mitigation: limit new assertions to deterministic adapter selection and + obvious no-fallback regressions for user-state wiring. Leave expanded + revision-conflict and full-matrix repository coverage to 3.5.6. + +## Agent team + +Planning used a Wyvern agent team for read-only reconnaissance. During the +approved implementation pass, agent use remains optional and constrained: + +- Coordinator: owns final design decisions, file edits, gates, commits, + CodeRabbit review, push, and PR updates. +- Wyvern A, if reused: read-only documentation and roadmap verification. +- Wyvern B, if reused: read-only code and test reconnaissance. + +Sub-agents must not run tests. If any worker-style implementation agents are +introduced after approval, each must receive a disjoint write scope and must be +told that other agents may be editing the repository. ## Progress -- [x] Reviewed roadmap item 3.5.5, adjacent items 3.5.2 through 3.5.4, - the current state of `state_builders.rs`, and existing test coverage. -- [x] Drafted this ExecPlan at - `docs/execplans/backend-3-5-5-harden-startup-mode-composition.md`. -- [x] Approval gate: user approved implementation. -- [x] Stage A: analyze and design the helper seam pattern and regression - assertion strategy. -- [x] Stage B: extract helper seams and add fixture-mode composition checks - in `backend/tests/state_builders_composition_unit.rs`. -- [x] Stage C: add BDD behavioural suite exercising the full startup-mode - composition matrix at the HTTP boundary with embedded PostgreSQL. - Completed: four BDD scenarios (fixture happy path, DB-present happy - path, schema-loss unhappy path, validation stability edge path) pass - against all 16 ports with cross-mode validation envelope comparison. -- [ ] Stage D: record design decisions in - `docs/wildside-backend-architecture.md` and mark roadmap item 3.5.5 - done in `docs/backend-roadmap.md`. -- [ ] Stage E: run doc checks and full repository gates, retaining logs. +- [x] (2026-05-21) Loaded the requested `$leta`, `$rust-router`, and + `$hexagonal-architecture` skills. +- [x] (2026-05-21) Created the Leta workspace for this worktree with + `leta workspace add`. +- [x] (2026-05-21) Renamed the local branch to + `backend-3-5-5-harden-startup-mode-composition`. +- [x] (2026-05-21) Used a Wyvern agent team for documentation and code + reconnaissance. +- [x] (2026-05-21) Used Firecrawl to check current public references for + `rstest`, `rstest-bdd`, `proptest`, and Rust ports-and-adapters prior art. +- [x] (2026-05-21) Drafted this pre-implementation ExecPlan. +- [ ] Obtain explicit user approval for this plan. +- [ ] Implement the approved helper seams and regression assertions. +- [ ] Update architecture, developer, and roadmap documentation after + implementation evidence exists. +- [ ] Run `coderabbit review --agent` after the implementation milestone and + clear all concerns. +- [ ] Run and record final quality gates: + `make check-fmt`, `make lint`, and `make test`. +- [ ] Mark roadmap item 3.5.5 done only after the implementation and all gates + succeed. ## Surprises & discoveries -2026-04-03: Stage A analysis confirmed that `state_builders.rs` should stay -focused on production wiring. Fixture-mode composition checks therefore live -in `backend/tests/state_builders_composition_unit.rs`, and DB-present/full -HTTP coverage lives in `backend/tests/startup_mode_composition_bdd.rs`. - -2026-04-03: The composition checks use observable behaviour rather than type -introspection. Each test calls a lightweight operation on each port and -asserts the response shape matches the expected fixture or DB-backed -contract, preserving the hexagonal boundary. - -2026-04-03: Stage B composition checks were created as the integration test -`backend/tests/state_builders_composition_unit.rs` rather than in-module -because `state_builders.rs` is in the binary crate (`src/main.rs`), not the -library crate (`src/lib.rs`). The initial implementation used `#[path]` -includes following existing repository patterns; the current implementation -imports the seam via -`use backend::test_support::server::{ServerConfig, build_http_state}`. - -2026-04-03: Attempting to mirror the fixture-mode composition check with a -DB-only composition test added more setup cost than value. The final split -keeps DB-present assertions in the HTTP-level BDD suite, which already owns -the embedded-PostgreSQL lifecycle correctly. - -2026-04-03: Stage C BDD implementation created the dedicated startup-mode -composition suite: feature file -(`backend/tests/features/startup_mode_composition.feature`), support modules, -and test harness (`backend/tests/startup_mode_composition_bdd.rs`). The -fixture-mode integration test covers direct composition assertions, while the -BDD suite owns DB-present and cross-mode HTTP verification. - -2026-04-15: Stage C BDD failures traced to multiple ephemeral Tokio -runtimes being created per BDD step. Dropping each runtime also dropped -the DB pool's background tasks, leaving later connection attempts in a -broken state. The fix in this PR shares a single -`Arc` across the whole `World` fixture and uses -`runtime.block_on(...)` consistently for setup, seeding, and flow runners -instead of creating fresh runtimes per step. Schema-loss unhappy-path -assertions were also updated to match `DieselLoginService`'s real -behaviour: HTTP 500 with a stable error envelope, not an assumed HTTP 200 -fixture fallback. Verification: all four BDD scenarios now pass under -`make test`. +- Observation: `docs/users-guide.md` is not present in this checkout. + Evidence: `fd 'users.*guide|user.*guide' docs` found specific tool guides + but no repository-wide `docs/users-guide.md`. + Impact: this plan records that user-guide updates are conditional. If + implementation changes user-visible server behaviour, update the relevant + existing user-facing document or create a separate documentation decision. + +- Observation: `backend/tests/features/startup_mode_composition.feature` + already states that it covers all HTTP-facing ports for roadmap item 3.5.5. + Evidence: the feature file names all 16 `HttpStatePorts` and + `HttpStateExtraPorts` and includes fixture, DB-present, schema-loss, and + validation-stability scenarios. + Impact: implementation should strengthen the existing suite rather than + replace it wholesale. + +- Observation: `build_http_state` is already the narrow composition point for + HTTP state, while route-submission service construction lives in + `backend/src/server/mod.rs`. + Evidence: Leta found `state_builders.rs:build_http_state` and an ambiguous + separate `mod.rs:build_route_submission_service`. + Impact: this item should avoid moving route submission unless a regression + test proves the current split prevents deterministic user-state composition. ## Decision log -**Decision A1 (2026-04-03):** Use observable-behaviour assertions for -composition checks. - -Rationale: The observable-behaviour strategy is non-invasive: fixture -adapters return hardcoded data (for example, `FixtureLoginService` accepts -`admin`/`password`), while DB-backed adapters reject fixture credentials or -require real data. Each test can distinguish adapters by calling a -lightweight method and inspecting the response. - -Trade-off: observable-behaviour tests are slightly slower than pure type -checks and depend on fixture behaviour remaining stable. However, they -provide stronger regression coverage because they test composition and -runtime behaviour together. - -**Decision A2 (2026-04-03):** Keep composition checks in integration tests -from the start. - -Rationale: `state_builders.rs` is currently 321 lines. Keeping the -composition checks in `backend/tests/state_builders_composition_unit.rs` -avoids hitting the 400-line limit and keeps production wiring separate from -regression assertions. - -Fixture-mode composition checks live in -`backend/tests/state_builders_composition_unit.rs`; DB-present/full HTTP -matrix coverage lives in `backend/tests/startup_mode_composition_bdd.rs`. - -**Decision C1 (2026-04-03, updated 2026-04-15):** HTTP-level BDD suite -completed with full startup-mode coverage. - -Rationale: Stage B successfully implemented observable-behaviour -fixture-mode composition checks at -`backend/tests/state_builders_composition_unit.rs`. These tests exercise the -composition decision directly and fail immediately if wiring diverges. -Existing BDD suites (`user_state_startup_modes_bdd.rs`, -`user_state_profile_interests_startup_modes_bdd.rs`) already provide -HTTP-level regression coverage for login/users/profile/interests port groups -across both modes with embedded PostgreSQL, and the dedicated -`startup_mode_composition_bdd` suite covers the broader DB-present matrix. - -The PR applied the shared-runtime fix for Stage C and revised the -schema-loss assertions to match the real `DieselLoginService` error -contract. All four scenarios now pass: -`fixture_fallback_happy_path`, `db_present_happy_path`, -`schema_loss_unhappy_path`, and `validation_stability_edge_path`. -Defence-in-depth coverage is now complete through the Stage B fixture-mode -composition checks plus the full HTTP BDD matrix; no DB-present scenarios -remain deferred. +- Decision: keep this PR as a pre-implementation planning PR. + Rationale: the user explicitly required plan approval before implementation. + Date/Author: 2026-05-21 / Codex. + +- Decision: group login, users, profile, interests, preferences, and + route-annotation startup selection under an explicit user-state helper seam. + Rationale: these ports share identity-bearing user-state semantics and are + the highest-risk area for accidental DB-present fixture fallback. + Date/Author: 2026-05-21 / Codex. + +- Decision: do not introduce `proptest`, `kani`, or `verus` for the planned + implementation unless the approved implementation introduces a true state + transition invariant. + Rationale: this hardening primarily concerns deterministic branch selection + over two startup modes, which is better captured by table-driven `rstest` + and behaviour-level `rstest-bdd` assertions. + Date/Author: 2026-05-21 / Codex. + +- Decision: document Firecrawl findings as supporting context, not as + authoritative design input. + Rationale: repository docs and local conventions are more specific than + general public prior art. Public references only confirmed that the existing + tools and ports-as-traits approach remain appropriate. + Date/Author: 2026-05-21 / Codex. ## Outcomes & retrospective -(Not yet applicable. This section will be filled at completion.) +This section is intentionally empty during draft. It must be updated after +approval, implementation, CodeRabbit review, gate execution, and roadmap +closure. ## Context and orientation -This roadmap item sits immediately after the 3.5.4 revision-safe interests -work. The relevant code and test landscape is as follows. +`backend/src/server/state_builders.rs` constructs the Actix `web::Data` value +that carries backend ports into HTTP handlers. It imports domain port traits, +fixture implementations, and outbound adapter constructors, then chooses +between DB-backed and fixture-backed implementations based on +`ServerConfig.db_pool`. + +The key functions and types are: + +- `backend/src/server/state_builders.rs::build_http_state`, which builds + `HttpStatePorts` and `HttpStateExtraPorts` before calling + `HttpState::new_with_extra`. +- `backend/src/server/state_builders.rs::build_login_users_pair`, which chooses + `DieselLoginService` plus `DieselUsersQuery` in DB-present mode and + `FixtureLoginService` plus `FixtureUsersQuery` in fixture-fallback mode. +- `backend/src/server/state_builders.rs::build_profile_interests_pair`, which + chooses `DieselUserProfileQuery` plus `DieselUserInterestsCommand` in + DB-present mode and fixture implementations otherwise. +- `backend/src/server/state_builders.rs::build_user_preferences_pair`, created + by the existing `build_idempotent_pair!` macro. +- `backend/src/server/state_builders.rs::build_route_annotations_pair`, also + created by the existing `build_idempotent_pair!` macro. +- `backend/src/inbound/http/state.rs::HttpState`, `HttpStatePorts`, and + `HttpStateExtraPorts`, which define the injected HTTP state bundle. +- `backend/src/server/mod.rs::build_route_submission_service`, which composes + route submission outside `state_builders.rs`. + +The most relevant tests are: + +- `backend/tests/startup_mode_composition_bdd.rs` and + `backend/tests/features/startup_mode_composition.feature`, which exercise + startup-mode behaviour through HTTP-facing flows. +- `backend/tests/startup_mode_composition_bdd/flow_support.rs`, which stores + snapshots and shared assertions. +- `backend/tests/startup_mode_composition_bdd/flows.rs`, which drives the + happy and validation-error request flows. +- `backend/tests/user_state_startup_modes_bdd.rs`, which covers login/users + startup mode behaviour. +- `backend/tests/user_state_profile_interests_startup_modes_bdd.rs`, which + covers profile/interests startup mode behaviour. +- `backend/tests/diesel_login_users_adapters.rs` and + `backend/tests/diesel_profile_interests_adapters.rs`, which exercise + adapter-specific DB-backed behaviour. + +Supporting documentation to consult during implementation: + +- `docs/backend-roadmap.md` for roadmap item 3.5.5 and closure style. +- `docs/wildside-backend-architecture.md` for hexagonal module boundaries, + composition-root guidance, and design-decision placement. +- `docs/developers-guide.md` for `rstest-bdd` v0.5.0 scenario conventions and + behaviour-test layout. +- `docs/rust-testing-with-rstest-fixtures.md` for fixture scope, composition, + and `#[once]` caveats. +- `docs/rstest-bdd-users-guide.md` for `ScenarioState`, `Slot`, fixture + injection, and async-step guidance. +- `docs/rust-doctest-dry-guide.md` if public examples or doc comments are + changed. +- `docs/complexity-antipatterns-and-refactoring-strategies.md` for helper + extraction discipline if `state_builders.rs` grows too complex. +- `docs/pg-embed-setup-unpriv-users-guide.md` for embedded PostgreSQL fixture + behaviour. +- `docs/documentation-style-guide.md` for roadmap and ADR/documentation style. -### Composition root: `backend/src/server/state_builders.rs` (322 lines) +## Plan of work -This module is the composition root for all HTTP-facing domain ports. It -is declared as `mod state_builders` inside `backend/src/server/mod.rs` and -is exposed to tests via `backend::test_support::server`. The module -currently contains no `#[cfg(test)]` blocks. +### Stage A: approval and baseline evidence -The main entry point is: +Wait for explicit approval of this ExecPlan. After approval, re-read the plan, +confirm `git status --short --branch`, and verify that the branch is still +`backend-3-5-5-harden-startup-mode-composition`. -```rust -pub fn build_http_state( - config: &ServerConfig, - route_submission: Arc, -) -> web::Data +Run targeted baseline commands with logs: + +```bash +make check-fmt 2>&1 | tee /tmp/check-fmt-wildside-backend-3-5-5-harden-startup-mode-composition.out +make lint 2>&1 | tee /tmp/lint-wildside-backend-3-5-5-harden-startup-mode-composition.out +make test 2>&1 | tee /tmp/test-wildside-backend-3-5-5-harden-startup-mode-composition.out ``` -Test callers import this function as -`use backend::test_support::server::build_http_state`. +If any baseline gate fails for reasons unrelated to this plan, stop and record +the failure in `Surprises & Discoveries`. -This function calls individual builder helpers and assembles `HttpStatePorts` -(11 ports) and `HttpStateExtraPorts` (5 ports) into a single `HttpState`. +### Stage B: add the explicit user-state seam -The individual builders follow three patterns: +In `backend/src/server/state_builders.rs`, introduce a private helper type or +function that composes the identity-bearing user-state ports together. The +preferred shape is a small private bundle, for example +`UserStatePortsBundle`, returned by a helper such as +`compose_user_state_ports(config: &ServerConfig)`. -1. Generic pool-branching helper: `build_service_pair` takes a - generic `Pool`, a factory closure, fixture defaults, and a cast - function, then branches on `Option` and returns - `(Arc, Arc)`. +The helper should own calls to: -2. Idempotent-service macro: - `build_idempotent_pair!` generates named builder functions that compose - `build_service_pair` with `build_idempotent_service` for services - needing both a domain repository and an idempotency repository. +- `build_login_users_pair(config)`; +- `build_profile_interests_pair(config)`; +- `build_user_preferences_pair(config)`; +- `build_route_annotations_pair(config)`. -3. Standalone match-based builders: - `build_login_users_pair`, `build_profile_interests_pair`, - `build_catalogue_services`, `build_offline_bundles_pair`, - `build_walk_sessions_pair`, and `build_enrichment_provenance_repository` - each contain an explicit `match &config.db_pool` block. +Then update `build_http_state` to destructure the bundle and pass its fields +into `HttpStatePorts`. Keep catalogue, offline bundles, walk sessions, +enrichment provenance, and route submission on their current paths unless tests +or review reveal a direct reason to group them too. -### Server configuration: `backend/src/server/config.rs` +The helper must be private unless tests require `pub(super)` visibility. If +visibility is widened, document why in the decision log. -```rust -pub struct ServerConfig { - pub(crate) key: Key, - pub(crate) cookie_secure: bool, - pub(crate) same_site: SameSite, - pub(crate) bind_addr: SocketAddr, - pub(crate) db_pool: Option, - #[cfg(feature = "metrics")] - pub(crate) prometheus: Option, -} +Add or adjust `rstest` unit tests close to existing startup-mode unit coverage. +The unit tests should table-drive `db_present: bool` where practical and assert +that user-state branch selection remains complete. Do not assert concrete type +names through brittle reflection if endpoint-level assertions give stronger +evidence. + +Run targeted Rust tests for the changed module or nearest suite, then commit +this stage if it passes: + +```bash +cargo test -p backend startup_modes_reject_invalid_credentials_with_unauthorised_envelope -- --nocapture 2>&1 | tee /tmp/test-targeted-user-state-seam-backend-3-5-5-harden-startup-mode-composition.out ``` -The critical field is `db_pool: Option`. When `Some`, all -DB-backed adapters should be wired. When `None`, all fixture fallbacks -should be wired. - -### HTTP state types: `backend/src/inbound/http/state.rs` - -`HttpStatePorts` contains 11 ports (as `Arc`s): `login`, `users`, `profile`, -`interests`, `preferences`, `preferences_query`, `route_annotations`, -`route_annotations_query`, `route_submission`, `catalogue`, `descriptors`. - -`HttpStateExtraPorts` contains 5 ports (as `Arc`s): `offline_bundles`, -`offline_bundles_query`, `enrichment_provenance`, `walk_sessions`, -`walk_sessions_query`. - -`HttpState` flattens both into a single struct with 16 fields. - -### Fixture port implementations - -Each domain port trait has a corresponding `Fixture*` struct in the -domain ports module (for example, `FixtureLoginService`, -`FixtureUsersQuery`, `FixtureUserProfileQuery`, -`FixtureUserInterestsCommand`, `FixtureUserPreferencesCommand`, -`FixtureUserPreferencesQuery`, etc.). These return hardcoded data and are -used when `db_pool` is `None`. - -### DB-backed adapter implementations - -Each domain port trait has a corresponding `Diesel*` struct in the -outbound persistence module (for example, `DieselLoginService`, -`DieselUsersQuery`, `DieselUserProfileQuery`, -`DieselUserInterestsCommand`, `DieselUserPreferencesRepository`, -`DieselRouteAnnotationRepository`, `DieselCatalogueRepository`, -`DieselDescriptorRepository`, `DieselEnrichmentProvenanceRepository`, -`DieselOfflineBundleRepository`, `DieselWalkSessionRepository`). - -### Existing test coverage - -There are currently six test files that exercise `state_builders.rs` -through HTTP-level flows: - -- `backend/tests/diesel_login_users_adapters.rs` (rstest, 387 lines): - tests login/users startup-mode branching with fixture and DB modes. -- `backend/tests/diesel_profile_interests_adapters.rs` (rstest): - tests profile/interests startup-mode branching. -- `backend/tests/user_state_startup_modes_bdd.rs` (rstest-bdd, 400 lines): - BDD scenarios for login/users startup-mode composition. -- `backend/tests/user_state_profile_interests_startup_modes_bdd.rs` - (rstest-bdd, 270 lines): BDD scenarios for profile/interests - startup-mode composition. -- `backend/tests/user_interests_revision_conflicts_bdd.rs`: BDD - scenarios for interests revision conflicts, uses - `state_builders::build_http_state` via a flow-support helper. -- `backend/tests/adapter_guardrails/`: adapter-level tests using - recording doubles that construct `HttpState` directly (bypassing - `state_builders`). - -All six files originally accessed `state_builders.rs` via -`#[path = "../src/server/state_builders.rs"]` include directives. None of -them test the composition decision in isolation; they all test through the -full HTTP request/response cycle. This pattern was superseded in Stage B: -`build_http_state` and `ServerConfig` are now imported through -`backend::test_support::server`, so tests no longer need `#[path]` -includes. - -**Coverage split**: fixture-mode composition is asserted directly in -`backend/tests/state_builders_composition_unit.rs`. DB-present and -cross-mode HTTP behaviour is exercised by -`backend/tests/startup_mode_composition_bdd.rs` plus the pre-existing -startup-mode BDD suites for login/users/profile/interests. - -## Agent team and ownership - -This implementation should be executed by the following agent team. One -person may play multiple roles if needed, but the responsibilities should -stay separate. - -- Coordinator agent: - owns sequencing, keeps this ExecPlan current, enforces tolerances, - collects gate evidence, and decides when the work is ready to mark - roadmap 3.5.5 done. - -- Composition seam agent: - refactors `backend/src/server/state_builders.rs` to expose - well-defined helper seams, adds the fixture-mode composition checks in - `backend/tests/state_builders_composition_unit.rs`, and ensures the - module stays within the 400-line limit. - -- Quality assurance (QA) agent: - adds the BDD behavioural suite exercising the full startup-mode - composition matrix at the HTTP boundary with embedded PostgreSQL, - covering happy, unhappy, and edge paths. - -- Documentation agent: - updates `docs/wildside-backend-architecture.md` with the hardening - design decision, updates `docs/backend-roadmap.md` to mark 3.5.5 done, - and ensures Markdown passes `make markdownlint` and `make nixie`. - -Hand-off order: - -1. Composition seam agent lands the helper-seam refactoring and - fixture-mode composition checks. -2. QA agent adds the BDD behavioural suite for the full startup-mode - composition matrix. -3. Documentation agent records the decisions and closes the roadmap item. -4. Coordinator agent runs final gates and updates this ExecPlan. +After the commit, run: -## Plan of work +```bash +coderabbit review --agent +``` + +Clear every actionable concern before proceeding. -### Stage A: analyze and design the helper seam and assertion strategy - -Before writing code, identify the exact assertion mechanism that will prove -adapter selection determinism without violating hexagonal boundaries. The -chosen approach is to test through observable behaviour: call a lightweight -method on each port and assert the response shape distinguishes fixture from -DB-backed implementations. - -Audit the existing builder visibility to determine which helpers need -`pub(crate)` or `pub(super)` exposure for testability, and confirm the -refactor keeps `state_builders.rs` within the 400-line limit. - -### Stage B: extract helper seams and add fixture-mode composition checks - -Refactor `state_builders.rs` to make each builder function's branching -decision independently testable. The specific changes depend on the -Stage A analysis, but the likely steps are: - -1. Ensure every builder function has a consistent signature that takes - `&Option` (or `&ServerConfig`) and returns the appropriate port - pair or single port. Functions like `build_login_users_pair`, - `build_profile_interests_pair`, etc. already follow this pattern. - -2. For builders using the `build_idempotent_pair!` macro - (`build_user_preferences_pair` and `build_route_annotations_pair`), - verify the generated functions are testable by calling them directly in - the test module. - -3. Add or update `backend/tests/state_builders_composition_unit.rs` - containing a fixture-mode test that constructs `ServerConfig` with - `db_pool: None`, calls `build_http_state`, and exercises every port - through a lightweight fixture contract. - -4. Keep DB-present assertions in the HTTP-level BDD suite instead of adding - a second composition-only DB test. - -Stage B ends when `cargo test --test state_builders_composition_unit` -passes and the fixture-mode test covers all 16 ports. - -### Stage C: add BDD behavioural suite for full startup-mode matrix - -Add a new behavioural test suite that exercises the complete startup-mode -composition at the HTTP boundary. This extends the coverage provided by -the existing `user_state_startup_modes_bdd` and -`user_state_profile_interests_startup_modes_bdd` suites to cover the -remaining port pairs. - -1. Create a feature file at - `backend/tests/features/startup_mode_composition.feature` with - scenarios covering: - - - **Happy path (fixture mode)**: given fixture-fallback startup mode, - when executing requests against all major endpoint groups, then all - responses match fixture fallback contracts. - - **Happy path (DB mode)**: given DB-present startup mode backed by - embedded PostgreSQL, when executing requests against all major - endpoint groups, then all responses match DB-backed contracts. - - **Unhappy path (schema loss)**: given DB-present startup mode with a - critical table dropped, when executing requests, then responses - produce stable error envelopes rather than fixture data. - - **Edge path (validation stability)**: given both startup modes, when - executing a request with invalid input, then validation error - envelopes are identical regardless of startup mode. - -2. Create the test harness at - `backend/tests/startup_mode_composition_bdd.rs` with a companion - flow-support module at - `backend/tests/startup_mode_composition_bdd/flow_support.rs`. - -3. The `World` struct should track: - - current startup mode (fixture or DB), - - optional DB context (pool, database URL) from embedded PostgreSQL, - - snapshots for each endpoint response, - - optional skip reason if cluster setup fails. - -4. Step definitions should exercise at least one representative endpoint - per port group: - - Login/Users: `POST /api/v1/login`, `GET /api/v1/users` - - Profile: `GET /api/v1/users/me` - - Interests: `PUT /api/v1/users/me/interests` - - Preferences: `GET /api/v1/users/me/preferences`, - `PUT /api/v1/users/me/preferences` - - Route annotations: `GET /api/v1/routes/{route_id}/annotations` - - Catalogue: `GET /api/v1/catalogue/explore`, - `GET /api/v1/catalogue/descriptors` - - Offline bundles: `GET /api/v1/offline/bundles` - - Walk sessions: `POST /api/v1/walk-sessions` (with valid payload) - - Enrichment provenance: `GET /api/v1/admin/enrichment/provenance` - -5. Use `pg-embedded-setup-unpriv` helpers from - `backend/tests/support/embedded_postgres.rs` for the DB-present mode. - -Stage C ends when the BDD suite passes under `make test` with embedded -PostgreSQL. - -### Stage D: documentation, roadmap closure, and gate replay - -1. Record the design decision in `docs/wildside-backend-architecture.md`: - the state-builder composition root is hardened with explicit helper - seams and regression assertions so every port is provably wired to the - correct adapter for each startup mode. Note the type-witness (or - observable-behaviour) assertion strategy and the decision to keep tests - at both unit and BDD levels for defence in depth. - -2. Mark roadmap item 3.5.5 as done in `docs/backend-roadmap.md` by - changing `- [ ] 3.5.5.` to `- [x] 3.5.5.` only after all gates pass. - -3. Run documentation-specific checks: - - ```bash - set -o pipefail - make fmt 2>&1 | tee /tmp/3-5-5-fmt.out - set -o pipefail - make markdownlint 2>&1 | tee /tmp/3-5-5-markdownlint.out - set -o pipefail - make nixie 2>&1 | tee /tmp/3-5-5-nixie.out - ``` - -### Stage E: final quality gates - -Run repository-wide gates required before closure: +### Stage C: strengthen behavioural regression assertions + +Update `backend/tests/startup_mode_composition_bdd.rs` and the helper modules +under `backend/tests/startup_mode_composition_bdd/` to add a shared assertion +that the mode evidence is complete for user-state endpoints. + +The preferred helper name is `assert_user_state_adapter_selection`, placed in +`backend/tests/startup_mode_composition_bdd/flow_support.rs` if it is reused +across more than one step. It should verify both: + +- fixture-fallback mode returns fixture identity/profile evidence; and +- DB-present mode returns seeded DB identity/profile evidence. + +Strengthen the existing schema-loss scenario so that DB-present mode with the +`users` table missing returns a stable internal-error envelope and does not set +later fixture-backed snapshots. Keep this as a no-fallback assertion rather +than broadening into every 3.5.6 matrix case. + +If the feature file needs clearer language, update +`backend/tests/features/startup_mode_composition.feature` without changing +scenario intent. Keep step names aligned with `rstest-bdd` bindings. + +Run the relevant behavioural tests with logs: ```bash -set -o pipefail -make check-fmt 2>&1 | tee /tmp/3-5-5-check-fmt.out -set -o pipefail -make lint 2>&1 | tee /tmp/3-5-5-lint.out -set -o pipefail -make test 2>&1 | tee /tmp/3-5-5-test.out +cargo test -p backend startup_mode_composition --test startup_mode_composition_bdd -- --nocapture 2>&1 | tee /tmp/test-startup-mode-composition-bdd-backend-3-5-5-harden-startup-mode-composition.out ``` -## Concrete steps +Commit this stage only after the targeted BDD suite passes or is skipped solely +by existing embedded PostgreSQL skip handling. Run `coderabbit review --agent` +and clear concerns before proceeding. -Run all commands from `/home/user/project`. Use `set -o pipefail` and -`tee` for every meaningful command so the exit code survives truncation -and the log is retained. +### Stage D: documentation and roadmap closure -1. Capture the current baseline. +Update documentation only after implementation evidence exists. - ```bash - set -o pipefail - cargo test --test state_builders_composition_unit 2>&1 \ - | tee /tmp/3-5-5-baseline-state-builders.out - ``` +Update `docs/wildside-backend-architecture.md` with a short design-decision +entry that records the explicit user-state composition helper seam and why it +exists. If the change is more substantive than a local composition decision, +create an ADR following `docs/documentation-style-guide.md` and link it from +the architecture document. - Expected: existing fixture-mode composition checks pass. +Update `docs/developers-guide.md` if the implementation introduces a reusable +startup-mode test convention, helper naming convention, or BDD layout practice +that future contributors need to follow. -2. Verify existing startup-mode BDD suites still pass. +Update `docs/users-guide.md` only if that file exists or if a user-visible +server behaviour change is introduced. If it remains absent and behaviour is +unchanged, record this in the plan rather than creating a generic guide solely +for this item. - ```bash - set -o pipefail - make test 2>&1 | tee /tmp/3-5-5-baseline-full.out - ``` +Update `docs/backend-roadmap.md` to mark 3.5.5 done only after the helper seam, +tests, CodeRabbit review, and full gates succeed. Follow the style used by +3.5.2 and 3.5.3: include a short execution note naming the main tests and +gate evidence. - Expected: all existing tests pass. +Run Markdown and repository gates: -3. Add fixture-mode composition checks for deterministic adapter selection. +```bash +make check-fmt 2>&1 | tee /tmp/check-fmt-wildside-backend-3-5-5-harden-startup-mode-composition.out +make lint 2>&1 | tee /tmp/lint-wildside-backend-3-5-5-harden-startup-mode-composition.out +make test 2>&1 | tee /tmp/test-wildside-backend-3-5-5-harden-startup-mode-composition.out +``` + +Commit documentation and roadmap closure only after the gates pass. Run +`coderabbit review --agent` one final time and clear concerns. + +## Concrete steps - ```bash - set -o pipefail - cargo test --test state_builders_composition_unit 2>&1 \ - | tee /tmp/3-5-5-unit-tests.out - ``` +All commands run from: - Expected: tests pass, proving all 16 ports are correctly wired in - fixture mode. +```plaintext +/home/leynos/.lody/repos/github---leynos---wildside/worktrees/f407c9df-ba22-40a7-842e-5e0eb11778b9 +``` -4. Add BDD behavioural suite for the full composition matrix. +Before implementation: - ```bash - set -o pipefail - cargo test -p backend --test startup_mode_composition_bdd 2>&1 \ - | tee /tmp/3-5-5-bdd-tests.out - ``` +```bash +git status --short --branch +git branch --show-current +``` + +Expected branch: + +```plaintext +backend-3-5-5-harden-startup-mode-composition +``` + +Use Leta for code navigation: + +```bash +leta show state_builders.rs:build_http_state -n 20 +leta show build_login_users_pair -n 10 +leta show build_profile_interests_pair -n 10 +leta grep "startup_mode" "backend/tests" -k function,method +``` - Expected: BDD scenarios pass for fixture-fallback, DB-present, - schema-loss, and validation-stability paths. +Implement Stage B and Stage C with small patches. Before each commit: -5. Run documentation checks after doc updates. +```bash +git diff --check +git status --short +``` - ```bash - set -o pipefail - make fmt 2>&1 | tee /tmp/3-5-5-fmt.out - set -o pipefail - make markdownlint 2>&1 | tee /tmp/3-5-5-markdownlint.out - set -o pipefail - make nixie 2>&1 | tee /tmp/3-5-5-nixie.out - ``` +Commit with a file-based message: -6. Run final repository-wide gates. +```bash +COMMIT_MSG_DIR=$(mktemp -d) +cat > "$COMMIT_MSG_DIR/COMMIT_MSG.md" << 'ENDOFMSG' +Harden user-state startup composition + +Group user-state HTTP port wiring behind an explicit composition helper +and add regression assertions that keep DB-present and fixture-fallback +startup modes deterministic. +ENDOFMSG +git commit -F "$COMMIT_MSG_DIR/COMMIT_MSG.md" +rm -rf "$COMMIT_MSG_DIR" +``` - ```bash - set -o pipefail - make check-fmt 2>&1 | tee /tmp/3-5-5-check-fmt.out - set -o pipefail - make lint 2>&1 | tee /tmp/3-5-5-lint.out - set -o pipefail - make test 2>&1 | tee /tmp/3-5-5-test.out - ``` +Use an appropriately narrower summary for each actual milestone commit. ## Validation and acceptance -The implementation is done only when all of the following are true: - -- Unit tests: - - `cargo test --test state_builders_composition_unit` passes. - - Fixture-mode test asserts all 16 ports resolve to fixture contracts. - -- BDD tests: - - Fixture-fallback happy path: all endpoint groups return - fixture-shaped responses. - - DB-present happy path: all endpoint groups return DB-backed - responses. - - Schema-loss unhappy path: DB-present mode with dropped tables - produces stable error envelopes, not fixture data. - - Validation edge path: validation errors are identical regardless of - startup mode. - - All BDD scenarios pass under `make test` with embedded PostgreSQL. - -- Composition invariant: - - No port in `HttpStatePorts` or `HttpStateExtraPorts` can silently - resolve to the wrong adapter type for the configured startup mode. - - The regression suite fails if a future change breaks the invariant. - -- Lint/format/docs: - - `make fmt`, `make markdownlint`, and `make nixie` pass after doc - changes. - - `make check-fmt` and `make lint` pass. - -- Documentation: - - `docs/wildside-backend-architecture.md` records the state-builder - hardening decision. - - `docs/backend-roadmap.md` marks 3.5.5 done only after every gate - above is green. +Acceptance requires all of the following after approved implementation: + +- `build_http_state` still returns `web::Data` and existing callers + compile unchanged. +- User-state ports are visibly grouped through an explicit helper seam in + `backend/src/server/state_builders.rs`. +- Fixture-fallback startup mode returns fixture identity/profile evidence for + user-state endpoints. +- DB-present startup mode returns seeded DB identity/profile evidence for + user-state endpoints. +- DB-present startup with the `users` table missing returns an internal-error + envelope and does not continue with fixture-backed profile or preferences + snapshots. +- Validation error envelopes remain stable across fixture-fallback and + DB-present modes. +- `rstest` unit/regression coverage exists for deterministic selection or the + nearest existing unit suite is expanded with a table-driven case. +- `rstest-bdd` behavioural coverage exists for HTTP-observable startup-mode + behaviour. +- Architecture or developer documentation records any new internal convention. +- `docs/backend-roadmap.md` marks item 3.5.5 done only after all validation + succeeds. +- `coderabbit review --agent` has no unresolved actionable concerns. +- Final gates pass: + +```bash +make check-fmt +make lint +make test +``` + +Property tests, Kani, and Verus are not acceptance requirements for the draft +scope. Reconsider this decision only if implementation introduces a new +invariant over a range of input states, state transitions, or ordering +constraints that cannot be covered by the two startup modes and current BDD +matrix. ## Idempotence and recovery -This plan is intentionally re-runnable. - -- Re-running focused tests is safe and expected. -- Re-running the embedded PostgreSQL suites is safe; they provision - temporary databases and clean them up automatically. -- If `pg-embedded-setup-unpriv` fails with - `cannot create /dev/null: Permission denied`, repair `/dev/null` to the - standard character device (`mknod -m 666 /dev/null c 1 3` and - `chown root:root /dev/null`) before retrying and record that repair in - the execution notes. -- If `make lint` fails because `yamllint` or `actionlint` is missing, - install the required tool and rerun the same command; do not skip the - lint stage. -- Do not mark the roadmap item complete until the final gate logs exist - and show success. +The implementation steps are additive and can be repeated. If a targeted test +fails, inspect the corresponding `/tmp` log before changing code. If a +CodeRabbit review raises a concern that would exceed a tolerance, update this +plan and ask for approval before proceeding. + +If an implementation commit proves wrong, prefer a new corrective commit over +rewriting shared history after the branch is pushed. If the branch has not been +pushed and no other agent depends on it, an amend is acceptable for fixing the +immediate previous commit after rerunning the relevant gate. + +Embedded PostgreSQL setup failures should use the repository's existing skip +diagnostics. Do not replace them with silent fixture fallback. ## Artifacts and notes -Retain at least these logs: - -- `/tmp/3-5-5-baseline-state-builders.out` -- `/tmp/3-5-5-baseline-full.out` -- `/tmp/3-5-5-unit-tests.out` -- `/tmp/3-5-5-bdd-tests.out` -- `/tmp/3-5-5-fmt.out` -- `/tmp/3-5-5-markdownlint.out` -- `/tmp/3-5-5-nixie.out` -- `/tmp/3-5-5-check-fmt.out` -- `/tmp/3-5-5-lint.out` -- `/tmp/3-5-5-test.out` - -Important evidence to capture in the final version of this plan: - -- One passing transcript showing the unit test suite proves all 16 ports - are deterministically wired in fixture mode. -- One passing transcript showing the unit test suite proves all 16 ports - are deterministically wired in DB-present mode. -- One passing transcript showing the BDD suite covers the full - composition matrix. -- One passing transcript showing full gates succeeded. +Planning evidence collected before this draft: + +- `leta workspace add` registered this worktree. +- `leta grep ".*" "backend/src/server/state_builders.rs" -k function,method,struct,enum` + identified `build_http_state`, user-state pair builders, and extra port + builders. +- Firecrawl searches found current public references for: + - `https://docs.rs/rstest`; + - `https://docs.rs/rstest-bdd`; + - `https://docs.rs/proptest`. +- Wyvern documentation reconnaissance confirmed roadmap scope, documentation + update targets, and `rstest-bdd` v0.5.0 conventions. +- Wyvern code reconnaissance identified the current composition seam, existing + startup-mode BDD suite, and `HttpStateExtraPorts::default()` masking risk. ## Interfaces and dependencies -The implementation should end with these stable interfaces and -relationships. +No new dependency is planned. -Existing composition entry point (unchanged public signature): +The planned private helper in `backend/src/server/state_builders.rs` should use +existing domain port trait objects: ```rust -pub fn build_http_state( - config: &ServerConfig, - route_submission: Arc, -) -> web::Data -``` - -Test callers import this function as -`use backend::test_support::server::build_http_state`. - -Required adapter dependency direction (unchanged): +struct UserStatePortsBundle { + login: Arc, + users: Arc, + profile: Arc, + interests: Arc, + preferences: Arc, + preferences_query: Arc, + route_annotations: Arc, + route_annotations_query: Arc, +} -- `backend::server::state_builders` depends on - `backend::domain::ports::*` (port traits and fixture impls) and - `backend::outbound::persistence::*` (DB-backed adapters). -- `backend::domain` does not depend on `backend::outbound` or Actix - types. -- `backend::inbound::http` depends only on `backend::domain::ports::*`. +fn compose_user_state_ports(config: &ServerConfig) -> UserStatePortsBundle; +``` -No new dependencies are expected. Reuse existing Diesel adapters, domain -ports, fixture implementations, and `pg-embedded-setup-unpriv` test -helpers. +The exact names may change during implementation if a shorter local convention +fits better, but the helper must remain explicit and private unless tests need +`pub(super)` access. ## Revision note -Initial draft created on 2026-04-03 to prepare roadmap item 3.5.5 for -implementation. The draft identifies the composition-determinism gap in -the existing test coverage and proposes a type-witness regression -assertion strategy at the unit level combined with BDD coverage at the -HTTP boundary for defence in depth. +Initial draft created on 2026-05-21. The draft captures roadmap item 3.5.5 +scope, current code anchors, test strategy, Firecrawl research findings, +Wyvern reconnaissance, approval gate, and implementation tolerances. No +implementation has been performed under this plan. From af44e631e5421e24552389f84eec81ccd7d404d4 Mon Sep 17 00:00:00 2001 From: leynos Date: Tue, 26 May 2026 19:44:53 +0200 Subject: [PATCH 2/7] Clarify operator guide scope Record that docs/users-guide.md refers to operators of the Wildside server application, not product end users. Narrow the planned documentation update requirement to behaviour that is visible to server operators. --- ...d-3-5-5-harden-startup-mode-composition.md | 33 ++++++++++++++----- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/docs/execplans/backend-3-5-5-harden-startup-mode-composition.md b/docs/execplans/backend-3-5-5-harden-startup-mode-composition.md index 5fe75537..06e42a0f 100644 --- a/docs/execplans/backend-3-5-5-harden-startup-mode-composition.md +++ b/docs/execplans/backend-3-5-5-harden-startup-mode-composition.md @@ -77,8 +77,9 @@ Observable success means: - Run `coderabbit review --agent` after each major milestone and clear all concerns before moving to the next milestone. - If `docs/users-guide.md` remains absent, record that no user-guide update was - possible and update the nearest relevant user-facing document only if - behaviour changes are actually visible to users. + possible and update the nearest relevant operator-facing document only if + behaviour changes are visible to operators of the Wildside server + application. ## Tolerances (exception triggers) @@ -171,6 +172,8 @@ told that other agents may be editing the repository. - [x] (2026-05-21) Used Firecrawl to check current public references for `rstest`, `rstest-bdd`, `proptest`, and Rust ports-and-adapters prior art. - [x] (2026-05-21) Drafted this pre-implementation ExecPlan. +- [x] (2026-05-26) Clarified that `docs/users-guide.md` references server + operators, not end users of the product experience. - [ ] Obtain explicit user approval for this plan. - [ ] Implement the approved helper seams and regression assertions. - [ ] Update architecture, developer, and roadmap documentation after @@ -188,8 +191,9 @@ told that other agents may be editing the repository. Evidence: `fd 'users.*guide|user.*guide' docs` found specific tool guides but no repository-wide `docs/users-guide.md`. Impact: this plan records that user-guide updates are conditional. If - implementation changes user-visible server behaviour, update the relevant - existing user-facing document or create a separate documentation decision. + implementation changes server behaviour that an operator of the Wildside + server application should know about, update the relevant existing + operator-facing document or create a separate documentation decision. - Observation: `backend/tests/features/startup_mode_composition.feature` already states that it covers all HTTP-facing ports for roadmap item 3.5.5. @@ -234,6 +238,13 @@ told that other agents may be editing the repository. tools and ports-as-traits approach remain appropriate. Date/Author: 2026-05-21 / Codex. +- Decision: interpret `docs/users-guide.md` as an operator guide for the + Wildside server application. + Rationale: the implementation plan needs to distinguish server operator + behaviour from product end-user behaviour when deciding whether a user-guide + update is required. + Date/Author: 2026-05-26 / Codex, based on user clarification. + ## Outcomes & retrospective This section is intentionally empty during draft. It must be updated after @@ -413,10 +424,11 @@ Update `docs/developers-guide.md` if the implementation introduces a reusable startup-mode test convention, helper naming convention, or BDD layout practice that future contributors need to follow. -Update `docs/users-guide.md` only if that file exists or if a user-visible -server behaviour change is introduced. If it remains absent and behaviour is -unchanged, record this in the plan rather than creating a generic guide solely -for this item. +Update `docs/users-guide.md` only if that file exists or if a server behaviour +change is introduced that an operator of the Wildside server application should +know about. If it remains absent and operator-visible behaviour is unchanged, +record this in the plan rather than creating a generic guide solely for this +item. Update `docs/backend-roadmap.md` to mark 3.5.5 done only after the helper seam, tests, CodeRabbit review, and full gates succeed. Follow the style used by @@ -591,3 +603,8 @@ Initial draft created on 2026-05-21. The draft captures roadmap item 3.5.5 scope, current code anchors, test strategy, Firecrawl research findings, Wyvern reconnaissance, approval gate, and implementation tolerances. No implementation has been performed under this plan. + +Revision on 2026-05-26 clarifies that `docs/users-guide.md` means an operator +guide for the Wildside server application. This narrows user-guide update +requirements to operator-visible server behaviour and leaves implementation +scope otherwise unchanged. From 9c5f34b7cc23890c1b013d9c1129d7f24a10cb97 Mon Sep 17 00:00:00 2001 From: leynos Date: Tue, 26 May 2026 20:19:24 +0200 Subject: [PATCH 3/7] Group user-state startup composition Add a private user-state port bundle in `state_builders.rs` so the identity-bearing HTTP ports are selected through one explicit helper seam. Record the approval, baseline gates, targeted checks, and milestone gate evidence in the execplan before moving to behavioural hardening. --- backend/src/server/state_builders.rs | 45 +++++++++++++++++-- ...d-3-5-5-harden-startup-mode-composition.md | 43 +++++++++++++++--- 2 files changed, 79 insertions(+), 9 deletions(-) diff --git a/backend/src/server/state_builders.rs b/backend/src/server/state_builders.rs index 5b35c218..87f7a8d0 100644 --- a/backend/src/server/state_builders.rs +++ b/backend/src/server/state_builders.rs @@ -82,6 +82,19 @@ struct ServicePairFactory { cast: ServiceCast, } +/// User-state ports selected together so DB-present mode cannot drift one port +/// at a time as HTTP state wiring evolves. +struct UserStatePortsBundle { + login: Arc, + users: Arc, + profile: Arc, + interests: Arc, + preferences: Arc, + preferences_query: Arc, + route_annotations: Arc, + route_annotations_query: Arc, +} + pub(super) fn build_login_users_pair_with_pool( pool: &Option, make_pair: impl FnOnce(&Pool) -> (Arc, Arc), @@ -232,6 +245,24 @@ build_idempotent_pair!( FixtureRouteAnnotationsQuery ); +fn compose_user_state_ports(config: &ServerConfig) -> UserStatePortsBundle { + let (login, users) = build_login_users_pair(config); + let (profile, interests) = build_profile_interests_pair(config); + let (preferences, preferences_query) = build_user_preferences_pair(config); + let (route_annotations, route_annotations_query) = build_route_annotations_pair(config); + + UserStatePortsBundle { + login, + users, + profile, + interests, + preferences, + preferences_query, + route_annotations, + route_annotations_query, + } +} + fn build_offline_bundles_pair( config: &ServerConfig, ) -> (Arc, Arc) { @@ -312,10 +343,16 @@ pub fn build_http_state( config: &ServerConfig, route_submission: Arc, ) -> web::Data { - let (login, users) = build_login_users_pair(config); - let (profile, interests) = build_profile_interests_pair(config); - let (preferences, preferences_query) = build_user_preferences_pair(config); - let (route_annotations, route_annotations_query) = build_route_annotations_pair(config); + let UserStatePortsBundle { + login, + users, + profile, + interests, + preferences, + preferences_query, + route_annotations, + route_annotations_query, + } = compose_user_state_ports(config); let (offline_bundles, offline_bundles_query) = build_offline_bundles_pair(config); let (walk_sessions, walk_sessions_query) = build_walk_sessions_pair(config); let enrichment_provenance = build_enrichment_provenance_repository(config); diff --git a/docs/execplans/backend-3-5-5-harden-startup-mode-composition.md b/docs/execplans/backend-3-5-5-harden-startup-mode-composition.md index 06e42a0f..40438c17 100644 --- a/docs/execplans/backend-3-5-5-harden-startup-mode-composition.md +++ b/docs/execplans/backend-3-5-5-harden-startup-mode-composition.md @@ -5,10 +5,10 @@ This ExecPlan (execution plan) is a living document. The sections `Decision Log`, and `Outcomes & Retrospective` must be kept up to date as work proceeds. -Status: DRAFT (awaiting approval before implementation) +Status: IN PROGRESS (implementation approved on 2026-05-26) -This plan covers roadmap item 3.5.5 only. It must be explicitly approved before -any implementation work begins. +This plan covers roadmap item 3.5.5 only. Implementation began after explicit +approval on 2026-05-26. ## Purpose / big picture @@ -174,8 +174,24 @@ told that other agents may be editing the repository. - [x] (2026-05-21) Drafted this pre-implementation ExecPlan. - [x] (2026-05-26) Clarified that `docs/users-guide.md` references server operators, not end users of the product experience. -- [ ] Obtain explicit user approval for this plan. -- [ ] Implement the approved helper seams and regression assertions. +- [x] (2026-05-26) Obtained explicit user approval for this plan. +- [x] (2026-05-26) Ran Stage A baseline gates before implementation: + `make check-fmt`, `make lint`, and `make test` all passed. The full test + gate reported 1220 Rust tests passed with 4 skipped, followed by passing + frontend/workspace tests. +- [x] (2026-05-26) Implemented the private `UserStatePortsBundle` and + `compose_user_state_ports` seam in `backend/src/server/state_builders.rs` + without changing the public `build_http_state` signature. +- [x] (2026-05-26) Ran Stage B targeted checks: + `cargo test -p backend startup_modes_reject_invalid_credentials_with_unauthorised_envelope -- --nocapture` + passed 2 selected tests, and + `cargo test -p backend --test state_builders_composition_unit -- --nocapture` + passed the fixture-mode composition unit test. +- [x] (2026-05-26) Ran Stage B milestone gates before CodeRabbit: + `make check-fmt`, `make lint`, and `make test` all passed. The full test + gate again reported 1220 Rust tests passed with 4 skipped, followed by + passing frontend/workspace tests. +- [ ] Strengthen startup-mode behavioural regression assertions. - [ ] Update architecture, developer, and roadmap documentation after implementation evidence exists. - [ ] Run `coderabbit review --agent` after the implementation milestone and @@ -211,6 +227,17 @@ told that other agents may be editing the repository. Impact: this item should avoid moving route submission unless a regression test proves the current split prevents deterministic user-state composition. +- Observation: the Stage B targeted command named in the draft plan selects + existing invalid-credential adapter tests rather than the + `state_builders_composition_unit` test. + Evidence: + `cargo test -p backend startup_modes_reject_invalid_credentials_with_unauthorised_envelope -- --nocapture` + passed two selected tests from `diesel_login_users_adapters.rs` and filtered + out the state-builder composition test. + Impact: keep the drafted command as historical evidence, but also run + `cargo test -p backend --test state_builders_composition_unit -- --nocapture` + for the helper-seam milestone. + ## Decision log - Decision: keep this PR as a pre-implementation planning PR. @@ -245,6 +272,12 @@ told that other agents may be editing the repository. update is required. Date/Author: 2026-05-26 / Codex, based on user clarification. +- Decision: keep `compose_user_state_ports` private. + Rationale: `build_http_state` remains the public composition entrypoint, and + integration tests can verify observable fixture/DB behaviour without + widening helper visibility. + Date/Author: 2026-05-26 / Codex. + ## Outcomes & retrospective This section is intentionally empty during draft. It must be updated after From 19828a85437dc4417c5082c2fe3b05a66f3cf671 Mon Sep 17 00:00:00 2001 From: leynos Date: Tue, 26 May 2026 20:32:13 +0200 Subject: [PATCH 4/7] Assert startup adapter selection Add HTTP-boundary regression assertions that tie startup-mode selection to current-user, users-list, and preferences evidence. --- backend/tests/startup_mode_composition_bdd.rs | 20 ++++-------- .../flow_support.rs | 29 +++++++++++++++++ ...d-3-5-5-harden-startup-mode-composition.md | 32 ++++++++++++++++++- 3 files changed, 66 insertions(+), 15 deletions(-) diff --git a/backend/tests/startup_mode_composition_bdd.rs b/backend/tests/startup_mode_composition_bdd.rs index 1ec9eb91..57f62f47 100644 --- a/backend/tests/startup_mode_composition_bdd.rs +++ b/backend/tests/startup_mode_composition_bdd.rs @@ -27,7 +27,8 @@ mod flows; use db_support::{seed_route, seed_user, setup_db_context}; use flow_support::{ - World, assert_internal, assert_profile_response, extract_validation_baseline, is_skipped, + World, assert_internal, assert_user_state_adapter_selection, extract_validation_baseline, + is_skipped, }; use flows::{run_comprehensive_flow, run_validation_error_flow}; @@ -82,11 +83,6 @@ fn assert_snapshot_ok( } fn assert_user_state_snapshots(world: &World) { - // Users list should return 200 - let users_list = world.users_list.as_ref().expect("users_list snapshot"); - assert_eq!(users_list.status, 200); - - // Preferences should return 200 with all required fields assert_snapshot_ok( &world.preferences, "preferences", @@ -162,7 +158,8 @@ fn assert_shared_happy_path_contracts(world: &World, profile_name: &str) { assert!(login.session_cookie.is_some()); let profile = world.profile.as_ref().expect("profile snapshot"); - assert_profile_response(profile, profile_name); + assert_eq!(profile.status, 200); + assert_user_state_adapter_selection(world, profile_name); assert_user_state_snapshots(world); assert_catalogue_snapshots(world); @@ -268,13 +265,8 @@ fn all_responses_match_db_backed_contracts(world: &mut World) { assert_shared_happy_path_contracts(world, DB_PROFILE_NAME); - // DB-specific assertion: verify userId matches seeded user - let preferences = world.preferences.as_ref().expect("preferences snapshot"); - let prefs_body = preferences.body.as_ref().expect("preferences body"); - assert_eq!( - prefs_body.get("userId").and_then(|v| v.as_str()), - Some(FIXTURE_AUTH_ID) - ); + // The shared assertion has already verified that user-state endpoints + // expose the seeded DB profile name rather than fixture data. } // ------------------------------------------------------------------------ diff --git a/backend/tests/startup_mode_composition_bdd/flow_support.rs b/backend/tests/startup_mode_composition_bdd/flow_support.rs index 2964d977..c5701efc 100644 --- a/backend/tests/startup_mode_composition_bdd/flow_support.rs +++ b/backend/tests/startup_mode_composition_bdd/flow_support.rs @@ -121,3 +121,32 @@ pub(crate) fn assert_profile_response(snapshot: &Snapshot, expected_display_name Some(expected_display_name) ); } + +/// Assert that user-state endpoints expose one deterministic adapter choice. +pub(crate) fn assert_user_state_adapter_selection(world: &World, expected_display_name: &str) { + let profile = world.profile.as_ref().expect("profile snapshot"); + assert_profile_response(profile, expected_display_name); + + let users_list = world.users_list.as_ref().expect("users_list snapshot"); + assert_eq!(users_list.status, 200); + let users = users_list + .body + .as_ref() + .and_then(|body| body.get("data")) + .and_then(Value::as_array) + .expect("users data array"); + assert!( + users.iter().any(|user| { + user.get("displayName").and_then(Value::as_str) == Some(expected_display_name) + }), + "users list should expose the selected user-state adapter data" + ); + + let preferences = world.preferences.as_ref().expect("preferences snapshot"); + assert_eq!(preferences.status, 200); + let preferences_body = preferences.body.as_ref().expect("preferences body"); + assert_eq!( + preferences_body.get("userId").and_then(Value::as_str), + Some(FIXTURE_AUTH_ID) + ); +} diff --git a/docs/execplans/backend-3-5-5-harden-startup-mode-composition.md b/docs/execplans/backend-3-5-5-harden-startup-mode-composition.md index 40438c17..a3ae3d50 100644 --- a/docs/execplans/backend-3-5-5-harden-startup-mode-composition.md +++ b/docs/execplans/backend-3-5-5-harden-startup-mode-composition.md @@ -191,7 +191,19 @@ told that other agents may be editing the repository. `make check-fmt`, `make lint`, and `make test` all passed. The full test gate again reported 1220 Rust tests passed with 4 skipped, followed by passing frontend/workspace tests. -- [ ] Strengthen startup-mode behavioural regression assertions. +- [x] (2026-05-26) Ran CodeRabbit after Stage B helper-seam commit + `af4d5d6`; the agent review completed with 0 findings. +- [x] (2026-05-26) Strengthened startup-mode behavioural regression + assertions with a shared user-state adapter-selection helper that verifies + current-user, users-list, and preferences evidence at the HTTP boundary. +- [x] (2026-05-26) Ran Stage C targeted BDD checks. The drafted filtered + command selected 0 generated scenarios, so + `cargo test -p backend --test startup_mode_composition_bdd -- --nocapture` + was run instead and passed all 12 tests. +- [x] (2026-05-26) Ran Stage C milestone gates before CodeRabbit: + `make check-fmt`, `make lint`, and `make test`. + All passed. The full test gate reported 1220 Rust tests passed with 4 + skipped, followed by passing frontend/workspace tests. - [ ] Update architecture, developer, and roadmap documentation after implementation evidence exists. - [ ] Run `coderabbit review --agent` after the implementation milestone and @@ -238,6 +250,16 @@ told that other agents may be editing the repository. `cargo test -p backend --test state_builders_composition_unit -- --nocapture` for the helper-seam milestone. +- Observation: filtering the startup-mode BDD binary with + `startup_mode_composition` selected support tests only and filtered out all + generated scenarios. + Evidence: + `cargo test -p backend startup_mode_composition --test startup_mode_composition_bdd -- --nocapture` + reported 0 executed tests and 12 filtered out. + Impact: use + `cargo test -p backend --test startup_mode_composition_bdd -- --nocapture` + for this suite when validating generated `rstest-bdd` scenarios. + ## Decision log - Decision: keep this PR as a pre-implementation planning PR. @@ -258,6 +280,14 @@ told that other agents may be editing the repository. and behaviour-level `rstest-bdd` assertions. Date/Author: 2026-05-21 / Codex. +- Decision: assert users-list adapter selection by display-name evidence + rather than by authenticated user ID in the shared startup-mode helper. + Rationale: fixture fallback exposes the current user profile and users-list + fixture as separate fixture records, while DB-present mode exposes the seeded + database display name through both endpoints. Preferences still assert the + authenticated fixture UUID to keep identity wiring covered. + Date/Author: 2026-05-26 / Codex. + - Decision: document Firecrawl findings as supporting context, not as authoritative design input. Rationale: repository docs and local conventions are more specific than From 6c1ed2ff111b99b5a71dbd78e5a132fb2c594c00 Mon Sep 17 00:00:00 2001 From: leynos Date: Tue, 26 May 2026 20:41:00 +0200 Subject: [PATCH 5/7] Document startup composition hardening Record the 3.5.5 composition decision, BDD validation convention, roadmap closure, and final gate evidence for the implementation. --- docs/backend-roadmap.md | 9 ++++++++- docs/developers-guide.md | 14 +++++++++++++ ...d-3-5-5-harden-startup-mode-composition.md | 20 ++++++++++++------- docs/wildside-backend-architecture.md | 12 +++++++++++ 4 files changed, 47 insertions(+), 8 deletions(-) diff --git a/docs/backend-roadmap.md b/docs/backend-roadmap.md index ff3a29a8..b7d9db0b 100644 --- a/docs/backend-roadmap.md +++ b/docs/backend-roadmap.md @@ -203,10 +203,17 @@ so persistence details stay confined to outbound adapters. - [x] 3.5.4. Define and implement the revision-safe interests update strategy (for example optimistic concurrency via expected revision checks), including the persistence contract and error mapping for stale-write conflicts. -- [ ] 3.5.5. Harden `backend/src/server/state_builders.rs` startup-mode +- [x] 3.5.5. Harden `backend/src/server/state_builders.rs` startup-mode composition with explicit helper seams and regression assertions so DB-present versus fixture-fallback adapter selection remains deterministic as user-state wiring evolves. + - Execution note (2026-05-26): implementation added the private + `compose_user_state_ports` helper seam and `UserStatePortsBundle`, then + strengthened `backend/tests/startup_mode_composition_bdd.rs` with + adapter-selection assertions at the HTTP boundary. `make check-fmt`, + `make lint`, `make test`, and CodeRabbit agent review passed before + closure; evidence is tracked in + `docs/execplans/backend-3-5-5-harden-startup-mode-composition.md`. - [ ] 3.5.6. Expand behavioural and repository-level regression coverage for the full login/users/profile/interests startup matrix, and include revision-conflict interests scenarios after 3.5.4 lands. diff --git a/docs/developers-guide.md b/docs/developers-guide.md index 266fe22e..cd81c196 100644 --- a/docs/developers-guide.md +++ b/docs/developers-guide.md @@ -333,6 +333,17 @@ When adding a new behaviour: When migrating existing suites, prefer incremental edits that preserve scenario intent and avoid broad rewrites that obscure regressions. +When validating generated `rstest-bdd` integration binaries, prefer running +the binary directly: + +```bash +cargo test -p backend --test startup_mode_composition_bdd -- --nocapture +``` + +Name filters can miss generated scenario functions or select only support +module tests. Use filters only after confirming the generated scenario names +match the filter text. + ## Shared workspace crate testing Shared workspace crates (such as `backend/crates/pagination`) provide @@ -493,6 +504,9 @@ Related domain helpers: - No feature flags required; BDD tests are in the `tests/` integration harness and run unconditionally with `cargo test` +To run BDD tests locally: + +```bash ### RedisTestServer harness Integration tests use `RedisTestServer` from `backend/src/test_support/redis.rs`: diff --git a/docs/execplans/backend-3-5-5-harden-startup-mode-composition.md b/docs/execplans/backend-3-5-5-harden-startup-mode-composition.md index a3ae3d50..4c882ca0 100644 --- a/docs/execplans/backend-3-5-5-harden-startup-mode-composition.md +++ b/docs/execplans/backend-3-5-5-harden-startup-mode-composition.md @@ -204,14 +204,20 @@ told that other agents may be editing the repository. `make check-fmt`, `make lint`, and `make test`. All passed. The full test gate reported 1220 Rust tests passed with 4 skipped, followed by passing frontend/workspace tests. -- [ ] Update architecture, developer, and roadmap documentation after - implementation evidence exists. -- [ ] Run `coderabbit review --agent` after the implementation milestone and - clear all concerns. -- [ ] Run and record final quality gates: +- [x] (2026-05-26) Committed Stage C as `ce7e588` and ran + `coderabbit review --agent`; the agent review completed with 0 findings. +- [x] (2026-05-26) Updated architecture, developer, and roadmap documentation + after implementation evidence existed. No `docs/users-guide.md` update was + made because this item preserves operator-visible server behaviour and the + repository has no existing `docs/users-guide.md` file. +- [x] (2026-05-26) Re-ran final quality gates after documentation updates: `make check-fmt`, `make lint`, and `make test`. -- [ ] Mark roadmap item 3.5.5 done only after the implementation and all gates - succeed. + All passed. The full test gate reported 1220 Rust tests passed with 4 + skipped, followed by passing frontend/workspace tests. +- [ ] Run final CodeRabbit review after the documentation milestone and clear + all concerns. +- [ ] Push the branch and update the draft PR with the final implementation + summary and validation evidence. ## Surprises & discoveries diff --git a/docs/wildside-backend-architecture.md b/docs/wildside-backend-architecture.md index 9c1ff36c..6000834a 100644 --- a/docs/wildside-backend-architecture.md +++ b/docs/wildside-backend-architecture.md @@ -809,6 +809,18 @@ services. > `DieselUserRepository` into a mixed-responsibility type. Roadmap item 3.5.4 > then completed the shared-revision write contract so interests writes now use > the same optimistic-concurrency semantics as full preferences updates. +> +> **Design decision (2026-05-26):** Roadmap item 3.5.5 hardens the HTTP state +> composition root with a private user-state port bundle in +> `backend/src/server/state_builders.rs`. `build_http_state` still owns the +> public startup boundary, but login, users, profile, interests, preferences, +> and route-annotation port selection now pass through one explicit helper seam +> before being inserted into `HttpStatePorts` and `HttpStateExtraPorts`. +> DB-present mode continues to select outbound Diesel adapters, DB-absent mode +> continues to select fixture fallbacks, and regression coverage asserts the +> selected adapter through current-user, users-list, and preferences responses. +> The helper remains private so persistence details stay confined to outbound +> adapters and HTTP handlers continue to consume domain ports only. For screen readers: The following sequence diagram shows the idempotent offline bundle upsert flow, including replay handling and duplicate-key race recovery. From 54e60af8cf22c9e09ece0ddcdd5e4c200e0ef8ab Mon Sep 17 00:00:00 2001 From: leynos Date: Tue, 26 May 2026 20:53:37 +0200 Subject: [PATCH 6/7] Record final review evidence Record the final CodeRabbit result and gate evidence in the 3.5.5 ExecPlan before publishing the branch for PR review. --- .../backend-3-5-5-harden-startup-mode-composition.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/execplans/backend-3-5-5-harden-startup-mode-composition.md b/docs/execplans/backend-3-5-5-harden-startup-mode-composition.md index 4c882ca0..abc6d8cc 100644 --- a/docs/execplans/backend-3-5-5-harden-startup-mode-composition.md +++ b/docs/execplans/backend-3-5-5-harden-startup-mode-composition.md @@ -214,8 +214,13 @@ told that other agents may be editing the repository. `make check-fmt`, `make lint`, and `make test`. All passed. The full test gate reported 1220 Rust tests passed with 4 skipped, followed by passing frontend/workspace tests. -- [ ] Run final CodeRabbit review after the documentation milestone and clear - all concerns. +- [x] (2026-05-26) Ran final CodeRabbit review after the documentation + milestone; the agent review completed with 0 findings. +- [x] (2026-05-26) Ran final plan-note commit gates: + `make check-fmt`, `make lint`, and `make test`. All passed. The full test + gate reported 1220 Rust tests passed with 4 skipped, followed by passing + frontend/workspace tests. +- [ ] Commit the recorded final review result and push the branch. - [ ] Push the branch and update the draft PR with the final implementation summary and validation evidence. From 21dff70c79ca4a110d9a5331951df4752803aff2 Mon Sep 17 00:00:00 2001 From: leynos Date: Wed, 27 May 2026 21:42:26 +0200 Subject: [PATCH 7/7] Wrap long lines in startup-mode execplan This shortens overlong markdown lines in the execplan document to satisfy the repository's MD013 line-length policy and keeps the document compatible with `markdownlint-cli2`. --- .../backend-3-5-5-harden-startup-mode-composition.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/docs/execplans/backend-3-5-5-harden-startup-mode-composition.md b/docs/execplans/backend-3-5-5-harden-startup-mode-composition.md index abc6d8cc..e39347b9 100644 --- a/docs/execplans/backend-3-5-5-harden-startup-mode-composition.md +++ b/docs/execplans/backend-3-5-5-harden-startup-mode-composition.md @@ -183,7 +183,9 @@ told that other agents may be editing the repository. `compose_user_state_ports` seam in `backend/src/server/state_builders.rs` without changing the public `build_http_state` signature. - [x] (2026-05-26) Ran Stage B targeted checks: - `cargo test -p backend startup_modes_reject_invalid_credentials_with_unauthorised_envelope -- --nocapture` + `cargo test -p backend` + `startup_modes_reject_invalid_credentials_with_unauthorised_envelope` + `-- --nocapture` passed 2 selected tests, and `cargo test -p backend --test state_builders_composition_unit -- --nocapture` passed the fixture-mode composition unit test. @@ -254,7 +256,9 @@ told that other agents may be editing the repository. existing invalid-credential adapter tests rather than the `state_builders_composition_unit` test. Evidence: - `cargo test -p backend startup_modes_reject_invalid_credentials_with_unauthorised_envelope -- --nocapture` + `cargo test -p backend` + `startup_modes_reject_invalid_credentials_with_unauthorised_envelope` + `-- --nocapture` passed two selected tests from `diesel_login_users_adapters.rs` and filtered out the state-builder composition test. Impact: keep the drafted command as historical evidence, but also run @@ -265,7 +269,9 @@ told that other agents may be editing the repository. `startup_mode_composition` selected support tests only and filtered out all generated scenarios. Evidence: - `cargo test -p backend startup_mode_composition --test startup_mode_composition_bdd -- --nocapture` + `cargo test -p backend` + `startup_mode_composition` + `--test startup_mode_composition_bdd -- --nocapture` reported 0 executed tests and 12 filtered out. Impact: use `cargo test -p backend --test startup_mode_composition_bdd -- --nocapture`