Skip to content

Bump to 0.17.0, publish the missing example pages, and route the agent orientation - #308

Merged
chris-colinsky merged 4 commits into
mainfrom
chore/bump-0170-and-example-docs
Oct 3, 2026
Merged

chris-colinsky merged 4 commits into
mainfrom
chore/bump-0170-and-example-docs

Conversation

@chris-colinsky

Copy link
Copy Markdown
Member

Release prep for v0.17.0, plus two defects the prep surfaced. No behaviour change beyond the collision message in the first commit.

The version bump lands in five places, not three

RELEASING.md names pyproject.toml and __version__. Two more track it and regenerate from it:

file why
pyproject.toml the tag must match it minus the v
src/openarmature/__init__.py __version__
tests/test_smoke.py asserts the two agree
uv.lock locks the local package's own version
src/openarmature/AGENTS.md the bundle stamps "version 0.17.0 (spec v0.118.2)" into its header

Both of the last two are version-only diffs. The conformance.toml since = "0.16.0" entries are historical records of which release a proposal shipped in and stay as they are.

The docs sweep found no stale wording, and one gap

The stale-reference half came up clean. I grepped for the renamed StructuredOutputInvalid fields, the pre-0122 extras shape, "silently dropped/lost/ignored", the old disable_llm_payload flag name, spec-pin references and the 0085 status. Nothing in docs/, README.md or examples/ carries wording this cycle invalidated.

What it found instead was sixteen examples on disk and thirteen pages on the site.

missing page shipped in
structured-output-reask #304
provider-extras #304
retrieval-rag v0.16.0

The examples were reaching the sdist and the bundled AGENTS.md index while the published site did not know they existed. The docs-in-sync rule says a page lands with its code; for two of these it did not, and #304 merged without them.

Three pages written in the established shape. Output sections come from real runs where credentials allowed: the reask demo and provider-extras against a local endpoint, retrieval-rag from its print statements with the indices marked as shape rather than expected values, since it needs an OpenAI and a Cohere key.

A guard now compares the example directories against both the pages and the mkdocs nav. The nav half matters on its own: a page absent from mkdocs.yml is unreachable even when the file exists, and mkdocs build --strict reports that as INFO rather than failing. Deleting either a page or a nav line fails the guard.

The agent orientation was in the wrong channel

docs/agent/tldr.md and docs/agent/non-obvious-shapes.md are the generator's input for the wheel's AGENTS.md, and they are deliberately out of the site nav because they are written in agent register rather than as browsable prose.

The site already has a channel for that register: the llmstxt plugin emits /llms.txt and /llms-full.txt, and mkdocs.yml's own comment says it exists to match the charter's agent-friendly framing. Those two files were the only content written for that audience and the only content the plugin did not carry — while mkdocs built them as unlinked HTML anyway, since it renders every page under docs_dir regardless of nav.

So they are now an llmstxt section, listed first because they are the orientation the rest reads against. The ingest goes 602KB to 624KB and the index gains a heading. The HTML stays unlinked, which is the right shape for a page whose job is to be the canonical URL an ingest entry points at.

Guarded against the plugin config rather than a rendered artifact, so it needs no site build.

One defect, found by running an example rather than by a test

The collision hint added in #304 chose between two messages by whether the colliding extras key was inherited from the base config. Both arms assume a per-attempt override exists.

examples/provider-extras has no retry config at all, and printed this in its own output:

extras key 'temperature' conflicts with the mapping-managed wire field
'temperature' (managed value 0.2, extras value 0.9); a managed field cannot
be overridden via extras. the per-attempt override sets 'temperature' as a
declared field and as an extras key; remove one

There is no per-attempt override. The caller put a declared field and an extras key of the same name in one config, and got told about an override they never wrote.

Three states, not two:

situation message
no override in play none; §8.1's message stands alone
override, colliding key inherited from base name the base's channel
override sets the key both ways itself say to remove one

Every unit test for that hint supplied a per_attempt_override, so the path was never exercised. This is the second time in three days that a single-shape test suite hid a defect in a field I had just changed, which is an argument for running the examples in the gate rather than only compiling them.

Evidence

Mutation Result
delete a docs page killed
delete a nav line killed
drop the llmstxt agent section killed
pass an empty set instead of None where no override applies killed
pytest                  2318 passed, 498 skipped
mkdocs build --strict   clean; Agent orientation leads /llms.txt
ruff / pyright          clean
manifest check          125 proposals, consistent

The new no-override test also asserts the request never reaches the transport, since the refusal happens pre-send.

Not in this PR

The CHANGELOG date. The 0.17.0 heading says 2026-09-25 and has to match the tag day, so it gets set at tag time rather than drifting again here.

The collision hint added two commits ago chose between two messages
by whether the colliding extras key was inherited from the base.
Both arms assume a per-attempt override exists. A caller who put a
declared field and an extras key of the same name in one config,
with no retry schedule anywhere, got told "the per-attempt override
sets it both ways" about an override they never wrote.

Three states, not two: no override, so no advice and the section 8.1
message stands alone; an override whose colliding key came from the
base, so name the base's channel; an override that sets the key both
ways itself, so say to remove one.

Found by running examples/provider-extras, which has no retry config
at all and printed the false advice in its own output. Every unit
test for the hint supplied an override, so the path was never
exercised. The new test covers it and asserts the request never goes
out, since the refusal happens before the transport is reached.
The version lands in five places, not the three the release doc
names: pyproject, __version__, the smoke-test assertion, and then
uv.lock and the bundled AGENTS.md, which both embed it and
regenerate from it.

