@linimin/pi-letscook 0.1.72 → 0.1.74

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/CHANGELOG.md CHANGED
@@ -1,12 +1,28 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.74
4
+
5
+ ### Fixed
6
+
7
+ - made active `/cook` workflows sticky across subsequent turns so routine continuation, exact await-user-input replies, and mandatory completion-role dispatch no longer depend on prompt-shaped driver turns or repeated manual `/cook` re-entry
8
+ - updated smoke, canonical-evidence, release-check, and completion-role gating regressions to enforce the new sticky active-workflow self-healing behavior
9
+
10
+ ## 0.1.73
11
+
12
+ ### Fixed
13
+
14
+ - made active `/cook` workflows sticky across subsequent turns so completion-role dispatch and workflow context continue to self-heal from canonical active state instead of depending on prompt-shaped driver turns
15
+ - stopped pushing users to rerun `/cook` for routine active-workflow continuation or exact await-user-input replies when canonical workflow state is already active
16
+ - added regression coverage so release-check fails if sticky active-workflow dispatch falls back to prompt-only gating again
17
+
3
18
  ## 0.1.72
4
19
 
5
20
  ### Fixed
6
21
 
7
22
  - relaxed reviewer no-follow-up routing parsing so `Acceptable as-is: yes` now also accepts `none, proceed to completion-auditor` and `none - proceed to auditor` in addition to the original exact allowance, reducing avoidable completion transcription warnings without weakening the follow-up-slice guard
8
23
  - fixed completion-role continuation gating so an already-active `/cook` workflow with `continuation_policy: continue` can keep dispatching mandatory follow-up roles even when the harness no longer recognizes the current turn text as an explicit `/cook` or workflow-driver prompt, while still blocking ordinary main-chat turns from calling `completion_role`
9
- - added a dedicated `completion-role-gating-test` regression so release-check now fails if active-workflow continuation falls back to the old prompt-only dispatch gate or stops rejecting ordinary main-chat turns
24
+ - fixed `/cook` await-user-input resumptions so a user's exact reply in the active workflow can dispatch the mandatory follow-up completion role without forcing an extra `/cook` rerun
25
+ - added a dedicated `completion-role-gating-test` regression so release-check now fails if active-workflow continuation falls back to the old prompt-only dispatch gate, await-user-input replies lose workflow dispatch rights, or ordinary main-chat turns stop being rejected
10
26
 
11
27
  ## 0.1.71
12
28
 
@@ -257,32 +257,8 @@ function isCompletionDriverPromptTurn(snapshot: CompletionStateSnapshot | undefi
257
257
  return true;
258
258
  }
259
259
 
