@anchorlock/cli 0.0.0-stage → 0.2.0

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.
package/CHANGELOG.md ADDED
@@ -0,0 +1,67 @@
1
+ # Changelog
2
+
3
+ ## 0.2.0
4
+
5
+ - Kernel is now `@anchorlock/core` >= 0.6.0 (was `@keelforge/spec-forge`). A sibling `anchorlock-core` checkout is used first when present.
6
+ - `npm test` works on Windows and on Node 22+.
7
+
8
+ ## 0.1.9
9
+
10
+ - Docs: several coding agents is not a third path. Share one pin and one check; field scope still moves only when the backend releases.
11
+ - Product page: `#start` is a long section; the 16% intersection reveal left it invisible and left a blank footer. Content is visible by default; multi-agent is its own section.
12
+
13
+ ## 0.1.8
14
+
15
+ - `anchor repin --from <new openapi.json> --tag vX.Y.Z`: swap the whole pin when the backend ships a release. Keeps the existing `url` (tag substituted) and `change`, recomputes the hash, no `--force` needed, warns when the artifact is byte-identical.
16
+ - `anchor pin --change <id>` records which requirement moved the contract; `check` prints `pin v1.5.0 · change PROJ-123` so nobody has to open the file.
17
+ - Docs answer the second round: field scope moves when the backend releases, never by hand-editing the pin. Agents must not repin to turn a red gate green.
18
+ - Path B drafts state what to keep (names crossing a process boundary) and what to delete (functions, locals, types, single-file names).
19
+
20
+ ## 0.1.7
21
+
22
+ - FAIL output tells humans to fix code (not the pin) and paste the log to the coding agent.
23
+ - Product page LIVE CHECK matches FAIL/PASS plus the next hint; Path A check commands include `--strict --require-hash`.
24
+ - `--strict` ignores identifiers used only as function calls or declarations (`lookupMine()`, `encodeURIComponent()`), so FAIL lists field names an agent can actually fix.
25
+ - Path A (pinned OpenAPI in the consumer repo) is the product. `anchor init` installs the skill only and does not write a field list.
26
+ - Agents must not author `constraints.json` from a PRD. Path B is draft-from-code plus a human confirm.
27
+ - Consumer CI snippets use `--strict --require-hash`.
28
+
29
+ ## 0.1.6
30
+
31
+ - Check always prints `scan` (path + file count). Humans use `--changed` (git edits); do not pick a single source file.
32
+ - `--src` is for CI / Agent only.
33
+
34
+ ## 0.1.5
35
+
36
+ - `anchor check --src` no longer switches to undeclared mode. `--src` is scan root only; `--strict` is the full gate.
37
+
38
+ ## 0.1.4
39
+
40
+ - `anchor init` writes empty `units: []`. Demo field names stay in `anchor demo`, not in a real repo.
41
+
42
+ ## 0.1.3
43
+
44
+ - `anchor check` maps `units[].aliases` to the unit `id` (`orderId` / `order_sn` / quoted `单号` → `order_no`).
45
+ - `anchor demo` (and `npm run demo`) is that story: red then green.
46
+ - `pin.json` includes `path` so check can run offline when the artifact is still on disk.
47
+ - CI snippets: `ci/check-contract.github.yml` / `ci/check-contract.gitlab.yml`.
48
+ - `anchor pin --from openapi.json --url … --tag vX.Y.Z` writes `pin.json` (sha256).
49
+ - Kernel: `@keelforge/spec-forge` >= 0.5.19.
50
+
51
+ ## 0.1.2
52
+
53
+ - Two onboarding paths: have OpenAPI (pin + `check`) or scan code (`anchor init --from-code` → `constraints.json`).
54
+ - `anchor constraints --from <openapi|url|pin.json>` projects OpenAPI → `constraints.json` for SemanticAnchor.
55
+ - `anchor init --from-code` drafts `constraints.json` from source (aliases included). Blank template is `constraints --init` only.
56
+ - Kernel: `@keelforge/spec-forge` >= 0.5.17.
57
+
58
+ ## 0.1.1
59
+
60
+ - `anchor check --contract` accepts a tagged OpenAPI URL or `pin.json` (`url` + `hash` + `version`).
61
+ - `--hash sha256:…` / `--require-hash`. Frontend pins the release artifact; does not clone the backend repo.
62
+
63
+ ## 0.1.0
64
+
65
+ - First wedge: `anchor check` and `anchor init --from-code` (draft).
66
+ - Kernel: `@keelforge/spec-forge` >= 0.5.15 (`checkNames` / `draftIds`).
67
+ - No compile / pack / demand-repo commands.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Anchorlock
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.
package/README.md CHANGED
@@ -1,3 +1,119 @@
1
- # Temporary Holding Version
1
+ # Anchorlock
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ **Consumer repos check field names against a pinned OpenAPI — not a list the coding agent wrote.**
4
+
5
+ Think: ESLint for API field names at merge. The spec comes from the **backend tagged artifact**. Same-repo Java/TS that the compiler already checks is not the point.
6
+
7
+ | | When | Command |
8
+ |---|---|---|
9
+ | **Haul** | Before merge in the **frontend / client / test** repo | `anchor check --contract pin.json --src src --strict --require-hash` |
10
+ | **Cast** | Agent about to write a file | SemanticAnchor MCP: `bind` / `verify_usage` on a `constraints.json` **projected from that same OpenAPI** |
11
+ | Project | Prepare the file for cast | `anchor constraints --from pin.json` — this is **not** the cast |
12
+
13
+ Do not invent `constraints.json` from a PRD. The agent that writes the list is the agent that writes the code; that is not a lock.
14
+
15
+ - Product page (Chinese): [`public/index.html`](./public/index.html)
16
+ - CLI guide (Chinese): [`docs/usage.md`](./docs/usage.md)
17
+
18
+ ## Path A (default) — you already have OpenAPI
19
+
20
+ Backend **build-exports** `openapi.json` (Springdoc / Nest / FastAPI). Tag a release. Do not hand-write the spec. Do not point at a live `/v3/api-docs`.
21
+
22
+ In the **consumer** repo, once:
23
+
24
+ ```bash
25
+ npm i -D @anchorlock/cli
26
+ npx anchor init # agent skill only — no field list
27
+ ```
28
+
29
+ Pin the tagged download URL (not a git clone of the backend):
30
+
31
+ ```bash
32
+ npx anchor pin --from dist/openapi.json \
33
+ --url https://…/releases/v1.4.0/openapi.json --tag v1.4.0 \
34
+ -o contracts/order.pin.json
35
+ npx anchor check --contract contracts/order.pin.json --src src --strict --require-hash
36
+ ```
37
+
38
+ `orderId` / `order_sn` vs spec `order_no` → **red**. Same name as the spec → **green**. Fix the code, not the pin.
39
+
40
+ | `--contract` | What it is |
41
+ |---|---|
42
+ | `openapi.json` | Local copy of the spec |
43
+ | `https://…/openapi.json` | Tagged release artifact (pass `--hash`) |
44
+ | `order.pin.json` | `{ url, hash, version, path }` — `check` uses `path` if that file is still on disk; otherwise it downloads `url` |
45
+
46
+ Copy [`ci/check-contract.github.yml`](./ci/check-contract.github.yml) into the consumer repo.
47
+
48
+ ### Second round — the backend shipped a new tag
49
+
50
+ Field scope moves when the **backend releases**, not when you and the agent talk about a ticket. Never hand-add a field to the pin; swap the whole thing:
51
+
52
+ ```bash
53
+ npx anchor repin --from ../order-api/dist/openapi.json --tag v1.5.0 --change PROJ-123
54
+ git diff contracts/order.pin.json # version + hash changed
55
+ npx anchor check --contract contracts/order.pin.json --src src --strict --require-hash
56
+ ```
57
+
58
+ `repin` keeps the existing `url` (swapping the tag inside it) and `change`, recomputes the hash, and overwrites without `--force`. Every `check` prints what you are locked to:
59
+
60
+ ```
61
+ pin v1.5.0 · change PROJ-123
62
+ ```
63
+
64
+ History is `git log contracts/order.pin.json`. A coding agent must never `repin` to turn a red gate green — that is the human's call, driven by a release.
65
+
66
+ ### Several agents (not a third path)
67
+
68
+ Same pin, same check. More agents does not mean more contracts. Backend ships a tagged OpenAPI; a human `repin`s; frontend / client / test agents only change source. Paste FAIL to the agent that drifted. Do not let each agent draft its own list.
69
+
70
+ Need SemanticAnchor? Project, then point SA at the file:
71
+
72
+ ```bash
73
+ npx anchor constraints --from contracts/order.pin.json -o constraints.json
74
+ ```
75
+
76
+ ## Path B — no OpenAPI yet
77
+
78
+ Draft **once** from a **narrow** tree. A human confirms ids. Other repos only `check` that file.
79
+
80
+ ```bash
81
+ npx anchor init --from-code path/to/one-module -o constraints.json
82
+ # human deletes junk, confirms id / aliases
83
+ npx anchor check --contract constraints.json --src src
84
+ ```
85
+
86
+ Keep names that cross a process boundary (HTTP JSON keys, SQL columns, MQ payloads, stored fields). Delete function names, locals, type names, framework words, and anything seen in a single file. When unsure, delete: a missing lock costs you nothing, a wrong lock makes CI demand changes to correct code.
87
+
88
+ Do not `init --from-code` in every repo. Two drafts (`orderStatus` vs `order_status`) both go green and still disagree at runtime.
89
+
90
+ ## What this is not
91
+
92
+ | Not this | Use instead |
93
+ |---|---|
94
+ | Shape / enum values at write time | SemanticAnchor `verify_usage` |
95
+ | Demand-repo authoring / compile / pack | `@anchorlock/core` |
96
+ | A field list the coding agent wrote from the ticket | Path A pin, or Path B + **human** confirm |
97
+ | Company defaults, design tokens | your org's spec CLI (e.g. `@your-org/spec-cli`) |
98
+
99
+ ## Install
100
+
101
+ Node.js 18+. Kernel: `@anchorlock/core` ≥ 0.6.0, installed with the CLI.
102
+
103
+ ```bash
104
+ npm install -D @anchorlock/cli
105
+ npx anchor --help
106
+ ```
107
+
108
+ ```bash
109
+ npm run preview # product page → http://localhost:4174
110
+ ```
111
+
112
+ ## License
113
+
114
+ MIT
115
+
116
+ ## Publish (maintainers)
117
+
118
+ 1. Publish the kernel: in `anchorlock-core`, `npm test && npm publish --access public`.
119
+ 2. Here, `npm install` so the dependency resolves from the registry, then `npm test && npm publish --access public`.
package/bin/anchor.js ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const { runCli } = require('../lib/cli');
5
+
6
+ runCli(process.argv)
7
+ .then(function (code) {
8
+ if (typeof code === 'number' && code !== 0) process.exit(code);
9
+ })
10
+ .catch(function (err) {
11
+ const msg = err && err.message ? err.message : String(err);
12
+ process.stderr.write('\nanchor ' + msg + '\n');
13
+ process.stderr.write('hint: anchor --help\n');
14
+ process.exit(1);
15
+ });
@@ -0,0 +1,25 @@
1
+ # 消费仓:对照钉死的契约扫源码
2
+ #
3
+ # 提交 contracts/order.pin.json(url + hash + version,可选 path)。
4
+ # 不要对生产 /v3/api-docs 扫。私有制品把只读 token 放进 ANCHOR_CONTRACT_TOKEN 或 SPEC_CONTRACT_TOKEN。
5
+
6
+ name: check-contract
7
+
8
+ on:
9
+ pull_request:
10
+ push:
11
+ branches: [main, master]
12
+
13
+ jobs:
14
+ field-names:
15
+ runs-on: ubuntu-latest
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+ - uses: actions/setup-node@v4
19
+ with:
20
+ node-version: '20'
21
+ - name: Field-name gate
22
+ env:
23
+ ANCHOR_CONTRACT_TOKEN: ${{ secrets.ANCHOR_CONTRACT_TOKEN }}
24
+ SPEC_CONTRACT_TOKEN: ${{ secrets.SPEC_CONTRACT_TOKEN }}
25
+ run: npx --yes @anchorlock/cli check --contract contracts/order.pin.json --src src --strict --require-hash
@@ -0,0 +1,12 @@
1
+ # 消费仓:对照钉死的契约扫源码(GitLab)
2
+ #
3
+ # 提交 contracts/order.pin.json。私有 Release 把只读 token 放进 CI 变量 ANCHOR_CONTRACT_TOKEN。
4
+
5
+ check-contract:
6
+ stage: test
7
+ image: node:20
8
+ rules:
9
+ - if: $CI_PIPELINE_SOURCE == "merge_request_event"
10
+ - if: $CI_COMMIT_BRANCH == "main"
11
+ script:
12
+ - npx --yes @anchorlock/cli check --contract contracts/order.pin.json --src src --strict --require-hash
package/docs/usage.md ADDED
@@ -0,0 +1,320 @@
1
+ # Anchorlock CLI 使用说明
2
+
3
+ 包:`@anchorlock/cli` · 命令:`anchor`
4
+ 本包装的是**收紧**:消费仓合并前,对照一份**仓外权威**的契约扫字段名。权威来自后端发版的 OpenAPI(钉 hash),不是本仓 Agent 根据需求编的名单。
5
+
6
+ **抛锚**是写盘前的 SemanticAnchor MCP(`bind` / `verify_usage`)。有 OpenAPI 时,先 `anchor constraints --from` 投影出它要吃的 `constraints.json`;投影本身不是抛锚。
7
+
8
+ 需要 **Node.js 18+**。内核 `@anchorlock/core` ≥ **0.6.0**,随 cli 自动安装。本机若与 `anchorlock-core` 同级,会优先用 `../anchorlock-core`。
9
+
10
+ ---
11
+
12
+ ## 0. 先问一句:有没有 OpenAPI?
13
+
14
+ 有 → **路 A(默认)**。没有 → 路 B,且必须人确认一次起草稿。不要两条一起走。不要从 PRD 手写/让 Agent 写字段名单。
15
+
16
+ ---
17
+
18
+ ## 0.5 两份文件长什么样
19
+
20
+ `check` 只读字段名。下面是**对公转账**里最容易漂的五个名字,是切片,不是整份支付说明书。
21
+
22
+ **openapi.json**(路 A,后端发版,不要手写):
23
+
24
+ ```json
25
+ {
26
+ "openapi": "3.0.3",
27
+ "info": { "title": "payment-api", "version": "1.4.0" },
28
+ "paths": {
29
+ "/payments/transfers": {
30
+ "post": {
31
+ "operationId": "createTransfer",
32
+ "requestBody": {
33
+ "required": true,
34
+ "content": {
35
+ "application/json": {
36
+ "schema": { "$ref": "#/components/schemas/TransferReq" }
37
+ }
38
+ }
39
+ }
40
+ }
41
+ }
42
+ },
43
+ "components": {
44
+ "schemas": {
45
+ "TransferReq": {
46
+ "type": "object",
47
+ "required": ["payer_account", "payee_account", "payment_amount", "payment_currency"],
48
+ "properties": {
49
+ "payer_account": { "type": "string", "description": "付款账号" },
50
+ "payee_account": { "type": "string", "description": "收款账号" },
51
+ "payment_amount": { "type": "string", "description": "转账金额,十进制字符串" },
52
+ "payment_currency": { "type": "string", "description": "币种,ISO 4217" }
53
+ }
54
+ },
55
+ "TransferRes": {
56
+ "type": "object",
57
+ "properties": {
58
+ "payment_id": { "type": "string", "description": "支付流水号" },
59
+ "payment_status": { "type": "string" }
60
+ }
61
+ }
62
+ }
63
+ }
64
+ }
65
+ ```
66
+
67
+ 规范名在 `properties` 上。消费仓不提交这份文件,只提交 pin:`{ "url", "hash", "version" }`。
68
+
69
+ **constraints.json**(路 B 人确认后的名单;路 A 用 `anchor constraints --from` 投影,不要手写):
70
+
71
+ ```json
72
+ {
73
+ "schemaVersion": "1",
74
+ "units": [
75
+ {
76
+ "id": "payment_id",
77
+ "type": "string",
78
+ "aliases": ["paymentId", "payId", "bank_serial_no", "支付流水号"]
79
+ },
80
+ {
81
+ "id": "payer_account",
82
+ "type": "string",
83
+ "aliases": ["fromAccount", "payerAcct", "payer_acct", "付款账号"]
84
+ },
85
+ {
86
+ "id": "payee_account",
87
+ "type": "string",
88
+ "aliases": ["toAccount", "payeeAcct", "payee_acct", "收款账号"]
89
+ },
90
+ {
91
+ "id": "payment_amount",
92
+ "type": "string",
93
+ "aliases": ["amount", "txnAmt", "pay_amt", "转账金额"]
94
+ },
95
+ {
96
+ "id": "payment_currency",
97
+ "type": "string",
98
+ "aliases": ["currency", "ccy", "币种"]
99
+ }
100
+ ]
101
+ }
102
+ ```
103
+
104
+ `id` 是规范名。前端的 `fromAccount` / `amount`,或测试里的 `"付款账号"`,应对齐 `payer_account` / `payment_amount`。
105
+
106
+ ---
107
+
108
+ ## 1. 路 A:钉死的 OpenAPI(推荐)
109
+
110
+ 后端构建导出说明书(Springdoc / Nest / FastAPI),打 tag,把 `openapi.json` 挂到 Release。前端**不 clone 后端仓**,不对着线上 `/v3/api-docs` 扫。
111
+
112
+ 消费仓只提交 pin:
113
+
114
+ ```bash
115
+ npm i -D @anchorlock/cli
116
+ npx anchor init # 只装 Agent skill,不写名单
117
+
118
+ npx anchor pin --from dist/openapi.json \
119
+ --url "https://git.example.com/group/order-api/-/releases/v1.4.0/downloads/openapi.json" \
120
+ --tag v1.4.0 -o contracts/order.pin.json
121
+
122
+ npx anchor check --contract contracts/order.pin.json --src src --strict --require-hash
123
+ ```
124
+
125
+ `check` 做的事:
126
+
127
+ 1. 读 pin:优先用本地 `path`,否则下 `url`,用 `hash` 核对没下错文件。
128
+ 2. 从说明书 schema 属性名抽出契约认的字段(如 `order_no`)。
129
+ 3. 扫 `--src`(一般是 `src/`,跳过 `node_modules`)。近义词或说明书上没有的字段名 → 退出码非 0。
130
+
131
+ 红了改**代码**对齐说明书,不要改 pin。CI 复制 [`ci/check-contract.github.yml`](../ci/check-contract.github.yml),挂在 merge 保护上。
132
+
133
+ ```json
134
+ {
135
+ "url": "https://git.example.com/group/order-api/-/releases/v1.4.0/downloads/openapi.json",
136
+ "hash": "sha256:后端发版时算出的64位指纹",
137
+ "version": "v1.4.0",
138
+ "path": "../dist/openapi.json"
139
+ }
140
+ ```
141
+
142
+ 本地还能读到 `path` 时不联网。私有制品把只读 token 放进 `ANCHOR_CONTRACT_TOKEN`。
143
+
144
+ 合主干用 `--strict`:契约来自完整 OpenAPI,名单外的字段名也应失败。这在 TypeScript / JS 消费仓可用。不要拿 `--strict` 去扫同一个 Java 后端仓里的方法名和 SQL 列名——那是编译器和 MyBatis 的范围。
145
+
146
+ ### 抛锚
147
+
148
+ ```bash
149
+ anchor constraints --from contracts/order.pin.json -o constraints.json
150
+ # 然后 SA_CONSTRAINTS 指向这份文件,SemanticAnchor 跑 bind / verify_usage
151
+ ```
152
+
153
+ 收紧继续:`anchor check --contract contracts/order.pin.json --src src --strict --require-hash`。
154
+
155
+ ---
156
+
157
+ ## 1.5 第二回合:这次需求要加字段
158
+
159
+ **字段范围由后端发版推进,不由你和 Agent 的对话推进。** 需求说要加「优惠券抵扣」,不等于你可以往契约里加一个 `couponAmount`——先让后端发版,说明书上有了,前端才认。
160
+
161
+ pin 是**整份换**,不是往里面加字段:
162
+
163
+ ```bash
164
+ # 1. 后端:改完接口,重新导出、打新 tag、挂到 Release
165
+ # 2. 前端:换到新那一版(url 里的 tag 会跟着换)
166
+ npx anchor repin --from ../order-api/dist/openapi.json --tag v1.5.0 --change PROJ-123
167
+
168
+ # 3. 看清楚换了什么,再提交
169
+ git diff contracts/order.pin.json
170
+ git add contracts/order.pin.json
171
+
172
+ # 4. 改业务代码用新字段,跑同一条 check
173
+ npx anchor check --contract contracts/order.pin.json --src src --strict --require-hash
174
+ ```
175
+
176
+ `repin` 就是「换版专用的 pin」:沿用原来的 `url` 和 `change`,把 tag 换掉、hash 重算、直接覆盖(不用记 `--force`)。制品和上一版一字不差时会警告,多半是后端还没重新导出。
177
+
178
+ 版本管理靠两样东西,不需要另建目录:
179
+
180
+ | 东西 | 谁记的 |
181
+ |---|---|
182
+ | 锁在哪一版 | `pin.json` 里的 `version` + `hash` |
183
+ | 为什么换 | `pin.json` 里的 `change`(需求号)+ 这次 commit |
184
+ | 换过几次 | `git log contracts/order.pin.json` |
185
+
186
+ `check` 每次都会把这两样打出来,人不用去翻文件:
187
+
188
+ ```
189
+ pin v1.5.0 · change PROJ-123
190
+ ```
191
+
192
+ **编码 Agent 不许自己 repin。** 换版是「后端发了新版本」触发的,不是「check 红了想换绿」。红了只能改代码。
193
+
194
+ ---
195
+
196
+ ## 1.6 多 Agent 协作(不是第三条路)
197
+
198
+ Cursor、Claude、Copilot 同时写,或前端 / 后端 / 测试各开一个 Agent,**仍走路 A(没有说明书则路 B)**。协作增加的是写代码的人,不是契约的份数。
199
+
200
+ 共享这一份:
201
+
202
+ ```text
203
+ contracts/order.pin.json
204
+ ```
205
+
206
+ 所有编码 Agent 吃同一份 `anchor init` 装进去的 skill。CI 只认这一条:
207
+
208
+ ```bash
209
+ npx anchor check --contract contracts/order.pin.json --src src --strict --require-hash
210
+ ```
211
+
212
+ | 谁 | 做什么 | 不许做什么 |
213
+ |---|---|---|
214
+ | 人 | 第一次 `pin`;后端发新 tag 后 `repin --change <需求号>` | 让 Agent 按对话补字段 |
215
+ | 后端 Agent | 实现接口、导出 OpenAPI、打 tag | 在消费仓 `check` 自己的 Java;手写名单当权威 |
216
+ | 前端 / 客户端 / 测试 Agent | 用说明书上的名字写代码;红了把 FAIL 贴回去改源码 | `repin`、改 pin、从 PRD 编名单 |
217
+ | CI | 合主干前跑上面那条 | 没有 |
218
+
219
+ 同一仓里几个 Agent 并行:开工先看 `check` 打出来的 `pin v1.5.0 · change …` 是不是同一版;各改各的源码;红了把整段 FAIL 贴给**写飘了的那个** Agent。冲突在 git,不在契约。
220
+
221
+ 跨仓时顺序不能倒:后端发版 → 人 `repin` → 前端 / 测试 Agent 才用新字段。前端 Agent 不能先发明 `couponAmount` 再让后端追。测试仓和前端仓 pin **同一个** url/tag。
222
+
223
+ 写盘前可选:投影一次,各 Agent 的 SemanticAnchor 都指向**这一份** `constraints.json`。投影不能代替 `check`,也不能手改当权威。
224
+
225
+ 路 B:只允许**一处**起草并由人确认。其它 Agent 只 `check`。各扫各的(`orderStatus` vs `order_status`)两边都绿,联调照样炸。
226
+
227
+ ---
228
+
229
+ ## 2. 路 B:没有 OpenAPI
230
+
231
+ 只在**一处、已经圈定的模块**起草。人确认 `id`,其它仓只 `check` 这一份。
232
+
233
+ ```bash
234
+ anchor init --from-code path/to/one-module -o constraints.json
235
+ # 人确认之后:
236
+ anchor check --contract constraints.json --src src
237
+ ```
238
+
239
+ 起草稿里留什么、删什么,按这条判:
240
+
241
+ | 留 | 删 |
242
+ |---|---|
243
+ | 会跨进程传的名字:HTTP JSON key、SQL 列名、MQ payload、存储字段 | 函数名、方法名、局部变量、类型名、框架词 |
244
+ | 两个以上模块都在用的业务名 | 只在一个文件里出现过的名字 |
245
+
246
+ 拿不准就删。漏锁只是这个字段没保护;错锁会让 CI 逼别人改本来正确的代码,工具很快就被关掉。
247
+
248
+ 路 B 没有 tag 和 hash,`constraints.json` 的版本就是 git 历史。加字段的规矩和路 A 一样:**先改这一份、人确认,再写代码**,不要写完代码回头补名单。
249
+
250
+ 不要每个仓都起草:前端扫出 `orderStatus`、后端扫出 `order_status`,两份都绿,联调照样炸。
251
+
252
+ 不要让 Agent 按「实现订单查询」往 `units` 里加字段。那是自己给自己发证。
253
+
254
+ 连代码都没有、要从零发明字段时,才用 `anchor constraints --init`。那是例外。
255
+
256
+ ---
257
+
258
+ ## 3. `check` 在查什么
259
+
260
+ 词法扫描 `.js` / `.ts` / `.vue` / `.java` 等(**不扫 `.xml`**),对照契约的 `id` / `aliases`,以及驼峰↔蛇形折合。不读 PRD,不追踪调用链。
261
+
262
+ | 情况 | 结果 |
263
+ |------|------|
264
+ | 与某个 `id` 完全一致 | 通过 |
265
+ | 出现在某单元 `aliases`(含 `"单号"`) | **漂移 · 红** |
266
+ | `orderNo` 能折成契约里的 `order_no` | **漂移 · 红** |
267
+ | 长得像字段但不在契约里 | 默认忽略;`--strict` 时 **未声明 · 红** |
268
+ | 只作为函数出现:`lookupMine()`、`encodeURIComponent()`、`const fetchOrder = async () =>` | `--strict` 下忽略 |
269
+ | `id` / `name` / `status`、PascalCase 类型、`useXxx` | 忽略 |
270
+
271
+ `--src` 只定扫描根。人不要靠它指定某一个源文件。看未提交改动用 `--changed`。
272
+
273
+ ---
274
+
275
+ ## 4. 接到 CI(消费仓)
276
+
277
+ ```yaml
278
+ - run: npx anchor check --contract contracts/order.pin.json --src src --strict --require-hash
279
+ ```
280
+
281
+ 门禁挂在 **merge / 主分支保护** 上。Prompt「请统一命名」挡不住下一次生成。
282
+
283
+ ---
284
+
285
+ ## 5. 抛锚和收紧
286
+
287
+ | | 何时 | 命令 | 吃什么 |
288
+ |---|---|---|---|
289
+ | **抛锚** | Agent 正在写文件 | SemanticAnchor MCP:`bind` / `verify_usage` | 从 OpenAPI **投影**出的 `constraints.json` |
290
+ | **收紧** | 合进主干之前 | `anchor check --contract pin.json --src src --strict` | 钉死的 OpenAPI / pin |
291
+ | 投影(不是抛锚) | 给抛锚准备文件 | `anchor constraints --from pin.json` | 钉住的说明书 |
292
+
293
+ `compile` / `pipeline` / `new-change` 会直接拒绝。
294
+
295
+ ---
296
+
297
+ ## 6. 常见问题
298
+
299
+ **每个仓都生成一份名单?**
300
+ 不要。路 A 钉同一份发版 OpenAPI。路 B 只扫一次,各仓共用这一份已确认文件。
301
+
302
+ **这次需求新增的字段放哪?**
303
+ 不往 pin 里加。后端发新 tag → `anchor repin --tag <新 tag> --change <需求号>` 整份换 → 再改代码。见上面「第二回合」。
304
+
305
+ **我怎么知道自己锁在哪一版?**
306
+ `check` 会打 `pin v1.5.0 · change PROJ-123`。历史看 `git log contracts/order.pin.json`。
307
+
308
+ **有 OpenAPI 还要让 Agent 写 constraints 吗?**
309
+ 不要。
310
+
311
+ **在 Java 后端仓跑 check 扫到 SQL 列名 / getter?**
312
+ 别这么用。本工具锁的是消费仓对接口字段;同一仓的 Java 交给编译器。合主干的 `--strict` 给 TS/JS 消费仓。
313
+
314
+ **红了改清单还是改代码?**
315
+ 改代码对齐契约。FAIL 末尾会写:不要改 pin,把这段贴给编码 Agent,改完再跑同一条 check。
316
+
317
+ **多个 Agent 同时写,要走第三条路吗?**
318
+ 不要。仍是路 A(或路 B)。共享同一份 pin,各自改代码,CI 收紧。见上面「多 Agent 协作」。
319
+
320
+ **想先看闸、不碰仓库:** `npx @anchorlock/cli demo` 或本仓 `npm run demo`。