@erclx/canon 5.39.0 → 5.40.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 +5 -5
- package/claude/.claude-plugin/plugin.json +1 -1
- package/claude/skills/canvas-board/SKILL.md +1 -1
- package/claude/skills/draft-slides/SKILL.md +2 -3
- package/claude/skills/draft-slides/references/deck.md +183 -0
- package/claude/skills/git-pr/references/evidence.md +2 -2
- package/claude/skills/git-pr/references/final-command.md +1 -1
- package/claude/skills/git-pr/references/labels.md +1 -1
- package/claude/skills/git-pr/references/publish.md +1 -1
- package/claude/skills/review-ui/SKILL.md +1 -0
- package/claude/skills/role-orchestrator/references/orchestrator-handback.md +1 -1
- package/claude/skills/task-board/references/board.md +1 -1
- package/claude/skills/task-board/references/tasks.md +1 -1
- package/claude/skills/ux-measure/SKILL.md +1 -1
- package/docs/agents/audits.md +61 -60
- package/docs/agents/context-audit.md +40 -58
- package/docs/agents/index.md +5 -51
- package/docs/agents/install-and-sync.md +1 -0
- package/docs/agents/markdown-audit.md +53 -99
- package/docs/agents/records.md +37 -79
- package/docs/agents/sessions-transfer.md +26 -70
- package/docs/target/index.md +0 -1
- package/docs/target/migrations.md +57 -66
- package/docs/target/projects.md +36 -78
- package/docs/target/scenarios.md +9 -19
- package/docs/target/sync.md +27 -68
- package/docs/workflow/ai-workflow.md +22 -68
- package/docs/workflow/operating-model.md +32 -186
- package/docs/workflow/parallel-features.md +29 -66
- package/docs/workflow/skill-map-on-request.md +22 -24
- package/docs/workflow/skill-map.md +72 -76
- package/docs/workflow/visual-design-workflow.md +25 -118
- package/docs/workflow/zshrc-aliases.md +18 -37
- package/package.json +1 -1
- package/src/canvas/client/inspector/fields.css +4 -0
- package/src/canvas/index.html +6 -0
- package/src/claude/skills-reach.ts +2 -2
- package/src/cli/ui.ts +3 -1
- package/src/commands/canvas.ts +23 -0
- package/src/commands/capture.ts +14 -0
- package/src/commands/claude.ts +13 -0
- package/src/commands/demo.ts +22 -0
- package/src/commands/deps.ts +6 -0
- package/src/commands/driver.ts +28 -2
- package/src/commands/gov.ts +52 -18
- package/src/commands/intake.ts +17 -0
- package/src/commands/labels.ts +38 -5
- package/src/commands/pr.ts +160 -43
- package/src/commands/records.ts +8 -0
- package/src/commands/secrets.ts +6 -0
- package/src/commands/sessions.ts +8 -0
- package/src/commands/tasks.ts +52 -0
- package/src/commands/teach.ts +13 -0
- package/src/commands/upstream.ts +25 -1
- package/src/commands/worktrees.ts +5 -0
- package/src/context/audit.ts +22 -4
- package/src/context/folders.ts +1 -1
- package/src/context/shape-gated.ts +4 -2
- package/src/counts/scan.ts +1 -1
- package/src/design/components.ts +1 -1
- package/src/design/document.ts +1 -1
- package/src/design/tokens.ts +1 -1
- package/src/gate/boundaries.ts +4 -1
- package/src/gate/measures.ts +1 -1
- package/src/gate/stages.ts +7 -7
- package/src/git/env.ts +6 -0
- package/src/markdown/scope.ts +5 -17
- package/src/pr/lift.ts +153 -19
- package/src/pr/painted.ts +87 -0
- package/src/roots/record.ts +18 -1
- package/src/sandbox/census.ts +1 -1
- package/src/sandbox/headless/run.ts +3 -2
- package/src/sandbox/headless/watch.ts +3 -4
- package/src/slides/convert/master.ts +3 -0
- package/src/sync/format.ts +46 -0
- package/src/sync/stamp.ts +6 -0
- package/tooling/astro/reference.md +2 -2
- package/tooling/base/reference.md +1 -1
- package/tooling/go/reference.md +1 -1
- package/tooling/nextjs/reference.md +1 -1
- package/tooling/php/reference.md +1 -1
- package/tooling/python/reference.md +1 -1
- package/tooling/vite-react/reference.md +1 -1
- package/tooling/web/reference.md +1 -1
- package/docs/agents/canvas.md +0 -86
- package/docs/agents/capture.md +0 -65
- package/docs/agents/census.md +0 -23
- package/docs/agents/comments.md +0 -34
- package/docs/agents/context-audit-checks.md +0 -108
- package/docs/agents/context-classify.md +0 -101
- package/docs/agents/counts.md +0 -74
- package/docs/agents/demo.md +0 -140
- package/docs/agents/driver.md +0 -103
- package/docs/agents/gate.md +0 -90
- package/docs/agents/hooks.md +0 -78
- package/docs/agents/intake.md +0 -81
- package/docs/agents/key-changes.md +0 -116
- package/docs/agents/label-coverage.md +0 -86
- package/docs/agents/markdown-audit-density.md +0 -39
- package/docs/agents/markdown-audit-weight.md +0 -83
- package/docs/agents/pr-evidence-layout.md +0 -70
- package/docs/agents/pr-evidence.md +0 -273
- package/docs/agents/pr-frames.md +0 -131
- package/docs/agents/pr-lift.md +0 -132
- package/docs/agents/pr-local.md +0 -111
- package/docs/agents/pr-preview.md +0 -155
- package/docs/agents/pr-reads.md +0 -111
- package/docs/agents/pr-tick.md +0 -72
- package/docs/agents/records-push.md +0 -61
- package/docs/agents/records-stale.md +0 -40
- package/docs/agents/records-validate.md +0 -57
- package/docs/agents/restated.md +0 -83
- package/docs/agents/routing.md +0 -61
- package/docs/agents/rule-citations.md +0 -90
- package/docs/agents/sandbox.md +0 -126
- package/docs/agents/sessions.md +0 -128
- package/docs/agents/skills-audit.md +0 -120
- package/docs/agents/skills-reach.md +0 -64
- package/docs/agents/slides.md +0 -176
- package/docs/agents/state-scoped-risk.md +0 -105
- package/docs/agents/superseded.md +0 -85
- package/docs/agents/tasks-archive.md +0 -83
- package/docs/agents/tasks-plans.md +0 -152
- package/docs/agents/tasks-validate.md +0 -133
- package/docs/agents/tasks.md +0 -123
- package/docs/agents/teach-authoring.md +0 -118
- package/docs/agents/teach.md +0 -157
- package/docs/agents/test-order.md +0 -58
- package/docs/agents/upstream.md +0 -94
- package/docs/agents/worktrees.md +0 -118
- package/docs/target/standards-moved.md +0 -34
package/src/commands/pr.ts
CHANGED
|
@@ -69,7 +69,9 @@ import {
|
|
|
69
69
|
type LiftRefusal,
|
|
70
70
|
type PullState,
|
|
71
71
|
readBaseStanding,
|
|
72
|
+
readChangedPaths,
|
|
72
73
|
} from '@/pr/lift'
|
|
74
|
+
import { loadPaintedPatterns } from '@/pr/painted'
|
|
73
75
|
import {
|
|
74
76
|
dropAddressLine,
|
|
75
77
|
findLocalServer,
|
|
@@ -421,6 +423,24 @@ export function register(program: Command): void {
|
|
|
421
423
|
'points the change at a locative the path does not name. Read the bullet on',
|
|
422
424
|
"the record's preview before filing an unmet path as a stale claim.",
|
|
423
425
|
'',
|
|
426
|
+
'Refusals, read off `reason`:',
|
|
427
|
+
' no-section the body carries no `## Key changes` heading, so it',
|
|
428
|
+
' claims nothing',
|
|
429
|
+
' no-claims the heading is there and no span resolved, which is',
|
|
430
|
+
' the extractor failing over prose and raises nothing',
|
|
431
|
+
' no-changes the pull request changed no files, so no claim has',
|
|
432
|
+
' anything to answer',
|
|
433
|
+
' unreadable-body the pull request body could not be read',
|
|
434
|
+
' unreadable-tree the file tree of the head could not be read',
|
|
435
|
+
' no-base GitHub reported no merge base for the pull request',
|
|
436
|
+
' bad-base `--base` names a ref sharing no history with HEAD',
|
|
437
|
+
' unreadable-changes the changed files could not be listed',
|
|
438
|
+
' ambiguous-pull two open pull requests share the branch against',
|
|
439
|
+
' different bases, so name a number',
|
|
440
|
+
' gh-missing `gh` is not on the path, so no pull request could be',
|
|
441
|
+
' resolved',
|
|
442
|
+
' gh-failed `gh` could not answer for this repository or branch',
|
|
443
|
+
'',
|
|
424
444
|
'Exit codes:',
|
|
425
445
|
' 0 every claimed path is in the diff',
|
|
426
446
|
' 1 refused, with the reason on stderr or in the JSON record',
|
|
@@ -455,6 +475,17 @@ export function register(program: Command): void {
|
|
|
455
475
|
'tip from the remote with `git ls-remote` and reports which commit each',
|
|
456
476
|
'source names.',
|
|
457
477
|
'',
|
|
478
|
+
'Refusals, read off `reason`:',
|
|
479
|
+
' ambiguous-pull two open pull requests share the branch against',
|
|
480
|
+
' different bases, so name a number',
|
|
481
|
+
' no-branch the pull request carries no head branch name',
|
|
482
|
+
' unresolvable-ref the remote read failed, which is not an absent branch',
|
|
483
|
+
' no-remote-branch the remote carries no branch by that name, so there',
|
|
484
|
+
' is no tip to compare against',
|
|
485
|
+
' gh-missing `gh` is not on the path, so no pull request resolved',
|
|
486
|
+
' gh-failed `gh` could not answer for this branch, so name a number',
|
|
487
|
+
' no-object-head the pull request object reported no head commit',
|
|
488
|
+
'',
|
|
458
489
|
'Read the verdict off the record rather than off the exit. A stale head',
|
|
459
490
|
'exits 0, because an operator shell profile can wrap this binary in a',
|
|
460
491
|
'function whose status comes from a trailing command.',
|
|
@@ -492,6 +523,19 @@ export function register(program: Command): void {
|
|
|
492
523
|
'an empty row list, and reading that as passing is the false green the',
|
|
493
524
|
'sha key alone does not close.',
|
|
494
525
|
'',
|
|
526
|
+
'Refusals, read off `reason`:',
|
|
527
|
+
' ambiguous-pull two open pull requests share the branch against',
|
|
528
|
+
' different bases, so name a number',
|
|
529
|
+
' no-branch the pull request carries no head branch name',
|
|
530
|
+
' unresolvable-ref the remote read failed, which is not an absent branch',
|
|
531
|
+
' no-remote-branch the remote carries no branch by that name, so there',
|
|
532
|
+
' is no tip to compare against',
|
|
533
|
+
' runs-unreadable the check runs for the tip could not be read, which is',
|
|
534
|
+
' unread rather than none',
|
|
535
|
+
' gh-missing `gh` is not on the path, so no pull request resolved',
|
|
536
|
+
' gh-failed `gh` could not answer for this branch, so name a number',
|
|
537
|
+
' no-object-head the pull request object reported no head commit',
|
|
538
|
+
'',
|
|
495
539
|
'A branch conflicting with its base gets no merge ref, so no run ever',
|
|
496
540
|
'starts for it and the state stays pending. The record carries',
|
|
497
541
|
'`conflicted: true` beside it when `mergeStateStatus` reads DIRTY and no',
|
|
@@ -538,6 +582,19 @@ export function register(program: Command): void {
|
|
|
538
582
|
' fallback a pass posted before the marker shipped, off GitHub stamps',
|
|
539
583
|
' none the thread carries no pass, so the next one is a first pass',
|
|
540
584
|
'',
|
|
585
|
+
'Refusals, read off `reason`:',
|
|
586
|
+
' ambiguous-pull two open pull requests share the branch against',
|
|
587
|
+
' different bases, so name a number',
|
|
588
|
+
' no-branch the pull request carries no head branch name',
|
|
589
|
+
' unresolvable-ref the remote read failed, which is not an absent branch',
|
|
590
|
+
' no-remote-branch the remote carries no branch by that name, so there',
|
|
591
|
+
' is no tip to compare against',
|
|
592
|
+
' reviews-unreadable the reviews could not be parsed, which is unread',
|
|
593
|
+
' rather than none',
|
|
594
|
+
' gh-missing `gh` is not on the path, so no pull request resolved',
|
|
595
|
+
' gh-failed `gh` could not answer for this branch, so name a number',
|
|
596
|
+
' no-object-head the pull request object reported no head commit',
|
|
597
|
+
'',
|
|
541
598
|
'Exit codes:',
|
|
542
599
|
' 0 the thread was read, whether it carries a pass or not',
|
|
543
600
|
' 1 refused, with the reason on stderr or in the JSON record',
|
|
@@ -656,6 +713,17 @@ export function register(program: Command): void {
|
|
|
656
713
|
'The record carries `owed` and the same marked fields. --check refuses',
|
|
657
714
|
'with --preview, --local, or --checklist as check-writes.',
|
|
658
715
|
'',
|
|
716
|
+
'Refusals, read off `reason`:',
|
|
717
|
+
' unreadable-checklist --checklist named a path that could not be read,',
|
|
718
|
+
' or one holding nothing',
|
|
719
|
+
' no-branch the pull request carries no head branch name',
|
|
720
|
+
' no-base GitHub reported no merge base for the pull request',
|
|
721
|
+
' unreadable-changes GitHub could not list what the pull request changed',
|
|
722
|
+
' gh-missing `gh` is not on the path',
|
|
723
|
+
' gh-failed `gh` could not read or edit the pull request or its',
|
|
724
|
+
' comments',
|
|
725
|
+
' no-object-head the pull request object reported no head commit',
|
|
726
|
+
'',
|
|
659
727
|
'Exit codes:',
|
|
660
728
|
' 0 read, whether it produced a body, reported no-evidence, or checked',
|
|
661
729
|
' 1 refused, with the reason on stderr or in the JSON record',
|
|
@@ -696,7 +764,11 @@ export function register(program: Command): void {
|
|
|
696
764
|
'they are read:',
|
|
697
765
|
' review the newest `## Review` is closed at the head, off its marker',
|
|
698
766
|
' ui the newest `## UI review` is closed at the head, bound only',
|
|
699
|
-
' where `canon pr evidence --check` carries a checklist',
|
|
767
|
+
' where `canon pr evidence --check` carries a checklist. A close',
|
|
768
|
+
' on an earlier commit still holds when it is an ancestor of the',
|
|
769
|
+
' head and no painted path changed since, and the record names',
|
|
770
|
+
' it as `carriedFrom`. A painted change refuses `ui-behind` and',
|
|
771
|
+
' lists the paths as `paintedPaths`',
|
|
700
772
|
' evidence the evidence check reads settled',
|
|
701
773
|
' standing the head is fresh against the base, not stale or conflicted',
|
|
702
774
|
' ci every check run on the tip passed, waited on up to --wait',
|
|
@@ -705,8 +777,21 @@ export function register(program: Command): void {
|
|
|
705
777
|
'wait, so a reviewer whose partner is still driving refuses at once. The',
|
|
706
778
|
'head and the reviews are read again before the mutation.',
|
|
707
779
|
'',
|
|
780
|
+
'Painted paths are what a browser renders (`.tsx`, `.jsx`, `.vue`,',
|
|
781
|
+
'`.svelte`, `.astro`, `.html`, `.css`, `.scss`, `.svg`, images, fonts)',
|
|
782
|
+
'minus test, spec, and story files. A project adds to or trims the list',
|
|
783
|
+
'in `canon/config/painted.json` as `{ "painted": [glob], "unpainted":',
|
|
784
|
+
'[glob] }`.',
|
|
785
|
+
'',
|
|
786
|
+
'A pull request that is already ready has the review, ui, and evidence',
|
|
787
|
+
'gates read too. One that reads not clear returns to draft with reason',
|
|
788
|
+
'`redrafted` and the failing reason as `failed`, exit 1. Standing and CI',
|
|
789
|
+
'are reported and never cause a re-draft. A ready pull request that',
|
|
790
|
+
'clears reports `already-ready`, exit 0. Under --check nothing is',
|
|
791
|
+
'written and the first failing reason is reported.',
|
|
792
|
+
'',
|
|
708
793
|
'Reasons:',
|
|
709
|
-
' lifted, already-ready, clear (--check)',
|
|
794
|
+
' lifted, already-ready, redrafted, clear (--check)',
|
|
710
795
|
' pull-closed, review-missing, review-open, review-unmarked,',
|
|
711
796
|
' review-behind, ui-missing, ui-open, ui-unmarked, ui-behind,',
|
|
712
797
|
' evidence-owed, standing-stale, standing-conflict, standing-unknown,',
|
|
@@ -784,6 +869,15 @@ export function register(program: Command): void {
|
|
|
784
869
|
' building a deploy of the tip is still running, so wait',
|
|
785
870
|
' no-build no deploy of this branch has succeeded',
|
|
786
871
|
'',
|
|
872
|
+
'Refusals before either read, read off `reason`:',
|
|
873
|
+
' bad-timeout --timeout was not a positive number',
|
|
874
|
+
' check-timeout --check was combined with --timeout',
|
|
875
|
+
' no-branch the pull request carries no head branch name',
|
|
876
|
+
' unreadable-changes the changed paths of the pull request could not be read',
|
|
877
|
+
' gh-missing `gh` is not on the path',
|
|
878
|
+
' gh-failed `gh` could not read the pull request, or list or',
|
|
879
|
+
' dispatch the workflow',
|
|
880
|
+
'',
|
|
787
881
|
'Exit codes:',
|
|
788
882
|
' 0 a preview was published, or --check read fresh',
|
|
789
883
|
' 1 refused, or --check read anything but fresh',
|
|
@@ -838,6 +932,10 @@ export function register(program: Command): void {
|
|
|
838
932
|
' no-comment --remove found no marked comment, a no-op',
|
|
839
933
|
' no-line --remove found no address line, a no-op',
|
|
840
934
|
'',
|
|
935
|
+
'Refusals, read off `reason`:',
|
|
936
|
+
' gh-missing `--remove` needs `gh` and it is not on the path',
|
|
937
|
+
' gh-failed `--remove` could not read or edit the comment',
|
|
938
|
+
'',
|
|
841
939
|
'Exit codes:',
|
|
842
940
|
' 0 a server was found, or --remove finished, including a no-op',
|
|
843
941
|
' 1 refused, with the reason on stderr or in the JSON record',
|
|
@@ -3169,6 +3267,43 @@ const LIFT_REFUSALS: Record<LiftRefusal | 'bad-wait' | 'gh-missing', string> = {
|
|
|
3169
3267
|
const READY_MUTATION =
|
|
3170
3268
|
'mutation($id: ID!) { markPullRequestReadyForReview(input: {pullRequestId: $id}) { pullRequest { isDraft } } }'
|
|
3171
3269
|
|
|
3270
|
+
const CONVERT_DRAFT_MUTATION =
|
|
3271
|
+
'mutation($id: ID!) { convertPullRequestToDraft(input: {pullRequestId: $id}) { pullRequest { isDraft } } }'
|
|
3272
|
+
|
|
3273
|
+
/**
|
|
3274
|
+
* Runs one pull request mutation and answers whether the draft flag now reads
|
|
3275
|
+
* `isDraft`. The flag is read back rather than the exit trusted, since the
|
|
3276
|
+
* mutation can answer 200 with an errors array and no change.
|
|
3277
|
+
*/
|
|
3278
|
+
async function flipDraft(
|
|
3279
|
+
root: string,
|
|
3280
|
+
mutation: string,
|
|
3281
|
+
field: 'markPullRequestReadyForReview' | 'convertPullRequestToDraft',
|
|
3282
|
+
nodeId: string,
|
|
3283
|
+
isDraft: boolean,
|
|
3284
|
+
): Promise<boolean> {
|
|
3285
|
+
const result = await execa(
|
|
3286
|
+
'gh',
|
|
3287
|
+
['api', 'graphql', '-f', `query=${mutation}`, '-f', `id=${nodeId}`],
|
|
3288
|
+
{
|
|
3289
|
+
cwd: root,
|
|
3290
|
+
reject: false,
|
|
3291
|
+
timeout: GH_TIMEOUT_MS,
|
|
3292
|
+
env: gitEnv(),
|
|
3293
|
+
extendEnv: false,
|
|
3294
|
+
},
|
|
3295
|
+
)
|
|
3296
|
+
if (result.exitCode !== 0) return false
|
|
3297
|
+
try {
|
|
3298
|
+
const body = JSON.parse(String(result.stdout)) as {
|
|
3299
|
+
data?: Record<string, { pullRequest?: { isDraft?: boolean } } | undefined>
|
|
3300
|
+
}
|
|
3301
|
+
return body.data?.[field]?.pullRequest?.isDraft === isDraft
|
|
3302
|
+
} catch {
|
|
3303
|
+
return false
|
|
3304
|
+
}
|
|
3305
|
+
}
|
|
3306
|
+
|
|
3172
3307
|
function gitRunner(root: string): GitRunner {
|
|
3173
3308
|
return async (args) => {
|
|
3174
3309
|
const result = await execa('git', ['-C', root, ...args], {
|
|
@@ -3292,44 +3427,23 @@ function liftApi(root: string, number: string, baseRef: string): LiftApi {
|
|
|
3292
3427
|
return undefined
|
|
3293
3428
|
}
|
|
3294
3429
|
},
|
|
3295
|
-
markReady:
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
},
|
|
3313
|
-
)
|
|
3314
|
-
if (result.exitCode !== 0) return false
|
|
3315
|
-
// The flag is read back rather than the exit trusted, since the
|
|
3316
|
-
// mutation can answer 200 with an errors array and no change.
|
|
3317
|
-
try {
|
|
3318
|
-
const body = JSON.parse(String(result.stdout)) as {
|
|
3319
|
-
data?: {
|
|
3320
|
-
markPullRequestReadyForReview?: {
|
|
3321
|
-
pullRequest?: { isDraft?: boolean }
|
|
3322
|
-
}
|
|
3323
|
-
}
|
|
3324
|
-
}
|
|
3325
|
-
return (
|
|
3326
|
-
body.data?.markPullRequestReadyForReview?.pullRequest?.isDraft ===
|
|
3327
|
-
false
|
|
3328
|
-
)
|
|
3329
|
-
} catch {
|
|
3330
|
-
return false
|
|
3331
|
-
}
|
|
3332
|
-
},
|
|
3430
|
+
markReady: (nodeId) =>
|
|
3431
|
+
flipDraft(
|
|
3432
|
+
root,
|
|
3433
|
+
READY_MUTATION,
|
|
3434
|
+
'markPullRequestReadyForReview',
|
|
3435
|
+
nodeId,
|
|
3436
|
+
false,
|
|
3437
|
+
),
|
|
3438
|
+
markDraft: (nodeId) =>
|
|
3439
|
+
flipDraft(
|
|
3440
|
+
root,
|
|
3441
|
+
CONVERT_DRAFT_MUTATION,
|
|
3442
|
+
'convertPullRequestToDraft',
|
|
3443
|
+
nodeId,
|
|
3444
|
+
true,
|
|
3445
|
+
),
|
|
3446
|
+
readChanged: (from, to) => readChangedPaths(git, from, to),
|
|
3333
3447
|
sleep: (ms) => new Promise((done) => setTimeout(done, ms)),
|
|
3334
3448
|
now: () => Date.now(),
|
|
3335
3449
|
}
|
|
@@ -3385,6 +3499,7 @@ async function runLift(
|
|
|
3385
3499
|
const record = await lift(liftApi(root, resolved.number, `origin/${base}`), {
|
|
3386
3500
|
isCheck: opts.check ?? false,
|
|
3387
3501
|
waitSeconds,
|
|
3502
|
+
painted: loadPaintedPatterns(root),
|
|
3388
3503
|
})
|
|
3389
3504
|
|
|
3390
3505
|
logStep(record.reason)
|
|
@@ -3393,9 +3508,11 @@ async function runLift(
|
|
|
3393
3508
|
? `#${resolved.number} is ready for review at ${record.head?.slice(0, 8)}`
|
|
3394
3509
|
: record.reason === 'already-ready'
|
|
3395
3510
|
? `#${resolved.number} is already ready for review`
|
|
3396
|
-
: record.reason === '
|
|
3397
|
-
?
|
|
3398
|
-
:
|
|
3511
|
+
: record.reason === 'redrafted'
|
|
3512
|
+
? `#${resolved.number} is back to draft, since ${record.failed === undefined ? 'a gate' : LIFT_REFUSALS[record.failed]}`
|
|
3513
|
+
: record.reason === 'clear'
|
|
3514
|
+
? `every gate reads clear for #${resolved.number}`
|
|
3515
|
+
: LIFT_REFUSALS[record.reason],
|
|
3399
3516
|
)
|
|
3400
3517
|
for (const gate of record.gates) {
|
|
3401
3518
|
if (!gate.isOk) logWarn(`${gate.gate}: ${gate.reason ?? 'not clear'}`)
|
package/src/commands/records.ts
CHANGED
|
@@ -100,6 +100,10 @@ export function register(program: Command): void {
|
|
|
100
100
|
' groundwork README and current-state files, numbering, dating, and a half-closed track',
|
|
101
101
|
' intake overview file, numbering, dating, and the four bullets every item carries',
|
|
102
102
|
'',
|
|
103
|
+
'Refusals, read off `reason`:',
|
|
104
|
+
" no-folder none of the kind's directories exist at either record root",
|
|
105
|
+
' unknown-kind the argument names no published kind',
|
|
106
|
+
'',
|
|
103
107
|
'Exit codes:',
|
|
104
108
|
' 0 every check passed',
|
|
105
109
|
' 1 refused, with the reason on stderr or in the JSON record',
|
|
@@ -216,6 +220,10 @@ export function register(program: Command): void {
|
|
|
216
220
|
'and the release tags merged into HEAD after that day, or never when it',
|
|
217
221
|
'carries no date.',
|
|
218
222
|
'',
|
|
223
|
+
'Refusals, read off `reason`:',
|
|
224
|
+
' unknown-kind any kind but `canonical`',
|
|
225
|
+
' unreadable-tags the release tags could not be read',
|
|
226
|
+
'',
|
|
219
227
|
'Exit codes:',
|
|
220
228
|
' 0 the reading completed, whatever it found',
|
|
221
229
|
' 1 refused, with the reason on stderr or in the JSON record',
|
package/src/commands/secrets.ts
CHANGED
|
@@ -60,6 +60,12 @@ export function register(program: Command): void {
|
|
|
60
60
|
' 1 refused, with the reason on stderr',
|
|
61
61
|
' 2 at least one value was found',
|
|
62
62
|
'',
|
|
63
|
+
'Reasons:',
|
|
64
|
+
' no-manifest, no-publish, no-shipped-files',
|
|
65
|
+
' nothing is published from this tree, so the corpus is absent',
|
|
66
|
+
' no-files-field a publish would pack the whole tree and none of it was read',
|
|
67
|
+
' no-git git could not list the tree, so the corpus is unknown',
|
|
68
|
+
'',
|
|
63
69
|
'Examples:',
|
|
64
70
|
' canon secrets scan',
|
|
65
71
|
' canon secrets scan --json',
|
package/src/commands/sessions.ts
CHANGED
|
@@ -69,6 +69,14 @@ export function register(program: Command): void {
|
|
|
69
69
|
.addHelpText(
|
|
70
70
|
'after',
|
|
71
71
|
[
|
|
72
|
+
'',
|
|
73
|
+
'Refusals, read off `reason`:',
|
|
74
|
+
' no-registry no session registry exists, so nothing was read and',
|
|
75
|
+
' the roster is not empty',
|
|
76
|
+
' no-repository `--branch` resolved no repository. Run inside one or',
|
|
77
|
+
' pass `--repository`',
|
|
78
|
+
' no-self-identity `--self` found nothing to name this session with',
|
|
79
|
+
' no-self-row `--self` found no roster row for this session',
|
|
72
80
|
'',
|
|
73
81
|
'Exit codes:',
|
|
74
82
|
' 0 the roster was read',
|
package/src/commands/tasks.ts
CHANGED
|
@@ -176,6 +176,18 @@ export function register(program: Command): void {
|
|
|
176
176
|
'listed, and pending-branch while a Pending branch: line names a branch',
|
|
177
177
|
'whose own pull request is not recorded yet. A stem skips both.',
|
|
178
178
|
'',
|
|
179
|
+
'Refusals, read off `reason`:',
|
|
180
|
+
' no-board the repository holds no `.canon/tasks/` board',
|
|
181
|
+
' no-match no task matches the selector',
|
|
182
|
+
' ambiguous more than one task matches the selector',
|
|
183
|
+
' no-outcomes the task carries no outcomes to check',
|
|
184
|
+
' open-outcomes an outcome is still unchecked',
|
|
185
|
+
' earlier-slice `--pull-request` names a number that is not the last on',
|
|
186
|
+
' the task',
|
|
187
|
+
' pending-branch a `Pending branch:` line names a branch whose pull',
|
|
188
|
+
' request is not recorded yet',
|
|
189
|
+
' bad-input a malformed selector',
|
|
190
|
+
'',
|
|
179
191
|
'Exit codes:',
|
|
180
192
|
' 0 the task was archived',
|
|
181
193
|
' 1 refused, with the reason on stderr or in the JSON record',
|
|
@@ -209,6 +221,12 @@ export function register(program: Command): void {
|
|
|
209
221
|
'decided against at any outcome state, and the two never share a',
|
|
210
222
|
'refusal set since they answer different questions.',
|
|
211
223
|
'',
|
|
224
|
+
'Refusals, read off `reason`:',
|
|
225
|
+
' no-board the repository holds no `.canon/tasks/` board',
|
|
226
|
+
' no-match no task matches the selector',
|
|
227
|
+
' ambiguous more than one task matches the selector',
|
|
228
|
+
' bad-input a missing `--reason`, or a `--by` that resolves to nobody',
|
|
229
|
+
'',
|
|
212
230
|
'Exit codes:',
|
|
213
231
|
' 0 the task was declined',
|
|
214
232
|
' 1 refused, with the reason on stderr or in the JSON record',
|
|
@@ -243,6 +261,12 @@ export function register(program: Command): void {
|
|
|
243
261
|
' every Run now row names the files it touches',
|
|
244
262
|
' a file two Run now rows share is listed, never failed',
|
|
245
263
|
'',
|
|
264
|
+
'Refusals, read off `reason`:',
|
|
265
|
+
' no-board the repository holds no `.canon/tasks/` board',
|
|
266
|
+
' no-ordering the board has no ordering file',
|
|
267
|
+
' no-groups the board groups rows under headings of its own, with no',
|
|
268
|
+
' declared columns',
|
|
269
|
+
'',
|
|
246
270
|
'Exit codes:',
|
|
247
271
|
' 0 every check passed',
|
|
248
272
|
' 1 refused, with the reason on stderr or in the JSON record',
|
|
@@ -422,6 +446,12 @@ export function register(program: Command): void {
|
|
|
422
446
|
.addHelpText(
|
|
423
447
|
'after',
|
|
424
448
|
[
|
|
449
|
+
'',
|
|
450
|
+
'Refusals, read off `reason`:',
|
|
451
|
+
' no-board the repository holds no `.canon/tasks/` board',
|
|
452
|
+
' no-match no task matches the selector',
|
|
453
|
+
' ambiguous more than one task matches the selector',
|
|
454
|
+
' bad-input a number that is not a pull request number',
|
|
425
455
|
'',
|
|
426
456
|
'Exit codes:',
|
|
427
457
|
' 0 the line was added, appended to, corrected, or already correct',
|
|
@@ -468,6 +498,13 @@ export function register(program: Command): void {
|
|
|
468
498
|
.addHelpText(
|
|
469
499
|
'after',
|
|
470
500
|
[
|
|
501
|
+
'',
|
|
502
|
+
'Refusals, read off `reason`:',
|
|
503
|
+
' no-board the repository holds no `.canon/tasks/` board',
|
|
504
|
+
' no-match no task matches the selector',
|
|
505
|
+
' no-plan the plan named does not exist',
|
|
506
|
+
' several-plans the task line links more than one plan',
|
|
507
|
+
' bad-input a malformed argument',
|
|
471
508
|
'',
|
|
472
509
|
'Exit codes:',
|
|
473
510
|
' 0 the line was added, appended to, corrected, or already correct',
|
|
@@ -514,6 +551,14 @@ export function register(program: Command): void {
|
|
|
514
551
|
.addHelpText(
|
|
515
552
|
'after',
|
|
516
553
|
[
|
|
554
|
+
'',
|
|
555
|
+
'Refusals, read off `reason`:',
|
|
556
|
+
' no-board the repository holds no `.canon/tasks/` board',
|
|
557
|
+
' no-match no task matches the selector',
|
|
558
|
+
' ambiguous more than one task matches the selector',
|
|
559
|
+
' no-outcomes the task carries no outcomes',
|
|
560
|
+
' out-of-range a `--close` position past the end of the outcome list',
|
|
561
|
+
' bad-input a malformed position',
|
|
517
562
|
'',
|
|
518
563
|
'Exit codes:',
|
|
519
564
|
' 0 every named outcome is closed',
|
|
@@ -557,6 +602,10 @@ export function register(program: Command): void {
|
|
|
557
602
|
'archive holds labels the live board no longer shows and a scan',
|
|
558
603
|
'confined to the board hands out one already spent.',
|
|
559
604
|
'',
|
|
605
|
+
'Refusals, read off `reason`:',
|
|
606
|
+
' no-board the repository holds no `.canon/tasks/` board',
|
|
607
|
+
' label-contended `--claim` lost five reservations, and the verb exits 2',
|
|
608
|
+
'',
|
|
560
609
|
'Exit codes:',
|
|
561
610
|
' 0 the label is derived',
|
|
562
611
|
' 1 refused with no-board',
|
|
@@ -593,6 +642,9 @@ export function register(program: Command): void {
|
|
|
593
642
|
'or both, read from priority.md and backlog.md through the parsers',
|
|
594
643
|
'validate uses. It lists the live root only, never archive/ or declined/.',
|
|
595
644
|
'',
|
|
645
|
+
'Refusals, read off `reason`:',
|
|
646
|
+
' no-board the repository holds no `.canon/tasks/` board',
|
|
647
|
+
'',
|
|
596
648
|
'Exit codes:',
|
|
597
649
|
' 0 the list is derived',
|
|
598
650
|
' 1 refused with no-board',
|
package/src/commands/teach.ts
CHANGED
|
@@ -120,6 +120,19 @@ export function register(program: Command): void {
|
|
|
120
120
|
'rewriting anything, canon teach list <topic> prints the canon serve',
|
|
121
121
|
'line, with the teach folder written from where you stand.',
|
|
122
122
|
'',
|
|
123
|
+
'Reasons, on stderr or in the JSON record:',
|
|
124
|
+
' no-teach the root carries no .canon/teach/ folder',
|
|
125
|
+
' no-workspace no workspace matches the topic',
|
|
126
|
+
' ambiguous two workspaces claim one topic',
|
|
127
|
+
' exists a workspace already covers the topic an open names',
|
|
128
|
+
' no-file the workspace carries no file the verb writes into',
|
|
129
|
+
' no-section RESOURCES.md carries no heading the entries belong under',
|
|
130
|
+
' listed a URL is already read, or a lead is offered as listed',
|
|
131
|
+
' defined a term already carries a glossary entry',
|
|
132
|
+
' bad-input the command line is malformed',
|
|
133
|
+
' no-port up found every port in reach taken',
|
|
134
|
+
' bind-failed up could not bind, such as a privileged port',
|
|
135
|
+
'',
|
|
123
136
|
].join('\n'),
|
|
124
137
|
)
|
|
125
138
|
|
package/src/commands/upstream.ts
CHANGED
|
@@ -103,6 +103,14 @@ export function register(program: Command): void {
|
|
|
103
103
|
'Writes nothing. The cursor moves only through `upstream advance`, so a',
|
|
104
104
|
'run that dies midway reads the same range next time.',
|
|
105
105
|
'',
|
|
106
|
+
'Reasons:',
|
|
107
|
+
' no-cursor no cursor is stored and no --since was given',
|
|
108
|
+
' bad-since --since is not a dotted version',
|
|
109
|
+
' cursor-missing every release the feed returned is newer than the cursor',
|
|
110
|
+
' rate-limited GitHub refused the request, so set GH_TOKEN or GITHUB_TOKEN',
|
|
111
|
+
' network the feed could not be reached',
|
|
112
|
+
' http GitHub answered with an error status',
|
|
113
|
+
'',
|
|
106
114
|
'Exit codes:',
|
|
107
115
|
' 0 the range was read, possibly empty',
|
|
108
116
|
' 1 refused, with the reason on stderr or in the JSON record',
|
|
@@ -154,7 +162,19 @@ export function register(program: Command): void {
|
|
|
154
162
|
'Due when the cursor is a week old and the installed version is ahead of',
|
|
155
163
|
'it, or sooner when an Added line in the gap names a canon surface. The',
|
|
156
164
|
'gap is read from GitHub at most once a day and cached under the scratch',
|
|
157
|
-
'folder, and a failed read is held for the day as well.',
|
|
165
|
+
'folder, and a failed read is held for the day as well. The record',
|
|
166
|
+
'carries due, reason, releases, installed, and cursor, and releases is',
|
|
167
|
+
'null when the feed was not read.',
|
|
168
|
+
'',
|
|
169
|
+
'Reasons:',
|
|
170
|
+
' Due, by reason:',
|
|
171
|
+
' no-cursor no digest has been run',
|
|
172
|
+
' week the cursor is seven or more days old and the install is ahead',
|
|
173
|
+
' vocabulary an Added line in the gap names a plugin, skill, hook, or worktree',
|
|
174
|
+
' Not due, by reason:',
|
|
175
|
+
' current the install is at or below the cursor, as after a downgrade',
|
|
176
|
+
' recent the cursor is under a week old and no line in the gap matches',
|
|
177
|
+
' unknown-version claude --version could not be read',
|
|
158
178
|
'',
|
|
159
179
|
'Exit codes:',
|
|
160
180
|
' 0 answered, whether or not a digest is due',
|
|
@@ -185,6 +205,10 @@ export function register(program: Command): void {
|
|
|
185
205
|
'folder through a temp file and a rename. It refuses a version older than',
|
|
186
206
|
'the stored cursor.',
|
|
187
207
|
'',
|
|
208
|
+
'Reasons:',
|
|
209
|
+
' invalid-version the version is not a dotted number',
|
|
210
|
+
' older-than-cursor the version is older than the stored cursor',
|
|
211
|
+
'',
|
|
188
212
|
'Exit codes:',
|
|
189
213
|
' 0 the cursor moved',
|
|
190
214
|
' 1 refused, with the reason on stderr or in the JSON record',
|
|
@@ -119,6 +119,11 @@ const CONDITIONS = [
|
|
|
119
119
|
'command is running in, since git removes the directory a caller stands',
|
|
120
120
|
'in and every later call scoped to it then fails.',
|
|
121
121
|
'',
|
|
122
|
+
'A "refusals" entry is one of these reasons:',
|
|
123
|
+
...Object.entries(REFUSALS).map(
|
|
124
|
+
([reason, meaning]) => ` ${reason.padEnd(24)}${meaning}`,
|
|
125
|
+
),
|
|
126
|
+
'',
|
|
122
127
|
'An unreadable input refuses the whole reading rather than reporting',
|
|
123
128
|
'every worktree as not reclaimable, since an absent merge state and a',
|
|
124
129
|
'branch with no merged pull request produce the same empty answer.',
|
package/src/context/audit.ts
CHANGED
|
@@ -104,6 +104,19 @@ export const LAYOUT_PARAGRAPH_CAP = 1
|
|
|
104
104
|
/** The one Layout H3 not titled with a path, holding the trivial folders. */
|
|
105
105
|
export const OTHER_FOLDERS = 'Other folders'
|
|
106
106
|
|
|
107
|
+
/** The run-command table H3 only the development entry carries under `Layout`. */
|
|
108
|
+
export const SCRIPTS_HEADING = 'Scripts'
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Whether an entry path is the development entry, being the flat entry, the
|
|
112
|
+
* overview of a split one a target may still carry, or this repository's
|
|
113
|
+
* `local-loop` entry. The standard exempts the
|
|
114
|
+
* `Scripts` H3 for that entry alone, so the path is what tells.
|
|
115
|
+
*/
|
|
116
|
+
function isDevelopmentEntry(rel: string): boolean {
|
|
117
|
+
return /(?:^|\/)development(?:\/(?:overview|local-loop))?\.md$/.test(rel)
|
|
118
|
+
}
|
|
119
|
+
|
|
107
120
|
/**
|
|
108
121
|
* The context standard's list, kept as its own export because the audit record
|
|
109
122
|
* has published it flat under `checkpoints.requiredSections` since before any
|
|
@@ -584,13 +597,18 @@ interface FolderHeading {
|
|
|
584
597
|
* A cap reports once per section or folder heading however far past it the
|
|
585
598
|
* count runs, since the fix is one cut either way. `Other folders` is exempt
|
|
586
599
|
* from both folder caps, its one-line bullets being held by the bullet run
|
|
587
|
-
* limit `canon markdown audit` already gates.
|
|
600
|
+
* limit `canon markdown audit` already gates. The development entry's `Scripts`
|
|
601
|
+
* H3 is exempt from the heading rule and both caps the same way, its table
|
|
602
|
+
* being a run-command surface rather than a folder.
|
|
588
603
|
*/
|
|
589
604
|
function shapeIssues(
|
|
605
|
+
rel: string,
|
|
590
606
|
lines: readonly BodyLine[],
|
|
591
607
|
retired: readonly string[],
|
|
592
608
|
): ShapeIssue[] {
|
|
593
609
|
const issues: ShapeIssue[] = []
|
|
610
|
+
const exempt = new Set([OTHER_FOLDERS])
|
|
611
|
+
if (isDevelopmentEntry(rel)) exempt.add(SCRIPTS_HEADING)
|
|
594
612
|
let section: string | undefined
|
|
595
613
|
let gotchas: { line: number; bullets: number } | undefined
|
|
596
614
|
let folder: FolderHeading | undefined
|
|
@@ -599,7 +617,7 @@ function shapeIssues(
|
|
|
599
617
|
let afterBlank = false
|
|
600
618
|
|
|
601
619
|
const closeFolder = (): void => {
|
|
602
|
-
if (folder && folder.text
|
|
620
|
+
if (folder && !exempt.has(folder.text)) {
|
|
603
621
|
if (folder.paragraphs > LAYOUT_PARAGRAPH_CAP) {
|
|
604
622
|
issues.push({
|
|
605
623
|
line: folder.line,
|
|
@@ -659,7 +677,7 @@ function shapeIssues(
|
|
|
659
677
|
} else if (section === 'Layout' && level === 3) {
|
|
660
678
|
closeFolder()
|
|
661
679
|
folder = { line: line.number, text, bullets: 0, paragraphs: 0 }
|
|
662
|
-
if (!FOLDER_TITLE.test(text) && text
|
|
680
|
+
if (!FOLDER_TITLE.test(text) && !exempt.has(text)) {
|
|
663
681
|
issues.push({ line: line.number, kind: 'layout-heading', text })
|
|
664
682
|
}
|
|
665
683
|
} else if (section === 'Layout') {
|
|
@@ -780,7 +798,7 @@ export function measureEntry(
|
|
|
780
798
|
bareReferences: bareReferences(lines, siblings),
|
|
781
799
|
sections: declaredSections(lines, required),
|
|
782
800
|
otherSections: otherSections(lines, allowed, retired),
|
|
783
|
-
shape: governsContent ? shapeIssues(lines, retired) : [],
|
|
801
|
+
shape: governsContent ? shapeIssues(rel, lines, retired) : [],
|
|
784
802
|
stub: isStubSeed(source),
|
|
785
803
|
governed: governsContent,
|
|
786
804
|
}
|
package/src/context/folders.ts
CHANGED
|
@@ -56,7 +56,7 @@ export interface AuditedFolder {
|
|
|
56
56
|
* The requested folder name this was resolved under, which is what says
|
|
57
57
|
* which standard governs the entries. A nested split folder carries the name
|
|
58
58
|
* of the folder it sits beneath rather than its own, so
|
|
59
|
-
* `canon/context
|
|
59
|
+
* `canon/context/<domain>` is governed as `context`.
|
|
60
60
|
*/
|
|
61
61
|
readonly name: string
|
|
62
62
|
/**
|
|
@@ -15,8 +15,10 @@
|
|
|
15
15
|
*/
|
|
16
16
|
export const SHAPE_GATED_FOLDERS: readonly string[] = [
|
|
17
17
|
'canon/context/context-model.md',
|
|
18
|
-
'canon/context/
|
|
19
|
-
'canon/context/
|
|
18
|
+
'canon/context/development/',
|
|
19
|
+
'canon/context/features/',
|
|
20
|
+
'canon/context/surfaces/',
|
|
21
|
+
'canon/context/workflow/',
|
|
20
22
|
]
|
|
21
23
|
|
|
22
24
|
/** Whether a repository-relative entry path sits under a gated prefix. */
|
package/src/counts/scan.ts
CHANGED
|
@@ -80,7 +80,7 @@ function sentencesOf(line: string): string[] {
|
|
|
80
80
|
* the 290 showed removing all of them while keeping that one.
|
|
81
81
|
*
|
|
82
82
|
* `authors` joined the list on a corpus measurement rather than on the shape
|
|
83
|
-
* argument above.
|
|
83
|
+
* argument above. A context entry asserting `the
|
|
84
84
|
* toolkit authors 69 rules under governance/rules/`, which is the assertion
|
|
85
85
|
* shape this list was built for with a verb the list did not carry, and the
|
|
86
86
|
* word costs one further sentence across the whole corpus and no finding.
|
package/src/design/components.ts
CHANGED
|
@@ -250,7 +250,7 @@ const TEACH_CHROME: Component = {
|
|
|
250
250
|
'navigation shared by every teach page. Recovered from two gitignored',
|
|
251
251
|
'course.css files that predate a regression that dropped this layer from the',
|
|
252
252
|
"generator, and rewritten onto this module's tokens rather than the",
|
|
253
|
-
'incompatible palette they carried. See canon/context/features/teach
|
|
253
|
+
'incompatible palette they carried. See canon/context/features/teach.md',
|
|
254
254
|
'for the shape.',
|
|
255
255
|
].join('\n '),
|
|
256
256
|
reads: [
|