Skip to content

Commit 284a635

Browse files
committed
chore(wheelhouse): cascade template@69b7686a
Auto-applied by socket-wheelhouse sync-scaffolding into cascade-socket-lib-93451. 186 file(s) touched: - .claude/hooks/fleet/_shared/dated-citation.mts - .claude/hooks/fleet/_shared/foreign-paths.mts - .claude/hooks/fleet/_shared/package-manager-auto-update.mts - .claude/hooks/fleet/alpha-sort-reminder/README.md - .claude/hooks/fleet/alpha-sort-reminder/index.mts - .claude/hooks/fleet/c8-ignore-reason-guard/index.mts - .claude/hooks/fleet/c8-ignore-reason-guard/test/index.test.mts - .claude/hooks/fleet/claude-segmentation-guard/README.md - .claude/hooks/fleet/commit-author-guard/README.md - .claude/hooks/fleet/commit-author-guard/index.mts - .claude/hooks/fleet/commit-author-guard/test/index.test.mts - .claude/hooks/fleet/commit-cadence-reminder/README.md - .claude/hooks/fleet/commit-cadence-reminder/index.mts - .claude/hooks/fleet/compound-lessons-reminder/index.mts - .claude/hooks/fleet/concurrent-cargo-build-guard/README.md - .claude/hooks/fleet/concurrent-cargo-build-guard/index.mts - .claude/hooks/fleet/concurrent-cargo-build-guard/test/index.test.mts - .claude/hooks/fleet/copy-on-select-hint-reminder/README.md - .claude/hooks/fleet/copy-on-select-hint-reminder/index.mts - .claude/hooks/fleet/copy-on-select-hint-reminder/test/index.test.mts ... and 166 more
1 parent dc7d849 commit 284a635

186 files changed

