@bongos/core 1.21.29 → 1.21.31

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 (60) hide show
  1. package/.bongos-core.json +70 -65
  2. package/docs/copy-inventory.md +374 -389
  3. package/docs/copy-registry.json +472 -637
  4. package/docs/module-api-changelog.md +4 -0
  5. package/docs/page-readings.json +873 -883
  6. package/modules/hall-ui/public/atlas.html +3 -3
  7. package/modules/hall-ui/public/blockers.html +3 -3
  8. package/modules/hall-ui/public/board-room.html +3 -3
  9. package/modules/hall-ui/public/city.html +3 -3
  10. package/modules/hall-ui/public/collab.html +3 -3
  11. package/modules/hall-ui/public/commands.html +3 -3
  12. package/modules/hall-ui/public/copy-desk.html +3 -3
  13. package/modules/hall-ui/public/craft-mode.js +57 -3
  14. package/modules/hall-ui/public/deploy.html +3 -3
  15. package/modules/hall-ui/public/diagrams.html +3 -3
  16. package/modules/hall-ui/public/drachmae.html +3 -3
  17. package/modules/hall-ui/public/fleet.html +3 -3
  18. package/modules/hall-ui/public/gate.html +3 -3
  19. package/modules/hall-ui/public/goal-map.html +3 -3
  20. package/modules/hall-ui/public/goals.html +3 -3
  21. package/modules/hall-ui/public/government.html +3 -3
  22. package/modules/hall-ui/public/idea.html +3 -3
  23. package/modules/hall-ui/public/ideas.html +3 -3
  24. package/modules/hall-ui/public/index.html +3 -3
  25. package/modules/hall-ui/public/modules.html +3 -3
  26. package/modules/hall-ui/public/primer.html +3 -3
  27. package/modules/hall-ui/public/profile.html +3 -3
  28. package/modules/hall-ui/public/project-settings.html +3 -3
  29. package/modules/hall-ui/public/ranks.html +3 -3
  30. package/modules/hall-ui/public/roadmap.html +3 -3
  31. package/modules/hall-ui/public/roster.html +3 -3
  32. package/modules/hall-ui/public/sessions.html +3 -3
  33. package/modules/hall-ui/public/settings.html +3 -3
  34. package/modules/hall-ui/public/shell.js +2 -0
  35. package/modules/hall-ui/public/street.html +3 -3
  36. package/modules/hall-ui/public/studio.html +3 -3
  37. package/modules/hall-ui/public/style.css +2 -0
  38. package/modules/hall-ui/public/task.html +3 -3
  39. package/modules/hall-ui/public/thinking.html +3 -3
  40. package/modules/hall-ui/public/tweak-editor.html +3 -3
  41. package/modules/hall-ui/public/watch.html +3 -3
  42. package/modules/hall-ui/public/work.html +3 -3
  43. package/modules/hall-ui/records/craft-mode.md +7 -0
  44. package/modules/public-landing/public/projects-hall-live.states.json +1 -41
  45. package/modules/public-landing/public/projects.html +109 -72
  46. package/modules/public-landing/public/projects.probes.json +5 -5
  47. package/modules/public-landing/public/projects.states.json +7 -7
  48. package/package-lock.json +2 -2
  49. package/package.json +1 -1
  50. package/release-notes.json +12 -0
  51. package/src/module-api.js +1 -1
  52. package/tests/craft_mode_nav.mjs +202 -0
  53. package/tests/hall_tweak_editor.mjs +1 -1
  54. package/tests/nav_permission_atoms.mjs +1 -1
  55. package/tests/projects_hub.mjs +7 -4
  56. package/tests/projects_hub_app_step.mjs +4 -3
  57. package/tests/projects_hub_module_picker.mjs +12 -10
  58. package/tests/projects_hub_pre_uat.mjs +1 -1
  59. package/tests/wizard_physics_more.mjs +58 -34
  60. package/tests/wizard_six_screens.mjs +10 -8
