Skip to content

Commit 1d26c82

Browse files
Address PR #72 review: generator hardening + bundle hygiene
CodeQL / github-code-quality flagged implicit string concatenation in three list literals in scripts/build_agents_md.py (_capability_summaries, _patterns, _example_index). Switched to explicit + concatenation across all three sites — same intent (multi-line string assembly for readability), no possibly-missing- comma ambiguity. CoPilot flagged four bundle-quality issues: 1. _assert_pin_at_tag used reverse lexicographic sort, which gets multi-digit semver wrong (v0.10.0 < v0.9.0 lexicographically). Switched to git tag --sort=-version:refname for native version-aware ordering. No new Python deps. 2. Extracted spec sections retained ## 1. Purpose headings that were higher-level than the wrapping ### Capability: header. _extract_sections_1_2 now demotes ATX headings by two levels so the bundled markdown maintains a clean hierarchy. 3. Patterns were inlined verbatim with their original # headings (multiple H1s under the bundle) and relative ../concepts/...md / ../examples/...md links (broken in the installed wheel). New _transform_pattern_content demotes ATX headings by two levels and rewrites relative doc-tree links to absolute openarmature.ai/<section>/<name>/ URLs via _PATTERN_LINK_RE. 4. Bundle header / TLDR / discovery footer used openarmature.ai/capabilities/ for spec links, which isn't a real URL (.ai serves python docs; .org serves spec). Normalized those three sites to openarmature.org/capabilities/. The pattern files' existing .org URLs stay correct.
1 parent fe7e6d9 commit 1d26c82

3 files changed

Lines changed: 166 additions & 84 deletions

File tree

