deadlatch 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 (57) hide show
  1. deadlatch-0.1.0/CONTRIBUTING.md +81 -0
  2. deadlatch-0.1.0/DISCLAIMER.md +68 -0
  3. deadlatch-0.1.0/LICENSE +21 -0
  4. deadlatch-0.1.0/MANIFEST.in +19 -0
  5. deadlatch-0.1.0/PKG-INFO +305 -0
  6. deadlatch-0.1.0/README.md +268 -0
  7. deadlatch-0.1.0/README.zh-CN.md +236 -0
  8. deadlatch-0.1.0/SECURITY.md +67 -0
  9. deadlatch-0.1.0/docs/assets/agent-blocked.gif +0 -0
  10. deadlatch-0.1.0/docs/quickstart/cli.sh +101 -0
  11. deadlatch-0.1.0/docs/quickstart/mcp_client.py +126 -0
  12. deadlatch-0.1.0/docs/quickstart/python.py +116 -0
  13. deadlatch-0.1.0/pyproject.toml +76 -0
  14. deadlatch-0.1.0/setup.cfg +4 -0
  15. deadlatch-0.1.0/src/deadlatch/__init__.py +32 -0
  16. deadlatch-0.1.0/src/deadlatch/_decimal.py +37 -0
  17. deadlatch-0.1.0/src/deadlatch/_resources.py +33 -0
  18. deadlatch-0.1.0/src/deadlatch/_timeutil.py +25 -0
  19. deadlatch-0.1.0/src/deadlatch/_validation.py +176 -0
  20. deadlatch-0.1.0/src/deadlatch/audit.py +325 -0
  21. deadlatch-0.1.0/src/deadlatch/cli.py +201 -0
  22. deadlatch-0.1.0/src/deadlatch/direction.py +115 -0
  23. deadlatch-0.1.0/src/deadlatch/engine.py +256 -0
  24. deadlatch-0.1.0/src/deadlatch/exposure.py +161 -0
  25. deadlatch-0.1.0/src/deadlatch/guard.py +151 -0
  26. deadlatch-0.1.0/src/deadlatch/mcp_server.py +582 -0
  27. deadlatch-0.1.0/src/deadlatch/migrations.py +196 -0
  28. deadlatch-0.1.0/src/deadlatch/model.py +172 -0
  29. deadlatch-0.1.0/src/deadlatch/report.py +135 -0
  30. deadlatch-0.1.0/src/deadlatch/rules/__init__.py +6 -0
  31. deadlatch-0.1.0/src/deadlatch/rules/base.py +40 -0
  32. deadlatch-0.1.0/src/deadlatch/rules/cash_margin_check.py +169 -0
  33. deadlatch-0.1.0/src/deadlatch/rules/data_freshness.py +48 -0
  34. deadlatch-0.1.0/src/deadlatch/rules/input_validity.py +57 -0
  35. deadlatch-0.1.0/src/deadlatch/rules/kill_switch.py +51 -0
  36. deadlatch-0.1.0/src/deadlatch/rules/max_daily_loss.py +47 -0
  37. deadlatch-0.1.0/src/deadlatch/rules/max_drawdown.py +49 -0
  38. deadlatch-0.1.0/src/deadlatch/rules/max_order_quantity.py +37 -0
  39. deadlatch-0.1.0/src/deadlatch/rules/max_order_value.py +44 -0
  40. deadlatch-0.1.0/src/deadlatch/rules/max_symbol_exposure.py +95 -0
  41. deadlatch-0.1.0/src/deadlatch/rules/max_total_exposure.py +82 -0
  42. deadlatch-0.1.0/src/deadlatch/rules/missing_data_fail_closed.py +132 -0
  43. deadlatch-0.1.0/src/deadlatch/rules/order_time_validity.py +62 -0
  44. deadlatch-0.1.0/src/deadlatch/rules/registry.py +111 -0
  45. deadlatch-0.1.0/src/deadlatch/rules/stubs.py +47 -0
  46. deadlatch-0.1.0/src/deadlatch/schemas/audit-record.schema.json +86 -0
  47. deadlatch-0.1.0/src/deadlatch/schemas/order.schema.json +143 -0
  48. deadlatch-0.1.0/src/deadlatch/schemas/policy.schema.json +138 -0
  49. deadlatch-0.1.0/src/deadlatch/schemas/portfolio.schema.json +143 -0
  50. deadlatch-0.1.0/src/deadlatch/schemas/result.schema.json +148 -0
  51. deadlatch-0.1.0/src/deadlatch/schemas/shadow-report.schema.json +110 -0
  52. deadlatch-0.1.0/src/deadlatch.egg-info/PKG-INFO +305 -0
  53. deadlatch-0.1.0/src/deadlatch.egg-info/SOURCES.txt +55 -0
  54. deadlatch-0.1.0/src/deadlatch.egg-info/dependency_links.txt +1 -0
  55. deadlatch-0.1.0/src/deadlatch.egg-info/entry_points.txt +3 -0
  56. deadlatch-0.1.0/src/deadlatch.egg-info/requires.txt +24 -0
  57. deadlatch-0.1.0/src/deadlatch.egg-info/top_level.txt +1 -0
