@maka/maka-cli 5.64.0 → 5.65.1

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 (167) hide show
  1. package/bundle/typescript/package.json +4 -3
  2. package/bundle/typescript/src/commands/game/sideQuest/commands/backlog.d.ts +29 -2
  3. package/bundle/typescript/src/commands/game/sideQuest/commands/backlog.js +165 -1
  4. package/bundle/typescript/src/commands/game/sideQuest/commands/backlog.js.map +1 -1
  5. package/bundle/typescript/src/commands/game/sideQuest/commands/bluff.d.ts +40 -13
  6. package/bundle/typescript/src/commands/game/sideQuest/commands/bluff.js +107 -25
  7. package/bundle/typescript/src/commands/game/sideQuest/commands/bluff.js.map +1 -1
  8. package/bundle/typescript/src/commands/game/sideQuest/commands/breach.d.ts +11 -15
  9. package/bundle/typescript/src/commands/game/sideQuest/commands/breach.js +13 -16
  10. package/bundle/typescript/src/commands/game/sideQuest/commands/breach.js.map +1 -1
  11. package/bundle/typescript/src/commands/game/sideQuest/commands/buy.d.ts +8 -0
  12. package/bundle/typescript/src/commands/game/sideQuest/commands/buy.js +30 -2
  13. package/bundle/typescript/src/commands/game/sideQuest/commands/buy.js.map +1 -1
  14. package/bundle/typescript/src/commands/game/sideQuest/commands/bypass-command.d.ts +40 -49
  15. package/bundle/typescript/src/commands/game/sideQuest/commands/bypass-command.js +121 -160
  16. package/bundle/typescript/src/commands/game/sideQuest/commands/bypass-command.js.map +1 -1
  17. package/bundle/typescript/src/commands/game/sideQuest/commands/cast.d.ts +7 -8
  18. package/bundle/typescript/src/commands/game/sideQuest/commands/cast.js +13 -13
  19. package/bundle/typescript/src/commands/game/sideQuest/commands/cast.js.map +1 -1
  20. package/bundle/typescript/src/commands/game/sideQuest/commands/cover.d.ts +30 -0
  21. package/bundle/typescript/src/commands/game/sideQuest/commands/cover.js +65 -0
  22. package/bundle/typescript/src/commands/game/sideQuest/commands/cover.js.map +1 -0
  23. package/bundle/typescript/src/commands/game/sideQuest/commands/dispel.d.ts +1 -1
  24. package/bundle/typescript/src/commands/game/sideQuest/commands/dispel.js +2 -2
  25. package/bundle/typescript/src/commands/game/sideQuest/commands/dispel.js.map +1 -1
  26. package/bundle/typescript/src/commands/game/sideQuest/commands/go.js +25 -3
  27. package/bundle/typescript/src/commands/game/sideQuest/commands/go.js.map +1 -1
  28. package/bundle/typescript/src/commands/game/sideQuest/commands/hack.d.ts +2 -3
  29. package/bundle/typescript/src/commands/game/sideQuest/commands/hack.js +2 -2
  30. package/bundle/typescript/src/commands/game/sideQuest/commands/hack.js.map +1 -1
  31. package/bundle/typescript/src/commands/game/sideQuest/commands/job-offer.js +9 -1
  32. package/bundle/typescript/src/commands/game/sideQuest/commands/job-offer.js.map +1 -1
  33. package/bundle/typescript/src/commands/game/sideQuest/commands/jobs.js +2 -1
  34. package/bundle/typescript/src/commands/game/sideQuest/commands/jobs.js.map +1 -1
  35. package/bundle/typescript/src/commands/game/sideQuest/commands/look.js +58 -2
  36. package/bundle/typescript/src/commands/game/sideQuest/commands/look.js.map +1 -1
  37. package/bundle/typescript/src/commands/game/sideQuest/commands/map.js +18 -4
  38. package/bundle/typescript/src/commands/game/sideQuest/commands/map.js.map +1 -1
  39. package/bundle/typescript/src/commands/game/sideQuest/commands/move.js +1 -1
  40. package/bundle/typescript/src/commands/game/sideQuest/commands/pick.d.ts +2 -3
  41. package/bundle/typescript/src/commands/game/sideQuest/commands/pick.js +2 -2
  42. package/bundle/typescript/src/commands/game/sideQuest/commands/pick.js.map +1 -1
  43. package/bundle/typescript/src/commands/game/sideQuest/commands/posture.d.ts +45 -0
  44. package/bundle/typescript/src/commands/game/sideQuest/commands/posture.js +115 -0
  45. package/bundle/typescript/src/commands/game/sideQuest/commands/posture.js.map +1 -0
  46. package/bundle/typescript/src/commands/game/sideQuest/commands/say.d.ts +1 -0
  47. package/bundle/typescript/src/commands/game/sideQuest/commands/say.js +80 -9
  48. package/bundle/typescript/src/commands/game/sideQuest/commands/say.js.map +1 -1
  49. package/bundle/typescript/src/commands/game/sideQuest/commands/search.js +35 -24
  50. package/bundle/typescript/src/commands/game/sideQuest/commands/search.js.map +1 -1
  51. package/bundle/typescript/src/commands/game/sideQuest/commands/take.js +12 -0
  52. package/bundle/typescript/src/commands/game/sideQuest/commands/take.js.map +1 -1
  53. package/bundle/typescript/src/commands/game/sideQuest/commands/unlock.js +1 -1
  54. package/bundle/typescript/src/commands/game/sideQuest/commands/unlock.js.map +1 -1
  55. package/bundle/typescript/src/commands/game/sideQuest/commands/use.d.ts +17 -0
  56. package/bundle/typescript/src/commands/game/sideQuest/commands/use.js +158 -1
  57. package/bundle/typescript/src/commands/game/sideQuest/commands/use.js.map +1 -1
  58. package/bundle/typescript/src/commands/game/sideQuest/engine-version.d.ts +1 -1
  59. package/bundle/typescript/src/commands/game/sideQuest/engine-version.js +15 -1
  60. package/bundle/typescript/src/commands/game/sideQuest/engine-version.js.map +1 -1
  61. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-chunks.d.ts +82 -43
  62. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-chunks.js +419 -263
  63. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-chunks.js.map +1 -1
  64. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-factory.js +82 -84
  65. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-factory.js.map +1 -1
  66. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-seed-generator.d.ts +23 -4
  67. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-seed-generator.js +95 -79
  68. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-seed-generator.js.map +1 -1
  69. package/bundle/typescript/src/commands/game/sideQuest/factories/seed-migration.d.ts +30 -0
  70. package/bundle/typescript/src/commands/game/sideQuest/factories/seed-migration.js +176 -0
  71. package/bundle/typescript/src/commands/game/sideQuest/factories/seed-migration.js.map +1 -0
  72. package/bundle/typescript/src/commands/game/sideQuest/game.d.ts +10 -0
  73. package/bundle/typescript/src/commands/game/sideQuest/game.js +21 -5
  74. package/bundle/typescript/src/commands/game/sideQuest/game.js.map +1 -1
  75. package/bundle/typescript/src/commands/game/sideQuest/headless.js +24 -0
  76. package/bundle/typescript/src/commands/game/sideQuest/headless.js.map +1 -1
  77. package/bundle/typescript/src/commands/game/sideQuest/models/_index.d.ts +0 -1
  78. package/bundle/typescript/src/commands/game/sideQuest/models/_index.js +0 -1
  79. package/bundle/typescript/src/commands/game/sideQuest/models/_index.js.map +1 -1
  80. package/bundle/typescript/src/commands/game/sideQuest/models/device.d.ts +121 -0
  81. package/bundle/typescript/src/commands/game/sideQuest/models/device.js +145 -0
  82. package/bundle/typescript/src/commands/game/sideQuest/models/device.js.map +1 -0
  83. package/bundle/typescript/src/commands/game/sideQuest/models/door.d.ts +27 -8
  84. package/bundle/typescript/src/commands/game/sideQuest/models/door.js +15 -10
  85. package/bundle/typescript/src/commands/game/sideQuest/models/door.js.map +1 -1
  86. package/bundle/typescript/src/commands/game/sideQuest/models/npc.d.ts +81 -0
  87. package/bundle/typescript/src/commands/game/sideQuest/models/npc.js +138 -21
  88. package/bundle/typescript/src/commands/game/sideQuest/models/npc.js.map +1 -1
  89. package/bundle/typescript/src/commands/game/sideQuest/models/player.d.ts +68 -0
  90. package/bundle/typescript/src/commands/game/sideQuest/models/player.js +102 -0
  91. package/bundle/typescript/src/commands/game/sideQuest/models/player.js.map +1 -1
  92. package/bundle/typescript/src/commands/game/sideQuest/models/room.d.ts +72 -43
  93. package/bundle/typescript/src/commands/game/sideQuest/models/room.js +82 -59
  94. package/bundle/typescript/src/commands/game/sideQuest/models/room.js.map +1 -1
  95. package/bundle/typescript/src/commands/game/sideQuest/models/scene.d.ts +1 -4
  96. package/bundle/typescript/src/commands/game/sideQuest/models/scene.js +0 -22
  97. package/bundle/typescript/src/commands/game/sideQuest/models/scene.js.map +1 -1
  98. package/bundle/typescript/src/commands/game/sideQuest/scenes/scene1.json +78 -109
  99. package/bundle/typescript/src/commands/game/sideQuest/scenes/scene2.json +4 -101
  100. package/bundle/typescript/src/commands/game/sideQuest/types/save-file.d.ts +24 -0
  101. package/bundle/typescript/src/commands/game/sideQuest/types/seed/device-seed.d.ts +52 -0
  102. package/bundle/typescript/src/commands/game/sideQuest/types/seed/device-seed.js +2 -0
  103. package/bundle/typescript/src/commands/game/sideQuest/types/seed/device-seed.js.map +1 -0
  104. package/bundle/typescript/src/commands/game/sideQuest/types/seed/legacy-puzzle-seed.d.ts +63 -0
  105. package/bundle/typescript/src/commands/game/sideQuest/types/seed/legacy-puzzle-seed.js +2 -0
  106. package/bundle/typescript/src/commands/game/sideQuest/types/seed/legacy-puzzle-seed.js.map +1 -0
  107. package/bundle/typescript/src/commands/game/sideQuest/types/seed/player-seed.d.ts +6 -0
  108. package/bundle/typescript/src/commands/game/sideQuest/types/seed/room-seed.d.ts +4 -0
  109. package/bundle/typescript/src/commands/game/sideQuest/types/seed/scene-seed.d.ts +3 -2
  110. package/bundle/typescript/src/commands/game/sideQuest/types/shared/abstracts.d.ts +2 -28
  111. package/bundle/typescript/src/commands/game/sideQuest/types/shared/abstracts.js +1 -14
  112. package/bundle/typescript/src/commands/game/sideQuest/types/shared/abstracts.js.map +1 -1
  113. package/bundle/typescript/src/commands/game/sideQuest/ui.js +24 -0
  114. package/bundle/typescript/src/commands/game/sideQuest/ui.js.map +1 -1
  115. package/bundle/typescript/src/commands/game/sideQuest/utilities/affordances.d.ts +55 -0
  116. package/bundle/typescript/src/commands/game/sideQuest/utilities/affordances.js +36 -0
  117. package/bundle/typescript/src/commands/game/sideQuest/utilities/affordances.js.map +1 -0
  118. package/bundle/typescript/src/commands/game/sideQuest/utilities/backlog-review.d.ts +65 -0
  119. package/bundle/typescript/src/commands/game/sideQuest/utilities/backlog-review.js +137 -0
  120. package/bundle/typescript/src/commands/game/sideQuest/utilities/backlog-review.js.map +1 -0
  121. package/bundle/typescript/src/commands/game/sideQuest/utilities/backlog.d.ts +90 -1
  122. package/bundle/typescript/src/commands/game/sideQuest/utilities/backlog.js +103 -1
  123. package/bundle/typescript/src/commands/game/sideQuest/utilities/backlog.js.map +1 -1
  124. package/bundle/typescript/src/commands/game/sideQuest/utilities/combat-exchange.js +37 -4
  125. package/bundle/typescript/src/commands/game/sideQuest/utilities/combat-exchange.js.map +1 -1
  126. package/bundle/typescript/src/commands/game/sideQuest/utilities/credentials.d.ts +26 -0
  127. package/bundle/typescript/src/commands/game/sideQuest/utilities/credentials.js +48 -0
  128. package/bundle/typescript/src/commands/game/sideQuest/utilities/credentials.js.map +1 -0
  129. package/bundle/typescript/src/commands/game/sideQuest/utilities/devices.d.ts +23 -0
  130. package/bundle/typescript/src/commands/game/sideQuest/utilities/devices.js +55 -0
  131. package/bundle/typescript/src/commands/game/sideQuest/utilities/devices.js.map +1 -0
  132. package/bundle/typescript/src/commands/game/sideQuest/utilities/fuzzy-match.d.ts +57 -2
  133. package/bundle/typescript/src/commands/game/sideQuest/utilities/fuzzy-match.js +46 -3
  134. package/bundle/typescript/src/commands/game/sideQuest/utilities/fuzzy-match.js.map +1 -1
  135. package/bundle/typescript/src/commands/game/sideQuest/utilities/log-style.d.ts +16 -0
  136. package/bundle/typescript/src/commands/game/sideQuest/utilities/log-style.js +18 -0
  137. package/bundle/typescript/src/commands/game/sideQuest/utilities/log-style.js.map +1 -1
  138. package/bundle/typescript/src/commands/game/sideQuest/utilities/persistence.js +68 -38
  139. package/bundle/typescript/src/commands/game/sideQuest/utilities/persistence.js.map +1 -1
  140. package/bundle/typescript/src/commands/game/sideQuest/utilities/room-grid.d.ts +3 -3
  141. package/bundle/typescript/src/commands/game/sideQuest/utilities/room-grid.js +3 -3
  142. package/bundle/typescript/src/commands/game/sideQuest/utilities/spots.d.ts +36 -12
  143. package/bundle/typescript/src/commands/game/sideQuest/utilities/spots.js +101 -27
  144. package/bundle/typescript/src/commands/game/sideQuest/utilities/spots.js.map +1 -1
  145. package/bundle/typescript/src/commands/game/sideQuest/utilities/use-target.d.ts +106 -0
  146. package/bundle/typescript/src/commands/game/sideQuest/utilities/use-target.js +184 -0
  147. package/bundle/typescript/src/commands/game/sideQuest/utilities/use-target.js.map +1 -0
  148. package/bundle/typescript/src/commands/game/sideQuest.sub.cmd.js +54 -5
  149. package/bundle/typescript/src/commands/game/sideQuest.sub.cmd.js.map +1 -1
  150. package/bundle/typescript/src/maka.js +1 -1
  151. package/bundle/typescript/src/maka.js.map +1 -1
  152. package/bundle/typescript/src/tools/sdk-loader/relocate.d.ts +19 -2
  153. package/bundle/typescript/src/tools/sdk-loader/relocate.js +22 -2
  154. package/bundle/typescript/src/tools/sdk-loader/relocate.js.map +1 -1
  155. package/package.json +4 -3
  156. package/bundle/typescript/src/commands/game/sideQuest/commands/solve.d.ts +0 -6
  157. package/bundle/typescript/src/commands/game/sideQuest/commands/solve.js +0 -72
  158. package/bundle/typescript/src/commands/game/sideQuest/commands/solve.js.map +0 -1
  159. package/bundle/typescript/src/commands/game/sideQuest/factories/puzzle-factory.d.ts +0 -6
  160. package/bundle/typescript/src/commands/game/sideQuest/factories/puzzle-factory.js +0 -26
  161. package/bundle/typescript/src/commands/game/sideQuest/factories/puzzle-factory.js.map +0 -1
  162. package/bundle/typescript/src/commands/game/sideQuest/models/puzzle.d.ts +0 -152
  163. package/bundle/typescript/src/commands/game/sideQuest/models/puzzle.js +0 -393
  164. package/bundle/typescript/src/commands/game/sideQuest/models/puzzle.js.map +0 -1
  165. package/bundle/typescript/src/commands/game/sideQuest/types/seed/puzzle-seed.d.ts +0 -28
  166. package/bundle/typescript/src/commands/game/sideQuest/types/seed/puzzle-seed.js +0 -2
  167. package/bundle/typescript/src/commands/game/sideQuest/types/seed/puzzle-seed.js.map +0 -1
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maka/maka-cli",
3
- "version": "5.64.0",
3
+ "version": "5.65.1",
4
4
  "type": "module",
