diff --git a/docs/plugin-v3-ui.md b/docs/plugin-v3-ui.md index 8d3fa4a..9477233 100644 --- a/docs/plugin-v3-ui.md +++ b/docs/plugin-v3-ui.md @@ -192,10 +192,12 @@ out of the capability graph. ## Injecting into core shells (profile, dashboard) -Core screens that accept plugin sections render **empty mount points** and -announce each (re)build with a DOM event, because their `innerHTML` swap wipes -anything previously injected. A plugin listens for the event and fills the -mount by id — the same seam every time: +Core screens that accept plugin sections render **mount points** — usually +empty, sometimes holding core's own **fallback content** (the Dashboard's +career slot ships the plugin-count stat) — and announce each (re)build with a +DOM event, because their `innerHTML` swap wipes anything previously injected. +A plugin listens for the event and **replaces the mount's content** (never +append — a fallback may be present) by id — the same seam every time: | Shell | Event | Mounts | | --- | --- | --- | diff --git a/plugins/career/screen.js b/plugins/career/screen.js index acefe8e..1188d25 100644 --- a/plugins/career/screen.js +++ b/plugins/career/screen.js @@ -937,12 +937,15 @@ const walls = []; for (const inst of (_pp.config || {}).instruments || []) { const d = (_pp.instruments || {})[inst]; - if (!d || !d.committed_at) continue; + // A commitment with no opened passport isn't a wall yet — the + // external surfaces (profile, home card) stay ABSENT until a + // passport exists (absent-not-empty). + if (!d || !d.committed_at || !(d.passports || []).length) continue; const earned = (d.passports || []).filter((p) => p.badge === 'earned' || p.badge === 'gold'); badges += earned.length; seconds += (d.passports || []).reduce((t, p) => t + (p.seconds_total || 0), 0); gigs += d.gig_count || 0; - walls.push({ inst, earned, opened: (d.passports || []).length }); + walls.push({ inst, earned, opened: d.passports.length }); } if (!walls.length) return null; return { badges, seconds, gigs, walls }; diff --git a/plugins/career/tests/passports.test.js b/plugins/career/tests/passports.test.js index bc42556..05cdeae 100644 --- a/plugins/career/tests/passports.test.js +++ b/plugins/career/tests/passports.test.js @@ -159,6 +159,10 @@ test('careerTotals / wall + dash card stay absent without commitment', () => { t.setView({ config: { instruments: ['guitar'] }, instruments: { guitar: { committed_at: null, passports: [] } } }); assert.equal(t.careerTotals(), null); + // Committed but zero passports opened: still absent (no zero-wall). + t.setView({ config: { instruments: ['guitar'] }, + instruments: { guitar: { committed_at: 'x', passports: [] } } }); + assert.equal(t.careerTotals(), null); // Committed with an earned badge + hours → totals aggregate. t.setView({ config: { instruments: ['guitar', 'bass'] }, instruments: {