260
- function isCompletionWorkflowSessionTurn(snapshot: CompletionStateSnapshot | undefined, ctx: { sessionManager?: any }): boolean {
261
- if (!(hasCompletionRoutingActivation(snapshot) || hasActiveWorkflowEntry(snapshot))) return false;
262
- return isCompletionDriverPromptTurn(snapshot, ctx) || isCookCommandTurn(ctx);
263
- }
264
-
265
- function isOrdinaryMainChatTurnDuringActiveWorkflow(
266
- snapshot: CompletionStateSnapshot | undefined,
267
- ctx: { sessionManager?: any },
268
- ): boolean {
269
- if (!hasActiveWorkflowEntry(snapshot)) return false;
270
- const latest = latestUserOrCustomTurnText(ctx);
271
- if (!latest) return false;
272
- if (isCookCommandTurn(ctx)) return false;
273
- if (isCompletionDriverPromptTurn(snapshot, ctx)) return false;
274
- return true;
275
- }
276
-
277
- function isCompletionRoleDispatchAllowedTurn(
278
- snapshot: CompletionStateSnapshot | undefined,
279
- ctx: { sessionManager?: any },
280
- ): boolean {
281
- if (hasCompletionRoutingActivation(snapshot)) return true;
282
- if (!hasActiveWorkflowEntry(snapshot)) return false;
283
- if (isCompletionWorkflowSessionTurn(snapshot, ctx)) return true;
284
- if (isOrdinaryMainChatTurnDuringActiveWorkflow(snapshot, ctx)) return false;
285
- return asString(snapshot?.state?.continuation_policy) === "continue";
260
+ function isCompletionWorkflowSessionTurn(snapshot: CompletionStateSnapshot | undefined, _ctx: { sessionManager?: any }): boolean {
261
+ return hasCompletionRoutingActivation(snapshot) || hasActiveWorkflowEntry(snapshot);
286
262
  }
287
263
 
288
264
  function shouldInjectCompletionWorkflowContext(snapshot: CompletionStateSnapshot | undefined, ctx: { sessionManager?: any }): boolean {
@@ -1104,7 +1080,7 @@ export default function completionExtension(pi: ExtensionAPI) {
1104
1080
  const snapshot = await loadCompletionSnapshot(cwd);
1105
1081
  const completionActive = Boolean(snapshot) && asString(snapshot?.state?.continuation_policy) !== "done";
1106
1082
  const root = snapshot?.files.root ?? findRepoRoot(cwd) ?? cwd;
1107
- const completionRoleDispatchAllowed = Boolean(role) || isCompletionRoleDispatchAllowedTurn(snapshot, ctx);
1083
+ const completionRoleDispatchAllowed = Boolean(role) || isCompletionWorkflowSessionTurn(snapshot, ctx);
1108
1084
  const reason = toolCallBlockReason({
1109
1085
  toolName: event.toolName,
1110
1086
  input: isRecord(event.input) ? event.input : undefined,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@linimin/pi-letscook",
3
- "version": "0.1.72",
3
+ "version": "0.1.74",
4
4
  "description": "Pi package for long-running completion workflows with canonical .agent state, role-based subagents, continuity, and verification helpers.",
5
5
  "license": "MIT",
6
6
  "private": false,
@@ -510,7 +510,10 @@ import sys
510
510
  from pathlib import Path
511
511
 
512
512
  reminder = Path(sys.argv[1])
513
- assert not reminder.exists(), 'ordinary non-/cook turn should not inject completion reminder solely from selected-slice canonical state'
513
+ assert reminder.exists(), 'active selected-slice canonical state should inject the completion reminder on subsequent non-/cook turns'
514
+ text = reminder.read_text()
515
+ assert 'Completion workflow detected.' in text, 'selected-slice reminder should expose canonical workflow context'
516
+ assert 'Verification evidence subject: selected_slice' in text, 'selected-slice reminder should expose the canonical evidence subject'
514
517
  PY
515
518
 
516
519
  python3 - <<'PY'
@@ -21,26 +21,26 @@ const assertNotIncludes = (file, snippet) => {
21
21
  }
22
22
  };
23
23
 
24
- assertIncludes('extensions/completion/index.ts', 'function isOrdinaryMainChatTurnDuringActiveWorkflow(');
25
- assertIncludes('extensions/completion/index.ts', 'function isCompletionRoleDispatchAllowedTurn(');
26
- assertIncludes('extensions/completion/index.ts', 'if (isOrdinaryMainChatTurnDuringActiveWorkflow(snapshot, ctx)) return false;');
27
- assertIncludes('extensions/completion/index.ts', 'return asString(snapshot?.state?.continuation_policy) === "continue";');
28
- assertIncludes('extensions/completion/index.ts', 'const completionRoleDispatchAllowed = Boolean(role) || isCompletionRoleDispatchAllowedTurn(snapshot, ctx);');
29
- assertIncludes('extensions/completion/index.ts', 'if (isCookCommandTurn(ctx)) return false;');
30
- assertIncludes('extensions/completion/index.ts', 'if (isCompletionDriverPromptTurn(snapshot, ctx)) return false;');
24
+ assertIncludes('extensions/completion/index.ts', 'function isCompletionWorkflowSessionTurn(');
25
+ assertIncludes('extensions/completion/index.ts', 'return hasCompletionRoutingActivation(snapshot) || hasActiveWorkflowEntry(snapshot);');
26
+ assertIncludes('extensions/completion/index.ts', 'const completionRoleDispatchAllowed = Boolean(role) || isCompletionWorkflowSessionTurn(snapshot, ctx);');
31
27
  assertIncludes('extensions/completion/policy-guards.ts', 'return "completion_role may only be used from an active /cook workflow session.";');
32
- assertIncludes('CHANGELOG.md', 'fixed completion-role continuation gating so an already-active `/cook` workflow with `continuation_policy: continue` can keep dispatching mandatory follow-up roles');
28
+ assertIncludes('CHANGELOG.md', 'made active `/cook` workflows sticky across subsequent turns so completion-role dispatch and workflow context continue to self-heal from canonical active state instead of depending on prompt-shaped driver turns');
29
+ assertIncludes('CHANGELOG.md', 'stopped pushing users to rerun `/cook` for routine active-workflow continuation or exact await-user-input replies when canonical workflow state is already active');
33
30
 
34
- assertNotIncludes(
35
- 'extensions/completion/index.ts',
36
- 'const completionRoleDispatchAllowed = Boolean(role) || isCompletionWorkflowSessionTurn(snapshot, ctx);',
37
- );
31
+ assertNotIncludes('extensions/completion/index.ts', 'function isOrdinaryMainChatTurnDuringActiveWorkflow(');
32
+ assertNotIncludes('extensions/completion/index.ts', 'function isCompletionRoleDispatchAllowedTurn(');
33
+ assertNotIncludes('extensions/completion/index.ts', 'function isAwaitingUserInputWorkflowReplyTurn(');
38
34
 
39
35
  const indexText = read('extensions/completion/index.ts');
40
- const ordinaryGuardIndex = indexText.indexOf('if (isOrdinaryMainChatTurnDuringActiveWorkflow(snapshot, ctx)) return false;');
41
- const continueFallbackIndex = indexText.indexOf('return asString(snapshot?.state?.continuation_policy) === "continue";');
42
- if (ordinaryGuardIndex === -1 || continueFallbackIndex === -1 || ordinaryGuardIndex > continueFallbackIndex) {
43
- throw new Error('extensions/completion/index.ts must reject ordinary main-chat turns before allowing the continuation_policy=continue fallback.');
36
+ const sessionTurnIndex = indexText.indexOf('function isCompletionWorkflowSessionTurn(');
37
+ const stickyReturnIndex = indexText.indexOf('return hasCompletionRoutingActivation(snapshot) || hasActiveWorkflowEntry(snapshot);');
38
+ const toolGateIndex = indexText.indexOf('const completionRoleDispatchAllowed = Boolean(role) || isCompletionWorkflowSessionTurn(snapshot, ctx);');
39
+ if (sessionTurnIndex === -1 || stickyReturnIndex === -1 || toolGateIndex === -1) {
40
+ throw new Error('extensions/completion/index.ts must derive workflow legitimacy from canonical active state and reuse that gate for completion_role dispatch.');
41
+ }
42
+ if (!(sessionTurnIndex < stickyReturnIndex && stickyReturnIndex < toolGateIndex)) {
43
+ throw new Error('extensions/completion/index.ts should define sticky workflow-session detection before reusing it for completion_role dispatch.');
44
44
  }
45
45
  NODE
46
46
 
@@ -34,8 +34,8 @@ checks = {
34
34
  'description: "/cook workflow: start or replace workflow only from an explicit primary-agent handoff, or resume the current workflow from canonical state"',
35
35
  '"Do not call completion_role from ordinary chat; it is reserved for active /cook workflow sessions."',
36
36
  '`COMPLETION WORKFLOW DRIVER\\nStart or continue the completion workflow for this repo.',
37
- 'function isCompletionRoleDispatchAllowedTurn(',
38
- 'return asString(snapshot?.state?.continuation_policy) === "continue";',
37
+ 'function isCompletionWorkflowSessionTurn(',
38
+ 'return hasCompletionRoutingActivation(snapshot) || hasActiveWorkflowEntry(snapshot);',
39
39
  ],
40
40
  "extensions/completion/policy-guards.ts": [
41
41
  'return "completion_role may only be used from an active /cook workflow session.";',
@@ -273,20 +273,16 @@ reminder = Path(sys.argv[3])
273
273
  handoff = Path(sys.argv[4])
274
274
  auto_resume = Path(sys.argv[5])
275
275
 
276
- assert not reminder.exists(), 'ordinary non-/cook turn should not inject completion reminder solely from canonical state'
277
- assert handoff.exists(), 'ordinary non-/cook turn should inject the /cook handoff boundary reminder'
278
- handoff_text = handoff.read_text()
279
- assert 'ordinary main chat unless the user explicitly runs /cook' in handoff_text, 'ordinary handoff reminder should preserve explicit /cook workflow entry'
280
- assert 'directly implement requested repo changes, including multi-file work' in handoff_text, 'ordinary handoff reminder should allow direct ordinary-chat implementation'
281
- assert 'Do not proactively tell the user to run /cook' in handoff_text, 'ordinary handoff reminder should keep ordinary chat neutral until explicit /cook entry'
282
- assert '/cook is optional workflow mode' in handoff_text, 'ordinary handoff reminder should position /cook as optional workflow mode'
283
- assert 'In ordinary chat, do not load or follow completion-protocol, and do not call completion_role.' in handoff_text, 'ordinary handoff reminder should forbid workflow-role routing before explicit /cook'
284
- assert 'If the user wants direct implementation now, stay in ordinary chat and help directly instead of blocking on /cook.' in handoff_text, 'ordinary handoff reminder should avoid blocking implementation on /cook'
285
- assert 'the extension should call a primary-agent handoff synthesis step from the current task context' in handoff_text, 'ordinary handoff reminder should describe same-entry primary-agent handoff synthesis for /cook'
286
- assert 'Do not expect /cook to infer or guess startup intent from recent discussion alone' in handoff_text, 'ordinary handoff reminder should forbid /cook-side guessing'
287
- assert 'do not silently rewrite discussion into canonical workflow state' in handoff_text, 'ordinary handoff reminder should preserve non-canonical ordinary-chat behavior'
288
- assert not auto_resume.exists(), 'ordinary non-/cook turn should not queue auto-resume before /cook activation'
289
- assert 'Skipped completion workflow auto-resume prompt (test mode)' not in output, 'ordinary non-/cook turn should not attempt auto-resume'
276
+ assert reminder.exists(), 'active workflow should inject the completion reminder on subsequent non-/cook turns'
277
+ reminder_text = reminder.read_text()
278
+ assert 'Completion workflow detected.' in reminder_text, 'active workflow reminder should inject canonical workflow context'
279
+ assert 'If continuation_policy == continue, do not stop after a slice or ask whether to continue; dispatch the next mandatory role directly.' in reminder_text, 'active workflow reminder should direct mandatory continuation'
280
+ assert not handoff.exists(), 'active workflow should not fall back to the ordinary /cook handoff boundary reminder'
281
+ assert auto_resume.exists(), 'active workflow should queue an auto-resume prompt from canonical state without requiring another /cook'
282
+ auto_resume_text = auto_resume.read_text()
283
+ assert 'COMPLETION WORKFLOW DRIVER' in auto_resume_text, 'auto-resume prompt should use the workflow driver format'
284
+ assert 'Resume the completion workflow from canonical state.' in auto_resume_text, 'auto-resume prompt should resume canonical workflow state'
285
+ assert 'Skipped completion workflow auto-resume prompt (test mode)' in output, 'active workflow should attempt auto-resume in test mode'
290
286
  PY
291
287
 
292
288
  PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
@@ -389,11 +385,19 @@ active['evaluation_profile'] = profile['evaluation_profile']
389
385
  active_path.write_text(json.dumps(active, indent=2) + '\n')
390
386
  PY
391
387
 
388
+ if ! git rev-parse HEAD >/dev/null 2>&1; then
389
+ git config user.name "smoke-test"
390
+ git config user.email "smoke-test@example.invalid"
391
+ git commit --allow-empty -m "smoke baseline" >/dev/null
392
+ fi
393
+
392
394
  python3 - <<'PY'
393
395
  import json
396
+ import subprocess
394
397
  from pathlib import Path
395
398
  path = Path('.agent/active-slice.json')
396
399
  active = json.loads(path.read_text())
400
+ head = subprocess.check_output(['git', 'rev-parse', 'HEAD'], text=True).strip()
397
401
  active.update({
398
402
  'status': 'selected',
399
403
  'slice_id': 'smoke-slice',
@@ -405,7 +409,7 @@ active.update({
405
409
  'must_fix_findings': [],
406
410
  'implementation_surfaces': ['extensions/completion/index.ts', '.agent/verify_completion_control_plane.sh'],
407
411
  'verification_commands': ['bash .agent/verify_completion_control_plane.sh', 'npm run smoke-test'],
408
- 'basis_commit': 'deadbeef',
412
+ 'basis_commit': head,
409
413
  'remaining_contract_ids_before': ['smoke-contract'],
410
414
  'release_blocker_count_before': 1,
411
415
  'high_value_gap_count_before': 0,