Metadata-Version: 2.4
Name: sourcelock
Version: 0.1.0
Summary: Zero-PHI source-assurance CLI for healthcare's public data inputs
Author: SourceLock
License: MIT License
        
        Copyright (c) 2026 SourceLock
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
License-File: LICENSE
Requires-Python: >=3.11
Requires-Dist: cryptography>=42
Requires-Dist: httpx<1.0,>=0.27
Requires-Dist: mcp==2.0.0
Requires-Dist: pydantic<3.0,>=2.7
Requires-Dist: rich>=13.0
Requires-Dist: typer>=0.12
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: pyyaml>=6.0; extra == 'dev'
Requires-Dist: respx>=0.21; extra == 'dev'
Description-Content-Type: text/markdown

# SourceLock

**Every answer comes with a receipt, and your build fails when the source moves.**

SourceLock reads the public data healthcare software runs on — CMS coverage
policy, NPPES and PECOS, ICD-10-CM and HCPCS releases, CMS-HCC risk models, the
OIG LEIE — and returns, with every answer, the release it came from, the SHA-256
of the raw bytes behind it, when those bytes were retrieved, and what the answer
does not prove. `hc-source lock init` pins those sources in a `source-lock.json`;
`hc-source doctor` verifies that pin and fails CI the day one of them changes
underneath you.

## Install

```
git clone https://github.com/writtenonwater99/sourcelock
cd sourcelock
pipx install .
```

Python 3.11 or newer. There is no PyPI release yet, so `pip install sourcelock`
does not work and this file does not pretend it does — see
[RELEASING.md](RELEASING.md) for what publishing takes.

## See it work, offline, right now

```
hc-source valid-on E11.9 2026-07-01
```

No network, no credentials, no lockfile. The release tables are vendored, so
that command answers in under a second on a plane:

```json
{
  "code": "E11.9",
  "code_nodot": "E119",
  "code_system": "ICD-10-CM",
  "date": "2026-07-01",
  "valid": true,
  "description": "Type 2 diabetes mellitus without complications",
  "release": "fy2026-april",
  "release_effective_from": "2026-04-01",
  "release_effective_to": "2026-09-30"
}
```

followed by the receipt — source, route, release, effective window, retrieval
time, upstream status, the hash of the bytes the answer was derived from, the
transform version, and whether a mirror answered — and then the part most tools
leave out. Four non-claims, printed in full; the first line of each:

```
does not prove:
  - PHI_NOT_EXPECTED: this tool accepts only public typed parameters.
  - DOES_NOT_INCLUDE_CPT_DESCRIPTORS: no HCPCS Level I (CPT) record and no ADA
  - DOES_NOT_PROVE_COVERAGE: a code being valid for a date of service says
  - DOES_NOT_PROVE_CURRENCY: answers come from the release train vendored at
```

The real output does not wrap and does not trail off: the CPT one runs to a
paragraph naming all seventeen CMS descriptions that quote a CPT number, because
that is what an AMA licence question actually needs. These four are what
`codes.valid_on` claims it cannot tell you — every one of them, not a
representative sample, and nothing this product does not actually print.

## What it is for

If you are building AI or automation over revenue-cycle data, you have run into
some version of this:

- **A model cited a code set and nobody can say which release.** Every answer
  here carries `source_version`, `effective_from`/`effective_to`, and
  `raw_sha256`. That is an audit trail, not a log line.
- **A pipeline broke because CMS renamed something.** NPPES retired its V1 bulk
  filenames on 2026-03-03; jobs that still generated them got silent 404s.
  `hc-source lock init` pins 23 cheap checks across six sources and `hc-source
  doctor` fails the build the day one moves — with the fix, not a stack trace.
- **An agent will confidently answer from a stale cache.** Receipts distinguish
  when bytes were *retrieved* from when upstream last *confirmed* them, a canary
  that read out-of-date data is not allowed to report `ok`, and a source that
  could not be read is never reported as unchanged.
- **Your reference data is not all public.** Sources are plugins. Your licensed
  AMA CPT tables can be a first-class adapter without forking anything.
- **PHI must not end up in a tool call.** Parameters are public and typed, no
  receipt or log carries a payload, and a guard refuses input shaped like a
  patient record. It is a structural best-effort refusal, not a HIPAA control:
  `hc_source/guard.py` states exactly what it detects and, just as plainly,
  which evasion classes it does not close.

## The six sources

| source     | what it answers                                                             |
|------------|-----------------------------------------------------------------------------|
| `codes`    | ICD-10-CM / HCPCS Level II validity on a date of service, release trains    |
| `hcc`      | CMS-HCC V24/V28 mapping, hierarchies, community continuing-enrollee scoring |
| `provider` | NPI Registry lookups, NPPES bulk-file cadence, PECOS enrollment snapshot    |
| `coverage` | CMS coverage policy: NCDs, LCDs and articles, MCD weekly snapshot identity  |
| `leie`     | OIG LEIE exclusion checks by NPI, candidate search, monthly refresh status  |
| `demo`     | packaged reference adapter used in tests and examples                       |

`codes` and `hcc` answer offline from vendored release data. The rest read live
CMS and OIG endpoints.

## Everyday commands

```
hc-source valid-on E11.9 2026-07-01                     # offline
hc-source hcc score --dx E11.9,I50.9 --model v28 --year 2026 --age 72 --sex F
hc-source lookup-npi 1003000126                         # NPPES
hc-source check-npi 1003000126                          # OIG LEIE screen
hc-source tools                                         # every route, with its parameters
hc-source call coverage.lookup_ncd --param section=30.3 # the general form
hc-source mcp                                           # serve it all to an agent over MCP
```

