Skip to content

Commit 31fd3b5

Browse files
Address PR #72 round 2: wording precision + pattern intra-links
Three "reachable from" → "AT" wording fixes (build_agents_md.py module docstring, .github/workflows/ci.yml comment, CHANGELOG.md entry). The strict check (git tag --points-at HEAD) is the load-bearing invariant — a wheel built from a commit between two release tags would silently ship as if it were the prior tag. Wording now matches implementation; the check is unchanged. Module docstring's section-4 description previously said patterns were inlined "verbatim", which became stale when _transform_pattern_content landed earlier in this PR. Updated to describe both transforms (heading demotion + relative-link rewrite). _transform_pattern_content gains a second regex, _PATTERN_INTRA_LINK_RE, that rewrites pattern-to-pattern bare-name .md references to in-document anchors (e.g., `(bypass-if-output-exists.md)` → `(#bypass-if-output-exists)`). The demoted H3 heading slug matches the filename slug, so the anchor resolves cleanly in the bundled single-file context. Closes the broken pattern cross-link in tool-dispatch-as-node's "Cross-references" section.
1 parent 6a68663 commit 31fd3b5

4 files changed

Lines changed: 35 additions & 12 deletions

File tree

.github/workflows/ci.yml

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,13 @@ jobs:
2929
- name: Fetch submodule tags
3030
# actions/checkout's submodule clone is shallow and doesn't
3131
# carry tags. ``scripts/build_agents_md.py`` asserts the
32-
# submodule HEAD is reachable from a ``v*`` tag (refuses to
33-
# bundle draft spec text); ``tests/test_agents_md_drift.py``
34-
# runs that assertion. Fetch tag refs only — the HEAD commit
35-
# is already present from the submodule checkout, and we
36-
# don't need history beyond what tags point at.
32+
# submodule HEAD is AT a ``v*`` tag (``git tag --points-at
33+
# HEAD``; refuses to bundle draft spec text or text from
34+
# a commit between two release tags);
35+
# ``tests/test_agents_md_drift.py`` runs that assertion.
36+
# Fetch tag refs only — the HEAD commit is already present
37+
# from the submodule checkout, and we don't need history
38+
# beyond what tags point at.
3739
run: git -C openarmature-spec fetch --tags
3840

3941
- name: Install uv

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). The
88

99
### Added
1010

11-
- **Bundled agent documentation at `openarmature/AGENTS.md`.** The wheel now ships a generated `AGENTS.md` file at the installed package root, agent-discoverable via `python -c "import openarmature; print(openarmature.__path__[0] + '/AGENTS.md')"`. Sections include a TL;DR, capability summaries pulled from the pinned spec submodule's §1 (Purpose) + §2 (Concepts), the patterns docs, hand-written non-obvious-shapes recipes, and a one-line example index. Generator lives at `scripts/build_agents_md.py`; the committed file is CI-drift-checked by `tests/test_agents_md_drift.py`. The submodule pin discipline (build refuses unless the submodule HEAD is reachable from a `v*` tag) prevents draft spec text from leaking into a release bundle. Adopting projects can point their own `AGENTS.md` / `CLAUDE.md` at this path so agent sessions in their codebase find it automatically.
11+
- **Bundled agent documentation at `openarmature/AGENTS.md`.** The wheel now ships a generated `AGENTS.md` file at the installed package root, agent-discoverable via `python -c "import openarmature; print(openarmature.__path__[0] + '/AGENTS.md')"`. Sections include a TL;DR, capability summaries pulled from the pinned spec submodule's §1 (Purpose) + §2 (Concepts), the patterns docs, hand-written non-obvious-shapes recipes, and a one-line example index. Generator lives at `scripts/build_agents_md.py`; the committed file is CI-drift-checked by `tests/test_agents_md_drift.py`. The submodule pin discipline (build refuses unless the submodule HEAD is AT a `v*` tag via `git tag --points-at HEAD`) prevents draft (untagged) spec text — or text from a commit between two release tags — from leaking into a release bundle. Adopting projects can point their own `AGENTS.md` / `CLAUDE.md` at this path so agent sessions in their codebase find it automatically.
1212
- **`FanOutInstanceProgress.result_is_error` field** (proposal 0027, accepted in spec v0.21.0). Explicit boolean discriminator on each per-instance entry in `CheckpointRecord.fan_out_progress``True` for `collect`-mode error contributions (roll forward into `errors_field`), `False` for success contributions (roll forward into `target_field`). The engine reads the explicit field on resume rather than inferring routing from `result`'s shape; the previous structural heuristic (`_looks_like_error_record`) is removed. Backward-compat path on load: pre-0027 records that omit the key default to `False`.
1313
- **Strict `CheckpointRecordInvalid` on fan-out count drift** (proposal 0029, accepted in spec v0.22.0). When the resumed run's resolved instance count differs from the saved `fan_out_progress` entry's `instance_count`, the engine raises `CheckpointRecordInvalid` before any fan-out instance work runs on the resumed path. Replaces the pre-0029 pad/truncate behavior which silently dropped `completed` contributions on shrink (breaking §10.11.1's exactly-once guarantee) and dispatched unsaved work on grow.
1414
- **`tool_choice` parameter on `Provider.complete()`** (proposal 0025, accepted in spec v0.20.0). Optional discriminated-union value constraining the model's tool-calling behavior — one of `"auto"`, `"required"`, `"none"`, or a `ForceTool(name=...)` record. Validation runs pre-send: `"required"` and `ForceTool` both demand non-empty `tools`, and `ForceTool.name` must appear in the supplied list; violations raise `ProviderInvalidRequest` (§7's existing category — no new error category). When `tool_choice` is `None` (the default) the wire field is omitted and the provider's own default applies, preserving pre-0025 behavior exactly. The `OpenAIProvider` maps the spec shape onto OpenAI's wire shape per §8.1.1 (the `ForceTool.type="tool"` renames to wire `type="function"`).

scripts/build_agents_md.py

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,15 @@
1313
capability spec, read from the pinned ``openarmature-spec``
1414
submodule via ``git show <sha>:spec/...`` rather than the
1515
working tree.
16-
4. ``docs/patterns/*.md`` — verbatim concatenation of the patterns
17-
docs (excluding ``index.md``).
16+
4. ``docs/patterns/*.md`` — concatenation of the patterns docs
17+
(excluding ``index.md``), with bundle-side transforms applied
18+
in ``_transform_pattern_content``: ATX headings demoted by two
19+
levels (so a pattern's ``# Title`` H1 renders as ``### Title``
20+
H3 under the bundle's ``## Patterns`` H2) and relative
21+
``../concepts/...md`` / ``../examples/...md`` links rewritten
22+
to absolute ``openarmature.ai`` URLs (the relative paths
23+
resolve in the MkDocs source tree but not in the installed
24+
wheel).
1825
5. ``docs/agent/non-obvious-shapes.md`` — hand-written opinionated
1926
recipes.
2027
6. Example index — one-line description + path for each
@@ -25,8 +32,9 @@
2532
Build-time invariants (matches proposal-0028 follow-on review's
2633
submodule-pin discipline):
2734
28-
- Submodule HEAD MUST be reachable from a ``v*`` tag. The build
29-
refuses to read draft (untagged) spec text into a release bundle.
35+
- Submodule HEAD MUST be AT a ``v*`` tag (``git tag --points-at HEAD``).
36+
The build refuses to read draft (untagged) spec text — or text from
37+
a commit between two release tags — into a release bundle.
3038
- Spec text is read from the pinned commit via ``git show``, NOT
3139
from the submodule working tree. Closes the "submodule HEAD
3240
moved but bundle still reads stale tree" failure mode.
@@ -203,6 +211,13 @@ def _capability_summaries(spec_tag: str) -> str:
203211

204212
_PATTERN_LINK_RE = re.compile(r"\(\.\./(concepts|examples)/([^)]+?)\.md\)")
205213

214+
# Matches bare-name ``.md`` references in the patterns markdown
215+
# (pattern-to-pattern links like ``(bypass-if-output-exists.md)``).
216+
# The negative lookahead skips ``../`` parent-relative paths
217+
# (handled by ``_PATTERN_LINK_RE``), ``http(s)://`` absolute URLs,
218+
# and ``#`` in-document anchors.
219+
_PATTERN_INTRA_LINK_RE = re.compile(r"\((?!\.\.|https?://|#)([a-z0-9-]+)\.md\)")
220+
206221

207222
def _transform_pattern_content(text: str) -> str:
208223
"""Bundle-side rewrite of a pattern doc's markdown.
@@ -242,7 +257,13 @@ def _rewrite(m: re.Match[str]) -> str:
242257
return f"(https://openarmature.ai/{section}/)"
243258
return f"(https://openarmature.ai/{section}/{name}/)"
244259

245-
return _PATTERN_LINK_RE.sub(_rewrite, out)
260+
out = _PATTERN_LINK_RE.sub(_rewrite, out)
261+
# Rewrite intra-pattern links to in-document anchors. Bare-name
262+
# ``.md`` references render fine on the MkDocs site (sibling-file
263+
# resolution) but break in the bundled single-file AGENTS.md.
264+
# The demoted H3 heading slug matches the filename slug — e.g.,
265+
# ``(bypass-if-output-exists.md)`` → ``(#bypass-if-output-exists)``.
266+
return _PATTERN_INTRA_LINK_RE.sub(lambda m: f"(#{m.group(1)})", out)
246267

247268

248269
def _patterns() -> str:

src/openarmature/AGENTS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -815,7 +815,7 @@ for malformed `ToolCall.arguments`, and trace output.
815815

816816
- Tools have side effects you can't replay safely on resume. Wrap
817817
each side-effecting tool with the
818-
[bypass-if-output-exists](bypass-if-output-exists.md) pattern so
818+
[bypass-if-output-exists](#bypass-if-output-exists) pattern so
819819
a crashed run resumes without re-side-effecting.
820820
- The "tools" are long-running async pipelines, not function
821821
calls. Model them as subgraphs and let the LLM node route via

0 commit comments

Comments
 (0)