poi-plugin-remodel-assistant 1.2.5 → 1.2.6

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.
Files changed (26) hide show
  1. package/assets/main.css +97 -33
  2. package/data/changelog.json +8 -0
  3. package/i18n/en-US.json +8 -1
  4. package/i18n/ja-JP.json +8 -1
  5. package/i18n/zh-CN.json +8 -1
  6. package/i18n/zh-TW.json +8 -1
  7. package/package.json +1 -1
  8. package/views/new-interface/app/decision-snapshot-cache.js +2 -2
  9. package/views/new-interface/app/decision-snapshot-jobs.js +147 -28
  10. package/views/new-interface/app/decision-snapshot-service.js +5 -1
  11. package/views/new-interface/app/decision-snapshot-worker.js +27 -1
  12. package/views/new-interface/app/new-interface-area.js +85 -35
  13. package/views/new-interface/app/startup-decision-snapshot-storage.js +84 -0
  14. package/views/new-interface/decisions/domain/stable-state-validation-domain.js +21 -5
  15. package/views/new-interface/equipment/components/equipment-detail.js +52 -7
  16. package/views/new-interface/incremental/plan-decision-index.js +120 -43
  17. package/views/new-interface/plan/domain/allocation-owner-domain.js +190 -0
  18. package/views/new-interface/plan/domain/consumable-plan-domain.js +175 -50
  19. package/views/new-interface/plan/domain/current-start-assignment-domain.js +51 -2
  20. package/views/new-interface/plan/domain/current-start-selection-domain.js +12 -5
  21. package/views/new-interface/plan/domain/plan-management-domain.js +4 -3
  22. package/views/new-interface/shared/components/detail-panel.js +70 -40
  23. package/views/shared/plugin-resource-paths.js +9 -1
  24. package/views/shared/plugin-state-commands.js +32 -3
  25. package/views/shared/plugin-state-schema.js +146 -11
  26. package/views/shared/plugin-state-storage.js +4 -1
package/assets/main.css CHANGED
@@ -10955,67 +10955,120 @@ body.decision-modal-is-dragging * {
10955
10955
  text-overflow: ellipsis;
10956
10956
  }
10957
10957
 
10958
- /* Current improvement identity stays beside the large equipment artwork. All
10959
- * plan references are a separate scan block with deliberate horizontal air. */
10958
+ /* Current improvement identity stays beside the large equipment artwork.
10959
+ * Header tools own a stable top-right column, while plan references may grow
10960
+ * vertically without pushing the tools onto a later flex line. */
10960
10961
  #item-improvement .decision-detail-header {
10961
- flex-wrap: wrap;
10962
+ display: grid;
10963
+ grid-template-columns: auto minmax(0, 1fr) max-content;
10964
+ grid-template-areas: "artwork heading tools";
10965
+ align-items: start;
10962
10966
  gap: 12px;
10963
10967
  }
10964
10968
 
10969
+ #item-improvement .decision-detail-header.has-related-plans {
10970
+ grid-template-columns: auto minmax(180px, 0.9fr) minmax(180px, 1.1fr) max-content;
10971
+ grid-template-areas: "artwork heading related tools";
10972
+ }
10973
+
10974
+ #item-improvement .decision-detail-artwork {
10975
+ grid-area: artwork;
10976
+ margin-right: 0;
10977
+ }
10978
+
10965
10979
  #item-improvement .decision-detail-heading-copy {
10966
- flex: 0 1 auto;
10980
+ grid-area: heading;
10981
+ min-width: 0;
10982
+ }
10983
+
10984
+
10985
+ #item-improvement .decision-detail-show-parent-plans {
10986
+ display: inline-flex;
10987
+ align-items: center;
10988
+ gap: 5px;
10989
+ margin: 0;
10990
+ font-size: var(--improvement-font-small);
10991
+ font-weight: 600;
10992
+ cursor: pointer;
10993
+ }
10994
+
10995
+ #item-improvement .decision-detail-show-parent-plans input {
10996
+ margin: 0;
10967
10997
  }
10968
10998
 
10969
10999
  #item-improvement .decision-detail-related-plans {
10970
11000
  display: grid;
10971
- flex: 0 1 auto;
10972
- gap: 6px;
10973
- min-width: 140px;
10974
- margin-left: 28px;
11001
+ grid-area: related;
11002
+ gap: 7px;
11003
+ min-width: 180px;
11004
+ margin-left: 16px;
10975
11005
  }
10976
11006
 
10977
- #item-improvement .decision-detail-related-plans-label {
10978
- color: #8fa8b4;
11007
+ #item-improvement .decision-detail-related-plan-group {
11008
+ display: grid;
11009
+ gap: 5px;
11010
+ }
11011
+
11012
+ #item-improvement .decision-detail-related-plan-group-label {
11013
+ width: fit-content;
11014
+ padding: 2px 8px;
11015
+ border-radius: 999px;
11016
+ background: rgba(103, 198, 193, 0.09);
11017
+ color: #67c6c1;
10979
11018
  font-size: var(--improvement-font-small);
10980
11019
  font-weight: 700;
11020
+ line-height: 1.5;
10981
11021
  }
10982
11022
 
