M1, M2 and M3 complete * M1, M2 and M3 complete * review changes * docs * docs
6.1 KiB
Mutable Metadata Plan v3 Review (Codex)
A review of plans/mutable.metadata.plan.combo.v3.md and plans/mutable.metadata.plan.combo.v3.tracking.md since they are to be used as a pair to implement the plans/mutable.metadata.plan.combo.v3.md plan.
I reviewed the current legacy field-extraction flow first so the plan review is anchored to real behavior:
api/queryAPI/fieldextractions.go:20-69binds the request, callsFieldExtraction.CreateFieldExtraction, and currently maps any create error to HTTP 500.internal/fieldextraction/service.go:27-100creates the extraction row, then locks existing version rows, computesmax(version)+1, and inserts the version row.- Authorization today is checked as
(user, action, first-path-segment resource)with no document/client object passed into Permit.io:internal/cognitoauth/middleware.go:84-110,internal/cognitoauth/permitio.go:79-89.
Findings
-
Blocking: the tracking doc’s migration numbers collide with live migrations already in the repo. References:
plans/mutable.metadata.plan.combo.v3.tracking.md:48-75internal/database/migrations: existing00000000000120_*through00000000000123_*are already present (00000000000120_remove_text_extraction,00000000000121_create_eula_tables,00000000000122_make_eula_effective_date_nullable,00000000000123_create_ui_settings) Impact:- Phase 1 cannot start as written.
- Any implementation that follows the tracking doc literally will create filename collisions and broken migration ordering. Recommendation:
- Renumber all new mutable-metadata migrations to the next free sequence and update every tracking reference before coding.
-
Blocking: the plan grants
/custom-metadataread/write toclient_user, but the current auth model is not tenant-scoped. References:- Plan grants:
plans/mutable.metadata.plan.combo.v3.md:730-780 - Current auth check:
internal/cognitoauth/middleware.go:105-110,internal/cognitoauth/permitio.go:79-89 - Current documented limitation:
cmd/auth_related/permit.setup/permit_policies.yaml:155-184Impact: custom-metadata:get/postwould be authorized purely by resource/action on the first path segment.- Without new application-level document/client ownership checks, a
client_useroruser_adminwho knows another document UUID can read/write that document’s custom metadata across clients. Recommendation: - Resolve scoping before implementation. Either:
- Add explicit document/client ownership enforcement in the custom-metadata service/controller and track it as first-class work, or
- Do not grant
client_user/user_adminwrite access to/custom-metadatauntil tenant scoping exists.
- Plan grants:
-
High: Phase 5a in the tracking doc misses the HTTP-layer work needed to return the documented 409. References:
- Tracking:
plans/mutable.metadata.plan.combo.v3.tracking.md:290-299 - Current controller behavior:
api/queryAPI/fieldextractions.go:45-50Impact: - Adding only a service pre-check will not produce the promised HTTP 409.
- As written,
POST /field-extractionswill still return 500 for the new mutual-exclusivity error. Recommendation: - Add explicit controller error mapping and HTTP tests for the legacy write guard, not just service tests.
- Tracking:
-
High: the reset-metadata section is internally inconsistent about documents that already have legacy field extractions. References:
plans/mutable.metadata.plan.combo.v3.md:643-653Impact:- The behavior bullets say a legacy-extraction document “succeeds trivially.”
- The validation section says the same condition returns
409 Conflict. - The tracking doc appears to assume
409, so the plan and tracker are not actually aligned. Recommendation: - Pick one behavior and update both docs. I recommend
409, because it matches the mutual-exclusivity rule and avoids false-success responses on legacy documents.
-
High: the custom-metadata versioning plan overstates its concurrency safety, especially on first write. References:
- Plan claim:
plans/mutable.metadata.plan.combo.v3.md:152-153 - Tracking task:
plans/mutable.metadata.plan.combo.v3.tracking.md:239-245 - Current legacy pattern:
internal/fieldextraction/service.go:70-100Impact: SELECT ... FOR UPDATEon the max-version row does not serialize the first write for a document, because there is no row to lock yet.- Two concurrent first writes can still race and one will fail on
UNIQUE (document_id, version). Recommendation: - Decide on a real serialization strategy now:
- Lock the document row before version assignment, or
- Retry on unique-violation explicitly.
- Add a “concurrent first write” test to the tracker, not just a generic concurrency test.
- Plan claim:
-
Medium: actor/audit provenance is underspecified and currently inconsistent with the plan’s audit goals. References:
- Request bodies still carry caller-supplied
createdBy:plans/mutable.metadata.plan.combo.v3.md:566-571,847-858 - Tracking only explicitly extracts caller identity for reset:
plans/mutable.metadata.plan.combo.v3.tracking.md:431-447,502 - Reset refers to a not-yet-defined “same log sink used by
AssignSchema/DeleteSchema”:plans/mutable.metadata.plan.combo.v3.md:638,1069Impact: - If
created_byis meant to be audit-authoritative, client-supplied JSON is not trustworthy. - The tracker does not currently include the work to derive actor identity from auth context for schema create/update/delete, folder assign, or metadata writes.
- The audit sink itself is assumed rather than designed. Recommendation:
- Decide now whether actor identity is authoritative or merely display metadata.
- If authoritative, derive it from auth context everywhere and add explicit tracking tasks.
- Define the concrete audit sink/API instead of leaving it implied.
- Request bodies still carry caller-supplied
Bottom Line
I would not start implementation until Findings 1 and 2 are resolved. Findings 3 through 6 should be folded into the tracking doc before work starts, otherwise the implementation will either drift from the plan or discover these gaps mid-phase.