@jetta/cargo-stabilizer 0.1.0-10 → 0.1.0-12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -13,13 +13,14 @@ Format based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
13
13
  - Empty-space candidate discovery for floor and elevated slots via `@jetta/empty-spaces-manager`.
14
14
  - Supporter-top candidate slots aligned to each stacked item footprint, included in beam expansion (`top_k ∪ supporter_top`).
15
15
  - Elevated candidates must sit on a supporter `(x, z)` origin so stacked loose items copy the supporter footprint.
16
- - `cargo_stabilizer.pin_tie_ratio_blocks` (default `false`): when true, 3_2/2_3 groups stay pinned; otherwise every item is movable.
16
+ - `cargo_stabilizer.pin_tie_ratio_blocks` (default `false`): when true, only **floor five-slot** `3_2`/`2_3` walls stay pinned; stacked tie-ratio items and generic groups stay movable.
17
17
  - Public contract exports: `parse_stabilizer_input`, `parse_stabilizer_output`, `safe_parse_stabilizer_input`, `safe_parse_stabilizer_output`, and related domain types.
18
18
 
19
19
  ### Changed
20
20
 
21
21
  - README documents I/O contract mapping, configuration keys, dependencies, and test/coverage commands.
22
22
  - Reorder objective is whole-cargo `transport_score`; loose-item score sum is only a tie-break. Axle, waypoint, and milkrun gates are unchanged.
23
+ - `pin_tie_ratio_blocks` pins only floor five-slot `3_2`/`2_3` walls; stacked members of the same group stay movable.
23
24
 
24
25
  ### Testing
25
26
 
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @jetta/cargo-stabilizer
2
2
 
3
- TypeScript library that reorders items to improve **whole-cargo transport stability**. By default every item is movable; set `cargo_stabilizer.pin_tie_ratio_blocks` to pin 3_2/2_3 groups. The reorder objective is `transport_score` (loose-item score sum is a tie-break). Implements five config-selectable strategies with organization gates (waypoint order, milkrun wall reservation) and empty-space candidate discovery (JET-2262).
3
+ TypeScript library that reorders items to improve **whole-cargo transport stability**. By default every item is movable; set `cargo_stabilizer.pin_tie_ratio_blocks` to pin **floor five-slot** `3_2`/`2_3` walls. Stacked tie-ratio pieces and generic groups stay movable one item at a time. The reorder objective is `transport_score` (loose-item score sum is a tie-break). Implements five config-selectable strategies with organization gates (waypoint order, milkrun wall reservation) and empty-space candidate discovery (JET-2262).
4
4
 
5
5
  ## Requirements
6
6
 
