@erclx/canon 5.31.0 → 5.32.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 +1 -1
- package/claude/.claude-plugin/plugin.json +1 -1
- package/claude/skills/api-design/references/adopted.md +1 -1
- package/claude/skills/auto-ship/REQUIREMENT.md +4 -16
- package/claude/skills/auto-ship/SKILL.md +13 -58
- package/claude/skills/auto-ship/references/draft-mark.md +2 -2
- package/claude/skills/auto-ship/references/failure-recovery.md +1 -5
- package/claude/skills/auto-ship/references/tier-failures.md +1 -1
- package/claude/skills/auto-ship/references/ui-checklist.md +2 -2
- package/claude/skills/auto-ship/references/verb-absent.md +3 -27
- package/claude/skills/backlog-triage/SKILL.md +1 -1
- package/claude/skills/canon-cli/REQUIREMENT.md +4 -6
- package/claude/skills/canon-cli/SKILL.md +2 -2
- package/claude/skills/canon-feedback/REQUIREMENT.md +1 -1
- package/claude/skills/canon-feedback/SKILL.md +10 -13
- package/claude/skills/canon-feedback-triage/REQUIREMENT.md +13 -14
- package/claude/skills/canon-feedback-triage/SKILL.md +49 -30
- package/claude/skills/canvas/REQUIREMENT.md +3 -4
- package/claude/skills/canvas/SKILL.md +5 -5
- package/{standards → claude/skills/canvas/references}/canvas.md +12 -1
- package/claude/skills/ci-workflow/REQUIREMENT.md +0 -1
- package/claude/skills/context-fold/REQUIREMENT.md +8 -20
- package/claude/skills/context-fold/SKILL.md +20 -44
- package/claude/skills/context-fold/references/classify.md +7 -8
- package/claude/skills/context-fold/references/completion-report.md +2 -6
- package/claude/skills/context-fold/references/context-refresh.md +5 -15
- package/claude/skills/context-fold/references/promoted-pages.md +3 -3
- package/claude/skills/context-fold/references/receipt-sweep.md +6 -17
- package/claude/skills/context-fold/references/unusable-baseline.md +4 -6
- package/claude/skills/create-rule/REQUIREMENT.md +0 -1
- package/claude/skills/create-skill/REQUIREMENT.md +6 -4
- package/claude/skills/create-skill/SKILL.md +8 -7
- package/{standards → claude/skills/create-skill/references}/skill-paths.md +1 -0
- package/claude/skills/decision-escalate/REQUIREMENT.md +1 -1
- package/claude/skills/decision-escalate/SKILL.md +1 -1
- package/claude/skills/design-taste/REQUIREMENT.md +0 -2
- package/claude/skills/design-taste/SKILL.md +1 -3
- package/claude/skills/design-taste/references/vocabulary.md +1 -1
- package/claude/skills/docs-sync/REQUIREMENT.md +3 -3
- package/claude/skills/document-health/REQUIREMENT.md +2 -2
- package/claude/skills/document-health/SKILL.md +4 -5
- package/claude/skills/document-health/references/axes.md +4 -6
- package/claude/skills/draft-and-pick/REQUIREMENT.md +16 -14
- package/claude/skills/draft-and-pick/SKILL.md +40 -32
- package/claude/skills/draft-and-pick/references/live-arms.md +9 -9
- package/claude/skills/draft-doc/REQUIREMENT.md +5 -5
- package/claude/skills/draft-doc/SKILL.md +9 -10
- package/claude/skills/draft-doc/references/requirements.md +61 -0
- package/claude/skills/draft-doc/references/wiki-kind.md +53 -0
- package/claude/skills/draft-doc/references/wiki.md +50 -30
- package/claude/skills/draft-figure/REQUIREMENT.md +2 -2
- package/claude/skills/draft-figure/SKILL.md +4 -4
- package/claude/skills/draft-figure/references/architecture-set.md +4 -4
- package/claude/skills/draft-figure/references/hand-drawn.md +1 -1
- package/claude/skills/draft-figure/references/mermaid-path.md +55 -0
- package/claude/skills/draft-figure/references/mermaid.md +43 -34
- package/claude/skills/draft-identity/REQUIREMENT.md +1 -1
- package/claude/skills/draft-identity/SKILL.md +9 -7
- package/claude/skills/draft-ready/REQUIREMENT.md +1 -1
- package/claude/skills/draft-ready/SKILL.md +6 -6
- package/claude/skills/draft-ready/references/assembly.md +1 -1
- package/{standards → claude/skills/draft-ready/references}/ready.md +13 -5
- package/claude/skills/git-issue/references/issue.md +3 -3
- package/claude/skills/git-ship/REQUIREMENT.md +0 -2
- package/claude/skills/git-ship/SKILL.md +16 -24
- package/claude/skills/plan-feature/SKILL.md +2 -6
- package/{standards → claude/skills/plan-feature/references}/plan.md +9 -10
- package/claude/skills/plan-groundwork/SKILL.md +5 -6
- package/{standards → claude/skills/plan-groundwork/references}/groundwork.md +17 -4
- package/claude/skills/plan-intake/SKILL.md +2 -3
- package/{standards → claude/skills/plan-intake/references}/intake.md +17 -2
- package/claude/skills/plan-intake-answer/REQUIREMENT.md +1 -1
- package/claude/skills/plan-intake-answer/SKILL.md +1 -1
- package/claude/skills/review-address/SKILL.md +1 -1
- package/claude/skills/review-craft/REQUIREMENT.md +3 -3
- package/claude/skills/review-craft/SKILL.md +2 -2
- package/claude/skills/review-craft/references/ui.md +4 -4
- package/claude/skills/review-pr/REQUIREMENT.md +1 -1
- package/claude/skills/review-pr/SKILL.md +5 -7
- package/claude/skills/review-ui/REQUIREMENT.md +0 -1
- package/claude/skills/review-ui/SKILL.md +1 -1
- package/claude/skills/role-orchestrator/REQUIREMENT.md +0 -3
- package/claude/skills/role-orchestrator/SKILL.md +8 -8
- package/claude/skills/role-orchestrator/references/orchestrator-dispatch.md +3 -3
- package/claude/skills/role-orchestrator/references/orchestrator-handoff.md +7 -10
- package/claude/skills/role-orchestrator/references/orchestrator-parked.md +2 -2
- package/claude/skills/role-orchestrator/references/orchestrator-refill.md +0 -3
- package/claude/skills/role-orchestrator/references/orchestrator-resume.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 +2 -2
- package/claude/skills/session-compact/REQUIREMENT.md +1 -4
- package/claude/skills/session-compact/SKILL.md +3 -13
- package/claude/skills/session-compact/references/handoff-note.md +2 -2
- package/claude/skills/session-map/REQUIREMENT.md +1 -9
- package/claude/skills/session-map/SKILL.md +8 -16
- package/{standards → claude/skills/session-map/references}/session.md +17 -11
- package/claude/skills/session-relay/REQUIREMENT.md +1 -1
- package/claude/skills/session-resume/REQUIREMENT.md +4 -6
- package/claude/skills/session-resume/SKILL.md +3 -9
- package/claude/skills/session-worktree/SKILL.md +1 -1
- package/claude/skills/session-worktree/references/main-root-writes.md +2 -2
- package/claude/skills/systematic-debugging/REQUIREMENT.md +0 -1
- package/claude/skills/target-check/REQUIREMENT.md +3 -3
- package/claude/skills/target-check/SKILL.md +1 -1
- package/claude/skills/target-check/references/domains.md +1 -1
- package/claude/skills/target-setup/REQUIREMENT.md +4 -4
- package/claude/skills/target-setup/SKILL.md +3 -3
- package/claude/skills/target-setup/references/detect.md +1 -1
- package/claude/skills/task-board/SKILL.md +4 -4
- package/claude/skills/task-board/references/archive.md +1 -1
- package/{standards → claude/skills/task-board/references}/board.md +9 -0
- package/claude/skills/task-board/references/decline.md +1 -1
- package/{standards → claude/skills/task-board/references}/tasks.md +13 -8
- package/claude/skills/teach-workspace/SKILL.md +1 -1
- package/claude/skills/teach-workspace/references/revisit.md +1 -1
- package/{standards → claude/skills/teach-workspace/references}/teach.md +17 -5
- package/claude/skills/ux-audit/REQUIREMENT.md +2 -2
- package/claude/skills/ux-audit/SKILL.md +2 -3
- package/claude/skills/write-human/REQUIREMENT.md +0 -1
- package/claude/skills/write-human/SKILL.md +1 -1
- package/docs/agents/audits.md +1 -1
- package/docs/agents/canvas.md +1 -1
- package/docs/agents/commands.md +1 -6
- package/docs/agents/context-audit-checks.md +9 -23
- package/docs/agents/context-audit.md +7 -15
- package/docs/agents/context-classify.md +5 -5
- package/docs/agents/counts.md +5 -5
- package/docs/agents/drift-surfaces.md +2 -2
- package/docs/agents/index.md +3 -5
- package/docs/agents/install-and-sync.md +6 -6
- package/docs/agents/intake.md +1 -1
- package/docs/agents/records-stale.md +5 -5
- package/docs/agents/records-validate.md +1 -1
- package/docs/agents/records.md +2 -2
- package/docs/agents/restated.md +1 -1
- package/docs/agents/rule-citations.md +1 -1
- package/docs/agents/skills-audit.md +2 -2
- package/docs/agents/state-scoped-risk.md +2 -2
- package/docs/agents/superseded.md +2 -2
- package/docs/agents/tasks-plans.md +1 -1
- package/docs/agents/tasks-validate.md +1 -1
- package/docs/agents/tasks.md +1 -1
- package/docs/agents/teach.md +1 -1
- package/docs/agents/upstream.md +26 -2
- package/docs/target/migrations.md +24 -0
- package/docs/target/projects.md +5 -7
- package/docs/target/scenarios.md +2 -2
- package/docs/target/sync.md +3 -3
- package/docs/workflow/ai-workflow.md +7 -38
- package/docs/workflow/index.md +1 -1
- package/docs/workflow/operating-model.md +8 -8
- package/docs/workflow/parallel-features.md +7 -7
- package/docs/workflow/skill-map-on-request.md +12 -15
- package/docs/workflow/skill-map.md +20 -32
- package/docs/workflow/visual-design-workflow.md +11 -14
- package/governance/renames.toml +0 -19
- package/governance/rules/canon/605-worktrees.md +1 -1
- package/governance/rules/{standards → canon}/plan.md +4 -5
- package/governance/rules/canon/tasks.md +18 -0
- package/governance/stacks/base.toml +1 -1
- package/package.json +1 -1
- package/scripts/core/install-check.sh +1 -1
- package/scripts/core/regen-web-previews.ts +1 -1
- package/src/audits/catalog.ts +5 -97
- package/src/claude/cases/authoring.ts +0 -18
- package/src/claude/cases/misc.ts +0 -13
- package/src/claude/cases/setup.ts +3 -20
- package/src/claude/cases/workflow.ts +0 -33
- package/src/claude/seeds.ts +1 -7
- package/src/claude/skills-audit.ts +1 -1
- package/src/cli/help.ts +1 -4
- package/src/cli.ts +0 -4
- package/src/commands/claude.ts +8 -14
- package/src/commands/context.ts +18 -280
- package/src/commands/feedback.ts +22 -91
- package/src/commands/records.ts +5 -5
- package/src/commands/sync.ts +5 -3
- package/src/commands/upstream.ts +135 -0
- package/src/context/audit.ts +5 -14
- package/src/context/classify/extract.ts +6 -13
- package/src/context/classify/patterns.ts +2 -41
- package/src/context/classify/prompts.ts +2 -6
- package/src/context/classify/run.ts +3 -3
- package/src/context/folders.ts +7 -12
- package/src/context/gate.ts +11 -91
- package/src/context/requirements.ts +13 -4
- package/src/counts/scan.ts +3 -3
- package/src/design/components.ts +2 -2
- package/src/feedback/body.ts +0 -12
- package/src/gate/measures.ts +6 -57
- package/src/gov/citations.ts +9 -3
- package/src/gov/restated.ts +3 -3
- package/src/markdown/structure.ts +2 -2
- package/src/migrate/record-layout.ts +3 -3
- package/src/records/ordinal.ts +1 -1
- package/src/records/prune.ts +2 -1
- package/src/records/stale-canonical.ts +2 -2
- package/src/roots/record.ts +1 -1
- package/src/sandbox/scenario.ts +115 -11
- package/src/shipped/references.ts +3 -3
- package/src/standards/read.ts +31 -0
- package/src/sync/seeds-report.ts +1 -1
- package/src/tasks/reach.ts +2 -2
- package/src/tasks/validate.ts +1 -1
- package/src/teach/workspace.ts +1 -1
- package/src/upstream/cursor.ts +1 -1
- package/src/upstream/due.ts +128 -0
- package/src/upstream/releases.ts +2 -1
- package/standards/context.md +3 -5
- package/standards/design.md +2 -3
- package/standards/index.md +0 -22
- package/standards/publish.md +1 -1
- package/standards/skill.md +1 -1
- package/tooling/base/reference.md +1 -1
- package/tooling/claude/reference.md +6 -20
- package/tooling/claude/seeds/.claude/hooks/records-index.sh +8 -20
- package/tooling/claude/seeds/.claude/hooks/standards-audit.sh +3 -3
- package/tooling/claude/seeds/CLAUDE.md +1 -3
- package/tooling/claude/seeds/canon/REQUIREMENTS.md +2 -9
- package/tooling/web/configs/e2e/screenshot.ts +0 -1
- package/claude/skills/auto-ship/references/review-findings.md +0 -20
- package/claude/skills/bash-cli-script/REQUIREMENT.md +0 -43
- package/claude/skills/bash-cli-script/SKILL.md +0 -53
- package/claude/skills/bash-cli-script/references/template.md +0 -19
- package/claude/skills/canon-operator/REQUIREMENT.md +0 -62
- package/claude/skills/canon-operator/SKILL.md +0 -113
- package/claude/skills/canon-rollout/REQUIREMENT.md +0 -59
- package/claude/skills/canon-rollout/SKILL.md +0 -140
- package/claude/skills/canon-rollout/references/failure-recovery.md +0 -20
- package/claude/skills/context-fold/references/anchor-sweep.md +0 -58
- package/claude/skills/context-fold/references/wireframe-sweep.md +0 -45
- package/claude/skills/create-standard/REQUIREMENT.md +0 -33
- package/claude/skills/create-standard/SKILL.md +0 -31
- package/claude/skills/design-extract/REQUIREMENT.md +0 -42
- package/claude/skills/design-extract/SKILL.md +0 -108
- package/claude/skills/draft-and-pick/references/frame.html +0 -118
- package/claude/skills/draft-doc/references/wireframes.md +0 -48
- package/claude/skills/markdown-propose/REQUIREMENT.md +0 -47
- package/claude/skills/markdown-propose/SKILL.md +0 -122
- package/claude/skills/markdown-propose/references/format.md +0 -118
- package/claude/skills/memory-capture/REQUIREMENT.md +0 -50
- package/claude/skills/memory-capture/SKILL.md +0 -117
- package/claude/skills/memory-review/REQUIREMENT.md +0 -52
- package/claude/skills/memory-review/SKILL.md +0 -129
- package/claude/skills/memory-review/references/later-phases.md +0 -118
- package/claude/skills/memory-review/references/receipt-format.md +0 -64
- package/claude/skills/project-commands/REQUIREMENT.md +0 -42
- package/claude/skills/project-commands/SKILL.md +0 -87
- package/claude/skills/repo-metadata/REQUIREMENT.md +0 -37
- package/claude/skills/repo-metadata/SKILL.md +0 -54
- package/claude/skills/restate-plainly/REQUIREMENT.md +0 -41
- package/claude/skills/restate-plainly/SKILL.md +0 -41
- package/claude/skills/review-branch/REQUIREMENT.md +0 -40
- package/claude/skills/review-branch/SKILL.md +0 -137
- package/claude/skills/seed-sync/REQUIREMENT.md +0 -45
- package/claude/skills/seed-sync/SKILL.md +0 -158
- package/claude/skills/sketch-design/REQUIREMENT.md +0 -38
- package/claude/skills/sketch-design/SKILL.md +0 -94
- package/claude/skills/ux-walkthrough/REQUIREMENT.md +0 -56
- package/claude/skills/ux-walkthrough/SKILL.md +0 -53
- package/claude/skills/ux-walkthrough/references/builds.md +0 -15
- package/claude/skills/ux-walkthrough/references/candidate-pages.md +0 -11
- package/claude/skills/ux-walkthrough/references/measuring.md +0 -22
- package/claude/skills/ux-walkthrough/references/record.md +0 -36
- package/claude/skills/ux-walkthrough/references/relay.md +0 -25
- package/docs/agents/architecture-record.md +0 -36
- package/docs/agents/review-classification.md +0 -76
- package/governance/rules/canon/603-memory.md +0 -11
- package/governance/rules/lang/120-bash.md +0 -18
- package/governance/rules/standards/architecture.md +0 -13
- package/governance/rules/standards/groundwork.md +0 -13
- package/governance/rules/standards/intake.md +0 -12
- package/governance/rules/standards/memory.md +0 -12
- package/governance/rules/standards/ready.md +0 -13
- package/governance/rules/standards/requirements.md +0 -12
- package/governance/rules/standards/session.md +0 -13
- package/governance/rules/standards/tasks.md +0 -15
- package/governance/rules/standards/teach.md +0 -14
- package/governance/rules/standards/wiki.md +0 -13
- package/governance/rules/standards/wireframes.md +0 -13
- package/src/autoship/classify.ts +0 -75
- package/src/autoship/paths.ts +0 -50
- package/src/commands/autoship.ts +0 -129
- package/src/commands/repo.ts +0 -393
- package/src/context/architecture.ts +0 -654
- package/src/context/wireframe-states.ts +0 -238
- package/src/repo/metadata.ts +0 -206
- package/standards/architecture.md +0 -127
- package/standards/memory.md +0 -158
- package/standards/mermaid.md +0 -64
- package/standards/requirements.md +0 -79
- package/standards/wiki.md +0 -77
- package/standards/wireframes.md +0 -143
- package/tooling/claude/seeds/.claude/memory/index.md +0 -8
- package/tooling/claude/seeds/canon/ARCHITECTURE.md +0 -23
- package/tooling/claude/seeds/canon/wireframes/index.md +0 -8
- /package/{standards → claude/skills/create-skill/references}/skill-practice.md +0 -0
- /package/{standards → claude/skills/create-skill/references}/skill-requirement.md +0 -0
- /package/{standards → claude/skills/plan-feature/references}/plan-lifecycle.md +0 -0
- /package/{standards → claude/skills/plan-groundwork/references}/groundwork-spikes.md +0 -0
- /package/{standards → claude/skills/task-board/references}/tasks-decline.md +0 -0
- /package/{standards → claude/skills/teach-workspace/references}/glossary.md +0 -0
package/src/commands/records.ts
CHANGED
|
@@ -300,10 +300,10 @@ export function register(program: Command): void {
|
|
|
300
300
|
'Entries are ordered due first, then those citing a missing path, then the',
|
|
301
301
|
'longest unreviewed, then by name, so a review takes the first N as a batch.',
|
|
302
302
|
'',
|
|
303
|
-
'canonical: reads REQUIREMENTS.md
|
|
304
|
-
'
|
|
305
|
-
'
|
|
306
|
-
'
|
|
303
|
+
'canonical: reads REQUIREMENTS.md at whichever surface root carries it,',
|
|
304
|
+
'in the current worktree since it is tracked. It reports its reviewed date',
|
|
305
|
+
'and the release tags merged into HEAD after that day, or never when it',
|
|
306
|
+
'carries no date. --days does not apply.',
|
|
307
307
|
'',
|
|
308
308
|
'Exit codes:',
|
|
309
309
|
' 0 the reading completed, whatever it found',
|
|
@@ -645,7 +645,7 @@ async function runStaleCanonical(
|
|
|
645
645
|
logStep('Canonical')
|
|
646
646
|
|
|
647
647
|
if (outcome.docs.length === 0) {
|
|
648
|
-
logInfo('
|
|
648
|
+
logInfo('REQUIREMENTS.md does not exist')
|
|
649
649
|
} else {
|
|
650
650
|
for (const doc of outcome.docs) logInfo(describeCanonical(doc, outcome))
|
|
651
651
|
}
|
package/src/commands/sync.ts
CHANGED
|
@@ -282,7 +282,7 @@ function renderTooling(report: CheckReport): void {
|
|
|
282
282
|
/**
|
|
283
283
|
* Seeds print their own section because no sync command applies them. A `stale`
|
|
284
284
|
* seed is safe to take whole and a `drifted` one holds edits, which is the split
|
|
285
|
-
*
|
|
285
|
+
* that decides what needs a hand merge.
|
|
286
286
|
*/
|
|
287
287
|
function renderSeeds(report: CheckReport): void {
|
|
288
288
|
const notable = report.seeds.entries.filter(
|
|
@@ -300,7 +300,9 @@ function renderSeeds(report: CheckReport): void {
|
|
|
300
300
|
logWarn(`${entry.rel} (${entry.state})`)
|
|
301
301
|
}
|
|
302
302
|
|
|
303
|
-
logInfo(
|
|
303
|
+
logInfo(
|
|
304
|
+
'A stale seed is safe to take whole. Merge a drifted one by hand against the toolkit copy.',
|
|
305
|
+
)
|
|
304
306
|
}
|
|
305
307
|
|
|
306
308
|
/**
|
|
@@ -401,7 +403,7 @@ async function runSync(target: string): Promise<number> {
|
|
|
401
403
|
|
|
402
404
|
if (existsSync(join(resolved, '.claude'))) {
|
|
403
405
|
process.stderr.write(
|
|
404
|
-
`${GREY}Tip:
|
|
406
|
+
`${GREY}Tip: merge a drifted seed by hand against the toolkit copy, and take a stale one whole.${NC}\n`,
|
|
405
407
|
)
|
|
406
408
|
}
|
|
407
409
|
|
package/src/commands/upstream.ts
CHANGED
|
@@ -1,23 +1,36 @@
|
|
|
1
1
|
import { resolve } from 'node:path'
|
|
2
|
+
import { $ } from 'bun'
|
|
2
3
|
import type { Command } from 'commander'
|
|
3
4
|
import { intro, logInfo, logStep, logWarn, outro, plural } from '@/cli/ui'
|
|
4
5
|
import { currentWorktreeRoot, mainWorktreeRoot } from '@/git/worktree'
|
|
5
6
|
import { buildCatalog, renderCatalog } from '@/upstream/catalog'
|
|
6
7
|
import {
|
|
7
8
|
advanceCursor,
|
|
9
|
+
compareVersions,
|
|
8
10
|
llmsDifference,
|
|
9
11
|
readCursor,
|
|
10
12
|
readLastLlms,
|
|
11
13
|
} from '@/upstream/cursor'
|
|
12
14
|
import { buildDigest } from '@/upstream/digest'
|
|
15
|
+
import {
|
|
16
|
+
decideDue,
|
|
17
|
+
passesVocabulary,
|
|
18
|
+
readDueCache,
|
|
19
|
+
writeDueCache,
|
|
20
|
+
type DueReason,
|
|
21
|
+
type Gap,
|
|
22
|
+
} from '@/upstream/due'
|
|
13
23
|
import {
|
|
14
24
|
githubPageFetcher,
|
|
15
25
|
grepHint,
|
|
26
|
+
keepLines,
|
|
27
|
+
walkReleases,
|
|
16
28
|
type FetchFailure,
|
|
17
29
|
} from '@/upstream/releases'
|
|
18
30
|
|
|
19
31
|
const LLMS_URL = 'https://code.claude.com/docs/llms.txt'
|
|
20
32
|
const VERSION = /^\d+(\.\d+)*$/
|
|
33
|
+
const READ_BUDGET_MS = 5000
|
|
21
34
|
|
|
22
35
|
interface RootOption {
|
|
23
36
|
readonly root?: string
|
|
@@ -128,6 +141,33 @@ export function register(program: Command): void {
|
|
|
128
141
|
process.exitCode = await runCatalog(opts)
|
|
129
142
|
})
|
|
130
143
|
|
|
144
|
+
upstream
|
|
145
|
+
.command('due')
|
|
146
|
+
.description('Say whether a digest is due, from the cursor and the install')
|
|
147
|
+
.helpOption('-h, --help', 'Show this help message')
|
|
148
|
+
.option('--root <path>', 'Repository to read, defaulting to the main root')
|
|
149
|
+
.option('--json', 'Add a machine-readable record on stdout')
|
|
150
|
+
.addHelpText(
|
|
151
|
+
'after',
|
|
152
|
+
[
|
|
153
|
+
'',
|
|
154
|
+
'Due when the cursor is a week old and the installed version is ahead of',
|
|
155
|
+
'it, or sooner when an Added line in the gap names a canon surface. The',
|
|
156
|
+
'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.',
|
|
158
|
+
'',
|
|
159
|
+
'Exit codes:',
|
|
160
|
+
' 0 answered, whether or not a digest is due',
|
|
161
|
+
'',
|
|
162
|
+
'Examples:',
|
|
163
|
+
' canon upstream due --json',
|
|
164
|
+
'',
|
|
165
|
+
].join('\n'),
|
|
166
|
+
)
|
|
167
|
+
.action(async (opts: RootOption) => {
|
|
168
|
+
process.exitCode = await runDue(opts)
|
|
169
|
+
})
|
|
170
|
+
|
|
131
171
|
upstream
|
|
132
172
|
.command('advance')
|
|
133
173
|
.description('Move the cursor to a version once its digest is filed')
|
|
@@ -240,6 +280,101 @@ async function runFetch(opts: FetchOptions): Promise<number> {
|
|
|
240
280
|
return 0
|
|
241
281
|
}
|
|
242
282
|
|
|
283
|
+
const DUE_TEXT: Record<DueReason, string> = {
|
|
284
|
+
'no-cursor': 'No digest has been run, so there is no cursor.',
|
|
285
|
+
week: 'A week has passed since the last digest.',
|
|
286
|
+
vocabulary: 'A release since the last digest added something canon may use.',
|
|
287
|
+
current: 'The installed version is at or below the cursor.',
|
|
288
|
+
recent:
|
|
289
|
+
'The last digest is under a week old and nothing in the gap stands out.',
|
|
290
|
+
'unknown-version': 'The installed version could not be read.',
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
async function installedVersion(): Promise<string | null> {
|
|
294
|
+
try {
|
|
295
|
+
const result = await $`claude --version`.quiet().nothrow()
|
|
296
|
+
const match = /^\d+(\.\d+)*/.exec(result.stdout.toString().trim())
|
|
297
|
+
|
|
298
|
+
return match ? match[0] : null
|
|
299
|
+
} catch {
|
|
300
|
+
return null
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// A failed read is stored as a null gap, so a rate limit hit by one session of
|
|
305
|
+
// a wave is not retried by the nineteen after it.
|
|
306
|
+
async function readGap(
|
|
307
|
+
root: string,
|
|
308
|
+
installed: string,
|
|
309
|
+
cursor: string,
|
|
310
|
+
now: Date,
|
|
311
|
+
): Promise<Gap | null> {
|
|
312
|
+
const cached = readDueCache(root, installed, cursor, now)
|
|
313
|
+
if (cached) return cached.gap
|
|
314
|
+
|
|
315
|
+
// The hook that calls this has a 10 second timeout, and a network that drops
|
|
316
|
+
// packets would otherwise hold every session start to it with nothing cached.
|
|
317
|
+
const walk = await walkReleases(
|
|
318
|
+
cursor,
|
|
319
|
+
githubPageFetcher(process.env, AbortSignal.timeout(READ_BUDGET_MS)),
|
|
320
|
+
)
|
|
321
|
+
const gap: Gap | null =
|
|
322
|
+
walk.kind === 'reached'
|
|
323
|
+
? {
|
|
324
|
+
releases: walk.releases.length,
|
|
325
|
+
lines: walk.releases
|
|
326
|
+
.flatMap(keepLines)
|
|
327
|
+
.map((line) => line.text)
|
|
328
|
+
.filter(passesVocabulary),
|
|
329
|
+
}
|
|
330
|
+
: null
|
|
331
|
+
|
|
332
|
+
try {
|
|
333
|
+
await writeDueCache(root, {
|
|
334
|
+
checkedAt: now.toISOString(),
|
|
335
|
+
installed,
|
|
336
|
+
cursor,
|
|
337
|
+
gap,
|
|
338
|
+
})
|
|
339
|
+
} catch {
|
|
340
|
+
// A cache that cannot be written costs a request, not a wrong answer.
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
return gap
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
async function runDue(opts: RootOption): Promise<number> {
|
|
347
|
+
const root = opts.root ? resolve(opts.root) : await mainWorktreeRoot()
|
|
348
|
+
const now = new Date()
|
|
349
|
+
const cursor = readCursor(root)
|
|
350
|
+
const installed = await installedVersion()
|
|
351
|
+
const isAhead =
|
|
352
|
+
cursor !== null &&
|
|
353
|
+
installed !== null &&
|
|
354
|
+
compareVersions(installed, cursor.version) > 0
|
|
355
|
+
const gap = isAhead
|
|
356
|
+
? await readGap(root, installed, cursor.version, now)
|
|
357
|
+
: null
|
|
358
|
+
const result = decideDue({ installed, cursor, gap, now })
|
|
359
|
+
|
|
360
|
+
intro('canon upstream due')
|
|
361
|
+
logStep(result.due ? 'Due' : 'Not due')
|
|
362
|
+
logInfo(DUE_TEXT[result.reason])
|
|
363
|
+
outro()
|
|
364
|
+
|
|
365
|
+
if (opts.json) {
|
|
366
|
+
process.stdout.write(
|
|
367
|
+
`${JSON.stringify({
|
|
368
|
+
...result,
|
|
369
|
+
installed,
|
|
370
|
+
cursor: cursor?.version ?? null,
|
|
371
|
+
})}\n`,
|
|
372
|
+
)
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
return 0
|
|
376
|
+
}
|
|
377
|
+
|
|
243
378
|
async function runCatalog(opts: RootOption): Promise<number> {
|
|
244
379
|
const root = opts.root ? resolve(opts.root) : await currentWorktreeRoot()
|
|
245
380
|
const catalog = buildCatalog(root)
|
package/src/context/audit.ts
CHANGED
|
@@ -18,7 +18,7 @@ import { isStubSeed } from '@/tooling/seed-marker'
|
|
|
18
18
|
* Every reason `canon context audit` refuses for.
|
|
19
19
|
*
|
|
20
20
|
* `no-folders` is the one ordinary absence: a project that never adopted
|
|
21
|
-
* `canon/context
|
|
21
|
+
* `canon/context/`, and holds no `.canon/diagrams/`
|
|
22
22
|
* folder an older install left, names no corpus this audit can measure, the
|
|
23
23
|
* same state `no-skills` reads for the skill corpora. The other four are a malformed invocation or a checkout git
|
|
24
24
|
* cannot read, which stay a break rather than an absence.
|
|
@@ -54,16 +54,13 @@ const CATALOG_NAMED_RATIO = 0.6
|
|
|
54
54
|
*
|
|
55
55
|
* These names do not generalize the way a length threshold does, which is why
|
|
56
56
|
* each list is keyed on the folder whose standard states it. `context.md`
|
|
57
|
-
* requires an overview and a layout.
|
|
58
|
-
* surface and answers each in a section, so those four are required and
|
|
59
|
-
* `## Behavior` is not, since a static surface has no interaction to state. A
|
|
57
|
+
* requires an overview and a layout. A
|
|
60
58
|
* diagram entry declares a heading per kind and its standard requires none.
|
|
61
59
|
*/
|
|
62
60
|
export const REQUIRED_SECTIONS_BY_FOLDER: Readonly<
|
|
63
61
|
Record<string, readonly string[]>
|
|
64
62
|
> = {
|
|
65
63
|
context: ['Overview', 'Layout'],
|
|
66
|
-
wireframes: ['Regions', 'States', 'Copy', 'Not on this surface'],
|
|
67
64
|
}
|
|
68
65
|
|
|
69
66
|
/**
|
|
@@ -158,9 +155,8 @@ const SENTENCE_END = /[.!?:;]\s(?=[^.!?:;]*$)/
|
|
|
158
155
|
* reading that field for a gain of one word.
|
|
159
156
|
*
|
|
160
157
|
* `standards/context.md` opens its scope by handing figures to the
|
|
161
|
-
* `draft-figure` skill
|
|
162
|
-
*
|
|
163
|
-
* standard routes elsewhere. The length and table
|
|
158
|
+
* `draft-figure` skill, so a marker reported in a diagrams entry would cite a
|
|
159
|
+
* rule that entry's own standard routes elsewhere. The length and table
|
|
164
160
|
* checkpoints are quoted from the same standard and keep reaching every audited
|
|
165
161
|
* folder, because a threshold on how far a reader travels generalizes across
|
|
166
162
|
* entry types while a rule about what an entry may say does not.
|
|
@@ -495,7 +491,7 @@ function bareReferences(
|
|
|
495
491
|
* path prefix hardcodes what `--folder` exists to override and misses a domain
|
|
496
492
|
* split into `context/<sub-area>/`. Sibling names arrive the same way and for
|
|
497
493
|
* the same reason, since the folder an entry sits in is what holds them. The
|
|
498
|
-
* required sections arrive apart from jurisdiction, since a
|
|
494
|
+
* required sections arrive apart from jurisdiction, since a folder can owe
|
|
499
495
|
* sections under a standard that states no provenance rule.
|
|
500
496
|
*/
|
|
501
497
|
export function measureEntry(
|
|
@@ -652,11 +648,6 @@ export function matchesSiblings(folder: AuditedFolder): boolean {
|
|
|
652
648
|
* The standard sanctions omitting `## Layout` from a domain owning no paths,
|
|
653
649
|
* which no measure can tell from an entry that forgot it. An entry of that
|
|
654
650
|
* shape therefore reports, which is a reason this is printed and never gated on.
|
|
655
|
-
*
|
|
656
|
-
* A wireframe folder answers per file whether nested or not, since its
|
|
657
|
-
* standard keeps one surface per file. A subfolder of wireframes groups
|
|
658
|
-
* surfaces the way the named context folder groups domains, so a conforming
|
|
659
|
-
* sibling there says nothing about the surface beside it.
|
|
660
651
|
*/
|
|
661
652
|
export function missingSections(
|
|
662
653
|
root: string,
|
|
@@ -7,15 +7,13 @@ import { listChangedFiles, resolveBaseRef } from '@/git/files'
|
|
|
7
7
|
import { surfaceDir } from '@/roots/surface'
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
* The
|
|
10
|
+
* The three canonical doc types the classifier reaches, matching the file
|
|
11
11
|
* pattern the groundwork spike scoped its fixture to
|
|
12
12
|
* (`scripts/hunks.py`'s `DOC_PATH`) and the file-type catalog both system
|
|
13
13
|
* prompts state.
|
|
14
14
|
*/
|
|
15
15
|
export const CANONICAL_DOC_TYPES = [
|
|
16
16
|
'context',
|
|
17
|
-
'architecture',
|
|
18
|
-
'wireframes',
|
|
19
17
|
'design',
|
|
20
18
|
'requirements',
|
|
21
19
|
] as const
|
|
@@ -24,23 +22,19 @@ export type CanonicalDocType = (typeof CANONICAL_DOC_TYPES)[number]
|
|
|
24
22
|
|
|
25
23
|
const DOC_PATTERNS: Record<CanonicalDocType, RegExp> = {
|
|
26
24
|
context: /(^|\/)context\/.+\.md$/,
|
|
27
|
-
architecture: /(^|\/)ARCHITECTURE\.md$/,
|
|
28
|
-
wireframes: /(^|\/)wireframes\/.+\.md$/,
|
|
29
25
|
design: /(^|\/)DESIGN\.md$/,
|
|
30
26
|
requirements: /(^|\/)REQUIREMENTS\.md$/,
|
|
31
27
|
}
|
|
32
28
|
|
|
33
29
|
const INDEX_NAME = 'index.md'
|
|
34
30
|
|
|
35
|
-
/** The
|
|
31
|
+
/** The doc type `resolveFolders` discovers, paired with the folder name it takes. */
|
|
36
32
|
const MULTI_DOC_TYPES: readonly { type: CanonicalDocType; name: string }[] = [
|
|
37
33
|
{ type: 'context', name: 'context' },
|
|
38
|
-
{ type: 'wireframes', name: 'wireframes' },
|
|
39
34
|
]
|
|
40
35
|
|
|
41
|
-
/** The
|
|
36
|
+
/** The two doc types that resolve to one file each, paired with `surfaceDir`'s entry name. */
|
|
42
37
|
const SINGLE_DOC_TYPES: readonly { type: CanonicalDocType; entry: string }[] = [
|
|
43
|
-
{ type: 'architecture', entry: 'ARCHITECTURE.md' },
|
|
44
38
|
{ type: 'design', entry: 'DESIGN.md' },
|
|
45
39
|
{ type: 'requirements', entry: 'REQUIREMENTS.md' },
|
|
46
40
|
]
|
|
@@ -388,11 +382,10 @@ function readSections(
|
|
|
388
382
|
/**
|
|
389
383
|
* Every sweep-mode section across the requested canonical doc types.
|
|
390
384
|
*
|
|
391
|
-
* `context`
|
|
385
|
+
* `context` resolves through `resolveFolders`, the same
|
|
392
386
|
* folder discovery `canon context audit` uses, so a domain split into a
|
|
393
|
-
* folder of its own is swept the same way it is audited. The
|
|
394
|
-
* single-file types resolve through `surfaceDir
|
|
395
|
-
* `architectureRel`'s own resolution.
|
|
387
|
+
* folder of its own is swept the same way it is audited. The two
|
|
388
|
+
* single-file types resolve through `surfaceDir`.
|
|
396
389
|
*/
|
|
397
390
|
export async function extractSweepSections(
|
|
398
391
|
root: string,
|
|
@@ -37,44 +37,17 @@ export interface PatternVerdict<V extends string> {
|
|
|
37
37
|
const NARRATION =
|
|
38
38
|
/moved (again|twice|on)|has since|at this branch|on `feat|Measured in PR|did not survive|the operator picked|picked arm|\barm \d|closed on|was retired|no longer|used to|\bround\b|a review pass|this pass/i
|
|
39
39
|
|
|
40
|
-
/**
|
|
41
|
-
* A source-file path inside prose, the shape of implementation detail landing
|
|
42
|
-
* on a surface that should describe layout and intent instead. Ported
|
|
43
|
-
* verbatim from `heuristic.py`.
|
|
44
|
-
*/
|
|
45
|
-
const MECHANISM_IN_WIREFRAME = /`[\w./-]+\.(tsx?|py|css|spec\.ts)`/
|
|
46
|
-
|
|
47
40
|
function narrationQuote(text: string): string | undefined {
|
|
48
41
|
return text.match(NARRATION)?.[0]
|
|
49
42
|
}
|
|
50
43
|
|
|
51
|
-
/**
|
|
52
|
-
|
|
53
|
-
*
|
|
54
|
-
* `file` decides whether the wireframe-only MOVE test applies. A file argument
|
|
55
|
-
* rather than a boolean matches `run.ts`'s other call sites, which hold the
|
|
56
|
-
* path and not a pre-computed flag.
|
|
57
|
-
*/
|
|
58
|
-
export function diffPatternVerdict(
|
|
59
|
-
file: string,
|
|
60
|
-
added: string,
|
|
61
|
-
): PatternVerdict<DiffVerdict> {
|
|
44
|
+
/** Classifies one diff-mode chunk from its added text alone. */
|
|
45
|
+
export function diffPatternVerdict(added: string): PatternVerdict<DiffVerdict> {
|
|
62
46
|
const quote = narrationQuote(added)
|
|
63
47
|
if (quote !== undefined) {
|
|
64
48
|
return { verdict: 'HISTORY', quote, reason: 'narrates how this got here' }
|
|
65
49
|
}
|
|
66
50
|
|
|
67
|
-
if (file.includes('wireframes/')) {
|
|
68
|
-
const match = added.match(MECHANISM_IN_WIREFRAME)
|
|
69
|
-
if (match) {
|
|
70
|
-
return {
|
|
71
|
-
verdict: 'MOVE',
|
|
72
|
-
quote: match[0],
|
|
73
|
-
reason: 'names a source file, which is implementation detail',
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
51
|
return { verdict: 'KEEP', reason: 'no narration or wrong-surface pattern' }
|
|
79
52
|
}
|
|
80
53
|
|
|
@@ -87,7 +60,6 @@ export function diffPatternVerdict(
|
|
|
87
60
|
* regex layer follows it.
|
|
88
61
|
*/
|
|
89
62
|
export function sweepPatternVerdict(
|
|
90
|
-
file: string,
|
|
91
63
|
body: string,
|
|
92
64
|
): PatternVerdict<SweepVerdict> {
|
|
93
65
|
const quote = narrationQuote(body)
|
|
@@ -99,16 +71,5 @@ export function sweepPatternVerdict(
|
|
|
99
71
|
}
|
|
100
72
|
}
|
|
101
73
|
|
|
102
|
-
if (file.includes('wireframes/')) {
|
|
103
|
-
const match = body.match(MECHANISM_IN_WIREFRAME)
|
|
104
|
-
if (match) {
|
|
105
|
-
return {
|
|
106
|
-
verdict: 'MOVE',
|
|
107
|
-
quote: match[0],
|
|
108
|
-
reason: 'names a source file, which is implementation detail',
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
74
|
return { verdict: 'KEEP', reason: 'no narration or wrong-surface pattern' }
|
|
114
75
|
}
|
|
@@ -13,15 +13,13 @@ export const DIFF_SYSTEM_PROMPT = `You review one hunk just added to a project's
|
|
|
13
13
|
|
|
14
14
|
File types and what they hold:
|
|
15
15
|
- context/<domain>.md: one domain's structure, decisions (choice + the alternative that lost), gotchas. No history of how the domain got here, no change numbers, branch names, or dates attached to a change.
|
|
16
|
-
- ARCHITECTURE.md: cross-domain decisions only, each a few sentences: what was chosen, over what, why. A measurement or mechanism specific to one domain belongs in that domain's context entry. Loaded every session, so weight matters.
|
|
17
|
-
- wireframes/<surface>.md: layout sketches, reachable states, exact on-screen copy, interaction intent. Would the line still be true if the surface were rebuilt in another framework? Component names, test files, pixel constants, and mechanism belong in context.
|
|
18
16
|
- DESIGN.md: tokens and visual rules, current state only.
|
|
19
17
|
|
|
20
18
|
Verdicts (pick exactly one):
|
|
21
19
|
- KEEP: states current design, a rule, copy, a decision with its rejected alternative, or a live gotcha, on the right surface.
|
|
22
20
|
- REPLACE: restates a fact or figure the existing section already carries ("moved again", "now", "the count moved", a newer number appended after an older one). The old statement should be rewritten in place, not appended to.
|
|
23
21
|
- HISTORY: narrates how things got here: which branch/PR changed what, "closed on", "did not survive", review-pass stories, pick-by-pick rounds. The current state is what belongs; the trail goes to the PR.
|
|
24
|
-
- MOVE: correct content on the wrong surface (
|
|
22
|
+
- MOVE: correct content on the wrong surface (a rule written into a context entry, or a measured result written into the requirements record).
|
|
25
23
|
|
|
26
24
|
Return only JSON: {"verdict": "...", "quote": "<the shortest added phrase that decided it>", "reason": "<one sentence>"}
|
|
27
25
|
|
|
@@ -35,15 +33,13 @@ export const SWEEP_SYSTEM_PROMPT = `You review one whole section of a project's
|
|
|
35
33
|
|
|
36
34
|
File types and what they hold:
|
|
37
35
|
- context/<domain>.md: one domain's structure, decisions (choice + the alternative that lost), gotchas. No history of how the domain got here, no change numbers, branch names, or dates attached to a change.
|
|
38
|
-
- ARCHITECTURE.md: cross-domain decisions only, each a few sentences: what was chosen, over what, why. A measurement or mechanism specific to one domain belongs in that domain's context entry.
|
|
39
|
-
- wireframes/<surface>.md: layout, reachable states, exact on-screen copy, interaction intent. Would the line still be true if the surface were rebuilt in another framework? Component names, test files, and pixel constants belong in context.
|
|
40
36
|
- DESIGN.md: tokens and visual rules, current state only.
|
|
41
37
|
- REQUIREMENTS.md: problem, goals, non-goals, scope, constraints. Never measured results.
|
|
42
38
|
|
|
43
39
|
Verdicts (pick exactly one):
|
|
44
40
|
- KEEP: the section states the current design, rules, copy, or decisions once, on the right surface. A single commit anchor on a figure is fine.
|
|
45
41
|
- REWRITE: the section carries its own history: a figure followed by a later corrected figure, "superseded", "now", "moved", "no longer", "reverses the earlier reading", branch or PR narration, pick-by-pick rounds, a heading that narrates an event. The subject belongs here, but it must be restated as the current state.
|
|
46
|
-
- MOVE: the section, or most of it, belongs on another surface (results in requirements
|
|
42
|
+
- MOVE: the section, or most of it, belongs on another surface (results in requirements).
|
|
47
43
|
|
|
48
44
|
Decision checks, in order:
|
|
49
45
|
1. Judge what the text says, not how long it is. A long section that states current design once is KEEP.
|
|
@@ -245,7 +245,7 @@ async function modelVerdictFor<V extends string>(opts: {
|
|
|
245
245
|
* A finding's `verdict` takes the model's reading when the model ran and
|
|
246
246
|
* parsed, and the regex reading otherwise, since the groundwork measurements
|
|
247
247
|
* show the model catching what the regex layer structurally cannot
|
|
248
|
-
* (REPLACE
|
|
248
|
+
* (REPLACE and MOVE). Both readings stay on the finding
|
|
249
249
|
* regardless of which one decided, so a caller can see where they disagreed.
|
|
250
250
|
*/
|
|
251
251
|
export async function classifyDiff(
|
|
@@ -263,7 +263,7 @@ export async function classifyDiff(
|
|
|
263
263
|
|
|
264
264
|
const findings: DiffFinding[] = []
|
|
265
265
|
for (const chunk of extraction.chunks) {
|
|
266
|
-
const regex = withQuote(diffPatternVerdict(chunk.
|
|
266
|
+
const regex = withQuote(diffPatternVerdict(chunk.added))
|
|
267
267
|
const model = await modelVerdictFor({
|
|
268
268
|
plan,
|
|
269
269
|
client,
|
|
@@ -312,7 +312,7 @@ export async function classifySweep(
|
|
|
312
312
|
|
|
313
313
|
const findings: SweepFinding[] = []
|
|
314
314
|
for (const section of extraction.sections) {
|
|
315
|
-
const regex = withQuote(sweepPatternVerdict(section.
|
|
315
|
+
const regex = withQuote(sweepPatternVerdict(section.body))
|
|
316
316
|
const model = await modelVerdictFor({
|
|
317
317
|
plan,
|
|
318
318
|
client,
|
package/src/context/folders.ts
CHANGED
|
@@ -17,19 +17,14 @@ import { SURFACE_ROOTS } from '@/roots/surface'
|
|
|
17
17
|
* these names. A folder left off the list is never resolved, so a path into one
|
|
18
18
|
* goes stale silently rather than failing a push.
|
|
19
19
|
*/
|
|
20
|
-
export const DEFAULT_FOLDERS: readonly string[] = [
|
|
21
|
-
'context',
|
|
22
|
-
'diagrams',
|
|
23
|
-
'wireframes',
|
|
24
|
-
]
|
|
20
|
+
export const DEFAULT_FOLDERS: readonly string[] = ['context', 'diagrams']
|
|
25
21
|
|
|
26
22
|
/**
|
|
27
23
|
* The bases every folder in the default list is looked for under.
|
|
28
24
|
*
|
|
29
25
|
* `diagrams` is a session record and resolves under `RECORD_ROOTS`, while
|
|
30
|
-
* `context`
|
|
31
|
-
*
|
|
32
|
-
* folders on this list read from two different root lists. The base array is
|
|
26
|
+
* `context` is tracked and resolves under `SURFACE_ROOTS`, so two folders on
|
|
27
|
+
* this list read from two different root lists. The base array is
|
|
33
28
|
* their union rather than a per-name map, since a name resolving at the wrong
|
|
34
29
|
* root costs one extra `existsSync` and nothing else, where a map states the
|
|
35
30
|
* split a second time next to the one each resolver module already carries.
|
|
@@ -90,10 +85,10 @@ export interface AuditedFolder {
|
|
|
90
85
|
* Names the requested record-root folders that actually exist, which is the
|
|
91
86
|
* citation check's scope.
|
|
92
87
|
*
|
|
93
|
-
* A skill or seed pointing into
|
|
88
|
+
* A skill or seed pointing into an optional folder is a live instruction for
|
|
94
89
|
* a project that carries the folder and says nothing about one that does not.
|
|
95
|
-
* Checking a path into an absent folder would fail
|
|
96
|
-
*
|
|
90
|
+
* Checking a path into an absent folder would fail shipped references here for
|
|
91
|
+
* the sole reason that this repository does not carry it.
|
|
97
92
|
*
|
|
98
93
|
* A folder resolved at the project root is measured and stays out of this. The
|
|
99
94
|
* pattern the citation check builds spells the `.claude/` prefix, so admitting
|
|
@@ -172,7 +167,7 @@ function locate(
|
|
|
172
167
|
* beside `node_modules` and a build output.
|
|
173
168
|
*
|
|
174
169
|
* The project root is reached only when the caller opts in, so a target holding
|
|
175
|
-
* a root
|
|
170
|
+
* a root folder of its own name is not audited against a standard it never adopted by
|
|
176
171
|
* the mere act of running the command. `.claude/` still wins a name carried by
|
|
177
172
|
* both, and the scope line prints the resolved path so a caller reads which
|
|
178
173
|
* base was taken rather than inferring it.
|
package/src/context/gate.ts
CHANGED
|
@@ -1,61 +1,19 @@
|
|
|
1
1
|
import type { SectionFinding } from '@/context/audit'
|
|
2
2
|
import type { FolderDrift } from '@/context/index-drift'
|
|
3
|
-
import type { WireframeStatesReport } from '@/context/wireframe-states'
|
|
4
3
|
|
|
5
4
|
export interface GateInput {
|
|
6
5
|
/** Cited paths that resolved to nothing, which gate under either mode. */
|
|
7
6
|
readonly unresolvedCitations: number
|
|
8
|
-
/**
|
|
9
|
-
* Whether the architecture record is longer than the ceiling it derives for
|
|
10
|
-
* itself, which gates under either mode for the reason a citation does.
|
|
11
|
-
*
|
|
12
|
-
* False when the project carries no record and false under
|
|
13
|
-
* `--citations-only`, which never measures it. That mode runs one check by
|
|
14
|
-
* construction, so widening it here would gate on a reading it never took.
|
|
15
|
-
*/
|
|
16
|
-
readonly recordOverLength: boolean
|
|
17
|
-
/**
|
|
18
|
-
* Whether the record holds more decisions than the cap it states, which
|
|
19
|
-
* gates under the same two modes and is false in the same two cases.
|
|
20
|
-
*/
|
|
21
|
-
readonly recordOverCount: boolean
|
|
22
|
-
/**
|
|
23
|
-
* Whether a decision lacks the revisit sentence the record requires of
|
|
24
|
-
* every decision, which gates under the same two modes and is false in the
|
|
25
|
-
* same two cases, plus a record stating no such clause.
|
|
26
|
-
*/
|
|
27
|
-
readonly recordMissingRevisit: boolean
|
|
28
|
-
/**
|
|
29
|
-
* Whether a decision holds more words than the cap the record states, which
|
|
30
|
-
* gates under the same two modes and is false in the same two cases, plus a
|
|
31
|
-
* record stating no word cap.
|
|
32
|
-
*/
|
|
33
|
-
readonly recordOverWords: boolean
|
|
34
|
-
/** The same reading for the Risks section's bullet cap. */
|
|
35
|
-
readonly recordOverRisks: boolean
|
|
36
7
|
/**
|
|
37
8
|
* Whether the requirements record holds more words than the cap it states,
|
|
38
|
-
* which gates for the reason
|
|
9
|
+
* which gates under either mode for the reason a citation does. False when
|
|
10
|
+
* the project carries no record and false under `--citations-only`, which
|
|
11
|
+
* never measures it. That mode runs one check by construction, so widening
|
|
12
|
+
* it here would gate on a reading it never took.
|
|
39
13
|
*/
|
|
40
14
|
readonly requirementsOverWords: boolean
|
|
41
15
|
readonly sections: readonly SectionFinding[]
|
|
42
16
|
readonly drift: readonly FolderDrift[]
|
|
43
|
-
/**
|
|
44
|
-
* Every wireframe entry's states report, empty for a project carrying no
|
|
45
|
-
* `canon/wireframes/` folder. A states/evidence mismatch is a fact read off
|
|
46
|
-
* the file, the same standing the missing-section and index-drift findings
|
|
47
|
-
* already have, so it gates alongside them under the widened mode.
|
|
48
|
-
*
|
|
49
|
-
* A sketch beside existing evidence stays out of the gate on both modes.
|
|
50
|
-
* `sketchWithEvidence` reads the whole entry against whether any of its
|
|
51
|
-
* states has evidence, not the sketched layout against the evidence for
|
|
52
|
-
* that same layout, since the standard's `## States` table names states
|
|
53
|
-
* rather than layouts and the report has no narrower unit to match on. An
|
|
54
|
-
* entry with a captured default layout and a sketch for a breakpoint layout
|
|
55
|
-
* that is not built yet is conforming and still trips this reading, so the
|
|
56
|
-
* finding stays a judgment for a reader rather than a fact a push fails on.
|
|
57
|
-
*/
|
|
58
|
-
readonly wireframes: readonly WireframeStatesReport[]
|
|
59
17
|
/**
|
|
60
18
|
* Whether the caller asked for the widened gate. False leaves a missing
|
|
61
19
|
* section and a drifted index advisory, which is what the project-root stage
|
|
@@ -71,65 +29,27 @@ export function hasDrift(drift: readonly FolderDrift[]): boolean {
|
|
|
71
29
|
)
|
|
72
30
|
}
|
|
73
31
|
|
|
74
|
-
/** Whether any wireframe's States table disagrees with its evidence folders. */
|
|
75
|
-
export function hasStatesMismatch(
|
|
76
|
-
wireframes: readonly WireframeStatesReport[],
|
|
77
|
-
): boolean {
|
|
78
|
-
return wireframes.some(
|
|
79
|
-
(entry) =>
|
|
80
|
-
entry.missingFolders.length > 0 || entry.unlistedFolders.length > 0,
|
|
81
|
-
)
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/** Whether any wireframe carries a sketch beside evidence that already exists. */
|
|
85
|
-
export function hasSketchWithEvidence(
|
|
86
|
-
wireframes: readonly WireframeStatesReport[],
|
|
87
|
-
): boolean {
|
|
88
|
-
return wireframes.some((entry) => entry.sketchWithEvidence)
|
|
89
|
-
}
|
|
90
|
-
|
|
91
32
|
/**
|
|
92
33
|
* Whether the audit found something that should fail the caller.
|
|
93
34
|
*
|
|
94
35
|
* An unresolved citation is a broken pointer and gates unconditionally, and so
|
|
95
|
-
* does a record past its own
|
|
96
|
-
*
|
|
97
|
-
* own word cap: each record states its rules for itself, which makes those the
|
|
98
|
-
* measures here that are facts
|
|
99
|
-
* rather than thresholds a reader weighs. The findings `--gate` adds are
|
|
36
|
+
* does a requirements record past its own word cap: the record states its rule
|
|
37
|
+
* for itself, which makes it a fact rather than a threshold a reader weighs. The findings `--gate` adds are
|
|
100
38
|
* the ones answerable from the file itself: a required section it does not
|
|
101
|
-
* declare, an index disagreeing with its folder
|
|
102
|
-
* table
|
|
103
|
-
*
|
|
104
|
-
* existing evidence are judgments, so they stay out under both modes. The
|
|
105
|
-
* `wireframes` field's own doc states why the sketch finding is a judgment
|
|
106
|
-
* rather than a fact.
|
|
39
|
+
* declare, and an index disagreeing with its folder. Entry length, depth, bullet,
|
|
40
|
+
* table, provenance, and the record's claim coverage are judgments, so they
|
|
41
|
+
* stay out under both modes.
|
|
107
42
|
*/
|
|
108
43
|
export function isGating({
|
|
109
44
|
unresolvedCitations,
|
|
110
|
-
recordOverLength,
|
|
111
|
-
recordOverCount,
|
|
112
|
-
recordMissingRevisit,
|
|
113
|
-
recordOverWords,
|
|
114
|
-
recordOverRisks,
|
|
115
45
|
requirementsOverWords,
|
|
116
46
|
sections,
|
|
117
47
|
drift,
|
|
118
|
-
wireframes,
|
|
119
48
|
widened,
|
|
120
49
|
}: GateInput): boolean {
|
|
121
50
|
if (unresolvedCitations > 0) return true
|
|
122
|
-
if (
|
|
123
|
-
recordOverLength ||
|
|
124
|
-
recordOverCount ||
|
|
125
|
-
recordMissingRevisit ||
|
|
126
|
-
recordOverWords ||
|
|
127
|
-
recordOverRisks ||
|
|
128
|
-
requirementsOverWords
|
|
129
|
-
) {
|
|
130
|
-
return true
|
|
131
|
-
}
|
|
51
|
+
if (requirementsOverWords) return true
|
|
132
52
|
if (!widened) return false
|
|
133
53
|
|
|
134
|
-
return sections.length > 0 || hasDrift(drift)
|
|
54
|
+
return sections.length > 0 || hasDrift(drift)
|
|
135
55
|
}
|