@@ -184,9 +184,12 @@ function boot({ check = (body) => ({ stop: false, soft_limits: softLimitsFor(bod
184
184
  a: () => state.physics.answers,
185
185
  pick: (key, val) => q(`[data-phys="${key}"][data-val="${val}"]`)[0].click(),
186
186
  rowSays: (key) => [row(key).querySelector('[data-row-v]').textContent, row(key).querySelector('[data-row-s]').textContent],
187
- ok: (key) => row(key).querySelector('[data-row-ok]').click(),
187
+ // a row's own check and confirmation, as the page's one "Looks right, continue" runs it
188
+ // per row (task 1004550): there is no per-row Looks right any more
189
+ ok: (key) => vm.runInContext(`confirmRow('${key}')`, sandbox),
190
+ // Edit opens a row; the same control reads Done while it is open, and confirms it
188
191
  change: (key) => row(key).querySelector('[data-row-chg]').click(),
189
- use: (key) => row(key).querySelector('[data-row-use]').click(),
192
+ done: (key) => row(key).querySelector('[data-row-chg]').click(),
190
193
  isOpen: (key) => !row(key).querySelector('.opts').hidden,
191
194
  go: () => $('reviewGo'),
192
195
  settle: async () => { for (let i = 0; i < 4; i += 1) await tick(); },
@@ -343,9 +346,11 @@ test('Team size: one or many, a start count, and a guessed growth the founder co
343
346
  assert.deepEqual(w.rowSays('team'), ['4 people now', 'About 12 by month three · people you invite.']);
344
347
  w.state.teamShape = 'community'; w.run('paintSetup()');
345
348
  assert.equal(w.rowSays('team')[1], 'About 12 by month three · anyone can apply.');
346
- w.use('team');
349
+ assert.equal(w.row('team').querySelector('[data-row-chg]').textContent, 'Done', 'an open row\'s one control reads Done');
350
+ w.done('team');
347
351
  assert.equal(w.state.confirmed.team, true);
348
- assert.equal(w.isOpen('team'), false, 'Use this closes the row');
352
+ assert.equal(w.isOpen('team'), false, 'Done closes the row');
353
+ assert.equal(w.row('team').querySelector('[data-row-chg]').textContent, 'Edit');
349
354
  });
350
355
 
351
356
  test('changing the team re-asks the rows it moves: rewards and extras', async () => {
@@ -372,13 +377,13 @@ test('rewards are hidden for a solo founder: the real table hides the row, and t
372
377
  assert.equal(w.run("rowApplies('rewards')"), false, 'the REAL softLimitsFor hides reward_intents for one-and-one');
373
378
  assert.equal(w.row('rewards').hidden, true, 'and the row goes with it');
374
379
  assert.ok(!w.run('unconfirmedRows()').includes('rewards'), 'nothing to confirm for a question never asked');
375
- assert.equal(w.go().textContent, 'Confirm 6 more', 'six rows apply');
380
+ assert.equal(w.run('unconfirmedRows()').length, 6, 'six rows apply');
376
381
  assert.equal(w.run('createDetail()').reward_intents, undefined);
377
382
  w.change('team');
378
383
  w.q('[data-phys-solo="many"]')[0].click();
379
384
  await w.settle();
380
385
  assert.equal(w.row('rewards').hidden, false, 'a team brings the question back, once the table is asked again');
381
- assert.equal(w.go().textContent, 'Confirm 7 more');
386
+ assert.equal(w.run('unconfirmedRows()').length, 7);
382
387
  });
383
388
 
384
389
  test('rewards: a greyed option is disabled and leaves the list, and each reason is said once', async () => {
@@ -472,30 +477,46 @@ test('a demo\'s team, rewards and run are the demo\'s own: fixed, said plainly,
472
477
  });
473
478
 
474
479
  // ── confirming, and the locked Continue ──────────────────────────────────────
475
- test('the review locks Continue until every row that applies is confirmed, and validateStep opens the first one left', async () => {
480
+ test('one "Looks right, continue" confirms every row that applies, each through its own check (task 1004550)', async () => {
476
481
  const w = await onReview({ builders_at_start: 3, builders_expected: 8 });
477
- assert.equal(w.go().disabled, true);
478
- assert.equal(w.go().textContent, 'Confirm 7 more');
479
- assert.equal(w.run('validateStep(4)'), false);
480
- assert.equal(w.isOpen('code'), true, 'the first unconfirmed row opens');
481
- for (const [i, key] of ['code', 'address', 'app', 'extras', 'team', 'rewards'].entries()) {
482
- w.ok(key);
483
- assert.equal(w.state.confirmed[key], true, key);
484
- assert.equal(w.row(key).querySelector('[data-row-ok]').textContent, 'Confirmed');
485
- assert.equal(w.row(key).querySelector('[data-row-ok]').disabled, true);
486
- assert.equal(w.go().textContent, `Confirm ${6 - i} more`);
487
- }
482
+ assert.equal(w.go().disabled, false, 'Continue is never locked: it is the confirmation');
483
+ assert.equal(w.go().textContent, 'Looks right, continue');
484
+ assert.equal(w.row('code').querySelector('[data-row-ok]'), null, 'no row carries a Looks right of its own');
485
+ assert.equal(w.run('unconfirmedRows()').length, 7, 'nothing is confirmed before the founder says so');
486
+ assert.equal(w.run('validateStep(4)'), true, 'one Continue confirms them all');
487
+ for (const key of ['code', 'address', 'app', 'extras', 'team', 'rewards', 'run']) assert.equal(w.state.confirmed[key], true, key);
488
488
  assert.equal(w.state.modules && plain(w.state.modules).join(), 'discord', 'confirming the extras is the answer the create sends');
489
- w.ok('run');
490
- assert.equal(w.go().disabled, false);
491
- assert.equal(w.go().textContent, 'Continue');
492
- assert.equal(w.run('validateStep(4)'), true);
493
489
  w.change('team');
494
- assert.equal(w.state.confirmed.team, undefined, 'Change takes a row back');
490
+ assert.equal(w.state.confirmed.team, undefined, 'Edit takes a row back');
495
491
  assert.equal(w.isOpen('team'), true);
496
- assert.equal(w.go().textContent, 'Confirm 1 more');
492
+ assert.deepEqual(plain(w.run('unconfirmedRows()')), ['team']);
493
+ w.done('team');
494
+ assert.equal(w.isOpen('team'), false, 'Done closes it again');
495
+ assert.equal(w.state.confirmed.team, true, 'and Done is the answer');
496
+ });
497
+
498
+ test('Continue stops on the first row that fails its check, opened with its reason, and confirms nothing past it', async () => {
499
+ const w = await onReview({ builders_at_start: 3, builders_expected: 8 });
500
+ w.state.physics.answers.builders_at_start = 4; w.state.physics.answers.builders_expected = 2; // fewer later than now
501
+ assert.equal(w.run('validateStep(4)'), false, 'Continue refuses');
502
+ assert.equal(w.isOpen('team'), true, 'the failing row opens');
503
+ assert.equal(w.state.confirmed.team, undefined);
504
+ assert.equal(w.state.confirmed.rewards, undefined, 'nothing after it is confirmed on the founder\'s behalf');
505
+ assert.equal(w.state.confirmed.code, true, 'the rows before it held, and stay confirmed');
506
+ assert.match(w.$('h-review').textContent, /Fix the row that’s open/);
507
+ assert.equal(w.$('h-review').className, 'hint err');
508
+ });
509
+
510
+ test('opening a row closes another open one whose answer holds, and leaves one that fails open', async () => {
511
+ const w = await onReview({ builders_at_start: 3, builders_expected: 8 });
512
+ w.change('code');
497
513
  w.change('team');
498
- assert.equal(w.isOpen('team'), false, 'a second Change closes it again');
514
+ assert.equal(w.isOpen('code'), false, 'one row open at a time');
515
+ assert.equal(w.state.confirmed.code, true, 'closing it that way is the same as Done');
516
+ w.state.physics.answers.builders_expected = 1; // fewer later than now: the team row fails
517
+ w.change('run');
518
+ assert.equal(w.isOpen('team'), true, 'a row that fails its check stays open');
519
+ assert.equal(w.isOpen('run'), true);
499
520
  });
500
521
 
501
522
  test('the prefills never overwrite a founder\'s own answer', () => {
@@ -578,7 +599,7 @@ test('a new owner walks from the quick checks through a fully confirmed review,
578
599
  w.ok('code'); w.ok('address'); w.ok('app'); w.ok('extras');
579
600
  w.change('team');
580
601
  w.q('[data-phys-solo="many"]')[0].click();
581
- w.use('team');
602
+ w.done('team');
582
603
  await w.settle();
583
604
  assert.equal(w.state.confirmed.extras, undefined, 'the team moved the extras\' preselect, so that row is asked again');
584
605
  w.ok('extras');
@@ -586,16 +607,15 @@ test('a new owner walks from the quick checks through a fully confirmed review,
586
607
  w.change('rewards');
587
608
  w.pick('reward_intents', 'cash');
588
609
  w.pick('reward_intents', 'equity');
589
- w.use('rewards');
610
+ w.done('rewards');
590
611
  w.change('run');
591
612
  w.pick('framing', 'open-source');
592
613
  await w.settle();
593
- w.use('run');
614
+ w.done('run');
594
615
  assert.equal(w.state.confirmed.app, undefined, 'the framing moved the App hosting row');
595
616
  assert.equal(w.rowSays('app')[0], 'Runs on your own machines');
596
- w.ok('app');
597
- assert.equal(w.go().textContent, 'Continue');
598
- assert.equal(w.run('validateStep(4)'), true);
617
+ assert.equal(w.run('validateStep(4)'), true, 'one Continue confirms the rest');
618
+ assert.equal(w.state.confirmed.app, true);
599
619
  const d = plain(w.run('createDetail()'));
600
620
  assert.deepEqual({ voice: d.voice, framing: d.framing, services: d.services, team: [d.builders_at_start, d.builders_expected], shape: d.team_shape, rewards: d.reward_intents },
601
621
  { voice: 'professional', framing: 'open-source', services: undefined, team: [2, 5], shape: 'small-team', rewards: ['cash', 'equity'] });
@@ -611,10 +631,14 @@ test('no path reaches Look without the founder confirming the address: a prefill
611
631
  assert.equal(w.row('address').querySelector('[data-row-chg]').hidden, false, `${label}: it can always be changed`);
612
632
  for (const key of ['code', 'app', 'extras', 'team', 'rewards', 'run']) if (w.run(`rowApplies('${key}')`)) w.ok(key);
613
633
  assert.deepEqual(plain(w.run('unconfirmedRows()')), ['address'], `${label}: every other row confirmed, the address still asked`);
614
- assert.equal(w.go().disabled, true, `${label}: Continue stays locked`);
615
- assert.equal(w.run('validateStep(4)'), false, `${label}: Continue refuses`);
634
+ // the address is on the page the founder confirms (task 1004550): only their own
635
+ // "Looks right, continue" — or Done on the row — confirms it, and its check still runs
636
+ w.run('validateAddress = function () { return false; }');
637
+ assert.equal(w.run('validateStep(4)'), false, `${label}: an address that fails its check refuses Continue`);
616
638
  assert.equal(w.isOpen('address'), true, `${label}: and opens the address row`);
617
- w.use('address');
639
+ assert.equal(w.state.confirmed.address, undefined, `${label}: still unconfirmed`);
640
+ w.run('validateAddress = function () { return true; }');
641
+ w.done('address');
618
642
  assert.equal(w.state.confirmed.address, true, `${label}: choosing it is the answer`);
619
643
  assert.equal(w.run('validateStep(4)'), true, `${label}: then Continue goes on`);
620
644
  }
@@ -55,7 +55,7 @@ test('each screen opens with the mockup\'s own words', () => {
55
55
  ['panel-fork', 'You’re about to start a digital planet.', 'Try a small demo first, or start your real project.'],
56
56
  ['panel-what', 'What are you making?', null],
57
57
  ['panel-checks', 'A few quick checks.', 'These decide which legal and payout options we can offer.'],
58
- ['panel-review', 'Check what we set up.', 'We filled these in from your answers. Confirm each one, or change it.'],
58
+ ['panel-review', 'Check what we set up.', 'We filled these in from your answers. Change anything that’s wrong.'],
59
59
  ['panel-look', 'Give it a look.', 'So builders always know which project they’re in.'],
60
60
  ];
61
61
  for (const [id, h, lede] of pairs) {
@@ -81,19 +81,21 @@ test('planet words survive only as the light touch: the fork and the alive momen
81
81
  assert.match(SCRIPT, /if \(inst\.last_verified_up\) \{\n\s*\/\*[^*]*\*\/\n\s*\$\('done-h'\)\.textContent =/, 'said only once the project has answered (Live is a verified claim)');
82
82
  });
83
83
 
84
- test('the review cannot be skipped: seven rows, each confirmed or changed, and a locked Continue', () => {
84
+ test('the review cannot be skipped: seven rows, one Edit each, and one Continue that confirms them all (task 1004550)', () => {
85
85
  const p = panel('panel-review');
86
86
  const rows = [...p.matchAll(/<div class="setRow" data-row="([a-z]+)">/g)].map((m) => m[1]);
87
87
  assert.deepEqual(rows, ['code', 'address', 'app', 'extras', 'team', 'rewards', 'run']);
88
88
  for (const key of rows) {
89
89
  const r = (p.match(new RegExp(`<div class="setRow" data-row="${key}">[\\s\\S]*?<span class="acts">[^\\n]*\\n`)) || [''])[0];
90
- assert.match(r, /<button type="button" class="rowOk" data-row-ok>Looks right<\/button><button type="button" class="rowChg" data-row-chg aria-expanded="false">Change<\/button>/, `${key}: Looks right and Change`);
91
- assert.match(r, /<div class="opts" hidden>[\s\S]*<button type="button" class="btn small rowUse" data-row-use>Use this<\/button>/, `${key}: Change opens the row's own controls with Use this`);
90
+ assert.match(r, /<span class="acts"><button type="button" class="rowChg" data-row-chg aria-expanded="false">Edit<\/button><\/span>/, `${key}: one Edit, nothing else`);
91
+ assert.match(r, /<div class="opts" hidden>/, `${key}: Edit opens the row's own controls`);
92
92
  }
93
- assert.match(p, /<button type="submit" class="btn" id="reviewGo" disabled>Confirm 7 more<\/button>/, 'Continue is baked locked');
94
- assert.match(SCRIPT, /go\.textContent = left \? 'Confirm ' \+ left \+ ' more' : 'Continue';/, 'and says how many rows are left');
95
- assert.match(SCRIPT, /if \(step === 4\) \{\n\s*var open = unconfirmedRows\(\);\n\s*if \(open\.length\) \{ openRow\(open\[0\]\); return false; \}/, 'Continue with a row left opens that row');
96
- assert.match(SCRIPT, /if \(unconfirmedRows\(\)\.length\) \{ show\(4, true\); validateStep\(4\); return; \}/, 'and Create refuses until every row is confirmed');
93
+ assert.doesNotMatch(p, /data-row-ok|data-row-use|Use this/, 'no per-row Looks right and no Use this are left');
94
+ assert.match(p, /<button type="submit" class="btn" id="reviewGo">Looks right, continue<\/button>/, 'Continue is the one confirmation, never locked');
95
+ assert.match(SCRIPT, /if \(step === 4\) return confirmAllRows\(\);/, 'Continue confirms every row through its own check');
96
+ assert.match(SCRIPT, /if \(!confirmRow\(rows\[i\]\)\) \{/, 'and stops on the first that fails');
97
+ assert.match(SCRIPT, /if \(unconfirmedRows\(\)\.length\) \{ reopenReview\(\); return; \}/, 'and Create refuses until every row is confirmed');
98
+ assert.match(SCRIPT, /chg\.textContent = 'Done';/, 'an open row\'s control reads Done');
97
99
  });
98
100
 
99
101
  test('nothing the old steps could do is lost: their controls are moved whole into the rows', () => {