Repository navigation
Conversation
spec/schema.yaml: - workspace.plugins (map of group:plugin, S-EXEC, expert): one file each under plugins/, the request for a package and never the package. - agent.plugins (list of text, names: plugins, S-EXEC, expert): which of them this agent needs; one that applies-to every-agent need not be named. - the plugin group: description, package (<registry>:<name>), version (a range in the registry's own grammar), if-it-cannot-be-loaded (refuse-to-start, the default, or run-without-it), config (the plugin's own settings, open), and applies-to (the-agents-that-name-it or every-agent). - bundle.brings gains plugins (held equal to the workspace's collections by what_a_bundle_may_bring_is_what_a_workspace_can_hold); learning's needs-a-person-to-approve gains plugins. tests/golden/schema-fields.txt blessed: the eight new rows, nothing moved. README counts recomputed from the schema (57 kinds, 454 fields, 320 core; the stated 54/384 was stale) with a capability row; site-docs kind, field and named-field counts and the plugin row of reference/kinds.md. cargo test -p pact-cli --test authoring_surface: 16 passed. cargo test -p pact-loader --lib what_a_bundle_may_bring_is_what_a_workspace_can_hold: passed. cargo test -p pact-cli --test nothing_that_already_works_changes: 3 passed. pytest tests/test_the_documentation_site_tells_the_truth.py: 17 passed, 2 skipped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
crates/pact-loader/src/plugins.rs (new), wired into `pact check` beside the
bundle rule:
- loader/plugin-package-has-no-registry: `package:` is `<registry>:<name>`
with a registry a runtime loads from (npm, pypi).
- loader/plugin-version-is-not-a-range: `version:` is a range in that
registry's own grammar, npm by nodejs-semver and pypi by pep440_rs (two new
workspace dependencies, default features off).
- loader/plugin-secret-written-in-the-file: a `config:` key whose words name a
token, secret, password or API key carries `{by-reference: ...}`.
- loader/plugin-runs-only-where-loaded (note): the agents a refuse-to-start
plugin keeps to the runtimes that can load it, so --deny-warnings stays
usable on a tree that means it.
Nothing resolves, downloads or opens a package (R5, D17).
- unnamed.rs: a plugin nothing names and not for every agent is
loader/nothing-points-at-it; policy.rs: `plugin` is a kind stem, so
plugins/<name>/plugin.yaml is the folder form.
- `pact discover` lists each agent's plugins (name, package, version,
if-it-cannot-be-loaded with the default written out) through the same
`plugins::needed_by` the note reads.
- digests.rs needs no change: a plugin is in the document, so it moves the
workspace digest and the doc-digest of a workflow that reaches its agent,
and not the contract-digest; the module doc names it.
examples/plugin-desk (run-without-it, clean under --deny-warnings) with its
`pact show` golden; tests/trees/a-desk-that-needs-a-plugin (the note) and
tests/trees/a-plugin-with-no-registry (the error), each with a README.
site-docs/status/verified.md: 31 agents across 12 workspaces.
crates/pact-cli/tests/a_plugin_is_named_and_never_loaded.rs (11 tests): every
rule on a broken copy of plugin-desk, silence on the example itself, the
folder form, discover output, the digests, and the two trees.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Python reference (adapters/python): - ir.PluginSpec (name, package, version, if_cannot_load) and AgentSpec.plugins: the plugins an agent names plus every one that applies to every agent, the reading `pact discover` publishes. - harness.run(..., loaded_plugins=): what the host loaded, by name, in the pattern of retrieved_by, and registered in SUPPLIED_BY_THE_HOST. Before the first model call a missing refuse-to-start plugin halts the run `plugins-not-loaded` with the sentence naming plugin, package, range and fix, and emits turn.run.failed; a missing run-without-it plugin is one line on `unenforced`. Anything but `run-without-it` refuses. - Every transport's lattice (and the out-of-tree echo adapter's) gains `plugins: unsupported`, because the harness loads nothing, so the conformance report states it before execution. - learning.HIGH_RISK_FIELDS gains `plugins` (D23); the Pydantic AI registry gains agent.plugins as a loss the host holds. Node port (adapters/typescript): `plugins` joins AGENT_SPEC_FIELDS with the same refusal and `unenforced` sentence, word for word, and the Vercel lattice publishes `plugins: unsupported`. docs/20-ARCHITECTURE-DRAFT.md §7.28 list A gains the `plugins:` row and the README scope note names it (held by the_subset_the_second_port_runs.rs); the golden set sends `plugins` to Node; README: 31 agents, 186 pairs. New tests: test_a_plugin_nobody_loaded_stops_the_run_before_it_starts.py (7) and test_a_plugin_the_agent_can_do_without_is_named_on_unenforced.py (4), each driving the second port through run-trace.ts. Mutating the Python refusal or the Node `unenforced` line turns them red. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- docs/01-DECISIONS.md: D29 — runtime plugins, named, versioned, governed. Its consequences: the kind at S-EXEC (D23 holds it to a person); checking never resolves or runs a package (R5, D17); runtimes advertise a `plugins` lattice column, refuse an agent whose refuse-to-start plugin they did not load and name a run-without-it one on `unenforced`; D15 narrowed to the runtimes that load an agent's required registries; D27's claim covers agents with no plugins or only run-without-it ones; and what a plugin does once loaded is the loading runtime's to govern. T5's impact row is extended. - docs/50-NOT-COPIED.md: R17 "narrowed — see its last paragraph" (the R16 precedent) with §3.3's "Narrowed by D29"; a §4 row for packages a runtime loads; §8.6 says which half moved and which refusal stands; §8 and §8.2 recomputed (51 / 22 / 28; Runtime 3/6/2, Developer experience 4/2/5), six split rows. - research/notes/eve-capabilities.md: rows 84 and 99 are (a), split, citing workspace.plugins and plugin.package; the Count line follows. - docs/20-ARCHITECTURE-DRAFT.md: the extension-path status names `plugins:` as the runtime path and that `x-` fields are still never emitted. - site-docs/reference/adapters.md: the `plugins` lattice column. README: 29 binding decisions. CHANGELOG: the 26 October entry. Test counts written by ./scripts/sync-counts.sh (1494 Rust + 2972 adapter). cargo test -p pact-cli --test eve_inventory, --test deliberate_refusals, --test every_example_loads_clean: green. mkdocs build --strict: clean. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…implements spec/schema.yaml: plugin.implements (list of text, names: [tools, skills, knowledge], S-CAP, expert) names the interfaces whose code the plugin supplies. - Law 4: loader/plugin-implements-what-is-not-an-interface; a plugin counts as one of an interface's implementations (no an-interface-nothing-implements, and a chosen-by: run-inputs choice may name it); the warning loader/an-interface-only-an-absent-plugin-implements for an agent using an interface only plugins it is not loaded with implement. - Law 1: effects::of_agent adds every interface the agent's plugins implement. - Law 3: loader/a-plugin-action-with-no-claim for an interface action a plugin implements that cannot be undone and names no same-request-key. - Law 8: Learner.cycle holds a plugins proposal to pact check before holding it for a person (checking.check_agent_field, learning.CHECKED_BEFORE_HELD). - explode keeps a plugin's own config: whole (exploding.OPAQUE), so a package's camel-case setting names no longer make the document unexplodable. D29 consequence 7 in docs/01-DECISIONS.md; CHANGELOG; schema and test counts recomputed (scripts/sync-counts.sh). cargo test --workspace, clippy (and -D warnings release), every example under --deny-warnings, tsc and the Python suite with all extras pass. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
PR #2 (
spec/plugins-kind, D29) rebased onto master33b9800(#3 and #4), plus the four laws ofdocs/97-PRIMITIVES.md§4 that a plugin has to answer now that the laws are checker rules. This supersedes #2: the same four commits, then one commit for the laws. #2 is left as it is for review history.The rebase
Every conflict was two additions at the same place:
spec/schema.yaml(agent.turns/agent.voicebesideagent.plugins),Cargo.toml/Cargo.lock(minijinjabesidenodejs-semver/pep440_rs),pact discover(the law-1effect/moves-moneybesideplugins), the Python harness imports, the NodeAGENT_SPEC_FIELDSlist (boardsbesideplugins), the boundary test's skip list,CHANGELOG.md, and the counts in README and site-docs. The counts were recomputed from the schema and the suites (scripts/sync-counts.sh), not merged by hand.The laws for a plugin (D29 consequence 7)
A plugin is code no checker reads, so nothing about what it does can be computed from the code. What can be held is the contract it stands behind. New line:
plugin.implements:names the interfaces (interface: yestools, procedures or sets of documents) whose code the plugin supplies.implements:names only interfaces; a plugin counts as one of an interface's implementations (noan-interface-nothing-implementsfor it) and may be achosen-by: run-inputs.<name>choice; an agent using an interface only plugins it is not loaded with implement is warnedloader/plugin-implements-what-is-not-an-interface,loader/an-interface-only-an-absent-plugin-implementseffect/moves-moneyincludes the worst action of every interface its plugins implement, named onuses:or not, since the code is in its runtime either wayeffects::of_agentsame-request-key:; the interface is the only place a runtime retrying the call can read the claimloader/a-plugin-action-with-no-claimLearner.cycleholds apluginsproposal topact checkbefore holding it for a person, so nobody is asked to approve a tree that does not loadchecking.check_agent_field,learning.CHECKED_BEFORE_HELDA plugin that implements nothing gives an agent nothing to ask (it is how a host loads hooks, for example), and D29 consequence 6 still holds: its own traffic is the runtime's to confine.
Found while writing the law-8 test:
explodeheld every key in the document to the portable file-name alphabet, including the plugin's ownconfig:keys (searchMaxResultsinexamples/plugin-desk), so the shipped plugin example could never be written out for the checker and every proposal about it went unchecked.config:is now an opaque value: written inline, never split into folders, keys not held to the alphabet (exploding.OPAQUE, pinned to the schema'smap of anythingby a test).Evidence
cargo test --locked --workspace: green (new:a_plugin_is_held_to_the_laws_through_its_interfaces.rs, 7 tests;a_name_the_workspace_already_has.rsnarrows the new skill-naming line in its rewritten specification).cargo clippy --all-targetsandcargo clippy --release -p pact-cli -- -D warnings: clean.pact check --deny-warningson everyexamples/**/workspace.yaml: clean.adapters/typescript:tsc --noEmitclean.adapters/pythonwith--extra all --extra test: 3146 collected, all passing or skipped as on master (new:test_a_plugin_a_cycle_proposes_is_checked_then_held.py, 4 tests; two exploder tests).scripts/sync-counts.shrun, so the headline count test holds.Why (Bud Meta Harness)
BMH declares its runtime-loaded rows (decision hints, conversation capture, decision chains, serve defaults) through the plugins kind, and its PACT purpose is that nothing which changes an agent's behaviour lives only in BMH configuration. Holding plugins to the laws through declared interfaces keeps a plugin-backed agent portable and reviewable: the contract, the approvals and the computed effect are in the document, and the package is one way of doing it.
Review requested from the PACT maintainers.
🤖 Generated with Claude Code