@sellable/mcp 0.1.731 → 0.1.733
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.
|
@@ -1712,8 +1712,8 @@ export declare function executeRefillSendsCommand(input?: RefillSendsCommandInpu
|
|
|
1712
1712
|
readOnly: boolean;
|
|
1713
1713
|
ok: boolean;
|
|
1714
1714
|
status: "blocked";
|
|
1715
|
-
code: "workspace_exact_target_unavailable" | "workspace_exact_target_contract_missing" | "exact_target_config_unavailable" | "exact_target_config_mismatch" | "exact_target_projection_mismatch" | "new_campaign_required" | "actionable_supply_not_queued";
|
|
1716
|
-
blocker: "workspace_exact_target_unavailable" | "workspace_exact_target_contract_missing" | "exact_target_config_unavailable" | "exact_target_config_mismatch" | "exact_target_projection_mismatch" | "new_campaign_required" | "actionable_supply_not_queued";
|
|
1715
|
+
code: "workspace_exact_target_unavailable" | "workspace_exact_target_contract_missing" | "exact_target_config_unavailable" | "exact_target_config_mismatch" | "exact_target_projection_mismatch" | "new_campaign_required" | "actionable_supply_not_queued" | "source_supply_exhausted_at_rubric";
|
|
1716
|
+
blocker: "workspace_exact_target_unavailable" | "workspace_exact_target_contract_missing" | "exact_target_config_unavailable" | "exact_target_config_mismatch" | "exact_target_projection_mismatch" | "new_campaign_required" | "actionable_supply_not_queued" | "source_supply_exhausted_at_rubric";
|
|
1717
1717
|
note: string;
|
|
1718
1718
|
targetPlan: unknown;
|
|
1719
1719
|
exactTargetPlan: {} | null;
|
|
@@ -2931,8 +2931,8 @@ export declare function executeRefillSendsCommand(input?: RefillSendsCommandInpu
|
|
|
2931
2931
|
} | {
|
|
2932
2932
|
ok: boolean;
|
|
2933
2933
|
status: "blocked";
|
|
2934
|
-
code: "workspace_exact_target_unavailable" | "workspace_exact_target_contract_missing" | "exact_target_config_unavailable" | "exact_target_config_mismatch" | "exact_target_projection_mismatch" | "new_campaign_required" | "actionable_supply_not_queued";
|
|
2935
|
-
blocker: "workspace_exact_target_unavailable" | "workspace_exact_target_contract_missing" | "exact_target_config_unavailable" | "exact_target_config_mismatch" | "exact_target_projection_mismatch" | "new_campaign_required" | "actionable_supply_not_queued";
|
|
2934
|
+
code: "workspace_exact_target_unavailable" | "workspace_exact_target_contract_missing" | "exact_target_config_unavailable" | "exact_target_config_mismatch" | "exact_target_projection_mismatch" | "new_campaign_required" | "actionable_supply_not_queued" | "source_supply_exhausted_at_rubric";
|
|
2935
|
+
blocker: "workspace_exact_target_unavailable" | "workspace_exact_target_contract_missing" | "exact_target_config_unavailable" | "exact_target_config_mismatch" | "exact_target_projection_mismatch" | "new_campaign_required" | "actionable_supply_not_queued" | "source_supply_exhausted_at_rubric";
|
|
2936
2936
|
note: string;
|
|
2937
2937
|
targetPlan: unknown;
|
|
2938
2938
|
exactTargetPlan: {} | null;
|
|
@@ -454,7 +454,7 @@ function buildRefillSendsCommand(input) {
|
|
|
454
454
|
workflowAsset: {
|
|
455
455
|
subskillName: "refill-sends-workflow",
|
|
456
456
|
assetPath: "core/flow.v1.json",
|
|
457
|
-
expectedVersion: "v1.
|
|
457
|
+
expectedVersion: "v1.9",
|
|
458
458
|
},
|
|
459
459
|
yolo,
|
|
460
460
|
executionMode,
|
|
@@ -502,7 +502,7 @@ function buildRefillSendsCommand(input) {
|
|
|
502
502
|
},
|
|
503
503
|
firstOperationalSteps: [
|
|
504
504
|
'Load get_subskill_prompt({ subskillName: "refill-sends-workflow" }) before any product operation.',
|
|
505
|
-
'Load get_subskill_asset({ subskillName: "refill-sends-workflow", assetPath: "core/flow.v1.json" }) before any product operation; continue chunks until hasMore is false, parse JSON, and require exactly workflow:"refill-sends-workflow" and version:"v1.
|
|
505
|
+
'Load get_subskill_asset({ subskillName: "refill-sends-workflow", assetPath: "core/flow.v1.json" }) before any product operation; continue chunks until hasMore is false, parse JSON, and require exactly workflow:"refill-sends-workflow" and version:"v1.9". Report the verified version before D1. Any other or unverified version is workflow_version_mismatch and must stop before get_refill_target_plan or mutation.',
|
|
506
506
|
"A skill cannot create or invoke /goal by itself. If this refill is already running inside an active Codex goal, keep that goal open until every selected sender lane is horizon-filled by projected coverage (sent + scheduled), Christian explicitly stops/statuses the run, or a concrete non-scheduler blocker appears.",
|
|
507
507
|
`Call get_refill_target_plan({ intent: "${intent}"${workspaceId ? `, workspaceId: "${workspaceId}"` : ""}${input.campaignId ? `, campaignId: "${input.campaignId}"` : ""}${input.tableId ? `, tableId: "${input.tableId}"` : ""}${senderIds.length > 0 ? `, senderIds: ${JSON.stringify(senderIds)}` : ""}${senderNames.length > 0
|
|
508
508
|
? `, senderNames: ${JSON.stringify(senderNames)}`
|
|
@@ -1395,6 +1395,20 @@ async function selectWorkspaceExactTarget(input, workspacePlanOverride, options
|
|
|
1395
1395
|
const prefilterBlockedTargets = blockedExactTargets.filter((blocked) => blocked.blocker !== "complete" &&
|
|
1396
1396
|
blocked.blocker !== "capped_by_scheduler");
|
|
1397
1397
|
const seenTargetKeys = new Set();
|
|
1398
|
+
// fix(112ak): every refusal is recorded so a non-empty-census terminal can
|
|
1399
|
+
// name the concrete per-lane blocker instead of reporting a bare dead end.
|
|
1400
|
+
const coordinatorExcludedEdges = [];
|
|
1401
|
+
const recordExcludedEdge = (candidate, blocked, stage) => {
|
|
1402
|
+
coordinatorExcludedEdges.push({
|
|
1403
|
+
campaignId: candidate.campaignId,
|
|
1404
|
+
tableId: candidate.tableId,
|
|
1405
|
+
senderIds: candidate.senderIds,
|
|
1406
|
+
actionTypes: candidate.actionTypes,
|
|
1407
|
+
actionKey: blocked?.actionKey ?? candidate.actionKey ?? null,
|
|
1408
|
+
blocker: blocked?.blocker ?? null,
|
|
1409
|
+
stage,
|
|
1410
|
+
});
|
|
1411
|
+
};
|
|
1398
1412
|
const targetCandidates = actionTargets.filter((candidate) => {
|
|
1399
1413
|
const key = workspaceExactTargetKey(candidate);
|
|
1400
1414
|
if (seenTargetKeys.has(key))
|
|
@@ -1424,6 +1438,7 @@ async function selectWorkspaceExactTarget(input, workspacePlanOverride, options
|
|
|
1424
1438
|
workspaceActionFamily(candidate.actionKey) === "run_scheduler_sweep") {
|
|
1425
1439
|
return false;
|
|
1426
1440
|
}
|
|
1441
|
+
recordExcludedEdge(candidate, blocked, "prefilter");
|
|
1427
1442
|
return true;
|
|
1428
1443
|
}));
|
|
1429
1444
|
});
|
|
@@ -1484,6 +1499,12 @@ async function selectWorkspaceExactTarget(input, workspacePlanOverride, options
|
|
|
1484
1499
|
workspacePlan,
|
|
1485
1500
|
};
|
|
1486
1501
|
}
|
|
1502
|
+
// fix(112ak): empty-candidate terminal path — a ready-surplus gap resolves to
|
|
1503
|
+
// the truthful loaded terminal instead of the actionable_supply_not_queued
|
|
1504
|
+
// defect class (see readySurplusLoadedSelection).
|
|
1505
|
+
const readyLoadedSelection = readySurplusLoadedSelection(workspacePlan);
|
|
1506
|
+
if (readyLoadedSelection)
|
|
1507
|
+
return readyLoadedSelection;
|
|
1487
1508
|
return {
|
|
1488
1509
|
status: "blocked",
|
|
1489
1510
|
...classifyWorkspaceUnavailableTerminal(workspacePlan, (excludedTargets.length > 0 ||
|
|
@@ -1491,7 +1512,7 @@ async function selectWorkspaceExactTarget(input, workspacePlanOverride, options
|
|
|
1491
1512
|
blockedExactTargets.length > 0
|
|
1492
1513
|
? "The fresh workspace queue did not expose another exact campaign/table edge after excluding the structurally exhausted target. No refill run or mutation was started."
|
|
1493
1514
|
: "The fresh workspace queue did not expose one exact campaign/table edge at globalActionQueue[0]. No refill run or mutation was started.") +
|
|
1494
|
-
workspaceTypedBlockersNote(workspacePlan)),
|
|
1515
|
+
workspaceTypedBlockersNote(workspacePlan), coordinatorExcludedEdges),
|
|
1495
1516
|
workspacePlan,
|
|
1496
1517
|
};
|
|
1497
1518
|
}
|
|
@@ -1662,6 +1683,7 @@ async function selectWorkspaceExactTarget(input, workspacePlanOverride, options
|
|
|
1662
1683
|
blocked.blocker === "loaded_awaiting_scheduler") {
|
|
1663
1684
|
return false;
|
|
1664
1685
|
}
|
|
1686
|
+
recordExcludedEdge(exactSelectedTarget, blocked, "post_rebind");
|
|
1665
1687
|
return true;
|
|
1666
1688
|
})) {
|
|
1667
1689
|
continue;
|
|
@@ -1774,12 +1796,18 @@ async function selectWorkspaceExactTarget(input, workspacePlanOverride, options
|
|
|
1774
1796
|
workspacePlan,
|
|
1775
1797
|
};
|
|
1776
1798
|
}
|
|
1799
|
+
// fix(112ak): filtered-candidate terminal path — a ready-surplus gap resolves to
|
|
1800
|
+
// the truthful loaded terminal instead of the actionable_supply_not_queued defect
|
|
1801
|
+
// class (see readySurplusLoadedSelection).
|
|
1802
|
+
const readyLoadedSelection = readySurplusLoadedSelection(workspacePlan);
|
|
1803
|
+
if (readyLoadedSelection)
|
|
1804
|
+
return readyLoadedSelection;
|
|
1777
1805
|
return {
|
|
1778
1806
|
status: "blocked",
|
|
1779
1807
|
...classifyWorkspaceUnavailableTerminal(workspacePlan, (workspaceCoverage.remainingReadyOrProjectedGap !== 0
|
|
1780
1808
|
? `The fresh workspace plan still has ${workspaceCoverage.remainingReadyOrProjectedGap ?? "an unknown number of"} rows to prepare, but its executable exact-target candidates were all complete or scheduler-loaded. Refusing a false loaded_awaiting_scheduler terminal; the planner must expose the remaining actionable lane or a concrete blocker.`
|
|
1781
1809
|
: `The fresh workspace queue remained incomplete, but all ${completedExactTargetCount} exposed exact targets were already complete and no active preparation or import work was pending. No identical refill fence was reopened.`) +
|
|
1782
|
-
workspaceTypedBlockersNote(workspacePlan)),
|
|
1810
|
+
workspaceTypedBlockersNote(workspacePlan), coordinatorExcludedEdges),
|
|
1783
1811
|
workspacePlan,
|
|
1784
1812
|
};
|
|
1785
1813
|
}
|
|
@@ -1820,17 +1848,126 @@ function yoloCoverage(plan) {
|
|
|
1820
1848
|
targetShapeRevision: stringValue(recordValue(plan)?.targetShapeRevision),
|
|
1821
1849
|
};
|
|
1822
1850
|
}
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1851
|
+
function laneFrontierIsRubricTerminal(frontier) {
|
|
1852
|
+
const diagnosis = recordValue(recordValue(frontier)?.failureDiagnosis);
|
|
1853
|
+
if (!diagnosis)
|
|
1854
|
+
return false;
|
|
1855
|
+
if (diagnosis.retryable !== false)
|
|
1856
|
+
return false;
|
|
1857
|
+
if (stringValue(diagnosis.stage) !== "icp_or_rubric_rejection")
|
|
1858
|
+
return false;
|
|
1859
|
+
const status = stringValue(diagnosis.status);
|
|
1860
|
+
return status === "zero_yield" || status === "low_yield";
|
|
1861
|
+
}
|
|
1862
|
+
// One frontier receipt describes one campaign table's lane, however many senders
|
|
1863
|
+
// share it. Fall back to sender identity only when the receipt did not name the
|
|
1864
|
+
// table it measured, so independent lanes still sum.
|
|
1865
|
+
function workspaceLaneFrontierKey(lane) {
|
|
1866
|
+
return lane.campaignId && lane.tableId
|
|
1867
|
+
? `table:${lane.campaignId}:${lane.tableId}:${lane.actionType ?? ""}`
|
|
1868
|
+
: `sender:${lane.senderId ?? ""}:${lane.actionType ?? ""}`;
|
|
1869
|
+
}
|
|
1870
|
+
function workspaceLaneFrontier(planRecord, receipt, frontier, stageCounts) {
|
|
1871
|
+
const senderId = stringValue(planRecord?.senderId) ?? stringValue(receipt?.senderId);
|
|
1872
|
+
const diagnosis = recordValue(frontier?.failureDiagnosis);
|
|
1873
|
+
const evidence = recordValue(diagnosis?.evidence);
|
|
1874
|
+
const rejectionCounts = recordValue(evidence?.counts);
|
|
1875
|
+
return {
|
|
1876
|
+
senderId,
|
|
1877
|
+
senderIds: senderId ? [senderId] : [],
|
|
1878
|
+
actionType: stringValue(planRecord?.actionType) ?? stringValue(receipt?.actionType),
|
|
1879
|
+
campaignId: stringValue(receipt?.selectedCampaignId),
|
|
1880
|
+
tableId: stringValue(receipt?.selectedTableId),
|
|
1881
|
+
approval: Math.max(numberValue(receipt?.approvalCandidates) ?? 0, numberValue(stageCounts?.needsApproval) ?? 0),
|
|
1882
|
+
generate: numberValue(stageCounts?.needsGenerate) ?? 0,
|
|
1883
|
+
enrich: numberValue(stageCounts?.needsEnrich) ?? 0,
|
|
1884
|
+
rubric: numberValue(stageCounts?.needsRubric) ?? 0,
|
|
1885
|
+
hasMoreFrontierRows: frontier?.hasMoreFrontierRows === true,
|
|
1886
|
+
checkedRows: numberValue(evidence?.checkedRows) ?? null,
|
|
1887
|
+
rubricRejections: numberValue(rejectionCounts?.rubric_rejection) ?? null,
|
|
1888
|
+
};
|
|
1889
|
+
}
|
|
1890
|
+
// Keep the deepest observation of a shared table lane so a terminal quotes the
|
|
1891
|
+
// scan that actually proved exhaustion, not the shallowest — but carry EVERY
|
|
1892
|
+
// sender the lane covers so no enrolled sender is left unexplained.
|
|
1893
|
+
function mergeNonConvertibleLane(lanes, laneKey, lane) {
|
|
1894
|
+
const seen = lanes.get(laneKey);
|
|
1895
|
+
if (!seen) {
|
|
1896
|
+
lanes.set(laneKey, lane);
|
|
1897
|
+
return;
|
|
1898
|
+
}
|
|
1899
|
+
const senderIds = [...new Set([...seen.senderIds, ...lane.senderIds])];
|
|
1900
|
+
if ((lane.checkedRows ?? 0) > (seen.checkedRows ?? 0)) {
|
|
1901
|
+
lanes.set(laneKey, { ...lane, senderIds });
|
|
1902
|
+
}
|
|
1903
|
+
else {
|
|
1904
|
+
seen.senderIds = senderIds;
|
|
1905
|
+
}
|
|
1906
|
+
}
|
|
1907
|
+
// Same table lane observed twice: keep the richest observation rather than
|
|
1908
|
+
// summing duplicates of one frontier.
|
|
1909
|
+
function mergeConvertibleLane(lanes, laneKey, lane) {
|
|
1910
|
+
const existing = lanes.get(laneKey);
|
|
1911
|
+
if (!existing) {
|
|
1912
|
+
lanes.set(laneKey, lane);
|
|
1913
|
+
return;
|
|
1914
|
+
}
|
|
1915
|
+
existing.approval = Math.max(existing.approval, lane.approval);
|
|
1916
|
+
existing.generate = Math.max(existing.generate, lane.generate);
|
|
1917
|
+
existing.enrich = Math.max(existing.enrich, lane.enrich);
|
|
1918
|
+
existing.rubric = Math.max(existing.rubric, lane.rubric);
|
|
1919
|
+
existing.hasMoreFrontierRows =
|
|
1920
|
+
existing.hasMoreFrontierRows || lane.hasMoreFrontierRows;
|
|
1921
|
+
existing.senderIds = [...new Set([...existing.senderIds, ...lane.senderIds])];
|
|
1922
|
+
}
|
|
1833
1923
|
function workspaceActionableSupplyCensus(plan) {
|
|
1924
|
+
const target = yoloTarget(plan);
|
|
1925
|
+
const senderPlansRaw = target?.senderPlans;
|
|
1926
|
+
const senderPlans = Array.isArray(senderPlansRaw) ? senderPlansRaw : [];
|
|
1927
|
+
const convertibleByLane = new Map();
|
|
1928
|
+
const nonConvertibleByLane = new Map();
|
|
1929
|
+
for (const senderPlan of senderPlans) {
|
|
1930
|
+
const planRecord = recordValue(senderPlan);
|
|
1931
|
+
const receipt = recordValue(planRecord?.refillReceipt);
|
|
1932
|
+
const frontier = recordValue(receipt?.existingRowFrontier);
|
|
1933
|
+
const stageCounts = recordValue(frontier?.stageCounts);
|
|
1934
|
+
const lane = workspaceLaneFrontier(planRecord, receipt, frontier, stageCounts);
|
|
1935
|
+
const laneKey = workspaceLaneFrontierKey(lane);
|
|
1936
|
+
if (laneFrontierIsRubricTerminal(frontier)) {
|
|
1937
|
+
mergeNonConvertibleLane(nonConvertibleByLane, laneKey, lane);
|
|
1938
|
+
continue;
|
|
1939
|
+
}
|
|
1940
|
+
mergeConvertibleLane(convertibleByLane, laneKey, lane);
|
|
1941
|
+
}
|
|
1942
|
+
const convertibleLanes = [...convertibleByLane.values()];
|
|
1943
|
+
const nonConvertibleLanes = [...nonConvertibleByLane.values()];
|
|
1944
|
+
const sum = (pick) => convertibleLanes.reduce((total, lane) => total + pick(lane), 0);
|
|
1945
|
+
return {
|
|
1946
|
+
approvalFrontier: sum((lane) => lane.approval),
|
|
1947
|
+
generateFrontier: sum((lane) => lane.generate),
|
|
1948
|
+
enrichFrontier: sum((lane) => lane.enrich),
|
|
1949
|
+
rubricFrontier: sum((lane) => lane.rubric),
|
|
1950
|
+
frontierHasMoreRows: convertibleLanes.some((lane) => lane.hasMoreFrontierRows),
|
|
1951
|
+
senderPlanCount: senderPlans.length,
|
|
1952
|
+
convertibleLanes,
|
|
1953
|
+
nonConvertibleLanes,
|
|
1954
|
+
};
|
|
1955
|
+
}
|
|
1956
|
+
// fix(112ak, OWNER closing invariant): the workspace terminal classifier must
|
|
1957
|
+
// only count actionable supply / frontier signal from senders that still OWE
|
|
1958
|
+
// coverage. A COVERED sender (remainingProjectedGap 0) can carry surplus
|
|
1959
|
+
// hasMoreFrontierRows or surplus stage rows; that surplus must never be read as
|
|
1960
|
+
// "unexposed actionable supply" for a DIFFERENT sender's gap (Amplify t5:
|
|
1961
|
+
// Ali/Victor gap 0 but hasMoreFrontierRows true masked Sadhna's gap-10 lane
|
|
1962
|
+
// whose own stage census was empty and whose 10 rows were paid-InMail
|
|
1963
|
+
// ready_to_schedule surplus — so the workspace false-terminaled
|
|
1964
|
+
// actionable_supply_not_queued while every gap-bearing lane was actually
|
|
1965
|
+
// scheduler/pacing-bound). This re-derives the census over ONLY gap-bearing
|
|
1966
|
+
// senders and buckets each gap lane: real actionable supply, ready-surplus
|
|
1967
|
+
// (prepared rows awaiting the scheduler), or opaque (no ready/actionable/frontier
|
|
1968
|
+
// residue). A missing gap is treated as gap-bearing (conservative) so plans
|
|
1969
|
+
// without gap accounting keep the loud actionable classification.
|
|
1970
|
+
function workspaceGapBearingActionableSupply(plan) {
|
|
1834
1971
|
const target = yoloTarget(plan);
|
|
1835
1972
|
const senderPlansRaw = target?.senderPlans;
|
|
1836
1973
|
const senderPlans = Array.isArray(senderPlansRaw) ? senderPlansRaw : [];
|
|
@@ -1839,50 +1976,229 @@ function workspaceActionableSupplyCensus(plan) {
|
|
|
1839
1976
|
let enrichFrontier = 0;
|
|
1840
1977
|
let rubricFrontier = 0;
|
|
1841
1978
|
let frontierHasMoreRows = false;
|
|
1979
|
+
let gapSenderCount = 0;
|
|
1980
|
+
let readySurplusGapSenders = 0;
|
|
1981
|
+
let opaqueGapSenders = 0;
|
|
1982
|
+
// fix(112ak, supply validation + de-duplication): the gap-bearing census is the
|
|
1983
|
+
// authority behind the OWNER closing invariant, so it must count CONVERTIBLE
|
|
1984
|
+
// rows ONCE. Two live inflations broke that on the 2026-07-29 sweep.
|
|
1985
|
+
//
|
|
1986
|
+
// 1. Double counting. Every sender enrolled on a shared campaign reports the
|
|
1987
|
+
// SAME table frontier: sellable's "681 enrich" was one 68-row frontier on
|
|
1988
|
+
// campaign cmrcd4x4a counted 10x (nine send_invite lanes plus one
|
|
1989
|
+
// send_inmail_closed lane on the same table). Sums are now keyed by the
|
|
1990
|
+
// campaign/table/lane the receipt was actually measured on; plans without
|
|
1991
|
+
// table identity keep per-sender identity so independent lanes still sum.
|
|
1992
|
+
//
|
|
1993
|
+
// 2. Non-convertible rows counted as supply. Clover's lanes reported
|
|
1994
|
+
// stageCounts of ~0 with hasMoreFrontierRows true while their own receipts
|
|
1995
|
+
// carried failureDiagnosis {stage:"icp_or_rubric_rejection",
|
|
1996
|
+
// retryable:false} over the 4,993 rubric-rejected rows behind the scan
|
|
1997
|
+
// window. hasMoreFrontierRows is a SCAN-WINDOW flag, not evidence that
|
|
1998
|
+
// anything behind it can convert. Such a lane no longer inflates the
|
|
1999
|
+
// census; it is carried as non-convertible (with every sender it covers)
|
|
2000
|
+
// so the terminal can name a truthful exhaustion instead of the defect
|
|
2001
|
+
// class. Its bucket still counts honestly below, so a rubric-terminal lane
|
|
2002
|
+
// with no ready rows reads opaque and never fakes a loaded terminal.
|
|
2003
|
+
//
|
|
2004
|
+
// Only the receipt's own non-retryable rubric verdict disqualifies a lane.
|
|
2005
|
+
// Retryable diagnoses (sellable's dependency_blocked/retryable:true) and other
|
|
2006
|
+
// stages (Dotwork's approval_blocked over a real needsApproval row) stay
|
|
2007
|
+
// convertible, so the 112af-c/112ai masked-supply admission is untouched.
|
|
2008
|
+
const convertibleByLane = new Map();
|
|
2009
|
+
const nonConvertibleByLane = new Map();
|
|
1842
2010
|
for (const senderPlan of senderPlans) {
|
|
1843
|
-
const
|
|
2011
|
+
const rec = recordValue(senderPlan);
|
|
2012
|
+
const gap = numberValue(rec?.remainingProjectedGap);
|
|
2013
|
+
// Only lanes that still owe coverage classify the workspace gap. A gap-0
|
|
2014
|
+
// (covered) sender's surplus never contributes; a missing gap is included.
|
|
2015
|
+
if (typeof gap === "number" && gap <= 0)
|
|
2016
|
+
continue;
|
|
2017
|
+
gapSenderCount += 1;
|
|
2018
|
+
const receipt = recordValue(rec?.refillReceipt);
|
|
1844
2019
|
const frontier = recordValue(receipt?.existingRowFrontier);
|
|
1845
2020
|
const stageCounts = recordValue(frontier?.stageCounts);
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
2021
|
+
const lane = workspaceLaneFrontier(rec, receipt, frontier, stageCounts);
|
|
2022
|
+
const rubricTerminal = laneFrontierIsRubricTerminal(frontier);
|
|
2023
|
+
const laneActionable = rubricTerminal
|
|
2024
|
+
? 0
|
|
2025
|
+
: lane.approval + lane.generate + lane.enrich + lane.rubric;
|
|
2026
|
+
const laneFrontierFlag = rubricTerminal ? false : lane.hasMoreFrontierRows;
|
|
2027
|
+
const laneReady = numberValue(stageCounts?.ready) ?? 0;
|
|
2028
|
+
const laneKey = workspaceLaneFrontierKey(lane);
|
|
2029
|
+
if (rubricTerminal) {
|
|
2030
|
+
mergeNonConvertibleLane(nonConvertibleByLane, laneKey, lane);
|
|
2031
|
+
}
|
|
2032
|
+
else {
|
|
2033
|
+
mergeConvertibleLane(convertibleByLane, laneKey, lane);
|
|
2034
|
+
}
|
|
2035
|
+
if (laneFrontierFlag)
|
|
1851
2036
|
frontierHasMoreRows = true;
|
|
2037
|
+
if (laneActionable === 0 && !laneFrontierFlag) {
|
|
2038
|
+
if (laneReady > 0)
|
|
2039
|
+
readySurplusGapSenders += 1;
|
|
2040
|
+
else
|
|
2041
|
+
opaqueGapSenders += 1;
|
|
2042
|
+
}
|
|
2043
|
+
}
|
|
2044
|
+
const convertibleLanes = [...convertibleByLane.values()];
|
|
2045
|
+
const nonConvertibleLanes = [...nonConvertibleByLane.values()];
|
|
2046
|
+
for (const lane of convertibleLanes) {
|
|
2047
|
+
approvalFrontier += lane.approval;
|
|
2048
|
+
generateFrontier += lane.generate;
|
|
2049
|
+
enrichFrontier += lane.enrich;
|
|
2050
|
+
rubricFrontier += lane.rubric;
|
|
1852
2051
|
}
|
|
2052
|
+
const actionableRows = approvalFrontier + generateFrontier + enrichFrontier + rubricFrontier;
|
|
1853
2053
|
return {
|
|
1854
2054
|
approvalFrontier,
|
|
1855
2055
|
generateFrontier,
|
|
1856
2056
|
enrichFrontier,
|
|
1857
2057
|
rubricFrontier,
|
|
1858
2058
|
frontierHasMoreRows,
|
|
1859
|
-
|
|
2059
|
+
actionableRows,
|
|
2060
|
+
gapSenderCount,
|
|
2061
|
+
readySurplusGapSenders,
|
|
2062
|
+
opaqueGapSenders,
|
|
2063
|
+
convertibleLanes,
|
|
2064
|
+
nonConvertibleLanes,
|
|
1860
2065
|
};
|
|
1861
2066
|
}
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
2067
|
+
// fix(112ak): when the workspace gap is owed entirely by gap-bearing lanes whose
|
|
2068
|
+
// residue is ready-surplus (prepared rows in stageCounts.ready, zero actionable
|
|
2069
|
+
// stage supply, no own frontier), the gap is scheduler/pacing-bound — the rows
|
|
2070
|
+
// are ready and the scheduler places them as capacity frees — NOT a
|
|
2071
|
+
// planner-exposure defect. Return the truthful loaded terminal instead of
|
|
2072
|
+
// actionable_supply_not_queued (Amplify t5: Sadhna's gap-10 lane was 10 paid-InMail
|
|
2073
|
+
// ready_to_schedule rows on a fully-scheduled day while COVERED senders carried
|
|
2074
|
+
// the only frontier signal). Gated on the gap-bearing census being empty-actionable
|
|
2075
|
+
// AND every gap lane ready-surplus (no opaque residue), so a genuinely-stuck or
|
|
2076
|
+
// genuinely-actionable gap still classifies honestly. Applied at BOTH selection
|
|
2077
|
+
// terminal sites (empty-candidate early path and the filtered-candidate path).
|
|
2078
|
+
function readySurplusLoadedSelection(workspacePlan) {
|
|
2079
|
+
const gapSupply = workspaceGapBearingActionableSupply(workspacePlan);
|
|
2080
|
+
if (gapSupply.gapSenderCount > 0 &&
|
|
2081
|
+
gapSupply.actionableRows === 0 &&
|
|
2082
|
+
!gapSupply.frontierHasMoreRows &&
|
|
2083
|
+
gapSupply.opaqueGapSenders === 0 &&
|
|
2084
|
+
gapSupply.readySurplusGapSenders > 0) {
|
|
2085
|
+
return {
|
|
2086
|
+
status: "loaded_awaiting_scheduler",
|
|
2087
|
+
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." +
|
|
2088
|
+
workspaceTypedBlockersNote(workspacePlan),
|
|
2089
|
+
workspacePlan,
|
|
2090
|
+
};
|
|
2091
|
+
}
|
|
2092
|
+
return null;
|
|
2093
|
+
}
|
|
2094
|
+
// fix(112ak): a receipt-proven rubric-terminal frontier is a truthful source
|
|
2095
|
+
// exhaustion, so name it per lane instead of leaving the terminal generic.
|
|
2096
|
+
function workspaceRubricExhaustionNote(lanes) {
|
|
2097
|
+
const parts = lanes.slice(0, 12).map((lane) => {
|
|
2098
|
+
const scope = [
|
|
2099
|
+
lane.campaignId ?? lane.senderId,
|
|
2100
|
+
lane.actionType,
|
|
2101
|
+
lane.senderIds.length > 0 ? `${lane.senderIds.length} sender(s)` : null,
|
|
2102
|
+
]
|
|
2103
|
+
.filter((part) => Boolean(part))
|
|
2104
|
+
.join(":");
|
|
2105
|
+
const detail = [
|
|
2106
|
+
typeof lane.checkedRows === "number"
|
|
2107
|
+
? `${lane.checkedRows} checked`
|
|
2108
|
+
: null,
|
|
2109
|
+
typeof lane.rubricRejections === "number"
|
|
2110
|
+
? `${lane.rubricRejections} rubric-rejected`
|
|
2111
|
+
: null,
|
|
2112
|
+
]
|
|
2113
|
+
.filter((part) => Boolean(part))
|
|
2114
|
+
.join(", ");
|
|
2115
|
+
return detail
|
|
2116
|
+
? `${scope || "lane"} (${detail})`
|
|
2117
|
+
: scope || "unnamed sender lane";
|
|
2118
|
+
});
|
|
2119
|
+
if (parts.length === 0)
|
|
2120
|
+
return "";
|
|
2121
|
+
const overflow = lanes.length - parts.length;
|
|
2122
|
+
return ` Per-lane exhaustion evidence: ${parts.join("; ")}${overflow > 0 ? `; +${overflow} more lanes` : ""}.`;
|
|
2123
|
+
}
|
|
2124
|
+
function coordinatorExcludedEdgesNote(edges) {
|
|
2125
|
+
if (edges.length === 0)
|
|
2126
|
+
return "";
|
|
2127
|
+
const seen = new Set();
|
|
2128
|
+
const parts = [];
|
|
2129
|
+
for (const edge of edges) {
|
|
2130
|
+
const key = `${edge.campaignId}:${edge.tableId}:${edge.actionTypes.join(",")}:${edge.actionKey ?? ""}:${edge.blocker ?? ""}`;
|
|
2131
|
+
if (seen.has(key))
|
|
2132
|
+
continue;
|
|
2133
|
+
seen.add(key);
|
|
2134
|
+
const lane = edge.actionTypes.join("/") || "lane";
|
|
2135
|
+
const senders = edge.senderIds.length > 0
|
|
2136
|
+
? `${edge.senderIds.length} sender(s)`
|
|
2137
|
+
: "no named sender";
|
|
2138
|
+
parts.push(`${edge.campaignId}:${lane} (${senders}) refused as ${edge.blocker ?? "already-decided"}${edge.actionKey ? ` on ${edge.actionKey}` : ""} at ${edge.stage}`);
|
|
2139
|
+
if (parts.length >= 8)
|
|
2140
|
+
break;
|
|
2141
|
+
}
|
|
2142
|
+
const overflow = edges.length - parts.length;
|
|
2143
|
+
return ` Exact edges this run refused: ${parts.join("; ")}${overflow > 0 ? `; +${overflow} more` : ""}.`;
|
|
2144
|
+
}
|
|
2145
|
+
function classifyWorkspaceUnavailableTerminal(plan, contextNote, excludedEdges = []) {
|
|
2146
|
+
// fix(112ak): gap-gate the actionable_supply signal — only supply behind a
|
|
2147
|
+
// gap-bearing lane proves the planner failed to expose actionable work. A
|
|
2148
|
+
// covered sender's surplus frontier/stage rows do NOT justify the defect class.
|
|
2149
|
+
const census = workspaceGapBearingActionableSupply(plan);
|
|
2150
|
+
const actionableRows = census.actionableRows;
|
|
1868
2151
|
if (actionableRows > 0 || census.frontierHasMoreRows) {
|
|
2152
|
+
// fix(112ak, OWNER closing invariant): a non-empty census may never end as
|
|
2153
|
+
// a bare defect terminal — the coordinator must dispatch an edge or NAME
|
|
2154
|
+
// the concrete per-lane blocker. Every edge this run refused is already
|
|
2155
|
+
// recorded with its sender/lane/actionKey and the receipt that refused it,
|
|
2156
|
+
// so surface that instead of claiming no edge was exposed. On the live
|
|
2157
|
+
// 2026-07-29 sellable run the planner DID expose a prepare edge; the
|
|
2158
|
+
// coordinator dropped it because the exact preflight re-issued an
|
|
2159
|
+
// actionKey already proven exact_action_complete, then reported "no
|
|
2160
|
+
// executable exact edge" — true only of the coordinator, not the planner.
|
|
2161
|
+
const exclusionNote = coordinatorExcludedEdgesNote(excludedEdges);
|
|
1869
2162
|
return {
|
|
1870
2163
|
code: "actionable_supply_not_queued",
|
|
1871
2164
|
note: `Actionable existing-row supply remains (${census.approvalFrontier} approval, ` +
|
|
1872
2165
|
`${census.generateFrontier} generate, ${census.enrichFrontier} enrich, ` +
|
|
1873
|
-
`${census.rubricFrontier} rubric candidates across sender lanes` +
|
|
2166
|
+
`${census.rubricFrontier} rubric candidates across gap-bearing sender lanes` +
|
|
1874
2167
|
`${census.frontierHasMoreRows ? ", plus unprocessed frontier rows" : ""}) ` +
|
|
1875
|
-
|
|
2168
|
+
`${exclusionNote
|
|
2169
|
+
? "and every exact edge the planner exposed for it was refused by this run."
|
|
2170
|
+
: "but the fresh workspace queue exposed no executable exact edge for it."} ` +
|
|
1876
2171
|
"This is retryable: re-run refill_sends with a fresh command; if it repeats, " +
|
|
1877
2172
|
"report it as a planner-exposure defect. " +
|
|
1878
|
-
contextNote
|
|
2173
|
+
contextNote +
|
|
2174
|
+
exclusionNote,
|
|
2175
|
+
};
|
|
2176
|
+
}
|
|
2177
|
+
// fix(112ak): the convertible census is empty and the only frontier left is
|
|
2178
|
+
// receipt-proven rubric-terminal (Clover 2026-07-29: nine lanes, all
|
|
2179
|
+
// non-retryable icp_or_rubric_rejection, 4,993 rubric-rejected rows behind
|
|
2180
|
+
// the scan window). That is a truthful source exhaustion, not the
|
|
2181
|
+
// planner-exposure defect class and not a generic dead end.
|
|
2182
|
+
if (census.nonConvertibleLanes.length > 0) {
|
|
2183
|
+
return {
|
|
2184
|
+
code: "source_supply_exhausted_at_rubric",
|
|
2185
|
+
note: `Every remaining existing-row frontier is receipt-proven exhausted at the ICP/rubric ` +
|
|
2186
|
+
`stage across ${census.nonConvertibleLanes.length} campaign/table lane(s): the preparation ` +
|
|
2187
|
+
"receipts report a non-retryable zero/low-yield rubric rejection, so the rows behind " +
|
|
2188
|
+
"the scan window cannot convert into sends. No campaign was created and no source was " +
|
|
2189
|
+
"expanded — widening the rubric or adding fresh source rows is the only remaining fill " +
|
|
2190
|
+
"path; surface this to the user. " +
|
|
2191
|
+
contextNote +
|
|
2192
|
+
workspaceRubricExhaustionNote(census.nonConvertibleLanes) +
|
|
2193
|
+
coordinatorExcludedEdgesNote(excludedEdges),
|
|
1879
2194
|
};
|
|
1880
2195
|
}
|
|
1881
2196
|
const coverage = yoloCoverage(plan);
|
|
1882
2197
|
const gap = coverage.remainingReadyOrProjectedGap ?? coverage.remainingProjectedGap;
|
|
1883
2198
|
// Claim receipt-proven exhaustion only when sender receipts were actually
|
|
1884
2199
|
// present in the plan; a plan without senderPlans keeps the generic code.
|
|
1885
|
-
|
|
2200
|
+
const senderPlanCount = workspaceActionableSupplyCensus(plan).senderPlanCount;
|
|
2201
|
+
if (senderPlanCount > 0 && typeof gap === "number" && gap > 0) {
|
|
1886
2202
|
return {
|
|
1887
2203
|
code: "new_campaign_required",
|
|
1888
2204
|
note: `Receipts prove every existing planner-ranked campaign/lane/source family is ` +
|
package/package.json
CHANGED
|
@@ -75,7 +75,7 @@ get_subskill_asset({ subskillName: "refill-sends-workflow", assetPath: "core/flo
|
|
|
75
75
|
```
|
|
76
76
|
|
|
77
77
|
Continue chunks until `hasMore:false`; require
|
|
78
|
-
exactly `workflow:"refill-sends-workflow"` and `version:"v1.
|
|
78
|
+
exactly `workflow:"refill-sends-workflow"` and `version:"v1.9"`, then report
|
|
79
79
|
that verified version before D1. Any other or unverified version is
|
|
80
80
|
`workflow_version_mismatch`: stop before `get_refill_target_plan` or mutation.
|
|
81
81
|
Route intent is immutable:
|
|
@@ -484,9 +484,13 @@ flowchart TD
|
|
|
484
484
|
|
|
485
485
|
One scheduler-loaded exact lane is not a workspace terminal: skip it and
|
|
486
486
|
continue other planner-ranked lanes. Do not keep polling after the coordinator
|
|
487
|
-
returns workspace `loaded_awaiting_scheduler
|
|
488
|
-
remaining exact target is complete or scheduler-owned and
|
|
489
|
-
`remainingReadyOrProjectedGap` is zero
|
|
487
|
+
returns workspace `loaded_awaiting_scheduler`. That terminal is admissible by
|
|
488
|
+
either path: every remaining exact target is complete or scheduler-owned and
|
|
489
|
+
workspace `remainingReadyOrProjectedGap` is zero, OR every gap-bearing sender
|
|
490
|
+
lane is already prepared and its residue is `ready_to_schedule` surplus, so the
|
|
491
|
+
remaining gap is scheduler/pacing-bound rather than unexposed supply — on that
|
|
492
|
+
second path `remainingReadyOrProjectedGap` is not necessarily zero. Neither path
|
|
493
|
+
means unexposed actionable supply, and neither is permission for a host-owned
|
|
490
494
|
loop. The sole refill-ladder expansion
|
|
491
495
|
that asks the user in `--yolo` is `new_campaign_required`; refill itself never
|
|
492
496
|
creates that campaign. `new_campaign_required` is also the truthful terminal
|
|
@@ -497,7 +501,17 @@ remaining gap and ask the user; a future handler may automate the creation. A
|
|
|
497
501
|
(approval/generate/enrich/rubric or unprocessed frontier rows) still exist but
|
|
498
502
|
no executable exact edge surfaced: retry once with a fresh command, and if it
|
|
499
503
|
repeats report it as a planner-exposure defect instead of treating the
|
|
500
|
-
workspace as exhausted.
|
|
504
|
+
workspace as exhausted. That census counts CONVERTIBLE rows only, de-duplicated
|
|
505
|
+
per campaign/table lane — a frontier whose own preparation receipt carries a
|
|
506
|
+
non-retryable `icp_or_rubric_rejection` diagnosis is proven unable to convert,
|
|
507
|
+
so it never inflates the census. When such receipts are all that remain, the
|
|
508
|
+
terminal is `source_supply_exhausted_at_rubric`: a truthful source exhaustion,
|
|
509
|
+
not a defect and not retryable. Report the per-lane evidence from the note and
|
|
510
|
+
tell the user that widening the rubric or adding fresh source rows is the only
|
|
511
|
+
remaining fill path. Whenever a terminal reports a non-empty census, it also
|
|
512
|
+
names every exact edge the run refused (`Exact edges this run refused: ...`) —
|
|
513
|
+
surface those named blockers rather than describing the workspace as having no
|
|
514
|
+
available work.
|
|
501
515
|
|
|
502
516
|
## Non-negotiable authority and proof
|
|
503
517
|
|
|
@@ -24,7 +24,7 @@ In `--yolo`, the coordinator may
|
|
|
24
24
|
perform every fresh planner-ranked safe transition among existing campaigns,
|
|
25
25
|
lanes, and source families. It may not create a new campaign.
|
|
26
26
|
|
|
27
|
-
The host must load the entire asset and verify exactly `version:"v1.
|
|
27
|
+
The host must load the entire asset and verify exactly `version:"v1.9"` before
|
|
28
28
|
D1. It reports that verified version explicitly. A different or unverified
|
|
29
29
|
version is `workflow_version_mismatch` and stops before planner or mutation.
|
|
30
30
|
|
|
@@ -32,7 +32,7 @@ version is `workflow_version_mismatch` and stops before planner or mutation.
|
|
|
32
32
|
|
|
33
33
|
```mermaid
|
|
34
34
|
flowchart TD
|
|
35
|
-
A["Receive exact workspace, sender/action selectors, and date envelope"] --> ASSET["Verify complete flow.v1.json, workflow refill-sends-workflow, exact version v1.
|
|
35
|
+
A["Receive exact workspace, sender/action selectors, and date envelope"] --> ASSET["Verify complete flow.v1.json, workflow refill-sends-workflow, exact version v1.9"]
|
|
36
36
|
ASSET --> D1["Call get_refill_target_plan read-only with immutable route intent"]
|
|
37
37
|
D1 --> RENDER["Render sender/campaign waterfall, coverage ledger, bounded side effects, forbidden actions, and stop condition"]
|
|
38
38
|
RENDER --> D2["Call refill_sends with workspaceCoordinator true and displayed revision/action pins"]
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "v1.
|
|
2
|
+
"version": "v1.9",
|
|
3
3
|
"workflow": "refill-sends-workflow",
|
|
4
4
|
"principle": "The planner chooses one exact next action and refill_sends alone executes it. Every bounded action is receipted and followed by a canonical replan. Yolo may traverse every planner-ranked existing campaign, lane, and source family; only new campaign creation requires the user.",
|
|
5
5
|
"requiredBootstrap": [
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"mustReadUntil": { "hasMore": false },
|
|
18
18
|
"validation": {
|
|
19
19
|
"workflow": "refill-sends-workflow",
|
|
20
|
-
"exactVersion": "v1.
|
|
20
|
+
"exactVersion": "v1.9",
|
|
21
21
|
"requiredTopLevelKeys": [
|
|
22
22
|
"states",
|
|
23
23
|
"gateOrder",
|
|
@@ -430,11 +430,12 @@
|
|
|
430
430
|
],
|
|
431
431
|
"terminalContract": {
|
|
432
432
|
"complete": "Projected sent plus scheduler-owned scheduled coverage meets the canonical target.",
|
|
433
|
-
"loaded_awaiting_scheduler": "
|
|
434
|
-
"blocked_retryable": "Return the exact blocker, receipt identity, and safe retry condition.",
|
|
433
|
+
"loaded_awaiting_scheduler": "Terminal, no continuation. Admissible by EITHER path: (a) every remaining exact target is complete or scheduler-owned and workspace remainingReadyOrProjectedGap is zero after the bounded scheduler wait; or (b) every gap-bearing sender lane is already prepared and its residue is ready_to_schedule surplus, so the remaining gap is scheduler/pacing-bound (ready rows awaiting placement) rather than unexposed supply — remainingReadyOrProjectedGap is NOT necessarily zero on this path. Neither path means unexposed actionable supply. Report the ready inventory and stop without a host poll loop.",
|
|
434
|
+
"blocked_retryable": "Retryable. Return the exact blocker, receipt identity, and safe retry condition. Specific blockers (for example no_forward_progress, provider outages, lease_lost) are reported UNDER this class, not as their own terminal classes.",
|
|
435
435
|
"lanes_exhausted": "Internal handoff only when another pinned existing target remains; continue with next_exact_target.",
|
|
436
436
|
"new_campaign_required": "Every existing campaign, lane, and source family is spent or infeasible; ask whether to create the proposed campaign. Refill never creates it.",
|
|
437
|
-
"
|
|
437
|
+
"source_supply_exhausted_at_rubric": "Terminal and NOT retryable — do not re-run and do not treat it as blocked_retryable. Every remaining existing-row frontier is receipt-proven exhausted at the ICP/rubric stage: the preparation receipts carry a non-retryable icp_or_rubric_rejection diagnosis, so the rows behind the scan window cannot convert into sends no matter how many times the command runs. Distinct from new_campaign_required: the source rows exist, they simply cannot pass the current rubric. Report the per-lane evidence from the note (campaign/table lane, senders covered, rows checked, rows rubric-rejected) and tell the user the only remaining fill paths are widening the rubric or adding fresh source supply. Refill changes neither on its own.",
|
|
438
|
+
"actionable_supply_not_queued": "Actionable existing-row supply (approval/generate/enrich/rubric candidates or unprocessed frontier rows) remains but no executable exact edge was dispatched for it. The census counts CONVERTIBLE rows only, de-duplicated per campaign/table lane; frontiers proven non-convertible by their own receipts are reported as source_supply_exhausted_at_rubric instead. When the run refused edges the planner did expose, the note NAMES each one (campaign:lane, sender count, actionKey, and the refusal point) — report those named blockers, not just the census. Retry with a fresh command once; if it repeats, report a planner-exposure defect with the census and the named refused edges from the note."
|
|
438
439
|
},
|
|
439
440
|
"yoloMode": {
|
|
440
441
|
"approvalSource": "explicit_yolo_flag",
|