-
Notifications
You must be signed in to change notification settings - Fork 35
Expand file tree
/
Copy pathpyproject.toml
More file actions
162 lines (148 loc) · 7.67 KB
/
Copy pathpyproject.toml
File metadata and controls
162 lines (148 loc) · 7.67 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
[project]
name = "backgroundagent"
version = "0.1.0"
description = "Background coding agent — runs tasks in isolated cloud environments and produces pull requests"
requires-python = ">=3.13"
dependencies = [
"boto3==1.43.38", #https://pypi.org/project/boto3/
# Vestigial from the parked AgentCore Identity flow (Phase 2.0a).
# Phase 2.0b reads per-workspace Linear OAuth tokens directly from
# Secrets Manager because AgentCore Identity's USER_FEDERATION
# flow has an open service-side bug (see memory/project_oauth_2_0b.md).
# Kept here so the workload-token bridge in `server.py` still
# imports cleanly when Phase 2.0c eventually resumes the
# AgentCore Identity path. The bridge is now wrapped in
# try/except (ImportError, AttributeError), so removing this dep
# would degrade gracefully — but for now we keep the dep to
# preserve the clean code path.
"bedrock-agentcore==1.16.0", #https://pypi.org/project/bedrock-agentcore/
"claude-agent-sdk==0.2.110", #https://github.com/anthropics/claude-agent-sdk-python/releases/tag/v0.2.110 (bundles claude CLI 2.1.191; kept in lockstep with the npm CLI pin in the Dockerfile, #215)
"requests==2.34.2", #https://pypi.org/project/requests/
"fastapi==0.139.0", #https://pypi.org/project/fastapi/
"uvicorn==0.49.0", #https://pypi.org/project/uvicorn/
"aws-opentelemetry-distro==0.18.0", #https://pypi.org/project/aws-opentelemetry-distro/
"mcp==1.28.1", #https://pypi.org/project/mcp/
# CEDAR ENGINE PARITY — DO NOT BUMP IN ISOLATION.
# cedarpy (Python, agent runtime) and @cedar-policy/cedar-wasm (TypeScript,
# CDK Lambdas) are two language bindings over the same Cedar Rust core.
# Even patch-version drift between the bindings can produce divergent
# (decision, matching_rule_ids) on the same (policy, input) — a class
# of bug invisible to per-side unit tests. The contracts/cedar-parity/
# golden fixtures are how CI catches divergence; if you bump cedarpy
# you MUST bump @cedar-policy/cedar-wasm to a tested-compatible version
# in cdk/package.json AND refresh the parity fixtures, in the same
# commit. See docs/design/CEDAR_HITL_GATES.md §15.6 (decision #23) and
# the parity-contract banner in mise.toml.
# EXACT pin (no ^/~). The binding version (4.8.4) is the cedarpy package
# release, NOT the Cedar Rust core version — it differs from the TypeScript
# binding @cedar-policy/cedar-wasm (pinned at 4.8.2 in cdk/package.json).
# Matching binding version *strings* across languages is neither necessary
# nor sufficient for behavioral parity; parity is established empirically by
# the contracts/cedar-parity/ golden fixtures in CI, which assert identical
# (decision, matching_rule_ids) for both bindings on the same (policy, input).
"cedarpy==4.8.4", #https://github.com/k9securityio/cedar-py
# Workflow-driven tasks (#248): the step runner loads YAML workflow files
# and validates them against agent/workflows/schema/workflow.schema.json.
# Both were previously only transitively present; declared directly so the
# workflow loader does not depend on another package's transitive pin.
"pyyaml==6.0.3", #https://pypi.org/project/PyYAML/
"jsonschema==4.26.0", #https://pypi.org/project/jsonschema/
]
[tool.uv]
constraint-dependencies = [
"pyjwt>=2.13.0", # PYSEC-2026-175/177/178/179 — transitive via mcp; remove when mcp bumps floor (#267)
"pydantic-settings>=2.14.2", # GHSA-4xgf-cpjx-pc3j — transitive via mcp; remove when mcp bumps floor
]
# Dead-code detection (#282, cairn MVG gate #6). ruff "F" catches unused
# imports/locals; vulture catches unused module-level functions/classes/methods
# that ruff cannot see. min_confidence 80 keeps the signal high; intentional
# keeps (e.g. SDK-mandated hook params) live in .vulture_allowlist.py.
[tool.vulture]
paths = ["src", ".vulture_allowlist.py"]
min_confidence = 80
[tool.bandit]
exclude_dirs = ["tests", ".venv"]
skips = [
"B101", # assert_used — tests use assert, ruff S101 handles per-file
"B602", # subprocess shell=True — needed for shell tool execution
"B603", # subprocess calls — needed for shell tool execution
"B607", # partial executable paths — sh -c pattern is safe
"B701", # jinja2_autoescape_false — templates are LLM prompts, not web HTML
]
[dependency-groups]
dev = [
"ruff",
"ty",
"pytest",
"pygments==2.20.0",
"pytest-cov==7.1.0",
"pytest-timeout==2.4.0", # per-test wall-clock cap: a single hung test (network/subprocess/Bedrock without its own timeout) must fail LOUDLY with a traceback, not silently burn the whole build-verify budget (ABCA-684/686: one hang stalled the baseline build past its 3600s ceiling)
"vulture==2.16", # dead-code detection (#282): unused functions/classes ruff F can't see
]
[tool.ruff]
target-version = "py313"
line-length = 100
# The vulture allowlist is bare-name expressions by design (that is how vulture
# "uses" a name to suppress it). ruff would flag every line F821/B018, so it is
# not Python source ruff should lint. See .vulture_allowlist.py (#282).
extend-exclude = [".vulture_allowlist.py"]
[tool.ruff.lint]
select = [
"E", # pycodestyle errors
"W", # pycodestyle warnings
"F", # pyflakes
"I", # isort
"B", # flake8-bugbear
"S", # flake8-bandit (security)
"UP", # pyupgrade
"SIM", # flake8-simplify
"TCH", # flake8-type-checking
"RUF", # ruff-specific rules
# AI007 guard (#258): magic-value comparisons should use named constants.
# Values shared across Python/TypeScript belong in contracts/constants.json
# (see contracts/constants.md).
"PLR2004", # pylint magic-value-comparison
]
ignore = [
"S603", # subprocess call — allowed for agent CLI operations
"S607", # partial executable path — agent relies on PATH for git, gh, mise
]
[tool.ruff.lint.per-file-ignores]
"src/entrypoint.py" = ["E402"] # re-export shim: importlib.reload() call must precede re-export from-imports
"src/prompts/*.py" = ["E501"] # long prompt strings
"tests/**" = ["S101", "S106", "S108", "E402", "PLR2004"] # assert; test tokens; /tmp paths; importorskip; literal assertions
[tool.pytest.ini_options]
testpaths = ["tests"]
pythonpath = ["src"]
# pytest-timeout: hard wall-clock cap PER TEST. A unit test that blocks (an
# unmocked network/subprocess/Bedrock call with no timeout of its own) otherwise
# hangs the whole `mise run build` until the 3600s build-verify ceiling kills it,
# turning one flaky test into an un-diagnosable 60-min stall (ABCA-684/686). With
# this, the offending test fails with a dumped stack in 120s and the suite moves on.
# method="signal" (SIGALRM) actually ABORTS the hung test — pytest runs
# single-threaded in the main thread here, so the alarm interrupts even a blocked
# C-level/socket call. method="thread" only PRINTS the stack and cannot interrupt a
# syscall-blocked test (proven live on ABCA-688: the thread method dumped the stack
# but the build kept hanging ~55 min until the 3600s ceiling). signal is the real fix.
timeout = 120
timeout_method = "signal"
[tool.coverage.run]
branch = true
source = ["src"]
relative_files = true
[tool.coverage.report]
fail_under = 72
precision = 2
show_missing = true
skip_covered = true
[tool.ty.rules]
# asyncio.get_event_loop_policy() is deprecated in 3.14+; still valid on 3.13.
deprecated = "ignore"
[tool.ty.environment]
python-version = "3.13"
extra-paths = ["src"]
[tool.ty.src]
# The vulture allowlist is bare-name expressions by design, not type-checkable
# Python — ty flags each line unresolved-reference. Excluded here for the same
# reason ruff excludes it via extend-exclude (#282).
exclude = [".vulture_allowlist.py"]