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
Conversation
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>
Author
Original prompt from Patrick
|
Author
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
⚙️ Control Options:
|
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>
* 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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes the gap between this platform and the public Nigeria National Single Window portal (
nsw.gov.ng). The comparison is indocs/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
stakeholderRegistrationsandstakeholderMandatestables. Registration mints a public reference, startspending, and grants nothing on its own — a reviewer must approve it. A mandate is a durable principal→agent grant with a validity window; revocation writesrevokedAt/revokedBy/revocationReasonrather 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:The mandate is re-checked at action time, not just at grant time, and
getActiveStakeholderMandateonly 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_FOUNDso the endpoint can't be used to discover which references exist. Rate-limited per IP via a newpublicRateLimitedProcedure. NSW's "Permit/COO Validation" tile covers both documents; we only did COO (/verify/:certNumber), sooga.validatePermitnow 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 bydriftVessel(). An anonymous caller asking where their cargo was got an invented position in the wrong country. These now read persistedvessel_tracking_eventsand manifests, and: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;nullrather than0/""—originLat/originLon: 0is a real coordinate in the Gulf of Guinea that a map will happily plot, andcargoStatus: "" as CargoStatuswas a cast into an enum with no such member;riskFlagisnullunless 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;logCargoEventno 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:
declarationsknew nothing about transport documents,billsOfLading.blNumberis indexed but not unique, andmanifestsidentified its vessel only byvesselNamewhilevesselTrackingEventskeys onmmsi/imoNumber. So the keys are now added rather than inferred — a nullablebillOfLadingId/billOfLadingNumberon declarations, nullablemmsi/imoon manifests — andgetShipmentPositionwalks declaration (number or UCR) → BL → manifest → vessel → latest AIS fix, reporting which link is missing rather than a blanket failure: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'sSwitchmatches in order, so/verify/permit/...would otherwise bindcertNumber = "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.CargoTrackingMapandPortHeatmapnow 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 --noEmitstays at the 72 errors of themainbaseline 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