@exaudeus/workrail 3.70.1 → 3.70.3

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.
@@ -473,16 +473,16 @@
473
473
  "sha256": "5fe866e54f796975dec5d8ba9983aefd86074db212d3fccd64eed04bc9f0b3da",
474
474
  "bytes": 8011
475
475
  },
476
- "console-ui/assets/index-BcZJOyVG.js": {
477
- "sha256": "30e50a4bf35f3383569bf5c19e27ab255611f2524048ce306017e88ddffb2b69",
478
- "bytes": 767983
479
- },
480
476
  "console-ui/assets/index-DHrKiMCf.css": {
481
477
  "sha256": "40290b50e21ee7e82433efe13b1aa31c1ea608bd057a5c4e324982f284bc928b",
482
478
  "bytes": 60673
483
479
  },
480
+ "console-ui/assets/index-Gmbzhc2B.js": {
481
+ "sha256": "6b036eb732162293f420948cabdf0c01fcb76ddafa7a37d0efe54e3a16677c88",
482
+ "bytes": 767983
483
+ },
484
484
  "console-ui/index.html": {
485
- "sha256": "813832d8d8ed9de1597a4c972111a165117cfaa97674a0bbb66783f8de954cb8",
485
+ "sha256": "c854d29d0cf2bdb702504ca72c8a5980beb76c7f77bd212cfd21c98496a46862",
486
486
  "bytes": 417
487
487
  },
488
488
  "console/standalone-console.d.ts": {
@@ -614,8 +614,8 @@
614
614
  "bytes": 1216
615
615
  },
616
616
  "daemon/daemon-events.d.ts": {
617
- "sha256": "ba8fbd7be2cdd5f0f53c45b3ed4f27b51739ca3cb782e931629469597f98250d",
618
- "bytes": 5359
617
+ "sha256": "469cc9b6954e19a5eb87c3fac42f96fd66cd31321b33ce741fb9c1aa64cb2b80",
618
+ "bytes": 5369
619
619
  },
620
620
  "daemon/daemon-events.js": {
621
621
  "sha256": "b6841eef4634bb266faf81961c1e387b535dd64a74d58582f3f2bad8c3469d95",
@@ -658,8 +658,8 @@
658
658
  "bytes": 8385
659
659
  },
660
660
  "daemon/workflow-runner.js": {
661
- "sha256": "0575f958423e4f0a4abbc712e0901c5bee8c5ed5f2ffa4b3a68083f4251e1243",
662
- "bytes": 102945
661
+ "sha256": "87e8dfbc87f2794f79b4c591eb65a204f12761e3b438174da0f81097fd1e5f4a",
662
+ "bytes": 103183
663
663
  },
664
664
  "di/container.d.ts": {
665
665
  "sha256": "003bb7fb7478d627524b9b1e76bd0a963a243794a687ff233b96dc0e33a06d9f",
@@ -1718,12 +1718,12 @@
1718
1718
  "bytes": 6968
1719
1719
  },
1720
1720
  "trigger/polling-scheduler.d.ts": {
1721
- "sha256": "60df456a31fa87ce71de76f5e31a6c460bfab588a24c8a2f06bf926fdcea550a",
1722
- "bytes": 1096
1721
+ "sha256": "c8bcd28794a23906feabe276725e63fe7af79749e0517a46c6e0ed41da0cabb2",
1722
+ "bytes": 1152
1723
1723
  },
1724
1724
  "trigger/polling-scheduler.js": {
1725
- "sha256": "a546506d1586b0020a64f1321c07d929f7b24920a0fbd18d6566ff73fb6d9185",
1726
- "bytes": 22409
1725
+ "sha256": "0f8264ac6393d91e22cc48d6fb89095de2391420175009b68b5848c13be3f7f0",
1726
+ "bytes": 22481
1727
1727
  },
1728
1728
  "trigger/trigger-listener.d.ts": {
1729
1729
  "sha256": "cbd89c24cdfe89cb555946b0dcaa6836bf81f37db56c5e80d408dc4a2fe42fb9",
@@ -2066,8 +2066,8 @@
2066
2066
  "bytes": 1664
2067
2067
  },
