NOTE: Historical document. `retired-affinescript` denotes the former UI source tree, which is absent from the current checkout. Commands and file-level examples using it are archival, not current build instructions. Current UI sources are in `src/ui/`; use the root Justfile for supported commands. == Debt register Known, measured debt in this repository: licensing, documentation, and code. Supersedes link:tech-debt-2026-05-26.md[`+tech-debt-2026-05-26.md+`], which is retained as a dated snapshot. *Every entry carries the command that produced its figure.* An entry without evidence is a rumour, and rumours do not belong in a debt register. Re-run the commands from the repository root before acting on any item — they are the definition of the finding, not a description of it. Entries are *not* issues. Where a GitHub issue already tracks an item it is linked; where the fix requires a decision only the owner can make, the entry says so explicitly and stops there. Priorities: *P0* — a downstream consumer can be actively misled or harmed. *P1* — a reader is misinformed but not exposed. *P2* — friction, cost, or latent risk. ''''' === P0 — Licensing: RE-RULED 2026-10-01 (D262) — current *Now:* the owner re-ruled ECHIDNA onto the estate default (ruling D262, 2026-10-01): *MPL-2.0* for code, the machine-readable specification surface and `+echidna-playground/+`, and *CC-BY-SA-4.0* for documentation. This supersedes the 2026-08-07 AGPL-3.0-or-later split recorded below. `+LICENSE+` is the verbatim MPL-2.0 text from mozilla.org, and every `+Cargo.toml+` `+license+` field, the guix package, `+NOTICE+`, `+.reuse/dep5+`, the README and the wiki now say so. *Deliberate interim — not drift.* The `+.a2ml+` manifests are being converted to `+.deed+` in a follow-up PR, which rewrites their headers. Until it lands, ten of them still carry an AGPL-3.0-or-later SPDX header (under `+HOL-o-extension/+`, `+audits/+` and `+training_data/+`), and `+LICENSES/AGPL-3.0-or-later.txt+` stays so those headers still resolve under `+reuse lint+`. The follow-up PR deletes that file. The `+training_data/+` corpus is excluded throughout: SPDX strings inside it are scraped content, not grants (see below). *Residual exposure.* Copies obtained between 2026-08-07 and 2026-10-01 were licensed AGPL-3.0-or-later and stay so for their recipients. The re-ruling only loosens the licence (MPL-2.0 grants a subset of the AGPL's obligations), so it misleads no earlier recipient. *Verification — these should come back clean:* [source,bash] ---- # no AGPL grants outside the interim .a2ml set, the corpus and licence texts git grep -lE 'SPDX-License-Identifier:[[:space:]]*AGPL' \ | grep -vE '\.a2ml$|^(training_data/|LICENSES/|docs/legal/)' # expect empty # no AGPL in packaging metadata git grep -In '^license *= *"AGPL' -- '*.toml' # expect empty # GitHub detects MPL-2.0 gh repo view hyperpolymath/echidna --json licenseInfo ---- ''''' === P0 — Licensing: the 2026-08-07 resolution (superseded by D262) *Was:* the repository stated four different licences at once — `+LICENSE+` and `+Cargo.toml+` said AGPL-3.0-or-later while 590 source files granted MPL-2.0, `+NOTICE+` described the project as MPL-2.0 while citing the AGPL file as its text, and `+.reuse/dep5+` claimed `+PMPL-1.0 AND Palimpsest-0.6+`. Because per-file SPDX headers are themselves a licence grant, a recipient could have taken the tree under MPL-2.0 — which, unlike AGPL, has no network clause. *Now:* reconciled to the owner’s AGPL ruling as a deliberate three-part split. [width="100%",cols="34%,33%,33%",options="header",] |=== |Part |Licence |Files |Code — `+src/+`, `+crates/+`, `+ffi/+`, `+proofs/+`, `+spark/+`, `+verification/+`, build system, CI, machine-readable metadata |*AGPL-3.0-or-later* |594 + 6 dual |Documentation — `+docs/+`, top-level `+.md+` / `+.adoc+` |*CC-BY-SA-4.0* |92 + 1 dual |`+echidna-playground/+` — Coq-Jr sub-project |*MPL-2.0*, unchanged |35 |=== The playground was *not* relicensed: it carries contributions attributed to "`Coq-Jr Contributors`", and relicensing another party’s work needs their consent. It does not need relicensing — MPL-2.0 §3.3 designates the GNU licences (including AGPL-3.0+) as Secondary Licenses, so MPL files combine into an AGPL work and the combined work distributes under AGPL while those files individually remain available under MPL. No file carries the Exhibit B "`Incompatible With Secondary Licenses`" notice that would break this. `+NOTICE+` and `+.reuse/dep5+` were rewritten to describe the split; `+LICENSE+` had two prepended SPDX lines removed so it is byte-identical to `+LICENSES/AGPL-3.0-or-later.txt+` — those lines were stopping GitHub’s licence detector matching the body, which is why the repository showed "`Other`". *Verification as it stood on 2026-08-07 (historical; inverted by D262 — use the check in the current section above):* [source,bash] ---- # no MPL outside the playground and licence reference material git grep -l 'SPDX-License-Identifier:.*MPL-2\.0' \ | grep -vE '^(echidna-playground/|LICENSES/|docs/legal/)' # expect empty # code is AGPL git grep -I -h -m1 -oP 'SPDX-License-Identifier:\s*\K[A-Za-z0-9.\-+]+( (AND|OR) [A-Za-z0-9.\-+]+)*' \ -- '*.rs' '*.jl' '*.zig' ':!echidna-playground/' | sort | uniq -c # GitHub now detects a licence gh repo view hyperpolymath/echidna --json licenseInfo ---- ==== Closed — `+Palimpsest-0.6+` grants removed Seven files (later found to be 29) carried `+Palimpsest-0.6+` in their SPDX identifier. The owner ruled these were never a deliberate legal grant: `+../.github/CONTRIBUTING.md+` already recorded that the Palimpsest licence proper "`is the legal licence only on `+palimpsest-license+`, `+palimpsest-plasma+`, and (prospectively) `+consent-aware-http+``", and that in ECHIDNA it is an _ethical framework_ reference, orthogonal to the SPDX choice. Version 0.6 is also superseded by the `+hyperpolymath/palimpsest-license+` repository. All Palimpsest SPDX identifiers were therefore removed. The framework reference is preserved in `+../.github/CONTRIBUTING.md+` and `+NOTICE+`, which now state explicitly that it is not a grant, so this cannot drift back in silently. Two exclusions, deliberate: `+training_data/proof_states_v2.jsonl+` contains SPDX strings as _scraped corpus content_ (proof goals harvested from files, recorded as data — editing them would corrupt the corpus), and `+docs/legal/+` holds licence reference texts. ==== Closed — three further licence-drift classes found by the deeper sweep The SPDX-header sweep alone would have missed all of these. Recorded because each is a distinct class worth re-checking after any future licence change: [arabic] . *Stale `+MIT+` grants.* `+retired-affinescript/.gitignore+`, `+styles/main.css+` and `+tailwind.config.js+` still declared `+MIT OR Palimpsest-0.6+` — pre-dating even the MPL migration. Removing only the Palimpsest half would have left them *MIT*. Now AGPL-3.0-or-later. . *Machine-readable `+license:+` fields*, which are what packaging and tooling actually read, and which carry no `+SPDX-License-Identifier:+` line for a header sweep to match. Eight declared MPL-2.0: `+docs-site/.well-known/aibdp.json+` (served on the site), `+stapeln.toml+`, `+container/manifest.toml+`, three Ada `+alire.toml+` manifests under `+spark/+`, `+.machine_readable/descriptiles/META.a2ml+`, and `+0-AI-MANIFEST.a2ml+`. Plus a *nested `+retired-affinescript/.reuse/dep5+`* declaring `+MIT OR Palimpsest-0.6+` for the UI sub-tree. . *A user-facing licence string.* `+retired-affinescript/src/Main.res+` rendered `+"MIT OR Palimpsest-0.6 License"+` in the UI — a false licence statement shown to users, invisible to every header-based check. . *OCI image labels* — 16 `+LABEL org.opencontainers.image.licenses="MPL-2.0"+` across `+Containerfile+`, `+container/Containerfile+`, and the `+.containerization/+` tree (11 in `+Containerfile.wave3+` alone, one per per-prover stage). These are *baked into every image published to ghcr.io* and read by registries, SBOM generators and supply-chain scanners, so the wrong value propagates downstream of the repository entirely. Also `+canonical-license+` in `+.machine_readable/anchors/ANCHOR.a2ml+` and two Idris2 `+.ipkg+` manifests. *Detector for next time* — a header sweep is not sufficient: [source,bash] ---- git grep -In '"license"' -- '*.json' git grep -In '^license' -- '*.toml' '*.a2ml' git grep -In '^licenses' -- '*.toml' find . -name dep5 -not -path './.git/*' # nested REUSE configs git grep -InE '"(MIT|MPL|AGPL|Apache)[^"]*"' -- 'src/' 'site/' # UI strings git grep -In 'org.opencontainers.image.licenses' # OCI labels # or, exhaustively — by value rather than by file type, which is what # actually caught the last three classes: git grep -InE '(license|licenses|licence)\s*[:=]\s*"?(MPL-2\.0|MIT|Palimpsest)' \ | grep -v '^echidna-playground/' ---- ''''' === P1 — Documentation ==== D1. Prover counts drift across surfaces _(issue https://github.com/hyperpolymath/echidna/issues/251[#251])_ *Largely addressed* in the documentation refresh that added this file; recorded because the underlying cause is structural and will recur. The tree contained five different counts — 48, 105, 128, 138, 141 — because four different things are all called "`the number of provers`": enum variants (141), implementation files (105), implementations with `+suggest_tactics+` (102), and default-exposed core backends (12). All four are defensible; none is "`the`" count. link:PROVER_COUNT.adoc[`+PROVER_COUNT.adoc+`] is now canonical, carries the commands that reproduce each figure, and explains the denominators. *Residual risk:* the `+R5a+` CI rule (`+.github/canonical-references/prover-counts.yml+`) that forbids bare counts covers only the top-level document set named in that file. `+docs/+`, `+.machine_readable/+`, `+crates/*/README.md+` and `+.github/*.md+` are *out of scope*, which is precisely where the drift accumulated. Extending the rule’s scope is unfinished work. ==== D2. Tier membership is hand-maintained and unverifiable `+PROVER_COUNT.adoc+`’s tier table is the routing contract, but only Tier 1 (`+ProverKind::all_core()+`) and Tier 9 are machine-checkable. Tier 4’s "`19 placeholder backends`" and Tier 8’s "`13 corpus-only provers`" are asserted, not derived, and were not re-measured in this pass — they are marked as indicative in the table. Making tier membership an attribute on each `+ProverKind+` variant would make the table generated rather than maintained. ==== D3. Unearned OpenSSF Best Practices badge `+README.md+` displays a hardcoded green "`OpenSSF Best Practices`" badge that links to the project *registration* form, not to a passing scorecard: [source,bash] ---- grep -oE 'bestpractices.dev[^)]*' README.md # bestpractices.dev/en/projects/new?repo_url=https://github.com/hyperpolymath/echidna ---- The badge image is a static `+img.shields.io+` green label, so it will read as "`passed`" regardless of the project’s actual standing — including now, when the project is not registered. Either register the project and use the real badge (which reflects the true tier and changes when it lapses), or remove the badge. A permanently-green badge that cannot fail is indistinguishable from a false claim. This is a known estate-wide pattern, not unique to this repository. ==== D4. Broken links in historical release notes Nine dead links remain in `+docs/releases/RELEASE_NOTES_v1.2.md+` and `+RELEASE_NOTES_v1.3.md+`, pointing at design documents that are not in the tree (`+CHAPEL_METALAYER_ANALYSIS.md+`, `+ZIG_FFI_ANALYSIS.md+`, `+FUTURE_DEVELOPMENT_ROADMAP.md+`, `+QUICKSTART.md+`, and others). Live documents were repaired in the refresh; these were deliberately left, because release notes are dated records and rewriting them rewrites history. They should either be de-linked in place or the referenced documents restored from git history. [source,bash] ---- # re-measure broken relative links across all Markdown/AsciiDoc git grep -nE '\]\([A-Za-z0-9_./-]+\.(md|adoc)\)' -- '*.md' '*.adoc' | while IFS= read -r l; do src="${l%%:*}"; ref=$(echo "$l" | grep -oE '\]\([A-Za-z0-9_./-]+\)' | sed -E 's/^\]\(//;s/\)$//') for r in $ref; do [ -e "$(dirname "$src")/$r" ] || echo "$src -> $r"; done done ---- ==== D5. `+echidna-playground/SECURITY.md+` references absent files It links to `+README.md+`, `+CHANGELOG.md+`, `+CONTRIBUTING.md+` and `+SECURITY-ACKNOWLEDGMENTS.md+` inside `+echidna-playground/+`; none exist (`+ls echidna-playground/+`). The sub-project’s security policy therefore directs a reporter to nothing. Either add the files or point the links at the parent repository’s equivalents. ==== D6. Root scaffold documents displaced the real ones — _fixed, recorded_ `+ARCHITECTURE.md+` and `+GOVERNANCE.md+` at the repository root contained generic template text with *zero* project-specific content, while the real documents sat at `+docs/ARCHITECTURE.md+` (160 lines) and `+GOVERNANCE.adoc+` (162 lines): [source,bash] ---- grep -icE 'echidna|prover|neurosym|trust' ARCHITECTURE.md # was 0 grep -icE 'echidna|prover|RSR|hyperpolymath' GOVERNANCE.md # was 0 ---- This was worse than duplication. GitHub surfaces the `+.md+` member in its community-standards checks and contributor prompts, so the document most readers landed on described a project that could have been anything. Both are now explicit pointers to the canonical documents, following the pattern `+CONTRIBUTING.md+` already used correctly. *Watch for recurrence:* these files match the estate’s scaffold-template shape, so a future template sweep may reinstate them. The check above is the detector — a root `+.md+` scoring 0 is scaffold, not documentation. ==== D7. `+.machine_readable/6a2/+` no longer exists The six descriptiles moved to `+.machine_readable/descriptiles/+`. Seventeen documents pointed at the old path and were repaired in the refresh, but the contractile configurations still name `+.machine_readable/6a2/DRIFT.a2ml+` and `+.machine_readable/6a2/ratification-.a2ml+` as *write destinations*: [source,bash] ---- git grep -n '6a2/' -- '*.ncl' ---- *Resolved 2026-10-05 (firstrun/20261005):* the write destinations were repointed to `+.machine_readable/descriptiles/+`, the directory that exists. rsr-template-repo forbids any contractile writing to `+6a2/+`, so repointing (not recreating) is the only conformant choice. The two sub-project `+6a2/+` directories (`+echidna-playground/+`, `+HOL-o-extension/+`) moved to `+descriptiles/+` in the same change. ''''' === P2 — Code ==== C1. AffineScript — removed 2026-08 The 37 AffineScript files (24 in `+retired-affinescript/+`, 13 orphaned `+.res+` prover clients in `+src/provers/+`) were deleted, along with the build and CI wiring that referenced them. AffineScript is a banned language under the estate policy. *The replacement is not ready, and that is now visible rather than hidden.* The AffineScript-TEA sources sit at `+src/ui/tea/+` but the compile pipeline is unwired, blocked on missing primitives — `+Http::fetch+`, `+Async+`, `+Json+` — tracked in https://github.com/hyperpolymath/echidna/issues/266[#266] and https://github.com/hyperpolymath/echidna/issues/117[#117]. `+just build-ui+` now *fails with an explanatory message* instead of silently doing nothing; `+serve-ui+` serves the static shell at `+src/ui/public/+`. `+echidna-playground/+` keeps its 8 `+.res+` files: that sub-project carries Coq-Jr contributions, so removing them is a separate decision from a language-policy cleanup. ==== C1b. `+.gitlab-ci.yml+` is not valid YAML `+python3 -c "import yaml; yaml.safe_load(open('.gitlab-ci.yml'))"+` fails on a bare `+%+` inside an unquoted shell command. *Pre-existing* — verified by parsing the file at `+HEAD+` before the AffineScript removal shifted the line number. Whether it matters depends on whether the GitLab mirror actually runs CI; if it does not, the file is decorative and should say so or go. ==== C2. 185 `+#[allow(dead_code)]+` suppressions [source,bash] ---- git grep -c 'allow(dead_code)' -- '*.rs' | awk -F: '{s+=$2} END{print s}' ---- Each one silences the compiler’s own report of unreachable code. At this volume the suppressions, not the compiler, define what counts as live code — so genuine dead code is no longer detectable. Worth a pass to distinguish scaffolding-for-planned-work (annotate with the tracking issue) from code that should be deleted. ==== C3. Placeholder backends are indistinguishable from working ones at the API Tier 4 backends are `+ProverKind+` variants with mock-only invocation, but they are reachable through the same `+ProverKind+` selection as real ones. A caller selecting one receives a response shaped like a proof result. `+container-ci.yml+` runs stub-sentinel detection for Tier-3 cells; the corresponding guarantee for Tier-4 placeholders at the API boundary is not documented. Related: D2 — the placeholder count itself is unverified. ==== C4. `+Dogfood Gate+` fails K9 validation on `+main+` Observed 2026-08-07 on every PR run: .... Hunt-level K9 file must include a 'signature' or 'signature_required' field K9 validation failed with 1 error(s) .... A contractile/K9 schema requirement that the repository’s own hunt-level K9 file does not satisfy — the dogfooding gate cannot pass its own rules. Not diagnosed further here; reproduce with `+gh run list -R hyperpolymath/echidna --workflow "Dogfood Gate"+` and read the failing step. ==== C5. `+Rust CI+` test failures on `+main+` `+cargo test --tests --workspace --locked+` exits 101 under `+llvm-cov+`. This is a genuine test failure, not infrastructure: it appears *after* the toolchain installs and the workspace builds. Distinguish it from the lockfile-pin startup failures that affected the same workflow until the `+dtolnay/rust-toolchain@stable+` relock — those failed _before_ running a step and produced no test output. Individual failing tests have not been enumerated; that is the next action. `+Secret Scanner+` also exits 1 on `+main+`; likely the dead `+VERISIMDB_PAT+` already tracked in https://github.com/hyperpolymath/echidna/issues/310[#310], but not confirmed here. ==== C6. Unfinished-work markers Low and healthy for a tree this size — recorded as a baseline to watch: [source,bash] ---- git grep -cE 'TODO|FIXME|XXX' -- '*.rs' | awk -F: '{s+=$2} END{print s}' # 11 across 6 files git grep -cE 'todo!\(\)|unimplemented!\(\)' -- '*.rs' | awk -F: '{s+=$2} END{print s}' # 1 ---- ''''' === Routing and verification claims (recon 2026-11-01) Source: link:reports/2026-11-01-routing-and-rule-engine-recon.adoc[`+docs/reports/2026-11-01-routing-and-rule-engine-recon.adoc+`]. Entries here are the ones that outlive that report. ==== D8. Core server and GraphQL interface both default to port 8081 *P1.* Two binaries, one default port. `+echidna server+` takes `+--port+` default 8081; `+echidna-graphql+` hard-coded 8081 (`+src/interfaces/graphql/main.rs+`). Whichever starts second fails to bind. This is not merely untidy: `0-canon/PORT-REGISTRY.adoc` in hyperpolymath/standards states "Every project MUST use a unique port. No two projects share a port." Fixed so far: `+ECHIDNA_GRAPHQL_ADDR+` override (default unchanged, so no client breaks) and corrections to `+QUICKSTART-USER.adoc+`, `+README.adoc+`, `+docs/ARCHITECTURE.adoc+`, `+site/docs/index.adoc+`, `+site/docs/api/graphql.adoc+` and `+.machine_readable/descriptiles/META.a2ml+`, which variously omitted the core server or claimed the three interfaces sat "on dedicated ports". Open: the default itself. Moving it needs a release note, since anything pinned to 8081 keeps working only while the two are not co-resident. [source,bash] ---- grep -n 'default_value = "8081"' src/rust/main.rs grep -n 'ECHIDNA_GRAPHQL_ADDR' src/interfaces/graphql/main.rs ---- ==== D9. Solver integrity is documented as enforced and is not enforced *P1.* `+docs/ARCHITECTURE.adoc+` and the published site listed solver binary integrity as step 1 of the trust pipeline. The check is conditional (`+dispatch.rs:632+`): with no checker attached, `+solver_integrity_ok` is `true`. `+with_integrity_checker+` has no caller in the tree, and `+config/solver-manifest.toml+` holds `+PLACEHOLDER_COMPUTE_ON_FIRST_RUN+` for every solver, so attaching one today would report `+Uninitialized+` — which the code also treats as passing. Fixed so far: the docs and the published site now say "capability present, not currently engaged" instead of claiming a step that does not run. Now machine-checked on the specification side: `+meta-checker/src/Echidna/Integrity.agda+` (`+docs/PROOF-STATUS.adoc+` R9) proves that verification requires an initialised manifest and that an uninitialised manifest never verifies — with collision resistance carried as an explicit hypothesis, and a sharpness result showing that hypothesis cannot be dropped. The accepting call sites are `+dispatch.rs+` 642, 848, 998 and `+solver_integrity.rs+` 359. So the divergence is checked on one side and read on the other, rather than read on both: closing D9 means changing those four lines, not the proof. Open: decide whether to wire it (introducing a refuse-on-mismatch failure mode) or to delete the step from the pipeline description. Recorded rather than silently changed. [source,bash] ---- grep -rn 'with_integrity_checker' src/ crates/ tests/ # definition only grep -n 'PLACEHOLDER' config/solver-manifest.toml ---- ==== D10. `+--features verisim+` changes verification routing, not just storage *P1.* `+POST /api/prove+` compiles two ways: through `+ProverDispatcher+` with the feature, straight to the backend factory without it (`+server.rs:423+`). A persistence flag therefore decides whether an HTTP proof is arbitrated, and no response field distinguishes the two. Open: build the dispatcher unconditionally and make `+with_verisim_writer+` purely additive. Not done here because it changes what a default build returns — it needs a decision record. [source,bash] ---- sed -n '420,450p' src/rust/server.rs ---- ==== D11. `+echidna-mcp+` calls a subcommand that does not exist *P2.* `+crates/echidna-mcp/src/main.rs+` runs `+echidna check-prover +`; `+Commands+` has no such variant, so the tool fails with a usage error every time. Its `+prove+` tool also uses the legacy `+--format json+` dialect (`+{"level":"success"}+`) instead of the `+--output json+` prove contract. Open: needs either a `+check-prover+` subcommand or a `/api/provers` probe, plus a rewrite onto the contract. Feature decision. [source,bash] ---- grep -n 'check-prover' crates/echidna-mcp/src/main.rs # one call site grep -n 'CheckProver\|check-prover' src/rust/main.rs # expect empty ---- ==== D12. `+docs-site/+` is unbuilt and its API pages are wrong *P2.* Nothing builds or deploys `+docs-site/+`: `+pages.yml+` publishes `+site/+` through Ddraig. Its pages still claim a `+/graphql+` endpoint and a `+/playground+` (the binary serves both at `+/+`), "30 supported prover backends", and a `+localhost:8000/api/v1+` base. Open: delete the tree or regenerate it from `+site/docs/+`. Left in place here because removing a documentation tree is an owner decision, not a cleanup. ==== Fixed en route (recorded so they are not re-triaged) * `+container/entrypoint.sh+` executed `+echidna serve+`; the subcommand is `+server+`. Every container start failed at `+exec+`. * `+container/compose.toml+` healthcheck probed `+/health+` and `+container/vordr.toml+` probed `+/health+` and `+/ready+`; the core server serves `+/api/health+` and never had `+/ready+`. * `+rules/security_learning.lgt+` was dead Logtalk, loaded by nothing; quarantined to `+archive/logtalk/+` with a `+DEAD SOURCE+` header. See that directory's README. * `+docs/ECOSYSTEM-INTEGRATION.adoc+` and `+echidna-playground/.machine_readable/descriptiles/NEUROSYM.a2ml+` claimed Hypatia emits Logtalk rules. It emits Elixir rules. ''''' === Already tracked as issues Open issues covering debt not duplicated above: https://github.com/hyperpolymath/echidna/issues/314[#314] (hypatia baseline gate un-armed — currently the reason `+Governance+` fails on `+main+`), https://github.com/hyperpolymath/echidna/issues/310[#310] (dead `+VERISIMDB_PAT+`), https://github.com/hyperpolymath/echidna/issues/252[#252] (machine-readable currency audit), https://github.com/hyperpolymath/echidna/issues/242[#242] (structural drift in path references), https://github.com/hyperpolymath/echidna/issues/240[#240] (AffineScript deprecated-API triage), https://github.com/hyperpolymath/echidna/issues/239[#239] (safety-alert classification), https://github.com/hyperpolymath/echidna/issues/216[#216] (SPDX-FileCopyrightText hook blocks commits — a licensing-adjacent item that should be resolved alongside P0). === Maintaining this file Add an entry when you find debt you are not fixing in the same change. Include the command that measures it. Remove an entry only when the command that detected it comes back clean — not when the work "`feels done`".