agentme 0.11.0 → 0.13.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/014-python-project-tooling.md +13 -12
- package/.xdrs/agentme/edrs/application/skills/005-create-python-project/SKILL.md +10 -12
- package/.xdrs/agentme/edrs/devops/017-tool-execution-and-scripting.md +1 -1
- package/.xdrs/agentme/edrs/index.md +2 -0
- package/.xdrs/agentme/edrs/principles/004-unit-test-requirements.md +7 -1
- package/.xdrs/agentme/edrs/principles/022-secrets-management.md +128 -0
- package/.xdrs/agentme/edrs/principles/023-coding-abstraction-practices.md +129 -0
- package/package.json +2 -2
package/.filedist-package.yml
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: agentme-edr-policy-014-python-project-tooling-and-structure
|
|
3
|
-
description: Defines the standard Python project toolchain, layout, and Makefile workflow using Mise, uv, ruff,
|
|
3
|
+
description: Defines the standard Python project toolchain, layout, and Makefile workflow using Mise, uv, ruff, ty, pytest, and pip-audit. Use when scaffolding or reviewing Python projects.
|
|
4
4
|
apply-to: Python projects
|
|
5
5
|
valid-from: 2026-05-25
|
|
6
6
|
---
|
|
@@ -15,7 +15,7 @@ What tooling and project structure should Python projects follow to ensure consi
|
|
|
15
15
|
|
|
16
16
|
## Decision Outcome
|
|
17
17
|
|
|
18
|
-
**Use a Mise-managed Python and uv toolchain with `pyproject.toml`, `ruff`, `
|
|
18
|
+
**Use a Mise-managed Python and uv toolchain with `pyproject.toml`, `ruff`, `ty`, `pytest`, `pytest-cov`, `pip-audit`, and a layout that follows [agentme-edr-016](../principles/016-cross-language-module-structure.md): a module root under `lib/`, runnable consumer examples in sibling `examples/`, and standardized `dist/` and `.cache/` locations.**
|
|
19
19
|
|
|
20
20
|
A single dependency manager, isolated package internals under `lib/`, and a standard Makefile contract keep Python projects predictable for contributors and CI while keeping the repository root clean.
|
|
21
21
|
|
|
@@ -29,12 +29,12 @@ A single dependency manager, isolated package internals under `lib/`, and a stan
|
|
|
29
29
|
| **uv** | Dependency management, lockfile management, virtualenv sync, build, publish |
|
|
30
30
|
| **pyproject.toml** | Single source of truth for package metadata and tool configuration |
|
|
31
31
|
| **ruff** | Formatting, import sorting, linting, and common code-quality checks |
|
|
32
|
-
| **
|
|
32
|
+
| **ty** | Static type checking |
|
|
33
33
|
| **pytest** | Test runner |
|
|
34
34
|
| **pytest-cov** | Coverage reporting and threshold enforcement |
|
|
35
35
|
| **pip-audit** | Dependency CVE audit |
|
|
36
36
|
|
|
37
|
-
All routine commands must run through the project `Makefile`, never by calling `uv`, `ruff`, `pytest`, or `
|
|
37
|
+
All routine commands must run through the project `Makefile`, never by calling `uv`, `ruff`, `pytest`, or `ty` directly in docs, CI, or daily development workflows.
|
|
38
38
|
|
|
39
39
|
The repository root MUST define a `.mise.toml` that pins Python and uv. Contributors and CI MUST bootstrap with `make setup` or `mise install`, then invoke routine work with `make <target>`. Each Makefile recipe MUST execute the underlying tool through `mise exec -- <tool> ...`, following [agentme-edr-017](../devops/017-tool-execution-and-scripting.md). Using routine project CLI commands directly outside the Makefile contract is not allowed.
|
|
40
40
|
|
|
@@ -79,7 +79,7 @@ No tool MUST write cache or state files to the project root, `src/`, `tests/`, o
|
|
|
79
79
|
│ │ └── shared/ # infrastructure-agnostic utilities
|
|
80
80
|
│ ├── tests/
|
|
81
81
|
│ │ ├── conftest.py # shared fixtures when needed
|
|
82
|
-
│ │ └──
|
|
82
|
+
│ │ └── *_test.py # e.g. hello_test.py (named after the tested file)
|
|
83
83
|
│ ├── tests_integration/ # optional integration tests for this module
|
|
84
84
|
│ ├── tests_benchmark/ # optional benchmark harnesses and datasets
|
|
85
85
|
│ └── dist/ # wheels / sdists built from lib/
|
|
@@ -94,7 +94,7 @@ No tool MUST write cache or state files to the project root, `src/`, `tests/`, o
|
|
|
94
94
|
|
|
95
95
|
Keep the repository root clean: source code, tests, distribution artifacts, and package metadata live under `lib/`, while the root contains only orchestration and repository-level files.
|
|
96
96
|
|
|
97
|
-
Use the `lib/src/` layout for import safety and packaging clarity. Keep tests under `lib/tests/` and shared test setup in `lib/tests/conftest.py`. Do not introduce `requirements.txt`, `setup.py`, `setup.cfg`, `tox.ini`, `ruff.toml`, or `
|
|
97
|
+
Use the `lib/src/` layout for import safety and packaging clarity. Keep tests under `lib/tests/` and shared test setup in `lib/tests/conftest.py`. Do not introduce `requirements.txt`, `setup.py`, `setup.cfg`, `tox.ini`, `ruff.toml`, or `ty.toml` by default; keep project metadata and tool configuration in `lib/pyproject.toml`.
|
|
98
98
|
|
|
99
99
|
Internal source code MUST be organized following [agentme-edr-021](021-pragmatic-hexagonal-architecture.md): `adapters/` (inbound and outbound I/O boundaries), `app/` (business logic), and `shared/` (infrastructure-agnostic utilities).
|
|
100
100
|
|
|
@@ -106,11 +106,11 @@ Python keeps unit tests under `lib/tests/` by default because that remains the m
|
|
|
106
106
|
|
|
107
107
|
- Runtime dependencies belong in `[project.dependencies]`.
|
|
108
108
|
- Development-only tooling belongs in `[dependency-groups].dev`.
|
|
109
|
-
- Configure Ruff,
|
|
109
|
+
- Configure Ruff, ty, and Pytest in `lib/pyproject.toml` under their `tool.*` sections.
|
|
110
110
|
- Commit `lib/uv.lock` and keep it in sync with `lib/pyproject.toml`.
|
|
111
111
|
- Expose CLI entry points with `[project.scripts]` when the project provides commands.
|
|
112
112
|
|
|
113
|
-
When
|
|
113
|
+
When ty runs from `lib/`, it auto-discovers the virtual environment via the `VIRTUAL_ENV` environment variable or the `UV_PROJECT_ENVIRONMENT` export set in the Makefile. No additional venv configuration is required in `pyproject.toml`.
|
|
114
114
|
|
|
115
115
|
Ruff is the default formatter and linter. Do not add Black, isort, or Flake8 unless another XDR for that repository explicitly requires them.
|
|
116
116
|
|
|
@@ -119,6 +119,7 @@ All Python projects must configure the following sections in `lib/pyproject.toml
|
|
|
119
119
|
```toml
|
|
120
120
|
[tool.pytest.ini_options]
|
|
121
121
|
cache_dir = ".cache/pytest"
|
|
122
|
+
python_files = "*_test.py"
|
|
122
123
|
|
|
123
124
|
[tool.coverage.run]
|
|
124
125
|
data_file = ".cache/.coverage"
|
|
@@ -156,7 +157,7 @@ ignore-overlong-task-comments = true
|
|
|
156
157
|
|
|
157
158
|
Adjust `target-version` to match the project's minimum supported Python version. The `cache-dir` keeps Ruff's cache under `.cache/ruff` alongside other tool caches. The `src` list must include every directory that contains importable Python code. The `select` list enables a broad set of rules covering style, correctness, performance, security, and documentation. The `ignore` list suppresses rules that are either too noisy or conflict with the chosen docstring style.
|
|
158
159
|
|
|
159
|
-
|
|
160
|
+
ty must run on every lint pass. The default rule set is the minimum baseline; projects may enable stricter rules as the codebase matures.
|
|
160
161
|
|
|
161
162
|
Pytest coverage must fail below 80% line and branch coverage, following [agentme-edr-004](../principles/004-unit-test-requirements.md).
|
|
162
163
|
|
|
@@ -187,8 +188,8 @@ The root `Makefile` is the only contract for CI and contributors. It delegates l
|
|
|
187
188
|
|--------|-------------|
|
|
188
189
|
| `install` | `mise exec -- uv sync --project . --frozen --all-extras --dev` using the shared root `.venv/` |
|
|
189
190
|
| `build` | `mise exec -- uv sync --project . --frozen --all-extras --dev && mise exec -- uv build --project . --out-dir dist` |
|
|
190
|
-
| `lint` | `mise exec -- uv run --project . ruff format --check . && mise exec -- uv run --project . ruff check . && mise exec -- uv run --project .
|
|
191
|
-
| `lint-fix` | `mise exec -- uv run --project . ruff format . && mise exec -- uv run --project . ruff check . --fix && mise exec -- uv run --project .
|
|
191
|
+
| `lint` | `mise exec -- uv run --project . ruff format --check . && mise exec -- uv run --project . ruff check . && mise exec -- uv run --project . ty check && mise exec -- uv run --project . pip-audit`, with caches redirected into `.cache/` |
|
|
192
|
+
| `lint-fix` | `mise exec -- uv run --project . ruff format . && mise exec -- uv run --project . ruff check . --fix && mise exec -- uv run --project . ty check && mise exec -- uv run --project . pip-audit`, with caches redirected into `.cache/` |
|
|
192
193
|
| `test-unit` | `mise exec -- uv run --project . pytest --cov=src/<package_name> --cov-branch --cov-report=term-missing --cov-fail-under=80`, with pytest and coverage outputs stored under `.cache/` |
|
|
193
194
|
| `clean` | Remove `dist/` and `.cache/` inside `lib/` |
|
|
194
195
|
| `all` | `build lint test-unit` |
|
|
@@ -203,7 +204,7 @@ The root `Makefile` must remain the only contract for CI and contributors, in li
|
|
|
203
204
|
|
|
204
205
|
* (REJECTED) **Mixed Python tooling** - Separate tools and config files such as `pip`, `requirements.txt`, `setup.cfg`, `flake8`, and `mypy`.
|
|
205
206
|
* Reason: Increases cognitive load, duplicates configuration, and weakens the standard command surface across projects.
|
|
206
|
-
* (CHOSEN) **uv + `lib/` package layout + Ruff/
|
|
207
|
+
* (CHOSEN) **uv + `lib/` package layout + Ruff/ty/Pytest toolchain** - One dependency manager, package internals isolated under `lib/`, consumer examples under `examples/`, and one root Makefile contract.
|
|
207
208
|
* Reason: Keeps packaging, dependency locking, static analysis, security auditing, and test execution consistent while aligning Python repositories with the established JavaScript layout.
|
|
208
209
|
|
|
209
210
|
## References
|
|
@@ -13,7 +13,7 @@ compatibility: Python 3.12+
|
|
|
13
13
|
## Overview
|
|
14
14
|
|
|
15
15
|
Creates a complete Python project from scratch using Mise, `uv`, `pyproject.toml`, Ruff,
|
|
16
|
-
|
|
16
|
+
ty, Pytest, and Makefiles. The layout keeps the package self-contained under `lib/`,
|
|
17
17
|
organizes internal code following [agentme-edr-021](../../021-pragmatic-hexagonal-architecture.md)
|
|
18
18
|
(`adapters/`, `app/`, `shared/`), uses a shared root `.venv/`, redirects persistent caches into
|
|
19
19
|
`.cache/`, and places runnable consumer projects under the sibling `examples/` folder.
|
|
@@ -157,13 +157,13 @@ build: install
|
|
|
157
157
|
lint: install
|
|
158
158
|
$(MISE) uv run --project . ruff format --check .
|
|
159
159
|
$(MISE) uv run --project . ruff check .
|
|
160
|
-
$(MISE) uv run --project .
|
|
160
|
+
$(MISE) uv run --project . ty check
|
|
161
161
|
$(MISE) uv run --project . pip-audit
|
|
162
162
|
|
|
163
163
|
lint-fix: install
|
|
164
164
|
$(MISE) uv run --project . ruff format .
|
|
165
165
|
$(MISE) uv run --project . ruff check . --fix
|
|
166
|
-
$(MISE) uv run --project .
|
|
166
|
+
$(MISE) uv run --project . ty check
|
|
167
167
|
$(MISE) uv run --project . pip-audit
|
|
168
168
|
|
|
169
169
|
test-unit: install
|
|
@@ -203,7 +203,7 @@ dev = []
|
|
|
203
203
|
[dependency-groups]
|
|
204
204
|
dev = [
|
|
205
205
|
"pip-audit>=2.9.0",
|
|
206
|
-
"
|
|
206
|
+
"ty>=0.1.0",
|
|
207
207
|
"pytest>=8.4.0",
|
|
208
208
|
"pytest-cov>=6.1.0",
|
|
209
209
|
"ruff>=0.11.0",
|
|
@@ -237,19 +237,17 @@ ignore = ["ANN002", "ANN003", "ANN401", "D100", "D101", "D102", "D103", "D104",
|
|
|
237
237
|
[tool.ruff.lint.pycodestyle]
|
|
238
238
|
ignore-overlong-task-comments = true
|
|
239
239
|
|
|
240
|
-
[tool.
|
|
241
|
-
|
|
242
|
-
venvPath = ".."
|
|
243
|
-
venv = ".venv"
|
|
244
|
-
typeCheckingMode = "standard"
|
|
240
|
+
[tool.ty]
|
|
241
|
+
src = ["src", "tests"]
|
|
245
242
|
|
|
246
243
|
[tool.pytest.ini_options]
|
|
247
244
|
testpaths = ["tests"]
|
|
245
|
+
python_files = "*_test.py"
|
|
248
246
|
addopts = "-q"
|
|
249
247
|
```
|
|
250
248
|
|
|
251
249
|
Use `lib/pyproject.toml` as the single configuration file for the package. Do not add
|
|
252
|
-
`requirements.txt`, `setup.py`, `setup.cfg`, `ruff.toml`, or `
|
|
250
|
+
`requirements.txt`, `setup.py`, `setup.cfg`, `ruff.toml`, or `ty.toml` by default.
|
|
253
251
|
|
|
254
252
|
**`lib/README.md`**
|
|
255
253
|
|
|
@@ -332,7 +330,7 @@ if __name__ == "__main__":
|
|
|
332
330
|
|
|
333
331
|
Create empty `adapters/connectors/` directory with a `.gitkeep` for outbound adapters.
|
|
334
332
|
|
|
335
|
-
**`lib/tests/
|
|
333
|
+
**`lib/tests/hello_test.py`**
|
|
336
334
|
|
|
337
335
|
```python
|
|
338
336
|
from [package_name].app.hello import hello
|
|
@@ -391,7 +389,7 @@ After creating the files:
|
|
|
391
389
|
- Create `Makefile`, `README.md`, `lib/pyproject.toml`, `lib/Makefile`, `lib/src/event_tools/`, `lib/tests/`, and `examples/`
|
|
392
390
|
- Scaffold `adapters/`, `app/`, `shared/` directories inside `lib/src/event_tools/`
|
|
393
391
|
- Add `lib/README.md`, `.cache/` handling, and install examples from the built wheel in `lib/dist/`
|
|
394
|
-
- Configure `uv`, Ruff,
|
|
392
|
+
- Configure `uv`, Ruff, ty, Pytest, `pytest-cov`, and `pip-audit`
|
|
395
393
|
- Verify with `make lint-fix`, `make test`, and `make build`
|
|
396
394
|
|
|
397
395
|
**Input:** "Scaffold a Python CLI package"
|
|
@@ -27,7 +27,7 @@ This keeps local development and CI aligned, reduces indirection, and lets contr
|
|
|
27
27
|
- A Makefile target MUST execute the real operation through `mise exec --` before invoking the tool itself, so it always uses the version pinned in `.mise.toml`. Avoid intermediate script layers that hide the actual command.
|
|
28
28
|
- Every Makefile target MUST start by echoing a concise summary of the target and folder or context, using fewer than 10 words. When delegating to another Makefile, echo the child path and delegated target before invoking it.
|
|
29
29
|
- Direct delegation to another Makefile is allowed when traversing repo, app, or module boundaries, for example `$(MAKE) -C lib build`.
|
|
30
|
-
- Calling the actual tool binary through its native executable launcher is allowed when that is the direct command under `mise exec --`, for example `mise exec -- pnpm exec eslint ./src`, `mise exec -- uv run
|
|
30
|
+
- Calling the actual tool binary through its native executable launcher is allowed when that is the direct command under `mise exec --`, for example `mise exec -- pnpm exec eslint ./src`, `mise exec -- uv run ty check`, `mise exec -- go test`, or `mise exec -- npx -y monotag`.
|
|
31
31
|
- Makefile targets MUST use `mise exec --` consistently before routine tool commands and MUST NOT call `npm run`, `pnpm run`, `yarn run`, `just`, `task`, or similar abstraction layers for routine project commands.
|
|
32
32
|
- Tool installation and environment preparation MUST be handled explicitly in developer setup instructions and CI workflow steps, using ecosystem-specific installation steps such as `actions/setup-node`, `actions/setup-go`, `astral-sh/setup-uv`, system package managers, or pinned install commands such as `mise install`.
|
|
33
33
|
- `package.json` scripts are optional and MAY exist only as direct reverse-compatibility aliases to one Make target, for example `"test": "make test"`. They MUST stay one-to-one and add no extra orchestration.
|
|
@@ -9,11 +9,13 @@ Propose changes via pull request. All changes must be verified for clarity and n
|
|
|
9
9
|
Foundational standards, principles, and guidelines.
|
|
10
10
|
|
|
11
11
|
- [agentme-edr-002](principles/002-coding-best-practices.md) - **Coding best practices** - Keep files small, tests nearby, and docs synchronized
|
|
12
|
+
- [agentme-edr-023](principles/023-coding-abstraction-practices.md) - **Coding abstraction practices** - Define when abstractions are justified and when they must be inlined
|
|
12
13
|
- [agentme-edr-004](principles/004-unit-test-requirements.md) - **Unit test requirements** - Define minimum unit-test coverage and naming expectations
|
|
13
14
|
- [agentme-edr-007](principles/007-project-quality-standards.md) - **Project quality standards** - Require build, lint, and test verification before completion
|
|
14
15
|
- [agentme-edr-009](principles/009-error-handling.md) - **Error handling** - Standardize explicit errors, logging, and propagation rules
|
|
15
16
|
- [agentme-edr-012](principles/012-continuous-xdr-enrichment.md) - **Continuous xdr improvement policy** - Promote recurring delivery lessons into reusable XDRs
|
|
16
17
|
- [agentme-edr-016](principles/016-cross-language-module-structure.md) - **Cross-language module structure** - Organize modules consistently across supported languages
|
|
18
|
+
- [agentme-edr-022](principles/022-secrets-management.md) - **Secrets management** - Handle secrets securely using native keychains and cloud secret managers
|
|
17
19
|
|
|
18
20
|
## Articles
|
|
19
21
|
|
|
@@ -70,7 +70,13 @@ Builds that miss the threshold must not be merged.
|
|
|
70
70
|
|
|
71
71
|
#### 04-must-place-test-files-alongside-source
|
|
72
72
|
|
|
73
|
-
Test files must live next to the source file they test, in the same directory, following the convention of the language/framework
|
|
73
|
+
Test files must live next to the source file they test, in the same directory, following the convention of the language/framework:
|
|
74
|
+
|
|
75
|
+
| Language | Pattern | Example |
|
|
76
|
+
|----------|---------|-------|
|
|
77
|
+
| TypeScript/JavaScript | `[name].test.ts` or `[name].spec.js` | `file1.test.ts` |
|
|
78
|
+
| Go | `[name]_test.go` | `file1_test.go` |
|
|
79
|
+
| Python | `[name]_test.py` | `myfunc_test.py` |
|
|
74
80
|
|
|
75
81
|
```
|
|
76
82
|
src/mymodule/group1/file1.ts ← source
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: agentme-edr-policy-022-secrets-management
|
|
3
|
+
description: Defines how secrets (API keys, passwords, tokens, credentials, private certificates) must be stored, fetched, and provisioned. Use when implementing secret handling in any language or deployment target.
|
|
4
|
+
apply-to: All software projects handling secrets
|
|
5
|
+
valid-from: 2026-05-28
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# agentme-edr-policy-022: Secrets management
|
|
9
|
+
|
|
10
|
+
## Context and Problem Statement
|
|
11
|
+
|
|
12
|
+
A "secret" is any sensitive information whose exposure would compromise security or access control. Examples include API keys, passwords, authentication tokens, credentials, and private certificates. Plain configuration data (feature flags, endpoint URLs, port numbers) is not a secret.
|
|
13
|
+
|
|
14
|
+
Secrets stored on disk — even in `.env` files excluded from version control — are vulnerable to accidental exposure, tooling leaks, and lateral movement after a compromise. Hardcoded or cached secrets resist rotation and create synchronization headaches across environments.
|
|
15
|
+
|
|
16
|
+
What practices should projects follow to handle secrets securely across local development and cloud deployments?
|
|
17
|
+
|
|
18
|
+
## Decision Outcome
|
|
19
|
+
|
|
20
|
+
**Use native OS keychains for local development and cloud secret managers for deployed environments, accessed through a unified connector with ordered fallback.**
|
|
21
|
+
|
|
22
|
+
All implementation practices derive from three guiding principles:
|
|
23
|
+
|
|
24
|
+
1. **Least exposure** — minimize the means, timespan, and surface of contact with the secret.
|
|
25
|
+
2. **Easiness in secret rotation** — design so rotating a secret requires no code change or redeployment.
|
|
26
|
+
3. **Support for local and cloud deployment runs** — the same application code must work transparently in both environments.
|
|
27
|
+
|
|
28
|
+
### Details
|
|
29
|
+
|
|
30
|
+
#### 01-no-secrets-on-disk
|
|
31
|
+
|
|
32
|
+
Secrets must never be stored on the disk of a developer machine or server. This includes `.env` files (even when gitignored), plaintext config files, embedded in source code, or any other file-based storage.
|
|
33
|
+
|
|
34
|
+
The only acceptable local persistence is through the operating system's native secret manager (e.g., macOS Keychain, Windows Credential Manager, Linux Secret Service).
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
#### 02-local-dev-uses-native-keychain
|
|
39
|
+
|
|
40
|
+
During local development, secrets must be stored and retrieved using the native OS secret manager. Use cross-platform libraries to keep the code OS-agnostic:
|
|
41
|
+
|
|
42
|
+
| Language | Library |
|
|
43
|
+
|----------|---------|
|
|
44
|
+
| Python | `keyring` |
|
|
45
|
+
| JavaScript/TypeScript | `cross-keychain` |
|
|
46
|
+
| Go | `go-keyring` |
|
|
47
|
+
|
|
48
|
+
The "group" (service name) defaults to the module name. The secret identifier should match the ID used in the cloud secret manager for consistency.
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
#### 03-fallback-lookup-order
|
|
53
|
+
|
|
54
|
+
Secret fetching must implement a fallback chain in the following order:
|
|
55
|
+
|
|
56
|
+
1. **Native OS keychain** — attempt to retrieve the secret using the local keychain library (used during local development).
|
|
57
|
+
2. **Cloud secret manager** — if not found locally, fetch from the configured cloud secret manager (AWS Secrets Manager, Azure Key Vault, etc.) (used in cloud environments).
|
|
58
|
+
3. **Raise an exception** — if both lookups fail, raise an exception with a clear message indicating both failed paths.
|
|
59
|
+
|
|
60
|
+
Example error message:
|
|
61
|
+
```
|
|
62
|
+
Secret 'db-password' could not be found in keychain under group 'mymodule' or in AWS Secrets Manager under secretId 'db-password'
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
#### 04-secret-fetching-in-connector
|
|
68
|
+
|
|
69
|
+
The secret fetching logic (including the fallback chain from rule 03) must live in a dedicated "connector" module or function. This isolates secret-access concerns from business logic and provides a single point to configure secret sources, caching policy, and error handling.
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
#### 05-setup-secrets-makefile-target
|
|
74
|
+
|
|
75
|
+
Every module that requires secrets must expose a `setup-secrets` Makefile target. This target:
|
|
76
|
+
|
|
77
|
+
- Prompts the user for each required secret value interactively.
|
|
78
|
+
- If the user provides an empty value, the existing secret is not updated.
|
|
79
|
+
- Stores the provided value in the native OS keychain.
|
|
80
|
+
- Uses the module name as the default group (service name).
|
|
81
|
+
- Uses secret identifiers that match the cloud secret manager IDs.
|
|
82
|
+
|
|
83
|
+
The desired developer flow:
|
|
84
|
+
|
|
85
|
+
```text
|
|
86
|
+
$ make run
|
|
87
|
+
Error: Secret 'api-key' could not be found in keychain under group 'mymodule'
|
|
88
|
+
or in AWS Secrets Manager under secretId 'api-key'
|
|
89
|
+
|
|
90
|
+
$ make setup-secrets
|
|
91
|
+
Enter value for 'api-key' (leave empty to skip):
|
|
92
|
+
> ****
|
|
93
|
+
Secret 'api-key' stored in keychain under group 'mymodule'.
|
|
94
|
+
|
|
95
|
+
$ make run
|
|
96
|
+
# Application starts successfully
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
#### 06-never-log-or-leak-secrets
|
|
102
|
+
|
|
103
|
+
Secrets must never be logged under any circumstance or sent to any service that is not clearly the intended consumer of that secret (authentication, encryption, etc.). This applies to all log levels including debug and trace. Error messages must reference the secret name or identifier, never its value.
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
#### 07-prefer-dynamic-fetching
|
|
108
|
+
|
|
109
|
+
Wherever possible, fetch secrets dynamically from the secret manager at the time of use. Avoid storing secrets in global variables or caching them indefinitely. Dynamic fetching through a specialized service enables:
|
|
110
|
+
|
|
111
|
+
- Automatic password rotation without redeployment.
|
|
112
|
+
- Immediate propagation of rotated secrets.
|
|
113
|
+
- Reduced window of exposure if memory is compromised.
|
|
114
|
+
|
|
115
|
+
Short-lived caching (e.g., a few minutes) is acceptable when performance requires it, but must have an explicit TTL.
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
#### 08-prefer-fetching-at-point-of-use
|
|
120
|
+
|
|
121
|
+
Prefer fetching the secret inside the function that directly needs it rather than passing it through multiple layers as a function argument. This minimizes the exposure surface by reducing the number of code paths that handle the raw secret value.
|
|
122
|
+
|
|
123
|
+
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.
|
|
124
|
+
|
|
125
|
+
## References
|
|
126
|
+
|
|
127
|
+
- [agentme-edr-008](../devops/008-common-targets.md) - Common development script names (defines Makefile target conventions)
|
|
128
|
+
- [agentme-edr-009](009-error-handling.md) - Error handling (governs how the fallback exception should be raised)
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: agentme-edr-policy-023-coding-abstraction-practices
|
|
3
|
+
description: Defines when abstractions (functions, classes, wrappers, factories) are justified and when they must be avoided. Tightly related to agentme-edr-policy-002-coding-best-practices.
|
|
4
|
+
apply-to: All software projects
|
|
5
|
+
valid-from: 2026-05-29
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# agentme-edr-policy-023: Coding abstraction practices
|
|
9
|
+
|
|
10
|
+
## Context and Problem Statement
|
|
11
|
+
|
|
12
|
+
Abstractions (helper functions, wrapper classes, factories, indirection layers) can clarify intent and reduce duplication — but when applied without discipline they obscure logic, force unnecessary navigation, and inflate codebases. Teams need clear criteria for when an abstraction earns its place versus when it adds unjustified indirection.
|
|
13
|
+
|
|
14
|
+
What principles should guide the decision to introduce — or reject — an abstraction?
|
|
15
|
+
|
|
16
|
+
## Decision Outcome
|
|
17
|
+
|
|
18
|
+
**Apply a bias toward explicit, functional, input→processing→output code. Introduce abstractions only when they demonstrably add value in logic, intent, reusability or readability.**
|
|
19
|
+
|
|
20
|
+
### Details
|
|
21
|
+
|
|
22
|
+
#### 01-prioritize-functional-programming
|
|
23
|
+
|
|
24
|
+
Prefer functional programming: pure functions with clear input → processing → output flow. Object-oriented patterns (classes, inheritance) are allowed **only** 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.
|
|
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.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
#### 02-prefer-explicit-calls-over-indirections
|
|
31
|
+
|
|
32
|
+
A sequence of direct calls to libraries and resources makes logic straightforward. Avoid:
|
|
33
|
+
|
|
34
|
+
- Aspect-oriented programming (AOP)
|
|
35
|
+
- Implicit context injection / dependency injection containers
|
|
36
|
+
- Magic decorators that alter control flow invisibly
|
|
37
|
+
|
|
38
|
+
These patterns obfuscate the main program flow and create behavioral indirections that require prior knowledge of state machines living outside the code itself.
|
|
39
|
+
|
|
40
|
+
*When indirection is acceptable:* Framework-mandated patterns (middleware chains, React context, etc.) where the reader already expects the indirection.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
#### 03-trivial-wrappers-are-prohibited
|
|
45
|
+
|
|
46
|
+
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
|
+
|
|
48
|
+
- Encapsulates non-trivial logic (validation, retry, transformation).
|
|
49
|
+
- Communicates a domain concept the underlying expression does not convey.
|
|
50
|
+
- Improves readability of a complex flow by giving a clear name to a cohesive block of work.
|
|
51
|
+
|
|
52
|
+
**Bad — trivial wrapper:**
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
function getUser(id: string) {
|
|
56
|
+
return db.users.findById(id);
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**Good — adds domain intent:**
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
function isEligibleForTrial(user: User): boolean {
|
|
64
|
+
return !user.hasSubscription && user.createdAt > trialCutoffDate && !user.isBanned;
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
#### 04-object-factories-must-add-value
|
|
71
|
+
|
|
72
|
+
A function that constructs an object (e.g., configuration, options) is only justified if it:
|
|
73
|
+
|
|
74
|
+
- Performs validation or assertion.
|
|
75
|
+
- Computes values dynamically based on inputs.
|
|
76
|
+
- Combines data in a non-linear or conditional way.
|
|
77
|
+
- Is reused by multiple callers.
|
|
78
|
+
|
|
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 must be inlined.
|
|
80
|
+
|
|
81
|
+
**Bad — trivial factory:**
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
function createConfig(port: number, host: string) {
|
|
85
|
+
return { port, host };
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**Good — adds validation and defaults:**
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
function createServerConfig(opts: Partial<ServerOpts>): ServerConfig {
|
|
93
|
+
if (opts.port && (opts.port < 1 || opts.port > 65535)) {
|
|
94
|
+
throw new Error(`Invalid port: ${opts.port}`);
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
port: opts.port ?? 3000,
|
|
98
|
+
host: opts.host ?? 'localhost',
|
|
99
|
+
timeout: computeTimeout(opts.environment),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
#### 05-abstractions-for-business-logic-are-encouraged
|
|
107
|
+
|
|
108
|
+
Extracting domain logic into a named function is **encouraged** when it:
|
|
109
|
+
|
|
110
|
+
- Encapsulates a business rule so the reader does not need to parse low-level conditions to understand domain intent.
|
|
111
|
+
- Communicates intent at a glance, making compound conditions or multi-step checks self-describing.
|
|
112
|
+
|
|
113
|
+
**Example:**
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
// Good — named function conveys business intent
|
|
117
|
+
if (isAllowed(event)) { ... }
|
|
118
|
+
|
|
119
|
+
// Bad — forces reader to decode domain meaning from raw conditions
|
|
120
|
+
if (event.status === 'active' && event.role !== 'guest' && event.quota > 0) { ... }
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
#### 06-idiomatic-framework-patterns-are-exempt
|
|
126
|
+
|
|
127
|
+
React hooks, higher-order components, middleware chains, and similar patterns established by the framework in use are **not** considered unnecessary abstraction. The reader already expects them, and fighting the framework's idioms creates more confusion than it removes.
|
|
128
|
+
|
|
129
|
+
This exemption does not override other rules — a trivial wrapper inside a hook is still prohibited.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentme",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"filedist": "^0.34.1"
|
|
@@ -22,6 +22,6 @@
|
|
|
22
22
|
"url": "https://github.com/flaviostutz/agentme.git"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
|
-
"xdrs-core": "^0.28.
|
|
25
|
+
"xdrs-core": "^0.28.1"
|
|
26
26
|
}
|
|
27
27
|
}
|