harbor-spec 1.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.
- harbor_spec-1.0.0/LICENSE +21 -0
- harbor_spec-1.0.0/PKG-INFO +211 -0
- harbor_spec-1.0.0/README.md +175 -0
- harbor_spec-1.0.0/harbor/__init__.py +2 -0
- harbor_spec-1.0.0/harbor/adapters/__init__.py +2 -0
- harbor_spec-1.0.0/harbor/adapters/python/__init__.py +2 -0
- harbor_spec-1.0.0/harbor/adapters/python/parser.py +269 -0
- harbor_spec-1.0.0/harbor/cli/__init__.py +2 -0
- harbor_spec-1.0.0/harbor/cli/main.py +216 -0
- harbor_spec-1.0.0/harbor/core/__init__.py +2 -0
- harbor_spec-1.0.0/harbor/core/audit.py +133 -0
- harbor_spec-1.0.0/harbor/core/ddt.py +172 -0
- harbor_spec-1.0.0/harbor/core/diary.py +211 -0
- harbor_spec-1.0.0/harbor/core/index.py +191 -0
- harbor_spec-1.0.0/harbor/core/l2.py +170 -0
- harbor_spec-1.0.0/harbor/core/sync.py +173 -0
- harbor_spec-1.0.0/harbor/core/utils.py +56 -0
- harbor_spec-1.0.0/harbor/test_utils.py +11 -0
- harbor_spec-1.0.0/harbor/utils/__init__.py +2 -0
- harbor_spec-1.0.0/harbor/utils/formatting.py +36 -0
- harbor_spec-1.0.0/harbor_spec.egg-info/PKG-INFO +211 -0
- harbor_spec-1.0.0/harbor_spec.egg-info/SOURCES.txt +32 -0
- harbor_spec-1.0.0/harbor_spec.egg-info/dependency_links.txt +1 -0
- harbor_spec-1.0.0/harbor_spec.egg-info/entry_points.txt +2 -0
- harbor_spec-1.0.0/harbor_spec.egg-info/requires.txt +6 -0
- harbor_spec-1.0.0/harbor_spec.egg-info/top_level.txt +1 -0
- harbor_spec-1.0.0/pyproject.toml +27 -0
- harbor_spec-1.0.0/setup.cfg +4 -0
- harbor_spec-1.0.0/tests/test_adapter_basic.py +45 -0
- harbor_spec-1.0.0/tests/test_audit.py +43 -0
- harbor_spec-1.0.0/tests/test_ddt_validate.py +61 -0
- harbor_spec-1.0.0/tests/test_index_builder.py +99 -0
- harbor_spec-1.0.0/tests/test_sync_engine.py +68 -0
- harbor_spec-1.0.0/tests/test_utils_format.py +28 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 李健
|
|
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,211 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: harbor-spec
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Harbor-spec v1.0.2 reference implementation (Python-only)
|
|
5
|
+
License: MIT License
|
|
6
|
+
|
|
7
|
+
Copyright (c) 2025 李健
|
|
8
|
+
|
|
9
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
10
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
11
|
+
in the Software without restriction, including without limitation the rights
|
|
12
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
13
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
14
|
+
furnished to do so, subject to the following conditions:
|
|
15
|
+
|
|
16
|
+
The above copyright notice and this permission notice shall be included in all
|
|
17
|
+
copies or substantial portions of the Software.
|
|
18
|
+
|
|
19
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
20
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
21
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
22
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
23
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
24
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
25
|
+
SOFTWARE.
|
|
26
|
+
|
|
27
|
+
Requires-Python: >=3.9
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
License-File: LICENSE
|
|
30
|
+
Requires-Dist: pyyaml
|
|
31
|
+
Requires-Dist: openai>=1.0.0
|
|
32
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
33
|
+
Provides-Extra: dev
|
|
34
|
+
Requires-Dist: pytest; extra == "dev"
|
|
35
|
+
Dynamic: license-file
|
|
36
|
+
|
|
37
|
+
<div align="center">
|
|
38
|
+
|
|
39
|
+
# ⚓ Harbor
|
|
40
|
+
### The Context Governance Engine for Vibe Coding
|
|
41
|
+
|
|
42
|
+
[](https://github.com/your-org/harbor-spec/actions)
|
|
43
|
+
[](https://www.python.org/)
|
|
44
|
+
[](LICENSE)
|
|
45
|
+
[](https://github.com/your-org/harbor-spec)
|
|
46
|
+
|
|
47
|
+
**让 AI 像代码一样被管理,让上下文像 Git 一样可追溯。**
|
|
48
|
+
|
|
49
|
+
[理念 (Philosophy)] • [架构 (Architecture)] • [快速开始 (Quick Start)] • [工作流 (Workflow)]
|
|
50
|
+
|
|
51
|
+
</div>
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## 🌌 The Era of Vibe Coding
|
|
56
|
+
|
|
57
|
+
编程正在经历一场范式转移。我们正在从 "Writing Code"(逐行编写)转向 **"Vibe Coding"**(通过自然语言与 AI 协作生成)。
|
|
58
|
+
|
|
59
|
+
在这个新时代,**代码生成的边际成本趋近于零,但上下文维护的成本却在指数级上升。**
|
|
60
|
+
- AI 改了代码,Docstring 还没改?👉 **Context Drift (上下文漂移)**
|
|
61
|
+
- 测试用例还在测旧版本的逻辑?👉 **Validation Gap (验证断层)**
|
|
62
|
+
- 为什么上周我们要把这个参数改成 Optional?👉 **Memory Loss (决策遗忘)**
|
|
63
|
+
|
|
64
|
+
**Harbor** 应运而生。它不是另一个 Copilot,它是 **Copilot 的监管者**。它是一套用于治理 AI 生成代码的 **"良知" (Conscience)** 与 **"记忆" (Memory)** 系统。它是“程序员到上下文工程师”这一革命性转变的关键工具,它的目标是成为vibe coding时代的Git。
|
|
65
|
+
|
|
66
|
+
## 🛡️ Core Philosophy
|
|
67
|
+
|
|
68
|
+
Harbor 的核心设计理念基于 **L3 Contract Theory**:
|
|
69
|
+
|
|
70
|
+
1. **Code is Volatile, Contract is Immutable**: 实现代码可以由 AI 随意重写,但 L3 级 Docstring(契约)是锚点,必须由人类或高级审计确认。
|
|
71
|
+
2. **Noise is Signal**: 未经索引的代码、未同步的文档、未绑定的测试,都是系统中的“噪音”。Harbor 不会静默处理,而是将其显性化。
|
|
72
|
+
3. **Trust, but Verify**: 我们信任 AI 的编码能力,但必须通过 AST 静态分析和 LLM 语义审计来验证其产出。
|
|
73
|
+
|
|
74
|
+
## 🏗️ Architecture
|
|
75
|
+
|
|
76
|
+
Harbor 通过六大核心子系统构建了一个闭环的治理体系:
|
|
77
|
+
|
|
78
|
+
```mermaid
|
|
79
|
+
graph TD
|
|
80
|
+
Source[Source Code] -->|AST Parse| Adapter(Adapter)
|
|
81
|
+
Adapter -->|Contract Hash| Index(L3 Index / Memory)
|
|
82
|
+
|
|
83
|
+
Index -->|Compare| Sync(Sync Engine)
|
|
84
|
+
Source -->|Body Hash| Sync
|
|
85
|
+
|
|
86
|
+
Sync -->|Drift Detected| Status[CLI Status]
|
|
87
|
+
Sync -->|Diff Target| Audit(Semantic Guard)
|
|
88
|
+
|
|
89
|
+
Env[.env / LLM] --> Audit
|
|
90
|
+
Audit -->|Semantic Check| Report[Audit Report]
|
|
91
|
+
|
|
92
|
+
Tests[Test Cases] -->|DDT Binding| Validator(DDT Validator)
|
|
93
|
+
Index -->|Version Match| Validator
|
|
94
|
+
|
|
95
|
+
Index -->|Aggregation| L2(L2 Generator)
|
|
96
|
+
Validator -->|Status| L2
|
|
97
|
+
|
|
98
|
+
User[Developer] -->|Log Decision| Diary(Diary / History)
|
|
99
|
+
````
|
|
100
|
+
|
|
101
|
+
- **🧠 Index (Memory)**: 这里的 `.harbor/cache` 是大脑,记录了代码的每一次“快照”与指纹。
|
|
102
|
+
- **⚖️ Sync (Conscience)**: 实时监测 "Implementation Drift"(代码变了,但契约没变)。
|
|
103
|
+
- **🌉 DDT (Bridge)**: **D**ecorator-driven **D**ata **T**esting。将测试用例与 L3 版本强绑定,拒绝“假绿灯”。
|
|
104
|
+
- **🤖 Audit (Guard)**: 集成 DeepSeek/OpenAI,对代码进行语义级审计,揪出逻辑与文档的违背之处。
|
|
105
|
+
- **📊 L2 (Dashboard)**: 自动生成 Markdown 视图,诚实地展示模块的测试覆盖率与契约状态。
|
|
106
|
+
- **📜 Diary (History)**: 结构化的决策日志,记录每一次变革背后的 "Why"。
|
|
107
|
+
|
|
108
|
+
## ⚡ Quick Start
|
|
109
|
+
|
|
110
|
+
### 1\. Installation
|
|
111
|
+
|
|
112
|
+
Harbor 是一个纯 Python 工具,推荐在开发模式下安装:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
git clone [https://github.com/your-org/harbor-spec.git](https://github.com/your-org/harbor-spec.git)
|
|
116
|
+
cd harbor-spec
|
|
117
|
+
pip install -e .
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
可选pypi安装:
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
pip install harbor-spec
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### 2\. Configuration
|
|
127
|
+
|
|
128
|
+
配置 LLM 以启用 AI 语义审计(支持 Ernie, DeepSeek, OpenAI 等兼容接口):
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
cp .env.example .env
|
|
132
|
+
# 编辑 .env 文件:
|
|
133
|
+
# HARBOR_LLM_PROVIDER=openai
|
|
134
|
+
# HARBOR_LLM_API_KEY=
|
|
135
|
+
# HARBOR_LLM_BASE_URL=https://api.openai.com/v1
|
|
136
|
+
# HARBOR_LANGUAGE=zh (可选,开启中文审计)
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### 3\. Initialize
|
|
140
|
+
|
|
141
|
+
构建初始索引,接管当前代码库:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
harbor build-index
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## 🎮 Workflow: A Day with Harbor
|
|
148
|
+
|
|
149
|
+
### 1\. Check Status
|
|
150
|
+
|
|
151
|
+
开始工作前,看看代码库是否干净。
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
harbor status
|
|
155
|
+
# 输出: No changes detected. (Or list of drifts)
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### 2\. Vibe Coding
|
|
159
|
+
|
|
160
|
+
使用你喜欢的 AI 助手(Cursor, Windsurf, Copilot)修改代码。
|
|
161
|
+
*假设你修改了 `harbor/utils.py` 的逻辑,但忘记更新 Docstring。*
|
|
162
|
+
|
|
163
|
+
### 3\. Detect Drift
|
|
164
|
+
|
|
165
|
+
Harbor 会发现你的代码实现了“偷跑”。
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
harbor status
|
|
169
|
+
# 输出: M harbor.utils.func (Body changed, Contract static)
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### 4\. AI Audit
|
|
173
|
+
|
|
174
|
+
让 Harbor 的 AI 审计员检查你的修改是否违背了契约。
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
harbor audit --semantic
|
|
178
|
+
# 输出: POSSIBLE_SEMANTIC_DRIFT ... [MISMATCH]: 代码抛出了 ValueError 但文档中未声明...
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### 5\. Lock & Record
|
|
182
|
+
|
|
183
|
+
修复问题后,更新索引并记录决策日志。
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
harbor build-index
|
|
187
|
+
harbor diary log --summary "Refactor utils validation logic" --type refactor --importance high
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## 🧩 Commands Cheatsheet
|
|
191
|
+
|
|
192
|
+
| Command | Description |
|
|
193
|
+
| :--- | :--- |
|
|
194
|
+
| `harbor status` | 检查代码与索引的差异(Drift检测) |
|
|
195
|
+
| `harbor build-index` | 更新 L3 索引缓存 (类似 `git commit`) |
|
|
196
|
+
| `harbor audit --semantic` | 调用 LLM 进行语义一致性检查 |
|
|
197
|
+
| `harbor ddt validate` | 验证测试用例与代码版本的绑定关系 |
|
|
198
|
+
| `harbor gen l2` | 自动生成模块级的 README 文档 |
|
|
199
|
+
| `harbor diary log` | 写入结构化的决策日志 |
|
|
200
|
+
|
|
201
|
+
## 🤝 Contribution
|
|
202
|
+
|
|
203
|
+
Harbor 遵循 **Strict L3** 开发规范。
|
|
204
|
+
|
|
205
|
+
- 所有 Public API 必须包含完整的 Google-style Docstring。
|
|
206
|
+
- 所有新增功能必须包含 DDT 测试绑定。
|
|
207
|
+
- 提交前请运行 `harbor audit --semantic` 自测。
|
|
208
|
+
|
|
209
|
+
## 📄 License
|
|
210
|
+
|
|
211
|
+
MIT © 2025 Harbor-spec Authors.
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# ⚓ Harbor
|
|
4
|
+
### The Context Governance Engine for Vibe Coding
|
|
5
|
+
|
|
6
|
+
[](https://github.com/your-org/harbor-spec/actions)
|
|
7
|
+
[](https://www.python.org/)
|
|
8
|
+
[](LICENSE)
|
|
9
|
+
[](https://github.com/your-org/harbor-spec)
|
|
10
|
+
|
|
11
|
+
**让 AI 像代码一样被管理,让上下文像 Git 一样可追溯。**
|
|
12
|
+
|
|
13
|
+
[理念 (Philosophy)] • [架构 (Architecture)] • [快速开始 (Quick Start)] • [工作流 (Workflow)]
|
|
14
|
+
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## 🌌 The Era of Vibe Coding
|
|
20
|
+
|
|
21
|
+
编程正在经历一场范式转移。我们正在从 "Writing Code"(逐行编写)转向 **"Vibe Coding"**(通过自然语言与 AI 协作生成)。
|
|
22
|
+
|
|
23
|
+
在这个新时代,**代码生成的边际成本趋近于零,但上下文维护的成本却在指数级上升。**
|
|
24
|
+
- AI 改了代码,Docstring 还没改?👉 **Context Drift (上下文漂移)**
|
|
25
|
+
- 测试用例还在测旧版本的逻辑?👉 **Validation Gap (验证断层)**
|
|
26
|
+
- 为什么上周我们要把这个参数改成 Optional?👉 **Memory Loss (决策遗忘)**
|
|
27
|
+
|
|
28
|
+
**Harbor** 应运而生。它不是另一个 Copilot,它是 **Copilot 的监管者**。它是一套用于治理 AI 生成代码的 **"良知" (Conscience)** 与 **"记忆" (Memory)** 系统。它是“程序员到上下文工程师”这一革命性转变的关键工具,它的目标是成为vibe coding时代的Git。
|
|
29
|
+
|
|
30
|
+
## 🛡️ Core Philosophy
|
|
31
|
+
|
|
32
|
+
Harbor 的核心设计理念基于 **L3 Contract Theory**:
|
|
33
|
+
|
|
34
|
+
1. **Code is Volatile, Contract is Immutable**: 实现代码可以由 AI 随意重写,但 L3 级 Docstring(契约)是锚点,必须由人类或高级审计确认。
|
|
35
|
+
2. **Noise is Signal**: 未经索引的代码、未同步的文档、未绑定的测试,都是系统中的“噪音”。Harbor 不会静默处理,而是将其显性化。
|
|
36
|
+
3. **Trust, but Verify**: 我们信任 AI 的编码能力,但必须通过 AST 静态分析和 LLM 语义审计来验证其产出。
|
|
37
|
+
|
|
38
|
+
## 🏗️ Architecture
|
|
39
|
+
|
|
40
|
+
Harbor 通过六大核心子系统构建了一个闭环的治理体系:
|
|
41
|
+
|
|
42
|
+
```mermaid
|
|
43
|
+
graph TD
|
|
44
|
+
Source[Source Code] -->|AST Parse| Adapter(Adapter)
|
|
45
|
+
Adapter -->|Contract Hash| Index(L3 Index / Memory)
|
|
46
|
+
|
|
47
|
+
Index -->|Compare| Sync(Sync Engine)
|
|
48
|
+
Source -->|Body Hash| Sync
|
|
49
|
+
|
|
50
|
+
Sync -->|Drift Detected| Status[CLI Status]
|
|
51
|
+
Sync -->|Diff Target| Audit(Semantic Guard)
|
|
52
|
+
|
|
53
|
+
Env[.env / LLM] --> Audit
|
|
54
|
+
Audit -->|Semantic Check| Report[Audit Report]
|
|
55
|
+
|
|
56
|
+
Tests[Test Cases] -->|DDT Binding| Validator(DDT Validator)
|
|
57
|
+
Index -->|Version Match| Validator
|
|
58
|
+
|
|
59
|
+
Index -->|Aggregation| L2(L2 Generator)
|
|
60
|
+
Validator -->|Status| L2
|
|
61
|
+
|
|
62
|
+
User[Developer] -->|Log Decision| Diary(Diary / History)
|
|
63
|
+
````
|
|
64
|
+
|
|
65
|
+
- **🧠 Index (Memory)**: 这里的 `.harbor/cache` 是大脑,记录了代码的每一次“快照”与指纹。
|
|
66
|
+
- **⚖️ Sync (Conscience)**: 实时监测 "Implementation Drift"(代码变了,但契约没变)。
|
|
67
|
+
- **🌉 DDT (Bridge)**: **D**ecorator-driven **D**ata **T**esting。将测试用例与 L3 版本强绑定,拒绝“假绿灯”。
|
|
68
|
+
- **🤖 Audit (Guard)**: 集成 DeepSeek/OpenAI,对代码进行语义级审计,揪出逻辑与文档的违背之处。
|
|
69
|
+
- **📊 L2 (Dashboard)**: 自动生成 Markdown 视图,诚实地展示模块的测试覆盖率与契约状态。
|
|
70
|
+
- **📜 Diary (History)**: 结构化的决策日志,记录每一次变革背后的 "Why"。
|
|
71
|
+
|
|
72
|
+
## ⚡ Quick Start
|
|
73
|
+
|
|
74
|
+
### 1\. Installation
|
|
75
|
+
|
|
76
|
+
Harbor 是一个纯 Python 工具,推荐在开发模式下安装:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
git clone [https://github.com/your-org/harbor-spec.git](https://github.com/your-org/harbor-spec.git)
|
|
80
|
+
cd harbor-spec
|
|
81
|
+
pip install -e .
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
可选pypi安装:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
pip install harbor-spec
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### 2\. Configuration
|
|
91
|
+
|
|
92
|
+
配置 LLM 以启用 AI 语义审计(支持 Ernie, DeepSeek, OpenAI 等兼容接口):
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
cp .env.example .env
|
|
96
|
+
# 编辑 .env 文件:
|
|
97
|
+
# HARBOR_LLM_PROVIDER=openai
|
|
98
|
+
# HARBOR_LLM_API_KEY=
|
|
99
|
+
# HARBOR_LLM_BASE_URL=https://api.openai.com/v1
|
|
100
|
+
# HARBOR_LANGUAGE=zh (可选,开启中文审计)
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### 3\. Initialize
|
|
104
|
+
|
|
105
|
+
构建初始索引,接管当前代码库:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
harbor build-index
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## 🎮 Workflow: A Day with Harbor
|
|
112
|
+
|
|
113
|
+
### 1\. Check Status
|
|
114
|
+
|
|
115
|
+
开始工作前,看看代码库是否干净。
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
harbor status
|
|
119
|
+
# 输出: No changes detected. (Or list of drifts)
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### 2\. Vibe Coding
|
|
123
|
+
|
|
124
|
+
使用你喜欢的 AI 助手(Cursor, Windsurf, Copilot)修改代码。
|
|
125
|
+
*假设你修改了 `harbor/utils.py` 的逻辑,但忘记更新 Docstring。*
|
|
126
|
+
|
|
127
|
+
### 3\. Detect Drift
|
|
128
|
+
|
|
129
|
+
Harbor 会发现你的代码实现了“偷跑”。
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
harbor status
|
|
133
|
+
# 输出: M harbor.utils.func (Body changed, Contract static)
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### 4\. AI Audit
|
|
137
|
+
|
|
138
|
+
让 Harbor 的 AI 审计员检查你的修改是否违背了契约。
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
harbor audit --semantic
|
|
142
|
+
# 输出: POSSIBLE_SEMANTIC_DRIFT ... [MISMATCH]: 代码抛出了 ValueError 但文档中未声明...
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### 5\. Lock & Record
|
|
146
|
+
|
|
147
|
+
修复问题后,更新索引并记录决策日志。
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
harbor build-index
|
|
151
|
+
harbor diary log --summary "Refactor utils validation logic" --type refactor --importance high
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## 🧩 Commands Cheatsheet
|
|
155
|
+
|
|
156
|
+
| Command | Description |
|
|
157
|
+
| :--- | :--- |
|
|
158
|
+
| `harbor status` | 检查代码与索引的差异(Drift检测) |
|
|
159
|
+
| `harbor build-index` | 更新 L3 索引缓存 (类似 `git commit`) |
|
|
160
|
+
| `harbor audit --semantic` | 调用 LLM 进行语义一致性检查 |
|
|
161
|
+
| `harbor ddt validate` | 验证测试用例与代码版本的绑定关系 |
|
|
162
|
+
| `harbor gen l2` | 自动生成模块级的 README 文档 |
|
|
163
|
+
| `harbor diary log` | 写入结构化的决策日志 |
|
|
164
|
+
|
|
165
|
+
## 🤝 Contribution
|
|
166
|
+
|
|
167
|
+
Harbor 遵循 **Strict L3** 开发规范。
|
|
168
|
+
|
|
169
|
+
- 所有 Public API 必须包含完整的 Google-style Docstring。
|
|
170
|
+
- 所有新增功能必须包含 DDT 测试绑定。
|
|
171
|
+
- 提交前请运行 `harbor audit --semantic` 自测。
|
|
172
|
+
|
|
173
|
+
## 📄 License
|
|
174
|
+
|
|
175
|
+
MIT © 2025 Harbor-spec Authors.
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import ast
|
|
4
|
+
import hashlib
|
|
5
|
+
import os
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import List, Optional, Tuple, Union, Literal
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass
|
|
12
|
+
class FunctionContract:
|
|
13
|
+
id: str
|
|
14
|
+
name: str
|
|
15
|
+
qualified_name: str
|
|
16
|
+
signature_hash: str
|
|
17
|
+
docstring: Optional[str]
|
|
18
|
+
docstring_raw_hash: Optional[str]
|
|
19
|
+
contract_hash: Optional[str]
|
|
20
|
+
lineno: int
|
|
21
|
+
col_offset: int
|
|
22
|
+
scope: Optional[Literal["public", "internal"]] = None
|
|
23
|
+
strictness: Optional[Literal["strict", "standard", "light"]] = None
|
|
24
|
+
is_method: bool = False
|
|
25
|
+
parent_class: Optional[str] = None
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class PythonAdapter:
|
|
29
|
+
def parse_file(self, file_path: str) -> List[FunctionContract]:
|
|
30
|
+
"""解析并提取指定 Python 文件中的函数/方法契约元数据。
|
|
31
|
+
|
|
32
|
+
功能:
|
|
33
|
+
- 读取并解析 Python 源文件的 AST。
|
|
34
|
+
- 提取所有顶层函数与类方法的名称、签名哈希与 Docstring 双哈希。
|
|
35
|
+
- 识别 Docstring 中的 `@harbor.*` 标签与契约区 (Args/Returns/Raises) 文本。
|
|
36
|
+
|
|
37
|
+
使用场景:
|
|
38
|
+
- Harbor 索引构建(Phase 1)基座;为 `build-index` 提供 L3 解析能力。
|
|
39
|
+
- `harbor sync l3 --check` 的前置分析。
|
|
40
|
+
|
|
41
|
+
依赖:
|
|
42
|
+
- Python 标准库 `ast`、`hashlib`。
|
|
43
|
+
|
|
44
|
+
@harbor.scope: public
|
|
45
|
+
@harbor.l3_strictness: strict
|
|
46
|
+
@harbor.idempotency: read-only
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
file_path (str): 需要解析的 Python 源文件路径。
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
List[FunctionContract]: 函数/方法契约元数据列表。
|
|
53
|
+
|
|
54
|
+
Raises:
|
|
55
|
+
IOError: 当文件读取失败。
|
|
56
|
+
SyntaxError: 当源文件存在语法错误,无法解析为 AST。
|
|
57
|
+
"""
|
|
58
|
+
p = Path(file_path)
|
|
59
|
+
if not p.exists():
|
|
60
|
+
raise IOError(f"file not found: {file_path}")
|
|
61
|
+
try:
|
|
62
|
+
source = p.read_text(encoding="utf-8")
|
|
63
|
+
except Exception as e:
|
|
64
|
+
raise IOError(str(e))
|
|
65
|
+
try:
|
|
66
|
+
tree = ast.parse(source)
|
|
67
|
+
except SyntaxError:
|
|
68
|
+
raise
|
|
69
|
+
module_qual = self._module_qual_from_path(p)
|
|
70
|
+
return self._extract_functions(tree, module_qual)
|
|
71
|
+
|
|
72
|
+
def _extract_functions(self, tree: ast.AST, module_qual: str) -> List[FunctionContract]:
|
|
73
|
+
"""提取顶层函数与类方法的契约元数据。
|
|
74
|
+
|
|
75
|
+
@harbor.scope: internal
|
|
76
|
+
@harbor.l3_strictness: standard
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
tree (ast.AST): 已解析的抽象语法树。
|
|
80
|
+
module_qual (str): 模块的点分限定名。
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
List[FunctionContract]: 契约元数据列表。
|
|
84
|
+
"""
|
|
85
|
+
items: List[FunctionContract] = []
|
|
86
|
+
for node in getattr(tree, "body", []):
|
|
87
|
+
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
|
|
88
|
+
items.append(self._contract_from_function(node, module_qual, None))
|
|
89
|
+
elif isinstance(node, ast.ClassDef):
|
|
90
|
+
for sub in node.body:
|
|
91
|
+
if isinstance(sub, (ast.FunctionDef, ast.AsyncFunctionDef)):
|
|
92
|
+
items.append(self._contract_from_function(sub, module_qual, node.name))
|
|
93
|
+
return items
|
|
94
|
+
|
|
95
|
+
def _contract_from_function(
|
|
96
|
+
self,
|
|
97
|
+
fn: Union[ast.FunctionDef, ast.AsyncFunctionDef],
|
|
98
|
+
module_qual: str,
|
|
99
|
+
parent_class: Optional[str],
|
|
100
|
+
) -> FunctionContract:
|
|
101
|
+
"""根据函数节点生成契约元数据。
|
|
102
|
+
|
|
103
|
+
@harbor.scope: internal
|
|
104
|
+
@harbor.l3_strictness: standard
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
fn: 函数或方法的 AST 节点。
|
|
108
|
+
module_qual: 模块限定名。
|
|
109
|
+
parent_class: 若为方法,则为父类名,否则为 None。
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
FunctionContract: 契约元数据。
|
|
113
|
+
"""
|
|
114
|
+
name = fn.name
|
|
115
|
+
is_method = parent_class is not None
|
|
116
|
+
qualified_name = (
|
|
117
|
+
f"{module_qual}.{parent_class}.{name}" if is_method else f"{module_qual}.{name}"
|
|
118
|
+
)
|
|
119
|
+
doc = ast.get_docstring(fn)
|
|
120
|
+
raw_hash, contract_hash = self._docstring_hashes(doc)
|
|
121
|
+
scope, strictness = self._parse_tags(doc) if doc else (None, None)
|
|
122
|
+
return FunctionContract(
|
|
123
|
+
id=qualified_name,
|
|
124
|
+
name=name,
|
|
125
|
+
qualified_name=qualified_name,
|
|
126
|
+
signature_hash=self._signature_hash(fn),
|
|
127
|
+
docstring=doc,
|
|
128
|
+
docstring_raw_hash=raw_hash,
|
|
129
|
+
contract_hash=contract_hash,
|
|
130
|
+
lineno=getattr(fn, "lineno", 0),
|
|
131
|
+
col_offset=getattr(fn, "col_offset", 0),
|
|
132
|
+
scope=scope,
|
|
133
|
+
strictness=strictness,
|
|
134
|
+
is_method=is_method,
|
|
135
|
+
parent_class=parent_class,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
def _signature_hash(self, fn: Union[ast.FunctionDef, ast.AsyncFunctionDef]) -> str:
|
|
139
|
+
"""计算函数签名的稳定哈希。
|
|
140
|
+
|
|
141
|
+
@harbor.scope: internal
|
|
142
|
+
@harbor.l3_strictness: standard
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
fn: 函数或方法的 AST 节点。
|
|
146
|
+
|
|
147
|
+
Returns:
|
|
148
|
+
str: 签名的 sha256 哈希。
|
|
149
|
+
"""
|
|
150
|
+
a = fn.args
|
|
151
|
+
parts = [
|
|
152
|
+
"posonly:" + ",".join([x.arg for x in getattr(a, "posonlyargs", [])]),
|
|
153
|
+
"args:" + ",".join([x.arg for x in a.args]),
|
|
154
|
+
"vararg:" + (a.vararg.arg if a.vararg else ""),
|
|
155
|
+
"kwonly:" + ",".join([x.arg for x in a.kwonlyargs]),
|
|
156
|
+
"kwarg:" + (a.kwarg.arg if a.kwarg else ""),
|
|
157
|
+
"defaults:" + str(len(a.defaults)) + "|" + str(len(a.kw_defaults)),
|
|
158
|
+
]
|
|
159
|
+
norm = "|".join(parts)
|
|
160
|
+
return hashlib.sha256(norm.encode("utf-8")).hexdigest()
|
|
161
|
+
|
|
162
|
+
def _docstring_hashes(self, doc: Optional[str]) -> Tuple[Optional[str], Optional[str]]:
|
|
163
|
+
"""计算 Docstring 的 raw/contract 双哈希。
|
|
164
|
+
|
|
165
|
+
@harbor.scope: internal
|
|
166
|
+
@harbor.l3_strictness: standard
|
|
167
|
+
|
|
168
|
+
Args:
|
|
169
|
+
doc: Docstring 文本或 None。
|
|
170
|
+
|
|
171
|
+
Returns:
|
|
172
|
+
Tuple[str | None, str | None]: (raw_hash, contract_hash)。
|
|
173
|
+
"""
|
|
174
|
+
if not doc:
|
|
175
|
+
return None, None
|
|
176
|
+
text = doc.replace("\r\n", "\n").strip()
|
|
177
|
+
raw = hashlib.sha256(text.encode("utf-8")).hexdigest()
|
|
178
|
+
contract_text = self._contract_area(text)
|
|
179
|
+
if not contract_text:
|
|
180
|
+
return raw, raw
|
|
181
|
+
contract = hashlib.sha256(contract_text.encode("utf-8")).hexdigest()
|
|
182
|
+
return raw, contract
|
|
183
|
+
|
|
184
|
+
def _contract_area(self, doc: str) -> str:
|
|
185
|
+
"""提取契约区文本(Args/Returns/Raises + @harbor.* tags)。找不到则返回空串。
|
|
186
|
+
|
|
187
|
+
@harbor.scope: internal
|
|
188
|
+
@harbor.l3_strictness: standard
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
doc: 完整 Docstring。
|
|
192
|
+
|
|
193
|
+
Returns:
|
|
194
|
+
str: 契约区文本。
|
|
195
|
+
"""
|
|
196
|
+
lines = doc.split("\n")
|
|
197
|
+
captured: List[str] = []
|
|
198
|
+
capturing = False
|
|
199
|
+
target_headers = {"Args:", "Returns:", "Raises:"}
|
|
200
|
+
for i, line in enumerate(lines):
|
|
201
|
+
s = line.strip()
|
|
202
|
+
if s.startswith("@harbor."):
|
|
203
|
+
captured.append(s)
|
|
204
|
+
continue
|
|
205
|
+
if s in target_headers:
|
|
206
|
+
capturing = True
|
|
207
|
+
captured.append(s)
|
|
208
|
+
continue
|
|
209
|
+
if capturing:
|
|
210
|
+
if s == "":
|
|
211
|
+
capturing = False
|
|
212
|
+
continue
|
|
213
|
+
if line.startswith(" ") or line.startswith("\t"):
|
|
214
|
+
captured.append(line.rstrip())
|
|
215
|
+
else:
|
|
216
|
+
capturing = False
|
|
217
|
+
return "\n".join([x for x in captured if x.strip()]).strip()
|
|
218
|
+
|
|
219
|
+
def _parse_tags(
|
|
220
|
+
self, doc: str
|
|
221
|
+
) -> Tuple[Optional[Literal["public", "internal"]], Optional[Literal["strict", "standard", "light"]]]:
|
|
222
|
+
"""从 Docstring 提取 @harbor.* 标签。
|
|
223
|
+
|
|
224
|
+
@harbor.scope: internal
|
|
225
|
+
@harbor.l3_strictness: standard
|
|
226
|
+
|
|
227
|
+
Args:
|
|
228
|
+
doc: 完整 Docstring。
|
|
229
|
+
|
|
230
|
+
Returns:
|
|
231
|
+
Tuple[scope, strictness]: 若未找到则返回 (None, None)。
|
|
232
|
+
"""
|
|
233
|
+
scope: Optional[Literal["public", "internal"]] = None
|
|
234
|
+
strictness: Optional[Literal["strict", "standard", "light"]] = None
|
|
235
|
+
for line in doc.split("\n"):
|
|
236
|
+
s = line.strip()
|
|
237
|
+
if s.startswith("@harbor.scope:"):
|
|
238
|
+
val = s.split(":", 1)[1].strip()
|
|
239
|
+
if val in ("public", "internal"):
|
|
240
|
+
scope = val # type: ignore
|
|
241
|
+
elif s.startswith("@harbor.l3_strictness:"):
|
|
242
|
+
val = s.split(":", 1)[1].strip()
|
|
243
|
+
if val in ("strict", "standard", "light"):
|
|
244
|
+
strictness = val # type: ignore
|
|
245
|
+
return scope, strictness
|
|
246
|
+
|
|
247
|
+
def _module_qual_from_path(self, p: Path) -> str:
|
|
248
|
+
"""根据文件路径生成模块限定名(点分格式)。
|
|
249
|
+
|
|
250
|
+
@harbor.scope: internal
|
|
251
|
+
@harbor.l3_strictness: standard
|
|
252
|
+
|
|
253
|
+
Args:
|
|
254
|
+
p: 源文件路径。
|
|
255
|
+
|
|
256
|
+
Returns:
|
|
257
|
+
str: 点分模块名,如 `harbor.adapters.python.parser`。
|
|
258
|
+
"""
|
|
259
|
+
root = Path.cwd().resolve()
|
|
260
|
+
try:
|
|
261
|
+
rel = p.resolve().relative_to(root)
|
|
262
|
+
except Exception:
|
|
263
|
+
rel = p.name if p.is_file() else str(p)
|
|
264
|
+
rel = Path(rel)
|
|
265
|
+
parts = list(rel.parts)
|
|
266
|
+
if parts and parts[-1].endswith(".py"):
|
|
267
|
+
parts[-1] = parts[-1][:-3]
|
|
268
|
+
return ".".join([x for x in parts if x and x not in (".", "..")])
|
|
269
|
+
|