Skip to content

Commit fc5b983

Browse files
committed
release: v0.3.0
1 parent c20c72d commit fc5b983

12 files changed

Lines changed: 234 additions & 43 deletions

File tree

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
name: Config drift demo (worked example)
2+
3+
on:
4+
pull_request:
5+
paths:
6+
- 'scripts/check-config-consistency.py'
7+
- 'examples/worked-example/**'
8+
push:
9+
branches: [main]
10+
paths:
11+
- 'scripts/check-config-consistency.py'
12+
- 'examples/worked-example/**'
13+
workflow_dispatch: {}
14+
15+
# ---------------------------------------------------------------------------
16+
# This job does NOT gate this repo's own config -- it proves that
17+
# check-config-consistency.py (TR-GOV-001) correctly detects the drift
18+
# planted in examples/worked-example/sample-app (gemma4:31b in CLAUDE.md
19+
# vs gemma4:26b in config/search_config.yaml.example -- see
20+
# examples/worked-example/README.md). Here, the detector *failing* on the
21+
# fixture is success; if it ever passes clean, the fixture or the detector
22+
# itself has regressed.
23+
#
24+
# A downstream user copies this pattern into their own app's CI, pointed at
25+
# their own --root, where a real drift SHOULD fail the build (see
26+
# docs/releasing.md and the sample release-check.yml in this repo for what
27+
# a real, blocking gate looks like).
28+
# ---------------------------------------------------------------------------
29+
30+
jobs:
31+
demo-detects-drift:
32+
runs-on: ubuntu-latest
33+
34+
steps:
35+
- uses: actions/checkout@v7.0.0
36+
37+
- uses: actions/setup-python@v6.3.0
38+
with:
39+
python-version: '3.12'
40+
41+
- name: Assert check-config-consistency.py catches the planted drift
42+
run: |
43+
set +e
44+
python3 scripts/check-config-consistency.py --root examples/worked-example
45+
code=$?
46+
set -e
47+
if [ "$code" -ne 1 ]; then
48+
echo "::error::Expected exit 1 (DRIFT detected) against examples/worked-example/sample-app, got exit $code instead -- the fixture or detector regressed."
49+
exit 1
50+
fi
51+
echo "Confirmed: check-config-consistency.py correctly detected the planted local-gemma-model drift (exit 1)."

.gitignore

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
11
__pycache__/
22
*.pyc
33
.pytest_cache/
4-
.venv/
5-
venv/

AGENTS.md

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,17 +37,35 @@ Every multi-step agent node declares four fields before implementation:
3737

3838
Missing any field means the design is incomplete.
3939

40-
**Reference implementation:** `examples/engine-interface/` shows this pattern
41-
applied to a multi-source polling pipeline (inspired by the SearXNG engine
42-
interface). Key properties: `source_name` (identity), `default_timeout`
43-
(budget), `fetch()` that never raises (exit condition = always returns),
44-
`list[Result]` output (normalized schema). These four map directly to the
45-
four TR-AGT-003 fields.
46-
4740
The exit condition must be verified by deterministic evidence when the agent
4841
changes persistent state, writes files, sends messages, or calls tools with side
4942
effects (TR-TEST-006).
5043

44+
See `examples/engine-interface/` for a concrete reference implementation: a
45+
SearXNG-inspired multi-source polling pattern where `source_name` (identity),
46+
`default_timeout` (budget), a never-raising `fetch()` (exit condition), and a
47+
normalized `list[Result]` (output schema) map directly onto the four fields above.
48+
49+
### MCP Tool Annotations (TR-AGT-003, field 5)
50+
51+
When a node is exposed as an MCP tool, declare four hint flags describing its blast
52+
radius. These are advisory hints to MCP clients (Claude Code, Cursor, opencode) — the
53+
MCP protocol does not enforce them, so declare them accurately regardless.
54+
55+
| Annotation | Meaning | Intended client behaviour |
56+
|---|---|---|
57+
| `readOnlyHint: true` | Tool never writes to external state | Act freely, safe to parallelize |
58+
| `destructiveHint: true` | Tool deletes or irreversibly mutates data | Always confirm, no exceptions |
59+
| `idempotentHint: true` | Safe to re-run after a retry or exhausted budget | Affects retry policy (field 4) |
60+
| `openWorldHint: true` | Tool reaches external systems (web, APIs, services) | Treat output as untrusted (TR-SEC-005) |
61+
62+
All four are required when registering an MCP tool (using MCP SDK `ToolAnnotations`
63+
keyword names); nodes not exposed as MCP tools are exempt. Example: `search_notes` is
64+
`readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=False`;
65+
`fetch_url` is `readOnlyHint=True, destructiveHint=False, idempotentHint=True,
66+
openWorldHint=True` (external fetch triggers TR-SEC-005 on its output); `delete_document`
67+
is `readOnlyHint=False, destructiveHint=True, idempotentHint=True, openWorldHint=False`.
68+
5169
### Trigger Classification (TR-AGT-004)
5270

