Skip to content

NSW parity: stakeholder registration, agent mandates, public tracking/validation, and honest cargo tracking - #35

Open
devin-ai-integration[bot] wants to merge 19 commits into
devin/1787587788-fail-closed-remediationfrom
devin/nsw-parity-registrations
Open

NSW parity: stakeholder registration, agent mandates, public tracking/validation, and honest cargo tracking#35
devin-ai-integration[bot] wants to merge 19 commits into
devin/1787587788-fail-closed-remediationfrom
devin/nsw-parity-registrations

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Aug 24, 2026

Copy link
Copy Markdown

Summary

Closes the gap between this platform and the public Nigeria National Single Window portal (nsw.gov.ng). The comparison is in docs/nsw-gov-ng-parity.md: on the core (declarations, risk, payments, AEO, OGA workflows, manifests) this platform is well ahead of what NSW exposes — NSW still lists Declarations as "Coming Soon" — but it was missing NSW's entire party-registration layer. Only importers/exporters could register; there was no way to register a licensed customs/freight-forwarding agent, shipping line, shipping company or airline/GHA, and no way for an importer to authorise an agent to clear on their behalf, which is the commonest customs arrangement there is.

Four gaps implemented, plus one defect found while comparing.

Stakeholder registration + agent mandates. New stakeholderRegistrations and stakeholderMandates tables. Registration mints a public reference, starts pending, and grants nothing on its own — a reviewer must approve it. A mandate is a durable principal→agent grant with a validity window; revocation writes revokedAt/revokedBy/revocationReason rather than deleting, so history survives audit.

Filing, paying and trade-finance now resolve who is acting for whom through one helper instead of trusting traderId:

// server/_core/mandateAuthorization.ts
resolveActingPrincipal(input.principalUserId, ctx.user)  // create paths
requireDeclarationActor(decl, ctx.user)                  // act-on-existing paths

The mandate is re-checked at action time, not just at grant time, and getActiveStakeholderMandate only returns a mandate whose agent still holds an approved, unexpired licence — so a licence lapsing silently strips the agent's authority. A declaration filed by an agent records both parties (principalId, actingAgentId); the principal keeps full access to their own record regardless of who filed it. No mandate, revoked, outside the window, or lapsed licence ⇒ FORBIDDEN; DB unreachable ⇒ SERVICE_UNAVAILABLE, never a pass.

Public "Track your Application" and permit validation. One unauthenticated lookup resolves a reference against registrations, declarations (number or UCR) and OGA permits, returning status and timestamps only — never goods, values, parties, risk lane or officer notes, and an unknown reference is uniformly NOT_FOUND so the endpoint can't be used to discover which references exist. Rate-limited per IP via a new publicRateLimitedProcedure. NSW's "Permit/COO Validation" tile covers both documents; we only did COO (/verify/:certNumber), so oga.validatePermit now answers authenticity/validity/expiry/issuing-agency for a permit number, mirroring the COO contract's disclosure level.

