Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"displayName": "Agent Stack",
"source": "./plugins/agent-stack",
"description": "Four skills: agent-orchestrator — tool-calling loops, pipelines with checkpoints, provider routing with fallback, memory architecture, plus the wallet side of reselling LLM access; agent-evals — run/trace/thread evals, LLM judges, and fixtures grown from production; agent-interop — MCP servers and clients, A2A agent cards, the MCP Registry, and gateways; agent-harness — system prompts, tool shaping, workflow-vs-agent, and auditing an agent system.",
"version": "0.26.0",
"version": "0.27.0",
"author": {
"name": "ssheleg",
"url": "https://x.com/sshlg93"
Expand Down
71 changes: 64 additions & 7 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,59 @@ jobs:
echo "OK: validator catches front matter the host would truncate silently"
fi

- name: Negative self-test (a description at 941 chars must fail; 940 passes)
run: |
set -eu
# Plan R26: this member keeps 30 characters of room under the house limit of 970.
# The plant replaces the whole description with a plain scalar of an exact length,
# so the boundary is tested at the character, not near it: 940 must pass and 941
# must fail, and fail for the ratchet, not for something else the edit broke.
for n in 940 941; do
rm -rf /tmp/ratchet-copy && cp -R . /tmp/ratchet-copy
python3 test/plant_guard.py snap /tmp/ratchet-copy
N="$n" python3 - <<'PY'
import os, re
n = int(os.environ["N"])
p = "/tmp/ratchet-copy/plugins/agent-stack/skills/agent-evals/SKILL.md"
s = open(p, encoding="utf-8").read()
s2, k = re.subn(r"^description: >-\n(?:[ \t]+.*\n)+", "description: " + "a" * n + "\n", s, count=1, flags=re.M)
assert k == 1, "PLANT DID NOT LAND: no folded description in agent-evals/SKILL.md"
open(p, "w", encoding="utf-8").write(s2)
PY
python3 test/plant_guard.py verify /tmp/ratchet-copy "description at $n chars"
if out=$(python3 /tmp/ratchet-copy/test/validate.py 2>&1); then
if [ "$n" = 941 ]; then echo "ERROR: validator accepted a 941-char description"; exit 1; fi
echo "OK: 940 chars passes"
else
if [ "$n" = 940 ]; then echo "ERROR: validator refused a 940-char description:"; echo "$out"; exit 1; fi
echo "$out" | grep -q "ratchet" || { echo "ERROR: 941 failed, but not on the ratchet:"; echo "$out"; exit 1; }
echo "OK: 941 chars fails on the ratchet"
fi
done

- name: Negative self-test (a removed estate-skill pointer in agent-interop must fail)
run: |
set -eu
# Plan R25: agent-interop names the Fabric adapter skills and
# observatory-log:handling-secrets as the owners of the estate's HOW.
rm -rf /tmp/r25-copy && cp -R . /tmp/r25-copy
python3 test/plant_guard.py snap /tmp/r25-copy
python3 - <<'PY'
p = "/tmp/r25-copy/plugins/agent-stack/skills/agent-interop/SKILL.md"
s = open(p, encoding="utf-8").read()
name = "`fabric-agent-adapter:building-fabric-services`"
assert name in s, "PLANT DID NOT LAND: the pointer is not in agent-interop/SKILL.md"
open(p, "w", encoding="utf-8").write(s.replace(name, "the services skill"))
PY
python3 test/plant_guard.py verify /tmp/r25-copy "a removed estate-skill pointer"
if out=$(python3 /tmp/r25-copy/test/validate.py 2>&1); then
echo "ERROR: validator accepted agent-interop without its building-fabric-services pointer"
exit 1
else
echo "$out" | grep -q "plan R25" || { echo "ERROR: failed, but not on the R25 check:"; echo "$out"; exit 1; }
echo "OK: validator refuses a missing estate-skill pointer"
fi

- name: Negative self-test (a reference nobody links must fail)
run: |
set -eu
Expand Down Expand Up @@ -650,23 +703,27 @@ jobs:
# Under the 1024 cap and over the 970 working limit — the state three of four
# skills shipped in, one with five characters of headroom. The hard-cap check
# passes on this plant, so it is the working limit or nothing.
#
# The plant sets an EXACT length, 971. It used to prepend a fixed 53-character
# line, which reached 970 only while agent-evals sat above 917: at 911 (0.27.0's
# first cut) the padded description was 964, the validator rightly passed it, and
# this step reported a working guard as broken. A plant measured from the
# current text has a shelf life; one that sets the length does not.
python3 - <<'PY'
import pathlib, re
p = pathlib.Path("/tmp/desc-copy/plugins/agent-stack/skills/agent-evals/SKILL.md")
s = p.read_text(encoding="utf-8")
# Anchored on the front-matter key, not on any sentence inside the description:
# the words get reflowed and a prose anchor stops planting silently.
m = re.search(r"(?m)^description: >-\n", s)
assert m, "PLANT DID NOT LAND: no folded description block to grow"
out = s[:m.end()] + " padding to reach the working limit and no further.\n" + s[m.end():]
assert out != s, "PLANT DID NOT LAND"
# Anchored on the front-matter key, not on any sentence inside the description.
out, k = re.subn(r"(?m)^description: >-\n(?:[ \t]+.*\n)+", "description: " + "a" * 971 + "\n", s, count=1)
assert k == 1, "PLANT DID NOT LAND: no folded description block to replace"
p.write_text(out, encoding="utf-8")
PY
python3 test/plant_guard.py verify /tmp/desc-copy "Negative self-test (a description must stay inside the working limit)"
if python3 /tmp/desc-copy/test/validate.py; then
if out=$(python3 /tmp/desc-copy/test/validate.py 2>&1); then
echo "ERROR: validator accepted a description past the house working limit"
exit 1
else
echo "$out" | grep -q "970-char working limit" || { echo "ERROR: 971 failed, but not on the working limit:"; echo "$out"; exit 1; }
echo "OK: a description cannot drift past the working limit"
fi

Expand Down
70 changes: 70 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,73 @@
## 0.27.0 — trust by machine, Fabric jobs, and room in every description

Integration wave 4 of the family's waves run (sshlg-skills plan R23–R26). The first cut
(`4f88a0e`) failed three blind readings; this entry describes what ships after the fix round.

**Final polish after the second reading** (unit and seam pass, product pass with risks):
the approval-document paragraph is scoped to T2 (a T0/T1 caller presents no `approval/1`);
the T0 row no longer lists the descriptor token as what places a caller — it is the host's
own credential (DL-12); the T1 row names the undecided proposal to scope T1 in
`authority.md`; `mcp.md` names all three per-reply keys (`degraded`, `human_steps`,
`sources`) while still not teaching them as a contract (B-156); the agent-interop trigger is
"токен делегирования", not "делегирование", so sub-agent delegation stays with
agent-orchestrator (939 chars); AG-06a/b name plan R28 as deferred; B-157 records that the
adapter's back-links run one way.

- **Delegation starts with whether a caller needs a token (R23).** The delegation section of
`agent-interop/references/gateway.md` now opens with the trust tiers, each sentence citing
the contract rule it restates:
- T0 is the operator's own machine, T1 an enrolled machine of the same owner, and T2
everyone else (DL-25). T1 enrolment is Fabric's roadmap item RM-21, still open, so no
machine is T1 yet.
- A tier belongs to the caller's origin, not its connection: a call relayed onto loopback
is T2 unless the relay proves the origin (DL-25).
- A callee must not require a token, an approval or a grant record from a T0 or T1
caller, even when it serves T2 callers too (DL-26). A token such a caller presents is
verified, refused when invalid, and used for attribution and narrowing.
- Every tier keeps the money limits (L0, the project's limit, each key's ceiling) and all
five human gates: sign-in, consent, payment, top-up, a secret typed into a hidden
prompt.
- A token-less T0 call is metered by Observatory's defaults: `slotsRequireGrant:
when-token`, keyserver delegation `off` (`METER.md` M0).
- The paragraphs older than the tiers are scoped to match the amended contract: failing
closed with no issuer applies to T2 callers (DL-8), and retiring the descriptor token
applies across the machine boundary (DL-12).
- The delegation pin moves from `8e49fbd` to `3cb03ab`, which carries DL-25 and DL-26.
The rationale sentences that had no source are gone. At the new pin the meter's onward
token has an exact scope and effect `none` (not "read-only"), and a device approval
no longer exists (DEC-0045), so both sentences now say that.
- **Fabric jobs as the worked fallback for MCP Tasks, and the well-known `degraded` list
(R24).** `references/mcp.md`, pinned to fabric-agent-contract `bcada4a`:
- names `output` and `scope` in the result envelope;
- says how to answer an input request (`inputResponses`, §C3.3);
- replaces "works unchanged" with what the contract says: a caller that does not
negotiate Tasks keeps receiving the job handle, and its states are the Task states;
- teaches the service's well-known `degraded` list as `service.md` defines it: always
present, empty means full health, `setup: true` marks a setup state.

The per-reply `degraded`/`human_steps` envelope has no contract home yet. It is named as
an estate convention and deferred to backlog row B-156.
- **Pointers out, not copies (R25).** `agent-interop` names
`fabric-agent-adapter:creating-fabric-agents`, `:adapting-projects-to-fabric` and
`:building-fabric-services`, and `observatory-log:handling-secrets`, each fully qualified.
A new named check in `test/validate.py` (`check_interop_points_to_the_estate_skills`)
fails when any of the four is missing.
- **Every description keeps 30 characters of room, and the gate holds it (R26).**
`test/validate.py` ratchets descriptions at 940 characters (house limit 970 unchanged).
Measured: evals 929, harness 936, interop 937, orchestrator 938.
- `agent-evals` names "observability" again.
- `agent-interop` adds "delegation token", "on behalf of" and «делегирование»; all 46 of
the hub's agent-stack triggers are still substrings of their descriptions.
- **Watched failing:** a 941-character description (940 passes), a removed
`building-fabric-services` pointer, and the first cut's `gateway.md` and `mcp.md` against
the new `test/audit_regressions/w4-fabric-doctrine.py` (5 of 5 cases fail). The first two
run as negative self-tests in CI.
- **A CI plant that had stopped planting.** "A description must stay inside the working
limit" prepended a fixed 53 characters, which reached 970 only while agent-evals sat above
917. At 911, in the first cut, it planted nothing and failed run 38204211436. It now sets
an exact length of 971 and requires the working-limit message.
- R28 (residue rows AG-06a/b) is not in this release and stays on the board.

## 0.26.0 — delegation: which agent acts, for which person

- **`agent-interop/references/gateway.md` gains the on-behalf-of model (B-155).** The file
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@ without one. Six references: `mcp.md` (the wire and the full deprecation registe
undoes both), `mcp-ship.md` (mounting, and the 404 that is really a double path),
`registry.md` (`server.json`, namespace proof, the registry's three refusals),
`a2a.md` (cards, task states, three bindings), `gateway.md` (what a gateway must do that
an API gateway does not). Plus a link map, and a verdict on each neighbouring standard —
an API gateway does not; delegation — which agent acts for which person — and trust by
machine: whether a caller needs a token at all, T0/T1/T2). Plus a link map, and a verdict on each neighbouring standard —
ACP, AGNTCY, AP2, Agent Skills — so an agent stops guessing.

**The harness skill** (`agent-harness/SKILL.md`) — the layer between the loop and the
Expand Down
2 changes: 1 addition & 1 deletion SKILL-CARD.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
| Field | Value |
|---|---|
| Pack | `agent-stack` |
| Version | `0.26.0` |
| Version | `0.27.0` |
| Skills | `agent-orchestrator`, `agent-evals`, `agent-interop`, `agent-harness` |
| License | MIT |
| Source | https://github.com/ssheleg/agent-stack |
Expand Down
Loading
Loading