From 1ccb0458894a6a7bf58041777f1d7df6231ef673 Mon Sep 17 00:00:00 2001 From: Disco DeDisco Date: Mon, 18 May 2026 10:54:30 -0400 Subject: [PATCH] =?UTF-8?q?Baltimorean=20"Ard!"=20=E2=86=92=20"Baltimorean?= =?UTF-8?q?"=20rename=20across=20inline=20surfaces=20(banner=20/=20scroll?= =?UTF-8?q?=20line=20/=20my-notes=20Title=20row);=20navbar=20DON=20greetin?= =?UTF-8?q?g=20keeps=20"Ayo,=20Ard!"=20as=20the=20sole=20Ard!=20flair=20?= =?UTF-8?q?=E2=80=94=20and=20a=20companion=20PronounsAppletFlowTest=20sync?= =?UTF-8?q?-point=20fix=20for=20the=20200/Brief=20response=20path=20introd?= =?UTF-8?q?uced=20by=20the=20Baltimorean=20unlock=20loop=20in=20435a192=20?= =?UTF-8?q?;=20FT=20fix=20(functional=5Ftests/test=5Fgame=5Fkit.py)=20?= =?UTF-8?q?=E2=80=94=20`PronounsAppletFlowTest.test=5Fpronoun=5Fflip=5Fpro?= =?UTF-8?q?pagates=5Fto=5Fbillscroll=5Fand=5Fmost=5Frecent`=20was=20writte?= =?UTF-8?q?n=20pre-Baltimorean=20when=20`set=5Fpronouns`=20always=20return?= =?UTF-8?q?ed=20204=20=E2=86=92=20reload;=20the=20Baltimorean=20loop=20spl?= =?UTF-8?q?it=20the=20response=20into=20200-w-brief=20(first-bawlmorese,?= =?UTF-8?q?=20no=20reload=20=E2=80=94=20Brief=20banner=20would=20be=20lost?= =?UTF-8?q?)=20vs=20204-reload=20(other=20pronouns),=20so=20the=20test's?= =?UTF-8?q?=20step-4=20`wait=5Ffor(.gk-pronoun-card.active[data-pronoun=3D?= =?UTF-8?q?'bawlmorese'])`=20hung=20indefinitely=20on=20the=20Brief=20bann?= =?UTF-8?q?er=20because=20the=20active=20class=20only=20updates=20after=20?= =?UTF-8?q?the=20reload=20that=20no=20longer=20happens;=20sync=20point=20s?= =?UTF-8?q?wapped=20to=20`wait=5Ffor(.note-banner)`=20=E2=80=94=20the=20Br?= =?UTF-8?q?ief=20banner's=20appearance=20is=20the=20natural=20post-commit?= =?UTF-8?q?=20signal=20that=20the=20server=20saved=20the=20pronoun=20(afte?= =?UTF-8?q?r=20which=20step-5/6=20navigate=20to=20billboard=20+=20scroll?= =?UTF-8?q?=20to=20verify=20"yos"=20prose)=20;=20rename=20core=20(drama/mo?= =?UTF-8?q?dels.py)=20=E2=80=94=20`Note.grant=5Fif=5Fnew`=20else=20branch'?= =?UTF-8?q?s=20`attr=5Fcombo`=20inline=20attribution=20now=20uses=20`note.?= =?UTF-8?q?display=5Fname`=20instead=20of=20`note.display=5Ftitle`,=20so?= =?UTF-8?q?=20the=20scroll=20line=20reads=20"Look!=E2=80=94new=20Note=20un?= =?UTF-8?q?locked.=20Baltimorean=20recognizes=20@disco=20the=20Baltimorean?= =?UTF-8?q?."=20instead=20of=20"=E2=80=A6the=20Ard!."=20(for=20stargazer/s?= =?UTF-8?q?chizo/nomad=20display=5Fname=20=3D=3D=20display=5Ftitle=20so=20?= =?UTF-8?q?the=20line=20is=20unchanged;=20only=20baltimorean's=20two=20val?= =?UTF-8?q?ues=20diverge=20=E2=80=94=20display=5Ftitle=3D"Ard!"=20for=20na?= =?UTF-8?q?vbar=20flair,=20display=5Fname=3D"Baltimorean"=20for=20everywhe?= =?UTF-8?q?re=20else);=20`Brief.title`=20field=20also=20swapped=20from=20`?= =?UTF-8?q?display=5Ftitle`=20to=20`display=5Fname`=20so=20the=20banner=20?= =?UTF-8?q?title=20slot=20reads=20"Baltimorean"=20not=20"Ard!"=20=E2=80=94?= =?UTF-8?q?=20admin-grant=20branch=20(=5FADMIN=5FNOTE=5FSLUGS:=20super-sch?= =?UTF-8?q?izo,=20super-nomad)=20still=20uses=20`display=5Ftitle`=20for=20?= =?UTF-8?q?the=20"honorary=20title=20of=20{X}"=20phrase=20because=20that?= =?UTF-8?q?=20branch=20DOES=20want=20the=20don-able=20title=20there=20("Sc?= =?UTF-8?q?hizoid=20Man"=20/=20"Stranger")=20;=20my-notes=20card=20"Title:?= =?UTF-8?q?"=20row=20rename=20=E2=80=94=20added=20`card=5Ftitle`=20key=20t?= =?UTF-8?q?o=20`=5FNOTE=5FDISPLAY["baltimorean"]`=20(`{"greeting":=20"Ayo,?= =?UTF-8?q?",=20"title":=20"Ard!",=20"card=5Ftitle":=20"Baltimorean"}`)=20?= =?UTF-8?q?+=20new=20`Note.card=5Ftitle`=20property=20that=20falls=20throu?= =?UTF-8?q?gh=20`=5FNOTE=5FDISPLAY[slug]["card=5Ftitle"]`=20then=20default?= =?UTF-8?q?s=20to=20`display=5Ftitle`;=20billboard/views.py=20`=5Fmy=5Fnot?= =?UTF-8?q?es=5Fcontext`=20swaps=20`"recognition=5Ftitle":=20n.display=5Ft?= =?UTF-8?q?itle`=20=E2=86=92=20`n.card=5Ftitle`=20so=20the=20my-notes=20Ba?= =?UTF-8?q?ltimorean=20card=20shows=20"Title:=20Baltimorean"=20instead=20o?= =?UTF-8?q?f=20"Title:=20Ard!"=20=E2=80=94=20super-schizo's=20card=20still?= =?UTF-8?q?=20reads=20"Title:=20Schizoid=20Man"=20because=20card=5Ftitle?= =?UTF-8?q?=20falls=20back=20to=20display=5Ftitle=20for=20slugs=20w.o=20an?= =?UTF-8?q?=20override=20;=20ONLY=20Ard!=20surface=20remaining:=20navbar?= =?UTF-8?q?=20DON=20greeting=20via=20`User.active=5Ftitle.display=5Ftitle`?= =?UTF-8?q?=20(lyric/models.py:158)=20=E2=80=94=20`display=5Ftitle`=20itse?= =?UTF-8?q?lf=20was=20deliberately=20untouched=20so=20the=20navbar=20still?= =?UTF-8?q?=20flips=20`Welcome,=20Earthman`=20=E2=86=92=20`Ayo,=20Ard!`=20?= =?UTF-8?q?after=20DON,=20which=20is=20the=20entire=20point=20of=20the=20B?= =?UTF-8?q?altimorean=20flair=20;=20existing=20DB=20rows=20w.=20stored=20"?= =?UTF-8?q?the=20Ard!"=20`Line.text`=20+=20`Brief.title=3D"Ard!"`=20from?= =?UTF-8?q?=20prior=20dev-DB=20grants=20will=20NOT=20update=20=E2=80=94=20?= =?UTF-8?q?those=20columns=20are=20persisted=20at=20`grant=5Fif=5Fnew`=20t?= =?UTF-8?q?ime,=20not=20computed=20live;=20user=20has=20accepted=20dev-DB?= =?UTF-8?q?=20wipe-and-re-acquire=20as=20the=20path=20forward,=20so=20no?= =?UTF-8?q?=20data=20migration=20;=20tests=20=E2=80=94=20drama/tests/unit/?= =?UTF-8?q?test=5Fmodels.py=20+2=20UTs=20(`test=5Fbaltimorean=5Fcard=5Ftit?= =?UTF-8?q?le=5Fis=5Fbaltimorean`=20pins=20the=20override,=20`test=5Fstarg?= =?UTF-8?q?azer=5Fcard=5Ftitle=5Ffalls=5Fback=5Fto=5Fdisplay=5Ftitle`=20pi?= =?UTF-8?q?ns=20the=20fallback=20for=20non-overridden=20slugs);=20drama/te?= =?UTF-8?q?sts/integrated/test=5Fnote=5Fbrief.py=20`test=5Ffirst=5Fgrant?= =?UTF-8?q?=5Fcreates=5Fpost=5Fline=5Fand=5Fbrief`=20updated=20assertion?= =?UTF-8?q?=20`brief.title=20=3D=3D=20note.display=5Fname`=20(was=20`displ?= =?UTF-8?q?ay=5Ftitle`)=20to=20reflect=20the=20new=20contract;=20dashboard?= =?UTF-8?q?/tests/integrated/test=5Fviews.py=20`test=5Fbrief=5Fpayload=5Fc?= =?UTF-8?q?arries=5Fbaltimorean=5Ftitle`=20expects=20"Baltimorean"=20not?= =?UTF-8?q?=20"Ard!";=20functional=5Ftests/test=5Fbill=5Fbaltimorean.py=20?= =?UTF-8?q?T1=20banner=20title=20check=20swapped=20"Ard!"=20=E2=86=92=20"B?= =?UTF-8?q?altimorean"=20+=20module=20docstring=20line=2010=20updated=20?= =?UTF-8?q?=E2=80=94=20T4=20(navbar=20DON=20greeting=20flip=20to=20"Ayo,?= =?UTF-8?q?=20Ard!")=20preserved=20verbatim,=20the=20one=20surface=20where?= =?UTF-8?q?=20Ard!=20still=20lives=20;=20full=20FT=20suite=20for=20baltimo?= =?UTF-8?q?rean=206/6=20green=20in=2053s;=20drama=20+=20dashboard=20+=20bi?= =?UTF-8?q?llboard=20ITs/UTs=20290=20green=20in=2016.5s=20=E2=80=94=20TDD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Code architected by Disco DeDisco Git commit message Co-Authored-By: Claude Opus 4.7 (1M context) --- src/apps/billboard/views.py | 2 +- .../dashboard/tests/integrated/test_views.py | 2 +- src/apps/drama/models.py | 19 ++++++++++++++++--- .../drama/tests/integrated/test_note_brief.py | 5 +++-- src/apps/drama/tests/unit/test_models.py | 11 +++++++++++ src/functional_tests/test_bill_baltimorean.py | 8 ++++---- src/functional_tests/test_game_kit.py | 9 +++++---- 7 files changed, 41 insertions(+), 15 deletions(-) diff --git a/src/apps/billboard/views.py b/src/apps/billboard/views.py index cee7604..d627e43 100644 --- a/src/apps/billboard/views.py +++ b/src/apps/billboard/views.py @@ -216,7 +216,7 @@ def my_notes(request): { "obj": n, "title": _NOTE_META.get(n.slug, {}).get("title", n.slug), - "recognition_title": n.display_title, + "recognition_title": n.card_title, "description": _NOTE_META.get(n.slug, {}).get("description", ""), "palette_options": _NOTE_META.get(n.slug, {}).get("palette_options", []), "swatch_label": _NOTE_META.get(n.slug, {}).get("swatch_label"), diff --git a/src/apps/dashboard/tests/integrated/test_views.py b/src/apps/dashboard/tests/integrated/test_views.py index 95f6ce9..ed4fe80 100644 --- a/src/apps/dashboard/tests/integrated/test_views.py +++ b/src/apps/dashboard/tests/integrated/test_views.py @@ -768,7 +768,7 @@ class SetPronounsBawlmoreseUnlockTest(TestCase): def test_brief_payload_carries_baltimorean_title(self): response = self.client.post(self.url, data={"pronouns": "bawlmorese"}) brief = response.json()["brief"] - self.assertEqual(brief["title"], "Ard!") + self.assertEqual(brief["title"], "Baltimorean") def test_brief_payload_square_url_jumps_to_my_notes(self): """NOTE_UNLOCK Briefs carry a `square_url` pointing at /billboard/my-notes/ diff --git a/src/apps/drama/models.py b/src/apps/drama/models.py index e129572..0ae9cea 100644 --- a/src/apps/drama/models.py +++ b/src/apps/drama/models.py @@ -212,7 +212,7 @@ _NOTE_DISPLAY = { "nomad": {"greeting": "Welcome,", "title": "Nomad"}, "super-schizo": {"greeting": "21st Century", "title": "Schizoid Man"}, "super-nomad": {"greeting": "Howdy,", "title": "Stranger"}, - "baltimorean": {"greeting": "Ayo,", "title": "Ard!"}, + "baltimorean": {"greeting": "Ayo,", "title": "Ard!", "card_title": "Baltimorean"}, } # Note slugs whose grant prose uses the long admin format ("The administration @@ -249,6 +249,15 @@ class Note(models.Model): def display_greeting(self): return _NOTE_DISPLAY.get(self.slug, {}).get("greeting", "Welcome,") + @property + def card_title(self): + """The string shown in the my-notes card's "Title:" row. Defaults to + `display_title` (the don-able title — most slugs render the title you + DON here, e.g. "Schizoid Man" for super-schizo). Baltimorean overrides + so the card reads "Baltimorean" instead of the navbar-only "Ard!" + flair.""" + return _NOTE_DISPLAY.get(self.slug, {}).get("card_title", self.display_title) + @property def display_name(self): """The Note's *name* (e.g., "Stargazer", "Super-Schizo") — the heading @@ -316,9 +325,13 @@ class Note(models.Model): "This does not entail any additional corporate benefits." ) else: + # Inline attribution reads "{handle} the {Note name}", not the + # don-able title. For most slugs the two coincide ("the Stargazer") + # but Baltimorean's title is the navbar flair "Ard!" — "the Ard!" + # reads oddly inline; "the Baltimorean" matches the Note name. attr_combo = ( f'{handle} ' - f'the {note.display_title}' + f'the {note.display_name}' ) line_text = ( f"Look!—new Note unlocked. {note_anchor} " @@ -339,6 +352,6 @@ class Note(models.Model): post=post, line=line, kind=Brief.KIND_NOTE_UNLOCK, - title=note.display_title, + title=note.display_name, ) return note, created, brief diff --git a/src/apps/drama/tests/integrated/test_note_brief.py b/src/apps/drama/tests/integrated/test_note_brief.py index bd80e87..fd8b940 100644 --- a/src/apps/drama/tests/integrated/test_note_brief.py +++ b/src/apps/drama/tests/integrated/test_note_brief.py @@ -30,8 +30,9 @@ class GrantIfNewSpawnsBriefTest(TestCase): self.assertEqual(brief.owner, self.user) # The Brief's line is one of the Post's lines self.assertIn(brief.line, list(post.lines.all())) - # Brief title matches Note display title - self.assertEqual(brief.title, note.display_title) + # Brief title matches Note display name (the Note's name — used in the + # banner's title slot and inline "the {name}" attribution). + self.assertEqual(brief.title, note.display_name) def test_second_grant_same_slug_returns_no_brief(self): Note.grant_if_new(self.user, "stargazer") diff --git a/src/apps/drama/tests/unit/test_models.py b/src/apps/drama/tests/unit/test_models.py index a947ce5..7c823db 100644 --- a/src/apps/drama/tests/unit/test_models.py +++ b/src/apps/drama/tests/unit/test_models.py @@ -27,6 +27,12 @@ class NoteDisplayBaltimoreanTest(SimpleTestCase): through to `slug.title()` → 'Baltimorean'.""" self.assertEqual(_note("baltimorean").display_name, "Baltimorean") + def test_baltimorean_card_title_is_baltimorean(self): + """The my-notes card's "Title:" row uses `card_title`, which Baltimorean + explicitly overrides to "Baltimorean" so the navbar-only "Ard!" flair + doesn't leak onto the card.""" + self.assertEqual(_note("baltimorean").card_title, "Baltimorean") + class NoteDisplayStargazerTest(SimpleTestCase): """Smoke tests for the existing stargazer entry — pins the contract that @@ -37,3 +43,8 @@ class NoteDisplayStargazerTest(SimpleTestCase): def test_stargazer_display_greeting_is_welcome(self): self.assertEqual(_note("stargazer").display_greeting, "Welcome,") + + def test_stargazer_card_title_falls_back_to_display_title(self): + """Slugs without a `card_title` override fall through to display_title — + for stargazer that's "Stargazer" (don-able title == Note name).""" + self.assertEqual(_note("stargazer").card_title, "Stargazer") diff --git a/src/functional_tests/test_bill_baltimorean.py b/src/functional_tests/test_bill_baltimorean.py index fcf73fa..ad1a402 100644 --- a/src/functional_tests/test_bill_baltimorean.py +++ b/src/functional_tests/test_bill_baltimorean.py @@ -7,7 +7,7 @@ Baltimorean Note and fires the slide-down Brief banner. End-to-end loop captured here: 1. Game Kit → pronouns applet → click `bawlmorese` card → guard portal "OK" - 2. Brief banner slides in with title "Ard!" + Look! prose + FYI/NVM/?-square + 2. Brief banner slides in with title "Baltimorean" + Look! prose + FYI/NVM/?-square 3. Navigate to /billboard/my-notes/ → Baltimorean item w. the Aaron quote 4. DON the Baltimorean Note → navbar greeting flips "Welcome, Earthman" → "Ayo, Ard!" @@ -106,10 +106,10 @@ class BaltimoreanNoteFromGameKitTest(FunctionalTest): banner = self.wait_for_slow(lambda: self.browser.find_element( By.CSS_SELECTOR, ".note-banner" )) - # The title slot carries the recognition title — for baltimorean - # that's "Ard!" (vs stargazer's "Stargazer"). + # The title slot carries the Note name — "Baltimorean". The don-able + # title "Ard!" only surfaces in the navbar greeting after DON. self.assertIn( - "Ard!", + "Baltimorean", banner.find_element(By.CSS_SELECTOR, ".note-banner__title").text, ) # Look! prose lands in the description. diff --git a/src/functional_tests/test_game_kit.py b/src/functional_tests/test_game_kit.py index 2a24479..2c432a2 100644 --- a/src/functional_tests/test_game_kit.py +++ b/src/functional_tests/test_game_kit.py @@ -198,12 +198,13 @@ class PronounsAppletFlowTest(FunctionalTest): self.assertIn("Set pronoun preference?", portal.text) self.assertIn("yo/yo/yos", portal.text) - # 4. Click OK — the page reloads, .active class moves to bawlmorese. + # 4. Click OK — first-time bawlmorese unlocks the Baltimorean Note, so + # the server returns 200 w. a Brief payload (no reload — the banner + # would be lost). Wait for the banner to confirm the commit landed + # before navigating away. portal.find_element(By.CSS_SELECTOR, ".guard-yes").click() self.wait_for( - lambda: self.browser.find_element( - By.CSS_SELECTOR, ".gk-pronoun-card.active[data-pronoun='bawlmorese']" - ) + lambda: self.browser.find_element(By.CSS_SELECTOR, ".note-banner") ) # 5. Navigate to the Billboard — Most Recent Scroll applet must now