Plan G: DRY + YAGNI Cleanup

On this page

Status

Step Description Status

1

Plan filing — body lands in the docs-only Plan D refresh MR alongside Plans D/H/I/J/K. nav.adoc + CHANGELOG. No code changes.

Done (2026-05-15 via !307)

2

F-026 implementation: extract app.execute_within_tx(handler) helper (or axum extractor) on craig_api::AppState. Sweep the identified handler sites across the 5 stateful API services. Per-service batches.

Done (2026-05-16) — 2a (!318) + 2b (craig-cases 16/18 !319) + 2c (craig-placement 5/7 !321) + 2d (craig-financial 2/3 !323) + 2e (craig-exchange 2/4 !325) + 2f (craig-security 12/12) shipped 2026-05-16. All 37 helper-compatible sites migrated; 7 manual-handling sites preserved by design (compensation-on-blob-Err, retry loops, FOR UPDATE re-reads — structurally don’t fit the helper’s begin → commit → drop-rollback shape). Per-service: craig-cases 18 (16 swept + 2 manual); craig-placement 7 (5 swept + 2 manual); craig-financial 3 (2 swept + 1 manual: adjustments.rs:152 FOR UPDATE + re-read); craig-exchange 4 (2 swept + 2 manual: icpc.rs:594, 624 compensation-on-blob-Err); craig-security 12 (all swept).

3

