toolyard 0.1.0__tar.gz
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.
- toolyard-0.1.0/.editorconfig +23 -0
- toolyard-0.1.0/.github/workflows/ci.yml +136 -0
- toolyard-0.1.0/.github/workflows/release.yml +57 -0
- toolyard-0.1.0/.gitignore +104 -0
- toolyard-0.1.0/.importlinter +132 -0
- toolyard-0.1.0/.pre-commit-config.yaml +22 -0
- toolyard-0.1.0/CHANGELOG.md +250 -0
- toolyard-0.1.0/CONTRIBUTING.md +122 -0
- toolyard-0.1.0/LICENSE +201 -0
- toolyard-0.1.0/PKG-INFO +312 -0
- toolyard-0.1.0/README.md +280 -0
- toolyard-0.1.0/SECURITY.md +93 -0
- toolyard-0.1.0/acceptance/register_and_execute.py +174 -0
- toolyard-0.1.0/docs/packages/toolyard/development-plan.md +145 -0
- toolyard-0.1.0/docs/packages/toolyard/spec.md +492 -0
- toolyard-0.1.0/pyproject.toml +114 -0
- toolyard-0.1.0/requirements/README.md +71 -0
- toolyard-0.1.0/requirements/ci.lock +778 -0
- toolyard-0.1.0/requirements/release.in +6 -0
- toolyard-0.1.0/requirements/release.lock +493 -0
- toolyard-0.1.0/src/toolyard/__about__.py +5 -0
- toolyard-0.1.0/src/toolyard/__init__.py +165 -0
- toolyard-0.1.0/src/toolyard/_safe.py +236 -0
- toolyard-0.1.0/src/toolyard/containment.py +464 -0
- toolyard-0.1.0/src/toolyard/errors.py +122 -0
- toolyard-0.1.0/src/toolyard/executor.py +710 -0
- toolyard-0.1.0/src/toolyard/py.typed +0 -0
- toolyard-0.1.0/src/toolyard/registry.py +165 -0
- toolyard-0.1.0/src/toolyard/sandbox.py +1114 -0
- toolyard-0.1.0/src/toolyard/store.py +68 -0
- toolyard-0.1.0/src/toolyard/tools/__init__.py +54 -0
- toolyard-0.1.0/src/toolyard/tools/command.py +230 -0
- toolyard-0.1.0/src/toolyard/tools/fetch.py +563 -0
- toolyard-0.1.0/src/toolyard/tools/files.py +548 -0
- toolyard-0.1.0/src/toolyard/types.py +799 -0
- toolyard-0.1.0/src/toolyard/validation.py +389 -0
- toolyard-0.1.0/tests/conftest.py +66 -0
- toolyard-0.1.0/tests/fakes.py +377 -0
- toolyard-0.1.0/tests/fixtures/fetch/adr0026_vectors.json +370 -0
- toolyard-0.1.0/tests/goldens/builtin_wire_definitions.json +121 -0
- toolyard-0.1.0/tests/goldens/wire_definitions.json +52 -0
- toolyard-0.1.0/tests/integration/test_isolation.py +421 -0
- toolyard-0.1.0/tests/oracles.py +45 -0
- toolyard-0.1.0/tests/performance/test_dispatch_budget.py +118 -0
- toolyard-0.1.0/tests/property/test_executor_property.py +261 -0
- toolyard-0.1.0/tests/strategies.py +515 -0
- toolyard-0.1.0/tests/unit/test_boundaries.py +402 -0
- toolyard-0.1.0/tests/unit/test_builtin_wire_definitions.py +101 -0
- toolyard-0.1.0/tests/unit/test_command_tool.py +306 -0
- toolyard-0.1.0/tests/unit/test_containment.py +387 -0
- toolyard-0.1.0/tests/unit/test_executor.py +692 -0
- toolyard-0.1.0/tests/unit/test_fetch_tool.py +526 -0
- toolyard-0.1.0/tests/unit/test_files_tools.py +581 -0
- toolyard-0.1.0/tests/unit/test_records.py +376 -0
- toolyard-0.1.0/tests/unit/test_registry.py +154 -0
- toolyard-0.1.0/tests/unit/test_safe.py +223 -0
- toolyard-0.1.0/tests/unit/test_sandbox.py +1138 -0
- toolyard-0.1.0/tests/unit/test_store.py +123 -0
- toolyard-0.1.0/tests/unit/test_types.py +429 -0
- toolyard-0.1.0/tests/unit/test_validation.py +272 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
root = true
|
|
2
|
+
|
|
3
|
+
[*]
|
|
4
|
+
charset = utf-8
|
|
5
|
+
end_of_line = lf
|
|
6
|
+
insert_final_newline = true
|
|
7
|
+
trim_trailing_whitespace = true
|
|
8
|
+
indent_style = space
|
|
9
|
+
indent_size = 4
|
|
10
|
+
|
|
11
|
+
[*.py]
|
|
12
|
+
indent_size = 4
|
|
13
|
+
max_line_length = 100
|
|
14
|
+
|
|
15
|
+
[*.{toml,yml,yaml,json}]
|
|
16
|
+
indent_size = 2
|
|
17
|
+
|
|
18
|
+
[*.md]
|
|
19
|
+
trim_trailing_whitespace = false
|
|
20
|
+
max_line_length = off
|
|
21
|
+
|
|
22
|
+
[Makefile]
|
|
23
|
+
indent_style = tab
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
format:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
- uses: actions/setup-python@v5
|
|
14
|
+
with: { python-version: "3.12" }
|
|
15
|
+
- run: pip install ruff
|
|
16
|
+
- run: ruff format --check .
|
|
17
|
+
|
|
18
|
+
lint:
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@v4
|
|
22
|
+
- uses: actions/setup-python@v5
|
|
23
|
+
with: { python-version: "3.12" }
|
|
24
|
+
- run: pip install ruff
|
|
25
|
+
- run: ruff check .
|
|
26
|
+
|
|
27
|
+
types:
|
|
28
|
+
runs-on: ubuntu-latest
|
|
29
|
+
steps:
|
|
30
|
+
- uses: actions/checkout@v4
|
|
31
|
+
- uses: actions/setup-python@v5
|
|
32
|
+
with: { python-version: "3.12" }
|
|
33
|
+
- run: pip install --require-hashes -r requirements/ci.lock
|
|
34
|
+
- run: pip install . --no-deps
|
|
35
|
+
- run: mypy src tests
|
|
36
|
+
|
|
37
|
+
boundaries:
|
|
38
|
+
runs-on: ubuntu-latest
|
|
39
|
+
steps:
|
|
40
|
+
- uses: actions/checkout@v4
|
|
41
|
+
- uses: actions/setup-python@v5
|
|
42
|
+
with: { python-version: "3.12" }
|
|
43
|
+
- run: pip install --require-hashes -r requirements/ci.lock
|
|
44
|
+
- run: pip install . --no-deps
|
|
45
|
+
- run: lint-imports
|
|
46
|
+
|
|
47
|
+
tests:
|
|
48
|
+
runs-on: ubuntu-latest
|
|
49
|
+
strategy:
|
|
50
|
+
matrix:
|
|
51
|
+
python-version: ["3.12", "3.13"]
|
|
52
|
+
steps:
|
|
53
|
+
- uses: actions/checkout@v4
|
|
54
|
+
- uses: actions/setup-python@v5
|
|
55
|
+
with: { python-version: "${{ matrix.python-version }}" }
|
|
56
|
+
- run: pip install --require-hashes -r requirements/ci.lock
|
|
57
|
+
- run: pip install . --no-deps
|
|
58
|
+
- run: pytest -m "not live and not performance" --cov --cov-report=xml
|
|
59
|
+
|
|
60
|
+
tests-314-early-warning:
|
|
61
|
+
runs-on: ubuntu-latest
|
|
62
|
+
continue-on-error: true
|
|
63
|
+
steps:
|
|
64
|
+
- uses: actions/checkout@v4
|
|
65
|
+
- uses: actions/setup-python@v5
|
|
66
|
+
with: { python-version: "3.14" }
|
|
67
|
+
# Deliberately unpinned: ci.lock is resolved on 3.13, and pinning a version that has no
|
|
68
|
+
# 3.14 wheels would defeat the purpose of an early warning.
|
|
69
|
+
- run: pip install -e ".[dev]"
|
|
70
|
+
- run: pytest -m "not live and not performance"
|
|
71
|
+
|
|
72
|
+
coverage:
|
|
73
|
+
needs: [tests]
|
|
74
|
+
runs-on: ubuntu-latest
|
|
75
|
+
steps:
|
|
76
|
+
- uses: actions/checkout@v4
|
|
77
|
+
- uses: actions/setup-python@v5
|
|
78
|
+
with: { python-version: "3.12" }
|
|
79
|
+
- run: pip install --require-hashes -r requirements/ci.lock
|
|
80
|
+
- run: pip install . --no-deps
|
|
81
|
+
- run: pytest -m "not live and not performance" --cov --cov-report=term-missing --cov-fail-under=95
|
|
82
|
+
|
|
83
|
+
contracts:
|
|
84
|
+
runs-on: ubuntu-latest
|
|
85
|
+
steps:
|
|
86
|
+
- uses: actions/checkout@v4
|
|
87
|
+
- uses: actions/setup-python@v5
|
|
88
|
+
with: { python-version: "3.12" }
|
|
89
|
+
- run: pip install --require-hashes -r requirements/ci.lock
|
|
90
|
+
- run: pip install . --no-deps
|
|
91
|
+
- run: pytest -m contract
|
|
92
|
+
|
|
93
|
+
security:
|
|
94
|
+
runs-on: ubuntu-latest
|
|
95
|
+
steps:
|
|
96
|
+
- uses: actions/checkout@v4
|
|
97
|
+
# gitleaks scans *history*, and `actions/checkout` fetches a single commit by default.
|
|
98
|
+
# For a push it is handed `<first-pushed>^..<last-pushed>`, so the parent of the first
|
|
99
|
+
# pushed commit has to be in the object store; in a depth-1 clone it is not, and git
|
|
100
|
+
# answers "unknown revision", which the action reports as exit code 1. It fails the same
|
|
101
|
+
# way whether or not a secret exists, so a green run would not have meant anything either.
|
|
102
|
+
with: { fetch-depth: 0 }
|
|
103
|
+
- uses: actions/setup-python@v5
|
|
104
|
+
with: { python-version: "3.12" }
|
|
105
|
+
- run: pip install pip-audit
|
|
106
|
+
# Audit the locked sets, not the job's own environment: a bare `pip-audit` here would
|
|
107
|
+
# inspect an environment containing only pip-audit itself (Security Standards §11).
|
|
108
|
+
- run: pip-audit --require-hashes -r requirements/ci.lock
|
|
109
|
+
- run: pip-audit --require-hashes -r requirements/release.lock
|
|
110
|
+
- uses: gitleaks/gitleaks-action@v2
|
|
111
|
+
env:
|
|
112
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
113
|
+
|
|
114
|
+
build:
|
|
115
|
+
runs-on: ubuntu-latest
|
|
116
|
+
steps:
|
|
117
|
+
- uses: actions/checkout@v4
|
|
118
|
+
- uses: actions/setup-python@v5
|
|
119
|
+
with: { python-version: "3.12" }
|
|
120
|
+
- run: pip install --require-hashes -r requirements/release.lock
|
|
121
|
+
- run: python -m build --no-isolation
|
|
122
|
+
- run: twine check dist/*
|
|
123
|
+
- uses: actions/upload-artifact@v4
|
|
124
|
+
with: { name: dist, path: dist/ }
|
|
125
|
+
|
|
126
|
+
install-check:
|
|
127
|
+
needs: [build]
|
|
128
|
+
runs-on: ubuntu-latest
|
|
129
|
+
steps:
|
|
130
|
+
- uses: actions/checkout@v4
|
|
131
|
+
- uses: actions/setup-python@v5
|
|
132
|
+
with: { python-version: "3.12" }
|
|
133
|
+
- uses: actions/download-artifact@v4
|
|
134
|
+
with: { name: dist, path: dist/ }
|
|
135
|
+
- run: pip install dist/*.whl
|
|
136
|
+
- run: python -c "import toolyard"
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags: ["v*.*.*"]
|
|
6
|
+
workflow_dispatch: # manual TestPyPI dry run; see publish-testpypi below
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
id-token: write # required for PyPI Trusted Publishing
|
|
10
|
+
contents: write # required to create the GitHub release
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
release:
|
|
14
|
+
# Tag pushes only. Without this, clicking "Run workflow" for the TestPyPI dry run below would
|
|
15
|
+
# also fire this job and publish to real PyPI — the exact opposite of a dry run.
|
|
16
|
+
if: github.event_name == 'push'
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
environment: pypi # must match the Environment name set on the PyPI trusted publisher
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v4
|
|
21
|
+
- uses: actions/setup-python@v5
|
|
22
|
+
with: { python-version: "3.12" }
|
|
23
|
+
# Byte-for-byte the dry run's build chain below. A real release that resolved `build` and
|
|
24
|
+
# `hatchling` fresh from PyPI would not be the artifact the dry run proved.
|
|
25
|
+
- run: pip install --require-hashes -r requirements/release.lock
|
|
26
|
+
- run: python -m build --no-isolation
|
|
27
|
+
- run: twine check dist/*
|
|
28
|
+
- run: pip install "dist/$(ls dist | grep .whl)[dev]"
|
|
29
|
+
- run: pytest -m "not live and not performance"
|
|
30
|
+
- name: Publish to PyPI
|
|
31
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
32
|
+
- name: Create GitHub release
|
|
33
|
+
uses: softprops/action-gh-release@v2
|
|
34
|
+
with:
|
|
35
|
+
generate_release_notes: true
|
|
36
|
+
files: dist/*
|
|
37
|
+
|
|
38
|
+
publish-testpypi:
|
|
39
|
+
# Manual only, via Actions -> Release -> Run workflow. Packaging and Release Standards §6
|
|
40
|
+
# requires a successful TestPyPI publish ahead of a package's first real release; 0.1.0 is
|
|
41
|
+
# this package's first published version, so run this once before tagging v0.1.0. Later
|
|
42
|
+
# releases may skip it, or use it again as a dry run.
|
|
43
|
+
if: github.event_name == 'workflow_dispatch'
|
|
44
|
+
runs-on: ubuntu-latest
|
|
45
|
+
steps:
|
|
46
|
+
- uses: actions/checkout@v4
|
|
47
|
+
- uses: actions/setup-python@v5
|
|
48
|
+
with: { python-version: "3.12" }
|
|
49
|
+
- run: pip install --require-hashes -r requirements/release.lock
|
|
50
|
+
- run: python -m build --no-isolation
|
|
51
|
+
- run: twine check dist/*
|
|
52
|
+
- run: pip install "dist/$(ls dist | grep .whl)[dev]"
|
|
53
|
+
- run: pytest -m "not live and not performance"
|
|
54
|
+
- name: Publish to TestPyPI
|
|
55
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
56
|
+
with:
|
|
57
|
+
repository-url: https://test.pypi.org/legacy/
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# ---- Python ----
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
.Python
|
|
7
|
+
build/
|
|
8
|
+
develop-eggs/
|
|
9
|
+
dist/
|
|
10
|
+
downloads/
|
|
11
|
+
eggs/
|
|
12
|
+
.eggs/
|
|
13
|
+
lib/
|
|
14
|
+
lib64/
|
|
15
|
+
parts/
|
|
16
|
+
sdist/
|
|
17
|
+
var/
|
|
18
|
+
wheels/
|
|
19
|
+
*.egg-info/
|
|
20
|
+
.installed.cfg
|
|
21
|
+
*.egg
|
|
22
|
+
MANIFEST
|
|
23
|
+
|
|
24
|
+
# ---- Packaging / build backends ----
|
|
25
|
+
pip-wheel-metadata/
|
|
26
|
+
share/python-wheels/
|
|
27
|
+
|
|
28
|
+
# ---- Testing / coverage ----
|
|
29
|
+
.pytest_cache/
|
|
30
|
+
.cache
|
|
31
|
+
.coverage
|
|
32
|
+
.coverage.*
|
|
33
|
+
coverage.xml
|
|
34
|
+
*.cover
|
|
35
|
+
*.py,cover
|
|
36
|
+
htmlcov/
|
|
37
|
+
nosetests.xml
|
|
38
|
+
.hypothesis/
|
|
39
|
+
|
|
40
|
+
# ---- Type checking / linting ----
|
|
41
|
+
.mypy_cache/
|
|
42
|
+
.dmypy.json
|
|
43
|
+
dmypy.json
|
|
44
|
+
.ruff_cache/
|
|
45
|
+
.pytype/
|
|
46
|
+
|
|
47
|
+
# ---- Virtual environments ----
|
|
48
|
+
.venv/
|
|
49
|
+
venv/
|
|
50
|
+
ENV/
|
|
51
|
+
env/
|
|
52
|
+
env.bak/
|
|
53
|
+
venv.bak/
|
|
54
|
+
.python-version
|
|
55
|
+
|
|
56
|
+
# ---- Distribution / dependency locking ----
|
|
57
|
+
# requirements/*.lock is deliberately NOT ignored. The lock files are committed
|
|
58
|
+
# inputs: CI and release jobs install from them with --require-hashes (Packaging
|
|
59
|
+
# and Release Standards §4, Security Standards §11), so an ignored lock file
|
|
60
|
+
# means a checkout that cannot build.
|
|
61
|
+
|
|
62
|
+
# ---- Editors / OS ----
|
|
63
|
+
.vscode/
|
|
64
|
+
.idea/
|
|
65
|
+
*.swp
|
|
66
|
+
*.swo
|
|
67
|
+
.DS_Store
|
|
68
|
+
Thumbs.db
|
|
69
|
+
|
|
70
|
+
# ---- Suite runtime state (this component's local data) ----
|
|
71
|
+
# The application writes to XDG paths at runtime (~/.config, ~/.local/share,
|
|
72
|
+
# ~/.local/state per Master Architecture §1.2), never inside the repository.
|
|
73
|
+
# These entries only guard against a developer pointing XDG_* at the repo
|
|
74
|
+
# during local testing.
|
|
75
|
+
.local/
|
|
76
|
+
.config/
|
|
77
|
+
*.sqlite3
|
|
78
|
+
*.sqlite3-journal
|
|
79
|
+
*.sqlite3-wal
|
|
80
|
+
*.sqlite3-shm
|
|
81
|
+
/data/
|
|
82
|
+
/logs/
|
|
83
|
+
/backups/
|
|
84
|
+
/artifacts/
|
|
85
|
+
/exports/
|
|
86
|
+
|
|
87
|
+
# ---- Secrets ----
|
|
88
|
+
# Per Security Standards §8: secrets are never committed. Config files may
|
|
89
|
+
# only name where a secret comes from (*_env / *_file), never the value.
|
|
90
|
+
.env
|
|
91
|
+
.env.*
|
|
92
|
+
*.key
|
|
93
|
+
*.pem
|
|
94
|
+
secrets.toml
|
|
95
|
+
|
|
96
|
+
# ---- Node-free JS assets (MirrorWall consumers may still use a local tool) ----
|
|
97
|
+
node_modules/
|
|
98
|
+
|
|
99
|
+
# ---- Build artifacts from docs generation ----
|
|
100
|
+
docs/api/openapi-v1.json.tmp
|
|
101
|
+
|
|
102
|
+
# ---- import-linter cache ----
|
|
103
|
+
# `lint-imports` writes this on every gate run; it is a cache, never an input.
|
|
104
|
+
.import_linter_cache/
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
[importlinter]
|
|
2
|
+
root_package = toolyard
|
|
3
|
+
include_external_packages = True
|
|
4
|
+
|
|
5
|
+
[importlinter:contract:no-application-imports]
|
|
6
|
+
# Master architecture §2: packages never import applications. Never weakened, never extended —
|
|
7
|
+
# a package that "needs" an application type has been handed application responsibility.
|
|
8
|
+
name = ToolYard must not import applications
|
|
9
|
+
type = forbidden
|
|
10
|
+
source_modules = toolyard
|
|
11
|
+
forbidden_modules =
|
|
12
|
+
freeweight
|
|
13
|
+
loadcoach
|
|
14
|
+
ideapress
|
|
15
|
+
promptcadence
|
|
16
|
+
|
|
17
|
+
[importlinter:contract:no-sibling-packages]
|
|
18
|
+
# Spec §5: "Nothing else — in particular not `setspec` (records are in-process values;
|
|
19
|
+
# applications serialize them) and no sibling capability package." Never weakened, never extended.
|
|
20
|
+
name = ToolYard must not import sibling capability packages
|
|
21
|
+
type = forbidden
|
|
22
|
+
source_modules = toolyard
|
|
23
|
+
forbidden_modules =
|
|
24
|
+
setspec
|
|
25
|
+
modelrack
|
|
26
|
+
sweatmeter
|
|
27
|
+
weightsdb
|
|
28
|
+
mirrorwall
|
|
29
|
+
cutctx
|
|
30
|
+
loadledger
|
|
31
|
+
commissioner
|
|
32
|
+
spotcheck
|
|
33
|
+
|
|
34
|
+
[importlinter:contract:no-model-access]
|
|
35
|
+
# Spec §3: "No model access and no provider JSON." ToolYard exports a neutral wire definition and
|
|
36
|
+
# the caller adapts it; there is no client here to repurpose. Never weakened, never extended.
|
|
37
|
+
#
|
|
38
|
+
# allow_indirect_imports: these contracts describe what *this package's own modules* import. A
|
|
39
|
+
# chain through a dependency would be that dependency's budget to answer for.
|
|
40
|
+
name = ToolYard must hold no path to a model
|
|
41
|
+
allow_indirect_imports = True
|
|
42
|
+
type = forbidden
|
|
43
|
+
source_modules = toolyard
|
|
44
|
+
forbidden_modules =
|
|
45
|
+
openai
|
|
46
|
+
anthropic
|
|
47
|
+
ollama
|
|
48
|
+
litellm
|
|
49
|
+
transformers
|
|
50
|
+
torch
|
|
51
|
+
huggingface_hub
|
|
52
|
+
|
|
53
|
+
[importlinter:contract:no-persistence]
|
|
54
|
+
# Spec §10: "Data ownership: none." The application persists records through its own
|
|
55
|
+
# `ToolCallStore`; ToolYard defines the protocol and an in-memory implementation for tests.
|
|
56
|
+
# Never weakened, never extended.
|
|
57
|
+
name = ToolYard must touch no database
|
|
58
|
+
allow_indirect_imports = True
|
|
59
|
+
type = forbidden
|
|
60
|
+
source_modules = toolyard
|
|
61
|
+
forbidden_modules =
|
|
62
|
+
sqlalchemy
|
|
63
|
+
alembic
|
|
64
|
+
sqlite3
|
|
65
|
+
|
|
66
|
+
[importlinter:contract:subprocess-only-in-the-sandbox]
|
|
67
|
+
# LAYERED, and written for the module that does not exist yet. ADR-0053 decision 5 and ADR-0018
|
|
68
|
+
# put every process launch behind one door: the container → bwrap → refuse ladder. Phase 2 (row D1)
|
|
69
|
+
# writes `toolyard.sandbox` and *that module alone* may import `subprocess`; the exemption below is
|
|
70
|
+
# already here so D1 adds a module rather than editing a boundary rule.
|
|
71
|
+
#
|
|
72
|
+
# `unmatched_ignore_imports_alerting = none` is what lets an exemption name an import that does not
|
|
73
|
+
# exist yet. It is not a weakening: any *other* module importing `subprocess` still fails.
|
|
74
|
+
name = Only the sandbox may import subprocess
|
|
75
|
+
allow_indirect_imports = True
|
|
76
|
+
unmatched_ignore_imports_alerting = none
|
|
77
|
+
type = forbidden
|
|
78
|
+
source_modules = toolyard
|
|
79
|
+
forbidden_modules =
|
|
80
|
+
subprocess
|
|
81
|
+
multiprocessing
|
|
82
|
+
pty
|
|
83
|
+
ignore_imports =
|
|
84
|
+
toolyard.sandbox -> subprocess
|
|
85
|
+
|
|
86
|
+
[importlinter:contract:http-only-in-the-fetch-tool]
|
|
87
|
+
# LAYERED, same shape. ADR-0053 decision 6 puts the ADR-0026 §3 checks where the socket is opened.
|
|
88
|
+
# Phase 3 (row E2) writes `toolyard.tools.fetch` and *that module alone* may import `httpx`; every
|
|
89
|
+
# other HTTP client stays forbidden outright, in every module, forever — a second client is a
|
|
90
|
+
# second chance to omit the redirect check.
|
|
91
|
+
name = Only the fetch tool may hold an HTTP client
|
|
92
|
+
allow_indirect_imports = True
|
|
93
|
+
unmatched_ignore_imports_alerting = none
|
|
94
|
+
type = forbidden
|
|
95
|
+
source_modules = toolyard
|
|
96
|
+
forbidden_modules =
|
|
97
|
+
httpx
|
|
98
|
+
requests
|
|
99
|
+
aiohttp
|
|
100
|
+
urllib3
|
|
101
|
+
urllib
|
|
102
|
+
http
|
|
103
|
+
socket
|
|
104
|
+
ssl
|
|
105
|
+
ignore_imports =
|
|
106
|
+
toolyard.tools.fetch -> httpx
|
|
107
|
+
|
|
108
|
+
[importlinter:contract:filesystem-only-where-containment-lives]
|
|
109
|
+
# LAYERED. Spec §14: no argument is ever interpolated into a path. The way that is kept true is
|
|
110
|
+
# that path handling exists in one place — `toolyard.containment` — which resolves before it
|
|
111
|
+
# compares. Phase 2's `toolyard.sandbox` and Phase 3's `toolyard.tools.files` are the two modules
|
|
112
|
+
# that join it; their exemptions are already written.
|
|
113
|
+
name = Filesystem access lives only where containment does
|
|
114
|
+
allow_indirect_imports = True
|
|
115
|
+
unmatched_ignore_imports_alerting = none
|
|
116
|
+
type = forbidden
|
|
117
|
+
source_modules = toolyard
|
|
118
|
+
forbidden_modules =
|
|
119
|
+
os
|
|
120
|
+
pathlib
|
|
121
|
+
shutil
|
|
122
|
+
tempfile
|
|
123
|
+
glob
|
|
124
|
+
fnmatch
|
|
125
|
+
ignore_imports =
|
|
126
|
+
toolyard.containment -> pathlib
|
|
127
|
+
toolyard.containment -> os
|
|
128
|
+
toolyard.sandbox -> pathlib
|
|
129
|
+
toolyard.sandbox -> os
|
|
130
|
+
toolyard.sandbox -> shutil
|
|
131
|
+
toolyard.sandbox -> tempfile
|
|
132
|
+
toolyard.tools.files -> pathlib
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
3
|
+
rev: v0.6.9
|
|
4
|
+
hooks:
|
|
5
|
+
- id: ruff
|
|
6
|
+
args: [--fix]
|
|
7
|
+
- id: ruff-format
|
|
8
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
9
|
+
rev: v4.6.0
|
|
10
|
+
hooks:
|
|
11
|
+
- id: trailing-whitespace
|
|
12
|
+
- id: end-of-file-fixer
|
|
13
|
+
- id: check-toml
|
|
14
|
+
- id: check-json
|
|
15
|
+
- id: check-added-large-files
|
|
16
|
+
- id: check-merge-conflict
|
|
17
|
+
- id: mixed-line-ending
|
|
18
|
+
args: [--fix=lf]
|
|
19
|
+
- repo: https://github.com/gitleaks/gitleaks
|
|
20
|
+
rev: v8.18.4
|
|
21
|
+
hooks:
|
|
22
|
+
- id: gitleaks
|