@@ -0,0 +1,81 @@
1
+ # Contributing
2
+
3
+ Thanks for considering contributing to Deadlatch. This project follows a
4
+ second-auditor acceptance loop: changes are verified on disk by an independent
5
+ reviewer before any commit/tag/release. Keep the working tree reviewable.
6
+
7
+ ## Local environment
8
+
9
+ - Python 3.10+ (develop on 3.11; CI tests 3.10/3.11/3.12).
10
+ - Create the virtualenv and install editable with test/build/docs extras:
11
+
12
+ ```bash
13
+ python3 -m venv .venv
14
+ .venv/bin/pip install -e ".[dev]"
15
+ ```
16
+
17
+ ## Verification commands (must all pass)
18
+
19
+ ```bash
20
+ .venv/bin/python -m pytest -p no:cacheprovider tests/ -q # full suite
21
+ .venv/bin/python tools/validate_schemas.py # schema + meta-schema
22
+ .venv/bin/python tools/scan_sensitive.py # sensitive-data scan
23
+ COVERAGE_FILE=/tmp/deadlatch.coverage .venv/bin/python -m pytest -p no:cacheprovider \
24
+ --cov-branch --cov=deadlatch --cov-report=term tests/ -q # branch coverage ≥90%
25
+ git diff --check
26
+ ```
27
+
28
+ - No `skip`, no `xfail`. Branch coverage must stay ≥ 90%.
29
+ - Packaging verification after changing runtime resources:
30
+
31
+ ```bash
32
+ .venv/bin/python -m build
33
+ .venv/bin/python tools/verify_wheel.py dist/*.whl
34
+ ```
35
+
36
+ After version or documentation changes, regenerate the public hash
37
+ manifest so the root file cannot stay stale:
38
+
39
+ ```bash
40
+ .venv/bin/python tools/make_release_candidate.py --out /tmp/deadlatch-candidate --sync-source
41
+ .venv/bin/python tools/verify_hash_manifest.py /tmp/deadlatch-candidate
42
+ ```
43
+
44
+ Local and ordinary CI wheel/sdist hashes are not the PyPI upload
45
+ identity. Only the `release.yml` publish job's pre-upload
46
+ `name size sha256` lines, matched against the PyPI JSON readback, are
47
+ the published artifacts.
48
+
49
+ ## Rule discipline
50
+
51
+ - **Schema is the contract.** Rule behavior, exit codes (0/2/3/4/5), and
52
+ decision synthesis follow `rules-spec.md` and the versioned schemas in
53
+ `schemas/`. The packaged copies under `src/deadlatch/schemas/` must
54
+ stay byte-identical to the repo copies (`tests/test_resources.py` enforces it).
55
+ - **Decimal, never float, for money.** All amounts and ratios go through
56
+ `as_decimal` (S-7 single intake).
57
+ - **Fail-closed.** Missing/malformed data → BLOCK/3; input errors → 4;
58
+ internal errors → 5. Never report an uncertain state as PASS.
59
+ - **No network, no broker.** Business code must not open sockets, register
60
+ HTTP/SSE routes, or import broker/network stacks.
61
+ - **No secrets, no drift.** Error text must never echo caller-supplied values
62
+ (use the shared safe constructors in `_validation.py`). Tests must never
63
+ contain real accounts, positions, tokens, paths, or logs — use string
64
+ concatenation for sensitive-looking probes so the scanner stays clean.
65
+
66
+ ## Test change discipline (NEW-11)
67
+
68
+ - Every test **added**, **rewritten**, or **deleted** must be declared in
69
+ `CHANGELOG.md` with the reason, exactly like a design change.
70
+ - Do not weaken or delete existing assertions to make a suite green; if a test
71
+ expectation is wrong, justify the correction in the changelog.
72
+ - Do not use `try/except: return` or `assume()` to discard generated samples,
73
+ and do not delete fuzz samples to force green.
74
+
75
+ ## What never goes in a commit
76
+
77
+ Real account numbers, holdings, credentials, tokens, cookies, absolute local
78
+ paths, audit JSONL, coverage output, or local policy/portfolio/order files.
79
+ The sensitive-data scanner (`tools/scan_sensitive.py`) runs in CI and fails
80
+ the build on any un-exempted hit; do not broaden its allowlist to mask
81
+ documented private paths.
@@ -0,0 +1,68 @@
1
+ # Disclaimer
2
+
3
+ **Read this in full before using Deadlatch.** A short summary is provided
4
+ in the README; this document is the authoritative statement.
5
+
6
+ ## Not investment advice
7
+
8
+ Deadlatch is a technical risk-evaluation tool. It does not provide
9
+ investment, financial, legal, or tax advice. Nothing in this repository, its
10
+ documentation, examples, or outputs constitutes a recommendation to buy, sell,
11
+ or hold any security. No rule threshold, policy example, or default limit in
12
+ this repository is advice about how you should trade.
13
+
14
+ ## No guarantee against loss
15
+
16
+ Deadlatch evaluates orders against the policy you configure. It cannot
17
+ predict market moves, and it cannot prevent losses — including losses caused by
18
+ orders it evaluated as allowed (PASS or WARN), by orders placed without calling
19
+ it, by orders that ignored its BLOCK, or by incorrect inputs or configurations.
20
+ **The tool is a gate, not an insurance policy.** Do not trade capital you are
21
+ not prepared to lose.
22
+
23
+ ## You are responsible for your inputs and rules
24
+
25
+ The guard evaluates exactly what you give it:
26
+
27
+ - **Inputs:** order, portfolio snapshot, and policy are provided by you. A
28
+ stale, fabricated, or malformed snapshot can produce a wrong verdict.
29
+ Validate your data sources yourself.
30
+ - **Rules:** the 12 rules and their thresholds come from the policy you write.
31
+ You are responsible for configuring limits that match your actual account,
32
+ instruments, and risk tolerance.
33
+ - **Fail-closed is not correctness:** BLOCK on missing data is safe-by-default,
34
+ but a policy that is too loose, or inputs that are wrong in ways the schema
35
+ cannot detect, are outside this tool's control.
36
+
37
+ ## The guard never places orders
38
+
39
+ Deadlatch has no broker connectivity, no order routing, and no execution
40
+ capability. It only evaluates. Any order submission is performed exclusively by
41
+ you or by the system you integrate it into.
42
+
43
+ ## Advisory-only and bypass
44
+
45
+ Deadlatch is advisory. It cannot force an agent that never calls it to
46
+ call it, and it cannot stop an agent that ignores a BLOCK from submitting
47
+ orders elsewhere. Whether the agent calls the guard and honors the result is
48
+ the integrator's decision. Do not assume the guard can stop a fully bypassing
49
+ agent.
50
+
51
+ ## Examples are fictional
52
+
53
+ All tickers, orders, portfolios, policies, and numbers in examples, quick
54
+ starts, tests, and generated artifacts are fictional. They exist to demonstrate
55
+ mechanics, not to suggest real instruments, positions, or strategies. Any
56
+ resemblance to real securities or accounts is coincidental.
57
+
58
+ ## Test before live trading
59
+
60
+ The project is early `0.1.0` software. Before using it with real
61
+ capital, simulate, backtest, and verify behavior on your own data and against
62
+ your own broker semantics. Do not deploy it for live trading based solely on
63
+ this documentation.
64
+
65
+ ## No SLA
66
+
67
+ This project is provided as-is without any support or service-level commitment.
68
+ See the MIT [LICENSE](LICENSE) for the full warranty disclaimer.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Deadlatch contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,19 @@
1
+ # §二.5:sdist 只含发布物——排除 tests/工具/示例/CI/运行数据。
2
+ # FIX-006-1:docs 资产(演示 GIF + 三个 Quick Start)必须随 sdist 发布。
3
+ include LICENSE
4
+ include README.md
5
+ include README.zh-CN.md
6
+ include SECURITY.md
7
+ include CONTRIBUTING.md
8
+ include DISCLAIMER.md
9
+ include pyproject.toml
10
+ recursive-include src/deadlatch/schemas *.json
11
+ recursive-include docs *.md *.py *.sh *.gif
12
+ prune tests
13
+ prune tools
14
+ prune examples
15
+ prune .github
16
+ global-exclude *.py[cod]
17
+ global-exclude __pycache__
18
+ global-exclude .DS_Store
19
+ global-exclude *.audit.jsonl
@@ -0,0 +1,305 @@
1
+ Metadata-Version: 2.4
2
+ Name: deadlatch
3
+ Version: 0.1.0
4
+ Summary: Local-first pre-trade risk evaluation library and CLI for AI trading agents. Advisory-only: whether the agent calls the guard and honors its result is the integrator's decision. Broker-agnostic, fully auditable.
5
+ License-Expression: MIT
6
+ Keywords: trading,risk,pre-trade,guardrail,advisory,fail-closed,audit,mcp,agent
7
+ Classifier: Development Status :: 3 - Alpha
8
+ Classifier: Intended Audience :: Developers
9
+ Classifier: Operating System :: OS Independent
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.10
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Topic :: Office/Business :: Financial :: Investment
15
+ Requires-Python: >=3.10
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: jsonschema>=4.0
19
+ Requires-Dist: PyYAML>=6.0
20
+ Requires-Dist: mcp<3,>=2.0
21
+ Provides-Extra: test
22
+ Requires-Dist: pytest>=8.0; extra == "test"
23
+ Requires-Dist: pytest-cov>=5.0; extra == "test"
24
+ Requires-Dist: hypothesis>=6.100; extra == "test"
25
+ Requires-Dist: build>=1.0; extra == "test"
26
+ Requires-Dist: Pillow>=10.0; extra == "test"
27
+ Requires-Dist: tomli>=2.0; python_version < "3.11" and extra == "test"
28
+ Provides-Extra: build
29
+ Requires-Dist: build>=1.0; extra == "build"
30
+ Provides-Extra: docs
31
+ Requires-Dist: Pillow>=10.0; extra == "docs"
32
+ Provides-Extra: dev
33
+ Requires-Dist: deadlatch[test]; extra == "dev"
34
+ Requires-Dist: deadlatch[build]; extra == "dev"
35
+ Requires-Dist: deadlatch[docs]; extra == "dev"
36
+ Dynamic: license-file
37
+
38
+ # Deadlatch
39
+
40
+ **The pre-trade latch for your trading agent.** Advisory-only.
41
+
42
+ Three things you need to know before anything else:
43
+
44
+ 1. **You need an independent, cross-broker gate that you control.** If an agent can place orders on your account, the last check between the agent and the broker should not be the agent itself — and it should not be locked to one broker's UI or rules.
45
+ 2. **Deadlatch does not predict, does not recommend, and does not place orders.** It answers one question only: *is this order allowed right now?* It is not a signal generator and it is not a broker.
46
+ 3. **Every answer comes with reasons, evidence, and a local audit record.** PASS / WARN / BLOCK is never a bare verdict — you can see which rule hit, why, and what was evaluated, and every check is appended to a local JSONL audit log.
47
+
48
+ **Honest boundary (please read):** Deadlatch is advisory. It cannot force an agent that never calls it to call it, and it cannot stop an agent that ignores a BLOCK from submitting the order somewhere else. Whether the agent calls the guard and honors the result is the integrator's decision. Do not rely on this tool as a guarantee against loss — it is a gate, not an insurance policy.
49
+
50
+ - **License:** MIT — see [LICENSE](LICENSE).
51
+ - **中文文档:** [README.zh-CN.md](README.zh-CN.md)
52
+ - **Security:** [SECURITY.md](SECURITY.md) · **Contributing:** [CONTRIBUTING.md](CONTRIBUTING.md) · **Disclaimer:** [DISCLAIMER.md](DISCLAIMER.md)
53
+
54
+ ## Start here
55
+
56
+ <!-- mcp-name: io.github.Diabloluo/deadlatch -->
57
+
58
+ `0.1.0` is the first stable version line. **PyPI and the MCP Registry are not published yet.** Do not treat the commands below as live until https://pypi.org/p/deadlatch shows `deadlatch==0.1.0`. Until then, install the current GitHub pre-release wheel: [v0.1.0.dev1](https://github.com/Diabloluo/deadlatch/releases/tag/v0.1.0.dev1).
59
+
60
+ **Intended one-line install (after PyPI readback):**
61
+
62
+ ```bash
63
+ pip install deadlatch==0.1.0
64
+ ```
65
+
66
+ **MCP-first start (after that same readback):**
67
+
68
+ ```bash
69
+ uvx --from deadlatch==0.1.0 deadlatch-mcp --policy policy.yaml --portfolio portfolio.json
70
+ ```
71
+
72
+ `--policy` and `--portfolio` are required local files. `--audit-path` and `--kill-switch-path` are optional. Use fictional or your own simulated inputs only. Deadlatch is advisory-only: it never places orders and cannot stop an agent that never calls it. Option orders must use the broker's unique full contract code as `symbol`.
73
+
74
+ Then:
75
+
76
+ 1. **Run a fictional-data Quick Start** below (Python, CLI, or MCP). Confirm `PASS` → `BLOCK` → local audit.
77
+ 2. **Request a 20-minute integration assessment** only if you already have an order-intent or simulated execution path: [open the assessment form](https://github.com/Diabloluo/deadlatch/issues/new?template=integration-assessment.yml).
78
+
79
+ That GitHub issue is **public**. Do not paste accounts, positions, orders, API keys, tokens, customer names, or private paths. Security defects must go through [GitHub Security Advisories](https://github.com/Diabloluo/deadlatch/security/advisories), not a public issue.
80
+
81
+ ---
82
+
83
+ ## Quick Start (60 seconds each)
84
+
85
+ All three quick starts use fictional data and a temporary audit path. They are executed from the same source scripts by the test suite, so they cannot drift from the documentation.
86
+
87
+ ### 1. Python API
88
+
89
+ ```bash
90
+ pip install dist/deadlatch-*.whl # or: pip install -e .
91
+ python docs/quickstart/python.py
92
+ ```
93
+
94
+ Shows `Guard.from_policy(...)` → `Order` / `Portfolio` → `guard.check(...)`:
95
+ a valid order returns `PASS / 0`; an oversized order returns `BLOCK / 3` with the
96
+ hit rules; on `BLOCK` the example caller stops — no broker call is ever made.
97
+
98
+ ### 2. CLI
99
+
100
+ ```bash
101
+ bash docs/quickstart/cli.sh # requires `deadlatch` on PATH
102
+ ```
103
+
104
+ Creates fresh inputs in a temp directory (dynamic timestamps — never goes stale),
105
+ then runs `deadlatch check` for PASS (`exit 0`), BLOCK (`exit 3`), an input
106
+ error (`exit 4`), and a `--json` check, plus `shadow report --json` over the audit.
107
+
108
+ ### 3. MCP (stdio)
109
+
110
+ ```bash
111
+ python docs/quickstart/mcp_client.py # requires `deadlatch` installed
112
+ ```
113
+
114
+ Starts `deadlatch-mcp` as a real subprocess over stdio, lists the five tools,
115
+ and calls `check_order` once for PASS and once for BLOCK. `policy` / `portfolio` /
116
+ `audit` paths are **server startup configuration** — an agent cannot swap them as
117
+ tool arguments. BLOCK is a constraint the caller must honor; technically the guard
118
+ cannot force a fully bypassing agent to call it.
119
+
120
+ ---
121
+
122
+ ## What it is / is not
123
+
124
+ | Deadlatch **is** | Deadlatch **is not** |
125
+ |---|---|
126
+ | A local, deterministic risk gate evaluated before you submit | A signal generator, recommender, or portfolio optimizer |
127
+ | A library, a CLI, and a stdio MCP server — no broker connectivity, no policy mutation | A broker adapter, an execution engine, or a market feed |
128
+ | An auditable check: every evaluation is written to a local JSONL log | A cloud service, a database, or a telemetry sink |
129
+ | USD-only, single-leg orders, one snapshot per check (v0.1) | Multi-leg, multi-currency, Greeks/IV-aware (see limitations) |
130
+
131
+ ## Who should use it
132
+
133
+ - Teams that already have an order-intent or simulated execution path and
134
+ want an independent, deterministic pre-trade gate with a local audit trail.
135
+ - Developers who want a small, dependency-light, fail-closed building block they
136
+ can integrate into their own execution pipeline.
137
+ - Anyone who wants to evaluate orders against a *policy they control*, expressed
138
+ as plain YAML.
139
+
140
+ **Who should not use it:** anyone expecting a profit guarantee, a backtest engine,
141
+ a portfolio manager, or a tool that enforces itself. If the agent never calls the
142
+ guard, or ignores a BLOCK, nothing in this repository can stop it.
143
+
144
+ ## Core security boundary
145
+
146
+ - **Local:** everything runs on your machine; no account credentials are ever
147
+ stored, read, or transmitted.
148
+ - **No network core path:** the library, CLI, and MCP server never open a socket,
149
+ never register an HTTP/SSE route, and never call out for quotes or anything else
150
+ (the MCP SDK's HTTP stack is a transitive dependency that business code never imports).
151
+ - **Never places orders:** the core package (library, CLI, MCP server) has
152
+ no broker connectivity and never submits orders. Experimental read-only
153
+ mapping examples exist only in the development workspace; they are not
154
+ included in the public candidate or the wheel, and they are not
155
+ live-verified integrations.
156
+ - **Fail-closed:** missing or malformed data → BLOCK (`exit 3`); input/config errors →
157
+ `exit 4`; internal errors → `exit 5`. An uncertain state is never reported as PASS.
158
+ - **Direction is snapshot-derived:** order-side text is never accepted as proof of a
159
+ close. Stock and option closing intent is recognized only when a fresh portfolio
160
+ snapshot contains a matching, opposite-side position with sufficient quantity.
161
+ For options, `symbol` must be the broker's unique full contract code; never reuse
162
+ an underlying ticker across different expiries, strikes, or rights.
163
+ - **USD-only (v0.1):** any currency mismatch (order, portfolio, positions) is an
164
+ input error (`exit 4`); the MCP account-status tool fail-closes on mismatch.
165
+
166
+ **Write surface:** the tool never modifies `policy`, `portfolio`, or
167
+ kill-switch state, never connects to a broker, and never places an order.
168
+ Two kinds of intentional local file writes exist:
169
+
170
+ 1. **Audit subsystem:** `Guard.check()` / `check_order` append one sanitized
171
+ record to the local audit JSONL (30-day retention); the shadow-report
172
+ entry point (`deadlatch shadow report`) triggers the same retention
173
+ pruning, which atomically rewrites the audit file when expired records
174
+ exist; the audit implementation uses lock/tmp files and `os.replace` to
175
+ make each transaction atomic.
176
+ 2. **Explicit migration output:** `deadlatch migrate --output <file>`
177
+ writes the migrated document only when you explicitly pass `--output`.
178
+
179
+ ## The 12 rules (v0.1)
180
+
181
+ | # | Rule | What it guards |
182
+ |---|---|---|
183
+ | R1 | `kill_switch` | Global switch: `off` / `full` (block everything) / `reduce_only` (allow only inferred closing orders) |
184
+ | R2 | `input_validity` | Order passes schema, version gate, currency consistency, finite amounts (violations → `exit 4`) |
185
+ | R3 | `max_order_quantity` | Single-order quantity limit |
186
+ | R4 | `max_order_value` | Single-order notional limit (options: price × multiplier × quantity) |
187
+ | R5 | `max_symbol_exposure` | Exposure per underlying (options by strike × multiplier × quantity) |
188
+ | R6 | `max_total_exposure` | Portfolio gross exposure ratio |
189
+ | R7 | `cash_margin_check` | Post-trade cash floor and short-option margin |
190
+ | R8 | `max_daily_loss` | Daily loss ratio (PnL / day-start equity) |
191
+ | R9 | `max_drawdown` | Drawdown ratio from peak |
192
+ | R10 | `order_time_validity` | Order age / future timestamps (unparseable → fail-closed BLOCK) |
193
+ | R11 | `data_freshness` | Portfolio snapshot freshness (future snapshot → fail-closed) |
194
+ | R12 | `missing_data_fail_closed` | Missing/null/ill-formed portfolio data → `exit 3` (data unusable = risk) |
195
+
196
+ Optional rules (R3–R7) are toggled by their config keys in `policy.yaml`; a missing
197
+ optional key must be declared in `acknowledged_disabled` or the policy is rejected
198
+ (`exit 4`). Mandatory rules (R1, R2, R8–R12) can never be disabled.
199
+
200
+ ## Exit codes
201
+
202
+ | Code | Meaning |
203
+ |---|---|
204
+ | `0` | PASS — the order is allowed as given |
205
+ | `2` | WARN — proceed only if your execution policy explicitly allows warnings |
206
+ | `3` | BLOCK — the order must not be submitted (risk rule or fail-closed data) |
207
+ | `4` | Input / configuration error — the caller misused the API, not a risk event |
208
+ | `5` | Internal / rule exception — treated as BLOCK (fail-closed) |
209
+
210
+ In shadow mode the internal verdict is recorded (`shadow_verdict`) while the
211
+ external projection is `PASS / 0`; kill-switch hits and `exit 4/5` are never
212
+ projected away.
213
+
214
+ ## Data contracts & migration
215
+
216
+ Schemas are versioned JSON Schema 2020-12 files shipped inside the package:
217
+ `order`, `portfolio`, `policy`, `result`, `audit-record`, `shadow-report`.
218
+ Explicit offline migration is available for legacy documents:
219
+
220
+ ```bash
221
+ deadlatch migrate --kind order --input order_v1.json [--output out.json]
222
+ deadlatch migrate --kind policy --input policy_v1.json [--output out.json]
223
+ deadlatch migrate --kind portfolio --input portfolio_v1.json [--output out.json]
224
+ ```
225
+
226
+ Migration converts only adjudicated fields (e.g. policy v1 boolean kill switch →
227
+ `off`/`full`); it never guesses business fields. Normal evaluation entries reject
228
+ old versions (`exit 4`) rather than silently migrating.
229
+
230
+ ## Audit log
231
+
232
+ Every `Guard.check()` appends one record to a local JSONL audit file (default
233
+ `~/.deadlatch/audit.jsonl`, overridable via `--audit-path` /
234
+ `DEADLATCH_AUDIT_PATH`). Records are schema-validated, sanitized (no
235
+ credentials, cookies, or absolute paths in plaintext), and pruned to a **30-day
236
+ retention** window inside the same locked transaction as the append. If the audit
237
+ write fails, the returned result is degraded **severity-only-up**: PASS/0 → WARN/2;
238
+ BLOCK/3/4/5 keeps its decision and just attaches an `audit_write_failed` warning —
239
+ the disk and the returned Result never contradict each other.
240
+
241
+ ## MCP server
242
+
243
+ `deadlatch-mcp` is a **stdio-only** MCP server (no TCP listener, no
244
+ HTTP/SSE routes). The five tools are **read-only**: none of them can modify
245
+ `policy`, `portfolio`, or kill-switch state (those paths are startup
246
+ configuration, not tool arguments). Policy changes are validated and loaded
247
+ automatically on the next tool call. An optional independent kill-switch file is
248
+ read on every call and can only make the policy more restrictive. Note the server still appends each
249
+ `check_order` evaluation to the local audit log — that is by design, not a
250
+ tool capability. Five tools:
251
+
252
+ | Tool | Purpose |
253
+ |---|---|
254
+ | `check_order` | Evaluate one order; returns full `result` (decision, exit code, violations, evidence) |
255
+ | `get_account_status` | Snapshot freshness, equity, cash, PnL, drawdown, exposure utilization |
256
+ | `get_policy` | Read-only projection of the effective policy |
257
+ | `kill_switch_status` | Current kill-switch mode (read-only; no tool can change it) |
258
+ | `recent_decisions` | Recent audit records (oldest-first, optional `since`/`limit`) |
259
+
260
+ Start it with:
261
+
262
+ ```bash
263
+ deadlatch-mcp --policy policy.yaml --portfolio portfolio.json \
264
+ [--audit-path audit.jsonl] [--kill-switch-path kill-switch]
265
+ ```
266
+
267
+ The path arguments are startup configuration only; their file contents remain
268
+ live local state. A configured kill-switch file must contain exactly `off`,
269
+ `reduce_only`, or `full`. It cannot weaken a stricter mode already present in the
270
+ policy. A missing, malformed, or concurrently unstable live policy/switch fails
271
+ closed: tool errors are `isError=true` + `fail_closed`, and configuration errors
272
+ carry `input_error=true` + `exit_code=4`.
273
+
274
+ ## Demo
275
+
276
+ An agent calls `check_order` with an oversized order; the guard returns
277
+ `BLOCK / 3` with the hit rules; the agent stops instead of calling any broker
278
+ tool. Generated from a real local MCP stdio run with fictional data
279
+ (`tools/make_demo_gif.py`):
280
+
281
+ ![Agent blocked by Deadlatch](docs/assets/agent-blocked.gif)
282
+
283
+ ## Known limitations (v0.1)
284
+
285
+ - Naked short-call upside risk is unlimited. v0.1 uses a strike-based exposure
286
+ approximation and does not model that unlimited tail; do not treat it as a
287
+ conservative bound for short calls.
288
+ - Short-sell cash outflow is modeled as `0` (documented simplification).
289
+ - No Greeks, IV, multi-leg strategies, or multi-currency books.
290
+ - Audit cross-process locking relies on POSIX `fcntl`; on non-POSIX platforms the
291
+ lock degrades to a process-local lock (no cross-process guarantee).
292
+ - The guard cannot prevent complete bypass: an agent that never calls it, or that
293
+ ignores a BLOCK and calls the broker directly, cannot be stopped by this tool.
294
+ - Examples in the repository use fictional tickers and data only.
295
+
296
+ ## Governance
297
+
298
+ - [SECURITY.md](SECURITY.md) — supported versions, vulnerability scope, reporting.
299
+ - [CONTRIBUTING.md](CONTRIBUTING.md) — environment, test/schema/scan/coverage commands, rule discipline.
300
+ - [DISCLAIMER.md](DISCLAIMER.md) — full legal/risk disclaimer (summary below).
301
+ - [README.zh-CN.md](README.zh-CN.md) — 中文文档.
302
+
303
+ **Disclaimer (summary):** not investment advice; no guarantee against losses;
304
+ verify inputs and rules yourself; the guard never places orders; all examples are
305
+ fictional; test before trading real capital; no SLA. See [DISCLAIMER.md](DISCLAIMER.md) in full.