@sellable/mcp 0.1.748 → 0.1.749

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.
@@ -1470,12 +1470,18 @@ async function selectWorkspaceExactTarget(input, workspacePlanOverride, options
1470
1470
  // opening is barred for those targets (in the selection loop below).
1471
1471
  const completeDeferredTargets = blockedExactTargets.filter((blocked) => blocked.blocker === "complete" ||
1472
1472
  blocked.blocker === "capped_by_scheduler");
1473
+ const waiveCompleteClaims = options.waiveCompleteClaims === true;
1473
1474
  const prefilterBlockedTargets = blockedExactTargets.filter((blocked) => blocked.blocker !== "complete" &&
1474
- blocked.blocker !== "capped_by_scheduler");
1475
+ blocked.blocker !== "capped_by_scheduler" &&
1476
+ // fix(112as): on the bounded release pass, a self-issued
1477
+ // exact_action_complete claim no longer bars its own edge.
1478
+ !(waiveCompleteClaims && blocked.blocker === "exact_action_complete"));
1475
1479
  const seenTargetKeys = new Set();
1476
1480
  // fix(112ak): every refusal is recorded so a non-empty-census terminal can
1477
1481
  // name the concrete per-lane blocker instead of reporting a bare dead end.
1478
- const coordinatorExcludedEdges = [];
1482
+ const coordinatorExcludedEdges = [
1483
+ ...(options.seedExcludedEdges ?? []),
1484
+ ];
1479
1485
  const recordExcludedEdge = (candidate, blocked, stage) => {
1480
1486
  coordinatorExcludedEdges.push({
1481
1487
  campaignId: candidate.campaignId,
@@ -1487,6 +1493,46 @@ async function selectWorkspaceExactTarget(input, workspacePlanOverride, options
1487
1493
  stage,
1488
1494
  });
1489
1495
  };
1496
+ // fix(112as): a self-issued `exact_action_complete` claim is a BOUNDED
1497
+ // ATTEMPT, not a standing proof — the same principle as 112c.
1498
+ //
1499
+ // The claim is dropped at BOTH refusal points (prefilter and post_rebind),
1500
+ // and 112ar only clears it when a prepare/supply action LANDS on that
1501
+ // campaign. When every edge the planner exposed for the campaign is already
1502
+ // held by such a claim, nothing can land, so nothing ever clears it: a closed
1503
+ // loop that terminals `actionable_supply_not_queued` against visible supply
1504
+ // (Dotwork 2026-07-30/31: 100 enrichment candidates, approve_messages
1505
+ // refused at post_rebind on every run). 112ak made that terminal NAME the
1506
+ // refusal, which is honest but does not fill the day.
1507
+ //
1508
+ // So: if the ONLY reason we have no selection is our own completeness
1509
+ // claims, and the census still shows actionable supply, waive them and
1510
+ // re-select ONCE. Bounded by waiveCompleteClaims so it can never livelock,
1511
+ // and it reuses the SAME workspacePlan so no extra planner call is made and
1512
+ // the census stays consistent with the decision.
1513
+ const completeClaimReleaseRetry = async () => {
1514
+ if (waiveCompleteClaims)
1515
+ return null;
1516
+ if (coordinatorExcludedEdges.length === 0)
1517
+ return null;
1518
+ if (!coordinatorExcludedEdges.every((edge) => edge.blocker === "exact_action_complete")) {
1519
+ return null;
1520
+ }
1521
+ const census = workspaceGapBearingActionableSupply(workspacePlan);
1522
+ if (census.actionableRows <= 0 && !census.frontierHasMoreRows) {
1523
+ return null;
1524
+ }
1525
+ const released = await selectWorkspaceExactTarget(input, workspacePlan, {
1526
+ ...options,
1527
+ waiveCompleteClaims: true,
1528
+ seedExcludedEdges: [...coordinatorExcludedEdges],
1529
+ });
1530
+ // The release may only ADD a dispatch. If it fails to bind an edge, fall
1531
+ // back to this pass's terminal, which already NAMES the refused edges —
1532
+ // 112ak's honesty guarantee must not be traded away for an optimistic
1533
+ // retry that lands on a vaguer blocker.
1534
+ return released.status === "selected" ? released : null;
1535
+ };
1490
1536
  const targetCandidates = actionTargets.filter((candidate) => {
1491
1537
  const key = workspaceExactTargetKey(candidate);
1492
1538
  if (seenTargetKeys.has(key))
@@ -1583,6 +1629,9 @@ async function selectWorkspaceExactTarget(input, workspacePlanOverride, options
1583
1629
  const readyLoadedSelection = readySurplusLoadedSelection(workspacePlan);
1584
1630
  if (readyLoadedSelection)
1585
1631
  return readyLoadedSelection;
1632
+ const releasedEmptyCandidates = await completeClaimReleaseRetry();
1633
+ if (releasedEmptyCandidates)
1634
+ return releasedEmptyCandidates;
1586
1635
  return {
1587
1636
  status: "blocked",
1588
1637
  ...classifyWorkspaceUnavailableTerminal(workspacePlan, (excludedTargets.length > 0 ||
@@ -1761,6 +1810,13 @@ async function selectWorkspaceExactTarget(input, workspacePlanOverride, options
1761
1810
  blocked.blocker === "loaded_awaiting_scheduler") {
1762
1811
  return false;
1763
1812
  }
1813
+ // fix(112as): the release pass must waive here too. post_rebind
1814
+ // re-matches AFTER the exact preflight re-issues the semantic
1815
+ // actionKey, so waiving only at prefilter would leave the edge blocked.
1816
+ if (waiveCompleteClaims &&
1817
+ blocked.blocker === "exact_action_complete") {
1818
+ return false;
1819
+ }
1764
1820
  recordExcludedEdge(exactSelectedTarget, blocked, "post_rebind");
1765
1821
  return true;
1766
1822
  })) {
@@ -1880,6 +1936,9 @@ async function selectWorkspaceExactTarget(input, workspacePlanOverride, options
1880
1936
  const readyLoadedSelection = readySurplusLoadedSelection(workspacePlan);
1881
1937
  if (readyLoadedSelection)
1882
1938
  return readyLoadedSelection;
1939
+ const releasedFilteredCandidates = await completeClaimReleaseRetry();
1940
+ if (releasedFilteredCandidates)
1941
+ return releasedFilteredCandidates;
1883
1942
  return {
1884
1943
  status: "blocked",
1885
1944
  ...classifyWorkspaceUnavailableTerminal(workspacePlan, (workspaceCoverage.remainingReadyOrProjectedGap !== 0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sellable/mcp",
3
- "version": "0.1.748",
3
+ "version": "0.1.749",
4
4
  "type": "module",
5
5
  "description": "Sellable MCP server for Claude Code, Codex, and Hermes campaign workflows",
6
6
  "main": "dist/index.js",