Excellent code,
no way to ship it.
The first whole-repository pass since 2026-09-06. Every category is re-derived from the current tree rather than carried forward, and the mean lands at 8.6 / 10.
The previous 9.3 was a composite: each round from 2026-09-07 to 2026-09-10 reassessed one category and carried five forward. Nothing verified in those rounds regressed. What moves the number here is the long tail no recent round examined.
What is genuinely excellent
- Verifiable Intent chain verification fails closed at every branch I could construct — multi-pair L2, duplicate disclosure references, an L3 without its authorizing L2 mandate, mismatched agent keys,
kidmismatch, over-long L3 lifetime, budget constraints with no ledger.Constraint.Malformedfails closed regardless of strictness. - The intent ledger forces
synchronous_commit, locks accounts before reservations in writer order, counts occurrences in anAFTER INSERTtrigger that cannot fire on anON CONFLICTno-op, and treats an uncertain commit as a denial. - Outbound HTTP is SSRF-guarded by default, including the federation resolver, whose own comment explains why a bare client there would let a peer steer requests at loopback.
- No crypto anti-patterns. Every security-path random source is
SecureRandom; no trust-all managers, no hostname-verifier bypass, noGlobalScope; XML parsers disable DOCTYPE and external entities. - Test discipline. Zero
@Disabled; all 15 skips named and justified in policy; VI cross-checked against a pinned Python reference.
Findings
F1 · The tree does not pass its own build
./gradlew build fails — 232 ktlint violations across 10 files in kms:plugins:hashicorp (169 main, 63 test), 115 of them in VaultKeyManagementService.kt. This is the Vault work that closed finding R15, written and never linted. Everything else is clean: 3,897 tests pass and checkKotlinAbi passes.
F2 · main carries an unversioned copy of the reviewed code
169 uncommitted files. The same work is committed on codex/joint-remediation-20260910 and codex/configuration-data-20260910, neither merged; the tree differs from the former in two tracked files. Every score since 2026-09-07 was awarded to code reachable from no branch on main. CI on main is green as of e0a4464f — the red-since-August state no longer holds.
F3 · There is no publication path
No build file declares a publishing repository, so publishToMavenRepository is never created and publishToMavenLocal is the only thing that works. The POM has no scm block, which Central rejects outright, and its description points at docs/reference/module-maturity.md — a path that does not exist. v0.7.0 is tagged with no release and no artifact.
F4 · Coverage is bimodal
Recently reviewed modules are strong — observability 97.8%, verifiable-intent 90.0%, common 87.0%, kms-core 85.0%. The rest is not.
| Module | Lines | Line | Branch |
|---|---|---|---|
| credentials/credential-api | 4,095 | 48.6% | 38.1% |
| did/did-core | 2,491 | 58.1% | 33.4% |
| wallet/wallet-core | 216 | 24.5% | 24.0% |
| did/plugins/web | 191 | 21.5% | 29.7% |
| did/registrar | 768 | 11.5% | 11.7% |
| credentials/credential-models-mp | 1,214 | 6.8% | 14.3% |
| did plugins ion · sol · polygon · plc · cheqd · ens | 162–324 | 1.2–2.5% | ~2% |
The policy floors (56.0 line / 38.0 branch) sit just under the measurement — they record where the code is, not where it should be. Separately, of 2,363 Kotlin blocks inventoried across the docs, 9 are source-backed and executed.
F5 · 61 dependency coordinates escape the catalog, and have drifted
| Library | Hardcoded | Catalog |
|---|---|---|
| org.bitcoinj:bitcoinj-core | 0.16.2 | 0.17.1 |
| org.web3j:core | 4.10.0 | 5.0.2 / 4.14.0 |
| com.google.code.gson:gson | 2.10.1 | 2.14.0 |
| org.slf4j:slf4j-api | 2.0.9 | 2.0.17 |
| kotlinx-coroutines-test | 1.8.1 (5 modules) | 1.10.2 |
Dependabot raises PRs against the catalog; these modules never move — and they parse untrusted network input. The new observability module repeats the pattern, pinning OpenTelemetry 1.65.0 inline in four places.
F6 · Three shipped servers have no authentication primitive
DidRegistrarServer, VcApiServer and the status-list server ship with none. VcApiServer's own KDoc says it: "There is no API key, bearer token, or mTLS anywhere in this module." The registrar creates, updates and deactivates DIDs. A loopback default plus a note telling operators to add a proxy is guidance, not a control — and trust-registry-server already shows the right shape.
F7 · Instrumentation stops at the HTTP boundary
The observability module is excellent and wired into six hosts. Across the tree, 33 of 788 main-source files reference a logger; DID resolution, KMS, wallet and credential verification emit no structured events, metrics or correlation id. A host with a failed verification span has nothing to join it to.
F8 · Half the workflows are unpinned
ci.yml, release-evidence.yml and docs-check.yml pin every action by SHA. deploy.yml, conformance-nightly.yml and conformance-pr.yml pin none, and the last two declare no permissions block — the nightly job runs actions/github-script@v7, which creates issues, on the default token scope.
F9 · Nothing is declared production-supported
The capability catalog has 8 entries for 107 modules, none supported. The custody runbook states no profile is qualified. A consumer cannot tell which module is production code.
F10 · A presenter-reachable exception escapes the verification contract
ChainVerifier.verify translates only SQLException from the budget reservation, but the ledger also throws IllegalStateException on a stored-policy mismatch. The ledger scope is sha256 of the L2 JWT without disclosures, so one signed L2 carrying two payment mandates with different budgets reaches it. Fails hard, not open — but it breaks the result contract on presenter-controlled input. The adjacent negative-max variant is not a defect: it is caught as Malformed and fails closed.
F11 · Two documented cancellation gaps
BitstringStatusListManager lines 1211 and 1263 iterate 131,072 entries with no cooperative cancellation, in non-suspend functions that cannot reach coroutineContext. The TODOs prescribe the fix.
F12 · The validation gates read the wrong directory on Windows
check-test-evidence.py and check-junit-contract.py default to the in-repo build/, which on this project's documented Windows layout is a stale 2026-09-06 snapshot. Run with their defaults they report 40 evidence failures and 8 invalid JUnit methods; pointed at the real build root both exit 0 with zero failures. This caught me mid-review — the 8 looked like the known non-void @Test trap until I confirmed all six named OID4VP tests do appear in the current JUnit XML.
F13 · 550 broad catches, 21 with a comment for a body
Every security path I read fails closed correctly, but the surface has not been audited, and a swallowed CancellationException is exactly the class of defect these rounds keep finding one at a time. A balanced-brace scan finds zero genuinely empty catch bodies — an earlier line-counting grep in this review reported 12, which was wrong — and 21 whose body is only a comment. Four of those sat in suspend functions and swallowed cancellation. The wider surface is 187 broad catches inside suspend functions that neither rethrow nor mention cancellation; most convert to a sealed failure and are fine.
Scores
| Category | 2026-09-10 | Now | Why |
|---|---|---|---|
| Security and access control | 9.0 | 8.8 | F6, F8, F9, F10 |
| Observability and diagnosability | 9.6 | 9.0 | F7 — the 9.6 was scored against the six HTTP hosts, not the library |
| Reliability and scale | 9.5 | 9.2 | F11, and no deployment-scale evidence |
| Configuration and data | 9.5 | 8.8 | F5 |
| Deployment and release | 8.5 | 7.0 | F1, F2, F3, F12 |
| Testing and documentation | 9.5 | 8.5 | F4 |
Equal-weight mean: 51.3 ÷ 6 = 8.55, rounded to 8.6.
Getting above 9.5
Full acceptance criteria in tasks.json. Fifteen tasks; the projected mean on completion is 9.52.
Week 1 — stop the bleeding
- T01
ktlintFormatthe Vault module. Do not re-baseline — these are new violations. Under an hour. - T02 Reconcile the two local edits onto
codex/joint-remediation-20260910, merge tomain, push, confirm CI. Stage explicit paths, nevergit add -A. Half a day. - T08 SHA-pin the 14 unpinned action refs; add the two missing
permissionsblocks. Under an hour. - T10 Close the bitstring cancellation gaps. T12 Keep ledger failures inside the result contract. T13 Fix the Windows build-root default.
Weeks 2–3 — make it shippable
- T03 Central repository,
scmin the POM, correct doc URL, tag-triggered publish workflow, signed artifacts with SBOM and provenance. Two to three days plus namespace verification. - T05 All 61 coordinates into the catalog, converge the drift, add a gate that rejects literals. One to two days.
- T07 A
withAuthenticationhook besidewithObservability, failing closed on mutating routes, plus per-caller rate limiting. Three to four days.
Weeks 2–6 — the big one
- T04 Coverage from 57.3%/39.8% to roughly 75%/60%, top-down by untested line count:
credential-api, thendid-core, thenwallet-coreanddid:web. Decide whether the 1–3% DID plugins ship or are experimental. This single item limits the score more than any other.
Weeks 4–5, then ongoing
- T06 Instrument the library behind the observability SPI. T09 Classify all 107 modules and qualify the GA core. T14 Compile the doc blocks consumers copy.
- T11 Deployment-scale ledger qualification. T15 Broad-catch audit.
Two items do not compress no matter how fast the code goes: Sonatype namespace verification (T03) and real custody or hardware qualification (T09). Start them first even though they finish last.
Evidence and machine-readable assessment
- Full review (Markdown)
- scores.json · tasks.json
- tests.json — 3,897 tests, 0 failures, 489 suite XMLs
- coverage.json — merged and per-module Kover figures for 83 modules
- build-tail.log — the failing build result
- ktlintMainSourceSetCheck.txt · ktlintTestSourceSetCheck.txt
Remediation, same day
Nine of fifteen tasks closed with local evidence. Four remain open as multi-week work, and two need an action only the repository owner can take. Full record in remediation.json.
| Finding | State | What changed |
|---|---|---|
| F1 · build fails | Closed | ktlintFormat cleared all 232 violations in kms:plugins:hashicorp. The module baseline is untouched, so nothing was grandfathered. |
| F2 · unversioned tree | Partly | The tree is committed. Merging the codex/* branches and pushing rewrites shared history and publishes the work, so it is left to the owner. |
| F3 · no publication path | Closed in code | Maven repository declared in the root build and the BOM, scm and issueManagement in every POM, the dead documentation URL corrected, and a reviewer-gated publish job on v* tags. Sonatype namespace verification remains an external step — see the runbook. |
| F4 · coverage | Open | Two to three weeks of test writing; not attempted. |
| F5 · catalog drift | Closed | All 61 literals moved into the catalog across 34 build files. web3j, gson, slf4j and coroutines-test converged; bitcoinj stays on 0.16.2 through an explicit bitcoinj-legacy alias because 0.17 is a breaking API change — recorded rather than hidden. |
| F6 · no server authentication | Closed | New HostAuthentication: constant-time bearer tokens, host-supplied authorizers, an explicit frontedByProxy declaration, per-caller rate limiting. The three servers refuse mutations with 503 until a host says which applies. Breaking; in the changelog. |
| F7 · library instrumentation | Open | One week; not attempted. |
| F8 · unpinned workflows | Closed | All 14 references SHA-pinned, both missing permissions blocks added, and the nightly job's issues: write moved to a job that runs no repository code. |
| F9 · nothing supported | Partly | A ratchet: every module must declare a maturity or be recorded as unassessed, and the unassessed count may only fall. Classifying the 102 recorded modules is an owner decision. |
| F10 · escaping exception | Closed | Ledger IllegalStateException and IllegalArgumentException become an invalid result, with a regression driving both through a refusing data source. |
| F11 · cancellation gaps | Closed | Both loops are suspend and probe every 8192 bits. A test proves a cancelled revocation issues fewer statements than a completed one, so the decode really did abort. |
| F12 · wrong build root | Closed | scripts/build_root.py resolves the root the way the build does and refuses one holding no results, instead of reporting phantom gaps. |
| F13 · broad catches | Partly; count corrected | Zero genuinely empty bodies, not 12. Four of the 21 comment-only bodies sat in suspend functions and swallowed cancellation; all four now rethrow. 187 remain to triage. |
Five new gates keep the closed findings closed: check-workflow-pinning, check-dependency-catalog, check-publication, check-capability-coverage, and the shared build_root resolver. All are wired into CI and the release workflow, and each ships with unit tests.