`call` reaches every route, including ones provided by adapters you installed;
the named commands are shorthand for the four questions people arrive with.

## Pin your sources and fail the build when they move

```
hc-source lock init      # observe all six sources, write source-lock.json
hc-source doctor         # re-check upstream against the lockfile
hc-source init --ci      # scaffold the GitHub Actions workflow
```

Commit `source-lock.json` and review its diffs like any other lockfile. A green
run ends with:

```
23 ok, 0 drift, 0 unreachable, 0 schema_changed, 0 unpinned, 0 stale, 0 error
```

Exit codes are the contract: `0` everything matched, `1` drift or schema change,
`2` a source was unreachable or an adapter broke, `3` a canary was observed but
nothing pinned it, `4` everything matched but something reported that what it
read is out of date. `3` and `4` exist because a missing `source-lock.json` used
to report `23 ok, 0 drift` at exit 0 — a build that verified nothing, reporting
green. Full CI setup, per-canary severity, and the `on-unreachable` /
`on-stale` policies: [docs/ci.md](docs/ci.md).

## Watch it catch drift, in 90 seconds

You do not have to wait for CMS to change something. NPPES really did retire its
V1 bulk-file names on 2026-03-03. The repo carries a recording of what the
listing page would look like if that class of change happened again:

```
hc-source lock init
HC_SOURCE_PROVIDER_NPPES_FILES_URL="file://$PWD/tests/fixtures/provider/npi_files_v1_only.html" \
  hc-source doctor --source provider
```

Doctor exits `1`, marks `provider.nppes_v2_files` as `drift`, and prints:

```
provider.nppes_v2_files [drift]: NPPES retired the V1 bulk files on 2026-03-03; only
*_V2.zip names are published now (V2 extends the First Name and Legal Business Name
field lengths). The listing this canary just read carries a V1-style name with no _V2
suffix, which means the page has regressed, a stale mirror is being served, or
something upstream renamed the grammar again. Fix: open
https://download.cms.gov/nppes/NPI_Files.html, read the 'Important Information' block
(where CMS announced the V1 retirement), confirm the current version suffix, make sure
nothing in your pipeline still generates V1 filenames
(NPPES_Data_Dissemination_<Month>_<YYYY>.zip silently 404s), then re-pin with
`hc-source lock init`.
```

Run it again without the override and it goes back to green. That is the whole
product: pin what your build depends on, and get told — with an instruction, not
a stack trace — the moment upstream moves.

## For agents

`hc-source mcp` serves every discovered tool over MCP stdio: one MCP tool per
route, the same typed parameters, the same receipts, the same PHI refusals as
the CLI. It refuses to start if any adapter failed to import, because an agent
that sees a short `tools/list` reads it as the whole product.

Responses are cached on disk and revalidated conditionally — an entry is stored
only if the response carried an `ETag` or a `Last-Modified`, and a hit is a
request that came back `304`, so cached bytes are ones the source confirmed a
moment ago. A cache hit still reports when its bytes were *retrieved*, with a
`cache_hit` flag, rather than claiming a fresh read.

**How much it helps depends entirely on whether upstream sends validators, and
that varies a lot.** Measured here against live sources, median of five runs
each:

| command | cold | warm |
| --- | --- | --- |
| `hc-source call leie.check_npi --param npi=…` (15.5 MB OIG file) | 2.78s | **0.90s** |
| `hc-source doctor` (23 canaries) | ~6s | ~6s (no reliable difference) |

The LEIE file is served with a `Last-Modified`, so the warm run revalidates it
in one round trip instead of re-downloading 15.5 MB. `doctor` gets no measurable
benefit: its canaries are cheap by design, so the round trip already dominates
what a cache could save. Every path under `api.coverage.cms.gov/v1/` and the
NPPES NPI Registry API sends **neither** an `ETag` nor a `Last-Modified`, so
they are re-fetched every time by design — storing a body with no way to
re-confirm it is exactly the shortcut this product exists not to take.
`hc-source cache info` lists the endpoints in that state rather than leaving you
to wonder why the entry count is low.

`hc-source cache info|clear`; `HC_SOURCE_CACHE_DIR` moves or disables it.

## Adding your own source

Three homes, one contract: ship it in the package, publish it as a distribution
advertising the `sourcelock.adapters` entry point, or drop a file in
`./.sourcelock/adapters/` and switch that directory on with
`HC_SOURCE_LOCAL_ADAPTERS=1` (off by default: loading a file from it means
executing it, so it is never on where a pull request could add one). A
third-party adapter gets the same validation, the same PHI guard, and the same
receipts — and cannot claim a built-in source id.

[ADAPTER_GUIDE.md](ADAPTER_GUIDE.md) is the contract;
[examples/sourcelock-example-adapter/](examples/sourcelock-example-adapter/) is
a working skeleton with four marked places to change.

## More

- [ADAPTER_GUIDE.md](ADAPTER_GUIDE.md) — writing a source adapter
- [docs/ci.md](docs/ci.md) — the GitHub Action, severity, and CI policy
- [RELEASING.md](RELEASING.md) — publishing to PyPI
- `hc_source/guard.py` — what the PHI guard detects, and what it does not

## License

MIT. See [LICENSE](LICENSE). Source data carries its own terms; each adapter
records them in its `SourceContract.license_notes`.
