@journeykit/canon 0.25.0 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/skills/reference/brief-align.md +1 -1
- package/dist/skills/reference/brief-clarify.md +1 -1
- package/dist/skills/reference/brief-harden.md +1 -1
- package/dist/skills/reference/brief-polish.md +220 -17
- package/dist/skills/reference/brief-shape.md +2 -2
- package/dist/skills/reference/canon-storage.md +9 -2
- package/dist/skills/reference/canon-subject.md +8 -4
- package/dist/skills/reference/role-planner.md +27 -9
- package/package.json +4 -4
|
@@ -39,4 +39,4 @@ Implement, evidence and review (loop steps 5 to 7). If the brief had to change,
|
|
|
39
39
|
- Explaining writes nothing. Asked what a finding means or what a pass would do, answer in chat and leave the brief untouched.
|
|
40
40
|
- No pass requires another as a prerequisite. Run the one the findings or the human point at, in any order.
|
|
41
41
|
- A pass closes only the findings it rechecked: rerun `canon brief check` after the edit and report a finding as closed only when it is gone from that run. Never claim a rule you did not rerun.
|
|
42
|
-
- Write in the brief's language (`language:` in `canon.yaml`). Edits touch `.canon/changes/<slug>/brief.md` only. A brief edited after approval is not approved: it needs a new human answer and `canon brief approve` again.
|
|
42
|
+
- Write in the brief's language (detected from the text; `language:` in `canon.yaml` overrides it). Edits touch `.canon/changes/<slug>/brief.md` only. A brief edited after approval is not approved: it needs a new human answer and `canon brief approve` again.
|
|
@@ -39,4 +39,4 @@ Every open line is a question with a proposed default or a stated assumption. `c
|
|
|
39
39
|
- Explaining writes nothing. Asked what a finding means or what a pass would do, answer in chat and leave the brief untouched.
|
|
40
40
|
- No pass requires another as a prerequisite. Run the one the findings or the human point at, in any order.
|
|
41
41
|
- A pass closes only the findings it rechecked: rerun `canon brief check` after the edit and report a finding as closed only when it is gone from that run. Never claim a rule you did not rerun.
|
|
42
|
-
- Write in the brief's language (`language:` in `canon.yaml`). Edits touch `.canon/changes/<slug>/brief.md` only. A brief edited after approval is not approved: it needs a new human answer and `canon brief approve` again.
|
|
42
|
+
- Write in the brief's language (detected from the text; `language:` in `canon.yaml` overrides it). Edits touch `.canon/changes/<slug>/brief.md` only. A brief edited after approval is not approved: it needs a new human answer and `canon brief approve` again.
|
|
@@ -40,4 +40,4 @@ brief has none of them: harden its Invariants and Checks instead.
|
|
|
40
40
|
- Explaining writes nothing. Asked what a finding means or what a pass would do, answer in chat and leave the brief untouched.
|
|
41
41
|
- No pass requires another as a prerequisite. Run the one the findings or the human point at, in any order.
|
|
42
42
|
- A pass closes only the findings it rechecked: rerun `canon brief check` after the edit and report a finding as closed only when it is gone from that run. Never claim a rule you did not rerun.
|
|
43
|
-
- Write in the brief's language (`language:` in `canon.yaml`). Edits touch `.canon/changes/<slug>/brief.md` only. A brief edited after approval is not approved: it needs a new human answer and `canon brief approve` again.
|
|
43
|
+
- Write in the brief's language (detected from the text; `language:` in `canon.yaml` overrides it). Edits touch `.canon/changes/<slug>/brief.md` only. A brief edited after approval is not approved: it needs a new human answer and `canon brief approve` again.
|
|
@@ -1,33 +1,236 @@
|
|
|
1
1
|
# Brief pass: polish
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
approval
|
|
3
|
+
Make the brief something a human reads once and understands. Run it whenever
|
|
4
|
+
`canon brief check` reports `prose.*` advisories; `canon status` and
|
|
5
|
+
`meta.next` name it before the human stop. It never blocks approval, and it
|
|
6
|
+
is not finished by silencing rules: it is finished when the brief reads
|
|
7
|
+
plainly and each advisory is gone or waived with a reason.
|
|
6
8
|
|
|
7
|
-
##
|
|
9
|
+
## Mode first, style second
|
|
8
10
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
Fix what the document is before touching a word. Style rules cannot rescue a
|
|
12
|
+
brief that says the wrong thing.
|
|
13
|
+
|
|
14
|
+
- **Intent** states the current behavior: what a user sees and does today, and the gap. Not the history of how it got there.
|
|
15
|
+
- **Desired outcome** states what is true afterwards, as behavior a user can observe.
|
|
16
|
+
- **Interfaces** are described by behavior: inputs, outputs, errors, limits. Not by file, line or function.
|
|
17
|
+
- **Acceptance** gives, per step, a check a reader can run without asking the author, and one failure path naming the condition and what the user sees.
|
|
18
|
+
- **Provenance** goes on one line, `Source: <where this came from>`. That line is the only place a path or an identifier belongs.
|
|
19
|
+
- **No** paths, line numbers, identifiers, dates or decision history in the prose. A fact stated in one section is not restated in another; point to the section instead.
|
|
20
|
+
|
|
21
|
+
## Scope and language
|
|
22
|
+
|
|
23
|
+
Rules read the brief by section role. Intent and Questions and assumptions
|
|
24
|
+
get the phrase rules (`evaluative`, `connective`, `contrast`, `colloquial`,
|
|
25
|
+
`degree`) and the five newer ones; `decision-history` reads normative text
|
|
26
|
+
and Intent, not Questions. `sentence-length`, `paragraph-length` and
|
|
27
|
+
`key-sentence` read the normative sections only (Desired outcome, User flow,
|
|
28
|
+
Step detail, Acceptance; for a chore, Invariants and Checks). Out of scope
|
|
29
|
+
is not judged. Headings, code spans, fenced code, URLs, code-like tokens and
|
|
30
|
+
the `Source:` and `Approved:` lines are not counted as prose.
|
|
31
|
+
|
|
32
|
+
`canon brief check` detects the language of the text from the script of its
|
|
33
|
+
prose letters: Hangul is `ko`, Latin is `en`, kana or Han text is named
|
|
34
|
+
(`ja`, `zh`) and gets the structure rules only. A section in another script
|
|
35
|
+
than the rest is judged in its own language, and a section with too few
|
|
36
|
+
letters to tell inherits the brief's. The output prints one line,
|
|
37
|
+
`language: ko (detected)`; the JSON carries `language` and `languageSource`
|
|
38
|
+
(`detected` or `configured`). `language:` in `canon.yaml` is an override: when
|
|
39
|
+
set it decides, and `brief check` adds `languageNote` (one line in the human
|
|
40
|
+
output) if the text looks like another language. Report that note to the
|
|
41
|
+
human; do not translate the brief to fit the setting.
|
|
42
|
+
|
|
43
|
+
## The rules
|
|
44
|
+
|
|
45
|
+
Each finding carries the matched text or the measured value, and a hint.
|
|
46
|
+
Fix the sentence by saying the concrete thing; swapping in a near synonym only
|
|
47
|
+
moves the finding. The examples are invented.
|
|
48
|
+
|
|
49
|
+
### `prose.evaluative`
|
|
50
|
+
|
|
51
|
+
Catches a verdict standing in for a fact (en "it is important", "it is
|
|
52
|
+
crucial", "it is essential"; ko "중요하", "핵심이", "필요가 있", "바람직하",
|
|
53
|
+
"의미가 있"). Fix: state the fact the verdict stood for.
|
|
54
|
+
|
|
55
|
+
- en. Before: "It is important that export keeps the old file." After: "A failed export keeps the previous file."
|
|
56
|
+
- ko. 전: "내보내기가 실패해도 이전 파일을 유지하는 것이 중요하다." 후: "내보내기가 실패하면 이전 파일을 그대로 둔다."
|
|
57
|
+
|
|
58
|
+
### `prose.connective`
|
|
59
|
+
|
|
60
|
+
Catches a sentence that opens with a decorative connective (en "therefore",
|
|
61
|
+
"in conclusion"; ko "따라서", "결론적으로", "이처럼", "사실", "참고로"). Fix:
|
|
62
|
+
delete it; the order of the sentences already carries the link. Keep the
|
|
63
|
+
link only as a condition ("if", "unless").
|
|
64
|
+
|
|
65
|
+
- en. Before: "Therefore the list is sorted by date." After: "The list is sorted by date, newest first."
|
|
66
|
+
- ko. 전: "따라서 목록은 날짜순으로 정렬한다." 후: "목록은 날짜 내림차순으로 정렬한다."
|
|
67
|
+
|
|
68
|
+
### `prose.contrast`
|
|
69
|
+
|
|
70
|
+
Catches the rhetorical "not X but Y" (ko "A가 아니라 B"). Fix: state Y alone.
|
|
71
|
+
|
|
72
|
+
- en. Before: "This is not a rename but a new way to find notes." After: "Renaming a note updates its search entry at once."
|
|
73
|
+
- ko. 전: "이것은 이름 변경이 아니라 노트를 찾는 새로운 방식이다." 후: "노트 이름을 바꾸면 검색 항목이 바로 바뀐다."
|
|
74
|
+
|
|
75
|
+
### `prose.colloquial`
|
|
76
|
+
|
|
77
|
+
Korean only. Catches a spoken ending on a written sentence (~세요, ~어요,
|
|
78
|
+
~아요, ~해요, ~에요, ~네요, ~죠, …). Fix: the plain written form (`~다`).
|
|
79
|
+
|
|
80
|
+
- ko. 전: "이름이 겹치면 뒤에 숫자를 붙여 주세요." 후: "이름이 겹치면 뒤에 숫자를 붙인다."
|
|
81
|
+
|
|
82
|
+
### `prose.degree`
|
|
83
|
+
|
|
84
|
+
Catches a degree word nobody can check (en "very", "really", "extremely";
|
|
85
|
+
ko "매우", "겨우", "무려", "고작"). Fix: the number or the condition.
|
|
86
|
+
|
|
87
|
+
- en. Before: "The page loads very quickly." After: "The page accepts input within one second on the reference laptop."
|
|
88
|
+
- ko. 전: "화면이 매우 빠르게 열린다." 후: "화면은 기준 노트북에서 1초 안에 입력을 받는다."
|
|
89
|
+
|
|
90
|
+
### `prose.sentence-length`
|
|
91
|
+
|
|
92
|
+
Normative sections. Catches a sentence over 30 words (en) or 80 characters
|
|
93
|
+
(ko). Fix: one statement per sentence, split at the condition, the action
|
|
94
|
+
and the result.
|
|
95
|
+
|
|
96
|
+
- en. Before: "When a user who has not signed in opens a shared link that has already expired, the page, which also logs the attempt, shows a notice and a sign-in button and offers a way back to the list." After: "A signed-out user opens an expired shared link. The page shows the notice 'Link expired' and a sign-in button. The attempt is logged."
|
|
97
|
+
- ko. 전: "로그인하지 않은 사용자가 만료된 공유 링크를 열면 시도를 기록하면서 안내 문구와 로그인 버튼을 함께 보여 주고 이후 목록 화면으로 돌아가는 경로도 제공한다." 후: "로그인하지 않은 사용자가 만료된 공유 링크를 연다. 화면에 '링크가 만료되었습니다' 문구와 로그인 버튼이 뜬다. 시도는 기록한다."
|
|
98
|
+
|
|
99
|
+
### `prose.paragraph-length`
|
|
100
|
+
|
|
101
|
+
Normative sections. Catches a paragraph of more than five sentences. Fix: the
|
|
102
|
+
compression pass in `role-planner.md`: keep the sentences that state a
|
|
103
|
+
condition, an action, a result or a number; make three or more parallel
|
|
104
|
+
items a list; delete or move the rest.
|
|
105
|
+
|
|
106
|
+
### `prose.key-sentence`
|
|
107
|
+
|
|
108
|
+
Normative sections. Each carries exactly one key sentence marked `==**…**==`:
|
|
109
|
+
the line a reader must not miss. Fix: mark the single rule or result the
|
|
110
|
+
section turns on, and nothing else.
|
|
111
|
+
|
|
112
|
+
- en: `==**A deleted note stays recoverable for 30 days.**==`
|
|
113
|
+
- ko: `==**삭제한 노트는 30일 동안 복구할 수 있다.**==`
|
|
114
|
+
|
|
115
|
+
### `prose.code-citation`
|
|
116
|
+
|
|
117
|
+
Catches code references in sentences: paths with a known extension,
|
|
118
|
+
`path:line`, paths of two or more segments (`a/b/c`, `./x`), and backticked
|
|
119
|
+
code-shaped identifiers (`a::b`, `f()`, `snake_case`, `obj.member`,
|
|
120
|
+
`camelCase`). It does not catch UI labels, product names (`Next.js`, `iPhone`),
|
|
121
|
+
flags, commands, dates, routes or URLs, or the `Source:` line. Fix: say what the code does as behavior;
|
|
122
|
+
provenance goes on the one `Source:` line.
|
|
123
|
+
|
|
124
|
+
- en. Before: "`NoteStore.save` in `src/store/notes.ts:212` drops the tag list." After: "Saving a note drops its tags." and, once, `Source: the note store's save path.`
|
|
125
|
+
- ko. 전: "`src/store/notes.ts:212`의 `NoteStore.save`가 태그를 버린다." 후: "노트를 저장하면 태그가 사라진다." 그리고 한 번 `Source: 노트 저장소의 저장 경로.`
|
|
126
|
+
|
|
127
|
+
### `prose.parenthetical`
|
|
128
|
+
|
|
129
|
+
Catches a section with three or more parenthetical asides at a density over
|
|
130
|
+
four per 1000 characters. Labels like `(F1)` or `(30초)` do not count. Fix:
|
|
131
|
+
if the aside is a condition or a result, make it its own sentence; if it is
|
|
132
|
+
background, delete it; if it is a doubt, move it to Questions and assumptions.
|
|
133
|
+
|
|
134
|
+
- en. Before: "Sync runs (every hour), on open (when online), and retries (at most twice)." After: "Sync runs every hour and when the app opens while online. A failed sync retries twice."
|
|
135
|
+
- ko. 전: "동기화는 (한 시간마다), 앱을 열 때 (온라인이면 언제나) 실행되고 (최대 두 번) 재시도한다." 후: "동기화는 매시간, 그리고 온라인 상태에서 앱을 열 때 실행한다. 실패한 동기화는 두 번 재시도한다."
|
|
136
|
+
|
|
137
|
+
### `prose.decision-history`
|
|
138
|
+
|
|
139
|
+
Normative text, Intent and table cells. One finding per sentence that holds
|
|
140
|
+
a calendar date, in any language (`2026-10-21`, `2026년 10월`, `10월 21일`,
|
|
141
|
+
`Oct 21`, `21 October`, `October 2026`), or, in Korean text, one of
|
|
142
|
+
`결정했다`, `결정했고`, `결정하여`, `결정됐다`, `결정되었다`, `정해졌다`, `합의했다`,
|
|
143
|
+
`확정했다`, `~기로 했다`, `논의 결과`, `운영자 결정`, or, in English text, "we
|
|
144
|
+
decided", "we agreed", "was decided", "it was agreed". There is no gating: a
|
|
145
|
+
date next to 부터 or after is a finding too. Fix: delete the history and write
|
|
146
|
+
the decision as present behavior; a decision still open goes to Questions and
|
|
147
|
+
assumptions. When the date is part of the behavior (an effective date, a
|
|
148
|
+
deadline) or a quoted label, keep it and waive it with `match=` set to the
|
|
149
|
+
date (it compares the date or phrase the finding matched and the excerpt, nothing else), so the waiver clears that date only and every other note in the section
|
|
150
|
+
still draws a finding. Never waive the rule for the whole section.
|
|
151
|
+
|
|
152
|
+
- en. Before: "On 3 March the team agreed on a grid as the default view." After: "The default view is a grid."
|
|
153
|
+
- ko. 전: "3월 3일 회의에서 기본 보기를 격자로 하기로 했다." 후: "기본 보기는 격자다."
|
|
154
|
+
- waive one date: `<!-- canon:waive prose.decision-history detail "연체 요금은 2026-12-01부터 적용되는 동작이다" match="2026-12-01" -->`
|
|
155
|
+
|
|
156
|
+
What it misses: a decision written in other words ("회의에서 정한 대로" is not
|
|
157
|
+
listed on purpose, it reads as behavior). Over-reports: a date that is
|
|
158
|
+
behavior, and the closed-list phrases in behavior text; waive those with `match=`.
|
|
159
|
+
|
|
160
|
+
### `prose.ai-vocabulary`
|
|
161
|
+
|
|
162
|
+
Catches a short closed list of stock words and metaphors (en "seamless",
|
|
163
|
+
"robust", "leverage", "streamline", "empower", "north star", "game-changer",
|
|
164
|
+
…; ko "원활", "견고", "시너지", "패러다임", "북극성", "토대", …). Fix: the plain
|
|
165
|
+
verb and its object.
|
|
166
|
+
|
|
167
|
+
- en. Before: "This leverages the sync engine for a seamless experience." After: "A note edited on one device appears on the others within a minute."
|
|
168
|
+
- ko. 전: "동기화 엔진으로 원활한 사용 경험을 제공한다." 후: "한 기기에서 고친 노트가 1분 안에 다른 기기에 나타난다."
|
|
169
|
+
|
|
170
|
+
### `prose.ko-ending`
|
|
171
|
+
|
|
172
|
+
Korean only. Flagged everywhere, list items included: polite `~ㅂ니다`,
|
|
173
|
+
`~ㅂ니까`, `~ㅂ시다` (합니다, 입니다, 없습니다; a plain word like 아니다 is not
|
|
174
|
+
polite), `~십시오`, and a plain imperative: a terminated, unlabeled sentence
|
|
175
|
+
whose last word ends in 라 after a syllable with no final consonant (하라,
|
|
176
|
+
해라, 두라, 오라, 넣으라, 남겨라, 줘라). A syllable with a final consonant
|
|
177
|
+
(달라, 갈라) never counts. The `~요` forms in `prose.colloquial`'s list
|
|
178
|
+
(`~세요`, `~해요`, …) belong to that rule. Outside list items any terminated
|
|
179
|
+
sentence not ending in `~다` is flagged, a plain hortative `~자` included. In a
|
|
180
|
+
list item the plain `~자` is not detected: it is the noun suffix in 참가자,
|
|
181
|
+
후보자, 하자, 주자, so keep a hortative out of lists yourself. Not flagged:
|
|
182
|
+
questions, `없음.`, sentences ending in code or a quote, flow steps, list
|
|
183
|
+
fragments without a verb, a `label:` item, and an unterminated list item.
|
|
184
|
+
|
|
185
|
+
What it over-reports: a class of nouns, often loanwords, that end in an open
|
|
186
|
+
syllable plus 라 (보라 'purple', 카메라, 오페라, 인프라) as a terminated list
|
|
187
|
+
item; keep the sentence and waive with `match="<the noun>"`. What it misses: the
|
|
188
|
+
prohibitive `~말라` (`지우지 말라`: 말 has a final consonant), any consonant-stem
|
|
189
|
+
imperative written `~라` (달라 as a verb), and any hortative inside a list.
|
|
190
|
+
Fix: end the statement in `~다`.
|
|
191
|
+
|
|
192
|
+
- ko. 전: "목록이 비어 있으면 안내를 보여 줍니다." 후: "목록이 비어 있으면 안내를 보여 준다."
|
|
193
|
+
- ko. 전: "- 재고를 확인해라." 후: "- 재고를 확인한다."
|
|
194
|
+
|
|
195
|
+
### `prose.length`
|
|
196
|
+
|
|
197
|
+
Catches a section or the whole brief over its character budget; the finding
|
|
198
|
+
states the measured value and the budget. Section budgets (ko / en
|
|
199
|
+
characters): Intent 400 / 700, Outcome 500 / 900, Flow 800 / 1500, Detail
|
|
200
|
+
2500 / 3500, Acceptance 1000 / 1800, Questions 800 / 1200, Invariants and
|
|
201
|
+
Checks 800 / 1500 each (Out of scope has none); whole brief 4000 / 6000. Fix: the
|
|
202
|
+
compression pass in `role-planner.md`: delete sentences that restate another
|
|
203
|
+
section, turn parallel items into a list or table, move assumptions to
|
|
204
|
+
Questions and assumptions and a not-doing list to Out of scope. A brief this
|
|
205
|
+
long usually says several things twice. A waiver for the whole-brief budget
|
|
206
|
+
uses the section key `brief`.
|
|
13
207
|
|
|
14
208
|
## Inspect
|
|
15
209
|
|
|
16
|
-
- `canon brief check --json
|
|
17
|
-
-
|
|
18
|
-
|
|
19
|
-
|
|
210
|
+
- `canon brief check --json`: read the `prose.*` findings, then the sections they sit in. Fix by section, not by rule.
|
|
211
|
+
- A finding is a prompt to reread, not a verdict. When the flagged text is the precise term (a state name, a quoted label, a command), keep it and waive that rule in that section:
|
|
212
|
+
`<!-- canon:waive <rule-id> <section-key> "<reason>" -->`
|
|
213
|
+
To waive one finding and keep the rest of the rule live in that section, add the text the finding names: `<!-- canon:waive <rule-id> <section-key> "<reason>" match="<text>" -->` (after the reason's closing quote only a whole `match="<text>"` is allowed; `match =`, `MATCH=`, an unquoted or empty match or extra text is a blocker, and a reason holds no double quote).
|
|
214
|
+
A waiver is right when the text is a quotation or a fixed term, and wrong when it excuses a sentence you could simply rewrite. One rule, one section, a reason the human can read.
|
|
215
|
+
|
|
216
|
+
## The loop
|
|
217
|
+
|
|
218
|
+
1. Fix the mode problems above, then the findings, section by section.
|
|
219
|
+
2. Run `canon brief check --change-id <slug> --json`.
|
|
220
|
+
3. Repeat until no advisory remains, or each remaining one is waived with a reason.
|
|
221
|
+
4. Stop and report to the human, in the brief's language. Do not loop further: a clean run says nothing about whether the brief is good, only that these counts are within budget.
|
|
20
222
|
|
|
21
223
|
## NEVER
|
|
22
224
|
|
|
23
225
|
- Never change meaning: no flow step, rule, limit or failure path added, removed or softened while rewording.
|
|
24
|
-
- Never rewrite to dodge a rule (
|
|
25
|
-
- Never
|
|
26
|
-
- Never
|
|
226
|
+
- Never rewrite to dodge a rule (a near synonym, a list used to hide a long sentence); say the concrete thing.
|
|
227
|
+
- Never touch flow ids or subheadings, and never polish the markers.
|
|
228
|
+
- Never translate the brief to match a `language:` setting.
|
|
229
|
+
- Never treat a clean run as a claim about who wrote the brief.
|
|
27
230
|
|
|
28
231
|
## Verify
|
|
29
232
|
|
|
30
|
-
`canon brief check` no longer lists the `prose.*` findings you rewrote
|
|
233
|
+
`canon brief check` no longer lists the `prose.*` findings you rewrote, or lists them as waived with a reason; no blockers appeared.
|
|
31
234
|
|
|
32
235
|
## Next
|
|
33
236
|
|
|
@@ -39,4 +242,4 @@ Report to the human and `canon brief approve` from their answer.
|
|
|
39
242
|
- Explaining writes nothing. Asked what a finding means or what a pass would do, answer in chat and leave the brief untouched.
|
|
40
243
|
- No pass requires another as a prerequisite. Run the one the findings or the human point at, in any order.
|
|
41
244
|
- A pass closes only the findings it rechecked: rerun `canon brief check` after the edit and report a finding as closed only when it is gone from that run. Never claim a rule you did not rerun.
|
|
42
|
-
- Write in the brief's language (`language:` in `canon.yaml`). Edits touch `.canon/changes/<slug>/brief.md` only. A brief edited after approval is not approved: it needs a new human answer and `canon brief approve` again.
|
|
245
|
+
- Write in the brief's language (detected from the text, or `language:` in `canon.yaml` when set). Edits touch `.canon/changes/<slug>/brief.md` only. A brief edited after approval is not approved: it needs a new human answer and `canon brief approve` again.
|
|
@@ -22,7 +22,7 @@ marker, never by heading text.
|
|
|
22
22
|
- The flow is a numbered list with ids `F1`, `F2`, … each reading sees → does → becomes.
|
|
23
23
|
- Intent describes what the product does today and Outcome what is true after; leftover implementation paths belong in a `Source:` line or nowhere.
|
|
24
24
|
- A chore's category is exactly one of `refactor`, `dependency`, `build`, `ci`, `infra`; its invariants and checks say something.
|
|
25
|
-
- A `canon:waive <rule-id> <section-key> "<reason>"` comment is well formed with a real reason.
|
|
25
|
+
- A `canon:waive <rule-id> <section-key> "<reason>"` comment (optionally with `match="<text>"`) is well formed with a real reason.
|
|
26
26
|
|
|
27
27
|
## NEVER
|
|
28
28
|
|
|
@@ -44,4 +44,4 @@ marker, never by heading text.
|
|
|
44
44
|
- Explaining writes nothing. Asked what a finding means or what a pass would do, answer in chat and leave the brief untouched.
|
|
45
45
|
- No pass requires another as a prerequisite. Run the one the findings or the human point at, in any order.
|
|
46
46
|
- A pass closes only the findings it rechecked: rerun `canon brief check` after the edit and report a finding as closed only when it is gone from that run. Never claim a rule you did not rerun.
|
|
47
|
-
- Write in the brief's language (`language:` in `canon.yaml`). Edits touch `.canon/changes/<slug>/brief.md` only. A brief edited after approval is not approved: it needs a new human answer and `canon brief approve` again.
|
|
47
|
+
- Write in the brief's language (detected from the text; `language:` in `canon.yaml` overrides it). Edits touch `.canon/changes/<slug>/brief.md` only. A brief edited after approval is not approved: it needs a new human answer and `canon brief approve` again.
|
|
@@ -342,10 +342,17 @@ comment, only the missing ones):
|
|
|
342
342
|
- `.canon/audit/` — the query/export/purge access log.
|
|
343
343
|
- `.canon/ledger/kind=scenario/` — the scenario index; every reader
|
|
344
344
|
rebuilds it.
|
|
345
|
+
- `.canon/archive/kind=scenario/` — superseded scenario index rows;
|
|
346
|
+
derived like the index.
|
|
345
347
|
- `.canon/scenario-index.lock` — the lock serializing index rebuilds.
|
|
346
348
|
- `.canon/artifact-store.lock` — the lock serializing artifact store
|
|
347
349
|
writes.
|
|
350
|
+
- `.canon/dashboard-snapshot/` — the snapshot `canon dashboard`
|
|
351
|
+
regenerates on every run.
|
|
352
|
+
- `.canon/learn/` and `.canon/r2/` — local learn scratch and the local
|
|
353
|
+
object-store stand-in `canon report` writes.
|
|
348
354
|
|
|
349
355
|
A rerun on a repo that already has `canon.yaml` only refreshes
|
|
350
|
-
`AGENTS.md
|
|
351
|
-
|
|
356
|
+
`AGENTS.md`; `canon migrate` adds the lines an existing `.gitignore`
|
|
357
|
+
lacks, and `canon skills doctor` and `canon init --check-config` name a
|
|
358
|
+
missing one.
|
|
@@ -483,11 +483,15 @@ advisory, and is read as before.
|
|
|
483
483
|
or later, a flow step no scenario points at (`brief.flow-uncovered`) or
|
|
484
484
|
one with no failure scenario (`brief.flow-no-failure`): these two
|
|
485
485
|
**gate shipping, never approval**.
|
|
486
|
-
- **Advisories (prose, `prose.*`)**:
|
|
487
|
-
|
|
488
|
-
|
|
486
|
+
- **Advisories (prose, `prose.*`)**: judged in the language the text is
|
|
487
|
+
written in (`language:` overrides): phrase lists, code citations,
|
|
488
|
+
asides, dates and decision notes, stock vocabulary, Korean endings and
|
|
489
|
+
length budgets, in the normative sections plus Intent and Questions;
|
|
490
|
+
sentence, paragraph and key-sentence rules in normative sections.
|
|
491
|
+
Never an authorship verdict. `reference/brief-polish.md` has each rule.
|
|
489
492
|
- **Waivers**: `<!-- canon:waive <rule-id> <section-key> "<reason>" -->`,
|
|
490
|
-
one rule in one section, reason required;
|
|
493
|
+
one rule in one section, reason required; add `match="<text>"` to waive
|
|
494
|
+
only the findings whose matched text contains it. Listed in the output.
|
|
491
495
|
- **`approve`** records the human's answer: it writes
|
|
492
496
|
`Approved: <who> <when> — "<words>" (brief sha256:<digest>)` into the
|
|
493
497
|
brief and records the policy approval in the same call. It refuses
|
|
@@ -24,8 +24,10 @@ After the first `status`, follow `meta.next` of each write. `brief check`
|
|
|
24
24
|
lists blockers and advisories; bare `canon brief --as planner` names the next
|
|
25
25
|
steps. While the brief has blockers, `status` and `change new`'s `meta.next`
|
|
26
26
|
send you to write it (`write the brief: canon brief check --change-id <slug>
|
|
27
|
-
lists N blockers`);
|
|
28
|
-
|
|
27
|
+
lists N blockers`); then, while prose advisories remain, to the polish pass
|
|
28
|
+
(`polish the brief in <path> (reference/brief-polish.md), then run canon
|
|
29
|
+
brief check --change-id <slug>`, with the advisory count and the top rule
|
|
30
|
+
ids); the human step comes after it. A policy that no longer matches its approval fails closed
|
|
29
31
|
(`POLICY_CHANGED_SINCE_APPROVAL`): never edit it; the human re-approves.
|
|
30
32
|
|
|
31
33
|
## Steps
|
|
@@ -55,21 +57,37 @@ unapproved. A policy that no longer matches its approval fails closed
|
|
|
55
57
|
provenance line is the one exception. A PRD: intent, desired outcome, the user flow as
|
|
56
58
|
numbered steps `F1`, `F2`, … (sees → does → becomes), step detail per
|
|
57
59
|
step, acceptance per step with its failure path, questions and
|
|
58
|
-
assumptions, out of scope. Write it in the
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
60
|
+
assumptions, out of scope. Write it in the language of the request, or
|
|
61
|
+
the `language:` of `canon.yaml` when it is set (`brief check` detects the
|
|
62
|
+
language of the text and reports whether it used the detected or the
|
|
63
|
+
configured one; a `languageNote` means they disagree: tell the human, and
|
|
64
|
+
ask "which language?" in the questions). Anything that could change
|
|
65
|
+
policy goes to the questions: never decide it yourself. A change that
|
|
66
|
+
comes from an external plan the human
|
|
62
67
|
prepared (an openspec `proposal.md` read through `plans.sources`) gets a
|
|
63
68
|
`brief.md` whose `Source: <path>` line points at the plan; canon never
|
|
64
69
|
edits the plan.
|
|
65
70
|
3. **Check it.** `brief check` (read-only) lists findings: fix every blocker
|
|
66
|
-
(structure) before you stop
|
|
67
|
-
|
|
71
|
+
(structure) before you stop. Four optional refinement passes, each
|
|
72
|
+
standing alone and none needing another first:
|
|
68
73
|
- `reference/brief-shape.md`: structure (sections, order, leftover template text).
|
|
69
74
|
- `reference/brief-clarify.md`: open questions.
|
|
70
75
|
- `reference/brief-harden.md`: flow and step detail agree.
|
|
71
76
|
- `reference/brief-align.md`: scenarios cover the flow.
|
|
72
|
-
|
|
77
|
+
|
|
78
|
+
**Compress, then polish.** Before the human reads it, shorten the brief.
|
|
79
|
+
A human reads it once, so every sentence has to earn its place:
|
|
80
|
+
- Per paragraph, keep only sentences that state a condition, an action, a result or a number. Delete the rest.
|
|
81
|
+
- Three or more parallel items become a list or a table.
|
|
82
|
+
- Delete a sentence that restates another section: each fact lives in one place (single source of truth).
|
|
83
|
+
- An assumption goes to Questions and assumptions. A "not doing" list goes to Out of scope.
|
|
84
|
+
|
|
85
|
+
Then, when `brief check` reports prose advisories (`prose.*`; `status` and
|
|
86
|
+
`meta.next` name the step), the polish pass is required before the human
|
|
87
|
+
stop: follow `reference/brief-polish.md` and rerun `brief check` until
|
|
88
|
+
the advisories are gone or each remaining one is waived with a reason.
|
|
89
|
+
It never blocks approval, but you do not hand the human a brief with
|
|
90
|
+
unexamined advisories.
|
|
73
91
|
4. **Stop for the human.** Report the brief to the human, in the brief's
|
|
74
92
|
language, asking once, and say they can approve it in `canon dashboard`
|
|
75
93
|
(the briefs page, `/briefs.html`) or answer in chat. You never click
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@journeykit/canon",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.26.0",
|
|
4
4
|
"description": "canon — harness knowledge substrate. JS launcher: platform detection + native binary resolution.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -30,9 +30,9 @@
|
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {},
|
|
32
32
|
"optionalDependencies": {
|
|
33
|
-
"@journeykit/canon-core-darwin-arm64": "0.
|
|
34
|
-
"@journeykit/canon-core-linux-x64": "0.
|
|
35
|
-
"@journeykit/canon-core-win32-x64": "0.
|
|
33
|
+
"@journeykit/canon-core-darwin-arm64": "0.26.0",
|
|
34
|
+
"@journeykit/canon-core-linux-x64": "0.26.0",
|
|
35
|
+
"@journeykit/canon-core-win32-x64": "0.26.0"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@types/node": "^20.0.0",
|