Government ledger implementation (WS2)
Routes, role helpers, scoping, and audit behavior behind the /gov surface
This page describes how the government ledger is implemented. For instructions aimed at government users, see the government ledger user guide.
The ledger turns separate conversations into a shared, searchable record. It is protected by government session and role checks; it is not an industry-facing directory of internal notes.
Surface map
| Route | Purpose | Minimum access |
|---|---|---|
/gov | Dashboard counts, recent activity, duplicate-engagement signal. | Government read |
/gov/organizations | Directory with command, status, tag, and small-business filters. | Government read |
/gov/organizations/[id] | Profile, submissions, engagement timeline, dialogue notes. | Government read |
/gov/engagements | Ledger with command, type, status, and organization filters. | Government read |
/gov/engagements/[id] | Detail, notes, edit, delete. | Government read; write for mutations |
/gov/announcements | Read published announcements; publish form for publishers. | Government read / publisher |
/gov/calls | Open and closed calls; publish form and status toggle for publishers. | Government read / publisher |
/gov/commands | Command hierarchy and PAE portfolio browser. | Government read |
/gov/search | Cross-entity search via searchAll. | Government read |
/gov/notifications | Notification feed and settings. | Government read |
/gov/admin/users, /gov/admin/commands, /gov/admin/audit | Administration. | Admin-tier; audit is Program/System Admin |
The /gov layout requires a government session, enforces pending password rotation, and renders navigation conditionally: the Admin item appears only for ADMIN_TIER_ROLES.
Role helpers
Write helpers are intentionally distinct, and a role that passes one does not necessarily pass another. Check the route's helper rather than copying a UI permission:
| Helper | Covers | Roles that pass |
|---|---|---|
requireGovRead | All ledger reads. | GOV_READONLY, GOV_POC, GOV_COMMAND_LEAD, GOV_ADMIN, GOV_SYSTEM_ADMIN |
requireGovWrite | Engagements, notes, organization status, dialogue. | GOV_POC, GOV_COMMAND_LEAD, GOV_ADMIN, GOV_SYSTEM_ADMIN |
requireGovPublisher | Announcements and calls. | GOV_COMMAND_LEAD, GOV_ADMIN, GOV_SYSTEM_ADMIN |
| Admin helpers | Users and commands; audit and export are narrower. | ADMIN_TIER_ROLES; PROGRAM_ADMIN_ROLES for audit/export; GOV_SYSTEM_ADMIN for hierarchy and admin-role grants |
See authentication and roles for scoping examples.
Dashboard
The dashboard aggregates organization count, submissions in the current quarter, total/planned/follow-up/completed engagements, engagements by command, recent submissions and engagements, and a duplicate signal: organizations engaged by more than one command within the past 90 days or upcoming. The duplicate signal is a coordination warning, not a uniqueness constraint — legitimate multi-command work remains possible.
Organizations
PATCH /api/gov/organizations/[id]/status moves an organization between NEW, UNDER_REVIEW, ACTIVE, WATCHLIST, and INACTIVE; the client component applies the change on selection with no separate submit, and surfaces Unable to update status. on failure. POST /api/gov/organizations/[id]/dialogue appends a government-only note (4,000 character cap) recording author, organization, body, and timestamp. Both write audit events.
When reading a profile, keep the surfaces separate — profile facts are organization-provided, submission summaries are industry-provided, match scores are generated by code, engagements are government-entered, dialogue notes are internal context, and audit events record application actions. Conflating them turns an automated match into an apparent endorsement.
Engagements
The engagement route validates the organization and command foreign keys, creates the row, assigns the current government user as createdBy, and audits engagement_created. Notes are appended with POST /api/gov/engagements/[id]/notes and record their own author. Deletion removes the engagement and its notes.
Types: CONFERENCE, TRADE_SHOW, SYMPOSIUM, INDUSTRY_FORUM, TECH_EXERCISE, SITE_VISIT, MEETING, DEMO, OTHER. Statuses: PLANNED, COMPLETED, FOLLOW_UP, CLOSED.
The form disables submission when no organization or command exists, because both are required foreign keys.
Publishing
Calls are created with title (200 characters), description (8,000 characters), normalized lowercase tags (400 characters), and an optional close date; a blank date produces an open-ended call. The route creates an OPEN CallForTechnology, audits the publication, and calls notifyAllIndustry. Industry users then see the call at /portal/calls and may reference it from a submission until it is closed. A status toggle closes and reopens a call without deleting responses.
Announcements follow the same publication pattern with a title and body instead of a deadline. Both require requireGovPublisher; readonly users and POCs receive a role explanation instead of a form.
Commands, portfolios, and hierarchy
The command directory exposes name, abbreviation, echelon, parent, location, and mission. PortfolioNode records provide PAE technical groupings, tags, assigned interest profiles, and routed-submission counts. Administration separates metadata from hierarchy authority:
POST /api/gov/admin/commandscreates a command.PATCH /api/gov/admin/commands/[id]edits metadata.DELETE /api/gov/admin/commands/[id]removes an eligible command.PATCH /api/gov/admin/commands/[id]/parentreparents after self/cycle checks.
Only GOV_SYSTEM_ADMIN may change the authorization-relevant tree. Reparenting validates the body, rejects self-parenting, walks the proposed parent chain to reject cycles, and updates the row inside a serializable transaction so concurrent changes cannot jointly create a cycle; the previous and new parent IDs are recorded in admin_command_reparent. Other sessions refresh authorization attributes from the database.
Never exercise this flow against a shared production-like tree without an approved change plan: hierarchy is authorization data, not presentation.
User administration and audit
The admin user page lists government users within the caller's scope and creates or updates role, command assignment, and title. Passwords are validated and hashed server-side. Granting or revoking admin-tier roles is reserved for system-admin policy, a caller cannot modify their own account, and the route protects the last system administrator. The audit page is read-only and shows the 200 most recent events (timestamp, event, user, detail) to PROGRAM_ADMIN_ROLES.
Permission failures
Unauthenticated requests return 401. A signed-in industry user receives 403 for government operations. A command lead acting outside its command receives 403 plus an authorization_failure audit record. A Program Admin sees enterprise data but still cannot use system-admin-only hierarchy operations.