The docs sweep found no stale wording. What it found instead was
sixteen examples on disk and thirteen pages on the site. Two of the
three missing shipped in the previous commit range and one has been
missing since 0.16.0, so the examples were reaching the sdist and
the bundled index while the published site did not know they
existed. The docs-in-sync rule says a page lands with its code, and
it did not.

Three pages written in the established shape, with their output
sections taken from real runs where credentials allowed: the reask
demo and provider-extras against a local endpoint, retrieval-rag
from its print statements with the indices marked as shape rather
than expected values, since it needs two API keys.

A guard now compares the example directories against both the pages
and the mkdocs nav. The nav half matters on its own: a page missing
from it is unreachable even when the file exists, and mkdocs reports
that as INFO rather than failing the strict build. Removing either a
page or a nav line fails the guard.
docs/agent/tldr.md and docs/agent/non-obvious-shapes.md are the
generator's input for the AGENTS.md bundled in the wheel, and they
are deliberately not in the site nav: they are written in agent
register rather than as browsable prose.

The site already has a channel for that register. The llmstxt plugin
emits /llms.txt and /llms-full.txt for exactly this audience, and
those two files were the only content written for it and the only
content the plugin did not carry. Meanwhile mkdocs builds every page
under docs_dir whether the nav references it or not, so they were
publishing as unlinked HTML through the browse path they were never
written for.

Now an llmstxt section, listed first because it is the orientation
the rest reads against. The ingest grows 602KB to 624KB and the
index gains a heading; the HTML stays unlinked, which is what a page
that exists to be the canonical URL for an ingest entry should be.

Guarded against the plugin config rather than a rendered artifact,
so it needs no site build. Nothing failed before: a missing section
is silent, and the strict build calls an unreferenced page INFO.
Copilot AI balanced review requested due to automatic review settings October 3, 2026 02:23

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

The navigation guard still passes when an example’s navigation entry is commented out.

Review effort: Balanced
Findings: 1 Medium severity · 1 Low severity

Open (2)
What changed in this PR

Prepares OpenArmature v0.17.0, fills documentation gaps, and corrects misleading provider collision advice.

Changes:

  • Synchronizes package version references.
  • Publishes three example pages and routes agent orientation into LLM documentation feeds, with regression guards.
  • Suppresses per-attempt override advice when no override applies.
File Description
uv.lock Updates local package version.
tests/​unit/​test_llm_provider.py Tests collisions without overrides.
tests/​test_smoke.py Updates expected version.
tests/​test_examples_smoke.py Adds example publication guard.
tests/​test_agents_md_drift.py Guards agent feed inclusion.
src/​openarmature/​llm/​providers/​openai.py Corrects collision advice conditions.
src/​openarmature/​AGENTS.md Updates generated version stamp.
src/​openarmature/​__init__.py Sets runtime version.
pyproject.toml Sets release version.
mkdocs.yml Adds example navigation and agent feed section.
docs/​examples/​structured-output-reask.md Documents structured-output recovery.
docs/​examples/​retrieval-rag.md Documents retrieval and reranking.
docs/​examples/​provider-extras.md Documents extras behavior.
docs/​examples/​index.md Links the three added pages.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread tests/test_examples_smoke.py Outdated
Comment thread docs/examples/provider-extras.md Outdated
Two review findings, both in work added by this PR.

The example-docs guard searched mkdocs.yml as text, so a commented-out
nav entry satisfied it while the page had genuinely left the
navigation. Verified: commenting the line out left the guard passing.
A mention in a comment or a path under `plugins:` would have done the
same. That is the defect the guard exists to catch, one level up, and
the same shape as the sdist guard this PR replaced for asserting on
the exclude list's text rather than the built artifact.

It now loads the config and walks `nav` recursively. SafeLoader
rejects mkdocs-material's python tags, so the loader ignores unknown
tags rather than slicing the nav block out by line offsets, which
would be text matching again. Both removing and commenting out a nav
line now fail it.

The provider-extras page said structural keys reject always, two
bullets after saying a matching value is a no-op. Both cannot hold,
and the code sides with the second: the reject arm returns early when
the extras value equals the managed one, with no special case for
structural keys.

What is actually unconditional about model, messages, tools and
tool_choice is WHEN they are managed, not how they reject. Every
other reject-arm key is managed only where the mapping produced it,
so an unset temperature lets an extras temperature through. The
structural four are managed regardless, so a conflict rejects even
where the body carries nothing of that name, which is what stops an
extras tool array reaching the wire on a no-tools call. All four
arms of the rewritten claim were probed against the real provider.
@chris-colinsky
chris-colinsky merged commit 4ae59c9 into main Oct 3, 2026
6 checks passed
@chris-colinsky
chris-colinsky deleted the chore/bump-0170-and-example-docs branch October 3, 2026 21:19
chris-colinsky added a commit that referenced this pull request Oct 5, 2026
The release workflow compares pyproject's version to the pushed tag
through packaging.version.Version, so an rc needs the rc version in
pyproject: Version("0.17.0") != Version("0.17.0rc1") and the test job
fails before any publish step runs.

RELEASING.md calls for these as two separate commits, one before each
tag, because the normalized forms differ. The bump in #308 was the
real-release form done early, correct for v0.17.0 and wrong for the rc.
v0.16.0-rc1 carried 0.16.0rc1 in all three places and v0.16.0 carried
0.16.0, which is the precedent.

Five files rather than three: uv.lock locks the local package's own
version, and the bundled AGENTS.md stamps it into its header. Both
regenerate.

Verified against the workflow's own comparison: the rc tag matches and
the real-release tag does not, which is what the second bump is for.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants