@erclx/canon 4.19.1 → 4.19.2
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/claude/.claude-plugin/plugin.json +1 -1
- package/package.json +1 -1
- package/src/labels/phase.ts +60 -25
- package/standards/publish.md +1 -1
package/package.json
CHANGED
package/src/labels/phase.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { linesOutsideFences, maskCodeSpans } from '@/markdown/scan'
|
|
2
|
-
import { RECORD_ENTRIES, RECORD_ROOTS
|
|
2
|
+
import { RECORD_ENTRIES, RECORD_ROOTS } from '@/record-root'
|
|
3
|
+
import type { RecordRoot } from '@/record-root'
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* The two version namespaces `standards/versioning.md` keeps apart, and why a
|
|
@@ -70,13 +71,15 @@ function escapeLiteral(text: string): string {
|
|
|
70
71
|
}
|
|
71
72
|
|
|
72
73
|
/**
|
|
73
|
-
* What is ignored under
|
|
74
|
+
* What is ignored under the tracked root beyond the entries the record move
|
|
75
|
+
* relocated.
|
|
74
76
|
*
|
|
75
77
|
* `RECORD_ENTRIES` answers which folders that move carried across, and this
|
|
76
|
-
* check asks which paths a reader on a remote cannot open
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
78
|
+
* check asks which paths a reader on a remote cannot open under `.claude/`,
|
|
79
|
+
* the one root still read by entry name below. The two questions differ by
|
|
80
|
+
* exactly one entry: the worktrees folder is ignored and stays out of
|
|
81
|
+
* `RECORD_ENTRIES` deliberately, since the harness creates a worktree there
|
|
82
|
+
* and requires its target to sit there, so adding it upstream would tell the
|
|
80
83
|
* migration to relocate a folder the harness pins.
|
|
81
84
|
*
|
|
82
85
|
* It is also the entry a worker announcement names most often, which is what
|
|
@@ -85,29 +88,61 @@ function escapeLiteral(text: string): string {
|
|
|
85
88
|
const IGNORED_BEYOND_RECORDS: readonly string[] = ['worktrees']
|
|
86
89
|
|
|
87
90
|
/**
|
|
88
|
-
*
|
|
89
|
-
* entries
|
|
91
|
+
* Which reading each record root takes: `whole` for a root one `.gitignore`
|
|
92
|
+
* line covers entirely, `entries` for a root kept narrow by name.
|
|
90
93
|
*
|
|
91
|
-
* `.
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
94
|
+
* `.canon/` takes `whole`: the ignore file excludes the folder outright, and
|
|
95
|
+
* git does not descend into an excluded directory, so no entry list is ever
|
|
96
|
+
* wider than the root itself. `.claude/` takes `entries` because it is
|
|
97
|
+
* tracked. A `Record` over `RecordRoot` rather than a filtered list of the
|
|
98
|
+
* roots read one way, so a root added to `RECORD_ROOTS` fails to typecheck
|
|
99
|
+
* here until this map says which reading it takes, rather than falling
|
|
100
|
+
* through a filter into the entry-list branch unnoticed.
|
|
101
|
+
*/
|
|
102
|
+
const ROOT_READING: Record<RecordRoot, 'whole' | 'entries'> = {
|
|
103
|
+
'.canon': 'whole',
|
|
104
|
+
'.claude': 'entries',
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* A path a reader on a remote cannot open.
|
|
109
|
+
*
|
|
110
|
+
* The two roots differ by why they are unreadable rather than by which one
|
|
111
|
+
* they are. A root reading `whole` matches on the root alone, since one
|
|
112
|
+
* ignore line covers everything beneath it and no entry list can ever be
|
|
113
|
+
* narrower than that. A root reading `entries` keeps the entry-list reading:
|
|
114
|
+
* `.claude/` is tracked and holds `rules`, `skills`, `hooks`, and `context`,
|
|
115
|
+
* so a rule path resolves in any clone and is not a board reference, and only
|
|
116
|
+
* the entries the record move relocated are unreadable there. Reading the
|
|
117
|
+
* roots and the relocated entries from `src/record-root.ts` is what makes a
|
|
118
|
+
* folder added there matched here without an edit, and the list above is what
|
|
119
|
+
* covers the one thing that module deliberately does not carry.
|
|
120
|
+
*
|
|
121
|
+
* The root-alone branch requires at least one tail character ahead of the
|
|
122
|
+
* shared tail capture, so a root written bare, such as the ignore line naming
|
|
123
|
+
* it as a concept, reports nothing: there is no path there for an author to
|
|
124
|
+
* remove. The entry-list branch instead bounds the entry name on the right
|
|
125
|
+
* with a lookahead, so `plans` does not also match the prefix of a longer
|
|
126
|
+
* word; that lookahead has to sit inside the entry branch rather than after
|
|
127
|
+
* the whole alternation; the character right after a bare root is an ordinary
|
|
128
|
+
* word character, and the same lookahead there would reject every root-alone
|
|
129
|
+
* match.
|
|
97
130
|
*
|
|
98
|
-
* The tail runs to the first whitespace or closing delimiter, so a report
|
|
99
|
-
* the whole path an author has to remove rather than the prefix that
|
|
131
|
+
* The tail runs to the first whitespace or closing delimiter, so a report
|
|
132
|
+
* names the whole path an author has to remove rather than the prefix that
|
|
133
|
+
* matched.
|
|
100
134
|
*/
|
|
101
135
|
const RECORD_PATH = new RegExp(
|
|
102
|
-
`(?<![\\w./-])(?:${RECORD_ROOTS.map(
|
|
103
|
-
|
|
104
|
-
`${escapeLiteral(root)}/(
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
136
|
+
`(?<![\\w./-])(?:${RECORD_ROOTS.map((root) =>
|
|
137
|
+
ROOT_READING[root] === 'whole'
|
|
138
|
+
? `${escapeLiteral(root)}/(?=[^\\s\`)\\]])`
|
|
139
|
+
: `${escapeLiteral(root)}/(?:${[
|
|
140
|
+
...RECORD_ENTRIES,
|
|
141
|
+
...IGNORED_BEYOND_RECORDS,
|
|
142
|
+
]
|
|
143
|
+
.map((entry) => escapeLiteral(entry))
|
|
144
|
+
.join('|')})(?![\\w-])`,
|
|
145
|
+
).join('|')})[^\\s\`)\\]]*`,
|
|
111
146
|
'g',
|
|
112
147
|
)
|
|
113
148
|
|
package/standards/publish.md
CHANGED
|
@@ -40,7 +40,7 @@ This check is one of the two the destination rule above scopes. The reader insid
|
|
|
40
40
|
|
|
41
41
|
A phase label is one way text names the board, and a path under a record root is the other. Both resolve for a reader holding this checkout and neither resolves for anyone else, so this check is the second one the destination rule scopes.
|
|
42
42
|
|
|
43
|
-
Two shapes get past a reader scanning for a bare label. A code span quoting a label is still the label, so read a span whose whole content is one as a hit and leave a longer token inside a span alone, which is a fixture name rather than a reference. The second shape is a path under a record root, gitignored and therefore absent from every clone, so `.canon/review/feedback/` names a folder the remote's reader cannot open. Under
|
|
43
|
+
Two shapes get past a reader scanning for a bare label. A code span quoting a label is still the label, so read a span whose whole content is one as a hit and leave a longer token inside a span alone, which is a fixture name rather than a reference. The second shape is a path under a record root, gitignored and therefore absent from every clone, so `.canon/review/feedback/` names a folder the remote's reader cannot open. Under `.claude/`'s own tracked folders there is no hit, since `.claude/rules/core/005-behavior.md` resolves everywhere. `.canon/` carries no such carve-out: one ignore line covers the root whole, so every path beneath it is a hit regardless of which folder names it.
|
|
44
44
|
|
|
45
45
|
Rewrite a hit to name what the reader can reach rather than deleting it. A row's subject stated plainly replaces its label, and what a record folder holds, said in a sentence, replaces its path.
|
|
46
46
|
|