iriai-build 0.4.2 → 0.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iriai-build",
3
- "version": "0.4.2",
3
+ "version": "0.4.4",
4
4
  "description": "Iriai Build tool — AI agent orchestration CLI",
5
5
  "type": "module",
6
6
  "bin": {
@@ -2132,12 +2132,12 @@ export class Orchestrator {
2132
2132
  }
2133
2133
 
2134
2134
  // Handle [DECISION] blocks — render as separate decision prompts.
2135
- // Skip any that duplicate a deferred decision (already queued by _requestPhaseReview).
2135
+ // Skip ALL Operator-emitted decisions when a deferred decision exists for this feature.
2136
+ // The orchestrator already manages phase review / plan-approval decisions via
2137
+ // _requestPhaseReview → _postDeferredDecision. Operator [DECISION] blocks would
2138
+ // duplicate those, even if the IDs don't match exactly.
2136
2139
  for (const decision of parsed.decisions) {
2137
- if (this._deferredDecisions[feature.id]) {
2138
- const deferred = this._deferredDecisions[feature.id].decision;
2139
- if (deferred.id === decision.id) continue;
2140
- }
2140
+ if (this._deferredDecisions[feature.id]) continue;
2141
2141
  const options = decision.options.map(o => ({
2142
2142
  id: o.id,
2143
2143
  label: o.label,
@@ -2649,11 +2649,16 @@ export class Orchestrator {
2649
2649
  return;
2650
2650
  }
2651
2651
 
2652
+ // Re-read feature from DB — the file watcher's _handlePlanningDone may have
2653
+ // updated phase/role between the time the exit event was queued and now.
2654
+ const freshFeature = queries.getFeatureById(feature.id);
2655
+ if (!freshFeature) return;
2656
+
2652
2657
  // Check if the file watcher already processed .done (it deletes the file).
2653
2658
  // If review gate is posted or agent already marked done, don't retry.
2654
2659
  const meta = queries.getFeatureMetadata(feature.id);
2655
2660
  const agent = queries.getAgentById(agentId);
2656
- if (meta.awaiting_phase_review && meta.phase_review_role === roleName) {
2661
+ if (meta.awaiting_phase_review) {
2657
2662
  console.log(`[orchestrator] ${roleName} exit: review gate already posted, skipping retry`);
2658
2663
  queries.updateAgentStatus(agentId, "done");
2659
2664
  return;
@@ -2662,9 +2667,15 @@ export class Orchestrator {
2662
2667
  console.log(`[orchestrator] ${roleName} exit: already marked done, skipping retry`);
2663
2668
  return;
2664
2669
  }
2665
- // Also skip if phase has already advanced past this role
2666
- if (feature.active_planning_role && feature.active_planning_role !== roleName) {
2667
- console.log(`[orchestrator] ${roleName} exit: phase already advanced to ${feature.active_planning_role}, skipping retry`);
2670
+ // Skip if feature has left planning phase (e.g. plan-compiler PASS → plan-approval)
2671
+ if (freshFeature.phase !== "planning") {
2672
+ console.log(`[orchestrator] ${roleName} exit: feature phase is ${freshFeature.phase}, not planning — skipping retry`);
2673
+ queries.updateAgentStatus(agentId, "done");
2674
+ return;
2675
+ }
2676
+ // Skip if phase has already advanced past this role (null means role was cleared)
2677
+ if (freshFeature.active_planning_role !== roleName) {
2678
+ console.log(`[orchestrator] ${roleName} exit: active role is now ${freshFeature.active_planning_role || "none"}, skipping retry`);
2668
2679
  queries.updateAgentStatus(agentId, "done");
2669
2680
  return;
2670
2681
  }
package/v3/recovery.js CHANGED
@@ -100,8 +100,10 @@ export class Recovery {
100
100
  };
101
101
  }
102
102
 
103
- // Emit stale signals
103
+ // Emit stale signals — only for the active planning role
104
+ const activeRole = feature.active_planning_role;
104
105
  for (const [role, dir] of Object.entries(planningTree)) {
106
+ if (role !== activeRole) continue;
105
107
  for (const [sig, eventType] of [
106
108
  [SIGNAL.DONE, "planning:done"],
107
109
  [SIGNAL.AGENT_RESPONSE, "planning:response"],
@@ -281,8 +283,12 @@ export class Recovery {
281
283
  };
282
284
  }
283
285
 
284
- // Emit stale signals
286
+ // Emit stale signals — only for the active planning role to avoid
287
+ // re-processing signals from already-completed roles (which would cause
288
+ // duplicate artifact uploads and decision re-posts).
289
+ const activeRole = feature.active_planning_role;
285
290
  for (const [role, dir] of Object.entries(planningTree)) {
291
+ if (role !== activeRole) continue;
286
292
  for (const [sig, eventType] of [
287
293
  [SIGNAL.DONE, "planning:done"],
288
294
  [SIGNAL.AGENT_RESPONSE, "planning:response"],