@caddis/cli 0.4.3 → 0.4.5

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.
@@ -0,0 +1,150 @@
1
+ # Pipeline Glossary
2
+
3
+ > **Shipped as `CADDIS-GLOSSARY.md`.** This defines CADDIS's own vocabulary — the words the
4
+ > toolchain coined, which a user meets constantly and cannot guess from the plain word. Your
5
+ > project's own terms belong in `.caddis/kb/GLOSSARY.md` instead; run `/caddis:glossary` to
6
+ > build one. Two files, two subjects, deliberately different names.
7
+
8
+ Canonical terms used across all agents, skills, and pipeline infrastructure.
9
+ When writing agent instructions, skills, or documentation, use ONLY these terms.
10
+
11
+ Two vocabularies live here and they are NOT interchangeable. **Core Terms** onward describe the
12
+ agent PIPELINE (orchestrator, stages, `pipeline-state.json`). **Toolchain Terms** describe caddis
13
+ as a distributed product (pool, mirror, export target). A word can be canonical in one and absent
14
+ from the other; where they genuinely collide, the collision is named under Flagged ambiguities
15
+ rather than resolved by fiat.
16
+
17
+ Admission test, borrowed from `last30days-skill`'s CONCEPTS.md: a term belongs here when its
18
+ project meaning is **distinct enough from its ordinary technical sense that a newcomer would
19
+ misread it**. `commit` does not belong. `pool` does.
20
+
21
+ ---
22
+
23
+ ## Core Terms
24
+
25
+ | Canonical Term | Definition | DO NOT USE |
26
+ |---------------|------------|------------|
27
+ | artefact | Any file produced by an agent as pipeline output. This is the correct spelling in all contexts — field names, prose, section headers. | artifact, deliverable, output file |
28
+ | stage | A pipeline-level progression step (intent, prd, architect, plan, implement, tester, review, closed) | phase (for pipeline steps), step |
29
+ | phase | A subdivision of work within a Plan (Phase 1, Phase 2…). Phases exist WITHIN the implement stage. | stage (for plan subdivisions), step |
30
+ | chain_id | Feature tracking ID format: `FEAT-YYYY-MMDD-slug`. Links all artefacts for a feature. | feature_id, tracking_id |
31
+ | handoff | Transfer of work from one agent to another via Orchestrator | routing (for the transfer act), delegation, dispatch, hand-off |
32
+ | handoff payload | The `_notes.handoff_payload` object written by Orchestrator via `update_notes` MCP tool before routing to a specialist | routing context, task context |
33
+ | evidence bundle | Anchor's structured proof-of-work document (`agent-docs/anchor-evidence-*.md`) | proof, verification report |
34
+ | gate | A supervision checkpoint requiring approval before pipeline advances (`supervision_gates.*`) | checkpoint, approval point |
35
+ | HARD STOP | Absolute refusal to proceed — security violation, out-of-scope request, or rogue state edit | halt, block, refuse |
36
+ | skill | A reusable knowledge pack in `.github/skills/{category}/{name}/SKILL.md` | SKILL (when referring to the concept, not the filename) |
37
+ | prompt | A reusable prompt file in `.github/prompts/*.prompt.md` | skill (when referring to prompt files — prompts are not skills) |
38
+ | onboarding prompt | The project bootstrap prompt at `.github/prompts/onboarding.prompt.md` | onboarding skill |
39
+
40
+ ## Pipeline Modes
41
+
42
+ | Term | Definition |
43
+ |------|------------|
44
+ | supervised | All gates require manual user approval. Default mode. |
45
+ | assisted | Manual gates with AI guidance hints. Auto-routing between stages. |
46
+ | autopilot | All gates auto-satisfied except `intent_approved`. Fully hands-free after intent approval. |
47
+
48
+ ## Result Statuses
49
+
50
+ ### MCP `notify_orchestrator` — `result_status` parameter
51
+
52
+ | Value | Meaning | Used By |
53
+ |-------|---------|---------|
54
+ | `complete` | Standard stage completion | All agents |
55
+ | `phase_complete` | Multi-phase loop — implement stays in implement for next phase | Implement agent |
56
+ | `recovered` | Recovery after guard failure | Orchestrator (debug flow) |
57
+
58
+ ### Agent-Specific Result Fields
59
+
60
+ | Agent | Field | Possible Values | Meaning |
61
+ |-------|-------|-----------------|---------|
62
+ | Tester | `tester_result.status` | `passed`, `failed` | All tests pass / any test fails |
63
+ | Code Reviewer | `Verdict` | `approved`, `revision-requested` | Code meets standards / needs changes |
64
+ | All agents (artefact YAML header) | `approval` | `approved`, `pending`, `revision-requested` | Artefact acceptance status |
65
+
66
+ ### Orchestrator Artefact Validation Guards
67
+
68
+ | Guard | Checks |
69
+ |-------|--------|
70
+ | `artefact_exists` | File at `artefact_path` exists on disk |
71
+ | `artefact_approved` | YAML header `approval:` field is `approved` |
72
+ | `all_phases_done` | All implementation phases complete |
73
+
74
+ ## File Naming Conventions
75
+
76
+ | Pattern | Example | Usage |
77
+ |---------|---------|-------|
78
+ | Agent file | `frontend-developer.agent.md` | Lowercase kebab-case matching `name` frontmatter field |
79
+ | Skill file | `.github/skills/{category}/{name}/SKILL.md` | Category folder + name folder + SKILL.md |
80
+ | Plan file | `.github/plans/{feature-slug}.md` | Feature slug from chain_id |
81
+ | Evidence file | `agent-docs/anchor-evidence-{feature}.md` | Per-feature evidence bundle |
82
+ | Artefact registry | `agent-docs/ARTIFACTS.md` | Single registry for all pipeline artefacts |
83
+ | Pipeline state | `.github/pipeline-state.json` | Live pipeline state — written ONLY by MCP tools |
84
+
85
+ ## Pipeline State Field Ownership
86
+
87
+ All writes to `pipeline-state.json` go through MCP tools. Only `stages[*].status`, `stages[*].artefact`, `stages[*].completed_at`, and `blocked_by` may be set via `editFiles`.
88
+
89
+ | Field | Writer | Tool |
90
+ |-------|--------|------|
91
+ | `current_stage` | Pipeline runner | `notify_orchestrator` |
92
+ | `supervision_gates[*]` | Orchestrator | `satisfy_gate` |
93
+ | `pipeline_mode` | Orchestrator | `set_pipeline_mode` |
94
+ | `_notes.*` | Orchestrator / agents | `update_notes` |
95
+ | `project`, `feature`, `type` | Orchestrator | `pipeline_init` / `pipeline_reset` |
96
+
97
+ ## Common Conflations to Avoid
98
+
99
+ | Wrong | Correct | Why |
100
+ |-------|---------|-----|
101
+ | "Phase 3 of the pipeline" | "The implement stage" | Phases are within a Plan; stages are pipeline-level |
102
+ | "The artifact registry" | "The artefact registry" | British spelling is canonical (matches all field names) |
103
+ | "Run the onboarding skill" | "Run the onboarding prompt" | `.prompt.md` files are prompts, not skills |
104
+ | "Route to the agent" | "Hand off to the agent" | Routing is Orchestrator's internal logic; handoff is the transfer act |
105
+ | `[Stage/Phase N]` | `[Phase N]` (within Plan) or `[Stage: implement]` (pipeline-level) | Don't conflate the two with a slash |
106
+
107
+ ---
108
+
109
+ ## Toolchain Terms
110
+
111
+ caddis-the-product, as opposed to the agent pipeline above. These are the words caddis invented
112
+ out of ordinary English, which is exactly why they need defining: a reader who guesses from the
113
+ plain word gets them wrong. Usage counts were measured across 444 markdown files on 2026-08-29.
114
+
115
+ | Canonical Term | Definition | DO NOT USE |
116
+ |---|---|---|
117
+ | pool | The single source of every shipped resource, living in `.github/` (skills, agents, commands, prompts). Everything a user installs is EXPORTED from the pool; nothing is authored in a bundle. Versioned independently of the CLI, and bumped only by `caddis-push` — never by hand. *(222 uses)* | bundle (for the source), library |
118
+ | bundle | One EXPORTED copy of the pool, shaped for a single target — `dist/runtime-resources/<target>/`. A bundle is generated output; editing one is always a mistake. | pool (for a built copy) |
119
+ | export target | A named consumer in `.github/runtime-targets.json` — claude, antigravity, antigravity-plugin, codex, copilot, plus `-extras` variants. Each takes a different subset and layout from the same pool. | platform, host (for a target) |
120
+ | mirror | The public repo `saajunaid/caddis-plugin`, refreshed by `caddis-push`. The SOURCE repo is private, so the mirror is what any other machine can actually reach. | upstream, remote |
121
+ | relay | `.caddis/relay.md` — the durable resume doc, rewritten by `/caddis:handoff` and injected at SessionStart. Machine-local and gitignored. Only changes when someone runs a handoff, so it is stale between them by design. *(168 uses)* | handoff doc, session notes |
122
+ | session state | `.caddis/session-state.md` — auto-captured by the `Stop` hook at the end of EVERY turn. Distinct from the relay: current without anyone deciding to make it current, and it holds no agreed next step. | relay (they are different files with different guarantees) |
123
+ | drift | A gap between what a consumer HAS installed and what the pool currently ships. `caddis status` reports it per agent. Not code drift, not schema drift. *(160 uses)* | staleness, lag |
124
+ | live-fire | Driving a real binary end to end rather than asserting against a manifest. Coined because three defects in one month passed every test and still gave a human a broken install: the tests asked "does the export match the manifest", never "would this work". An export target is not trusted until it has been live-fired once. | smoke test, integration test, manual test |
125
+ | parking-lot | `.caddis/parking-lot/` — the ONE register of future work. It exists because future work previously scattered to nine places and no session could answer "what is left". Items carry `severity` and `status`; the backlog IS the plan when no plan is active. *(41 uses)* | backlog file, TODO list, icebox |
126
+ | model lane | A named provider route reached by its own binary — `claude-oss`, `claude-glm`, `claude-deepseek` — all speaking the Anthropic protocol. A lane is a route, not a model. | provider, backend, endpoint |
127
+ | core / extras | The two shipping tiers. Core is an EXPLICIT allowlist in `.github/runtime-targets.json`; anything not named there lands in extras. A skill defaults to extras, so placement is a decision, never an accident. | tier 1 / tier 2, full / lite |
128
+ | harness | The agent runtime that loads caddis — Claude Code, agy, Codex, Copilot. caddis is agent-agnostic; "the harness" without a name means whichever one is running. *(135 uses)* | client, IDE, platform |
129
+
130
+ ## Flagged ambiguities
131
+
132
+ Named rather than resolved. Pretending the vocabulary is clean is how a glossary starts lying.
133
+
134
+ - **`gate` is overloaded, badly.** Core Terms defines it as a supervision checkpoint requiring
135
+ approval. That is one of at least twelve senses in live use: *exit gate* (20), *quality gate*
136
+ (9), *evidence gate* (5), *decision gate* (3), plus pre-publish, handover and privacy gates —
137
+ none of which involve pipeline supervision. The word appears 1019 times across 168 files.
138
+ **Prefer the full two-word term everywhere**; a bare "gate" is ambiguous and the reader cannot
139
+ tell which one you meant.
140
+
141
+ - **`artefact` vs `artifact`.** Core Terms makes `artefact` canonical, and for pipeline outputs it
142
+ is. But the toolchain's own code says `artifact_root` and "artifact dir" (17 uses in
143
+ `scripts/setup_project_ai.py` alone), and those are API names that cannot be respelled without
144
+ a breaking change. So: **artefact** in pipeline prose and field names; **artifact dir /
145
+ `artifact_root`** when naming the `.caddis/` directory mechanism. Do not "fix" one into the other.
146
+
147
+ - **`skill` means two things across harnesses.** In the pool it is
148
+ `.github/skills/<category>/<name>/SKILL.md`. But agy CONVERTS commands into skills at install
149
+ time, so `/caddis:catchup` ships as a command and arrives as an agy skill. A skill count that
150
+ disagrees between two agents is usually this, not a bug.
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "caddis",
3
- "version": "1.3.88",
3
+ "version": "1.3.91",
4
4
  "description": "caddis — agent-agnostic dev harness as an Antigravity (agy) plugin: TDD skills + commands, single-sourced with the Claude Code plugin."
