Skip to content

[AAASM-5661] 🐛 (python-sdk): Say what the offline quick-start configuration evaluates - #318

Merged
Chisanan232 merged 9 commits into
mainfrom
v0.0.1/AAASM-5661/fix/quickstart_interceptor
Aug 14, 2026
Merged

[AAASM-5661] 🐛 (python-sdk): Say what the offline quick-start configuration evaluates#318
Chisanan232 merged 9 commits into
mainfrom
v0.0.1/AAASM-5661/fix/quickstart_interceptor

Conversation

@Chisanan232

@Chisanan232 Chisanan232 commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Description

docs/quick-start.md told a reader that after init_assembly() "every tool call from this
point on is routed through the policy gate". Measured on the configuration the page hands them —
init_assembly(gateway_url=..., api_key=..., agent_id=..., mode="sdk-only"), nothing listening on
the gateway URL, and a pure-Python pip install agent-assembly with no native
agent_assembly._core — no policy is evaluated at all. The SDK installs a deny-all
_FailClosedInterceptor (AAASM-4760) and refuses each governed tool call with a reason naming the
absent extension. Measured through Agno's own FunctionCall.execute:

RESULT: status='failure' error="[BLOCKED by governance policy] governance unavailable: the native
agent_assembly._core extension is not installed ..."
FILE EXISTS: False

So the reader's tools do not run — and nothing decides that they shouldn't.

Which branch of AC1 this takes, and why. The documentation branch. Installing a real
interceptor in that configuration is not achievable: build_governance_interceptor has exactly
three outcomes, and each needs an authority the offline example has none of — a connected
RuntimeClient (connect_runtime_client returns None with no native extension) or a gateway to
register against. With the native extension present and no gateway, init_assembly does not even
return: register_agent raises and enforce re-raises it as ConfigurationError. The SDK cannot
synthesise a policy source, so the honest fix is to correct the sentences, in ADR 0033 §6 terms —
Degraded, not Evaluated — and to say what the reader gets and what is needed to get a
decision from the SDK instead.

AC2. test/unit/test_quickstart_documented_configuration.py runs the page's four keyword
arguments verbatim (no enforcement_mode, because the page never mentions one) with no
install_fake_core(), and drives the governed call through Agno's real chokepoint rather than the
SDK's internal chain — so "an interceptor is installed" is observed, not assumed.

AC3. _governance_unavailable's fail-open branch returned the bare GatewayClient — which
exposes no check_tool_start, so the adapters allow — and emitted nothing. The only loud signal on
that configuration was about registration. It now writes an enforcement notice naming the gap and
the remedy, scoped to the fail-open branch.

Type of Change

  • 🔧 Bug fix
  • 📚 Documentation update

Breaking Changes

  • No

New stderr output on the observe / disabled fail-open path. No API or behaviour change.

Related Issues

  • Related JIRA ticket: AAASM-5661
  • Python counterpart of AAASM-4991 (the Node no-op default); resolves the AAASM-5661 unproven
    reasons AAASM-5529 left in test_quickstart_claim_bindings.py for the post-init claims.

Testing

  • Unit tests added/updated
  • Manual testing performed

The new control was proven able to fail. With the enforcement path severed (the fail-open
posture that predates AAASM-4760 restored in _governance_unavailable):

$ .venv/bin/python -m pytest test/unit/test_quickstart_documented_configuration.py -q --no-cov
3 failed, 4 passed
exit=1

failing on test_a_governance_hook_is_installed_on_agnos_own_tool_path (the Agno tool body runs and
the file appears), test_the_refusal_is_the_fail_closed_posture_rather_than_a_policy_decision, and
test_startup_reports_both_the_registration_and_the_enforcement_gap. With the path restored:

$ .venv/bin/python -m pytest test/unit/test_quickstart_documented_configuration.py -q --no-cov
7 passed
exit=0

Full suite: 1351 passed, 16 skipped. ruff check . clean, pre-commit run --all-files clean for
every file this PR touches.

Checklist

  • Code follows project style guidelines
  • Self-review completed
  • Comments added for complex logic
  • Documentation updated if needed
  • All tests passing

