matrx-mandate-scan 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. matrx_mandate_scan-0.1.0/.gitignore +303 -0
  2. matrx_mandate_scan-0.1.0/CLAUDE.md +91 -0
  3. matrx_mandate_scan-0.1.0/PKG-INFO +129 -0
  4. matrx_mandate_scan-0.1.0/README.md +106 -0
  5. matrx_mandate_scan-0.1.0/matrx_mandate_scan/__init__.py +20 -0
  6. matrx_mandate_scan-0.1.0/matrx_mandate_scan/__main__.py +4 -0
  7. matrx_mandate_scan-0.1.0/matrx_mandate_scan/adapters/__init__.py +5 -0
  8. matrx_mandate_scan-0.1.0/matrx_mandate_scan/adapters/config.py +137 -0
  9. matrx_mandate_scan-0.1.0/matrx_mandate_scan/adapters/python.py +2045 -0
  10. matrx_mandate_scan-0.1.0/matrx_mandate_scan/baseline.py +97 -0
  11. matrx_mandate_scan-0.1.0/matrx_mandate_scan/cli.py +270 -0
  12. matrx_mandate_scan-0.1.0/matrx_mandate_scan/contract.py +378 -0
  13. matrx_mandate_scan-0.1.0/matrx_mandate_scan/fixtures/__init__.py +7 -0
  14. matrx_mandate_scan-0.1.0/matrx_mandate_scan/fixtures/sources.py +375 -0
  15. matrx_mandate_scan-0.1.0/matrx_mandate_scan/module_index.py +376 -0
  16. matrx_mandate_scan-0.1.0/matrx_mandate_scan/provider_vocabulary.py +251 -0
  17. matrx_mandate_scan-0.1.0/matrx_mandate_scan/repo.py +171 -0
  18. matrx_mandate_scan-0.1.0/matrx_mandate_scan/report.py +154 -0
  19. matrx_mandate_scan-0.1.0/matrx_mandate_scan/scanner.py +900 -0
  20. matrx_mandate_scan-0.1.0/matrx_mandate_scan/selftest.py +549 -0
  21. matrx_mandate_scan-0.1.0/matrx_mandate_scan/transport.py +287 -0
  22. matrx_mandate_scan-0.1.0/matrx_mandate_scan/walkers.py +243 -0
  23. matrx_mandate_scan-0.1.0/pyproject.toml +62 -0
  24. matrx_mandate_scan-0.1.0/tests/test_cli.py +110 -0
  25. matrx_mandate_scan-0.1.0/tests/test_index_cache.py +146 -0
  26. matrx_mandate_scan-0.1.0/tests/test_python_adapter.py +193 -0
  27. matrx_mandate_scan-0.1.0/tests/test_report_payload.py +157 -0
  28. matrx_mandate_scan-0.1.0/tests/test_revision_truth.py +134 -0
  29. matrx_mandate_scan-0.1.0/tests/test_walkers.py +91 -0