5
5
  "summary": "A command line tool for scaffolding Meteor 2.x applications using either React.",
6
6
  "description": "A command line tool for scaffolding Meteor 2.x applications using React.",
@@ -25,14 +25,15 @@
25
25
  "maka": "./typescript/src/maka.js",
26
26
  "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
27
27
  "develop": "npm run build && concurrently \"tsc -w\" \"wait-on -d 10 ./src/exec.js && chmod +x ./src/exec.js\"",
28
- "build": "node scripts/check-deps.mjs && rm -rf ./bundle && tsc && npm run prep && node scripts/check-command-registry.mjs",
28
+ "build": "node scripts/check-deps.mjs && rm -rf ./bundle && tsc && npm run typecheck && npm run prep && node scripts/check-command-registry.mjs",
29
29
  "check:deps": "node scripts/check-deps.mjs",
30
30
  "prep": " cp -R ./src/templates ./bundle/typescript/src/ && cp ./README.md ./bundle/typescript/src/ && cp ./package.json ./bundle/typescript/ && cp ./src/commands/game/sideQuest/scenes/*.json ./bundle/typescript/src/commands/game/sideQuest/scenes/ && chmod +x ./bundle/typescript/src/exec.js",
31
31
  "build:bundle": "node scripts/build-bundle.mjs",
