Imported from metade/radar-de-financiamento (
AGENTS.md). Install upstream withnpx skills add metade/radar-de-financiamento. Copyright stays with the author.
Agent Notes
Project
EU Funding Radar is a static Ruby/Jekyll/Tailwind site for weekly funding intelligence aimed at Portuguese local authorities.
Do not convert this into Rails, add a database, or add a backend server. Keep the project static and GitHub Pages compatible.
Core Commands
Run tests:
bundle exec rake test
Build CSS:
npm run build:css
Build the local Web Awesome components used by the report filters:
npm run build:js
Build the site:
bundle exec jekyll build
Complete local site/API build after generating a report:
bundle exec ruby bin/generate_report
npm run build:css
npm run build:js
bundle exec jekyll build
Generate the weekly report from live EU Funding & Tenders data:
bundle exec ruby bin/generate_report
Inspect the live EU source without writing a report:
bundle exec ruby bin/check_eu_source --year 2026 --limit 20
Inspect the live Portugal 2030 source without writing a report:
bundle exec ruby bin/check_portugal_2030_source --limit 20
Inspect the live European Urban Initiative source without writing a report:
bundle exec ruby bin/check_european_urban_initiative_source --raw --limit 20
Inspect the live Interreg Sudoe source without writing a report:
bundle exec ruby bin/check_interreg_sudoe_source
Funding Sources
The default sources are:
FundingRadar::Sources::EuFundingTendersSourceFundingRadar::Sources::Portugal2030SourceFundingRadar::Sources::InterregSudoeSourceFundingRadar::Sources::EuropeanUrbanInitiativeSourceFundingRadar::Sources::TurismoDePortugalSourceFundingRadar::Sources::ImtMobilityFundSourceFundingRadar::Sources::EdpSolidariaSource
The EDP Energia Solidária adapter monitors the programme page and edition-matched HTML rules. Exclude completed editions and expired phase-1 deadlines; phase 2 is invitation-only. Preserve public-body restrictions (municipalities and parish councils are partners only under the 2025 rules), and do not infer eligibility or budgets from old rules. Inspect with bundle exec ruby bin/check_edp_solidaria_source. Tests must use fake HTTP clients.
The Portugal 2030 adapter reads the official annual-plan XLSX workbook. It retains Lisboa/AML opportunities, multi-region opportunities that explicitly include AML, and nationwide or extra-regional opportunities; it excludes notices limited to other regions. It must not depend on live calls in tests.
The Lisboa 2030 programme is covered by the Portugal 2030 workbook and should not be added as a separate adapter unless it publishes regional detail missing from the central plan.
The Interreg Sudoe adapter discovers candidate call pages from the official calls, calendar, and next-calls pages, with current call URLs as fallback. It must skip results/closed/navigation pages, tolerate future call URL patterns, normalize HTML as UTF-8, and preserve valid records when one candidate page is malformed. It must not depend on live calls in tests.
Fixture data exists in data/sources/fixtures.yml, but normal report generation must not include it. Fixtures are opt-in only:
INCLUDE_FIXTURES=true bundle exec ruby bin/generate_report
Tests should not depend on live network calls. Use recorded/minimal payloads and fake HTTP clients.
Static AI API
The public API is static and vendor-neutral. It is generated by _plugins/static_api_generator.rb during the Jekyll build from the same report documents used by the website. Do not add a runtime API, database, MCP server, authentication, request-time search, or LLM call for API requests.
The generated public resources are /api/v1/meta.json, /api/v1/catalog.json, /api/v1/opportunities/{id}.json, and /api/v1/schema.json. The API output is written to _site/api/v1/; it is not a second manually maintained dataset and should not be committed as source data.
Keep opportunity IDs stable and URL-safe, preserve authoritative source URLs, and keep facts separate from Funding Radar analysis. Do not expose internal report/debug fields, cache keys, comparison-only fields, or private processing metadata. The catalogue includes opportunities with open or unknown deadlines; expired opportunities remain available as detail records when retained in the report archive.
/api/v1/ is a public interface. Prefer additive changes within v1; breaking representation changes require a new /api/v2/ rather than silently changing v1 semantics. Update the JSON Schema, API tests, README, and AI usage documentation when the public representation changes.
EU Funding & Tenders Notes
The official no-JavaScript topic index is mostly historical, so the source falls back to structured programme/year search terms such as HORIZON-NEB-2026, LIFE-2026, CERV-2026, and ERASMUS-SPORT-2026.
The EC search API can return noisy records such as FAQs and organisations. The source should normalize only Funding & Tenders topic records and skip closed calls where the payload exposes closed status.
Prefer the API's English results (language=en). If a topic has only explicitly non-English records, omit it rather than publishing a translated title or summary. When the API returns no language metadata, retain the record and prefer the richest available topic payload.
Many topic records do not expose deadlines reliably. Preserve nil deadlines and let the report show "Por confirmar".
Budget data may be nested in budgetOverview/budgetTopicActionMap, including total topic budgets and maximum contributions per project. Normalize these into readable euro amounts, and leave the amount blank when no numeric budget is available.
Verification Expectations
Before finishing source, report, or template changes, run:
bundle exec rake test
npm run build:css
npm run build:js
bundle exec jekyll build
Frontend Filter Notes
The report theme filter uses locally bundled Web Awesome components. Keep the Web Awesome assets self-hosted; do not replace them with a CDN runtime dependency.
Theme keys such as climate and community_development are canonical internal IDs used by scoring and source normalization. Portuguese labels and URL-safe query slugs are maintained in _data/theme_labels.yml. Shared filter URLs use repeated tema parameters, for example ?tema=clima&tema=inclusao; the JavaScript continues to read legacy English theme parameters for compatibility.
The date filters and theme filters are client-side only, preserve their state in the URL, and must continue to work on static GitHub Pages without a backend.
The source filter is also client-side only and uses repeated Portuguese fonte parameters, for example ?fonte=Portugal%202030; legacy source parameters remain readable for compatibility.
If report generation is changed, confirm the generated report contains only live-source records unless INCLUDE_FIXTURES=true is explicitly used.
Git
This repo uses main. Keep generated weekly reports in _reports/ committed so the archive is durable.
