proofline-evidence 2.0.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 (69) hide show
  1. proofline_evidence-2.0.0/.gitignore +173 -0
  2. proofline_evidence-2.0.0/LICENSE +21 -0
  3. proofline_evidence-2.0.0/PKG-INFO +103 -0
  4. proofline_evidence-2.0.0/README.md +69 -0
  5. proofline_evidence-2.0.0/apps/api/proofline/__init__.py +3 -0
  6. proofline_evidence-2.0.0/apps/api/proofline/anchors.py +217 -0
  7. proofline_evidence-2.0.0/apps/api/proofline/api.py +1977 -0
  8. proofline_evidence-2.0.0/apps/api/proofline/attestations.py +525 -0
  9. proofline_evidence-2.0.0/apps/api/proofline/backup.py +211 -0
  10. proofline_evidence-2.0.0/apps/api/proofline/cli.py +873 -0
  11. proofline_evidence-2.0.0/apps/api/proofline/config.py +152 -0
  12. proofline_evidence-2.0.0/apps/api/proofline/data/architecture-decision.md +31 -0
  13. proofline_evidence-2.0.0/apps/api/proofline/database.py +62 -0
  14. proofline_evidence-2.0.0/apps/api/proofline/decision_health.py +179 -0
  15. proofline_evidence-2.0.0/apps/api/proofline/decision_impact_api.py +79 -0
  16. proofline_evidence-2.0.0/apps/api/proofline/decision_impacts.py +201 -0
  17. proofline_evidence-2.0.0/apps/api/proofline/decision_policy.py +98 -0
  18. proofline_evidence-2.0.0/apps/api/proofline/decision_review_api.py +348 -0
  19. proofline_evidence-2.0.0/apps/api/proofline/decision_reviews.py +559 -0
  20. proofline_evidence-2.0.0/apps/api/proofline/embeddings.py +357 -0
  21. proofline_evidence-2.0.0/apps/api/proofline/evaluation.py +935 -0
  22. proofline_evidence-2.0.0/apps/api/proofline/evidence_packages.py +959 -0
  23. proofline_evidence-2.0.0/apps/api/proofline/extraction.py +232 -0
  24. proofline_evidence-2.0.0/apps/api/proofline/folder_scanning.py +477 -0
  25. proofline_evidence-2.0.0/apps/api/proofline/folder_watching.py +162 -0
  26. proofline_evidence-2.0.0/apps/api/proofline/git_ingestion.py +138 -0
  27. proofline_evidence-2.0.0/apps/api/proofline/grounding.py +326 -0
  28. proofline_evidence-2.0.0/apps/api/proofline/ingestion.py +833 -0
  29. proofline_evidence-2.0.0/apps/api/proofline/integrity.py +583 -0
  30. proofline_evidence-2.0.0/apps/api/proofline/learning.py +50 -0
  31. proofline_evidence-2.0.0/apps/api/proofline/main.py +84 -0
  32. proofline_evidence-2.0.0/apps/api/proofline/migrations.py +882 -0
  33. proofline_evidence-2.0.0/apps/api/proofline/model_gateway.py +578 -0
  34. proofline_evidence-2.0.0/apps/api/proofline/models.py +515 -0
  35. proofline_evidence-2.0.0/apps/api/proofline/notes.py +44 -0
  36. proofline_evidence-2.0.0/apps/api/proofline/pilot_simulation.py +310 -0
  37. proofline_evidence-2.0.0/apps/api/proofline/portability.py +1766 -0
  38. proofline_evidence-2.0.0/apps/api/proofline/portable_import.py +666 -0
  39. proofline_evidence-2.0.0/apps/api/proofline/provenance_benchmark.py +347 -0
  40. proofline_evidence-2.0.0/apps/api/proofline/provenance_conformance.py +161 -0
  41. proofline_evidence-2.0.0/apps/api/proofline/real_model_evaluation.py +725 -0
  42. proofline_evidence-2.0.0/apps/api/proofline/reranking.py +119 -0
  43. proofline_evidence-2.0.0/apps/api/proofline/retrieval.py +119 -0
  44. proofline_evidence-2.0.0/apps/api/proofline/review_receipts.py +201 -0
  45. proofline_evidence-2.0.0/apps/api/proofline/runtime.py +66 -0
  46. proofline_evidence-2.0.0/apps/api/proofline/sarif.py +162 -0
  47. proofline_evidence-2.0.0/apps/api/proofline/schemas.py +812 -0
  48. proofline_evidence-2.0.0/apps/api/proofline/secret_store.py +70 -0
  49. proofline_evidence-2.0.0/apps/api/proofline/server.py +150 -0
  50. proofline_evidence-2.0.0/apps/api/proofline/stale_decision_demo.py +219 -0
  51. proofline_evidence-2.0.0/apps/api/proofline/studio.py +208 -0
  52. proofline_evidence-2.0.0/apps/api/proofline/studio_exports.py +208 -0
  53. proofline_evidence-2.0.0/apps/api/proofline/web/assets/index-B6UyXvhp.js +205 -0
  54. proofline_evidence-2.0.0/apps/api/proofline/web/assets/index-Cq2uTymx.css +1 -0
  55. proofline_evidence-2.0.0/apps/api/proofline/web/index.html +15 -0
  56. proofline_evidence-2.0.0/pyproject.toml +96 -0
  57. proofline_evidence-2.0.0/spec/decision-evidence-package/README.md +17 -0
  58. proofline_evidence-2.0.0/spec/decision-evidence-package/VERSIONING.md +19 -0
  59. proofline_evidence-2.0.0/spec/decision-evidence-package/v1/schema.json +191 -0
  60. proofline_evidence-2.0.0/spec/decision-evidence-package/v1/test-vectors/README.md +16 -0
  61. proofline_evidence-2.0.0/spec/decision-evidence-package/v1/test-vectors/expected.json +1 -0
  62. proofline_evidence-2.0.0/spec/decision-evidence-package/v1/test-vectors/mutations.json +26 -0
  63. proofline_evidence-2.0.0/spec/decision-evidence-package/v1/test-vectors/valid-minimal.json +1 -0
  64. proofline_evidence-2.0.0/spec/decision-review-receipt/v1/schema.json +53 -0
  65. proofline_evidence-2.0.0/spec/decision-review-receipt/v1/test-vectors/expected.json +7 -0
  66. proofline_evidence-2.0.0/spec/decision-review-receipt/v1/test-vectors/mutations.json +12 -0
  67. proofline_evidence-2.0.0/spec/decision-review-receipt/v1/test-vectors/valid-minimal.json +21 -0
  68. proofline_evidence-2.0.0/spec/signed-attestation/v1/schema.json +55 -0
  69. proofline_evidence-2.0.0/spec/signed-attestation/v1/test-vectors/valid-ed25519.json +21 -0