2068
2068
  "v2/durable-core/domain/prompt-renderer.js": {
2069
- "sha256": "fe7d177a79cf8cbf2ee814a0e20e24b4cb3ea1b332c4f6aaf57d0f601a95419b",
2070
- "bytes": 22976
2069
+ "sha256": "aebd7b0b9db2b48e0f0d43aa0fd7c66212f51bed17b18a77b4e43eb06cbc759d",
2070
+ "bytes": 24140
2071
2071
  },
2072
2072
  "v2/durable-core/domain/reason-model.d.ts": {
2073
2073
  "sha256": "a944e7e0d9b3c73468488263cb0aa1e446c023f8084fd2af53cbda3f3bfcd37a",
@@ -16,10 +16,11 @@ export declare class PollingScheduler {
16
16
  private readonly router;
17
17
  private readonly store;
18
18
  private readonly fetchFn?;
19
+ private readonly sessionsDir;
19
20
  private readonly intervals;
20
21
  private readonly polling;
21
22
  private readonly dispatchingIssues;
22
- constructor(triggers: readonly TriggerDefinition[], router: TriggerRouter, store: PolledEventStore, fetchFn?: FetchFn | undefined);
23
+ constructor(triggers: readonly TriggerDefinition[], router: TriggerRouter, store: PolledEventStore, fetchFn?: FetchFn | undefined, sessionsDir?: string);
23
24
  start(): void;
24
25
  stop(): void;
25
26
  forcePoll(triggerId: string): Promise<ForcePollResult>;
@@ -46,11 +46,12 @@ function isPollingTrigger(trigger) {
46
46
  return trigger.pollingSource !== undefined;
47
47
  }
48
48
  class PollingScheduler {
49
- constructor(triggers, router, store, fetchFn) {
49
+ constructor(triggers, router, store, fetchFn, sessionsDir = path.join(os.homedir(), '.workrail', 'daemon-sessions')) {
50
50
  this.triggers = triggers;
51
51
  this.router = router;
52
52
  this.store = store;
53
53
  this.fetchFn = fetchFn;
54
+ this.sessionsDir = sessionsDir;
54
55
  this.intervals = new Map();
55
56
  this.polling = new Map();
56
57
  this.dispatchingIssues = new Set();
@@ -222,7 +223,7 @@ class PollingScheduler {
222
223
  await appendQueuePollLog({ event: 'poll_cycle_complete', triggerId, reason: 'not_implemented', queueType: queueConfig.type, ts: new Date().toISOString() });
223
224
  return;
224
225
  }
225
- const sessionsDir = path.join(os.homedir(), '.workrail', 'daemon-sessions');
226
+ const sessionsDir = this.sessionsDir;
226
227
  const activeSessions = await countActiveSessions(sessionsDir);
227
228
  if (activeSessions >= queueConfig.maxTotalConcurrentSessions) {
228
229
  console.log(`[QueuePoll] Skipping cycle: active sessions (${activeSessions}) >= maxTotalConcurrentSessions (${queueConfig.maxTotalConcurrentSessions}).`);
@@ -6,6 +6,7 @@ exports.assembleFragmentedPrompt = assembleFragmentedPrompt;
6
6
  exports.renderPendingPrompt = renderPendingPrompt;
7
7
  const neverthrow_1 = require("neverthrow");
8
8
  const workflow_js_1 = require("../../../types/workflow.js");
9
+ const workflow_definition_js_1 = require("../../../types/workflow-definition.js");
9
10
  const index_js_1 = require("../ids/index.js");
10
11
  const run_dag_js_1 = require("../../projections/run-dag.js");
11
12
  const node_outputs_js_1 = require("../../projections/node-outputs.js");
@@ -216,16 +217,16 @@ function buildMetricsSection(profile, isLastStep, cleanFormat) {
216
217
  if (!isLastStep)
217
218
  return shaFooter;
218
219
  const finalFooter = cleanFormat
219
- ? '\n\nMetrics (final): also set metrics_outcome, metrics_pr_numbers, metrics_files_changed, metrics_lines_added, metrics_lines_removed in context.'
220
- : '\n\n**METRICS (System):** This is the final step. Also report:\n- `metrics_outcome`: `"success"` | `"partial"` | `"abandoned"` | `"error"`\n- `metrics_pr_numbers`: array of integer PR numbers (not URLs)\n- `metrics_files_changed`: integer count\n- `metrics_lines_added`: integer count\n- `metrics_lines_removed`: integer count\n\nCall `continue_workflow` with all of the above in `context: { metrics_commit_shas: [...], metrics_outcome: "success", ... }`.';
220
+ ? '\n\nMetrics (final): also set metrics_outcome (exactly one of: "success", "partial", "abandoned", "error"), metrics_pr_numbers, metrics_files_changed, metrics_lines_added, metrics_lines_removed in context.'
221
+ : '\n\n**METRICS (System):** This is the final step. Also report:\n- `metrics_outcome`: set to exactly one of these four strings -- no other values are valid: `"success"`, `"partial"`, `"abandoned"`, `"error"`. Do not describe what you did -- classify the outcome using only these values.\n- `metrics_pr_numbers`: array of integer PR numbers (not URLs)\n- `metrics_files_changed`: integer count\n- `metrics_lines_added`: integer count\n- `metrics_lines_removed`: integer count\n\nCall `continue_workflow` with all of the above in `context: { metrics_commit_shas: [...], metrics_outcome: "success", ... }`.';
221
222
  return shaFooter + finalFooter;
222
223
  }
223
224
  case 'review': {
224
225
  if (!isLastStep)
225
226
  return '';
226
227
  return cleanFormat
227
- ? '\n\nMetrics (final): set metrics_pr_numbers (integer array) and metrics_outcome in context.'
228
- : '\n\n**METRICS (System):** This is the final step of a review workflow. Report:\n- `metrics_pr_numbers`: array of integer PR numbers reviewed (not URLs)\n- `metrics_outcome`: `"success"` | `"partial"` | `"abandoned"` | `"error"`\n\nCall `continue_workflow` with `context: { metrics_pr_numbers: [123], metrics_outcome: "success" }`.';
228
+ ? '\n\nMetrics (final): set metrics_pr_numbers (integer array) and metrics_outcome (exactly one of: "success", "partial", "abandoned", "error") in context.'
229
+ : '\n\n**METRICS (System):** This is the final step of a review workflow. Report:\n- `metrics_pr_numbers`: array of integer PR numbers reviewed (not URLs)\n- `metrics_outcome`: set to exactly one of these four strings -- no other values are valid: `"success"`, `"partial"`, `"abandoned"`, `"error"`. Do not describe what you did -- classify the outcome using only these values.\n\nCall `continue_workflow` with `context: { metrics_pr_numbers: [123], metrics_outcome: "success" }`.';
229
230
  }
230
231
  case 'research':
231
232
  case 'design':
@@ -233,8 +234,8 @@ function buildMetricsSection(profile, isLastStep, cleanFormat) {
233
234
  if (!isLastStep)
234
235
  return '';
235
236
  return cleanFormat
236
- ? '\n\nMetrics (final): set metrics_outcome in context.'
237
- : '\n\n**METRICS (System):** This is the final step. Report:\n- `metrics_outcome`: `"success"` | `"partial"` | `"abandoned"` | `"error"`\n\nCall `continue_workflow` with `context: { metrics_outcome: "success" }`.';
237
+ ? '\n\nMetrics (final): set metrics_outcome (exactly one of: "success", "partial", "abandoned", "error") in context.'
238
+ : '\n\n**METRICS (System):** This is the final step. Report:\n- `metrics_outcome`: set to exactly one of these four strings -- no other values are valid: `"success"`, `"partial"`, `"abandoned"`, `"error"`. Do not describe what you did -- classify the outcome using only these values.\n\nCall `continue_workflow` with `context: { metrics_outcome: "success" }`.';
238
239
  }
239
240
  }
240
241
  }
@@ -372,8 +373,17 @@ function renderPendingPrompt(args) {
372
373
  : '';
373
374
  const lastTopLevelStepId = args.workflow.definition.steps.at(-1)?.id;
374
375
  const isLastTopLevelStep = args.stepId === lastTopLevelStepId;
375
- const isLastExitStep = isExitStep && resolveParentLoopStep(args.workflow, args.stepId)?.id === lastTopLevelStepId;
376
- const isLastStep = isLastTopLevelStep || isLastExitStep;
376
+ const lastTopLevelStep = args.workflow.definition.steps.at(-1);
377
+ const isLastNonExitStepOfLastLoop = (() => {
378
+ if (!lastTopLevelStep || !(0, workflow_definition_js_1.isLoopStepDefinition)(lastTopLevelStep))
379
+ return false;
380
+ const body = lastTopLevelStep.body;
381
+ if (!Array.isArray(body) || body.length === 0)
382
+ return false;
383
+ const lastNonExit = [...body].reverse().find((b) => b.outputContract?.contractRef !== index_js_2.LOOP_CONTROL_CONTRACT_REF);
384
+ return lastNonExit?.id === args.stepId;
385
+ })();
386
+ const isLastStep = isLastTopLevelStep || isLastNonExitStepOfLastLoop;
377
387
  const metricsSection = buildMetricsSection(args.workflow.definition.metricsProfile, isLastStep, cleanResponseFormat);
378
388
  const enhancedPrompt = [
379
389
  loopBanner,
@@ -1,110 +1,107 @@
1
- # Design Review Findings: wr.discovery Goal Reframing
1
+ # Design Review Findings: Issue #393 Stale Tracker Close
2
2
 
3
- *Concise, actionable findings for main-agent synthesis. Not a final decision.*
4
- **Date:** 2026-04-18
3
+ **Reviewed design:** Candidate A -- Close Issue #393 with evidence comment
4
+ **Review date:** 2026-04-23
5
+ **Reviewer:** wr.discovery session (design_first / QUICK path)
5
6
 
6
7
  ---
7
8
 
8
9
  ## Tradeoff Review
9
10
 
10
- **Tradeoff 1: goalType classification remains agent judgment**
11
+ **Tradeoff 1: Close comment mentions why auto-close did not fire**
12
+ - Does not violate any decision criterion -- in fact satisfies Criterion 5 (captures the learning)
13
+ - Tone risk (sounds like blame): mitigated by using technical/mechanical language
14
+ - Hidden assumption: maintainer reads close comments -- reasonable for a 1-person repo
15
+ - **Verdict: Acceptable**
11
16
 
12
- - Acceptable: this pattern is established in the workflow (rigorMode, pathRecommendation are all agent-derived)
13
- - Classification examples in the procedure reduce misclassification probability
14
- - **Finding: YELLOW.** Add goalType classification examples to procedure text to reduce ambiguity at the problem_framed / opportunity_framed boundary.
15
-
16
- **Tradeoff 2: overhead for well-framed goals is nonzero**
17
-
18
- - A few additional context variable captures and procedure lines in Phase 0
19
- - Well-framed goals produce minimal output ('goalType = problem_framed, no impliedProblem needed')
20
- - **Finding: NON-ISSUE.** Overhead is trivial.
21
-
22
- **Tradeoff 3: Phase 1g always-on for design_first/full_spectrum**
23
-
24
- - One additional advance per session for these paths
25
- - Produces trivially short output for well-framed sessions ('pathChangedAfterContext = false')
26
- - **CRITICAL CORRECTION NEEDED:** Phase 1g runCondition must be an OR (`retriageNeeded = true OR pathRecommendation in [design_first, full_spectrum]`) not a replacement. Otherwise landscape_first sessions that explicitly need retriage will not trigger it.
27
- - **Finding: YELLOW.** Runnable as-designed if the OR condition is used correctly.
17
+ **Tradeoff 2: No AGENTS.md update -- pattern prevention sacrificed**
18
+ - Actively satisfies Criterion 3 (no new problems) by not touching a protected human-maintained file
19
+ - Only one observed instance of the failure mode (PR #790) -- insufficient evidence of a recurring pattern
20
+ - The learning is captured in the close comment itself, which is more contextually located than AGENTS.md
21
+ - **Verdict: Acceptable**
28
22
 
29
23
  ---
30
24
 
31
25
  ## Failure Mode Review
32
26
 
33
- **Failure mode 1: Agent misclassifies solution-framed goal as opportunity_framed**
34
- - Status: **Partially mitigated.** C3's Phase 1e/1f required 'what would make this framing wrong' output provides a downstream catch. Classification examples in Phase 0 reduce probability.
35
- - Missing mitigation: examples in procedure (address in revisions)
36
- - **Finding: MEDIUM risk, mitigated.**
27
+ **FM1: Maintainer re-opens because they wanted to close personally**
28
+ - Design handles it: close comment provides full evidence chain; re-open is 5-second CLI command; no data loss
29
+ - Likelihood: Low (0 comments, 2-day stale, daemon assignee, no checkbox activity)
30
+ - Severity if occurs: Minimal (issue re-opens, maintainer closes manually, traceability comment persists)
31
+ - Missing mitigations: None needed
37
32
 
38
- **Failure mode 2: 'What would make this framing wrong' output is formulaic**
39
- - Status: **Partially mitigated.** Making it required non-empty enforces form but not quality.
40
- - Missing mitigation: specificity instruction ('name ONE concrete condition, not a general caveat')
41
- - **Finding: LOW-MEDIUM risk.**
42
-
43
- **Failure mode 3: Phase 1g doesn't surface new insights for well-framed sessions**
44
- - Status: **Non-issue by design.** For well-framed sessions, Phase 1g is a graceful no-op that confirms the path is still correct. One advance wasted, nothing more.
45
- - **Finding: LOW risk, acceptable.**
33
+ **FM2: CI secretly failing for the test file**
34
+ - Design handles it: 14/14 passing verified locally immediately before action; test is isolated unit coverage
35
+ - No related CI failure issues exist in the open issues list for this test file
36
+ - Severity if occurs: Low (tracker close is orthogonal to CI state; no test regression introduced)
37
+ - Missing mitigations: None needed
46
38
 
47
39
  ---
48
40
 
49
41
  ## Runner-Up / Simpler Alternative Review
50
42
 
51
- **C2 (mandatory Phase 0a):** The structural enforcement advantage is real but comes at the cost of a mandatory overhead step for all sessions. The C1+C3 hybrid achieves most of C2's value via procedure-level enforcement plus structural runCondition changes. C2 is the right escalation if the hybrid proves insufficient.
52
-
53
- **Simpler variant:** Just one sentence added to Phase 0: 'If the goal is solution-framed, derive the underlying problem.' Too narrow -- no context variables means no downstream reference to the reframing.
54
-
55
- **`alternativeFraming` addition from C2:** Borrowing C2's `alternativeFraming` requirement (one reframe even when the original goal seems correct) is high-value, low-cost. Add to Phase 0 design doc entry, not as a context variable.
43
+ **Runner-up (Candidate B -- close + AGENTS.md note):**
44
+ - Candidate B's only distinct value (keyword failure mode documentation) is fully absorbed into Candidate A's close comment
45
+ - No element of B is orphaned; no hybrid needed
56
46
 
57
- **Finding: C1+C3 hybrid with two refinements (examples, alternativeFraming) stands. No direction change needed.**
47
+ **Simpler variant (close without comment):**
48
+ - Fails Criterion 2 (no traceability) and Criterion 5 (no learning capture)
49
+ - Not viable -- the comment is load-bearing, not decorative
58
50
 
59
51
  ---
60
52
 
61
53
  ## Philosophy Alignment
62
54
 
63
- | Principle | Status |
64
- |---|---|
65
- | Validate at boundaries, trust inside | SATISFIED -- Phase 0 becomes an active validator |
66
- | Make illegal states unrepresentable | PARTIALLY SATISFIED -- C3 structural changes help; C2 would fully satisfy |
67
- | YAGNI with discipline | SATISFIED -- no new steps, minimal additions |
68
- | Architectural fixes over patches | SATISFIED -- runCondition changes and required output contracts are structural |
69
- | Determinism over cleverness | SATISFIED -- same goalType input produces same path behavior |
55
+ **Clearly satisfied:**
56
+ - Validate at boundaries, trust inside -- all validation done before action
57
+ - Observability -- close comment makes the state transition and rationale fully visible
58
+ - Document "why" not "what" -- comment explains rationale, not just action
59
+ - Atomicity -- single CLI call, no partial state possible
60
+ - Architectural fixes over patches -- reframe correctly identified the real problem (stale tracker) before acting
70
61
 
71
- **One explicit philosophy tension:** 'Make illegal states unrepresentable' vs 'YAGNI with discipline' -- deliberately accepted, C2 is escalation path.
62
+ **Under tension:**
63
+ - Agent authority over human-filed issues -- mild tension; resolved by reversibility + transparent comment
64
+ - **Verdict: Acceptable tension, not risky**
72
65
 
73
66
  ---
74
67
 
75
68
  ## Findings
76
69
 
77
- ### Yellow findings
70
+ No RED or ORANGE findings. All challenges and reviews converge.
78
71
 
79
- **Y1: goalType classification boundary ambiguity**
80
- The boundary between `problem_framed` and `opportunity_framed` is unclear without examples. Add classification examples to Phase 0 procedure to reduce misclassification at this boundary.
81
-
82
- **Y2: Phase 1g runCondition must be OR, not replacement**
83
- The retriage step runCondition must be: `retriageNeeded = true OR pathRecommendation == design_first OR pathRecommendation == full_spectrum`. A straight replacement would break landscape_first sessions that legitimately need retriage.
84
-
85
- **Y3: 'What would make this framing wrong' needs specificity instruction**
86
- The required output field should specify 'name ONE concrete falsification condition, not a general caveat.' Without this, the field can be satisfied by formulaic responses.
87
-
88
- ### No Red or Orange findings
89
-
90
- The selected C1+C3 direction has no material structural weaknesses.
72
+ **YELLOW -- Tone of close comment (INFO)**
73
+ - Risk: the explanation of why auto-close did not fire could read as attributing a mistake to the PR author
74
+ - Mitigation: use mechanical/technical language ("GitHub requires `Closes #NNN` syntax; PR #790 used different phrasing") rather than evaluative language
75
+ - Action: word the comment accordingly -- no structural change to the design needed
91
76
 
92
77
  ---
93
78
 
94
79
  ## Recommended Revisions
95
80
 
96
- 1. **Add goalType classification examples** to Phase 0 procedure (solution_framed: 'add X', 'implement Y', 'build X'; problem_framed: 'reduce X', 'fix Y'; opportunity_framed: 'explore X', 'decide whether Y'; decision_framed: 'choose between A and B')
81
+ **Revision 1 (from YELLOW finding): Prescribe exact comment wording**
97
82
 
98
- 2. **Add `alternativeFraming`** as a required design doc entry in Phase 0: 'Before selecting a path, generate one alternative framing -- if the stated goal is wrong, what would a better goal be?'
83
+ Use this comment text:
99
84
 
100
- 3. **Use OR condition for Phase 1g runCondition:** `retriageNeeded = true OR pathRecommendation in [design_first, full_spectrum]`
85
+ > All acceptance criteria for this issue are satisfied on `main`.
86
+ >
87
+ > - `loadSessionNotes` is exported at `src/daemon/workflow-runner.ts` (added in PR #790)
88
+ > - All 4 failure paths (token decode, store load, projection, unexpected exception) and the happy path are covered by `tests/unit/workflow-runner-load-session-notes.test.ts` (added in PR #782)
89
+ > - 14 tests pass: `npx vitest run tests/unit/workflow-runner-load-session-notes.test.ts`
90
+ >
91
+ > Note: PR #790 referenced this issue as "Closes issue #393 pre-existing test failures" but GitHub's auto-close requires the exact syntax `Closes #393` -- the non-standard phrasing is why the issue was not automatically closed on merge.
101
92
 
102
- 4. **Add specificity instruction** to Phase 1e/1f 'what would make this framing wrong' field: require naming one concrete falsification condition.
93
+ This wording is factual, neutral, and gives any future reader everything they need to verify or re-open.
103
94
 
104
95
  ---
105
96
 
106
97
  ## Residual Concerns
107
98
 
108
- 1. The goalType classification is LLM-dependent. Without empirical testing on real sessions, we cannot confirm the classification is reliable. This is an inherent limitation of the approach.
99
+ **RC1 (low): Pattern recurrence unaddressed**
100
+ If the PR keyword failure mode recurs on a second PR, the case for Candidate B (AGENTS.md note) strengthens. This is not actionable now but should be noted for future monitoring.
101
+
102
+ **RC2 (very low): Open CI failures on main**
103
+ There are 10+ open "CI failure on main blocking release" issues. These are unrelated to the test file in question (verified locally). If main CI is broken in a way that affects this test file, the close would be slightly premature. Probability is very low given local verification.
104
+
105
+ ---
109
106
 
110
- 2. The C1+C3 hybrid does not prevent path-selection bias for the window between Phase 0 path selection and Phase 1g retriage. A session that selects the wrong path in Phase 0 runs several steps in the wrong direction before Phase 1g can correct it. Acceptable for STANDARD rigor; C2 is the correct escalation if this proves problematic.
107
+ **Overall verdict: PROCEED with Candidate A as designed, using the prescribed comment wording from Revision 1.**