agentme 0.24.2 → 0.25.2
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/019-ai-agents-development-standards.md +2 -2
- package/.xdrs/agentme/edrs/application/021-ai-workflow-development-standards.md +7 -7
- package/.xdrs/agentme/edrs/application/024-ml-dataset-structure.md +25 -10
- package/.xdrs/agentme/edrs/application/026-pragmatic-hexagonal-architecture.md +61 -8
- package/.xdrs/agentme/edrs/application/028-ai-eval-standards.md +99 -40
- package/.xdrs/agentme/edrs/application/030-ai-test-types-taxonomy.md +98 -0
- package/.xdrs/agentme/edrs/devops/005-monorepo-structure.md +2 -8
- package/.xdrs/agentme/edrs/devops/006-github-pipelines.md +3 -3
- package/.xdrs/agentme/edrs/devops/008-common-targets.md +26 -26
- package/.xdrs/agentme/edrs/devops/027-environment-variable-configuration.md +8 -8
- 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 +6 -16
- package/.xdrs/agentme/edrs/principles/004-unit-test-requirements.md +6 -6
- package/.xdrs/agentme/edrs/principles/007-project-quality-standards.md +8 -7
- 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 +26 -8
- package/.xdrs/agentme/edrs/principles/023-coding-abstraction-practices.md +6 -8
- package/.xdrs/agentme/index.md +9 -0
- package/.xdrs/index.md +10 -2
- package/package.json +2 -2
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
|
|
|
@@ -108,11 +108,11 @@ When agents are used as nodes in workflows, the node name MUST use the `_agent`
|
|
|
108
108
|
|
|
109
109
|
#### 05-agent-composition
|
|
110
110
|
|
|
111
|
-
When multiple agents are needed:
|
|
111
|
+
When multiple agents are needed, one of these composition patterns MUST be chosen:
|
|
112
112
|
|
|
113
113
|
- **Single agent with multiple tools:** Use when tools share a common goal and context (e.g., a code analysis agent with `read_file`, `search_code`, and `analyze_pattern` tools).
|
|
114
114
|
- **Multiple agents as workflow nodes:** Use when agents have distinct responsibilities and outputs that feed into each other. Orchestrate them using LangGraph per [agentme-edr-021](021-ai-workflow-development-standards.md).
|
|
115
|
-
-
|
|
115
|
+
- Nested agent loops (agent calling agent autonomously) MUST NOT be created. Use workflows for multi-agent orchestration.
|
|
116
116
|
|
|
117
117
|
**Decision criteria:**
|
|
118
118
|
|
|
@@ -37,7 +37,7 @@ Use **MLflow** for all workflow observability and evaluation:
|
|
|
37
37
|
|
|
38
38
|
#### 04-dataset-driven-accuracy-measurement
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
Projects MUST follow the eval dataset and implementation requirements defined in [agentme-edr-028](028-ai-eval-standards.md). Testing requirements (when evals are required, release gates) are defined in [agentme-edr-007](../principles/007-project-quality-standards.md) rule `09-ai-project-testing-requirements`.
|
|
41
41
|
|
|
42
42
|
#### 05-flow-documentation
|
|
43
43
|
|
|
@@ -101,11 +101,11 @@ lib/src/<package_name>/
|
|
|
101
101
|
|
|
102
102
|
#### 08-workflow-evals
|
|
103
103
|
|
|
104
|
-
|
|
104
|
+
Projects MUST follow the eval folder structure and script requirements defined in [agentme-edr-028](028-ai-eval-standards.md).
|
|
105
105
|
|
|
106
106
|
#### 09-node-naming-conventions
|
|
107
107
|
|
|
108
|
-
|
|
108
|
+
Nodes MUST follow the naming conventions defined in [agentme-edr-029](029-ai-workflow-naming-conventions.md) rule `01-node-naming-conventions`.
|
|
109
109
|
|
|
110
110
|
#### 10-workflow-unit-testing
|
|
111
111
|
|
|
@@ -159,15 +159,15 @@ Workflows MUST accept the LLM instance as a constructor parameter so that unit t
|
|
|
159
159
|
|
|
160
160
|
#### 11-state-type-conventions
|
|
161
161
|
|
|
162
|
-
|
|
162
|
+
State types MUST follow the conventions defined in [agentme-edr-029](029-ai-workflow-naming-conventions.md) rule `02-state-type-conventions`.
|
|
163
163
|
|
|
164
164
|
#### 12-workflow-naming-conventions
|
|
165
165
|
|
|
166
|
-
|
|
166
|
+
Workflows MUST be named following the conventions in [agentme-edr-029](029-ai-workflow-naming-conventions.md) rule `04-workflow-naming-conventions`.
|
|
167
167
|
|
|
168
168
|
#### 13-judge-node-output-format
|
|
169
169
|
|
|
170
|
-
|
|
170
|
+
Judge nodes MUST use the output format defined in [agentme-edr-029](029-ai-workflow-naming-conventions.md) rule `03-judge-node-output-format`.
|
|
171
171
|
|
|
172
172
|
#### 15-workflow-state-persistence
|
|
173
173
|
|
|
@@ -200,7 +200,7 @@ result = graph.invoke(input_state, config={"thread_id": "session-123"})
|
|
|
200
200
|
|
|
201
201
|
#### 16-cross-element-naming-coherence
|
|
202
202
|
|
|
203
|
-
|
|
203
|
+
All workflow elements MUST maintain naming coherence as defined in [agentme-edr-029](029-ai-workflow-naming-conventions.md) rule `05-cross-element-naming-coherence`.
|
|
204
204
|
|
|
205
205
|
## References
|
|
206
206
|
|
|
@@ -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
|
|
@@ -21,7 +21,7 @@ How should application source code be organized to separate business logic from
|
|
|
21
21
|
|
|
22
22
|
#### 01-three-layer-separation
|
|
23
23
|
|
|
24
|
-
Every application
|
|
24
|
+
Every application MUST be organized into these three conceptual layers:
|
|
25
25
|
|
|
26
26
|
| Layer | Description |
|
|
27
27
|
|-------|-------------|
|
|
@@ -31,6 +31,8 @@ Every application is conceptually divided into three layers:
|
|
|
31
31
|
|
|
32
32
|
#### 02-adapter-naming-conventions
|
|
33
33
|
|
|
34
|
+
Adapters MUST follow these naming conventions:
|
|
35
|
+
|
|
34
36
|
**Inbound adapters** receive external requests or events and trigger application logic. Each gets a flat folder under `adapters/`:
|
|
35
37
|
|
|
36
38
|
- `cli/` — command-line interface entry point
|
|
@@ -50,14 +52,24 @@ Every application is conceptually divided into three layers:
|
|
|
50
52
|
#### 03-application-layer-rules
|
|
51
53
|
|
|
52
54
|
- Expose functionality as typed library interfaces
|
|
53
|
-
- All inputs
|
|
55
|
+
- All inputs MUST be explicitly passed as typed parameters
|
|
54
56
|
- No global variables, no direct environment variable access in `app/` or `shared/`
|
|
55
57
|
- Business logic with well-defined input/output behavior
|
|
56
58
|
- Group related logic into subfolders (aggregation roots)
|
|
57
59
|
- 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
60
|
|
|
61
|
+
- Data flow examples
|
|
62
|
+
|
|
63
|
+
```text
|
|
64
|
+
HTTP request → adapters/http/ → app/create-user → adapters/connectors/postgres/
|
|
65
|
+
CLI command → adapters/cli/ → app/create-dir → adapters/connectors/local-fs/
|
|
66
|
+
Kafka message → adapters/kafka/ → app/process-event → adapters/connectors/stripe-api/
|
|
67
|
+
```
|
|
68
|
+
|
|
59
69
|
#### 04-mandatory-folder-structure
|
|
60
70
|
|
|
71
|
+
All projects MUST follow this folder structure:
|
|
72
|
+
|
|
61
73
|
```text
|
|
62
74
|
mysystem/
|
|
63
75
|
Makefile # targets to run different inbound interfaces (e.g. run-http, run-cli)
|
|
@@ -89,7 +101,7 @@ mysystem/
|
|
|
89
101
|
|
|
90
102
|
#### 06-bootstrap-and-entry-points
|
|
91
103
|
|
|
92
|
-
- Each inbound adapter folder (`cli/`, `http/`, `grpc/`, etc.)
|
|
104
|
+
- Each inbound adapter folder (`cli/`, `http/`, `grpc/`, etc.) MUST contain the bootstrap and entry point for that interface
|
|
93
105
|
- The project root Makefile must have targets to run the different inbound interfaces following [agentme-edr-008](../devops/008-common-targets.md) extension conventions (e.g. `run-http`, `run-grpc`)
|
|
94
106
|
- Bootstrap code lives in the adapter that receives inbound requests, not in a separate wiring layer
|
|
95
107
|
|
|
@@ -98,15 +110,56 @@ mysystem/
|
|
|
98
110
|
- Trivial scripts and single-purpose tools (fewer than ~300 lines with a single I/O boundary) MAY skip this layering
|
|
99
111
|
- All other projects MUST use this structure from the start
|
|
100
112
|
|
|
101
|
-
####
|
|
113
|
+
#### 09-unit-testing-and-mocking-strategy
|
|
102
114
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
115
|
+
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.
|
|
116
|
+
|
|
117
|
+
The connector implementations themselves SHOULD have their own unit tests that mock the underlying SDK or HTTP client.
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
# Good — inject connector; unit test mocks it
|
|
121
|
+
class OrderService:
|
|
122
|
+
def __init__(self, db: OrderRepository):
|
|
123
|
+
self.db = db
|
|
124
|
+
|
|
125
|
+
def test_create_order_persists_record():
|
|
126
|
+
fake_db = FakeOrderRepository()
|
|
127
|
+
service = OrderService(db=fake_db)
|
|
128
|
+
order = service.create({"item": "widget", "qty": 2})
|
|
129
|
+
assert fake_db.find(order.id) is not None
|
|
107
130
|
```
|
|
108
131
|
|
|
132
|
+
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.
|
|
133
|
+
|
|
134
|
+
#### 10-mock-file-strategy
|
|
135
|
+
|
|
136
|
+
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.
|
|
137
|
+
|
|
138
|
+
**When to use a `_mock` file vs inline:**
|
|
139
|
+
- Single-test use → define the mock inline inside the test file (per rule `09` example; no file needed)
|
|
140
|
+
- Reusable across multiple tests OR used from `eval.py` → define in a separate `_mock` file
|
|
141
|
+
|
|
142
|
+
**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`).
|
|
143
|
+
|
|
144
|
+
**Naming:** insert `_mock` immediately before the file extension:
|
|
145
|
+
|
|
146
|
+
| Source file | Mock file |
|
|
147
|
+
|---|---|
|
|
148
|
+
| `client.py` | `client_mock.py` |
|
|
149
|
+
| `order_service.ts` | `order_service_mock.ts` |
|
|
150
|
+
| `user_store.go` | `user_store_mock_test.go` |
|
|
151
|
+
|
|
152
|
+
**Placement:** follows the project's test file placement convention per [agentme-edr-004](../principles/004-unit-test-requirements.md) rule `04`:
|
|
153
|
+
- Co-located test convention (TypeScript, Go) → mock file in the same directory as the source file
|
|
154
|
+
- 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`)
|
|
155
|
+
|
|
156
|
+
**Mock contract:**
|
|
157
|
+
- 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
|
|
158
|
+
- 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)
|
|
159
|
+
|
|
109
160
|
## References
|
|
110
161
|
|
|
111
162
|
- [agentme-edr-016](../principles/016-cross-language-module-structure.md) — Defines the module-root structure (Makefile, dist/, .cache/) that wraps this internal layout
|
|
112
163
|
- [agentme-edr-002](../principles/002-coding-best-practices.md) — File size limits and code organization practices that complement this architecture
|
|
164
|
+
- [agentme-edr-004](../principles/004-unit-test-requirements.md) — Rule `04`: test file placement convention per language (governs `_mock` file placement in rule `10`)
|
|
165
|
+
- [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`)
|