Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
177 commits
Select commit Hold shift + click to select a range
45f61f7
Add read-only Zetaris REST adapter with sample mode, retries and JSON…
HamidZ11 Oct 22, 2026
2ac638f
Register seed PoE data in Zetaris and read ports through the adapter
HamidZ11 Oct 22, 2026
8650757
Merge branch 'main' into zetaris-adapter
HamidZ11 Oct 22, 2026
1d3f908
Address PR #26 review: escapes, numeric check, central paths, setup s…
HamidZ11 Oct 22, 2026
72e32c4
Add Zetaris -> evaluator end-to-end test
HamidZ11 Oct 22, 2026
eff8c67
Integrate Meterless H-MEM engine (Python port, 13/13 conformance) as …
HamidZ11 Oct 22, 2026
bee620b
Evaluator follow-ups: strict official-source check, document reasons …
HamidZ11 Oct 22, 2026
6688bdc
Add Meterless Nightshift generator with NVIDIA Nemotron and sample fa…
HamidZ11 Oct 22, 2026
860f24d
Opt-in LLM_PROVIDER=meterless hook in llm.py
HamidZ11 Oct 22, 2026
85e840a
Add sponsor-tech write-up for Zetaris and H-MEM
HamidZ11 Oct 22, 2026
eb5701a
Generator: official METERLESS_* env, bounded timeouts, optional NVIDI…
HamidZ11 Oct 22, 2026
81c96b4
Merge origin/zetaris-adapter into run-pipeline
HamidZ11 Oct 22, 2026
c9d6311
Merge origin/hmem-engine into run-pipeline
HamidZ11 Oct 22, 2026
3f23083
Merge origin/evaluator-followups into run-pipeline
HamidZ11 Oct 22, 2026
9386fad
Merge origin/nightshift-generator into run-pipeline
HamidZ11 Oct 22, 2026
f11d972
Add run pipeline orchestrator with ResearchAgent and BriefingAgent
HamidZ11 Oct 22, 2026
04f9368
Add run.py CLI and POST /run server that work from a fresh clone
HamidZ11 Oct 22, 2026
6f1265c
Test run pipeline: fresh clone, trace, H-MEM research, fallbacks, bounds
HamidZ11 Oct 22, 2026
44a7aeb
Add .env.example, RUNNING.md and generated input/output examples
HamidZ11 Oct 22, 2026
2df1406
Generator: strip model reasoning (</think>) from content before use
HamidZ11 Oct 22, 2026
07f1d4f
Merge origin/nightshift-generator into run-pipeline
HamidZ11 Oct 22, 2026
4cdd51f
Add recorded live Nemotron run for Bermuda
HamidZ11 Oct 22, 2026
953b11d
RUNNING.md: use python3 (3.12+) and explain PORT when 8000 is busy
HamidZ11 Oct 22, 2026
1892f87
Re-record live Nemotron run with official METERLESS_* settings
HamidZ11 Oct 22, 2026
a1e234e
Merge origin/run-pipeline into sponsor-docs
HamidZ11 Oct 22, 2026
41ceb86
Sponsor write-up: add Nightshift, NVIDIA and full run pipeline
HamidZ11 Oct 22, 2026
6a93fe7
fix: Windows-compatible locking, centralized REPO_ROOT, validation be…
Oct 22, 2026
3d6de37
paths.py: shared superset of repo path constants and get_*() helpers
HamidZ11 Oct 22, 2026
e3a414c
paths.py: shared superset of repo path constants and get_*() helpers
HamidZ11 Oct 22, 2026
71bdafe
Merge origin/hmem-engine into run-pipeline
HamidZ11 Oct 22, 2026
f9709fb
Merge origin/zetaris-adapter into run-pipeline
HamidZ11 Oct 22, 2026
5d8719e
Merge origin/run-pipeline into sponsor-docs
HamidZ11 Oct 22, 2026
6ddb981
Sponsor write-up: update H-MEM lock limitation for cross-platform loc…
HamidZ11 Oct 22, 2026
ec5c065
H-MEM lock: acquire/release RLock in thread-only path, release fcntl …
HamidZ11 Oct 22, 2026
fc2b825
Merge origin/hmem-engine into run-pipeline
HamidZ11 Oct 22, 2026
6d74294
RUNNING.md: install filelock for cross-platform H-MEM locking
HamidZ11 Oct 22, 2026
fa05800
Merge origin/run-pipeline into sponsor-docs
HamidZ11 Oct 22, 2026
c6b4d5f
Sponsor write-up: lock limitation reflects fixed cross-platform locking
HamidZ11 Oct 22, 2026
5e71937
H-MEM remember: reinforce unchanged verdicts instead of duplicating r…
HamidZ11 Oct 22, 2026
103f69d
Merge origin/hmem-engine into run-pipeline
HamidZ11 Oct 22, 2026
7da0ce8
Ignore data/.lock written by the H-MEM file lock
HamidZ11 Oct 22, 2026
c7be246
Merge origin/hmem-engine into run-pipeline
HamidZ11 Oct 22, 2026
cec7f83
Demo page at GET / with GET /zones and GET /runs/{run_id}/trace
HamidZ11 Oct 23, 2026
3957adf
Test demo page, zones and trace routes, including bad run_ids
HamidZ11 Oct 23, 2026
ebb5b7f
RUNNING.md: document the demo page and new endpoints
HamidZ11 Oct 23, 2026
a7b3be7
Merge origin/run-pipeline into sponsor-docs
HamidZ11 Oct 23, 2026
0625bb0
Sponsor write-up: H-MEM reinforce behaviour and demo page
HamidZ11 Oct 23, 2026
79e85df
Stream runs as Server-Sent Events at GET /run/stream; add port coordi…
HamidZ11 Oct 23, 2026
ddc33b8
Redesign demo page as a verification console with chart and live agen…
HamidZ11 Oct 23, 2026
5c7bceb
Test the run stream and the redesigned page
HamidZ11 Oct 23, 2026
e6d9813
Regenerate sample outputs with coordinates; document the console and …
HamidZ11 Oct 23, 2026
b1bc4fe
Restore infra/agents/ tooling for batch processing
Oct 23, 2026
a461ac2
chore: restore infra/agents/ tooling for batch processing
Oct 23, 2026
bfaae80
feat(hackathon): pipeline agent Nemotron, Zetaris et H-MEM
Oct 23, 2026
3342215
feat(v2): integrate Nemotron specialization, Zetaris badges and ZoneF…
Oct 23, 2026
a07af3b
feat(v2): enforce Nightshift first-hop, active H-MEM loop and offline…
Oct 23, 2026
60b9bfa
Fix extract_ports_llm: extract once, then cross-reference Zetaris
HamidZ11 Oct 23, 2026
eea28c7
Fix POST /poe/verify: real Zetaris client, run ports, portable trace …
HamidZ11 Oct 23, 2026
1074c59
Restore fresh-clone safety in run.py and keep the --zone interface
HamidZ11 Oct 23, 2026
6847db1
Use real Nemotron chat model IDs in the role chains
HamidZ11 Oct 23, 2026
be14084
Pin NIM fallback-mechanics tests to the pre-Nemotron chains
HamidZ11 Oct 23, 2026
0e33b49
Route the primary LLM chain through Nightshift, falling back to NIM
HamidZ11 Oct 23, 2026
3d62862
Zetaris validation: live-failure fallback and name-normalised matching
HamidZ11 Oct 23, 2026
04ac11d
Make H-MEM load-bearing in the PoE pipeline scoring
HamidZ11 Oct 23, 2026
01da7f9
merge: sync with main and adopt updated infra scripts
Oct 23, 2026
52d3eef
merge: integrate origin/main into feat/integrate-poe-pipeline and res…
Oct 23, 2026
78afc7b
Register UN/LOCODE and the World Port Index in Zetaris as read-only r…
HamidZ11 Oct 23, 2026
785ec5a
Zetaris adapter: get_registry_ports and trace_event
HamidZ11 Oct 23, 2026
5a7fa93
Validate ports against independent registries, not the seed
HamidZ11 Oct 23, 2026
8359a6d
RUNNING.md: independent registries, licences and setup
HamidZ11 Oct 23, 2026
01aebfe
Tier ports by official-source and registry evidence instead of a bina…
HamidZ11 Oct 23, 2026
c10f09b
RUNNING.md: evidence tiers and where document text exists
HamidZ11 Oct 23, 2026
f9fc44a
ci: skip legacy V1 network integration tests on port 8001 via conftes…
Oct 23, 2026
b872ef8
Revert "ci: skip legacy V1 network integration tests on port 8001 via…
Oct 23, 2026
33568b4
fix(ci): restore confidence_parts and reasons_en in geojson (V2 contr…
Oct 23, 2026
6c5a29b
fix(ci): import _reasons_en in ports_to_geojson (shard 3/6 NameError)
Oct 23, 2026
d293de9
fix(ci): update fingerprint contract V2 (nemotron judge/page chains)
Oct 23, 2026
d920e5c
Merge origin/feat/integrate-poe-pipeline into zetaris-registries
HamidZ11 Oct 24, 2026
69d1a55
LLM readers send the window the caller chose; log the chars actually …
HamidZ11 Oct 24, 2026
93609ca
H-MEM: don't prefix "Port " to names that already start with Port
HamidZ11 Oct 24, 2026
dea81a2
SearXNG: default to 127.0.0.1:8890, off the local Zetaris ports
HamidZ11 Oct 24, 2026
af98049
/api/health: unreadable memory probe is unknown (null), not 100%
HamidZ11 Oct 24, 2026
f32764d
Test that concurrent zones each read with their own context window
HamidZ11 Oct 24, 2026
87769b5
0-port retry: put the grounded synthesis before the context
HamidZ11 Oct 24, 2026
4519e93
feat(v2): add UI badges, telemetry, remove admin gate and add export …
Oct 24, 2026
12f0c94
Merge pull request #35 from Berry-Mappemonde/phase1-fixes
NAVIGUIDE-for-Berry-Mappemonde Oct 24, 2026
694967d
Merge pull request #34 from Berry-Mappemonde/zetaris-registries
NAVIGUIDE-for-Berry-Mappemonde Oct 24, 2026
5dca95b
chore: resolve merge conflict in .gitignore
Oct 24, 2026
b986dd6
chore: ensure fixtures/ remains ignored to prevent GitHub 100MB limit
Oct 24, 2026
ac259d9
fix(ui): restore admin gate, fix zone click endpoint, resolve React r…
Oct 24, 2026
cf729d1
/poe/verify: return H-MEM counts and a trace name, not an absolute path
HamidZ11 Oct 24, 2026
a52de8c
V2 fiche: registry tier badges, explicit Verify, telemetry from the p…
HamidZ11 Oct 24, 2026
80088c3
fix(ui): isAdmin stays true locally when ADMIN_KEY unset
Oct 24, 2026
7000c2f
feat(ui): complete registry tiers badge wiring, explicit verify butto…
Oct 24, 2026
894c6cb
V2 fiche: one tier colour map for popup and sidebar, scores without "%"
HamidZ11 Oct 25, 2026
bc70535
Merge origin/feat/integrate-poe-pipeline into sponsor-docs
HamidZ11 Oct 25, 2026
b26e9f3
Sponsor write-up: Zetaris registries and evidence tiers; target the V…
HamidZ11 Oct 25, 2026
9f3a2ad
RUNNING.md: sample tier breakdown is 13 tier_registered / 7 tier3
HamidZ11 Oct 25, 2026
9403ef7
Official evidence: entry term near the port's name, not an address
HamidZ11 Oct 25, 2026
4114000
Sample official pages: verbatim snapshots that designate ports of entry
HamidZ11 Oct 25, 2026
8a33d79
Stored official evidence: build at generation, look up in run.py and …
HamidZ11 Oct 25, 2026
2081bc6
RUNNING.md: stronger official evidence, stored snippets, new sample b…
HamidZ11 Oct 25, 2026
008b757
Merge origin/feat/v2-ui-and-fixtures (PR #36) into evidence-storage
HamidZ11 Oct 25, 2026
b368381
Port card: show official evidence (source link, snippet collapsed)
HamidZ11 Oct 25, 2026
0503860
Sponsor write-up: stored official evidence (PR #41) and the new tier …
HamidZ11 Oct 25, 2026
974da96
Official evidence: strong vs weak entry terms; CBP Alaska page qualifies
HamidZ11 Oct 25, 2026
803066c
RUNNING.md: strong and weak entry terms; sample breakdown 2 / 2 / 11 / 5
HamidZ11 Oct 25, 2026
a51740f
Merge pull request #41 from Berry-Mappemonde/evidence-storage
NAVIGUIDE-for-Berry-Mappemonde Oct 25, 2026
5e17647
Sponsor write-up: strong/weak entry terms and the final sample breakdown
HamidZ11 Oct 25, 2026
42d54df
Official evidence: "point(s) of entry" as a weak entry term
HamidZ11 Oct 25, 2026
fe00743
Merge origin/feat/integrate-poe-pipeline into sponsor-docs
HamidZ11 Oct 25, 2026
c8ae007
Sponsor write-up: link #41's files; weak terms as on the base
HamidZ11 Oct 25, 2026
4122c86
README: judge-facing PoE Reconciler section; pycountry in the minimal…
HamidZ11 Oct 25, 2026
8e9f150
README: note the Python version on macOS and PORT for --serve
HamidZ11 Oct 25, 2026
d0f6b4f
pycountry: in the minimal install, and one warning when it is missing
HamidZ11 Oct 25, 2026
e30ea65
Evaluator: registry official evidence counts as an official, direct s…
HamidZ11 Oct 25, 2026
a5698aa
Regenerate sample outputs (run.py --input, sample mode)
HamidZ11 Oct 25, 2026
4d0d0c0
README: tier vs verdict limit after #45 (St. George's tier1, flag at 60)
HamidZ11 Oct 25, 2026
47fd757
Add input example 04_benin: a retry round in the example traces
HamidZ11 Oct 25, 2026
7e0c9e4
README: define tier (legal-status evidence) and verdict (data-quality…
HamidZ11 Oct 25, 2026
b048f56
README: point to the Benin example for the retry round (added in #45)
HamidZ11 Oct 25, 2026
7f98ff9
Evaluator: a close registry position corroborates the geocode, for ti…
HamidZ11 Oct 25, 2026
0857c36
Regenerate sample outputs; RUNNING.md: Bermuda passes with WPI-corrob…
HamidZ11 Oct 25, 2026
832e2d3
README: St. George's passes with #45 (expected summary, tier vs verdi…
HamidZ11 Oct 25, 2026
b29aaea
Brief: list official evidence pages under Sources; never an empty Sou…
HamidZ11 Oct 25, 2026
8932b76
Regenerate sample outputs (brief lists the official evidence pages)
HamidZ11 Oct 25, 2026
06d5b61
Sponsor write-up: match #45 (official evidence in the score, corrobor…
HamidZ11 Oct 25, 2026
653e02b
Merge pull request #42 from Berry-Mappemonde/evidence-followup
NAVIGUIDE-for-Berry-Mappemonde Oct 25, 2026
f92fa47
Merge pull request #44 from Berry-Mappemonde/pycountry-required
NAVIGUIDE-for-Berry-Mappemonde Oct 25, 2026
79a6cf7
chore: sync with feat/integrate-poe-pipeline (#42 and #44 merged)
Oct 25, 2026
f6b014d
Merge pull request #45 from Berry-Mappemonde/tier-score-consistency
NAVIGUIDE-for-Berry-Mappemonde Oct 25, 2026
e113ba0
chore: sync sponsor-docs with base
Oct 25, 2026
ecb4e68
Merge pull request #31 from Berry-Mappemonde/sponsor-docs
NAVIGUIDE-for-Berry-Mappemonde Oct 25, 2026
8a6374b
Merge pull request #43 from Berry-Mappemonde/readme-v2
NAVIGUIDE-for-Berry-Mappemonde Oct 25, 2026
164f740
Frontend portKey: same rules as backend port_key, shared test cases
HamidZ11 Oct 25, 2026
9404cea
NemotronJudge: advisory critic after the first evaluation
HamidZ11 Oct 25, 2026
74f6299
Regenerate sample outputs (judge skipped in sample mode); RUNNING.md:…
HamidZ11 Oct 25, 2026
28a065e
Merge pull request #47 from Berry-Mappemonde/v2-salvage
NAVIGUIDE-for-Berry-Mappemonde Oct 25, 2026
84834f8
Re-record the live Bermuda run (Zetaris live, Nightshift Nemotron, Ne…
HamidZ11 Oct 25, 2026
51475c1
Docs: NemotronJudge in SPONSOR_TECH (Nemotron section), README archit…
HamidZ11 Oct 25, 2026
8084af6
NemotronJudge trace event carries each reason and quoted excerpt (200…
HamidZ11 Oct 25, 2026
4b6933c
Add seed_evidence_from_snapshots.py: fill poe_evidence without regene…
HamidZ11 Oct 25, 2026
cb2ae93
Re-record the live Bermuda run with evidence from Mongo (source "mongo")
HamidZ11 Oct 25, 2026
9b93322
Merge pull request #48 from Berry-Mappemonde/v2-salvage
NAVIGUIDE-for-Berry-Mappemonde Oct 25, 2026
ddc0123
Merge pull request #49 from Berry-Mappemonde/seed-evidence
NAVIGUIDE-for-Berry-Mappemonde Oct 25, 2026
c2b6f1f
Add metadata.json: machine-readable project summary for the submission
HamidZ11 Oct 25, 2026
23d0771
Add docs/SUBMISSION_DECLARATION.md: pre-existing work declaration
HamidZ11 Oct 25, 2026
3cce13a
Merge pull request #50 from Berry-Mappemonde/submission-metadata
NAVIGUIDE-for-Berry-Mappemonde Oct 25, 2026
f7aeefe
Merge pull request #51 from Berry-Mappemonde/docs-declaration
NAVIGUIDE-for-Berry-Mappemonde Oct 26, 2026
6ea7064
Polish the top nav and Ports of entry screens with Meridian tokens
HamidZ11 Oct 26, 2026
fbc44c7
Add before/after screenshots of the Ports of entry polish
HamidZ11 Oct 26, 2026
78b9f43
Keep PoE markers clickable, keep the verify result, neutral EEZ polygons
HamidZ11 Oct 26, 2026
1d598a6
Refresh the after screenshots of the Ports of entry polish
HamidZ11 Oct 26, 2026
882d09b
Fit the map to the selected zone, readable evidence excerpts, cleaner…
HamidZ11 Oct 26, 2026
ad8d162
Refresh the round-2 screenshots of the Ports of entry polish
HamidZ11 Oct 26, 2026
da24acb
Polish non-PoE mode sidebars with Meridian presentation
HamidZ11 Oct 26, 2026
9bf56e7
Refresh sidebar screenshots on latest ui-polish baseline
HamidZ11 Oct 26, 2026
afbb6e7
Frame the ports on zone selection, dedupe the attribution bar
HamidZ11 Oct 26, 2026
7086f4e
Refresh the round-3 screenshots of the Ports of entry polish
HamidZ11 Oct 26, 2026
7ad91b1
Hide the selected EEZ outline above zoom 9
HamidZ11 Oct 26, 2026
034e2d9
Make the pressed Show review toggle unmistakable
HamidZ11 Oct 26, 2026
66abefc
Merge remote-tracking branch 'origin/ui-polish' into ui-polish-modes
HamidZ11 Oct 26, 2026
4e934fe
Verify loading state, Meridian disclaimer modal, neutral map chrome, …
HamidZ11 Oct 26, 2026
c1af449
Add before/after screenshots for the round-4 polish
HamidZ11 Oct 26, 2026
762aee9
Reminder on every basemap, focus management, html lang, link placemen…
HamidZ11 Oct 26, 2026
7e43e05
Refresh the round-5 screenshots
HamidZ11 Oct 26, 2026
d7b90d8
Show the sheet heading focus ring for keyboard focus only
HamidZ11 Oct 26, 2026
e2f1e09
Refresh the Bermuda screenshot (no focus ring after a mouse click)
HamidZ11 Oct 26, 2026
585a071
Merge pull request #52 from Berry-Mappemonde/ui-polish
NAVIGUIDE-for-Berry-Mappemonde Oct 26, 2026
917058a
Merge pull request #53 from Berry-Mappemonde/ui-polish-modes
NAVIGUIDE-for-Berry-Mappemonde Oct 26, 2026
195fd68
Merge pull request #54 from Berry-Mappemonde/ui-polish-2
NAVIGUIDE-for-Berry-Mappemonde Oct 26, 2026
d7abcf9
Header: explicit mode test ids; README note on the torch pin (#57)
HamidZ11 Oct 26, 2026
3e1f557
Projects rows: external link beside the row button, not inside it (#56)
HamidZ11 Oct 26, 2026
b10da8c
Legend sliver, wordmark collision, evidence domain shown once (#59)
HamidZ11 Oct 26, 2026
c811ab3
Update README.md
NAVIGUIDE-for-Berry-Mappemonde Oct 27, 2026
cc4bc56
refactor(evaluator): add type hint, document IDN limitation and go.xx…
Oct 23, 2026
7dd7206
Merge pull request #63 from Berry-Mappemonde/cursor/cherry-be6e83a-a5d7
NAVIGUIDE-for-Berry-Mappemonde Nov 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Copy to .env (gitignored) and fill in. Placeholders only: never commit real values.
# With no .env at all, backend/run.py runs in SAMPLE_MODE on bundled data.

# Zetaris (data layer)
ZETARIS_REST_URL=http://localhost:8888/api/v1.0
ZETARIS_ORG_ID=your-org-id
ZETARIS_API_KEY=your-zetaris-api-key

# Meterless Nightshift (OpenAI-compatible runtime)
METERLESS_BASE_URL=http://127.0.0.1:8088/v1
METERLESS_RUNTIME_TOKEN=your-nightshift-runtime-token
METERLESS_MODEL=meterless/auto

# Optional: direct NVIDIA Nemotron fallback when Nightshift is unreachable
NVIDIA_API_KEY=

# Optional: self-hosted SearXNG for the PoE search step (infra/searxng/).
# 8890 is also the default; 8888-8889 belong to the local Zetaris API.
SEARXNG_URL=http://127.0.0.1:8890

# true forces bundled sample data and no model calls; leave empty to auto-detect
SAMPLE_MODE=

# python backend/run.py --serve
PORT=8000
LOG_LEVEL=INFO
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,10 @@ credentials.json
backend/data/climatology/**/*.partial.npz
backend/data/climatology/**/*.npz.tmp
logs/*.jsonl
data/hmem/
data/.lock
# Independent port registries downloaded by backend/scripts/zetaris_setup.py --part registries
backend/data/registries/

# Fixtures volumineuses — ne jamais suivre par Git (limite GitHub 100 Mo)
fixtures/
177 changes: 171 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,14 @@
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE-MIT)
[![License: Apache 2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE-APACHE)
[![NVIDIA Nemotron](https://img.shields.io/badge/NVIDIA-Nemotron-76B900.svg)](https://www.nvidia.com/en-us/ai-data-science/foundation-models/nemotron/)
[![Nebius Token Factory](https://img.shields.io/badge/Nebius-Token%20Factory-0B1F3A.svg)](https://tokenfactory.nebius.com/)

A [Berry-Mappemonde](https://berrymappemonde.org) project. Code is **MIT or Apache-2.0**.

> [!WARNING]
> **Ne convient pas à la navigation / Not for navigation.**
> Les cartes et les chiffres viennent de sources publiques et de calculs.
> Ce n’est pas une carte marine officielle. Vérifiez toujours les publications
> gouvernementales avant une décision en mer.
> **Not for navigation.**
> Maps and figures come from public sources and computations.
> This is not an official nautical chart. Always check government
> publications before making any decision at sea.

**Blue Intelligence** ([blueintelligence.online](https://blueintelligence.online))
is the memory of maritime sources: Ports of Entry per EEZ, marinas,
Expand All @@ -24,6 +23,172 @@ The live voyage application **NAVIGUIDE** is a separate repository:
([naviguide.fr](https://naviguide.fr)). It reads Blue Intelligence over its
public API, `https://blueintelligence.online/api`.

## PoE Reconciler (Open Agent Hackathon 2026)

**What it does.** Give it one maritime zone (an EEZ, such as Bermuda or the Azores)
and it returns that zone's Ports of Entry, the places where a sailor may legally
clear customs and immigration on arrival. Each port comes with a verdict, a 0–100
confidence score with reasons, its sources, and an **evidence tier** that says what
backs it: an official government page that designates it, an independent port
registry (UN/LOCODE, NGA World Port Index), both, or neither. A short brief for
sailors closes each run. The answer is built from scattered public sources, and a
registry listing alone is never presented as port-of-entry status.

- **Tier = legal-status evidence:** what says this port is a port of entry (an official page, a registry, both or neither).
- **Verdict = data-quality review:** whether the record is reliable enough to use as is (`pass`) or needs a human look (`flag`), from its sources, reading and coordinates.

### Architecture

```text
RunPipeline ─► ZetarisAdapter candidates from Zetaris (blue_intel.poe_ports)
─► ZetarisAdapter registry check (UN/LOCODE + WPI views in Zetaris)
+ stored official evidence → evidence tier
─► HMemAgent recall earlier verdicts (Meterless H-MEM engine)
─► EvaluatorAgent score each port: pass, flag or retry
─► NemotronJudge advisory critique of the official text (Nemotron)
─► EvaluatorAgent ⇄ ResearchAgent retry with memory (max 2)
─► EvaluatorAgent finalise: pass or flag, never dropped
─► BriefingAgent brief via Meterless Nightshift → NVIDIA Nemotron
─► HMemAgent remember verdicts (created / reinforced / superseded)
```

- **Agents:** seven named agents share one `run_id` and one JSONL trace, with
handoffs, critique and retry events: `RunPipeline` (the orchestrator),
`ZetarisAdapter`, `HMemAgent`, `EvaluatorAgent`, `NemotronJudge`,
`ResearchAgent` and `BriefingAgent`. Each model call is also traced as
`MeterlessGenerator`, recording the requested and the served model.
- **NemotronJudge (advisory critic):** one Nemotron call per zone, through
Nightshift, on the official evidence text the run already holds. It records a
verdict (`poe`, `not_poe`, `unclear`), a reason and a quoted excerpt per port,
and whether that excerpt is really in the evidence text. It never overrides
evidence: no tier, validation, official evidence or score comes from it. When it
calls a flagged port a port of entry, it triggers one more research round. It is
skipped, and the trace says why, without credentials (`SAMPLE_MODE`) or without
an answer. Code: [`poe_judge.py`](backend/app/agents/poe_judge.py); tests:
[`test_poe_judge.py`](backend/tests/test_poe_judge.py); live recording:
[`01_bermuda.live_nemotron.json`](output_examples/01_bermuda.live_nemotron.json)
and [its trace](output_examples/01_bermuda.live_nemotron.trace.jsonl).
- **Zetaris (data layer):** ports, and the two independent registries, are read
through Zetaris SQL with a read-only guard, a timeout and a fallback to bundled
real fixtures.
- **Evidence tiers:**

| Tier | Meaning |
| --- | --- |
| `tier1` Official Commercial Port of Entry | official page designates it, and a registry lists it |
| `tier2` Official Yachting Port of Entry | official page designates it, and no registry lists it (typical for marinas) |
| `tier_registered` | registry match only; entry status unverified |
| `tier3` | neither: review needed |

An official page counts only if its URL is a government domain and its text
names the port near an entry term. A strong term ("port of entry", "clear
customs", "designated marina") is enough on its own. A weak term ("customs",
"immigration") also needs a port word nearby and the port's full name. Pages
fetched during generation are stored as verbatim snippets, never model output.
Runs then look the snippets up by zone.
- **Meterless:** the H-MEM engine (a Python port that passes the 13 official
conformance vectors) makes memory load-bearing. Nightshift is the primary model
provider and serves Nemotron.

### Quickstart: fresh clone, no credentials, no database

```bash
git clone https://github.com/Berry-Mappemonde/Blue-Intelligence.git
cd Blue-Intelligence
python3 -m venv backend/.venv # Python 3.12+ (macOS's /usr/bin/python3 is 3.9: too old)
backend/.venv/bin/pip install fastapi==0.110.1 httpx==0.28.1 pydantic==2.13.4 \
python-dotenv==1.2.3 uvicorn==0.25.0 filelock==3.32.3 pycountry==26.2.16
backend/.venv/bin/python backend/run.py --zone Bermuda
backend/.venv/bin/python backend/run.py --input input_examples/01_bermuda.json --output bermuda.json
```

With no `.env`, `run.py` turns on `SAMPLE_MODE` and logs it on stderr. In this
mode the agents run for real on bundled data, and only the Zetaris and model
calls are replaced. Expected output:

- **stderr:** a summary block (abridged):

```text
WARNING run: SAMPLE_MODE enabled automatically; missing: ZETARIS_API_KEY, ZETARIS_ORG_ID, METERLESS_RUNTIME_TOKEN or MODEL_API_KEY
Pipeline Result
Zone: Bermuda
Mode: sample
Ports: 1
Summary: {'pass': 1}
LLM: sample / caller-sample-fn
Memory: 0 recalled, remembered
```

- **stdout:** each trace event as one JSON line, then the result JSON, with keys
`run_id`, `zone`, `mode`, `ports`, `summary`, `brief`, `llm`, `components`,
`registry`, `judge`, `retries`, `memory`, `trace_file`, `timings_ms` and `error`.
`--output` also writes the result to a file.
- **Trace file:** `logs/trace-<run_id>.jsonl`. For the input example that's
`logs/trace-example-bermuda.jsonl`.
- **Memory:** run the same zone again and `memory.recalled` is no longer 0, because
H-MEM stored the first run in `data/hmem/`.

The 20 sample zones include Bermuda, Angola, Azores, Alaska, Bahamas and Antigua
and Barbuda. `input_examples/04_benin.json` shows a retry round: EvaluatorAgent
critiques the port, ResearchAgent looks for a remembered official source, twice. Across them the sample run gives 2 `tier1`, 2 `tier2`, 11
`tier_registered` and 5 `tier3`. Official evidence comes from small verbatim
snapshots of real government pages in
[`backend/data/sample/official_pages/`](backend/data/sample/official_pages/).

The same pipeline runs over HTTP:

```bash
backend/.venv/bin/python backend/run.py --serve # http://127.0.0.1:8000; PORT=… if taken
curl -s -X POST http://127.0.0.1:8000/run -H 'Content-Type: application/json' \
-d '{"zone_name": "Angola"}'
```

It also serves `GET /healthz` and a demo page at `/`.

### Live mode

```bash
cp .env.example .env # fill in the values below
backend/.venv/bin/python backend/run.py --input input_examples/02_angola.json
```

- **Zetaris:** `ZETARIS_REST_URL`, `ZETARIS_ORG_ID` and `ZETARIS_API_KEY`. The
PoE view and the registry views are registered once by
`backend/scripts/zetaris_setup.py`.
- **Meterless Nightshift:** `METERLESS_BASE_URL` (default
`http://127.0.0.1:8088/v1`), `METERLESS_RUNTIME_TOKEN` and `METERLESS_MODEL`
(default `meterless/auto`).
- **Optional:** `NVIDIA_API_KEY`, for a direct Nemotron fallback when Nightshift
is down.

Every external call has a timeout and a fallback, in this order: Nightshift, then
NVIDIA, then the sample brief. A Zetaris failure falls back to the bundled
fixtures. `llm.provider` and `llm.model` report the model that actually wrote the
brief; Nightshift may route to a different Nemotron than the one requested.

### Sponsor technology and evidence

[`docs/sponsors/SPONSOR_TECH.md`](docs/sponsors/SPONSOR_TECH.md) (merges with
PR #31) links every claim about Zetaris, Meterless H-MEM, Nightshift and NVIDIA
Nemotron to the code and tests that back it. Setup details, bounds and tests are
in [`docs/RUNNING.md`](docs/RUNNING.md).

### Limits, stated plainly

- **Sample data is small:** 20 real ports, one per zone, and small real
subsets of the registries. Official evidence exists only for zones whose
official pages have been fetched: 4 zones in the sample.
- **Tier and verdict can differ** (definitions above). Only a port with official
evidence can have its position corroborated by a registry (≤ 2 km), which `pass`
needs: St. George's passes (`tier1`, 67), Road Bay stays `flag` (`tier_registered`).
- **Grounding is a heuristic:** it checks the official domain, the entry term
and the distance. A port name with "Port of" stripped can match the town.
The committed snapshots were reviewed by hand.
- **Recorded output:** the recorded live run in `output_examples/` is for
reference. Nothing reads it, and judges cannot reproduce it without
credentials.
- **Not for navigation:** always check official publications before arriving.

## Dependencies

| Part | Runtime | Dependencies |
Expand All @@ -42,7 +207,7 @@ cd Blue-Intelligence
# API
cd backend
python3.12 -m venv .venv
.venv/bin/pip install -r requirements.txt
.venv/bin/pip install -r requirements.txt # pins a Linux-only torch +cpu wheel: on macOS use the Quickstart pins above, or install requirements.txt without the torch line
cat > .env <<'EOF'
MONGO_URL=mongodb://127.0.0.1:27017
DB_NAME=blueintel_db
Expand Down
1 change: 1 addition & 0 deletions backend/app/adapters/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Adapters for external data sources."""
152 changes: 152 additions & 0 deletions backend/app/adapters/evidence_store.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
"""Official-source evidence records: stored snippets of fetched official text.

A record is built deterministically from text fetched at generation (or from the
committed official-page snapshots in sample mode), never from model output:

{mrgid, port_key, url, domain, matched_name, snippet, content_sha256, fetched_at,
extract_level}

``snippet`` is a verbatim slice (at most 400 chars) around a mention of the port that
passes registry_check.entry_mention: an official URL, the port's name near an entry term,
not inside an address. At most MAX_PER_PORT records per port (best match per URL).

Records live in the Mongo collection ``poe_evidence`` (unique on mrgid + port_key + url),
not on the port document, because the run pipeline and /api/poe/verify read ports from
Zetaris. ``lookup`` reads Mongo, or the snapshots in backend/data/sample/official_pages/
in SAMPLE_MODE or when Mongo is unreachable; any other failure means no evidence, so a
lookup can never make a port official on its own.
"""
from __future__ import annotations

import hashlib
import json
import os

from app.adapters.zetaris import ZetarisClient
from app.agents import registry_check
from app.agents.evaluator import is_official_source
from app.core import paths

COLLECTION = "poe_evidence"
MAX_PER_PORT = 3
MONGO_TIMEOUT_MS = 2000
SAMPLE_PAGES_DIR = paths.SAMPLE_DATA_DIR / "official_pages"
RECORD_FIELDS = ("mrgid", "port_key", "url", "domain", "matched_name", "snippet",
"content_sha256", "fetched_at", "extract_level")


def sha256_text(text: str) -> str:
return hashlib.sha256((text or "").encode("utf-8")).hexdigest()


def build_records(mrgid: int | None, port_names: list[str], documents: list[dict]) -> list[dict]:
"""Evidence records for each port name from official documents.

documents: [{url, text, sha256, fetched_at, extract_level}], where sha256 is the hash of
the page's full fetched text (text may be a section of it). Only official URLs count;
each port keeps its closest match per URL, MAX_PER_PORT URLs at most.
"""
if mrgid is None:
return []
official = [d for d in documents if d.get("text") and is_official_source([d.get("url") or ""])]
records = []
for name in dict.fromkeys(n for n in port_names if n):
variants = registry_check.grounding_variants(name)
best: dict[str, tuple[int, dict]] = {}
for doc in official:
mention = registry_check.entry_mention(doc["text"], variants)
if not mention:
continue
record = dict(mrgid=int(mrgid), port_key=registry_check.port_key(name), url=doc["url"],
domain=registry_check._domain(doc["url"]), matched_name=mention["matched_name"],
snippet=mention["snippet"], content_sha256=doc.get("sha256") or sha256_text(doc["text"]),
fetched_at=doc.get("fetched_at"), extract_level=doc.get("extract_level"))
if doc["url"] not in best or mention["gap"] < best[doc["url"]][0]:
best[doc["url"]] = (mention["gap"], record)
records += [r for _, r in sorted(best.values(), key=lambda item: item[0])][:MAX_PER_PORT]
return records


def documents_from_context(text: str | None, used_sources: list[dict]) -> list[dict]:
"""Generation documents: the "[SOURCE: url]" blocks of the collected text with their
fetch metadata (sha256 of the full fetched text, collected_at, extraction level)."""
meta = {s.get("url"): s for s in used_sources or []}
return [dict(url=url, text=body, sha256=(meta.get(url) or {}).get("sha256"),
fetched_at=(meta.get(url) or {}).get("collected_at"),
extract_level=(meta.get(url) or {}).get("level"))
for url, body in registry_check.source_texts_from_context(text).items()]


async def store(db, records: list[dict]) -> int:
"""Upsert records (one per mrgid + port_key + url) and keep the newest MAX_PER_PORT per port."""
if not records:
return 0
coll = db[COLLECTION]
await coll.create_index([("mrgid", 1), ("port_key", 1), ("url", 1)], unique=True,
name="mrgid_port_key_url")
for record in records:
doc = {k: record.get(k) for k in RECORD_FIELDS}
await coll.update_one({k: doc[k] for k in ("mrgid", "port_key", "url")}, {"$set": doc}, upsert=True)
for mrgid, key in {(r["mrgid"], r["port_key"]) for r in records}:
stale = await coll.find({"mrgid": mrgid, "port_key": key}, {"_id": 1}) \
.sort("fetched_at", -1).skip(MAX_PER_PORT).to_list(100)
if stale:
await coll.delete_many({"_id": {"$in": [d["_id"] for d in stale]}})
return len(records)


def sample_documents(mrgid: int | None) -> list[dict]:
"""Documents from the committed official-page snapshots for a zone (one per section)."""
if mrgid is None or not SAMPLE_PAGES_DIR.is_dir():
return []
docs = []
for path in sorted(SAMPLE_PAGES_DIR.glob("*.json")):
page = json.loads(path.read_text(encoding="utf-8"))
if int(mrgid) not in {int(z["mrgid"]) for z in page.get("zones") or []}:
continue
docs += [dict(url=page["url"], text=section["text"], sha256=page["sha256"],
fetched_at=page["fetched_at"], extract_level=page.get("extract_level"))
for section in page.get("sections") or []]
return docs


def _mongo_records(mrgid: int) -> list[dict]:
from pymongo import MongoClient
client = MongoClient(os.environ.get("MONGO_URL") or "mongodb://127.0.0.1:27017",
serverSelectionTimeoutMS=MONGO_TIMEOUT_MS, connectTimeoutMS=MONGO_TIMEOUT_MS,
socketTimeoutMS=MONGO_TIMEOUT_MS)
try:
coll = client[os.environ.get("DB_NAME") or "bi_local"][COLLECTION]
return list(coll.find({"mrgid": int(mrgid)}, {"_id": 0}).limit(500))
finally:
client.close()


def lookup(mrgid: int | None, port_names: list[str], run_id: str) -> list[dict]:
"""Stored evidence records for a zone's ports; [] on any failure (never official).

SAMPLE_MODE=true reads the official-page snapshots and builds records with
build_records, as generation does. Otherwise Mongo (poe_evidence); if Mongo is
unreachable, the snapshots. Traced as a ZetarisAdapter ``evidence_lookup`` event.
"""
source, status, records, error = "none", "ok", [], None
try:
if mrgid is None:
source = "no-mrgid"
elif (os.environ.get("SAMPLE_MODE") or "").strip().lower() == "true":
source, records = "sample_pages", build_records(mrgid, port_names, sample_documents(mrgid))
else:
try:
source, records = "mongo", _mongo_records(mrgid)
except Exception as exc: # noqa: BLE001 - Mongo down: use the snapshots
error, status = f"{type(exc).__name__}: {str(exc)[:120]}", "fallback"
source, records = "sample_pages", build_records(mrgid, port_names, sample_documents(mrgid))
except Exception as exc: # noqa: BLE001 - no evidence is the safe answer
source, status, records, error = "error", "error", [], f"{type(exc).__name__}: {str(exc)[:120]}"
by_port = {}
for r in records:
by_port[r.get("port_key")] = by_port.get(r.get("port_key"), 0) + 1
ZetarisClient(run_id=run_id).trace_event(
"evidence_lookup", status=status, source=source, records=len(records), records_by_port=by_port,
input_summary={"mrgid": mrgid, "ports": len(port_names)}, **({"error": error} if error else {}))
return records
Loading
Loading