concord-engine 0.0.0-stage → 0.3.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Harsh N. Chandak
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,504 @@
1
- # Temporary Holding Version
1
+ # Concord Engine
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
+ **Scale parallelism. Preserve alignment.**
4
+
5
+ Describe a task. A fleet of agents builds it. They come back with work that is
6
+ finished, tested and documented — and that agrees with itself, because none of
7
+ them was allowed to contradict a decision another had already made.
8
+
9
+ Parallel agents don't scale past the human in the middle. You run five to go
10
+ faster, four of them are right, the product is broken, and the only thing that
11
+ catches it is you — reading everything, holding the whole picture,
12
+ re-explaining choices that were already settled.
13
+
14
+ That isn't a prompting problem. Your agents have **memory** and they have
15
+ **verification**, and neither answers the question that actually breaks a
16
+ parallel run:
17
+
18
+ > Did these two agents just settle the same question differently?
19
+
20
+ Concord Engine answers it by comparison, never by a model, and refuses the
21
+ second answer at the moment it is written.
22
+
23
+ [![A real Claude Code session: a refunds feature is split across four workers, two of them settle the error format differently, and the second is refused with the decision already in force, who made it and why.](assets/brag.gif)](https://github.com/harsh-chandak/concord-engine/blob/main/assets/brag.gif)
24
+
25
+ <sub>Recorded, not drawn. `brag/record.mjs` runs the real CLI and captures its
26
+ output verbatim; `brag/video.html` only renders what was captured.</sub>
27
+
28
+ ## What a decision is
29
+
30
+ This is the part that gets undersold. A "decision" is not a variable name. It
31
+ is **any question two agents could answer differently and both be reasonable** —
32
+ and the cost is never the question itself, it's that nothing goes red.
33
+
34
+ | Kind | Real examples |
35
+ | --- | --- |
36
+ | `convention` | money is integer minor units, never a float · timestamps are UTC ISO-8601 strings · pagination is cursor-based · every non-2xx body is `{"error":"<message>"}` · retries are idempotent and capped at 3 · migrations ship in two reversible stages |
37
+ | `api-contract` | `POST /api/orders` returns 201, not 200 · tenant scoping lives in the path, not a header |
38
+ | `enum` | order status is `pending\|paid\|shipped` · roles are `owner\|admin\|member` · webhook events are a closed set |
39
+ | `schema` | `id` is a uuid and not null · `amount_cents` is an integer · soft deletes use `deleted_at`, not a boolean |
40
+ | `naming` | the identifier is `user_id` · the destination field is `url`, not `target` |
41
+
42
+ Four agents implementing four reasonable answers to "what does an error body
43
+ look like" produce four green test suites and one broken product. That is the
44
+ whole category, and it is much wider than route paths.
45
+
46
+ ## The failure it stops
47
+
48
+ A refunds feature, split across a backend worker and a client worker. The
49
+ backend settles the error shape. The client, working in parallel and with no
50
+ visibility into the backend's files, reaches a different answer:
51
+
52
+ ```
53
+ $ concord-engine decide --unit client --kind convention \
54
+ --subject "error response body shape" \
55
+ --value 'every non-2xx body is {"code":"<slug>","detail":"<message>"}' --as client
56
+
57
+ refused this contradicts a decision already in force.
58
+ d-003 api (lead): error response body shape = every non-2xx body is {"error":"<message>"}
59
+ because: the client shows it to a person
60
+ yours: every non-2xx body is {"code":"<slug>","detail":"<message>"}
61
+
62
+ Adopt the existing value, or supersede it deliberately:
63
+ --supersedes d-003 --layer pm (an override, recorded as one)
64
+ --force (record the disagreement for `check` to surface)
65
+ ```
66
+
67
+ <sub>Verbatim from the recording above.</sub>
68
+
69
+ The agent is stopped at the moment it tries to disagree, and told what is in
70
+ force, who decided it, why, and the two legitimate ways out. Not at
71
+ integration. Not by you.
72
+
73
+ ## Three ways to run it
74
+
75
+ **As a skill, which is how most people will use it.** Describe the task; the
76
+ agent runs the whole loop — authors the contract, binds it, briefs each
77
+ worker, fans out, gates every decision, runs the acceptance checks, writes the
78
+ design doc.
79
+
80
+ ```
81
+ /concord-engine:concord-engine add refunds across the API, the client and the schema
82
+ /concord-engine:concord-engine migrate us off the legacy auth tables, five workers
83
+ /concord-engine:concord-engine rename the tenant concept to workspace everywhere
84
+ ```
85
+
86
+ Nothing about the task has to be small, and nothing has to be a route.
87
+
88
+ **As an MCP server**, so an agent already mid-task can reach the gate without
89
+ being handed a workflow. Twelve tools: `write_contract`, `bind_contract`,
90
+ `brief_unit`, `fleet_plan`, `run_acceptance`, `generate_docs`, `cost_report`,
91
+ plus `record_decision`, `binding_decisions`, `open_conflicts`,
92
+ `record_attempt`, `status`.
93
+
94
+ **As a CLI**, for humans and for CI. `concord-engine check` exits non-zero
95
+ when anything contradicts, which is a one-line pipeline step.
96
+
97
+ ## Why this isn't another memory tool
98
+
99
+ | Tool | Answers | Doesn't answer |
100
+ | --- | --- | --- |
101
+ | Handoff / memory tools | "what did the last agent know?" | "do these two agree?" |
102
+ | Orchestrator verification | "did this worker do its task?" | "do these two agree?" |
103
+ | Edit-conflict detection | "did two agents touch one file?" | "do these two agree?" |
104
+
105
+ And the obvious way to answer it — store decisions as prose, ask a model whether
106
+ two of them conflict — is slow, costs a call per check, and is wrong often
107
+ enough that people stop trusting it. A coordination layer nobody trusts is
108
+ worse than none, because it adds latency and false alarms to a workflow that
109
+ had neither.
110
+
111
+ **Concord Engine decides agreement by comparison, never by a model.** Decisions are
112
+ typed and narrow, so a contradiction is a comparison: same subject, different
113
+ value. No API call exists anywhere in the decision path — there's a test that
114
+ fails if any file in that path can reach outside the process, and a second one
115
+ that fails if any file in the package, anywhere, calls a model.
116
+
117
+ ## The loop
118
+
119
+ The gate above is one organ. v0.2 is the body around it: you describe a task,
120
+ and the fleet that builds it is bound to one set of decisions from before the
121
+ first line of code.
122
+
123
+ ```
124
+ describe a task
125
+ |
126
+ [1] CONTRACT the agent authors JSON to a published schema;
127
+ | the package validates it and stores it
128
+ v
129
+ [2] BIND the contract's decisions enter the log at PM layer,
130
+ | binding before any code exists
131
+ v
132
+ [3] BRIEF one brief per unit: its slice, the decisions it must
133
+ | honour, what it may not decide, how it will be judged
134
+ v
135
+ [4] FAN OUT the host spawns one worker per unit, each getting a
136
+ | brief and nothing else
137
+ v
138
+ [5] GATE a worker that tries to decide something contradictory
139
+ | is refused, told what is in force and who may override
140
+ v
141
+ [6] ACCEPT every acceptance criterion carries a command; the run
142
+ | is not done until all of them exit 0
143
+ v
144
+ [7] DOCS the design doc is generated from the decision log —
145
+ | what was decided, by whom, and why
146
+ v
147
+ [8] COST what the fan-out sent, in bytes
148
+ ```
149
+
150
+ ```bash
151
+ concord-engine spec --schema # the contract shape, as JSON Schema
152
+ concord-engine spec --from contract.json # validate and store it
153
+ concord-engine bind --contract add-refunds
154
+ concord-engine fleet --contract add-refunds # every brief, as JSON
155
+ concord-engine brief --contract add-refunds api # one worker's brief
156
+ concord-engine accept --contract add-refunds # run every check
157
+ concord-engine docs --contract add-refunds
158
+ concord-engine cost --contract add-refunds
159
+ ```
160
+
161
+ Every loop command takes `--contract`. The MCP server exposes the same set as
162
+ `write_contract`, `bind_contract`, `brief_unit`, `fleet_plan`, `run_acceptance`,
163
+ `generate_docs` and `cost_report`.
164
+
165
+ ## The contract
166
+
167
+ One JSON document. The agent writes it; the package decides whether it holds.
168
+
169
+ `openSubjects` is the part people skip and shouldn't. Naming a thing before
170
+ anyone decides it is what stops two workers inventing two different names for
171
+ it — see **Honest limits** for the run where that went wrong.
172
+
173
+ ```jsonc
174
+ {
175
+ "id": "add-refunds",
176
+ "goal": "one sentence a reviewer can judge the result against",
177
+ "scope": { "in": ["..."], "out": ["..."] },
178
+ "decisions": [ // binding from t=0, at PM layer
179
+ { "kind": "api-contract", "subject": "refund endpoint",
180
+ "value": { "method": "POST", "path": "/api/refunds", "status": 201 },
181
+ "rationale": "why this and not the alternative" }
182
+ ],
183
+ "openSubjects": [ // known to need deciding, not yet decided
184
+ { "kind": "convention", "subject": "error response body shape",
185
+ "note": "the server and the client both serialise this" }
186
+ ],
187
+ "units": [ // one worker each
188
+ { "id": "api", "goal": "...", "files": ["src/api/**"],
189
+ "mayDecide": ["convention"], // everything else must be requested
190
+ "dependsOn": [] }
191
+ ],
192
+ "acceptance": [
193
+ { "id": "a1", "statement": "refunds endpoint returns 201",
194
+ "check": "npm test -- refunds", "unit": "api" }
195
+ ]
196
+ }
197
+ ```
198
+
199
+ Validation is not a formality. A contract is rejected when two of its own
200
+ decisions contradict each other, when a unit depends on one that doesn't exist,
201
+ when the dependency graph has a cycle, when a unit has no acceptance criterion,
202
+ or when a criterion has no command. All of it in one pass, because fixing one
203
+ error per round trip is the expensive way to find four:
204
+
205
+ ```
206
+ $ concord-engine spec --from contract.json
207
+
208
+ concord-engine: contract is not valid:
209
+ - unit "api" depends on "ui", which is not a unit in this contract
210
+ - acceptance[a1] has no check — a criterion without a command is a wish
211
+ ```
212
+
213
+ `bind` then puts the contract's decisions into the log at PM layer. If one of
214
+ them contradicts a decision already live, binding **refuses** — a new contract
215
+ quietly overriding decisions in force is the exact failure this tool exists to
216
+ prevent.
217
+
218
+ ## What one worker gets
219
+
220
+ A brief, and not the conversation. This is the actual brief the unit that wrote
221
+ this file was given. Verbatim, abridged only where marked:
222
+
223
+ ```
224
+ $ concord-engine brief --contract concord-engine-v2 readme
225
+
226
+ You are one worker on "concord-engine-v2". Your unit is "readme".
227
+
228
+ GOAL
229
+ Rewrite README.md around the loop without losing the v0.1 pitch that the gate is the product.
230
+
231
+ FILES YOU OWN
232
+ README.md
233
+ Do not edit files outside this list. Another worker owns them.
234
+
235
+ DECISIONS ALREADY IN FORCE — these are not suggestions
236
+ - [convention] model calls inside the package = no file in the package may call a model or open a network connection; enforced by a test (the one defensible sentence is that agreement is decided by comparison, never by a model — if authoring moves inside the package that stops being true)
237
+ - [naming] cli flag for selecting a contract = --contract (every loop command takes it, so it has to be the same word everywhere)
238
+ ... 3 more decisions in force
239
+
240
+ WHAT YOU MAY DECIDE
241
+ nothing — you write code and decide nothing
242
+
243
+ WHAT YOU MUST NOT DECIDE ALONE
244
+ naming, convention, api-contract, enum, schema
245
+
246
+ RECORD A DECISION LIKE THIS — the flags are what enforce the two lists above
247
+ concord-engine decide --contract concord-engine-v2 --unit readme \
248
+ --kind <kind> --subject "<what>" --value '<value>' --as readme --rationale "<why>"
249
+
250
+ Omitting --unit skips the check on what you may decide. Use the MCP tool
251
+ record_decision if you have it; it gates the same way.
252
+ A refusal means a decision already covers this: adopt the existing value.
253
+ Do not work around it, and do not --force it.
254
+
255
+ DONE MEANS
256
+ [a-readme] the README covers the loop and keeps the gate pitch
257
+ $ test -f README.md && grep -q 'spec --schema' README.md && grep -q 'refus' README.md && grep -q -i 'estimate' README.md
258
+ ```
259
+
260
+ `fleet` prints all of them at once, grouped into waves by `dependsOn`, so the
261
+ host knows what it can run in parallel.
262
+
263
+ ## What the package decides, and what it doesn't
264
+
265
+ **The package never calls a model.** The agent authors; the host spawns; the
266
+ package decides everything that can be decided by comparison.
267
+
268
+ | | authored by a model | decided by the package |
269
+ | --- | --- | --- |
270
+ | What the contract says | ✅ | |
271
+ | Whether the contract is well-formed | | ✅ |
272
+ | What each worker is told | | ✅ |
273
+ | The code each worker writes | ✅ | |
274
+ | Whether a decision contradicts one in force | | ✅ |
275
+ | Whether an acceptance criterion passed | | ✅ |
276
+ | What the design doc says | | ✅ (generated from the log) |
277
+
278
+ Every row on the right is a comparison, a schema check or a subprocess exit
279
+ code. Two tests enforce the split: nothing in the decision path may leave the
280
+ process by any means, and no file anywhere — including `accept`, which exists
281
+ to spawn subprocesses — may reach a model.
282
+
283
+ This is the differentiator, not a gap. Fan-out is a loop and a prompt; every
284
+ orchestrator has one. What nobody does is make the fleet's decisions binding on
285
+ each other, and that only stays trustworthy while no model is in the path.
286
+
287
+ ## Install
288
+
289
+ ```bash
290
+ claude plugin marketplace add harsh-chandak/shadowjoin
291
+ claude plugin install concord-engine@harsh-chandak
292
+ ```
293
+
294
+ <sub>The `harsh-chandak` marketplace is hosted in the shadowjoin repo and carries
295
+ both tools — that first line is not a typo.</sub>
296
+
297
+ Then describe the work and let it run the loop:
298
+
299
+ ```
300
+ /concord-engine:concord-engine add refunds across the API, the client and the schema
301
+ ```
302
+
303
+ Or without Claude Code:
304
+
305
+ ```bash
306
+ npx concord-engine --help
307
+ ```
308
+
309
+ ## Use
310
+
311
+ Leads and the PM decide. **Workers write code and decide nothing.**
312
+
313
+ ```bash
314
+ # a decision, gated: --unit enforces what this unit is allowed to settle,
315
+ # and the subject must be one the contract declares
316
+ concord-engine decide --contract add-refunds --unit api \
317
+ --kind convention --subject "error response body shape" \
318
+ --value 'every non-2xx body is {"error":"<message>"}' \
319
+ --as api --rationale "the client shows it to a person"
320
+
321
+ concord-engine check # exits 1 if anything contradicts
322
+ concord-engine verify # are the decided routes actually served?
323
+ concord-engine report # how often did a conflict reach you?
324
+ ```
325
+
326
+ Outside a contract, `decide` still refuses a decision that contradicts one in
327
+ force — it just cannot check what a unit was allowed to settle, because
328
+ without `--unit` there is nothing that says.
329
+
330
+ `verify` means route verification, as it did in v0.1. Acceptance criteria are
331
+ run by `accept`.
332
+
333
+ Five kinds, each with its own comparison rule:
334
+
335
+ | kind | value | compared by |
336
+ | --- | --- | --- |
337
+ | `naming` | `"user_id"` | exact |
338
+ | `convention` | `"snake_case in payloads"` | exact |
339
+ | `api-contract` | `{"method":"POST","path":"/orders","status":201}` | structurally, ignoring trailing slashes and param naming |
340
+ | `enum` | `["pending","paid","shipped"]` | set equality |
341
+ | `schema` | `{"field":"id","type":"uuid","nullable":false}` | structurally |
342
+
343
+ ## Where the log lives
344
+
345
+ `.concord-engine/decisions.jsonl`, in the working directory — append-only JSON
346
+ Lines, one decision per line, no parser needed to read it. Contracts sit beside
347
+ it in `.concord-engine/contracts/`.
348
+
349
+ **Commit it.** The log is the contract between your agents, so it has to travel
350
+ with the code the way a migration does. Set `CONCORD_ENGINE_DIR` or pass
351
+ `--dir` to put it elsewhere; the CLI and the MCP server resolve the location
352
+ the same way, so `check` always audits the file the server writes.
353
+
354
+ ## Who resolves what
355
+
356
+ ```
357
+ worker vs worker -> team lead
358
+ lead vs lead -> PM
359
+ PM can't resolve -> you
360
+ ```
361
+
362
+ Adjudication is read off the hierarchy, not judged. A PM decision overrides a
363
+ lead's — that's an override, not a conflict. Two leads who disagree go to the
364
+ PM. Two PMs who disagree reach you, because nothing outranks them.
365
+
366
+ Two attempts at a layer, then it escalates. What reaches you is a packet — both
367
+ positions, who holds each, why, and what has already been tried:
368
+
369
+ ```
370
+ CONFLICT c-01fc0217 naming: user identifier in API payloads
371
+ backend-lead (lead) user_id "matches the users table column"
372
+ frontend-lead (lead) userId "matches the TypeScript style guide"
373
+
374
+ tried: proposed user_id; frontend-lead rejected, 3 call sites already public
375
+ tried: proposed an alias field; rejected as duplication
376
+ owner: you
377
+ ```
378
+
379
+ Not "something went wrong, go read the logs."
380
+
381
+ ## The design doc nobody writes
382
+
383
+ `docs` renders the run from the decision log: the goal, what was in and out of
384
+ scope, every decision in force with its author, timestamp and rationale, and a
385
+ table of the commands that proved the work. It is generated, so it can't drift
386
+ from what was actually decided — and it's the first design doc that costs
387
+ nothing to keep current, because nobody writes it.
388
+
389
+ ## The number that matters
390
+
391
+ ```
392
+ $ concord-engine report
393
+
394
+ 47 conflicts recorded
395
+ settled without you 44
396
+ reached the PM 11
397
+ reached you 3 6.4%
398
+ ```
399
+
400
+ Every other orchestrator reports agents launched, tokens spent, tasks
401
+ completed. None of them reports whether the system handled itself — which is
402
+ the only number that says whether running ten agents actually cost you less
403
+ than running one.
404
+
405
+ ## What the fan-out cost
406
+
407
+ The saving is structural, not a trick: **a worker receives its brief, not the
408
+ conversation.** A brief is one unit's slice plus the decisions that bind it —
409
+ small, fixed, and identical across the run. A transcript is none of those
410
+ things.
411
+
412
+ The package measures **bytes**, because bytes are what it can count. Run on
413
+ this repo's own contract — five units, five binding decisions. Reproduce it
414
+ by cloning and running the same command:
415
+
416
+ ```
417
+ $ concord-engine cost --contract concord-engine-v2
418
+ mcp 2333 B brief vs 4132 B whole contract
419
+ skill 2470 B brief vs 4132 B whole contract
420
+ tests 2286 B brief vs 4132 B whole contract
421
+ readme 2297 B brief vs 4132 B whole contract
422
+ e2e 2214 B brief vs 4132 B whole contract
423
+
424
+ 11600 B sent instead of 20660 B — 44% less
425
+ ~2900 vs ~5165 tokens, ESTIMATED at 4 chars/token.
426
+ No tokenizer ships with this package; these are divisions, not counts.
427
+ ```
428
+
429
+ Every token figure it prints is labelled an estimate and states the
430
+ chars-per-token assumption behind it. The baseline is the whole contract per
431
+ worker, not your real session — your actual saving is larger, and this tool
432
+ will not guess by how much.
433
+
434
+ ## A worked example
435
+
436
+ `examples/linkfold.contract.json` is the contract from the run this README
437
+ keeps citing: a URL shortener built by four workers who never saw each other's
438
+ code, which came out working — 55 tests and a real HTTP round trip between
439
+ three units that agreed on a wire format they never discussed.
440
+
441
+ It carries the `openSubjects` the original run was missing, which is why that
442
+ run settled the error format twice under two names. CI revalidates it on every
443
+ push.
444
+
445
+ ```bash
446
+ concord-engine spec --from examples/linkfold.contract.json
447
+ concord-engine fleet --contract linkfold
448
+ concord-engine brief --contract linkfold api
449
+ ```
450
+
451
+ ## Honest limits
452
+
453
+ - **Only what's recorded is checked.** A lead who decides something in prose and
454
+ never records it is invisible to this.
455
+ - **Subjects are matched by wording, not by meaning.** Case, punctuation and
456
+ accents are normalised, so "User Identifier" and "user identifier" collide.
457
+ Two agents who describe the same thing in genuinely different words do not.
458
+ This is the tool's sharpest edge, and it is not theoretical: on the first
459
+ run of this loop against another project, two workers settled the error
460
+ format under "error response body shape" and "api error response body",
461
+ chose incompatible payloads, and `check` reported no contradiction because
462
+ it never compared them.
463
+
464
+ The mitigation is `openSubjects`: name the thing in the contract before
465
+ anyone decides it, and `decide --contract <id> --unit <id>` refuses a
466
+ subject the contract never declared, offering the declared spelling instead.
467
+ That makes subject identity a lookup rather than a guess. Matching subjects
468
+ by similarity would close the gap too, and would put a judgement back in the
469
+ decision path — which is the one thing this package will not do.
470
+
471
+ The residual risk is real: a decision recorded without `--contract`, or with
472
+ `--new-subject`, is matched by wording again.
473
+ - **`verify` only recognises Express-shaped registrations.** It looks for
474
+ `app`/`router`/`server`/`api` followed by a method call, plus FastAPI and
475
+ Flask decorators. A plain `node:http` server that switches on
476
+ `req.url` serves routes it cannot see. Broadening the pattern to any
477
+ identifier would make `cache.get("key")` look like a route, so it stays
478
+ narrow and under-reports.
479
+ - **`verify` reads literal paths only.** A router mounted under a prefix, or a
480
+ path built at runtime, is invisible to static extraction. It under-reports
481
+ rather than accusing falsely, and prints how many files it read so the number
482
+ isn't mistaken for completeness.
483
+ - **It doesn't spawn workers.** `fleet` emits briefs; your host spawns. A package
484
+ that shells out to another agent CLI inherits that tool's auth, rate limits
485
+ and failure modes, and stops being a library.
486
+ - **It doesn't write the contract.** Authoring is a model call, and model calls
487
+ happen outside the package.
488
+ - **It doesn't count tokens.** No tokenizer ships. Bytes are measured; tokens are
489
+ divided and labelled.
490
+ - **It doesn't verify the work was done.** `accept` runs the commands the
491
+ contract named. If the contract named weak commands, it will pass.
492
+ - **It doesn't judge which decision is better.** It reports that two can't both
493
+ hold, and who gets to rule.
494
+ - **A worker can still bypass the gate.** `decide` without `--unit` skips the
495
+ check on what that unit may decide, and `--force` records a contradiction
496
+ deliberately. Both are visible in the log, and `report` counts them. The
497
+ gate is a guardrail, not a sandbox.
498
+ - **Five kinds, deliberately.** `dependency` is deferred until semver range
499
+ intersection is implemented properly; a comparator that calls `^1.0.0` and
500
+ `^1.2.0` incompatible would be worse than not having it.
501
+
502
+ ## License
503
+
504
+ MIT © Harsh N. Chandak