mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-08-17 18:25:42 +02:00
[eric] release: one changelog source feeds the Help agent, the GitHub body, and the in-app card
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
"""The release story, in one place, for three surfaces: the in-app What's New card, the GitHub
|
||||
release body, and the Help agent's context. One source means the agent can never answer from a
|
||||
stale picture of the app, and a release can never ship with no story."""
|
||||
|
||||
from typing import Dict, List
|
||||
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
from typeguard import typechecked
|
||||
|
||||
|
||||
class ReleaseNote(BaseModel):
|
||||
model_config = ConfigDict(validate_assignment=True)
|
||||
version: str
|
||||
headline: str
|
||||
# User-facing lines only: what changed for the person using the app, not the diff.
|
||||
highlights: List[str]
|
||||
fixes: List[str]
|
||||
|
||||
|
||||
P_RELEASES: List[ReleaseNote] = [
|
||||
ReleaseNote(
|
||||
version="1.7.5",
|
||||
headline="Fewer dead ends, and the app tells us when something breaks.",
|
||||
highlights=[
|
||||
"Scrolling inside any panel, chat, or browser stays in that panel instead of dragging the canvas.",
|
||||
"Clicking the Marketplace window frames it like every other window, and camera moves land softly instead of snapping.",
|
||||
"While a browser agent works you see the live mini browser only, not the same action log twice.",
|
||||
],
|
||||
fixes=[
|
||||
"Dictation lands in the field you started in, even if you click elsewhere while it is still transcribing.",
|
||||
"The first message after opening a chat reuses the warmed-up connection, so it answers sooner.",
|
||||
"A provider hiccup that fixes itself no longer shows a scary reconnect card.",
|
||||
"Crashes, freezes, and runaway memory now report themselves, so bugs get diagnosed instead of guessed at.",
|
||||
],
|
||||
),
|
||||
ReleaseNote(
|
||||
version="1.7.4",
|
||||
headline="Chats survive a hiccup instead of stopping.",
|
||||
highlights=[
|
||||
"A dropped local connection retries and resumes the same answer instead of failing the message.",
|
||||
"The spawn composer steps aside when a window is open.",
|
||||
],
|
||||
fixes=[
|
||||
"App previews reconnect on their own after a backend restart.",
|
||||
"Dictation cue sounds default to a level you can actually hear.",
|
||||
],
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
@typechecked
|
||||
def release_notes(version: str) -> ReleaseNote | None:
|
||||
for note in P_RELEASES:
|
||||
if note.version == version:
|
||||
return note
|
||||
return None
|
||||
|
||||
|
||||
@typechecked
|
||||
def latest_release() -> ReleaseNote:
|
||||
return P_RELEASES[0]
|
||||
|
||||
|
||||
@typechecked
|
||||
def as_markdown(note: ReleaseNote) -> str:
|
||||
"""The GitHub release body; identical words to the in-app card, so nobody reads two stories."""
|
||||
lines = [f"## {note.version} — {note.headline}", ""]
|
||||
if note.highlights:
|
||||
lines.append("### New")
|
||||
lines += [f"- {h}" for h in note.highlights]
|
||||
lines.append("")
|
||||
if note.fixes:
|
||||
lines.append("### Fixed")
|
||||
lines += [f"- {f}" for f in note.fixes]
|
||||
return "\n".join(lines).strip()
|
||||
|
||||
|
||||
@typechecked
|
||||
def help_context_block(app_version: str) -> str:
|
||||
"""What the Help agent must know about what just changed, so "what's new" is never stale."""
|
||||
note = release_notes(app_version) or latest_release()
|
||||
body = [f"Version {note.version}: {note.headline}"]
|
||||
body += [f"- new: {h}" for h in note.highlights]
|
||||
body += [f"- fixed: {f}" for f in note.fixes]
|
||||
return "\n".join(body)
|
||||
|
||||
|
||||
@typechecked
|
||||
def all_versions() -> Dict[str, str]:
|
||||
return {n.version: n.headline for n in P_RELEASES}
|
||||
@@ -136,6 +136,9 @@ def p_issues_block() -> str:
|
||||
|
||||
|
||||
@typechecked
|
||||
from backend.apps.help.changelog import help_context_block
|
||||
|
||||
|
||||
def build_system_prompt(shortcuts: List[HelpShortcut], app_version: str) -> str:
|
||||
shortcut_lines = "\n".join(f"- {s.keys}: {s.action}" for s in shortcuts)
|
||||
os_name = "macOS" if IS_MAC else platform.system()
|
||||
@@ -158,6 +161,11 @@ def build_system_prompt(shortcuts: List[HelpShortcut], app_version: str) -> str:
|
||||
shortcut_lines,
|
||||
"</shortcuts>",
|
||||
"",
|
||||
"<whats_new>",
|
||||
"What actually changed in this build. Answer \"what's new\" from THIS, never from memory.",
|
||||
help_context_block(app_version),
|
||||
"</whats_new>",
|
||||
"",
|
||||
"<known_issues>",
|
||||
"The complete list of issues shipped with this build. You cannot see live bug reports.",
|
||||
p_issues_block(),
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
"""One release story, three surfaces. A release with no story, or a Help agent answering from a
|
||||
stale picture of the app, are both bugs this pins shut."""
|
||||
|
||||
from backend.apps.help.changelog import (
|
||||
all_versions, as_markdown, help_context_block, latest_release, release_notes,
|
||||
)
|
||||
from backend.apps.service.version import APP_VERSION
|
||||
|
||||
|
||||
def test_the_shipping_version_has_a_story():
|
||||
note = release_notes(APP_VERSION)
|
||||
assert note is not None, f"{APP_VERSION} ships with no release notes; write them before tagging"
|
||||
assert note.headline and (note.highlights or note.fixes)
|
||||
|
||||
|
||||
def test_notes_are_written_for_users_not_committers():
|
||||
for note in (latest_release(),):
|
||||
for line in note.highlights + note.fixes:
|
||||
assert not line.startswith("["), "no commit-style prefixes"
|
||||
assert "commit" not in line.lower() and "refactor" not in line.lower()
|
||||
assert "—" not in line and "–" not in line, "house style: no em/en dashes"
|
||||
|
||||
|
||||
def test_markdown_body_carries_the_same_words_as_the_app():
|
||||
note = latest_release()
|
||||
md = as_markdown(note)
|
||||
assert note.headline in md
|
||||
for line in note.highlights + note.fixes:
|
||||
assert line in md, "the GitHub body must not drift from the in-app card"
|
||||
|
||||
|
||||
def test_help_context_names_the_version_and_its_changes():
|
||||
block = help_context_block(APP_VERSION)
|
||||
assert APP_VERSION in block
|
||||
note = release_notes(APP_VERSION)
|
||||
assert note is not None
|
||||
assert note.highlights[0] in block
|
||||
|
||||
|
||||
def test_unknown_version_falls_back_to_the_latest_story_not_silence():
|
||||
assert release_notes("0.0.0") is None
|
||||
assert latest_release().version in help_context_block("0.0.0")
|
||||
assert len(all_versions()) >= 2
|
||||
Reference in New Issue
Block a user