Skip to content

Hygiene backlog: duplicated logic single-owners, runner path divergence, quadratic scheduling, workspace #43

Description

@lann

From the adversarial review (2026-08-04). Backlog of verified duplication/hygiene items; none urgent, each a drift vector. Checkboxes for piecemeal cleanup:

Single-owner refactors

  • Decline-pair lint implemented 3x: sdk-macro (string-level strip_prefix('!'), bypassing core::tags despite already depending on core), Lockfile::validate, and the runner's materialized-cases variant. Static vs materialized justifies two call sites, not three implementations — extract core::tags::decline_pair_check(...).
  • "Name is under generated prefix" predicate implemented 5x with one subtly different rule (sdk/lib.rs generated_named allows equality): CaseName::is_under(&str) / covering_prefix() in core; give Inventory a prefix_of mirroring Lockfile::prefix_of (the runner re-implements longest-prefix inline because it's missing).
  • core::name::TAGS_SECTION has zero consumers while the string is hardcoded in formats/inventory.rs, sdk-macro (#[link_section] emission), and sdk (case!). Formats should import it; the proc-macro can interpolate it as a literal; document the one genuinely stuck copy (macro_rules! case!).
  • Two normalization stories: core::normalize_segment (pub, re-exported, encodes a README rule, zero callers) vs the macro's private ident_to_segment (_->-, the mapping actually applied). Route the macro through core or demote/document normalize_segment for the future JS SDK.

Runner

  • Parallel vs sequential paths are copy-pastes that diverged: on new_session failure the parallel path records a per-case trap and continues; the sequential path aborts the run, leaving JSONL unterminated. Extract a shared helper; per-case-trap + continue is the policy consistent with "poisoning is contained".
  • Quadratic scheduling pre-pass: tags_of is a linear scan + per-hit Tags::new(clone) called once per name across three passes (drift check, decline-pair, plan) — O(names x cases) at exactly the ~8k scale M1 targets. Same pattern in aggregate (loop-invariant case_tags rebuilt per target). One upfront map each.
  • COMPONENT_TEST_PROFILE env knob is documented nowhere and double-enumerates per session.

Aggregate/CLI

  • Undeclared target cascades: after pushing "not declared in the manifest", the loop proceeds with missing = &[], producing a false applicability error per decline-tagged case. continue after the root-cause error.
  • Failure predicate Fail | NotReached duplicated between Aggregate::has_failures and the CLI counting loop.

JS runner

  • runner.mjs: on a non-verdict throw the concurrent drain promise is abandoned and ctx/observer never disposed; if the trap errors the stream, the unhandled rejection kills Node and masks the real trap. try/finally + await drain.catch(() => {}). Verdict-mapping switch duplicated across both .mjs runners.

Provider (moved from #42)

  • Silent diagnostic drop window: a diagnostic call that finds the writer taken by a concurrent call black-holes its message while the runner reports diagnostics-complete: true (components/provider/src/lib.rs, take/put-back protocol). Unreachable through in-repo runners today (a parked reader makes write_one complete synchronously — verified 50/50 delivery under 50-way join), but the failure mode is silent loss marked complete; a real fix needs a pending-message queue drained on put-back. The sibling drop/close inversion was fixed in 38faf9a.

Workspace

  • default-members = ["crates/*"] would collapse the 4-crate exclusion list maintained in three places (justfile lint/test + AGENTS.md); the "native tests fail" rationale is already gone from AGENTS.md (7902841).
  • components/*/Cargo.toml hardcode version/edition (done in 450d94d)
  • component-test-runner lacks description; wasmparser = "0.243" in formats is stale vs the 0.25x already in-tree via wasmtime (duplicate build).

From #34 (parked at crate split time)

Metadata

Metadata

Assignees

No one assigned

    Labels

    toolingCLI, lockfiles, composition helpers

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions