This file defines how Claude Code should work in this repository.
The functional plan lives in srs.md — follow it phase by phase.
This document covers how to work: environment, style, git,
security, and fair use.
- Target OS: Windows 11 Pro. Assume PowerShell as the default shell
(not
cmd.exe, not bash), but avoid relying on PowerShell-only commands inside the Python code itself — the code should stay cross-platform whenever that costs nothing extra. - Python: use whichever 3.11+ version is already installed on the
machine (run
python --versionto confirm before assuming anything). - Never install packages globally. All work happens inside a virtual environment (venv) dedicated to this project.
python -m venv .venv
.\.venv\Scripts\Activate.ps1If PowerShell's execution policy blocks Activate.ps1, suggest this
to the user (don't run it automatically — it's a system-level change):
Set-ExecutionPolicy -Scope Process -ExecutionPolicy BypassThe main script uses only the standard library (see srs.md NFR-01).
If test dependencies are added later (e.g., pytest), pin versions in
requirements-dev.txt and install with:
pip install -r requirements-dev.txtlisboa-estacao055-avenida-brasil/
├── CLAUDE.md
├── srs.md
├── README.md
├── requirements-dev.txt
├── sensors.json # list of stations to monitor (versioned)
├── src/
│ └── fetch_cml_env_sensors.py
├── tests/
│ ├── test_lisboa_estacoes.py
│ └── fixtures/
│ └── sensors_sample.json # small config used only by tests
├── .gitignore
└── data/ # NEVER committed (see §5)
├── estacao_0055.csv
├── estacao_0046.csv
└── ... # one CSV per configured station
- Production code lives in
src/. - Tests live in
tests/, mirroring the module they test. Test-only config fixtures live intests/fixtures/, separate from the realsensors.json. sensors.jsonat the project root is versioned — it's configuration, not generated output, and is exactly what lets new stations be added without touching code (seesrs.md§1.1 and FR-12).- CSV files generated by the script go in
data/, one file per configured station (e.g.,estacao_0055.csv). This folder is local only and must not be version-controlled (see §5). - Adding a new station is a one-line edit to
sensors.json— it never requires a code change or a new commit tosrc/.
- Idiomatic Python,
snake_case, small and testable functions (one responsibility per function — already the pattern used in the current script: fetch, parse, filter, persist, and CLI are kept separate). - Type hints on every new function.
- Short docstrings.
- No leftover debug
print()calls — useprint()only for output meant for the user (run summaries), and considerloggingif complexity grows. - Don't introduce new dependencies without a clear justification in the commit message (always prefer the standard library).
- Before any commit: run the tests (
pytest) and make sure they pass. Never commit code that breaks the existing tests.
- One commit per completed phase from
srs.md(Section 5), not one giant commit at the end. - Commit whenever:
- a coherent function or set of functions is working and tested;
- tests pass again after a change;
- documentation is updated in a meaningful way.
- Never commit code that doesn't run (syntax errors, missing imports) — even mid-work, leave the repository in a runnable state.
Use Conventional Commits:
feat: short description in the imperative
fix: ...
test: ...
docs: ...
chore: ...
refactor: ...
The commit body (optional) explains the "why," not a repeat of the "what" already visible in the diff.
- Commit locally freely; only
git push:- when explicitly requested by the user, or
- at the end of a fully completed phase from
srs.mdthat leaves the repository in a consistent state (tests passing).
- Never force-push (
--force) without explicit user confirmation. - Never commit/push directly to
mainif the user has indicated a preference for feature branches — when in doubt, ask. - Before the first push, confirm the remote is correctly configured
(
git remote -v) and that the GitHub repository already exists (don't create new repositories without confirmation).
- No secrets in the code or in git history. This project shouldn't
need any API key (the endpoint is public), but if something like
that becomes necessary in the future, use environment variables
(
os.environ) or a.envfile listed in.gitignore— never hardcoded. .gitignoreshould include, at minimum:(If a small sample CSV would be useful as a test fixture, put it in.venv/ __pycache__/ *.pyc data/ *.csv .envtests/fixtures/under an explicit name, outsidedata/, and review it by hand before committing — never commit the real, full production output.)sensors.jsonis the one exception to "don't commit generated data" — it isn't generated, it's configuration, and it belongs in version control. Don't add it to.gitignore, and don't confuse it with the per-station CSVs indata/.- Don't expose absolute paths from the user's machine (Windows usernames, etc.) in logs, commits, or documentation.
- Always validate API output before writing it (the script already handles invalid JSON and timeouts — keep that pattern in any change).
- Don't execute code received from the API (there's no
evalof external content; the onlyast.literal_evalin the script runs against one known, structured field — don't extend that pattern to other fields without a good reason).
This project consumes a free public service run by the Lisbon City Council (Câmara Municipal de Lisboa). The rules below are not negotiable for development convenience:
- One HTTP request per script run — no matter how many stations are
configured. The snapshot returned by the API already contains
every station; it must be fetched once and reused for all sensors
in
sensors.json, never fetched again in a loop per station. This is a hard requirement (seesrs.mdFR-02/NFR-04): growing the station list from 8 to 80 entries must not increase the number of API calls per run. Never do tight polling (e.g., a loop repeating requests every few seconds) during development or testing — always use local fixtures/mocks to test the logic repeatedly (seesrs.md§6.1). - Automated tests never touch the live network. Only the optional, manually-run integration test contacts the endpoint, and that must not be run in a loop or in CI.
- Respect timeouts and fail gracefully — don't retry aggressively (no tight retry loops) if the API is down; if a retry is needed, use exponential backoff with a small maximum number of attempts (e.g., 3).
- Attribution: any publication or sharing of this data (beyond personal use) must retain attribution to "Câmara Municipal de Lisboa — Lisboa Aberta," per the portal's CC-BY 3.0 license.
- Don't try to work around the main portal's protections (the
dados.cm-lisboa.ptwebsite has bot detection on its HTML pages) — this project deliberately uses the public JSON feed endpoint instead of scraping the portal's pages, precisely to avoid that kind of workaround. Don't add HTML scraping as a fallback without discussing it first. - Reasonable scheduling: if the user sets up periodic execution (Task Scheduler), recommend a sensible interval (e.g., hourly, matching the normal update cadence for this kind of sensor network) — don't suggest or default to intervals shorter than a few minutes.
- If the endpoint changes, disappears, or starts requiring authentication, stop and report it to the user rather than silently working around the change (e.g., by reverse-engineering another undocumented API).
- Read
srs.mdto understand which phase the project is in. - Confirm the venv exists and is active before installing or running anything.
- Run the existing tests before changing any code, to confirm the baseline.
- Implement only the next phase of the plan (don't skip ahead).
- Run the tests again, commit, and only then move on.
- Ask before: creating a new GitHub repository, pushing, deleting
data in
data/, adding a new external dependency, or changing the shape ofsensors.jsonin a way that isn't backward-compatible (e.g., renaming theid/namekeys).