@bongos/core 1.20.48 → 1.20.50

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 (54) hide show
  1. package/.bongos-core.json +84 -54
  2. package/.claude/skills/new-project/SKILL.md +5 -0
  3. package/clients/bongos-client/index.d.ts +1 -1
  4. package/docs/api/openapi.json +3 -0
  5. package/docs/api-reference.md +1 -1
  6. package/docs/copy-inventory.md +434 -400
  7. package/docs/copy-registry.json +816 -508
  8. package/docs/module-api-changelog.md +4 -0
  9. package/docs/page-inventory.json +11 -2
  10. package/docs/page-readings.json +733 -690
  11. package/modules/hall-ui/public/criterion-uat-ui.js +280 -0
  12. package/modules/hall-ui/public/goal-map.html +5 -3
  13. package/modules/hall-ui/public/goals-page.js +23 -44
  14. package/modules/hall-ui/public/goals-render.js +24 -15
  15. package/modules/hall-ui/public/goals.css +45 -0
  16. package/modules/hall-ui/public/goals.html +5 -2
  17. package/modules/hall-ui/public/goals.states.json +38 -0
  18. package/modules/hall-ui/public/sky-data.js +9 -1
  19. package/modules/hall-ui/public/sky-interaction.js +1 -1
  20. package/modules/hall-ui/public/sky-panel.js +1 -1
  21. package/modules/hall-ui/public/thinking.html +5 -3
  22. package/modules/lifecycle/done-when.js +3 -1
  23. package/modules/lifecycle/sky.js +23 -8
  24. package/modules/platform-identity/routes/sso.js +5 -0
  25. package/modules/provisioning/migrations/provisioning_033_terms_ack.sql +33 -0
  26. package/modules/provisioning/onboard-plan.js +5 -0
  27. package/modules/provisioning/provisioning.js +7 -4
  28. package/modules/provisioning/routes/provisioning.js +3 -3
  29. package/modules/provisioning/terms-ack.js +36 -0
  30. package/modules/public-landing/public/projects.html +226 -77
  31. package/modules/public-landing/public/projects.probes.json +8 -2
  32. package/modules/public-landing/public/projects.states.json +16 -12
  33. package/modules/ui-design/kit/serve.js +6 -0
  34. package/package-lock.json +2 -2
  35. package/package.json +1 -1
  36. package/release-notes.json +12 -0
  37. package/scripts/hall-preview/server.js +2 -0
  38. package/src/module-api.js +1 -1
  39. package/tests/claim_action.mjs +6 -1
  40. package/tests/criterion_uat_ui.mjs +113 -0
  41. package/tests/goal_criterion_confirm_ui.mjs +188 -91
  42. package/tests/goal_map_page.mjs +2 -1
  43. package/tests/hall_sky.mjs +16 -0
  44. package/tests/helpers.mjs +3 -1
  45. package/tests/projects_hub.mjs +4 -2
  46. package/tests/projects_hub_app_status.mjs +1 -1
  47. package/tests/projects_hub_app_step.mjs +9 -9
  48. package/tests/projects_hub_module_picker.mjs +7 -7
  49. package/tests/projects_hub_pre_uat.mjs +4 -1
  50. package/tests/provisioning_terms_ack.mjs +216 -0
  51. package/tests/sky_read.mjs +7 -1
  52. package/tests/wizard_draft_resume.mjs +4 -4
  53. package/tests/wizard_front_door.mjs +271 -0
  54. package/tests/wizard_intent_resume.mjs +31 -19
@@ -15,12 +15,13 @@
15
15
  // ruled out the task's other option (drop the record at a terminal state).
16
16
  //
17
17
  // The reset fires only when there is a SECOND DRAFT to lose, and the draft's own step is
18
- // what says so. A landed create clears DRAFT_KEY — but show(9) then persists the done
18
+ // what says so. A landed create clears DRAFT_KEY — but show(11) then persists the done
19
19
  // panel's position straight back into it, so a plain "is there a draft?" test is always
20
20
  // yes and would skip the reset forever, leaving the finished project's name sitting in
21
21
  // the fresh form. (That is exactly what the first cut of this change shipped into a
22
22
  // render: "still watching Mercury" above a name field still reading "Mercury".) Only
23
- // steps 1-8 are wizard steps; anything else is the done panel's own bookkeeping.
23
+ // steps 1-10 are wizard steps (1-8 before task 1004417's front door took 1 and 2);
24
+ // anything else is the done panel's own bookkeeping.
24
25
  //
25
26
  // Harness: there is no jsdom in this repo, so this follows tests/wizard_draft_resume.mjs
26
27
  // and tests/project_door_ui.mjs — slice the real region out of the page's INLINE script