Fabricated cargo tracking removed. Not a parity gap — a defect in the same family as the fabricated-data findings in #33. Five unauthenticated procedures served hardcoded Kenyan fixtures — MSC NAIROBI, "Mombasa International Port", portCode: "KEMBA", coverageArea: "Indian Ocean — East Africa Corridor" — with positions synthesised from the wall clock by driftVessel(). An anonymous caller asking where their cargo was got an invented position in the wrong country. These now read persisted vessel_tracking_events and manifests, and:

  • a failed query ⇒ SERVICE_UNAVAILABLE; a successful query with no rows ⇒ an explicit empty result. Reporting empty as broken is as untrue as reporting it as healthy, and it made a no-match vessel search look like an outage;
  • unknown fields return null rather than 0/""originLat/originLon: 0 is a real coordinate in the Gulf of Guinea that a map will happily plot, and cargoStatus: "" as CargoStatus was a cast into an enum with no such member;
  • vessel riskFlag is null unless a real assessment exists. An intermediate version derived it from an inline country list (["IRN","PRK","SYR","RUS","BLR"]) — an invented compliance signal is the same lie as the Kenyan fixtures, just less obvious;
  • logCargoEvent no longer swallows a Kafka failure and returns { success: true };
    Shipment tracking by declaration reference (NSW's "Cargo Tracking" tile) could not be answered at all, because the linkage didn't exist in the schema: declarations knew nothing about transport documents, billsOfLading.blNumber is indexed but not unique, and manifests identified its vessel only by vesselName while vesselTrackingEvents keys on mmsi/imoNumber. So the keys are now added rather than inferred — a nullable billOfLadingId/billOfLadingNumber on declarations, nullable mmsi/imo on manifests — and getShipmentPosition walks declaration (number or UCR) → BL → manifest → vessel → latest AIS fix, reporting which link is missing rather than a blanket failure:
not_linked   bill_of_lading_not_linked          declared BL absent or not yet filed
unavailable  ambiguous_bill_of_lading           the BL number matches more than one row
unavailable  bill_of_lading_not_in_manifest     BL filed, manifest unavailable
unavailable  vessel_identifier_missing | ambiguous_vessel_name | no_ais_position

A consignee whose cargo has no AIS fix yet is told exactly that, not that the service is broken. Filing resolves a declared BL number to exactly one row (qualified by manifest number when needed) and rejects ambiguity instead of picking one; for manifests predating the identifier columns there's one controlled fallback — an exact vessel-name match resolving to a single distinct mmsi — and the response labels linkage as identifier-derived vs name-matched so a name match is never passed off as positive identification. Public and rate-limited like the other trackers, returning position/ETA/destination only — no consignee, shipper, goods, HS code, values or risk lane.

Client. Public: application tracker, shipment tracker, permit validation (registered before /verify/:certNumber — wouter's Switch matches in order, so /verify/permit/... would otherwise bind certNumber = "permit"), both linked as eService tiles from the landing page. Authenticated: stakeholder registration, a reviewer queue gated to reviewer roles, and mandate management showing active/scheduled/expired/revoked distinctly — a deliberate revocation should not look like a lapse. The mandate form picks an agent by organisation name and licence number from an approved-agent directory; an importer has no way to know their agent's internal user id. CargoTrackingMap and PortHeatmap now render unavailable and empty differently — an outage must not look like a calm, empty map.

Verification

Behavioural tests throughout (no source-text assertions, no test weakened to pass). Targeted suites for stakeholder registration, mandate enforcement, parity follow-ups, declarations, payments, rules-of-origin and cargo all pass; tsc --noEmit stays at the 72 errors of the main baseline with no diagnostics in changed files.

No UI evidence: the browser subsystem on this machine was unavailable for the whole session, so every page above is verified server-side only — nobody has seen them rendered. Treat the visual layer as untested.

Stacked on the #33 remediation branch (base devin/1787587788-fail-closed-remediation), so review that first.

Link to Devin session: https://app.devin.ai/sessions/e68f0a7bf0e04fb8a3ba32ddb8e1fa23
Requested by: @munisp

devin-ai-integration Bot and others added 5 commits August 24, 2026 17:55
Co-Authored-By: Patrick Munis <pmunis@gmail.com>
Co-Authored-By: Patrick Munis <pmunis@gmail.com>
Co-Authored-By: Patrick Munis <pmunis@gmail.com>
Co-Authored-By: Patrick Munis <pmunis@gmail.com>
Co-Authored-By: Patrick Munis <pmunis@gmail.com>
@devin-ai-integration

Copy link
Copy Markdown
Author
Original prompt from Patrick

Clone, review and analyze this https://github.com/munisp/singlewindow
Afterwhich, using Attach file analyze the codebase and implement the findings
ATTACHMENT:"https://app.devin.ai/attachments/80878dc3-af42-4287-9b79-2ab7bdfe3f03/codebase-defect-discovery-prompt.pdf"

@devin-ai-integration

Copy link
Copy Markdown
Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

devin-ai-integration Bot and others added 7 commits August 24, 2026 18:39
Co-Authored-By: Patrick Munis <pmunis@gmail.com>
…ing manifest

Co-Authored-By: Patrick Munis <pmunis@gmail.com>
Co-Authored-By: Patrick Munis <pmunis@gmail.com>
Co-Authored-By: Patrick Munis <pmunis@gmail.com>
Co-Authored-By: Patrick Munis <pmunis@gmail.com>
Co-Authored-By: Patrick Munis <pmunis@gmail.com>
Co-Authored-By: Patrick Munis <pmunis@gmail.com>
munisp and others added 7 commits August 24, 2026 19:30
* feat: add excise digital tax stamps domain

Co-Authored-By: Patrick Munis <pmunis@gmail.com>

* fix: coarsen anonymous excise scan locations

Co-Authored-By: Patrick Munis <pmunis@gmail.com>

* fix: harden excise lifecycle and settlement

Co-Authored-By: Patrick Munis <pmunis@gmail.com>

* fix: fail closed without excise coordination

Co-Authored-By: Patrick Munis <pmunis@gmail.com>

* test: cover excise lifecycle safety

Co-Authored-By: Patrick Munis <pmunis@gmail.com>

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Patrick Munis <pmunis@gmail.com>
Co-Authored-By: Patrick Munis <pmunis@gmail.com>
Co-Authored-By: Patrick Munis <pmunis@gmail.com>
Co-Authored-By: Patrick Munis <pmunis@gmail.com>
Co-Authored-By: Patrick Munis <pmunis@gmail.com>
Co-Authored-By: Patrick Munis <pmunis@gmail.com>
* feat: add regulatory obligation layer

Co-Authored-By: Patrick Munis <pmunis@gmail.com>

* fix: harden regulatory clearance controls

Co-Authored-By: Patrick Munis <pmunis@gmail.com>

* fix: wire quota ledger accounts

Co-Authored-By: Patrick Munis <pmunis@gmail.com>

* fix: version quota allocation retries

Co-Authored-By: Patrick Munis <pmunis@gmail.com>

* fix: enforce regulatory clearance obligations

Co-Authored-By: Patrick Munis <pmunis@gmail.com>

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Patrick Munis <pmunis@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant