@erclx/canon 5.14.0 → 5.15.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 (79) hide show
  1. package/README.md +3 -2
  2. package/claude/.claude-plugin/plugin.json +1 -1
  3. package/claude/skills/draft-screencast/REQUIREMENT.md +2 -1
  4. package/claude/skills/draft-screencast/SKILL.md +14 -9
  5. package/claude/skills/read-frames/SKILL.md +3 -2
  6. package/claude/skills/record-screencast/REQUIREMENT.md +3 -0
  7. package/claude/skills/record-screencast/SKILL.md +28 -5
  8. package/claude/skills/review-pr/SKILL.md +1 -1
  9. package/claude/skills/review-pr/references/close-out.md +1 -1
  10. package/claude/skills/review-pr/references/post.md +1 -1
  11. package/claude/skills/role-orchestrator/SKILL.md +1 -1
  12. package/claude/skills/role-orchestrator/references/orchestrator-poll.md +10 -10
  13. package/claude/skills/role-orchestrator/scripts/poll.ts +628 -0
  14. package/claude/skills/role-orchestrator/scripts/repo.ts +69 -0
  15. package/claude/skills/role-orchestrator/scripts/watch.ts +267 -0
  16. package/docs/agents/commands.md +3 -0
  17. package/docs/agents/demo.md +34 -7
  18. package/docs/agents/migrate.md +1 -1
  19. package/docs/agents/pr-reads.md +1 -1
  20. package/docs/agents/sandbox.md +28 -3
  21. package/docs/agents/state-scoped-risk.md +2 -2
  22. package/docs/target/migrations.md +1 -1
  23. package/docs/workflow/ai-workflow.md +2 -3
  24. package/docs/workflow/skill-map.md +1 -1
  25. package/governance/renames.toml +7 -5
  26. package/governance/rules/canon/630-requirements.md +1 -1
  27. package/package.json +8 -6
  28. package/scripts/core/check-color-source.sh +11 -1
  29. package/scripts/core/install-check.sh +13 -4
  30. package/scripts/lib/sandbox-dispatch.sh +1 -1
  31. package/scripts/lib/sandbox-fixtures.sh +2 -2
  32. package/scripts/lib/sandbox-path.sh +1 -1
  33. package/scripts/manage-sandbox.sh +2 -2
  34. package/src/canvas/client/app.tsx +85 -0
  35. package/src/canvas/client/main.tsx +62 -0
  36. package/src/canvas/client/pages-panel.tsx +123 -0
  37. package/src/canvas/client/shell.css +319 -0
  38. package/src/canvas/client/state.ts +186 -0
  39. package/src/canvas/client/surface.tsx +213 -0
  40. package/src/canvas/content.ts +326 -0
  41. package/src/canvas/index.html +14 -0
  42. package/src/canvas/server.ts +302 -0
  43. package/src/canvas/tokens.ts +71 -0
  44. package/src/claude/seeds.ts +0 -1
  45. package/src/cli.ts +2 -0
  46. package/src/commands/canvas.ts +325 -0
  47. package/src/commands/claude.ts +9 -14
  48. package/src/commands/demo.ts +6 -6
  49. package/src/commands/sandbox.ts +110 -2
  50. package/src/context/citations.ts +1 -1
  51. package/src/context/classify/prompts.ts +1 -1
  52. package/src/context/folders.ts +0 -1
  53. package/src/demo/compile.ts +8 -4
  54. package/src/demo/container.ts +1 -1
  55. package/src/gov/renames.ts +3 -2
  56. package/src/help.ts +1 -0
  57. package/src/record-root.ts +1 -0
  58. package/src/sandbox/census.ts +1 -1
  59. package/src/sandbox/coverage.ts +1 -1
  60. package/src/sandbox/equivalence.ts +847 -0
  61. package/src/sandbox/expect.ts +0 -1
  62. package/src/serve/static.ts +25 -11
  63. package/src/shipped/references.ts +8 -10
  64. package/src/surface-root.ts +0 -1
  65. package/src/targets/pulls.ts +2 -2
  66. package/standards/architecture.md +1 -1
  67. package/standards/context.md +1 -1
  68. package/standards/design.md +1 -1
  69. package/standards/index.md +0 -1
  70. package/standards/requirements.md +4 -6
  71. package/standards/wireframes.md +2 -2
  72. package/tooling/claude/reference.md +0 -1
  73. package/tooling/claude/seeds/canon/REQUIREMENTS.md +0 -2
  74. package/tsconfig.json +1 -1
  75. package/claude/skills/role-orchestrator/scripts/poll.sh +0 -504
  76. package/claude/skills/role-orchestrator/scripts/watch.sh +0 -181
  77. package/governance/rules/canon/645-decisions.md +0 -12
  78. package/standards/decisions.md +0 -105
  79. package/tooling/claude/seeds/canon/decisions/index.md +0 -8
