@erclx/canon 5.27.0 → 5.29.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/draft-ready/SKILL.md +3 -1
- package/claude/skills/git-pr/references/evidence.md +1 -1
- package/claude/skills/review-address/REQUIREMENT.md +4 -2
- package/claude/skills/review-address/SKILL.md +16 -7
- package/claude/skills/review-address/references/rebase-conflicts.md +4 -2
- package/claude/skills/review-craft/references/ui.md +4 -4
- package/claude/skills/review-ui/REQUIREMENT.md +2 -2
- package/claude/skills/review-ui/SKILL.md +13 -11
- package/claude/skills/review-ui/references/driving.md +8 -8
- package/claude/skills/review-ui/references/post.md +19 -7
- package/claude/skills/review-ui/references/tick.md +22 -0
- package/claude/skills/role-orchestrator/REQUIREMENT.md +15 -15
- package/claude/skills/role-orchestrator/SKILL.md +24 -26
- package/claude/skills/role-orchestrator/references/orchestrator-dispatch.md +18 -16
- package/claude/skills/role-orchestrator/references/orchestrator-handback.md +12 -2
- package/claude/skills/role-orchestrator/references/orchestrator-inbound.md +1 -1
- package/claude/skills/role-orchestrator/references/orchestrator-launch.md +1 -1
- package/claude/skills/role-orchestrator/references/orchestrator-output.md +4 -4
- package/claude/skills/role-orchestrator/references/orchestrator-parked.md +5 -5
- package/claude/skills/role-orchestrator/references/orchestrator-poll.md +11 -2
- package/claude/skills/role-orchestrator/references/orchestrator-refill.md +6 -6
- package/claude/skills/role-orchestrator/references/orchestrator-sweep.md +2 -2
- package/claude/skills/role-orchestrator/scripts/poll.ts +304 -10
- package/claude/skills/role-planner/REQUIREMENT.md +1 -1
- package/claude/skills/role-planner/SKILL.md +2 -2
- package/claude/skills/role-reviewer/SKILL.md +1 -1
- package/claude/skills/role-worker/SKILL.md +3 -2
- package/claude/skills/ui-checklist/SKILL.md +1 -1
- package/docs/agents/canvas.md +12 -4
- package/docs/agents/gate.md +3 -1
- package/docs/agents/index.md +2 -1
- package/docs/agents/pr-evidence.md +15 -14
- package/docs/agents/pr-tick.md +72 -0
- package/docs/agents/sandbox.md +2 -2
- package/docs/agents/tasks-plans.md +1 -1
- package/docs/agents/tasks-validate.md +18 -18
- package/docs/agents/tasks.md +1 -1
- package/docs/workflow/operating-model.md +10 -15
- package/docs/workflow/parallel-features.md +7 -7
- package/package.json +1 -1
- package/src/audits/baseline.ts +1 -1
- package/src/audits/run.ts +1 -1
- package/src/canvas/capture.ts +1 -1
- package/src/canvas/client/app.tsx +105 -4
- package/src/canvas/client/inspector/field.tsx +61 -2
- package/src/canvas/client/inspector/fields.css +78 -0
- package/src/canvas/client/inspector/token-select.tsx +109 -0
- package/src/canvas/client/inspector/values.ts +74 -2
- package/src/canvas/client/inspector.tsx +134 -15
- package/src/canvas/client/keys.ts +71 -0
- package/src/canvas/client/pages-panel.tsx +5 -1
- package/src/canvas/client/panel-handle.tsx +90 -0
- package/src/canvas/client/selection.tsx +3 -1
- package/src/canvas/client/shell.css +194 -4
- package/src/canvas/client/state.ts +255 -11
- package/src/canvas/client/surface.tsx +231 -18
- package/src/canvas/client/tools.tsx +153 -0
- package/src/canvas/content.ts +66 -1
- package/src/canvas/edit.ts +207 -25
- package/src/canvas/history.ts +116 -0
- package/src/canvas/server.ts +119 -13
- package/src/canvas/tokens.ts +1 -1
- package/src/claude/seeds.ts +2 -2
- package/src/claude/skills-reach.ts +1 -1
- package/src/{exec.ts → cli/exec.ts} +2 -2
- package/src/{help.ts → cli/help.ts} +1 -1
- package/src/cli.ts +1 -1
- package/src/commands/audits.ts +9 -1
- package/src/commands/autoship.ts +1 -1
- package/src/commands/canvas.ts +2 -2
- package/src/commands/capture.ts +1 -1
- package/src/commands/census.ts +1 -1
- package/src/commands/claude.ts +5 -5
- package/src/commands/comments.ts +1 -1
- package/src/commands/context.ts +1 -1
- package/src/commands/demo.ts +9 -1
- package/src/commands/deps.ts +1 -1
- package/src/commands/design.ts +12 -4
- package/src/commands/docs.ts +2 -2
- package/src/commands/driver.ts +9 -1
- package/src/commands/feedback.ts +3 -3
- package/src/commands/gate.ts +10 -3
- package/src/commands/gov.ts +3 -3
- package/src/commands/indexes.ts +1 -1
- package/src/commands/init.ts +11 -3
- package/src/commands/intake.ts +1 -1
- package/src/commands/inventory.ts +9 -1
- package/src/commands/labels.ts +1 -1
- package/src/commands/markdown.ts +1 -1
- package/src/commands/migrate.ts +2 -2
- package/src/commands/pr.ts +324 -9
- package/src/commands/records.ts +1 -1
- package/src/commands/repo.ts +1 -1
- package/src/commands/sandbox.ts +11 -14
- package/src/commands/secrets.ts +9 -1
- package/src/commands/sessions.ts +1 -1
- package/src/commands/slides.ts +2 -2
- package/src/commands/standards.ts +1 -1
- package/src/commands/sync.ts +3 -3
- package/src/commands/targets.ts +1 -1
- package/src/commands/tasks.ts +20 -3
- package/src/commands/teach.ts +1 -1
- package/src/commands/tooling.ts +2 -2
- package/src/commands/transcripts.ts +2 -2
- package/src/commands/upgrade.ts +2 -2
- package/src/commands/wiki.ts +2 -2
- package/src/commands/worktrees.ts +1 -1
- package/src/context/architecture.ts +1 -1
- package/src/context/citations.ts +2 -2
- package/src/context/classify/extract.ts +1 -1
- package/src/context/folders.ts +2 -2
- package/src/context/requirements.ts +1 -1
- package/src/docs/read.ts +1 -1
- package/src/feedback/github.ts +1 -1
- package/src/gate/measures.ts +2 -2
- package/src/gate/sequencer.ts +55 -3
- package/src/gate/stages.ts +8 -0
- package/src/gate/suite-lock.ts +214 -0
- package/src/init/run.ts +1 -1
- package/src/intake/folder.ts +1 -1
- package/src/labels/map.ts +1 -1
- package/src/labels/phase.ts +3 -3
- package/src/migrate/record-layout.ts +1 -1
- package/src/migrate/records.ts +11 -11
- package/src/migrate/rename.ts +1 -1
- package/src/migrate/scratch-evidence.ts +1 -1
- package/src/migrate/surface-roots.ts +6 -6
- package/src/pr/evidence.ts +188 -16
- package/src/pr/tick.ts +56 -0
- package/src/process/harness.ts +1 -1
- package/src/records/backup.ts +1 -1
- package/src/records/ordinal.ts +1 -1
- package/src/records/prune.ts +1 -1
- package/src/records/size.ts +1 -1
- package/src/records/stale-canonical.ts +1 -1
- package/src/records/stale.ts +1 -1
- package/src/records/validate.ts +2 -2
- package/src/{project-root.ts → roots/project.ts} +2 -2
- package/src/{record-root.ts → roots/record.ts} +1 -1
- package/src/sandbox/coverage.ts +11 -12
- package/src/sandbox/equivalence.ts +57 -26
- package/src/sandbox/headless/run.ts +1 -1
- package/src/sandbox/hooks.ts +1 -1
- package/src/sandbox/provision.ts +79 -29
- package/src/sandbox/scenario.ts +588 -0
- package/src/serve/report.ts +1 -1
- package/src/sessions/live.ts +4 -1
- package/src/sessions/transfer/bundle.ts +1 -1
- package/src/slides/decks.ts +1 -1
- package/src/standards/read.ts +1 -1
- package/src/sync/engine.ts +2 -2
- package/src/sync/git.ts +1 -1
- package/src/sync/layout.ts +2 -2
- package/src/sync/stamp.ts +1 -1
- package/src/sync/workflow.ts +1 -1
- package/src/targets/validate.ts +2 -2
- package/src/tasks/answers.ts +1 -1
- package/src/tasks/archive.ts +1 -1
- package/src/tasks/label.ts +1 -1
- package/src/tasks/reach.ts +1 -1
- package/src/tasks/validate.ts +38 -14
- package/src/teach/nav.ts +1 -1
- package/src/teach/workspace.ts +1 -1
- package/src/tooling/inject.ts +2 -2
- package/src/tooling/read.ts +1 -1
- package/src/tooling/scan.ts +1 -1
- package/src/tooling/verify-stack.ts +1 -1
- package/src/version/installed.ts +1 -1
- package/src/version/skew.ts +1 -1
- package/standards/board.md +3 -3
- package/standards/plan.md +13 -7
- package/tsconfig.json +1 -1
- /package/src/{cli-run.ts → cli/run.ts} +0 -0
- /package/src/{ui.ts → cli/ui.ts} +0 -0
- /package/src/{legacy-path.ts → roots/legacy.ts} +0 -0
- /package/src/{surface-root.ts → roots/surface.ts} +0 -0
package/src/sync/workflow.ts
CHANGED
package/src/targets/validate.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { statSync } from 'node:fs'
|
|
2
2
|
import { resolve } from 'node:path'
|
|
3
|
-
import { checkoutMismatchWarning } from '@/project
|
|
4
|
-
import { logError, logWarn, outro } from '@/ui'
|
|
3
|
+
import { checkoutMismatchWarning } from '@/roots/project'
|
|
4
|
+
import { logError, logWarn, outro } from '@/cli/ui'
|
|
5
5
|
|
|
6
6
|
export function isDirectory(path: string): boolean {
|
|
7
7
|
try {
|
package/src/tasks/answers.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { existsSync } from 'node:fs'
|
|
|
2
2
|
import { readFile } from 'node:fs/promises'
|
|
3
3
|
import { isAbsolute, relative, resolve } from 'node:path'
|
|
4
4
|
import { isUnder } from '@/tasks/paths'
|
|
5
|
-
import { recordDir, recordDirs } from '@/record
|
|
5
|
+
import { recordDir, recordDirs } from '@/roots/record'
|
|
6
6
|
import {
|
|
7
7
|
hasStagedBatches,
|
|
8
8
|
normalizeOperatorCall,
|
package/src/tasks/archive.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { mkdir, readdir, readFile, rename, writeFile } from 'node:fs/promises'
|
|
|
3
3
|
import { basename, dirname, join, relative, resolve, sep } from 'node:path'
|
|
4
4
|
import { regenOne } from '@/indexes/regen'
|
|
5
5
|
import { isUnder } from '@/tasks/paths'
|
|
6
|
-
import { recordDir, recordDirs } from '@/record
|
|
6
|
+
import { recordDir, recordDirs } from '@/roots/record'
|
|
7
7
|
|
|
8
8
|
const TASKS = 'tasks'
|
|
9
9
|
const PLANS = 'plans'
|
package/src/tasks/label.ts
CHANGED
package/src/tasks/reach.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { existsSync } from 'node:fs'
|
|
|
2
2
|
import { readdir, readFile } from 'node:fs/promises'
|
|
3
3
|
import { basename, join } from 'node:path'
|
|
4
4
|
import { listChangedFiles, resolveBaseRef } from '@/git/files'
|
|
5
|
-
import { recordDir } from '@/record
|
|
5
|
+
import { recordDir } from '@/roots/record'
|
|
6
6
|
import { splitPlanSections } from '@/records/validate'
|
|
7
7
|
import { type AnswersRefused, resolvePlanReference } from '@/tasks/answers'
|
|
8
8
|
import { orderingPath, readBoard } from '@/tasks/validate'
|
package/src/tasks/validate.ts
CHANGED
|
@@ -50,7 +50,6 @@ export const FINDING_KINDS = [
|
|
|
50
50
|
'row-misordered',
|
|
51
51
|
'row-unranked',
|
|
52
52
|
'touches-unstated',
|
|
53
|
-
'touches-collided',
|
|
54
53
|
'blocker-settled',
|
|
55
54
|
'blocker-unresolved',
|
|
56
55
|
'blocker-declined',
|
|
@@ -89,6 +88,20 @@ export interface FolderClaim {
|
|
|
89
88
|
readonly message: string
|
|
90
89
|
}
|
|
91
90
|
|
|
91
|
+
/**
|
|
92
|
+
* A path two `## Run now` rows both name in their `Touches` cells. A shared
|
|
93
|
+
* file is no hold, since the dispatch gate starts both rows and the branch
|
|
94
|
+
* merging second rebases, so the pair reports beside the findings and moves no
|
|
95
|
+
* exit code. It stays listed because merge order reads it, and because it is
|
|
96
|
+
* the candidate list a dispatcher reads for the dependency, contract,
|
|
97
|
+
* relocation, and sweep holds a `Touches` cell cannot state.
|
|
98
|
+
*/
|
|
99
|
+
export interface SharedFiles {
|
|
100
|
+
readonly group: BoardGroup
|
|
101
|
+
readonly subject: string
|
|
102
|
+
readonly message: string
|
|
103
|
+
}
|
|
104
|
+
|
|
92
105
|
/**
|
|
93
106
|
* A code span in a board cell that the markdown preview cannot break, so it
|
|
94
107
|
* pushes the table past the pane and makes it scroll sideways. Either the
|
|
@@ -151,6 +164,7 @@ export interface ValidateReport {
|
|
|
151
164
|
readonly findings: readonly Finding[]
|
|
152
165
|
readonly untested: readonly Untested[]
|
|
153
166
|
readonly claims: readonly FolderClaim[]
|
|
167
|
+
readonly shared: readonly SharedFiles[]
|
|
154
168
|
readonly wide: readonly WideToken[]
|
|
155
169
|
readonly unplaced: readonly Unplaced[]
|
|
156
170
|
}
|
|
@@ -892,17 +906,16 @@ function planPath(target: string, dir: string, root: string): string {
|
|
|
892
906
|
}
|
|
893
907
|
|
|
894
908
|
/**
|
|
895
|
-
*
|
|
896
|
-
*
|
|
897
|
-
*
|
|
909
|
+
* Reports a `## Run now` row stating no file set. The `Touches` column is the
|
|
910
|
+
* only place a row's set is written down, so a row stating none drops out of
|
|
911
|
+
* every pair `checkSharedFiles` reads, and its merge order and reach go unread.
|
|
898
912
|
*/
|
|
899
|
-
function
|
|
913
|
+
function checkTouches(rows: readonly BoardRow[]): Finding[] {
|
|
900
914
|
const findings: Finding[] = []
|
|
901
|
-
const ready = rows.filter((row) => row.group === 'Run now')
|
|
902
915
|
|
|
903
|
-
for (const row of
|
|
904
|
-
// An absent column and an unreadable one both leave the row
|
|
905
|
-
//
|
|
916
|
+
for (const row of rows.filter((candidate) => candidate.group === 'Run now')) {
|
|
917
|
+
// An absent column and an unreadable one both leave the row out of every
|
|
918
|
+
// pair, so reporting only the second would pass a board whose
|
|
906
919
|
// `## Run now` table declares no file set at all.
|
|
907
920
|
if (!row.touches || row.touches.length === 0) {
|
|
908
921
|
findings.push({
|
|
@@ -914,6 +927,17 @@ function checkCollisions(rows: readonly BoardRow[]): Finding[] {
|
|
|
914
927
|
}
|
|
915
928
|
}
|
|
916
929
|
|
|
930
|
+
return findings
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
/**
|
|
934
|
+
* Lists every path two `## Run now` rows both name, one entry per pair of
|
|
935
|
+
* rows. It reports rather than fails, since sharing a file holds nothing.
|
|
936
|
+
*/
|
|
937
|
+
function checkSharedFiles(rows: readonly BoardRow[]): SharedFiles[] {
|
|
938
|
+
const pairs: SharedFiles[] = []
|
|
939
|
+
const ready = rows.filter((row) => row.group === 'Run now')
|
|
940
|
+
|
|
917
941
|
for (let i = 0; i < ready.length; i += 1) {
|
|
918
942
|
for (let j = i + 1; j < ready.length; j += 1) {
|
|
919
943
|
const left = ready[i]
|
|
@@ -929,8 +953,7 @@ function checkCollisions(rows: readonly BoardRow[]): Finding[] {
|
|
|
929
953
|
|
|
930
954
|
if (shared.length === 0) continue
|
|
931
955
|
|
|
932
|
-
|
|
933
|
-
kind: 'touches-collided',
|
|
956
|
+
pairs.push({
|
|
934
957
|
group: 'Run now',
|
|
935
958
|
subject: `${subjectOf(left)} and ${subjectOf(right)}`,
|
|
936
959
|
message: `both touch ${joinShared(shared)}.`,
|
|
@@ -938,7 +961,7 @@ function checkCollisions(rows: readonly BoardRow[]): Finding[] {
|
|
|
938
961
|
}
|
|
939
962
|
}
|
|
940
963
|
|
|
941
|
-
return
|
|
964
|
+
return pairs
|
|
942
965
|
}
|
|
943
966
|
|
|
944
967
|
function subjectOf(row: BoardRow): string {
|
|
@@ -982,7 +1005,7 @@ function joinShared(clauses: readonly string[]): string {
|
|
|
982
1005
|
* the row does rewrite the directory, so this states the reach rather than
|
|
983
1006
|
* calling it a defect.
|
|
984
1007
|
*
|
|
985
|
-
* The scan takes `## Run now` alone, where `
|
|
1008
|
+
* The scan takes `## Run now` alone, where `checkSharedFiles` takes it. A cell in
|
|
986
1009
|
* another group describes work nobody has planned, so it is written as a
|
|
987
1010
|
* sentence and rewritten at planning time, and a claim read off one reports on
|
|
988
1011
|
* prose rather than on a file set. That is the shape that teaches a reader to
|
|
@@ -1377,7 +1400,7 @@ export async function validateBoard(
|
|
|
1377
1400
|
...checkPlans(rows, dir, root),
|
|
1378
1401
|
...(await checkPlanAgreement(rows, dir, root)),
|
|
1379
1402
|
...(await checkGroupClaims(rows, dir, root)),
|
|
1380
|
-
...
|
|
1403
|
+
...checkTouches(rows),
|
|
1381
1404
|
...checkOrdering(rows),
|
|
1382
1405
|
...parked.findings,
|
|
1383
1406
|
]
|
|
@@ -1391,6 +1414,7 @@ export async function validateBoard(
|
|
|
1391
1414
|
findings,
|
|
1392
1415
|
untested: parked.untested,
|
|
1393
1416
|
claims: checkFolderClaims(rows, root),
|
|
1417
|
+
shared: checkSharedFiles(rows),
|
|
1394
1418
|
wide: checkWideTokens(rows),
|
|
1395
1419
|
unplaced: checkUnplaced(rows, backlog, stems),
|
|
1396
1420
|
}
|
package/src/teach/nav.ts
CHANGED
|
@@ -9,7 +9,7 @@ import { buildDesignCss } from '@/design/css'
|
|
|
9
9
|
import { FAVICON_COLORS, faviconLink, renderFavicon } from '@/design/favicon'
|
|
10
10
|
import { stripFrontmatter } from '@/markdown/frontmatter'
|
|
11
11
|
import { parseFrontmatter, readField } from '@/indexes/frontmatter'
|
|
12
|
-
import { PROJECT_ROOT } from '@/project
|
|
12
|
+
import { PROJECT_ROOT } from '@/roots/project'
|
|
13
13
|
import { compileScript } from '@/teach/browser/compile'
|
|
14
14
|
import { dismiss } from '@/teach/browser/dismiss'
|
|
15
15
|
import { focusLine } from '@/teach/browser/focus-line'
|
package/src/teach/workspace.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { TEACH_STYLESHEET_COMPONENTS } from '@/design/components'
|
|
|
5
5
|
import { buildDesignCss } from '@/design/css'
|
|
6
6
|
import { parseFrontmatter, readField } from '@/indexes/frontmatter'
|
|
7
7
|
import { type BodyLine, bodyLines } from '@/markdown/scan'
|
|
8
|
-
import { recordDir } from '@/record
|
|
8
|
+
import { recordDir } from '@/roots/record'
|
|
9
9
|
import { TEACH_FONT_FACES } from '@/teach/fonts'
|
|
10
10
|
import { writeIfChanged } from '@/teach/write-if-changed'
|
|
11
11
|
|
package/src/tooling/inject.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { dirname, join, relative } from 'node:path'
|
|
|
4
4
|
import { $ } from 'bun'
|
|
5
5
|
import { copyPreservingMode } from '@/sync/copy'
|
|
6
6
|
import { rewritesOnInstall, stripSeedMarker } from '@/tooling/seed-marker'
|
|
7
|
-
import { resolveSurfacePath } from '@/surface
|
|
7
|
+
import { resolveSurfacePath } from '@/roots/surface'
|
|
8
8
|
import { mergeSections, pruneSections } from '@/tooling/gitignore'
|
|
9
9
|
import {
|
|
10
10
|
ancestorsFirst,
|
|
@@ -27,7 +27,7 @@ import {
|
|
|
27
27
|
SUBFOLDER_SPELL_CONFIG,
|
|
28
28
|
subfolderSpellConfig,
|
|
29
29
|
} from '@/tooling/subfolder'
|
|
30
|
-
import { logAdd, logRemove, logStep, logWarn } from '@/ui'
|
|
30
|
+
import { logAdd, logRemove, logStep, logWarn } from '@/cli/ui'
|
|
31
31
|
|
|
32
32
|
export async function injectConfigs(
|
|
33
33
|
chain: readonly Manifest[],
|
package/src/tooling/read.ts
CHANGED
package/src/tooling/scan.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from 'node:fs'
|
|
2
2
|
import { join } from 'node:path'
|
|
3
|
-
import { resolveSurfacePath } from '@/surface
|
|
3
|
+
import { resolveSurfacePath } from '@/roots/surface'
|
|
4
4
|
import { mergeSections } from '@/tooling/gitignore'
|
|
5
5
|
import {
|
|
6
6
|
ancestorsFirst,
|
|
@@ -2,7 +2,7 @@ import { existsSync, mkdirSync, readFileSync, rmSync } from 'node:fs'
|
|
|
2
2
|
import { join } from 'node:path'
|
|
3
3
|
import { execa, type Options } from 'execa'
|
|
4
4
|
import { isStackExcluded, loadManifest } from '@/tooling/manifest'
|
|
5
|
-
import { intro, logError, logInfo, logStep, logWarn, outro } from '@/ui'
|
|
5
|
+
import { intro, logError, logInfo, logStep, logWarn, outro } from '@/cli/ui'
|
|
6
6
|
|
|
7
7
|
const SCREENSHOT_TIMEOUT_MS = 60_000
|
|
8
8
|
const TAIL_LINES = 20
|
package/src/version/installed.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs'
|
|
2
2
|
import { join } from 'node:path'
|
|
3
|
-
import { PROJECT_ROOT } from '@/project
|
|
3
|
+
import { PROJECT_ROOT } from '@/roots/project'
|
|
4
4
|
|
|
5
5
|
/** What a reader prints in place of a field the manifest did not carry. */
|
|
6
6
|
export const UNKNOWN_LABEL = 'unknown'
|
package/src/version/skew.ts
CHANGED
package/standards/board.md
CHANGED
|
@@ -21,7 +21,7 @@ Does not govern:
|
|
|
21
21
|
|
|
22
22
|
A board works when a session reading it alone can answer:
|
|
23
23
|
|
|
24
|
-
- Which task can a worker be handed now, and which files
|
|
24
|
+
- Which task can a worker be handed now, and which files does it share with work already running?
|
|
25
25
|
- What is each blocked task waiting on, and what would clear it?
|
|
26
26
|
- Which task gets planned next, and what is it ranked against?
|
|
27
27
|
|
|
@@ -49,7 +49,7 @@ Group tasks by readiness rather than by status, one row per task, under the colu
|
|
|
49
49
|
|
|
50
50
|
Readiness is three groups under fixed headings, `## Run now`, `## Up next`, and `## Needs a plan`, in that order. The names are the contract rather than a suggestion, because a board grouped by readiness under names of its own satisfies every other rule here and still reads as empty to anything counting rows under a heading. Add no fourth group. A task belongs to exactly one, and the tests are read in order.
|
|
51
51
|
|
|
52
|
-
- `## Run now`: a written plan covers every open outcome, and the task carries no reason it cannot start. A collision
|
|
52
|
+
- `## Run now`: a written plan covers every open outcome, and the task carries no reason it cannot start. A collision with running work is one such reason only where no merge settles it, being a contract, a relocation, or a sweep. A shared file alone is none, since the branch merging second rebases. The `Touches` column states the set to read. A worker is handed a task from this group alone.
|
|
53
53
|
- `## Up next`: a written plan exists, and the task carries a stated reason it cannot start. The `Waiting on` column names that reason.
|
|
54
54
|
- `## Needs a plan`: everything else. The task has no plan, or the plan it carries no longer describes the work.
|
|
55
55
|
|
|
@@ -69,7 +69,7 @@ Position alone carries it, and no rank column exists. A number beside each row i
|
|
|
69
69
|
|
|
70
70
|
The `Waiting on` column under `## Up next` carries that reason in one of three forms. `## Needs a plan` states no file set at all, because a task with no plan has no bounded one to state. A group with no rows keeps its heading and its header row.
|
|
71
71
|
|
|
72
|
-
Under `## Up next` a collision names the file held by the task already running, a sibling task names that task, and an external condition names both the condition and what would satisfy it. Naming what would satisfy it is what separates a blocked row from one nobody has examined, so a cell stating a condition with no way out of it fails the test. The header text is the contract the way the group names are, because anything reading the cell resolves the column by header rather than by position.
|
|
72
|
+
Under `## Up next` a collision names the file held by the task already running and which hold it is: a contract, a relocation, or a sweep. A sibling task names that task, and an external condition names both the condition and what would satisfy it. Naming what would satisfy it is what separates a blocked row from one nobody has examined, so a cell stating a condition with no way out of it fails the test. The header text is the contract the way the group names are, because anything reading the cell resolves the column by header rather than by position.
|
|
73
73
|
|
|
74
74
|
Under `## Needs a plan` the cell carries two halves and each takes one clause: what the task needs before it can be planned, then why it sits at this position. The second clause is comparative, and it names what the row is ranked against in one of two forms: a sibling row, or the class of rows it sits in. The row `leads`, `heads`, `opens`, `closes`, `trails`, `precedes`, `follows`, or `outranks` whichever it names, or `sits under`, `above`, or `below` it. Either form puts something other than this row in the clause, which is what makes the position readable by anyone but its author.
|
|
75
75
|
|
package/standards/plan.md
CHANGED
|
@@ -18,6 +18,8 @@ Applies to a feature plan at `.canon/plans/feature-<slug>.md`. One file holds on
|
|
|
18
18
|
|
|
19
19
|
The folder is gitignored, and backed by `canon records push` wherever a records remote is configured. Nothing backs a plan deleted before a push, which is why the archive step is a move rather than a cleanup.
|
|
20
20
|
|
|
21
|
+
<!-- canon-length-exempt: the constraint forms and the answer contract share one reader, and a split into a second standard is queued as its own change -->
|
|
22
|
+
|
|
21
23
|
## Scope
|
|
22
24
|
|
|
23
25
|
Governs a feature plan under `.canon/plans/feature-<slug>.md`: the filename, the required sections, what each holds, and the suggested-and-answer contract.
|
|
@@ -40,7 +42,7 @@ A plan works when a session holding none of the conversation that produced it ca
|
|
|
40
42
|
- What is being built, and which files does it change?
|
|
41
43
|
- Which decisions are already settled, and which are still open?
|
|
42
44
|
- For each open decision, what happens when nobody answers it?
|
|
43
|
-
- What is likely to go wrong,
|
|
45
|
+
- What is likely to go wrong, what does the work share with something already in flight, and what must it wait on?
|
|
44
46
|
- How is each outcome proven, and which input would break it?
|
|
45
47
|
|
|
46
48
|
A plan failing these is non-conforming even when it satisfies every shape rule below.
|
|
@@ -81,20 +83,22 @@ The document opens with `# Feature: <short title>` and one paragraph stating wha
|
|
|
81
83
|
|
|
82
84
|
### Constraints
|
|
83
85
|
|
|
84
|
-
A
|
|
86
|
+
Work in flight reaches a plan as a hold or as a shared path. A hold leaves a surface alone and says which of two acts it forbids, rather than leaving the executing session to guess. Neither act is forbidden over a shared path.
|
|
85
87
|
|
|
88
|
+
- Write a hold only for a dependency, a contract the other track consumes, a relocation, or a sweep, which a rebase cannot settle mechanically. A bare shared file is none of these, and forbidding it sends a worker to concede a file the change needs.
|
|
86
89
|
- Forbid conforming that surface to whatever shape the change introduces, which keeps the branch to one concern.
|
|
87
90
|
- Never forbid retargeting a pointer the change breaks. A rename, a split, or a deletion that leaves a citation behind ships a dangling reference, so repairing it is required work rather than scope creep.
|
|
88
|
-
- Decide both acts for every surface
|
|
91
|
+
- Decide both acts for every surface a hold names, not for one file among its siblings.
|
|
92
|
+
- Write a shared path as the track, the path, and the branch that rebases, being the one merging second. The executing session edits the file and keeps both sides on the rebase.
|
|
89
93
|
|
|
90
|
-
A constraint measured against work in flight expires when that work merges, so stamp the block with what it was measured against.
|
|
94
|
+
A constraint measured against work in flight expires when that work merges, as a shared-path line does, so stamp the block with what it was measured against.
|
|
91
95
|
|
|
92
96
|
- Give the stamp its own leading bullet, written as Measured against `<commit>` on <YYYY-MM-DD>. One stamp covers the whole block.
|
|
93
97
|
- Stamp the commit rather than the track names or a date alone. The constraint already names each track, and a date does not separate two plans written on either side of a merge.
|
|
94
|
-
- Re-test a stamped constraint before honoring it with `git fetch origin main --quiet && git log <commit>..origin/main --oneline -- <the paths the constraint names>`. Any commit it reports means the track landed and the constraint is dead. Fetch
|
|
98
|
+
- Re-test a stamped constraint before honoring it with `git fetch origin main --quiet && git log <commit>..origin/main --oneline -- <the paths the constraint names>`. Any commit it reports means the track landed and the constraint is dead. Fetch and scope the log to the paths in the same command, since a stale ref reads a dead constraint as live and a squashed subject names no track.
|
|
95
99
|
- Read an unstamped constraint as unverified rather than as live, and confirm it against the open pull request list for the named paths.
|
|
96
100
|
|
|
97
|
-
A constraint naming a `.canon/ready/` folder is a third shape beside the
|
|
101
|
+
A constraint naming a `.canon/ready/` folder is a third shape beside the hold and the shared path. It makes that folder's files the verbatim source for the paths this plan's `**Files to touch:**` lists, per `ready.md`, so the executing session copies those paths rather than authoring them.
|
|
98
102
|
|
|
99
103
|
### Verification
|
|
100
104
|
|
|
@@ -106,6 +110,7 @@ A constraint naming a `.canon/ready/` folder is a third shape beside the two abo
|
|
|
106
110
|
|
|
107
111
|
- Name the collision rather than the category. A risk a reader cannot act on is padding.
|
|
108
112
|
- Where the work establishes a resource with more than one consumer, list the consumers and mark each one read or write. A policy stated over that resource has to hold for the writers and not only for the consumer that prompted it.
|
|
113
|
+
- Where the work changes a contract another track consumes, such as an exported signature, a verb's flags or record, or a config key, name the contract and list each consumer. The dispatcher reads this bullet for the contract hold, so a change left out reaches it as a clean shared file.
|
|
109
114
|
|
|
110
115
|
### Review focus
|
|
111
116
|
|
|
@@ -161,7 +166,8 @@ This contract inverts the one an intake folder keeps, where an empty slot means
|
|
|
161
166
|
**Constraints:**
|
|
162
167
|
|
|
163
168
|
- Measured against `<commit>` on <YYYY-MM-DD>
|
|
164
|
-
- <the work in flight, its file set, and which of the two acts the constraint forbids>
|
|
169
|
+
- <a hold: the work in flight, its file set, and which of the two acts the constraint forbids>
|
|
170
|
+
- <a shared path: the work in flight, the path, and which branch rebases>
|
|
165
171
|
- <durable rule the work must respect>
|
|
166
172
|
|
|
167
173
|
**Files to touch:**
|
package/tsconfig.json
CHANGED
|
File without changes
|
/package/src/{ui.ts → cli/ui.ts}
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|