10983
11023
  #item-improvement .decision-detail-related-plan-list {
10984
11024
  display: flex;
10985
11025
  flex-wrap: wrap;
10986
- gap: 6px;
11026
+ align-items: flex-start;
11027
+ gap: 7px 8px;
10987
11028
  }
10988
11029
 
10989
11030
  #item-improvement .decision-detail-related-plan {
10990
11031
  display: inline-flex;
10991
11032
  align-items: baseline;
10992
11033
  gap: 5px;
10993
- padding: 3px 7px;
11034
+ padding: 5px 9px;
10994
11035
  border-radius: 999px;
10995
11036
  background: rgba(127, 127, 127, 0.09);
10996
11037
  white-space: nowrap;
10997
11038
  }
10998
11039
 
10999
- #item-improvement .decision-detail-related-plan em {
11000
- color: #67c6c1;
11001
- font-size: var(--improvement-font-caption);
11002
- font-style: normal;
11040
+ #item-improvement .decision-detail-related-plan strong {
11041
+ font-size: var(--improvement-font-body-large);
11003
11042
  font-weight: 700;
11004
11043
  }
11005
11044
 
11006
- #item-improvement .decision-detail-related-plan-parent {
11007
- color: #a9b7bf;
11008
- font-size: var(--improvement-font-small);
11009
- font-weight: 600;
11045
+ #item-improvement .decision-detail-related-plan.is-consumable {
11046
+ background: rgba(69, 169, 165, 0.1);
11010
11047
  }
11011
11048
 
11012
- #item-improvement .decision-detail-related-plan strong {
11013
- font-size: var(--improvement-font-small);
11014
- font-weight: 700;
11049
+ #item-improvement .decision-detail-consumable-plan-reference {
11050
+ display: grid;
11051
+ align-content: start;
11052
+ gap: 3px;
11053
+ min-width: max-content;
11015
11054
  }
11016
11055
 
11017
- #item-improvement .decision-detail-related-plan.is-consumable {
11018
- background: rgba(69, 169, 165, 0.1);
11056
+ #item-improvement .decision-detail-consumable-plan-reference .decision-detail-related-plan-parent {
11057
+ display: flex;
11058
+ align-items: center;
11059
+ gap: 5px;
11060
+ padding: 2px 8px 0 10px;
11061
+ color: #c8d2d7;
11062
+ font-size: var(--improvement-font-body-large);
11063
+ font-weight: 650;
11064
+ line-height: 1.45;
11065
+ white-space: normal;
11066
+ overflow-wrap: anywhere;
11067
+ }
11068
+
11069
+ #item-improvement .decision-detail-related-plan-parent-arrow {
11070
+ color: #67c6c1;
11071
+ font-weight: 700;
11019
11072
  }
11020
11073
 
11021
11074
  @media (max-width: 760px) {
@@ -11462,17 +11515,28 @@ body.decision-modal-is-dragging * {
11462
11515
  /* Page-specific detail-section manager. */
11463
11516
  #item-improvement .decision-detail-panel-tools {
11464
11517
  display: flex;
11465
- flex: 0 0 auto;
11466
- align-self: flex-start;
11518
+ grid-area: tools;
11519
+ align-items: center;
11520
+ align-self: start;
11467
11521
  justify-content: flex-end;
11468
- margin-left: auto;
11522
+ gap: 10px;
11523
+ margin-left: 0;
11524
+ white-space: nowrap;
11469
11525
  }
11470
11526
 
11471
- /* Keep the manager as the last flex item in the detail header. This makes
11472
- * auto margin resolve against the real right edge even when Related Plans
11473
- * wraps differently across Poi/Electron machines. */
11474
- #item-improvement .decision-detail-header > .decision-detail-panel-tools {
11475
- order: 99;
11527
+ /* On narrow detail panes the plan groups may move below the identity, but the
11528
+ * parent-plan switch and detail manager stay pinned to the top-right cell. */
11529
+ @media (max-width: 900px) {
11530
+ #item-improvement .decision-detail-header.has-related-plans {
11531
+ grid-template-columns: auto minmax(0, 1fr) max-content;
11532
+ grid-template-areas:
11533
+ "artwork heading tools"
11534
+ "artwork related related";
11535
+ }
11536
+
11537
+ #item-improvement .decision-detail-related-plans {
11538
+ margin-left: 0;
11539
+ }
11476
11540
  }
11477
11541
 
11478
11542
  #item-improvement .decision-detail-layout-manager-button {
