@erclx/canon 4.73.0 → 4.75.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.
Files changed (52) hide show
  1. package/claude/.claude-plugin/plugin.json +1 -1
  2. package/claude/skills/create-skill/SKILL.md +0 -1
  3. package/claude/skills/docs-fold/SKILL.md +1 -1
  4. package/claude/skills/git-followup/SKILL.md +9 -5
  5. package/claude/skills/git-pr/SKILL.md +21 -0
  6. package/claude/skills/markdown-propose/REQUIREMENT.md +1 -1
  7. package/claude/skills/markdown-propose/SKILL.md +9 -7
  8. package/claude/skills/markdown-propose/references/format.md +5 -3
  9. package/claude/skills/plan-feature/SKILL.md +3 -3
  10. package/claude/skills/plan-groundwork/SKILL.md +6 -5
  11. package/claude/skills/plan-intake/SKILL.md +1 -1
  12. package/claude/skills/review-pr/SKILL.md +2 -2
  13. package/claude/skills/role-orchestrator/references/orchestrator-dispatch.md +1 -0
  14. package/claude/skills/role-orchestrator/references/orchestrator-parked.md +2 -1
  15. package/claude/skills/role-orchestrator/scripts/poll.sh +16 -10
  16. package/claude/skills/task-board/SKILL.md +37 -3
  17. package/claude/skills/teach-workspace/references/lesson-craft.md +1 -6
  18. package/docs/agents/commands.md +2 -0
  19. package/docs/agents/index.md +2 -1
  20. package/docs/agents/install-and-sync.md +7 -4
  21. package/docs/agents/pr-evidence.md +108 -0
  22. package/docs/agents/records.md +29 -9
  23. package/docs/agents/tasks.md +2 -0
  24. package/docs/target-projects.md +4 -0
  25. package/governance/rules/ui/440-surface-capture.md +1 -0
  26. package/package.json +1 -1
  27. package/src/commands/design.ts +10 -2
  28. package/src/commands/pr.ts +220 -0
  29. package/src/commands/records.ts +138 -0
  30. package/src/commands/transcripts.ts +20 -3
  31. package/src/design/base.css +59 -0
  32. package/src/design/components.ts +80 -50
  33. package/src/design/fonts.ts +21 -0
  34. package/src/init/plan.ts +8 -1
  35. package/src/init/steps.ts +17 -0
  36. package/src/intake/folder.ts +1 -1
  37. package/src/pr/evidence.ts +171 -0
  38. package/src/records/backup.ts +110 -38
  39. package/src/records/ordinal.ts +243 -0
  40. package/src/records/validate.ts +28 -0
  41. package/src/tasks/answers.ts +10 -1
  42. package/src/teach/fonts.ts +9 -11
  43. package/src/transcripts/fetch.ts +31 -1
  44. package/standards/architecture.md +1 -0
  45. package/standards/figures.md +51 -0
  46. package/standards/groundwork.md +2 -1
  47. package/standards/index.md +1 -0
  48. package/standards/intake.md +2 -1
  49. package/standards/plan.md +1 -0
  50. package/standards/skill.md +1 -1
  51. package/tooling/base/configs/.husky/post-merge +27 -0
  52. package/tooling/claude/seeds/.claude/hooks/tasks-index.sh +5 -4
@@ -73,6 +73,7 @@ export const FINDING_KINDS = [
73
73
  'item-incomplete',
74
74
  'category-mismatch',
75
75
  'operator-call-phrasing',
76
+ 'batch-unsplit',
76
77
  ] as const
77
78
 
78
79
  export type FindingKind = (typeof FINDING_KINDS)[number]
@@ -235,6 +236,18 @@ async function listFolders(dir: string): Promise<string[]> {
235
236
 
236
237
  const PLAN_NAME = /^feature-[a-z0-9]+(-[a-z0-9]+)*\.md$/
237
238
  const PLAN_TITLE = /^#[ \t]+Feature:[ \t]+\S/
239
+ const BATCH_LABEL = /^\*\*Batch\s+\d+\b.*\*\*/i
240
+
241
+ /**
242
+ * Reads the raw text rather than `splitPlanSections`'s output, since a
243
+ * colon-bearing `**Batch 1: ...**` line is itself a `MARKER_LINE` and is
244
+ * already dropped from the split `Files to touch` section before any check
245
+ * sees it.
246
+ */
247
+ export function hasStagedBatches(text: string): boolean {
248
+ return linesOutsideFences(text).some((line) => BATCH_LABEL.test(line.trim()))
249
+ }
250
+
238
251
  /**
239
252
  * An entry names a file and says something about it. Both halves are tested as
240
253
  * facts rather than as a syntax: a backticked span anywhere, and prose left over
@@ -463,6 +476,21 @@ export function checkPlan(name: string, text: string): Finding[] {
463
476
 
464
477
  findings.push(...checkQuestionContract(name, sections.get('Questions') ?? []))
465
478
 
479
+ if (hasStagedBatches(text)) {
480
+ const staged = linesOutsideFences(text).find((line) =>
481
+ BATCH_LABEL.test(line.trim()),
482
+ )
483
+
484
+ findings.push(
485
+ finding(
486
+ 'batch-unsplit',
487
+ name,
488
+ shorten((staged ?? '').trim()),
489
+ "stages a batch inside one file, so the batch sharing this plan's branch has nothing left to open a pull request against once an earlier one merges. Split it into one plan file per batch.",
490
+ ),
491
+ )
492
+ }
493
+
466
494
  return findings
467
495
  }
468
496
 
@@ -4,6 +4,7 @@ import { isAbsolute, relative, resolve } from 'node:path'
4
4
  import { isUnder } from '@/paths'
5
5
  import { recordDir, recordDirs } from '@/record-root'
6
6
  import {
7
+ hasStagedBatches,
7
8
  normalizeOperatorCall,
8
9
  OPERATOR_CALL,
9
10
  readQuestions,
@@ -219,9 +220,17 @@ export async function planAnswers(
219
220
  const resolved = resolvePlanReference(root, reference)
220
221
  if (!resolved.ok) return resolved
221
222
 
222
- const sections = splitPlanSections(await readFile(resolved.path, 'utf8'))
223
+ const text = await readFile(resolved.path, 'utf8')
224
+ const sections = splitPlanSections(text)
223
225
  const open = openQuestions(sections.get('Questions') ?? [])
224
226
 
227
+ if (hasStagedBatches(text)) {
228
+ open.push({
229
+ label: 'Batch staging',
230
+ why: 'stages a batch inside one file and must split into one plan file per batch before it can dispatch.',
231
+ })
232
+ }
233
+
225
234
  return {
226
235
  ok: true,
227
236
  plan: resolved.plan,