5
5
  }
@@ -0,0 +1,268 @@
1
+ """caddis_glossary — propose the words a project INVENTED, so they can be defined once.
2
+
3
+ WHY. Every project coins vocabulary out of ordinary English, and those are the worst words in
4
+ the codebase: a reader who guesses from the plain word gets them wrong and never knows. caddis
5
+ itself coined `pool`, `relay`, `drift`, `parking-lot`, `live-fire` — and its own glossary went
6
+ five and a half months without mentioning any of them, because nothing measured the gap.
7
+
8
+ WHAT THIS DOES AND DOES NOT DO. It PROPOSES candidates by measurement. It never writes a
9
+ definition, because a definition is domain knowledge that only a person or a model reading the
10
+ code can supply. A file of correct words with empty meanings is worse than no file: it looks
11
+ authoritative and teaches nothing. `/caddis:glossary` is the half that writes the meanings.
12
+
13
+ python caddis_glossary.py # propose candidates, ranked
14
+ python caddis_glossary.py --check # exit 1 if a proposed term has no entry
15
+ python caddis_glossary.py --scaffold # create the file if it is missing
16
+
17
+ HOW A CANDIDATE IS CHOSEN. Frequency alone is useless — "the file" beats every real term. A
18
+ word is a candidate when it is COMMON IN THIS REPO but is not ordinary technical English. The
19
+ stop-list below is the load-bearing part, and it is deliberately generous: a false positive
20
+ costs a human three seconds to ignore, while a false negative leaves a coined word undefined
21
+ forever, which is the failure being fixed.
22
+
23
+ ACCURACY, MEASURED. On a normal application repo it is good: run against `docket` it proposed
24
+ `board`, `lane`, `card`, `gate` — that project's actual coined vocabulary, top of the list. On a
25
+ HARNESS repo full of documentation ABOUT agents it is noisier, because words like `protocol` and
26
+ `intent` are both ordinary English and genuinely overloaded here. It is a suggester, not an
27
+ oracle; a human or the model decides what earns an entry.
28
+
29
+ Stdlib only, cross-platform, and it never raises on a bad file — it is run by a command and by
30
+ a gate, and a traceback from either is worse than a missing suggestion.
31
+ """
32
+ from __future__ import annotations
33
+
34
+ import argparse
35
+ import collections
36
+ import os
37
+ import re
38
+ import sys
39
+
40
+ # ── what a project's own vocabulary lives in ────────────────────────────────────────────
41
+ DOC_GLOBS = (".md",)
42
+ SKIP_DIRS = {
43
+ ".git", "node_modules", ".venv", "venv", "dist", "build", "__pycache__",
44
+ ".mypy_cache", ".pytest_cache", "vendor", "site-packages", ".next", "target",
45
+ # Vendored and reference material is someone ELSE'S vocabulary. Without these, caddis's
46
+ # own run proposed `aws4` (33 files) and `mxgraph` (11) out of bundled AWS and draw-io
47
+ # reference docs — words no caddis reader will ever meet.
48
+ "references", "assets", "fixtures", ".archive", "archive", "examples", "sample",
49
+ "samples", "third_party", "vendored", "node_modules", "bundles", "runtime-resources",
50
+ }
51
+
52
+ # Ordinary technical English. A word here is never proposed, however often it appears.
53
+ # Longer than it looks necessary on purpose: the cost of a false positive is a human ignoring
54
+ # one line; the cost of a false negative is a coined word nobody ever defines.
55
+ COMMON = {
56
+ # english scaffolding
57
+ "the", "and", "for", "with", "that", "this", "from", "into", "than", "then", "when",
58
+ "where", "which", "what", "who", "why", "how", "not", "but", "all", "any", "each",
59
+ "one", "two", "only", "also", "more", "most", "less", "some", "such", "same", "other",
60
+ "its", "it's", "you", "your", "our", "their", "they", "them", "has", "have", "had",
61
+ "was", "were", "been", "being", "are", "can", "will", "would", "should", "must", "may",
62
+ "does", "did", "done", "doing", "use", "used", "using", "make", "made", "get", "got",
63
+ "see", "read", "write", "run", "runs", "running", "ran", "set", "put", "take", "give",
64
+ "new", "old", "first", "last", "next", "now", "here", "there", "before", "after",
65
+ "over", "under", "out", "off", "own", "per", "via", "yes", "no", "if", "in", "on", "of",
66
+ "to", "at", "by", "as", "is", "be", "or", "so", "up", "do", "an", "a",
67
+ # ordinary software vocabulary — predictable from the word itself
68
+ "file", "files", "path", "paths", "dir", "directory", "folder", "code", "line", "lines",
69
+ "test", "tests", "testing", "error", "errors", "bug", "fix", "fixed", "build", "built",
70
+ "commit", "commits", "branch", "merge", "push", "pull", "repo", "repository", "git",
71
+ "api", "cli", "url", "http", "https", "json", "yaml", "toml", "html", "css", "sql",
72
+ "user", "users", "name", "names", "value", "values", "key", "keys", "type", "types",
73
+ "list", "lists", "table", "tables", "row", "rows", "column", "field", "fields",
74
+ "function", "method", "class", "module", "package", "library", "script", "scripts",
75
+ "server", "client", "request", "response", "data", "input", "output", "config",
76
+ "version", "release", "install", "update", "delete", "create", "add", "remove",
77
+ "return", "returns", "call", "calls", "check", "checks", "log", "logs", "note", "notes",
78
+ "step", "steps", "case", "cases", "example", "default", "option", "options", "flag",
79
+ "flags", "command", "commands", "tool", "tools", "agent", "agents", "model", "models",
80
+ "session", "sessions", "context", "prompt", "prompts", "token", "tokens", "cache",
81
+ "state", "status", "result", "results", "change", "changes", "work", "works", "time",
82
+ "does", "doc", "docs", "document", "section", "page", "text", "content", "format",
83
+ "project", "projects", "team", "user", "system", "service", "process", "task", "tasks",
84
+ "issue", "issues", "problem", "solution", "reason", "cause", "point", "part", "way",
85
+ "thing", "things", "need", "needs", "want", "know", "like", "just", "even", "still",
86
+ "never", "always", "every", "both", "because", "while", "since", "about", "against",
87
+ # added after the first real run proposed all of these — ordinary in any codebase
88
+ "skill", "skills", "plan", "plans", "phase", "phases", "design", "designs", "review",
89
+ "reviews", "mode", "modes", "pattern", "patterns", "implementation", "reference",
90
+ "description", "descriptions", "workflow", "workflows", "feature", "features",
91
+ "component", "components", "interface", "structure", "approach", "detail", "details",
92
+ "requirement", "requirements", "specification", "architecture", "instructions",
93
+ "guidelines", "standard", "standards", "best", "practice", "practices", "overview",
94
+ "summary", "report", "reports", "action", "actions", "level", "levels", "order",
95
+ "number", "count", "size", "start", "stop", "begin", "end", "open", "close", "load",
96
+ "save", "store", "fetch", "send", "receive", "handle", "handles", "support", "supports",
97
+ "references", "security", "scope", "verification", "react", "python", "typescript",
98
+ }
99
+
100
+ _WORD = re.compile(r"[a-z][a-z0-9]*(?:-[a-z0-9]+)*", re.I)
101
+
102
+ # A term must appear at least this often, in at least this many files, to be proposed.
103
+ # One mention is a passing usage; a coined term earns its definition by recurring.
104
+ MIN_USES = 12
105
+ MIN_FILES = 3
106
+ MAX_SUGGESTIONS = 25
107
+ # A coined term is specific, so in a LARGE corpus it shows up in a minority of files. Measured:
108
+ # `description` appeared in 2112 of ~2500 caddis docs and outranked every real term.
109
+ #
110
+ # But this rule INVERTS on a small repo. Applied unconditionally it deleted docket's four best
111
+ # candidates — `board`, `lane`, `card`, `docket` — because in a 50-file repo the core vocabulary
112
+ # genuinely is in most files. So it only engages once there are enough files for "share" to
113
+ # mean anything.
114
+ MAX_FILE_SHARE = 0.25
115
+ SHARE_RULE_MIN_CORPUS = 200
116
+
117
+
118
+ def _doc_files(root: str) -> list[str]:
119
+ found = []
120
+ for dirpath, dirnames, filenames in os.walk(root):
121
+ dirnames[:] = [d for d in dirnames if d not in SKIP_DIRS and not d.startswith(".venv")]
122
+ for n in filenames:
123
+ if n.endswith(DOC_GLOBS):
124
+ found.append(os.path.join(dirpath, n))
125
+ return found
126
+
127
+
128
+ def glossary_path(root: str) -> str:
129
+ """`.caddis/kb/GLOSSARY.md` — beside the KB, which is where reference docs live."""
130
+ return os.path.join(root, ".caddis", "kb", "GLOSSARY.md")
131
+
132
+
133
+ def defined_terms(path: str) -> set[str]:
134
+ """Canonical terms are the first cell of each markdown table row."""
135
+ if not os.path.isfile(path):
136
+ return set()
137
+ terms = set()
138
+ try:
139
+ with open(path, encoding="utf-8", errors="replace") as fh:
140
+ for line in fh:
141
+ if not line.startswith("|"):
142
+ continue
143
+ cells = [c.strip() for c in line.strip().strip("|").split("|")]
144
+ if len(cells) < 2 or not cells[0] or set(cells[0]) <= {"-", ":", " "}:
145
+ continue
146
+ head = cells[0].strip("`*").lower()
147
+ if head in {"term", "canonical term", "word"}:
148
+ continue
149
+ terms.add(head)
150
+ except OSError:
151
+ return set()
152
+ return terms
153
+
154
+
155
+ def propose(root: str) -> list[tuple[str, int, int]]:
156
+ """Return [(term, uses, files)] — words this repo says often that plain English does not."""
157
+ uses: collections.Counter = collections.Counter()
158
+ files: dict[str, set] = collections.defaultdict(set)
159
+ for f in _doc_files(root):
160
+ try:
161
+ text = open(f, encoding="utf-8", errors="replace").read().lower()
162
+ except OSError:
163
+ continue
164
+ # Strip fenced code and inline code: identifiers are not project vocabulary.
165
+ text = re.sub(r"```.*?```", " ", text, flags=re.S)
166
+ text = re.sub(r"`[^`]*`", " ", text)
167
+ for w in _WORD.findall(text):
168
+ if len(w) < 4 or w in COMMON:
169
+ continue
170
+ uses[w] += 1
171
+ files[w].add(f)
172
+ total = max(1, len(_doc_files(root)))
173
+ out = [
174
+ (w, n, len(files[w]))
175
+ for w, n in uses.items()
176
+ if n >= MIN_USES
177
+ and len(files[w]) >= MIN_FILES
178
+ and (total < SHARE_RULE_MIN_CORPUS or (len(files[w]) / total) <= MAX_FILE_SHARE)
179
+ ]
180
+ out.sort(key=lambda r: (-r[1], r[0]))
181
+ return out
182
+
183
+
184
+ TEMPLATE = """# Glossary
185
+
186
+ The words this project invented out of ordinary English, defined once.
187
+
188
+ **Admission test:** a term belongs here when its meaning in this project is *distinct enough
189
+ from its ordinary technical sense that a newcomer would misread it*. `commit` does not belong.
190
+ A word this team uses in its own way does.
191
+
192
+ Proposed by `caddis_glossary.py`, which measures which words this repo says often that plain
193
+ English does not. It cannot write the definitions — run `/caddis:glossary` and the model will
194
+ draft them from the actual code, or write them yourself.
195
+
196
+ | Term | Definition | DO NOT USE |
197
+ |---|---|---|
198
+ | _example_ | Replace this row. A definition should say what the word means HERE, and why the ordinary reading is wrong. | the synonyms that cause confusion |
199
+
200
+ ## Flagged ambiguities
201
+
202
+ Name the words that are still overloaded rather than pretending the vocabulary is clean. A
203
+ glossary that hides its own dirt starts lying. Delete this section only when there is genuinely
204
+ nothing to flag.
205
+ """
206
+
207
+
208
+ def main(argv: list[str] | None = None) -> int:
209
+ ap = argparse.ArgumentParser(description=__doc__.splitlines()[0])
210
+ ap.add_argument("--root", default=os.getcwd())
211
+ ap.add_argument("--check", action="store_true",
212
+ help="exit 1 if a proposed term has no entry")
213
+ ap.add_argument("--scaffold", action="store_true",
214
+ help="write the glossary template if it does not exist")
215
+ args = ap.parse_args(argv)
216
+
217
+ root = os.path.abspath(args.root)
218
+ path = glossary_path(root)
219
+
220
+ if args.scaffold:
221
+ if os.path.isfile(path):
222
+ print("[glossary] present — kept: %s" % os.path.relpath(path, root))
223
+ else:
224
+ os.makedirs(os.path.dirname(path), exist_ok=True)
225
+ with open(path, "w", encoding="utf-8", newline="\n") as fh:
226
+ fh.write(TEMPLATE)
227
+ print("[glossary] wrote %s" % os.path.relpath(path, root))
228
+ return 0
229
+
230
+ candidates = propose(root)
231
+ defined = defined_terms(path)
232
+ missing = [(w, n, f) for w, n, f in candidates if w not in defined]
233
+
234
+ if args.check:
235
+ # Advisory by design. A project may legitimately decide a frequent word needs no
236
+ # definition, and a gate that cannot be satisfied gets bypassed rather than obeyed.
237
+ if not os.path.isfile(path):
238
+ print("[glossary] no %s — run with --scaffold" % os.path.relpath(path, root))
239
+ return 0
240
+ if missing:
241
+ print("[glossary] %d frequent term(s) with no entry:" % len(missing))
242
+ for w, n, f in missing[:MAX_SUGGESTIONS]:
243
+ print(" %-24s %4d uses in %d files" % (w, n, f))
244
+ print(" Define them, or leave them — this is advisory. `/caddis:glossary` drafts them.")
245
+ return 1
246
+ print("[glossary] every frequent term is defined (%d entries)" % len(defined))
247
+ return 0
248
+
249
+ if not candidates:
250
+ print("[glossary] no candidates — too few docs, or nothing recurs enough.")
251
+ return 0
252
+ print("Candidate vocabulary for %s" % root)
253
+ print(" (>= %d uses across >= %d files, ordinary English excluded)\n" % (MIN_USES, MIN_FILES))
254
+ print(" %-26s %6s %6s %s" % ("TERM", "USES", "FILES", "DEFINED?"))
255
+ for w, n, f in candidates[:MAX_SUGGESTIONS]:
256
+ print(" %-26s %6d %6d %s" % (w, n, f, "yes" if w in defined else "-- no --"))
257
+ if missing:
258
+ print("\n %d undefined. Run `/caddis:glossary` to draft definitions from the code."
259
+ % len(missing))
260
+ return 0
261
+
262
+
263
+ if __name__ == "__main__":
264
+ try:
265
+ sys.exit(main())
266
+ except Exception as exc: # pragma: no cover — a suggester must never break a caller
267
+ print("[glossary] skipped: %s" % exc)
268
+ sys.exit(0)
@@ -902,6 +902,39 @@ memory.jsonl
902
902
  """
903
903
 
904
904
 
905
+ GLOSSARY_TEMPLATE = """\
906
+ # Glossary
907
+
908
+ The words this project invented out of ordinary English, defined once.
909
+
910
+ **Admission test:** a term belongs here when its meaning in this project is *distinct
911
+ enough from its ordinary technical sense that a newcomer would misread it*. `commit` does
912
+ not belong. A word this team uses in its own way does.
913
+
914
+ To find candidates, run the measurer. It ranks the words this repo says often that plain
915
+ English does not, and marks which already have an entry:
916
+
917
+ python <caddis>/scripts/caddis_glossary.py
918
+
919
+ It cannot write the definitions - only a person or the model reading the code can say what
920
+ a coined word MEANS here. Run `/caddis:glossary` to have them drafted, or write them.
921
+
922
+ This file is for YOUR project's vocabulary. caddis's own coined words - pool, relay, drift,
923
+ export target - are defined separately in `CADDIS-GLOSSARY.md` at the plugin root, which also
924
+ serves as a worked example of what a good entry looks like. Do not copy those terms in here.
925
+
926
+ | Term | Definition | DO NOT USE |
927
+ |---|---|---|
928
+ | _example_ | Replace this row. Say what the word means HERE, and why the ordinary reading is wrong. | the synonyms that cause confusion |
929
+
930
+ ## Flagged ambiguities
931
+
932
+ Name the words that are still overloaded rather than pretending the vocabulary is clean. A
933
+ glossary that hides its own dirt starts lying. Delete this section only when there is
934
+ genuinely nothing to flag.
935
+ """
936
+
937
+
905
938
  ARTIFACT_CONFIG_EXAMPLE = """\
