@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.
- package/README.md +3 -2
- package/claude/.claude-plugin/plugin.json +1 -1
- package/claude/skills/draft-screencast/REQUIREMENT.md +2 -1
- package/claude/skills/draft-screencast/SKILL.md +14 -9
- package/claude/skills/read-frames/SKILL.md +3 -2
- package/claude/skills/record-screencast/REQUIREMENT.md +3 -0
- package/claude/skills/record-screencast/SKILL.md +28 -5
- package/claude/skills/review-pr/SKILL.md +1 -1
- package/claude/skills/review-pr/references/close-out.md +1 -1
- package/claude/skills/review-pr/references/post.md +1 -1
- package/claude/skills/role-orchestrator/SKILL.md +1 -1
- package/claude/skills/role-orchestrator/references/orchestrator-poll.md +10 -10
- package/claude/skills/role-orchestrator/scripts/poll.ts +628 -0
- package/claude/skills/role-orchestrator/scripts/repo.ts +69 -0
- package/claude/skills/role-orchestrator/scripts/watch.ts +267 -0
- package/docs/agents/commands.md +3 -0
- package/docs/agents/demo.md +34 -7
- package/docs/agents/migrate.md +1 -1
- package/docs/agents/pr-reads.md +1 -1
- package/docs/agents/sandbox.md +28 -3
- package/docs/agents/state-scoped-risk.md +2 -2
- package/docs/target/migrations.md +1 -1
- package/docs/workflow/ai-workflow.md +2 -3
- package/docs/workflow/skill-map.md +1 -1
- package/governance/renames.toml +7 -5
- package/governance/rules/canon/630-requirements.md +1 -1
- package/package.json +8 -6
- package/scripts/core/check-color-source.sh +11 -1
- package/scripts/core/install-check.sh +13 -4
- package/scripts/lib/sandbox-dispatch.sh +1 -1
- package/scripts/lib/sandbox-fixtures.sh +2 -2
- package/scripts/lib/sandbox-path.sh +1 -1
- package/scripts/manage-sandbox.sh +2 -2
- package/src/canvas/client/app.tsx +85 -0
- package/src/canvas/client/main.tsx +62 -0
- package/src/canvas/client/pages-panel.tsx +123 -0
- package/src/canvas/client/shell.css +319 -0
- package/src/canvas/client/state.ts +186 -0
- package/src/canvas/client/surface.tsx +213 -0
- package/src/canvas/content.ts +326 -0
- package/src/canvas/index.html +14 -0
- package/src/canvas/server.ts +302 -0
- package/src/canvas/tokens.ts +71 -0
- package/src/claude/seeds.ts +0 -1
- package/src/cli.ts +2 -0
- package/src/commands/canvas.ts +325 -0
- package/src/commands/claude.ts +9 -14
- package/src/commands/demo.ts +6 -6
- package/src/commands/sandbox.ts +110 -2
- package/src/context/citations.ts +1 -1
- package/src/context/classify/prompts.ts +1 -1
- package/src/context/folders.ts +0 -1
- package/src/demo/compile.ts +8 -4
- package/src/demo/container.ts +1 -1
- package/src/gov/renames.ts +3 -2
- package/src/help.ts +1 -0
- package/src/record-root.ts +1 -0
- package/src/sandbox/census.ts +1 -1
- package/src/sandbox/coverage.ts +1 -1
- package/src/sandbox/equivalence.ts +847 -0
- package/src/sandbox/expect.ts +0 -1
- package/src/serve/static.ts +25 -11
- package/src/shipped/references.ts +8 -10
- package/src/surface-root.ts +0 -1
- package/src/targets/pulls.ts +2 -2
- package/standards/architecture.md +1 -1
- package/standards/context.md +1 -1
- package/standards/design.md +1 -1
- package/standards/index.md +0 -1
- package/standards/requirements.md +4 -6
- package/standards/wireframes.md +2 -2
- package/tooling/claude/reference.md +0 -1
- package/tooling/claude/seeds/canon/REQUIREMENTS.md +0 -2
- package/tsconfig.json +1 -1
- package/claude/skills/role-orchestrator/scripts/poll.sh +0 -504
- package/claude/skills/role-orchestrator/scripts/watch.sh +0 -181
- package/governance/rules/canon/645-decisions.md +0 -12
- package/standards/decisions.md +0 -105
- 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`.
|
package/standards/decisions.md
DELETED
|
@@ -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
|
-
```
|