@@ -1,6 +1,14 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "entries": [
4
+ {
5
+ "version": "1.2.6",
6
+ "items": [
7
+ "changelog_1_2_6_1",
8
+ "changelog_1_2_6_2",
9
+ "changelog_1_2_6_3"
10
+ ]
11
+ },
4
12
  {
5
13
  "version": "1.2.5",
6
14
  "items": [
package/i18n/en-US.json CHANGED
@@ -905,8 +905,11 @@
905
905
  "shared.Corresponding plan target": "Corresponding Plan target",
906
906
  "shared.Current plan target": "Plan target",
907
907
  "shared.For plan": "for",
908
+ "shared.Parent plan": "Parent plan",
908
909
  "shared.Related plans": "Related plans",
909
910
  "shared.Consumable plan": "Consumable plan",
911
+ "shared.User plans": "User plans",
912
+ "shared.Consumable plans": "Consumable plans",
910
913
  "workbench.Plan targets": "Plan targets",
911
914
  "workbench.Plan targets available for improvement on selected day": "Today improvable",
912
915
  "workbench.No plan target is available for improvement on selected day": "No unfinished Plan target needs action on the selected day",
@@ -1592,5 +1595,9 @@
1592
1595
  "app.changelog_1_2_5_7": "Fixed several interface and data-handling issues.",
1593
1596
  "data.Backup summary %p plans %r routes %l low priority": "%p plans · %r routes · %l low-priority equipment",
1594
1597
  "equipment.Search equipment name": "Search equipment name",
1595
- "workbench.Search equipment name": "Search equipment name"
1598
+ "workbench.Search equipment name": "Search equipment name",
1599
+ "app.changelog_1_2_6_1": "Improved plan calculation and startup speed.",
1600
+ "app.changelog_1_2_6_2": "Fixed plan-count and equipment-allocation issues that could occur in rare edge cases.",
1601
+ "shared.Show parent plans": "Show parent plans",
1602
+ "app.changelog_1_2_6_3": "Improved related-plan and consumable-plan display for better readability."
1596
1603
  }
package/i18n/ja-JP.json CHANGED
@@ -1344,6 +1344,8 @@
1344
1344
  "shared.Comfortable density": "ゆったり",
1345
1345
  "shared.Compact density": "コンパクト",
1346
1346
  "shared.Consumable plan": "消耗材計画",
1347
+ "shared.User plans": "ユーザー計画",
1348
+ "shared.Consumable plans": "消耗材計画",
1347
1349
  "shared.Corresponding plan target": "対応する計画目標",
1348
1350
  "shared.Current improvement starting point": "現在の改修開始点",
1349
1351
  "shared.Current plan target": "現在の計画目標",
@@ -1353,6 +1355,7 @@
1353
1355
  "shared.Equipment category filter": "装備カテゴリ絞り込み",
1354
1356
  "shared.Expanded": "展開",
1355
1357
  "shared.For plan": "使用先",
1358
+ "shared.Parent plan": "親計画",
1356
1359
  "shared.Improvement starting point": "改修開始点",
1357
1360
  "shared.Interface density": "表示密度",
1358
1361
  "shared.Move down": "下へ",
@@ -1592,5 +1595,9 @@
1592
1595
  "app.changelog_1_2_5_7": "一部の画面表示とデータ処理の問題を修正しました。",
1593
1596
  "data.Backup summary %p plans %r routes %l low priority": "プラン %p · ルート %r · 低優先装備 %l",
1594
1597
  "equipment.Search equipment name": "装備名を検索",
1595
- "workbench.Search equipment name": "装備名を検索"
1598
+ "workbench.Search equipment name": "装備名を検索",
1599
+ "app.changelog_1_2_6_1": "計画計算と起動速度を改善しました。",
1600
+ "app.changelog_1_2_6_2": "ごくまれな条件で計画数や装備割り当てが正しくならない問題を修正しました。",
1601
+ "shared.Show parent plans": "親計画を表示",
1602
+ "app.changelog_1_2_6_3": "関連計画と消耗品計画の表示を改善し、読みやすくしました。"
1596
1603
  }
package/i18n/zh-CN.json CHANGED
@@ -905,8 +905,11 @@
905
905
  "shared.Corresponding plan target": "对应计划目标",
906
906
  "shared.Current plan target": "当前计划目标",
907
907
  "shared.For plan": "用于",
908
+ "shared.Parent plan": "父计划",
908
909
  "shared.Related plans": "相关计划",
909
910
  "shared.Consumable plan": "耗材计划",
911
+ "shared.User plans": "用户计划",
912
+ "shared.Consumable plans": "耗材计划",
910
913
  "workbench.Plan targets": "计划目标",
911
914
  "workbench.Plan targets available for improvement on selected day": "今日可改",
912
915
  "workbench.No plan target is available for improvement on selected day": "当天没有需要处理的未完成计划目标",
@@ -1592,5 +1595,9 @@
1592
1595
  "app.changelog_1_2_5_7": "修复部分界面与数据处理问题。",
1593
1596
  "data.Backup summary %p plans %r routes %l low priority": "计划 %p · 路线 %r · 低优先装备 %l",
1594
1597
  "equipment.Search equipment name": "搜索装备名称",
1595
- "workbench.Search equipment name": "搜索装备名称"
1598
+ "workbench.Search equipment name": "搜索装备名称",
1599
+ "app.changelog_1_2_6_1": "优化计划计算与启动速度。",
1600
+ "app.changelog_1_2_6_2": "修复极端情况下计划数量和装备分配异常的问题。",
1601
+ "shared.Show parent plans": "显示父计划",
1602
+ "app.changelog_1_2_6_3": "优化相关计划与耗材计划的显示,提高可读性。"
1596
1603
  }
package/i18n/zh-TW.json CHANGED
@@ -1344,6 +1344,8 @@
1344
1344
  "shared.Comfortable density": "寬鬆",
1345
1345
  "shared.Compact density": "緊湊",
1346
1346
  "shared.Consumable plan": "耗材計畫",
1347
+ "shared.User plans": "使用者計畫",
1348
+ "shared.Consumable plans": "耗材計畫",
1347
1349
  "shared.Corresponding plan target": "對應計畫目標",
1348
1350
  "shared.Current improvement starting point": "目前改修起點",
1349
1351
  "shared.Current plan target": "目前計畫目標",
@@ -1353,6 +1355,7 @@
1353
1355
  "shared.Equipment category filter": "裝備分類篩選",
1354
1356
  "shared.Expanded": "已展開",
1355
1357
  "shared.For plan": "用於",
1358
+ "shared.Parent plan": "父計畫",
1356
1359
  "shared.Improvement starting point": "改修起點",
1357
1360
  "shared.Interface density": "介面密度",
1358
1361
  "shared.Move down": "下移",
@@ -1592,5 +1595,9 @@
1592
1595
  "app.changelog_1_2_5_7": "修復部分介面與資料處理問題。",
1593
1596
  "data.Backup summary %p plans %r routes %l low priority": "計畫 %p · 路線 %r · 低優先裝備 %l",
1594
1597
  "equipment.Search equipment name": "搜尋裝備名稱",
1595
- "workbench.Search equipment name": "搜尋裝備名稱"
1598
+ "workbench.Search equipment name": "搜尋裝備名稱",
1599
+ "app.changelog_1_2_6_1": "最佳化計畫計算與啟動速度。",
1600
+ "app.changelog_1_2_6_2": "修復極端情況下計畫數量與裝備分配異常的問題。",
1601
+ "shared.Show parent plans": "顯示父計畫",
1602
+ "app.changelog_1_2_6_3": "最佳化相關計畫與耗材計畫的顯示,提高可讀性。"
1596
1603
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "poi-plugin-remodel-assistant",
3
- "version": "1.2.5",
3
+ "version": "1.2.6",
4
4
  "description": "View enhanced KanColle improvement data, manage improvement plans, and receive improvement recommendations.",
5
5
  "main": "index.js",
