# Phase 2A.1 Foundation Handoff

## Base and review

- Base commit: `ff49e54c9d07b5028e009ab6f8669dadb893b249`
- Base verification: `origin/main` contains the required commit and had no
  additional commits after it when fetched.
- Branch: `phase-2a/01-foundation-guardrails`
- Implementation commits:
  - `a3063871b299b79525f330ece8f14c49f95d6445` — foundation
  - `58693c6273d6ba8fa949a324c81e69f01cf294d5` — initial handoff evidence
  - `be21d3434e145a81866df845b58a8dc2da9cab7d` — phase-gate ordering
  - `217a332bea5b7f808592d6814ddf765bae047f86` — verification correction
- Correction verification head: `217a332bea5b7f808592d6814ddf765bae047f86`
- Pull request: [#2](https://github.com/lutzkind/osm-lead-source-service/pull/2)
  (accepted for merge; merge is recorded in the external post-merge handoff)

## Scope

This handoff covers Phase 2A.1 only: the Python application foundation,
immutable configuration guardrails, typed domain contracts, read-only ports,
offline CLI, unit tests, CI, and project documentation.

No Phase 2A.2 work was started.

## Architecture choices

- Python 3.12 minimum with standard `pyproject.toml` packaging and `src/`
  layout.
- Standard-library frozen dataclasses and enums keep the contract surface
  immutable without adding a runtime validation dependency.
- Configuration reads only known non-secret environment variables and rejects
  every prohibited capability when explicitly enabled.
- `NocoReadRepository`, `SourceCatalog`, and `ReportStore` are protocols;
  there is no Noco adapter, production report store, or mutation client.
- Planned reconciliation actions are deterministic report records. Their
  execution property is always false and execution raises
  `UnsupportedOperation`.
- The CLI uses `argparse` and emits stable JSON with sorted keys and compact
  separators for the offline doctor.
- CI uses Python 3.12 and `contents: read` permissions. Actions are pinned to
  `actions/checkout` `9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0` (v7.0.0) and
  `actions/setup-python` `ece7cb06caefa5fff74198d8649806c4678c61a1` (v6.3.0).
- Structured logging requires a validated event identifier and keeps a human
  message separate; repeated explicit configuration replaces only the package's
  own handler.

## Files added or changed

- `.github/workflows/ci.yml`, `.gitignore`, `pyproject.toml`
- `AGENTS.md`, `README.md`
- `docs/PHASE_2A_PLAN.md`, `docs/PROJECT_STATE.md`, `docs/ROADMAP.md`
- `docs/handoffs/2026-07-10-phase-2a-01-foundation.md`
- `src/osm_lead_source_service/` package, domain contracts, and ports
- `tests/conftest.py`, logging tests, and the read-only boundary tests

## Commands and exact results

The host has Python 3.12.3 as `python3` but no global `python` executable. The
documented commands below were run after creating and activating `.venv`, so
`python` resolved to the virtual environment interpreter.

```text
python -m pip install -e ".[dev]"                         exit 0
python -m ruff check .                                    exit 0: All checks passed!
python -m ruff format --check .                           exit 0: 20 files already formatted
python -m mypy src                                         exit 0: Success: no issues found in 14 source files
python -m pytest                                           exit 0: 53 passed in 0.08s
python -m build                                            exit 0: Successfully built osm_lead_source_service-0.1.0.tar.gz and osm_lead_source_service-0.1.0-py3-none-any.whl
git diff --check                                          exit 0: no output
git status --short                                        exit 0: no output
git log -1 --stat                                          exit 0: correction commit 217a332bea5b7f808592d6814ddf765bae047f86
```

CLI results:

```text
osm-lead-source version                                  exit 0
0.1.0

osm-lead-source validate-config                          exit 0
configuration valid

osm-lead-source doctor --offline                         exit 0
doctor: ok

osm-lead-source doctor --offline --json                  exit 0
{"checks":{"basic_configuration":{"ok":true},"no_production_writer_configured":{"modules":[],"ok":true},"package_importability":{"ok":true,"package":"osm_lead_source_service"},"python_version_supported":{"minimum":"3.12","ok":true,"running":"3.12.3"},"read_only_enforced":{"ok":true}},"command":"doctor","config_error":null,"offline":true,"ok":true,"phase":"2A.1"}

python -m osm_lead_source_service version                 exit 0
0.1.0

OSM_LEAD_SOURCE_NOCO_WRITE_ENABLED=true osm-lead-source validate-config
                                                           exit 2
invalid configuration: noco_write_enabled cannot be enabled in Phase 2A.1
```

The structural boundary tests also passed with socket creation blocked; no
application, CLI, or test path opened a network or database connection.

## CI result

The correction PR CI run passed:

- Workflow: `CI`
- Job: `validate`
- Run: [29106830304](https://github.com/lutzkind/osm-lead-source-service/actions/runs/29106830304)
- Job URL: [validate job](https://github.com/lutzkind/osm-lead-source-service/actions/runs/29106830304/job/86409280388)
- Result: `SUCCESS`
- The raw job log contains the new checkout/setup-python revisions and no
  Node.js 20 deprecation warning. The only matched warning text was Git's
  generic default-branch hint, unrelated to action runtime versions.

The final branch head after this handoff refresh is recorded in the shared
handoff manifest because a committed file cannot contain its own future commit
SHA. This handoff refresh is documentation-only; the correction verification
head above is the code/test head used for the results.

## Known limitations

The source catalog and report store are contracts only. There are no profile
rules, normalization, PBF fixtures/parser, sidecar schema/migrations, Noco
read adapter, adoption scanner, integrated shadow reconciliation, deployment,
scheduler, worker, FastAPI app, or MCP implementation.

## Production impact and safety

Production impact is none. The legacy scraper was not modified. No production
data, production credentials, PostgreSQL, PostGIS, NocoDB, PBF extract, or
sidecar database was accessed. The required GitHub fetch/push/PR and development
package installation used external network services only for source/review and
tooling; application and test execution remained offline. No NocoDB write or
direct Noco PostgreSQL write is possible from this package.

## Next proposed subphase

Phase 2A.2 — PostgreSQL/PostGIS sidecar schema and migrations.

Phase 2A.2 was not started.

## Final acceptance

- Acceptance date: `2026-07-10`
- Accepted PR: `#2`
- Accepted reviewed branch head: `5ac695d8e0c066c87e67dffea0b2e8e33540f63a`
- Review result: passed
- CI result: successful
- Tests: 53 passed
- Ruff, formatting, mypy, build, CLI, and safety checks: passed
- Deterministic structured logging correction: accepted
- Global socket blocking for `socket.socket` and `socket.create_connection`: accepted
- Final reviewed CI had no Node.js 20 action-runtime warning
- Production impact: none
- NocoDB access: none
- Database or migration: none
- PBF handling: none
- Deployment, scheduler, worker, FastAPI, and MCP: none
- Legacy scraper modification: none
- Phase 2A.2: not started

The final squash-merge SHA is recorded in the external post-merge handoff
because a committed file cannot contain its own future merge SHA.
