@leing2021/super-pi 0.23.2 → 0.23.4

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.
@@ -310,6 +310,7 @@ const contextHandoffParams = Type.Object({
310
310
  Type.Literal("load"),
311
311
  Type.Literal("latest"),
312
312
  Type.Literal("status"),
313
+ Type.Literal("validate"),
313
314
  ], { description: "Handoff operation" }),
314
315
  repoRoot: Type.String({ description: "Repository root" }),
315
316
  currentStage: Type.Optional(Type.String({ description: "Current pipeline stage (e.g. 02-plan)" })),
@@ -331,6 +332,7 @@ const contextHandoffParams = Type.Object({
331
332
  openDecisions: Type.Optional(Type.Array(Type.String(), { description: "Pending decisions that affect next steps" })),
332
333
  recentlyAccessedFiles: Type.Optional(Type.Array(Type.String(), { description: "Files recently read or edited (defaults to activeFiles)" })),
333
334
  compressionRisk: Type.Optional(Type.Array(Type.String(), { description: "Context compression risks to watch for" })),
335
+ activeRules: Type.Optional(Type.Array(Type.String(), { description: "1-5 must-know rules for continuation (TDD gates, constraints, do-not-repeat)" })),
334
336
  })
335
337
 
336
338
  const patternExtractorParams = Type.Object({
@@ -393,18 +395,21 @@ export default function ceCoreExtension(pi: ExtensionAPI) {
393
395
  if (thinkingStrategy) {
394
396
  const targetThinking = thinkingStrategy[stageKey] ?? thinkingStrategy.default
395
397
  if (targetThinking) {
396
- const levelMap: Record<string, "low" | "medium" | "high"> = {
398
+ const levelMap: Record<string, ReturnType<ExtensionAPI["getThinkingLevel"]>> = {
399
+ off: "off",
400
+ minimal: "minimal",
397
401
  low: "low",
398
402
  medium: "medium",
399
403
  high: "high",
404
+ xhigh: "xhigh",
400
405
  "0": "low",
401
406
  "1": "medium",
402
407
  "2": "high",
403
408
  }
404
409
  const normalized = levelMap[targetThinking.toLowerCase()] ?? "medium"
405
- const currentLevel = ctx.getThinkingLevel?.() ?? "medium"
410
+ const currentLevel = pi.getThinkingLevel()
406
411
  if (currentLevel !== normalized) {
407
- ctx.setThinkingLevel?.(normalized)
412
+ pi.setThinkingLevel(normalized)
408
413
  if (ctx.hasUI) {
409
414
  ctx.ui.notify(`Switched thinking level for ${stageKey}: ${normalized}`, "info")
410
415
  }
@@ -748,7 +753,7 @@ export default function ceCoreExtension(pi: ExtensionAPI) {
748
753
  pi.registerTool({
749
754
  name: contextHandoff.name,
750
755
  label: "Context Handoff",
751
- description: "Manage cross-stage context handoffs with evidence-first templates. Supports save (write handoff + state), load (read handoff + state), latest (read latest dated handoff), and status (read current state).",
756
+ description: "Manage cross-stage context handoffs with evidence-first templates. Supports save (write handoff + state), load (read handoff + state), latest (read latest dated handoff), status (read current state), and validate (check continuation readiness with deterministic probes).",
752
757
  parameters: contextHandoffParams,
753
758
  async execute(_toolCallId, params) {
754
759
  const result = await contextHandoff.execute({
@@ -768,6 +773,7 @@ export default function ceCoreExtension(pi: ExtensionAPI) {
768
773
  openDecisions: params.openDecisions,
769
774
  recentlyAccessedFiles: params.recentlyAccessedFiles,
770
775
  compressionRisk: params.compressionRisk,
776
+ activeRules: params.activeRules,
771
777
  })
772
778
 
773
779
  return {
@@ -887,4 +893,4 @@ export {
887
893
  export { normalizeSlug } from "./utils/name-utils"
888
894
  export { filterBashOutput } from "./tools/bash-output-filter"
889
895
  export { filterReadOutput } from "./tools/read-output-filter"
890
- export { COMPACTION_FOCUS_INSTRUCTIONS, TURN_PREFIX_FOCUS_INSTRUCTIONS } from "./tools/compaction-optimizer"
896
+ export { COMPACTION_FOCUS_INSTRUCTIONS } from "./tools/compaction-optimizer"
@@ -27,12 +27,4 @@ export const COMPACTION_FOCUS_INSTRUCTIONS = `Additional focus for this summary:
27
27
  6. If any tests were run, summarize results by: file, pass/fail count, and specific failure messages
28
28
  7. Note any blocked items and their exact error state`
29
29
 
30
- /**
31
- * Additional instructions for turn-prefix summaries (split turns).
32
- * These are more concise since the turn suffix is retained.
33
- */
34
- export const TURN_PREFIX_FOCUS_INSTRUCTIONS = `Focus the turn-prefix summary on:
35
- - What the user originally asked for
36
- - Key decisions made in the prefix
37
- - Exact file paths and identifiers needed to understand the retained suffix
38
- Skip reasoning details — only keep actionable context.`
30
+
@@ -5,8 +5,23 @@ import { normalizeSlug } from "../utils/name-utils"
5
5
 
6
6
  export type ContextHealth = "good" | "watch" | "heavy" | "critical"
7
7
 
8
+ export type ContextHandoffRecommendedAction = "continue" | "save_handoff" | "fill_required_context"
9
+
10
+ export interface ContextHandoffValidationCheck {
11
+ name: string
12
+ passed: boolean
13
+ reason: string
14
+ }
15
+
16
+ export interface ContextHandoffValidationProbes {
17
+ recall: boolean
18
+ continuation: boolean
19
+ artifact: boolean
20
+ decision: boolean
21
+ }
22
+
8
23
  export interface ContextHandoffInput {
9
- operation: "save" | "load" | "latest" | "status"
24
+ operation: "save" | "load" | "latest" | "status" | "validate"
10
25
  repoRoot: string
11
26
  currentStage?: string
12
27
  nextStage?: string
@@ -22,6 +37,7 @@ export interface ContextHandoffInput {
22
37
  openDecisions?: string[]
23
38
  recentlyAccessedFiles?: string[]
24
39
  compressionRisk?: string[]
40
+ activeRules?: string[]
25
41
  }
26
42
 
27
43
  export interface ContextStateEntry {
@@ -39,6 +55,7 @@ export interface ContextStateEntry {
39
55
  openDecisions: string[]
40
56
  recentlyAccessedFiles: string[]
41
57
  compressionRisk: string[]
58
+ activeRules: string[]
42
59
  recommendNewSession: boolean
43
60
  updatedAt: string
44
61
  }
@@ -62,7 +79,15 @@ export interface ContextHandoffResult {
62
79
  openDecisions?: string[]
63
80
  recentlyAccessedFiles?: string[]
64
81
  compressionRisk?: string[]
82
+ activeRules?: string[]
65
83
  updatedAt?: string
84
+ // Validation fields
85
+ ok?: boolean
86
+ probes?: ContextHandoffValidationProbes
87
+ checks?: ContextHandoffValidationCheck[]
88
+ missing?: string[]
89
+ warnings?: string[]
90
+ recommendedAction?: ContextHandoffRecommendedAction
66
91
  }
67
92
 
68
93
  function ceDir(repoRoot: string): string {
@@ -132,6 +157,7 @@ function buildDefaultHandoffMarkdown(input: {
132
157
  openDecisions: string[]
133
158
  recentlyAccessedFiles: string[]
134
159
  compressionRisk: string[]
160
+ activeRules: string[]
135
161
  }): string {
136
162
  const currentTask = input.nextStage
137
163
  ? `Continue from ${input.currentStage} to ${input.nextStage}.`
@@ -172,6 +198,9 @@ function buildDefaultHandoffMarkdown(input: {
172
198
  "## Active Files",
173
199
  activeFiles,
174
200
  "",
201
+ "## Active Rules",
202
+ formatBullets(input.activeRules),
203
+ "",
175
204
  "## Recently Accessed Files",
176
205
  formatBullets(input.recentlyAccessedFiles),
177
206
  "",
@@ -223,6 +252,7 @@ function normalizeStateEntry(raw: unknown): ContextStateEntry | null {
223
252
  ? toStringArray(state.recentlyAccessedFiles)
224
253
  : activeFiles.slice(0, 5),
225
254
  compressionRisk: toStringArray(state.compressionRisk),
255
+ activeRules: toStringArray(state.activeRules),
226
256
  recommendNewSession: typeof state.recommendNewSession === "boolean" ? state.recommendNewSession : false,
227
257
  updatedAt: typeof state.updatedAt === "string" ? state.updatedAt : new Date(0).toISOString(),
228
258
  }
@@ -268,6 +298,8 @@ export function createContextHandoffTool() {
268
298
  return latest(input)
269
299
  case "status":
270
300
  return status(input)
301
+ case "validate":
302
+ return validate(input)
271
303
  default:
272
304
  throw new Error(`Unknown operation: ${input.operation}`)
273
305
  }
@@ -290,6 +322,7 @@ async function save(input: ContextHandoffInput): Promise<ContextHandoffResult> {
290
322
  ? input.recentlyAccessedFiles
291
323
  : activeFiles.slice(0, 5)
292
324
  const compressionRisk = input.compressionRisk ?? []
325
+ const activeRules = input.activeRules ?? []
293
326
 
294
327
  const handoffMarkdown = input.handoffMarkdown?.trim().length
295
328
  ? input.handoffMarkdown
@@ -305,6 +338,7 @@ async function save(input: ContextHandoffInput): Promise<ContextHandoffResult> {
305
338
  openDecisions,
306
339
  recentlyAccessedFiles,
307
340
  compressionRisk,
341
+ activeRules,
308
342
  })
309
343
 
310
344
  const recommendNewSession = computeRecommendNewSession(currentStage, nextStage, contextHealth)
@@ -332,6 +366,7 @@ async function save(input: ContextHandoffInput): Promise<ContextHandoffResult> {
332
366
  openDecisions,
333
367
  recentlyAccessedFiles,
334
368
  compressionRisk,
369
+ activeRules,
335
370
  recommendNewSession,
336
371
  updatedAt: new Date().toISOString(),
337
372
  }
@@ -355,6 +390,7 @@ async function save(input: ContextHandoffInput): Promise<ContextHandoffResult> {
355
390
  openDecisions,
356
391
  recentlyAccessedFiles,
357
392
  compressionRisk,
393
+ activeRules,
358
394
  recommendNewSession,
359
395
  updatedAt: state.updatedAt,
360
396
  }
@@ -395,6 +431,7 @@ async function load(input: ContextHandoffInput): Promise<ContextHandoffResult> {
395
431
  openDecisions: state.openDecisions,
396
432
  recentlyAccessedFiles: state.recentlyAccessedFiles,
397
433
  compressionRisk: state.compressionRisk,
434
+ activeRules: state.activeRules,
398
435
  recommendNewSession: state.recommendNewSession,
399
436
  handoffMarkdown: markdown,
400
437
  updatedAt: state.updatedAt,
@@ -429,11 +466,278 @@ async function latest(input: ContextHandoffInput): Promise<ContextHandoffResult>
429
466
  openDecisions: state.openDecisions,
430
467
  recentlyAccessedFiles: state.recentlyAccessedFiles,
431
468
  compressionRisk: state.compressionRisk,
469
+ activeRules: state.activeRules,
432
470
  recommendNewSession: state.recommendNewSession,
433
471
  updatedAt: state.updatedAt,
434
472
  }
435
473
  }
436
474
 
475
+ const PLACEHOLDER_VALUES = new Set(["n/a", "na", "not run", "none", "", "-"])
476
+ const PLACEHOLDER_PREFIXES = ["- n/a", "- na", "- not run", "- none", "- "]
477
+
478
+ function isPlaceholder(text: string): boolean {
479
+ const trimmed = text.trim().toLowerCase()
480
+ if (PLACEHOLDER_VALUES.has(trimmed)) return true
481
+ for (const prefix of PLACEHOLDER_PREFIXES) {
482
+ if (trimmed === prefix) return true
483
+ }
484
+ return false
485
+ }
486
+
487
+ function isMeaningfulText(value?: string): boolean {
488
+ return Boolean(value && !isPlaceholder(value))
489
+ }
490
+
491
+ function toPublicHandoffPath(repoRoot: string, filePath: string): string {
492
+ return path.isAbsolute(filePath) ? toRepoRelative(repoRoot, filePath) : filePath
493
+ }
494
+
495
+ function extractSection(markdown: string, heading: string): string {
496
+ const lines = markdown.split("\n")
497
+ const sectionLines: string[] = []
498
+ let inSection = false
499
+
500
+ for (const line of lines) {
501
+ if (line.startsWith("## ")) {
502
+ if (inSection) break
503
+ if (line.slice(3).trim() === heading) {
504
+ inSection = true
505
+ }
506
+ continue
507
+ }
508
+ if (inSection) {
509
+ sectionLines.push(line)
510
+ }
511
+ }
512
+
513
+ return sectionLines.join("\n").trim()
514
+ }
515
+
516
+ function sectionHasMeaningfulContent(markdown: string, heading: string): boolean {
517
+ const section = extractSection(markdown, heading)
518
+ if (!section) return false
519
+
520
+ const lines = section.split("\n")
521
+ for (const line of lines) {
522
+ const trimmed = line.trim()
523
+ if (!trimmed) continue
524
+ if (!isPlaceholder(trimmed)) return true
525
+ }
526
+ return false
527
+ }
528
+
529
+ function hasMeaningfulArray(arr: string[]): boolean {
530
+ return arr.some(item => isMeaningfulText(item))
531
+ }
532
+
533
+ function hasMeaningfulRecord(rec: Record<string, string | undefined>): boolean {
534
+ return Object.values(rec).some(isMeaningfulText)
535
+ }
536
+
537
+ function isMeaningfulStage(value?: string): boolean {
538
+ return Boolean(value && value.trim().length > 0 && value !== "unknown" && !isPlaceholder(value))
539
+ }
540
+
541
+ async function validate(input: ContextHandoffInput): Promise<ContextHandoffResult> {
542
+ const checks: ContextHandoffValidationCheck[] = []
543
+ const missing: string[] = []
544
+ const warnings: string[] = []
545
+
546
+ // Read state
547
+ const state = await readState(input.repoRoot)
548
+ const hasState = state !== null
549
+
550
+ // Read markdown
551
+ let markdown = ""
552
+ let hasMarkdown = false
553
+ let handoffFile = ""
554
+
555
+ if (input.handoffPath) {
556
+ const absolutePath = resolveRepoPath(input.repoRoot, input.handoffPath)
557
+ if (existsSync(absolutePath)) {
558
+ markdown = await readFile(absolutePath, "utf8")
559
+ hasMarkdown = markdown.trim().length > 0
560
+ handoffFile = toPublicHandoffPath(input.repoRoot, input.handoffPath)
561
+ }
562
+ }
563
+
564
+ if (!hasMarkdown && state?.latestHandoffPath) {
565
+ const absolutePath = resolveRepoPath(input.repoRoot, state.latestHandoffPath)
566
+ if (existsSync(absolutePath)) {
567
+ markdown = await readFile(absolutePath, "utf8")
568
+ hasMarkdown = markdown.trim().length > 0
569
+ handoffFile = toPublicHandoffPath(input.repoRoot, state.latestHandoffPath)
570
+ }
571
+ }
572
+
573
+ if (!hasMarkdown) {
574
+ const latestPath = latestHandoffPath(input.repoRoot)
575
+ if (existsSync(latestPath)) {
576
+ markdown = await readFile(latestPath, "utf8")
577
+ hasMarkdown = markdown.trim().length > 0
578
+ handoffFile = toRepoRelative(input.repoRoot, latestPath)
579
+ }
580
+ }
581
+
582
+ const found = hasState || hasMarkdown
583
+
584
+ checks.push({
585
+ name: "state_exists",
586
+ passed: hasState,
587
+ reason: hasState ? "Found context-state.json" : "No context-state.json found",
588
+ })
589
+
590
+ checks.push({
591
+ name: "handoff_exists",
592
+ passed: hasMarkdown,
593
+ reason: hasMarkdown ? "Found handoff markdown" : "No handoff markdown found",
594
+ })
595
+
596
+ // --- Recall probe ---
597
+ const hasCurrentTruth = state ? hasMeaningfulArray(state.currentTruth) : false
598
+ const hasCurrentStage = state ? isMeaningfulStage(state.currentStage) : false
599
+ const hasCurrentTaskSection = hasMarkdown && sectionHasMeaningfulContent(markdown, "Current Task")
600
+
601
+ const recallPass = hasCurrentTruth || (hasCurrentStage && (hasMarkdown || (state?.nextStage !== undefined))) || hasCurrentTaskSection
602
+
603
+ checks.push({
604
+ name: "recall_current_truth",
605
+ passed: hasCurrentTruth,
606
+ reason: hasCurrentTruth ? `Found ${state!.currentTruth.length} current truth entries` : "No current truth entries",
607
+ })
608
+
609
+ checks.push({
610
+ name: "recall_current_task",
611
+ passed: hasCurrentTaskSection,
612
+ reason: hasCurrentTaskSection ? "Current Task section has meaningful content" : "Current Task section is missing or placeholder",
613
+ })
614
+
615
+ checks.push({
616
+ name: "recall_current_stage",
617
+ passed: hasCurrentStage,
618
+ reason: hasCurrentStage ? `Current stage: ${state!.currentStage}` : "No meaningful current stage",
619
+ })
620
+
621
+ if (!recallPass) {
622
+ missing.push("recall: current task or goal evidence")
623
+ }
624
+
625
+ // --- Continuation probe ---
626
+ // Tightened: only actionable next-step evidence passes
627
+ const hasNextStage = state ? isMeaningfulStage(state.nextStage) : false
628
+ const hasNextMinimalStep = hasMarkdown && sectionHasMeaningfulContent(markdown, "Next Minimal Step")
629
+
630
+ const continuationPass = hasNextMinimalStep || hasNextStage
631
+
632
+ checks.push({
633
+ name: "continuation_next_stage",
634
+ passed: hasNextStage,
635
+ reason: hasNextStage ? `Next stage: ${state!.nextStage}` : "No meaningful next stage",
636
+ })
637
+
638
+ checks.push({
639
+ name: "continuation_next_minimal_step",
640
+ passed: hasNextMinimalStep,
641
+ reason: hasNextMinimalStep ? "Next Minimal Step has meaningful content" : "Next Minimal Step is missing or placeholder",
642
+ })
643
+
644
+ // Diagnostic checks (do not affect continuation pass)
645
+ const hasBlocker = state ? Boolean(state.blocker && !isPlaceholder(state.blocker)) : false
646
+ const hasVerification = state ? Boolean(state.verification && !isPlaceholder(state.verification)) : false
647
+ const hasVerificationSection = hasMarkdown && sectionHasMeaningfulContent(markdown, "Verification")
648
+
649
+ checks.push({
650
+ name: "continuation_blocker",
651
+ passed: hasBlocker,
652
+ reason: hasBlocker ? `Blocker: ${state!.blocker}` : "No blocker information",
653
+ })
654
+
655
+ checks.push({
656
+ name: "continuation_verification",
657
+ passed: hasVerification || hasVerificationSection,
658
+ reason: (hasVerification || hasVerificationSection) ? "Verification evidence present" : "No verification information",
659
+ })
660
+
661
+ if (!continuationPass) {
662
+ missing.push("continuation: next minimal step or next stage evidence")
663
+ }
664
+
665
+ // --- Artifact probe ---
666
+ const hasActiveFiles = state ? hasMeaningfulArray(state.activeFiles) : false
667
+ const hasRecentlyAccessed = state ? hasMeaningfulArray(state.recentlyAccessedFiles) : false
668
+ const hasArtifacts = state ? hasMeaningfulRecord(state.artifacts) : false
669
+ const hasActiveFilesSection = hasMarkdown && sectionHasMeaningfulContent(markdown, "Active Files")
670
+ const hasRecentlyAccessedSection = hasMarkdown && sectionHasMeaningfulContent(markdown, "Recently Accessed Files")
671
+ const hasArtifactsSection = hasMarkdown && sectionHasMeaningfulContent(markdown, "Artifacts")
672
+
673
+ const artifactPass = hasActiveFiles || hasRecentlyAccessed || hasArtifacts
674
+ || hasActiveFilesSection || hasRecentlyAccessedSection || hasArtifactsSection
675
+
676
+ checks.push({
677
+ name: "artifact_active_files",
678
+ passed: hasActiveFiles || hasActiveFilesSection,
679
+ reason: (hasActiveFiles || hasActiveFilesSection) ? "Active files present" : "No active files",
680
+ })
681
+
682
+ if (!artifactPass) {
683
+ warnings.push("artifact: active files or artifacts are missing")
684
+ }
685
+
686
+ // --- Decision probe ---
687
+ const hasOpenDecisions = state ? hasMeaningfulArray(state.openDecisions) : false
688
+ const hasInvalidatedAssumptions = state ? hasMeaningfulArray(state.invalidatedAssumptions) : false
689
+ const hasOpenDecisionsSection = hasMarkdown && sectionHasMeaningfulContent(markdown, "Open Decisions")
690
+ const hasInvalidatedSection = hasMarkdown && sectionHasMeaningfulContent(markdown, "Invalidated Assumptions")
691
+ const hasCurrentTruthSection = hasMarkdown && sectionHasMeaningfulContent(markdown, "Current Truth")
692
+
693
+ const decisionPass = hasOpenDecisions || hasInvalidatedAssumptions || hasCurrentTruth
694
+ || hasOpenDecisionsSection || hasInvalidatedSection || hasCurrentTruthSection
695
+
696
+ checks.push({
697
+ name: "decision_open_decisions",
698
+ passed: hasOpenDecisions || hasOpenDecisionsSection,
699
+ reason: (hasOpenDecisions || hasOpenDecisionsSection) ? "Open decisions present" : "No open decisions",
700
+ })
701
+
702
+ if (!decisionPass) {
703
+ warnings.push("decision: decisions or invalidated assumptions are missing")
704
+ }
705
+
706
+ // --- ok derivation ---
707
+ const ok = recallPass && continuationPass
708
+
709
+ // --- recommended action ---
710
+ let recommendedAction: ContextHandoffRecommendedAction
711
+ if (!found) {
712
+ recommendedAction = "save_handoff"
713
+ } else if (!ok) {
714
+ recommendedAction = "fill_required_context"
715
+ } else {
716
+ recommendedAction = "continue"
717
+ }
718
+
719
+ return {
720
+ operation: "validate",
721
+ found,
722
+ ok,
723
+ path: handoffFile || undefined,
724
+ probes: {
725
+ recall: recallPass,
726
+ continuation: continuationPass,
727
+ artifact: artifactPass,
728
+ decision: decisionPass,
729
+ },
730
+ checks,
731
+ missing,
732
+ warnings,
733
+ recommendedAction,
734
+ currentStage: state?.currentStage,
735
+ nextStage: state?.nextStage,
736
+ contextHealth: state?.contextHealth,
737
+ updatedAt: state?.updatedAt,
738
+ }
739
+ }
740
+
437
741
  async function status(input: ContextHandoffInput): Promise<ContextHandoffResult> {
438
742
  const state = await readState(input.repoRoot)
439
743
 
@@ -465,6 +769,7 @@ async function status(input: ContextHandoffInput): Promise<ContextHandoffResult>
465
769
  openDecisions: state.openDecisions,
466
770
  recentlyAccessedFiles: state.recentlyAccessedFiles,
467
771
  compressionRisk: state.compressionRisk,
772
+ activeRules: state.activeRules,
468
773
  recommendNewSession: state.recommendNewSession,
469
774
  updatedAt: state.updatedAt,
470
775
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leing2021/super-pi",
3
- "version": "0.23.2",
3
+ "version": "0.23.4",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Pi-native Compound Engineering package for iterative development workflows",
@@ -39,13 +39,14 @@ Every unit follows **RED → GREEN → REFACTOR**:
39
39
 
40
40
  ## Planning flow
41
41
 
42
- 1. Read relevant brainstorm from `docs/brainstorms/`
43
- 2. Run solution search (keywords → grep frontmatter → read top 3)
44
- 3. Gather repository context
45
- 4. If plan exists: use `plan_diff` `compare` → review with user → `patch`
46
- 5. If no plan: write new plan under `docs/plans/` using `references/plan-template.md`
47
- 6. Structure work using `references/implementation-unit-template.md`
48
- 7. Verify every unit follows TDD gates
42
+ 1. **Load context**: consume latest handoff before any broad file reads — `context_handoff load` or read `.context/compound-engineering/handoffs/latest.md`. If found, use `activeFiles` and `blocker` as starting point. If not found, proceed normally (new project).
43
+ 2. Read relevant brainstorm from `docs/brainstorms/`
44
+ 3. Run solution search (keywords → grep frontmatter → read top 3)
45
+ 4. Gather repository context
46
+ 5. If plan exists: use `plan_diff` `compare` review with user → `patch`
47
+ 6. If no plan: write new plan under `docs/plans/` using `references/plan-template.md`
48
+ 7. Structure work using `references/implementation-unit-template.md`
49
+ 8. Verify every unit follows TDD gates
49
50
 
50
51
  ## Optional: CEO Review
51
52
 
@@ -41,16 +41,18 @@ Every step follows **RED → GREEN → REFACTOR**:
41
41
 
42
42
  ## Workflow
43
43
 
44
- 1. Detect input type (plan path vs bare prompt)
45
- 2. Read implementation units if plan path
46
- 3. Load `session_checkpoint` to skip completed units
47
- 4. Use `task_splitter` for dependency analysis
48
- 5. Execute: **inline mode** by default, `ce_parallel_subagent` for independent units
49
- 6. Follow TDD per unit: RED minimal code GREEN refactor → unit-level **verification**
50
- 7. Record progress via `references/progress-update-format.md`
51
- 8. Save `session_checkpoint` after each unit
52
- 9. On failure: `session_checkpoint` `fail` `retry` → follow strategy
53
- 10. Provide completion report (see `references/completion-report.md`)
54
- 11. Handoff to `04-review` using `references/handoff.md`
44
+ 1. **Load context**: consume latest handoff before any broad file reads — `context_handoff load` or read `.context/compound-engineering/handoffs/latest.md`. If found, use `activeFiles`, `blocker`, `verification`, `activeRules` as starting point. If not found, proceed normally.
45
+ 2. Detect input type (plan path vs bare prompt)
46
+ 3. Read implementation units if plan path
47
+ 4. Load `session_checkpoint` to skip completed units
48
+ 5. Use `task_splitter` for dependency analysis
49
+ 6. Execute: **inline mode** by default, `ce_parallel_subagent` for independent units
50
+ 7. Follow TDD per unit: RED → minimal code → GREEN → refactor → unit-level **verification**
51
+ 8. Record progress via `references/progress-update-format.md`
52
+ 9. Save `session_checkpoint` after each unit
53
+ 10. On failure: `session_checkpoint` `fail` `retry` → follow strategy
54
+ 11. Provide completion report (see `references/completion-report.md`)
55
+ 12. **Save handoff**: `context_handoff save` with current stage, next stage, activeFiles, blocker, verification, activeRules
56
+ 13. Handoff to `04-review` using `references/handoff.md`
55
57
 
56
58
  Before finishing this skill, apply the completion checklist in [shared pipeline instructions](../references/pipeline-config.md).
@@ -46,14 +46,15 @@ Code review is **technical evaluation**, not social performance:
46
46
 
47
47
  ## Workflow
48
48
 
49
- 1. Determine diff scope from branch or explicit target
50
- 2. Collect stats (files, insertions, deletions) call `review_router`
51
- 3. Read matching plan artifact
52
- 4. Run solution search
53
- 5. Apply each reviewer persona from `review_router`
54
- 6. Merge into structured findings
55
- 7. Verify each finding against codebase
56
- 8. Apply autofixes, re-run tests, re-review if needed
49
+ 1. **Load context**: consume latest handoff before any broad file reads — `context_handoff load` or read `.context/compound-engineering/handoffs/latest.md`. If found, use `activeFiles`, `artifacts.plan` as starting point. If not found, proceed normally.
50
+ 2. Determine diff scope from branch or explicit target
51
+ 3. Collect stats (files, insertions, deletions) → call `review_router`
52
+ 4. Read matching plan artifact
53
+ 5. Run solution search
54
+ 6. Apply each reviewer persona from `review_router`
55
+ 7. Merge into structured findings
56
+ 8. Verify each finding against codebase
57
+ 9. Apply autofixes, re-run tests, re-review if needed
57
58
 
58
59
  ## Optional: QA Test Mode
59
60
 
@@ -22,8 +22,9 @@ Use this skill when the user wants to know what to run next in the Compound Engi
22
22
  When the user asks "what should I do next?", "continue", or runs `/skill:06-next`:
23
23
 
24
24
  1. Call `workflow_state` with the repo root
25
- 2. Apply recommendation logic from `references/recommendation-logic.md`
26
- 3. Return: skill name, reason (1-2 lines), brief workflow state summary
25
+ 2. Inspect `workflow_state.context` first — apply **context-first priority chain** from `references/recommendation-logic.md` (health → blocker → recommendNewSession → nextStage → mismatch → fallback)
26
+ 3. If no context signals trigger, fall back to artifact-count rules
27
+ 4. Return: skill name, reason (1-2 lines), brief workflow state summary
27
28
 
28
29
  ### Verbose mode: "full status"
29
30
 
@@ -31,7 +32,8 @@ When the user asks "show status", "what's the current state", or uses `--verbose
31
32
 
32
33
  1. Call `workflow_state` with the repo root
33
34
  2. Call `session_history` with `latest` operation
34
- 3. Return: latest artifacts (path + summary), status of each phase, recommended next step
35
+ 3. Include context health assessment from `workflow_state.context` in the status report
36
+ 4. Return: latest artifacts (path + summary), status of each phase, context health, recommended next step
35
37
 
36
38
  ## Artifact locations
37
39
 
@@ -1,6 +1,51 @@
1
1
  # Recommendation logic
2
2
 
3
- Apply these rules in order. Return the first match.
3
+ Apply these rules in strict priority order. Return the first match.
4
+
5
+ ## Context-first priority chain
6
+
7
+ `06-next` must call `workflow_state` and inspect `workflow_state.context` **before** applying artifact-count rules. Context health and runtime state override stale artifact counts.
8
+
9
+ Field mapping: the requirement uses `context.health` as a conceptual alias. Use the actual field name `workflow_state.context.contextHealth`.
10
+
11
+ ### Priority 1: Critical context health
12
+
13
+ If `context.contextHealth` is `"critical"`:
14
+ - Recommend: save a `context_handoff` with full handoff-lite template, then open a **new session**.
15
+ - Reason: Current session context is critically inflated. Continuing will degrade decision quality and waste tokens.
16
+ - Output: handoff-save guidance + copyable new-session prompt with repo path and artifact references.
17
+
18
+ ### Priority 2: Active blocker
19
+
20
+ If `context.blocker` exists and is not `"N/A"` or a placeholder:
21
+ - Recommend: return to the current stage (`context.currentStage`) and resolve the blocker before advancing.
22
+ - Reason: A known blocker exists in the current pipeline stage.
23
+ - Output: `/skill:<currentStage>` with blocker description.
24
+
25
+ ### Priority 3: New session recommended
26
+
27
+ If `context.recommendNewSession === true`:
28
+ - Recommend: open a new Pi session.
29
+ - Reason: The previous stage determined that context health + phase transition warrant a fresh session.
30
+ - Output: copyable new-session prompt referencing `context.latestHandoffPath`, artifacts, and next stage.
31
+
32
+ ### Priority 4: Explicit next stage
33
+
34
+ If `context.nextStage` exists, is meaningful, and differs from `context.currentStage`:
35
+ - Recommend: `/skill:<context.nextStage>`.
36
+ - Reason: The previous stage explicitly requested this transition.
37
+ - Output: recommended skill command.
38
+
39
+ ### Priority 5: Stage mismatch detection
40
+
41
+ If `context.currentStage` does not match the most recent artifact state (e.g. a plan exists but context says `"01-brainstorm"`):
42
+ - Recommend: the skill that matches the most recent artifact, or ask the user to clarify.
43
+ - Reason: Runtime context and artifact state are out of sync.
44
+ - Output: suggested correction with explanation of the mismatch.
45
+
46
+ ### Priority 6: Fallback — artifact-count rules
47
+
48
+ If none of the above context rules triggered, fall back to the artifact-count rules below.
4
49
 
5
50
  ## Rule 1: No brainstorm artifacts
6
51
 
@@ -18,7 +18,34 @@ Supported `modelStrategy` formats in `.pi/settings.json`:
18
18
  - Full reference: `"02-plan": "anthropic/claude-opus-4-1"`
19
19
  - Bare model id (reuses current provider): `"02-plan": "claude-opus-4-1"`
20
20
 
21
- ## End of skill: status + context
21
+ ## Start of skill: context loading
22
+
23
+ Before reading any project files or running repository-wide scans, load the most recent handoff:
24
+
25
+ 1. Try `context_handoff load` or `context_handoff latest` first.
26
+ 2. Fallback: `read .context/compound-engineering/handoffs/latest.md` if tool is unavailable.
27
+ 3. **Handoff found?** Consume its `activeFiles`, `blocker`, `verification`, `currentTruth`, `activeRules` before any broad project reads.
28
+ 4. **No handoff?** Proceed normally — this is a new project or first run. Do not block.
29
+
30
+ Core principle: **consume handoff before broad project file reads** — a single handoff read (~500 tokens) avoids 5-10 project file scans (~5K-10K tokens).
31
+
32
+ ## End of skill: save handoff + status + context
33
+
34
+ Every Phase 1 skill (02-plan through 05-learn) must save context handoff at completion:
35
+
36
+ ```
37
+ context_handoff save
38
+ currentStage: <stageKey>
39
+ nextStage: <next stage>
40
+ contextHealth: good | watch | heavy | critical
41
+ activeFiles: [1-5 currently active paths]
42
+ blocker: <blocker or N/A>
43
+ verification: <latest command + result>
44
+ activeRules: [1-5 rules critical for continuation]
45
+ currentTruth: [validated truths]
46
+ ```
47
+
48
+ If `context_handoff` is unavailable, manually write the Handoff-lite template to `.context/compound-engineering/handoffs/latest.md`.
22
49
 
23
50
  Before final completion, always output these blocks (replace placeholders with real values, never output angle-bracket placeholders literally):
24
51