Pure·AdminTech Stack · Upgrade Register
The stack overview (MD)
One place · every upgrade this platform still needs

The Upgrade Register

The gap register stopped being a paragraph in a report and became rows. Every upgrade PURE needs — declared by review or derived from a live signal — with an owner, an effort, the evidence that proves it is real, and the check that will prove it done. Reads mcp_upgrade_register(); deep-link any row by its id.

Source Rows As of

The registerEvery upgrade, filterable

Two kinds of row. Declared — ruled by review, the gap register made durable. Derived — produced by a live probe, so it appears the day it becomes true and disappears the day it is fixed. A derived row nobody can close by hand is the point.

IdUpgradeAreaOwnerEffortSeverityStatus

ContractWhat Code creates so this page stops carrying its own data

The page ships reading a live RPC with a declared fallback. This is the table and read that replace the fallback — one register, two row sources, admin-editable.

-- THE UPGRADE REGISTER · Design → Code, 9/1/26 · declared rows + derived rows, one read
-- NOT a duplicate of pure_upgrade_audit: that rail is page-ENGINE-ERA migration (v4.8 → engine
-- version, per-route). This is platform upgrades. The era rail becomes ONE derived source below.

create table if not exists pure_upgrade_register (
  id          text primary key,              -- U-01 … stable, deep-linkable, quotable in tickets
  title       text not null,
  why         text not null,                 -- the consequence if it stays undone
  area        text not null,                 -- security|data|delivery|governance|ux|comms|scale
  owner_lane  text not null,                 -- Code | Design | Mike | Poppy
  effort      text not null,                 -- S | M | L | gated
  severity    text not null,                 -- critical | high | medium | low
  status      text not null default 'open',  -- open | in_flight | blocked | done
  evidence    text,                          -- the query or probe that proves it real
  done_check  text,                          -- the observable that closes it
  link        text, ticket_id text, sort int default 100,
  updated_at  timestamptz default now(), updated_by text
);
revoke all on function public.mcp_upgrade_register() from public, anon;  -- Gate 0, at birth

create or replace function mcp_upgrade_register() returns jsonb
language sql security definer set search_path=public as $$
select jsonb_build_object('ok',true,'as_of',now(),'declared',
  coalesce((select jsonb_agg(to_jsonb(r) order by r.severity, r.sort) from pure_upgrade_register r
            where r.status <> 'done'),'[]'::jsonb),
 'derived', jsonb_build_array(
   -- each derived row is a live count; it self-closes when the count reaches its target
   jsonb_build_object('id','D-flood','title','Board intake flood not dedupe-guarded',
     'area','data','owner_lane','Code','severity','critical',
     'n',(select count(*) from pure_board where title ilike 'Bug: sd%'),'target',0),
   jsonb_build_object('id','D-spine','title','Pages in no spine (invisible, not excluded)',
     'area','governance','owner_lane','Code','severity','high',
     'n',(select count(*) from pure_pages p where coalesce(p.enabled,true) and not exists
          (select 1 from pure_spine_pages s where regexp_replace(s.page,'^/','')=regexp_replace(p.path,'^/',''))),'target',0),
   jsonb_build_object('id','D-era','title','Routes still on the old responsive era',
     'area','ux','owner_lane','Design','severity','medium',
     'n',(select count(*) from pure_responsive_audit where era='v4.8'),'target',0),
   jsonb_build_object('id','D-queues','title','Job queues with no registry row (orphan queues)',
     'area','governance','owner_lane','Code','severity','medium',
     'n',greatest(0,(select count(*) from information_schema.tables
        where table_schema='public' and table_name ~ '_(jobs|queue)$')
        -(select count(*) from pure_queue_registry)),'target',0)
 )); $$;

-- Cron: refresh nothing (the read is live). Instead a daily tick files ONE deduped ticket per
-- derived row whose count moved the wrong way — state-change alerting, per the health canon.

Why not reuse pure_upgrade_audit

Its name promises more than it holds: that rail tracks page engine-era migrations — route, from_era, to_version, commit sha, revert. It answers "which routes are still v4.8", not "what does the platform still need". Reusing it would force platform upgrades into a per-route shape they do not have. So it stays, and becomes derived row D-era here. Named, so nobody re-litigates it later.

Declared vs derived

A declared row is a judgment — someone decided this matters. A derived row is arithmetic — it exists while a count is wrong. Declared rows need a human to close them; derived rows cannot be closed by hand at all, which is exactly why the flood and the orphans belong in that half.

Deep links everywhere

Every row is #U-07. A ticket, a lane mail, or Poppy can point at one upgrade and land on it highlighted. Filters live in the URL too — ?owner=Code&crit=1 is a shareable view.