32
32
  "test:parity": "node scripts/check-bundle-parity.mjs",
33
33
  "package": "npm run build:bundle && node scripts/package.mjs",
34
34
  "distribute": "node scripts/distribute.mjs",
35
- "prepare": "husky install"
35
+ "prepare": "husky install",
36
+ "typecheck": "tsc -p tsconfig.typecheck.json"
36
37
  },
37
38
  "author": "Maka <maka@maka-cli.com>",
38
39
  "license": "MIT",
@@ -22,12 +22,39 @@ import { Command, ICommand } from './command.js';
22
22
  export declare class BacklogCommand extends Command implements ICommand {
23
23
  protected static verb: string;
24
24
  protected static description: string;
25
- protected static subcommands: {
25
+ protected static subcommands: ({
26
26
  usage: string;
27
27
  short: string;
28
- }[];
28
+ long?: undefined;
29
+ } | {
30
+ usage: string;
31
+ short: string;
32
+ long: string;
33
+ })[];
29
34
  execute(args?: string[]): Promise<string>;
30
35
  private file;
36
+ /**
37
+ * THE REVIEW QUEUE (user request 2026-08-27). Bare "backlog review"
38
+ * lists what is waiting on you, numbered 1..n; "backlog review <n>
39
+ * <your words>" answers item n.
40
+ *
41
+ * The numbering is the delicate part and utilities/backlog-review.ts
42
+ * holds the invariant: a number resolves against the snapshot the
43
+ * player was SHOWN, never against a fresh fetch. Answering does not
44
+ * renumber the list on screen either.
45
+ */
46
+ private review;
47
+ /** Fetch and render the queue, adopting its order as the numbering. */
48
+ private showQueue;
49
+ /**
50
+ * One queue row. The two kinds of request READ DIFFERENTLY on purpose
51
+ * even though the server models both as a question awaiting the
52
+ * reporter: "somebody asked about your report" and "this is fixed,
53
+ * can you confirm it in play" are different jobs, and a play-tester
54
+ * skimming a list should be able to tell at a glance which one they
55
+ * are being asked for.
56
+ */
57
+ private renderItem;
31
58
  private list;
32
59
  /** One item: what it says, where it stands, and what the books said. */
33
60
  private row;
@@ -1,8 +1,16 @@
1
1
  import { Command } from './command.js';
2
2
  import { hint } from '../utilities/hints.js';
3
3
  import { authToken } from '../utilities/cloud-saves.js';
4
- import { submitBacklog, fetchBacklog, BACKLOG_STATUSES, } from '../utilities/backlog.js';
4
+ import { submitBacklog, fetchBacklog, postBacklogComment, postBacklogSignoff, BACKLOG_STATUSES, } from '../utilities/backlog.js';
5
5
  import { noteBacklogOutcome } from '../utilities/playtester.js';
6
+ import { takeIndex } from '../utilities/fuzzy-match.js';
7
+ import { optionMark } from '../utilities/log-style.js';
8
+ import { loadReviewQueue, reviewSnapshot, reviewItemAt, isValidationRow, markReviewAnswered, isReviewAnswered, } from '../utilities/backlog-review.js';
9
+ /** One line of somebody's report, kept to a skimmable width. */
10
+ function truncate(text, max) {
11
+ const flat = text.replace(/\s+/g, ' ').trim();
12
+ return flat.length <= max ? flat : `${flat.slice(0, max - 1)}…`;
13
+ }
6
14
  /**
7
15
  * THE BACKLOG -- play-tester feedback that somebody acts on.
8
16
  *
@@ -30,6 +38,16 @@ export class BacklogCommand extends Command {
30
38
  { usage: 'backlog <text>', short: 'File an item against this session.' },
31
39
  { usage: 'backlog list', short: 'The whole board, newest first.' },
32
40
  { usage: 'backlog list <status>', short: `Filter: ${BACKLOG_STATUSES.join(', ')}.` },
41
+ {
42
+ usage: 'backlog review',
43
+ short: 'What the board is waiting on YOU for, numbered.',
44
+ long: 'Items where somebody has asked you something and is waiting: a clarifying question about a report you filed, or a fix that has landed and wants confirming in play. Listed 1..n, oldest first.',
45
+ },
46
+ {
47
+ usage: 'backlog review <n> <your answer>',
48
+ short: 'Answer item n from that list.',
49
+ long: 'Posts your words against item n as an answer. The numbers come from the list you were last shown and do not move when you answer one, so you can work down them. "backlog review <n>" on its own reads that item in full without replying.',
50
+ },
33
51
  ];
34
52
  async execute(args = []) {
35
53
  // An anonymous runner's data never leaves the machine -- the same
@@ -45,6 +63,13 @@ export class BacklogCommand extends Command {
45
63
  if (args.length === 0 || sub === 'list') {
46
64
  return this.list(args[1]);
47
65
  }
66
+ // "review" is checked BEFORE the fall-through to file(), or
67
+ // answering an item would file a new one whose text begins with the
68
+ // word "review" -- the same shape as `backlog open` filing an item
69
+ // called "open", which is why list has subcommands at all.
70
+ if (sub === 'review') {
71
+ return this.review(args.slice(1));
72
+ }
48
73
  return this.file(args.join(' '));
49
74
  }
50
75
  async file(text) {
@@ -78,6 +103,141 @@ export class BacklogCommand extends Command {
78
103
  return this.explain(outcome);
79
104
  return `{cyan-fg}Filed. It'll come back checked against the books.${hint(` ("backlog list" reads the board.)`)}{/cyan-fg}`;
80
105
  }
106
+ /**
107
+ * THE REVIEW QUEUE (user request 2026-08-27). Bare "backlog review"
108
+ * lists what is waiting on you, numbered 1..n; "backlog review <n>
109
+ * <your words>" answers item n.
110
+ *
111
+ * The numbering is the delicate part and utilities/backlog-review.ts
112
+ * holds the invariant: a number resolves against the snapshot the
113
+ * player was SHOWN, never against a fresh fetch. Answering does not
114
+ * renumber the list on screen either.
115
+ */
116
+ async review(args) {
117
+ // Bare "backlog review": fetch, render, and adopt that order as the
118
+ // numbering. This is the ONLY place the queue is refetched.
119
+ if (args.length === 0)
120
+ return this.showQueue();
121
+ const queue = reviewSnapshot();
122
+ if (queue.length === 0) {
123
+ // No snapshot means no list they could have counted on, so we do
124
+ // not fetch one and hope the numbering matches what they had in
125
+ // mind. Show it and let them pick against what they can see.
126
+ const shown = await this.showQueue();
127
+ return `${shown}\n\n{white-fg}(Pick from the list above -- "backlog review <n> <your answer>".){/white-fg}`;
128
+ }
129
+ // ONE INDEX RESOLVER FOR THE WHOLE GAME (session-1's takeIndex).
130
+ // It is pure and holds no order, which it says out loud -- so the
131
+ // guarantee that this list has not reshuffled since it was printed
132
+ // stays MINE, and lives in utilities/backlog-review.ts. What it
133
+ // buys here is that "review 9" against a 3-item list refuses in the
134
+ // same words as picking the ninth exit or the ninth item on a
135
+ // vendor's shelf, and the remainder comes back VERBATIM -- this
136
+ // trailing text is a human's answer, not arguments.
137
+ const picked = takeIndex(args.join(' '), queue.length);
138
+ if (!picked.ok)
139
+ return `${picked.message}${hint(` "backlog review" shows the list again.`)}`;
140
+ const item = reviewItemAt(picked.at + 1);
141
+ if (!item)
142
+ return `There's no item ${picked.at + 1}. The list runs 1-${queue.length}.`;
143
+ const n = picked.at + 1;
144
+ const text = picked.rest;
145
+ if (text.length === 0) {
146
+ // A number on its own reads the item back rather than posting an
147
+ // empty answer -- most of the time that IS what they wanted.
148
+ return this.renderItem(item, n, { full: true });
149
+ }
150
+ // TWO KINDS OF ROW, TWO DIFFERENT ACTS. A question wants an ANSWER;
151
+ // a validation request wants a SIGN-OFF, and only the second one can
152
+ // close anything.
153
+ //
154
+ // This branch did not exist until 2026-08-27 (item jyjMYRXrS4KojAL2W).
155
+ // Every row was answered with a comment, including the ones that
156
+ // said "CAN YOU CONFIRM THIS IN PLAY?" -- so confirming a fix posted
157
+ // a remark and changed nothing. The row stayed, because the queue
158
+ // holds an in-review item until it is SIGNED, and the board could
159
+ // never reach `complete`, because the server refuses that without a
160
+ // sign-off nobody was sending. The play-tester's confirmation was
161
+ // the one input in this flow that did nothing at all.
162
+ //
163
+ // The kind comes from the SERVER (IReviewItem.kind), with the same
164
+ // status fallback renderItem uses, so both halves agree about which
165
+ // row this is.
166
+ const validation = isValidationRow(item);
167
+ const outcome = validation
168
+ ? await postBacklogSignoff(item.id, text, this.actor?.name)
169
+ : await postBacklogComment(item.id, text, {
170
+ kind: 'answer',
171
+ // The question is answered, so it is no longer awaiting them.
172
+ awaitingReporter: false,
173
+ runnerName: this.actor?.name,
174
+ });
175
+ noteBacklogOutcome(outcome);
176
+ if (outcome !== 'ok')
177
+ return this.explain(outcome);
178
+ markReviewAnswered(item.id);
179
+ const left = reviewSnapshot().filter(i => !isReviewAnswered(i.id)).length;
180
+ return [
181
+ validation
182
+ ? `{green-fg}Signed off on item ${n}. That closes it -- your word is what the board was waiting for.{/green-fg}`
183
+ : `{cyan-fg}Answered item ${n}. It's on the record against your report.{/cyan-fg}`,
184
+ left > 0
185
+ ? `{white-fg}${left} still waiting on you -- the numbers above haven't moved.{/white-fg}`
186
+ : `{white-fg}That was the last one. Nothing else is waiting on you.{/white-fg}`,
187
+ ].join('\n');
188
+ }
189
+ /** Fetch and render the queue, adopting its order as the numbering. */
190
+ async showQueue() {
191
+ const { outcome, items } = await loadReviewQueue();
192
+ if (outcome !== 'ok' || !items)
193
+ return this.explain(outcome);
194
+ if (items.length === 0) {
195
+ return `Nothing's waiting on you. The board will say so when somebody needs a word back.`;
196
+ }
197
+ return [
198
+ `{cyan-fg}WAITING ON YOU{/cyan-fg}`,
199
+ ...items.map((it, i) => this.renderItem(it, i + 1, { full: false })),
200
+ hint(`"backlog review <n> <your answer>" replies. "backlog review <n>" alone reads one in full.`),
201
+ ].filter(Boolean).join('\n');
202
+ }
203
+ /**
204
+ * One queue row. The two kinds of request READ DIFFERENTLY on purpose
205
+ * even though the server models both as a question awaiting the
206
+ * reporter: "somebody asked about your report" and "this is fixed,
207
+ * can you confirm it in play" are different jobs, and a play-tester
208
+ * skimming a list should be able to tell at a glance which one they
209
+ * are being asked for.
210
+ */
211
+ renderItem(it, n, opts) {
212
+ // THE SERVER SAYS WHICH KIND; the status heuristic is only a
213
+ // fallback for a server too old to say. Deriving it from status was
214
+ // a client guess, and it guessed wrong for the obvious case: a
215
+ // clarifying question about an item that happens to be in-progress
216
+ // is still a question, not a request to go confirm a fix.
217
+ const validation = isValidationRow(it);
218
+ const lead = validation
219
+ ? `{cyan-fg}CAN YOU CONFIRM THIS IN PLAY?{/cyan-fg}`
220
+ : `{cyan-fg}QUESTION ABOUT YOUR REPORT{/cyan-fg}`;
221
+ const asker = it.asked?.by ? ` -- ${it.asked.by}` : '';
222
+ const yours = opts.full ? it.text : truncate(it.text, 72);
223
+ // optionMark, not a hand-rolled "1.", so this list sits in the same
224
+ // gutter as the exits, the shelves and the jobs board -- and so it
225
+ // keeps the promise that mark carries: a numbered row means the
226
+ // number works, and this one is resolved by the same takeIndex.
227
+ const rows = [`${optionMark(n)}${lead}${asker}`];
228
+ // `asked` is OPTIONAL on the wire. A review item should always have
229
+ // one -- and "should always" is how a render throws in front of a
230
+ // player. An item that arrives without one still lists, still
231
+ // numbers, and still takes an answer.
232
+ if (it.asked?.text)
233
+ rows.push(` {white-fg}"${it.asked.text}"{/white-fg}`);
234
+ rows.push(` {white-fg}your report: ${yours}{/white-fg}`);
235
+ // What changed, when the ask is to confirm that it did.
236
+ if (validation && it.commits?.length) {
237
+ rows.push(` {white-fg}fixed by: ${it.commits.slice(0, 3).join(', ')}{/white-fg}`);
238
+ }
239
+ return rows.join('\n');
240
+ }
81
241
  async list(statusArg) {
82
242
  let status;
83
243
  if (statusArg) {
@@ -135,6 +295,10 @@ export class BacklogCommand extends Command {
135
295
  switch (status) {
136
296
  case 'complete': return '{green-fg}[done]{/green-fg}';
137
297
  case 'in-progress': return '{cyan-fg}[wip] {/cyan-fg}';
298
+ // Fixed, and waiting on the reporter to confirm it in play. It
299
+ // used to fall through to the default and read "[open]" -- an
300
+ // item awaiting your word shown as one nobody had touched.
301
+ case 'in-review': return '{cyan-fg}[you?] {/cyan-fg}';
138
302
  case 'wont-fix': return '{cyan-fg}[no] {/cyan-fg}';
139
303
  default: return '{yellow-fg}[open]{/yellow-fg}';
140
304
  }
@@ -1 +1 @@
1
- {"version":3,"file":"backlog.js","sourceRoot":"","sources":["../../../../../../../src/commands/game/sideQuest/commands/backlog.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAY,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AACxD,OAAO,EACL,aAAa,EAAE,YAAY,EAAE,gBAAgB,GAE9C,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAEhE;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,OAAO,cAAe,SAAQ,OAAO;IAC/B,MAAM,CAAC,IAAI,GAAG,SAAS,CAAC;IACxB,MAAM,CAAC,WAAW,GAAG,2QAA2Q,CAAC;IAEjS,MAAM,CAAC,WAAW,GAAG;QAC7B,EAAE,KAAK,EAAE,gBAAgB,EAAE,KAAK,EAAE,oCAAoC,EAAE;QACxE,EAAE,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE,gCAAgC,EAAE;QAClE,EAAE,KAAK,EAAE,uBAAuB,EAAE,KAAK,EAAE,WAAW,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE;KACrF,CAAC;IAEF,KAAK,CAAC,OAAO,CAAC,OAAiB,EAAE;QAC/B,kEAAkE;QAClE,iEAAiE;QACjE,gEAAgE;QAChE,IAAI,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,CAAC;YAC3B,OAAO,kGAAkG,IAAI,CAAC,4CAA4C,CAAC,EAAE,CAAC;QAChK,CAAC;QACD,IAAI,CAAC,SAAS,EAAE,EAAE,CAAC;YACjB,OAAO,uEAAuE,IAAI,CAAC,2BAA2B,CAAC,EAAE,CAAC;QACpH,CAAC;QAED,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;QAC1C,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,GAAG,KAAK,MAAM,EAAE,CAAC;YACxC,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QAC5B,CAAC;QACD,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACnC,CAAC;IAEO,KAAK,CAAC,IAAI,CAAC,IAAY;QAC7B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QACvB,iEAAiE;QACjE,uEAAuE;QACvE,mEAAmE;QACnE,oEAAoE;QACpE,kEAAkE;QAClE,mEAAmE;QACnE,gDAAgD;QAChD,MAAM,EAAE,UAAU,EAAE,aAAa,EAAE,GAAG,MAAM,MAAM,CAAC,4BAA4B,CAAC,CAAC;QACjF,MAAM,OAAO,GAAG,MAAM,aAAa,CAAC,IAAI,EAAE;YACxC,UAAU,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI;YAC5B,gEAAgE;YAChE,6DAA6D;YAC7D,uCAAuC;YACvC,SAAS,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,SAAS;YAC9E,mEAAmE;YACnE,gEAAgE;YAChE,kEAAkE;YAClE,kEAAkE;YAClE,WAAW,EAAE,IAAI,EAAE,aAAa,EAAE,EAAE;YACpC,UAAU,EAAE,UAAU,EAAE;YACxB,aAAa,EAAE,aAAa,EAAE;YAC9B,QAAQ,EAAE,OAAO,CAAC,QAAQ;SAC3B,CAAC,CAAC;QACH,oEAAoE;QACpE,kBAAkB,CAAC,OAAO,CAAC,CAAC;QAC5B,IAAI,OAAO,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QACnD,OAAO,6DAA6D,IAAI,CAAC,oCAAoC,CAAC,YAAY,CAAC;IAC7H,CAAC;IAEO,KAAK,CAAC,IAAI,CAAC,SAAkB;QACnC,IAAI,MAAiC,CAAC;QACtC,IAAI,SAAS,EAAE,CAAC;YACd,MAAM,MAAM,GAAG,SAAS,CAAC,WAAW,EAAmB,CAAC;YACxD,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;gBACvC,OAAO,qBAAqB,SAAS,WAAW,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;YACjF,CAAC;YACD,MAAM,GAAG,MAAM,CAAC;QAClB,CAAC;QAED,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,MAAM,YAAY,CAAC,MAAM,CAAC,CAAC;QACtD,oEAAoE;QACpE,kBAAkB,CAAC,OAAO,CAAC,CAAC;QAC5B,IAAI,OAAO,KAAK,IAAI,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC7D,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,OAAO,MAAM;gBACX,CAAC,CAAC,WAAW,MAAM,gBAAgB;gBACnC,CAAC,CAAC,qBAAqB,IAAI,CAAC,sDAAsD,CAAC,EAAE,CAAC;QAC1F,CAAC;QAED,gEAAgE;QAChE,qEAAqE;QACrE,sDAAsD;QACtD,MAAM,KAAK,GAAG,CAAC,YAAY,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QAC1E,KAAK,MAAM,IAAI,IAAI,KAAK;YAAE,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;QACxD,MAAM,MAAM,GAAG,IAAI,CAAC,2EAA2E,CAAC,CAAC;QACjG,IAAI,MAAM;YAAE,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QACnC,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAED,wEAAwE;IAChE,GAAG,CAAC,IAAkB;QAC5B,MAAM,GAAG,GAAa,EAAE,CAAC;QACzB,GAAG,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QAC3D,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC/E,IAAI,GAAG;YAAE,GAAG,CAAC,IAAI,CAAC,SAAS,GAAG,EAAE,CAAC,CAAC;QAElC,MAAM,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC;QACvB,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;YAChC,GAAG,CAAC,IAAI,CAAC,gDAAgD,CAAC,CAAC;QAC7D,CAAC;aAAM,IAAI,CAAC,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;YAChC,GAAG,CAAC,IAAI,CAAC,sDAAsD,CAAC,CAAC;QACnE,CAAC;aAAM,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;YACrB,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;iBAC1C,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,IAAI,IAAI,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACvD,GAAG,CAAC,IAAI,CAAC,SAAS,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAC/F,IAAI,KAAK;gBAAE,GAAG,CAAC,IAAI,CAAC,kBAAkB,KAAK,YAAY,CAAC,CAAC;QAC3D,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IAEO,UAAU,CAAC,MAAqB;QACtC,QAAQ,MAAM,EAAE,CAAC;YACf,KAAK,UAAU,CAAC,CAAC,OAAO,6BAA6B,CAAC;YACtD,KAAK,aAAa,CAAC,CAAC,OAAO,2BAA2B,CAAC;YACvD,KAAK,UAAU,CAAC,CAAC,OAAO,2BAA2B,CAAC;YACpD,OAAO,CAAC,CAAC,OAAO,+BAA+B,CAAC;QAClD,CAAC;IACH,CAAC;IAED;;0DAEsD;IAC9C,UAAU,CAAC,OAAe;QAChC,QAAQ,OAAO,EAAE,CAAC;YAChB,KAAK,iBAAiB,CAAC,CAAC,OAAO,oCAAoC,CAAC;YACpE,KAAK,KAAK,CAAC,CAAC,OAAO,uBAAuB,CAAC;YAC3C,KAAK,aAAa,CAAC,CAAC,OAAO,wCAAwC,CAAC;YACpE,KAAK,mBAAmB,CAAC,CAAC,OAAO,wCAAwC,CAAC;YAC1E,OAAO,CAAC,CAAC,OAAO,kCAAkC,CAAC;QACrD,CAAC;IACH,CAAC;IAEO,OAAO,CAAC,OAAuB;QACrC,QAAQ,OAAO,EAAE,CAAC;YAChB,KAAK,UAAU,CAAC,CAAC,OAAO,sEAAsE,CAAC;YAC/F,KAAK,SAAS,CAAC,CAAC,OAAO,wEAAwE,CAAC;YAChG,KAAK,WAAW,CAAC,CAAC,OAAO,wEAAwE,CAAC;YAClG,KAAK,UAAU,CAAC,CAAC,OAAO,mEAAmE,CAAC;YAC5F,OAAO,CAAC,CAAC,OAAO,+DAA+D,IAAI,CAAC,0CAA0C,CAAC,EAAE,CAAC;QACpI,CAAC;IACH,CAAC"}
1
+ {"version":3,"file":"backlog.js","sourceRoot":"","sources":["../../../../../../../src/commands/game/sideQuest/commands/backlog.ts"],"names":[],"mappings":"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"}
@@ -1,18 +1,45 @@
1
- import { BypassCommand } from './bypass-command.js';
2
- import { PuzzleBypassType } from '../types/seed/puzzle-seed.js';
1
+ import { Command, ICommand } from './command.js';
3
2
  /**
4
- * The CON skill's barrier verb: fast-talk past a social gate -- a doorman,
5
- * an intercom challenge, a receptionist with a list (bypassType "con").
6
- * Same stakes as pick/hack/cast: a blown story burns the approach for
7
- * good -- they know exactly what you are now, and the district hears
8
- * about it. Rolled on Charisma + con.
3
+ * BLUFF IS SOCIAL, AND SOCIAL MEANS A PERSON (player ruling 2026-08-27:
4
+ * "'bluff' shouldn't work on Barriers like locks/wards/etc. Bluff is a
5
+ * social workaround. The real barrier, in this case, should be Coyote --
6
+ * NPCs can be barriers (can't move through them)").
7
+ *
8
+ * This used to be a BypassCommand: bluff was a bypassType on the barrier
9
+ * itself, so a keypad could be "really a doorman" and you talked the
10
+ * keypad round. That was a category error in two directions at once. A
11
+ * lock has no opinion to change, so there was nothing for the con to
12
+ * work on; and modelling the DOORMAN as a Puzzle to get the social route
13
+ * gave a human being a shortcut that could be BURNED, a coin flip in
14
+ * place of an opposed test, and a "BARRIER:" label in search.
15
+ *
16
+ * So bluff no longer touches barriers at all. What it does instead is
17
+ * the thing the ruling asked for: an OPPOSED SOCIAL TEST against a
18
+ * person who is holding a doorway. An obstructive NPC is one whose
19
+ * `guarding` flag makes utilities/spots.ts holdsGround() keep the way
20
+ * shut; winning clears it and they step aside.
21
+ *
22
+ * Charisma + con against their Willpower + Intuition -- the will not to
23
+ * be moved, and the read on you. Not a coin flip, because a coin flip is
24
+ * what you roll against a mechanism.
25
+ *
26
+ * Losing does NOT burn anything. They remember the story and won't hear
27
+ * it twice from you (conAttemptedBy), but they can still be leaned on,
28
+ * bought, or fought. That distinction is the ruling in miniature: a
29
+ * barrier gets burned, a person just doesn't buy it.
30
+ *
31
+ * Every refusal here NAMES THE ROUTE THAT WOULD WORK, per the standing
32
+ * rule from v5.63/v5.64 -- a bare denial in front of an obvious obstacle
33
+ * is what made a real session conclude the barrier was broken. Typing
34
+ * "bluff" at a maglock says the maglock was never the kind of thing you
35
+ * talk to, and points at what is.
9
36
  */
10
- export declare class BluffCommand extends BypassCommand {
37
+ export declare class BluffCommand extends Command implements ICommand {
11
38
  protected static verb: string;
12
39
  protected static description: string;
13
- protected bypassType: PuzzleBypassType;
14
- protected verbName: string;
15
- protected noPuzzleMessage: string;
16
- execute(args: string[]): Promise<string>;
17
- protected determineOutcome(): Promise<boolean>;
40
+ execute(args?: string[]): Promise<string>;
41
+ /** Is there a barrier in this room at all? Only used to word the
42
+ * refusal correctly -- a player who typed "bluff" at a lock deserves
43
+ * to be pointed at the verbs that do work on it. */
44
+ private roomHasBarrier;
18
45
  }
@@ -1,19 +1,46 @@
1
- import { BypassCommand } from './bypass-command.js';
1
+ import { Command } from './command.js';
2
+ import { hint } from '../utilities/hints.js';
3
+ import { sameSpot, spotOf } from '../utilities/spots.js';
2
4
  import { rollPool, formatRoll } from '../utilities/dice.js';
3
5
  /**
4
- * The CON skill's barrier verb: fast-talk past a social gate -- a doorman,
5
- * an intercom challenge, a receptionist with a list (bypassType "con").
6
- * Same stakes as pick/hack/cast: a blown story burns the approach for
7
- * good -- they know exactly what you are now, and the district hears
8
- * about it. Rolled on Charisma + con.
6
+ * BLUFF IS SOCIAL, AND SOCIAL MEANS A PERSON (player ruling 2026-08-27:
7
+ * "'bluff' shouldn't work on Barriers like locks/wards/etc. Bluff is a
8
+ * social workaround. The real barrier, in this case, should be Coyote --
9
+ * NPCs can be barriers (can't move through them)").
10
+ *
11
+ * This used to be a BypassCommand: bluff was a bypassType on the barrier
12
+ * itself, so a keypad could be "really a doorman" and you talked the
13
+ * keypad round. That was a category error in two directions at once. A
14
+ * lock has no opinion to change, so there was nothing for the con to
15
+ * work on; and modelling the DOORMAN as a Puzzle to get the social route
16
+ * gave a human being a shortcut that could be BURNED, a coin flip in
17
+ * place of an opposed test, and a "BARRIER:" label in search.
18
+ *
19
+ * So bluff no longer touches barriers at all. What it does instead is
20
+ * the thing the ruling asked for: an OPPOSED SOCIAL TEST against a
21
+ * person who is holding a doorway. An obstructive NPC is one whose
22
+ * `guarding` flag makes utilities/spots.ts holdsGround() keep the way
23
+ * shut; winning clears it and they step aside.
24
+ *
25
+ * Charisma + con against their Willpower + Intuition -- the will not to
26
+ * be moved, and the read on you. Not a coin flip, because a coin flip is
27
+ * what you roll against a mechanism.
28
+ *
29
+ * Losing does NOT burn anything. They remember the story and won't hear
30
+ * it twice from you (conAttemptedBy), but they can still be leaned on,
31
+ * bought, or fought. That distinction is the ruling in miniature: a
32
+ * barrier gets burned, a person just doesn't buy it.
33
+ *
34
+ * Every refusal here NAMES THE ROUTE THAT WOULD WORK, per the standing
35
+ * rule from v5.63/v5.64 -- a bare denial in front of an obvious obstacle
36
+ * is what made a real session conclude the barrier was broken. Typing
37
+ * "bluff" at a maglock says the maglock was never the kind of thing you
38
+ * talk to, and points at what is.
9
39
  */
10
- export class BluffCommand extends BypassCommand {
40
+ export class BluffCommand extends Command {
11
41
  static verb = 'bluff';
12
- static description = 'Talk your way past a social barrier (a doorman, a voice-gate) without the passphrase -- Charisma + con. Blow the story and they\'ll never buy another word (the real credential still works).';
13
- bypassType = 'con';
14
- verbName = 'bluff';
15
- noPuzzleMessage = `There's nobody here to bluff past.`;
16
- async execute(args) {
42
+ static description = 'Talk a PERSON round -- Charisma + con. Locks, wards and terminals have no opinion to change; work those with "pick", "hack" or "cast".';
43
+ async execute(args = []) {
17
44
  // Fast-talk needs a face and a voice on the scene -- your meat ones.
18
45
  if (this.actor.plane === 'matrix') {
19
46
  return `Your silver tongue is meat-side, slumped with the rest of you. "jack out" to run your game in person.`;
@@ -24,25 +51,80 @@ export class BluffCommand extends BypassCommand {
24
51
  if (this.actor.plane === 'drone') {
25
52
  return `Nobody buys a story from a hovering drone with a tinny speaker -- a con needs a FACE. "jump" back to yours.`;
26
53
  }
54
+ const room = this.actor.currentLocation;
55
+ const here = room
56
+ ? this.scene.getActorsInRoom(room).filter((a) => a !== this.actor && !a.isIncapacitated())
57
+ : [];
58
+ // NAME THE PEOPLE, because "bluff" is now a question about people and
59
+ // the honest answer to "who?" is a list of who is actually here.
60
+ if (here.length === 0) {
61
+ return `There's nobody here to work. A con needs an audience -- ${room && this.roomHasBarrier() ? `and whatever's blocking you isn't one. It's a THING: work it with "pick", "hack" or "cast", or find the answer and "solve" it.` : `find someone who can say yes.`}`;
62
+ }
63
+ const named = args.join(' ').trim();
64
+ const target = named
65
+ ? here.find(a => a.name.toLowerCase().includes(named.toLowerCase()))
66
+ : undefined;
67
+ if (named && !target) {
68
+ return `You don't see anyone called "${named}" to work. Here: ${here.map(a => a.name).join(', ')}.`;
69
+ }
27
70
  // Working a mark out loud is the opposite of sneaking (see sneak.ts).
28
71
  this.actor.sneaking = false;
29
- return super.execute(args);
30
- }
31
- async determineOutcome() {
32
- // A CON test (Charisma + con, 2 hits; unskilled Charisma - 1) -- the
33
- // Face's answer to the decker's terminal and the ops specialist's
34
- // maglock.
72
+ const who = target ?? here[0];
73
+ if (!sameSpot(this.actor, who)) {
74
+ const spot = spotOf(who);
75
+ return `A story needs to be told close. ${who.name} is at the ${spot ?? 'other side of the room'} -- "move to ${spot ?? who.name}" first.`;
76
+ }
77
+ if (!who.guarding) {
78
+ return [
79
+ `You size ${who.name} up for a story -- but they aren't in your way. A con needs something to BE talked past.`,
80
+ hint(` (Say your piece with "talk-to ${who.name} <what you tell them>", or lean on them with "persuade"/"intimidate".)`),
81
+ ].join('');
82
+ }
83
+ // A MARK REMEMBERS. Not a burned route -- the distinction is the
84
+ // whole ruling. They didn't buy your story; they can still be leaned
85
+ // on, bought, or fought.
86
+ if (who.conAttemptedBy.has(this.actor.name)) {
87
+ return `${who.name} has heard your story once already, and they didn't buy it the first time. Try something that isn't talk.`;
88
+ }
89
+ who.conAttemptedBy.add(this.actor.name);
90
+ // OPPOSED, against the PERSON -- not a coin flip against a lock.
91
+ // Their Willpower + Intuition is what sees through a story: the will
92
+ // not to be moved, and the read on you.
35
93
  const skill = this.actor.skillRating('con');
36
- // A drink beforehand loosens the delivery (see use.ts liquid courage).
37
- // Public Awareness cuts AGAINST a con -- a face people can place is
38
- // a face that can't claim to be someone else (capped at -2).
94
+ // Public Awareness cuts AGAINST a con: a face people can place is a
95
+ // face that can't claim to be someone else (capped at -2).
39
96
  const fame = Math.min(2, this.actor.publicAwareness);
40
- const pool = Math.max(1, this.actor.charisma + (skill > 0 ? skill : -1) + this.actor.consumeLiquidCourage() + this.actor.consumeFirstImpression() + this.actor.augBonus('social') - fame + this.actor.woundModifier - this.actor.sustainingPenalty);
41
- const roll = rollPool(pool);
97
+ const pool = Math.max(1, this.actor.charisma + (skill > 0 ? skill : -1)
98
+ + this.actor.consumeLiquidCourage() + this.actor.consumeFirstImpression()
99
+ + this.actor.augBonus('social') - fame
100
+ + this.actor.woundModifier - this.actor.sustainingPenalty);
101
+ const theirs = Math.max(1, who.willpower + who.intuition);
102
+ const mine = rollPool(pool);
103
+ const resist = rollPool(theirs);
42
104
  if (this.scene.isHumanControlled(this.actor)) {
43
- this.logger.meta(`Con: ${formatRoll(roll)} (2+ to sell it)`);
105
+ this.logger.meta(`Con: ${formatRoll(mine)}`);
106
+ this.logger.meta(` ${who.name} sees through it: ${formatRoll(resist)}`);
44
107
  }
45
- return roll.hits >= 2;
108
+ this.actor.performAction('spins a story for', who.name);
109
+ this.logger.write(`Bluff: ${this.actor.name} vs ${who.name} -- ${mine.hits} vs ${resist.hits} (guarding).`);
110
+ if (mine.hits <= resist.hits) {
111
+ void Promise.resolve(who.hear(this.actor, `[System note: ${this.actor.name} just tried to talk their way past you and you DIDN'T buy it. You are still holding this doorway. Say so, in your own voice, in one short line.]`)).catch(() => undefined);
112
+ return `${who.name} lets you finish, then doesn't move. "Nice try."`;
113
+ }
114
+ // They stand down -- for everyone, deliberately (see Player.guarding).
115
+ who.guarding = false;
116
+ void Promise.resolve(who.hear(this.actor, `[System note: ${this.actor.name} just talked you round and you're STEPPING ASIDE -- you bought the story. One short line as you move, in your own voice. Don't re-block the way.]`)).catch(() => undefined);
117
+ return [
118
+ `${who.name} weighs it, decides you're somebody's problem but not theirs, and steps out of the way.`,
119
+ hint(` (The way is open while they stay stood down.)`),
120
+ ].join('');
121
+ }
122
+ /** Is there a barrier in this room at all? Only used to word the
123
+ * refusal correctly -- a player who typed "bluff" at a lock deserves
124
+ * to be pointed at the verbs that do work on it. */
125
+ roomHasBarrier() {
126
+ const room = this.actor.currentLocation;
127
+ return (room?.openableDevices?.() ?? []).length > 0;
46
128
  }
47
129
  }
48
130
  //# sourceMappingURL=bluff.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"bluff.js","sourceRoot":"","sources":["../../../../../../../src/commands/game/sideQuest/commands/bluff.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAE5D;;;;;;GAMG;AACH,MAAM,OAAO,YAAa,SAAQ,aAAa;IACnC,MAAM,CAAC,IAAI,GAAG,OAAO,CAAC;IACtB,MAAM,CAAC,WAAW,GAAG,+LAA+L,CAAC;IACrN,UAAU,GAAqB,KAAK,CAAC;IACrC,QAAQ,GAAG,OAAO,CAAC;IACnB,eAAe,GAAG,oCAAoC,CAAC;IAEjE,KAAK,CAAC,OAAO,CAAC,IAAc;QAC1B,qEAAqE;QACrE,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;YAClC,OAAO,uGAAuG,CAAC;QACjH,CAAC;QACD,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;YAClC,OAAO,qFAAqF,CAAC;QAC/F,CAAC;QACD,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,KAAK,OAAO,EAAE,CAAC;YACjC,OAAO,6GAA6G,CAAC;QACvH,CAAC;QACD,sEAAsE;QACtE,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,KAAK,CAAC;QAC5B,OAAO,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC;IAES,KAAK,CAAC,gBAAgB;QAC9B,qEAAqE;QACrE,kEAAkE;QAClE,WAAW;QACX,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;QAC5C,uEAAuE;QACvE,oEAAoE;QACpE,6DAA6D;QAC7D,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;QACrD,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,oBAAoB,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,sBAAsB,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa,GAAG,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;QACpP,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC5B,IAAI,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YAC7C,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,UAAU,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;QAC/D,CAAC;QACD,OAAO,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC;IACxB,CAAC"}
1
+ {"version":3,"file":"bluff.js","sourceRoot":"","sources":["../../../../../../../src/commands/game/sideQuest/commands/bluff.ts"],"names":[],"mappings":"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"}
@@ -1,34 +1,30 @@
1
1
  import { BypassCommand } from './bypass-command.js';
2
- import { PuzzleBypassType } from '../types/seed/puzzle-seed.js';
3
- import { PuzzleTarget } from '../models/room.js';
2
+ import { Device } from '../models/device.js';
4
3
  /**
5
4
  * The FORCEFUL route past a physical barrier (player ruling: "solve" was
6
5
  * too broad -- a maglock wants finesse OR muscle, and muscle deserves its
7
- * own verb). Same barrier class as "pick" (bypassType lockpick), opposite
6
+ * own verb). Same device kind as "pick" (a lock), opposite
8
7
  * philosophy: raw Strength + Body instead of Agility + locksmith, and it
9
8
  * is LOUD no matter what -- a successful breach still raises the world
10
9
  * and ends any sneak (onBypassSuccess), where a clean pick stays quiet.
11
- * The two verbs share the barrier's one burned flag: a seized mechanism
12
- * or a sprung frame spends the shortcut for both -- the findable answer
13
- * remains untouched, as always. The SHARED flag is deliberate; the
14
- * shared WORDING was not. This used to carry a note calling it an
15
- * accepted wart that a breach-burned barrier re-refused with the
16
- * snapped-pick line -- it stopped being acceptable the moment a player
17
- * noticed being told about a pick they never owned (2026-08-27). Hence
18
- * burnedFlavor below: same flag, own fiction. Do not reinstate the wart.
10
+ * NOTHING BURNS ANY MORE. Both verbs used to share the barrier's one
11
+ * "burned" flag -- a failed attempt spent the shortcut for good -- and
12
+ * that flag went with the puzzle mechanic. A lock you failed to force
13
+ * is a lock you can throw yourself at again, which is what a lock is.
14
+ * The trade against "pick" is undiminished: breach is loud whether or
15
+ * not it works, and that is what you are choosing.
19
16
  */
20
17
  export declare class BreachCommand extends BypassCommand {
21
18
  protected static verb: string;
22
19
  protected static description: string;
23
- protected bypassType: PuzzleBypassType;
20
+ protected deviceKind: "lock";
24
21
  protected verbName: string;
25
- protected noPuzzleMessage: string;
22
+ protected noDeviceMessage: string;
26
23
  protected failureFlavor: string;
27
- protected burnedFlavor: string;
28
24
  execute(args: string[]): Promise<string>;
29
25
  protected determineOutcome(): Promise<boolean>;
30
26
  /** Breaching is loud when it WORKS, too -- the defining trade against
31
27
  * pick. The world event lets every listening NPC react (same channel
32
28
  * the failure alarm uses); the sneak is over either way. */
33
- protected onBypassSuccess(target: PuzzleTarget): Promise<string[]>;
29
+ protected onForceSuccess(device: Device): Promise<string[]>;
34
30
  }