@@ -73,7 +73,7 @@ Exported parsers: `parse_stabilizer_input`, `parse_stabilizer_output`, `safe_par
73
73
  | `cargo_stabilizer.transport_stability_floor` | `0` | Hard minimum transport stability score (gate, not the maximize objective) |
74
74
  | `cargo_stabilizer.max_iteration` | `10` | Greedy / SA iteration budget |
75
75
  | `cargo_stabilizer.preserve_tie_block_organization` | `true` | When true, enforce waypoint Z zones and macro delivery order |
76
- | `cargo_stabilizer.pin_tie_ratio_blocks` | `false` | When true, do not move 3_2/2_3 groups; other items remain movable |
76
+ | `cargo_stabilizer.pin_tie_ratio_blocks` | `false` | When true, do not move floor five-slot `3_2`/`2_3` walls; stacked tie-ratio items and other groups remain movable |
77
77
  | `cargo_stabilizer.enforce_milkrun_wall_reservation` | `false` | When true, reserve rear compound-wall Z bands using profile key below |
78
78
  | `number_of_wall_to_allocate_from_last_waypoints` | `0` | Count of trailing waypoints whose compound walls are reserved (read from `cargo_state.config.algoritmo` when milkrun enforcement is enabled) |
79
79
  | `folga_de_peso_nos_eixos` | `0` | Axle folga percent |
@@ -1 +1 @@
1
- {"version":3,"file":"axle_adapter.d.ts","sourceRoot":"","sources":["../../../src/core/adapters/axle_adapter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oDAAoD,CAAA;AAEvF;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAC9B,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,GAClB,OAAO,CAGT;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAejF"}
1
+ {"version":3,"file":"axle_adapter.d.ts","sourceRoot":"","sources":["../../../src/core/adapters/axle_adapter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oDAAoD,CAAA;AAEvF;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAC9B,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,GAClB,OAAO,CAGT;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAejF"}
@@ -1,4 +1,5 @@
1
1
  import { run_axle_calculator } from './run_axle_calculator.js';
2
+ import { has_penetrating_overlap } from '../geometry/has_penetrating_overlap.js';
2
3
  /**
3
4
  * @param final_kg - Loaded weight including tare.
4
5
  * @param max_kg - Axle supported max.
@@ -20,11 +21,14 @@ export function is_axle_load_valid(env, folga_pct) {
20
21
  }
21
22
  const result = run_axle_calculator(env);
22
23
  if (!result.calculated) {
23
- return false;
24
+ return is_false_positive_overlap(env, result.reason);
24
25
  }
25
26
  const containers = (result.env.containers ?? []);
26
27
  return containers.every(container => container_axles_within_folga(container, folga_pct));
27
28
  }
29
+ function is_false_positive_overlap(env, reason) {
30
+ return reason.includes('Item overlap error') && !has_penetrating_overlap(env.containers);
31
+ }
28
32
  function container_axles_within_folga(container, folga_pct) {
29
33
  const axles = (container.pesoEixos ?? []);
30
34
  return axles.every(axle => {
@@ -15,7 +15,7 @@ export interface StabilityContainerPayload {
15
15
  item_list: StabilityItemPayload[];
16
16
  }
17
17
  /**
18
- * Maps contract coordinates to cargo-stability payload coordinates.
18
+ * Maps contract CM coordinates onto cargo-stability axes (y = height, z = length).
19
19
  * @param item - Stabilizer item record.
20
20
  * @returns Stability item payload entry.
21
21
  */
@@ -1,6 +1,6 @@
1
1
  import { item_to_box } from '../geometry/box_geometry.js';
2
2
  /**
3
- * Maps contract coordinates to cargo-stability payload coordinates.
3
+ * Maps contract CM coordinates onto cargo-stability axes (y = height, z = length).
4
4
  * @param item - Stabilizer item record.
5
5
  * @returns Stability item payload entry.
6
6
  */
@@ -3,7 +3,7 @@ import type { LooseItemRef } from './loose_item_filter.js';
3
3
  /**
4
4
  * Collects items the stabilizer may move.
5
5
  * @param containers - Container list from env.
6
- * @param pin_tie_ratio_blocks - When true, skip 3_2/2_3 groups.
6
+ * @param pin_tie_ratio_blocks - When true, skip floor five-slot 3_2/2_3 walls.
7
7
  * @returns Movable item references with container index.
8
8
  */
9
9
  export declare function collect_movable_items(containers: {
@@ -11,8 +11,8 @@ export declare function collect_movable_items(containers: {
11
11
  }[], pin_tie_ratio_blocks: boolean): LooseItemRef[];
12
12
  /**
13
13
  * @param item - Item record.
14
- * @param tie_ratio_group_ids - Group ids that contain a 3_2/2_3 member.
15
- * @returns True when the item belongs to a pinned tie-ratio group.
14
+ * @param pinned_group_ids - Floor five-slot 3_2/2_3 group ids.
15
+ * @returns True when the item is a floor member of a pinned wall.
16
16
  */
17
- export declare function is_tie_ratio_block_item(item: StabilizerItem, tie_ratio_group_ids: Set<string>): boolean;
17
+ export declare function is_tie_ratio_block_item(item: StabilizerItem, pinned_group_ids: Set<string>): boolean;
18
18
  //# sourceMappingURL=collect_movable_items.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"collect_movable_items.d.ts","sourceRoot":"","sources":["../../../src/core/filters/collect_movable_items.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oDAAoD,CAAA;AAExF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAA;AAE1D;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACjC,UAAU,EAAE;IAAE,KAAK,EAAE,cAAc,EAAE,CAAA;CAAE,EAAE,EACzC,oBAAoB,EAAE,OAAO,GAC9B,YAAY,EAAE,CAMhB;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CACnC,IAAI,EAAE,cAAc,EACpB,mBAAmB,EAAE,GAAG,CAAC,MAAM,CAAC,GACjC,OAAO,CAOT"}
1
+ {"version":3,"file":"collect_movable_items.d.ts","sourceRoot":"","sources":["../../../src/core/filters/collect_movable_items.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oDAAoD,CAAA;AACxF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAA;AAI1D;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACjC,UAAU,EAAE;IAAE,KAAK,EAAE,cAAc,EAAE,CAAA;CAAE,EAAE,EACzC,oBAAoB,EAAE,OAAO,GAC9B,YAAY,EAAE,CAMhB;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CACnC,IAAI,EAAE,cAAc,EACpB,gBAAgB,EAAE,GAAG,CAAC,MAAM,CAAC,GAC9B,OAAO,CAOT"}
@@ -1,38 +1,27 @@
1
- import { TIE_RATIO_ORIENTATION } from '../constants/TieRatioOrientation.js';
1
+ import { collect_pinned_floor_tie_ratio_group_ids } from './collect_pinned_floor_tie_ratio_group_ids.js';
2
+ import { is_item_on_cm_floor } from './is_item_on_cm_floor.js';
2
3
  /**
3
4
  * Collects items the stabilizer may move.
4
5
  * @param containers - Container list from env.
5
- * @param pin_tie_ratio_blocks - When true, skip 3_2/2_3 groups.
6
+ * @param pin_tie_ratio_blocks - When true, skip floor five-slot 3_2/2_3 walls.
6
7
  * @returns Movable item references with container index.
7
8
  */
8
9
  export function collect_movable_items(containers, pin_tie_ratio_blocks) {
9
10
  const pinned_groups = pin_tie_ratio_blocks
10
- ? collect_tie_ratio_group_ids(flatten_container_items(containers))
11
+ ? collect_pinned_floor_tie_ratio_group_ids(flatten_container_items(containers))
11
12
  : new Set();
12
13
  return collect_items_except_pinned(containers, pinned_groups);
13
14
  }
14
15
  /**
15
16
  * @param item - Item record.
16
- * @param tie_ratio_group_ids - Group ids that contain a 3_2/2_3 member.
17
- * @returns True when the item belongs to a pinned tie-ratio group.
17
+ * @param pinned_group_ids - Floor five-slot 3_2/2_3 group ids.
18
+ * @returns True when the item is a floor member of a pinned wall.
18
19
  */
19
- export function is_tie_ratio_block_item(item, tie_ratio_group_ids) {
20
- if (item.group_id == null) {
20
+ export function is_tie_ratio_block_item(item, pinned_group_ids) {
21
+ if (item.group_id == null || !is_item_on_cm_floor(item)) {
21
22
  return false;
22
23
  }
23
- return tie_ratio_group_ids.has(item.group_id);
24
- }
25
- function collect_tie_ratio_group_ids(items) {
26
- const group_ids = new Set();
27
- for (const item of items) {
28
- if (item.group_id == null) {
29
- continue;
30
- }
31
- if (is_tie_ratio_orientation(read_tie_ratio_orientation(item))) {
32
- group_ids.add(item.group_id);
33
- }
34
- }
35
- return group_ids;
24
+ return pinned_group_ids.has(item.group_id);
36
25
  }
37
26
  function collect_items_except_pinned(containers, pinned_groups) {
38
27
  const movable = [];
@@ -62,11 +51,3 @@ function flatten_container_items(containers) {
62
51
  }
63
52
  return items;
64
53
  }
65
- function read_tie_ratio_orientation(item) {
66
- const orientation = item.attributes?.tie_ratio_orientation;
67
- return typeof orientation === 'string' ? orientation : undefined;
68
- }
69
- function is_tie_ratio_orientation(orientation) {
70
- return orientation === TIE_RATIO_ORIENTATION.THREE_TWO
71
- || orientation === TIE_RATIO_ORIENTATION.TWO_THREE;
72
- }
@@ -0,0 +1,8 @@
1
+ import type { StabilizerItem } from '../validation/contract/stabilizer_domain_schema.js';
2
+ /**
3
+ * Group ids of floor five-slot 3_2/2_3 walls that the stabilizer must pin.
4
+ * @param items - All items in the cargo.
5
+ * @returns Group ids with exactly five floor tie-ratio members.
6
+ */
7
+ export declare function collect_pinned_floor_tie_ratio_group_ids(items: StabilizerItem[]): Set<string>;
8
+ //# sourceMappingURL=collect_pinned_floor_tie_ratio_group_ids.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collect_pinned_floor_tie_ratio_group_ids.d.ts","sourceRoot":"","sources":["../../../src/core/filters/collect_pinned_floor_tie_ratio_group_ids.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oDAAoD,CAAA;AAMxF;;;;GAIG;AACH,wBAAgB,wCAAwC,CAAC,KAAK,EAAE,cAAc,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,CAQ7F"}
@@ -0,0 +1,41 @@
1
+ import { TIE_RATIO_ORIENTATION } from '../constants/TieRatioOrientation.js';
2
+ import { is_item_on_cm_floor } from './is_item_on_cm_floor.js';
3
+ const FULL_TIE_RATIO_FLOOR_COUNT = 5;
4
+ /**
5
+ * Group ids of floor five-slot 3_2/2_3 walls that the stabilizer must pin.
6
+ * @param items - All items in the cargo.
7
+ * @returns Group ids with exactly five floor tie-ratio members.
8
+ */
9
+ export function collect_pinned_floor_tie_ratio_group_ids(items) {
10
+ const floor_tie_count_by_group = new Map();
11
+ for (const item of items) {
12
+ add_floor_tie_member(floor_tie_count_by_group, item);
13
+ }
14
+ return groups_with_full_floor_wall(floor_tie_count_by_group);
15
+ }
16
+ function add_floor_tie_member(counts, item) {
17
+ if (item.group_id == null || !is_item_on_cm_floor(item)) {
18
+ return;
19
+ }
20
+ if (!is_tie_ratio_orientation(read_tie_ratio_orientation(item))) {
21
+ return;
22
+ }
23
+ counts.set(item.group_id, (counts.get(item.group_id) ?? 0) + 1);
24
+ }
25
+ function groups_with_full_floor_wall(counts) {
26
+ const pinned = new Set();
27
+ for (const [group_id, count] of counts) {
28
+ if (count === FULL_TIE_RATIO_FLOOR_COUNT) {
29
+ pinned.add(group_id);
30
+ }
31
+ }
32
+ return pinned;
33
+ }
34
+ function read_tie_ratio_orientation(item) {
35
+ const orientation = item.attributes?.tie_ratio_orientation;
36
+ return typeof orientation === 'string' ? orientation : undefined;
37
+ }
38
+ function is_tie_ratio_orientation(orientation) {
39
+ return orientation === TIE_RATIO_ORIENTATION.THREE_TWO
40
+ || orientation === TIE_RATIO_ORIENTATION.TWO_THREE;
41
+ }
@@ -0,0 +1,8 @@
1
+ import type { StabilizerItem } from '../validation/contract/stabilizer_domain_schema.js';
2
+ /**
3
+ * True when the item rests on the cargo-movement floor (z is height).
4
+ * @param item - Stabilizer item in CM coordinates.
5
+ * @returns True when height origin is on the floor.
6
+ */
7
+ export declare function is_item_on_cm_floor(item: StabilizerItem): boolean;
8
+ //# sourceMappingURL=is_item_on_cm_floor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"is_item_on_cm_floor.d.ts","sourceRoot":"","sources":["../../../src/core/filters/is_item_on_cm_floor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oDAAoD,CAAA;AAIxF;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAGjE"}
@@ -0,0 +1,9 @@
1
+ const CM_FLOOR_HEIGHT_EPSILON = 1e-6;
2
+ /**
3
+ * True when the item rests on the cargo-movement floor (z is height).
4
+ * @param item - Stabilizer item in CM coordinates.
5
+ * @returns True when height origin is on the floor.
6
+ */
7
+ export function is_item_on_cm_floor(item) {
8
+ return item.z <= CM_FLOOR_HEIGHT_EPSILON;
9
+ }
@@ -0,0 +1,9 @@
1
+ import type { StabilizerContainer } from '../validation/contract/stabilizer_domain_schema.js';
2
+ /**
3
+ * True when any two items penetrate in the axle-calculator CM frame
4
+ * (x + width, y + depth, z + height) by more than a face-touch epsilon.
5
+ * @param containers - Layout to inspect.
6
+ * @returns True when a pair occupies the same volume beyond float error.
7
+ */
8
+ export declare function has_penetrating_overlap(containers: StabilizerContainer[]): boolean;
9
+ //# sourceMappingURL=has_penetrating_overlap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"has_penetrating_overlap.d.ts","sourceRoot":"","sources":["../../../src/core/geometry/has_penetrating_overlap.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAkB,MAAM,oDAAoD,CAAA;AAI7G;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,mBAAmB,EAAE,GAAG,OAAO,CAGlF"}
@@ -0,0 +1,27 @@
1
+ const PENETRATION_EPSILON = 1e-6;
2
+ /**
3
+ * True when any two items penetrate in the axle-calculator CM frame
4
+ * (x + width, y + depth, z + height) by more than a face-touch epsilon.
5
+ * @param containers - Layout to inspect.
6
+ * @returns True when a pair occupies the same volume beyond float error.
7
+ */
8
+ export function has_penetrating_overlap(containers) {
9
+ return containers.some(container => container_has_penetrating_overlap(container));
10
+ }
11
+ function container_has_penetrating_overlap(container) {
12
+ const items = container.items;
13
+ for (let i = 0; i < items.length; i++) {
14
+ for (let j = i + 1; j < items.length; j++) {
15
+ if (items_penetrate(items[i], items[j])) {
16
+ return true;
17
+ }
18
+ }
19
+ }
20
+ return false;
21
+ }
22
+ function items_penetrate(left, right) {
23
+ return interval_penetration({ min: left.x, max: left.x + left.width }, { min: right.x, max: right.x + right.width }) && interval_penetration({ min: left.y, max: left.y + left.depth }, { min: right.y, max: right.y + right.depth }) && interval_penetration({ min: left.z, max: left.z + left.height }, { min: right.z, max: right.z + right.height });
24
+ }
25
+ function interval_penetration(left, right) {
26
+ return Math.min(left.max, right.max) - Math.max(left.min, right.min) > PENETRATION_EPSILON;
27
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"BeamSearchReorder.d.ts","sourceRoot":"","sources":["../../../../src/core/strategies/reorder/BeamSearchReorder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAC3E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,uDAAuD,CAAA;AAahG;;GAEG;AACH,qBAAa,iBAAkB,YAAW,eAAe;IAErD,GAAG,CAAC,OAAO,EAAE,cAAc,GAAG,mBAAmB,EAAE;CAkBtD"}
1
+ {"version":3,"file":"BeamSearchReorder.d.ts","sourceRoot":"","sources":["../../../../src/core/strategies/reorder/BeamSearchReorder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAC3E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,uDAAuD,CAAA;AAchG;;GAEG;AACH,qBAAa,iBAAkB,YAAW,eAAe;IAErD,GAAG,CAAC,OAAO,EAAE,cAAc,GAAG,mBAAmB,EAAE;CAkBtD"}
@@ -4,6 +4,7 @@ import { select_beam_expansion_candidates } from './select_beam_expansion_candid
4
4
  import { apply_move, clone_containers } from '../../move/apply_move.js';
5
5
  import { evaluate_trial_layout, evaluation_context_from_reorder } from '../../evaluation/state_evaluator.js';
6
6
  import { read_item_score } from '../../adapters/stability_adapter.js';
7
+ import { select_surviving_beam } from './select_surviving_beam.js';
7
8
  /**
8
9
  * Beam search reorder with configurable width and top-K expansion.
9
10
  */
@@ -34,8 +35,7 @@ function expand_beam(request) {
34
35
  loose: request.loose
35
36
  });
36
37
  }
37
- expanded.sort((left, right) => right.objective - left.objective);
38
- return expanded.slice(0, request.reorder.config.beam_width);
38
+ return select_surviving_beam(expanded, request.beam, request.reorder.config.beam_width);
39
39
  }
40
40
  function append_beam_branches(beam_context) {
41
41
  const { reorder, evaluation, expanded, state, loose } = beam_context;
@@ -0,0 +1,10 @@
1
+ import type { BeamState } from './beam_context.js';
2
+ /**
3
+ * Keeps ranked expansions, or the parent beam when every trial failed the gates.
4
+ * @param expanded - Trial states that passed gates.
5
+ * @param parent_beam - Beam states before this item's expansion.
6
+ * @param beam_width - Maximum states to keep.
7
+ * @returns Next beam, never empty when the parent beam had states.
8
+ */
9
+ export declare function select_surviving_beam(expanded: BeamState[], parent_beam: BeamState[], beam_width: number): BeamState[];
10
+ //# sourceMappingURL=select_surviving_beam.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"select_surviving_beam.d.ts","sourceRoot":"","sources":["../../../../src/core/strategies/reorder/select_surviving_beam.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAElD;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACjC,QAAQ,EAAE,SAAS,EAAE,EACrB,WAAW,EAAE,SAAS,EAAE,EACxB,UAAU,EAAE,MAAM,GACnB,SAAS,EAAE,CAKb"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Keeps ranked expansions, or the parent beam when every trial failed the gates.
3
+ * @param expanded - Trial states that passed gates.
4
+ * @param parent_beam - Beam states before this item's expansion.
5
+ * @param beam_width - Maximum states to keep.
6
+ * @returns Next beam, never empty when the parent beam had states.
7
+ */
8
+ export function select_surviving_beam(expanded, parent_beam, beam_width) {
9
+ const ranked = [...parent_beam, ...expanded]
10
+ .sort((left, right) => right.objective - left.objective);
11
+ return ranked.slice(0, beam_width);
12
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jetta/cargo-stabilizer",
3
- "version": "0.1.0-10",
3
+ "version": "0.1.0-12",
4
4
  "description": "Stable loose-item reorder library for cargo-movement (JET-2262)",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",