F-027 implementation: add typed source variants to ApiError (Db(#[from] sqlx::Error) + ObjectStore + Http + Csv + Uuid) so ? propagation collapses ~473 .map_err(ApiError::internal)? postfix sites across the 5 stateful API services. Mirrors Step 2’s split: Step 3a is the helper-only MR (crates/craig-common/src/error.rs only); Steps 3b-3f are the per-service sweeps. Deviation from 2026-05-15 draft (Path A as written) per ADR-030 §3: the draft named impl From<events::PublishError> for ApiError; there is no events::PublishError type (publish_* returns Result<(), sqlx::Error>). The chosen shape — typed source variants on ApiError directly — covers ~473 sites (~79% sqlx + tier-2 object_store/reqwest/csv/uuid) versus the draft’s ~40 publish-only sites, advances Plan H F-032’s typed-everywhere policy incrementally, and avoids YAGNI scaffolding (no wrapper enum that adds zero information beyond sqlx::Error). The legacy Internal { detail: String } variant + ApiError::internal(impl Display) constructor remain as a transitional escape hatch for ~79 non-typed sites (string-literal misconfig + anyhow inners + custom enums); F-032 deprecates this catch-all in the broader String-payload audit.

Done (2026-05-16) — 3a (!329) + 3b (craig-cases 169 · 3 preserved match-arms · 236/236) + 3c (craig-placement 75 · 7 preserved string-literal stubs · 99/99) + 3d (craig-financial 39 · 0 preserved · 106/106) + 3e (craig-exchange 54 · 1 preserved adapter-edge · 92/92) + 3f (craig-security 70 · 5 preserved match-arms · 192/192). Total: 407 sites collapsed across the 5 stateful services; 16 preserved by design (Plan H F-032 territory); net -489 LOC; 725/725 service tests + 34/34 craig-common unchanged. Closes #417.

4

F-028 implementation: extract test-fixture helpers into craig-test-lib for the patterns duplicated across services. Specifically the healthz_carries_documented_csp shape added by !306 (7 backend services × 20 lines = ~160 lines).

Done (2026-05-16) — 3 async helpers (assert_backend_api_healthz_csp + assert_intake_standalone_healthz_csp + assert_web_root_csp) added to crates/craig-test-lib/src/csp.rs; 9 service test files collapsed (7 backend services + craig-intake api/standalone modes + craig-web BFF). Net -23 LOC; cargo nextest run -p craig-test-lib csp 7/7 pass; workspace cargo build --tests + cargo clippy --tests clean.

5

F-029 implementation: audit each YAGNI-flagged config knob; either delete (if truly never overridden) or wire override paths + document in .env.example. Targets: payment_period, introspection_cache_ttl_seconds, introspection_cache_max_entries, introspection_serve_on_outage, public_rate_limit, captcha_secret, captcha_verify_url.

Done (2026-05-17) — 3-agent audit found ALL 7 knobs are real operator-tunable knobs (zero DELETE candidates). All 7 promoted: CRAIG_FINANCIALPAYMENT_PERIOD added to financial section; CRAIG_INTAKEPUBLIC_RATE_LIMIT + CRAIG_INTAKECAPTCHA_SECRET + CRAIG_INTAKECAPTCHA_VERIFY_URL added to intake section; 5 introspection knobs (incl. 2 bonus: INTROSPECTION_ENDPOINT + INTROSPECTION_AUTH_METHOD surfaced during audit) wired into new dedicated "Introspection-mode tuning" section keyed per-service. New Introspection-mode tuning subsection added to docs/modules/ROOT/pages/idp-integration.adoc § CRAIG-Side Configuration with per-knob default + when-to-override guidance.

6

F-065 implementation: cross-handler DRY consolidation scan. Inventory the per-handler helpers extracted during Plan I F-033 + F-034 (and any other code-quality plan’s decompositions); cluster by shape; promote ≥3-instance clusters to shared helpers in craig-api / craig-store / craig-common. Per-cluster MR. BLOCKED until Plans D / G / H / I / J / K / L all reach Done — the scan’s value depends on a stable post-decomposition substrate. See issue #462 for candidate categories surfaced during !320 / !322 (multipart upload boilerplate, Tx1+Tx2-audit pattern, idempotent-create, PII encrypt/decrypt boundary orchestration, actor-sub parsing).

Done (2026-06-10) — gate satisfied (Plans D/G2-5/H/I/J/K/L all Done; M/N/P/Q/R landed since, so the substrate was even more stable than the gating clause demanded). 2-lens scan: thesis-driven verification of the 5 #462 candidate categories + unbiased sweep of 290 private helpers across 127 files / 9 services. Verdicts — multipart upload parsing: CLUSTER (5 sites) → consolidated into NEW craig_api::multipart::parse_upload_multipart (+ UploadedFile carrier + buffer_file_field streaming helper; 5 thin per-site wrappers preserve each handler’s parts struct, metadata defaults, and validate_upload placement exactly; craig-web/craig-intake proxy parsers excluded by design — forwarding semantics, not storage); tx1+tx2-best-effort-audit: BELOW-THRESHOLD (1 site — reports/conversion.rs; the 3 attachment compensation txs are a different pattern, preserved by design per Plan G F-026); idempotent-create short-circuit: BELOW-THRESHOLD (1 site); PII encrypt/decrypt orchestration: ALREADY-SOLVED (cases api/encryption/ module; 12 uniform call sites, zero duplicated orchestration); actor-sub parsing: DEAD (claims.sub canonicalization left 1 defensive middleware parse; no helper warranted). Unbiased sweep found 0 additional ≥3 clusters; 5 below-threshold 2-instance twins documented in the MR description per the audit-as-deliverable bias. 5 new unit tests (3 happy / 2 sad); B1 ratchet 8→7 + B6 65→64 via --write-lock. Closes #462. !671 / 53dc0d79

7

Plan completion audit + archive.

Done (2026-05-17) — substantive plan-completion audit per .claude/docs/delivery-protocol.md § Plan Completion Audit. All 5 executable steps (1-5) shipped 2026-05-15 → 2026-05-17 across 13 MRs (!307 + !318 / !319 / !321 / !323 / !325 / !327 / !329 / !331 / !332 / !333 / !334 / !335 / !336 / !337 + this audit MR). Step 6 (F-065 cross-handler consolidation scan) remains Blocked per §Step 6 gating clause; plan stays in nav.adoc § Active and cargo xtask docs plan-archive correctly excludes it (plan_fully_done() returns false while any step is non-Done). Plan G eligible for archive at the same time the cross-handler scan unblocks (after Plans D/G/H/I/J/K/L all reach Done — see #462). Closes #420. (Step 6 subsequently unblocked + Done 2026-06-10 via !671 / 53dc0d79; archive executed in the Plan G archive MR.)

Epic: &29 (epic: DRY + YAGNI Cleanup (Plan G))
Issues: #416 (Step 2) · #417 (Step 3) · #418 (Step 4) · #419 (Step 5) · #462 (Step 6 — cross-handler DRY scan; BLOCKED) · #420 (Step 7 — plan completion)
Branch prefix: chore/dry-yagni- / refactor/dry-yagni-
Milestone: TBD (lowest-priority of the maintainability suite; ship on reviewer capacity)

Context

Two themes surfaced during the 2026-05-15 post-Plan-C/F audit:

  • F-026 / F-027 (DRY — P0): 45 handlers across 5 services follow the same let mut tx = app.db.inner().begin().await.map_err(ApiError::internal)? → store call → events::publish_*(&mut tx, …​)tx.commit() shape. ~40+ event-publish error chains repeat the same .await.map_err(ApiError::internal)? postfix. The duplication is purely mechanical — handlers differ only in what they pass to the store. Both are extractable as a thin helper on AppState (or as an axum extractor).

  • F-028 (DRY — P1): Recent MRs added test-fixture boilerplate across services (e.g. the 7-services-textually-identical healthz_carries_documented_csp test bodies in !306). When a future contributor adds a 9th service or needs to extend the CSP assertion, they have to remember to update 7 files. Pulling the shape into craig_test_lib::assert_service_csp makes the call site 1 line.

  • F-029 (YAGNI — P2): 7 config knobs identified (re-anchored 2026-05-15 from earlier "6" count) that have a single hardcoded default + are never overridden in .env.example, docker-compose.yml, or any deployment surface: payment_period, introspection_cache_ttl_seconds, introspection_cache_max_entries, introspection_serve_on_outage, public_rate_limit, captcha_secret, captcha_verify_url. Either they’re operational constants masquerading as config (delete the knob; document the constant) or they’re real knobs that need to be wired into deployment surfaces (otherwise operators can’t toggle them).

This plan is the application-layer slice of the broader code-quality work; Plans D (taxonomy + visibility + skip + ignore-audit), H (idiomatic Rust + clippy), I (KISS / SOLID / separation), J (env-var sprawl), and K (canopy backports) cover the rest. Plans D / G / H / I / J / K run independently except where Step 5 touches the same handler files (sync via per-service batches).

Cross-cutting invariants

  1. No behavior change. Every extraction must produce identical handler behavior. New helpers are mechanical refactors, not logic changes. Pre/post cargo nextest run --workspace must show identical pass/fail.

  2. Per-service batches. F-026 + F-027 sweeps run in independent batches per service crate (5 batches for the 5 services that hold the 45 sites). Each batch is an independent MR; conflicts only on craig-api/src/lib.rs (the helper itself).

  3. Helper API design before sweep — Step 2 splits into 2a + 2b-2f. Step 2a is the helper-only MR (zero call-site changes; craig-api/src/lib.rs only). Steps 2b-2f are the 5 service-batch MRs (per-service callsite migration). Avoids landing 45 changes against an API the reviewer hasn’t seen yet. Step 3 (F-027) follows the same shape.

  4. Plan I F-033 sequencing. Plan I F-033 decomposes fat handlers (convert_report 148 lines + 5 others); many of those handlers ARE the 45 tx-boilerplate sites. Ordering constraint: Plan G’s Step 2 + Step 3 sweeps must precede Plan I F-033 within the same service crate, OR Plan G must be fully merged before F-033 starts. Don’t co-merge — the diff stacks become unreviewable.

  5. F-029 audit-then-decide. Each YAGNI knob is either deleted (constant inlined; doc note added if non-obvious) or promoted (wired into .env.example + docker-compose.yml). No middle ground — "leave it as undocumented config" is the current state and explicitly not acceptable. F-029’s promote-path overlaps with Plan J F-038’s .env.example expansion — if Plan J ships first, F-029 just deletes the surviving knobs; if Plan G ships first, the promoted knobs feed Plan J’s audit.

Scope

In scope (5 findings):

  • F-026 transaction-boilerplate helper extraction

  • F-027 event-publish error-chain collapse

  • F-028 test-fixture helper extraction (CSP + similar duplication patterns)

  • F-029 unused config knob audit (delete or promote)

  • F-065 cross-handler DRY consolidation scan (post-decomposition; gated on Plans D / G / H / I / J / K / L completion)

Out of scope:

  • Strum DTO conversion — Plan D

  • pub(crate) discipline — Plan D

  • Idiomatic Rust + clippy — Plan H

  • KISS / SOLID / separation — Plan I

  • Env-var sprawl / .env.example expansion — Plan J

  • Canopy xtask backports — Plan K

Steps

Step 2: F-026 transaction-boilerplate helper

Audit grep (45 sites enumerable via):

rg -n 'let mut tx = app\.db\.inner\(\)\.begin\(\)' services/craig-{cases,placement,financial,exchange,security}/src/api/

Per-service counts (2026-05-15 baseline): craig-cases 18 · craig-placement 7 · craig-financial 3 · craig-exchange 5 · craig-security 12. Re-anchor at branch time.

Files:

  • crates/craig-api/src/lib.rs — add method on AppState (NOT a trait — keeps callsites app.execute_within_tx(…​) rather than <AppState as AppStateExt>::execute_within_tx(&app, …​)). Signature as shipped (Step 2a, !MR-pending 2026-05-16):

    impl AppState {
        pub async fn execute_within_tx<F, T>(
            &self,
            handler: F,
        ) -> Result<T, craig_common::ApiError>
        where
            F: for<'c> AsyncFnOnce(
                &'c mut sqlx::Transaction<'_, sqlx::Postgres>,
            ) -> Result<T, craig_common::ApiError>,
        {
            let mut tx = self
                .db
                .inner()
                .begin()
                .await
                .map_err(craig_common::ApiError::internal)?;
            match handler(&mut tx).await {
                Ok(value) => {
                    tx.commit()
                        .await
                        .map_err(craig_common::ApiError::internal)?;
                    Ok(value)
                }
                // sqlx::Transaction rolls back on Drop; explicit rollback is optional.
                Err(e) => Err(e),
            }
        }
    }
    handler closure receives &mut sqlx::Transaction<', Postgres> (NOT &mut PgConnection) — this is the type all events::publish* and store-fn signatures already accept, and what Step 3’s publish_within_tx will also take.

    Deviation from plan body 2026-05-15 draft: the draft called for FnOnce + Future with Fut named as a generic parameter. That shape does not satisfy Rust 2024’s lifetime rules — the returned future needs to capture the &'c mut Transaction borrow, but a single named Fut type cannot depend on the HRTB lifetime 'c. AsyncFnOnce (stable since Rust 1.85; CRAIG is on 1.94) is the correct form and is what the helper ships with. The draft listed AsyncFnOnce as "a valid alternative"; promoting it to the chosen path per ADR-030 §3 (deviation → update Design/Scope, not errata).

  • Per-service batches (5 MRs):

    • services/craig-cases/src/api/*.rs — 18 sites

    • services/craig-placement/src/api/*.rs — 7 sites

    • services/craig-financial/src/api/*.rs — 3 sites

    • services/craig-exchange/src/api/*.rs — 5 sites

    • services/craig-security/src/api/*.rs — 12 sites

Branch (per-batch): refactor/dry-yagni-step2-tx-helper-<service> (5 branches)

Verification (per batch):

  1. cargo build -p <service> clean

  2. cargo nextest run -p <service> — pass/fail count unchanged from pre-extraction baseline

  3. grep -c "let mut tx" services/<service>/src/api/ — drops to ~0

CHANGELOG draft: per-MR, list of handlers refactored + before/after line count.

Step 3: F-027 event-publish error-chain collapse

Chosen shape (substantiated 2026-05-16 via 7-agent architectural review). Add typed source variants to ApiError in crates/craig-common/src/error.rs so ? propagates sqlx::Error / object_store::Error / reqwest::Error / csv::Error / uuid::Error directly without .map_err(ApiError::internal)? postfix. This collapses ~473 sites (sqlx 438 + object_store ~10 + reqwest ~16 + csv ~3 + uuid ~6) — ~85% of the workspace’s .map_err(ApiError::internal)? surface.

#[derive(Debug, thiserror::Error)]
pub enum ApiError {
    // ... NotFound / BadRequest / Unauthorized / Forbidden / Conflict unchanged ...

    #[error("database error: {0}")]
    Db(#[from] sqlx::Error),

    #[error("object store error: {0}")]
    ObjectStore(#[from] object_store::Error),

    #[error("http error: {0}")]
    Http(#[from] reqwest::Error),

    #[error("csv error: {0}")]
    Csv(#[from] csv::Error),

    #[error("uuid error: {0}")]
    Uuid(#[from] uuid::Error),

    // Transitional catch-all — F-032 (Plan H Step 5) deprecates this:
    #[error("internal error: {detail}")]
    Internal { detail: String },
}
Deviation from 2026-05-15 draft per ADR-030 §3. The draft named impl From<events::PublishError> for ApiError; no events::PublishError type exists (publish_* returns Result<(), sqlx::Error>). Path B (publish_within_tx helper) was rejected — each publish_* fn has different params; no common Publishable trait covers them. The typed-source-variant shape is the architecturally sound expression of "Path A" given the actual code, advances Plan H F-032’s typed-everywhere policy (.claude/docs/coding-conventions.md:23-27) at the trait boundary instead of locking in the Internal { detail: String } antipattern via a blanket From<sqlx::Error> → Internal-stringify impl, and covers ~85% of the postfix surface (vs the draft’s ~13% publish-only scope).

Preserved-by-design sites (DO NOT migrate to ?):

  1. services/craig-cases/src/api/reports.rs:148-161Err(e) arm with rollback + DB error code "23505" idempotency probe. Match-on-variant, not ?.

  2. services/craig-cases/src/api/report_persons.rs:258-269match { Ok | Err(unique_violation) | Err(e) } returning 409 Conflict.

  3. services/craig-cases/src/matching/mod.rs:377-393Ok | Err(unique_violation) | Err(other) idempotent-replay swallow with logging.

These three sites pattern-match on sqlx::Error::Database(…​).is_unique_violation() to produce typed ApiError::Conflict or silent retry behavior — semantically distinct from "any sqlx error is an internal error." The sweep agents preserve them with a // PLAN-G-F027-RETAIN-MATCH: comment annotation.

Out-of-scope (handled by F-032 in Plan H Step 5):

  • BadRequest { detail: String } / NotFound { entity, id: String } / Conflict { entity, detail: String } — client-facing String payloads. F-032 audits these for typed *Kind enums.

  • The ~79 non-typed sites (28 string-literal misconfig + ~17 anyhow::Error inners from crypto/token/matching + custom enums JwsReplayError/EngineError/ApiError-passthrough). The transitional Internal { detail: String } variant + ApiError::internal(impl Display) constructor remain available; F-032 deprecates them in the broader audit.

Per-MR structure (mirrors Step 2’s split):

  • Step 3a — helper-only MR (crates/craig-common/src/error.rs only). Add 5 typed variants + #[from] derives. Extend IntoResponse with explicit arms for each variant (all map to HTTP 500 with the redacted body; inner detail logged at tracing::error!). Add 4 unit tests covering round-trip + status + redaction. Zero callsite changes. Done (2026-05-16) on branch refactor/dry-yagni-step3a-typed-source-variants.

  • Steps 3b-3f — per-service sweeps. Each batch removes .map_err(ApiError::internal)? from sqlx/object_store/reqwest/csv/uuid sites in one service crate. Preserve the 3 explicit-match sites above + any tracing::warn!(…​) audit comments. Branch: refactor/dry-yagni-step3<letter>-<service>.

Verification (per batch):

  1. cargo build -p <service> clean

  2. cargo nextest run -p <service> — pass/fail count unchanged from pre-extraction baseline

  3. grep -c "map_err(ApiError::internal)" services/<service>/src/api/ — drops by the typed-source quota (sqlx + tier-2 sites)

  4. Preserved-by-design sites still present (audit comment + match-on-variant intact)

Step 4: F-028 test-fixture helper extraction

Files:

  • crates/craig-test-lib/src/csp.rs — add assert_service_csp<F: Future<Output = HeaderMap>>(…​) async helper that takes a URL + the appropriate assert_*_csp callback. Each service’s healthz_carries_documented_csp collapses to ~5 lines.

  • services/craig-{rules,cases,placement,exchange,financial,reporting,security}/tests/api/health.rs — collapse to helper invocation.

  • services/craig-intake/tests/api/health.rs — both modes use the helper with different CSP assertions.

  • services/craig-web/tests/csp.rs — same.

Branch: refactor/dry-yagni-step4-csp-test-helper

Verification:

  1. cargo nextest run --workspace healthz_carries_documented_csp — 11/11 pass (same as today)

  2. Line count delta: -120 LOC across the 9 test files (160 LOC removed; ~40 LOC added in craig-test-lib)

Step 5: F-029 unused config knob audit

For each of the 6 flagged knobs:

  • payment_period (crates/craig-common/src/settings.rs:149) — verify in craig-financial handlers whether it’s actually read; if not consumed, delete. If consumed but never overridden, inline the default + delete the config field.

  • introspection_cache_ttl_seconds, introspection_cache_max_entries, introspection_serve_on_outage (settings.rs:162-173) — Plan F shipped without operator-visible overrides. Either wire into .env.example + document in docs/modules/ROOT/pages/idp-integration.adoc OR delete + inline the constants. Probably the former — operators tuning introspection latency need these.

  • public_rate_limit (services/craig-intake/src/config.rs:90) — verify whether it’s actually used by the rate-limit middleware; document if so.

  • captcha_secret + captcha_verify_url — already partially documented in .env.example; check coverage.

Branch: refactor/dry-yagni-step5-config-audit

MR title: refactor(craig-common, craig-intake, deployment-guide): audit unused config knobs — promote or delete [Step 5 of dry-yagni]

Verification:

  1. cargo nextest run --workspace — no test regression from default changes

  2. .env.example diff covers each promoted knob with a comment explaining when an operator would override

Step 6: F-065 cross-handler DRY consolidation scan (was gated on Plans D / G / H / I / J / K / L — completed 2026-06-10 via !671)

Gating. Do not start until every other code-quality plan (D, G Steps 2-5, H, I, J, K, L) is on main with all-Done Status rows. The scan’s value depends on a stable post-decomposition substrate — running it earlier produces speculative helpers that decompose work-in-flight.

Approach:

  1. Inventory. Grep + cluster the per-handler helpers landed across services/*/src/api/ during Plan I F-033 + F-034 decompositions. The 2026-05-16 starter helpers (prepare_conversion, create_referral_from_conversion, parse_upload_multipart, place_upload_object, etc.) are the substrate.

  2. Cluster by shape. For each ≥3-instance cluster, draft a shared helper signature. Candidate categories surfaced during !320 / !322 (see issue #462):

    • Multipart upload boilerplate — likely shared as craig-store::upload_helpers::receive_multipart_to_store(…​) → (UploadParts, object_key, file_size).

    • Tx1-commit + Tx2-best-effort-audit pattern — only if 3+ sites emerge; candidate AppState::execute_with_audit(primary, audit).

    • Idempotent-create — find_by_X short-circuit; candidate store::find_or_insert_*.

    • PII encrypt/decrypt-at-boundary orchestration — the call shape, not the existing helpers themselves.

    • Actor-sub parsing (claims.sub.parse::<Uuid>()) — canonicalize in craig-auth.

  3. Per-cluster MR. One MR per consolidation. Behavior preserved; tests pass; line-count delta should be net negative.

Out of scope for this step:

  • Speculative helpers without ≥3 call sites.

  • Helpers crossing service boundaries that would require new crates.

  • Pre-completion consolidation (substrate not stable).

Branch prefix: refactor/dry-yagni-step6-consolidate-<cluster-name>

Tracking: #462.

Step 7: Plan completion audit + archive

Mirror Plan B Step 8 / Plan C Step 18 / Plan F Step 6 pattern.

Files Touched

File Step Change

crates/craig-api/src/lib.rs

2,3

EDIT (add execute_within_tx + publish_within_tx)

services/{cases,placement,financial,exchange,security}/src/api/*.rs

2,3

EDIT (~45 + ~40 sites)

crates/craig-test-lib/src/csp.rs

4

EDIT (add assert_service_csp helper)

services/*/tests/api/health.rs + services/craig-web/tests/csp.rs

4

EDIT (collapse to helper invocations)

crates/craig-common/src/settings.rs

5

EDIT (delete or promote 6 config knobs)

services/craig-intake/src/config.rs

5

EDIT

.env.example

5

EDIT (promoted knobs)

deployment-guide.adoc, idp-integration.adoc

5

EDIT (document promoted knobs)

Verification

After every step: cargo xtask validate --skip-docker + cargo nextest run --workspace.

Risks

Risk Mitigation

Step 2’s execute_within_tx helper covers only the "simple" tx pattern; some handlers have multiple tx blocks or conditional commit logic

Per-handler audit during refactor; helpers that don’t fit stay as-is and are documented as exceptions

Step 5 deletion of a "never overridden" knob breaks a deployment surface that wasn’t audited

Search GitLab issues + glab mr search for any prior reference to the knob before deletion; document the deletion in MR body

Step 4’s assert_service_csp helper grows new signature parameters as future CSP tests need more flexibility

Accept and refactor in-place; the helper is internal to craig-test-lib

After this plan lands

  • 45 handler tx-open/commit boilerplate sites collapsed to helper calls

  • ~40 event-publish error chains collapsed to helper calls

  • 7+ duplicate CSP test bodies replaced with 1-line helper invocations

  • 6 unused config knobs either deleted or promoted to documented config

Edit this page · latest