diff --git a/AGENTS.md b/AGENTS.md index 7638d00e..f985d31c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -118,6 +118,11 @@ Additive-only within `/api/v1`: - New features expose capability endpoints for feature detection rather than relying on version sniffing. Contract strategy and tooling: issue #135. +Treat this as binding. The one exception: `/api/v1` is not locked yet, so a removal taken before +lock is in scope — but only when it is recorded in the pre-lock removals table in +[docs/architecture/v1-scope.md](docs/architecture/v1-scope.md) and ships before the lock. Assume +any removal not listed there is a mistake. + ## Pull requests Conventional Commit subjects (`feat(playback): add realtime session hub`). One concern per PR. diff --git a/docs/architecture/v1-scope.md b/docs/architecture/v1-scope.md index 46e3d762..04a2b2a3 100644 --- a/docs/architecture/v1-scope.md +++ b/docs/architecture/v1-scope.md @@ -18,6 +18,21 @@ When the scope locks, this file becomes the source of truth and will contain: Until lock: treat any capability not tracked as `Proposed`/`Locked` on the project as out of scope for feature PRs (see the scope gate in `CLAUDE.md`). +## Breaking removals taken before lock + +The additive-only rule in item 2 binds at lock. Before then a removal is in scope, and there is no +amendment to write because the amendment process in item 3 does not exist yet. `CLAUDE.md` states +the rule without that qualifier, which reads as a contradiction — it is not, but a removal taken +now has to be recorded here so a reader after lock can tell a deliberate decision from a violation. + +Each entry names what goes, why waiting is worse, and the design that decided it. **Every removal +listed here must have shipped before the scope locks.** One still outstanding at lock loses its +justification and falls back to the Deprecation/Sunset flow like anything else. + +| Removed | Release | Rationale | +|---|---|---| +| String `GET`/`PUT`/`DELETE /api/v1/settings…`, the unknown-key extension bag, preference fields on profile/library/series DTOs | Cross-platform settings contract, [design](../superpowers/specs/2026-07-10-cross-platform-user-settings-contract-design.md) | Replaced wholesale by the typed settings contract. Deferring past lock would mean carrying the Deprecation/Sunset surface *and* the untyped key bag — which lets any client invent a production setting the server stores unvalidated — through the deprecation window, which is the exact surface the contract exists to close. | + Feature-detection precedent: clients discover which metadata providers (including the built-in NFO provider, #216) apply to a library type via `GET /api/v1/libraries/provider-defaults` rather than version sniffing. New capabilities diff --git a/docs/superpowers/specs/2026-07-10-cross-platform-user-settings-contract-design.md b/docs/superpowers/specs/2026-07-10-cross-platform-user-settings-contract-design.md index 4aa90b0b..0ec73d25 100644 --- a/docs/superpowers/specs/2026-07-10-cross-platform-user-settings-contract-design.md +++ b/docs/superpowers/specs/2026-07-10-cross-platform-user-settings-contract-design.md @@ -521,6 +521,12 @@ the amendment process it describes only exists *after* lock. There is therefore write and no exception to request: before lock, removing the legacy settings surface is simply in scope. +That argument does not live only here. Reasoning kept in a design doc is invisible to whoever reads +the policy later and sees a removal that appears to break it, so the removal is recorded in the +**pre-lock removals** table in `docs/architecture/v1-scope.md`, which is the file that governs it. +The table also carries the deadline: **this work must ship before the scope locks.** If it has not, +the justification lapses and the removal goes through Deprecation/Sunset like anything else. + **This is an argument for doing the work now rather than after lock.** After lock, the same removal would need the Deprecation/Sunset flow the v1 policy mandates and the codebase already implements (`internal/api/handlers/legacy_read_routes.go`), which reintroduces exactly the transitional