@@ -1,181 +0,0 @@
1
- #!/usr/bin/env bash
2
- # Reports worker and pull request transitions as they happen. Reads only.
3
- #
4
- # The poll answers "what moved since the last run" on a schedule the session
5
- # holds. This answers "what just changed" on a loop of its own, and it exists
6
- # because the two readings a dispatch needs are not one reading: a worker that
7
- # finishes goes idle and a worker that crashes vanishes, so a watch matching
8
- # only the pull request list stays silent through the second. A third case is
9
- # neither: a worker that stops on a question or a prompt neither finishes nor
10
- # crashes, and sits reading "waiting" beside the two statuses that resolve on
11
- # their own until something reports it, which is the WORKER-STOPPED line
12
- # below, or WORKER-UNMEASURABLE for a "waiting" row too old to carry a stamp
13
- # this reads.
14
- #
15
- # `set -e` is deliberately not set. This runs for hours, and one transient `gh`
16
- # failure aborting the loop is a silent stop rather than a reported one. Every
17
- # read below states its own failure instead.
18
- set -uo pipefail
19
-
20
- # Seconds between passes. A pass costs two cheap reads and the transitions it
21
- # watches take minutes, so the number trades staleness against little. Raise it
22
- # in a project whose workers run long.
23
- INTERVAL=60
24
-
25
- # Seconds a worker can sit in "waiting" before this reports it as stopped
26
- # rather than folding it into the ordinary status-change lines below. "busy"
27
- # and "idle" resolve on their own; "waiting" does not, so a dwell that keeps
28
- # growing there is a session blocked on something outside itself.
29
- #
30
- # Measured against the live session registry: 341 usable records carried a
31
- # status at all, and exactly one carried "waiting", too sparse a sample to fit
32
- # a distribution. The number is picked from the two bounds the measurement can
33
- # still name rather than from a round guess: well above INTERVAL, so a handful
34
- # of passes confirm the row before it reports rather than one slow tool call
35
- # tripping it, and well inside the ten-to-thirty-minute span a dispatched build
36
- # ordinarily runs, so a real stall is caught with most of that window still
37
- # open to act on it.
38
- #
39
- # That one record carried `waitingFor: "approve Bash"`, a permission prompt
40
- # rather than a question, and the two clear on different schedules: a prompt
41
- # resolves the moment a person approves it, where a question can sit
42
- # legitimately while a controller finishes a turn. This number is defensible
43
- # for the second and generous for the first, and the gap stays open rather
44
- # than splitting the constant on a sample of one.
45
- STALL_THRESHOLD_S=300
46
-
47
- # Resolving the main worktree root rather than this file's folder keeps a watch
48
- # started from a linked worktree reading the same repository as one started from
49
- # main. The repository field on a roster row is that root's `.git`.
50
- MAIN_ROOT="$(git worktree list --porcelain 2>/dev/null | awk '/^worktree /{print $2; exit}')"
51
- if [ -z "$MAIN_ROOT" ]; then
52
- echo "watch: not a git repository, so nothing is watched" >&2
53
- exit 1
54
- fi
55
- REPOSITORY="$MAIN_ROOT/.git"
56
-
57
- # The base branch is read rather than assumed, since this ships to projects that
58
- # do not all call it `main`. It is what separates a worker from the controlling
59
- # session, which sits on the base branch in the main worktree.
60
- BASE_REF="$(git symbolic-ref --quiet --short refs/remotes/origin/HEAD 2>/dev/null || true)"
61
- if [ -z "$BASE_REF" ]; then
62
- BASE_REF="origin/$(gh repo view --json defaultBranchRef --jq .defaultBranchRef.name 2>/dev/null || echo main)"
63
- fi
64
- BASE_BRANCH="${BASE_REF#origin/}"
65
-
66
- # Every session in this repository holding a branch other than the base one is a
67
- # worker, whoever launched it. The prototype matched the `orchestrator-` prefix
68
- # instead, which reads a dispatched worker and misses every hand-launched one,
69
- # and those are the ordinary shape whenever the operator is launching.
70
- #
71
- # The row goes out tab-separated and comes back read with IFS set to a tab. A
72
- # name this client actually writes carries spaces, `Update session markdown
73
- # and check runnable commands (3)` among them, and splitting on whitespace
74
- # took that one apart into a wrong name, branch, status, and dwell.
75
- read_workers() {
76
- canon sessions list --json 2>/dev/null | tail -1 | jq -r \
77
- --arg repository "$REPOSITORY" --arg base "$BASE_BRANCH" '
78
- .sessions[]?
79
- | select(.repository == $repository)
80
- | select(.branch != null and .branch != $base)
81
- | [.name, .branch, .status, (.statusDwellMs // -1 | tostring)]
82
- | @tsv
83
- ' 2>/dev/null | sort
84
- }
85
-
86
- read_pulls() {
87
- gh pr list --state open --json number,headRefName,title --jq \
88
- '.[] | "PR-OPEN #\(.number) \(.headRefName) \(.title)"' 2>/dev/null | sort
89
- }
90
-
91
- # A failed read is reported and the previous baseline kept. Reading an empty
92
- # result as the current state would report every worker gone and every pull
93
- # request new on the pass after, which is the one failure that makes a detection
94
- # tool worth less than no tool.
95
- #
96
- # The two sources carry a baseline flag apiece rather than sharing one. A read
97
- # that fails on the first pass leaves its baseline empty while the other source
98
- # establishes one, and a single flag would then report that source's whole list
99
- # as new on the pass after it recovers.
100
- prev_pulls=""
101
- prev_workers=""
102
- prev_names=""
103
- pulls_seen=0
104
- workers_seen=0
105
-
106
- # One row per worker name currently reported, waiting past STALL_THRESHOLD_S
107
- # or waiting with no stamp to measure, so a reader watching the log is told
108
- # once rather than on every remaining pass. The prototype for that shape is
109
- # the WORKER-GONE/WORKER pair above, tracked in the same state a status change
110
- # is: a name drops out the pass it stops reading "waiting", which lets the
111
- # same worker report a second stall later in its life rather than being
112
- # marked forever by its first one.
113
- declare -A stalled
114
-
115
- while true; do
116
- pulls="$(read_pulls)"
117
- pulls_read=$?
118
- workers="$(read_workers)"
119
- workers_read=$?
120
-
121
- if [ "$pulls_read" -ne 0 ]; then
122
- echo "watch: the open pull request list failed to load, so none is classified this pass"
123
- fi
124
- if [ "$workers_read" -ne 0 ]; then
125
- echo "watch: the session roster failed to load, so no worker is classified this pass"
126
- fi
127
-
128
- names="$(printf '%s\n' "$workers" | awk -F'\t' 'NF {print $1}' | sort -u)"
129
-
130
- if [ "$pulls_seen" -eq 1 ] && [ "$pulls_read" -eq 0 ]; then
131
- comm -13 <(printf '%s\n' "$prev_pulls") <(printf '%s\n' "$pulls") | grep . || true
132
- fi
133
-
134
- if [ "$workers_seen" -eq 1 ] && [ "$workers_read" -eq 0 ]; then
135
- comm -13 <(printf '%s\n' "$prev_workers") <(printf '%s\n' "$workers") |
136
- grep . | sed 's/^/WORKER /' || true
137
- comm -23 <(printf '%s\n' "$prev_names") <(printf '%s\n' "$names") |
138
- grep . | sed 's/^/WORKER-GONE /' || true
139
- fi
140
-
141
- if [ "$workers_read" -eq 0 ]; then
142
- while IFS=$'\t' read -r w_name w_branch w_status w_dwell_ms; do
143
- [ -z "$w_name" ] && continue
144
-
145
- if [ "$w_status" != "waiting" ]; then
146
- unset "stalled[$w_name]"
147
- continue
148
- fi
149
-
150
- if [ "$w_dwell_ms" = "-1" ]; then
151
- if [ -z "${stalled[$w_name]:-}" ]; then
152
- echo "WORKER-UNMEASURABLE $w_name $w_branch"
153
- stalled[$w_name]=1
154
- fi
155
- continue
156
- fi
157
-
158
- w_dwell_s=$((w_dwell_ms / 1000))
159
- if [ "$w_dwell_s" -ge "$STALL_THRESHOLD_S" ]; then
160
- if [ -z "${stalled[$w_name]:-}" ]; then
161
- echo "WORKER-STOPPED $w_name $w_branch ${w_dwell_s}s"
162
- stalled[$w_name]=1
163
- fi
164
- else
165
- unset "stalled[$w_name]"
166
- fi
167
- done <<<"$workers"
168
- fi
169
-
170
- if [ "$pulls_read" -eq 0 ]; then
171
- prev_pulls="$pulls"
172
- pulls_seen=1
173
- fi
174
- if [ "$workers_read" -eq 0 ]; then
175
- prev_workers="$workers"
176
- prev_names="$names"
177
- workers_seen=1
178
- fi
179
-
180
- sleep "$INTERVAL"
181
- done
@@ -1,12 +0,0 @@
1
- ---
2
- description: Route canon/decisions/ edits to the decisions standard for record shape and lifecycle
3
- paths:
4
- - 'canon/decisions/**'
5
- - '.claude/decisions/**'
6
- ---
7
-
8
- # Decisions standards
9
-
10
- ## Authority
11
-
12
- - Follow the decisions standard for folder layout, the ordinal filename, frontmatter, record sections, and the append-only lifecycle. It is the single source. Read it with `canon standards decisions`.
@@ -1,105 +0,0 @@
1
- ---
2
- title: Decisions reference
3
- description: Folder layout, ordinal filename, frontmatter, record sections, and the append-only lifecycle for canon/decisions/
4
- ---
5
-
6
- # Decisions reference
7
-
8
- Applies to `canon/decisions/`. Holds the history a canonical doc used to carry itself: pick rounds, superseded figures, and a decision's rejected alternatives, in a tracked folder nothing loads eagerly. A canonical doc points at a record from its own history section rather than restating it.
9
-
10
- ## Scope
11
-
12
- Governs `canon/decisions/`: folder layout, the ordinal filename, frontmatter, record sections, and the append-only lifecycle.
13
-
14
- Does not govern:
15
-
16
- - Which canonical doc points here and when, and what stays behind in that doc's own body: `architecture.md`, `context.md`, `wireframes.md`, `design.md`, `requirements.md`, once each states its own retirement rule
17
- - Voice, rhythm, and sentence construction: the `write-human` skill
18
- - Headings, punctuation, word choice, and file references: `markdown.md`
19
-
20
- ## What a working record looks like
21
-
22
- A record works when a reader who has never opened the project can follow it from the file alone:
23
-
24
- - What was decided, stated once, without needing the canonical doc that points here
25
- - What else was considered, and why each alternative lost
26
- - Which claim rests on a measurement, and what commit that measurement was read against
27
-
28
- A record failing these is non-conforming even when it satisfies every shape rule below.
29
-
30
- ## Folder name
31
-
32
- - `canon/decisions/`, resolved the way every tracked surface is: at `canon/decisions/` in a project that has moved, at `.claude/decisions/` in one that has not.
33
- - Never add `canon/decisions/index.md` to a `CLAUDE.md` `@` import. A log that loads eagerly rebuilds the bloat it exists to absorb. A canonical doc's own pointer is how a reader reaches a record, one file at a time.
34
-
35
- ## Record filename
36
-
37
- - Name each record `<nn>-<slug>.md`, a two-digit zero-padded ordinal followed by a kebab-case slug, the same shape a groundwork track's folder takes.
38
- - The ordinal is the order the record was written in, which is what lets a listing sort by when a decision landed rather than alphabetically by subject.
39
- - Never renumber an existing record. A later reader cites it by that name, and a record whose number moved is a record a stale citation can no longer find.
40
-
41
- ## Frontmatter
42
-
43
- - `title` (required): the decision in sentence case
44
- - `description` (required): one line naming what was decided
45
-
46
- ## Sections
47
-
48
- Use `## Context`, `## Decision`, `## Alternatives`, `## Revisit when`, and `## Measurements`.
49
-
50
- - `## Context`: the problem as it stood, stated so a reader needs nothing else open. Restate a fact rather than pointing at where it was found.
51
- - `## Decision`: what was chosen, and why, in enough detail that a reader can tell it apart from an alternative that sounds similar.
52
- - `## Alternatives`: each one considered and dropped, with the reason it lost. An alternative with no stated reason reads as a claim nobody checked.
53
- - `## Revisit when`: one bullet per finding that would reopen the decision. Required when the record came from a groundwork track whose deciding leans carried "Overturned by" lines, which carry over as written. It is optional otherwise, for a decision resting on how an outside tool, an installer, or the model behaves. Skip it for a decision resting on the project's own code.
54
- - `## Measurements`: skip when the decision cites no number. When it does, state the number and close with the commit it was read against, per Verification anchors below.
55
-
56
- ## Verification anchors
57
-
58
- A record's reasoning stays correct while the numbers it cites move. The anchor records what a measured claim was read against, so a reader can tell a number that was checked and held from one nobody has looked at since.
59
-
60
- - Close a `## Measurements` section with a trailing sentence naming the short commit SHA and the ISO date that number was read: `Measured at <short-sha> on <YYYY-MM-DD>.`
61
- - Anchor on the number alone. A record citing none carries no `## Measurements` section at all.
62
- - Read an absent section as unchecked rather than as current. A record with no measurements has nothing due a re-read.
63
-
64
- ## Lifecycle
65
-
66
- - Append-only. A written record is never edited to reflect a later reversal.
67
- - Write a new record when a later decision supersedes an earlier one, naming the record it supersedes. The old record stays as it was written, since it is history rather than a live statement of the current shape.
68
- - Never auto-loaded. A canonical doc's own history section links to a record by relative path, and a reader reaches it by following that link, not by the folder loading with the session.
69
-
70
- ## Citation
71
-
72
- - Never cite `.canon/`. A record restates what it needs, since a clone without the gitignored records folder resolves nothing there.
73
- - Cite a same-repository pull request or commit the way `publish.md` fixes for any tracked document.
74
-
75
- ## Template
76
-
77
- ```markdown
78
- ---
79
- title: <Decision, in sentence case>
80
- description: <one line naming what was decided>
81
- ---
82
-
83
- # <Decision title>
84
-
85
- ## Context
86
-
87
- <The problem as it stood, self-contained.>
88
-
89
- ## Decision
90
-
91
- <What was chosen, and why.>
92
-
93
- ## Alternatives
94
-
95
- - **<Alternative>.** <Why it lost.>
96
- - **<Alternative>.** <Why it lost.>
97
-
98
- ## Revisit when
99
-
100
- - <The finding that would reopen the decision.>
101
-
102
- ## Measurements
103
-
104
- <The number the decision rests on.> Measured at <short-sha> on <YYYY-MM-DD>.
105
- ```
@@ -1,8 +0,0 @@
1
- ---
2
- title: Decisions
3
- subtitle: Decision history a project doc points at, never loaded eagerly
4
- ---
5
-
6
- # Decisions
7
-
8
- Decision history a project doc points at, never loaded eagerly