Lines changed: 10320 additions & 1199 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/**
2+
* @file Shared "is this rule rationale a dated incident log?" matcher. The
3+
* dated-citation-reminder (PreToolUse, nudges at edit time) and the
4+
* rule-citations-are-generic check (`check --all`, blocks committed prose)
5+
* both gate on the same definition, so the two surfaces never drift on what
6+
* counts as a too-specific citation.
7+
*
8+
* The rule (CLAUDE.md "Compound lessons into rules"): when a rule / hook /
9+
* SKILL / doc cites the case that motivated it, write it GENERICALLY, framed
10+
* as an example ("e.g. a cascade that shipped without its reconciled
11+
* lockfile") — NOT as a dated incident log ("2026-06-07: pnpm 11.0.0 vs
12+
* 11.5.1 at SHA abc1234"). Dates, version deltas, percentages, and commit
13+
* SHAs age into a changelog and leak detail; the example shape is timeless.
14+
*
15+
* Scope: only RATIONALE prose is flagged — a line carrying a rationale marker
16+
* (`**Why:**`, "incident", "Past incident", "regression", "red-lined") that
17+
* ALSO carries a specificity token. A bare date elsewhere (a SHA-pin
18+
* `# <tag> (YYYY-MM-DD)` comment, a `# published: YYYY-MM-DD` soak annotation,
19+
* a `.gitmodules` `# name-version`, a CHANGELOG entry, a version constant in
20+
* code) is NOT rationale and is left alone — those dates are required by other
21+
* rules. Memory files are exempt at the path layer (see EXEMPT_PATH_RE).
22+
*/
23+
24+
// A line is "rationale" if it carries one of these markers. Only rationale
25+
// lines are candidates — this keeps the matcher off required-date annotations.
26+
const RATIONALE_MARKER_RE =
27+
/\*\*Why:\*\*|\b(?:past\s+)?incident\b|\bred-lined?\b|\bregressed?\b|\bregression\b/i
28+
29+
// Specificity tokens that turn a generic example into a dated incident log.
30+
const SPECIFICITY_PATTERNS: ReadonlyArray<{ label: string; regex: RegExp }> = [
31+
// ISO-8601 date — the loudest "this is a log entry, not an example" signal.
32+
{ label: 'ISO date (YYYY-MM-DD)', regex: /\b20\d\d-\d\d-\d\d\b/ },
33+
// Percentage delta (coverage 98.9%→99.15%, etc).
34+
{
35+
label: 'percentage delta',
36+
regex: /\b\d+(?:\.\d+)?%\s*(?:|->|to)\s*\d+(?:\.\d+)?%/,
37+
},
38+
// Version delta — two semver-ish versions joined by vs / → / -> ("11.4.0 vs
39+
// 11.3.0", "bump to 11.5.0"). A SINGLE version alone is not flagged (a rule
40+
// may legitimately name the version it targets); the delta framing is what
41+
// marks a changelog entry.
42+
{
43+
label: 'version delta',
44+
regex:
45+
/\bv?\d+\.\d+(?:\.\d+)?\s*(?:vs\.?||->|versus)\s*v?\d+\.\d+(?:\.\d+)?\b/i,
46+
},
47+
// Commit SHA (7–40 hex) named in rationale prose ("at SHA abc1234", "broke
48+
// at deadbeef"). Requires a sha-ish lead-in word so prose words like
49+
// "deceased" or hex-looking ids elsewhere don't false-fire.
50+
{
51+
label: 'commit SHA',
52+
regex: /\b(?:sha|commit|at)\s+[0-9a-f]{7,40}\b/i,
53+
},
54+
]
55+
56+
// Paths whose prose is NOT fleet-facing rule rationale, so dated citations are
57+
// fine there. Memory files keep absolute dates for recall; CHANGELOG has its
58+
// own date convention; lockstep headers + .gitmodules carry required version
59+
// stamps.
60+
export const EXEMPT_PATH_RE =
61+
/(?:^|\/)(?:CHANGELOG\.md|\.gitmodules|lockstep\.json)$|\/memory\/|\/\.claude\/(?:plans|reports)\//
62+
63+
export interface DatedCitationHit {
64+
readonly label: string
65+
readonly line: number
66+
readonly text: string
67+
}
68+
69+
/**
70+
* Scan prose for dated-incident citations. Returns one hit per offending
71+
* rationale line (first matching specificity token wins per line). `text` is
72+
* the trimmed offending line, truncated for display.
73+
*/
74+
export function findDatedCitations(content: string): DatedCitationHit[] {
75+
const lines = content.split('\n')
76+
const hits: DatedCitationHit[] = []
77+
for (let i = 0, { length } = lines; i < length; i += 1) {
78+
const line = lines[i]!
79+
if (!RATIONALE_MARKER_RE.test(line)) {
80+
continue
81+
}
82+
for (let j = 0, { length: pLen } = SPECIFICITY_PATTERNS; j < pLen; j += 1) {
83+
const pattern = SPECIFICITY_PATTERNS[j]!
84+
if (pattern.regex.test(line)) {
85+
const trimmed = line.trim()
86+
hits.push({
87+
label: pattern.label,
88+
line: i + 1,
89+
text: trimmed.length > 160 ? `${trimmed.slice(0, 157)}…` : trimmed,
90+
})
91+
break
92+
}
93+
}
94+
}
95+
return hits
96+
}
97+
98+
/**
99+
* True when `filePath` is a fleet-facing rule-prose surface whose citations
100+
* must be generic. Used by both the edit-time hook and the commit-time check.
101+
*/
102+
export function isRuleProseSurface(filePath: string): boolean {
103+
if (EXEMPT_PATH_RE.test(filePath)) {
104+
return false
105+
}
106+
return (
107+
/(?:^|\/)CLAUDE\.md$/.test(filePath) ||
108+
/(?:^|\/)docs\/claude\.md\/fleet\//.test(filePath) ||
109+
/(?:^|\/)\.claude\/skills\/.*\/SKILL\.md$/.test(filePath) ||
110+
/(?:^|\/)\.claude\/hooks\/fleet\/[^/]+\/README\.md$/.test(filePath)
111+
)
112+
}

.claude/hooks/fleet/_shared/foreign-paths.mts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import os from 'node:os'
2727
import path from 'node:path'
2828

2929
// Untracked-by-default path prefixes — kept in lock-step with
30-
// dirty-worktree-on-stop-reminder. Vendored / build-copied trees are
30+
// dirty-worktree-stop-reminder. Vendored / build-copied trees are
3131
// expected to be dirty and are never "another agent's work".
3232
const UNTRACKED_BY_DEFAULT_PREFIXES = [
3333
'additions/source-patched/',

0 commit comments

Comments
 (0)