906
939
  # caddis per-repo configuration — a documented template.
907
940
  #
@@ -1198,6 +1231,20 @@ def scaffold_artifact_dir(target: Path, dry: bool) -> list[str]:
1198
1231
  em.parent.mkdir(parents=True, exist_ok=True)
1199
1232
  em.write_text(ENVIRONMENT_MAP_TEMPLATE, encoding="utf-8")
1200
1233
  notes.append(f"scaffold: wrote {label}/kb/environment-map.md")
1234
+ # A vocabulary stub, seeded but never auto-filled. `caddis_glossary.py` can measure
1235
+ # which words a repo coined, but only a person or the model reading the code can say
1236
+ # what they MEAN - and a file of correct words with empty meanings looks authoritative
1237
+ # while teaching nothing. The stub carries the admission test so whoever fills it in
1238
+ # knows what qualifies.
1239
+ gl = root / "kb" / "GLOSSARY.md"
1240
+ if gl.exists():
1241
+ notes.append(f"scaffold: {label}/kb/GLOSSARY.md present - kept")
1242
+ else:
1243
+ if not dry:
1244
+ gl.parent.mkdir(parents=True, exist_ok=True)
1245
+ gl.write_text(GLOSSARY_TEMPLATE, encoding="utf-8")
1246
+ notes.append(f"scaffold: wrote {label}/kb/GLOSSARY.md (run /caddis:glossary to fill it)")
1247
+
1201
1248
  gi = root / ".gitignore"