@@ -57,8 +58,8 @@ test('both slices are still findable (a rename must fail loudly, not silently pa
57
58
  assert.ok(offerSlice, 'paintResumeOffer() was not found in projects.html');
58
59
  assert.match(enterSlice, /if \(focus && !resumeIntent\) \{/, 'the intent branch is part of the slice');
59
60
  assert.match(enterSlice, /startProgress\(created\.id\);/, 'so is the resume branch it guards');
60
- assert.match(enterSlice, /draft\.step >= 1 && draft\.step <= 8/,
61
- 'the STORED draft\'s step decides, not state.step — state.step is 9 for the whole life of a finished standup');
61
+ assert.match(enterSlice, /draft\.step >= 1 && draft\.step <= 10/,
62
+ 'the STORED draft\'s step decides, not state.step — state.step is 11 for the whole life of a finished standup');
62
63
  assert.match(SCRIPT, /if \(created && created\.id\) \{ enterCreated\(created, !!focus\); return; \}/,
63
64
  'enter() still hands the created record straight to it');
64
65
  });
@@ -72,7 +73,7 @@ function runEnter({ focus, resumeIntent = false, draft = null, record }) {
72
73
  const calls = { show: [], resets: 0, offer: [], done: 0, progress: [], polls: 0, focused: 0 };
73
74
  // state.step is the DONE step for the whole life of a finished standup — the value
74
75
  // a draft-blind reading would have trusted
75
- const state = { step: draft ? draft.step : 9, name: 'Mercury', slug: 'mercury', domain: '', mode: 'greenfield', noAddress: false, modules: null };
76
+ const state = { step: draft ? draft.step : 11, name: 'Mercury', slug: 'mercury', domain: '', mode: 'greenfield', noAddress: false, modules: null };
76
77
  const sandbox = {
77
78
  record, focus, resumeIntent, state,
78
79
  DRAFT_KEY: 'draft',
@@ -98,7 +99,7 @@ const REC = { id: 'inst-1', name: 'Acme', slug: 'acme', domain: 'acme.example',
98
99
 
99
100
  test('a reload / Back / identity settle (focus false) still resumes the done panel, watch and all', () => {
100
101
  const { calls, state } = runEnter({ focus: false, record: REC });
101
- assert.deepEqual(calls.show, [[9, false]], 'the done panel, and no focus steal on a non-deliberate arrival');
102
+ assert.deepEqual(calls.show, [[11, false]], 'the done panel, and no focus steal on a non-deliberate arrival');
102
103
  assert.equal(calls.resets, 0, 'nothing is reset — this is the same standup continuing');
103
104
  assert.equal(calls.done, 1, 'the checklist is re-rendered');
104
105
  assert.deepEqual(calls.progress, ['inst-1'], 'the progress watch is restarted from the record');
@@ -113,7 +114,7 @@ test('a deliberate "New project" click after a finished standup opens a FRESH st
113
114
  assert.equal(calls.progress.length, 0, 'and its watch is not restarted');
114
115
  assert.equal(calls.resets, 1, 'with no second draft to lose, the wizard forgets the finished project');
115
116
  assert.deepEqual(calls.show, [[1, false]], 'step 1, and step 1 does not take the heading focus');
116
- assert.equal(calls.focused, 1, 'the name field does — the deliberate-nav rule enter() already applies');
117
+ assert.equal(calls.focused, 1, 'its one control, the terms tick, does — the deliberate-nav rule enter() already applies');
117
118
  assert.equal(state.name, '', 'the old name is gone from the form');
118
119
  });
119
120
 
@@ -126,7 +127,7 @@ test('…and the standup stays one click away, named, on the resume link', () =>
126
127
 
127
128
  test('the resume link gets the done panel back — the record was never cleared', () => {
128
129
  const { calls, sandbox } = runEnter({ focus: true, resumeIntent: true, record: REC });
129
- assert.deepEqual(calls.show, [[9, true]], 'a deliberate resume moves focus to the done panel');
130
+ assert.deepEqual(calls.show, [[11, true]], 'a deliberate resume moves focus to the done panel');
130
131
  assert.deepEqual(calls.progress, ['inst-1'], 'the watch comes back with it');
131
132
  assert.equal(sandbox.resumeIntent, false, 'the flag is ONE-SHOT — the next plain nav click must mean "new" again');
132
133
  assert.deepEqual(calls.offer, [null], 'and the offer comes down behind it');
@@ -144,20 +145,20 @@ test('a second draft already under way is NOT wiped by navigating back to the wi
144
145
  });
145
146
 
146
147
  test('the done panel\'s own saved step is NOT a second draft — it must not block the reset', () => {
147
- // Caught in a render, not in a unit test: show(9) calls saveDraft(), so DRAFT_KEY is
148
+ // Caught in a render, not in a unit test: show(11) calls saveDraft(), so DRAFT_KEY is
148
149
  // non-empty again the moment a create lands. Reading "is there a draft?" left the
149
150
  // finished project's name in the fresh form, under a line offering to resume it.
150
- const { calls, state } = runEnter({ focus: true, record: REC, draft: { step: 9, name: 'Mercury' } });
151
+ const { calls, state } = runEnter({ focus: true, record: REC, draft: { step: 11, name: 'Mercury' } });
151
152
  assert.equal(calls.resets, 1, 'the done step is bookkeeping, not a project being typed');
152
- assert.deepEqual(calls.show, [[1, false]], 'out-of-range steps land on the name step');
153
+ assert.deepEqual(calls.show, [[1, false]], 'out-of-range steps land on step 1, the front door');
153
154
  assert.equal(state.name, '', 'and the finished project\'s name does not survive into it');
154
155
  assert.equal(calls.done, 0, 'never back onto the done panel');
155
156
  });
156
157
 
157
158
  test('a draft on the app step or the review is live too — the last two numbered steps (task 1004351)', () => {
158
- // The app step moved the review from 7 to 8. A guard left at 7 would reset a second
159
- // project sitting on its review, silently wiping it.
160
- for (const step of [7, 8]) {
159
+ // The app step moved the review from 7 to 8, and the front door (task 1004417) to 10. A
160
+ // guard left behind would reset a second project sitting on its review, silently wiping it.
161
+ for (const step of [9, 10]) {
161
162
  const { calls, state } = runEnter({ focus: true, record: REC, draft: { step } });
162
163
  assert.equal(calls.resets, 0, `step ${step} is a live draft`);
163
164
  assert.deepEqual(calls.show, [[step, true]]);
@@ -165,8 +166,16 @@ test('a draft on the app step or the review is live too — the last two numbere
165
166
  }
166
167
  });
167
168
 
168
- test('a draft whose step is missing or junk resets too — only 1-8 preserves anything', () => {
169
- for (const draft of [{ step: 0 }, { step: 9 }, { name: 'half-typed' }]) {
169
+ test('a draft on the front door is live too — a founder part-way through it keeps their place (task 1004417)', () => {
170
+ for (const step of [1, 2]) {
171
+ const { calls, state } = runEnter({ focus: true, record: REC, draft: { step } });
172
+ assert.equal(calls.resets, 0, `step ${step} is a live draft`);
173
+ assert.equal(state.step, step);
174
+ }
175
+ });
176
+
177
+ test('a draft whose step is missing or junk resets too — only 1-10 preserves anything', () => {
178
+ for (const draft of [{ step: 0 }, { step: 11 }, { name: 'half-typed' }]) {
170
179
  const { calls } = runEnter({ focus: true, record: REC, draft });
171
180
  assert.equal(calls.resets, 1, `step ${JSON.stringify(draft.step)} is not a wizard step`);
172
181
  assert.deepEqual(calls.show, [[1, false]]);
@@ -191,11 +200,14 @@ test('paintResumeOffer names the project by textContent and hides on null (no li
191
200
  });
192
201
 
193
202
  test('the offer ships as real markup inside step 1, hidden until there is something to resume', () => {
194
- const panel = HUB.slice(HUB.indexOf('id="panel-name"'), HUB.indexOf('id="panel-type"'));
203
+ // step 1 is the front door's terms screen since task 1004417 — the offer moved with it
204
+ const panel = HUB.slice(HUB.indexOf('id="panel-terms"'), HUB.indexOf('id="panel-fork"'));
195
205
  assert.match(panel, /<p class="resumeOffer" id="wizResume" hidden>still watching <strong id="wizResumeName">your project<\/strong> — <a href="#" id="wizResumeGo">resume it<\/a><\/p>/,
196
206
  'baked into the panel (C7), carrying its own hidden state');
197
- assert.ok(panel.indexOf('id="wizResume"') < panel.indexOf('<form id="newForm"'),
198
- 'it sits above the name form, where a visitor reads it before typing');
207
+ assert.ok(panel.indexOf('id="wizResume"') < panel.indexOf('<form data-next="2">'),
208
+ 'it sits above the terms form, where a visitor reads it before anything else');
209
+ assert.doesNotMatch(HUB.slice(HUB.indexOf('id="panel-name"'), HUB.indexOf('id="panel-type"')), /id="wizResume"/,
210
+ 'and only once — a second copy would be a duplicate id');
199
211
  assert.match(SCRIPT, /\$\('wizResumeGo'\)\.addEventListener\('click', function \(ev\) \{\n\s*ev\.preventDefault\(\);\n\s*resumeIntent = true;\n\s*enter\(true\);/,
200
212
  'the link raises the one-shot intent and re-enters, rather than href-navigating');
201
213
  });