docs/agent/tldr.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
OpenArmature is a workflow framework for LLM pipelines and tool-calling agents — typed state, compile-time topology checks, observability, and crash-safe checkpoints baked into a graph engine. The graph layer has no concept of LLMs or tools; the same primitives drive deterministic ETL pipelines and tool-calling agents alike. Nodes return partial updates; the engine merges into a frozen state snapshot. Behavior is defined by [openarmature-spec](https://openarmature.ai/capabilities/) and verified by conformance fixtures; this package is the reference Python implementation.
1+
OpenArmature is a workflow framework for LLM pipelines and tool-calling agents — typed state, compile-time topology checks, observability, and crash-safe checkpoints baked into a graph engine. The graph layer has no concept of LLMs or tools; the same primitives drive deterministic ETL pipelines and tool-calling agents alike. Nodes return partial updates; the engine merges into a frozen state snapshot. Behavior is defined by [openarmature-spec](https://openarmature.org/capabilities/) and verified by conformance fixtures; this package is the reference Python implementation.
22

33
**What OpenArmature is NOT:** not a chat framework (no built-in messages channel), not an LLM SDK (Provider is the abstraction layer; OpenAIProvider is the canonical impl), not a state-management library (state is per-invocation, not application-wide), not an evaluation framework (deferred to `openarmature-eval`).

scripts/build_agents_md.py

Lines changed: 100 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737

3838
from __future__ import annotations
3939

40+
import re
4041
import subprocess
4142
import sys
4243
from pathlib import Path
@@ -84,19 +85,31 @@ def _assert_pin_at_tag() -> str:
8485
Returns the tag name (e.g., ``v0.22.1``). Raises ``RuntimeError``
8586
on a non-tag pin so a release can't accidentally ship a bundle
8687
pinned to a draft spec commit.
88+
89+
Prefers the highest semver tag when multiple ``v*`` tags point at
90+
the same SHA (the v0.19.0 / v0.20.0 / v0.20.1 retag during the
91+
fixture 052 backport produced this shape). Uses git's native
92+
``--sort=-version:refname`` rather than Python lexicographic sort,
93+
which mis-orders multi-digit versions (``v0.9.0`` lex-sorts after
94+
``v0.10.0``).
8795
"""
8896
sha = _git_in_spec("rev-parse", "HEAD")
89-
tags_out = _git_in_spec("tag", "--points-at", sha, "--list", "v*")
97+
tags_out = _git_in_spec(
98+
"tag",
99+
"--sort=-version:refname",
100+
"--points-at",
101+
sha,
102+
"--list",
103+
"v*",
104+
)
90105
if not tags_out:
91106
raise RuntimeError(
92107
f"submodule HEAD {sha[:8]} is not at a v* tag; "
93108
f"bundle build refuses to read draft (untagged) spec text. "
94109
f"Pin the submodule to a published tag before regenerating."
95110
)
96-
# Prefer the highest version tag if multiple point at the same SHA
97-
# (e.g., during a re-tag) — sort by version-string descending.
98-
tags = sorted(tags_out.splitlines(), reverse=True)
99-
return tags[0]
111+
# Git's version-aware descending sort puts the highest semver tag first.
112+
return tags_out.splitlines()[0]
100113

101114

102115
def _read_pinned_spec(path_in_spec: str) -> str:
@@ -121,7 +134,7 @@ def _header(version: str, spec_tag: str) -> str:
121134
f"*This is the agent guide bundled with the openarmature Python package, "
122135
f"version {version} (spec {spec_tag}). For the full docs site see "
123136
f"[openarmature.ai](https://openarmature.ai). For the canonical spec text see "
124-
f"[openarmature.ai/capabilities](https://openarmature.ai/capabilities/). "
137+
f"[openarmature.org/capabilities](https://openarmature.org/capabilities/). "
125138
f"For project-specific conventions for the code you're editing, see the host "
126139
f"project's `AGENTS.md` or `CLAUDE.md`.*"
127140
)
@@ -133,7 +146,17 @@ def _tldr() -> str:
133146

134147

135148
def _extract_sections_1_2(spec_text: str) -> str:
136-
"""Extract content between ``## 1.`` and ``## 3.`` (inclusive of §1+§2)."""
149+
"""Extract content between ``## 1.`` and ``## 3.`` (inclusive of §1+§2).
150+
151+
Demotes ATX headings by two levels so the bundled markdown's
152+
hierarchy stays consistent: the wrapping ``### Capability: ...``
153+
H3 sits above the extracted ``## 1. Purpose`` rendered as
154+
``#### 1. Purpose`` (H4). Any deeper nested headings inside §1+§2
155+
(e.g., ``### State``) preserve their relative depth one step
156+
deeper. Without this demotion, the spec's H2 headings would
157+
appear higher in the document than the H3 they sit under,
158+
breaking TOC rendering and navigation.
159+
"""
137160
out: list[str] = []
138161
in_target = False
139162
for line in spec_text.splitlines():
@@ -142,6 +165,9 @@ def _extract_sections_1_2(spec_text: str) -> str:
142165
elif line.startswith("## 3."):
143166
break
144167
if in_target:
168+
if line.startswith("#"):
169+
# Demote ATX heading by two levels.
170+
line = "##" + line
145171
out.append(line)
146172
if not out:
147173
raise RuntimeError(
@@ -152,13 +178,19 @@ def _extract_sections_1_2(spec_text: str) -> str:
152178

153179

154180
def _capability_summaries(spec_tag: str) -> str:
181+
# Long-string entries use explicit ``+`` concat (not Python's
182+
# implicit adjacent-string-literal concat) so CodeQL / static
183+
# analyzers don't flag the pattern as a possibly-missing comma
184+
# inside the list literal.
155185
sections = [
156186
"## Capability contracts",
157187
"",
158-
f"_Sourced from openarmature-spec {spec_tag}. Each entry below "
159-
f"reproduces §1 (Purpose) and §2 (Concepts) of the capability's "
160-
f"`spec.md`. For the full spec text (execution model, error semantics, "
161-
f"determinism, observer hooks, etc.) see the linked docs site._",
188+
(
189+
f"_Sourced from openarmature-spec {spec_tag}. Each entry below "
190+
+ "reproduces §1 (Purpose) and §2 (Concepts) of the capability's "
191+
+ "`spec.md`. For the full spec text (execution model, error semantics, "
192+
+ "determinism, observer hooks, etc.) see the linked docs site._"
193+
),
162194
]
163195
for cap in CAPABILITIES:
164196
text = _read_pinned_spec(f"spec/{cap}/spec.md")
@@ -169,17 +201,64 @@ def _capability_summaries(spec_tag: str) -> str:
169201
return "\n".join(sections)
170202

171203

204+
_PATTERN_LINK_RE = re.compile(r"\(\.\./(concepts|examples)/([^)]+?)\.md\)")
205+
206+
207+
def _transform_pattern_content(text: str) -> str:
208+
"""Bundle-side rewrite of a pattern doc's markdown.
209+
210+
Two transforms applied for the wheel-shipped bundle (the source
211+
files in ``docs/patterns/`` stay unchanged — they're MkDocs source
212+
where relative links work correctly):
213+
214+
1. **Demote ATX headings by two levels.** Pattern files open with
215+
``# Title`` (H1); inlined verbatim under the bundle's
216+
``## Patterns`` H2, those H1s would create multiple top-level
217+
headings in the same document. Prepending ``##`` to every
218+
``#``-prefixed line puts pattern titles at H3 (under
219+
``## Patterns``) and preserves the relative depth of any
220+
deeper nested headings.
221+
222+
2. **Rewrite relative doc-tree links to absolute docs-site URLs.**
223+
Patterns link to ``../concepts/<name>.md`` and
224+
``../examples/<name>.md`` — relative paths that resolve in the
225+
MkDocs source tree but break in the installed wheel (no docs/
226+
tree present). The MkDocs site strips ``.md`` and serves at
227+
``/<section>/<name>/``, so the rewrite is mechanical.
228+
``../<section>/index.md`` collapses to the section root.
229+
"""
230+
# Demote headings.
231+
demoted: list[str] = []
232+
for line in text.splitlines():
233+
if line.startswith("#"):
234+
line = "##" + line
235+
demoted.append(line)
236+
out = "\n".join(demoted)
237+
238+
# Rewrite relative doc-tree links.
239+
def _rewrite(m: re.Match[str]) -> str:
240+
section, name = m.group(1), m.group(2)
241+
if name == "index":
242+
return f"(https://openarmature.ai/{section}/)"
243+
return f"(https://openarmature.ai/{section}/{name}/)"
244+
245+
return _PATTERN_LINK_RE.sub(_rewrite, out)
246+
247+
172248
def _patterns() -> str:
249+
# See ``_capability_summaries`` for the explicit-concat rationale.
173250
sections = [
174251
"## Patterns",
175252
"",
176-
"_Recipes that compose the primitives. Not framework contracts — "
177-
"these are how to do common things idiomatically._",
253+
(
254+
"_Recipes that compose the primitives. Not framework contracts — "
255+
+ "these are how to do common things idiomatically._"
256+
),
178257
]
179258
pattern_files = sorted(p for p in (DOCS / "patterns").glob("*.md") if p.name != "index.md")
180259
for pf in pattern_files:
181260
sections.append("")
182-
sections.append(pf.read_text().rstrip())
261+
sections.append(_transform_pattern_content(pf.read_text()).rstrip())
183262
return "\n".join(sections)
184263

185264

@@ -214,12 +293,15 @@ def _extract_first_docstring_paragraph(source: str) -> str:
214293

215294

216295
def _example_index() -> str:
296+
# See ``_capability_summaries`` for the explicit-concat rationale.
217297
sections = [
218298
"## Example index",
219299
"",
220-
"_Runnable example programs shipped in the source tree at `examples/`. "
221-
"The full code is not bundled here (each example is 300+ lines); read "
222-
"the file at the listed path to see the canonical shape for that use case._",
300+
(
301+
"_Runnable example programs shipped in the source tree at `examples/`. "
302+
+ "The full code is not bundled here (each example is 300+ lines); read "
303+
+ "the file at the listed path to see the canonical shape for that use case._"
304+
),
223305
"",
224306
]
225307
for ex in sorted(EXAMPLES.glob("*/main.py")):
@@ -236,7 +318,7 @@ def _discovery_footer() -> str:
236318
"If your question isn't covered above, look here:\n"
237319
"\n"
238320
"- **Full docs site:** [openarmature.ai](https://openarmature.ai)\n"
239-
"- **Spec text:** [openarmature.ai/capabilities](https://openarmature.ai/capabilities/)\n"
321+
"- **Spec text:** [openarmature.org/capabilities](https://openarmature.org/capabilities/)\n"
240322
"- **API reference:** [openarmature.ai/reference](https://openarmature.ai/reference/)\n"
241323
"- **Host project conventions:** the project's own `AGENTS.md` / `CLAUDE.md`\n"
242324
)

0 commit comments

Comments
 (0)