1202
1249
  if gi.exists():
1203
1250
  # APPEND what is missing rather than keeping the file frozen. The old behaviour was
@@ -76,6 +76,7 @@
76
76
  | Feature Plan | `feature-plan/` | Create a phased, TDD-structured implementation plan that acts as the durable spine for multi-session work |
77
77
  | Gate Preflight | `gate-preflight/` | Deterministic headless preflight — validates a plan against the actual codebase, self-contained (never depends on a skill activating) |
78
78
  | Gate Review | `gate-review/` | Deterministic headless code review — adversarial diff review, self-contained (never depends on a skill activating) |
79
+ | Glossary | `glossary/` | Define the words this project invented, in one place — .caddis/kb/GLOSSARY.md |
79
80
  | Handoff | `handoff/` | End-of-session handoff — capture exact state so the next session resumes with zero re-discovery |
80
81
  | Implement | `implement/` | Headless plan executor — implement an approved plan phase-by-phase on the current feature branch, TDD-first, committing per phase |
81
82
  | Kb | `kb/` | Rebuild the KB index (.caddis/kb/DOC-MAP.md) — create it if missing, index un-indexed notes, report dangling links |
@@ -0,0 +1,89 @@
1
+ ---
2
+ name: glossary
3
+ description: Define the words this project invented, in one place — .caddis/kb/GLOSSARY.md
4
+ ---
5
+
6
+ # /glossary — define what this project coined
7
+
8
+ Every project invents vocabulary out of ordinary English, and those are the most dangerous words
9
+ in the codebase: a reader who guesses from the plain word gets it wrong and never finds out.
10
+ caddis itself coined `pool`, `relay`, `drift`, `parking-lot` — and its own glossary went five and
11
+ a half months without mentioning any of them, because nothing measured the gap.
12
+
13
+ Your job is the half a script cannot do: **write what the words mean here.**
14
+
15
+ ## Step 1 — get the candidates
16
+
17
+ ```
18
+ python <caddis-plugin-root>/scripts/caddis_glossary.py
19
+ ```
20
+
21
+ It ranks the words this repo says often that plain English does not, and marks which already
22
+ have an entry. `--scaffold` creates `.caddis/kb/GLOSSARY.md` if it is missing.
23
+
24
+ It is a **suggester, not an oracle**. It is good on an application repo — run against a kanban
25
+ tool it proposed `board`, `lane`, `card` at the top — and noisier on a repo whose docs are ABOUT
26
+ software, where words like `protocol` are both ordinary and genuinely overloaded. Expect to
27
+ discard some suggestions. That is the intended cost: a false positive wastes three seconds, a
28
+ missing term stays undefined forever.
29
+
30
+ If the script is not available, read the repo yourself and look for the same thing.
31
+
32
+ ## Step 2 — apply the admission test to each candidate
33
+
34
+ A term belongs **only if its meaning here is distinct enough from its ordinary technical sense
35
+ that a newcomer would misread it.**
36
+
37
+ - `commit`, `branch`, `endpoint` — **no**. The ordinary meaning is correct.
38
+ - A word the team bent to its own purpose — **yes**. That is the whole point.
39
+
40
+ Discard the rest without comment. A padded glossary is skimmed and then ignored.
41
+
42
+ ## Step 3 — write each definition from the CODE, not from the word
43
+
44
+ Read where the term is actually used before defining it. A definition that restates the word
45
+ teaches nothing, and a wrong one is worse than a gap because it is believed.
46
+
47
+ Each row gets three things:
48
+
49
+ | Column | What goes in it |
50
+ |---|---|
51
+ | **Term** | The canonical spelling. Pick one and stop using the others. |
52
+ | **Definition** | What it means HERE, and — where it helps — why the ordinary reading misleads. Say the thing a newcomer would get wrong. |
53
+ | **DO NOT USE** | The synonyms actually causing confusion in this repo. Leave blank rather than inventing some. |
54
+
55
+ **A worked example ships with caddis.** `CADDIS-GLOSSARY.md` at the plugin root defines caddis's
56
+ own coined vocabulary the same way — read a few entries before writing yours. It is caddis's
57
+ words, not this project's: never copy its terms into a project glossary.
58
+
59
+ Good: *"pool — the single source of every shipped resource, in `.github/`. Everything installed is EXPORTED from it; nothing is authored in a bundle."*
60
+
61
+ Bad: *"pool — a pool of resources."*
62
+
63
+ ## Step 4 — flag what is still dirty
64
+
65
+ Fill the **Flagged ambiguities** section with the words that remain overloaded. This is the part
66
+ people skip and it is the most valuable part, because a glossary that hides its own dirt starts
67
+ lying.
68
+
69
+ Look for a term used in several incompatible senses. In caddis, `gate` is defined as one thing
70
+ and used as at least twelve — exit gate, quality gate, evidence gate, privacy gate. The honest
71
+ move is to name the overload and recommend the two-word form, not to pick a winner by fiat.
72
+
73
+ Also flag a term whose canonical spelling the code itself contradicts. Do not "fix" either side
74
+ when both are load-bearing; document the split and where each applies.
75
+
76
+ ## Step 5 — report
77
+
78
+ Say how many terms you added, how many candidates you rejected and roughly why, and what you
79
+ flagged as ambiguous. If you found nothing worth adding, say that — it is a real answer, and
80
+ better than padding the file to look productive.
81
+
82
+ ## Keeping it honest
83
+
84
+ Re-run `caddis_glossary.py --check` later. It exits 1 when a frequent term has no entry. It is
85
+ **advisory**: a project may reasonably decide a common word needs no definition, and a gate that
86
+ cannot be satisfied gets bypassed rather than obeyed.
87
+
88
+ Do not enforce the DO-NOT-USE column by grepping. Most banned synonyms are ordinary English —
89
+ "step", "block", "refuse" — and a check that cries wolf gets deleted, leaving no check at all.
@@ -1,7 +1,7 @@
1
1
  {
2
- "poolVersion": "1.3.88",
2
+ "poolVersion": "1.3.91",
3
3
  "bundles": {
4
- "antigravity-plugin": "1.3.88",
4
+ "antigravity-plugin": "1.3.91",
5
5
  "antigravity-plugin-extras": "1.3.28",
6
6
  "codex": "unknown"
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@caddis/cli",
3
- "version": "0.4.3",
3
+ "version": "0.4.5",
4
4
  "description": "Install, update and diagnose the caddis dev harness across every coding agent you use (Claude Code, agy/Antigravity, and more).",
5
5
  "keywords": [
6
6
  "caddis",