03 UI Source Discovery + Deploy Handoff
03 · UI Source Discovery and Deploy Handoff (Phase B)
Discovery (live, via ssh)
- Nuxt source:
/opt/incomex/docker/nuxt-repo/web(git repo, branchmainahead 17 / behind 13 of origin). - Existing Process-Axis-relevant routes already scaffolded:
web/pages/knowledge/registries-pivot/index.vue(12 KB)web/server/api/registries-pivot/{node,rows,summary}.get.ts
- Live serving: nginx serves the built static output at
/opt/incomex/deploys/nuxt-output/public(last built 2026-05-31). Backups:nuxt-output.prev,nuxt-output.bak.20260531-rp-final, etc. - Write perms: source is writable as root.
Why handoff, not auto-deploy
A live frontend rebuild + static swap is outward-facing and not safely reversible in-band, the working tree is git-diverged (ahead 17/behind 13), and a failed build would degrade the knowledge UI. This exceeds "safe additive/reversible." Data layer is 100% ready (all views exist and are dual-path verified), so the UI only needs thin wiring + an operator-run build. Classified BLOCKED_UI_DEPLOY_ACCESS → packet below; no rediscovery needed.
Exact API pattern to match (from existing node.get.ts)
Routes are pure pass-throughs: const rows = await rpQuery('SELECT * FROM <view/fn>($1)', [arg]); return {source, generated_at, ...}. The Process Axis routes need no fn — they read the verified views directly.
Handoff — files to add
Server routes under web/server/api/process-axis/:
// canon-gate.get.ts
export default defineEventHandler(async () => ({
source: 'pg:v_rp_process_canon_gate_summary',
generated_at: new Date().toISOString(),
rows: await rpQuery('SELECT * FROM v_rp_process_canon_gate_summary', []),
}));
// owner-flow.get.ts -> SELECT * FROM v_process_axis_owner_decision_flow ORDER BY sort_rank
// candidates.get.ts -> SELECT * FROM wf_process_candidate ORDER BY candidate_code
// candidate-detail.get.ts (code param) -> wf_process_candidate + wf_process_candidate_member
// residual.get.ts -> SELECT * FROM v_workflow_residual_evidence_hardening_v4
// remediation.get.ts -> SELECT * FROM v_workflow_orphan_remediation_v2
// owner-gate-queue.get.ts -> SELECT * FROM v_process_axis_owner_gate_queue
// birth-queue.get.ts -> SELECT * FROM v_process_axis_birth_request_queue
// action-vocab.get.ts -> SELECT * FROM v_wf_candidate_action_handler_status
// action-preview.post.ts -> SELECT fn_wf_candidate_action_execute($1,$2,$3,$4,$5,true) (preview only; never preview=false from UI)
Page web/pages/knowledge/registries-pivot/process-axis.vue — tabs:
- Official vs Candidate coverage (canon-gate: official 0/453 vs candidate_visible 69)
- Candidate list + detail + component graph (candidates + members)
- Remediation v2 + Residual v4
- Owner gate queue + Birth request queue
- Action panel — renders
action-vocab; buttons callaction-previewonly;gate_classdrives a disabled/locked badge for FAIL_CLOSED_* actions. No checkboxes, no direct mutation, no Nuxt-side math — all counts come from the views.
Build + deploy (operator)
cd /opt/incomex/docker/nuxt-repo/web
npm run build # nuxt build -> .output
cp -r /opt/incomex/deploys/nuxt-output /opt/incomex/deploys/nuxt-output.bak.$(date +%Y%m%d%H%M%S)
rsync -a .output/public/ /opt/incomex/deploys/nuxt-output/public/
# reload nginx if needed
Rollback: restore /opt/incomex/deploys/nuxt-output.prev (or the dated backup) and reload nginx.
Blocker
UI deploy access / git divergence resolution is an operator action. No engineering blocker; the data layer + route spec are complete.