6
6
  "author": {
@@ -9,7 +9,7 @@ var _equipmentBlacklistDomain = require("../equipment/domain/equipment-blacklist
9
9
 
10
10
  function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
11
11
 
12
- const SNAPSHOT_INPUT_KEYS = ['dataRevision', 'dataIdentity', 'plans', 'initialDay', 'masterEquips', 'masterUseitems', 'equipTypes', 'staticItems', 'actualEquipLevels', 'actualEquipInventory', 'actualUseitems', 'actualResourceBudget', 'equipmentRealityReady', 'actualOwnedShipMasterIds', 'decisionSelections', 'planRouteSelections', 'currentInventoryBindings', 'equipmentBlacklist', 'routeAcquisitionIndex'];
12
+ const SNAPSHOT_INPUT_KEYS = ['dataRevision', 'dataIdentity', 'plans', 'initialDay', 'masterEquips', 'masterUseitems', 'equipTypes', 'staticItems', 'actualEquipLevels', 'actualEquipInventory', 'actualUseitems', 'actualResourceBudget', 'equipmentRealityReady', 'actualOwnedShipMasterIds', 'decisionSelections', 'planRouteSelections', 'currentInventoryBindings', 'currentInventoryBindingOwnerRefs', 'equipmentBlacklist', 'routeAcquisitionIndex'];
13
13
  exports.SNAPSHOT_INPUT_KEYS = SNAPSHOT_INPUT_KEYS;
14
14
  const SNAPSHOT_FEEDBACK_CONTEXT_KEYS = SNAPSHOT_INPUT_KEYS.filter(key => key !== 'currentInventoryBindings');
15
15
  exports.SNAPSHOT_FEEDBACK_CONTEXT_KEYS = SNAPSHOT_FEEDBACK_CONTEXT_KEYS;
@@ -63,7 +63,7 @@ const stableSignature = value => JSON.stringify(stableValue(value || {})); // `m
63
63
  // cheap secondary guard for unexpected replacement under the same data identity.
64
64
 
65
65
 
66
- const CONTENT_IDENTITY_KEYS = new Set(['plans', 'actualEquipLevels', 'actualEquipInventory', 'actualUseitems', 'actualResourceBudget', 'actualOwnedShipMasterIds', 'decisionSelections', 'planRouteSelections', 'currentInventoryBindings']); // `routeAcquisitionIndex` is a large selector-derived static projection. It
66
+ const CONTENT_IDENTITY_KEYS = new Set(['plans', 'actualEquipLevels', 'actualEquipInventory', 'actualUseitems', 'actualResourceBudget', 'actualOwnedShipMasterIds', 'decisionSelections', 'planRouteSelections', 'currentInventoryBindings', 'currentInventoryBindingOwnerRefs']); // `routeAcquisitionIndex` is a large selector-derived static projection. It
67
67
  // still needs semantic equality so a reconstructed-but-equivalent generation
68
68
  // can reuse the completed Snapshot, but repeatedly sorting/stringifying the
69
69
  // same nested object on every React update makes Plan edits unnecessarily slow.
@@ -13,6 +13,8 @@ var _consumablePlanDomain = require("../plan/domain/consumable-plan-domain");
13
13
 
14
14
  var _currentStartAssignmentDomain = require("../plan/domain/current-start-assignment-domain");
15
15
 
16
+ var _allocationOwnerDomain = require("../plan/domain/allocation-owner-domain");
17
+
16
18
  var _improvementPlanDomain = require("../plan/domain/improvement-plan-domain");
17
19
 
18
20
  function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
@@ -37,18 +39,35 @@ const prefixedProfile = (profile, prefix) => typeof profile === 'function' ? (ev
37
39
  // - composition:只提供 Derived Consumable 展示元数据。
38
40
 
39
41
 
42
+ const attachUserPlanAuthorityProjection = (items, userItems) => {
43
+ const userById = new Map((userItems || []).map(item => [Number(item && item.id), item]));
44
+ return (items || []).map(item => {
45
+ const userItem = userById.get(Number(item && item.id)) || null;
46
+ if (!userItem) return item;
47
+ return _extends({}, item, {
48
+ userPlanAuthorityProjection: {
49
+ planState: userItem.planState || null,
50
+ planRouteEntries: Array.isArray(userItem.planRouteEntries) ? userItem.planRouteEntries : [],
51
+ routePlanning: userItem.routePlanning || null
52
+ }
53
+ });
54
+ });
55
+ };
56
+
40
57
  const publishDecisionSnapshot = ({
41
58
  userAllocation,
42
59
  consumableAllocation,
43
60
  composition,
61
+ userPlans = {},
62
+ bindingTransferTransitions = [],
44
63
  useitems = {},
45
64
  resourceBudget = {}
46
65
  }) => {
47
66
  const effective = consumableAllocation;
48
- const decoratedItems = (0, _consumablePlanDomain.decorateConsumablePlanEntries)({
67
+ const decoratedItems = attachUserPlanAuthorityProjection((0, _consumablePlanDomain.decorateConsumablePlanEntries)({
49
68
  items: effective.items,
50
69
  metadataByEquipmentId: composition.metadataByEquipmentId
51
- });
70
+ }), userAllocation && userAllocation.items || []);
52
71
  const items = (0, _improvementPlanDomain.materializeEquipmentDetailResolutions)({
53
72
  items: decoratedItems,
54
73
  equipLevels: effective.effectiveEquipLevels,
@@ -59,6 +78,10 @@ const publishDecisionSnapshot = ({
59
78
  items: effective.workbenchItems,
60
79
  metadataByEquipmentId: composition.metadataByEquipmentId
61
80
  });
81
+ const liveAllocationOwners = (0, _allocationOwnerDomain.buildLiveAllocationOwnerSet)({
82
+ userPlans,
83
+ consumableMetadataByEquipmentId: composition.metadataByEquipmentId
84
+ });
62
85
  const snapshot = {
63
86
  effectiveEquipLevels: effective.effectiveEquipLevels,
64
87
  effectiveUseitems: useitems,
@@ -67,9 +90,10 @@ const publishDecisionSnapshot = ({
67
90
  items,
68
91
  workbenchItems,
69
92
  chainItems: effective.chainItems || [],
93
+ liveAllocationOwners,
70
94
  retainedBindingCurrentIds: effective.retainedBindingCurrentIds || [],
71
95
  assignmentPersistenceBindings: effective.assignmentPersistenceBindings || effective.currentInventoryBindings || {},
72
- assignmentBindingTransitions: [...(userAllocation && userAllocation.assignmentBindingTransitions || []), ...(effective.assignmentBindingTransitions || [])],
96
+ assignmentBindingTransitions: [...(userAllocation && userAllocation.assignmentBindingTransitions || []), ...(bindingTransferTransitions || []), ...(effective.assignmentBindingTransitions || [])],
73
97
  // invariant findings 读取最终阶段即可;它不是阶段历史,不做重复累积。
74
98
  assignmentFindings: effective.assignmentFindings || [],
75
99
  assignmentDiagnostics: [...(userAllocation && userAllocation.assignmentDiagnostics || []), ...(effective.assignmentDiagnostics || [])]
@@ -230,10 +254,15 @@ const allocationStageProjection = (result, allocationScope) => ({
230
254
 
231
255
  const buildFullDecisionSnapshotPayload = input => {
232
256
  const job = input || {};
257
+ const userLiveOwners = (0, _allocationOwnerDomain.buildLiveAllocationOwnerSet)({
258
+ userPlans: job.plans || {}
259
+ });
260
+ const baseRouteSelections = (0, _allocationOwnerDomain.filterRouteSelectionsForLiveAllocationOwners)(job.planRouteSelections || {}, userLiveOwners);
233
261
  const baseBindings = (0, _currentStartAssignmentDomain.filterCurrentInventoryBindingsForPlanSpace)(job.plans || {}, job.currentInventoryBindings || {}); // Phase 1: UserAllocation(用户计划主件分配). All eligible User Plan
234
262
  // main-equipment Bindings are established before Consumable demand derivation.
235
263
 
236
264
  const base = (0, _decisionSnapshotDomain.buildFullDecisionSnapshotCore)(_extends({}, job, {
265
+ planRouteSelections: baseRouteSelections,
237
266
  currentInventoryBindings: baseBindings,
238
267
  allocationScope: 'user'
239
268
  }));
@@ -246,24 +275,63 @@ const buildFullDecisionSnapshotPayload = input => {
246
275
  // whole User owner set. Derived Consumable demand is computed only after it.
247
276
  // UserBindingPhaseBoundary(用户绑定阶段边界).
248
277
 
249
- const demands = (0, _consumablePlanDomain.collectConsumablePlanDemands)(base.workbenchItems);
278
+ const requirements = (0, _consumablePlanDomain.collectConsumableRequirements)(base.workbenchItems);
279
+ observeAllocationStage(job, 'ConsumableRequirementDiscovery', {
280
+ mode: 'full',
281
+ requirements,
282
+ requirementCount: requirements.length
283
+ });
284
+ const requirementCoverage = (0, _consumablePlanDomain.computeConsumableRequirementCoverage)(requirements, base.workbenchItems);
285
+ observeAllocationStage(job, 'ConsumableCoverage', {
286
+ mode: 'full',
287
+ requirements,
288
+ requirementCoverage,
289
+ userBindings: base.currentInventoryBindings || {}
290
+ });
291
+ const demands = (0, _consumablePlanDomain.deriveConsumableAcquisitionTasks)(requirements, requirementCoverage);
292
+ observeAllocationStage(job, 'ConsumableAcquisitionDerivation', {
293
+ mode: 'full',
294
+ requirements,
295
+ requirementCoverage,
296
+ demands,
297
+ requirementCount: requirements.length,
298
+ demandCount: demands.length
299
+ });
250
300
  const composition = demands.length > 0 ? (0, _consumablePlanDomain.composeConsumablePlansAfter)(job.plans || {}, demands) : {
251
301
  plans: job.plans || {},
252
302
  demands: [],
253
303
  metadataByEquipmentId: {}
254
304
  };
255
- observeAllocationStage(job, 'ConsumableDerivation', {
305
+ observeAllocationStage(job, 'ConsumableAcquisitionUnitExpansion', {
256
306
  mode: 'full',
257
- allocationScope: 'user',
258
307
  demands: composition.demands || demands,
259
- demandCount: demands.length,
260
- userBindings: base.currentInventoryBindings || {}
308
+ metadataByEquipmentId: composition.metadataByEquipmentId || {}
309
+ });
310
+ const effectiveLiveOwners = (0, _allocationOwnerDomain.buildLiveAllocationOwnerSet)({
311
+ userPlans: job.plans || {},
312
+ consumableMetadataByEquipmentId: composition.metadataByEquipmentId
313
+ });
314
+ observeAllocationStage(job, 'LiveOwnerSpace', {
315
+ mode: 'full',
316
+ liveAllocationOwners: effectiveLiveOwners
317
+ });
318
+ const effectiveRouteSelections = (0, _allocationOwnerDomain.filterRouteSelectionsForLiveAllocationOwners)(job.planRouteSelections || {}, effectiveLiveOwners);
319
+ const bindingTransfer = (0, _currentStartAssignmentDomain.transferBindingsAfterUserAllocation)({
320
+ previousBindings: job.currentInventoryBindings || {},
321
+ userPlanBindingsBeforeAllocation: baseBindings,
322
+ frozenUserBindings: base.currentInventoryBindings || {}
323
+ });
324
+ const remappedTransferredBindings = (0, _allocationOwnerDomain.remapAllocationBindingsForLiveOwners)({
325
+ bindings: bindingTransfer.bindings,
326
+ ownerRefsByTransport: job.currentInventoryBindingOwnerRefs || {},
327
+ owners: effectiveLiveOwners
261
328
  });
262
- const effectiveBindings = (0, _currentStartAssignmentDomain.filterCurrentInventoryBindingsForPlanSpace)(composition.plans, _extends({}, job.currentInventoryBindings || {}, base.currentInventoryBindings || {})); // Phase 2: ConsumableAllocation(耗材计划分配). Existing User Bindings
329
+ const effectiveBindings = (0, _currentStartAssignmentDomain.filterCurrentInventoryBindingsForPlanSpace)(composition.plans, remappedTransferredBindings); // Phase 2: ConsumableAllocation(耗材计划分配). Existing User Bindings
263
330
  // are retained; only Derived Consumable demands may materialize new Bindings.
264
331
 
265
332
  const effective = (0, _decisionSnapshotDomain.buildFullDecisionSnapshotCore)(_extends({}, job, {
266
333
  plans: composition.plans,
334
+ planRouteSelections: effectiveRouteSelections,
267
335
  currentInventoryBindings: effectiveBindings,
268
336
  allocationScope: 'consumable'
269
337
  }));
@@ -274,24 +342,32 @@ const buildFullDecisionSnapshotPayload = input => {
274
342
  userAllocation: base,
275
343
  consumableAllocation: effective,
276
344
  composition,
345
+ userPlans: job.plans || {},
346
+ bindingTransferTransitions: bindingTransfer.released || [],
277
347
  useitems: job.useitems || {},
278
348
  resourceBudget: job.resourceBudget || {}
279
349
  });
280
- };
350
+ }; // Transient projection caches only. They are optimization state, never Plan/Binding authority.
351
+ // Clearing them must be semantically equivalent to a Full rebuild from current Reality.
352
+
281
353
 
282
354
  exports.buildFullDecisionSnapshotPayload = buildFullDecisionSnapshotPayload;
283
- let baseIncrementalSnapshotState = null;
284
- let effectiveIncrementalSnapshotState = null;
355
+ let baseIncrementalProjectionCacheState = null;
356
+ let effectiveIncrementalProjectionCacheState = null;
285
357
 
286
358
  const buildDecisionSnapshotPayload = (input, profile = null) => {
287
359
  const job = input || {};
288
360
 
289
361
  if (_pluginMeta.FORCE_FULL_DECISION_REBUILD) {
290
- baseIncrementalSnapshotState = null;
291
- effectiveIncrementalSnapshotState = null;
362
+ baseIncrementalProjectionCacheState = null;
363
+ effectiveIncrementalProjectionCacheState = null;
292
364
  return buildFullDecisionSnapshotPayload(job, profile);
293
365
  }
294
366
 
367
+ const userLiveOwners = (0, _allocationOwnerDomain.buildLiveAllocationOwnerSet)({
368
+ userPlans: job.plans || {}
369
+ });
370
+ const baseRouteSelections = (0, _allocationOwnerDomain.filterRouteSelectionsForLiveAllocationOwners)(job.planRouteSelections || {}, userLiveOwners);
295
371
  let phaseStartedAt = profileNow();
296
372
  const baseBindings = (0, _currentStartAssignmentDomain.filterCurrentInventoryBindingsForPlanSpace)(job.plans || {}, job.currentInventoryBindings || {});
297
373
  measureProfile(profile, 'base-bindings', phaseStartedAt, {
@@ -300,16 +376,17 @@ const buildDecisionSnapshotPayload = (input, profile = null) => {
300
376
  phaseStartedAt = profileNow(); // Phase 1: UserAllocation(用户计划主件分配).
301
377
 
302
378
  const baseResult = (0, _planDecisionIndex.buildIncrementalDecisionSnapshot)(_extends({}, job, {
379
+ planRouteSelections: baseRouteSelections,
303
380
  currentInventoryBindings: baseBindings,
304
- previous: baseIncrementalSnapshotState,
381
+ previous: baseIncrementalProjectionCacheState,
305
382
  allocationScope: 'user',
306
383
  profile: prefixedProfile(profile, 'base')
307
384
  }));
308
385
  measureProfile(profile, 'base-core-total', phaseStartedAt, {
309
- hadPrevious: Boolean(baseIncrementalSnapshotState),
386
+ hadPrevious: Boolean(baseIncrementalProjectionCacheState),
310
387
  planGroupCount: Object.keys(job.plans || {}).length
311
388
  });
312
- baseIncrementalSnapshotState = baseResult.state;
389
+ baseIncrementalProjectionCacheState = baseResult.state;
313
390
  const base = baseResult.value;
314
391
  observeAllocationStage(job, 'UserAllocation', _extends({
315
392
  mode: 'incremental'
@@ -318,8 +395,30 @@ const buildDecisionSnapshotPayload = (input, profile = null) => {
318
395
  mode: 'incremental'
319
396
  }, allocationStageProjection(base, 'user')));
320
397
  phaseStartedAt = profileNow();
321
- const demands = (0, _consumablePlanDomain.collectConsumablePlanDemands)(base.workbenchItems);
398
+ const requirements = (0, _consumablePlanDomain.collectConsumableRequirements)(base.workbenchItems);
399
+ observeAllocationStage(job, 'ConsumableRequirementDiscovery', {
400
+ mode: 'incremental',
401
+ requirements,
402
+ requirementCount: requirements.length
403
+ });
404
+ const requirementCoverage = (0, _consumablePlanDomain.computeConsumableRequirementCoverage)(requirements, base.workbenchItems);
405
+ observeAllocationStage(job, 'ConsumableCoverage', {
406
+ mode: 'incremental',
407
+ requirements,
408
+ requirementCoverage,
409
+ userBindings: base.currentInventoryBindings || {}
410
+ });
411
+ const demands = (0, _consumablePlanDomain.deriveConsumableAcquisitionTasks)(requirements, requirementCoverage);
412
+ observeAllocationStage(job, 'ConsumableAcquisitionDerivation', {
413
+ mode: 'incremental',
414
+ requirements,
415
+ requirementCoverage,
416
+ demands,
417
+ requirementCount: requirements.length,
418
+ demandCount: demands.length
419
+ });
322
420
  measureProfile(profile, 'consumable-demands', phaseStartedAt, {
421
+ requirementCount: requirements.length,
323
422
  demandCount: demands.length,
324
423
  workbenchCount: Array.isArray(base.workbenchItems) ? base.workbenchItems.length : 0
325
424
  });
@@ -329,18 +428,35 @@ const buildDecisionSnapshotPayload = (input, profile = null) => {
329
428
  demands: [],
330
429
  metadataByEquipmentId: {}
331
430
  };
332
- observeAllocationStage(job, 'ConsumableDerivation', {
431
+ observeAllocationStage(job, 'ConsumableAcquisitionUnitExpansion', {
333
432
  mode: 'incremental',
334
- allocationScope: 'user',
335
433
  demands: composition.demands || demands,
336
- demandCount: demands.length,
337
- userBindings: base.currentInventoryBindings || {}
434
+ metadataByEquipmentId: composition.metadataByEquipmentId || {}
338
435
  });
339
436
  measureProfile(profile, 'consumable-composition', phaseStartedAt, {
340
437
  effectivePlanGroupCount: Object.keys(composition.plans || {}).length
341
438
  });
342
439
  phaseStartedAt = profileNow();
343
- const effectiveBindings = (0, _currentStartAssignmentDomain.filterCurrentInventoryBindingsForPlanSpace)(composition.plans, _extends({}, job.currentInventoryBindings || {}, base.currentInventoryBindings || {}));
440
+ const effectiveLiveOwners = (0, _allocationOwnerDomain.buildLiveAllocationOwnerSet)({
441
+ userPlans: job.plans || {},
442
+ consumableMetadataByEquipmentId: composition.metadataByEquipmentId
443
+ });
444
+ observeAllocationStage(job, 'LiveOwnerSpace', {
445
+ mode: 'incremental',
446
+ liveAllocationOwners: effectiveLiveOwners
447
+ });
448
+ const effectiveRouteSelections = (0, _allocationOwnerDomain.filterRouteSelectionsForLiveAllocationOwners)(job.planRouteSelections || {}, effectiveLiveOwners);
449
+ const bindingTransfer = (0, _currentStartAssignmentDomain.transferBindingsAfterUserAllocation)({
450
+ previousBindings: job.currentInventoryBindings || {},
451
+ userPlanBindingsBeforeAllocation: baseBindings,
452
+ frozenUserBindings: base.currentInventoryBindings || {}
453
+ });
454
+ const remappedTransferredBindings = (0, _allocationOwnerDomain.remapAllocationBindingsForLiveOwners)({
455
+ bindings: bindingTransfer.bindings,
456
+ ownerRefsByTransport: job.currentInventoryBindingOwnerRefs || {},
457
+ owners: effectiveLiveOwners
458
+ });
459
+ const effectiveBindings = (0, _currentStartAssignmentDomain.filterCurrentInventoryBindingsForPlanSpace)(composition.plans, remappedTransferredBindings);
344
460
  measureProfile(profile, 'effective-bindings', phaseStartedAt, {
345
461
  bindingCount: Object.keys(effectiveBindings || {}).length
346
462
  });
@@ -352,10 +468,11 @@ const buildDecisionSnapshotPayload = (input, profile = null) => {
352
468
  // incremental diff remains the correctness gate and recomputes every changed
353
469
  // Plan/Binding target.
354
470
 
355
- const effectivePrevious = effectiveIncrementalSnapshotState || baseResult.state;
356
- const seededEffectiveFromBase = !effectiveIncrementalSnapshotState && Boolean(baseResult.state);
471
+ const effectivePrevious = effectiveIncrementalProjectionCacheState || baseResult.state;
472
+ const seededEffectiveFromBase = !effectiveIncrementalProjectionCacheState && Boolean(baseResult.state);
357
473
  const effectiveResult = (0, _planDecisionIndex.buildIncrementalDecisionSnapshot)(_extends({}, job, {
358
474
  plans: composition.plans,
475
+ planRouteSelections: effectiveRouteSelections,
359
476
  currentInventoryBindings: effectiveBindings,
360
477
  previous: effectivePrevious,
361
478
  allocationScope: 'consumable',
@@ -366,7 +483,7 @@ const buildDecisionSnapshotPayload = (input, profile = null) => {
366
483
  seededFromBase: seededEffectiveFromBase,
367
484
  planGroupCount: Object.keys(composition.plans || {}).length
368
485
  });
369
- effectiveIncrementalSnapshotState = effectiveResult.state;
486
+ effectiveIncrementalProjectionCacheState = effectiveResult.state;
370
487
  const effective = effectiveResult.value;
371
488
  observeAllocationStage(job, 'ConsumableAllocation', _extends({
372
489
  mode: 'incremental'
@@ -376,6 +493,8 @@ const buildDecisionSnapshotPayload = (input, profile = null) => {
376
493
  userAllocation: base,
377
494
  consumableAllocation: effective,
378
495
  composition,
496
+ userPlans: job.plans || {},
497
+ bindingTransferTransitions: bindingTransfer.released || [],
379
498
  useitems: job.useitems || {},
380
499
  resourceBudget: job.resourceBudget || {}
381
500
  });
@@ -389,8 +508,8 @@ const buildDecisionSnapshotPayload = (input, profile = null) => {
389
508
  exports.buildDecisionSnapshotPayload = buildDecisionSnapshotPayload;
390
509
 
391
510
  const resetDecisionSnapshotJobState = () => {
392
- baseIncrementalSnapshotState = null;
393
- effectiveIncrementalSnapshotState = null;
511
+ baseIncrementalProjectionCacheState = null;
512
+ effectiveIncrementalProjectionCacheState = null;
394
513
  };
395
514
 
396
515
  exports.resetDecisionSnapshotJobState = resetDecisionSnapshotJobState;