docs: add implementation plans for specs 0a and 0b
- 2026-07-24-shared-module.md: 8 tasks, extract the shared Go module - 2026-07-24-instance-rename.md: 9 tasks, Org -> Instance including the database field, with migration 0004 and a rollback command No automated tests per instruction; verification is by compiler, grep, and manual end-to-end plus production-snapshot rehearsal. Spec testing sections updated to match.
This commit is contained in:
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -160,23 +160,38 @@ meaning and the marketing site is the first place a customer meets it.
|
||||
|
||||
## Testing
|
||||
|
||||
Extends the suite started in spec 0a.
|
||||
**No automated tests.** Decision taken 2026-07-24, consistent with spec 0a.
|
||||
|
||||
1. **Migration test, empty database.** Runs clean, writes the marker.
|
||||
2. **Migration test, seeded database.** Seed one document in every collection in
|
||||
`scopedCollections` with a known `org_id`. Run. Assert every document has
|
||||
`instance_id` with the same value, no document has `org_id`, and per-collection
|
||||
counts are unchanged.
|
||||
3. **Idempotency.** Run the migration twice. Second run is a no-op and does not
|
||||
error.
|
||||
4. **Interrupted run.** Rename half the collections, then run the full migration.
|
||||
It completes the rest without error and passes verification.
|
||||
5. **Guard.** With `instances` present and `orgs` absent, the migration
|
||||
short-circuits and records the marker.
|
||||
6. **Scoped-collection completeness.** A test that reads the collection list from
|
||||
the live database and fails if any collection outside `scopedCollections`
|
||||
contains an `org_id` field. This test protects the list from going stale.
|
||||
7. **`shared/provision` tests from 0a** pass unchanged after the rename.
|
||||
This is the change where that costs the most: it moves the tenant isolation key
|
||||
across 17 collections, and a mistake orphans a customer's entire fleet rather
|
||||
than breaking a build. The compensating controls are therefore not optional, and
|
||||
the implementation plan makes each a mandatory step:
|
||||
|
||||
1. **Dry run against a restored copy** before the code is even committed —
|
||||
migrate a `mongorestore`d duplicate of production and read the per-collection
|
||||
rename counts.
|
||||
2. **Idempotency by hand** — run the dry run twice; the second must complete
|
||||
with no error and nothing left to rename.
|
||||
3. **Interrupted-run recovery by hand** — rename `orgs` manually, then run the
|
||||
migration; it must complete and leave every document carrying `instance_id`.
|
||||
4. **Count comparison against a production snapshot** — record every
|
||||
collection's document count before and after; any difference stops the
|
||||
release.
|
||||
5. **Per-tenant isolation comparison** — for three real tenants, count rows in
|
||||
`servers`, `keys`, `workflows`, `monitors`, `secrets` and `audit_logs` by
|
||||
`org_id` before and by `instance_id` after. Identical, or the release stops.
|
||||
This is the check that proves tenant isolation survived.
|
||||
6. **Stale-field sweep** — assert no collection anywhere still holds an
|
||||
`org_id`.
|
||||
7. **Rollback rehearsal** — migrate a third copy, run `rename-rollback`, confirm
|
||||
the counts return to baseline and the pre-release binary boots against it.
|
||||
Deploying without having done this is not permitted.
|
||||
8. **Boot guard, both directions** — sitesvc must refuse an unmigrated database
|
||||
and start normally against a migrated one.
|
||||
|
||||
`AssertNoScopedCollectionMissed` runs at every boot and is fatal. With no test
|
||||
suite it is the standing protection against a future collection being added
|
||||
without being added to `ScopedCollections`.
|
||||
|
||||
## Verification before merge
|
||||
|
||||
|
||||
@@ -226,23 +226,27 @@ called out here so it is a decision rather than an accident.
|
||||
|
||||
## Testing
|
||||
|
||||
The repo has no Go test suite today. This refactor is where one starts, because
|
||||
the moved code is exactly the code whose correctness is load-bearing.
|
||||
**No automated tests.** Decision taken 2026-07-24: the repo has no Go test suite
|
||||
and one is not being started here. Verification is by compiler, `grep`, and
|
||||
running both services end to end.
|
||||
|
||||
`shared/provision` unit tests, against a real MongoDB (testcontainers or a
|
||||
`MONGO_TEST_URI` env guard — skip when unset rather than fail):
|
||||
That places the whole weight on three manual checks, which the implementation
|
||||
plan makes mandatory steps rather than suggestions:
|
||||
|
||||
1. `Slugify` — table test covering the existing regex behaviour: casing,
|
||||
punctuation runs collapsing to a single `-`, leading/trailing trim.
|
||||
2. `BaseSlug` — rejects under `MinSlugLength`, truncates over `MaxSlugLength`,
|
||||
rejects every entry in `ReservedSlugs`.
|
||||
3. `CreateOrg` — a second org with a colliding name gets `-2`; a third gets `-3`.
|
||||
4. `CreateOrg` under a concurrent duplicate insert returns the clean
|
||||
"slug already taken" error rather than a raw Mongo error.
|
||||
5. `CreateUser` — hash verifies with bcrypt at cost 12; duplicate email is
|
||||
rejected by the unique index.
|
||||
6. `RollbackOrg` — deletes an org with no users; refuses one that has users.
|
||||
7. `EnsureCoreIndexes` — idempotent across two calls.
|
||||
1. **bson tag diff** — `diff` the `bson:"…"` tags of each moved struct against
|
||||
the originals. A changed tag orphans production data silently, and this is
|
||||
the only thing that catches it.
|
||||
2. **Slug behaviour walkthrough** — a throwaway `main` printing `Slugify`,
|
||||
`BaseSlug` and `NextSlug` output for a fixed input table, compared against
|
||||
expected output recorded in the plan.
|
||||
3. **End-to-end agreement** — sign up through sitesvc against a scratch
|
||||
database, open the verification link, then log into the control plane with
|
||||
those credentials. This is the check that proves the two services still agree
|
||||
about the documents they share. If it passes, the refactor worked.
|
||||
|
||||
Plus `grep` assertions that exactly one definition of `Slugify` and
|
||||
`ReservedSlugs` survives repo-wide, and that no struct under `sitesvc/` carries
|
||||
a `bson:"org_id"` tag.
|
||||
|
||||
## Verification before merge
|
||||
|
||||
|
||||
Reference in New Issue
Block a user