Skip to content

Commit 2574808

Browse files
lukischLukas Geigerclaude
authored
feat: versioned system agent template (system-agent, OCEAN P05) (#2)
* feat: versioned system agent template (`system-agent`, OCEAN P05) Ship a neutral system agent template as package data and expose it with `githubbot-core --json system-agent` (schema githubbot-core.system-agent/v1): version (= package version), sha256 of the canonical template, capability, effective permissions and a blueprint using the field names of the ellmos agent-studio blueprint payload. The template exists only where the package is installed, which is the capability binding the consumer needs. Policy refs and the model fallback chain are deliberately not part of the template yet; they depend on the consumer contract (BACH/ellmos-system-gui). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LYHP4Js5ggpzQ78JXWRe5E * chore: release 0.2.0 (version bump, changelog, neutral wording) Review fixes by independent reviewer: version 0.1.0 -> 0.2.0 so tag and template version match; remove product name from template/docs; fix spacing typo in test. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LYHP4Js5ggpzQ78JXWRe5E --------- Co-authored-by: Lukas Geiger <lukas@um-bruch.org> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 3c6a200 commit 2574808

10 files changed

Lines changed: 126 additions & 6 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,15 @@ Format: [Keep a Changelog](https://keepachangelog.com/de/1.1.0/), [Semantic Vers
66

77
## [Unreleased]
88

9+
## [0.2.0] - 2026-10-11
10+
11+
### Hinzugefügt / Added
12+
13+
- Befehl `system-agent`: versionierte, gehashte Systemagent-Vorlage (`githubbot-core.system-agent/v1`)
14+
mit Agent-Studio-Blueprint-Feldern, nur bei installiertem Paket vorhanden /
15+
`system-agent` command: versioned, hashed system agent template with agent-studio blueprint fields,
16+
present only where the package is installed.
17+
918
## [0.1.0] - 2026-10-11
1019

1120
### Hinzugefügt / Added

‎README.md‎

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ pull-request summaries, read-only health/traffic/star reports, a preview/apply c
77
write, a plugin SPI and signed exact-SHA review receipts. Pure Python standard library, no runtime
88
dependencies.
99

10-
> Status: alpha (0.1.0). The CLI, the JSON result envelope and SPI version 1 are the contract;
10+
> Status: alpha (0.2.0). The CLI, the JSON result envelope and SPI version 1 are the contract;
1111
> everything else may still change.
1212
1313
## Features
@@ -62,8 +62,9 @@ githubbot-core duplicates --input issues.json --threshold 0.8
6262
# Pull-request summary (+ checks from installed plugins)
6363
githubbot-core review-pr --repo owner/repo --pr 12
6464

65-
# Plugins
65+
# Plugins and system agent template
6666
githubbot-core plugins
67+
githubbot-core --json system-agent
6768
githubbot-core run <plugin> [--plugin-arg ...] [--apply]
6869
```
6970

@@ -85,6 +86,15 @@ The Python API is the package itself: `githubbot_core.triage.IssueTriage`,
8586
`githubbot_core.actions.preview/apply`, `githubbot_core.health.RepoHealthCollector`,
8687
`githubbot_core.plugins`, `githubbot_core.review_gate`, `githubbot_core.credentials`.
8788

89+
### System agent template
90+
91+
`githubbot-core --json system-agent` prints the neutral agent template shipped with the package
92+
(schema `githubbot-core.system-agent/v1`): `version` (package version), `sha256` of the canonical
93+
template, `capability`, the effective `permissions` and a `blueprint` with the field names of the
94+
agent-studio blueprint payload (persona, role, contractus, governance, skills). The template
95+
exists only where the package is installed. Consumers bind an agent to `version` + `sha256` and must
96+
not overwrite an existing agent configuration when a newer template appears.
97+
8898
## Configuration and permissions
8999

90100
The packaged defaults (`githubbot_core/default_config.json`) are extended with `--config file.json`

‎README_de.md‎

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ Duplikaterkennung, Pull-Request-Zusammenfassungen, lesende Berichte zu Gesundhei
77
Sternen, ein Vorschau-/Ausführungsvertrag für jede Schreibaktion, eine Plugin-Schnittstelle (SPI)
88
und signierte Exact-SHA-Review-Belege. Reine Python-Standardbibliothek, keine Laufzeitabhängigkeiten.
99

10-
> Status: Alpha (0.1.0). Verbindlich sind CLI, JSON-Ergebnishülle und SPI-Version 1; alles andere
10+
> Status: Alpha (0.2.0). Verbindlich sind CLI, JSON-Ergebnishülle und SPI-Version 1; alles andere
1111
> kann sich noch ändern.
1212
1313
## Funktionen
@@ -62,8 +62,9 @@ githubbot-core duplicates --input issues.json --threshold 0.8
6262
# Pull-Request-Zusammenfassung (+ Prüfungen installierter Plugins)
6363
githubbot-core review-pr --repo owner/repo --pr 12
6464

65-
# Plugins
65+
# Plugins und Systemagent-Vorlage
6666
githubbot-core plugins
67+
githubbot-core --json system-agent
6768
githubbot-core run <plugin> [--plugin-arg ...] [--apply]
6869
```
6970

@@ -86,6 +87,15 @@ Die Python-API ist das Paket selbst: `githubbot_core.triage.IssueTriage`,
8687
`githubbot_core.actions.preview/apply`, `githubbot_core.health.RepoHealthCollector`,
8788
`githubbot_core.plugins`, `githubbot_core.review_gate`, `githubbot_core.credentials`.
8889

90+
### Systemagent-Vorlage
91+
92+
`githubbot-core --json system-agent` gibt die neutrale, mit dem Paket ausgelieferte Agentenvorlage aus
93+
(Schema `githubbot-core.system-agent/v1`): `version` (Paketversion), `sha256` der kanonischen Vorlage,
94+
`capability`, die wirksamen `permissions` und ein `blueprint` mit den Feldnamen des
95+
Agent-Studio-Blueprint-Payloads (Persona, Rolle, Contractus, Governance, Skills). Die Vorlage
96+
existiert nur dort, wo das Paket installiert ist. Konsumenten binden einen Agenten an `version` +
97+
`sha256` und überschreiben bei einer neueren Vorlage keine bestehende Agentenkonfiguration.
98+
8999
## Konfiguration und Rechte
90100

91101
Die mitgelieferten Standardwerte (`githubbot_core/default_config.json`) werden durch

‎llms.txt‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ Languages: en, de
1313
- Python API: `githubbot_core.triage`, `githubbot_core.actions`, `githubbot_core.health`,
1414
`githubbot_core.traffic`, `githubbot_core.stars`, `githubbot_core.pr_review`,
1515
`githubbot_core.plugins`, `githubbot_core.review_gate`, `githubbot_core.credentials`.
16+
- System agent template: `githubbot-core --json system-agent` (schema
17+
`githubbot-core.system-agent/v1`, version + sha256, agent-studio blueprint fields).
1618
- Plugin SPI v1: entry-point group `githubbot_core.plugins`, factory returning an object with
1719
`name`, `spi_version`, `describe()`, optional `plan(ctx)` and `pr_policy_checks(pr, files, diff)`.
1820

‎pyproject.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ version = { attr = "githubbot_core.__version__" }
4545
where = ["src"]
4646

4747
[tool.setuptools.package-data]
48-
githubbot_core = ["default_config.json", "templates/*.yml"]
48+
githubbot_core = ["default_config.json", "system_agent.json", "templates/*.yml"]
4949

5050
[tool.pytest.ini_options]
5151
testpaths = ["tests"]

‎src/githubbot_core/__init__.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@
55
a plugin SPI and signed exact-SHA review receipts. Standard library only.
66
"""
77

8-
__version__ = "0.1.0"
8+
__version__ = "0.2.0"

‎src/githubbot_core/cli.py‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from __future__ import annotations
1616

1717
import argparse
18+
import hashlib
1819
import json
1920
import shutil
2021
import sys
@@ -114,6 +115,18 @@ def cmd_plugins(args, config, transport) -> int:
114115
return EXIT_OK
115116

116117

118+
def cmd_system_agent(args, config, transport) -> int:
119+
template = json.loads(resources.files("githubbot_core").joinpath("system_agent.json").read_text(encoding="utf-8"))
120+
canonical = json.dumps(template, sort_keys=True, separators=(",", ":"), ensure_ascii=False)
121+
data = {"schema": template["_meta"]["schema"], "id": template["id"], "version": __version__,
122+
"sha256": hashlib.sha256(canonical.encode("utf-8")).hexdigest(),
123+
"capability": {"package": "githubbot-core", "version": __version__, "spi_version": plugins.SPI_VERSION},
124+
"blueprint": template["blueprint"], "permissions": config.get("permissions", {})}
125+
text = f"system agent {data['id']} {data['version']} sha256 {data['sha256']}"
126+
_emit(args, "system-agent", True, "read", data, [], text)
127+
return EXIT_OK
128+
129+
117130
def cmd_classify(args, config, transport) -> int:
118131
classifier = IssueClassifier(config)
119132
results = [classifier.classify(issue) for issue in _read_issues(Path(args.input))]
@@ -240,6 +253,8 @@ def build_parser() -> argparse.ArgumentParser:
240253
sub.add_parser("info", help="version, contract and plugin overview").set_defaults(func=cmd_info)
241254
sub.add_parser("doctor", help="check gh CLI, credentials and permissions").set_defaults(func=cmd_doctor)
242255
sub.add_parser("plugins", help="list installed plugins").set_defaults(func=cmd_plugins)
256+
sub.add_parser("system-agent", help="versioned system agent template of this package").set_defaults(
257+
func=cmd_system_agent)
243258

244259
p = sub.add_parser("classify", help="classify issues from a JSON file (offline)")
245260
p.add_argument("--input", required=True)
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"_meta": {
3+
"schema": "githubbot-core.system-agent/v1",
4+
"description": "Neutral system agent template shipped with this package. It exists only where githubbot-core is installed; read it with `githubbot-core --json system-agent`. `blueprint` uses the field names of the agent-studio blueprint payload. Consumers may change persona, skills, governance and model; a newer template version must not silently overwrite an existing agent configuration."
5+
},
6+
"id": "githubbot",
7+
"blueprint": {
8+
"name": "githubbot",
9+
"title": "GitHub Maintenance Agent",
10+
"kind": "agent",
11+
"description": "Issue triage, duplicate detection, pull-request summaries and read-only health, traffic and star reports. Every write is a previewed plan that is applied only with explicit approval.",
12+
"persona_role": "github-maintainer",
13+
"persona_prompt": "You are the GitHub maintenance agent of githubbot-core. You triage issues, find duplicates, summarise pull requests and produce read-only health, traffic and star reports for the repositories assigned to you. Every write is first shown as a preview plan; you apply a plan only when a current, explicit approval names its plan_sha256, and you never widen the configured permissions. A review verdict is valid only for the exact head SHA it was made on. If a source cannot be read, report the error instead of treating it as empty.",
14+
"skills": [],
15+
"animus_type": "cli",
16+
"modus": "casualis",
17+
"contractus": {"turns": 15},
18+
"governance": {"profile": "read_only_research", "tool_whitelist": ["read_files", "search_content"]}
19+
}
20+
}

‎tests/test_consumer_install.py‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ def test_installed_package_cli_without_private_extension(tmp_path: Path) -> None
4444
envelope = json.loads(info.stdout)
4545
assert envelope["ok"] is True and envelope["data"]["plugins"] == []
4646

47+
agent = _run([str(cli), "--json", "system-agent"], cwd=work) # template ships as package data
48+
assert agent.returncode == 0, agent.stderr
49+
assert json.loads(agent.stdout)["data"]["blueprint"]["name"] == "githubbot"
50+
4751
origin = _run([str(python), "-c", "import githubbot_core, sys; print(githubbot_core.__file__)"], cwd=work)
4852
assert "site-packages" in origin.stdout, origin.stdout # not the source tree
4953

‎tests/test_system_agent.py‎

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
"""System agent template: versioned, hashed, shaped like the agent-studio blueprint payload."""
2+
from __future__ import annotations
3+
4+
import hashlib
5+
import json
6+
import re
7+
from importlib import resources
8+
9+
from githubbot_core import __version__, cli
10+
11+
# Rules of the consuming agent-studio save/materialize path (blueprint service).
12+
NAME = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._-]{0,79}$")
13+
KINDS = {"agent", "role", "skill", "workflow", "service", "contractus"}
14+
ANIMUS = {"api", "cli", "subscription", "local"}
15+
EXECUTABLE_MODUS = {"casualis", "usus"}
16+
17+
18+
def _system_agent(capsys, *extra: str) -> dict:
19+
assert cli.main([*extra, "--json", "system-agent"]) == 0
20+
envelope = json.loads(capsys.readouterr().out)
21+
assert envelope["ok"] is True and envelope["mode"] == "read"
22+
return envelope["data"]
23+
24+
25+
def test_template_identity_is_version_plus_canonical_hash(capsys):
26+
data = _system_agent(capsys)
27+
template = json.loads(resources.files("githubbot_core").joinpath("system_agent.json").read_text(encoding="utf-8"))
28+
canonical = json.dumps(template, sort_keys=True, separators=(",", ":"), ensure_ascii=False)
29+
assert data["schema"] == "githubbot-core.system-agent/v1"
30+
assert data["id"] == "githubbot" and data["version"] == __version__
31+
assert data["sha256"] == hashlib.sha256(canonical.encode("utf-8")).hexdigest()
32+
assert data["capability"] == {"package": "githubbot-core", "version": __version__, "spi_version": 1}
33+
34+
35+
def test_blueprint_is_accepted_by_the_agent_studio_payload_rules(capsys):
36+
bp = _system_agent(capsys)["blueprint"]
37+
assert NAME.fullmatch(bp["name"]) and bp["name"] == bp["name"].lower()
38+
assert bp["kind"] in KINDS and bp["kind"] == "agent"
39+
assert bp["persona_prompt"].strip() and len(bp["title"]) <= 120
40+
assert bp["animus_type"] in ANIMUS and bp["modus"] in EXECUTABLE_MODUS
41+
assert isinstance(bp["skills"], list) and all(NAME.fullmatch(s) for s in bp["skills"])
42+
assert isinstance(bp["contractus"], dict) and isinstance(bp["governance"], dict)
43+
assert "is_template" not in bp # system templates are registered by the consumer, never self-declared
44+
45+
46+
def test_permissions_are_the_effective_narrowed_configuration(capsys, tmp_path):
47+
layer = tmp_path / "narrow.json"
48+
layer.write_text(json.dumps({"permissions": {"enabled": []}}), encoding="utf-8")
49+
assert _system_agent(capsys)["permissions"]["enabled"] == ["issue.add_labels", "issue.comment"]
50+
assert _system_agent(capsys, "--config", str(layer))["permissions"]["enabled"] == []

0 commit comments

Comments
 (0)