diff --git a/loopx/bootstrap.py b/loopx/bootstrap.py index 4138655e83..36ed136faf 100644 --- a/loopx/bootstrap.py +++ b/loopx/bootstrap.py @@ -465,6 +465,22 @@ def apply_onboarding_todos_to_state( return "\n".join(lines) + "\n" +def render_objective_markdown(objective: str) -> str: + """Render objective prose as an isolated Markdown blockquote. + + Generated state grammar owns fences, comments, and headings. Raw + objective text is quoted line by line so a start-goal-collapsed or + unclosed fence, comment, or heading example inside the objective can + never open machine grammar and hide the generated Todo sections below. + The frontmatter ``objective`` field keeps the raw text; readers of the + body section strip the quote prefix to recover it. + """ + lines = str(objective or "").splitlines() + if not lines: + return "" + return "\n".join(f"> {line}" if line.strip() else ">" for line in lines) + + def render_state_markdown( *, project: Path, @@ -516,7 +532,7 @@ def render_state_markdown( ## Objective -{objective} +{render_objective_markdown(objective)} ## Authority Sources diff --git a/loopx/chat_server.py b/loopx/chat_server.py index 32ec182d90..378fc9a3ca 100644 --- a/loopx/chat_server.py +++ b/loopx/chat_server.py @@ -37,6 +37,7 @@ from .chat_ssh_source_api import SshSourceRequestMixin from .chat_store import ChatSessionStore from .capabilities.manager_runtime import manager_runtime_capability_projection +from .control_plane.goals.active_state_sections import active_state_section_text from .control_plane.status.ssh_host_catalog import ( SSH_HOST_CATALOG_PATH, ssh_host_catalog_payload, @@ -125,19 +126,7 @@ def _compact_text(value: Any, *, limit: int = 600) -> str: def _active_state_section(state_text: str, heading: str) -> str: - marker = f"## {heading}" - start = state_text.find(marker) - if start < 0: - return "" - content_start = start + len(marker) - end = state_text.find("\n## ", content_start) - section = state_text[content_start : end if end >= 0 else None] - lines = [ - line.strip().removeprefix("- ").strip() - for line in section.splitlines() - if line.strip() and not line.lstrip().startswith("