@@ -0,0 +1,173 @@
1
+ # Canonical ignore rules for repositories owned by thangldw.
2
+ # Keep this file identical across repositories.
3
+
4
+ # Operating systems and file managers
5
+ .DS_Store
6
+ .AppleDouble
7
+ .LSOverride
8
+ ._*
9
+ .DocumentRevisions-V100
10
+ .Spotlight-V100
11
+ .TemporaryItems
12
+ .Trashes
13
+ .VolumeIcon.icns
14
+ .fseventsd
15
+ .localized
16
+ __MACOSX/
17
+ Thumbs.db
18
+ Thumbs.db:encryptable
19
+ ehthumbs.db
20
+ ehthumbs_vista.db
21
+ [Dd]esktop.ini
22
+ $RECYCLE.BIN/
23
+ *.lnk
24
+
25
+ # Editors and local assistants
26
+ .idea/
27
+ .vscode/
28
+ *.code-workspace
29
+ *.sublime-project
30
+ *.sublime-workspace
31
+ *.swp
32
+ *.swo
33
+ *~
34
+ .history/
35
+ .claude/
36
+ .codex/
37
+ .worktrees/
38
+
39
+ # Secrets and local configuration
40
+ .env
41
+ .env.*
42
+ !.env.example
43
+ !.env.*.example
44
+ *.local
45
+ *.secret
46
+ *.secrets
47
+ *.key
48
+ *.pem
49
+ *.p12
50
+ *.pfx
51
+ credentials.json
52
+ service-account*.json
53
+
54
+ # Logs, temporary files, backups and local databases
55
+ *.log
56
+ *.tmp
57
+ *.temp
58
+ *.bak
59
+ *.backup
60
+ *.orig
61
+ *.rej
62
+ *.stackdump
63
+ tmp/
64
+ temp/
65
+ logs/
66
+ backups/
67
+ *.db
68
+ *.db-journal
69
+ *.db-shm
70
+ *.db-wal
71
+ *.sqlite
72
+ *.sqlite3
73
+
74
+ # JavaScript and TypeScript
75
+ node_modules/
76
+ .npm/
77
+ .npm-cache/
78
+ .yarn/
79
+ .pnpm-store/
80
+ .vite/
81
+ .next/
82
+ .nuxt/
83
+ .svelte-kit/
84
+ dist/
85
+ build/
86
+ generated/
87
+ coverage/
88
+ *.tsbuildinfo
89
+ playwright-report/
90
+ test-results/
91
+ blob-report/
92
+ src/generated/
93
+ supabase/.temp/
94
+
95
+ # Python
96
+ __pycache__/
97
+ *.py[cod]
98
+ *$py.class
99
+ .venv/
100
+ .venv*/
101
+ venv/
102
+ env/
103
+ ENV/
104
+ .python-version
105
+ .pytest_cache/
106
+ .ruff_cache/
107
+ .mypy_cache/
108
+ .pyre/
109
+ .pytype/
110
+ .hypothesis/
111
+ .tox/
112
+ .nox/
113
+ .coverage
114
+ .coverage.*
115
+ htmlcov/
116
+ *.egg-info/
117
+ .eggs/
118
+ wheels/
119
+ pip-wheel-metadata/
120
+ reports/
121
+ .ipynb_checkpoints/
122
+ **/.ipynb_checkpoints/
123
+
124
+ # Rust, Go, Java and .NET
125
+ **/target/
126
+ **/gen/schemas/
127
+ vendor/
128
+ bin/
129
+ obj/
130
+ *.class
131
+ *.jar
132
+ *.war
133
+ *.nupkg
134
+
135
+ # Swift and Apple build artifacts
136
+ **/.build/
137
+ DerivedData/
138
+ *.xcuserstate
139
+ *.xcworkspace/xcuserdata/
140
+ *.xcodeproj/xcuserdata/
141
+ *.app/
142
+ *.dmg
143
+ **/release/
144
+
145
+ # Windows installers and packaged artifacts
146
+ *.cab
147
+ *.msi
148
+ *.msix
149
+ *.msm
150
+ *.msp
151
+ *.exe
152
+ *.zip
153
+ release-artifacts/
154
+ artifacts/
155
+
156
+ # QA and generated visual output
157
+ qa/
158
+ qa-*.png
159
+ qa-*.jpg
160
+ qa-*.jpeg
161
+ audit-*.png
162
+ prototype-*.png
163
+ gameplay-*.png
164
+ design-qa.md
165
+
166
+ # Project-specific generated resources
167
+ public/ocr/paddleocr/
168
+ src-tauri/generated-resources/ocr/*
169
+ !src-tauri/generated-resources/ocr/.gitkeep
170
+ analysis/outputs/
171
+ analysis/*-artifact.json
172
+ analysis/*.html
173
+ NamiQuant-Claude-Design.html
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 thang
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,103 @@
1
+ Metadata-Version: 2.5
2
+ Name: proofline-evidence
3
+ Version: 2.0.0
4
+ Summary: Evidence-first engineering decision memory
5
+ Project-URL: Homepage, https://github.com/thangldw/proofline
6
+ Project-URL: Repository, https://github.com/thangldw/proofline.git
7
+ Project-URL: Issues, https://github.com/thangldw/proofline/issues
8
+ Project-URL: Changelog, https://github.com/thangldw/proofline/blob/main/CHANGELOG.md
9
+ Author: Thang Luu
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: engineering,knowledge,local-first,provenance,retrieval
13
+ Requires-Python: >=3.11
14
+ Requires-Dist: cryptography<51,>=50
15
+ Requires-Dist: fastapi<1,>=0.115
16
+ Requires-Dist: httpx<1,>=0.27
17
+ Requires-Dist: keyring<26,>=25
18
+ Requires-Dist: pillow<13,>=12
19
+ Requires-Dist: pydantic<3,>=2.9
20
+ Requires-Dist: python-pptx<2,>=1.0
21
+ Requires-Dist: sqlalchemy<3,>=2.0
22
+ Requires-Dist: uvicorn[standard]<1,>=0.32
23
+ Provides-Extra: dev
24
+ Requires-Dist: build<2,>=1.2; extra == 'dev'
25
+ Requires-Dist: hypothesis<7,>=6; extra == 'dev'
26
+ Requires-Dist: jsonschema<5,>=4.23; extra == 'dev'
27
+ Requires-Dist: pip-audit<3,>=2.10.1; extra == 'dev'
28
+ Requires-Dist: pip<27,>=26.2; extra == 'dev'
29
+ Requires-Dist: pyinstaller<7,>=6.21; extra == 'dev'
30
+ Requires-Dist: pytest<10,>=9.1.1; extra == 'dev'
31
+ Requires-Dist: ruff<1,>=0.8; extra == 'dev'
32
+ Requires-Dist: twine<8,>=7; extra == 'dev'
33
+ Description-Content-Type: text/markdown
34
+
35
+ # Proofline
36
+
37
+ [English](#english) · [Tiếng Việt](#tiếng-việt) · [日本語](#日本語)
38
+
39
+ Proofline shows which immutable evidence justified an engineering decision, traces explicit downstream impact when that evidence changes, and signs portable evidence attestations.
40
+
41
+ ```mermaid
42
+ %%{init: {"theme":"base","themeVariables":{"background":"#FFFFFF","fontFamily":"Arial, sans-serif","lineColor":"#667085","primaryTextColor":"#172B4D"}}}%%
43
+ flowchart LR
44
+ S["Source version<br/>Nguồn / ソース"]:::yellow
45
+ P["Exact span<br/>Dòng trích / 引用範囲"]:::blue
46
+ D["Decision<br/>Quyết định / 判断"]:::purple
47
+ C["Deterministic check<br/>Kiểm tra / 検証"]:::green
48
+ R["Review warning<br/>Cảnh báo / 警告"]:::pink
49
+ S --> P --> D --> C
50
+ C -->|changed| R
51
+ classDef yellow fill:#FFF4A3,stroke:#C9A227,stroke-width:2px,color:#172B4D
52
+ classDef blue fill:#D9EAFD,stroke:#4C78A8,stroke-width:2px,color:#172B4D
53
+ classDef purple fill:#E9DDF7,stroke:#8064A2,stroke-width:2px,color:#172B4D
54
+ classDef green fill:#DDF5E3,stroke:#4F9D69,stroke-width:2px,color:#172B4D
55
+ classDef pink fill:#FFE1E6,stroke:#C96A7B,stroke-width:2px,color:#172B4D
56
+ ```
57
+
58
+ ## English
59
+
60
+ Proofline is a local-first Engineering Decision Memory for evidence-backed ADRs. It preserves source identity, source version and exact cited spans, exports self-contained Decision Evidence Packages, and performs deterministic stale-decision checks without requiring an AI provider.
61
+
62
+ Decision status and evidence health are separate: an approved ADR remains historical fact while a stale citation is shown as **Accepted · review required**. Proofline never silently rewrites an accepted decision.
63
+
64
+ Explicit `based_on` and `implements` relations produce cycle-safe transitive impact paths. Ed25519 attestations bind a verified package and optional review receipt to a trusted public key; signing and verification remain local and AI-provider independent.
65
+
66
+ Requirements: Python 3.11+, Node.js 20+ and npm.
67
+
68
+ ```bash
69
+ python -m pip install proofline-evidence
70
+ proofline demo stale-decision
71
+ ```
72
+
73
+ For repository development:
74
+
75
+ ```bash
76
+ make setup
77
+ .venv/bin/proofline demo stale-decision
78
+ .venv/bin/proofline verify-package proofline-demo-stale-decision/evidence.zip
79
+ .venv/bin/proofline verify-review-receipt proofline-demo-stale-decision/decision-review.json
80
+ .venv/bin/proofline check-impacts --format sarif
81
+ make test
82
+ make check
83
+ ```
84
+
85
+ The current boundary is a single-user local workflow with recoverable data. Hosted sync, shared workspaces, organization identity, trusted timestamps, key revocation and permission-aware connector fleets are not implemented.
86
+
87
+ Current technical references: [architecture](docs/architecture.md), [evidence package formats](docs/evidence-packages.md), [operations](docs/OPERATIONS.md), [security](SECURITY.md) and [v2.0.0 release notes](docs/releases/v2.0.0.md).
88
+
89
+ The repository is also packaged as a local plugin for ChatGPT, Codex, Claude Code and Cowork. Proofline is listed in the public [OpenAI Plugins Directory](https://chatgpt.com/plugins/plugins_6a6efdf2ccbc81919ebb4cb01805ebaa); the exact submitted/public version is tracked in the [directory submission package](docs/submission/DIRECTORY_SUBMISSION.md). It does not claim hosted sync or a remote MCP connector. See the [privacy policy](PRIVACY.md), [terms](TERMS.md), and [support guidance](SUPPORT.md).
90
+
91
+ ## Tiếng Việt
92
+
93
+ Proofline là bộ nhớ quyết định kỹ thuật local-first dành cho ADR có bằng chứng. Hệ thống giữ định danh nguồn, phiên bản nguồn và đúng đoạn trích; phát hiện transitive impact qua quan hệ explicit; xuất package độc lập và ký attestation Ed25519 mà không phụ thuộc nhà cung cấp AI.
94
+
95
+ Yêu cầu: Python 3.11+, Node.js 20+ và npm. Dùng các lệnh ở phần English để cài đặt, chạy demo và kiểm thử. Phạm vi hiện tại là quy trình local cho một người dùng; chưa có đồng bộ hosted, workspace dùng chung, định danh tổ chức, trusted timestamp hoặc hệ connector phân quyền.
96
+
97
+ ## 日本語
98
+
99
+ Proofline は、根拠付き ADR のためのローカルファーストな Engineering Decision Memory です。正確な引用範囲を保持し、明示的な関係による推移的影響を追跡し、AI プロバイダーなしで Ed25519 attestation を署名・検証します。
100
+
101
+ 必要環境は Python 3.11 以上、Node.js 20 以上、npm です。現在は単一ユーザーのローカル利用が対象で、ホスト同期、共有ワークスペース、組織 identity、信頼時刻、権限対応コネクター群は未実装です。
102
+
103
+ Released under the [MIT License](LICENSE).
@@ -0,0 +1,69 @@
1
+ # Proofline
2
+
3
+ [English](#english) · [Tiếng Việt](#tiếng-việt) · [日本語](#日本語)
4
+
5
+ Proofline shows which immutable evidence justified an engineering decision, traces explicit downstream impact when that evidence changes, and signs portable evidence attestations.
6
+
7
+ ```mermaid
8
+ %%{init: {"theme":"base","themeVariables":{"background":"#FFFFFF","fontFamily":"Arial, sans-serif","lineColor":"#667085","primaryTextColor":"#172B4D"}}}%%
9
+ flowchart LR
10
+ S["Source version<br/>Nguồn / ソース"]:::yellow
11
+ P["Exact span<br/>Dòng trích / 引用範囲"]:::blue
12
+ D["Decision<br/>Quyết định / 判断"]:::purple
13
+ C["Deterministic check<br/>Kiểm tra / 検証"]:::green
14
+ R["Review warning<br/>Cảnh báo / 警告"]:::pink
15
+ S --> P --> D --> C
16
+ C -->|changed| R
17
+ classDef yellow fill:#FFF4A3,stroke:#C9A227,stroke-width:2px,color:#172B4D
18
+ classDef blue fill:#D9EAFD,stroke:#4C78A8,stroke-width:2px,color:#172B4D
19
+ classDef purple fill:#E9DDF7,stroke:#8064A2,stroke-width:2px,color:#172B4D
20
+ classDef green fill:#DDF5E3,stroke:#4F9D69,stroke-width:2px,color:#172B4D
21
+ classDef pink fill:#FFE1E6,stroke:#C96A7B,stroke-width:2px,color:#172B4D
22
+ ```
23
+
24
+ ## English
25
+
26
+ Proofline is a local-first Engineering Decision Memory for evidence-backed ADRs. It preserves source identity, source version and exact cited spans, exports self-contained Decision Evidence Packages, and performs deterministic stale-decision checks without requiring an AI provider.
27
+
28
+ Decision status and evidence health are separate: an approved ADR remains historical fact while a stale citation is shown as **Accepted · review required**. Proofline never silently rewrites an accepted decision.
29
+
30
+ Explicit `based_on` and `implements` relations produce cycle-safe transitive impact paths. Ed25519 attestations bind a verified package and optional review receipt to a trusted public key; signing and verification remain local and AI-provider independent.
31
+
32
+ Requirements: Python 3.11+, Node.js 20+ and npm.
33
+
34
+ ```bash
35
+ python -m pip install proofline-evidence
36
+ proofline demo stale-decision
37
+ ```
38
+
39
+ For repository development:
40
+
41
+ ```bash
42
+ make setup
43
+ .venv/bin/proofline demo stale-decision
44
+ .venv/bin/proofline verify-package proofline-demo-stale-decision/evidence.zip
45
+ .venv/bin/proofline verify-review-receipt proofline-demo-stale-decision/decision-review.json
46
+ .venv/bin/proofline check-impacts --format sarif
47
+ make test
48
+ make check
49
+ ```
50
+
51
+ The current boundary is a single-user local workflow with recoverable data. Hosted sync, shared workspaces, organization identity, trusted timestamps, key revocation and permission-aware connector fleets are not implemented.
52
+
53
+ Current technical references: [architecture](docs/architecture.md), [evidence package formats](docs/evidence-packages.md), [operations](docs/OPERATIONS.md), [security](SECURITY.md) and [v2.0.0 release notes](docs/releases/v2.0.0.md).
54
+
55
+ The repository is also packaged as a local plugin for ChatGPT, Codex, Claude Code and Cowork. Proofline is listed in the public [OpenAI Plugins Directory](https://chatgpt.com/plugins/plugins_6a6efdf2ccbc81919ebb4cb01805ebaa); the exact submitted/public version is tracked in the [directory submission package](docs/submission/DIRECTORY_SUBMISSION.md). It does not claim hosted sync or a remote MCP connector. See the [privacy policy](PRIVACY.md), [terms](TERMS.md), and [support guidance](SUPPORT.md).
56
+
57
+ ## Tiếng Việt
58
+
59
+ Proofline là bộ nhớ quyết định kỹ thuật local-first dành cho ADR có bằng chứng. Hệ thống giữ định danh nguồn, phiên bản nguồn và đúng đoạn trích; phát hiện transitive impact qua quan hệ explicit; xuất package độc lập và ký attestation Ed25519 mà không phụ thuộc nhà cung cấp AI.
60
+
61
+ Yêu cầu: Python 3.11+, Node.js 20+ và npm. Dùng các lệnh ở phần English để cài đặt, chạy demo và kiểm thử. Phạm vi hiện tại là quy trình local cho một người dùng; chưa có đồng bộ hosted, workspace dùng chung, định danh tổ chức, trusted timestamp hoặc hệ connector phân quyền.
62
+
63
+ ## 日本語
64
+
65
+ Proofline は、根拠付き ADR のためのローカルファーストな Engineering Decision Memory です。正確な引用範囲を保持し、明示的な関係による推移的影響を追跡し、AI プロバイダーなしで Ed25519 attestation を署名・検証します。
66
+
67
+ 必要環境は Python 3.11 以上、Node.js 20 以上、npm です。現在は単一ユーザーのローカル利用が対象で、ホスト同期、共有ワークスペース、組織 identity、信頼時刻、権限対応コネクター群は未実装です。
68
+
69
+ Released under the [MIT License](LICENSE).
@@ -0,0 +1,3 @@
1
+ """Proofline API package."""
2
+
3
+ __version__ = "2.0.0"
@@ -0,0 +1,217 @@
1
+ from __future__ import annotations
2
+
3
+ import hashlib
4
+ import re
5
+ import unicodedata
6
+ from dataclasses import dataclass
7
+ from difflib import SequenceMatcher
8
+ from typing import Literal
9
+
10
+ ANCHOR_VERSION = "markdown-context-v1"
11
+ MIN_CHANGED_SIMILARITY = 0.60
12
+ EMPTY_SHA256 = hashlib.sha256(b"").hexdigest()
13
+
14
+ AnchorState = Literal["current", "unchanged", "moved", "ambiguous", "changed", "deleted"]
15
+
16
+ _HEADING = re.compile(r"^\s{0,3}(#{1,6})[ \t]+(.+?)\s*#*\s*$")
17
+
18
+
19
+ @dataclass(frozen=True)
20
+ class EvidenceAnchor:
21
+ version: str
22
+ section_path: tuple[str, ...]
23
+ prefix_sha256: str
24
+ suffix_sha256: str
25
+
26
+
27
+ @dataclass(frozen=True)
28
+ class AnchorCandidate:
29
+ start_offset: int
30
+ end_offset: int
31
+ start_line: int
32
+ end_line: int
33
+ section_path: tuple[str, ...]
34
+ similarity: float
35
+
36
+
37
+ @dataclass(frozen=True)
38
+ class AnchorResolution:
39
+ state: AnchorState
40
+ candidates: tuple[AnchorCandidate, ...]
41
+
42
+
43
+ @dataclass(frozen=True)
44
+ class _Line:
45
+ text: str
46
+ start: int
47
+ end: int
48
+
49
+
50
+ def _lines(content: str) -> list[_Line]:
51
+ records: list[_Line] = []
52
+ cursor = 0
53
+ for raw in content.splitlines(keepends=True):
54
+ text = raw.rstrip("\r\n")
55
+ records.append(_Line(text=text, start=cursor, end=cursor + len(text)))
56
+ cursor += len(raw)
57
+ if not records or cursor < len(content):
58
+ records.append(_Line(text=content[cursor:], start=cursor, end=len(content)))
59
+ return records
60
+
61
+
62
+ def _normalize(value: str) -> str:
63
+ return " ".join(unicodedata.normalize("NFC", value).strip().split())
64
+
65
+
66
+ def _hash_lines(values: list[str]) -> str:
67
+ return hashlib.sha256("\n".join(values).encode("utf-8")).hexdigest()
68
+
69
+
70
+ def _section_paths(lines: list[_Line]) -> list[tuple[str, ...]]:
71
+ path: list[str] = []
72
+ result: list[tuple[str, ...]] = []
73
+ for line in lines:
74
+ match = _HEADING.match(line.text)
75
+ if match:
76
+ level = len(match.group(1))
77
+ heading = _normalize(match.group(2))
78
+ path = path[: level - 1]
79
+ path.append(heading)
80
+ result.append(tuple(path))
81
+ return result
82
+
83
+
84
+ def _line_index(lines: list[_Line], offset: int) -> int:
85
+ for index, line in enumerate(lines):
86
+ if line.start <= offset <= line.end:
87
+ return index
88
+ return len(lines) - 1
89
+
90
+
91
+ def build_evidence_anchor(content: str, start_offset: int, end_offset: int) -> EvidenceAnchor:
92
+ if not 0 <= start_offset < end_offset <= len(content):
93
+ raise ValueError("anchor_span_invalid")
94
+ lines = _lines(content)
95
+ start_index = _line_index(lines, start_offset)
96
+ end_index = _line_index(lines, end_offset - 1)
97
+ paths = _section_paths(lines)
98
+
99
+ prefix: list[str] = []
100
+ for line in reversed(lines[:start_index]):
101
+ normalized = _normalize(line.text)
102
+ if normalized:
103
+ prefix.append(normalized)
104
+ if len(prefix) == 2:
105
+ break
106
+ prefix.reverse()
107
+
108
+ suffix: list[str] = []
109
+ for line in lines[end_index + 1 :]:
110
+ normalized = _normalize(line.text)
111
+ if normalized:
112
+ suffix.append(normalized)
113
+ if len(suffix) == 2:
114
+ break
115
+
116
+ return EvidenceAnchor(
117
+ version=ANCHOR_VERSION,
118
+ section_path=paths[start_index],
119
+ prefix_sha256=_hash_lines(prefix),
120
+ suffix_sha256=_hash_lines(suffix),
121
+ )
122
+
123
+
124
+ def _candidate(content: str, start: int, end: int, similarity: float) -> AnchorCandidate:
125
+ anchor = build_evidence_anchor(content, start, end)
126
+ return AnchorCandidate(
127
+ start_offset=start,
128
+ end_offset=end,
129
+ start_line=content.count("\n", 0, start) + 1,
130
+ end_line=content.count("\n", 0, end - 1) + 1,
131
+ section_path=anchor.section_path,
132
+ similarity=similarity,
133
+ )
134
+
135
+
136
+ def _exact_occurrences(content: str, quote: str) -> list[int]:
137
+ offsets: list[int] = []
138
+ cursor = 0
139
+ while True:
140
+ found = content.find(quote, cursor)
141
+ if found < 0:
142
+ return offsets
143
+ offsets.append(found)
144
+ cursor = found + 1
145
+
146
+
147
+ def _context_matches(cited: EvidenceAnchor, current: EvidenceAnchor) -> bool:
148
+ return (
149
+ cited.section_path == current.section_path
150
+ and (cited.prefix_sha256 == EMPTY_SHA256 or cited.prefix_sha256 == current.prefix_sha256)
151
+ and (cited.suffix_sha256 == EMPTY_SHA256 or cited.suffix_sha256 == current.suffix_sha256)
152
+ )
153
+
154
+
155
+ def _changed_candidates(
156
+ *, quote: str, cited_anchor: EvidenceAnchor, current_content: str
157
+ ) -> tuple[AnchorCandidate, ...]:
158
+ lines = _lines(current_content)
159
+ paths = _section_paths(lines)
160
+ quote_line_count = max(1, len(quote.splitlines()))
161
+ normalized_quote = _normalize(quote)
162
+ candidates: list[AnchorCandidate] = []
163
+ terminal_heading = cited_anchor.section_path[-1:] or ()
164
+
165
+ for index in range(0, len(lines) - quote_line_count + 1):
166
+ window = lines[index : index + quote_line_count]
167
+ if terminal_heading and paths[index][-1:] != terminal_heading:
168
+ continue
169
+ start = window[0].start
170
+ end = window[-1].end
171
+ if end <= start:
172
+ continue
173
+ similarity = SequenceMatcher(
174
+ None,
175
+ normalized_quote,
176
+ _normalize(current_content[start:end]),
177
+ autojunk=False,
178
+ ).ratio()
179
+ if similarity < MIN_CHANGED_SIMILARITY:
180
+ continue
181
+ candidates.append(_candidate(current_content, start, end, similarity))
182
+
183
+ return tuple(sorted(candidates, key=lambda item: (-item.similarity, item.start_offset)))
184
+
185
+
186
+ def resolve_evidence_anchor(
187
+ *, quote: str, cited_anchor: EvidenceAnchor, current_content: str
188
+ ) -> AnchorResolution:
189
+ if not quote:
190
+ raise ValueError("anchor_quote_invalid")
191
+ if cited_anchor.version != ANCHOR_VERSION:
192
+ raise ValueError("anchor_version_unsupported")
193
+
194
+ occurrences = _exact_occurrences(current_content, quote)
195
+ exact = tuple(
196
+ _candidate(current_content, start, start + len(quote), 1.0) for start in occurrences
197
+ )
198
+ if len(exact) > 1:
199
+ return AnchorResolution(state="ambiguous", candidates=exact)
200
+ if len(exact) == 1:
201
+ current_anchor = build_evidence_anchor(
202
+ current_content, exact[0].start_offset, exact[0].end_offset
203
+ )
204
+ state: AnchorState = (
205
+ "unchanged" if _context_matches(cited_anchor, current_anchor) else "moved"
206
+ )
207
+ return AnchorResolution(state=state, candidates=exact)
208
+
209
+ candidates = _changed_candidates(
210
+ quote=quote,
211
+ cited_anchor=cited_anchor,
212
+ current_content=current_content,
213
+ )
214
+ return AnchorResolution(
215
+ state="changed" if candidates else "deleted",
216
+ candidates=candidates,
217
+ )