Skip to content

Epic: Maintenance debt — retire superseded mechanisms, reduce process weight #528

Description

@mparrett

Umbrella for behavior-preserving cleanup: mechanisms that have been superseded but never retired, logic that exists in several copies, and build/CI weight that outlived its reason. Distinct from runtime performance and concurrency (#464), lowering/codegen (#258), and Clojure-semantics parity (#257): nothing here changes what programs do — only how much code and process we maintain to do it.

The recurring pattern, across otherwise-unrelated corners: a migration lands and the predecessor stays. Each leftover is small; together they're where a lot of the "why are there two of these?" questions come from. A roll-up makes them visible and cheap to veto before anyone spends PR effort.

The lists below are the instances I've verified so far — exemplars of the pattern plus known finds, not an exhaustive inventory. New sightings accumulate here as sub-issues.

Gate for everything under this epic: behavior unchanged. Anything that would redraw a behavior line (however small) gets its own issue and discussion first.

Superseded but still alive

  • Per-file //go:embed source vars (AsyncSrc, IRBuildSrc, IROpsSrc, the 19 IR pass vars, and friends) superseded by the all:core FS embed in core_embed_fs.go. Roughly 33 vars with zero references (including the whole files irpasses.go, irbuild.go, irops.go, asyncembed.go), and each embeds a second copy of its core source into every non-bootstrap binary. Only CoreSrc is live.
  • scripts/lginterop.lg, superseded by cmd/lginterop Corrected (see comments): it's the live codegen back end — cmd/lginterop reads it from the repo root on every invocation (writeGenScript, main.go:419). The item is now code homing: //go:embed the macro library (or move it under cmd/lginterop/) so the binary is self-contained and the pairing is visible from both sides.
  • The wasm/ playground: pre-LetGoHost design (xterm 5.3, bare window.Eval, no COI/worker path) maintained in parallel with the pkg/rt/wasm assets that lg -w ships. pages.yml deploys the old one as the homepage. Proposal: regenerate the playground from the current assets and delete wasm/main.go + wasm/index.html. (This one touches the public playground, so it's a "needs a call" item: the page would gain the COI/worker boot path.)
  • Two pre-commit systems: scripts/pre-commit (symlink install, runs check-generated) and .pre-commit-config.yaml (prek). README and the YAML give contradictory install instructions; check-generated is already enforced by the unit test and CI. Fold the hook into the YAML as a local hook and keep one documented install path.
  • pkg/rt/core/ir/ops.lg ("S3 walking skeleton"): the op-registry dispatch it introduces still coexists with the cond arms it was meant to replace. Finish the migration or delete the skeleton. (On hold: a larger ops-system refactor is queued for after the 1.12 cut — see comments.)

Dead code

  • pkg/ir/spike_rpnvm_test.go + bench (~4k lines): the RPN-VM research spike whose conclusions now live in the gogen path.
  • check-lowered-fresh Makefile target: the Makefile's own comments describe it as a silent no-op pointing at a path that no longer exists.
  • scripts/turnaround-ratchet.lg: no Makefile/CI/hook reference, and its baseline file (docs/perf/turnaround-baseline.edn) doesn't exist. Its openspec change dir is likewise stale — as is openspec/changes/purify-clojure-core/, which shipped in refactor(rt,core): purify clojure.core — let-go.core / let-go.types for lg-isms #410 and can be archived.
  • tap_migrations.json at the repo root: a Homebrew tap-migration map with zero references in-tree; belongs in the tap repo.
  • Zero-caller odds and ends: compiler.NewDebugCompiler (and the permanently-false debug branch it feeds), vm.MakeFloat, and the if smart branch in cmd/lginterop that emits byte-identical output on both sides.

Same logic, several copies

None of these are style-level DRY: in each case the copies encode one invariant that must change in lockstep (or two binaries that must behave identically), and the collapse adds no new package dependency: every consumer already imports the package the helper would live in. Where duplication is deliberate it stays (the frozen v2Stride migration table is frozen precisely so it does not track the live enum).

  • Opcode stride tables ×4: CodeChunk.Debug (vm.go), rt/disasm.go, wasmhost/request.go (byte-identical to the disasm copy), plus the deliberately frozen v2Stride in the migration code. Instruction widths are a single source of truth; a drifted copy silently misparses bytecode. Export one vm.OpcodeStride and keep only the frozen table separate.
  • The "infer bundle signature, maybe remap" sequence appears in four decode paths in pkg/bytecode/decoder.go; the next opcode migration has to be threaded through all four. Same-file extraction, one helper.
  • The bundled-self-exec boot in lg.go near-duplicates cmd/lg-runtime's runBundle (same store-id fallback, resource decode, args plumbing). lg-runtime's contract is "identical to lg minus the compiler," so the resolver install is the only intended difference. Extract a shared helper parameterized on exactly that, making any other divergence impossible rather than latent.

Process weight

  • The Makefile clones makeplus/makes unpinned from GitHub at build time and includes it. go.yml's own comments document removing exactly this pattern for the Clojure toolchain; the Go build path still has it. Pin to a SHA or vendor the few .mk files used.
  • Generation freshness is checked and then regenerated anyway in the same CI build job, and check-generated-manifest duplicates the front half of check-generated. One check, one regeneration.
  • The Go version is pinned in three places (go.mod, Makefile GO-VERSION, mise.toml).

Related, tracked elsewhere

Workflow

Same as #464: file maintenance-debt issues as sub-issues here, each standing on its own (what's dead/duplicated, the evidence, the proposed removal or collapse). I'll take the first batch: the dead-code group is grep-verifiable and PR-sized. The proposals above are my default shapes, open to different cuts. If any item is intentional rather than leftover (the wasm/ playground's separate identity, the openspec archiving convention, the migration-registry posture), a one-line veto here saves a PR round-trip.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    Status
    Todo

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions