codexspec 0.7.10__tar.gz → 0.7.12__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.
- {codexspec-0.7.10 → codexspec-0.7.12}/PKG-INFO +2 -1
- {codexspec-0.7.10 → codexspec-0.7.12}/README.md +1 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/pyproject.toml +1 -1
- {codexspec-0.7.10 → codexspec-0.7.12}/src/codexspec/__init__.py +1 -1
- {codexspec-0.7.10 → codexspec-0.7.12}/src/codexspec/commands/installer.py +10 -3
- {codexspec-0.7.10 → codexspec-0.7.12}/src/codexspec/profile.py +30 -11
- codexspec-0.7.12/templates/commands/distill.md +216 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/templates/commands/evolve.md +2 -0
- codexspec-0.7.12/templates/commands/onboard.md +89 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/templates/commands/specify.md +1 -1
- codexspec-0.7.10/templates/commands/distill.md +0 -111
- {codexspec-0.7.10 → codexspec-0.7.12}/.gitignore +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/LICENSE +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/codexspec-icon.svg +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/codexspec-logo-dark.svg +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/codexspec-logo-light.svg +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/scripts/bash/check-i18n-completeness.sh +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/scripts/bash/check-i18n-structure.sh +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/scripts/bash/check-prerequisites.sh +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/scripts/bash/common.sh +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/scripts/bash/create-new-feature.sh +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/scripts/bash/review-context.sh +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/scripts/powershell/check-prerequisites.ps1 +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/scripts/powershell/common.ps1 +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/scripts/powershell/create-new-feature.ps1 +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/scripts/powershell/review-context.ps1 +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/src/codexspec/commands/__init__.py +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/src/codexspec/i18n.py +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/src/codexspec/idea.md +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/src/codexspec/integrations/__init__.py +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/src/codexspec/integrations/base.py +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/src/codexspec/integrations/claude.py +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/src/codexspec/integrations/codex.py +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/src/codexspec/translator.py +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/templates/commands/analyze.md +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/templates/commands/checklist.md +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/templates/commands/clarify.md +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/templates/commands/commit-staged.md +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/templates/commands/config.md +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/templates/commands/constitution.md +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/templates/commands/debug.md +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/templates/commands/generate-spec.md +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/templates/commands/implement-tasks.md +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/templates/commands/plan-to-tasks.md +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/templates/commands/pr.md +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/templates/commands/quick.md +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/templates/commands/release-notes.md +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/templates/commands/review-code.md +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/templates/commands/review-design.md +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/templates/commands/review-plan.md +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/templates/commands/review-spec.md +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/templates/commands/review-tasks.md +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/templates/commands/spec-to-design.md +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/templates/commands/spec-to-plan.md +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/templates/commands/tasks-to-issues.md +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/templates/docs/checklist-template.md +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/templates/docs/constitution-template.md +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/templates/docs/design-template.md +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/templates/docs/plan-template-detailed.md +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/templates/docs/plan-template-simple.md +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/templates/docs/requirements-template.md +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/templates/docs/spec-template-detailed.md +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/templates/docs/spec-template-simple.md +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/templates/docs/tasks-template-detailed.md +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/templates/docs/tasks-template-simple.md +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/templates/translations/de.json +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/templates/translations/en.json +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/templates/translations/es.json +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/templates/translations/fr.json +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/templates/translations/ja.json +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/templates/translations/ko.json +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/templates/translations/pt-BR.json +0 -0
- {codexspec-0.7.10 → codexspec-0.7.12}/templates/translations/zh-CN.json +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: codexspec
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.12
|
|
4
4
|
Summary: CodexSpec - A Requirements-First SDD toolkit for Claude Code
|
|
5
5
|
Project-URL: Homepage, https://github.com/Zts0hg/codexspec
|
|
6
6
|
Project-URL: Repository, https://github.com/Zts0hg/codexspec
|
|
@@ -608,6 +608,7 @@ Implementation follows **conditional TDD workflow**:
|
|
|
608
608
|
| --- | --- |
|
|
609
609
|
| `/codexspec:distill` | Distill reusable cross-feature knowledge into `.codexspec/profile/` |
|
|
610
610
|
| `/codexspec:evolve` | Compile profile knowledge into a command/skill and contribute upstream via a reviewed PR |
|
|
611
|
+
| `/codexspec:onboard` | Cold-start the project profile by scanning an existing codebase (conventions + constraints) |
|
|
611
612
|
|
|
612
613
|
#### Git Workflow Commands
|
|
613
614
|
|
|
@@ -563,6 +563,7 @@ Implementation follows **conditional TDD workflow**:
|
|
|
563
563
|
| --- | --- |
|
|
564
564
|
| `/codexspec:distill` | Distill reusable cross-feature knowledge into `.codexspec/profile/` |
|
|
565
565
|
| `/codexspec:evolve` | Compile profile knowledge into a command/skill and contribute upstream via a reviewed PR |
|
|
566
|
+
| `/codexspec:onboard` | Cold-start the project profile by scanning an existing codebase (conventions + constraints) |
|
|
566
567
|
|
|
567
568
|
#### Git Workflow Commands
|
|
568
569
|
|
|
@@ -44,7 +44,7 @@ from .profile import ensure_profile_scaffold, inject_profile_block
|
|
|
44
44
|
from .translator import SUPPORTED_LANGUAGES, translate
|
|
45
45
|
|
|
46
46
|
# Version info
|
|
47
|
-
__version__ = "0.7.
|
|
47
|
+
__version__ = "0.7.12"
|
|
48
48
|
__author__ = "CodexSpec Team"
|
|
49
49
|
|
|
50
50
|
# Constitution file path constants
|
|
@@ -47,8 +47,8 @@ def get_commands_metadata() -> list[CommandMetadata]:
|
|
|
47
47
|
|
|
48
48
|
Returns:
|
|
49
49
|
List of CommandMetadata dictionaries sorted by category priority:
|
|
50
|
-
core (11) -> enhanced (
|
|
51
|
-
Total:
|
|
50
|
+
core (11) -> enhanced (8) -> git (3) -> review (1) -> utility (2)
|
|
51
|
+
Total: 25 commands
|
|
52
52
|
"""
|
|
53
53
|
return [
|
|
54
54
|
# Core Commands (11)
|
|
@@ -129,7 +129,7 @@ def get_commands_metadata() -> list[CommandMetadata]:
|
|
|
129
129
|
"category": "core",
|
|
130
130
|
"file_name": "implement-tasks.md",
|
|
131
131
|
},
|
|
132
|
-
# Enhanced Commands (
|
|
132
|
+
# Enhanced Commands (8)
|
|
133
133
|
{
|
|
134
134
|
"name": "clarify",
|
|
135
135
|
"display_name": "/codexspec:clarify",
|
|
@@ -172,6 +172,13 @@ def get_commands_metadata() -> list[CommandMetadata]:
|
|
|
172
172
|
"category": "enhanced",
|
|
173
173
|
"file_name": "evolve.md",
|
|
174
174
|
},
|
|
175
|
+
{
|
|
176
|
+
"name": "onboard",
|
|
177
|
+
"display_name": "/codexspec:onboard",
|
|
178
|
+
"description": "扫描现有代码库冷启动项目 profile(提取 conventions 与 constraints)",
|
|
179
|
+
"category": "enhanced",
|
|
180
|
+
"file_name": "onboard.md",
|
|
181
|
+
},
|
|
175
182
|
{
|
|
176
183
|
"name": "debug",
|
|
177
184
|
"display_name": "/codexspec:debug",
|
|
@@ -4,13 +4,13 @@ Wires a user project's ``.codexspec/profile/`` into the AI context files so that
|
|
|
4
4
|
knowledge distilled by ``/codexspec:distill`` is consulted in later work.
|
|
5
5
|
|
|
6
6
|
Store layout: **one record per file** under a per-category directory
|
|
7
|
-
(``constraints/`` ``conventions/`` ``pitfalls/`` ``decisions/``
|
|
8
|
-
feature branches each add differently-named
|
|
9
|
-
knowledge merges without conflict.
|
|
7
|
+
(``constraints/`` ``conventions/`` ``pitfalls/`` ``decisions/`` ``strategies/``
|
|
8
|
+
``runbooks/``). Because parallel feature branches each add differently-named
|
|
9
|
+
record files, their distilled knowledge merges without conflict.
|
|
10
10
|
|
|
11
11
|
Two concerns live here:
|
|
12
12
|
|
|
13
|
-
- ``ensure_profile_scaffold`` — create the
|
|
13
|
+
- ``ensure_profile_scaffold`` — create the six category directories (each kept
|
|
14
14
|
by a ``.gitkeep``) so every injected reference resolves, independent of whether
|
|
15
15
|
any knowledge has been distilled yet.
|
|
16
16
|
- ``render_profile_block`` / ``inject_profile_block`` — render a bounded,
|
|
@@ -27,7 +27,14 @@ from pathlib import Path
|
|
|
27
27
|
|
|
28
28
|
# Ordered: constraints first (highest weight, honored first). Each is a directory
|
|
29
29
|
# holding one record per file.
|
|
30
|
-
PROFILE_CATEGORIES = (
|
|
30
|
+
PROFILE_CATEGORIES = (
|
|
31
|
+
"constraints",
|
|
32
|
+
"conventions",
|
|
33
|
+
"pitfalls",
|
|
34
|
+
"decisions",
|
|
35
|
+
"strategies",
|
|
36
|
+
"runbooks",
|
|
37
|
+
)
|
|
31
38
|
|
|
32
39
|
PROFILE_BLOCK_START = "<!-- CODEXSPEC PROFILE START -->"
|
|
33
40
|
PROFILE_BLOCK_END = "<!-- CODEXSPEC PROFILE END -->"
|
|
@@ -43,24 +50,33 @@ _PROFILE_BLOCK = "".join(
|
|
|
43
50
|
"**Project constraints (highest priority — read these FIRST):** before any non-trivial work you MUST ",
|
|
44
51
|
"read every record under `.codexspec/profile/constraints/` — the project's hard prohibitions ",
|
|
45
52
|
"(严禁 / 仅允许). Honor them before anything else.\n\n",
|
|
46
|
-
"**
|
|
53
|
+
"**Actively recall relevant knowledge before non-trivial work.** Do not wait to stumble on it: ",
|
|
54
|
+
"scan the record headings and their `scope/when` (and, for strategies, `trigger`) lines across the ",
|
|
55
|
+
"categories below, and read every record whose condition matches the task you are about to start ",
|
|
47
56
|
"(each directory holds one record per file):\n\n",
|
|
48
57
|
"- `.codexspec/profile/conventions/` — cross-feature conventions / steering; ",
|
|
49
58
|
"read before adopting a pattern, structure, or naming choice.\n",
|
|
50
59
|
"- `.codexspec/profile/pitfalls/` — known traps and their workarounds; ",
|
|
51
60
|
"read before implementing or debugging in an area that may have bitten before.\n",
|
|
52
61
|
"- `.codexspec/profile/decisions/` — past cross-feature / architectural decisions; ",
|
|
53
|
-
"read before deciding in the same area, to reuse prior rationale rather than re-litigate it.\n
|
|
62
|
+
"read before deciding in the same area, to reuse prior rationale rather than re-litigate it.\n",
|
|
63
|
+
"- `.codexspec/profile/strategies/` — metacognitive trigger→action rules (and `scope: self` notes on ",
|
|
64
|
+
"the agent's own recurring slips); read before choosing an approach or when a fix is not converging.\n",
|
|
65
|
+
"- `.codexspec/profile/runbooks/` — ordered multi-step procedures with failure recovery; ",
|
|
66
|
+
"read before carrying out a known multi-step task.\n\n",
|
|
54
67
|
"Read the full record — each carries a `status` of `candidate` or `vetted`; ",
|
|
55
68
|
"weight `candidate` items with appropriate caution. A directory may be empty ",
|
|
56
|
-
"until `/codexspec:distill` has captured knowledge.\n",
|
|
69
|
+
"until `/codexspec:distill` has captured knowledge.\n\n",
|
|
70
|
+
"**Capture knowledge as you go.** When this session produces reusable cross-feature knowledge — even ",
|
|
71
|
+
"in plain chat or a non-SDD fix — run `/codexspec:distill` near that moment rather than only at ",
|
|
72
|
+
"wrap-up. It is non-blocking and early-exits when there is nothing new.\n",
|
|
57
73
|
f"{PROFILE_BLOCK_END}\n",
|
|
58
74
|
]
|
|
59
75
|
)
|
|
60
76
|
|
|
61
77
|
|
|
62
78
|
def ensure_profile_scaffold(target_dir: Path) -> Path:
|
|
63
|
-
"""Create ``.codexspec/profile/`` and its
|
|
79
|
+
"""Create ``.codexspec/profile/`` and its six category directories if absent.
|
|
64
80
|
|
|
65
81
|
Each category is a directory of one-record-per-file entries; a ``.gitkeep``
|
|
66
82
|
keeps an empty category tracked so every pointer resolves. Idempotent and
|
|
@@ -79,10 +95,13 @@ def ensure_profile_scaffold(target_dir: Path) -> Path:
|
|
|
79
95
|
def render_profile_block() -> str:
|
|
80
96
|
"""Render the bounded managed profile block.
|
|
81
97
|
|
|
82
|
-
Identical for CLAUDE.md and AGENTS.md: constraints and the
|
|
98
|
+
Identical for CLAUDE.md and AGENTS.md: constraints and the five on-demand
|
|
83
99
|
categories are all delivered as pointers to their directories — no ``@import``
|
|
84
100
|
anywhere — so the block is channel-neutral and the store can be
|
|
85
|
-
one-file-per-record directories that merge without conflict.
|
|
101
|
+
one-file-per-record directories that merge without conflict. The block also
|
|
102
|
+
directs active recall (scan record ``scope/when``/``trigger`` and read matches
|
|
103
|
+
before non-trivial work) and near-moment ``distill`` capture; it is a fixed
|
|
104
|
+
directive whose size is independent of how many records the profile holds.
|
|
86
105
|
"""
|
|
87
106
|
return _PROFILE_BLOCK
|
|
88
107
|
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Distill reusable, cross-feature knowledge from an interaction into the project profile
|
|
3
|
+
argument-hint: "[interaction segment or context to distill]"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Distill
|
|
7
|
+
|
|
8
|
+
## Language Preference
|
|
9
|
+
|
|
10
|
+
Read `.codexspec/config.yml`. Two independent language controls apply (each falls back to `language.output`, then English):
|
|
11
|
+
|
|
12
|
+
- **Interaction language** (`language.interaction`): language for all conversation with the user — questions, explanations, status messages, and `codexspec` CLI terminal output.
|
|
13
|
+
- **Document language** (`language.document`): language for generated artifact files (the profile records).
|
|
14
|
+
|
|
15
|
+
Converse in the interaction language and author artifacts in the document language. Apply the project's translation standard to both: translate by meaning (not word-for-word), keep English for terms with no good native equivalent, and write as if originally in that language. **Exception**: `evidence.facts` quotes the user's original words verbatim and MUST NOT be translated.
|
|
16
|
+
|
|
17
|
+
## User Input
|
|
18
|
+
|
|
19
|
+
`$ARGUMENTS`
|
|
20
|
+
|
|
21
|
+
## Operating Model
|
|
22
|
+
|
|
23
|
+
`distill` extracts the reusable, cross-feature knowledge produced during work and persists it to the project-level store `.codexspec/profile/`. It runs two ways:
|
|
24
|
+
|
|
25
|
+
- **Auto (primary)**: embedded in wrap-up commands (`implement-tasks` on completion, `commit-staged`, `pr`), gated by `workflow.auto_distill` in `.codexspec/config.yml` (**default enabled**; disabled only when explicitly set to the literal `false`).
|
|
26
|
+
- **Near-moment (ambient)**: driven by the profile block's "capture knowledge as you go" rule, distill may be invoked **near the moment** reusable cross-feature knowledge is produced — in **any session**, including plain chat or a **non-SDD fix** that never reaches a wrap-up command. This is the primary way knowledge from ad-hoc work is captured at all.
|
|
27
|
+
- **Long-run**: in a long-running `implement-tasks`, distill **along the way** near each knowledge-producing event rather than only at the very end, so mid-task evidence is not lost to context compaction; the end-of-task `auto_distill` still runs as a **backstop**.
|
|
28
|
+
- **Manual (fallback)**: invoked directly on the supplied or most-recent interaction segment.
|
|
29
|
+
|
|
30
|
+
distill is **non-blocking and non-interactive**: it never prompts, never gates another command, and **early-exits without writing** when the delta contains nothing reusable.
|
|
31
|
+
|
|
32
|
+
**Input contract**: distill operates on "a segment of interaction to distill." It MUST NOT assume it is live in the conversation, so the same routine works whether embedded (fed live context) or invoked manually on a supplied segment.
|
|
33
|
+
|
|
34
|
+
## What distill captures — and what it must NOT
|
|
35
|
+
|
|
36
|
+
Capture **only** knowledge that is reusable **across features** and that the per-feature SDD artifacts structurally cannot accumulate.
|
|
37
|
+
|
|
38
|
+
Apply this boundary test to every candidate: **"Would a single feature's `requirements.md` / `spec.md` / `plan.md` record this?"**
|
|
39
|
+
|
|
40
|
+
- **Yes** → it is feature-scoped; leave it in that artifact. **Do NOT** copy it into the profile. (Requirement rationale already lives in `requirements.md`; approach rationale in plan/design.)
|
|
41
|
+
- **No / it spans features** → it may enter the profile.
|
|
42
|
+
|
|
43
|
+
**Never** create a feature-local store. The profile is project-level only; a feature's memory is its existing spec directory.
|
|
44
|
+
|
|
45
|
+
## The profile store: `.codexspec/profile/`
|
|
46
|
+
|
|
47
|
+
Six **category directories**, each holding **one record per file** (`<id>.md`) with **only current-effective** knowledge — dense, with no "retired" section (git history is the ledger). One-file-per-record is deliberate: parallel feature branches each add differently-named files, so distilled knowledge merges without conflict. Create the directory and record file on first write.
|
|
48
|
+
|
|
49
|
+
- `constraints/` — negative constraints (`严禁 / 仅允许`). These carry the **highest** weight and MUST be honored first.
|
|
50
|
+
- `conventions/` — positive cross-feature conventions / steering.
|
|
51
|
+
- `pitfalls/` — cross-feature traps and their workarounds.
|
|
52
|
+
- `decisions/` — cross-feature / architectural decisions only (ADR-lite). **Never** single-feature requirement rationale.
|
|
53
|
+
- `strategies/` — metacognitive `trigger → action` rules: when you recognize signal X, switch to approach Y. A **self-model** (knowledge about the agent's own recurring slips in this project) lives here too, marked `scope: self`. A strategy is the layer **above** a pitfall — the transferable "what to do when" generalized across many specific traps.
|
|
54
|
+
- `runbooks/` — ordered multi-step procedures with failure recovery: how to carry out a known multi-step task (e.g. a release), step by step, including what to do when a step fails.
|
|
55
|
+
|
|
56
|
+
There is **no** `facts/` category — a bare fact with no "therefore do X" is either feature-scoped (leave it in `requirements.md`) or belongs as a `convention`/`constraint` with an evidence anchor.
|
|
57
|
+
|
|
58
|
+
### Record format — `claim` and `evidence` physically separated
|
|
59
|
+
|
|
60
|
+
Every record MUST separate the distilled claim from the evidence it rests on:
|
|
61
|
+
|
|
62
|
+
- `id` — **type letter + full source-feature id + local sequence**, e.g. `P-2026-0812-14054p-1` or `Con-2026-0812-14054p-1`. It is **both** the record's `### <id>: <title>` heading **and its filename** (`pitfalls/P-2026-0812-14054p-1.md`). The **source-feature id** is the distilling feature's full spec-dir id `{YYYY-MMDD-HHMM}{rr}` (e.g. `2026-0812-14054p`); it is globally unique by the timestamp+random scheme spec directories use, so records distilled on parallel feature branches never collide on id **or filename** (they merge with no conflict). Keep the **full** id (not a short tail) so the record is self-describing: the date supports recency/staleness reading, and the feature id ties the record to its originating change for decision context and scope. When distilling with no feature context, generate a fresh `{YYYY-MMDD-HHMM}{rr}` id now (same convention as create-new-feature). **Never** use a bare sequential id such as `P-001` — those collide across parallel branches.
|
|
63
|
+
- `claim` — one-sentence reusable **summary** (a title line, not the actionable body — for a `pitfall` the usable content lives in the three body parts below, not in this sentence).
|
|
64
|
+
- `type` — `convention` | `constraint` | `pitfall` | `decision` | `strategy` | `runbook` (`constraint` = highest priority).
|
|
65
|
+
- `scope/when` — natural-language applicability condition (e.g. "when editing Python code"); omit for global. **No formal syntax.**
|
|
66
|
+
- `evidence.facts` — the concrete observations behind it; **quote the user's original words, do not paraphrase**.
|
|
67
|
+
- `evidence.state` — the context/validity when true (feature / commit / config; still valid?).
|
|
68
|
+
- `provenance` — source feature/session, trigger, timestamp, `derivation = explicit | inferred`, `confidence = high | medium | low`. `derivation` records only **how the claim was first obtained** (the user's own words vs inferred); it is **not** a gate on `status` (see below).
|
|
69
|
+
- `status` — `candidate` | `vetted` | `conflict/needs-adjudication`. A record is `vetted` **only** when BOTH (a) it was **verified by an outcome** (a test passed, a workaround worked) AND (b) a **human endorsed it** — either it came from the user's own words (`derivation: explicit`) **or** the user approved it in `/distill review`. `derivation` is **not** itself a gate: an `inferred` record that is outcome-verified and human-approved becomes `vetted` and is eligible for `evolve`. Un-verified speculation MUST NOT be `vetted`. `conflict/needs-adjudication` marks a deferred conflict (see Conflict adjudication) and is not `evolve`-eligible. Only `vetted` records are eligible for `evolve`.
|
|
70
|
+
|
|
71
|
+
**Pitfall records carry more than a claim.** A `pitfall` (or any trap-type record) MUST, beyond `claim` and `evidence`, spell out three body parts — a bare "here is the trap" record is a defect (the next person just re-hits it):
|
|
72
|
+
|
|
73
|
+
- `root-cause` — *why* the trap happens (the underlying mechanism), not merely what it is.
|
|
74
|
+
- `workaround` — the concrete way around it, with the code / paths / commands to apply.
|
|
75
|
+
- `lesson` — the transferable takeaway that generalizes beyond this one instance.
|
|
76
|
+
|
|
77
|
+
If you cannot state all three, the pitfall is not yet understood well enough to record. `convention` / `constraint` / `decision` records usually need only `claim` + `evidence`; this three-part body is required specifically for traps.
|
|
78
|
+
|
|
79
|
+
**Strategy records carry a `trigger → action` body.** A `strategy` MUST, beyond `claim` and `evidence`, spell out two parts:
|
|
80
|
+
|
|
81
|
+
- `trigger` — the **signature** by which you recognize you are in this situation (e.g. "a substring contract test fails", "a fix is not converging after several attempts"). This is what lets the strategy be recalled by the situation, not by remembering the one incident that produced it.
|
|
82
|
+
- `action` — what to do when the trigger fires.
|
|
83
|
+
|
|
84
|
+
A **self-model** — a strategy about your *own* recurring slip (e.g. "I keep forgetting to regenerate derived forms") — is a strategy marked `scope: self`; its `trigger` is self-referential but the body is identical.
|
|
85
|
+
|
|
86
|
+
**Runbook records carry an ordered body.** A `runbook` MUST, beyond `claim` and `evidence`, spell out:
|
|
87
|
+
|
|
88
|
+
- `steps` — the ordered steps to carry out the task.
|
|
89
|
+
- `failure-recovery` — for the steps that can fail, what to do to recover (the branch that a flat one-line record loses).
|
|
90
|
+
|
|
91
|
+
If you cannot state these parts, the strategy or runbook is not yet worth recording — the same anti-hollow bar as pitfalls.
|
|
92
|
+
|
|
93
|
+
> **onboard variant**: `/codexspec:onboard` writes to this same store and format, with one difference — its records are inferred from code, so `evidence.facts` holds a verbatim **code observation** (path + snippet) instead of a user quote, `provenance` marks the onboard scan, and `derivation` is always `inferred`. onboard therefore writes `status: candidate` **at write time** (it never writes `vetted` itself); such a record can still be promoted to `vetted` later once it is outcome-verified and approved in `/distill review` — its `inferred` origin is not a barrier (per the `status` rule above).
|
|
94
|
+
|
|
95
|
+
This separation is what makes a later error locatable as **misread** (facts wrong) vs **overreach** (claim over-generalized) vs **stale** (state no longer holds).
|
|
96
|
+
|
|
97
|
+
Example — a `convention` (claim + evidence is enough), file `conventions/Con-2026-0809-2219gg-1.md`:
|
|
98
|
+
|
|
99
|
+
```markdown
|
|
100
|
+
### Con-2026-0809-2219gg-1: Prefer absolute imports
|
|
101
|
+
- claim: Always use absolute imports in `src/`.
|
|
102
|
+
- type: convention
|
|
103
|
+
- scope/when: Python modules under `src/`
|
|
104
|
+
- evidence.facts: "Use absolute imports; relative ones broke the packaged wheel last time."
|
|
105
|
+
- evidence.state: confirmed at feature 2026-0809-2219gg; commit a1b2c3d
|
|
106
|
+
- provenance: distill @implement-tasks, 2026-08-09, derivation: explicit, confidence: high
|
|
107
|
+
- status: vetted
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Example — a `pitfall` (note the required `root-cause` / `workaround` / `lesson` body), file `pitfalls/P-2026-0810-1330ab-1.md`:
|
|
111
|
+
|
|
112
|
+
```markdown
|
|
113
|
+
### P-2026-0810-1330ab-1: `re.sub` with a string replacement corrupts blocks containing backslashes
|
|
114
|
+
- claim: Inject a rendered block with a function replacement in `re.sub`, never a string.
|
|
115
|
+
- type: pitfall
|
|
116
|
+
- scope/when: upserting a rendered block into a file via `re.sub` in `src/`
|
|
117
|
+
- root-cause: a string replacement passed to `re.sub` interprets `\g<...>` and backslash escapes, so any such sequence inside the block silently corrupts the output.
|
|
118
|
+
- workaround: pass a callable replacement — `pattern.sub(lambda _m: block, text)` — so the block is inserted verbatim.
|
|
119
|
+
- lesson: whenever the replacement is data (not a pattern), use the callable form; the same trap applies in any language whose replace interprets `$1` / `\1`.
|
|
120
|
+
- evidence.facts: the injected block contained `\g<0>` and rendered as garbage until switched to the lambda form.
|
|
121
|
+
- evidence.state: confirmed at feature 2026-0810-1330ab; commit c0ffee1. Still valid.
|
|
122
|
+
- provenance: distill @implement-tasks, 2026-08-10, derivation: inferred, confidence: high
|
|
123
|
+
- status: candidate
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Example — a `strategy` (note the `trigger` / `action` body), file `strategies/S-2026-0813-1606fz-1.md`:
|
|
127
|
+
|
|
128
|
+
```markdown
|
|
129
|
+
### S-2026-0813-1606fz-1: When a substring contract test fails, suspect markdown emphasis first
|
|
130
|
+
- claim: A failing substring assertion over a prose template is usually a wrong assertion, not a wrong template.
|
|
131
|
+
- type: strategy
|
|
132
|
+
- scope/when: writing or debugging substring contract tests over `templates/commands/*.md`
|
|
133
|
+
- trigger: a `test_*_template.py` substring assertion fails on a phrase you believe the template contains.
|
|
134
|
+
- action: check whether the template wrote inline `**`/`*`/backticks inside the asserted span before touching the template; re-target the assertion at an emphasis-free span.
|
|
135
|
+
- evidence.facts: "断言 span 里含 `**not**` 导致子串匹配失败"
|
|
136
|
+
- evidence.state: confirmed at feature 2026-0813-1606fz; still valid.
|
|
137
|
+
- provenance: distill @implement-tasks, 2026-08-13, derivation: inferred, confidence: high
|
|
138
|
+
- status: candidate
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Example — a `runbook` (note the ordered `steps` + `failure-recovery` body), file `runbooks/R-2026-0813-1143el-1.md`:
|
|
142
|
+
|
|
143
|
+
```markdown
|
|
144
|
+
### R-2026-0813-1143el-1: Release a new CodexSpec version
|
|
145
|
+
- claim: The end-to-end steps (and failure recovery) to cut a release.
|
|
146
|
+
- type: runbook
|
|
147
|
+
- scope/when: publishing a new version of this repo
|
|
148
|
+
- steps: 1) bump the version manually in `pyproject.toml`, `__version__`, and `uv.lock`; 2) run `publish.sh`; 3) push the tag; 4) commit the marketplace update.
|
|
149
|
+
- failure-recovery: if the `pip-audit` pre-commit hook aborts on a transient SSL error, reset the half-applied bump and re-run with `SKIP=pip-audit`.
|
|
150
|
+
- evidence.facts: "publish.sh 不自动 bump __version__;pip-audit 偶发 SSL 失败中止发布"
|
|
151
|
+
- evidence.state: confirmed at feature 2026-0813-1143el; still valid.
|
|
152
|
+
- provenance: distill @implement-tasks, 2026-08-13, derivation: inferred, confidence: high
|
|
153
|
+
- status: candidate
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Extraction
|
|
157
|
+
|
|
158
|
+
Read the interaction segment and extract, per the dimensions above, only **verified** knowledge — prefer facts confirmed by outcomes over speculation; speculation MUST NOT become `vetted`.
|
|
159
|
+
|
|
160
|
+
Before writing, **read the current profile** (the record files under each category directory) and skip anything already covered; update anything changed via `replace`. **This is how deduplication is done — by judgment, not an algorithm.**
|
|
161
|
+
|
|
162
|
+
## Debounce across the trigger surface
|
|
163
|
+
|
|
164
|
+
distill can now fire from several points close together — the near-moment ambient rule, the long-run along-the-way calls, and the wrap-up hooks (`implement-tasks` → `commit-staged` → `pr`) — often over largely the **same** work. To avoid re-distilling and near-duplicate records, apply a lightweight debounce:
|
|
165
|
+
|
|
166
|
+
- **Session-local boundary.** Keep a **session-local already-distilled boundary** — your own memory, in this conversation's context, of what you have already distilled this session and up to which point of the interaction. This is held **in conversation context**; introduce **no persistent runtime state** (no marker file, no counter on disk).
|
|
167
|
+
- **Delta-only.** On a later trigger, distill only the **substantive new delta** since that boundary. If nothing substantive was produced since, **early-exit** (report "nothing to distill") without deep-reading the whole profile.
|
|
168
|
+
- **Cross-session fallback.** When the boundary is unavailable — a new session, or context was compacted — there is no session memory to lean on, so distill **falls back to reading the profile and skipping covered records** (the judgment dedup above). Worst case is one extra early-exiting scan, never a duplicate.
|
|
169
|
+
|
|
170
|
+
## Conflict adjudication
|
|
171
|
+
|
|
172
|
+
When a new item conflicts with an existing rule, resolve in this order:
|
|
173
|
+
|
|
174
|
+
1. **Recency** — newer corrections win (usually a `replace`).
|
|
175
|
+
2. **Specificity** — a specific instruction overrides the general one **only within its scope**.
|
|
176
|
+
3. **Scenario-decoupling** — if neither wins, keep **both** under a `scope/when` condition rather than forcing a winner.
|
|
177
|
+
4. **Defer, don't guess** — if genuinely unresolvable, write the record with `status: conflict/needs-adjudication` and surface it at the next interactive point or at evolve time. **Never block, never guess.**
|
|
178
|
+
|
|
179
|
+
## Mutation discipline
|
|
180
|
+
|
|
181
|
+
Change the profile **only** through three conceptual operations (you edit the files directly — these are a discipline, **not** a tool API or matching algorithm):
|
|
182
|
+
|
|
183
|
+
- `add` — create a new record file `<category>/<id>.md` for a verified item.
|
|
184
|
+
- `replace` — supersede an outdated/wrong item **in its own file** (keeps records dense).
|
|
185
|
+
- `remove` — delete the record's file when a changed environment invalidates it.
|
|
186
|
+
|
|
187
|
+
git history is the audit ledger. Do **NOT** keep a retired file or a retired section.
|
|
188
|
+
|
|
189
|
+
## Consolidation (compress narrow records into general rules)
|
|
190
|
+
|
|
191
|
+
Growth is not just accumulation: many narrow records on one theme should compress into **one general rule plus its exceptions** (the way experience compacts into judgment). distill performs the **detect-and-mark** half automatically; a human performs the **merge** half in `/distill review`.
|
|
192
|
+
|
|
193
|
+
- **Mark, don't merge.** When you notice a cluster of narrow records sharing a generalization (e.g. several derived-artifact pitfalls, or several lockstep conventions), **mark** them as **consolidation candidates** by writing a **per-record field** into each member's own file — `consolidation: candidate; cluster: <short-theme-key>` — using the same shared `<short-theme-key>` on every member. This is judgment, not an algorithm. It is **non-destructive**: marking **does not auto-rewrite or delete** any record.
|
|
194
|
+
- **No central index.** The cluster lives only as that shared key across the members' own files — there is **no central index** or manifest file (which would be a merge-conflict magnet and break the conflict-free store). `/distill review` discovers a cluster by scanning for records that share a `cluster:` key.
|
|
195
|
+
- **Cross-category promotion.** A consolidation may cross categories — e.g. **promoting several** `pitfalls` **into one** `strategy` (the transferable "what to do when" above the individual traps). The general record is written in whichever category fits the generalized claim.
|
|
196
|
+
|
|
197
|
+
The merge itself is confirmed by a human in `/distill review` (below); distill never merges unattended.
|
|
198
|
+
|
|
199
|
+
## Vetting candidates (manual, interactive)
|
|
200
|
+
|
|
201
|
+
Auto-distill writes `candidate` records non-interactively and **never prompts**. Promote them through the **manual review mode** — `/distill review` (or `/distill` with no new segment to distill): list every pending `candidate` compactly (claim + evidence + provenance) and let the user approve inline — "vet all", "vet 1,3", "edit 2", "drop 4". Apply the choices by editing each record's `status` (a `replace`). **The user never hand-edits the profile files.**
|
|
202
|
+
|
|
203
|
+
The user's approval here **is** the human endorsement half of the `vetted` gate: an approved `candidate` that is already outcome-verified becomes `vetted` **regardless of its original `derivation`** (an `inferred` record is not blocked from vetting). If a candidate has not yet been outcome-verified, approval keeps it `candidate` (or the user may attest the outcome to complete the gate). This is the path by which `inferred` knowledge — including everything `/codexspec:onboard` writes — reaches `vetted` and becomes `evolve`-eligible.
|
|
204
|
+
|
|
205
|
+
**Consolidation review.** In the same review mode, also surface any **consolidation candidate** clusters (records sharing a `cluster:` key, from the Consolidation section above): show the cluster's members compactly and let the user confirm the merge inline — "merge 1", "merge all", "keep separate". On confirmation, write the single general record (a **general rule plus its exceptions**), `remove` the superseded members, and clear the `cluster:`/`consolidation:` marker from anything that survives so no stale flag lingers. If the user declines, leave every member untouched. distill only ever **marked** the cluster; the merge happens **only** here, on explicit confirmation.
|
|
206
|
+
|
|
207
|
+
## Self-check before finishing
|
|
208
|
+
|
|
209
|
+
A lightweight judgment pass (not an engineered lint) over what you just wrote:
|
|
210
|
+
|
|
211
|
+
- **Not hollow** — every `pitfall` states `root-cause` + `workaround` + `lesson`, not just a `claim`. If it collapses to one line, either flesh it out or drop it.
|
|
212
|
+
- **Links resolve** — any `[[id]]` cross-link points to an existing record file under `.codexspec/profile/`, or is an intentional forward reference to one you are also writing now. Don't leave a link to a record that will never exist.
|
|
213
|
+
|
|
214
|
+
## Output
|
|
215
|
+
|
|
216
|
+
Report concisely in the interaction language: which records were added / replaced / removed and in which file, any `conflict` records deferred, or "nothing to distill" on early-exit. distill **never** gates the caller.
|
|
@@ -31,6 +31,8 @@ Promote only records that are **both**:
|
|
|
31
31
|
|
|
32
32
|
Project-specific knowledge **stays** in the profile. Only generally-useful capability is promoted upstream. When nothing qualifies, stop and report — do not force a promotion.
|
|
33
33
|
|
|
34
|
+
This selection spans **all six** profile categories. `strategies/` (metacognitive `trigger → action` rules) and `runbooks/` (ordered multi-step procedures) are often the **most** promotable material — a vetted, general strategy or runbook compiles cleanly into a reusable skill/command — but they clear the **same** `vetted` gate as every other category; the gate is unchanged.
|
|
35
|
+
|
|
34
36
|
## Compiling the draft
|
|
35
37
|
|
|
36
38
|
Compile the selected sediment into a SKILL.md / command-template draft that conforms to both:
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Cold-start the project profile by scanning an existing codebase for conventions and constraints
|
|
3
|
+
argument-hint: "[path]"
|
|
4
|
+
allowed-tools: Read, Grep, Glob, Bash(git:*), Bash(ls/cat/find:*), Edit, Write
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Codebase Onboarding
|
|
8
|
+
|
|
9
|
+
## Language Preference
|
|
10
|
+
|
|
11
|
+
Read `.codexspec/config.yml`. Two independent language controls apply (each falls back to `language.output`, then English):
|
|
12
|
+
|
|
13
|
+
- **Interaction language** (`language.interaction`): language for all conversation with the user — questions, explanations, status messages, and `codexspec` CLI terminal output.
|
|
14
|
+
- **Document language** (`language.document`): language for generated artifact files (the profile records).
|
|
15
|
+
|
|
16
|
+
Converse in the interaction language and author artifacts in the document language. Apply the project's translation standard to both: translate by meaning (not word-for-word), keep English for terms with no good native equivalent, and write as if originally in that language. **Exception**: `evidence.facts` records a verbatim code observation (path + snippet) and MUST NOT be translated.
|
|
17
|
+
|
|
18
|
+
## User Input
|
|
19
|
+
|
|
20
|
+
`$ARGUMENTS`
|
|
21
|
+
|
|
22
|
+
## Role and Operating Model
|
|
23
|
+
|
|
24
|
+
`onboard` is the **cold-start / bulk counterpart to `distill`**. Where `distill` writes the project profile incrementally from interaction, `onboard` scans an **existing codebase** once and batch-writes the reusable knowledge that is **implicit in the code and not already recorded accessibly** into the shared store `.codexspec/profile/`. It exists to bootstrap a brownfield project's profile so accumulated project knowledge is grounded immediately, instead of only after enough work has flowed through `distill`.
|
|
25
|
+
|
|
26
|
+
`onboard` is **read-only on the codebase** and **write-only to `.codexspec/profile/`**: it never modifies source, tests, git state, or the constitution. It is a **standalone, user-invoked command** — not an SDD pipeline stage: it has **no auto-next successor** and **no automatic hook**, and it leaves no persistent document beyond the profile records (no map, no walkthrough — those, if ever wanted, belong to a separate `explain`).
|
|
27
|
+
|
|
28
|
+
## Prerequisite & Scaffold
|
|
29
|
+
|
|
30
|
+
Before scanning:
|
|
31
|
+
|
|
32
|
+
- If `.codexspec/` is **absent**, the project is not codexspec-initialized. **Stop** and direct the user to run `codexspec init`. Do not scaffold a whole project.
|
|
33
|
+
- If `.codexspec/` is present but the profile store is missing, **ensure the canonical scaffold** — the six category directories `.codexspec/profile/{constraints,conventions,pitfalls,decisions,strategies,runbooks}/` (matching what `codexspec init` produces) — before writing.
|
|
34
|
+
- git is **not required**. onboard runs on a plain directory.
|
|
35
|
+
|
|
36
|
+
## Codebase Scan
|
|
37
|
+
|
|
38
|
+
Scan strategy is **high-signal-first over the whole repository in a single pass**:
|
|
39
|
+
|
|
40
|
+
- Respect `.gitignore`. When there is **no git / no `.gitignore`**, fall back to sensible defaults that skip vendored, build, and dependency directories (e.g. `node_modules`, `dist`, `build`, `.venv`, `target`), and say so in the summary.
|
|
41
|
+
- **Deep-read high-value sources**: directory structure; build / dependency / lint / formatter / type-checker config; entry points; existing docs (README, CONTRIBUTING, ADRs); test layout; and the frequently-imported core modules. **Shallow-sample** the bulk of business code rather than reading every file.
|
|
42
|
+
- **Stream findings to the store as you go** — write each convention as soon as it is confirmed — so the scan is **interruptible and resumable**. Do **not** block until the whole scan finishes before writing or interacting; a run interrupted mid-scan keeps what it already wrote and can be re-run to continue.
|
|
43
|
+
- An optional `[path]` argument (from `$ARGUMENTS`) **narrows** the scan to a single subdirectory or module.
|
|
44
|
+
- Never claim full coverage when you sampled — the summary distinguishes deep-read from sampled areas.
|
|
45
|
+
|
|
46
|
+
## What onboard Extracts — and What It Must NOT
|
|
47
|
+
|
|
48
|
+
Extraction uses your **flexible judgment over what the code actually shows** — not a fixed checklist of filenames or markers. onboard actively extracts **only two** of the six profile categories:
|
|
49
|
+
|
|
50
|
+
- **`conventions`** (the primary yield) — the code's observable regularities: directory/module structure, naming schemes, import style, the tech stack and toolchain (read from manifests), lint/format/type configuration, test framework and layout, and patterns repeated across the codebase. **Observable architecture / tech-stack facts** are captured here as fact-plus-steering, not as ADR-style decisions.
|
|
51
|
+
- **`constraints`** (narrow, high-risk) — **only** config-level **explicit hard prohibitions**: lint/type rules set to *error* that ban imports or APIs, `do not edit` / generated-file / managed-block markers, and CODEOWNERS / protected-path conventions. Every constraint candidate carries a **precise evidence anchor** (`file:line` or a config snippet). **Absent an explicit prohibition signal, propose no constraint** — silence, never a guess.
|
|
52
|
+
|
|
53
|
+
onboard **never** extracts `decisions`, `pitfalls`, `strategies`, or `runbooks`. A documented decision or pitfall is already readable in the repo (redundant to copy); an undocumented one is unreliable to infer from a cold scan (pitfalls are experiential; decision rationale would be fabricated). `strategies` (metacognitive trigger→action rules) and `runbooks` (lived multi-step procedures) are likewise experiential — a cold scan cannot reliably infer either. All four remain `distill`'s channels, where the rationale and lived experience are available.
|
|
54
|
+
|
|
55
|
+
## Record Format
|
|
56
|
+
|
|
57
|
+
onboard **reuses `distill`'s profile store and record format verbatim** — one record per file under a category directory (`conventions/<id>.md`, `constraints/<id>.md`), ids namespaced by the source-feature id, and `claim` physically separated from `evidence`. See `distill.md` for the canonical format. onboard writes with these **deltas**:
|
|
58
|
+
|
|
59
|
+
- `provenance` marks the **onboard scan** as the source (distinct from `distill`), with `derivation: inferred` — always, because the knowledge is inferred from code, never quoted from the user.
|
|
60
|
+
- An onboard record's `status` is always **`candidate`** at write time — onboard **never** writes `vetted` itself. Its `inferred` origin is **not** a permanent barrier: such a record can later be promoted to `vetted` via `/distill review` once it is outcome-verified and the user approves it (the `evolve` gate remains `vetted`). See the `status` rule in `distill.md`.
|
|
61
|
+
- `evidence.facts` holds the **concrete code observation** — the file path plus the relevant snippet or config anchor — instead of a user quote.
|
|
62
|
+
|
|
63
|
+
## Integration with the Existing Profile
|
|
64
|
+
|
|
65
|
+
Before writing, **read the existing profile** and integrate:
|
|
66
|
+
|
|
67
|
+
- **De-duplicate** — skip anything already covered by an existing record (by judgment, not an algorithm).
|
|
68
|
+
- **Adjudicate conflicts** per `distill`'s order (recency → specificity → scenario-decoupling → defer); if genuinely unresolvable, write the record `status: conflict/needs-adjudication` and surface it — never block, never guess.
|
|
69
|
+
- **Never clobber.** onboard MUST NOT overwrite or delete any existing `vetted`, hand-authored, or `distill`-written record. It only **adds** new files (namespaced ids merge with zero conflict) or edits **its own** candidate file. This makes re-running onboard (whole repo, or a narrowed `[path]`) safe and **idempotent** — it augments without destroying.
|
|
70
|
+
|
|
71
|
+
## Safety Gate — quick review for high-risk, immediate effect for the rest
|
|
72
|
+
|
|
73
|
+
`candidate` records take local effect immediately (weighted with caution); vetting only gates `evolve`, not local effect. Because onboard is high-volume cold inference, it gates the one high-risk category and lets the bulk flow:
|
|
74
|
+
|
|
75
|
+
- **`conventions` → written immediately as `candidate`.** They take effect at once and are refined later, asynchronously and at your pace, via `/distill review`. No synchronous audit.
|
|
76
|
+
- **`constraints` → held for a quick in-session review at the end of the scan.** Because a wrong, top-weighted constraint would otherwise take honored-first effect unreviewed, onboard accumulates constraint candidates and, at the end, presents them for a fast review. This is a **persist / do-not-persist** decision for *this scan's* constraints — **not** a promotion to `vetted`, and **not** an invocation of `/distill review`. For each candidate you may **persist**, **edit then persist**, or **drop**; only persisted ones are written (as `candidate`). If the scan found **no** constraint candidates, there is no synchronous step.
|
|
77
|
+
|
|
78
|
+
The user reviews only the small high-risk set here; the ongoing, backlog-wide vetting of any `candidate` remains the separate asynchronous `/distill review` channel.
|
|
79
|
+
|
|
80
|
+
## Output Summary
|
|
81
|
+
|
|
82
|
+
Report concisely in the interaction language: the records added / updated per category and file, any `conflict` records deferred, and **which areas were deep-read versus sampled** (never imply full coverage when you sampled). On an empty or knowledge-free scan, write nothing and report "nothing to onboard".
|
|
83
|
+
|
|
84
|
+
## Boundaries (recap)
|
|
85
|
+
|
|
86
|
+
- Read-only on code; write-only to `.codexspec/profile/`; no source / test / git / constitution mutation.
|
|
87
|
+
- Standalone: no auto-next, no automatic hook, and **no Automatic Distillation step** (onboard is not a wrap-up command).
|
|
88
|
+
- Writes only `conventions` and `constraints`; never `decisions`, `pitfalls`, `strategies`, or `runbooks`.
|
|
89
|
+
- Produces no persistent document beyond profile records.
|
|
@@ -55,7 +55,7 @@ When the argument identifies an existing feature:
|
|
|
55
55
|
Before discussing and finalizing requirements, read the project profile under `.codexspec/profile/` when it exists so the confirmed `requirements.md` is a synthesis that already accounts for accumulated project knowledge. Each category is a directory holding one record per file:
|
|
56
56
|
|
|
57
57
|
- `constraints/` first — the project's hard prohibitions (highest weight); requirements MUST NOT contradict them.
|
|
58
|
-
- `pitfalls/`, `conventions/`, `decisions/` — read the records relevant to this feature's area, to avoid re-hitting known traps, to follow established conventions,
|
|
58
|
+
- `pitfalls/`, `conventions/`, `decisions/`, `strategies/`, `runbooks/` — read the records relevant to this feature's area, to avoid re-hitting known traps, to follow established conventions, to reuse past cross-feature/architectural decisions rather than re-litigating them, and to apply proven strategies and procedures.
|
|
59
59
|
|
|
60
60
|
Each record carries a `status` (`candidate` or `vetted`); weight `candidate` entries with appropriate caution. Fold what is relevant into the discussion and the resulting entries; cite a profile record as evidence when it materially shapes a decision. This is the single point where the profile enters the SDD pipeline — downstream stages keep `requirements.md` as authority and do not re-read the profile. Degrade silently when the profile is absent or empty (nothing to apply); never block on it.
|
|
61
61
|
|
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Distill reusable, cross-feature knowledge from an interaction into the project profile
|
|
3
|
-
argument-hint: "[interaction segment or context to distill]"
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Distill
|
|
7
|
-
|
|
8
|
-
## Language Preference
|
|
9
|
-
|
|
10
|
-
Read `.codexspec/config.yml`. Two independent language controls apply (each falls back to `language.output`, then English):
|
|
11
|
-
|
|
12
|
-
- **Interaction language** (`language.interaction`): language for all conversation with the user — questions, explanations, status messages, and `codexspec` CLI terminal output.
|
|
13
|
-
- **Document language** (`language.document`): language for generated artifact files (the profile records).
|
|
14
|
-
|
|
15
|
-
Converse in the interaction language and author artifacts in the document language. Apply the project's translation standard to both: translate by meaning (not word-for-word), keep English for terms with no good native equivalent, and write as if originally in that language. **Exception**: `evidence.facts` quotes the user's original words verbatim and MUST NOT be translated.
|
|
16
|
-
|
|
17
|
-
## User Input
|
|
18
|
-
|
|
19
|
-
`$ARGUMENTS`
|
|
20
|
-
|
|
21
|
-
## Operating Model
|
|
22
|
-
|
|
23
|
-
`distill` extracts the reusable, cross-feature knowledge produced during work and persists it to the project-level store `.codexspec/profile/`. It runs two ways:
|
|
24
|
-
|
|
25
|
-
- **Auto (primary)**: embedded in wrap-up commands (`implement-tasks` on completion, `commit-staged`, `pr`), gated by `workflow.auto_distill` in `.codexspec/config.yml` (**default enabled**; disabled only when explicitly set to the literal `false`).
|
|
26
|
-
- **Manual (fallback)**: invoked directly on the supplied or most-recent interaction segment.
|
|
27
|
-
|
|
28
|
-
distill is **non-blocking and non-interactive**: it never prompts, never gates another command, and **early-exits without writing** when the delta contains nothing reusable.
|
|
29
|
-
|
|
30
|
-
**Input contract**: distill operates on "a segment of interaction to distill." It MUST NOT assume it is live in the conversation, so the same routine works whether embedded (fed live context) or invoked manually on a supplied segment.
|
|
31
|
-
|
|
32
|
-
## What distill captures — and what it must NOT
|
|
33
|
-
|
|
34
|
-
Capture **only** knowledge that is reusable **across features** and that the per-feature SDD artifacts structurally cannot accumulate.
|
|
35
|
-
|
|
36
|
-
Apply this boundary test to every candidate: **"Would a single feature's `requirements.md` / `spec.md` / `plan.md` record this?"**
|
|
37
|
-
|
|
38
|
-
- **Yes** → it is feature-scoped; leave it in that artifact. **Do NOT** copy it into the profile. (Requirement rationale already lives in `requirements.md`; approach rationale in plan/design.)
|
|
39
|
-
- **No / it spans features** → it may enter the profile.
|
|
40
|
-
|
|
41
|
-
**Never** create a feature-local store. The profile is project-level only; a feature's memory is its existing spec directory.
|
|
42
|
-
|
|
43
|
-
## The profile store: `.codexspec/profile/`
|
|
44
|
-
|
|
45
|
-
Four **category directories**, each holding **one record per file** (`<id>.md`) with **only current-effective** knowledge — dense, with no "retired" section (git history is the ledger). One-file-per-record is deliberate: parallel feature branches each add differently-named files, so distilled knowledge merges without conflict. Create the directory and record file on first write.
|
|
46
|
-
|
|
47
|
-
- `constraints/` — negative constraints (`严禁 / 仅允许`). These carry the **highest** weight and MUST be honored first.
|
|
48
|
-
- `conventions/` — positive cross-feature conventions / steering.
|
|
49
|
-
- `pitfalls/` — cross-feature traps and their workarounds.
|
|
50
|
-
- `decisions/` — cross-feature / architectural decisions only (ADR-lite). **Never** single-feature requirement rationale.
|
|
51
|
-
|
|
52
|
-
### Record format — `claim` and `evidence` physically separated
|
|
53
|
-
|
|
54
|
-
Every record MUST separate the distilled claim from the evidence it rests on:
|
|
55
|
-
|
|
56
|
-
- `id` — **type letter + full source-feature id + local sequence**, e.g. `P-2026-0812-14054p-1` or `Con-2026-0812-14054p-1`. It is **both** the record's `### <id>: <title>` heading **and its filename** (`pitfalls/P-2026-0812-14054p-1.md`). The **source-feature id** is the distilling feature's full spec-dir id `{YYYY-MMDD-HHMM}{rr}` (e.g. `2026-0812-14054p`); it is globally unique by the timestamp+random scheme spec directories use, so records distilled on parallel feature branches never collide on id **or filename** (they merge with no conflict). Keep the **full** id (not a short tail) so the record is self-describing: the date supports recency/staleness reading, and the feature id ties the record to its originating change for decision context and scope. When distilling with no feature context, generate a fresh `{YYYY-MMDD-HHMM}{rr}` id now (same convention as create-new-feature). **Never** use a bare sequential id such as `P-001` — those collide across parallel branches.
|
|
57
|
-
- `claim` — one-sentence reusable statement.
|
|
58
|
-
- `type` — `convention` | `constraint` | `pitfall` | `decision` (`constraint` = highest priority).
|
|
59
|
-
- `scope/when` — natural-language applicability condition (e.g. "when editing Python code"); omit for global. **No formal syntax.**
|
|
60
|
-
- `evidence.facts` — the concrete observations behind it; **quote the user's original words, do not paraphrase**.
|
|
61
|
-
- `evidence.state` — the context/validity when true (feature / commit / config; still valid?).
|
|
62
|
-
- `provenance` — source feature/session, trigger, timestamp, `derivation = explicit | inferred`.
|
|
63
|
-
- `status` — `vetted` **only** when `derivation = explicit` (the user's own words) AND the item was verified by an outcome (a test passed, a workaround worked); every `inferred` item stays `candidate`. Only `vetted` records are eligible for `evolve`.
|
|
64
|
-
|
|
65
|
-
This separation is what makes a later error locatable as **misread** (facts wrong) vs **overreach** (claim over-generalized) vs **stale** (state no longer holds).
|
|
66
|
-
|
|
67
|
-
Example entry — file `conventions/Con-2026-0809-2219gg-1.md`:
|
|
68
|
-
|
|
69
|
-
```markdown
|
|
70
|
-
### Con-2026-0809-2219gg-1: Prefer absolute imports
|
|
71
|
-
- claim: Always use absolute imports in `src/`.
|
|
72
|
-
- type: convention
|
|
73
|
-
- scope/when: Python modules under `src/`
|
|
74
|
-
- evidence.facts: "Use absolute imports; relative ones broke the packaged wheel last time."
|
|
75
|
-
- evidence.state: confirmed at feature 2026-0809-2219gg; commit a1b2c3d
|
|
76
|
-
- provenance: distill @implement-tasks, 2026-08-09, derivation: explicit
|
|
77
|
-
- status: vetted
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
## Extraction
|
|
81
|
-
|
|
82
|
-
Read the interaction segment and extract, per the dimensions above, only **verified** knowledge — prefer facts confirmed by outcomes over speculation; speculation MUST NOT become `vetted`.
|
|
83
|
-
|
|
84
|
-
Before writing, **read the current profile** (the record files under each category directory) and skip anything already covered; update anything changed via `replace`. **This is how deduplication is done — by judgment, not an algorithm.**
|
|
85
|
-
|
|
86
|
-
## Conflict adjudication
|
|
87
|
-
|
|
88
|
-
When a new item conflicts with an existing rule, resolve in this order:
|
|
89
|
-
|
|
90
|
-
1. **Recency** — newer corrections win (usually a `replace`).
|
|
91
|
-
2. **Specificity** — a specific instruction overrides the general one **only within its scope**.
|
|
92
|
-
3. **Scenario-decoupling** — if neither wins, keep **both** under a `scope/when` condition rather than forcing a winner.
|
|
93
|
-
4. **Defer, don't guess** — if genuinely unresolvable, write the record with `status: conflict/needs-adjudication` and surface it at the next interactive point or at evolve time. **Never block, never guess.**
|
|
94
|
-
|
|
95
|
-
## Mutation discipline
|
|
96
|
-
|
|
97
|
-
Change the profile **only** through three conceptual operations (you edit the files directly — these are a discipline, **not** a tool API or matching algorithm):
|
|
98
|
-
|
|
99
|
-
- `add` — create a new record file `<category>/<id>.md` for a verified item.
|
|
100
|
-
- `replace` — supersede an outdated/wrong item **in its own file** (keeps records dense).
|
|
101
|
-
- `remove` — delete the record's file when a changed environment invalidates it.
|
|
102
|
-
|
|
103
|
-
git history is the audit ledger. Do **NOT** keep a retired file or a retired section.
|
|
104
|
-
|
|
105
|
-
## Vetting candidates (manual, interactive)
|
|
106
|
-
|
|
107
|
-
Auto-distill writes `candidate` records non-interactively and **never prompts**. Promote them through the **manual review mode** — `/distill review` (or `/distill` with no new segment to distill): list every pending `candidate` compactly (claim + evidence + provenance) and let the user approve inline — "vet all", "vet 1,3", "edit 2", "drop 4". Apply the choices by editing each record's `status` (a `replace`). **The user never hand-edits the profile files.**
|
|
108
|
-
|
|
109
|
-
## Output
|
|
110
|
-
|
|
111
|
-
Report concisely in the interaction language: which records were added / replaced / removed and in which file, any `conflict` records deferred, or "nothing to distill" on early-exit. distill **never** gates the caller.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|