@maka/maka-cli 5.231.0 → 5.232.0

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maka/maka-cli",
3
- "version": "5.231.0",
3
+ "version": "5.232.0",
4
4
  "type": "module",
5
5
  "summary": "A command line tool for scaffolding Meteor 3.x applications using either React.",
6
6
  "description": "A command line tool for scaffolding Meteor 3.x applications using React.",
@@ -1418,5 +1418,42 @@
1418
1418
  // alone would be a fresh canon violation wearing a feature's clothes.
1419
1419
  // A technomancer still comes up on the living persona, which is what
1420
1420
  // the reporter's point 4 asks for.
1421
- export const ENGINE_VERSION = '1.83.0';
1421
+ // 1.84.0 (2026-09-16): HIDING IS SLEAZE, NOT FIREWALL
1422
+ // (eJANPpm7qCaCAJBJw, fourth rejection.) The reporter read the
1423
+ // mechanics line and did the arithmetic out loud:
1424
+ // "Device Rating + Firewall on the Camera is 2d6? so, the firewall
1425
+ // of the host is a 1 (1 + 1 = 2 dice)?"
1426
+ // No -- and the question exposed something bigger than the number.
1427
+ // FIREWALL WAS NEVER THE RIGHT ATTRIBUTE. p.235-236 and p.241 both
1428
+ // give the opposed roll as Computer + Intuition [Data Processing] v.
1429
+ // LOGIC + SLEAZE. Firewall resists an INTRUSION (p.238/p.240) and has
1430
+ // nothing to do with being seen. silent-icons.ts had been reusing
1431
+ // deviceDefensePool -- the intrusion pool -- as the hide pool since
1432
+ // 1.80.0, so every hide roll in the game rolled the wrong attribute
1433
+ // and then printed its name on the line the reporter was reading.
1434
+ // The 2d6 was Device Rating twice (1 + 1), not a host Firewall of 1.
1435
+ // - A PLAIN DEVICE HAS NO SLEAZE AT ALL (p.226: "most devices have
1436
+ // only two Matrix attributes: Data Processing and Firewall" --
1437
+ // Attack and Sleaze belong to decks and hosts). So a LOOSE camera
1438
+ // or maglock hides behind one number: its rating standing in for
1439
+ // the Logic it has not got. A p.356 camera with no host rolls a
1440
+ // single die, which is right. A stray lens is not hard to find.
1441
+ // - A SLAVED one borrows, and this is the reporter's ruling on p.233:
1442
+ // "either its own or its master's rating for each rating in the
1443
+ // test ... whichever is higher." Its Sleaze of nothing loses to the
1444
+ // host's, which p.247 gives as a real number off the Host Rating
1445
+ // array. A lens on a HR 3 host now hides behind Rating 3 + Sleaze 3
1446
+ // = 6 dice, and the line says so.
1447
+ // THIS IS WHY 1.80.1 WAS WRONG TO REMOVE THE BORROW. The first cut
1448
+ // handed cameras their master's FIREWALL and produced 16 dice at HR 8
1449
+ // against a sweep capped near 5 -- zero finds in twelve looks -- and
1450
+ // the fix was to cut the host out entirely. The borrow was never the
1451
+ // mistake; the attribute was. With Sleaze it is the same rule that
1452
+ // makes a corporate lens hard and a stray one easy, instead of a
1453
+ // balance knob pretending to be canon.
1454
+ // The icon still prints the LENS's own rating ([DR 1], p.356) while
1455
+ // the roll shows what the network lends it. Those are two different
1456
+ // facts on purpose, and there is a test pinning them apart so nobody
1457
+ // "fixes" one to match the other.
1458
+ export const ENGINE_VERSION = '1.84.0';
1422
1459
  //# sourceMappingURL=engine-version.js.map