@@ -0,0 +1,303 @@
1
+ *.pyc
2
+ secrets/
3
+ ignore/
4
+ temp/
5
+ logs/
6
+ # The broad `logs/` rule above is for RUNTIME log output, but it also matched
7
+ # the dashboard's SOURCE directory and silently swallowed an entire feature's
8
+ # files (only the pre-existing index.tsx stayed tracked), breaking the prod
9
+ # Docker build with "Could not resolve ./structured-tab". Re-include the source.
10
+ !apps/dashboard/src/features/logs/
11
+ !apps/dashboard/src/features/logs/**
12
+ todo
13
+ text_notes/
14
+ aidream/secrets/2.env
15
+ automation_matrix/matrix_processing/temp/*
16
+ cd
17
+ # Byte-compiled / optimized / DLL files
18
+ __pycache__/
19
+ *.py[cod]
20
+ *$py.class
21
+
22
+ # C extensions
23
+ *.so
24
+ .venv/
25
+
26
+ # Distribution / packaging
27
+ .Python
28
+ build/
29
+ develop-eggs/
30
+ dist/
31
+ downloads/
32
+ eggs/
33
+ .eggs/
34
+ lib/
35
+ lib64/
36
+ # The blanket lib/ rule above is from the standard Python .gitignore template
37
+ # and was silently swallowing TS source under the SPA `src/lib/` folders.
38
+ # Re-allow them explicitly so frontend builds don't ship without their lib layer.
39
+ !apps/dashboard/src/lib/
40
+ !apps/dashboard/src/lib/**
41
+ !apps/dashboard/src/features/crawler/lib/
42
+ !apps/dashboard/src/features/crawler/lib/**
43
+ !apps/workflow-studio/src/lib/
44
+ !apps/workflow-studio/src/lib/**
45
+ parts/
46
+ sdist/
47
+ var/
48
+ wheels/
49
+ share/python-wheels/
50
+ *.egg-info/
51
+ .installed.cfg
52
+ *.egg
53
+ MANIFEST
54
+
55
+ # PyInstaller
56
+ # Usually these files are written by a python script from a template
57
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
58
+ *.manifest
59
+ *.spec
60
+
61
+ # Installer logs
62
+ pip-log.txt
63
+ pip-delete-this-directory.txt
64
+
65
+ # Unit test / coverage reports
66
+ ai/tests/clean_response.json
67
+ ai/tests/cx_storage_response.json
68
+ ai/tests/execution_test.py
69
+ ai/tests/final_response.json
70
+ htmlcov/
71
+ .tox/
72
+ .nox/
73
+ .coverage
74
+ .coverage.*
75
+ .cache
76
+ nosetests.xml
77
+ coverage.xml
78
+ *.cover
79
+ *.py,cover
80
+ .hypothesis/
81
+ .pytest_cache/
82
+ cover/
83
+
84
+ # Translations
85
+ *.mo
86
+ *.pot
87
+
88
+ # Django stuff:
89
+ *.log
90
+ local_settings.py
91
+ db.sqlite3
92
+ db.sqlite3-journal
93
+
94
+ # Flask stuff:
95
+ instance/
96
+ .webassets-cache
97
+
98
+ # Scrapy stuff:
99
+ .scrapy
100
+
101
+ # Sphinx documentation
102
+ docs/_build/
103
+
104
+ # PyBuilder
105
+ .pybuilder/
106
+ target/
107
+
108
+ # Jupyter Notebook
109
+ .ipynb_checkpoints
110
+
111
+ # IPython
112
+ profile_default/
113
+ ipython_config.py
114
+
115
+ # pyenv
116
+ # For a library or package, you might want to ignore these files since the code is
117
+ # intended to run in multiple environments; otherwise, check them in:
118
+ # .python-version
119
+
120
+ # pipenv
121
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
122
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
123
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
124
+ # install all needed dependencies.
125
+ #Pipfile.lock
126
+
127
+ # poetry
128
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
129
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
130
+ # commonly ignored for libraries.
131
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
132
+
133
+ # pdm
134
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
135
+ #pdm.lock
136
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
137
+ # in version control.
138
+ # https://pdm.fming.dev/#use-with-ide
139
+ .pdm.toml
140
+
141
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
142
+ __pypackages__/
143
+
144
+ # Celery stuff
145
+ celerybeat-schedule
146
+ celerybeat.pid
147
+
148
+ # SageMath parsed files
149
+ *.sage.py
150
+
151
+ # Environments
152
+ .env
153
+ .env_remote
154
+ .venv
155
+ env/
156
+ venv/
157
+ ENV/
158
+ env.bak/
159
+ venv.bak/
160
+ .env.armanonly
161
+
162
+ # Spyder project settings
163
+ .spyderproject
164
+ .spyproject
165
+
166
+ # Rope project settings
167
+ .ropeproject
168
+
169
+ # mkdocs documentation
170
+ /site
171
+
172
+ # mypy
173
+ .mypy_cache/
174
+ .dmypy.json
175
+ dmypy.json
176
+
177
+ # Pyre type checker
178
+ .pyre/
179
+
180
+ # random armani files
181
+ /armani_dev/secrets/
182
+ /armani/
183
+ /_armani/
184
+
185
+
186
+
187
+ # pytype static type analyzer
188
+ .pytype/
189
+
190
+ # Cython debug symbols
191
+ cython_debug/
192
+
193
+ .idea/
194
+ .vscode/
195
+ /node_modules/
196
+
197
+ # Frontend pnpm workspace (apps/) — node_modules at the workspace root and any
198
+ # member, plus Vite caches and build output. The unified lockfile (apps/pnpm-lock.yaml)
199
+ # IS committed; everything below is regenerated.
200
+ node_modules/
201
+ apps/**/.vite/
202
+ apps/**/dist/
203
+ .vite/
204
+
205
+ dump.rdb
206
+
207
+ frontend/
208
+
209
+ # AME Temp Files and directory structure
210
+ # Ignore all files in the temp directory and its subdirectories
211
+ /temp/**/*
212
+ /tmp/**/*
213
+
214
+ # Allow .gitkeep files to retain directory structure
215
+ !/temp/**/.gitkeep
216
+ !/tmp/**/.gitkeep
217
+
218
+ # Armani
219
+ .history*
220
+ .history/
221
+ /local_data/
222
+ local_reports_data/
223
+ webscraper/quick_scrapes/temp/
224
+ automation_matrix/ai_apis/fireworks/_dev/*
225
+ automation_matrix/ai_apis/fireworks/_dev/fireworks_sample.py
226
+ *.pdf
227
+ *.flac
228
+ *.mp3
229
+ *.wav
230
+ miniconda.sh
231
+ /database/python_sql/temp_data/
232
+ .history*
233
+ .history/
234
+ .history/
235
+
236
+ _dev/
237
+ /_dev/
238
+ requirements_filtered.txt
239
+
240
+ # matrx-dev-tools backups
241
+ .env-backups/
242
+ # Matrx Ship config (contains API key)
243
+ .matrx-ship.json
244
+
245
+ # Matrx config (contains API keys)
246
+ .matrx.json
247
+ .matrx-tools.conf
248
+
249
+ # Claude Code local worktrees and per-user settings
250
+ .claude/worktrees/
251
+ .worktrees/
252
+ .claude/settings.local.json
253
+
254
+ # Append-only snapshots from matrx_utils.update_history (unbounded; do not commit)
255
+ common/utils/data_in_code/data_history.json
256
+ packages/matrx-utils/matrx_utils/data_in_code/data_history.json
257
+
258
+ # Tool-dispatch debug logs — one file per server start, never committed
259
+ .matrx-debug/
260
+ # Per-definition BEFORE snapshots written by a live migration script. They are
261
+ # the recovery record for one machine's run, not repo content — the durable
262
+ # audit trail is the `metadata.migrations` stamp on the row itself.
263
+ .matrx-migrations/
264
+
265
+ # macOS Finder metadata
266
+ .DS_Store
267
+ **/.DS_Store
268
+
269
+ # Environment files
270
+ .env
271
+ .env.*
272
+ *.env
273
+ *.env.*
274
+
275
+ # Keep safe templates trackable
276
+ !.env.example
277
+ !.env.sample
278
+ !.env.template
279
+
280
+ # Never commit local OAuth client/token artifacts
281
+ credentials.json
282
+ token*.pickle
283
+ token*.json
284
+ tests_trials/rag_tests/kg_export_output/
285
+
286
+ # Pooler-contamination watch output (machine-local evidence, not source)
287
+ db/evidence/
288
+
289
+ # Pooler contamination evidence — machine-local forensic capture, not source
290
+ db/evidence/
291
+ db/mirror/.env.mirror
292
+ supabase/.temp/
293
+ **/supabase/.temp/
294
+ # Packed npm artifacts. Kept locally for a one-time bootstrap publish; never
295
+ # committed (they are large binaries regenerable with `pnpm pack`).
296
+ apps/shared/*/*.tgz
297
+
298
+ # Guard forcing-function scratch packages. These tests plant real files inside
299
+ # the real tree the guard scans (a temp dir would prove nothing) and delete
300
+ # them in teardown — ignoring them keeps the auto-commit tooling from
301
+ # capturing one mid-run.
302
+ _kind_boundary_scratch*/
303
+ _kind_marker_law_scratch*/
@@ -0,0 +1,91 @@
1
+ # matrx-mandate-scan — agent notes
2
+
3
+ **Archetype A (library).** Consumers: aidream (`scripts/audit_mandate_wiring.py`,
4
+ `aidream/services/mandates/code_truth.py`, `release.sh`, CI), and — via `uvx` — every
5
+ other Matrx repo's release path. It runs where aidream does not exist, which is the
6
+ whole point.
7
+
8
+ ## Hard rules for this package
9
+
10
+ - **Never import `aidream` or `matrx-orm`.** `scripts/check_package_boundaries.py`
11
+ fails the build on it. Submission uses raw asyncpg on purpose (`transport.py`), and
12
+ the SYSTEM-org constant there is a documented mirror of matrx-orm's, not an import.
13
+ - **`contract.py` is FROZEN.** Adding a `reference_type`, a finding code, or a field
14
+ is an amendment recorded in
15
+ `common-docs/projects/mandate-declaration-reporting/REGISTER.md` — never a silent edit.
16
+ A **breaking** amendment bumps `CONTRACT_VERSION` and lane L1's
17
+ `mandate.submit_scan_report(jsonb)` moves in the same change. A purely **additive**
18
+ one (amendment 1, 2026-09-09: `coverage.dirty` / `coverage.head_moved`; amendment 2,
19
+ 2026-09-10: the repo-level `coverage.top_level` inventory) does not —
20
+ the door accepts exactly version 1 and stores `coverage` as jsonb wholesale, so a
21
+ bump would make it refuse every report over keys it already accepts. Either way the
22
+ amendment is written down before the code lands.
23
+ - **Never make a command exit non-zero without `--strict`** (D23).
24
+ - **Never widen classification to shape.** A dotted string is a reference because a
25
+ carrier holds it, never because it looks like a key. The moment that slips,
26
+ `consumerId="extend.chat"` becomes a mandate and the inventory is worthless. The
27
+ self-test guards exactly this.
28
+ - **THERE IS EXACTLY ONE KEY RESOLVER, AND IT LIVES HERE** (coordinator ruling,
29
+ 2026-09-10): `adapters/python.py::_Resolver`. aidream's
30
+ `scripts/check_mandate_carrier_args.py` is a THIN CALLER of `carrier_sites()` — it
31
+ owns no resolution logic and must never grow any. Two resolvers means the number you
32
+ read depends on which tool printed it, and the two DID disagree: the census certified
33
+ as clean 16 sites the scanner was reporting as `UNRESOLVED_KEY`. A new resolution
34
+ shape goes in `_Resolver`, and both tools get it.
35
+ - **A bypass is a request TARGET, never a mention.** The provider names and hosts live
36
+ in `provider_vocabulary.py`, which is listed in its OWN allowlist beside
37
+ `matrx_ai/providers/**`. A host string counts when it is URL-shaped or sits in a
38
+ request-target position; a host name in a tuple is data, and a URL quoted in a
39
+ docstring is prose. "Any string that names a provider" made this package's own
40
+ vocabulary 13 live `NEW_BYPASS` rows.
41
+ - **Every importable top-level package is scanned or listed as skipped with a reason.**
42
+ `top_level_inventory()` names every top-level directory once. aidream had twelve
43
+ importable packages that were neither, and the report still said `complete`.
44
+ - **The ratchet only goes down.** `baseline.write` refuses to grow; keep it that way.
45
+ - **`repo.py` mirrors `platform.repo`.** An unknown remote is `UNMEASURED`. Never
46
+ infer a slug from a folder name — "ai-matrx" the repo is the `matrx-frontend`
47
+ directory, and guessing gets it backwards.
48
+ - **Nothing about a checkout comes from its DIRECTORY NAME.** The repo root is
49
+ `git rev-parse --show-toplevel`; the slug is the git remote; the root package is what
50
+ the manifests declare, else the registered slug. A worktree, a CI checkout, a fork PR
51
+ and a container build stage all rename the directory, and V-L2 § 6 measured the cost:
52
+ the entire 4,000-file root package silently absent while the report still said
53
+ `complete`.
54
+ - **The revision is read ONCE, at scan start, and the report says whether the tree was
55
+ clean.** A candidate report describes exactly ONE committed tree; `coverage.dirty` /
56
+ `coverage.head_moved` and an `UNMEASURED` finding say so when it does not (V-L2 § 7:
57
+ live rows attributed to a commit that predates the package they scanned). `--revision`
58
+ is a CI-only override and an override that disagrees with HEAD is reported, never
59
+ trusted.
60
+
61
+ ## Where the walkers came from
62
+
63
+ `walkers.py` holds the AST walkers that used to be private copies in
64
+ `scripts/audit_mandate_wiring.py` and `aidream/services/mandates/code_truth.py`. Both
65
+ now import from here. Their behavior is byte-for-byte the same (proven by a parity run
66
+ over 11,406 files at the move). Change one and you change both — that is the point.
67
+
68
+ ## Proving a change
69
+
70
+ ```bash
71
+ uv run pytest packages/matrx-mandate-scan/tests -q # 71 tests
72
+ uv run matrx-mandate-scan --self-test # 44 checks; runs from a uvx install too
73
+ uv run matrx-mandate-scan scan -o /tmp/scan.json # the whole repo
74
+ uv run pytest aidream/services/mandates/tests/test_code_truth.py -q
75
+ uv run pytest aidream/services/mandates/tests/test_scanner_resolver_agreement.py -q
76
+ python scripts/check_mandate_carrier_args.py --self-test --strict # the thin caller
77
+ ```
78
+
79
+ **Cost.** A cross-module key is only static if the scanner has seen the module that
80
+ declares it, so `build_module_index` reads every python file once before the real pass
81
+ (`module_index.py`). That pre-pass is CACHED per file on `(repo-relative path,
82
+ st_mtime_ns, st_size)` under `<repo>/.cache/matrx-mandate-scan/`. Measured on aidream:
83
+ **cold 62s, warm 34s**, byte-identical payloads. The cache answers only the pre-pass
84
+ question; every reference and finding still comes from reading the file in the real
85
+ pass, a changed file is always re-read, and a cache from another `scanner_version` is
86
+ discarded rather than trusted.
87
+
88
+ A new carrier or a new resolution shape gets a fixture in
89
+ `matrx_mandate_scan/fixtures/sources.py` and a case in `selftest.py` — the pytest suite
90
+ imports those cases, so the two can never disagree. Break the adapter deliberately and
91
+ watch the case go red before you believe it.
@@ -0,0 +1,129 @@
1
+ Metadata-Version: 2.5
2
+ Name: matrx-mandate-scan
3
+ Version: 0.1.0
4
+ Summary: The one Mandate reference scanner: finds every mandate carrier, every bypass, and reports contract-v1 JSON to the platform
5
+ Project-URL: Homepage, https://github.com/AI-Matrix-Engine/aidream
6
+ Project-URL: Repository, https://github.com/AI-Matrix-Engine/aidream
7
+ Project-URL: Issues, https://github.com/AI-Matrix-Engine/aidream/issues
8
+ Author-email: Matrx <admin@aimatrx.com>
9
+ Maintainer-email: Matrx <admin@aimatrx.com>
10
+ License: MIT
11
+ Keywords: governance,mandates,matrx,static-analysis
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Topic :: Software Development :: Quality Assurance
18
+ Requires-Python: >=3.13
19
+ Requires-Dist: pyyaml>=6.0.3
20
+ Provides-Extra: submit
21
+ Requires-Dist: asyncpg>=0.31.0; extra == 'submit'
22
+ Description-Content-Type: text/markdown
23
+
24
+ # matrx-mandate-scan
25
+
26
+ The ONE Mandate reference scanner. It finds every place code reaches for
27
+ platform intelligence — through a Mandate carrier or around it — reports the
28
+ result to the database in one frozen contract, and screams (never blocks) when
29
+ something is unresolved, unmeasured, or bypassing the mandate system.
30
+
31
+ Built for the **Mandate Declaration & Usage Reporting** program
32
+ (`common-docs/projects/mandate-declaration-reporting/`), lane L2.
33
+
34
+ ## Install / run
35
+
36
+ ```bash
37
+ # from any repo, no checkout needed
38
+ uvx --from matrx-mandate-scan==0.1.0 matrx-mandate-scan check
39
+
40
+ # inside the aidream workspace (vendored via [tool.uv.sources])
41
+ uv run matrx-mandate-scan scan --root aidream --root packages/matrx-ai
42
+ ```
43
+
44
+ ## Commands
45
+
46
+ | Command | What it does |
47
+ |---|---|
48
+ | `scan` | Contract-v1 JSON on **stdout**, the red human report on **stderr**. |
49
+ | `report` | `scan`, then submit through `mandate.submit_scan_report(jsonb)` and file every red finding into `ops.system_error` (`source_app='mandate-scan'`). |
50
+ | `check` | The release-path command: scan + report + reconcile. |
51
+ | `explain <file:line>` | What the scanner sees at one location, and why. |
52
+ | `--self-test` | The built-in RED→GREEN fixture suite. Runs from a `uvx` install. |
53
+
54
+ **Every command exits 0** unless `--strict` is passed. That is ruling **D23**:
55
+ a mandate check is loud and non-blocking; `--strict` exists for humans and for
56
+ the scheduled remediation task, never for a release script.
57
+
58
+ ## What counts as a reference
59
+
60
+ Classification is **by carrier, never by the word or the path**. A dotted
61
+ string is a mandate key only where a carrier puts it — so `consumerId =
62
+ "extend.chat"` is not a reference and never becomes one.
63
+
64
+ Python carriers:
65
+
66
+ - `declare_mandate` / `declare_generated_mandate` / `declare_mandated_agent` → `declaration`
67
+ - `declare_mandate_family(prefix, members=...)` → `family_declaration`, one `constant` per resolvable member, `dynamic_family` when the iterable is computed
68
+ - `resolve_mandate` → `resolution`; `run_mandate` → `execution`
69
+ - `run_mandated(Cls)` / `Cls.run()` → `execution`
70
+ - a `NamedAgent` subclass `mandate_key` (including `type(name, (NamedAgent,), {...})`) → `declaration`
71
+ - `seed_agent_id=<uuid>` inside a `declare_*` → `seed_holder`
72
+ - `@mandate_passthrough` / `MandateKeyParam` → `passthrough`, with the caller attributed when it lives in the same module
73
+
74
+ Config carriers (JSON / YAML / TOML) — the **property name** is the carrier:
75
+ `mandate_key`, `mandateKey`, `defaultMandateKey`, `fallback_mandate_key`.
76
+ Anything else that merely looks key-shaped is `unclassified` and advisory.
77
+
78
+ Keys resolve through literals, module and function constants (UPPER or not),
79
+ attribute constants, f-strings, `+` concatenations, ternaries (both branches
80
+ become real references), tuple/list loop members, aliases, literal-container
81
+ subscripts, and one level of analyzable module-local wrapper function.
82
+
83
+ An argument that resolves to none of those → finding **`UNRESOLVED_KEY`**, plus
84
+ an `unresolved`-flagged reference: D21 says unreachable is a flag, never a
85
+ filter, so nothing is ever dropped from the inventory.
86
+
87
+ ## Bypass detection and the ratchet
88
+
89
+ Importing a provider SDK (`anthropic`, `openai`, `groq`, `google.genai`,
90
+ `google.generativeai`, `litellm`, `xai`, `ollama`, `cohere`, `mistralai`) or
91
+ naming a provider host, anywhere outside
92
+ `packages/matrx-ai/matrx_ai/providers/**` and the one D10-approved module
93
+ (`conversation_labeler.py`), is a `bypass` reference. The exact standalone
94
+ RAG default embedding adapter (`packages/matrx-rag/matrx_rag/embeddings.py`) is
95
+ also an approved provider adapter: it is injected through `EmbeddingProvider`,
96
+ does not select a Mandate holder, and is independently ratcheted by
97
+ `scripts/check_raw_llm_clients.py`. No broader `matrx-rag` exemption exists.
98
+
99
+ - in the `--baseline` file → **`CONVERSION_PENDING`** (flag `conversion_pending`)
100
+ - not in it → **`NEW_BYPASS`** (D20: no new ones)
101
+ - a dynamic import that hides its target → **`UNRESOLVED_IMPORT`**
102
+
103
+ `--write-baseline` regenerates the file and **refuses to write a larger one**.
104
+ No entry in the baseline is an approved class; every one is a defect awaiting
105
+ conversion.
106
+
107
+ ## Coverage is mandatory output
108
+
109
+ Every file is scanned or listed with a reason (`generated`, `test_fixture`,
110
+ `parse_error`, `unsupported_language`). Any `parse_error` makes the package
111
+ `verification_status = incomplete` and files an `UNMEASURED` finding.
112
+
113
+ ## Reference identity
114
+
115
+ `sha256(repo_slug · package_path · file_path · symbol · occurrence_n ·
116
+ reference_type · mandate_key_or_prefix)`.
117
+
118
+ `repo_slug` is always the repo the scan ran in, resolved from
119
+ `git remote get-url origin` through the `platform.repo` mirror in
120
+ `matrx_mandate_scan/repo.py`. **An unknown remote is `UNMEASURED`, never a
121
+ guessed slug** — a folder name is not a repo identity.
122
+
123
+ ## Boundaries
124
+
125
+ Per `docs/packages/PACKAGE_DOCTRINE.md`, this package imports **neither
126
+ `aidream` nor `matrx-orm`**. It carries the AST walkers that used to live in
127
+ `scripts/audit_mandate_wiring.py` and
128
+ `aidream/services/mandates/code_truth.py`; those two modules now import them
129
+ from here, so there is exactly one definition of "what a carrier looks like".
@@ -0,0 +1,106 @@
1
+ # matrx-mandate-scan
2
+
3
+ The ONE Mandate reference scanner. It finds every place code reaches for
4
+ platform intelligence — through a Mandate carrier or around it — reports the
5
+ result to the database in one frozen contract, and screams (never blocks) when
6
+ something is unresolved, unmeasured, or bypassing the mandate system.
7
+
8
+ Built for the **Mandate Declaration & Usage Reporting** program
9
+ (`common-docs/projects/mandate-declaration-reporting/`), lane L2.
10
+
11
+ ## Install / run
12
+
13
+ ```bash
14
+ # from any repo, no checkout needed
15
+ uvx --from matrx-mandate-scan==0.1.0 matrx-mandate-scan check
16
+
17
+ # inside the aidream workspace (vendored via [tool.uv.sources])
18
+ uv run matrx-mandate-scan scan --root aidream --root packages/matrx-ai
19
+ ```
20
+
21
+ ## Commands
22
+
23
+ | Command | What it does |
24
+ |---|---|
25
+ | `scan` | Contract-v1 JSON on **stdout**, the red human report on **stderr**. |
26
+ | `report` | `scan`, then submit through `mandate.submit_scan_report(jsonb)` and file every red finding into `ops.system_error` (`source_app='mandate-scan'`). |
27
+ | `check` | The release-path command: scan + report + reconcile. |
28
+ | `explain <file:line>` | What the scanner sees at one location, and why. |
29
+ | `--self-test` | The built-in RED→GREEN fixture suite. Runs from a `uvx` install. |
30
+
31
+ **Every command exits 0** unless `--strict` is passed. That is ruling **D23**:
32
+ a mandate check is loud and non-blocking; `--strict` exists for humans and for
33
+ the scheduled remediation task, never for a release script.
34
+
35
+ ## What counts as a reference
36
+
37
+ Classification is **by carrier, never by the word or the path**. A dotted
38
+ string is a mandate key only where a carrier puts it — so `consumerId =
39
+ "extend.chat"` is not a reference and never becomes one.
40
+
41
+ Python carriers:
42
+
43
+ - `declare_mandate` / `declare_generated_mandate` / `declare_mandated_agent` → `declaration`
44
+ - `declare_mandate_family(prefix, members=...)` → `family_declaration`, one `constant` per resolvable member, `dynamic_family` when the iterable is computed
45
+ - `resolve_mandate` → `resolution`; `run_mandate` → `execution`
46
+ - `run_mandated(Cls)` / `Cls.run()` → `execution`
47
+ - a `NamedAgent` subclass `mandate_key` (including `type(name, (NamedAgent,), {...})`) → `declaration`
48
+ - `seed_agent_id=<uuid>` inside a `declare_*` → `seed_holder`
49
+ - `@mandate_passthrough` / `MandateKeyParam` → `passthrough`, with the caller attributed when it lives in the same module
50
+
51
+ Config carriers (JSON / YAML / TOML) — the **property name** is the carrier:
52
+ `mandate_key`, `mandateKey`, `defaultMandateKey`, `fallback_mandate_key`.
53
+ Anything else that merely looks key-shaped is `unclassified` and advisory.
54
+
55
+ Keys resolve through literals, module and function constants (UPPER or not),
56
+ attribute constants, f-strings, `+` concatenations, ternaries (both branches
57
+ become real references), tuple/list loop members, aliases, literal-container
58
+ subscripts, and one level of analyzable module-local wrapper function.
59
+
60
+ An argument that resolves to none of those → finding **`UNRESOLVED_KEY`**, plus
61
+ an `unresolved`-flagged reference: D21 says unreachable is a flag, never a
62
+ filter, so nothing is ever dropped from the inventory.
63
+
64
+ ## Bypass detection and the ratchet
65
+
66
+ Importing a provider SDK (`anthropic`, `openai`, `groq`, `google.genai`,
67
+ `google.generativeai`, `litellm`, `xai`, `ollama`, `cohere`, `mistralai`) or
68
+ naming a provider host, anywhere outside
69
+ `packages/matrx-ai/matrx_ai/providers/**` and the one D10-approved module
70
+ (`conversation_labeler.py`), is a `bypass` reference. The exact standalone
71
+ RAG default embedding adapter (`packages/matrx-rag/matrx_rag/embeddings.py`) is
72
+ also an approved provider adapter: it is injected through `EmbeddingProvider`,
73
+ does not select a Mandate holder, and is independently ratcheted by
74
+ `scripts/check_raw_llm_clients.py`. No broader `matrx-rag` exemption exists.
75
+
76
+ - in the `--baseline` file → **`CONVERSION_PENDING`** (flag `conversion_pending`)
77
+ - not in it → **`NEW_BYPASS`** (D20: no new ones)
78
+ - a dynamic import that hides its target → **`UNRESOLVED_IMPORT`**
79
+
80
+ `--write-baseline` regenerates the file and **refuses to write a larger one**.
81
+ No entry in the baseline is an approved class; every one is a defect awaiting
82
+ conversion.
83
+
84
+ ## Coverage is mandatory output
85
+
86
+ Every file is scanned or listed with a reason (`generated`, `test_fixture`,
87
+ `parse_error`, `unsupported_language`). Any `parse_error` makes the package
88
+ `verification_status = incomplete` and files an `UNMEASURED` finding.
89
+
90
+ ## Reference identity
91
+
92
+ `sha256(repo_slug · package_path · file_path · symbol · occurrence_n ·
93
+ reference_type · mandate_key_or_prefix)`.
94
+
95
+ `repo_slug` is always the repo the scan ran in, resolved from
96
+ `git remote get-url origin` through the `platform.repo` mirror in
97
+ `matrx_mandate_scan/repo.py`. **An unknown remote is `UNMEASURED`, never a
98
+ guessed slug** — a folder name is not a repo identity.
99
+
100
+ ## Boundaries
101
+
102
+ Per `docs/packages/PACKAGE_DOCTRINE.md`, this package imports **neither
103
+ `aidream` nor `matrx-orm`**. It carries the AST walkers that used to live in
104
+ `scripts/audit_mandate_wiring.py` and
105
+ `aidream/services/mandates/code_truth.py`; those two modules now import them
106
+ from here, so there is exactly one definition of "what a carrier looks like".
@@ -0,0 +1,20 @@
1
+ """matrx-mandate-scan — the ONE Mandate reference scanner.
2
+
3
+ Finds every mandate carrier, every dynamic family, every pass-through, and
4
+ every path to intelligence that goes around the mandate system; emits the
5
+ frozen contract-v1 report; submits it through the one DB door; files every red
6
+ finding into the platform's one error system.
7
+
8
+ Laws it is built to (campaign register, 2026-09-09):
9
+
10
+ * **D23 — never block a release.** Every command exits 0 unless ``--strict``.
11
+ * **D20 — no new bypasses; existing ones are a ratchet**, not an approved class.
12
+ * **D21 — unreachable is a flag, never a filter.** Nothing is dropped for being
13
+ unresolved, orphaned or broken.
14
+ * **PACKAGE_DOCTRINE** — this package never imports ``aidream`` or ``matrx-orm``.
15
+ """
16
+
17
+ from .contract import CONTRACT_VERSION, SCANNER_VERSION, ScanReport, identity_hash
18
+
19
+ __version__ = SCANNER_VERSION
20
+ __all__ = ["CONTRACT_VERSION", "SCANNER_VERSION", "ScanReport", "identity_hash", "__version__"]
@@ -0,0 +1,4 @@
1
+ from matrx_mandate_scan.cli import main
2
+
3
+ if __name__ == "__main__":
4
+ raise SystemExit(main())
@@ -0,0 +1,5 @@
1
+ """Language adapters. One tool, many adapters, one writer (DESIGN § 3.3)."""
2
+
3
+ from matrx_mandate_scan.adapters import config, python
4
+
5
+ __all__ = ["config", "python"]