Chapter 3: The baseline nobody had verified

The spec's Phase 1.3 and 1.4 say "verify dotnet build passes" and "verify dotnet run serves the app." Those were the only two steps in the entire plan that were supposed to be free. Running them turned up four things.


1. It builds, but not to the spec's standard

Build succeeded.
    16 Warning(s)
    0 Error(s)
Time Elapsed 00:00:33.62

All 16 are NU1903 against the same package, but they are not the same warning. They are 8 distinct high-severity advisories, each reported twice (once for the project, once for the solution):

GHSA-w3x6-4m5h-cxqf    GHSA-mmjf-rqrv-855v
GHSA-g8r8-53c2-pm3f    GHSA-cvvh-rhrc-wg4q
GHSA-8q5v-6pqq-x66h    GHSA-6588-8gv4-xfgh
GHSA-37gx-xxp4-5rgx    GHSA-23rf-6693-g89p

All against System.Security.Cryptography.Xml 9.0.3. One of them, GHSA-w3x6-4m5h-cxqf, is CVE-2026-26171, .NET Denial of Service Vulnerability.

It is a direct PackageReference in Equinox.Infra.Data, not something dragged in transitively. The advisory lists 9.0.15 as the first patched version in the 9.x line; the current release is 9.0.18. So this is a one-line version bump, not a redesign.

And that collides with a project constraint

The constraints say: "Do NOT modify the domain layer, application layer, or infrastructure layer." Equinox.Infra.Data is the infrastructure layer.

Taken literally, the rules forbid patching 8 known high-severity vulnerabilities in an application about to be deployed to the public internet and linked from a blog post.

The spec's own success criterion, "0 warnings", cannot be met without breaking the spec's own constraint. That's the second internal contradiction found, after the .cshtml/Identity one.

Logged as a decision rather than silently resolved: see D11.

2. The database swap was already done

appsettings.Development.json:

"ConnectionStrings": { "DefaultConnection": "Data Source=EquinoxProject.db" }

The app boots, DbMigrationHelpers.EnsureSeedData() runs, and EquinoxProject.db appears. Phase 2 of the spec, four steps of SQL Server → SQLite migration, is already complete for Development.

appsettings.json still has no connection string at all, so Production has nothing to connect to. That's the real work Phase 2 should have described, and it's the one that matters for deployment.

3. The routes are nothing like the spec assumes

GET /Customer404.

CustomerController uses explicit attribute routes, not the conventional {controller}/{action} pattern the default route registers:

Action Actual route
Index /customer-management/list-all
Details /customer-management/customer-details/{id:guid}
Create /customer-management/register-new
Edit /customer-management/edit-customer/{id:guid}
Delete /customer-management/remove-customer/{id:guid}
History /customer-management/customer-history/{id:guid}

Any verification script written from the spec would have tested /Customer/Index and /Customer/Create, gotten 404s, and reported the migration broken when nothing was wrong.

4. Half of CRUD requires authentication

CustomerController is [Authorize] at the class level, with [AllowAnonymous] on three actions:

Operation Anonymous?
Index / list ✅ yes
Details ✅ yes
History ✅ yes
Create ❌ 302 → /Identity/Account/Login
Edit ❌ requires login
Delete ❌ requires login

Verified live:

/customer-management/list-all       HTTP 200
/customer-management/register-new   HTTP 302 -> /Identity/Account/Login?ReturnUrl=...
/Identity/Account/Login             HTTP 200
/Identity/Account/Register          HTTP 200

The spec's criterion, "Customer CRUD works end-to-end in the browser", silently requires a registered, logged-in user. Nothing in the plan mentions authentication.

Two knock-on effects

The CRUD oracle just got harder. It can't simply POST a form. It has to register a user, log in, carry the auth cookie and the antiforgery token through create → edit → delete. That's the single most complex piece of the verification script, and the spec treats it as a checkbox.

The Identity Razor Pages are on the critical path. Decision D2 kept them on Razor because rewriting authentication in an unproven view engine was too risky. That now looks less like a concession and more like the only workable choice: those pages are required to exercise the feature being demonstrated. Deleting them, as the original criterion demanded, would have made the success criterion untestable.

And it fixes a problem I'd expected to have. Earlier the plan assumed public unauthenticated CRUD, with the risk that a demo link would get filled with junk by visitor #50. It turns out anonymous users can read but not write. The abuse surface is much smaller than assumed, because writes need an account.


One self-inflicted wound, for honesty

The first build attempt failed with 10 × NETSDK1064: Package Microsoft.CodeAnalysis.Analyzers, version 3.3.4 was not found. Nothing to do with Equinox: restore and build ran in two separate podman run --rm invocations, so the second container started with an empty NuGet cache.

Fixed with a persistent volume, which also makes every subsequent build faster:

podman volume create nuget-cache
podman run -v nuget-cache:/root/.nuget/packages ...

Worth recording because the error message points confidently at a missing package and says nothing about containers. An automated loop would very plausibly have "fixed" this by pinning package versions or editing csproj files, chasing a phantom.


Score so far

Ten claims in the original spec have now been falsified, and the two steps meant to be free produced four findings. The migration hasn't started.

Writing is AI assisted. Thoughts and publishing are human-gated.

Rendered from jloor/equinox-jsxcore at f34e312. The markdown in that repository is the source of truth; if this page disagrees with it, this page is stale.