@@ -92,6 +92,58 @@ export function parseMarkDeclaration(words) {
92
92
  export function deviceDefensePool(device) {
93
93
  return { label: 'Device Rating + Firewall', pool: Math.max(0, device.rating * 2) };
94
94
  }
95
+ /**
96
+ * WHAT A SILENT ICON ROLLS TO STAY HIDDEN -- and it is NOT a Firewall
97
+ * test (eJANPpm7qCaCAJBJw, fourth rejection: "Device Rating + Firewall
98
+ * on the Camera is 2d6? so, the firewall of the host is a 1?").
99
+ *
100
+ * The reporter was reading the label and doing the arithmetic, and the
101
+ * label was naming the wrong attribute. p.235-236 and p.241 both give
102
+ * the opposed roll as Computer + Intuition [Data Processing] v. LOGIC +
103
+ * SLEAZE. Firewall resists an intrusion (p.238/p.240); it has nothing
104
+ * to do with being seen. silent-icons.ts was reusing deviceDefensePool
105
+ * -- the intrusion pool -- for the hiding roll, so every hide test in
106
+ * the game rolled the wrong attribute and printed its name.
107
+ *
108
+ * AND A PLAIN DEVICE HAS NO SLEAZE AT ALL. p.226: "most devices have
109
+ * only two Matrix attributes: Data Processing and Firewall" -- Attack
110
+ * and Sleaze belong to decks and hosts. So a loose camera or maglock
111
+ * hides behind its Device Rating standing in for the Logic it has not
112
+ * got, and nothing more. One die for a p.356 camera, which is correct:
113
+ * a lens with no host behind it is not a hard thing to find.
114
+ */
115
+ export function deviceHidePool(device) {
116
+ return { label: 'Device Rating for Logic; p.226 gives it no Sleaze', pool: Math.max(0, device.rating) };
117
+ }
118
+ /**
119
+ * A SLAVED icon hiding, which is where the dice actually come from
120
+ * (reporter's ruling 2026-09-16, on p.233): "whenever a slaved device is
121
+ * called on to make a defense test, it uses either its own or its
122
+ * master's rating for each rating in the test ... whichever is higher in
123
+ * each instance." Resisting a sweep IS the slave defending, so it
124
+ * borrows per rating: its own Rating against the host's Rating, and its
125
+ * own Sleaze -- which p.226 says is nothing -- against the host's, which
126
+ * p.247 gives as a real number off the Host Rating array.
127
+ *
128
+ * That is what makes a corporate lens hard to find and a loose one easy,
129
+ * out of one rule rather than a balance knob. 1.80.1 removed the host's
130
+ * contribution entirely because the first version borrowed the host's
131
+ * FIREWALL and produced 16 dice against a sweep capped near 5 (zero
132
+ * finds in twelve looks). The borrow was never the mistake; the
133
+ * attribute was.
134
+ *
135
+ * Only from the grid: inside the host you are directly connected and a
136
+ * slave "cannot use its master's ratings" (p.233), which is
137
+ * deviceHidePool.
138
+ */
139
+ export function wanHidePool(device, host) {
140
+ const rating = Math.max(device.rating, host.rating);
141
+ // The device's own Sleaze is 0 (p.226), so the master's always wins
142
+ // this half -- written as a max anyway, because the rule is per
143
+ // rating and a future device WITH a Sleaze must not lose it.
144
+ const sleaze = Math.max(0, host.attributes().sleaze);
145
+ return { label: "Rating + Sleaze, the master's where higher, p.233", pool: Math.max(0, rating + sleaze) };
146
+ }
95
147
  /**
96
148
  * A SLAVE ATTACKED THROUGH ITS MASTER (SR5 p.233, PANs and WANs):
97
149
  * "Whenever a slaved device is called on to make a defense test, it
@@ -1,12 +1,17 @@
1
1
  import { rollPool, formatRoll } from './dice.js';
2
- import { deviceDefensePool } from './matrix-intrusion.js';
3
- import { isWatched } from './surveillance.js';
4
- function hiddenDevice(device) {
2
+ import { deviceHidePool, wanHidePool } from './matrix-intrusion.js';
3
+ import { isWatched, cameraMasterHost } from './surveillance.js';
4
+ function hiddenDevice(device, master) {
5
+ // p.235-236: the hider rolls LOGIC + SLEAZE. Not Firewall -- that
6
+ // resists an intrusion (p.238/p.240) and has nothing to do with being
7
+ // seen. This used to call deviceDefensePool, the INTRUSION pool, so
8
+ // the hide test rolled the wrong attribute and then printed its name
9
+ // (eJANPpm7qCaCAJBJw: "Device Rating + Firewall on the Camera is 2d6?
10
+ // so, the firewall of the host is a 1?").
11
+ const hide = master ? wanHidePool(device, master) : deviceHidePool(device);
5
12
  return {
6
13
  name: device.name,
7
- // Its rating stands in for the Mental attribute it has not got
8
- // (p.237-238) -- the same pool it defends an intrusion with.
9
- hide: { pool: deviceDefensePool(device).pool, label: 'Device Rating (no Logic to hide behind)' },
14
+ hide: { pool: hide.pool, label: hide.label },
10
15
  reveal: (viewer) => { device.spottedBy.add(viewer); },
11
16
  };
12
17
  }
@@ -28,20 +33,22 @@ function hiddenPersona(actor) {
28
33
  * body does.
29
34
  */
30
35
  export function hiddenIconsIn(scene, room, viewer) {
36
+ // A device in a room with a host is slaved to it (p.233), and from
37
+ // the grid it borrows the master's ratings. Inside the host you are
38
+ // directly connected and it stands alone -- deviceHidePool.
39
+ const deviceMaster = viewer.hostInside ? undefined : room.host;
31
40
  const out = room.devices
32
41
  .filter(d => d.isHiddenFrom(viewer.name))
33
- .map(hiddenDevice);
42
+ .map(d => hiddenDevice(d, deviceMaster));
34
43
  // THE CAMERAS, which p.217 names FIRST. They are not a Device -- the
35
44
  // cluster is derived from the room (surveillance.ts isWatched) -- so
36
45
  // the rating rides the ROOM (Room.cameraRating), seeded per site.
37
46
  //
38
- // THE FIRST CUT GAVE THEM THEIR MASTER HOST'S RATING and that was
39
- // wrong twice over. Canon first: p.233's slaving rule is about what a
40
- // slave DEFENDS AN ATTACK with, and hiding is not an attack being
41
- // defended. Then arithmetic: a rating-8 host gave its lenses 16 dice
42
- // against a sweep the Data Processing limit caps near 5, so a camera
43
- // on any corporate host was not hard to find, it was impossible --
44
- // measured at zero finds in twelve looks.
47
+ // THE FIRST CUT GAVE THEM THEIR MASTER HOST'S FIREWALL, and the
48
+ // borrow was right while the attribute was wrong -- which took three
49
+ // versions to see. A rating-8 host gave its lenses 16 dice against a
50
+ // sweep the Data Processing limit caps near 5: not hard to find,
51
+ // impossible, measured at zero finds in twelve looks.
45
52
  //
46
53
  // THE SECOND CUT hard-coded a 4 in here, which fixed the arithmetic
47
54
  // and left the engine holding THREE answers to one question: this
@@ -51,9 +58,23 @@ export function hiddenIconsIn(scene, room, viewer) {
51
58
  // printed. One seeded number feeds all three now, so what the player
52
59
  // reads is what it rolls (reporter's ruling: per-site, not global).
53
60
  if (isWatched(room) && !room.camerasSpottedBy.has(viewer.name)) {
61
+ // THE THIRD CUT, and the reporter found it by doing the arithmetic
62
+ // out loud: "Device Rating + Firewall on the Camera is 2d6? so, the
63
+ // firewall of the host is a 1?" No -- Firewall was never the right
64
+ // attribute. p.235-236 hides behind Logic + Sleaze, p.226 gives a
65
+ // plain device no Sleaze at all, and p.233 lets a slave borrow its
66
+ // master's rating for each rating in the test. So a lens on a host
67
+ // hides behind the HOST's Rating and Sleaze, and a loose one behind
68
+ // its own single die. That is why a corporate camera is hard and a
69
+ // stray one is easy -- one rule, not a balance knob (reporter's
70
+ // ruling 2026-09-16).
71
+ const masterRoom = scene ? cameraMasterHost(scene.getRooms(), room) : undefined;
72
+ const master = viewer.hostInside ? undefined : masterRoom?.host;
73
+ const lens = { rating: room.cameraRating };
74
+ const hide = master ? wanHidePool(lens, master) : deviceHidePool(lens);
54
75
  out.push({
55
76
  name: 'Camera cluster',
56
- hide: { pool: Math.max(0, room.cameraRating * 2), label: 'Device Rating + Firewall' },
77
+ hide: { pool: hide.pool, label: hide.label },
57
78
  reveal: (v) => { room.camerasSpottedBy.add(v); },
58
79
  });
59
80
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maka/maka-cli",
3
- "version": "5.231.0",
3
+ "version": "5.232.0",
4
4
  "type": "module",
5
5
  "summary": "A command line tool for scaffolding Meteor 3.x applications using either React.",
6
6
  "description": "A command line tool for scaffolding Meteor 3.x applications using React.",