agentme 0.24.1 → 0.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.filedist-package.yml +1 -1
- package/.xdrs/agentme/edrs/application/015-cli-tool-standards.md +27 -27
- package/.xdrs/agentme/edrs/application/024-ml-dataset-structure.md +25 -10
- package/.xdrs/agentme/edrs/application/026-pragmatic-hexagonal-architecture.md +54 -5
- package/.xdrs/agentme/edrs/application/028-ai-eval-standards.md +99 -40
- package/.xdrs/agentme/edrs/application/030-ai-test-types-taxonomy.md +96 -0
- package/.xdrs/agentme/edrs/devops/005-monorepo-structure.md +0 -8
- package/.xdrs/agentme/edrs/devops/008-common-targets.md +25 -25
- package/.xdrs/agentme/edrs/governance/013-contributing-guide-requirements.md +2 -2
- package/.xdrs/agentme/edrs/index.md +1 -0
- package/.xdrs/agentme/edrs/observability/011-service-health-check-endpoint.md +25 -3
- package/.xdrs/agentme/edrs/principles/002-coding-best-practices.md +3 -13
- package/.xdrs/agentme/edrs/principles/007-project-quality-standards.md +1 -0
- package/.xdrs/agentme/edrs/principles/009-error-handling.md +9 -19
- package/.xdrs/agentme/edrs/principles/012-continuous-xdr-enrichment.md +7 -7
- package/.xdrs/agentme/edrs/principles/016-cross-language-module-structure.md +7 -7
- package/.xdrs/agentme/edrs/principles/022-secrets-management.md +18 -0
- package/.xdrs/agentme/edrs/principles/023-coding-abstraction-practices.md +3 -5
- package/.xdrs/agentme/index.md +9 -0
- package/.xdrs/index.md +10 -2
- package/package.json +3 -3
package/.filedist-package.yml
CHANGED
|
@@ -23,21 +23,21 @@ This keeps the user-facing command predictable while preserving a clean library
|
|
|
23
23
|
|
|
24
24
|
#### CLI command surface
|
|
25
25
|
|
|
26
|
-
- CLI tools
|
|
26
|
+
- CLI tools SHOULD default to the format `[tool] [command] [options] [arguments]`.
|
|
27
27
|
- Example: `filedist extract --packages=test mydir`
|
|
28
|
-
- A single-action tool
|
|
29
|
-
- Every CLI tool
|
|
28
|
+
- A single-action tool MAY omit `[command]` only when adding a subcommand would be artificial and there is no meaningful action split.
|
|
29
|
+
- Every CLI tool MUST expose:
|
|
30
30
|
- `--help` on the root command
|
|
31
31
|
- `--version` on the root command
|
|
32
32
|
- `--verbose` on the root command and on subcommands when flags are parsed per command
|
|
33
|
-
- Root `--help` output
|
|
33
|
+
- Root `--help` output MUST list all available commands, key options, and usage examples. Command-specific help MUST describe that command's arguments and options.
|
|
34
34
|
|
|
35
35
|
#### CLI to application separation
|
|
36
36
|
|
|
37
37
|
- Structure the software as `cli -> app` — the CLI adapter delegates to the application layer, following [agentme-edr-026](026-pragmatic-hexagonal-architecture.md).
|
|
38
|
-
- The CLI layer
|
|
39
|
-
- Domain logic
|
|
40
|
-
- Every feature available through the CLI
|
|
38
|
+
- The CLI layer MUST only parse arguments, load config, call the application layer, and format output.
|
|
39
|
+
- Domain logic MUST live in the application layer and be usable without CLI globals such as `argv`, `stdout`, or process exit handlers.
|
|
40
|
+
- Every feature available through the CLI MUST also be available through the application API.
|
|
41
41
|
- Organize the application layer by action so the mapping stays direct and obvious.
|
|
42
42
|
- `extract` command -> `app/extract(...)`
|
|
43
43
|
- `validate` command -> `app/validate(...)`
|
|
@@ -45,49 +45,49 @@ This keeps the user-facing command predictable while preserving a clean library
|
|
|
45
45
|
|
|
46
46
|
#### Application API shape
|
|
47
47
|
|
|
48
|
-
- Each CLI action
|
|
49
|
-
- Application APIs
|
|
48
|
+
- Each CLI action SHOULD map to a dedicated exported application function with typed inputs and outputs appropriate for the language.
|
|
49
|
+
- Application APIs SHOULD accept in-memory options objects or typed parameters, not require config files or environment variables unless application-level config-file support is an explicit requirement.
|
|
50
50
|
- The CLI layer is responsible for translating flags, positional arguments, and config-file contents into application inputs.
|
|
51
|
-
- The application layer
|
|
51
|
+
- The application layer SHOULD return explicit results and errors so the CLI can decide what to print and which exit code to use.
|
|
52
52
|
|
|
53
53
|
#### Configuration
|
|
54
54
|
|
|
55
55
|
- Prefer flags and positional arguments for simple inputs.
|
|
56
56
|
- When configuration becomes long, nested, or repetitive, use a YAML config file instead of pushing all values into flags. See [agentme-edr-027](../devops/027-environment-variable-configuration.md) for when `.env` values should be referenced from within that file.
|
|
57
|
-
- By default, config-file discovery and loading
|
|
58
|
-
- When a config file is supported, the CLI
|
|
59
|
-
- The CLI
|
|
60
|
-
- The application layer
|
|
61
|
-
- The application layer
|
|
57
|
+
- By default, config-file discovery and loading MUST happen in the CLI layer, not in the application layer.
|
|
58
|
+
- When a config file is supported, the CLI MUST try to load a YAML file from `[cwd]/[tool-name].yml` by default.
|
|
59
|
+
- The CLI MUST also support an explicit config path flag such as `--config`.
|
|
60
|
+
- The application layer MUST NOT depend on the presence of the config file; it SHOULD receive parsed configuration values from the CLI layer.
|
|
61
|
+
- The application layer MAY load or parse config files only when that behavior is an explicit requirement of the application contract for non-CLI consumers as well.
|
|
62
62
|
|
|
63
63
|
#### Output and progress
|
|
64
64
|
|
|
65
|
-
- Standard output
|
|
65
|
+
- Standard output MUST show a start message when work begins and a result message when work completes successfully.
|
|
66
66
|
- When processing is long-running or multi-stage, print concise intermediate progress messages.
|
|
67
|
-
- `--verbose`
|
|
68
|
-
- Default output
|
|
69
|
-
- Errors
|
|
67
|
+
- `--verbose` MUST reveal more internal detail about what the tool is doing without changing the meaning of the command result.
|
|
68
|
+
- Default output SHOULD stay concise and readable for humans.
|
|
69
|
+
- Errors SHOULD be written to standard error with an actionable message. Stack traces or raw internal errors SHOULD stay hidden by default and MAY be shown in verbose mode.
|
|
70
70
|
|
|
71
71
|
#### Exit behavior
|
|
72
72
|
|
|
73
73
|
- Exit with `0` only when the requested action completed successfully.
|
|
74
74
|
- Exit with `1` when the requested action could not be completed.
|
|
75
|
-
- The application layer
|
|
75
|
+
- The application layer SHOULD surface failure as return values, result objects, or language-idiomatic errors; the CLI is responsible for converting that outcome into user-facing messages and process exit codes.
|
|
76
76
|
|
|
77
77
|
#### Documentation
|
|
78
78
|
|
|
79
|
-
- `README.md`
|
|
80
|
-
- `README.md`
|
|
81
|
-
- If the tool supports config files, at least 1 README example
|
|
82
|
-
- Examples
|
|
79
|
+
- `README.md` MUST include at least 4 CLI usage examples.
|
|
80
|
+
- `README.md` MUST include at least 2 application API examples for the same operation also available through the CLI.
|
|
81
|
+
- If the tool supports config files, at least 1 README example SHOULD show config-file usage.
|
|
82
|
+
- Examples MUST use the public command and public application API, not internal modules or private files.
|
|
83
83
|
|
|
84
84
|
#### Distribution and versioning
|
|
85
85
|
|
|
86
|
-
- The implementation language is project-dependent, but the packaging and entry-point strategy
|
|
86
|
+
- The implementation language is project-dependent, but the packaging and entry-point strategy MUST match how users are expected to run the tool.
|
|
87
87
|
- Choose language tooling that stays compatible with ecosystem launchers such as `npx`, `pnpm dlx`, `uvx`, or equivalent distribution commands for that ecosystem.
|
|
88
|
-
- `--version`
|
|
88
|
+
- `--version` MUST print the same version declared in the published package or release artifact metadata.
|
|
89
89
|
- Do not hard-code a second version string that can drift from the published package version.
|
|
90
|
-
- Language-specific project structure and packaging rules still apply and
|
|
90
|
+
- Language-specific project structure and packaging rules still apply and SHOULD be combined with this XDR, especially [agentme-edr-003](003-javascript-project-tooling.md), [agentme-edr-010](010-golang-project-tooling.md), and [agentme-edr-014](014-python-project-tooling.md).
|
|
91
91
|
|
|
92
92
|
## Considered Options
|
|
93
93
|
|
|
@@ -9,7 +9,7 @@ valid-from: 2026-05-27
|
|
|
9
9
|
|
|
10
10
|
## Context and Problem Statement
|
|
11
11
|
|
|
12
|
-
ML projects accumulate datasets of different shapes: file-paired annotations, tabular CSVs, and
|
|
12
|
+
ML projects accumulate datasets of different shapes: file-paired annotations, tabular CSVs, and complex per-record structures. Without a shared layout convention, tooling and agents cannot reliably discover schema files, consume data programmatically, or understand what a dataset contains.
|
|
13
13
|
|
|
14
14
|
How should ML datasets be organized on disk so they are self-describing, easy to consume, and consistent across dataset types?
|
|
15
15
|
|
|
@@ -58,6 +58,8 @@ Placing the annotation file next to its source file (same name + `.json`) keeps
|
|
|
58
58
|
|
|
59
59
|
Subdirectories inside `data/` are allowed when the number of files warrants grouping, but the `.json` sibling convention MUST be preserved at each level.
|
|
60
60
|
|
|
61
|
+
Each `.json` annotation file MUST include a top-level `$schema` property whose value is the correct relative path to the dataset's root `dataset.schema.json`, accounting for subdirectory depth (e.g. `"$schema": "../dataset.schema.json"` at one level, `"../../dataset.schema.json"` at two levels). This is the standard editor-tooling convention for associating a JSON instance with its schema, and is validated by rule `06`.
|
|
62
|
+
|
|
61
63
|
#### 03-tabular-datasets-must-use-csv-files-at-root
|
|
62
64
|
|
|
63
65
|
Datasets composed of column-oriented tabular data MUST place CSV files at the root of the dataset folder. All tabular files MUST conform to the schema defined in `dataset.schema.json`, which MUST describe columns as named attributes with their types.
|
|
@@ -70,27 +72,40 @@ Datasets composed of column-oriented tabular data MUST place CSV files at the ro
|
|
|
70
72
|
README.md
|
|
71
73
|
```
|
|
72
74
|
|
|
73
|
-
Multiple CSV files are allowed when they represent different slices or splits of the same schema (e.g. train/test splits, subsets by source). All files in the same dataset MUST share the same column schema.
|
|
75
|
+
Multiple CSV files are allowed when they represent different slices or splits of the same schema (e.g. train/test splits, subsets by source). All files in the same dataset MUST share the same column schema. Each row MUST also be validated by `make lint` per rule `06` — CSV has no `$schema` field (not applicable to that format), so only the row content is checked, not a schema pointer.
|
|
74
76
|
|
|
75
|
-
#### 04-complex-structured-datasets-must-use-
|
|
77
|
+
#### 04-complex-structured-datasets-must-use-per-entry-json-files
|
|
76
78
|
|
|
77
|
-
Datasets with complex or heterogeneous per-record structures (e.g. LLM workflow evaluation sets, Q&A pairs, input → expected_output pairs) MUST use
|
|
79
|
+
Datasets with complex or heterogeneous per-record structures (e.g. LLM workflow evaluation sets, Q&A pairs, input → expected_output pairs) MUST use one JSON file per entry, placed inside the `data/` subfolder. Each file MUST conform to the schema defined in `dataset.schema.json`.
|
|
78
80
|
|
|
79
81
|
```
|
|
80
82
|
/[name-of-dataset]/
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
83
|
+
data/
|
|
84
|
+
case-001.json
|
|
85
|
+
case-002.json
|
|
86
|
+
dataset.schema.json (schema defining the structure of each entry file)
|
|
84
87
|
README.md
|
|
85
88
|
```
|
|
86
89
|
|
|
87
|
-
|
|
90
|
+
Each entry file MUST include a top-level `$schema` property whose value is the correct relative path to the dataset's root `dataset.schema.json`, accounting for subdirectory depth (e.g. `"$schema": "../dataset.schema.json"` at one level, `"../../dataset.schema.json"` at two levels), validated by rule `06`.
|
|
91
|
+
|
|
92
|
+
Subdirectories inside `data/` are allowed for grouping entries by collection source, time period, actor, or similar dimensions when the number of files warrants it. ALL files in one dataset MUST conform to the SAME `dataset.schema.json` — if a project needs a different schema for a different set of entries, that MUST be a separate dataset (its own folder, README, and `dataset.schema.json`), not multiple schemas inside one dataset.
|
|
88
93
|
|
|
89
94
|
#### 05-referenced-files-must-live-in-data-folder
|
|
90
95
|
|
|
91
|
-
When any dataset type (tabular,
|
|
96
|
+
When any dataset type (tabular, per-entry JSON, or annotation-pair) contains references to external files as part of the data (e.g. an entry record that includes a file path), those referenced files MUST be stored inside the `data/` subfolder of the dataset. Paths inside data records MUST be relative to the dataset root.
|
|
97
|
+
|
|
98
|
+
#### 06-datasets-must-be-lint-validated-against-schema
|
|
99
|
+
|
|
100
|
+
Every dataset MUST expose a `make lint` target (in the Makefile of the project/component that owns the dataset) that validates its data against `dataset.schema.json` using the Python [`jsonschema`](https://pypi.org/project/jsonschema/) library:
|
|
101
|
+
|
|
102
|
+
- Per-entry JSON files (rule `04`) and annotation-pair `.json` siblings (rule `02`) MUST each be validated against `dataset.schema.json`, and their `$schema` property MUST be present and resolve to the dataset's actual schema file.
|
|
103
|
+
- CSV rows (rule `03`) MUST each be converted to a JSON object (column header → value) and validated against the same `dataset.schema.json`.
|
|
104
|
+
- `make lint` MUST list every violation found across all files/rows before exiting with a non-zero status (not fail-fast on the first violation).
|
|
105
|
+
- `jsonschema` MUST be declared as a normal project dependency per [agentme-edr-014](014-python-project-tooling.md); no special-casing.
|
|
92
106
|
|
|
93
107
|
## References
|
|
94
108
|
|
|
95
109
|
- [JSON Schema specification](https://json-schema.org/)
|
|
96
|
-
- [
|
|
110
|
+
- [jsonschema (Python library)](https://pypi.org/project/jsonschema/)
|
|
111
|
+
- [agentme-edr-014](014-python-project-tooling.md) — Python project tooling and dependency conventions
|
|
@@ -56,6 +56,14 @@ Every application is conceptually divided into three layers:
|
|
|
56
56
|
- Group related logic into subfolders (aggregation roots)
|
|
57
57
|
- Environment variables must be read only in the bootstrap/entry-point layer of inbound adapters, converted into typed configuration objects, and passed explicitly to all other components
|
|
58
58
|
|
|
59
|
+
- Data flow examples
|
|
60
|
+
|
|
61
|
+
```text
|
|
62
|
+
HTTP request → adapters/http/ → app/create-user → adapters/connectors/postgres/
|
|
63
|
+
CLI command → adapters/cli/ → app/create-dir → adapters/connectors/local-fs/
|
|
64
|
+
Kafka message → adapters/kafka/ → app/process-event → adapters/connectors/stripe-api/
|
|
65
|
+
```
|
|
66
|
+
|
|
59
67
|
#### 04-mandatory-folder-structure
|
|
60
68
|
|
|
61
69
|
```text
|
|
@@ -98,15 +106,56 @@ mysystem/
|
|
|
98
106
|
- Trivial scripts and single-purpose tools (fewer than ~300 lines with a single I/O boundary) MAY skip this layering
|
|
99
107
|
- All other projects MUST use this structure from the start
|
|
100
108
|
|
|
101
|
-
####
|
|
109
|
+
#### 09-unit-testing-and-mocking-strategy
|
|
102
110
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
111
|
+
Unit tests for the `app/` layer MUST mock outbound adapter/connector interfaces at the `app/` → `adapters/connectors/` boundary. Inject connectors as constructor parameters or function arguments so tests can substitute them without touching real databases, HTTP APIs, or external services.
|
|
112
|
+
|
|
113
|
+
The connector implementations themselves SHOULD have their own unit tests that mock the underlying SDK or HTTP client.
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
# Good — inject connector; unit test mocks it
|
|
117
|
+
class OrderService:
|
|
118
|
+
def __init__(self, db: OrderRepository):
|
|
119
|
+
self.db = db
|
|
120
|
+
|
|
121
|
+
def test_create_order_persists_record():
|
|
122
|
+
fake_db = FakeOrderRepository()
|
|
123
|
+
service = OrderService(db=fake_db)
|
|
124
|
+
order = service.create({"item": "widget", "qty": 2})
|
|
125
|
+
assert fake_db.find(order.id) is not None
|
|
107
126
|
```
|
|
108
127
|
|
|
128
|
+
Inbound adapters (`cli/`, `http/`, `grpc/`) are entry points and do not need to be mocked — test the `app/` layer directly by injecting fakes for its outbound connectors. See rule `10` for the naming and placement convention for shared mock files.
|
|
129
|
+
|
|
130
|
+
#### 10-mock-file-strategy
|
|
131
|
+
|
|
132
|
+
When a mock implementation needs to be **reused across multiple tests or imported by an eval script** (e.g. `eval.py` using `mock_fixtures` from [agentme-edr-030](030-ai-test-types-taxonomy.md) rule `02`), define it in a dedicated `_mock` file rather than inline.
|
|
133
|
+
|
|
134
|
+
**When to use a `_mock` file vs inline:**
|
|
135
|
+
- Single-test use → define the mock inline inside the test file (per rule `09` example; no file needed)
|
|
136
|
+
- Reusable across multiple tests OR used from `eval.py` → define in a separate `_mock` file
|
|
137
|
+
|
|
138
|
+
**Scope:** applies to any source file in `adapters/connectors/`, `app/`, or `shared/`. MUST NOT be used for inbound adapters (`cli/`, `http/`, `grpc/`) — those are entry points and are never mocked (rule `09`).
|
|
139
|
+
|
|
140
|
+
**Naming:** insert `_mock` immediately before the file extension:
|
|
141
|
+
|
|
142
|
+
| Source file | Mock file |
|
|
143
|
+
|---|---|
|
|
144
|
+
| `client.py` | `client_mock.py` |
|
|
145
|
+
| `order_service.ts` | `order_service_mock.ts` |
|
|
146
|
+
| `user_store.go` | `user_store_mock_test.go` |
|
|
147
|
+
|
|
148
|
+
**Placement:** follows the project's test file placement convention per [agentme-edr-004](../principles/004-unit-test-requirements.md) rule `04`:
|
|
149
|
+
- Co-located test convention (TypeScript, Go) → mock file in the same directory as the source file
|
|
150
|
+
- Separate test folder convention (Python) → mock file mirrors the source path under the test folder (e.g. `lib/src/<pkg>/adapters/connectors/user-db/client.py` → `lib/tests/<pkg>/adapters/connectors/user-db/client_mock.py`)
|
|
151
|
+
|
|
152
|
+
**Mock contract:**
|
|
153
|
+
- MUST accept a `fixtures` parameter (constructor argument or factory function argument); the value is whatever `mock_fixtures[key]` contains from the dataset entry — its internal structure is opaque and interpreted by the mock implementation
|
|
154
|
+
- MUST NOT fall back to real external calls under any circumstance — if a call cannot be satisfied from the provided fixtures, MUST raise an explicit error (never silently return `null`, `undefined`, or an empty value)
|
|
155
|
+
|
|
109
156
|
## References
|
|
110
157
|
|
|
111
158
|
- [agentme-edr-016](../principles/016-cross-language-module-structure.md) — Defines the module-root structure (Makefile, dist/, .cache/) that wraps this internal layout
|
|
112
159
|
- [agentme-edr-002](../principles/002-coding-best-practices.md) — File size limits and code organization practices that complement this architecture
|
|
160
|
+
- [agentme-edr-004](../principles/004-unit-test-requirements.md) — Rule `04`: test file placement convention per language (governs `_mock` file placement in rule `10`)
|
|
161
|
+
- [agentme-edr-030](030-ai-test-types-taxonomy.md) — Rule `02`: `mock_fixtures` golden dataset envelope that drives `_mock` usage in eval scripts
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: agentme-edr-policy-028-ai-eval-standards
|
|
3
|
-
description: Defines how to structure, write, and run eval tests for AI projects — folder layout,
|
|
3
|
+
description: Defines how to structure, write, and run eval tests for AI projects — folder layout, golden dataset, --type test-type filtering, mock_fixtures wiring, entry-first eval loop, per-type Makefile targets and reports, and MLflow tracking. Use when implementing evals for LLM, Agent, or Workflow projects. For when evals are required see agentme-edr-007 rule 09-ai-project-testing-requirements. For the test type taxonomy and mock_fixtures envelope see agentme-edr-030. For mock file naming see agentme-edr-026 rule 10.
|
|
4
4
|
apply-to: Python AI projects (LLM, Agent, or Workflow tier) that implement eval testing
|
|
5
5
|
valid-from: 2026-06-05
|
|
6
6
|
---
|
|
@@ -29,10 +29,10 @@ Evals are grouped first by the component being evaluated, then by the specific e
|
|
|
29
29
|
evals/
|
|
30
30
|
<component>/ # the component being evaluated (e.g., workflow-x, agent-y, model-z)
|
|
31
31
|
eval-<name>/
|
|
32
|
-
|
|
32
|
+
golden_dataset/ # EDR-024 + EDR-030 compliant golden dataset (README.md, dataset.schema.json, data/)
|
|
33
33
|
eval.py # evaluation script
|
|
34
|
-
report
|
|
35
|
-
Makefile # eval and
|
|
34
|
+
report-<type>.md # generated report, one per evaluated test type (overwritten on each run — see rule 03)
|
|
35
|
+
Makefile # lint, eval, run, and eval-<type> targets
|
|
36
36
|
eval-<name2>/
|
|
37
37
|
...
|
|
38
38
|
<component2>/
|
|
@@ -41,73 +41,126 @@ evals/
|
|
|
41
41
|
|
|
42
42
|
`<component>` MUST match the name of the component under evaluation and use lowercase hyphen-separated words (e.g., `workflow-document-review`, `agent-support`, `model-classifier`).
|
|
43
43
|
|
|
44
|
-
`<name>` identifies the specific evaluation scenario using lowercase hyphen-separated words (e.g., `eval-basic`, `eval-complex`, `eval-edge-cases
|
|
44
|
+
`<name>` identifies the specific evaluation scenario using lowercase hyphen-separated words (e.g., `eval-basic`, `eval-complex`, `eval-edge-cases`). A scenario's `golden_dataset` MAY mix multiple test types across its entries: label each entry with its applicable `test_types` ([agentme-edr-030](030-ai-test-types-taxonomy.md) rule `04`) and use the `eval-<type>` targets below to run one type at a time.
|
|
45
45
|
|
|
46
|
-
The `
|
|
46
|
+
The `golden_dataset/` subfolder MUST be a valid [agentme-edr-024](024-ml-dataset-structure.md) dataset (`README.md`, `dataset.schema.json`, one JSON file per entry under `data/` per rule `04-complex-structured-datasets-must-use-per-entry-json-files`, lint-validated per rule `06`) whose entries follow the golden dataset envelope defined in [agentme-edr-030](030-ai-test-types-taxonomy.md) rule `02`.
|
|
47
47
|
|
|
48
|
-
Each `evals/<component>/eval-<name>/Makefile` MUST define:
|
|
48
|
+
Each `evals/<component>/eval-<name>/Makefile` MUST declare a `TEST_TYPES` variable listing the `test_types` values present in its golden dataset, and define:
|
|
49
49
|
|
|
50
50
|
| Target | Behaviour |
|
|
51
51
|
|---|---|
|
|
52
|
-
| `
|
|
53
|
-
| `
|
|
52
|
+
| `lint` | Validates every `golden_dataset/data/*.json` file against `golden_dataset/dataset.schema.json` per [agentme-edr-024](024-ml-dataset-structure.md) rule `06` |
|
|
53
|
+
| `eval` | Depends on `lint`; runs `eval.py --type=all` with threshold enforcement; exits non-zero on failure (CI-safe) |
|
|
54
|
+
| `run` | Depends on `lint`; runs `eval.py --type=all` without threshold enforcement (exploration / debugging) |
|
|
55
|
+
| `eval-<type>` | Depends on `lint`; runs `eval.py --type=<type>` for one declared test type, following [agentme-edr-008](../devops/008-common-targets.md) rule `03`'s `eval-<qualifier>` convention |
|
|
54
56
|
|
|
55
|
-
|
|
57
|
+
```makefile
|
|
58
|
+
TEST_TYPES := smoke functional safety
|
|
59
|
+
|
|
60
|
+
lint:
|
|
61
|
+
mise exec -- uv run --project . python lint_dataset.py golden_dataset/
|
|
62
|
+
|
|
63
|
+
eval: lint
|
|
64
|
+
mise exec -- uv run --project . python eval.py --type=all
|
|
65
|
+
|
|
66
|
+
run: lint
|
|
67
|
+
mise exec -- uv run --project . python eval.py --type=all --no-threshold
|
|
68
|
+
|
|
69
|
+
eval-%: lint
|
|
70
|
+
mise exec -- uv run --project . python eval.py --type=$*
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
The module root Makefile MUST expose `make eval` and `make lint` targets that delegate to `eval` and `lint` respectively in every `evals/<component>/eval-<name>/Makefile`:
|
|
56
74
|
|
|
57
75
|
```makefile
|
|
58
76
|
eval:
|
|
59
77
|
$(MAKE) -C evals/workflow-document-review/eval-basic eval
|
|
60
78
|
$(MAKE) -C evals/workflow-document-review/eval-complex eval
|
|
79
|
+
|
|
80
|
+
lint:
|
|
81
|
+
$(MAKE) -C evals/workflow-document-review/eval-basic lint
|
|
82
|
+
$(MAKE) -C evals/workflow-document-review/eval-complex lint
|
|
61
83
|
```
|
|
62
84
|
|
|
63
85
|
#### 02-eval-script-requirements
|
|
64
86
|
|
|
65
87
|
Each `eval.py` script MUST:
|
|
66
88
|
|
|
67
|
-
- Load the dataset from `
|
|
68
|
-
-
|
|
69
|
-
-
|
|
70
|
-
-
|
|
71
|
-
-
|
|
72
|
-
-
|
|
89
|
+
- Load the golden dataset from `golden_dataset/` in the same eval folder, following [agentme-edr-024](024-ml-dataset-structure.md) and the entry envelope in [agentme-edr-030](030-ai-test-types-taxonomy.md) rule `02` (one JSON file per entry, `test_types` array, `input`, `expected_output`, optional `mock_fixtures`).
|
|
90
|
+
- Accept a required `--type=<test_type>|all` CLI argument and filter entries whose `test_types` array contains the requested value; `--type=all` includes every entry.
|
|
91
|
+
- Iterate **entry-first**: for each entry in the filtered set, invoke the real component exactly once; then score that single `actual_output` for every `test_types` value the entry carries that falls within the current `--type` scope — never invoke the component more than once per entry per run.
|
|
92
|
+
- When an entry contains `mock_fixtures` ([agentme-edr-030](030-ai-test-types-taxonomy.md) rule `02`), configure each named mock adapter with its fixture data BEFORE invoking the component for that entry. Each entry MUST use fresh mock instances so fixture state does not bleed across entries. `mock_fixtures` applies to all test types including `human`. `mock_fixtures` MUST NOT configure LLM adapters — the LLM call MUST always be real (see [agentme-edr-030](030-ai-test-types-taxonomy.md) rule `03`). How mock adapters are discovered and instantiated is left to the project; see [agentme-edr-026](026-pragmatic-hexagonal-architecture.md) rule `10` for the `_mock` file naming and placement convention.
|
|
93
|
+
- Run every component invocation against **real LLM providers** (not mocked responses), to capture model drift.
|
|
94
|
+
- For `human` entries: invoke the component to capture `actual_output`, export each entry's `input`, `expected_output.human_test` instructions, and `actual_output` into a manual-review checklist (`report-human.md`). MUST NOT invoke an automated scorer and MUST NOT enforce a pass/fail threshold for it. Other `test_types` on the same entry (e.g. `functional`) are still scored automatically.
|
|
95
|
+
- After all entries are processed, compute aggregate metrics per test type, log them to a local MLflow experiment (see rule `04`), write one `report-<type>.md` per evaluated test type (rule `03`), and exit with a non-zero status when any metric falls below its defined threshold per [agentme-edr-007](../principles/007-project-quality-standards.md) rule `07-statistical-models-must-have-eval-targets`. The `human` type has no threshold and does not trigger a non-zero exit.
|
|
96
|
+
- Compare outputs to expected values using project-defined quality thresholds per test type. Thresholds MUST be declared explicitly (e.g., in a Makefile variable or README) — this Policy does not mandate which test types a project must threshold or what value to use (see [agentme-edr-030](030-ai-test-types-taxonomy.md) rule `06`).
|
|
73
97
|
|
|
74
98
|
**Example:**
|
|
75
99
|
|
|
76
100
|
```python
|
|
101
|
+
import argparse
|
|
102
|
+
from collections import defaultdict
|
|
77
103
|
import mlflow
|
|
78
104
|
from my_package.app.workflows.document_review_workflow.graph import graph
|
|
79
105
|
|
|
80
|
-
EVAL_MIN_ACCURACY = 0.85
|
|
106
|
+
EVAL_MIN_ACCURACY = {"functional": 0.85, "smoke": 0.85}
|
|
107
|
+
|
|
108
|
+
parser = argparse.ArgumentParser()
|
|
109
|
+
parser.add_argument("--type", required=True)
|
|
110
|
+
args = parser.parse_args()
|
|
81
111
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
for sample in load_dataset("dataset/"):
|
|
85
|
-
output = graph.invoke({"document": sample["input"]})
|
|
86
|
-
results.append(output["label"] == sample["expected_label"])
|
|
112
|
+
entries = load_golden_dataset("golden_dataset/", test_type=args.type) # "all" loads every entry
|
|
113
|
+
resolved_types = resolve_types(args.type, entries)
|
|
87
114
|
|
|
88
|
-
|
|
89
|
-
mlflow.log_metric("accuracy", accuracy)
|
|
115
|
+
mlflow.set_experiment("document-review/eval-basic")
|
|
90
116
|
|
|
91
|
-
|
|
117
|
+
with mlflow.start_run():
|
|
118
|
+
mlflow.set_tag("test_types", ",".join(sorted(resolved_types)))
|
|
92
119
|
|
|
93
|
-
|
|
94
|
-
|
|
120
|
+
results = defaultdict(list)
|
|
121
|
+
|
|
122
|
+
# Entry-first loop: invoke each entry exactly once
|
|
123
|
+
for entry in entries:
|
|
124
|
+
# Configure mock adapters from mock_fixtures before invocation
|
|
125
|
+
# (implementation left to the project — see agentme-edr-026 rule 10)
|
|
126
|
+
if entry.get("mock_fixtures"):
|
|
127
|
+
configure_mocks(entry["mock_fixtures"]) # project-defined helper
|
|
128
|
+
|
|
129
|
+
actual_output = invoke_component(entry, graph)
|
|
130
|
+
|
|
131
|
+
for test_type in [t for t in entry["test_types"] if t in resolved_types]:
|
|
132
|
+
if test_type == "human":
|
|
133
|
+
export_human_review(entry, actual_output)
|
|
134
|
+
continue
|
|
135
|
+
results[test_type].append(score(test_type, actual_output, entry["expected_output"]))
|
|
136
|
+
|
|
137
|
+
# Aggregate, report, and enforce thresholds per test type
|
|
138
|
+
for test_type in resolved_types:
|
|
139
|
+
if test_type == "human":
|
|
140
|
+
continue
|
|
141
|
+
|
|
142
|
+
accuracy = sum(results[test_type]) / len(results[test_type])
|
|
143
|
+
mlflow.log_metric(f"{test_type}_accuracy", accuracy)
|
|
144
|
+
write_eval_report(test_type, results[test_type], thresholds={"accuracy": EVAL_MIN_ACCURACY[test_type]})
|
|
145
|
+
|
|
146
|
+
if accuracy < EVAL_MIN_ACCURACY[test_type]:
|
|
147
|
+
raise SystemExit(f"Eval failed: {test_type} accuracy {accuracy:.2f} < {EVAL_MIN_ACCURACY[test_type]}")
|
|
95
148
|
```
|
|
96
149
|
|
|
97
150
|
#### 03-eval-report-file
|
|
98
151
|
|
|
99
|
-
Each eval script MUST produce `report
|
|
152
|
+
Each eval script MUST produce one `report-<type>.md` per evaluated test type in the same `evals/<component>/eval-<name>/` folder and overwrite each on every run — only the types included in the current `--type` invocation are (re)written; report files for other types are left untouched. The `human` type does not produce a metrics report (see below).
|
|
100
153
|
|
|
101
154
|
**Generation constraint:** The report MUST be produced programmatically, reading raw metric values directly from MLflow. No LLM or generative model may write, summarize, or paraphrase any section of the report, to prevent hallucinated metric values.
|
|
102
155
|
|
|
103
156
|
The report MUST follow this template:
|
|
104
157
|
|
|
105
158
|
```markdown
|
|
106
|
-
# Eval Report: <name>
|
|
159
|
+
# Eval Report: <name> — <type>
|
|
107
160
|
|
|
108
161
|
**Date:** <ISO date>
|
|
109
|
-
**Dataset:**
|
|
110
|
-
**Script:** eval.py
|
|
162
|
+
**Dataset:** golden_dataset/
|
|
163
|
+
**Script:** eval.py --type=<type>
|
|
111
164
|
**Thresholds:** accuracy ≥ <value>, F1 ≥ <value>
|
|
112
165
|
|
|
113
166
|
## Overall Results
|
|
@@ -142,14 +195,14 @@ $$\frac{\hat{p} + \frac{z^2}{2n} \pm z\sqrt{\frac{\hat{p}(1-\hat{p})}{n} + \frac
|
|
|
142
195
|
|
|
143
196
|
Where $\hat{p}$ is observed accuracy and $n$ is sample count. Accuracy and F1 are required; precision and recall are recommended.
|
|
144
197
|
|
|
145
|
-
**Filled-in example** (`evals/workflow-document-review/eval-basic/report.md` for a document review workflow):
|
|
198
|
+
**Filled-in example** (`evals/workflow-document-review/eval-basic/report-functional.md` for a document review workflow):
|
|
146
199
|
|
|
147
200
|
```markdown
|
|
148
|
-
# Eval Report: eval-basic
|
|
201
|
+
# Eval Report: eval-basic — functional
|
|
149
202
|
|
|
150
203
|
**Date:** 2026-06-12
|
|
151
|
-
**Dataset:**
|
|
152
|
-
**Script:** eval.py
|
|
204
|
+
**Dataset:** golden_dataset/
|
|
205
|
+
**Script:** eval.py --type=functional
|
|
153
206
|
**Thresholds:** accuracy ≥ 0.85, F1 ≥ 0.80
|
|
154
207
|
|
|
155
208
|
## Overall Results
|
|
@@ -169,7 +222,7 @@ Where $\hat{p}$ is observed accuracy and $n$ is sample count. Accuracy and F1 ar
|
|
|
169
222
|
## Per-item Results
|
|
170
223
|
|
|
171
224
|
| ID | Input Summary | Expected | Actual | Correct |
|
|
172
|
-
|
|
225
|
+
|-----|--------------------------------------|----------|----------|---------|
|
|
173
226
|
| 001 | Contract renewal, 3 pages, standard | approve | approve | ✓ |
|
|
174
227
|
| 002 | NDA with unusual liability clause | escalate | escalate | ✓ |
|
|
175
228
|
| 003 | Vendor invoice, missing PO number | reject | reject | ✓ |
|
|
@@ -179,20 +232,26 @@ Where $\hat{p}$ is observed accuracy and $n$ is sample count. Accuracy and F1 ar
|
|
|
179
232
|
## Notes
|
|
180
233
|
|
|
181
234
|
- Sample 005 misclassified: redlined IP clause not flagged as escalation trigger. Possible model drift.
|
|
182
|
-
- MLflow run: experiment `workflow-document-review/eval-basic` — view with `mlflow ui`
|
|
235
|
+
- MLflow run: experiment `workflow-document-review/eval-basic`, tag `test_types=functional` — view with `mlflow ui`
|
|
183
236
|
```
|
|
184
237
|
|
|
238
|
+
**`human` type artifact:** instead of `report-human.md` with metrics, `--type=human` produces a checklist artifact (still named `report-human.md`) listing, per entry, its `input`, `expected_output.human_test` instructions, and the captured `actual_output` — with no Overall Results table, threshold, or PASS/FAIL section, since this type is never auto-scored.
|
|
239
|
+
|
|
185
240
|
#### 04-eval-mlflow-unique-port
|
|
186
241
|
|
|
187
242
|
Each `evals/<component>/eval-<name>/Makefile` MUST start its MLflow tracking server on a **unique port** to prevent conflicts when multiple eval Makefiles are run concurrently or in parallel (e.g., in CI or across multiple terminal sessions).
|
|
188
243
|
|
|
189
|
-
Ports MUST be statically assigned per eval scenario and MUST NOT reuse the default `5000` port (reserved for `dev-mlflow` per [agentme-edr-008](../devops/008-common-targets.md) rule `09-ai-project-dev-targets`). Assign ports starting at `5100` and incrementing by 1 for each additional eval scenario across the entire project.
|
|
244
|
+
Ports MUST be statically assigned per eval scenario (not per test type) and MUST NOT reuse the default `5000` port (reserved for `dev-mlflow` per [agentme-edr-008](../devops/008-common-targets.md) rule `09-ai-project-dev-targets`). Assign ports starting at `5100` and incrementing by 1 for each additional eval scenario across the entire project.
|
|
245
|
+
|
|
246
|
+
The MLflow **experiment** is scoped to the eval scenario: `<component>/<eval-name>` (e.g. `document-review/eval-basic`). Each `mlflow.start_run()` call MUST set a `test_types` tag listing the test types evaluated in that invocation (comma-separated, e.g. `"functional,smoke"` for `--type=all`, `"smoke"` for `--type=smoke`). A remote MLflow server MUST NOT be required — all tracking is local.
|
|
190
247
|
|
|
191
248
|
## References
|
|
192
249
|
|
|
193
250
|
- [agentme-edr-007](../principles/007-project-quality-standards.md) — Project quality standards: when evals are required per AI tier (rule `09-ai-project-testing-requirements`) and statistical model eval targets (rule `07-statistical-models-must-have-eval-targets`)
|
|
251
|
+
- [agentme-edr-030](030-ai-test-types-taxonomy.md) — AI test types taxonomy: `test_types` enum, golden dataset entry envelope (including `mock_fixtures`), and mocking constraints per type
|
|
252
|
+
- [agentme-edr-026](026-pragmatic-hexagonal-architecture.md) — Rule `10`: `_mock` file naming and placement convention for mock adapters used in `mock_fixtures`
|
|
194
253
|
- [agentme-edr-018](018-ai-llm-development-standards.md) — LLM development standards: LangChain framework and observability
|
|
195
254
|
- [agentme-edr-019](019-ai-agents-development-standards.md) — Agent development standards
|
|
196
255
|
- [agentme-edr-021](021-ai-workflow-development-standards.md) — Workflow development standards
|
|
197
|
-
|
|
198
|
-
- [agentme-edr-
|
|
256
|
+
- [agentme-edr-024](024-ml-dataset-structure.md) — ML dataset structure, per-entry JSON format, and schema-lint validation for golden datasets
|
|
257
|
+
- [agentme-edr-008](../devops/008-common-targets.md) — `eval-<qualifier>` Makefile convention (rule `03`) and Mise tool-execution flow (rule `02`)
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: agentme-edr-policy-030-ai-test-types-taxonomy
|
|
3
|
+
description: Names AI-application test types (safety, responsible-AI, quality-eval, prompt, code-level) with their group, objective, mocking constraint, and relevance, and defines the shared "golden dataset" entry envelope that agentme-edr-028's eval tooling filters by test_types. Use when deciding which AI test types to implement or when authoring a golden dataset entry.
|
|
4
|
+
apply-to: AI projects (LLM, Agent, or Workflow tier) implementing AI-specific test types beyond generic code-level unit/integration tests
|
|
5
|
+
valid-from: 2026-07-05
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# agentme-edr-policy-030: AI test types taxonomy
|
|
9
|
+
|
|
10
|
+
## Context and Problem Statement
|
|
11
|
+
|
|
12
|
+
AI components need test types beyond generic unit/integration tests (safety, fairness, groundedness, functional accuracy, etc.). Which test types should be named, and how should their datasets and eval tooling work?
|
|
13
|
+
|
|
14
|
+
## Decision Outcome
|
|
15
|
+
|
|
16
|
+
**Adopt a named taxonomy of AI test types plus a shared "golden dataset" entry envelope that agentme-edr-028's eval tooling filters by `test_types`.**
|
|
17
|
+
|
|
18
|
+
Each test type is named with its group, objective, mocking constraint, applicability, and relevance; every golden dataset entry is labeled with the test types it applies to.
|
|
19
|
+
|
|
20
|
+
### Details
|
|
21
|
+
|
|
22
|
+
#### 01-golden-dataset-concept
|
|
23
|
+
|
|
24
|
+
A **golden dataset** comprises all eval case entries used to test an AI component (LLM, Agent, or Workflow tier); each entry is labeled with the `test_types` (rule `04`) it applies to. It is the dataset consumed by [agentme-edr-028](028-ai-eval-standards.md) evals and stored as one JSON file per entry per [agentme-edr-024](024-ml-dataset-structure.md) rule `04`, at `evals/<component>/eval-<name>/golden_dataset/`.
|
|
25
|
+
|
|
26
|
+
#### 02-golden-dataset-entry-envelope
|
|
27
|
+
|
|
28
|
+
Every golden dataset entry (a JSON file in `golden_dataset/data/`) MUST have this shape, in addition to any project-specific fields:
|
|
29
|
+
|
|
30
|
+
```json
|
|
31
|
+
{
|
|
32
|
+
"$schema": "../dataset.schema.json",
|
|
33
|
+
"test_types": ["functional"],
|
|
34
|
+
"input": "...",
|
|
35
|
+
"expected_output": "...",
|
|
36
|
+
"mock_fixtures": {
|
|
37
|
+
"system_y": [{"123": {"name": "Flavio"}}, {"456": {"name": "Andrew"}}]
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
- `test_types` — array, values MUST come from rule `04`'s enum, MUST contain at least one value. An entry MAY carry more than one value additively (e.g. `["functional", "smoke", "human"]`) — no test type excludes another.
|
|
43
|
+
- `input` — for Prompt-tier components, a raw prompt string or the prompt template's input parameters object; for Agent/Workflow-tier components, the input attributes object passed to the component.
|
|
44
|
+
- `expected_output` — the fields used to score the entry under each of its automated `test_types`: output attributes for an LLM-as-judge rubric, a target for vector-similarity scoring, or exact attribute values for strict comparison. When `human` is one of the entry's `test_types`, `expected_output` MUST additionally include a `human_test` string field with manual-verification instructions (e.g. `"check for ethical issues, verify record change in system X"`) — this supplements, and never replaces, the entry's automated scoring fields.
|
|
45
|
+
- `mock_fixtures` — optional object; keys identify the adapter or external system to mock (SHOULD match the connector folder name under `adapters/connectors/<name>` for readability, though not enforced), values are any valid JSON interpreted by the mock implementation. When present, eval.py MUST configure each named mock adapter with its fixture data BEFORE invoking the component for that entry; each entry MUST use fresh mock instances to prevent state from bleeding across entries. `mock_fixtures` applies to all `test_types` including `human` — the component is still invoked for human entries to capture `actual_output`. `mock_fixtures` MUST NOT include keys for LLM adapters: all golden dataset test types are rated `mocks disallowed for LLM calls` (rule `03`), so the LLM call MUST always be real; LLM provider mocking belongs exclusively to unit tests via [agentme-edr-018](018-ai-llm-development-standards.md) rule `04`. See [agentme-edr-026](026-pragmatic-hexagonal-architecture.md) rule `10` for the `_mock` file naming and placement convention.
|
|
46
|
+
- The dataset's `dataset.schema.json` MUST require `test_types`, `input`, and `expected_output`, and SHOULD declare `mock_fixtures` as optional (`"type": "object", "additionalProperties": {}`), per [agentme-edr-024](024-ml-dataset-structure.md) rule `04`.
|
|
47
|
+
|
|
48
|
+
#### 03-mocks-allowed-values
|
|
49
|
+
|
|
50
|
+
The taxonomy in rule `05` rates each test type using one of three values:
|
|
51
|
+
|
|
52
|
+
| Value | Meaning |
|
|
53
|
+
|---|---|
|
|
54
|
+
| `mocks allowed` | Fully offline; fakes may replace every dependency (e.g. `FakeListChatModel` per [agentme-edr-018](018-ai-llm-development-standards.md) rule `04`). |
|
|
55
|
+
| `mocks disallowed` | No mocking of any dependency — real external systems required. |
|
|
56
|
+
| `mocks disallowed for LLM calls` | Tools and other external/dependency calls MAY be mocked; only the LLM call itself MUST be real for the test to be meaningful. |
|
|
57
|
+
|
|
58
|
+
#### 04-test-types-enum
|
|
59
|
+
|
|
60
|
+
A golden dataset entry's `test_types` array MUST only use these values: `safety`, `adversarial`, `fairness`, `bias`, `robustness`, `explainability`, `groundedness`, `functional`, `prompt`, `smoke`, `human`. These correspond to the dataset-driven rows of rule `05`. **Unit test** and **Integration test** (the two Code-level rows) are NOT part of this enum — they have no golden dataset entries and remain governed entirely by [agentme-edr-004](../principles/004-unit-test-requirements.md) and [agentme-edr-007](../principles/007-project-quality-standards.md) rule `08`.
|
|
61
|
+
|
|
62
|
+
#### 05-test-type-taxonomy
|
|
63
|
+
|
|
64
|
+
| Test Type Name | Group | Test Objective | Mocks Allowed | When to Apply | Relevance – Business | Relevance – Development Team | Priority (1-5) |
|
|
65
|
+
|---|---|---|---|---|---|---|---|
|
|
66
|
+
| Safety/content eval | Safety & adversarial | Detect harmful, biased, or policy-violating output | mocks disallowed for LLM calls | Any user-facing release | Avoids reputational harm; acceptable-use compliance | Automated content gate before merge/release | 5 |
|
|
67
|
+
| Adversarial/red-team test | Safety & adversarial | Probe for prompt injection, jailbreaks, unsafe tool use | mocks disallowed for LLM calls | System exposes tool-invocation or agent loops | Reduces security-incident/breach liability | Finds exploitable tool-loop paths before attackers do | 5 |
|
|
68
|
+
| Fairness test | Responsible AI | Verify equitable outcomes across user groups | mocks disallowed for LLM calls | Output affects decisions about individuals/groups | Regulatory requirement; protects equitable access | Surfaces uneven outcomes before release | 4 |
|
|
69
|
+
| Bias test | Responsible AI | Detect skewed or stereotyped associations | mocks disallowed for LLM calls | User-facing content generation | Lowers legal/reputational exposure | Catches bias introduced by data/prompts/fine-tuning | 3 |
|
|
70
|
+
| Robustness test | Responsible AI | Verify stable behavior under noisy/out-of-distribution input | mocks disallowed for LLM calls | Inputs come from untrusted/variable sources | Protects reliability/SLAs | Confirms graceful degradation, guides input validation | 3 |
|
|
71
|
+
| Explainability test | Responsible AI | Verify output is justifiable with a faithful rationale | mocks disallowed for LLM calls | Output must be justified to users/auditors/regulators | Required for auditability; builds user trust | Gives rationale trace for debugging wrong answers | 2 |
|
|
72
|
+
| Groundedness (RAG) eval | Quality eval | Verify the answer is supported by retrieved context | mocks disallowed for LLM calls | System uses retrieval-augmented generation | Avoids confidently-wrong answers reaching customers | Pinpoints retrieval/prompt bugs | 4 |
|
|
73
|
+
| Human evaluation | Quality eval | Manually verify aspects automated scoring can't (ethics, side effects, external state) | mocks disallowed for LLM calls | Before major releases; periodic spot-check | Defensible, human-reviewed sign-off | Catches what automated metrics miss | 3 |
|
|
74
|
+
| Functional eval (golden-dataset accuracy / LLM-as-judge) | Quality eval | Measure output correctness against the golden dataset | mocks disallowed for LLM calls | Required before every Workflow release ([agentme-edr-007](../principles/007-project-quality-standards.md) rule `09`); advised elsewhere | Auditable evidence of business correctness before release | Detects regressions from model/provider/prompt changes | 5 |
|
|
75
|
+
| Smoke test | Quality eval | Fast pass/fail check on a small, critical subset before running fuller suites | mocks disallowed for LLM calls | Every commit/PR, before functional/responsible-AI evals run | Cheap early warning before slower evals run | Fast, cheap feedback loop | 4 |
|
|
76
|
+
| Prompt regression test | Prompt/LLM | Detect behavior change when a prompt or model version changes | mocks disallowed for LLM calls | Whenever a prompt template or model version changes | Prevents shipping a worse experience via a "small" tweak | Fast check on every prompt edit | 3 |
|
|
77
|
+
| Integration test | Code-level | Verify real interaction with external systems | mocks disallowed | Component depends on external systems | Reduces production outages from integration mismatches | Catches wiring bugs unit tests can't see | 2 |
|
|
78
|
+
| Unit test (offline, mocked) | Code-level | Verify deterministic logic in isolation, offline | mocks allowed | Required for Workflow tier every commit ([agentme-edr-007](../principles/007-project-quality-standards.md) rule `09`) | Lowest-cost point to catch defects | Fastest, fully offline feedback on every commit | 5 |
|
|
79
|
+
|
|
80
|
+
#### 06-priority-and-relevance-are-descriptive-only
|
|
81
|
+
|
|
82
|
+
Priority, Relevance, and When to Apply in rule `05` are guidance for prioritization conversations — they do NOT mandate which test types a project must implement, nor their thresholds. [agentme-edr-007](../principles/007-project-quality-standards.md) rule `09` remains the only tier-level testing requirement in force (Workflow unit tests + functional evals). Once a project chooses to implement and threshold a test type, [agentme-edr-028](028-ai-eval-standards.md) rule `02`'s failing-threshold behavior applies uniformly, regardless of this table's priority rating — a project may enforce fairness at 70% and functional at 90%, or skip fairness entirely; that choice is a project/business decision, not one this Policy makes.
|
|
83
|
+
|
|
84
|
+
#### 07-smoke-is-distinct-from-test-smoke
|
|
85
|
+
|
|
86
|
+
The `smoke` test type (surfaced as the `eval-smoke` Makefile target, a fast subset of the golden-dataset functional eval) is a different concept from [agentme-edr-008](../devops/008-common-targets.md)'s existing `test-smoke` target (a fast subset of code-level tests). Both may exist in the same project; do not conflate them.
|
|
87
|
+
|
|
88
|
+
## References
|
|
89
|
+
|
|
90
|
+
- [agentme-edr-024](024-ml-dataset-structure.md) — Golden dataset file layout, per-entry JSON format, `$schema` pointer, and schema-lint validation
|
|
91
|
+
- [agentme-edr-028](028-ai-eval-standards.md) — Eval folder structure, `--type` filtering, per-type Makefile targets, and per-type reports that consume this taxonomy
|
|
92
|
+
- [agentme-edr-026](026-pragmatic-hexagonal-architecture.md) — Rule `10`: `_mock` file naming and placement convention for mock adapters referenced by `mock_fixtures`
|
|
93
|
+
- [agentme-edr-007](../principles/007-project-quality-standards.md) — Rule `09` tier-level testing requirements (the only mandated AI testing baseline)
|
|
94
|
+
- [agentme-edr-008](../devops/008-common-targets.md) — Rule `03` `eval-<qualifier>` Makefile convention; rule `03`'s `test-smoke` (distinguished in rule `07`)
|
|
95
|
+
- [agentme-edr-018](018-ai-llm-development-standards.md) — LLM tier definition and mocking utilities referenced by the `mocks allowed` value
|
|
96
|
+
- [agentme-edr-004](../principles/004-unit-test-requirements.md) — Unit test requirements underlying the Code-level rows
|
|
@@ -57,8 +57,6 @@ Module folder responsibilities, artifact locations, and test-folder conventions
|
|
|
57
57
|
- **MUST** contain a `README.md` with: purpose, architecture overview, how to build, and how to run.
|
|
58
58
|
- **MAY** contain `examples/`, `tests_integration/`, and `tests_benchmark/` when those artifacts apply to multiple modules inside the application.
|
|
59
59
|
|
|
60
|
-
*Why:* Isolating applications prevents implicit coupling and makes the `shared/` boundary explicit and intentional.
|
|
61
|
-
|
|
62
60
|
#### 03-module-folders
|
|
63
61
|
|
|
64
62
|
- A module is a subfolder inside an application that is independently compilable and produces a build artifact.
|
|
@@ -87,8 +85,6 @@ Module Makefiles **SHOULD** also provide `lint-fix` and `install` when the under
|
|
|
87
85
|
The root `Makefile` **MUST** also define a `setup` target that guides a new contributor to prepare their machine.
|
|
88
86
|
The root `setup` target **MUST** run `mise install` and any small repository bootstrap required before routine targets work.
|
|
89
87
|
|
|
90
|
-
*Why:* Makefiles provide a universal, stack-agnostic entry point regardless of programming language.
|
|
91
|
-
|
|
92
88
|
#### 06-mise-for-tooling-management
|
|
93
89
|
|
|
94
90
|
- [Mise](https://mise.jdx.dev/) **MUST** be used to pin all tool versions (compilers, runtimes, CLI tools).
|
|
@@ -100,8 +96,6 @@ The root `setup` target **MUST** run `mise install` and any small repository boo
|
|
|
100
96
|
- If a required tool is missing, the first remediation step **MUST** be to update `.mise.toml` or run `mise install`, not to install ad-hoc global tools with language-specific installers such as `go install`, `npm install -g`, `pip install --user`, or `cargo install`.
|
|
101
97
|
- Root and module `Makefile` targets **MUST** work when invoked as plain `make <target>` after `make setup`.
|
|
102
98
|
|
|
103
|
-
*Why:* Eliminates "works on my machine" build failures by ensuring identical tool versions across all environments.
|
|
104
|
-
|
|
105
99
|
#### 07-root-readme
|
|
106
100
|
|
|
107
101
|
The root `README.md` **MUST** include: overview, machine setup, quickstart, and a repository map.
|
|
@@ -116,8 +110,6 @@ All releases **MUST** be tagged using the format `<module-name>/<semver>` (e.g.,
|
|
|
116
110
|
|
|
117
111
|
`<module-name>` is preferably the path-like identifier of the module being released. A custom name is allowed but the folder name is strongly preferred.
|
|
118
112
|
|
|
119
|
-
*Why:* Namespacing tags by module prevents collisions and makes it easy to filter release history when multiple modules release independently.
|
|
120
|
-
|
|
121
113
|
---
|
|
122
114
|
|
|
123
115
|
#### 11-summary-of-requirements
|
|
@@ -15,7 +15,7 @@ What standard set of Makefile target names and execution rules should projects a
|
|
|
15
15
|
|
|
16
16
|
## Decision Outcome
|
|
17
17
|
|
|
18
|
-
**Every project
|
|
18
|
+
**Every project MUST expose its development actions through a root `Makefile` using a defined set of standardized target names. Target implementation and tool-execution rules follow [agentme-edr-017](017-tool-execution-and-scripting.md), which requires `mise exec --` before routine tool commands.**
|
|
19
19
|
|
|
20
20
|
Standardizing both the target names and the execution chain removes per-project guesswork, makes CI pipelines reusable, and keeps tooling behavior visible in one place.
|
|
21
21
|
|
|
@@ -23,18 +23,18 @@ Standardizing both the target names and the execution chain removes per-project
|
|
|
23
23
|
|
|
24
24
|
#### 01-every-project-must-have-root-makefile
|
|
25
25
|
|
|
26
|
-
The project root
|
|
26
|
+
The project root **MUST** contain a single authoritative `Makefile` that exposes the standard target names defined in rule 3. Developers and CI pipelines **MUST** invoke routine actions through this `Makefile`, **NEVER** by calling underlying tools directly in documentation, CI, or daily workflow commands.
|
|
27
27
|
|
|
28
28
|
`make <target>` is the shared contract across projects and languages.
|
|
29
29
|
|
|
30
|
-
- The root `Makefile`
|
|
31
|
-
- The root `Makefile`
|
|
32
|
-
- Reverse-compatibility wrappers are allowed when an ecosystem expects them, but they
|
|
30
|
+
- The root `Makefile` **MUST** be the entry point for both developers and pipelines.
|
|
31
|
+
- The root `Makefile` **MUST** expose at minimum the common targets defined in this XDR.
|
|
32
|
+
- Reverse-compatibility wrappers are allowed when an ecosystem expects them, but they **MUST** stay trivial.
|
|
33
33
|
- Allowed: `package.json` script `"test": "make test"`
|
|
34
34
|
- Not allowed: `make test` -> `npm run test` -> tool command
|
|
35
|
-
- Project logic
|
|
35
|
+
- Project logic **MUST NOT** live in npm scripts, Mise tasks, shell wrappers, or other secondary runners when the same logic belongs in the `Makefile`.
|
|
36
36
|
|
|
37
|
-
*Why:* The project entry point
|
|
37
|
+
*Why:* The project entry point **MUST** stay language-agnostic and obvious. A developer **SHOULD** be able to inspect the `Makefile` and immediately see which real tool commands will run.
|
|
38
38
|
|
|
39
39
|
#### 02-makefile-recipes-must-use-mise
|
|
40
40
|
|
|
@@ -47,10 +47,10 @@ make <target>
|
|
|
47
47
|
-> explicit tool command
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
-
- The `setup` target
|
|
51
|
-
- Routine targets such as `build`, `lint`, `test`, `run`, and `publish`
|
|
52
|
-
- Each Makefile recipe
|
|
53
|
-
- Makefile recipes
|
|
50
|
+
- The `setup` target **MUST** run `mise install` and any small project-specific bootstrap needed before normal targets work.
|
|
51
|
+
- Routine targets such as `build`, `lint`, `test`, `run`, and `publish` **MUST** be invoked as `make <target>` by both contributors and CI.
|
|
52
|
+
- Each Makefile recipe **MUST** call the real underlying command through `mise exec --`, following [agentme-edr-017](017-tool-execution-and-scripting.md).
|
|
53
|
+
- Makefile recipes **MUST NOT** add extra script layers such as `npm run`, `pnpm run`, `yarn run`, `mise run`, `mise tasks`, or shell aliases when those layers only forward to another command.
|
|
54
54
|
- Calling the actual tool is allowed even when that tool itself launches another program as part of its normal interface.
|
|
55
55
|
- Allowed: `mise exec -- pnpm exec eslint ./src`
|
|
56
56
|
- Allowed: `mise exec -- go test -cover ./...`
|
|
@@ -66,17 +66,17 @@ make <target>
|
|
|
66
66
|
|
|
67
67
|
#### 03-standard-target-groups-and-names
|
|
68
68
|
|
|
69
|
-
Targets are organized into five lifecycle groups. Projects
|
|
69
|
+
Targets are organized into five lifecycle groups. Projects **MUST** use these names unchanged. Extensions are allowed (see rule 5) but the core names **MUST NOT** be repurposed.
|
|
70
70
|
|
|
71
71
|
##### Developer group
|
|
72
72
|
|
|
73
73
|
| Target | Purpose |
|
|
74
74
|
|--------|---------|
|
|
75
75
|
| `setup` | Run `mise install` and any small project bootstrap needed before normal targets work. This is the first command after checkout. |
|
|
76
|
-
| `all` | Alias that runs `build`, `lint`, and `test` in sequence.
|
|
76
|
+
| `all` | Alias that runs `build`, `lint`, and `test` in sequence. **MUST** be the default target (i.e., running `make` or the runner with no arguments invokes `all`). Used by developers as a fast pre-push check to verify the software meets minimum quality standards in one command. **MUST** only invoke targets that run **offline** — no external credentials, running servers, paid APIs, or environment-specific configuration outside the repository. |
|
|
77
77
|
| `clean` | Remove all temporary or generated files created during build, lint, or test (e.g., `node_modules`, virtual environments, compiled binaries, generated files). Used both locally and in CI for a clean slate. |
|
|
78
78
|
| `dev` | Run the software locally for development (e.g., start a Node.js API server, open a Jupyter notebook, launch a React dev server). May have debugging tools, verbose logging, or hot reloading features enabled. |
|
|
79
|
-
| `run` | Run the software in production mode (e.g., start a compiled binary, launch a production server).
|
|
79
|
+
| `run` | Run the software in production mode (e.g., start a compiled binary, launch a production server). Debugging or development-only features **SHOULD NOT** be enabled. |
|
|
80
80
|
| `update-lockfile` | Update the dependency lockfile to reflect the latest resolved versions of all dependencies. |
|
|
81
81
|
|
|
82
82
|
##### Build group
|
|
@@ -93,17 +93,17 @@ Targets are organized into five lifecycle groups. Projects must use these names
|
|
|
93
93
|
|
|
94
94
|
| Target | Purpose |
|
|
95
95
|
|--------|---------|
|
|
96
|
-
| `lint` | Run **all static quality checks** outside of tests. This MUST include: code formatting validation, code style enforcement, code smell detection, static analysis, dependency audits for known CVEs, security vulnerability scans (e.g., SAST), and project/configuration structure checks. All checks
|
|
96
|
+
| `lint` | Run **all static quality checks** outside of tests. This MUST include: code formatting validation, code style enforcement, code smell detection, static analysis, dependency audits for known CVEs, security vulnerability scans (e.g., SAST), and project/configuration structure checks. All checks **MUST** be non-destructive (read-only); fixes are handled by `lint-fix`. **MUST** only invoke subtargets that run **offline** (no external credentials or services). |
|
|
97
97
|
| `lint-fix` | Automatically fix linting and formatting issues where possible. || `lint-format` | *(Optional)* Check code formatting only (e.g., Prettier, gofmt, Black). |
|
|
98
98
|
##### Test group
|
|
99
99
|
|
|
100
100
|
| Target | Purpose |
|
|
101
101
|
|--------|---------|
|
|
102
|
-
| `test` | Run **all offline tests** required for the project. This MUST include unit tests (with coverage enforcement — the build MUST fail if coverage thresholds are not met) and any integration or end-to-end tests that run **offline** (no external servers, credentials, or paid APIs). Normally delegates to `test-unit` and, when offline, `test-integration` in sequence. Suffixed targets that require external dependencies
|
|
102
|
+
| `test` | Run **all offline tests** required for the project. This MUST include unit tests (with coverage enforcement — the build MUST fail if coverage thresholds are not met) and any integration or end-to-end tests that run **offline** (no external servers, credentials, or paid APIs). Normally delegates to `test-unit` and, when offline, `test-integration` in sequence. Suffixed targets that require external dependencies **MUST NOT** be invoked automatically — see rule 08. |
|
|
103
103
|
| `test-unit` | Run unit tests only, including coverage report generation and coverage threshold enforcement. |
|
|
104
|
-
| `test-integration` | *(Optional)* Run integration and end-to-end tests only. Projects without integration tests
|
|
104
|
+
| `test-integration` | *(Optional)* Run integration and end-to-end tests only. Projects without integration tests MAY omit this target. |
|
|
105
105
|
| `test-smoke` | *(Optional)* Run a fast, minimal subset of tests to verify the software is basically functional. Useful as a post-deploy health check. |
|
|
106
|
-
| `eval` | *(Optional)* Run **all evaluations** for the module. Used alongside `test` to measure the accuracy and performance of statistical systems such as ML models, AI agents, or noisy systems. Typically runs against a live or near-live system (similar to an integration test) and produces a performance analysis report (e.g., F1 score, Accuracy, Precision, Recall).
|
|
106
|
+
| `eval` | *(Optional)* Run **all evaluations** for the module. Used alongside `test` to measure the accuracy and performance of statistical systems such as ML models, AI agents, or noisy systems. Typically runs against a live or near-live system (similar to an integration test) and produces a performance analysis report (e.g., F1 score, Accuracy, Precision, Recall). **MUST NOT** be included in `test` or `all` — evals are opt-in because they require live dependencies and MAY be slow or costly to run. Individual evaluations **MUST** follow the prefix convention: `eval-<qualifier>` (e.g., `eval-simple`, `eval-complex`). |
|
|
107
107
|
|
|
108
108
|
##### Release group
|
|
109
109
|
|
|
@@ -119,18 +119,18 @@ Targets are organized into five lifecycle groups. Projects must use these names
|
|
|
119
119
|
|
|
120
120
|
#### 04-standard-environment-variables
|
|
121
121
|
|
|
122
|
-
Two environment variables have defined semantics and
|
|
122
|
+
Two environment variables have defined semantics and **MUST** be used consistently.
|
|
123
123
|
|
|
124
124
|
| Variable | Purpose |
|
|
125
125
|
|----------|---------|
|
|
126
|
-
| `STAGE` | Identifies the runtime environment. Format: `[prefix][-variant]`. Common prefixes: `dev`, `tst`, `acc`, `prd`. Examples: `dev`, `dev-pr123`, `tst`, `prd-blue`.
|
|
126
|
+
| `STAGE` | Identifies the runtime environment. Format: `[prefix][-variant]`. Common prefixes: `dev`, `tst`, `acc`, `prd`. Examples: `dev`, `dev-pr123`, `tst`, `prd-blue`. **MAY** be required by any target that is environment-aware (build, lint, deploy, etc.). |
|
|
127
127
|
| `VERSION` | Sets the explicit version used during packaging and deployment. Used when there is no automatic version-tagging utility, or to override it. |
|
|
128
128
|
|
|
129
129
|
---
|
|
130
130
|
|
|
131
131
|
#### 05-extending-targets-with-prefixes
|
|
132
132
|
|
|
133
|
-
Projects
|
|
133
|
+
Projects **MAY** add custom targets beyond the standard set. Custom targets **MUST** be named by prefixing a standard target name with a descriptive qualifier, keeping the naming intuitive and consistent with the group it belongs to.
|
|
134
134
|
|
|
135
135
|
**Examples:**
|
|
136
136
|
|
|
@@ -166,15 +166,15 @@ dev-mlflow:
|
|
|
166
166
|
|
|
167
167
|
#### 08-default-targets-must-only-include-offline-subtargets
|
|
168
168
|
|
|
169
|
-
`make all`, `make test`, and `make lint`
|
|
169
|
+
`make all`, `make test`, and `make lint` **MUST** include every subtarget that runs **offline** — meaning it requires no external credentials, no running servers, no paid APIs, and no environment-specific configuration outside the repository.
|
|
170
170
|
|
|
171
|
-
Subtargets that require external dependencies (e.g., `test-integration` against a live database, `test-e2e` against a staging environment, `lint-api` against a remote schema registry) **
|
|
171
|
+
Subtargets that require external dependencies (e.g., `test-integration` against a live database, `test-e2e` against a staging environment, `lint-api` against a remote schema registry) **MUST** exist as named targets so developers can invoke them explicitly, but **MUST NOT** be invoked from `all`, `test`, or `lint`.
|
|
172
172
|
|
|
173
173
|
---
|
|
174
174
|
|
|
175
175
|
#### 06-monorepo-usage
|
|
176
176
|
|
|
177
|
-
In a monorepo, each module has its own `Makefile` with its own `build`, `lint`, `test`, and `deploy` targets scoped to that module. Parent-level Makefiles (at the application or repo root) delegate to child Makefiles in sequence. The parent Makefile
|
|
177
|
+
In a monorepo, each module has its own `Makefile` with its own `build`, `lint`, `test`, and `deploy` targets scoped to that module. Parent-level Makefiles (at the application or repo root) delegate to child Makefiles in sequence. The parent Makefile **SHOULD** call `$(MAKE) -C <child> <target>` directly, while each child `Makefile` runs its actual tool commands through `mise exec --`.
|
|
178
178
|
|
|
179
179
|
```makefile
|
|
180
180
|
# root Makefile — delegates to all modules
|
|
@@ -187,7 +187,7 @@ test:
|
|
|
187
187
|
$(MAKE) -C module-b test
|
|
188
188
|
```
|
|
189
189
|
|
|
190
|
-
A developer can run `make test` at the repo root to test everything, or `cd module-a && make test` to test a single module. Both
|
|
190
|
+
A developer can run `make test` at the repo root to test everything, or `cd module-a && make test` to test a single module. Both **MUST** work.
|
|
191
191
|
|
|
192
192
|
**Reference:** See [agentme-edr-005](005-monorepo-structure.md) for the full monorepo layout convention.
|
|
193
193
|
|
|
@@ -15,9 +15,9 @@ What contributor workflow guidance must every project publish so contributors kn
|
|
|
15
15
|
|
|
16
16
|
## Decision Outcome
|
|
17
17
|
|
|
18
|
-
**Every project
|
|
18
|
+
**Every project MUST publish a root CONTRIBUTING.md with a small, explicit contribution workflow.**
|
|
19
19
|
|
|
20
|
-
Projects
|
|
20
|
+
Projects MUST keep a `CONTRIBUTING.md` file at the repository root. The file MUST explain where bugs, feature discussions, and code changes belong so contributors follow a predictable workflow before opening pull requests.
|
|
21
21
|
|
|
22
22
|
### Details
|
|
23
23
|
|
|
@@ -37,6 +37,7 @@ Language and framework-specific tooling and project structure.
|
|
|
37
37
|
- [agentme-edr-021](application/021-ai-workflow-development-standards.md) - **AI workflow development standards** - Standard toolchain (LangGraph), evaluation, and testing patterns for workflow projects
|
|
38
38
|
- [agentme-edr-029](application/029-ai-workflow-naming-conventions.md) - **AI workflow naming conventions** - Node suffix/prefix roles, state type and attribute naming, judge output schema, workflow class names, and cross-element coherence rules
|
|
39
39
|
- [agentme-edr-028](application/028-ai-eval-standards.md) - **AI eval standards** - Folder structure, script requirements, and MLflow tracking for eval tests across LLM, Agent, and Workflow tiers
|
|
40
|
+
- [agentme-edr-030](application/030-ai-test-types-taxonomy.md) - **AI test types taxonomy** - Names AI test types (safety, responsible-AI, quality-eval, prompt, code-level) with group, objective, mocking constraint, and relevance, and defines the shared golden dataset entry envelope
|
|
40
41
|
- [agentme-edr-024](application/024-ml-dataset-structure.md) - **ML dataset structure** - Standard folder layout and file conventions for ML datasets
|
|
41
42
|
- [agentme-edr-025](application/025-ai-agent-xdrs-knowledge-layer.md) - **AI agent XDRS knowledge layer** - How to integrate XDRS as the runtime source of truth for policies and skills in AI agents (apply only when the project explicitly uses XDRS)
|
|
42
43
|
- [agentme-edr-026](application/026-pragmatic-hexagonal-architecture.md) - **Pragmatic hexagonal architecture** - Organize application layers as External/Adapters/Application with practical coupling rules
|
|
@@ -17,7 +17,7 @@ How should services expose their health status and validate operational readines
|
|
|
17
17
|
|
|
18
18
|
**Standardized `/health` endpoint with dependency validation**
|
|
19
19
|
|
|
20
|
-
All services
|
|
20
|
+
All services **MUST** expose a `GET /health` endpoint that validates external dependencies using read-only operations and returns structured status with appropriate HTTP codes.
|
|
21
21
|
|
|
22
22
|
### Details
|
|
23
23
|
|
|
@@ -45,12 +45,12 @@ All services must expose a `GET /health` endpoint that validates external depend
|
|
|
45
45
|
|
|
46
46
|
- `health` (required): overall state — `OK`, `WARNING`, or `ERROR`
|
|
47
47
|
- `latencyMs` (required): total milliseconds to run all checks
|
|
48
|
-
- `message` (required): human-readable summary;
|
|
48
|
+
- `message` (required): human-readable summary; **MUST NEVER** expose credentials, internal IPs, or stack traces
|
|
49
49
|
|
|
50
50
|
**Dependency validation rules:**
|
|
51
51
|
|
|
52
52
|
- Check all external dependencies (databases, downstream APIs, queues, caches) using read-only operations (e.g., `SELECT 1`, lightweight GET, connection ping)
|
|
53
|
-
-
|
|
53
|
+
- **MUST NOT** execute write operations or create side effects
|
|
54
54
|
- `OK`: all dependencies healthy within expected thresholds
|
|
55
55
|
- `WARNING`: non-critical dependency degraded, or elevated but acceptable response times
|
|
56
56
|
- `ERROR`: critical dependency unavailable or service unable to process requests
|
|
@@ -70,6 +70,28 @@ All services must expose a `GET /health` endpoint that validates external depend
|
|
|
70
70
|
- Monitoring: periodic polling with alerts on `210` and `503` responses
|
|
71
71
|
- CI/CD: poll `/health` to confirm deployment success
|
|
72
72
|
|
|
73
|
+
**Unit testing and mocking strategy:** Each dependency checker MUST be injectable so unit tests can simulate `OK`, `WARNING`, and `ERROR` states independently without a real database or API. Integration tests MUST NOT mock dependency checkers — they MUST run against real dependencies to verify the wiring.
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
// Good — injectable checkers; unit test controls each state
|
|
77
|
+
function buildHealthHandler(checkers: DependencyChecker[]) {
|
|
78
|
+
return async () => {
|
|
79
|
+
const results = await Promise.all(checkers.map(c => c.check()));
|
|
80
|
+
const status = results.some(r => r.status === "ERROR") ? "ERROR"
|
|
81
|
+
: results.some(r => r.status === "WARNING") ? "WARNING" : "OK";
|
|
82
|
+
return { health: status };
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
it("returns ERROR when the database checker fails", async () => {
|
|
87
|
+
const handler = buildHealthHandler([
|
|
88
|
+
{ check: async () => ({ name: "db", status: "ERROR" }) },
|
|
89
|
+
]);
|
|
90
|
+
const response = await handler();
|
|
91
|
+
expect(response.health).toBe("ERROR");
|
|
92
|
+
});
|
|
93
|
+
```
|
|
94
|
+
|
|
73
95
|
## Considered Options
|
|
74
96
|
|
|
75
97
|
* (REJECTED) **No health checks** — detect failures through request errors
|
|
@@ -21,12 +21,10 @@ What coding practices should be followed across all languages and projects to ke
|
|
|
21
21
|
|
|
22
22
|
#### 01-keep-files-short
|
|
23
23
|
|
|
24
|
-
A file
|
|
24
|
+
A file MUST NOT exceed **400 lines**. When a file grows beyond this limit, split related functions or types into separate, focused modules.
|
|
25
25
|
|
|
26
26
|
One exception are test files, which normally are bigger than the tested resources.
|
|
27
27
|
|
|
28
|
-
*Why:* Large files make navigation slow, increase merge conflicts, and obscure the single-responsibility principle.
|
|
29
|
-
|
|
30
28
|
**Example (TypeScript):**
|
|
31
29
|
|
|
32
30
|
```
|
|
@@ -50,8 +48,6 @@ src/
|
|
|
50
48
|
|
|
51
49
|
When a function's main logic contains well-defined sections and **any individual section exceeds ~20 lines**, extract each section into its own named function. The outer function becomes an orchestrator that calls the extracted helpers in sequence.
|
|
52
50
|
|
|
53
|
-
*Why:* Named sub-functions serve as inline documentation, are independently testable, and reduce cognitive load.
|
|
54
|
-
|
|
55
51
|
**Example (Python):**
|
|
56
52
|
|
|
57
53
|
```python
|
|
@@ -85,9 +81,7 @@ def _persist_order(order, total): ...
|
|
|
85
81
|
|
|
86
82
|
#### 03-put-entry-point-function-first
|
|
87
83
|
|
|
88
|
-
Place the **entry-point function** (the outermost caller) at the **top** of the file. All helper or sub-functions it calls internally
|
|
89
|
-
|
|
90
|
-
*Why:* Readers can follow the overall logic top-down without jumping around the file. The most important function is immediately visible when the file is opened.
|
|
84
|
+
Place the **entry-point function** (the outermost caller) at the **top** of the file. All helper or sub-functions it calls internally MUST appear **below** it.
|
|
91
85
|
|
|
92
86
|
**Example (Python):**
|
|
93
87
|
|
|
@@ -106,22 +100,18 @@ def _persist_order(order, total): ...
|
|
|
106
100
|
|
|
107
101
|
#### 04-keep-readme-tests-and-examples-in-sync
|
|
108
102
|
|
|
109
|
-
Every change to a public interface, behavior, or configuration option
|
|
103
|
+
Every change to a public interface, behavior, or configuration option MUST be reflected in:
|
|
110
104
|
|
|
111
105
|
- `README.md` — update usage examples, option tables, and feature descriptions.
|
|
112
106
|
- Unit/integration tests — update or add tests that cover the changed behavior.
|
|
113
107
|
- `examples/` resources — update runnable examples so they continue to work.
|
|
114
108
|
|
|
115
|
-
*Why:* Stale documentation and broken examples erode trust and waste time for consumers of the code.
|
|
116
|
-
|
|
117
109
|
---
|
|
118
110
|
|
|
119
111
|
#### 05-declare-types-in-file-where-used
|
|
120
112
|
|
|
121
113
|
If a type (struct, interface, class, typedef, etc.) is used in only **one** file, declare it in that same file. Move a type to a shared module only when it is referenced in two or more files.
|
|
122
114
|
|
|
123
|
-
*Why:* Co-locating a type with its sole consumer removes the need to navigate to a separate types file and makes the type's purpose immediately obvious from context.
|
|
124
|
-
|
|
125
115
|
---
|
|
126
116
|
|
|
127
117
|
#### 06-keep-test-files-next-to-source
|
|
@@ -262,3 +262,4 @@ AI projects are classified into three tiers — LLM, Agent, and Workflow — def
|
|
|
262
262
|
- Accuracy below project-defined thresholds MUST block the release. Thresholds MUST be documented in the eval Makefile or README.
|
|
263
263
|
- Evals MUST run against real LLM providers (not mocks) to capture model drift.
|
|
264
264
|
- For eval folder structure and script requirements, see [agentme-edr-028](../application/028-ai-eval-standards.md).
|
|
265
|
+
- For the taxonomy of AI test types (safety, responsible-AI, quality-eval, prompt, code-level) and the golden dataset entry format, see [agentme-edr-030](../application/030-ai-test-types-taxonomy.md).
|
|
@@ -21,9 +21,7 @@ What error handling practices should be followed across all languages and projec
|
|
|
21
21
|
|
|
22
22
|
#### 01-catch-only-where-handled
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
*Why:* Swallowed exceptions hide bugs and make incidents impossible to diagnose. Every silent `catch` is a future mystery.
|
|
24
|
+
MUST NOT catch an exception unless the catching site can genuinely recover from it, translate it into a meaningful domain error, or enrich it with context before re-throwing. MUST NOT swallow exceptions silently. When suppressing an exception is intentional, MUST add a comment explaining exactly why, or log it at an appropriate level.
|
|
27
25
|
|
|
28
26
|
**Examples:**
|
|
29
27
|
|
|
@@ -77,9 +75,7 @@ except CacheError:
|
|
|
77
75
|
|
|
78
76
|
#### 02-avoid-exceptions-in-public-interfaces
|
|
79
77
|
|
|
80
|
-
At module and service boundaries, prefer returning a value that signals success or failure (e.g., a result type, a discriminated union, or a `(value, error)` tuple as in Go) over throwing exceptions. This forces callers to explicitly acknowledge and handle the error case before using the result.
|
|
81
|
-
|
|
82
|
-
*Why:* Exceptions are invisible in signatures. A caller who doesn't know an exception can be thrown will never write a handler. Explicit error return values make the contract visible and encourage handling at the call site.
|
|
78
|
+
At module and service boundaries, SHOULD prefer returning a value that signals success or failure (e.g., a result type, a discriminated union, or a `(value, error)` tuple as in Go) over throwing exceptions. This forces callers to explicitly acknowledge and handle the error case before using the result.
|
|
83
79
|
|
|
84
80
|
**Examples:**
|
|
85
81
|
|
|
@@ -161,9 +157,7 @@ def fetch_user(user_id: str) -> Ok[User] | Err:
|
|
|
161
157
|
|
|
162
158
|
#### 03-centralise-repetitive-catch-logic
|
|
163
159
|
|
|
164
|
-
If the same `try/catch` pattern (e.g., logging, classifying HTTP errors, wrapping exceptions) appears in multiple places,
|
|
165
|
-
|
|
166
|
-
*Why:* Scattered catch blocks drift out of sync — one gets updated, the others don't. A central utility is tested once and applied everywhere consistently.
|
|
160
|
+
If the same `try/catch` pattern (e.g., logging, classifying HTTP errors, wrapping exceptions) appears in multiple places, MUST be extracted into a shared utility. MUST NOT copy-paste catch blocks across the codebase.
|
|
167
161
|
|
|
168
162
|
**Examples:**
|
|
169
163
|
|
|
@@ -213,13 +207,11 @@ def save_order(order: Order): ...
|
|
|
213
207
|
|
|
214
208
|
#### 04-communicate-failure-at-boundaries
|
|
215
209
|
|
|
216
|
-
Every system boundary
|
|
210
|
+
Every system boundary MUST signal failure explicitly:
|
|
217
211
|
|
|
218
|
-
- **OS processes**
|
|
219
|
-
- **HTTP services**
|
|
220
|
-
- **All error responses**
|
|
221
|
-
|
|
222
|
-
*Why:* Orchestrators, CI runners, load balancers, and callers all rely on these signals to detect failures automatically. A process or service that reports success on failure leads to silent data corruption and missed alerts.
|
|
212
|
+
- **OS processes** MUST exit with a **non-zero exit code** when something went wrong. Exit code `0` means success.
|
|
213
|
+
- **HTTP services** MUST return a **non-2xx/3xx status code** on error, accompanied by a response body that describes the problem without exposing internal system details (stack traces, SQL queries, internal paths, etc.).
|
|
214
|
+
- **All error responses** SHOULD be logged to the console/structured logger, especially system-level or unexpected errors. Operational teams must be able to find the cause from logs alone.
|
|
223
215
|
|
|
224
216
|
**Examples:**
|
|
225
217
|
|
|
@@ -276,11 +268,9 @@ def create_order_endpoint(payload: OrderRequest):
|
|
|
276
268
|
|
|
277
269
|
#### 05-write-test-cases-for-error-scenarios
|
|
278
270
|
|
|
279
|
-
Every module that handles errors
|
|
280
|
-
|
|
281
|
-
*Why:* Error handling code is the code most likely to be broken and the code least likely to be exercised in manual testing. Without automated tests, regressions in error paths go undetected until production.
|
|
271
|
+
Every module that handles errors MUST have dedicated test cases that verify the error paths. Do not only test the happy path.
|
|
282
272
|
|
|
283
|
-
|
|
273
|
+
**Mocking strategy:** External dependencies (databases, HTTP services, file systems) MUST be mocked in error-path unit tests. Simulate failure by configuring the mock to throw or return an error value — do not rely on a real dependency being unavailable.
|
|
284
274
|
|
|
285
275
|
- The dependency (DB, HTTP service, file system) is unavailable or times out.
|
|
286
276
|
- The input is invalid, missing, or out of range.
|
|
@@ -17,16 +17,16 @@ Question: What policy should developers follow to continuously enrich XDRs so re
|
|
|
17
17
|
|
|
18
18
|
**Develop features with shared-first XDR enrichment and controlled divergence**
|
|
19
19
|
|
|
20
|
-
Developers
|
|
20
|
+
Developers MUST treat reusable missing guidance discovered during implementation as an XDR gap to be proposed and reviewed, not as permanent prompt-only context or repeated vibe coding.
|
|
21
21
|
|
|
22
22
|
### Details
|
|
23
23
|
|
|
24
|
-
- The main objective is sharing, discussing, and converging practices across teams. Controlled divergence during exploration is acceptable, but recurring successful decisions
|
|
25
|
-
- The non _local scope exists to share practices across projects, company areas, and functionally organized teams. Decisions placed in `_local`
|
|
26
|
-
- When developers or coding agents need too much detailed steering to complete a task, they
|
|
27
|
-
- This includes cases where an agent implemented a feature without a framework, pattern, coding standard, or other practice that should likely be standardized. Missing reusable guardrails
|
|
28
|
-
- Teams
|
|
29
|
-
- If a big decision is not yet covered, developers
|
|
24
|
+
- The main objective is sharing, discussing, and converging practices across teams. Controlled divergence during exploration is acceptable, but recurring successful decisions MUST be converged into shared XDRs.
|
|
25
|
+
- The non _local scope exists to share practices across projects, company areas, and functionally organized teams. Decisions placed in `_local` SHOULD be truly specific to the needs of a single application or repository.
|
|
26
|
+
- When developers or coding agents need too much detailed steering to complete a task, they MUST reflect on whether those details would help other teams or future implementations. If yes, create or update an XDR proposal in the broadest appropriate shared scope.
|
|
27
|
+
- This includes cases where an agent implemented a feature without a framework, pattern, coding standard, or other practice that should likely be standardized. Missing reusable guardrails SHOULD trigger an XDR proposal.
|
|
28
|
+
- Teams SHOULD aim to keep at least 80% of big coding decisions covered by accepted XDRs. Big decisions include framework or tool selection, overall code organization, monorepo structure, complex business flows, and coding standards.
|
|
29
|
+
- If a big decision is not yet covered, developers SHOULD either propose a new XDR or document why the decision is intentionally local and should not be shared.
|
|
30
30
|
- Leaders responsible for the affected scope are accountable for reviewing XDR proposals, adjusting them, and publishing the accepted decision.
|
|
31
31
|
- It is good practice to ask the coding agent which missing XDRs made the task harder, increased adjustment rounds, or forced more vibe coding. Those gaps should feed the XDR backlog.
|
|
32
32
|
- In SDD, specifications describe the feature being built; XDRs describe reusable decisions and guardrails that should survive beyond one feature. Do not keep durable engineering policy only inside feature specs.
|
|
@@ -17,7 +17,7 @@ What baseline structure rules must every buildable module follow regardless of l
|
|
|
17
17
|
|
|
18
18
|
**Standardize every buildable module around its own folder root, with `dist/`, `.cache/`, sibling consumer examples, a module README, and predictable test locations.**
|
|
19
19
|
|
|
20
|
-
Language-specific EDRs
|
|
20
|
+
Language-specific EDRs MAY add ecosystem details, but they MUST NOT redefine these baseline folder responsibilities.
|
|
21
21
|
|
|
22
22
|
### Details
|
|
23
23
|
|
|
@@ -27,7 +27,7 @@ A module is the smallest independently buildable, testable, or publishable unit.
|
|
|
27
27
|
|
|
28
28
|
- a `Makefile` following [agentme-edr-008](../devops/008-common-targets.md)
|
|
29
29
|
- a `README.md` for the module itself
|
|
30
|
-
- all configuration files
|
|
30
|
+
- all configuration files needed to build, lint, test, package, or publish that module
|
|
31
31
|
- its generated `dist/` directory when the module produces distributable artifacts
|
|
32
32
|
- a module-local `.cache/` when tool caches are not intentionally shared with a parent aggregation root
|
|
33
33
|
|
|
@@ -44,11 +44,11 @@ Example module root:
|
|
|
44
44
|
|
|
45
45
|
#### 02-parent-folders-are-aggregation-roots
|
|
46
46
|
|
|
47
|
-
Parent folders such as a repository root, an application folder, or `lib/`
|
|
47
|
+
Parent folders such as a repository root, an application folder, or `lib/` MAY aggregate multiple modules. They MAY also hold shared consumer examples or multi-module test harnesses.
|
|
48
48
|
|
|
49
|
-
They MUST keep the public aggregation obvious: deleting an aggregation folder
|
|
49
|
+
They MUST keep the public aggregation obvious: deleting an aggregation folder SHOULD remove a coherent API surface or entry-point area, not scatter unrelated internal implementation across the repository.
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
Example aggregation pattern:
|
|
52
52
|
|
|
53
53
|
```text
|
|
54
54
|
<parent>/
|
|
@@ -88,7 +88,7 @@ Examples that demonstrate how to consume a library or reusable module MUST live
|
|
|
88
88
|
Examples MUST exercise the module through its public distribution surface:
|
|
89
89
|
|
|
90
90
|
- use the package built into `dist/` when the ecosystem supports local packaged artifacts
|
|
91
|
-
- otherwise use the public module path or equivalent consumer-facing import surface
|
|
91
|
+
- otherwise use the public module path or equivalent consumer-facing import surface; **MUST NOT** use relative source-file imports or direct references to internal implementation paths
|
|
92
92
|
|
|
93
93
|
Example:
|
|
94
94
|
|
|
@@ -106,7 +106,7 @@ Each module MUST contain a `README.md` that shows how to use the module as a con
|
|
|
106
106
|
|
|
107
107
|
The end of the README MUST also include short developer instructions for that module, covering at least the standard build, lint, and test entry points.
|
|
108
108
|
|
|
109
|
-
Repository-level READMEs
|
|
109
|
+
Repository-level READMEs MAY describe the workspace, but they do not replace the module README.
|
|
110
110
|
|
|
111
111
|
#### 07-tests-use-predictable-locations
|
|
112
112
|
|
|
@@ -142,6 +142,24 @@ Prefer fetching the secret inside the function that directly needs it rather tha
|
|
|
142
142
|
|
|
143
143
|
Passing secrets via function arguments is acceptable when the consuming function cannot access the connector directly, but the default design should fetch at the point of use.
|
|
144
144
|
|
|
145
|
+
#### 09-unit-testing-and-mocking-strategy
|
|
146
|
+
|
|
147
|
+
Code that calls the secret connector MUST accept it as an injectable parameter. Unit tests MUST inject a fake connector that returns a pre-configured value without touching the OS keychain or any cloud secret manager.
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
# Good — injectable connector; unit test provides a fake
|
|
151
|
+
class MyService:
|
|
152
|
+
def __init__(self, secrets: SecretConnector):
|
|
153
|
+
self.api_key = secrets.get("api-key")
|
|
154
|
+
|
|
155
|
+
def test_service_uses_api_key():
|
|
156
|
+
fake = FakeSecretConnector({"api-key": "test-key-123"})
|
|
157
|
+
svc = MyService(secrets=fake)
|
|
158
|
+
assert svc.api_key == "test-key-123"
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Integration tests MAY use the real keychain on developer machines or CI after `make setup-secrets` has been run.
|
|
162
|
+
|
|
145
163
|
## References
|
|
146
164
|
|
|
147
165
|
- [agentme-edr-008](../devops/008-common-targets.md) - Common development script names (defines Makefile target conventions)
|
|
@@ -21,9 +21,7 @@ What principles should guide the decision to introduce — or reject — an abst
|
|
|
21
21
|
|
|
22
22
|
#### 01-prioritize-functional-programming
|
|
23
23
|
|
|
24
|
-
Prefer functional programming: pure functions with clear input → processing → output flow. Object-oriented patterns (classes, inheritance)
|
|
25
|
-
|
|
26
|
-
*Why:* Functional units are simpler to reason about, test, and compose. OO introduces shared mutable state and implicit coupling that must earn its place.
|
|
24
|
+
Prefer functional programming: pure functions with clear input → processing → output flow. Object-oriented patterns (classes, inheritance) MAY only be used when there is a clear benefit from the additional abstraction they bring — e.g., when complex context management or true inheritance hierarchies are intrinsically part of the best solution for a problem.
|
|
27
25
|
|
|
28
26
|
---
|
|
29
27
|
|
|
@@ -43,7 +41,7 @@ These patterns obfuscate the main program flow and create behavioral indirection
|
|
|
43
41
|
|
|
44
42
|
#### 03-trivial-wrappers-are-prohibited
|
|
45
43
|
|
|
46
|
-
A function that merely delegates to another function or API call without adding meaningful logic, domain intent, or readability **
|
|
44
|
+
A function that merely delegates to another function or API call without adding meaningful logic, domain intent, or readability **MUST be inlined**. A wrapper is justified only when it:
|
|
47
45
|
|
|
48
46
|
- Encapsulates non-trivial logic (validation, retry, transformation).
|
|
49
47
|
- Communicates a domain concept the underlying expression does not convey.
|
|
@@ -76,7 +74,7 @@ A function that constructs an object (e.g., configuration, options) is only just
|
|
|
76
74
|
- Combines data in a non-linear or conditional way.
|
|
77
75
|
- Is reused by multiple callers.
|
|
78
76
|
|
|
79
|
-
A function that restructures simple static data in an almost 1-to-1 mapping forces the reader to trace indirection for no benefit and
|
|
77
|
+
A function that restructures simple static data in an almost 1-to-1 mapping forces the reader to trace indirection for no benefit and MUST be inlined.
|
|
80
78
|
|
|
81
79
|
**Bad — trivial factory:**
|
|
82
80
|
|
package/.xdrs/agentme/index.md
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: agentme
|
|
3
|
+
description: Curated library of XDRs and skills encoding best practices for AI coding agents across JavaScript, Go, and Python projects.
|
|
4
|
+
scope-type: standard
|
|
5
|
+
follows: agentme-core
|
|
6
|
+
apply-to: AI coding agents and developers adopting agentme engineering standards.
|
|
7
|
+
valid-from: 2025-01-01
|
|
8
|
+
---
|
|
9
|
+
|
|
1
10
|
# agentme Scope Overview
|
|
2
11
|
|
|
3
12
|
## Overview
|
package/.xdrs/index.md
CHANGED
|
@@ -11,7 +11,15 @@ XDRS scopes listed last override the ones listed first
|
|
|
11
11
|
### _core
|
|
12
12
|
|
|
13
13
|
Decisions about how XDRs work
|
|
14
|
-
[View _core
|
|
14
|
+
[View scope _core](_core/index.md)
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
### agentme-core
|
|
19
|
+
|
|
20
|
+
Meta-governance for the agentme scope (writing standards, content conventions, authoring guidance). Not distributed to consumers.
|
|
21
|
+
|
|
22
|
+
[View scope agentme-core](agentme-core/index.md)
|
|
15
23
|
|
|
16
24
|
---
|
|
17
25
|
|
|
@@ -19,7 +27,7 @@ Decisions about how XDRs work
|
|
|
19
27
|
|
|
20
28
|
Opiniated set of decisions and skills for common development tasks
|
|
21
29
|
|
|
22
|
-
[View agentme
|
|
30
|
+
[View scope agentme](agentme/index.md)
|
|
23
31
|
|
|
24
32
|
---
|
|
25
33
|
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentme",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.25.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"dependencies": {
|
|
6
|
-
"filedist": "^0.
|
|
6
|
+
"filedist": "^0.39.0"
|
|
7
7
|
},
|
|
8
8
|
"bin": "bin/filedist.js",
|
|
9
9
|
"files": [
|
|
@@ -18,6 +18,6 @@
|
|
|
18
18
|
"url": "https://github.com/flaviostutz/agentme.git"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
|
-
"xdrs-core": "^0.
|
|
21
|
+
"xdrs-core": "^0.37.1"
|
|
22
22
|
}
|
|
23
23
|
}
|