@sellable/mcp 0.1.731 → 0.1.732

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.
@@ -1484,6 +1484,12 @@ async function selectWorkspaceExactTarget(input, workspacePlanOverride, options
1484
1484
  workspacePlan,
1485
1485
  };
1486
1486
  }
1487
+ // fix(112ak): empty-candidate terminal path — a ready-surplus gap resolves to
1488
+ // the truthful loaded terminal instead of the actionable_supply_not_queued
1489
+ // defect class (see readySurplusLoadedSelection).
1490
+ const readyLoadedSelection = readySurplusLoadedSelection(workspacePlan);
1491
+ if (readyLoadedSelection)
1492
+ return readyLoadedSelection;
1487
1493
  return {
1488
1494
  status: "blocked",
1489
1495
  ...classifyWorkspaceUnavailableTerminal(workspacePlan, (excludedTargets.length > 0 ||
@@ -1774,6 +1780,12 @@ async function selectWorkspaceExactTarget(input, workspacePlanOverride, options
1774
1780
  workspacePlan,
1775
1781
  };
1776
1782
  }
1783
+ // fix(112ak): filtered-candidate terminal path — a ready-surplus gap resolves to
1784
+ // the truthful loaded terminal instead of the actionable_supply_not_queued defect
1785
+ // class (see readySurplusLoadedSelection).
1786
+ const readyLoadedSelection = readySurplusLoadedSelection(workspacePlan);
1787
+ if (readyLoadedSelection)
1788
+ return readyLoadedSelection;
1777
1789
  return {
1778
1790
  status: "blocked",
1779
1791
  ...classifyWorkspaceUnavailableTerminal(workspacePlan, (workspaceCoverage.remainingReadyOrProjectedGap !== 0
@@ -1859,18 +1871,115 @@ function workspaceActionableSupplyCensus(plan) {
1859
1871
  senderPlanCount: senderPlans.length,
1860
1872
  };
1861
1873
  }
1874
+ // fix(112ak, OWNER closing invariant): the workspace terminal classifier must
1875
+ // only count actionable supply / frontier signal from senders that still OWE
1876
+ // coverage. A COVERED sender (remainingProjectedGap 0) can carry surplus
1877
+ // hasMoreFrontierRows or surplus stage rows; that surplus must never be read as
1878
+ // "unexposed actionable supply" for a DIFFERENT sender's gap (Amplify t5:
1879
+ // Ali/Victor gap 0 but hasMoreFrontierRows true masked Sadhna's gap-10 lane
1880
+ // whose own stage census was empty and whose 10 rows were paid-InMail
1881
+ // ready_to_schedule surplus — so the workspace false-terminaled
1882
+ // actionable_supply_not_queued while every gap-bearing lane was actually
1883
+ // scheduler/pacing-bound). This re-derives the census over ONLY gap-bearing
1884
+ // senders and buckets each gap lane: real actionable supply, ready-surplus
1885
+ // (prepared rows awaiting the scheduler), or opaque (no ready/actionable/frontier
1886
+ // residue). A missing gap is treated as gap-bearing (conservative) so plans
1887
+ // without gap accounting keep the loud actionable classification.
1888
+ function workspaceGapBearingActionableSupply(plan) {
1889
+ const target = yoloTarget(plan);
1890
+ const senderPlansRaw = target?.senderPlans;
1891
+ const senderPlans = Array.isArray(senderPlansRaw) ? senderPlansRaw : [];
1892
+ let approvalFrontier = 0;
1893
+ let generateFrontier = 0;
1894
+ let enrichFrontier = 0;
1895
+ let rubricFrontier = 0;
1896
+ let frontierHasMoreRows = false;
1897
+ let gapSenderCount = 0;
1898
+ let readySurplusGapSenders = 0;
1899
+ let opaqueGapSenders = 0;
1900
+ for (const senderPlan of senderPlans) {
1901
+ const rec = recordValue(senderPlan);
1902
+ const gap = numberValue(rec?.remainingProjectedGap);
1903
+ // Only lanes that still owe coverage classify the workspace gap. A gap-0
1904
+ // (covered) sender's surplus never contributes; a missing gap is included.
1905
+ if (typeof gap === "number" && gap <= 0)
1906
+ continue;
1907
+ gapSenderCount += 1;
1908
+ const receipt = recordValue(rec?.refillReceipt);
1909
+ const frontier = recordValue(receipt?.existingRowFrontier);
1910
+ const stageCounts = recordValue(frontier?.stageCounts);
1911
+ const approval = Math.max(numberValue(receipt?.approvalCandidates) ?? 0, numberValue(stageCounts?.needsApproval) ?? 0);
1912
+ const generate = numberValue(stageCounts?.needsGenerate) ?? 0;
1913
+ const enrich = numberValue(stageCounts?.needsEnrich) ?? 0;
1914
+ const rubric = numberValue(stageCounts?.needsRubric) ?? 0;
1915
+ const laneActionable = approval + generate + enrich + rubric;
1916
+ const laneFrontier = frontier?.hasMoreFrontierRows === true;
1917
+ const laneReady = numberValue(stageCounts?.ready) ?? 0;
1918
+ approvalFrontier += approval;
1919
+ generateFrontier += generate;
1920
+ enrichFrontier += enrich;
1921
+ rubricFrontier += rubric;
1922
+ if (laneFrontier)
1923
+ frontierHasMoreRows = true;
1924
+ if (laneActionable === 0 && !laneFrontier) {
1925
+ if (laneReady > 0)
1926
+ readySurplusGapSenders += 1;
1927
+ else
1928
+ opaqueGapSenders += 1;
1929
+ }
1930
+ }
1931
+ const actionableRows = approvalFrontier + generateFrontier + enrichFrontier + rubricFrontier;
1932
+ return {
1933
+ approvalFrontier,
1934
+ generateFrontier,
1935
+ enrichFrontier,
1936
+ rubricFrontier,
1937
+ frontierHasMoreRows,
1938
+ actionableRows,
1939
+ gapSenderCount,
1940
+ readySurplusGapSenders,
1941
+ opaqueGapSenders,
1942
+ };
1943
+ }
1944
+ // fix(112ak): when the workspace gap is owed entirely by gap-bearing lanes whose
1945
+ // residue is ready-surplus (prepared rows in stageCounts.ready, zero actionable
1946
+ // stage supply, no own frontier), the gap is scheduler/pacing-bound — the rows
1947
+ // are ready and the scheduler places them as capacity frees — NOT a
1948
+ // planner-exposure defect. Return the truthful loaded terminal instead of
1949
+ // actionable_supply_not_queued (Amplify t5: Sadhna's gap-10 lane was 10 paid-InMail
1950
+ // ready_to_schedule rows on a fully-scheduled day while COVERED senders carried
1951
+ // the only frontier signal). Gated on the gap-bearing census being empty-actionable
1952
+ // AND every gap lane ready-surplus (no opaque residue), so a genuinely-stuck or
1953
+ // genuinely-actionable gap still classifies honestly. Applied at BOTH selection
1954
+ // terminal sites (empty-candidate early path and the filtered-candidate path).
1955
+ function readySurplusLoadedSelection(workspacePlan) {
1956
+ const gapSupply = workspaceGapBearingActionableSupply(workspacePlan);
1957
+ if (gapSupply.gapSenderCount > 0 &&
1958
+ gapSupply.actionableRows === 0 &&
1959
+ !gapSupply.frontierHasMoreRows &&
1960
+ gapSupply.opaqueGapSenders === 0 &&
1961
+ gapSupply.readySurplusGapSenders > 0) {
1962
+ return {
1963
+ status: "loaded_awaiting_scheduler",
1964
+ note: "Every gap-bearing sender lane is already prepared and ready to schedule; the remaining workspace gap is scheduler/pacing-bound (ready rows awaiting placement), not unexposed actionable supply. No refill run or mutation was started." +
1965
+ workspaceTypedBlockersNote(workspacePlan),
1966
+ workspacePlan,
1967
+ };
1968
+ }
1969
+ return null;
1970
+ }
1862
1971
  function classifyWorkspaceUnavailableTerminal(plan, contextNote) {
1863
- const census = workspaceActionableSupplyCensus(plan);
1864
- const actionableRows = census.approvalFrontier +
1865
- census.generateFrontier +
1866
- census.enrichFrontier +
1867
- census.rubricFrontier;
1972
+ // fix(112ak): gap-gate the actionable_supply signal — only supply behind a
1973
+ // gap-bearing lane proves the planner failed to expose actionable work. A
1974
+ // covered sender's surplus frontier/stage rows do NOT justify the defect class.
1975
+ const census = workspaceGapBearingActionableSupply(plan);
1976
+ const actionableRows = census.actionableRows;
1868
1977
  if (actionableRows > 0 || census.frontierHasMoreRows) {
1869
1978
  return {
1870
1979
  code: "actionable_supply_not_queued",
1871
1980
  note: `Actionable existing-row supply remains (${census.approvalFrontier} approval, ` +
1872
1981
  `${census.generateFrontier} generate, ${census.enrichFrontier} enrich, ` +
1873
- `${census.rubricFrontier} rubric candidates across sender lanes` +
1982
+ `${census.rubricFrontier} rubric candidates across gap-bearing sender lanes` +
1874
1983
  `${census.frontierHasMoreRows ? ", plus unprocessed frontier rows" : ""}) ` +
1875
1984
  "but the fresh workspace queue exposed no executable exact edge for it. " +
1876
1985
  "This is retryable: re-run refill_sends with a fresh command; if it repeats, " +
@@ -1882,7 +1991,8 @@ function classifyWorkspaceUnavailableTerminal(plan, contextNote) {
1882
1991
  const gap = coverage.remainingReadyOrProjectedGap ?? coverage.remainingProjectedGap;
1883
1992
  // Claim receipt-proven exhaustion only when sender receipts were actually
1884
1993
  // present in the plan; a plan without senderPlans keeps the generic code.
1885
- if (census.senderPlanCount > 0 && typeof gap === "number" && gap > 0) {
1994
+ const senderPlanCount = workspaceActionableSupplyCensus(plan).senderPlanCount;
1995
+ if (senderPlanCount > 0 && typeof gap === "number" && gap > 0) {
1886
1996
  return {
1887
1997
  code: "new_campaign_required",
1888
1998
  note: `Receipts prove every existing planner-ranked campaign/lane/source family is ` +
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sellable/mcp",
3
- "version": "0.1.731",
3
+ "version": "0.1.732",
4
4
  "type": "module",
5
5
  "description": "Sellable MCP server for Claude Code, Codex, and Hermes campaign workflows",
6
6
  "main": "dist/index.js",