@mgiles/perk 3.1.0 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -0
- package/extension/authoring/gist/draft.ts +198 -0
- package/extension/authoring/gist/prose.ts +46 -0
- package/extension/authoring/gist/review.ts +133 -0
- package/extension/authoring/gist/save.ts +118 -0
- package/extension/authoring/objective/draft.ts +345 -0
- package/extension/{factories/objectiveDreamReport.ts → authoring/objective/dreamReportGate.ts} +74 -131
- package/extension/authoring/objective/planning.ts +124 -0
- package/extension/authoring/objective/prose.ts +103 -0
- package/extension/authoring/objective/review.ts +128 -0
- package/extension/authoring/objective/save.ts +224 -0
- package/extension/authoring/plan/draft.ts +84 -0
- package/extension/authoring/plan/prose.ts +41 -0
- package/extension/authoring/plan/review.ts +269 -0
- package/extension/authoring/plan/save.ts +256 -0
- package/extension/authoring/plan/source.ts +82 -0
- package/extension/authoring/refinement/context.ts +468 -0
- package/extension/authoring/refinement/draft.ts +261 -0
- package/extension/authoring/refinement/prose.ts +79 -0
- package/extension/authoring/refinement/review.ts +111 -0
- package/extension/authoring/refinement/save.ts +119 -0
- package/extension/authoring/review/approvalGate.ts +34 -0
- package/extension/authoring/review/draftContext.ts +68 -0
- package/extension/codeReview/automated.ts +352 -0
- package/extension/codeReview/submission.ts +229 -0
- package/extension/delivery/address.ts +295 -0
- package/extension/delivery/ci.ts +355 -0
- package/extension/delivery/commitCompact.ts +93 -0
- package/extension/delivery/conflictResolution.ts +247 -0
- package/extension/delivery/ready.ts +193 -0
- package/extension/delivery/stackConflict.ts +361 -0
- package/extension/delivery/stackObjective.ts +16 -0
- package/extension/delivery/stackReconcile.ts +165 -0
- package/extension/delivery/submit.ts +171 -0
- package/extension/index.ts +369 -359
- package/extension/learning/analystWave.ts +324 -0
- package/extension/learning/audit.ts +667 -0
- package/extension/learning/capture.ts +92 -0
- package/extension/learning/containment.ts +104 -0
- package/extension/{waves/dreamWave.ts → learning/dream.ts} +112 -94
- package/extension/learning/dreamAnalysis.ts +435 -0
- package/extension/{waves/dreamReducerWave.ts → learning/dreamReducer.ts} +46 -41
- package/extension/{waves → learning}/dreamReport.ts +35 -31
- package/extension/learning/harvest.ts +491 -0
- package/extension/learning/prose.ts +66 -0
- package/extension/learning/routing.ts +79 -0
- package/extension/pi/v1/bashScanTimeout.ts +64 -0
- package/extension/{doors/prReview.ts → pi/v1/codeReview/automated.ts} +215 -310
- package/extension/{doors/prReviewBrowser.ts → pi/v1/codeReview/browser.ts} +125 -57
- package/extension/{doors/hunkHandoff.ts → pi/v1/codeReview/checkout.ts} +12 -8
- package/extension/{doors/reviewWaveTools.ts → pi/v1/codeReview/reviewWave.ts} +169 -116
- package/extension/pi/v1/codeReview/stack.ts +606 -0
- package/extension/pi/v1/codeReview/submit.ts +354 -0
- package/extension/{doors/prReviewTerminal.ts → pi/v1/codeReview/terminal.ts} +32 -27
- package/extension/pi/v1/contextEvidence.ts +80 -0
- package/extension/pi/v1/contextInjection.ts +207 -0
- package/extension/{doors → pi/v1/delivery}/address.ts +163 -265
- package/extension/pi/v1/delivery/ci.ts +570 -0
- package/extension/pi/v1/delivery/commitCompact.ts +201 -0
- package/extension/pi/v1/delivery/conflictResolverEngine.ts +425 -0
- package/extension/{doors → pi/v1/delivery}/land.ts +128 -60
- package/extension/pi/v1/delivery/ready.ts +322 -0
- package/extension/pi/v1/delivery/stackConflictResolver.ts +172 -0
- package/extension/pi/v1/delivery/stackDrive.ts +120 -0
- package/extension/pi/v1/delivery/stackLand.ts +223 -0
- package/extension/pi/v1/delivery/stackRecover.ts +265 -0
- package/extension/pi/v1/delivery/stackStatus.ts +237 -0
- package/extension/pi/v1/delivery/stackSync.ts +658 -0
- package/extension/pi/v1/delivery/submit.ts +389 -0
- package/extension/pi/v1/delivery/submitConflict.ts +186 -0
- package/extension/pi/v1/draftReview.ts +431 -0
- package/extension/{doors → pi/v1}/draftReviewWaveTools.ts +141 -151
- package/extension/pi/v1/gist.ts +794 -0
- package/extension/pi/v1/learning/audit.ts +186 -0
- package/extension/pi/v1/learning/dream.ts +207 -0
- package/extension/{doors/learnFactory.ts → pi/v1/learning/factory.ts} +18 -65
- package/extension/{doors/harvestWaveTools.ts → pi/v1/learning/harvest.ts} +46 -100
- package/extension/pi/v1/learning/learn.ts +585 -0
- package/extension/{doors → pi/v1}/lifecycleGates.ts +14 -59
- package/extension/{factories → pi/v1}/objective.ts +53 -33
- package/extension/pi/v1/objectiveAuthoring.ts +672 -0
- package/extension/pi/v1/objectiveDreamGate.ts +160 -0
- package/extension/pi/v1/objectivePlanning.ts +776 -0
- package/extension/pi/v1/objectiveRefinement.ts +1320 -0
- package/extension/pi/v1/objectiveReview.ts +451 -0
- package/extension/{doors → pi/v1}/objectiveReviewBrowser.ts +259 -172
- package/extension/pi/v1/plan.ts +812 -0
- package/extension/pi/v1/planReview.ts +820 -0
- package/extension/{doors → pi/v1}/planReviewBrowser.ts +228 -152
- package/extension/{doors/annotationPush.ts → pi/v1/providers/annotations.ts} +158 -89
- package/extension/pi/v1/providers/plannotator.ts +487 -0
- package/extension/{doors → pi/v1/providers}/plannotatorHandoff.ts +152 -34
- package/extension/pi/v1/providers/selection.ts +43 -0
- package/extension/{adapters/planAdapterTombell.ts → pi/v1/providers/tombell.ts} +43 -72
- package/extension/pi/v1/review.ts +538 -0
- package/extension/pi/v1/reviewOutcome.ts +9 -0
- package/extension/pi/v1/scoutWave.ts +318 -0
- package/extension/{doors → pi/v1}/selfcheck.ts +4 -4
- package/extension/session/branchWorkflowSession.ts +60 -0
- package/extension/session/lifecycle.ts +644 -0
- package/extension/session/lifecycleGates.ts +64 -0
- package/extension/session/saveDestination.ts +87 -0
- package/extension/session/workflowSession.ts +971 -0
- package/extension/substrate/agentScratch.ts +27 -54
- package/extension/substrate/bashScanTimeout.ts +181 -0
- package/extension/substrate/bindingDelivery.ts +38 -30
- package/extension/substrate/bindings.ts +4 -5
- package/extension/substrate/cache.ts +64 -12
- package/extension/substrate/childRestrictions.ts +39 -0
- package/extension/substrate/coldDoor.ts +17 -1
- package/extension/substrate/config.ts +157 -19
- package/extension/substrate/git.ts +88 -6
- package/extension/substrate/modelVisible.ts +53 -0
- package/extension/substrate/paths.ts +2 -7
- package/extension/substrate/prompts.ts +22 -0
- package/extension/substrate/registry.ts +2 -0
- package/extension/substrate/resolverLease.ts +364 -0
- package/extension/substrate/sessionData.ts +85 -152
- package/extension/substrate/toolGating.ts +279 -84
- package/extension/substrate/unifiedDiff.ts +1 -1
- package/extension/substrate/workflowState.ts +191 -166
- package/extension/substrate/worktreeResolverLock.ts +261 -0
- package/extension/surfaces/surfaces.ts +79 -27
- package/extension/waves/adversarialReviewWave.ts +103 -48
- package/extension/waves/blockedReports.ts +59 -0
- package/extension/waves/draftReviewWave.ts +42 -42
- package/extension/waves/laneIdentity.ts +77 -0
- package/extension/waves/objectiveExplorerWave.ts +24 -24
- package/extension/waves/prReviewWave.ts +89 -77
- package/extension/waves/reportWave.ts +438 -578
- package/extension/waves/reviewClassifierWave.ts +22 -22
- package/extension/waves/rpcAdapter.ts +100 -15
- package/extension/waves/scoutWave.ts +192 -0
- package/extension/waves/transport.ts +480 -0
- package/extension/worker/sdkAdapter.ts +494 -0
- package/extension/worker/stageExecution.ts +679 -0
- package/extension/workerMain.ts +18 -19
- package/package.json +6 -4
- package/prompts/_fixtures/live.yaml +98 -10
- package/prompts/contexts/adapters/plannotator-gist.md +6 -0
- package/prompts/contexts/adapters/plannotator-objective.md +6 -0
- package/prompts/contexts/adapters/plannotator-plan.md +8 -1
- package/prompts/contexts/adapters/plannotator-refinement.md +22 -0
- package/prompts/contexts/adapters/tombell-plan.md +4 -0
- package/prompts/contexts/objective-refinement.md +17 -0
- package/prompts/contexts/plan-authoring.md +6 -5
- package/prompts/contexts/read-only.md +1 -1
- package/prompts/stages/conflict-resolution-continuation.md +9 -0
- package/prompts/stages/conflict-resolution.md +4 -4
- package/prompts/stages/objective-author/adopt.md +1 -1
- package/prompts/stages/objective-author/file.md +1 -1
- package/prompts/stages/objective-author/seed.md +1 -1
- package/prompts/stages/objective-plan/guidance.md +2 -2
- package/prompts/stages/objective-plan/seed.md +9 -1
- package/prompts/stages/objective-reconcile-ready.md +7 -0
- package/prompts/stages/objective-reconcile.md +1 -1
- package/prompts/stages/objective-refine/seed.md +18 -0
- package/prompts/stages/objective-review-browser.md +4 -4
- package/prompts/stages/objective-sync.md +1 -1
- package/prompts/stages/plan-review-browser.md +4 -4
- package/prompts/stages/pr-review-browser/active.md +3 -4
- package/prompts/stages/pr-review-browser/foreign.md +3 -4
- package/prompts/stages/pr-review-terminal/active.md +3 -3
- package/prompts/stages/pr-review-terminal/foreign.md +3 -3
- package/prompts/stages/pr-review.md +3 -3
- package/prompts/stages/stack-review/cold.md +1 -0
- package/prompts/stages/stack-review-browser/stack.md +22 -0
- package/shared/README.md +8 -3
- package/shared/bindings.yaml +6 -3
- package/shared/contracts.md +4499 -2147
- package/shared/fixtures/issues-table.json +130 -0
- package/shared/registry.yaml +29 -1
- package/shared/schemas/outputs/objective-node-engagement.schema.json +318 -0
- package/shared/schemas/outputs/objective-stack-status.schema.json +178 -2
- package/shared/schemas/outputs/pr-ready.schema.json +110 -2
- package/shared/schemas/outputs/pr-review-context.schema.json +54 -9
- package/shared/schemas/outputs/pr-review-stack-context.schema.json +196 -0
- package/extension/adapters/planAdapterPlannotator.ts +0 -362
- package/extension/doors/auditWaveTools.ts +0 -352
- package/extension/doors/ciExecutor.ts +0 -756
- package/extension/doors/commitCompact.ts +0 -251
- package/extension/doors/dreamWaveTools.ts +0 -475
- package/extension/doors/learn.ts +0 -655
- package/extension/doors/objectiveStack.ts +0 -1143
- package/extension/doors/prReviewDynamic.ts +0 -276
- package/extension/doors/ready.ts +0 -87
- package/extension/doors/submit.ts +0 -347
- package/extension/doors/submitPrReview.ts +0 -408
- package/extension/factories/gistAuthor.ts +0 -94
- package/extension/factories/gistDraft.ts +0 -265
- package/extension/factories/gistSave.ts +0 -251
- package/extension/factories/implementHere.ts +0 -116
- package/extension/factories/objectiveAuthor.ts +0 -98
- package/extension/factories/objectiveDraft.ts +0 -466
- package/extension/factories/objectivePlan.ts +0 -975
- package/extension/factories/objectiveSave.ts +0 -363
- package/extension/factories/planDraft.ts +0 -140
- package/extension/factories/planMode.ts +0 -205
- package/extension/factories/planReview.ts +0 -1237
- package/extension/factories/planSave.ts +0 -604
- package/extension/factories/planTitle.ts +0 -141
- package/extension/substrate/structuredOutput.ts +0 -202
- package/extension/waves/auditWave.ts +0 -312
- package/extension/waves/harvestWave.ts +0 -399
- package/extension/waves/learnWave.ts +0 -155
- package/extension/waves/memoryAdapter.ts +0 -139
- package/extension/waves/prReviewDynamicWave.ts +0 -777
- package/extension/worker/readOnlySession.ts +0 -294
- package/extension/worker/worker.ts +0 -899
- package/prompts/stages/pr-review-dynamic.md +0 -7
- package/shared/contracts-history.md +0 -605
package/extension/index.ts
CHANGED
|
@@ -1,79 +1,101 @@
|
|
|
1
|
-
// perk Pi extension — the session *interior
|
|
1
|
+
// perk Pi extension — the session *interior*: the composition root.
|
|
2
2
|
//
|
|
3
|
-
// The tier-3 session-state mechanics (contracts.md §8.2/§8.3)
|
|
3
|
+
// The tier-3 session-state mechanics (contracts.md §8.2/§8.3) — claim PERK_RUN_ID on
|
|
4
4
|
// `session_start` (verified-linkage), rebuild `perk:workflow-state` on `session_start` AND
|
|
5
|
-
// `session_tree` (per-field LWW),
|
|
5
|
+
// `session_tree` (per-field LWW), derive a child run_id on fork, reconcile the stage-gated plan
|
|
6
|
+
// linkage — are OWNED by `session/lifecycle.ts` (identity arms + the two-phase startup facts +
|
|
7
|
+
// the navigation facts). This file binds the production ports and keeps the Pi effects visibly
|
|
8
|
+
// ORDERED: gate sync from the pure scope slice → claimed-only refinement import → the post-gate
|
|
9
|
+
// facts → implementation pointer capture → feedback receiver sync → presentation/probe tail.
|
|
6
10
|
|
|
7
11
|
import { existsSync, mkdirSync } from "node:fs";
|
|
8
12
|
import { basename, join } from "node:path";
|
|
9
13
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
|
|
14
|
+
import { createDraftReviewWaveState } from "./authoring/review/draftContext.ts";
|
|
15
|
+
import { createHunkFeedbackReceiver, type HunkFeedbackReceiver } from "./hunkFeedback/receiver.ts";
|
|
16
|
+
import { registerBashScanTimeout } from "./pi/v1/bashScanTimeout.ts";
|
|
17
|
+
import { installAutomatedReviewBindings } from "./pi/v1/codeReview/automated.ts";
|
|
18
|
+
import { installPrReviewBrowserBindings } from "./pi/v1/codeReview/browser.ts";
|
|
19
|
+
import { installReviewWaveBindings } from "./pi/v1/codeReview/reviewWave.ts";
|
|
20
|
+
import { installStackReviewBindings } from "./pi/v1/codeReview/stack.ts";
|
|
21
|
+
import { installCuratedSubmissionBindings } from "./pi/v1/codeReview/submit.ts";
|
|
22
|
+
import { installPrReviewTerminalBindings } from "./pi/v1/codeReview/terminal.ts";
|
|
23
|
+
import { installAddressBindings } from "./pi/v1/delivery/address.ts";
|
|
24
|
+
import { installCiBindings } from "./pi/v1/delivery/ci.ts";
|
|
25
|
+
import { installCommitCompactBindings } from "./pi/v1/delivery/commitCompact.ts";
|
|
26
|
+
import {
|
|
27
|
+
type ConflictResolverEngineOptions,
|
|
28
|
+
createConflictResolverEngine,
|
|
29
|
+
} from "./pi/v1/delivery/conflictResolverEngine.ts";
|
|
30
|
+
import { installLandBindings } from "./pi/v1/delivery/land.ts";
|
|
31
|
+
import { installReadyBindings } from "./pi/v1/delivery/ready.ts";
|
|
32
|
+
import { createStackConflictResolver } from "./pi/v1/delivery/stackConflictResolver.ts";
|
|
33
|
+
import { installStackLandBindings } from "./pi/v1/delivery/stackLand.ts";
|
|
34
|
+
import { installStackRecoverBindings } from "./pi/v1/delivery/stackRecover.ts";
|
|
35
|
+
import { installStackStatusBindings } from "./pi/v1/delivery/stackStatus.ts";
|
|
36
|
+
import {
|
|
37
|
+
installStackSyncBindings,
|
|
38
|
+
type StackResolutionDelivery,
|
|
39
|
+
} from "./pi/v1/delivery/stackSync.ts";
|
|
40
|
+
import { installSubmitBindings } from "./pi/v1/delivery/submit.ts";
|
|
41
|
+
import { installSubmitConflictBindings } from "./pi/v1/delivery/submitConflict.ts";
|
|
42
|
+
import { createDraftReviewSlot } from "./pi/v1/draftReview.ts";
|
|
43
|
+
import { registerDraftReviewWaveTools } from "./pi/v1/draftReviewWaveTools.ts";
|
|
44
|
+
import { installGistBindings } from "./pi/v1/gist.ts";
|
|
45
|
+
import { installAuditBindings } from "./pi/v1/learning/audit.ts";
|
|
46
|
+
import { installDreamBindings } from "./pi/v1/learning/dream.ts";
|
|
47
|
+
import { installLearnFactoryBindings } from "./pi/v1/learning/factory.ts";
|
|
48
|
+
import { installHarvestBindings } from "./pi/v1/learning/harvest.ts";
|
|
49
|
+
import { installLearnBindings } from "./pi/v1/learning/learn.ts";
|
|
50
|
+
import { registerLifecycleGates } from "./pi/v1/lifecycleGates.ts";
|
|
51
|
+
import { installObjectiveBindings } from "./pi/v1/objective.ts";
|
|
52
|
+
import { installObjectiveAuthoringBindings } from "./pi/v1/objectiveAuthoring.ts";
|
|
53
|
+
import { installObjectivePlanningBindings } from "./pi/v1/objectivePlanning.ts";
|
|
54
|
+
import {
|
|
55
|
+
importRefinementContextOnClaim,
|
|
56
|
+
installObjectiveRefinementBindings,
|
|
57
|
+
} from "./pi/v1/objectiveRefinement.ts";
|
|
24
58
|
import {
|
|
25
59
|
openObjectiveReviewSurface,
|
|
26
60
|
registerObjectiveReviewBrowser,
|
|
27
|
-
} from "./
|
|
28
|
-
import {
|
|
29
|
-
import {
|
|
30
|
-
import {
|
|
31
|
-
import {
|
|
32
|
-
import {
|
|
33
|
-
import {
|
|
34
|
-
import {
|
|
35
|
-
import {
|
|
36
|
-
import {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
import { registerObjective } from "./factories/objective.ts";
|
|
45
|
-
import { registerObjectiveAuthor } from "./factories/objectiveAuthor.ts";
|
|
46
|
-
import { registerObjectiveDraft } from "./factories/objectiveDraft.ts";
|
|
47
|
-
import { registerObjectivePlan } from "./factories/objectivePlan.ts";
|
|
48
|
-
import { registerObjectiveSave } from "./factories/objectiveSave.ts";
|
|
49
|
-
import { registerPlanDraft } from "./factories/planDraft.ts";
|
|
50
|
-
import { registerPlanMode } from "./factories/planMode.ts";
|
|
51
|
-
import { registerPlanReview } from "./factories/planReview.ts";
|
|
52
|
-
import { registerPlanSave } from "./factories/planSave.ts";
|
|
53
|
-
import { createHunkFeedbackReceiver } from "./hunkFeedback/receiver.ts";
|
|
61
|
+
} from "./pi/v1/objectiveReviewBrowser.ts";
|
|
62
|
+
import { installPlanBindings } from "./pi/v1/plan.ts";
|
|
63
|
+
import { openPlanReviewSurface, registerPlanReviewBrowser } from "./pi/v1/planReviewBrowser.ts";
|
|
64
|
+
import { createAnnotationState, installAnnotationBindings } from "./pi/v1/providers/annotations.ts";
|
|
65
|
+
import { installPlannotatorPlanAdapter } from "./pi/v1/providers/plannotator.ts";
|
|
66
|
+
import { plannotatorPresent } from "./pi/v1/providers/plannotatorHandoff.ts";
|
|
67
|
+
import { installTombellPlanAdapter } from "./pi/v1/providers/tombell.ts";
|
|
68
|
+
import { installScoutWaveBindings } from "./pi/v1/scoutWave.ts";
|
|
69
|
+
import { registerSelfcheck } from "./pi/v1/selfcheck.ts";
|
|
70
|
+
import {
|
|
71
|
+
branchSessionStateStore,
|
|
72
|
+
establishSessionIdentity,
|
|
73
|
+
reflectSessionReadOnlyFloor,
|
|
74
|
+
resolveSessionStartFacts,
|
|
75
|
+
sessionStartToolScope,
|
|
76
|
+
sessionTreeFacts,
|
|
77
|
+
} from "./session/lifecycle.ts";
|
|
54
78
|
import { createAgentScratchProvisioner, registerAgentScratch } from "./substrate/agentScratch.ts";
|
|
55
79
|
import { registerBindingDelivery } from "./substrate/bindingDelivery.ts";
|
|
56
80
|
import {
|
|
57
81
|
atomicWriteFileSync,
|
|
58
82
|
ensureRunScratch,
|
|
83
|
+
listRunIds,
|
|
59
84
|
markHandoffConsumed,
|
|
60
85
|
readHandoff,
|
|
61
86
|
readPlanRef,
|
|
62
87
|
setMarker,
|
|
63
88
|
workflowDir,
|
|
64
89
|
} from "./substrate/cache.ts";
|
|
65
|
-
import {
|
|
90
|
+
import { decodeReadOnlyFloor, isRunnerChild } from "./substrate/childRestrictions.ts";
|
|
91
|
+
import { loadRegistry, type Registry } from "./substrate/registry.ts";
|
|
66
92
|
import { perkVersion, sharedDir, versionStamp } from "./substrate/resources.ts";
|
|
67
93
|
import { mintRunId } from "./substrate/runId.ts";
|
|
68
94
|
import { captureSessionPointer } from "./substrate/sessionPointers.ts";
|
|
69
95
|
import { registerToolGating } from "./substrate/toolGating.ts";
|
|
70
96
|
import {
|
|
71
|
-
appendWorkflowState,
|
|
72
97
|
branchOf,
|
|
73
|
-
decideClaim,
|
|
74
|
-
planRefsEqual,
|
|
75
98
|
rebuildWorkflowState,
|
|
76
|
-
resolveRunStage,
|
|
77
99
|
WORKFLOW_STATE_TYPE,
|
|
78
100
|
type WorkflowState,
|
|
79
101
|
} from "./substrate/workflowState.ts";
|
|
@@ -90,6 +112,7 @@ import {
|
|
|
90
112
|
} from "./surfaces/surfaces.ts";
|
|
91
113
|
import { registerBtw } from "./vendor/btw/btw.ts";
|
|
92
114
|
import { registerWhimsical } from "./vendor/whimsical/whimsical.ts";
|
|
115
|
+
import { createReportWave } from "./waves/reportWave.ts";
|
|
93
116
|
|
|
94
117
|
// Cross-plane proof marker (TS writes via cache.ts; the Python helper reads it — gate check 3).
|
|
95
118
|
const T3_MARKER = "t3-extension-cache-write";
|
|
@@ -123,18 +146,43 @@ function writeT3Sentinel(
|
|
|
123
146
|
}
|
|
124
147
|
}
|
|
125
148
|
|
|
126
|
-
export default function (
|
|
149
|
+
export default function perk(
|
|
150
|
+
pi: ExtensionAPI,
|
|
151
|
+
options: {
|
|
152
|
+
resolverEngine?: Pick<ConflictResolverEngineOptions, "configPath" | "acquire">;
|
|
153
|
+
stackResolutionDelivery?: StackResolutionDelivery;
|
|
154
|
+
/**
|
|
155
|
+
* Construction-only: the hunk feedback receiver factory (default `createHunkFeedbackReceiver`).
|
|
156
|
+
* Constructed ONCE per activation exactly like production; the registered-path suites bind a
|
|
157
|
+
* recording receiver to observe the startup/navigation sync order and inputs.
|
|
158
|
+
*/
|
|
159
|
+
feedbackReceiverFactory?: (pi: ExtensionAPI) => HunkFeedbackReceiver;
|
|
160
|
+
} = {},
|
|
161
|
+
) {
|
|
127
162
|
const version = perkVersion();
|
|
128
163
|
|
|
129
164
|
// The read-only tool-gating primitive. Attaches to perk:workflow-state.mode; synced on
|
|
130
165
|
// both session_start AND session_tree below. enter/exit are the surface the gated stages consume.
|
|
131
|
-
|
|
166
|
+
// The two native-child booleans (§8.3), re-read at every session_start: the runner bit (also the
|
|
167
|
+
// `() => runnerChild` closure every injected authoring/adapter context takes as its fence — the
|
|
168
|
+
// REAL closure, never a constant), and the read-only floor a runner child derives from the
|
|
169
|
+
// report restriction packet — latched for the activation (`||=`) so no later session_start,
|
|
170
|
+
// gate exit, or tree navigation can clear it.
|
|
171
|
+
let runnerChild = false;
|
|
172
|
+
let readOnlyFloor = false;
|
|
173
|
+
const gating = registerToolGating(pi, () => readOnlyFloor);
|
|
174
|
+
|
|
175
|
+
// The bash scan-timeout guard: always on in every perk session — gated or not, runner children
|
|
176
|
+
// included (the slow gitignore-blind scans were observed in read-write sessions too). The gate's
|
|
177
|
+
// `tool_call` hook runs first only because it registers first; a gate block short-circuits
|
|
178
|
+
// before injection matters.
|
|
179
|
+
registerBashScanTimeout(pi);
|
|
132
180
|
|
|
133
181
|
// Run-owned disposable scratch guidance for every eligible write-capable model turn. One
|
|
134
182
|
// activation-scoped provisioner shares retry/warning suppression with the isolated /btw side
|
|
135
183
|
// session; no model tool or process-global temp environment is introduced.
|
|
136
184
|
const agentScratch = createAgentScratchProvisioner();
|
|
137
|
-
registerAgentScratch(pi, agentScratch);
|
|
185
|
+
registerAgentScratch(pi, agentScratch, () => !gating.isActive() && !runnerChild);
|
|
138
186
|
|
|
139
187
|
// Vendored `btw`: a `/btw` human-only side-chat popover backed by an isolated in-memory
|
|
140
188
|
// AgentSession. Takes `gating` for the gate-mirror — its side-session toolset + cache key follow
|
|
@@ -147,46 +195,123 @@ export default function (pi: ExtensionAPI) {
|
|
|
147
195
|
// turn, via the headless-no-op `setWorkingMessage` surfaces seam. Always on, no config toggle.
|
|
148
196
|
registerWhimsical(pi);
|
|
149
197
|
|
|
150
|
-
//
|
|
151
|
-
//
|
|
152
|
-
//
|
|
153
|
-
|
|
198
|
+
// The draft-review pair's per-activation state: ONE instance serves the `plan_review` wave
|
|
199
|
+
// arm (via the closures below), the two draft-review browser doors, and the
|
|
200
|
+
// `start_draft_review_wave`/`collect_draft_review_wave` tool pair — per activation, never per
|
|
201
|
+
// process (two bound sessions in one process share nothing). Plain object construction, so
|
|
202
|
+
// creating it before any registration is order-safe.
|
|
203
|
+
const draftReviewWave = createDraftReviewWaveState();
|
|
204
|
+
|
|
205
|
+
// The annotation-push per-activation state: ONE instance serves the `push_annotations`
|
|
206
|
+
// installer and every priming door — the PR/stack review doors (review mode) and the
|
|
207
|
+
// plan/objective review doors (plan mode) — per activation, never per process (the
|
|
208
|
+
// `draftReviewWave` threading pattern).
|
|
209
|
+
const annotations = createAnnotationState();
|
|
210
|
+
|
|
211
|
+
// The composition root's ONE per-activation report-wave instance (the `draftReviewWave`
|
|
212
|
+
// threading pattern): the wave owns adapter selection (a fresh rpc adapter per launch over
|
|
213
|
+
// pi's event bus) and pending execution (instance-owned refs), and is threaded into every
|
|
214
|
+
// wave-consuming installer — no installer touches the transport tier. Plain construction, no
|
|
215
|
+
// Pi registration, order-safe.
|
|
216
|
+
const reportWave = createReportWave(pi.events);
|
|
217
|
+
|
|
218
|
+
const conflictResolver = createConflictResolverEngine({
|
|
219
|
+
events: pi.events,
|
|
220
|
+
enginePresent: () => pi.getAllTools().some((tool) => tool.name === "subagent"),
|
|
221
|
+
readOnly: () => gating.isActive(),
|
|
222
|
+
authorized: (request) =>
|
|
223
|
+
request.mode === "pr-rebase"
|
|
224
|
+
? submitConflict.authorized(request)
|
|
225
|
+
: stackConflict.authorized(request),
|
|
226
|
+
...options.resolverEngine,
|
|
227
|
+
});
|
|
228
|
+
const submitConflict = installSubmitConflictBindings(pi, conflictResolver, () =>
|
|
229
|
+
gating.isActive(),
|
|
230
|
+
);
|
|
231
|
+
const stackConflict = createStackConflictResolver(conflictResolver, () => gating.isActive());
|
|
232
|
+
|
|
233
|
+
// The v1 plan installer: perk-owned plan mode (the `/plan` + Ctrl+Alt+P + `--plan` toggle
|
|
234
|
+
// surface over the read-only gate, plus the plan-authoring context injection — this call
|
|
235
|
+
// sits at the frozen hooks-ordering slot the mode surface always held), the
|
|
236
|
+
// `plan_draft`/`plan_save` tools, the `/plan-save` + `/implement-here` commands, and
|
|
237
|
+
// `plan_review` — perk's UNIVERSAL review door (plannotator-selected → the event-bus bridge;
|
|
238
|
+
// ANY other selection → the first-party in-TUI editor review). Takes `gating` to toggle plan
|
|
239
|
+
// mode and to COMPOSE the approval→save seam (auto-save → D1a gate exit) — Invariant 1 holds:
|
|
240
|
+
// the surfaces compose the gate through the seams, never own it. The injected wave-launch
|
|
241
|
+
// deps power the plannotator launch chooser (§8.23): the presence probe + the two door open
|
|
242
|
+
// cores are composed HERE so plan.ts/planReview.ts import nothing from the browser modules
|
|
243
|
+
// (planReviewBrowser.ts/objectiveReviewBrowser.ts — the value-import cycle break:
|
|
244
|
+
// planReviewBrowser.ts value-imports the review arms). `draftReviews` is the ONE
|
|
245
|
+
// per-activation current-review slot + unconfirmed-save latch every review surface shares
|
|
246
|
+
// (§8.23 "Draft-review guards") — in-memory, nothing persisted.
|
|
247
|
+
const draftReviews = createDraftReviewSlot(pi);
|
|
248
|
+
|
|
249
|
+
// The composed `perk` status handle (charter D2): one slot carrying the objective segment +
|
|
250
|
+
// the ref-counted browser-wait activity. Created once here (no hidden module state) and
|
|
251
|
+
// threaded into the objective publisher, the plan installer (the warm plannotator arm) and
|
|
252
|
+
// the browser doors; the footer reads it back via get/subscribe.
|
|
253
|
+
const perkStatus = createPerkStatus();
|
|
254
|
+
|
|
255
|
+
installPlanBindings(pi, gating, draftReviews, () => runnerChild, perkStatus, {
|
|
256
|
+
present: () => plannotatorPresent(pi),
|
|
257
|
+
plan: (ctx, opts) =>
|
|
258
|
+
openPlanReviewSurface(
|
|
259
|
+
pi,
|
|
260
|
+
ctx,
|
|
261
|
+
gating,
|
|
262
|
+
opts,
|
|
263
|
+
draftReviewWave,
|
|
264
|
+
annotations,
|
|
265
|
+
draftReviews,
|
|
266
|
+
perkStatus,
|
|
267
|
+
),
|
|
268
|
+
objective: (ctx, opts) =>
|
|
269
|
+
openObjectiveReviewSurface(
|
|
270
|
+
pi,
|
|
271
|
+
ctx,
|
|
272
|
+
gating,
|
|
273
|
+
opts,
|
|
274
|
+
draftReviewWave,
|
|
275
|
+
annotations,
|
|
276
|
+
draftReviews,
|
|
277
|
+
perkStatus,
|
|
278
|
+
),
|
|
279
|
+
});
|
|
154
280
|
|
|
155
281
|
// The first 3rd-party plan adapter: a perk-owned, injection-only bridge that re-enables
|
|
156
282
|
// `@tombell/pi-plan` as a real plan provider. Always registered, but INERT unless
|
|
157
283
|
// `[providers] plan = "tombell-plan"`; it directs the foreign free-form prose `/plan` surface into
|
|
158
284
|
// perk's canonical `plan_save` → `cache.plan-ref` contract. It needs no `gating` (Invariant 1: the
|
|
159
285
|
// read-only gate stays perk's, engaged by the cold-door launch — the shim never arbitrates tools).
|
|
160
|
-
|
|
286
|
+
installTombellPlanAdapter(pi, () => runnerChild);
|
|
161
287
|
|
|
162
288
|
// The second 3rd-party plan adapter — AUGMENT posture: `@plannotator/pi-extension` contributes
|
|
163
|
-
// its browser plan-review UI while perk's plan surface + gate stay (
|
|
164
|
-
// `--plan`/`Ctrl+Alt+P` under this selection). Always registered, but INERT unless
|
|
165
|
-
// `[providers] plan = "plannotator-plan"`. Injection-only — the `plan_review`
|
|
166
|
-
//
|
|
167
|
-
//
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
//
|
|
171
|
-
//
|
|
172
|
-
//
|
|
173
|
-
//
|
|
174
|
-
//
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
//
|
|
184
|
-
//
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
//
|
|
188
|
-
|
|
189
|
-
registerGistAuthor(pi, gating);
|
|
289
|
+
// its browser plan-review UI while perk's plan surface + gate stay (the plan installer skips
|
|
290
|
+
// only `--plan`/`Ctrl+Alt+P` under this selection). Always registered, but INERT unless
|
|
291
|
+
// `[providers] plan = "plannotator-plan"`. Injection-only — the `plan_review` tool lives in
|
|
292
|
+
// the plan installer (above), which dispatches to this adapter's event-bus bridge when
|
|
293
|
+
// plannotator is selected.
|
|
294
|
+
installPlannotatorPlanAdapter(pi, () => runnerChild);
|
|
295
|
+
|
|
296
|
+
// The v1 objective-authoring installer: the objective-author context hook pair (this call
|
|
297
|
+
// sits at the frozen hooks-ordering slot the injection always held — keyed off (read-only
|
|
298
|
+
// gate AND stage === objective-author); planMode defers to it), plus the
|
|
299
|
+
// `objective_draft`/`objective_save` tools and the `/objective-save` command (registration is
|
|
300
|
+
// name-keyed — only the hooks ordering is frozen).
|
|
301
|
+
installObjectiveAuthoringBindings(pi, gating, draftReviews, () => runnerChild);
|
|
302
|
+
|
|
303
|
+
// The v1 gist installer: the gist-authoring context hook pair (this call sits at the frozen
|
|
304
|
+
// hooks-ordering slot the injection always held; planMode defers to it too), plus the
|
|
305
|
+
// `gist_draft`/`gist_save` tools and the `/gist-save` command (registration is name-keyed —
|
|
306
|
+
// only the hooks ordering is frozen).
|
|
307
|
+
installGistBindings(pi, gating, draftReviews, () => runnerChild);
|
|
308
|
+
|
|
309
|
+
// The v1 objective-refinement installer (contracts.md §8.67/§8.68): the refinement context hook
|
|
310
|
+
// pair (keyed off (read-only gate AND stage === objective-refine); plan mode and the provider
|
|
311
|
+
// adapters defer to it), the ONE model-facing `objective_refinement_draft` tool, the warm
|
|
312
|
+
// `/objective-refine` entry and the human `/objective-refinement-save` failsafe. Registered
|
|
313
|
+
// before the tool snapshots.
|
|
314
|
+
installObjectiveRefinementBindings(pi, gating, draftReviews, () => runnerChild);
|
|
190
315
|
let sharedOk = false;
|
|
191
316
|
try {
|
|
192
317
|
sharedDir();
|
|
@@ -206,11 +331,6 @@ export default function (pi: ExtensionAPI) {
|
|
|
206
331
|
}
|
|
207
332
|
const registryOk = registryStages > 0;
|
|
208
333
|
|
|
209
|
-
// The single-value `perk` status handle (charter D2): one slot carrying the objective
|
|
210
|
-
// segment. Created once here (no hidden module state) and threaded into the objective
|
|
211
|
-
// publisher below; the footer reads it back via get/subscribe.
|
|
212
|
-
const perkStatus = createPerkStatus();
|
|
213
|
-
|
|
214
334
|
// The generic full report-detail entry and the `perk:workflow-state` transition marker. Renderer
|
|
215
335
|
// bodies live in surfaces.ts; registration is wiring through the pre-0.80.4-safe seam. The report
|
|
216
336
|
// family is appended by command-attached sinks; one workflow registration covers every appender.
|
|
@@ -221,13 +341,23 @@ export default function (pi: ExtensionAPI) {
|
|
|
221
341
|
// globals). Synced from session_start/session_tree below; closed on session_shutdown so the
|
|
222
342
|
// consumer lease releases with the session. A stale /reload predecessor instance is retired
|
|
223
343
|
// by the lease fencing (fresh token per same-identity reacquire + verify-before-inject).
|
|
224
|
-
const feedbackReceiver = createHunkFeedbackReceiver(pi);
|
|
225
|
-
pi.on("session_shutdown", async () => {
|
|
344
|
+
const feedbackReceiver = (options.feedbackReceiverFactory ?? createHunkFeedbackReceiver)(pi);
|
|
345
|
+
pi.on("session_shutdown", async (_event, ctx) => {
|
|
346
|
+
submitConflict.shutdown();
|
|
347
|
+
stackConflict.shutdown();
|
|
348
|
+
await conflictResolver.shutdown();
|
|
226
349
|
feedbackReceiver.close();
|
|
350
|
+
// A browser-wait activity cannot outlive the session.
|
|
351
|
+
perkStatus.clearActivity(ctx);
|
|
227
352
|
});
|
|
228
353
|
|
|
229
354
|
pi.on("session_start", async (_event, ctx) => {
|
|
230
|
-
|
|
355
|
+
// Read the two native-child booleans and latch the floor before lifecycle work or tool rebuilds.
|
|
356
|
+
runnerChild = isRunnerChild(process.env);
|
|
357
|
+
readOnlyFloor ||= decodeReadOnlyFloor(runnerChild, process.env.PI_SUBAGENT_EXTENSION_BINDINGS);
|
|
358
|
+
|
|
359
|
+
submitConflict.setContext(ctx);
|
|
360
|
+
stackConflict.setContext(ctx);
|
|
231
361
|
const sessionFile = ctx.sessionManager.getSessionFile();
|
|
232
362
|
const currentSessionId = sessionFile ? basename(sessionFile) : null;
|
|
233
363
|
|
|
@@ -237,209 +367,108 @@ export default function (pi: ExtensionAPI) {
|
|
|
237
367
|
report(ctx, "workflow-state linkage error", "error", message, { alsoLog: true });
|
|
238
368
|
};
|
|
239
369
|
|
|
240
|
-
|
|
241
|
-
state: rebuildWorkflowState(branchEntries()),
|
|
242
|
-
currentSessionId,
|
|
243
|
-
envRunId: process.env.PERK_RUN_ID ?? null,
|
|
244
|
-
cwd: ctx.cwd,
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
// The session-audit exact-vintage stamp (§8.3), recorded by every run-identity arm below
|
|
370
|
+
// The session-audit exact-vintage stamp (§8.3), recorded by every run-identity arm
|
|
248
371
|
// (claim/fork/adopt/mint); undefined on the perkVersion() failure sentinel, which drops the
|
|
249
372
|
// key on serialize and leaves the session on the timestamp-estimate arm.
|
|
250
373
|
const stamp = versionStamp(version);
|
|
251
374
|
|
|
252
|
-
//
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
});
|
|
279
|
-
if (!okAppend) {
|
|
280
|
-
// do NOT consume
|
|
281
|
-
} else {
|
|
282
|
-
markHandoffConsumed(ctx.cwd, decision.runId, {
|
|
283
|
-
piSessionId: currentSessionId ?? undefined,
|
|
284
|
-
});
|
|
285
|
-
resolved = data;
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
} else if (decision.action === "fork") {
|
|
289
|
-
// Inherited a run_id from a different session file → isolate the child's scratch. A static
|
|
290
|
-
// redirect or filesystem failure is loud but does not prevent the derived workflow identity
|
|
291
|
-
// from settling; later eligible turns retry through the agent-scratch resolver.
|
|
292
|
-
try {
|
|
293
|
-
ensureRunScratch(ctx.cwd, decision.childRunId);
|
|
294
|
-
} catch (error) {
|
|
295
|
-
report(
|
|
296
|
-
ctx,
|
|
297
|
-
"run scratch",
|
|
298
|
-
"warning",
|
|
299
|
-
`could not create fork run root for ${decision.childRunId}: ${String(error)}`,
|
|
300
|
-
{ alsoLog: true },
|
|
301
|
-
);
|
|
302
|
-
}
|
|
303
|
-
const data: WorkflowState = {
|
|
304
|
-
run_id: decision.childRunId,
|
|
305
|
-
pi_session_id: currentSessionId ?? undefined,
|
|
306
|
-
predecessor: decision.parentRunId,
|
|
307
|
-
mode: decision.state.mode,
|
|
308
|
-
perk_version: stamp,
|
|
309
|
-
};
|
|
310
|
-
pi.appendEntry(WORKFLOW_STATE_TYPE, data);
|
|
311
|
-
resolved = data;
|
|
312
|
-
} else if (decision.action === "adopt") {
|
|
313
|
-
// An env-inherited run id whose handoff was already consumed by a different session: a
|
|
314
|
-
// spawned child (contracts §8.2). Mirror the fork arm — derived child identity, isolated
|
|
315
|
-
// scratch, inherited mode (read-only gating survives) — minus everything that belongs to
|
|
316
|
-
// the launched session: never re-consume the handoff (its pi_session_id keeps the true
|
|
317
|
-
// claimer), no `stage` (no stage impersonation / stage-binding injection), and no
|
|
318
|
-
// implementation/main pointer capture (resolveRunStage stays null for adopt).
|
|
319
|
-
try {
|
|
320
|
-
ensureRunScratch(ctx.cwd, decision.childRunId);
|
|
321
|
-
} catch (error) {
|
|
375
|
+
// The identity lifecycle (claim / fork / adopt / mint / keep) is the named session
|
|
376
|
+
// operation (session/lifecycle.ts owns the arms); this handler binds the production ports
|
|
377
|
+
// and renders the outcome's per-arm problems/warnings with the exact report scopes the
|
|
378
|
+
// arms always used. The strict appends keep reporting read-back failures through the
|
|
379
|
+
// strict-append seam's own loudness channel. The same cwd-bound handoff reader serves the
|
|
380
|
+
// post-gate facts below (ONE handoff authority).
|
|
381
|
+
const identityPorts = {
|
|
382
|
+
readHandoff: (runId: string) => readHandoff(ctx.cwd, runId),
|
|
383
|
+
listRunIds: () => listRunIds(ctx.cwd),
|
|
384
|
+
markHandoffConsumed: (runId: string, opts: { piSessionId?: string }) =>
|
|
385
|
+
markHandoffConsumed(ctx.cwd, runId, opts),
|
|
386
|
+
ensureRunScratch: (runId: string) => {
|
|
387
|
+
ensureRunScratch(ctx.cwd, runId);
|
|
388
|
+
},
|
|
389
|
+
mintRunId,
|
|
390
|
+
versionStamp: stamp,
|
|
391
|
+
};
|
|
392
|
+
const stateStore = branchSessionStateStore(pi, ctx);
|
|
393
|
+
let identity = establishSessionIdentity(stateStore, identityPorts, {
|
|
394
|
+
currentSessionId,
|
|
395
|
+
envRunId: process.env.PERK_RUN_ID ?? null,
|
|
396
|
+
});
|
|
397
|
+
if (readOnlyFloor) {
|
|
398
|
+
const reflected = reflectSessionReadOnlyFloor(stateStore, identity);
|
|
399
|
+
identity = reflected.outcome;
|
|
400
|
+
if (reflected.unexpectedFailure) {
|
|
322
401
|
report(
|
|
323
402
|
ctx,
|
|
324
|
-
"
|
|
325
|
-
"
|
|
326
|
-
|
|
403
|
+
"child restriction",
|
|
404
|
+
"error",
|
|
405
|
+
"could not persist child read-only restriction; in-memory restriction remains active",
|
|
327
406
|
{ alsoLog: true },
|
|
328
407
|
);
|
|
329
408
|
}
|
|
330
|
-
const data: WorkflowState = {
|
|
331
|
-
run_id: decision.childRunId,
|
|
332
|
-
pi_session_id: currentSessionId ?? undefined,
|
|
333
|
-
predecessor: decision.parentRunId,
|
|
334
|
-
mode: decision.mode,
|
|
335
|
-
perk_version: stamp,
|
|
336
|
-
};
|
|
337
|
-
pi.appendEntry(WORKFLOW_STATE_TYPE, data);
|
|
338
|
-
resolved = data;
|
|
339
|
-
} else if (decision.action === "none") {
|
|
340
|
-
// A warm session with no identity mints its own run_id so
|
|
341
|
-
// per-run state (the session data dir) can key off it. No disk artifacts —
|
|
342
|
-
// dirs are the accessor's job; provenance is recorded separately. A failed cold claim above never
|
|
343
|
-
// falls here (claim stays a loud unclaimed error).
|
|
344
|
-
const runId = mintRunId();
|
|
345
|
-
const data: WorkflowState = {
|
|
346
|
-
run_id: runId,
|
|
347
|
-
pi_session_id: currentSessionId ?? undefined,
|
|
348
|
-
perk_version: stamp,
|
|
349
|
-
};
|
|
350
|
-
const okAppend = appendWorkflowState(pi, ctx, {
|
|
351
|
-
data,
|
|
352
|
-
field: "run_id",
|
|
353
|
-
expected: runId,
|
|
354
|
-
scope: "workflow-state linkage error",
|
|
355
|
-
failure: `read-back failed for minted run ${runId}`,
|
|
356
|
-
});
|
|
357
|
-
if (okAppend) {
|
|
358
|
-
resolved = { ...decision.state, ...data };
|
|
359
|
-
minted = true;
|
|
360
|
-
}
|
|
361
409
|
}
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
//
|
|
370
|
-
//
|
|
371
|
-
//
|
|
372
|
-
//
|
|
373
|
-
// Fail-closed on the gate: if the sync throws, leave it as-is (a failed sync
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
? undefined
|
|
377
|
-
: (resolved.stage ?? (decision.action === "fork" ? decision.state.stage : undefined));
|
|
410
|
+
for (const problem of identity.problems) reportError(problem);
|
|
411
|
+
for (const warning of identity.warnings) {
|
|
412
|
+
report(ctx, "run scratch", "warning", warning, { alsoLog: true });
|
|
413
|
+
}
|
|
414
|
+
const decision = identity.decision;
|
|
415
|
+
const minted = identity.arm === "minted";
|
|
416
|
+
|
|
417
|
+
// PHASE 1 — reapply the read-only allowlist + stage scoping from the established identity
|
|
418
|
+
// FIRST, before the fallible post-gate facts below. The scope derivation is pure (no store,
|
|
419
|
+
// handoff, registry, or checkout read), so no read failure can leave the gate unsynced
|
|
420
|
+
// (defense in depth on top of the total cache readers); `resolved.mode` is final once the
|
|
421
|
+
// arms settle. Fail-closed on the gate: if the sync throws, leave it as-is (a failed sync
|
|
422
|
+
// never opens it).
|
|
423
|
+
const toolScope = sessionStartToolScope(identity);
|
|
378
424
|
try {
|
|
379
|
-
gating.syncFromState(
|
|
425
|
+
gating.syncFromState(toolScope.mode, toolScope.stage);
|
|
380
426
|
} catch (error) {
|
|
381
427
|
console.error(`perk: tool-gating sync failed on session_start — ${error}`);
|
|
382
428
|
}
|
|
383
429
|
|
|
384
|
-
//
|
|
385
|
-
//
|
|
386
|
-
//
|
|
387
|
-
// (
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
const runStage = resolveRunStage(decision, ctx.cwd);
|
|
394
|
-
// Registry-missing is permissive when a stage is present, to preserve implement linkage.
|
|
395
|
-
const consumesPlanRef =
|
|
396
|
-
runStage !== null && (registry === null || stageConsumesPlanRef(registry, runStage));
|
|
397
|
-
if (consumesPlanRef) {
|
|
398
|
-
const cachedRef = readPlanRef(ctx.cwd);
|
|
399
|
-
if (cachedRef !== null) {
|
|
400
|
-
if (planRefsEqual(linked, cachedRef)) {
|
|
401
|
-
resolved = { ...resolved, active_plan_ref: linked };
|
|
402
|
-
} else {
|
|
403
|
-
if (
|
|
404
|
-
appendWorkflowState(pi, ctx, {
|
|
405
|
-
data: { active_plan_ref: cachedRef },
|
|
406
|
-
field: "active_plan_ref",
|
|
407
|
-
expected: cachedRef,
|
|
408
|
-
scope: "workflow-state linkage error",
|
|
409
|
-
failure: `plan-ref read-back failed for ${cachedRef.provider}:${cachedRef.pr_id}`,
|
|
410
|
-
equals: planRefsEqual,
|
|
411
|
-
})
|
|
412
|
-
) {
|
|
413
|
-
resolved = { ...resolved, active_plan_ref: cachedRef };
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
} else if (linked !== null) {
|
|
417
|
-
resolved = { ...resolved, active_plan_ref: linked };
|
|
418
|
-
}
|
|
419
|
-
} else if (linked !== null) {
|
|
420
|
-
// Non-consuming stage (or no launched stage): preserve any already-linked ref via LWW,
|
|
421
|
-
// but NEVER read the cache file — the root selector must not leak in.
|
|
422
|
-
resolved = { ...resolved, active_plan_ref: linked };
|
|
430
|
+
// The refinement cold claim's ONE-TIME context import (contracts.md §8.67): only on the
|
|
431
|
+
// actual cold claim of an `objective-refine` handoff (never keep/fork/adopt/mint), after the
|
|
432
|
+
// identity settled and the gate synced. A refusal is loud and leaves the session gated
|
|
433
|
+
// without a usable context (no orphan repair, no reimport on reload).
|
|
434
|
+
if (identity.arm === "claimed" && typeof identity.resolved.run_id === "string") {
|
|
435
|
+
importRefinementContextOnClaim(pi, ctx, {
|
|
436
|
+
runId: identity.resolved.run_id,
|
|
437
|
+
stage: identity.resolved.stage,
|
|
438
|
+
});
|
|
423
439
|
}
|
|
424
440
|
|
|
441
|
+
// PHASE 2 — the post-gate facts (session/lifecycle.ts owns the decision tree): the lazy,
|
|
442
|
+
// stage-gated `cache.plan-ref` → `active_plan_ref` reconciliation (only a launched stage
|
|
443
|
+
// that *consumes* the ref reads the checkout; claim/keep read the handoff, fork/adopt/none
|
|
444
|
+
// never do; one strict verified append, idempotent by (provider, pr_id)), plus the derived
|
|
445
|
+
// implementation-capture and receiver inputs. Called HERE — after the gate and the
|
|
446
|
+
// refinement import — never while constructing gate inputs. A throwing branch/handoff read
|
|
447
|
+
// propagates to Pi's hook error boundary with the gate already synced: unreadability is
|
|
448
|
+
// never turned into confirmed absence, and no later effect runs from guessed facts.
|
|
449
|
+
const facts = resolveSessionStartFacts(
|
|
450
|
+
stateStore,
|
|
451
|
+
{ readHandoff: identityPorts.readHandoff, readPlanRef: () => readPlanRef(ctx.cwd) },
|
|
452
|
+
{ identity, registry, currentSessionId },
|
|
453
|
+
);
|
|
454
|
+
const resolved: WorkflowState = facts.resolved;
|
|
455
|
+
|
|
425
456
|
// Implementation session pointer (contracts.md §8.35): an implement session self-keys its own
|
|
426
457
|
// session file into the shared main checkout so a later/other session resolves it cross-run.
|
|
427
|
-
// The headless worker's inner session lands here too (.main);
|
|
458
|
+
// The headless worker's inner session lands here too (.main); runStage records the matching
|
|
428
459
|
// .worker. A forked implement session inherits the parent's launched stage + threads the
|
|
429
460
|
// inherited parent session id as fork provenance. Best-effort + non-fatal (carrier warns).
|
|
430
461
|
// First-write-wins (`preserveForeign`): this is the corroborated shadowing defect site — the
|
|
431
462
|
// claimer's original capture stays authoritative, and any future shadow vector warns loudly
|
|
432
463
|
// instead of silently corrupting /learn evidence.
|
|
433
|
-
|
|
434
|
-
runStage ?? (decision.action === "fork" ? (decision.state.stage ?? null) : null);
|
|
435
|
-
if (resolved.run_id && implStage === "implement") {
|
|
464
|
+
if (facts.implementationCapture !== null) {
|
|
436
465
|
captureSessionPointer({
|
|
437
466
|
cwd: ctx.cwd,
|
|
438
|
-
runId:
|
|
467
|
+
runId: facts.implementationCapture.runId,
|
|
439
468
|
klass: "implementation",
|
|
440
469
|
site: "main",
|
|
441
470
|
sessionFile,
|
|
442
|
-
parentSessionId:
|
|
471
|
+
parentSessionId: facts.implementationCapture.parentSessionId,
|
|
443
472
|
preserveForeign: true,
|
|
444
473
|
});
|
|
445
474
|
}
|
|
@@ -449,14 +478,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
449
478
|
// outbox. Eligibility (interactive TUI + implement stage + non-adopted + settled identity +
|
|
450
479
|
// plan-ref match against one fresh cache read) is evaluated inside sync; every ineligible
|
|
451
480
|
// shape closes any open inbox. Never throws (the controller contains its own failures).
|
|
452
|
-
feedbackReceiver.sync(ctx, {
|
|
453
|
-
stage: implStage,
|
|
454
|
-
adopted: decision.action === "adopt",
|
|
455
|
-
runId: resolved.run_id ?? null,
|
|
456
|
-
piSessionId: currentSessionId,
|
|
457
|
-
activePlanRef: resolved.active_plan_ref ?? null,
|
|
458
|
-
mode: ctx.mode ?? null,
|
|
459
|
-
});
|
|
481
|
+
feedbackReceiver.sync(ctx, { ...facts.feedback, mode: ctx.mode ?? null });
|
|
460
482
|
|
|
461
483
|
// Soft version-parity drift signal: pi can lazy-install / load a stale `npm:@mgiles/perk`, so the
|
|
462
484
|
// extension actually running may differ from the `perk` CLI that launched it. The local launch
|
|
@@ -521,165 +543,153 @@ export default function (pi: ExtensionAPI) {
|
|
|
521
543
|
|
|
522
544
|
// Non-negotiable: rebuild on branch navigation too, or state goes stale after /tree (§8.3).
|
|
523
545
|
pi.on("session_tree", async (_event, ctx) => {
|
|
546
|
+
stackConflict.setContext(ctx);
|
|
547
|
+
// ONE fresh full-branch rebuild; the navigation facts derive purely from it (no handoff/
|
|
548
|
+
// checkout read, claim, linkage, or capture on navigation — session/lifecycle.ts owns the
|
|
549
|
+
// asymmetry with startup).
|
|
524
550
|
const state = rebuildWorkflowState(branchOf(ctx));
|
|
551
|
+
const facts = sessionTreeFacts(state);
|
|
525
552
|
// Non-negotiable: re-sync the gate + stage scoping on tree navigation too (mode and stage are
|
|
526
553
|
// per-field LWW — the branch-rebuilt stage is the §8.40 key). Fail-closed on the gate.
|
|
527
554
|
try {
|
|
528
|
-
gating.syncFromState(
|
|
555
|
+
gating.syncFromState(facts.toolScope.mode, facts.toolScope.stage);
|
|
529
556
|
} catch (error) {
|
|
530
557
|
console.error(`perk: tool-gating sync failed on session_tree — ${error}`);
|
|
531
558
|
}
|
|
532
|
-
// Re-sync the feedback receiver from the LWW-rebuilt state (§8.58)
|
|
533
|
-
// right here: an env-adopted child's fresh branch carries no
|
|
534
|
-
// alone keeps it inert on tree navigation.
|
|
535
|
-
feedbackReceiver.sync(ctx, {
|
|
536
|
-
stage: state.stage ?? null,
|
|
537
|
-
adopted: false,
|
|
538
|
-
runId: state.run_id ?? null,
|
|
539
|
-
piSessionId: state.pi_session_id ?? null,
|
|
540
|
-
activePlanRef: state.active_plan_ref ?? null,
|
|
541
|
-
mode: ctx.mode ?? null,
|
|
542
|
-
});
|
|
559
|
+
// Re-sync the feedback receiver from the LWW-rebuilt state (§8.58) — gate first, then
|
|
560
|
+
// receiver; `adopted: false` is right here: an env-adopted child's fresh branch carries no
|
|
561
|
+
// stage, so the stage gate alone keeps it inert on tree navigation.
|
|
562
|
+
feedbackReceiver.sync(ctx, { ...facts.feedback, mode: ctx.mode ?? null });
|
|
543
563
|
if (process.env.PERK_SELFCHECK) {
|
|
544
564
|
writeT3Sentinel(ctx.cwd, "tree", state, ctx.mode ?? null);
|
|
545
565
|
}
|
|
546
566
|
});
|
|
547
567
|
|
|
548
|
-
// Warm door: the `plan_save` tool + `/plan-save` command. Takes `gating`:
|
|
549
|
-
// a successful command-path save exits read-only mode (the read-only → read-write boundary).
|
|
550
|
-
registerPlanSave(pi, gating);
|
|
551
|
-
|
|
552
|
-
// The `/implement-here` command: the human-only no-save exit from plan mode (§8.23) —
|
|
553
|
-
// implement the reviewed draft in-session, no issue created. Composes the gate through the
|
|
554
|
-
// implementHereExit seam; no model tool is registered (machine-unreachable by construction).
|
|
555
|
-
registerImplementHere(pi, gating);
|
|
556
|
-
|
|
557
|
-
// The `plan_draft` working-draft file tool. Registered in the factory so it
|
|
558
|
-
// exists before the gate snapshots tools; its name is in READ_ONLY_TOOLS (the structural
|
|
559
|
-
// session-data carve-out), so it survives plan mode.
|
|
560
|
-
registerPlanDraft(pi);
|
|
561
|
-
|
|
562
|
-
// The `objective_draft` working-objective file tool (the plan_draft twin).
|
|
563
|
-
registerObjectiveDraft(pi);
|
|
564
|
-
|
|
565
|
-
// The `gist_draft` working-gist file tool (the third draft carve-out).
|
|
566
|
-
registerGistDraft(pi);
|
|
567
|
-
|
|
568
568
|
// Lifecycle gates: the dirty-repo switch/fork guard + the guard-only `/implement`.
|
|
569
569
|
registerLifecycleGates(pi);
|
|
570
570
|
|
|
571
571
|
// Warm door: the `submit` tool + `/submit` command.
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
// The warm
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
//
|
|
585
|
-
|
|
572
|
+
installSubmitBindings(pi, submitConflict);
|
|
573
|
+
|
|
574
|
+
// The warm ready + handoff bindings: the deliberate draft→ready review gate (submit keeps
|
|
575
|
+
// draft). Takes `gating`: the warm ready→reconcile continuation refuses (loudly) to drive
|
|
576
|
+
// the ready-time pass into a read-only session (contracts.md §8.66).
|
|
577
|
+
installReadyBindings(pi, gating);
|
|
578
|
+
|
|
579
|
+
// Warm bindings: `land` merges + sets pending-learn; the v1 learn installer (the `learn` +
|
|
580
|
+
// `run_learn_wave` tools and the `/learn` command over the `learning/` feature ops) clears it.
|
|
581
|
+
installLandBindings(pi);
|
|
582
|
+
installLearnBindings(pi, reportWave);
|
|
583
|
+
|
|
584
|
+
// The warm stacked-delivery mutating surface (§8.51): the `/objective-sync`/
|
|
585
|
+
// `/objective-recover`/`/objective-land` drives + the four typed stack tools over the
|
|
586
|
+
// `delivery/stackConflict.ts` + `delivery/stackReconcile.ts` feature ops. Takes `gating` for
|
|
587
|
+
// the driving commands' gate-on soft refusal (stack sync/recovery mutates published
|
|
588
|
+
// branches; the stack tools never join READ_ONLY_TOOLS).
|
|
589
|
+
installStackSyncBindings(pi, gating, stackConflict, options.stackResolutionDelivery);
|
|
590
|
+
installStackRecoverBindings(pi, gating);
|
|
591
|
+
installStackLandBindings(pi, gating);
|
|
592
|
+
|
|
593
|
+
// The stacked-delivery status read: the `objective_stack_status` tool + the `/objective-stack`
|
|
594
|
+
// command (read-only end to end — the command works gate-on; the tool stays gate-off).
|
|
595
|
+
installStackStatusBindings(pi);
|
|
586
596
|
|
|
587
597
|
// The warm `/address` review loop: the submit-then-resolve `finalize_address` tool + `/address`
|
|
588
598
|
// command. Classify-then-act (the verbose feedback fetch + classification runs in an isolated
|
|
589
599
|
// spawned child; the parent fixes actionable items and finalizes the committed repairs).
|
|
590
|
-
|
|
600
|
+
installAddressBindings(pi, reportWave, submitConflict);
|
|
591
601
|
|
|
592
602
|
// The warm `/pr-review` door: automated code review in a FRESH, isolated subagent that
|
|
593
603
|
// POSTS its review to the PR (the deliberate departure from /address's read-only-child rule).
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
// The EXPERIMENTAL warm `/pr-review-dynamic` door: the selector-driven sibling — angle
|
|
597
|
-
// selection delegated to a fresh perk.review-angle-selector lane, normalized in
|
|
598
|
-
// module-rendered code; posting shares /pr-review's post_pr_review + clean guard. The
|
|
599
|
-
// baseline /pr-review stays canonical; promotion/retire is a later dogfood's call.
|
|
600
|
-
registerPrReviewDynamic(pi);
|
|
604
|
+
installAutomatedReviewBindings(pi, reportWave);
|
|
601
605
|
|
|
602
606
|
// The warm `submit_pr_review` tool: the human-gated curated-posting surface both review
|
|
603
607
|
// doors ride (contracts §8.4) — neither door registers tools of its own.
|
|
604
|
-
|
|
608
|
+
installCuratedSubmissionBindings(pi);
|
|
605
609
|
|
|
606
610
|
// The flow-scoped review-wave pair (`start_review_wave`/`collect_review_wave`) both human
|
|
607
611
|
// review doors drive: non-blocking adversarial-review launch + the typed collect, flow-scoped
|
|
608
612
|
// via the session's pending-wave guard.
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
+
installReviewWaveBindings(pi, reportWave);
|
|
614
|
+
installAuditBindings(pi, reportWave);
|
|
615
|
+
installHarvestBindings(pi, reportWave);
|
|
616
|
+
installDreamBindings(pi, reportWave);
|
|
617
|
+
|
|
618
|
+
// The scout launcher (`run_scout_wave`, contracts.md §8.70): the authoring sessions' blocking
|
|
619
|
+
// fan-out of self-contained read-only briefs onto fresh `perk.scout` lanes — one attempt,
|
|
620
|
+
// no retry, reachable in every gated stage except refinement.
|
|
621
|
+
installScoutWaveBindings(pi, reportWave);
|
|
613
622
|
|
|
614
623
|
// The flow-scoped draft-review-wave pair (`start_draft_review_wave`/
|
|
615
624
|
// `collect_draft_review_wave`) the draft-review door drives: non-blocking draft-review
|
|
616
625
|
// launch over the door-primed context + the typed collect.
|
|
617
|
-
registerDraftReviewWaveTools(pi);
|
|
626
|
+
registerDraftReviewWaveTools(pi, draftReviewWave, reportWave);
|
|
618
627
|
|
|
619
628
|
// The door-primed browser annotation tool (`push_annotations`): the browser door primes the
|
|
620
629
|
// surface handle on open and clears it on settle/degrade — the tool refuses outside a
|
|
621
630
|
// door-opened flow.
|
|
622
|
-
|
|
631
|
+
installAnnotationBindings(pi, annotations);
|
|
623
632
|
|
|
624
633
|
// The warm `/pr-review-terminal` door: the terminal review entry — hunk always, no provider
|
|
625
634
|
// dispatch (the command IS the selection); posting rides `submit_pr_review` above.
|
|
626
|
-
|
|
635
|
+
installPrReviewTerminalBindings(pi);
|
|
627
636
|
|
|
628
637
|
// The warm `/pr-review-browser` door: the browser review entry — plannotator always, opened
|
|
629
638
|
// in the background (pre-PR it absorbs the since-base local browser review); posting is the
|
|
630
639
|
// human's own platform-post from the UI, with `submit_pr_review` for request-changes only.
|
|
631
|
-
|
|
640
|
+
installPrReviewBrowserBindings(pi, annotations, perkStatus);
|
|
641
|
+
|
|
642
|
+
// The warm `/stack-review-browser` door + its cold-launch twin (`open_stack_review`): the
|
|
643
|
+
// stacked-PR browser review over the combined base→top diff — one reviewer wave with
|
|
644
|
+
// `stack: true`, then judgment-routed per-PR posting through `submit_pr_review`.
|
|
645
|
+
installStackReviewBindings(pi, annotations, perkStatus);
|
|
632
646
|
|
|
633
647
|
// The warm `/plan-review-browser` door: the summonable streaming draft review — the
|
|
634
648
|
// plannotator plan-review browser on the working plan draft, draft reviewers streaming
|
|
635
649
|
// phrase-anchored findings in; APPROVE auto-saves via the approvalSave seam, DENY returns a
|
|
636
650
|
// model-mediated revision round.
|
|
637
|
-
registerPlanReviewBrowser(pi, gating);
|
|
651
|
+
registerPlanReviewBrowser(pi, gating, draftReviewWave, annotations, draftReviews, perkStatus);
|
|
638
652
|
|
|
639
653
|
// The warm `/objective-review-browser` door: the summonable streaming objective-draft review
|
|
640
654
|
// — the plannotator plan-review browser on the RENDERED working objective draft, draft
|
|
641
655
|
// reviewers streaming phrase-anchored findings in; APPROVE auto-saves via the
|
|
642
656
|
// objectiveApprovalSave seam, Direct Edits = a model-mediated revise round (never auto-saved).
|
|
643
|
-
registerObjectiveReviewBrowser(
|
|
657
|
+
registerObjectiveReviewBrowser(
|
|
658
|
+
pi,
|
|
659
|
+
gating,
|
|
660
|
+
draftReviewWave,
|
|
661
|
+
annotations,
|
|
662
|
+
draftReviews,
|
|
663
|
+
perkStatus,
|
|
664
|
+
);
|
|
644
665
|
|
|
645
666
|
// The read-only CI executor: the `run_ci` tool + `/ci` command + `--allow-project-ci`
|
|
646
667
|
// flag. Runs the project's `[ci]` named checks deterministically and reports (never fixes/loops).
|
|
647
|
-
|
|
668
|
+
installCiBindings(pi);
|
|
648
669
|
|
|
649
670
|
// The objective substrate: `/objective` set/clear, budget accounting, threshold
|
|
650
671
|
// compaction, all keyed off the now-live `active_objective`. Inert when no objective is active.
|
|
651
672
|
// (The deterministic objective mechanics live in the Python plane: `perk objective …`.)
|
|
652
|
-
|
|
673
|
+
installObjectiveBindings(pi, perkStatus);
|
|
653
674
|
|
|
654
675
|
// The warm `/commit-and-compact` utility door: drive a commit of the work so far, compact once
|
|
655
676
|
// a successful outcome is known, then completion-gate an automatic evidence-first continuation
|
|
656
677
|
// (clean/read-only trees compact immediately; no commit → no compaction or continuation).
|
|
657
678
|
// Human-only — no tool twin.
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
// The warm `objective_save` door: the `objective_save` tool + `/objective-save` command
|
|
661
|
-
// (the objective mirror of plan-save). Takes `gating` for the read-only → read-write boundary.
|
|
662
|
-
registerObjectiveSave(pi, gating);
|
|
663
|
-
|
|
664
|
-
// The warm `gist_save` door: the `gist_save` tool + `/gist-save` command (the gist mirror).
|
|
665
|
-
registerGistSave(pi, gating);
|
|
679
|
+
installCommitCompactBindings(pi, gating);
|
|
666
680
|
|
|
667
681
|
// The objective plan factory's warm transition surface: the `objective_node` bounded
|
|
668
682
|
// tool (delegates to the Python cold door; `status:"done"` requires a completion audit) + the
|
|
669
683
|
// `/objective-plan` command (select the next node and author a bounded plan). The command now
|
|
670
684
|
// enters the read-only gate on invocation (parity with the cold door's `mode: read-only`
|
|
671
685
|
// handoff; exit stays with plan_save / `/plan` off) — hence `gating`.
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
// The learned-docs plan factory's warm surface: the `/learn-docs` command gathers open
|
|
675
|
-
// perk:learn issues into an inbox (via the `perk learn docs --gather` cold door) and injects the
|
|
676
|
-
// factory guidance so the model authors a docs/learned consolidation plan (no model tool).
|
|
677
|
-
registerLearnFactoryDoor(pi, DOCS_DOOR);
|
|
686
|
+
installObjectivePlanningBindings(pi, gating, reportWave);
|
|
678
687
|
|
|
679
|
-
// The learn
|
|
680
|
-
//
|
|
681
|
-
//
|
|
682
|
-
|
|
688
|
+
// The two learn plan factories' warm surfaces: `/learn-docs` gathers open perk:learn issues
|
|
689
|
+
// (via the `perk learn docs --gather` cold door) toward a docs/learned consolidation plan;
|
|
690
|
+
// `/learn-code` gathers pre-stamped SHOULD_BE_CODE issues (via `perk learn code --gather`)
|
|
691
|
+
// toward a code-routing plan. Guidance-injection only (no model tool).
|
|
692
|
+
installLearnFactoryBindings(pi);
|
|
683
693
|
|
|
684
694
|
// Warm-door skill-binding delivery: Mechanism A's `before_agent_start` injection of
|
|
685
695
|
// the launched stage's user-originated bindings (+ the stale-context strip). Mechanism B (the
|