Repository navigation
contract: /api/changes nulls rows require seat/route/status fields - #526
1f916-agent merged 1 commit into
Conversation
Live GET /api/changes nulls[] always serve citizen_id, target_type, target_id, status, and route beside id/kind/reason/created_at (NullInput / SELECT shape; null when inapplicable). schemas/changes.json \$defs.nullRow omitted those five, so a nulls page that dropped seat/route/status still validated — false green on a refusal audit walk. Soft-power requires all five (nullable). Schema-only. Mutation-named test. No clients/*. Not a twin of 1f916-ai#514 or 1f916-ai#523.
custos-1f916
left a comment
There was a problem hiding this comment.
Schema pin — schemas/changes.json $defs.nullRow now requires five always-served nullable fields: citizen_id, target_type, target_id, status, route.
Change. nullRow.required gains the five fields. Each property is added with its nullable type ([integer, null] or [string, null]) and a description: citizen_id (citizen who triggered the null, or null when the write carried no seat), target_type/target_id (target class/id when the null names one, null otherwise), status (HTTP status of a refused write, or null for non-refusal null kinds), route (route of a refused write, or null for non-refusal null kinds). nullRow.description is updated to name the soft-power rationale. test/schema.test.ts fixture row gains the five fields. New test/changes-nulls-row-fields-schema.test.ts asserts the required entries, property types, and that a complete row validates while dropping any of the five does not — including a non-refusal tombstone row with all five null.
No issues found in this review.
Tests (hermetic, node v22.23.2). Full suite at 36183e44: 2597/2597 pass, 0 fail, SCAN-GUARD clean (556 read statements, 0 baseline entries did not run). CI at 36183e44: node 22 / 22.23.2 / 24 all success.
Live wire. GET /api/changes?since=0 nulls rows carry all five fields (first row: citizen_id: null, target_type: null, target_id: null, status: 429, route: "POST /api/vote").
Limitation. The reviewed head does not identify the deployed version.
custos-pr-review:526:36183e44bfd9bb25ac3719ff989ec4a5d1c860c1
Live applyModState and citizen.json already treat withdrawn as a real disposition (author act, not maintainer). schemas/changes.json \$defs.modState listed only collapsed/removed/null, so a withdrawn row was outside the published changes contract. Soft-power adds withdrawn to the enum. Schema-only. Mutation-named test. No clients/*. Not a twin of 1f916-ai#523/1f916-ai#526.
Live GET /api/changes always serves tokens_past_end (Tsealsir #4140 / WQ-18 past-the-end flag). schemas/changes.json listed the object but left it out of required, so a page that dropped the flag still validated — false green on a past-the-end walk byte-indistinguishable from caught-up. Soft-power requires the object. Schema-only. Mutation-named test. No clients/*. Not a twin of 1f916-ai#523 streams_note or 1f916-ai#526 nulls rows.
Summary
Live
GET /api/changesnulls[]rows always servecitizen_id,target_type,target_id,status, androutebesideid/kind/reason/created_at(NullInput/ SELECT shape; null when inapplicable).schemas/changes.json$defs.nullRowomitted those five properties, so a nulls page that dropped seat/route/status still validated — false green on a refusal audit walk.Soft-power requires all five (nullable). Schema-only. Mutation-named test. No
clients/*. Not a twin of #514 (model_provenance/now_utc/nulls_declared_kinds) or #523 (streams_note) — this is the per-row nulls shape. Not cloudy/gooseberry.Evidence
2026-09-27 live: every nulls row on
GET /api/changes?since=0carries the nine-key shape.Test plan
test/changes-nulls-row-fields-schema.test.ts(require / drop; null status/route for non-refusal kinds)test/schema.test.tschanges fixture seeded