Not done here, and why

  • The three tab comments at docs/quick-start.md:141, :321, :468 ("init_assembly()
    installs a no-op hook; we revert it") are code comments inside the
    BEGIN GENERATED: quickstart-framework-tabs region, generated from quickstart_snippets/, which
    is vendored verbatim from ai-agent-assembly/examples. They are stale rather than merely
    imprecise — the hook has been deny-all since AAASM-4760, not a no-op — but rewriting the vendored
    copy would silently diverge from the upstream repo that owns them. TestTheWorkaroundTheFramework TabsCarry pins the step instead, so an upstream change that drops it turns the new prose red.
    A follow-up in examples is needed.
  • auto-start-probes-and-starts-a-gateway stays unproven in the bindings gate. It names a
    separate measured defect (the [project.scripts] aasm console script shadows the bundled binary,
    and the shadowing one has no start subcommand), which is a packaging change, not this one.
  • _FailClosedInterceptor sets no _enforce attribute, so _interceptor_enforces() reports
    False for it. Harmless today — its check_tool_start returns a known deny, which normalises
    the same either way — but the flag is inconsistent with RuntimeQueryInterceptor.

…iguration

The AAASM-5529 controls each call install_fake_core() before running
init_assembly, supplying an authoritative native runtime that the
configuration docs/quick-start.md hands a reader does not have. They are
therefore structurally unable to observe what the documented path does.

This control runs the page's four keyword arguments verbatim — with no
enforcement_mode, because the page never mentions one — against a
pure-Python install, and drives the governed call through Agno's own
FunctionCall.execute rather than the SDK's internal chain, so "a hook is
installed" is observed rather than assumed.

Refs AAASM-5661
_governance_unavailable's fail-open branch returns the bare GatewayClient,
which exposes no check_tool_start, so the adapters' missing-interceptor
fallback allows and a governed tool call reaches its body with no
in-process decision behind it. It emitted nothing at all; the only loud
signal on that configuration was _warn_agent_unregistered, which is about
registration, so a caller who read it still had no way to learn that
enforcement was gone too.

Emit a stderr notice naming the gap and the remedy, scoped to the
fail-open branch so a fail-closed caller is not told the opposite.

Refs AAASM-5661
Three framework tabs revert the hook init_assembly() installs and
re-apply one wired to the example's own LocalPolicyEngine. Their comments
call it a no-op hook, which has been false since AAASM-4760 — it is
deny-all — and the tab bodies are generated from quickstart_snippets/,
vendored from the examples repo, so their comments are not this repo's to
rewrite. The prose that explains the step is, and it needs a control that
turns red if an upstream snippet drops it.

Also asserts both startup warnings the documented configuration emits:
registration on stderr, the enforcement gap through warnings.

Refs AAASM-5661
The page told a reader that after init_assembly() "every tool call from
this point on is routed through the policy gate". Measured on the
configuration the page hands them — no gateway listening, no native
agent_assembly._core on a pure-Python install — nothing evaluates any
policy: the SDK installs a deny-all fail-closed hook and refuses each
governed tool call with a reason naming the absent extension.

Installing a real interceptor there is not achievable: with no runtime
and no gateway there is no authority to consult, and the SDK cannot
synthesise one. So the sentences are corrected rather than the code, in
ADR 0033 §6 terms — Degraded, not Evaluated — and a new section says what
the reader gets and what the SDK needs to decide instead.

Every reworded sentence is rebound to test_quickstart_documented_
configuration.py, which runs the page's arguments with no fake native
core, so the claims no longer rest on controls that supply the authority
the documented path lacks.

Refs AAASM-5661
The claim-bindings gate checks a named control still exists; it cannot
check the control still passes. ci.yaml does not run on a docs-only PR,
which is precisely the change that rewords a claim, so the named evidence
went unexecuted on exactly those PRs.

Refs AAASM-5661
@codecov

codecov Bot commented Aug 14, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Three unproven_reason referents named AAASM-5661 — the ticket this PR
closes. On merge each would have resolved to finished work that never
intended to deliver the capability, which is the stale-referent failure
AAASM-5750 exists to eliminate.

  auto-start-probes-and-starts-a-gateway        -> AAASM-5760
  no-arg-init-connects-and-appears-in-dashboard -> AAASM-5760
  gateway-returns-allow-deny-decisions          -> AAASM-5758

AAASM-5760 carries both measurements verbatim: the aasm console script
shadowing the bundled binary is its defect #1, and the gateway-less call
raising rather than degrading is its defect #2. AAASM-5758 runs each
documented quick-start from published artifacts against a real gateway,
which is the only place a claim about what a gateway returns can be shown
true; it lists AAASM-5661 among its blockers, so it cannot close first.

The shared _DOCUMENTED_PATH_UNMEASURED body no longer carries a ticket of
its own — that is what let one referent serve two claims with different
owners.

Refs AAASM-5661, AAASM-5760, AAASM-5758
…irst

The stale-referent rule compared against one string, so it banned the
module's original author and nobody after them — the one author who was
never going to break it. AAASM-5661 edited these bindings, pointed three
unproven claims at itself, and the gate stayed green for the whole PR
because the banned name was still AAASM-5529.

Widen it to a tuple every subsequent ticket appends itself to, since each
closes on merge and none may be a referent afterwards.

Proven able to fail: repointing one claim back at AAASM-5661 turns
test_an_unproven_reason_does_not_name_an_implementing_ticket red (exit 1);
reverting it green (exit 0).

Refs AAASM-5661
The AAASM-5529 controls monkeypatch _start_network_layer away, so nothing
observed what mode="sdk-only" actually does. Assert the no-op shutdown
hook rather than the network_mode string: the string is what the caller
asked for, a started sidecar would leave a real teardown behind.

Refs AAASM-5661
A full-page sweep for the class — a sentence asserting enforcement
unconditionally while resting on controls that install a fake native core
— found five. Two were corrected in the first pass; these are the rest.

  :672  "the in-process adapter enforces on tool calls with no network
        sidecar" — the same claim as the mode section below, left
        unconditional while that one was qualified. The enforcement half
        now lives only in the qualified sentence.
  :675  "**Tool calls were governed.**" — named by nobody; found by the
        sweep. A bare past-tense assertion about this example, where the
        calls were refused without a policy governing them.
  :681  "the policy denied the call" — the sentence a reader meets at the
        moment their tool blocks, ten lines above the section saying the
        opposite. Its own binding named a control whose point is that
        there is no verdict source.

:681 now says what refused it and how to tell which, with a control per
disjunct: the deny controls read the policy text out of the reason, the
documented-path control reads the absent-extension text out of it.

Refs AAASM-5661
@sonarqubecloud

Copy link
Copy Markdown

@Chisanan232
Chisanan232 merged commit 1d409a8 into main Aug 14, 2026
28 checks passed
@Chisanan232
Chisanan232 deleted the v0.0.1/AAASM-5661/fix/quickstart_interceptor branch August 14, 2026 02:35
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.

1 participant