@erclx/canon 5.43.0 → 5.44.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 (133) hide show
  1. package/claude/.claude-plugin/plugin.json +1 -1
  2. package/claude/skills/backlog-triage/references/apply.md +6 -6
  3. package/claude/skills/canon-cli/SKILL.md +4 -4
  4. package/claude/skills/canvas-board/REQUIREMENT.md +1 -1
  5. package/claude/skills/canvas-board/SKILL.md +1 -1
  6. package/claude/skills/context-fold/references/context.md +1 -1
  7. package/claude/skills/create-skill/REQUIREMENT.md +1 -1
  8. package/claude/skills/create-skill/SKILL.md +2 -2
  9. package/claude/skills/create-skill/references/skill-requirement.md +68 -9
  10. package/claude/skills/create-skill/references/skill.md +1 -1
  11. package/claude/skills/design-identity/REQUIREMENT.md +40 -0
  12. package/claude/skills/design-identity/SKILL.md +138 -0
  13. package/claude/skills/{draft-identity → design-identity}/references/card-route.md +2 -2
  14. package/claude/skills/design-taste/REQUIREMENT.md +4 -4
  15. package/claude/skills/design-taste/SKILL.md +2 -2
  16. package/claude/skills/design-taste/references/kinds.md +1 -1
  17. package/claude/skills/design-taste/references/preflight.md +1 -1
  18. package/claude/skills/draft-and-pick/REQUIREMENT.md +8 -69
  19. package/claude/skills/draft-and-pick/SKILL.md +3 -153
  20. package/claude/skills/draft-doc/REQUIREMENT.md +9 -30
  21. package/claude/skills/draft-doc/SKILL.md +4 -76
  22. package/claude/skills/draft-figure/REQUIREMENT.md +8 -42
  23. package/claude/skills/draft-figure/SKILL.md +3 -77
  24. package/claude/skills/draft-identity/REQUIREMENT.md +8 -23
  25. package/claude/skills/draft-identity/SKILL.md +3 -130
  26. package/claude/skills/draft-ready/REQUIREMENT.md +8 -37
  27. package/claude/skills/draft-ready/SKILL.md +3 -128
  28. package/claude/skills/draft-screencast/REQUIREMENT.md +8 -23
  29. package/claude/skills/draft-screencast/SKILL.md +3 -62
  30. package/claude/skills/draft-slides/REQUIREMENT.md +8 -27
  31. package/claude/skills/draft-slides/SKILL.md +3 -72
  32. package/claude/skills/draw-figure/REQUIREMENT.md +59 -0
  33. package/claude/skills/draw-figure/SKILL.md +85 -0
  34. package/claude/skills/{draft-figure → draw-figure}/references/architecture-set.md +2 -2
  35. package/claude/skills/{draft-figure → draw-figure}/references/hand-drawn.md +2 -2
  36. package/claude/skills/{draft-figure → draw-figure}/references/mermaid-path.md +2 -2
  37. package/claude/skills/{draft-figure → draw-figure}/references/mermaid.md +1 -1
  38. package/claude/skills/make-deck/REQUIREMENT.md +44 -0
  39. package/claude/skills/make-deck/SKILL.md +80 -0
  40. package/claude/skills/markdown-craft/REQUIREMENT.md +1 -1
  41. package/claude/skills/markdown-craft/SKILL.md +2 -2
  42. package/claude/skills/pick-by-eye/REQUIREMENT.md +86 -0
  43. package/claude/skills/pick-by-eye/SKILL.md +161 -0
  44. package/claude/skills/plan-feature/SKILL.md +2 -1
  45. package/claude/skills/plan-feature/references/plan.md +24 -10
  46. package/claude/skills/plan-groundwork/SKILL.md +1 -1
  47. package/claude/skills/plan-intake/SKILL.md +1 -1
  48. package/claude/skills/plan-ready/REQUIREMENT.md +54 -0
  49. package/claude/skills/plan-ready/SKILL.md +136 -0
  50. package/claude/skills/{draft-ready → plan-ready}/references/assembly.md +1 -1
  51. package/claude/skills/{draft-ready → plan-ready}/references/ready.md +1 -1
  52. package/claude/skills/record-screencast/REQUIREMENT.md +3 -3
  53. package/claude/skills/record-screencast/SKILL.md +2 -2
  54. package/claude/skills/role-orchestrator/REQUIREMENT.md +1 -1
  55. package/claude/skills/role-orchestrator/references/orchestrator-dispatch.md +5 -2
  56. package/claude/skills/role-orchestrator/references/orchestrator-parked.md +2 -2
  57. package/claude/skills/role-orchestrator/references/orchestrator-refill.md +18 -13
  58. package/claude/skills/role-planner/SKILL.md +2 -1
  59. package/claude/skills/role-worker/SKILL.md +9 -0
  60. package/claude/skills/script-screencast/REQUIREMENT.md +40 -0
  61. package/claude/skills/script-screencast/SKILL.md +70 -0
  62. package/claude/skills/{draft-screencast → script-screencast}/references/beats-template.md +2 -2
  63. package/claude/skills/search-craft/REQUIREMENT.md +4 -4
  64. package/claude/skills/search-craft/SKILL.md +1 -1
  65. package/claude/skills/target-setup/SKILL.md +1 -0
  66. package/claude/skills/task-board/SKILL.md +11 -9
  67. package/claude/skills/task-board/references/board.md +15 -12
  68. package/claude/skills/teach-workspace/SKILL.md +1 -1
  69. package/claude/skills/teach-workspace/references/lesson-craft.md +2 -2
  70. package/claude/skills/ui-checklist/SKILL.md +2 -0
  71. package/claude/skills/write-doc/REQUIREMENT.md +46 -0
  72. package/claude/skills/write-doc/SKILL.md +83 -0
  73. package/claude/skills/{draft-doc → write-doc}/references/context.md +2 -2
  74. package/claude/skills/{draft-doc → write-doc}/references/docs.md +2 -2
  75. package/claude/skills/{draft-doc → write-doc}/references/readme.md +2 -2
  76. package/claude/skills/{draft-doc → write-doc}/references/wiki-kind.md +3 -3
  77. package/docs/agents/markdown-audit.md +4 -2
  78. package/docs/agents/migrate.md +2 -2
  79. package/docs/target/migrations.md +17 -4
  80. package/docs/target/projects.md +1 -1
  81. package/docs/workflow/parallel-features.md +3 -2
  82. package/docs/workflow/skill-map-on-request.md +18 -12
  83. package/docs/workflow/skill-map.md +13 -12
  84. package/docs/workflow/visual-design-workflow.md +2 -2
  85. package/governance/ledger/framework/astro.md +73 -0
  86. package/governance/ledger/framework/fastapi.md +76 -0
  87. package/governance/ledger/framework/nextjs.md +86 -0
  88. package/governance/ledger/framework/react.md +83 -0
  89. package/governance/ledger/framework/shadcn.md +60 -0
  90. package/governance/ledger/framework/tailwind.md +55 -0
  91. package/governance/rules/framework/astro.md +12 -14
  92. package/governance/rules/framework/fastapi.md +20 -24
  93. package/governance/rules/framework/nextjs.md +19 -27
  94. package/governance/rules/framework/react.md +22 -33
  95. package/governance/rules/framework/shadcn.md +11 -15
  96. package/governance/rules/framework/tailwind.md +12 -14
  97. package/governance/rules/writing/markdown.md +1 -1
  98. package/package.json +1 -1
  99. package/src/claude/cases/authoring.ts +15 -3
  100. package/src/claude/cases/misc.ts +12 -0
  101. package/src/claude/cases/workflow.ts +19 -1
  102. package/src/claude/skills-audit.ts +113 -1
  103. package/src/commands/claude.ts +34 -11
  104. package/src/commands/demo.ts +1 -1
  105. package/src/commands/markdown.ts +1 -1
  106. package/src/commands/migrate.ts +4 -0
  107. package/src/commands/pr.ts +30 -6
  108. package/src/commands/tasks.ts +383 -2
  109. package/src/context/audit.ts +1 -1
  110. package/src/demo/beats.ts +1 -1
  111. package/src/gov/audit.ts +3 -2
  112. package/src/markdown/scope.ts +45 -35
  113. package/src/migrate/record-layout.ts +1 -1
  114. package/src/migrate/skill-names.ts +68 -21
  115. package/src/pr/frames.ts +14 -3
  116. package/src/records/validate.ts +31 -0
  117. package/src/tasks/board.ts +854 -0
  118. package/src/tasks/record.ts +20 -1
  119. package/src/tasks/validate.ts +206 -9
  120. package/tooling/astro/configs/card-src/pages/og-card.astro +1 -1
  121. package/tooling/astro/reference.md +2 -2
  122. package/tooling/claude/seeds/.claude/hooks/board-validate.sh +65 -0
  123. package/tooling/claude/seeds/.claude/settings.json +4 -0
  124. /package/claude/skills/{draft-figure → draw-figure}/references/figures.md +0 -0
  125. /package/claude/skills/{draft-slides → make-deck}/references/deck.md +0 -0
  126. /package/claude/skills/{draft-and-pick → pick-by-eye}/references/live-arms.md +0 -0
  127. /package/claude/skills/{draft-doc → write-doc}/references/readme/agent-facing.md +0 -0
  128. /package/claude/skills/{draft-doc → write-doc}/references/readme/application.md +0 -0
  129. /package/claude/skills/{draft-doc → write-doc}/references/readme/cli.md +0 -0
  130. /package/claude/skills/{draft-doc → write-doc}/references/readme/library.md +0 -0
  131. /package/claude/skills/{draft-doc → write-doc}/references/readme/plugin.md +0 -0
  132. /package/claude/skills/{draft-doc → write-doc}/references/requirements.md +0 -0
  133. /package/claude/skills/{draft-doc → write-doc}/references/wiki.md +0 -0
@@ -3,6 +3,17 @@ import type { Command } from 'commander'
3
3
  import { execa } from 'execa'
4
4
  import { gitEnv } from '@/git/env'
5
5
  import { type AnswersOutcome, planAnswers } from '@/tasks/answers'
6
+ import {
7
+ type BoardOutcome,
8
+ type BoardRefused,
9
+ blockTask,
10
+ moveTask,
11
+ PLANNED_WAITING,
12
+ type Position,
13
+ placeTask,
14
+ promoteTask,
15
+ WAITING_LIMIT,
16
+ } from '@/tasks/board'
6
17
  import { type BranchOutcome, planBranch } from '@/tasks/branch'
7
18
  import {
8
19
  type ArchiveOutcome,
@@ -40,6 +51,7 @@ import {
40
51
  recordPullRequest,
41
52
  } from '@/tasks/record'
42
53
  import {
54
+ type BoardGroup,
43
55
  type Finding,
44
56
  type FolderClaim,
45
57
  type SharedFiles,
@@ -118,6 +130,7 @@ interface PullRequestCommandOptions {
118
130
 
119
131
  interface PlanLinkCommandOptions {
120
132
  readonly add?: boolean
133
+ readonly waiting?: string
121
134
  readonly json?: boolean
122
135
  readonly root?: string
123
136
  }
@@ -140,6 +153,31 @@ interface NextLabelCommandOptions {
140
153
  readonly root?: string
141
154
  }
142
155
 
156
+ interface BoardCommandOptions {
157
+ readonly json?: boolean
158
+ readonly root?: string
159
+ }
160
+
161
+ interface PositionCommandOptions {
162
+ readonly top?: boolean
163
+ readonly bottom?: boolean
164
+ readonly before?: string
165
+ readonly after?: string
166
+ }
167
+
168
+ interface PlaceCommandOptions
169
+ extends BoardCommandOptions, PositionCommandOptions {
170
+ readonly group: string
171
+ readonly waiting?: string
172
+ }
173
+
174
+ interface PromoteCommandOptions extends BoardCommandOptions {
175
+ readonly waiting?: string
176
+ }
177
+
178
+ interface MoveCommandOptions
179
+ extends BoardCommandOptions, PositionCommandOptions {}
180
+
143
181
  export function register(program: Command): void {
144
182
  const tasks = program
145
183
  .command('tasks')
@@ -259,6 +297,10 @@ export function register(program: Command): void {
259
297
  ' every task file carries a board row or a backlog line, never both',
260
298
  ' no task carries more than one row',
261
299
  ' every Run now row names the files it touches',
300
+ ' every Touches cell matches what its plans derive',
301
+ ' every group header is the set of columns the group owns',
302
+ ` no Waiting on cell renders past ${WAITING_LIMIT} characters`,
303
+ ' a blocker citing a bare vNNN.N label settles like a linked one',
262
304
  ' a file two Run now rows share is listed, never failed',
263
305
  '',
264
306
  'Refusals, read off `reason`:',
@@ -493,6 +535,10 @@ export function register(program: Command): void {
493
535
  '--add',
494
536
  'Append the plan to the line, for a task sliced over plans',
495
537
  )
538
+ .option(
539
+ '--waiting <text>',
540
+ `The Waiting on cell a promoted row carries, "${PLANNED_WAITING}" when omitted`,
541
+ )
496
542
  .option('--json', 'Emit a machine-readable record on stdout')
497
543
  .option('--root <path>', 'Board root, defaulting to the main worktree')
498
544
  .addHelpText(
@@ -521,6 +567,10 @@ export function register(program: Command): void {
521
567
  'sliced task links one plan per slice. It reports unchanged when the',
522
568
  'plan is already listed and is allowed on a line holding several.',
523
569
  '',
570
+ 'A row under Needs a plan moves to the bottom of Up next through the',
571
+ 'board verbs, and `board` in the record says whether it moved and why',
572
+ 'not. A row elsewhere, or a task with no row, stays where it is.',
573
+ '',
524
574
  'Examples:',
525
575
  ' canon tasks plan-link v28.1-trigger-escalation dispatch-answer-gate',
526
576
  ' canon tasks plan-link v28.1-trigger-escalation second-slice --add',
@@ -661,6 +711,327 @@ export function register(program: Command): void {
661
711
  .action(async (opts: ListCommandOptions) => {
662
712
  process.exitCode = await runList(opts)
663
713
  })
714
+
715
+ registerBoardWrites(tasks)
716
+ }
717
+
718
+ const BOARD_WRITE_REFUSALS = [
719
+ 'Refusals, read off `reason`:',
720
+ ' no-board the repository holds no `.canon/tasks/` board',
721
+ ' no-ordering the board has no priority.md',
722
+ ' no-match no task file carries the stem',
723
+ ' no-row the task has no row on the board',
724
+ ' unreadable-row a row names no task, or its header cannot place its blocker',
725
+ ' board-busy another board verb held the lock past the wait, so run it again',
726
+ ' board-changed a hand edit landed while this one rendered, so run it again',
727
+ ' group-rule the target group forbids the row, naming the validate kind',
728
+ ' no-column the row sits under Run now, which has no Waiting on cell',
729
+ ' top-group the row already sits under Run now',
730
+ ` cell-long the Waiting on cell renders past ${WAITING_LIMIT} characters`,
731
+ ' bad-input a malformed group, position, or Waiting on text',
732
+ '',
733
+ 'Exit codes:',
734
+ ' 0 the board was written',
735
+ ' 1 refused, with the reason on stderr or in the JSON record',
736
+ '',
737
+ 'Every write reads priority.md once, renders every table from the task',
738
+ 'files and their plans, and writes it once. Task, Touches, and Plan are',
739
+ 'derived, so only Waiting on is typed. Safe from a linked worktree, since',
740
+ 'it resolves the board root in-process.',
741
+ ]
742
+
743
+ const GROUP_FLAGS: Readonly<Record<string, BoardGroup>> = {
744
+ 'run-now': 'Run now',
745
+ 'up-next': 'Up next',
746
+ 'needs-a-plan': 'Needs a plan',
747
+ }
748
+
749
+ function withPosition(command: Command): Command {
750
+ return command
751
+ .option('--top', 'Put the row first in its group')
752
+ .option('--bottom', 'Put the row last in its group, the default')
753
+ .option('--before <task>', 'Put the row right above this sibling')
754
+ .option('--after <task>', 'Put the row right below this sibling')
755
+ }
756
+
757
+ function registerBoardWrites(tasks: Command): void {
758
+ withPosition(
759
+ tasks
760
+ .command('place')
761
+ .description(
762
+ 'Put a task under a board group, from another group, the backlog, or nowhere',
763
+ )
764
+ .argument('<task>', 'Task filename stem, as in v28.1-trigger-escalation')
765
+ .requiredOption(
766
+ '--group <group>',
767
+ 'Target group: run-now, up-next, or needs-a-plan',
768
+ )
769
+ .option('--waiting <text>', 'The Waiting on cell, kept when omitted'),
770
+ )
771
+ .helpOption('-h, --help', 'Show this help message')
772
+ .option('--json', 'Emit a machine-readable record on stdout')
773
+ .option('--root <path>', 'Board root, defaulting to the main worktree')
774
+ .addHelpText(
775
+ 'after',
776
+ [
777
+ '',
778
+ ...BOARD_WRITE_REFUSALS,
779
+ '',
780
+ 'A backlog line for the task is removed in the same call. The row keeps',
781
+ 'its Waiting on cell when --waiting is omitted, and a row new to Up next',
782
+ 'or Needs a plan has none to keep, so it needs one.',
783
+ '',
784
+ 'Examples:',
785
+ ' canon tasks place v28.1-trigger-escalation --group needs-a-plan --bottom --waiting "Not planned yet"',
786
+ ' canon tasks place v28.1-trigger-escalation --group up-next --before v28.2-other --json',
787
+ '',
788
+ ].join('\n'),
789
+ )
790
+ .action(async (task: string, opts: PlaceCommandOptions) => {
791
+ process.exitCode = await runPlace(task, opts)
792
+ })
793
+
794
+ tasks
795
+ .command('promote')
796
+ .description('Move a row one group closer to Run now, to its bottom')
797
+ .argument('<task>', 'Task filename stem, as in v28.1-trigger-escalation')
798
+ .helpOption('-h, --help', 'Show this help message')
799
+ .option(
800
+ '--waiting <text>',
801
+ `The Waiting on cell a row entering Up next carries, "${PLANNED_WAITING}" when omitted`,
802
+ )
803
+ .option('--json', 'Emit a machine-readable record on stdout')
804
+ .option('--root <path>', 'Board root, defaulting to the main worktree')
805
+ .addHelpText(
806
+ 'after',
807
+ [
808
+ '',
809
+ ...BOARD_WRITE_REFUSALS,
810
+ '',
811
+ 'Up next moves to Run now, which reads as dispatched, so a dispatcher',
812
+ 'runs it once a launch succeeds. Needs a plan moves to Up next.',
813
+ '',
814
+ 'Examples:',
815
+ ' canon tasks promote v28.1-trigger-escalation',
816
+ ' canon tasks promote v28.1-trigger-escalation --json',
817
+ '',
818
+ ].join('\n'),
819
+ )
820
+ .action(async (task: string, opts: PromoteCommandOptions) => {
821
+ process.exitCode = await runPromote(task, opts)
822
+ })
823
+
824
+ withPosition(
825
+ tasks
826
+ .command('move')
827
+ .description('Reposition a row inside the group it sits in')
828
+ .argument('<task>', 'Task filename stem, as in v28.1-trigger-escalation'),
829
+ )
830
+ .helpOption('-h, --help', 'Show this help message')
831
+ .option('--json', 'Emit a machine-readable record on stdout')
832
+ .option('--root <path>', 'Board root, defaulting to the main worktree')
833
+ .addHelpText(
834
+ 'after',
835
+ [
836
+ '',
837
+ ...BOARD_WRITE_REFUSALS,
838
+ '',
839
+ 'Examples:',
840
+ ' canon tasks move v28.1-trigger-escalation --top',
841
+ ' canon tasks move v28.1-trigger-escalation --after v28.2-other --json',
842
+ '',
843
+ ].join('\n'),
844
+ )
845
+ .action(async (task: string, opts: MoveCommandOptions) => {
846
+ process.exitCode = await runMove(task, opts)
847
+ })
848
+
849
+ tasks
850
+ .command('block')
851
+ .description("Rewrite a row's Waiting on cell")
852
+ .argument('<task>', 'Task filename stem, as in v28.1-trigger-escalation')
853
+ .argument(
854
+ '<waiting>',
855
+ `What the row waits on, at most ${WAITING_LIMIT} rendered characters`,
856
+ )
857
+ .helpOption('-h, --help', 'Show this help message')
858
+ .option('--json', 'Emit a machine-readable record on stdout')
859
+ .option('--root <path>', 'Board root, defaulting to the main worktree')
860
+ .addHelpText(
861
+ 'after',
862
+ [
863
+ '',
864
+ ...BOARD_WRITE_REFUSALS,
865
+ '',
866
+ 'A link counts as its label alone toward the cap, since only the label',
867
+ 'renders.',
868
+ '',
869
+ 'Examples:',
870
+ ' canon tasks block v28.1-trigger-escalation "[v28.2](v28.2-other.md) merging"',
871
+ ' canon tasks block v28.1-trigger-escalation "The operator\'s call on the scope" --json',
872
+ '',
873
+ ].join('\n'),
874
+ )
875
+ .action(
876
+ async (task: string, waiting: string, opts: BoardCommandOptions) => {
877
+ process.exitCode = await runBlock(task, waiting, opts)
878
+ },
879
+ )
880
+ }
881
+
882
+ /**
883
+ * Reads the one position flag a call may carry. Two flags at once is refused
884
+ * rather than resolved by precedence, since either reading moves a row the
885
+ * caller did not mean to.
886
+ */
887
+ function positionFrom(
888
+ opts: PositionCommandOptions,
889
+ ): Position | undefined | BoardRefused {
890
+ const given: Position[] = [
891
+ ...(opts.top ? [{ at: 'top' } as const] : []),
892
+ ...(opts.bottom ? [{ at: 'bottom' } as const] : []),
893
+ ...(opts.before ? [{ before: opts.before }] : []),
894
+ ...(opts.after ? [{ after: opts.after }] : []),
895
+ ]
896
+
897
+ if (given.length > 1) {
898
+ return {
899
+ ok: false,
900
+ reason: 'bad-input',
901
+ message: 'Pass one of --top, --bottom, --before, or --after.',
902
+ detail: [],
903
+ }
904
+ }
905
+
906
+ return given[0]
907
+ }
908
+
909
+ async function runPlace(
910
+ task: string,
911
+ opts: PlaceCommandOptions,
912
+ ): Promise<number> {
913
+ const emitJson = opts.json ?? false
914
+ const group = GROUP_FLAGS[opts.group]
915
+ if (!group) {
916
+ return reportRecord(
917
+ 'canon tasks place',
918
+ {
919
+ ok: false,
920
+ reason: 'bad-input',
921
+ message: `Not a group: ${opts.group}. Pass run-now, up-next, or needs-a-plan.`,
922
+ detail: [opts.group],
923
+ },
924
+ emitJson,
925
+ process.cwd(),
926
+ )
927
+ }
928
+
929
+ const position = positionFrom(opts)
930
+ if (position && 'ok' in position) {
931
+ return reportRecord('canon tasks place', position, emitJson, process.cwd())
932
+ }
933
+
934
+ const root = opts.root ?? (await mainWorktreeRoot())
935
+ const outcome = await placeTask(root, task, group, {
936
+ waiting: opts.waiting,
937
+ position,
938
+ })
939
+
940
+ return reportBoardWrite('canon tasks place', outcome, emitJson, root)
941
+ }
942
+
943
+ async function runPromote(
944
+ task: string,
945
+ opts: PromoteCommandOptions,
946
+ ): Promise<number> {
947
+ const root = opts.root ?? (await mainWorktreeRoot())
948
+ const outcome = await promoteTask(root, task, { waiting: opts.waiting })
949
+
950
+ return reportBoardWrite(
951
+ 'canon tasks promote',
952
+ outcome,
953
+ opts.json ?? false,
954
+ root,
955
+ )
956
+ }
957
+
958
+ async function runMove(
959
+ task: string,
960
+ opts: MoveCommandOptions,
961
+ ): Promise<number> {
962
+ const emitJson = opts.json ?? false
963
+ const position = positionFrom(opts)
964
+ if (position === undefined || 'ok' in position) {
965
+ return reportRecord(
966
+ 'canon tasks move',
967
+ position ?? {
968
+ ok: false,
969
+ reason: 'bad-input',
970
+ message:
971
+ 'No position named. Pass --top, --bottom, --before, or --after.',
972
+ detail: [],
973
+ },
974
+ emitJson,
975
+ process.cwd(),
976
+ )
977
+ }
978
+
979
+ const root = opts.root ?? (await mainWorktreeRoot())
980
+ const outcome = await moveTask(root, task, position)
981
+
982
+ return reportBoardWrite('canon tasks move', outcome, emitJson, root)
983
+ }
984
+
985
+ async function runBlock(
986
+ task: string,
987
+ waiting: string,
988
+ opts: BoardCommandOptions,
989
+ ): Promise<number> {
990
+ const root = opts.root ?? (await mainWorktreeRoot())
991
+ const outcome = await blockTask(root, task, waiting)
992
+
993
+ return reportBoardWrite(
994
+ 'canon tasks block',
995
+ outcome,
996
+ opts.json ?? false,
997
+ root,
998
+ )
999
+ }
1000
+
1001
+ function reportBoardWrite(
1002
+ title: string,
1003
+ outcome: BoardOutcome,
1004
+ emitJson: boolean,
1005
+ root: string,
1006
+ ): number {
1007
+ if (!outcome.ok) return reportRecord(title, outcome, emitJson, root)
1008
+
1009
+ if (emitJson) {
1010
+ process.stdout.write(
1011
+ `${JSON.stringify({
1012
+ ok: true,
1013
+ root,
1014
+ task: outcome.stem,
1015
+ path: relative(root, outcome.path),
1016
+ group: outcome.group,
1017
+ from: outcome.from ?? null,
1018
+ position: outcome.position,
1019
+ backlog: outcome.backlog,
1020
+ })}\n`,
1021
+ )
1022
+ return 0
1023
+ }
1024
+
1025
+ intro(title)
1026
+ logStep('Written')
1027
+ logInfo(
1028
+ `${outcome.stem} sits at ${outcome.position} under ${outcome.group}${outcome.from && outcome.from !== outcome.group ? `, from ${outcome.from}` : ''}`,
1029
+ )
1030
+ if (outcome.backlog) logRemove('its backlog line')
1031
+ logAdd(relative(root, outcome.path))
1032
+ outro()
1033
+
1034
+ return 0
664
1035
  }
665
1036
 
666
1037
  function collectPosition(value: string, previous: string[]): string[] {
@@ -832,7 +1203,13 @@ async function runPlanLink(
832
1203
  ): Promise<number> {
833
1204
  const emitJson = opts.json ?? false
834
1205
  const root = opts.root ?? (await mainWorktreeRoot())
835
- const outcome = await recordPlan(root, task, plan, opts.add ?? false)
1206
+ const outcome = await recordPlan(
1207
+ root,
1208
+ task,
1209
+ plan,
1210
+ opts.add ?? false,
1211
+ opts.waiting,
1212
+ )
836
1213
 
837
1214
  return reportPlanLink(outcome, emitJson, root)
838
1215
  }
@@ -900,7 +1277,7 @@ async function runOutcome(
900
1277
 
901
1278
  function reportRecord(
902
1279
  title: string,
903
- refused: RecordRefused,
1280
+ refused: RecordRefused | BoardRefused,
904
1281
  emitJson: boolean,
905
1282
  root: string,
906
1283
  ): number {
@@ -985,6 +1362,7 @@ function reportPlanLink(
985
1362
  plan: outcome.plan,
986
1363
  action: outcome.action,
987
1364
  replaced: outcome.replaced,
1365
+ board: { ...outcome.board, from: outcome.board.from ?? null },
988
1366
  })}\n`,
989
1367
  )
990
1368
  return 0
@@ -995,6 +1373,9 @@ function reportPlanLink(
995
1373
  logInfo(`${outcome.stem} names plan ${outcome.plan}`)
996
1374
  if (outcome.replaced) logInfo(`replaced ${outcome.replaced}`)
997
1375
  if (outcome.action !== 'unchanged') logAdd(relative(root, outcome.path))
1376
+ logInfo(
1377
+ `board row ${outcome.board.moved ? '' : 'not moved, '}${outcome.board.note}`,
1378
+ )
998
1379
  outro()
999
1380
 
1000
1381
  return 0
@@ -209,7 +209,7 @@ const SENTENCE_END = /[.!?:;]\s(?=[^.!?:;]*$)/
209
209
  * reading that field for a gain of one word.
210
210
  *
211
211
  * `claude/skills/context-fold/references/context.md` opens its scope by handing figures to the
212
- * `draft-figure` skill, so a marker reported in a diagrams entry would cite a
212
+ * `draw-figure` skill, so a marker reported in a diagrams entry would cite a
213
213
  * rule that entry's own standard routes elsewhere. The length and table
214
214
  * checkpoints are quoted from the same standard and keep reaching every audited
215
215
  * folder, because a threshold on how far a reader travels generalizes across
package/src/demo/beats.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Reads the human-facing draft `draft-screencast` writes. Nothing here knows
2
+ * Reads the human-facing draft `script-screencast` writes. Nothing here knows
3
3
  * about a browser: the draft is prose aimed at a person, and turning it into
4
4
  * something executable is `@/demo/compile`'s job.
5
5
  */
package/src/gov/audit.ts CHANGED
@@ -27,12 +27,13 @@ export type RuleLimit = keyof typeof RULE_LIMITS | 'emphasis'
27
27
  * Rule folders whose findings fail `--gate`, as path prefixes from the root.
28
28
  *
29
29
  * Each folder's rewrite appends its own line once that folder reaches zero, so
30
- * the slices merge without touching one another's entries. `canon/` waits on
31
- * `plan.md`, which a later slice rewrites.
30
+ * the slices merge without touching one another's entries.
32
31
  */
33
32
  export const RULE_GATED_FOLDERS: readonly string[] = [
33
+ join('governance', 'rules', 'canon') + sep,
34
34
  join('governance', 'rules', 'claude') + sep,
35
35
  join('governance', 'rules', 'code') + sep,
36
+ join('governance', 'rules', 'framework') + sep,
36
37
  join('governance', 'rules', 'writing') + sep,
37
38
  join('internal', 'rules') + sep,
38
39
  ]
@@ -8,19 +8,28 @@ const BULLET_RUN_SCOPE = [
8
8
  /^(?:claude|\.claude)\/skills\/[^/]+\/references\/.+\.md$/,
9
9
  ]
10
10
 
11
+ /**
12
+ * The requirement beside each skill. Its findings gate only on a `GATED_PATHS`
13
+ * entry naming the file itself, so the prefix that gates a skill's body never
14
+ * sweeps in a requirement the rewrite has not reached yet.
15
+ */
16
+ const REQUIREMENT_SCOPE =
17
+ /^(?:claude|\.claude)\/skills\/[^/]+\/REQUIREMENT\.md$/
18
+
11
19
  /**
12
20
  * The tracked documents the density limits hold: the context entries, the docs
13
- * pages, the skill bodies and references of both skill trees, the tooling
14
- * seeds and stack references, the wiki, and the two root guides.
21
+ * pages, the skill bodies, references, and requirements of both skill trees,
22
+ * the tooling seeds and stack references, the wiki, and the two root guides.
15
23
  *
16
24
  * Rules stay out, since a rule is one imperative per bullet. So do
17
- * `CLAUDE.md`, the requirement and design records, a seed carrying one of
18
- * those three names, the changelog, the examples, and every `.canon/` record,
19
- * each with a shape and cap of its own. Named here and nowhere else, so the
20
- * audit, the gate stage reading its exit, and their tests read one scope.
25
+ * `CLAUDE.md`, the project requirement and design records, a seed carrying one
26
+ * of those three names, the changelog, the examples, and every `.canon/`
27
+ * record, each with a shape and cap of its own. Named here and nowhere else, so
28
+ * the audit, the gate stage reading its exit, and their tests read one scope.
21
29
  */
22
30
  const DENSITY_SCOPE = [
23
31
  ...BULLET_RUN_SCOPE,
32
+ REQUIREMENT_SCOPE,
24
33
  /^tooling\/[^/]+\/seeds\/(?:.+\/)?(?!(?:CLAUDE|REQUIREMENTS|DESIGN)\.md$)[^/]+\.md$/,
25
34
  /^tooling\/[^/]+\/reference\.md$/,
26
35
  /^wiki\/.+\.md$/,
@@ -105,35 +114,35 @@ export const GATED_PATHS: readonly string[] = [
105
114
  'claude/skills/docs-sync/references/readme.md',
106
115
  'claude/skills/document-health/SKILL.md',
107
116
  'claude/skills/document-health/references/axes.md',
108
- 'claude/skills/draft-and-pick/SKILL.md',
109
- 'claude/skills/draft-and-pick/references/live-arms.md',
110
- 'claude/skills/draft-doc/SKILL.md',
111
- 'claude/skills/draft-doc/references/context.md',
112
- 'claude/skills/draft-doc/references/docs.md',
113
- 'claude/skills/draft-doc/references/readme.md',
114
- 'claude/skills/draft-doc/references/readme/agent-facing.md',
115
- 'claude/skills/draft-doc/references/readme/application.md',
116
- 'claude/skills/draft-doc/references/readme/cli.md',
117
- 'claude/skills/draft-doc/references/readme/library.md',
118
- 'claude/skills/draft-doc/references/readme/plugin.md',
119
- 'claude/skills/draft-doc/references/requirements.md',
120
- 'claude/skills/draft-doc/references/wiki-kind.md',
121
- 'claude/skills/draft-doc/references/wiki.md',
122
- 'claude/skills/draft-figure/SKILL.md',
123
- 'claude/skills/draft-figure/references/architecture-set.md',
124
- 'claude/skills/draft-figure/references/figures.md',
125
- 'claude/skills/draft-figure/references/hand-drawn.md',
126
- 'claude/skills/draft-figure/references/mermaid-path.md',
127
- 'claude/skills/draft-figure/references/mermaid.md',
128
- 'claude/skills/draft-identity/SKILL.md',
129
- 'claude/skills/draft-identity/references/card-route.md',
130
- 'claude/skills/draft-ready/SKILL.md',
131
- 'claude/skills/draft-ready/references/assembly.md',
132
- 'claude/skills/draft-ready/references/ready.md',
133
- 'claude/skills/draft-screencast/SKILL.md',
134
- 'claude/skills/draft-screencast/references/beats-template.md',
135
- 'claude/skills/draft-slides/SKILL.md',
136
- 'claude/skills/draft-slides/references/deck.md',
117
+ 'claude/skills/pick-by-eye/SKILL.md',
118
+ 'claude/skills/pick-by-eye/references/live-arms.md',
119
+ 'claude/skills/write-doc/SKILL.md',
120
+ 'claude/skills/write-doc/references/context.md',
121
+ 'claude/skills/write-doc/references/docs.md',
122
+ 'claude/skills/write-doc/references/readme.md',
123
+ 'claude/skills/write-doc/references/readme/agent-facing.md',
124
+ 'claude/skills/write-doc/references/readme/application.md',
125
+ 'claude/skills/write-doc/references/readme/cli.md',
126
+ 'claude/skills/write-doc/references/readme/library.md',
127
+ 'claude/skills/write-doc/references/readme/plugin.md',
128
+ 'claude/skills/write-doc/references/requirements.md',
129
+ 'claude/skills/write-doc/references/wiki-kind.md',
130
+ 'claude/skills/write-doc/references/wiki.md',
131
+ 'claude/skills/draw-figure/SKILL.md',
132
+ 'claude/skills/draw-figure/references/architecture-set.md',
133
+ 'claude/skills/draw-figure/references/figures.md',
134
+ 'claude/skills/draw-figure/references/hand-drawn.md',
135
+ 'claude/skills/draw-figure/references/mermaid-path.md',
136
+ 'claude/skills/draw-figure/references/mermaid.md',
137
+ 'claude/skills/design-identity/SKILL.md',
138
+ 'claude/skills/design-identity/references/card-route.md',
139
+ 'claude/skills/plan-ready/SKILL.md',
140
+ 'claude/skills/plan-ready/references/assembly.md',
141
+ 'claude/skills/plan-ready/references/ready.md',
142
+ 'claude/skills/script-screencast/SKILL.md',
143
+ 'claude/skills/script-screencast/references/beats-template.md',
144
+ 'claude/skills/make-deck/SKILL.md',
145
+ 'claude/skills/make-deck/references/deck.md',
137
146
  'claude/skills/git-branch/SKILL.md',
138
147
  'claude/skills/git-branch/references/branch.md',
139
148
  'claude/skills/git-branch/references/slug.md',
@@ -301,6 +310,7 @@ export function densityGates(
301
310
  gated: readonly string[] = GATED_PATHS,
302
311
  ): boolean {
303
312
  if (!inDensityScope(rel)) return false
313
+ if (REQUIREMENT_SCOPE.test(rel)) return gated.includes(rel)
304
314
  if (
305
315
  ALWAYS_GATED.includes(kind) &&
306
316
  BULLET_RUN_SCOPE.some((pattern) => pattern.test(rel))
@@ -346,7 +346,7 @@ function entriesOf(dir: string, isDirectory: boolean): string[] {
346
346
  }
347
347
 
348
348
  /**
349
- * What `draft-and-pick` Step 6 writes into the folder it archives a choice to:
349
+ * What `pick-by-eye` Step 6 writes into the folder it archives a choice to:
350
350
  * a capture per canvas frame, named `arm-<id>`. A design handoff marks a pick
351
351
  * folder an earlier skill wrote. A folder carrying neither is evidence.
352
352
  */