5371
Classify every agent invocation at design time:

ATTRIBUTIONS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ and examples are maintainer-authored unless a file header states otherwise.
1414
| [DietrichGebert/ponytail](https://github.com/DietrichGebert/ponytail) | MIT | Debt-tag convention, config consistency checking concept; adapted as `LUMIA-DEBT:` / `POC-EXCEPTION:` and `check-config-consistency.py`. |
1515
| [MODSetter/SurfSense](https://github.com/MODSetter/SurfSense) | Apache-2.0 | Comparative analysis only; selective pattern adoption (RRF, automations) documented in private app ADRs — not shipped as SurfSense code. |
1616
| Karpathy LLM Wiki (public write-ups) | N/A (ideas) | Three-layer vault structure, file-based session state, ingest queue patterns — described and reimplemented independently. |
17+
| [SearXNG](https://github.com/searxng/searxng) | AGPL-3.0 | Comparative pattern reference only for `examples/engine-interface/` (TR-AGT-003 loop contract demo). No code copied — verified directly against `searx/engines/demo_online.py`, which uses a module-level `setup()`/`init()`/`request()`/`response()` plugin API, structurally unrelated to this example's synthetic ABC-based `source_name`/`fetch()`/`default_timeout` pattern. The per-engine declared-timeout *idea* is the only thing carried over. |
1718
| AGENTS.md ecosystem | Open standard | Complementary positioning; this repo focuses on governance/traceability, not replacing AGENTS.md. |
1819
| [Microsoft Agent Governance Toolkit](https://github.com/microsoft/agent-governance-toolkit) | (see upstream) | Comparative positioning only; this repo focuses on design-time and repo-time standards, not runtime agent governance. |
1920
| Dify and similar RAG/agent platforms | (varies by project) | Comparative positioning only; no code, docs, or implementation copied. |

CHANGELOG.md

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,60 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
55

66
## [Unreleased]
77

8-
No unreleased changes.
8+
## [0.3.0] - 2026-07-04
9+
10+
### Added
11+
12+
- `examples/engine-interface/` — reference implementation of the TR-AGT-003 loop-contract
13+
pattern (SearXNG-inspired multi-source polling engine interface), plus a Loop Contracts
14+
pointer in `AGENTS.md` and a `requirements-implementation-map.md` row update. This was
15+
published directly to the public repo (commit `c20c72d`) without a corresponding private-repo
16+
change; pulled back into the private source here so the next `publish-public-standards.yml`
17+
run (which mirrors private → public via `rsync --delete`) does not silently delete it.
18+
- `AGENTS.md` — new "MCP Tool Annotations (TR-AGT-003, field 5)" section: the
19+
`readOnlyHint`/`destructiveHint`/`idempotentHint`/`openWorldHint` convention for MCP-exposed
20+
nodes, ported from the private operating conventions (v0.3 roadmap item 1).
21+
`registry/tr-registry.yaml`'s `TR-AGT-003` entry extended to reference field 5.
22+
- `examples/worked-example/` — the TR-AGT-004 trace now runs to completion: step 5 shows real
23+
`check-config-consistency.py` output (previously only described hypothetically) and a new
24+
step 6, `.github/workflows/config-drift-demo.yml`, is a CI job whose success condition is
25+
that the example's planted `local-gemma-model` drift is still detected — the "TR-ID → ADR →
26+
maturity row → script output → CI gate" full path the v0.3 roadmap asked for. Corrected the
27+
maturity-checklist's Annex A mapping in the process: the CI-gate evidence now lives on a new
28+
A.10 row (third-party/supplier relationships, matching the template's own mapping for
29+
`check-config-consistency.py`) instead of conflated into A.9 (human oversight); the now-closed
30+
`LUMIA-DEBT` tag was removed from `sample-app/CLAUDE.md`.
31+
- `scripts/check-config-consistency.py` — new `--root PATH` flag (default: the script's own
32+
repo root), so it can scan any app monorepo, not just the one it lives in (P2). `README.md`'s
33+
usage example updated to show `--root /path/to/your/repo --app YourApp`.
34+
- `registry/tr-registry.yaml``TR-PUB-006`: agent persona drift check between the private
35+
operating-persona tree and its public-standards rewrite. Compares last-changed time for each
36+
pair and fails on drift, forcing a human reconciliation decision instead of silent staleness.
37+
- `.gitignore``__pycache__/`, `*.pyc`, `.pytest_cache/` (P3); nothing previously kept these
38+
out of a future `git add -A` in the public repo. Added to `docs/public-export-manifest.yaml`'s
39+
`generated:` list.
40+
41+
### Fixed
42+
43+
- `scripts/check-config-consistency.py` — an unknown `--app` name or a nonexistent `--root`
44+
previously printed a false "OK" (or crashed) instead of failing; now exits 2 with an
45+
explicit "Unknown app(s): ... . Known: ..." message (P1, P2).
46+
- `ATTRIBUTIONS.md` — added the `detect-secrets` (Yelp, Apache-2.0) entry that should have
47+
shipped alongside v0.2.0's secret-scanning CI step but was missed.
48+
- `ATTRIBUTIONS.md` — added a SearXNG (AGPL-3.0) entry for `examples/engine-interface/`'s
49+
cited pattern source, found during v0.3 release-readiness review; verified no code was
50+
copied (SearXNG's actual `searx/engines/demo_online.py` uses an unrelated module-level
51+
plugin API) before writing the entry.
52+
53+
### Changed
54+
55+
- `templates/completion-checklist.md` strengthened to match the private template's rigor:
56+
file:line evidence citation per acceptance criterion, an explicit anti-mock-masking clause
57+
on test completeness, and a "Reviewer scope complete" item (docs/ADRs, not just code, must
58+
be in a reviewer's stated scope). The public copy's own "Post-write verification" item is
59+
kept (P4) — nothing here required staying private.
60+
- `.github/workflows/release-check.yml``actions/checkout`/`actions/setup-python` bumped to
61+
Node.js 24-compatible versions, clearing the Node 20 deprecation warning.
962

1063
## [0.2.0] - 2026-06-26
1164

@@ -36,6 +89,7 @@ No unreleased changes.
3689
- `CONTRIBUTING.md`, `SECURITY.md`, issue/PR templates, `release-check` CI workflow
3790
- Roadmap and changelog for intentional release cadence
3891

39-
[Unreleased]: https://github.com/onesimplecode/ai-engineering-standards/compare/v0.2.0...HEAD
92+
[Unreleased]: https://github.com/onesimplecode/ai-engineering-standards/compare/v0.3.0...HEAD
93+
[0.3.0]: https://github.com/onesimplecode/ai-engineering-standards/compare/v0.2.0...v0.3.0
4094
[0.2.0]: https://github.com/onesimplecode/ai-engineering-standards/compare/v0.1.0...v0.2.0
4195
[0.1.0]: https://github.com/onesimplecode/ai-engineering-standards/releases/tag/v0.1.0

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public reuse.
2727
python3 scripts/public-export-check.py .
2828

2929
# Scan for config/model-string drift (point at your app monorepo)
30-
python3 scripts/check-config-consistency.py --app YourApp
30+
python3 scripts/check-config-consistency.py --root /path/to/your/repo --app YourApp
3131

3232
# Report deferred-work tags
3333
python3 scripts/debt-report.py --path /path/to/your/repo

examples/worked-example/README.md

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,40 @@ artifacts. **All content is fictional** — no private data or real application
99
|------|----------|----------|
1010
| 1 | `registry-snippet.yaml` | TR-AGT-004 defined as active |
1111
| 2 | `docs/decisions/ADR-001-example.md` | Trigger type classified: user-initiated |
12-
| 3 | `docs/maturity-checklist.md` | A.9 human oversight row = Partial |
13-
| 4 | `sample-app/CLAUDE.md` | Documents trigger + debt tag |
14-
| 5 | `check-config-consistency.py` | Would catch model-string drift in real app |
12+
| 3 | `docs/maturity-checklist.md` | A.9 human oversight row = Partial (trigger classified, no further action needed at POC) |
13+
| 4 | `sample-app/CLAUDE.md` | Documents trigger classification |
14+
| 5 | `check-config-consistency.py` | Real output below — catches the planted `local-gemma-model` drift (TR-GOV-001) |
15+
| 6 | `.github/workflows/config-drift-demo.yml` | CI gate: fails the job if the drift is ever *not* detected (see `docs/maturity-checklist.md` A.10) |
1516

1617
## Run (from public repo root)
1718

1819
```bash
1920
python3 scripts/public-export-check.py .
2021
python3 scripts/debt-report.py --path examples/worked-example
22+
python3 scripts/check-config-consistency.py --root examples/worked-example
2123
```
2224

2325
## Deliberate drift example
2426

2527
`sample-app/CLAUDE.md` references `gemma4:31b` while `config/search_config.yaml.example`
26-
uses `gemma4:26b`. In a real app at repo root, `check-config-consistency.py` would
27-
report **DRIFT** for the `local-gemma-model` family.
28+
uses `gemma4:26b`. Real output from the command above:
29+
30+
```
31+
sample-app:
32+
DRIFT local-gemma-model: 2 distinct values found
33+
'gemma4:31b' <- sample-app/CLAUDE.md:8
34+
'gemma4:26b' <- sample-app/config/search_config.yaml.example:5, sample-app/config/search_config.yaml.example:5
35+
36+
See TR-GOV-001 (docs/tr-registry.yaml) for the convention this enforces.
37+
```
38+
39+
(the duplicated location on the last line is a cosmetic quirk in the script itself:
40+
`SCAN_GLOBS` includes both `config/*.yaml.example` and `config/*.example`, and
41+
`search_config.yaml.example` matches both patterns with no dedup, so the file is scanned
42+
twice. Harmless — it doesn't cause a false positive or negative — but worth knowing if
43+
you copy this script into your own repo.)
44+
45+
(exit code 1). `.github/workflows/config-drift-demo.yml` runs this exact command in CI
46+
on every change to this example or the script — the job's *success* condition is that
47+
the drift is still caught. If a future change to the fixture or the detector makes this
48+
pass clean, the job fails loudly instead of silently losing the example's teaching value.
Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
# Maturity Checklist: Sample App (Synthetic)
22

3-
**Last updated:** 2026-06-17
3+
**Last updated:** 2026-07-04
44
**Current tier:** POC
55
**Operationalizes:** TR-GOV-004
66

77
| # | Objective | Mechanism | Applicable | Status | Evidence |
88
|---|-----------|-----------|------------|--------|----------|
99
| A.5 | AI impact assessment | templates/ai-impact-assessment.md | Yes | N/A | No user-facing AI output in this example |
10-
| A.9 | Human oversight | Tiered Autonomy | Yes | Partial | ADR-001 documents user-initiated trigger; no CI gate yet |
10+
| A.9 | Human oversight | Tiered Autonomy | Yes | Partial | ADR-001 documents user-initiated trigger classification (TR-AGT-004) |
11+
| A.10 | Third-party / supplier relationships | `scripts/check-config-consistency.py` (TR-GOV-001) | Yes | Met | `.github/workflows/config-drift-demo.yml` runs the script against this example on every change and fails if the planted `local-gemma-model` drift is ever not detected |
1112

1213
## Open items
1314

14-
- `sample-app/CLAUDE.md` — LUMIA-DEBT: add CI gate for config drift (TR-GOV-001)
15+
None currently open for this example.

examples/worked-example/sample-app/CLAUDE.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010

1111
## Known debt
1212

13-
LUMIA-DEBT: wire check-config-consistency.py into CI (TR-GOV-001)
13+
None currently open. `check-config-consistency.py` runs against this app in
14+
`.github/workflows/config-drift-demo.yml` (TR-GOV-001).
1415

1516
## Privacy (TR-SEC-003)
1617

registry/tr-registry.yaml

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,9 @@ requirements:
7676
section: "Agent Spawning"
7777
text: >
7878
Every agent pipeline node declares input schema, output schema, exit condition,
79-
and resource budget before implementation.
79+
and resource budget before implementation. Nodes exposed as MCP tools additionally
80+
declare four tool annotation hints (readOnlyHint, destructiveHint, idempotentHint,
81+
openWorldHint) describing blast radius — required only for MCP-exposed nodes.
8082
8183
- id: TR-AGT-002
8284
title: Deterministic operations use scripts not agents
@@ -205,3 +207,13 @@ requirements:
205207
text: >
206208
Public repo maintains ROADMAP.md and CHANGELOG.md with periodic curated releases,
207209
not continuous private-repo mirroring.
210+
211+
- id: TR-PUB-006
212+
title: Agent persona drift check between private and public trees
213+
status: active
214+
section: "Public Release"
215+
text: >
216+
A drift check compares the last-changed time of each private operating persona
217+
against its public-standards rewrite and fails if the private side changed more
218+
recently. This does not auto-sync content — the public rewrite stays hand-curated
219+
— it only forces a human reconciliation decision instead of silent drift.

0 commit comments

Comments
 (0)