@erclx/canon 5.41.0 → 5.43.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/claude/.claude-plugin/plugin.json +1 -1
- package/claude/skills/api-design/references/adopted.md +34 -14
- package/claude/skills/auto-ship/REQUIREMENT.md +1 -0
- package/claude/skills/auto-ship/SKILL.md +24 -4
- package/claude/skills/auto-ship/references/draft-mark.md +32 -6
- package/claude/skills/auto-ship/references/failure-recovery.md +15 -13
- package/claude/skills/auto-ship/references/tier-failures.md +31 -10
- package/claude/skills/auto-ship/references/ui-checklist.md +9 -4
- package/claude/skills/auto-ship/references/worktree-name.md +9 -3
- package/claude/skills/build-in-slices/references/adopted.md +35 -21
- package/claude/skills/canvas-board/references/canvas.md +17 -6
- package/claude/skills/canvas-board/references/design.md +26 -7
- package/claude/skills/ci-workflow/REQUIREMENT.md +2 -1
- package/claude/skills/ci-workflow/SKILL.md +12 -2
- package/claude/skills/ci-workflow/references/workflows.md +12 -9
- package/claude/skills/code-craft/references/adopted.md +58 -20
- package/claude/skills/codebase-layout/references/adopted.md +49 -16
- package/claude/skills/codebase-layout/references/frontend.md +11 -4
- package/claude/skills/codebase-layout/references/tests.md +4 -2
- package/claude/skills/context-fold/references/classify.md +10 -15
- package/claude/skills/context-fold/references/completion-report.md +11 -9
- package/claude/skills/context-fold/references/context-refresh.md +26 -9
- package/claude/skills/context-fold/references/unusable-baseline.md +5 -3
- package/claude/skills/create-rule/REQUIREMENT.md +5 -0
- package/claude/skills/create-rule/SKILL.md +10 -1
- package/claude/skills/create-rule/references/checklist.md +26 -0
- package/claude/skills/create-rule/references/rule.md +98 -20
- package/claude/skills/create-skill/SKILL.md +1 -0
- package/claude/skills/create-skill/references/skill-disclosure.md +10 -2
- package/claude/skills/create-skill/references/skill-frontmatter.md +43 -0
- package/claude/skills/create-skill/references/skill-paths.md +7 -4
- package/claude/skills/create-skill/references/skill-practice.md +6 -3
- package/claude/skills/create-skill/references/skill-requirement.md +4 -2
- package/claude/skills/create-skill/references/skill.md +67 -44
- package/claude/skills/decision-escalate/SKILL.md +1 -2
- package/claude/skills/deploy-app/references/vercel.md +2 -1
- package/claude/skills/deprecation-migration/references/adopted.md +44 -25
- package/claude/skills/design-taste/references/adopted.md +9 -1
- package/claude/skills/design-taste/references/craft.md +3 -1
- package/claude/skills/design-taste/references/kinds.md +8 -7
- package/claude/skills/design-taste/references/preflight.md +12 -0
- package/claude/skills/design-taste/references/systems.md +7 -6
- package/claude/skills/design-taste/references/tells.md +24 -25
- package/claude/skills/design-taste/references/vocabulary.md +7 -5
- package/claude/skills/document-health/references/axes.md +55 -30
- package/claude/skills/draft-and-pick/references/live-arms.md +2 -2
- package/claude/skills/draft-doc/references/context.md +4 -3
- package/claude/skills/draft-doc/references/docs.md +6 -3
- package/claude/skills/draft-doc/references/readme/agent-facing.md +6 -0
- package/claude/skills/draft-doc/references/readme/application.md +8 -2
- package/claude/skills/draft-doc/references/readme/cli.md +5 -1
- package/claude/skills/draft-doc/references/readme/library.md +4 -0
- package/claude/skills/draft-doc/references/readme/plugin.md +9 -1
- package/claude/skills/draft-doc/references/readme.md +30 -13
- package/claude/skills/draft-doc/references/requirements.md +17 -4
- package/claude/skills/draft-doc/references/wiki-kind.md +20 -10
- package/claude/skills/draft-doc/references/wiki.md +1 -1
- package/claude/skills/draft-figure/references/architecture-set.md +7 -3
- package/claude/skills/draft-figure/references/figures.md +8 -6
- package/claude/skills/draft-figure/references/hand-drawn.md +3 -1
- package/claude/skills/draft-figure/references/mermaid-path.md +19 -9
- package/claude/skills/draft-figure/references/mermaid.md +10 -5
- package/claude/skills/draft-ready/references/ready.md +22 -15
- package/claude/skills/git-branch/references/branch.md +11 -1
- package/claude/skills/git-branch/references/slug.md +17 -5
- package/claude/skills/git-commit/references/commit.md +9 -5
- package/claude/skills/git-commit/references/versioning.md +23 -17
- package/claude/skills/git-followup/REQUIREMENT.md +1 -1
- package/claude/skills/git-followup/SKILL.md +5 -6
- package/claude/skills/git-issue/references/issue.md +29 -16
- package/claude/skills/git-pr/REQUIREMENT.md +1 -5
- package/claude/skills/git-pr/SKILL.md +11 -22
- package/claude/skills/git-pr/references/final-command.md +21 -8
- package/claude/skills/git-pr/references/labels.md +45 -28
- package/claude/skills/git-pr/references/pr.md +29 -9
- package/claude/skills/git-pr/references/publish.md +42 -28
- package/claude/skills/git-pr/references/task-number.md +20 -8
- package/claude/skills/git-ship/SKILL.md +1 -1
- package/claude/skills/plan-feature/SKILL.md +1 -1
- package/claude/skills/plan-feature/references/plan-lifecycle.md +10 -3
- package/claude/skills/plan-feature/references/plan.md +58 -21
- package/claude/skills/plan-groundwork/references/groundwork-spikes.md +4 -1
- package/claude/skills/plan-groundwork/references/groundwork.md +14 -16
- package/claude/skills/plan-intake/references/intake.md +29 -24
- package/claude/skills/pr-evidence/REQUIREMENT.md +40 -0
- package/claude/skills/pr-evidence/SKILL.md +53 -0
- package/claude/skills/pr-evidence/references/evidence.md +150 -0
- package/claude/skills/review-address/references/post-review-findings.md +10 -3
- package/claude/skills/review-address/references/rebase-conflicts.md +15 -7
- package/claude/skills/review-address/references/rest-reads.md +3 -1
- package/claude/skills/review-craft/references/adopted.md +35 -18
- package/claude/skills/review-craft/references/security.md +42 -7
- package/claude/skills/review-craft/references/ui.md +23 -8
- package/claude/skills/review-pr/SKILL.md +6 -6
- package/claude/skills/review-pr/references/body-variants.md +29 -9
- package/claude/skills/review-pr/references/close-out.md +20 -5
- package/claude/skills/review-pr/references/key-changes.md +27 -3
- package/claude/skills/review-pr/references/post.md +83 -31
- package/claude/skills/review-pr/references/scope.md +54 -19
- package/claude/skills/review-ui/references/close-out.md +7 -5
- package/claude/skills/review-ui/references/driving.md +24 -4
- package/claude/skills/review-ui/references/post.md +43 -15
- package/claude/skills/role-orchestrator/SKILL.md +2 -0
- package/claude/skills/role-orchestrator/references/orchestrator-cloud-launch.md +67 -17
- package/claude/skills/role-orchestrator/references/orchestrator-dispatch.md +80 -40
- package/claude/skills/role-orchestrator/references/orchestrator-handback.md +52 -15
- package/claude/skills/role-orchestrator/references/orchestrator-handoff.md +48 -26
- package/claude/skills/role-orchestrator/references/orchestrator-inbound.md +12 -13
- package/claude/skills/role-orchestrator/references/orchestrator-launch.md +72 -36
- package/claude/skills/role-orchestrator/references/orchestrator-output.md +16 -4
- package/claude/skills/role-orchestrator/references/orchestrator-parked.md +64 -24
- package/claude/skills/role-orchestrator/references/orchestrator-poll.md +80 -26
- package/claude/skills/role-orchestrator/references/orchestrator-refill.md +41 -18
- package/claude/skills/role-orchestrator/references/orchestrator-resume.md +19 -7
- package/claude/skills/role-orchestrator/references/orchestrator-review-dispatch.md +22 -12
- package/claude/skills/role-orchestrator/references/orchestrator-reviewer-launch.md +15 -6
- package/claude/skills/role-orchestrator/references/orchestrator-sweep.md +25 -7
- package/claude/skills/role-orchestrator/references/orchestrator-watch.md +34 -8
- package/claude/skills/role-orchestrator/scripts/poll.ts +16 -24
- package/claude/skills/role-planner/REQUIREMENT.md +1 -1
- package/claude/skills/role-planner/SKILL.md +3 -3
- package/claude/skills/role-worker/REQUIREMENT.md +3 -0
- package/claude/skills/role-worker/SKILL.md +34 -2
- package/claude/skills/search-craft/REQUIREMENT.md +1 -1
- package/claude/skills/search-craft/SKILL.md +2 -2
- package/claude/skills/search-craft/references/adopted.md +18 -6
- package/claude/skills/session-map/references/session.md +26 -7
- package/claude/skills/session-worktree/references/main-root-writes.md +25 -8
- package/claude/skills/systematic-debugging/references/adopted.md +30 -16
- package/claude/skills/target-check/references/domains.md +39 -11
- package/claude/skills/target-setup/references/detect.md +80 -65
- package/claude/skills/target-setup/references/indexes.md +8 -6
- package/claude/skills/task-board/references/board.md +86 -30
- package/claude/skills/task-board/references/decline.md +29 -13
- package/claude/skills/task-board/references/tasks-decline.md +12 -5
- package/claude/skills/task-board/references/tasks.md +65 -33
- package/claude/skills/task-complete/references/archive.md +41 -17
- package/claude/skills/task-complete/references/receipt-sweep.md +23 -9
- package/claude/skills/task-complete/references/unusable-baseline.md +10 -3
- package/claude/skills/teach-workspace/references/glossary.md +6 -2
- package/claude/skills/teach-workspace/references/lesson-build.md +44 -16
- package/claude/skills/teach-workspace/references/lesson-craft.md +12 -7
- package/claude/skills/teach-workspace/references/pedagogy.md +6 -3
- package/claude/skills/teach-workspace/references/promotion-fold.md +17 -11
- package/claude/skills/teach-workspace/references/promotion.md +31 -8
- package/claude/skills/teach-workspace/references/revisit.md +8 -2
- package/claude/skills/teach-workspace/references/teach.md +17 -9
- package/claude/skills/test-craft/references/adopted.md +48 -14
- package/claude/skills/test-craft/references/component.md +7 -4
- package/claude/skills/test-craft/references/e2e.md +25 -7
- package/claude/skills/test-craft/references/unit.md +3 -1
- package/claude/skills/test-first/references/adopted.md +12 -4
- package/claude/skills/ui-checklist/REQUIREMENT.md +1 -1
- package/claude/skills/ui-checklist/SKILL.md +1 -1
- package/claude/skills/video-craft/references/adopted.md +62 -18
- package/claude/skills/write-human/references/adopted.md +21 -8
- package/docs/agents/audits.md +1 -0
- package/docs/agents/migrate.md +11 -8
- package/docs/target/migrations.md +11 -0
- package/docs/workflow/parallel-features.md +1 -1
- package/docs/workflow/skill-map.md +1 -0
- package/governance/detect.toml +4 -0
- package/governance/rules/canon/context.md +2 -1
- package/governance/rules/canon/plan.md +3 -5
- package/governance/rules/canon/scratch.md +3 -1
- package/governance/rules/canon/task-scope.md +2 -1
- package/governance/rules/canon/tasks.md +2 -1
- package/governance/rules/canon/worktrees.md +5 -2
- package/governance/rules/claude/output.md +7 -4
- package/governance/rules/claude/skill.md +6 -4
- package/governance/rules/tooling/ci-workflow.md +6 -39
- package/governance/rules/ui/design-reference.md +37 -0
- package/governance/rules/writing/markdown.md +2 -1
- package/package.json +6 -6
- package/src/audits/catalog.ts +42 -0
- package/src/claude/cases/workflow.ts +5 -0
- package/src/claude/settings.ts +8 -4
- package/src/commands/claude.ts +11 -1
- package/src/commands/gov.ts +198 -0
- package/src/commands/migrate.ts +121 -2
- package/src/commands/pr.ts +146 -105
- package/src/commands/tooling.ts +3 -3
- package/src/commands/worktrees.ts +49 -0
- package/src/gate/stages.ts +15 -0
- package/src/gov/audit.ts +440 -0
- package/src/listeners/belongs.ts +21 -0
- package/src/listeners/machine.ts +93 -0
- package/src/listeners/parse.ts +41 -0
- package/src/markdown/scope.ts +144 -0
- package/src/migrate/session-defaults.ts +138 -0
- package/src/pr/lift.ts +6 -3
- package/src/pr/local.ts +4 -69
- package/src/pr/review-state.ts +182 -0
- package/src/sync/check.ts +15 -0
- package/src/tooling/stamp.ts +56 -12
- package/src/worktrees/stop.ts +92 -0
- package/tooling/base/configs/.github/workflows/phase-label-gate.yml +6 -2
- package/tooling/base/configs/commitlint.config.js +0 -20
- package/tooling/base/reference.md +1 -1
- package/tooling/base/seeds/.github/workflows/verify.yml +4 -1
- package/tooling/claude/reference.md +13 -7
- package/tooling/claude/seeds/.claude/settings.json +4 -0
- package/tooling/claude/user/settings.template.json +0 -4
- package/tooling/cloudflare/configs/.github/workflows/deploy.yml +5 -5
- package/tooling/public/configs/.github/dependabot.yml +12 -0
- package/tooling/public/configs/.github/workflows/release-please.yml +30 -0
- package/tooling/public/manifest.toml +8 -0
- package/tooling/public/reference.md +53 -0
- package/tooling/public/seeds/.github/ISSUE_TEMPLATE/bug.yml +48 -0
- package/tooling/public/seeds/.github/ISSUE_TEMPLATE/config.yml +5 -0
- package/tooling/public/seeds/.github/ISSUE_TEMPLATE/feature.yml +16 -0
- package/tooling/public/seeds/.release-please-manifest.json +3 -0
- package/tooling/public/seeds/CONTRIBUTING.md +50 -0
- package/tooling/public/seeds/LICENSE +21 -0
- package/tooling/public/seeds/SECURITY.md +13 -0
- package/tooling/public/seeds/release-please-config.json +12 -0
- package/tooling/shell/configs/.github/workflows/shell.yml +4 -1
- package/tooling/web/configs/.github/workflows/readme-screenshot.yml +6 -3
- package/tooling/web/configs/.github/workflows/verify.yml +15 -12
- package/claude/skills/git-pr/references/evidence.md +0 -124
package/src/markdown/scope.ts
CHANGED
|
@@ -42,41 +42,113 @@ export const GATED_PATHS: readonly string[] = [
|
|
|
42
42
|
'canon/context/context-model.md',
|
|
43
43
|
'canon/context/index.md',
|
|
44
44
|
'claude/skills/api-design/SKILL.md',
|
|
45
|
+
'claude/skills/api-design/references/adopted.md',
|
|
45
46
|
'claude/skills/auto-ship/SKILL.md',
|
|
47
|
+
'claude/skills/auto-ship/references/draft-mark.md',
|
|
48
|
+
'claude/skills/auto-ship/references/failure-recovery.md',
|
|
49
|
+
'claude/skills/auto-ship/references/fan-in.md',
|
|
50
|
+
'claude/skills/auto-ship/references/tier-failures.md',
|
|
51
|
+
'claude/skills/auto-ship/references/ui-checklist.md',
|
|
52
|
+
'claude/skills/auto-ship/references/verb-absent.md',
|
|
53
|
+
'claude/skills/auto-ship/references/worktree-name.md',
|
|
46
54
|
'claude/skills/backlog-triage/SKILL.md',
|
|
47
55
|
'claude/skills/backlog-triage/references/apply.md',
|
|
48
56
|
'claude/skills/build-in-slices/SKILL.md',
|
|
57
|
+
'claude/skills/build-in-slices/references/adopted.md',
|
|
49
58
|
'claude/skills/canon-cli/SKILL.md',
|
|
50
59
|
'claude/skills/canon-feedback-triage/SKILL.md',
|
|
51
60
|
'claude/skills/canon-feedback/SKILL.md',
|
|
61
|
+
'claude/skills/canon-feedback/references/template.md',
|
|
52
62
|
'claude/skills/canvas-board/SKILL.md',
|
|
63
|
+
'claude/skills/canvas-board/references/canvas.md',
|
|
64
|
+
'claude/skills/canvas-board/references/design.md',
|
|
53
65
|
'claude/skills/ci-workflow/SKILL.md',
|
|
66
|
+
'claude/skills/ci-workflow/references/workflows.md',
|
|
54
67
|
'claude/skills/code-craft/SKILL.md',
|
|
68
|
+
'claude/skills/code-craft/references/adopted.md',
|
|
55
69
|
'claude/skills/codebase-layout/SKILL.md',
|
|
70
|
+
'claude/skills/codebase-layout/references/adopted.md',
|
|
71
|
+
'claude/skills/codebase-layout/references/frontend.md',
|
|
72
|
+
'claude/skills/codebase-layout/references/tests.md',
|
|
56
73
|
'claude/skills/context-fold/SKILL.md',
|
|
74
|
+
'claude/skills/context-fold/references/classify.md',
|
|
75
|
+
'claude/skills/context-fold/references/completion-report.md',
|
|
76
|
+
'claude/skills/context-fold/references/context-refresh.md',
|
|
77
|
+
'claude/skills/context-fold/references/context.md',
|
|
78
|
+
'claude/skills/context-fold/references/unusable-baseline.md',
|
|
57
79
|
'claude/skills/create-rule/SKILL.md',
|
|
80
|
+
'claude/skills/create-rule/references/rule.md',
|
|
58
81
|
'claude/skills/create-skill/SKILL.md',
|
|
82
|
+
'claude/skills/create-skill/references/skill-disclosure.md',
|
|
83
|
+
'claude/skills/create-skill/references/skill-frontmatter.md',
|
|
84
|
+
'claude/skills/create-skill/references/skill-paths.md',
|
|
85
|
+
'claude/skills/create-skill/references/skill-practice.md',
|
|
86
|
+
'claude/skills/create-skill/references/skill-reference.md',
|
|
87
|
+
'claude/skills/create-skill/references/skill-requirement.md',
|
|
88
|
+
'claude/skills/create-skill/references/skill.md',
|
|
59
89
|
'claude/skills/decision-escalate/SKILL.md',
|
|
60
90
|
'claude/skills/deploy-app/SKILL.md',
|
|
91
|
+
'claude/skills/deploy-app/references/cloudflare.md',
|
|
92
|
+
'claude/skills/deploy-app/references/vercel.md',
|
|
61
93
|
'claude/skills/deprecation-migration/SKILL.md',
|
|
94
|
+
'claude/skills/deprecation-migration/references/adopted.md',
|
|
62
95
|
'claude/skills/design-taste/SKILL.md',
|
|
96
|
+
'claude/skills/design-taste/references/adopted.md',
|
|
97
|
+
'claude/skills/design-taste/references/craft.md',
|
|
98
|
+
'claude/skills/design-taste/references/kinds.md',
|
|
99
|
+
'claude/skills/design-taste/references/preflight.md',
|
|
100
|
+
'claude/skills/design-taste/references/systems.md',
|
|
101
|
+
'claude/skills/design-taste/references/tells.md',
|
|
102
|
+
'claude/skills/design-taste/references/vocabulary.md',
|
|
63
103
|
'claude/skills/docs-sync/SKILL.md',
|
|
104
|
+
'claude/skills/docs-sync/references/docs.md',
|
|
105
|
+
'claude/skills/docs-sync/references/readme.md',
|
|
64
106
|
'claude/skills/document-health/SKILL.md',
|
|
107
|
+
'claude/skills/document-health/references/axes.md',
|
|
65
108
|
'claude/skills/draft-and-pick/SKILL.md',
|
|
109
|
+
'claude/skills/draft-and-pick/references/live-arms.md',
|
|
66
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',
|
|
67
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',
|
|
68
128
|
'claude/skills/draft-identity/SKILL.md',
|
|
69
129
|
'claude/skills/draft-identity/references/card-route.md',
|
|
70
130
|
'claude/skills/draft-ready/SKILL.md',
|
|
131
|
+
'claude/skills/draft-ready/references/assembly.md',
|
|
132
|
+
'claude/skills/draft-ready/references/ready.md',
|
|
71
133
|
'claude/skills/draft-screencast/SKILL.md',
|
|
72
134
|
'claude/skills/draft-screencast/references/beats-template.md',
|
|
73
135
|
'claude/skills/draft-slides/SKILL.md',
|
|
74
136
|
'claude/skills/draft-slides/references/deck.md',
|
|
75
137
|
'claude/skills/git-branch/SKILL.md',
|
|
138
|
+
'claude/skills/git-branch/references/branch.md',
|
|
139
|
+
'claude/skills/git-branch/references/slug.md',
|
|
76
140
|
'claude/skills/git-commit/SKILL.md',
|
|
141
|
+
'claude/skills/git-commit/references/commit.md',
|
|
142
|
+
'claude/skills/git-commit/references/versioning.md',
|
|
77
143
|
'claude/skills/git-followup/SKILL.md',
|
|
78
144
|
'claude/skills/git-issue/SKILL.md',
|
|
145
|
+
'claude/skills/git-issue/references/issue.md',
|
|
79
146
|
'claude/skills/git-pr/SKILL.md',
|
|
147
|
+
'claude/skills/git-pr/references/final-command.md',
|
|
148
|
+
'claude/skills/git-pr/references/labels.md',
|
|
149
|
+
'claude/skills/git-pr/references/pr.md',
|
|
150
|
+
'claude/skills/git-pr/references/publish.md',
|
|
151
|
+
'claude/skills/git-pr/references/task-number.md',
|
|
80
152
|
'claude/skills/git-ship/SKILL.md',
|
|
81
153
|
'claude/skills/git-ship/references/ci-watch.md',
|
|
82
154
|
'claude/skills/git-split/SKILL.md',
|
|
@@ -86,45 +158,117 @@ export const GATED_PATHS: readonly string[] = [
|
|
|
86
158
|
'claude/skills/git-worktree/SKILL.md',
|
|
87
159
|
'claude/skills/git-worktree/references/cleanup.md',
|
|
88
160
|
'claude/skills/markdown-craft/SKILL.md',
|
|
161
|
+
'claude/skills/markdown-craft/references/markdown.md',
|
|
89
162
|
'claude/skills/plan-feature/SKILL.md',
|
|
90
163
|
'claude/skills/plan-feature/references/batches.md',
|
|
164
|
+
'claude/skills/plan-feature/references/plan-lifecycle.md',
|
|
165
|
+
'claude/skills/plan-feature/references/plan.md',
|
|
91
166
|
'claude/skills/plan-groundwork/SKILL.md',
|
|
167
|
+
'claude/skills/plan-groundwork/references/groundwork-spikes.md',
|
|
168
|
+
'claude/skills/plan-groundwork/references/groundwork.md',
|
|
92
169
|
'claude/skills/plan-groundwork/references/output.md',
|
|
93
170
|
'claude/skills/plan-intake-answer/SKILL.md',
|
|
94
171
|
'claude/skills/plan-intake/SKILL.md',
|
|
172
|
+
'claude/skills/plan-intake/references/intake.md',
|
|
173
|
+
'claude/skills/pr-evidence/SKILL.md',
|
|
174
|
+
'claude/skills/pr-evidence/references/evidence.md',
|
|
95
175
|
'claude/skills/read-frames/SKILL.md',
|
|
96
176
|
'claude/skills/record-screencast/SKILL.md',
|
|
97
177
|
'claude/skills/review-address/SKILL.md',
|
|
178
|
+
'claude/skills/review-address/references/post-review-findings.md',
|
|
179
|
+
'claude/skills/review-address/references/rebase-conflicts.md',
|
|
98
180
|
'claude/skills/review-address/references/rebase-only.md',
|
|
181
|
+
'claude/skills/review-address/references/rest-reads.md',
|
|
99
182
|
'claude/skills/review-craft/SKILL.md',
|
|
183
|
+
'claude/skills/review-craft/references/adopted.md',
|
|
184
|
+
'claude/skills/review-craft/references/security.md',
|
|
185
|
+
'claude/skills/review-craft/references/ui.md',
|
|
100
186
|
'claude/skills/review-pr/SKILL.md',
|
|
187
|
+
'claude/skills/review-pr/references/body-variants.md',
|
|
188
|
+
'claude/skills/review-pr/references/close-out.md',
|
|
189
|
+
'claude/skills/review-pr/references/key-changes.md',
|
|
190
|
+
'claude/skills/review-pr/references/post.md',
|
|
191
|
+
'claude/skills/review-pr/references/scope.md',
|
|
101
192
|
'claude/skills/review-ui/SKILL.md',
|
|
193
|
+
'claude/skills/review-ui/references/close-out.md',
|
|
194
|
+
'claude/skills/review-ui/references/driving.md',
|
|
102
195
|
'claude/skills/review-ui/references/hosted-preview.md',
|
|
196
|
+
'claude/skills/review-ui/references/post.md',
|
|
197
|
+
'claude/skills/review-ui/references/tick.md',
|
|
103
198
|
'claude/skills/role-orchestrator/SKILL.md',
|
|
199
|
+
'claude/skills/role-orchestrator/references/orchestrator-cloud-launch.md',
|
|
200
|
+
'claude/skills/role-orchestrator/references/orchestrator-dispatch.md',
|
|
104
201
|
'claude/skills/role-orchestrator/references/orchestrator-filing.md',
|
|
202
|
+
'claude/skills/role-orchestrator/references/orchestrator-handback.md',
|
|
203
|
+
'claude/skills/role-orchestrator/references/orchestrator-handoff.md',
|
|
204
|
+
'claude/skills/role-orchestrator/references/orchestrator-inbound.md',
|
|
205
|
+
'claude/skills/role-orchestrator/references/orchestrator-launch.md',
|
|
206
|
+
'claude/skills/role-orchestrator/references/orchestrator-output.md',
|
|
207
|
+
'claude/skills/role-orchestrator/references/orchestrator-parked.md',
|
|
208
|
+
'claude/skills/role-orchestrator/references/orchestrator-poll.md',
|
|
209
|
+
'claude/skills/role-orchestrator/references/orchestrator-refill.md',
|
|
210
|
+
'claude/skills/role-orchestrator/references/orchestrator-resume.md',
|
|
211
|
+
'claude/skills/role-orchestrator/references/orchestrator-review-dispatch.md',
|
|
212
|
+
'claude/skills/role-orchestrator/references/orchestrator-reviewer-launch.md',
|
|
213
|
+
'claude/skills/role-orchestrator/references/orchestrator-sweep.md',
|
|
214
|
+
'claude/skills/role-orchestrator/references/orchestrator-watch.md',
|
|
105
215
|
'claude/skills/role-planner/SKILL.md',
|
|
106
216
|
'claude/skills/role-reviewer/SKILL.md',
|
|
107
217
|
'claude/skills/role-worker/SKILL.md',
|
|
108
218
|
'claude/skills/search-craft/SKILL.md',
|
|
219
|
+
'claude/skills/search-craft/references/adopted.md',
|
|
109
220
|
'claude/skills/session-compact/SKILL.md',
|
|
221
|
+
'claude/skills/session-compact/references/handoff-note.md',
|
|
110
222
|
'claude/skills/session-map/SKILL.md',
|
|
223
|
+
'claude/skills/session-map/references/session.md',
|
|
111
224
|
'claude/skills/session-relay/SKILL.md',
|
|
112
225
|
'claude/skills/session-resume/SKILL.md',
|
|
113
226
|
'claude/skills/session-worktree/SKILL.md',
|
|
227
|
+
'claude/skills/session-worktree/references/install-command.md',
|
|
228
|
+
'claude/skills/session-worktree/references/main-root-writes.md',
|
|
114
229
|
'claude/skills/systematic-debugging/SKILL.md',
|
|
230
|
+
'claude/skills/systematic-debugging/references/adopted.md',
|
|
115
231
|
'claude/skills/target-check/SKILL.md',
|
|
232
|
+
'claude/skills/target-check/references/domains.md',
|
|
116
233
|
'claude/skills/target-setup/SKILL.md',
|
|
234
|
+
'claude/skills/target-setup/references/detect.md',
|
|
235
|
+
'claude/skills/target-setup/references/indexes.md',
|
|
236
|
+
'claude/skills/target-setup/references/verify.md',
|
|
117
237
|
'claude/skills/task-board/SKILL.md',
|
|
238
|
+
'claude/skills/task-board/references/board.md',
|
|
118
239
|
'claude/skills/task-board/references/create-report.md',
|
|
240
|
+
'claude/skills/task-board/references/decline.md',
|
|
241
|
+
'claude/skills/task-board/references/tasks-decline.md',
|
|
242
|
+
'claude/skills/task-board/references/tasks.md',
|
|
119
243
|
'claude/skills/task-complete/SKILL.md',
|
|
244
|
+
'claude/skills/task-complete/references/archive.md',
|
|
245
|
+
'claude/skills/task-complete/references/receipt-sweep.md',
|
|
246
|
+
'claude/skills/task-complete/references/unusable-baseline.md',
|
|
120
247
|
'claude/skills/teach-workspace/SKILL.md',
|
|
248
|
+
'claude/skills/teach-workspace/references/glossary.md',
|
|
249
|
+
'claude/skills/teach-workspace/references/lesson-build.md',
|
|
250
|
+
'claude/skills/teach-workspace/references/lesson-craft.md',
|
|
251
|
+
'claude/skills/teach-workspace/references/pedagogy.md',
|
|
252
|
+
'claude/skills/teach-workspace/references/promotion-fold.md',
|
|
253
|
+
'claude/skills/teach-workspace/references/promotion.md',
|
|
254
|
+
'claude/skills/teach-workspace/references/revisit.md',
|
|
255
|
+
'claude/skills/teach-workspace/references/teach.md',
|
|
121
256
|
'claude/skills/test-craft/SKILL.md',
|
|
257
|
+
'claude/skills/test-craft/references/adopted.md',
|
|
258
|
+
'claude/skills/test-craft/references/component.md',
|
|
259
|
+
'claude/skills/test-craft/references/e2e.md',
|
|
260
|
+
'claude/skills/test-craft/references/unit.md',
|
|
122
261
|
'claude/skills/test-first/SKILL.md',
|
|
262
|
+
'claude/skills/test-first/references/adopted.md',
|
|
123
263
|
'claude/skills/ui-checklist/SKILL.md',
|
|
124
264
|
'claude/skills/ux-audit/SKILL.md',
|
|
125
265
|
'claude/skills/ux-measure/SKILL.md',
|
|
126
266
|
'claude/skills/video-craft/SKILL.md',
|
|
267
|
+
'claude/skills/video-craft/references/adopted.md',
|
|
127
268
|
'claude/skills/write-human/SKILL.md',
|
|
269
|
+
'claude/skills/write-human/references/adopted.md',
|
|
270
|
+
'claude/skills/write-human/references/density.md',
|
|
271
|
+
'claude/skills/write-human/references/machine-tells.md',
|
|
128
272
|
'claude/skills/youtube-transcripts/SKILL.md',
|
|
129
273
|
'.claude/skills/internal-',
|
|
130
274
|
'docs/',
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adds the session defaults the seeded `.claude/settings.json` carries to a
|
|
3
|
+
* target that was seeded before them.
|
|
4
|
+
*
|
|
5
|
+
* `canon claude init` copies a seed only when the file is absent, so a target
|
|
6
|
+
* holding its own settings never sees a later seed change. This verb adds each
|
|
7
|
+
* default only where the key is absent: a deny entry is never removed, and a
|
|
8
|
+
* person's `autoMemoryEnabled: true` is never overwritten.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { existsSync } from 'node:fs'
|
|
12
|
+
import { join } from 'node:path'
|
|
13
|
+
import {
|
|
14
|
+
readSettings,
|
|
15
|
+
type SettingsKey,
|
|
16
|
+
type SettingsRecord,
|
|
17
|
+
serializeSettings,
|
|
18
|
+
writeSettings,
|
|
19
|
+
} from '@/claude/settings'
|
|
20
|
+
|
|
21
|
+
/** Both names, since `Task` is the tool's name before Claude Code renamed it. */
|
|
22
|
+
export const DENIED_TOOLS: readonly string[] = ['Agent', 'Task']
|
|
23
|
+
|
|
24
|
+
export interface SessionDefaultsPlan {
|
|
25
|
+
readonly keys: readonly SettingsKey[]
|
|
26
|
+
readonly next: SettingsRecord
|
|
27
|
+
readonly changed: boolean
|
|
28
|
+
/**
|
|
29
|
+
* True when the file holds no default at all. A file holding either one has
|
|
30
|
+
* been through the seed or this verb, so a missing deny there was removed
|
|
31
|
+
* on purpose, which is how an operator opts back into subagents.
|
|
32
|
+
*/
|
|
33
|
+
readonly predates: boolean
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type SessionDefaultsRead =
|
|
37
|
+
| { readonly kind: 'refused'; readonly reason: string }
|
|
38
|
+
| { readonly kind: 'absent'; readonly path: string }
|
|
39
|
+
| {
|
|
40
|
+
readonly kind: 'planned'
|
|
41
|
+
readonly path: string
|
|
42
|
+
readonly indent: string
|
|
43
|
+
readonly plan: SessionDefaultsPlan
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function settingsPath(root: string): string {
|
|
47
|
+
return join(root, '.claude', 'settings.json')
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function isRecord(value: unknown): value is SettingsRecord {
|
|
51
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Appends the denied tools a list lacks, keeping the list's own order, so the
|
|
56
|
+
* diff a person reads is the added entries alone.
|
|
57
|
+
*/
|
|
58
|
+
export function planSessionDefaults(
|
|
59
|
+
current: SettingsRecord,
|
|
60
|
+
): SessionDefaultsPlan {
|
|
61
|
+
const next: SettingsRecord = { ...current }
|
|
62
|
+
const permissions = isRecord(current.permissions) ? current.permissions : {}
|
|
63
|
+
const deny = Array.isArray(permissions.deny) ? permissions.deny : []
|
|
64
|
+
|
|
65
|
+
const missing = DENIED_TOOLS.filter((tool) => !deny.includes(tool))
|
|
66
|
+
if (missing.length > 0) {
|
|
67
|
+
next.permissions = { ...permissions, deny: [...deny, ...missing] }
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const memoryAbsent = !('autoMemoryEnabled' in current)
|
|
71
|
+
if (memoryAbsent) next.autoMemoryEnabled = false
|
|
72
|
+
|
|
73
|
+
const keys: SettingsKey[] = [
|
|
74
|
+
{ label: 'permissions.deny', changed: missing.length > 0 },
|
|
75
|
+
{ label: 'autoMemoryEnabled', changed: memoryAbsent },
|
|
76
|
+
]
|
|
77
|
+
return {
|
|
78
|
+
keys,
|
|
79
|
+
next,
|
|
80
|
+
changed: keys.some((key) => key.changed),
|
|
81
|
+
predates: memoryAbsent && missing.length === DENIED_TOOLS.length,
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Refuses rather than plans whenever a write could replace something the
|
|
87
|
+
* person owns: the toolkit's own settings, a file `JSON.parse` cannot read,
|
|
88
|
+
* or a `permissions` or `deny` value of an unexpected shape.
|
|
89
|
+
*/
|
|
90
|
+
export async function readSessionDefaults(
|
|
91
|
+
root: string,
|
|
92
|
+
protectedRoot: string,
|
|
93
|
+
): Promise<SessionDefaultsRead> {
|
|
94
|
+
if (root === protectedRoot) {
|
|
95
|
+
return {
|
|
96
|
+
kind: 'refused',
|
|
97
|
+
reason:
|
|
98
|
+
'Cannot run against toolkit root. Files here are the source of truth.',
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const path = settingsPath(root)
|
|
103
|
+
if (!existsSync(path)) return { kind: 'absent', path }
|
|
104
|
+
|
|
105
|
+
let read: Awaited<ReturnType<typeof readSettings>>
|
|
106
|
+
try {
|
|
107
|
+
read = await readSettings(path)
|
|
108
|
+
} catch (error) {
|
|
109
|
+
return {
|
|
110
|
+
kind: 'refused',
|
|
111
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const { permissions } = read.value
|
|
116
|
+
if (permissions !== undefined && !isRecord(permissions)) {
|
|
117
|
+
return { kind: 'refused', reason: `${path}: permissions is not an object` }
|
|
118
|
+
}
|
|
119
|
+
if (permissions?.deny !== undefined && !Array.isArray(permissions.deny)) {
|
|
120
|
+
return {
|
|
121
|
+
kind: 'refused',
|
|
122
|
+
reason: `${path}: permissions.deny is not a list`,
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
kind: 'planned',
|
|
128
|
+
path,
|
|
129
|
+
indent: read.indent,
|
|
130
|
+
plan: planSessionDefaults(read.value),
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export async function applySessionDefaults(
|
|
135
|
+
read: Extract<SessionDefaultsRead, { kind: 'planned' }>,
|
|
136
|
+
): Promise<void> {
|
|
137
|
+
await writeSettings(read.path, serializeSettings(read.plan.next, read.indent))
|
|
138
|
+
}
|
package/src/pr/lift.ts
CHANGED
|
@@ -144,7 +144,10 @@ function fail(gate: GateName, reason: LiftRefusal): GateResult {
|
|
|
144
144
|
* since the commit it covers is a stamp GitHub wrote at submission, which the
|
|
145
145
|
* marker exists to replace.
|
|
146
146
|
*/
|
|
147
|
-
function reviewGate(
|
|
147
|
+
export function reviewGate(
|
|
148
|
+
reviews: readonly ReviewRow[],
|
|
149
|
+
head: string,
|
|
150
|
+
): GateResult {
|
|
148
151
|
const scope = resolveReviewScope({ reviews })
|
|
149
152
|
if (scope.state === 'none') return fail('review', 'review-missing')
|
|
150
153
|
if (scope.state === 'open') return fail('review', 'review-open')
|
|
@@ -175,8 +178,8 @@ function carried(gates: readonly GateResult[]): {
|
|
|
175
178
|
* checklist the reviewer drove as true as it was. A close that is not an
|
|
176
179
|
* ancestor, which a force-push or a rebase leaves, never carries.
|
|
177
180
|
*/
|
|
178
|
-
async function uiGate(
|
|
179
|
-
api: LiftApi,
|
|
181
|
+
export async function uiGate(
|
|
182
|
+
api: Pick<LiftApi, 'readChanged'>,
|
|
180
183
|
painted: PaintedPatterns,
|
|
181
184
|
reviews: readonly ReviewRow[],
|
|
182
185
|
head: string,
|
package/src/pr/local.ts
CHANGED
|
@@ -1,26 +1,11 @@
|
|
|
1
|
+
import { belongsTo } from '@/listeners/belongs'
|
|
2
|
+
import type { ListenerReader } from '@/listeners/machine'
|
|
1
3
|
import { readAddresses } from '@/pr/evidence'
|
|
2
4
|
|
|
3
|
-
/**
|
|
4
|
-
* The folder linked worktrees sit under. A listener there belongs to another
|
|
5
|
-
* branch even though its cwd sits inside the main checkout's toplevel.
|
|
6
|
-
*/
|
|
7
|
-
const LINKED_WORKTREES = '.claude/worktrees'
|
|
8
|
-
|
|
9
|
-
/** The state `/proc/net/tcp` writes for a socket in LISTEN. */
|
|
10
|
-
const PROC_LISTEN_STATE = '0A'
|
|
11
|
-
|
|
12
5
|
export type LocalRefusal = 'no-server' | 'no-listener-reader'
|
|
13
6
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
readonly pid: number
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/** The machine reads a detection makes, injected so it can be driven without binding a socket. */
|
|
20
|
-
export interface LocalRunner {
|
|
21
|
-
/** Every TCP socket in LISTEN with its owning pid, or undefined when this machine offers no way to read them. */
|
|
22
|
-
listListeners(): Promise<readonly Listener[] | undefined>
|
|
23
|
-
cwdOf(pid: number): Promise<string | undefined>
|
|
7
|
+
/** The listener reads a detection makes plus an HTML probe, injected so it can be driven without binding a socket. */
|
|
8
|
+
export interface LocalRunner extends ListenerReader {
|
|
24
9
|
/** Whether the port serves an HTML page at its root inside the runner's timeout. */
|
|
25
10
|
probe(port: number): Promise<boolean>
|
|
26
11
|
}
|
|
@@ -29,22 +14,6 @@ export type LocalResult =
|
|
|
29
14
|
| { readonly kind: 'found'; readonly port: number; readonly url: string }
|
|
30
15
|
| { readonly kind: 'refused'; readonly reason: LocalRefusal }
|
|
31
16
|
|
|
32
|
-
function isInside(path: string, root: string): boolean {
|
|
33
|
-
return path === root || path.startsWith(`${root}/`)
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Whether a process running in `cwd` serves this worktree. A tool that
|
|
38
|
-
* changes into a subfolder before listening still counts, while a sibling
|
|
39
|
-
* worktree nested under the main checkout's `.claude/worktrees/` does not.
|
|
40
|
-
*/
|
|
41
|
-
function belongsTo(cwd: string, worktreeRoot: string): boolean {
|
|
42
|
-
return (
|
|
43
|
-
isInside(cwd, worktreeRoot) &&
|
|
44
|
-
!isInside(cwd, `${worktreeRoot}/${LINKED_WORKTREES}`)
|
|
45
|
-
)
|
|
46
|
-
}
|
|
47
|
-
|
|
48
17
|
/**
|
|
49
18
|
* Finds the server this worktree is running: the lowest port whose listening
|
|
50
19
|
* process runs inside the worktree and answers an HTTP probe. A port rather
|
|
@@ -76,40 +45,6 @@ export async function findLocalServer(
|
|
|
76
45
|
return { kind: 'refused', reason: 'no-server' }
|
|
77
46
|
}
|
|
78
47
|
|
|
79
|
-
/** Reads `lsof -nP -iTCP -sTCP:LISTEN -Fpn` output, where a `p` line opens a process and each `n` line names one of its sockets. */
|
|
80
|
-
export function parseLsofListeners(text: string): Listener[] {
|
|
81
|
-
const listeners: Listener[] = []
|
|
82
|
-
let pid: number | undefined
|
|
83
|
-
for (const line of text.split('\n')) {
|
|
84
|
-
if (line.startsWith('p')) pid = Number(line.slice(1))
|
|
85
|
-
if (!line.startsWith('n') || pid === undefined) continue
|
|
86
|
-
const port = Number(line.slice(line.lastIndexOf(':') + 1))
|
|
87
|
-
if (Number.isInteger(port) && port > 0) listeners.push({ pid, port })
|
|
88
|
-
}
|
|
89
|
-
return listeners
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
export interface ProcSocket {
|
|
93
|
-
readonly port: number
|
|
94
|
-
readonly inode: string
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/** Reads `/proc/net/tcp` or `tcp6`, keeping each listening socket's port and inode for matching against `/proc/<pid>/fd`. */
|
|
98
|
-
export function parseProcNetTcp(text: string): ProcSocket[] {
|
|
99
|
-
const sockets: ProcSocket[] = []
|
|
100
|
-
for (const line of text.split('\n').slice(1)) {
|
|
101
|
-
const fields = line.trim().split(/\s+/)
|
|
102
|
-
const local = fields[1]
|
|
103
|
-
const state = fields[3]
|
|
104
|
-
const inode = fields[9]
|
|
105
|
-
if (local === undefined || inode === undefined) continue
|
|
106
|
-
if (state !== PROC_LISTEN_STATE) continue
|
|
107
|
-
const port = Number.parseInt(local.slice(local.lastIndexOf(':') + 1), 16)
|
|
108
|
-
if (Number.isInteger(port) && port > 0) sockets.push({ port, inode })
|
|
109
|
-
}
|
|
110
|
-
return sockets
|
|
111
|
-
}
|
|
112
|
-
|
|
113
48
|
/**
|
|
114
49
|
* Drops the address line of a marked body, hosted and local segments alike,
|
|
115
50
|
* since the close that runs this also deletes the branch's preview
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CHECKS_POLL_MS,
|
|
3
|
+
type GateResult,
|
|
4
|
+
type LiftApi,
|
|
5
|
+
type LiftRefusal,
|
|
6
|
+
reviewGate,
|
|
7
|
+
uiGate,
|
|
8
|
+
} from '@/pr/lift'
|
|
9
|
+
import { DEFAULT_PAINTED, type PaintedPatterns } from '@/pr/painted'
|
|
10
|
+
import {
|
|
11
|
+
coversHead,
|
|
12
|
+
type ReviewRow,
|
|
13
|
+
type ReviewScope,
|
|
14
|
+
resolveReviewScope,
|
|
15
|
+
resolveUiScope,
|
|
16
|
+
type UiScope,
|
|
17
|
+
} from '@/pr/review-scope'
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The reads the verb makes, a subset of the lift's so a worker waiting here and
|
|
21
|
+
* a reviewer lifting read one answer. A read that failed returns undefined, and
|
|
22
|
+
* the verb refuses `gh-failed` rather than reading absence as no pass.
|
|
23
|
+
*/
|
|
24
|
+
export type ReviewStateApi = Pick<
|
|
25
|
+
LiftApi,
|
|
26
|
+
'readReviews' | 'readEvidence' | 'readChanged' | 'sleep' | 'now'
|
|
27
|
+
> & {
|
|
28
|
+
/** The branch tip from the remote, which is what a pass has to cover. */
|
|
29
|
+
readTip(): Promise<string | undefined>
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** One family's standing at the tip. */
|
|
33
|
+
export interface FamilyState {
|
|
34
|
+
/** The lift's refusal for this family, absent when its gate passes. */
|
|
35
|
+
readonly reason?: LiftRefusal
|
|
36
|
+
/**
|
|
37
|
+
* Whether a pass covers the tip, which is what a waiting worker reads. True
|
|
38
|
+
* when the gate passes, or when the newest pass, open or unmarked, names the
|
|
39
|
+
* tip. The reason alone cannot answer it, since the lift reads an open pass
|
|
40
|
+
* before it reads currency.
|
|
41
|
+
*/
|
|
42
|
+
readonly isCovered: boolean
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface UiState extends UiScope, FamilyState {
|
|
46
|
+
/** Whether the evidence comment carries a checklist, which binds the family. */
|
|
47
|
+
readonly isBound: boolean
|
|
48
|
+
readonly carriedFrom?: string
|
|
49
|
+
readonly paintedPaths?: readonly string[]
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface ReviewStateRecord {
|
|
53
|
+
readonly head: string
|
|
54
|
+
readonly scope: ReviewScope
|
|
55
|
+
readonly review: FamilyState
|
|
56
|
+
readonly ui: UiState
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export type ReviewStateRead =
|
|
60
|
+
| { readonly kind: 'read'; readonly record: ReviewStateRecord }
|
|
61
|
+
| { readonly kind: 'refused'; readonly reason: 'gh-failed' }
|
|
62
|
+
|
|
63
|
+
export type ReviewWait =
|
|
64
|
+
| {
|
|
65
|
+
readonly kind: 'read'
|
|
66
|
+
readonly reason: 'covered' | 'timeout'
|
|
67
|
+
readonly record: ReviewStateRecord
|
|
68
|
+
readonly waitedSeconds: number
|
|
69
|
+
}
|
|
70
|
+
| {
|
|
71
|
+
readonly kind: 'refused'
|
|
72
|
+
readonly reason: 'gh-failed'
|
|
73
|
+
readonly waitedSeconds: number
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface WaitOptions {
|
|
77
|
+
readonly waitSeconds: number
|
|
78
|
+
readonly pollMs?: number
|
|
79
|
+
readonly painted?: PaintedPatterns
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function familyOf(gate: GateResult, commit: string | undefined, tip: string) {
|
|
83
|
+
return {
|
|
84
|
+
...(gate.reason !== undefined && { reason: gate.reason }),
|
|
85
|
+
isCovered: gate.isOk || (commit !== undefined && coversHead(commit, tip)),
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Both families at the tip through the lift's own gates, so a worker and the
|
|
91
|
+
* lift never disagree on a reason. The evidence is read by the caller, since a
|
|
92
|
+
* wait reads it once rather than once per poll.
|
|
93
|
+
*/
|
|
94
|
+
async function readAt(
|
|
95
|
+
api: ReviewStateApi,
|
|
96
|
+
hasChecklist: boolean,
|
|
97
|
+
painted: PaintedPatterns,
|
|
98
|
+
): Promise<ReviewStateRead> {
|
|
99
|
+
const [tip, reviews] = await Promise.all([api.readTip(), api.readReviews()])
|
|
100
|
+
if (tip === undefined || reviews === undefined) {
|
|
101
|
+
return { kind: 'refused', reason: 'gh-failed' }
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const listing: { reviews: readonly ReviewRow[] } = { reviews }
|
|
105
|
+
const scope = resolveReviewScope(listing)
|
|
106
|
+
const uiScope = resolveUiScope(listing)
|
|
107
|
+
const ui = await uiGate(api, painted, reviews, tip, hasChecklist)
|
|
108
|
+
if (ui.reason === 'gh-failed') return { kind: 'refused', reason: 'gh-failed' }
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
kind: 'read',
|
|
112
|
+
record: {
|
|
113
|
+
head: tip,
|
|
114
|
+
scope,
|
|
115
|
+
review: familyOf(reviewGate(reviews, tip), scope.commit, tip),
|
|
116
|
+
ui: {
|
|
117
|
+
...uiScope,
|
|
118
|
+
...familyOf(ui, uiScope.commit, tip),
|
|
119
|
+
isBound: hasChecklist,
|
|
120
|
+
...(ui.carriedFrom !== undefined && { carriedFrom: ui.carriedFrom }),
|
|
121
|
+
...(ui.paintedPaths !== undefined && { paintedPaths: ui.paintedPaths }),
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export async function readReviewState(
|
|
128
|
+
api: ReviewStateApi,
|
|
129
|
+
painted: PaintedPatterns = DEFAULT_PAINTED,
|
|
130
|
+
): Promise<ReviewStateRead> {
|
|
131
|
+
const evidence = await api.readEvidence()
|
|
132
|
+
if (evidence === undefined) return { kind: 'refused', reason: 'gh-failed' }
|
|
133
|
+
return readAt(api, evidence.hasChecklist, painted)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Holds until every bound family has a pass covering the tip, or the deadline
|
|
138
|
+
* passes. The tip is read again every poll, so a push during the wait moves
|
|
139
|
+
* what a pass has to cover. The evidence is read once, since a checklist
|
|
140
|
+
* appearing mid-wait is a ship-chain write the waiting worker made before it
|
|
141
|
+
* started.
|
|
142
|
+
*/
|
|
143
|
+
export async function waitForReviews(
|
|
144
|
+
api: ReviewStateApi,
|
|
145
|
+
opts: WaitOptions,
|
|
146
|
+
): Promise<ReviewWait> {
|
|
147
|
+
const pollMs = opts.pollMs ?? CHECKS_POLL_MS
|
|
148
|
+
const painted = opts.painted ?? DEFAULT_PAINTED
|
|
149
|
+
const started = api.now()
|
|
150
|
+
const deadline = started + opts.waitSeconds * 1000
|
|
151
|
+
const waited = () => Math.round((api.now() - started) / 1000)
|
|
152
|
+
|
|
153
|
+
const evidence = await api.readEvidence()
|
|
154
|
+
if (evidence === undefined) {
|
|
155
|
+
return { kind: 'refused', reason: 'gh-failed', waitedSeconds: waited() }
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
for (;;) {
|
|
159
|
+
const read = await readAt(api, evidence.hasChecklist, painted)
|
|
160
|
+
if (read.kind === 'refused') {
|
|
161
|
+
return { ...read, waitedSeconds: waited() }
|
|
162
|
+
}
|
|
163
|
+
const { record } = read
|
|
164
|
+
if (record.review.isCovered && record.ui.isCovered) {
|
|
165
|
+
return {
|
|
166
|
+
kind: 'read',
|
|
167
|
+
reason: 'covered',
|
|
168
|
+
record,
|
|
169
|
+
waitedSeconds: waited(),
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
if (api.now() + pollMs > deadline) {
|
|
173
|
+
return {
|
|
174
|
+
kind: 'read',
|
|
175
|
+
reason: 'timeout',
|
|
176
|
+
record,
|
|
177
|
+
waitedSeconds: waited(),
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
await api.sleep(pollMs)
|
|
181
|
+
}
|
|
182
|
+
}
|